mirror of
https://github.com/penpot/penpot.git
synced 2025-04-12 15:01:28 -05:00
✨ Improved text handling in plugins
This commit is contained in:
parent
ac58a5b8fa
commit
fbce59e81f
4 changed files with 175 additions and 91 deletions
|
@ -71,6 +71,13 @@
|
|||
(defn get-font-data [id]
|
||||
(get @fontsdb id))
|
||||
|
||||
(defn find-font-data [data]
|
||||
(d/seek
|
||||
(fn [font]
|
||||
(= (select-keys font (keys data))
|
||||
data))
|
||||
(vals @fontsdb)))
|
||||
|
||||
(defn resolve-variants
|
||||
[id]
|
||||
(get-in @fontsdb [id :variants]))
|
||||
|
@ -249,6 +256,11 @@
|
|||
(or (d/seek #(= (:id %) font-variant-id) variants)
|
||||
(get-default-variant font)))
|
||||
|
||||
(defn find-variant
|
||||
[{:keys [variants] :as font} variant-data]
|
||||
(let [props (keys variant-data)]
|
||||
(d/seek #(= (select-keys % props) variant-data) variants)))
|
||||
|
||||
;; Font embedding functions
|
||||
(defn get-node-fonts
|
||||
"Extracts the fonts used by some node"
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
(deftype PenpotFontVariant [name fontVariantId fontWeight fontStyle])
|
||||
|
||||
(defn variant-proxy? [p]
|
||||
(instance? PenpotFontVariant p))
|
||||
|
||||
(deftype PenpotFont [name fontId fontFamily fontStyle fontVariantId fontWeight variants]
|
||||
Object
|
||||
|
||||
|
@ -60,13 +63,13 @@
|
|||
(instance? PenpotFont p))
|
||||
|
||||
(defn font-proxy
|
||||
[{:keys [id name variants] :as font}]
|
||||
[{:keys [id family name variants] :as font}]
|
||||
(when (some? font)
|
||||
(let [default-variant (fonts/get-default-variant font)]
|
||||
(PenpotFont.
|
||||
name
|
||||
id
|
||||
id
|
||||
family
|
||||
(:style default-variant)
|
||||
(:id default-variant)
|
||||
(:weight default-variant)
|
||||
|
|
|
@ -23,6 +23,12 @@
|
|||
(when (some? coll)
|
||||
(apply array (keep format-fn coll))))
|
||||
|
||||
(defn format-mixed
|
||||
[value]
|
||||
(if (= value :multiple)
|
||||
"mixed"
|
||||
value))
|
||||
|
||||
;; export type PenpotPoint = { x: number; y: number };
|
||||
(defn format-point
|
||||
[{:keys [x y] :as point}]
|
||||
|
@ -183,7 +189,14 @@
|
|||
|
||||
(defn format-fills
|
||||
[fills]
|
||||
(when (some? fills)
|
||||
(cond
|
||||
(= fills :multiple)
|
||||
"mixed"
|
||||
|
||||
(= fills "mixed")
|
||||
"mixed"
|
||||
|
||||
(some? fills)
|
||||
(format-array format-fill fills)))
|
||||
|
||||
;; export interface PenpotStroke {
|
||||
|
@ -393,7 +406,7 @@
|
|||
(format-array format-command content)))
|
||||
|
||||
;; export type PenpotTrackType = 'flex' | 'fixed' | 'percent' | 'auto';
|
||||
;;
|
||||
;;
|
||||
;; export interface PenpotTrack {
|
||||
;; type: PenpotTrackType;
|
||||
;; value: number | null;
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
(ns app.plugins.text
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.record :as crc]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.parser :as parser]
|
||||
|
@ -21,11 +23,39 @@
|
|||
[app.util.text-editor :as ted]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
;; This regex seems duplicated but probably in the future when we support diferent units
|
||||
;; this will need to reflect changes for each property
|
||||
|
||||
(def font-size-re #"^\d*\.?\d*$")
|
||||
(def line-height-re #"^\d*\.?\d*$")
|
||||
(def letter-spacing-re #"^\d*\.?\d*$")
|
||||
(def text-transform-re #"uppercase|capitalize|lowercase|none")
|
||||
(def text-decoration-re #"underline|line-through|none")
|
||||
(def text-direction-re #"ltr|rtl")
|
||||
(def text-align-re #"left|center|right|justify")
|
||||
(def vertical-align-re #"top|center|bottom")
|
||||
|
||||
(defn mixed-value
|
||||
[values]
|
||||
(let [s (set values)]
|
||||
(if (= (count s) 1) (first s) "mixed")))
|
||||
|
||||
(defn font-data
|
||||
[font variant]
|
||||
(d/without-nils
|
||||
{:font-id (:id font)
|
||||
:font-family (:family font)
|
||||
:font-variant-id (:id variant)
|
||||
:font-style (:style variant)
|
||||
:font-weight (:weight variant)}))
|
||||
|
||||
(defn variant-data
|
||||
[variant]
|
||||
(d/without-nils
|
||||
{:font-variant-id (:id variant)
|
||||
:font-style (:style variant)
|
||||
:font-weight (:weight variant)}))
|
||||
|
||||
(deftype TextRange [$plugin $file $page $id start end]
|
||||
Object
|
||||
(applyTypography [_ typography]
|
||||
|
@ -71,12 +101,14 @@
|
|||
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontId value)
|
||||
(let [font (when (string? value) (fonts/get-font-data value))
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not (some? font))
|
||||
(u/display-not-valid :fontId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-id value}))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
|
||||
|
||||
{:name "fontFamily"
|
||||
:get #(let [range-data
|
||||
|
@ -85,25 +117,29 @@
|
|||
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
(let [font (fonts/find-font-data {:font-family value})
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-family value}))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
|
||||
|
||||
{:name "fontVariantId"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-variant-id) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
(fn [self value]
|
||||
(let [font (fonts/get-font-data (obj/get self "fontId"))
|
||||
variant (fonts/get-variant font value)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-variant-id value}))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
|
||||
{:name "fontSize"
|
||||
:get #(let [range-data
|
||||
|
@ -111,38 +147,43 @@
|
|||
(->> range-data (map :font-size) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontSize value)
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (not (re-matches font-size-re value)))
|
||||
(u/display-not-valid :fontSize value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-size value}))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-size value})))))}
|
||||
|
||||
{:name "fontWeight"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-weight) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontWeight value)
|
||||
(fn [self value]
|
||||
(let [font (fonts/get-font-data (obj/get self "fontId"))
|
||||
variant (fonts/find-variant font {:weight (dm/str value)})]
|
||||
(cond
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontWeight (dm/str "Font weight '" value "' not supported for the current font"))
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-weight value}))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
|
||||
{:name "fontStyle"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-style) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontStyle value)
|
||||
(fn [self value]
|
||||
(let [font (fonts/get-font-data (obj/get self "fontId"))
|
||||
variant (fonts/find-variant font {:weight (dm/str value)})]
|
||||
(cond
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontStyle (dm/str "Font style '" value "' not supported for the current font"))
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-style value}))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
|
||||
{:name "lineHeight"
|
||||
:get #(let [range-data
|
||||
|
@ -150,12 +191,13 @@
|
|||
(->> range-data (map :line-height) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (not (re-matches line-height-re value)))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:line-height value}))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:line-height value})))))}
|
||||
|
||||
{:name "letterSpacing"
|
||||
:get #(let [range-data
|
||||
|
@ -163,12 +205,13 @@
|
|||
(->> range-data (map :letter-spacing) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (re-matches letter-spacing-re value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:letter-spacing value}))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:letter-spacing value})))))}
|
||||
|
||||
{:name "textTransform"
|
||||
:get #(let [range-data
|
||||
|
@ -177,7 +220,7 @@
|
|||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(and (string? value) (re-matches text-transform-re value))
|
||||
(u/display-not-valid :textTransform value)
|
||||
|
||||
:else
|
||||
|
@ -190,7 +233,7 @@
|
|||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(and (string? value) (re-matches text-decoration-re value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
|
||||
:else
|
||||
|
@ -203,7 +246,7 @@
|
|||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(and (string? value) (re-matches text-direction-re value))
|
||||
(u/display-not-valid :direction value)
|
||||
|
||||
:else
|
||||
|
@ -216,7 +259,7 @@
|
|||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(and (string? value) (re-matches text-align-re value))
|
||||
(u/display-not-valid :text-align value)
|
||||
|
||||
:else
|
||||
|
@ -278,144 +321,157 @@
|
|||
(st/emit! (dwsh/update-shapes [id] #(assoc % :grow-type value))))))}
|
||||
|
||||
{:name "fontId"
|
||||
:get #(-> % u/proxy->shape text-props :font-id)
|
||||
:get #(-> % u/proxy->shape text-props :font-id format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(let [id (obj/get self "$id")
|
||||
font (when (string? value) (fonts/get-font-data value))
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(not (some? font))
|
||||
(u/display-not-valid :fontId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-id value})))))}
|
||||
(st/emit! (dwt/update-attrs id (font-data font variant))))))}
|
||||
|
||||
{:name "fontFamily"
|
||||
:get #(-> % u/proxy->shape text-props :font-family)
|
||||
:get #(-> % u/proxy->shape text-props :font-family format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(let [id (obj/get self "$id")
|
||||
font (fonts/find-font-data {:font-family value})
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(not (some? font))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-family value})))))}
|
||||
(st/emit! (dwt/update-attrs id (font-data font variant))))))}
|
||||
|
||||
{:name "fontVariantId"
|
||||
:get #(-> % u/proxy->shape text-props :font-variant-id)
|
||||
:get #(-> % u/proxy->shape text-props :font-variant-id format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(let [id (obj/get self "$id")
|
||||
font (fonts/get-font-data (obj/get self "fontId"))
|
||||
variant (fonts/get-variant font value)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(not (some? variant))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-variant-id value})))))}
|
||||
(st/emit! (dwt/update-attrs id (variant-data variant))))))}
|
||||
|
||||
{:name "fontSize"
|
||||
:get #(-> % u/proxy->shape text-props :font-size)
|
||||
:get #(-> % u/proxy->shape text-props :font-size format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(let [id (obj/get self "$id")
|
||||
value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(or (empty? value) (not (re-matches font-size-re value)))
|
||||
(u/display-not-valid :fontSize value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-size value})))))}
|
||||
|
||||
{:name "fontWeight"
|
||||
:get #(-> % u/proxy->shape text-props :font-weight)
|
||||
:get #(-> % u/proxy->shape text-props :font-weight format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(let [id (obj/get self "$id")
|
||||
font (fonts/get-font-data (obj/get self "fontId"))
|
||||
variant (fonts/find-variant font {:weight (dm/str value)})]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontWeight value)
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontWeight (dm/str "Font weight '" value "' not supported for the current font"))
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-weight value})))))}
|
||||
(st/emit! (dwt/update-attrs id (variant-data variant))))))}
|
||||
|
||||
{:name "fontStyle"
|
||||
:get #(-> % u/proxy->shape text-props :font-style)
|
||||
:get #(-> % u/proxy->shape text-props :font-style format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(let [id (obj/get self "$id")
|
||||
font (fonts/get-font-data (obj/get self "fontId"))
|
||||
variant (fonts/find-variant font {:weight (dm/str value)})]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontStyle value)
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontStyle (dm/str "Font style '" value "' not supported for the current font"))
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-style value})))))}
|
||||
(st/emit! (dwt/update-attrs id (variant-data variant))))))}
|
||||
|
||||
{:name "lineHeight"
|
||||
:get #(-> % u/proxy->shape text-props :line-height)
|
||||
:get #(-> % u/proxy->shape text-props :line-height format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(let [id (obj/get self "$id")
|
||||
value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(or (empty? value) (not (re-matches line-height-re value)))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:line-height value})))))}
|
||||
|
||||
{:name "letterSpacing"
|
||||
:get #(-> % u/proxy->shape text-props :letter-spacing)
|
||||
:get #(-> % u/proxy->shape text-props :letter-spacing format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(let [id (obj/get self "$id")
|
||||
value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(or (empty? value) (re-matches letter-spacing-re value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:letter-spacing value})))))}
|
||||
|
||||
{:name "textTransform"
|
||||
:get #(-> % u/proxy->shape text-props :text-transform)
|
||||
:get #(-> % u/proxy->shape text-props :text-transform format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(and (string? value) (re-matches text-transform-re value))
|
||||
(u/display-not-valid :textTransform value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-transform value})))))}
|
||||
|
||||
{:name "textDecoration"
|
||||
:get #(-> % u/proxy->shape text-props :text-decoration)
|
||||
:get #(-> % u/proxy->shape text-props :text-decoration format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(and (string? value) (re-matches text-decoration-re value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-decoration value})))))}
|
||||
|
||||
{:name "direction"
|
||||
:get #(-> % u/proxy->shape text-props :text-direction)
|
||||
:get #(-> % u/proxy->shape text-props :text-direction format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(and (string? value) (re-matches text-direction-re value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-decoration value})))))}
|
||||
|
||||
{:name "align"
|
||||
:get #(-> % u/proxy->shape text-props :text-align)
|
||||
:get #(-> % u/proxy->shape text-props :text-align format/format-mixed)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(and (string? value) (re-matches text-align-re value))
|
||||
(u/display-not-valid :align value)
|
||||
|
||||
:else
|
||||
|
@ -427,7 +483,7 @@
|
|||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(and (string? value) (re-matches vertical-align-re value))
|
||||
(u/display-not-valid :verticalAlign value)
|
||||
|
||||
:else
|
||||
|
|
Loading…
Add table
Reference in a new issue