mirror of
https://github.com/penpot/penpot.git
synced 2025-02-10 00:58:26 -05:00
✨ Support for fill,stroke,gradient,text
This commit is contained in:
parent
8d703a3fb4
commit
83879fb931
5 changed files with 179 additions and 45 deletions
|
@ -7,12 +7,12 @@
|
|||
(ns app.common.file-builder
|
||||
"A version parsing helper."
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.changes :as ch]
|
||||
[app.common.pages.init :as init]
|
||||
[app.common.pages.spec :as spec]
|
||||
[app.common.spec :as us]
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
(def root-frame uuid/zero)
|
||||
|
@ -130,6 +130,7 @@
|
|||
(lookup-shape file frame-id))
|
||||
obj (-> (init/make-minimal-shape type)
|
||||
(merge data)
|
||||
(d/without-nils)
|
||||
(cond-> frame
|
||||
(gsh/translate-from-frame frame)))]
|
||||
(commit-shape file obj)))
|
||||
|
|
|
@ -30,6 +30,15 @@
|
|||
:stop-color color
|
||||
:stop-opacity opacity}])]))
|
||||
|
||||
(defn add-metadata [props gradient]
|
||||
(-> props
|
||||
(obj/set! "penpot:start-x" (:start-x gradient))
|
||||
(obj/set! "penpot:start-x" (:start-x gradient))
|
||||
(obj/set! "penpot:start-y" (:start-y gradient))
|
||||
(obj/set! "penpot:end-x" (:end-x gradient))
|
||||
(obj/set! "penpot:end-y" (:end-y gradient))
|
||||
(obj/set! "penpot:width" (:width gradient))))
|
||||
|
||||
(mf/defc radial-gradient [{:keys [id gradient shape]}]
|
||||
(let [{:keys [x y width height]} (:selrect shape)
|
||||
center (gsh/center-shape shape)
|
||||
|
@ -59,13 +68,17 @@
|
|||
transform (gmt/multiply transform
|
||||
(gmt/translate-matrix translate-vec)
|
||||
(gmt/rotate-matrix angle)
|
||||
(gmt/scale-matrix scale-vec))]
|
||||
[:radialGradient {:id id
|
||||
:cx 0
|
||||
:cy 0
|
||||
:r 1
|
||||
:gradientUnits "userSpaceOnUse"
|
||||
:gradientTransform transform}
|
||||
(gmt/scale-matrix scale-vec))
|
||||
|
||||
base-props #js {:id id
|
||||
:cx 0
|
||||
:cy 0
|
||||
:r 1
|
||||
:gradientUnits "userSpaceOnUse"
|
||||
:gradientTransform transform}
|
||||
|
||||
props (-> base-props (add-metadata gradient))]
|
||||
[:> :radialGradient props
|
||||
(for [{:keys [offset color opacity]} (:stops gradient)]
|
||||
[:stop {:key (str id "-stop-" offset)
|
||||
:offset (or offset 0)
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
[app.main.ui.shapes.gradients :as grad]
|
||||
[app.main.ui.shapes.svg-defs :as defs]
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
[rumext.alpha :as mf]
|
||||
[app.util.json :as json]))
|
||||
|
||||
(defn add-metadata
|
||||
"Adds as metadata properties that we cannot deduce from the exported SVG"
|
||||
|
@ -26,7 +27,8 @@
|
|||
(let [ns-attr (str "penpot:" (-> attr d/name))]
|
||||
(-> props
|
||||
(obj/set! ns-attr val))))
|
||||
frame? (= :frame (:type shape))]
|
||||
frame? (= :frame (:type shape))
|
||||
text? (= :text (:type shape))]
|
||||
(-> props
|
||||
(add! :name (-> shape :name))
|
||||
(add! :blocked (-> shape (:blocked false) str))
|
||||
|
@ -45,6 +47,10 @@
|
|||
(add! :r3 (-> shape (:r3 0) str))
|
||||
(add! :r4 (-> shape (:r4 0) str))))
|
||||
|
||||
(cond-> text?
|
||||
(-> (add! :grow-type (-> shape :grow-type))
|
||||
(add! :content (-> shape :content json/encode))))
|
||||
|
||||
(cond-> frame?
|
||||
(obj/set! "xmlns:penpot" "https://penpot.app/xmlns")))))
|
||||
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[cuerdas.core :as str]
|
||||
[app.util.path.parser :as upp]))
|
||||
[app.util.color :as uc]
|
||||
[app.util.json :as json]
|
||||
[app.util.path.parser :as upp]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn valid?
|
||||
[root]
|
||||
|
@ -38,9 +40,9 @@
|
|||
(or (close? node)
|
||||
(contains? (:attrs node) :penpot:type)))
|
||||
|
||||
(defn get-attr
|
||||
(defn get-meta
|
||||
([m att]
|
||||
(get-attr m att identity))
|
||||
(get-meta m att identity))
|
||||
([m att val-fn]
|
||||
(let [ns-att (->> att d/name (str "penpot:") keyword)
|
||||
val (get-in m [:attrs ns-att])]
|
||||
|
@ -78,22 +80,25 @@
|
|||
(reduce-kv
|
||||
(fn [m k v]
|
||||
(if (#{:style :data-style} k)
|
||||
(assoc m :style (parse-style v))
|
||||
(merge m (parse-style v))
|
||||
(assoc m k v)))
|
||||
m
|
||||
attrs))
|
||||
|
||||
(defn get-data-node
|
||||
[node]
|
||||
|
||||
(let [data-tags #{:ellipse :rect :path}]
|
||||
(->> node
|
||||
(node-seq)
|
||||
(filter #(contains? data-tags (:tag %)))
|
||||
(map #(:attrs %))
|
||||
(reduce add-attrs {}))))
|
||||
|
||||
(def search-data-node? #{:rect :image :path :text :circle})
|
||||
|
||||
(defn get-shape-data
|
||||
[type node]
|
||||
|
||||
(if (search-data-node? type)
|
||||
(let [data-tags #{:ellipse :rect :path :text :foreignObject}]
|
||||
(->> node
|
||||
(node-seq)
|
||||
(filter #(contains? data-tags (:tag %)))
|
||||
(map #(:attrs %))
|
||||
(reduce add-attrs {})))
|
||||
(:attrs node)))
|
||||
|
||||
(def has-position? #{:frame :rect :image :text})
|
||||
|
||||
(defn parse-position
|
||||
|
@ -123,22 +128,103 @@
|
|||
(assoc :selrect selrect)
|
||||
(assoc :points points))))
|
||||
|
||||
(defn extract-data
|
||||
[type node]
|
||||
(let [data (if (search-data-node? type)
|
||||
(get-data-node node)
|
||||
(:attrs node))]
|
||||
(cond-> {}
|
||||
(has-position? type)
|
||||
(-> (parse-position data)
|
||||
(gsh/setup-selrect))
|
||||
(def url-regex #"url\(#([^\)]*)\)")
|
||||
|
||||
(= type :circle)
|
||||
(-> (parse-circle data)
|
||||
(gsh/setup-selrect))
|
||||
(defn seek-node [id coll]
|
||||
(->> coll (d/seek #(= id (-> % :attrs :id)))))
|
||||
|
||||
(= type :path)
|
||||
(parse-path data))))
|
||||
(defn parse-stops [gradient-node]
|
||||
(->> gradient-node
|
||||
(node-seq)
|
||||
(filter #(= :stop (:tag %)))
|
||||
(mapv (fn [{{:keys [offset stop-color stop-opacity]} :attrs}]
|
||||
{:color stop-color
|
||||
:opacity (d/parse-double stop-opacity)
|
||||
:offset (d/parse-double offset)}))))
|
||||
|
||||
(defn parse-gradient
|
||||
[node ref-url]
|
||||
(let [[_ url] (re-matches url-regex ref-url)
|
||||
gradient-node (->> node (node-seq) (seek-node url))
|
||||
stops (parse-stops gradient-node)]
|
||||
|
||||
(cond-> {:stops stops}
|
||||
(= :linearGradient (:tag gradient-node))
|
||||
(assoc :type :linear
|
||||
:start-x (-> gradient-node :attrs :x1 d/parse-double)
|
||||
:start-y (-> gradient-node :attrs :y1 d/parse-double)
|
||||
:end-x (-> gradient-node :attrs :x2 d/parse-double)
|
||||
:end-y (-> gradient-node :attrs :y2 d/parse-double)
|
||||
:width 1)
|
||||
|
||||
(= :radialGradient (:tag gradient-node))
|
||||
(assoc :type :radial
|
||||
:start-x (get-meta gradient-node :start-x d/parse-double)
|
||||
:start-y (get-meta gradient-node :start-y d/parse-double)
|
||||
:end-x (get-meta gradient-node :end-x d/parse-double)
|
||||
:end-y (get-meta gradient-node :end-y d/parse-double)
|
||||
:width (get-meta gradient-node :width d/parse-double)))))
|
||||
|
||||
(defn add-position
|
||||
[props type node data]
|
||||
(cond-> props
|
||||
(has-position? type)
|
||||
(-> (parse-position data)
|
||||
(gsh/setup-selrect))
|
||||
|
||||
(= type :circle)
|
||||
(-> (parse-circle data)
|
||||
(gsh/setup-selrect))
|
||||
|
||||
(= type :path)
|
||||
(parse-path data)))
|
||||
|
||||
(defn add-fill
|
||||
[props type node data]
|
||||
|
||||
(let [fill (:fill data)]
|
||||
(cond-> props
|
||||
(= fill "none")
|
||||
(assoc :fill-color nil
|
||||
:fill-opacity nil)
|
||||
|
||||
(str/starts-with? fill "url")
|
||||
(assoc :fill-color-gradient (parse-gradient node fill)
|
||||
:fill-color nil
|
||||
:fill-opacity nil)
|
||||
|
||||
(uc/hex? fill)
|
||||
(assoc :fill-color fill
|
||||
:fill-opacity (-> data (:fill-opacity "1") d/parse-double)))))
|
||||
|
||||
(defn add-stroke
|
||||
[props type node data]
|
||||
|
||||
(let [stroke-style (get-meta node :stroke-style keyword)
|
||||
stroke-alignment (get-meta node :stroke-alignment keyword)
|
||||
stroke (:stroke data)]
|
||||
|
||||
(cond-> props
|
||||
:always
|
||||
(assoc :stroke-alignment stroke-alignment
|
||||
:stroke-style stroke-style
|
||||
:stroke-color (-> data (:stroke "#000000"))
|
||||
:stroke-opacity (-> data (:stroke-opacity "1") d/parse-double)
|
||||
:stroke-width (-> data (:stroke-width "0") d/parse-double))
|
||||
|
||||
(str/starts-with? stroke "url")
|
||||
(assoc :stroke-color-gradient (parse-gradient node stroke)
|
||||
:stroke-color nil
|
||||
:stroke-opacity nil)
|
||||
|
||||
(= stroke-alignment :inner)
|
||||
(update :stroke-width / 2))))
|
||||
|
||||
(defn add-text-data
|
||||
[props node]
|
||||
(-> props
|
||||
(assoc :grow-type (get-meta node :grow-type keyword))
|
||||
(assoc :content (get-meta node :content json/decode))))
|
||||
|
||||
(defn str->bool
|
||||
[val]
|
||||
|
@ -148,17 +234,26 @@
|
|||
[type node]
|
||||
|
||||
(when-not (close? node)
|
||||
(let [name (get-attr node :name)
|
||||
blocked (get-attr node :blocked str->bool)
|
||||
hidden (get-attr node :hidden str->bool)
|
||||
transform (get-attr node :transform gmt/str->matrix)
|
||||
transform-inverse (get-attr node :transform-inverse gmt/str->matrix)]
|
||||
(let [name (get-meta node :name)
|
||||
blocked (get-meta node :blocked str->bool)
|
||||
hidden (get-meta node :hidden str->bool)
|
||||
transform (get-meta node :transform gmt/str->matrix)
|
||||
transform-inverse (get-meta node :transform-inverse gmt/str->matrix)
|
||||
data (get-shape-data type node)]
|
||||
|
||||
(-> (extract-data type node)
|
||||
(-> {}
|
||||
(add-position type node data)
|
||||
(add-fill type node data)
|
||||
(add-stroke type node data)
|
||||
(assoc :name name)
|
||||
(assoc :blocked blocked)
|
||||
(assoc :hidden hidden)
|
||||
|
||||
(cond-> (= :text type)
|
||||
(add-text-data node))
|
||||
|
||||
(cond-> (some? transform)
|
||||
(assoc :transform transform))
|
||||
|
||||
(cond-> (some? transform-inverse)
|
||||
(assoc :transform-inverse transform-inverse))))))
|
||||
|
|
19
frontend/src/app/util/json.cljs
Normal file
19
frontend/src/app/util/json.cljs
Normal file
|
@ -0,0 +1,19 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.util.json)
|
||||
|
||||
(defn decode
|
||||
[data]
|
||||
(-> data
|
||||
(js/JSON.parse)
|
||||
(js->clj :keywordize-keys true)))
|
||||
|
||||
(defn encode
|
||||
[data]
|
||||
(-> data
|
||||
(clj->js)
|
||||
(js/JSON.stringify)))
|
Loading…
Add table
Reference in a new issue