0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-12 18:18:24 -05:00

Plugins create svg shapes

This commit is contained in:
alonso.torres 2024-04-29 15:43:48 +02:00
parent 21d38a058b
commit 67d48435e7
6 changed files with 128 additions and 103 deletions

View file

@ -22,6 +22,7 @@
[app.common.svg :as csvg]
[app.common.svg.path :as path]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[cuerdas.core :as str]))
(def default-rect
@ -78,67 +79,68 @@
(declare parse-svg-element)
(defn create-svg-shapes
[svg-data {:keys [x y]} objects frame-id parent-id selected center?]
(let [[vb-x vb-y vb-width vb-height] (svg-dimensions svg-data)
([svg-data pos objects frame-id parent-id selected center?]
(create-svg-shapes (uuid/next) svg-data pos objects frame-id parent-id selected center?))
([id svg-data {:keys [x y]} objects frame-id parent-id selected center?]
(let [[vb-x vb-y vb-width vb-height] (svg-dimensions svg-data)
unames (cfh/get-used-names objects)
svg-name (str/replace (:name svg-data) ".svg" "")
unames (cfh/get-used-names objects)
svg-name (str/replace (:name svg-data) ".svg" "")
svg-data (-> svg-data
(assoc :x (mth/round
(if center?
(- x vb-x (/ vb-width 2))
x)))
(assoc :y (mth/round
(if center?
(- y vb-y (/ vb-height 2))
y)))
(assoc :offset-x vb-x)
(assoc :offset-y vb-y)
(assoc :width vb-width)
(assoc :height vb-height)
(assoc :name svg-name))
svg-data (-> svg-data
(assoc :x (mth/round
(if center?
(- x vb-x (/ vb-width 2))
x)))
(assoc :y (mth/round
(if center?
(- y vb-y (/ vb-height 2))
y)))
(assoc :offset-x vb-x)
(assoc :offset-y vb-y)
(assoc :width vb-width)
(assoc :height vb-height)
(assoc :name svg-name))
[def-nodes svg-data]
(-> svg-data
(csvg/fix-default-values)
(csvg/fix-percents)
(csvg/extract-defs))
[def-nodes svg-data]
(-> svg-data
(csvg/fix-default-values)
(csvg/fix-percents)
(csvg/extract-defs))
;; In penpot groups have the size of their children. To
;; respect the imported svg size and empty space let's create
;; a transparent shape as background to respect the imported
;; size
background
{:tag :rect
:attrs {:x (dm/str vb-x)
:y (dm/str vb-y)
:width (dm/str vb-width)
:height (dm/str vb-height)
:fill "none"
:id "base-background"}
:hidden true
:content []}
;; In penpot groups have the size of their children. To
;; respect the imported svg size and empty space let's create
;; a transparent shape as background to respect the imported
;; size
background
{:tag :rect
:attrs {:x (dm/str vb-x)
:y (dm/str vb-y)
:width (dm/str vb-width)
:height (dm/str vb-height)
:fill "none"
:id "base-background"}
:hidden true
:content []}
svg-data (-> svg-data
(assoc :defs def-nodes)
(assoc :content (into [background] (:content svg-data))))
svg-data (-> svg-data
(assoc :defs def-nodes)
(assoc :content (into [background] (:content svg-data))))
root-shape (create-svg-root id frame-id parent-id svg-data)
root-id (:id root-shape)
root-shape (create-svg-root frame-id parent-id svg-data)
root-id (:id root-shape)
;; Create the root shape
root-attrs (-> (:attrs svg-data)
(csvg/format-styles))
;; Create the root shape
root-attrs (-> (:attrs svg-data)
(csvg/format-styles))
[_ children]
(reduce (partial create-svg-children objects selected frame-id root-id svg-data)
[unames []]
(d/enumerate (->> (:content svg-data)
(mapv #(csvg/inherit-attributes root-attrs %)))))]
[_ children]
(reduce (partial create-svg-children objects selected frame-id root-id svg-data)
[unames []]
(d/enumerate (->> (:content svg-data)
(mapv #(csvg/inherit-attributes root-attrs %)))))]
[root-shape children]))
[root-shape children])))
(defn create-raw-svg
[name frame-id {:keys [x y width height offset-x offset-y]} {:keys [attrs] :as data}]
@ -157,12 +159,13 @@
:svg-viewbox vbox})))
(defn create-svg-root
[frame-id parent-id {:keys [name x y width height offset-x offset-y attrs]}]
[id frame-id parent-id {:keys [name x y width height offset-x offset-y attrs]}]
(let [props (-> (dissoc attrs :viewBox :view-box :xmlns)
(d/without-keys csvg/inheritable-props)
(csvg/attrs->props))]
(cts/setup-shape
{:type :group
{:id id
:type :group
:name name
:frame-id frame-id
:parent-id parent-id

View file

@ -203,7 +203,7 @@
ptk/WatchEvent
(watch [_ state _]
(let [selected (wsh/lookup-selected state)]
(rx/of group-shapes nil selected)))))
(rx/of (group-shapes nil selected))))))
(defn ungroup-shapes
[ids & {:keys [change-selection?] :or {change-selection? false}}]

View file

@ -459,3 +459,12 @@
(rx/tap on-success)
(rx/catch on-error)
(rx/finalize #(st/emit! (msg/hide-tag :media-loading)))))))))
(defn create-svg-shape
[id name svg-string position]
(ptk/reify ::create-svg-shape
ptk/WatchEvent
(watch [_ _ _]
(->> (svg->clj [name svg-string])
(rx/take 1)
(rx/map #(svg/add-svg-shapes id % position {:change-selection? false}))))))

View file

@ -13,6 +13,7 @@
[app.common.svg :as csvg]
[app.common.svg.shapes-builder :as csvg.shapes-builder]
[app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh]
@ -60,52 +61,58 @@
(rx/reduce conj {})))
(defn add-svg-shapes
[svg-data position]
(ptk/reify ::add-svg-shapes
ptk/WatchEvent
(watch [it state _]
(try
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
frame-id (ctst/top-nested-frame objects position)
selected (wsh/lookup-selected state)
base (cfh/get-base-shape objects selected)
([svg-data position]
(add-svg-shapes nil svg-data position nil))
selected-id (first selected)
selected-frame? (and (= 1 (count selected))
(= :frame (dm/get-in objects [selected-id :type])))
([id svg-data position {:keys [change-selection?] :or {change-selection? false}}]
(ptk/reify ::add-svg-shapes
ptk/WatchEvent
(watch [it state _]
(try
(let [id (d/nilv id (uuid/next))
page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
frame-id (ctst/top-nested-frame objects position)
selected (wsh/lookup-selected state)
base (cfh/get-base-shape objects selected)
parent-id (if (or selected-frame? (empty? selected))
frame-id
(:parent-id base))
selected-id (first selected)
selected-frame? (and (= 1 (count selected))
(= :frame (dm/get-in objects [selected-id :type])))
[new-shape new-children]
(csvg.shapes-builder/create-svg-shapes svg-data position objects frame-id parent-id selected true)
parent-id (if (or selected-frame? (empty? selected))
frame-id
(:parent-id base))
changes (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
(pcb/add-object new-shape))
[new-shape new-children]
(csvg.shapes-builder/create-svg-shapes id svg-data position objects frame-id parent-id selected true)
changes (reduce (fn [changes new-child]
(pcb/add-object changes new-child))
changes
new-children)
changes (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
(pcb/add-object new-shape))
changes (pcb/resize-parents changes
(->> (:redo-changes changes)
(filter #(= :add-obj (:type %)))
(map :id)
(reverse)
(vec)))
undo-id (js/Symbol)]
changes (reduce (fn [changes new-child]
(pcb/add-object changes new-child))
changes
new-children)
(rx/of (dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dws/select-shapes (d/ordered-set (:id new-shape)))
(ptk/data-event :layout/update {:ids [(:id new-shape)]})
(dwu/commit-undo-transaction undo-id)))
changes (pcb/resize-parents changes
(->> (:redo-changes changes)
(filter #(= :add-obj (:type %)))
(map :id)
(reverse)
(vec)))
undo-id (js/Symbol)]
(rx/of (dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(when change-selection?
(dws/select-shapes (d/ordered-set (:id new-shape))))
(ptk/data-event :layout/update {:ids [(:id new-shape)]})
(dwu/commit-undo-transaction undo-id)))
(catch :default cause
(js/console.log (.-stack cause))
(rx/throw {:type :svg-parser
:data cause})))))))
(catch :default cause
(js/console.log (.-stack cause))
(rx/throw {:type :svg-parser
:data cause}))))))

View file

@ -9,6 +9,7 @@
(:require
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as cb]
[app.common.geom.point :as gpt]
[app.common.record :as cr]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
@ -127,7 +128,14 @@
(createRectangle
[_]
(create-shape :rect))
)
(createShapeFromSvg
[_ svg-string]
(let [id (uuid/next)
page-id (:current-page-id @st/state)]
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
(shape/data->shape-proxy
(dm/get-in @st/state [:workspace-data :pages-index page-id :objects id])))))
(defn create-context
[]

View file

@ -67,12 +67,12 @@
(appendChild [self child]
(let [parent-id (get-data self :id)
child-id (uuid/uuid (obj/get child "id"))]
(st/emit! (udw/relocate-shapes #{ child-id } parent-id 0))))
(st/emit! (udw/relocate-shapes #{child-id} parent-id 0))))
(insertChild [self index child]
(let [parent-id (get-data self :id)
child-id (uuid/uuid (obj/get child "id"))]
(st/emit! (udw/relocate-shapes #{ child-id } parent-id index)))))
(st/emit! (udw/relocate-shapes #{child-id} parent-id index)))))
(crc/define-properties!
ShapeProxy
@ -124,16 +124,14 @@
:set (fn [self value]
(let [id (get-data self :id)
value (mapv #(utils/from-js %) value)]
(st/emit! (dwc/update-shapes [id] #(assoc % :fills value)))))
}
(st/emit! (dwc/update-shapes [id] #(assoc % :fills value)))))}
{:name "strokes"
:get #(get-state % :strokes make-strokes)
:set (fn [self value]
(let [id (get-data self :id)
value (mapv #(utils/from-js %) value)]
(st/emit! (dwc/update-shapes [id] #(assoc % :strokes value)))))
})
(st/emit! (dwc/update-shapes [id] #(assoc % :strokes value)))))})
(cond-> (or (cfh/frame-shape? data) (cfh/group-shape? data) (cfh/svg-raw-shape? data) (cfh/bool-shape? data))
(crc/add-properties!