0
Fork 0
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:
alonso.torres 2021-06-03 15:01:24 +02:00
parent 8d703a3fb4
commit 83879fb931
5 changed files with 179 additions and 45 deletions

View file

@ -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)))

View file

@ -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)

View file

@ -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")))))

View file

@ -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))))))

View 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)))