mirror of
https://github.com/penpot/penpot.git
synced 2025-07-23 09:07:17 +02:00
Merge remote-tracking branch 'origin/main' into develop
This commit is contained in:
commit
3d8c41cd69
52 changed files with 625 additions and 568 deletions
|
@ -10,17 +10,34 @@
|
|||
[beicon.core :as rx]))
|
||||
|
||||
(defonce cache (atom {}))
|
||||
(defonce pending (atom {}))
|
||||
|
||||
(defn with-cache
|
||||
[{:keys [key max-age]} observable]
|
||||
(let [entry (get @cache key)
|
||||
pending-entry (get @pending key)
|
||||
|
||||
age (when entry
|
||||
(dt/diff (dt/now)
|
||||
(:created-at entry)))]
|
||||
(if (and (some? entry) (< age max-age))
|
||||
(cond
|
||||
(and (some? entry) (< age max-age))
|
||||
(rx/of (:data entry))
|
||||
(->> observable
|
||||
(rx/tap
|
||||
(fn [data]
|
||||
(let [entry {:created-at (dt/now) :data data}]
|
||||
(swap! cache assoc key entry))))))))
|
||||
|
||||
(some? pending-entry)
|
||||
pending-entry
|
||||
|
||||
:else
|
||||
(let [subject (rx/subject)]
|
||||
(swap! pending assoc key subject)
|
||||
(->> observable
|
||||
(rx/catch #(do (rx/error! subject %)
|
||||
(swap! pending dissoc key)
|
||||
(rx/throw %)))
|
||||
(rx/tap
|
||||
(fn [data]
|
||||
(let [entry {:created-at (dt/now) :data data}]
|
||||
(swap! cache assoc key entry))
|
||||
(rx/push! subject data)
|
||||
(rx/end! subject)
|
||||
(swap! pending dissoc key))))))))
|
||||
|
|
|
@ -558,3 +558,11 @@
|
|||
(seq (.-children node)))]
|
||||
(->> root-node
|
||||
(tree-seq branch? get-children))))
|
||||
|
||||
(defn check-font? [font]
|
||||
(let [fonts (.-fonts globals/document)]
|
||||
(.check fonts font)))
|
||||
|
||||
(defn load-font [font]
|
||||
(let [fonts (.-fonts globals/document)]
|
||||
(.load fonts font)))
|
||||
|
|
|
@ -173,32 +173,34 @@
|
|||
(fetch-data-uri uri false))
|
||||
|
||||
([uri throw-err?]
|
||||
(c/with-cache {:key uri :max-age (dt/duration {:hours 4})}
|
||||
(let [request-stream
|
||||
(send! {:method :get
|
||||
:uri uri
|
||||
:response-type :blob
|
||||
:omit-default-headers true})
|
||||
(let [request-str
|
||||
(->> (send! {:method :get
|
||||
:uri uri
|
||||
:response-type :blob
|
||||
:omit-default-headers true})
|
||||
(rx/tap
|
||||
(fn [resp]
|
||||
(when (or (< (:status resp) 200) (>= (:status resp) 300))
|
||||
(rx/throw (js/Error. "Error fetching data uri" #js {:cause (clj->js resp)})))))
|
||||
|
||||
request-stream
|
||||
(if throw-err?
|
||||
(rx/tap #(when-not (and (>= (:status %) 200) (< (:status %) 300))
|
||||
;; HTTP ERRROR
|
||||
(throw (js/Error. "Error fetching data uri" #js {:cause (clj->js %)})))
|
||||
request-stream)
|
||||
(rx/filter #(= 200 (:status %))
|
||||
request-stream))]
|
||||
(->> request-stream
|
||||
(rx/map :body)
|
||||
(rx/mapcat wapi/read-file-as-data-url)
|
||||
(rx/map #(hash-map uri %)))))))
|
||||
(rx/map :body)
|
||||
(rx/mapcat wapi/read-file-as-data-url)
|
||||
(rx/map #(hash-map uri %))
|
||||
(c/with-cache {:key uri :max-age (dt/duration {:hours 4})}))]
|
||||
|
||||
;; We need to check `throw-err?` after the cache is resolved otherwise we cannot cache request
|
||||
;; with different values of throw-err. By default we throw always the exception and then we just
|
||||
;; ignore when `throw-err?` is `true`
|
||||
(if (not throw-err?)
|
||||
(->> request-str (rx/catch #(rx/empty)))
|
||||
request-str))))
|
||||
|
||||
(defn fetch-text [url]
|
||||
(c/with-cache {:key url :max-age (dt/duration {:hours 4})}
|
||||
(->> (send!
|
||||
{:method :get
|
||||
:mode :cors
|
||||
:omit-default-headers true
|
||||
:uri url
|
||||
:response-type :text})
|
||||
(rx/map :body))))
|
||||
(->> (send!
|
||||
{:method :get
|
||||
:mode :cors
|
||||
:omit-default-headers true
|
||||
:uri url
|
||||
:response-type :text})
|
||||
(rx/map :body)
|
||||
(c/with-cache {:key url :max-age (dt/duration {:hours 4})})))
|
||||
|
|
|
@ -23,14 +23,18 @@
|
|||
{:label "Français (community)" :value "fr"}
|
||||
{:label "Deutsch (community)" :value "de"}
|
||||
;; {:label "Italiano (community)" :value "it"}
|
||||
{:label "Norsk - Bokmål (community)" :value "nb_no"}
|
||||
{:label "Portuguese - Brazil (community)" :value "pt_br"}
|
||||
{:label "Polski (community)" :value "pl"}
|
||||
{:label "Русский (community)" :value "ru"}
|
||||
{:label "Rumanian (community)" :value "ro"}
|
||||
{:label "Türkçe (community)" :value "tr"}
|
||||
{:label "Rumanian (communit)" :value "ro"}
|
||||
{:label "Portuguese (Brazil, community)" :value "pt_br"}
|
||||
{:label "Ελληνική γλώσσα (community)" :value "el"}
|
||||
{:label "עִבְרִית (community)" :value "he"}
|
||||
{:label "عربي/عربى (community)" :value "ar"}
|
||||
{:label "简体中文 (community)" :value "zh_cn"}])
|
||||
{:label "فارسی (community)" :value "fa"}
|
||||
{:label "简体中文 (community)" :value "zh_cn"}
|
||||
{:label "中国传统的 (community)" :value "zh_hant"}])
|
||||
|
||||
(defn- parse-locale
|
||||
[locale]
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.util.import.parser
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.spec.interactions :as cti]
|
||||
|
@ -213,16 +214,29 @@
|
|||
(reduce add-attrs node-attrs))
|
||||
|
||||
(= type :frame)
|
||||
(let [;; The nodes with the "frame-background" class can have some anidation depending on the strokes they have
|
||||
(let [;; Old .penpot files doesn't have "g" nodes. They have a clipPath reference as a node attribute
|
||||
to-url #(dm/str "url(#" % ")")
|
||||
frame-clip-rect-node (->> (find-all-nodes node :defs)
|
||||
(mapcat #(find-all-nodes % :clipPath))
|
||||
(filter #(= (to-url (:id (:attrs %))) (:clip-path node-attrs)))
|
||||
(mapcat #(find-all-nodes % #{:rect :path}))
|
||||
(first))
|
||||
|
||||
;; The nodes with the "frame-background" class can have some anidation depending on the strokes they have
|
||||
g-nodes (find-all-nodes node :g)
|
||||
defs-nodes (flatten (map #(find-all-nodes % :defs) g-nodes))
|
||||
gg-nodes (flatten (map #(find-all-nodes % :g) g-nodes))
|
||||
|
||||
|
||||
rect-nodes (flatten [[(find-all-nodes node :rect)]
|
||||
(map #(find-all-nodes % #{:rect :path}) defs-nodes)
|
||||
(map #(find-all-nodes % #{:rect :path}) g-nodes)
|
||||
(map #(find-all-nodes % #{:rect :path}) gg-nodes)])
|
||||
svg-node (d/seek #(= "frame-background" (get-in % [:attrs :class])) rect-nodes)]
|
||||
(merge (add-attrs {} (:attrs svg-node)) node-attrs))
|
||||
(merge
|
||||
(add-attrs {} (:attrs frame-clip-rect-node))
|
||||
(add-attrs {} (:attrs svg-node))
|
||||
node-attrs))
|
||||
|
||||
(= type :svg-raw)
|
||||
(let [svg-content (get-data node :penpot:svg-content)
|
||||
|
|
|
@ -9,9 +9,11 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.transit :as transit]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.store :as st]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.text-position-data :as tpd]))
|
||||
[app.util.text-position-data :as tpd]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defn parse-text-nodes
|
||||
"Given a text node retrieves the rectangles for everyone of its paragraphs and its text."
|
||||
|
@ -27,6 +29,27 @@
|
|||
(map parse-entry)
|
||||
(tpd/parse-text-nodes parent-node text-node))))
|
||||
|
||||
(def load-promises (atom {}))
|
||||
|
||||
(defn load-font
|
||||
[font]
|
||||
(if (contains? @load-promises font)
|
||||
(get @load-promises font)
|
||||
(let [load-promise (dom/load-font font)]
|
||||
(swap! load-promises assoc font load-promise)
|
||||
load-promise)))
|
||||
|
||||
(defn resolve-font
|
||||
[^js node]
|
||||
|
||||
(let [styles (js/getComputedStyle node)
|
||||
font (.getPropertyValue styles "font")]
|
||||
(if (dom/check-font? font)
|
||||
(p/resolved font)
|
||||
(let [font-id (.getPropertyValue styles "--font-id")]
|
||||
(-> (fonts/ensure-loaded! font-id)
|
||||
(p/then #(when (not (dom/check-font? font))
|
||||
(load-font font))))))))
|
||||
|
||||
(defn calc-text-node-positions
|
||||
[base-node viewport zoom]
|
||||
|
@ -58,22 +81,25 @@
|
|||
:width (- (:x p2) (:x p1))
|
||||
:height (- (:y p2) (:y p1)))))
|
||||
|
||||
text-nodes (dom/query-all base-node ".text-node, span[data-text]")]
|
||||
|
||||
(->> text-nodes
|
||||
(mapcat
|
||||
(fn [parent-node]
|
||||
(let [direction (.-direction (js/getComputedStyle parent-node))]
|
||||
(->> (.-childNodes parent-node)
|
||||
(mapcat #(parse-text-nodes parent-node direction %))))))
|
||||
(mapv #(update % :position translate-rect))))))
|
||||
text-nodes (dom/query-all base-node ".text-node, span[data-text]")
|
||||
load-fonts (->> text-nodes (map resolve-font))]
|
||||
(-> (p/all load-fonts)
|
||||
(p/then
|
||||
(fn []
|
||||
(->> text-nodes
|
||||
(mapcat
|
||||
(fn [parent-node]
|
||||
(let [direction (.-direction (js/getComputedStyle parent-node))]
|
||||
(->> (.-childNodes parent-node)
|
||||
(mapcat #(parse-text-nodes parent-node direction %))))))
|
||||
(mapv #(update % :position translate-rect)))))))))
|
||||
|
||||
(defn calc-position-data
|
||||
[base-node]
|
||||
(let [viewport (dom/get-element "render")
|
||||
zoom (or (get-in @st/state [:workspace-local :zoom]) 1)]
|
||||
(when (and (some? base-node) (some? viewport))
|
||||
(let [text-data (calc-text-node-positions base-node viewport zoom)]
|
||||
(p/let [text-data (calc-text-node-positions base-node viewport zoom)]
|
||||
(when (d/not-empty? text-data)
|
||||
(->> text-data
|
||||
(mapv (fn [{:keys [node position text direction]}]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue