mirror of
https://github.com/penpot/penpot.git
synced 2026-08-06 12:58:55 +00:00
♻️ Move shared font and resources utils out of render_wasm
This commit is contained in:
parent
25c6bff3a9
commit
4971cb9667
@ -2,12 +2,17 @@
|
|||||||
|
|
||||||
The host-agnostic ClojureScript side of the render-wasm binary protocol: byte
|
The host-agnostic ClojureScript side of the render-wasm binary protocol: byte
|
||||||
layouts, memory helpers and serializers that turn Penpot shapes into the buffers
|
layouts, memory helpers and serializers that turn Penpot shapes into the buffers
|
||||||
`render-wasm` consumes, plus the google fonts catalog (baked from
|
`render-wasm` consumes.
|
||||||
`common/resources/fonts/gfonts.*.json`).
|
|
||||||
|
|
||||||
The workspace drives it from `app.render-wasm.*`, the headless exporter from
|
The workspace drives it from `app.render-wasm.*`, the headless exporter from
|
||||||
`app.wasm.*` — same code underneath, so the two cannot drift.
|
`app.wasm.*` — same code underneath, so the two cannot drift.
|
||||||
|
|
||||||
|
Font knowledge is *not* here even though both hosts need it for rendering: it is
|
||||||
|
not specific to the wasm backend, so the google fonts catalog (baked from
|
||||||
|
`common/resources/fonts/gfonts.*.json`), the bundled builtin family and the
|
||||||
|
emoji/script fallback tables live in `app.common.types.font.*`. Likewise the
|
||||||
|
image-id enumeration lives in `app.common.types.shape.images`.
|
||||||
|
|
||||||
`shared.js` is not here: it is a per-build artifact, so each host compiles
|
`shared.js` is not here: it is a per-build artifact, so each host compiles
|
||||||
against the copy from its own render-wasm build and passes it to
|
against the copy from its own render-wasm build and passes it to
|
||||||
`wasm/init-serializers!` (see `app.render-wasm.api.enums`, `app.wasm.enums`).
|
`wasm/init-serializers!` (see `app.render-wasm.api.enums`, `app.wasm.enums`).
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
;; 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) KALEIDOS INC Sucursal en España SL
|
|
||||||
|
|
||||||
(ns app.common.render-wasm.builtin-fonts
|
|
||||||
"Fonts bundled with the frontend, served from `<public-uri>/fonts/`. Shared so
|
|
||||||
the workspace and the exporter upload the same TTF for a given weight/style."
|
|
||||||
(:require
|
|
||||||
[app.common.render-wasm.gfonts :as gf]))
|
|
||||||
|
|
||||||
(def local-fonts
|
|
||||||
[{:id "sourcesanspro"
|
|
||||||
:name "Source Sans Pro"
|
|
||||||
:family "sourcesanspro"
|
|
||||||
:variants
|
|
||||||
[{:id "200" :name "200" :weight "200" :style "normal" :suffix "extralight" :ttf-url "sourcesanspro-extralight.ttf"}
|
|
||||||
{:id "200italic" :name "200 Italic" :weight "200" :style "italic" :suffix "extralightitalic" :ttf-url "sourcesanspro-extralightitalic.ttf"}
|
|
||||||
{:id "300" :name "300" :weight "300" :style "normal" :suffix "light" :ttf-url "sourcesanspro-light.ttf"}
|
|
||||||
{:id "300italic" :name "300 Italic" :weight "300" :style "italic" :suffix "lightitalic" :ttf-url "sourcesanspro-lightitalic.ttf"}
|
|
||||||
{:id "regular" :name "400" :weight "400" :style "normal" :ttf-url "sourcesanspro-regular.ttf"}
|
|
||||||
{:id "italic" :name "400 Italic" :weight "400" :style "italic" :ttf-url "sourcesanspro-italic.ttf"}
|
|
||||||
{:id "600" :name "600" :weight "600" :style "normal" :suffix "semibold" :ttf-url "sourcesanspro-semibold.ttf"}
|
|
||||||
{:id "600italic" :name "600 Italic" :weight "600" :style "italic" :suffix "semibolditalic" :ttf-url "sourcesanspro-semibolditalic.ttf"}
|
|
||||||
{:id "bold" :name "700" :weight "700" :style "normal" :ttf-url "sourcesanspro-bold.ttf"}
|
|
||||||
{:id "bolditalic" :name "700 Italic" :weight "700" :style "italic" :ttf-url "sourcesanspro-bolditalic.ttf"}
|
|
||||||
{:id "black" :name "900" :weight "900" :style "normal" :ttf-url "sourcesanspro-black.ttf"}
|
|
||||||
{:id "blackitalic" :name "900 Italic" :weight "900" :style "italic" :ttf-url "sourcesanspro-blackitalic.ttf"}]}])
|
|
||||||
|
|
||||||
(defn resolve-ttf-file
|
|
||||||
"Builtin TTF file name for `weight` and `style` (0 normal, 1 italic), by the
|
|
||||||
same nearest-weight rule as the google catalog."
|
|
||||||
[weight style]
|
|
||||||
(let [variants (:variants (first local-fonts))]
|
|
||||||
(:ttf-url (or (gf/closest-variant variants weight (if (zero? style) "normal" "italic"))
|
|
||||||
(first variants)))))
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
;; 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) KALEIDOS INC Sucursal en España SL
|
|
||||||
|
|
||||||
(ns app.common.render-wasm.gfonts
|
|
||||||
(:require-macros [app.common.render-wasm.gfonts :refer [preload-gfonts]])
|
|
||||||
(:require
|
|
||||||
[app.common.data :as d]
|
|
||||||
[app.common.uuid :as uuid]
|
|
||||||
[cuerdas.core :as str]))
|
|
||||||
|
|
||||||
;; --- catalog
|
|
||||||
|
|
||||||
(def catalog
|
|
||||||
(preload-gfonts "fonts/gfonts.2025.11.28.json"))
|
|
||||||
|
|
||||||
(def ^:private by-id
|
|
||||||
(reduce (fn [m font] (assoc m (:id font) font)) {} catalog))
|
|
||||||
|
|
||||||
(def ^:private by-uuid
|
|
||||||
(reduce (fn [m font] (assoc m (:uuid font) font)) {} catalog))
|
|
||||||
|
|
||||||
(defn gfont-id->uuid
|
|
||||||
"Maps a `gfont-<slug>` id to its (compilation-stable) catalog uuid, or nil."
|
|
||||||
[gfont-id]
|
|
||||||
(:uuid (get by-id gfont-id)))
|
|
||||||
|
|
||||||
;; --- font-id -> wasm uuid
|
|
||||||
|
|
||||||
(def ^:private custom-prefix "custom-")
|
|
||||||
(def ^:private gfont-prefix "gfont-")
|
|
||||||
|
|
||||||
(defn font-id->uuid
|
|
||||||
"Maps a content font-id to the uuid WASM keys fonts by:
|
|
||||||
|
|
||||||
- `gfont-<slug>` -> the catalog uuid,
|
|
||||||
- `custom-<uuid>` -> that uuid,
|
|
||||||
- anything else (builtin, unknown, malformed) -> `uuid/zero`, which WASM
|
|
||||||
resolves to the default font."
|
|
||||||
|
|
||||||
[font-id]
|
|
||||||
(cond
|
|
||||||
(not (string? font-id))
|
|
||||||
uuid/zero
|
|
||||||
|
|
||||||
(str/starts-with? font-id gfont-prefix)
|
|
||||||
(or (gfont-id->uuid font-id) uuid/zero)
|
|
||||||
|
|
||||||
(str/starts-with? font-id custom-prefix)
|
|
||||||
(or (uuid/parse* (subs font-id (count custom-prefix))) uuid/zero)
|
|
||||||
|
|
||||||
:else
|
|
||||||
uuid/zero))
|
|
||||||
|
|
||||||
;; --- proxy urls
|
|
||||||
|
|
||||||
(def ^:private gstatic-prefix
|
|
||||||
"https://fonts.gstatic.com/s")
|
|
||||||
|
|
||||||
(defn gstatic->proxy-url
|
|
||||||
[s base]
|
|
||||||
(let [base (str/rtrim (str base) "/")]
|
|
||||||
(str/replace (str s) gstatic-prefix base)))
|
|
||||||
|
|
||||||
;; --- variant resolution
|
|
||||||
|
|
||||||
(defn closest-variant
|
|
||||||
[variants target-weight target-style]
|
|
||||||
(when-let [target-weight (d/parse-integer target-weight)]
|
|
||||||
(let [result
|
|
||||||
(reduce
|
|
||||||
(fn [closest-match variant]
|
|
||||||
(let [weight (d/parse-integer (:weight variant))
|
|
||||||
distance (abs (- target-weight weight))
|
|
||||||
matches-style? (= target-style (:style variant))
|
|
||||||
current {:variant variant
|
|
||||||
:weight weight
|
|
||||||
:distance distance}]
|
|
||||||
(cond
|
|
||||||
;; Exact match found
|
|
||||||
(and (zero? distance)
|
|
||||||
(if target-style matches-style? true))
|
|
||||||
(reduced current)
|
|
||||||
|
|
||||||
(nil? closest-match) current
|
|
||||||
|
|
||||||
;; Update best match if this variant is closer or equal distance but higher weight
|
|
||||||
(or (< distance (:distance closest-match))
|
|
||||||
(and (= distance (:distance closest-match))
|
|
||||||
(> weight (:weight closest-match))))
|
|
||||||
current
|
|
||||||
|
|
||||||
;; Same weight as the `closest-match` but the style matches `target-style`
|
|
||||||
(and (= weight (:weight closest-match)) matches-style?)
|
|
||||||
current
|
|
||||||
|
|
||||||
:else
|
|
||||||
closest-match)))
|
|
||||||
nil
|
|
||||||
variants)]
|
|
||||||
(:variant result))))
|
|
||||||
|
|
||||||
(defn resolve-ttf-url
|
|
||||||
[font-uuid weight style]
|
|
||||||
(when-let [font (get by-uuid font-uuid)]
|
|
||||||
(let [style (if (zero? style) "normal" "italic")
|
|
||||||
variants (:variants font)]
|
|
||||||
(:ttf-url (or (closest-variant variants weight style)
|
|
||||||
(first variants))))))
|
|
||||||
@ -15,12 +15,12 @@
|
|||||||
Fully portable (no store/DOM/React), so it runs under Node too."
|
Fully portable (no store/DOM/React), so it runs under Node too."
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.render-wasm.gfonts :as gf]
|
|
||||||
[app.common.render-wasm.helpers :as h]
|
[app.common.render-wasm.helpers :as h]
|
||||||
[app.common.render-wasm.mem :as mem]
|
[app.common.render-wasm.mem :as mem]
|
||||||
[app.common.render-wasm.serializers :as sr]
|
[app.common.render-wasm.serializers :as sr]
|
||||||
[app.common.render-wasm.wasm :as wasm]
|
[app.common.render-wasm.wasm :as wasm]
|
||||||
[app.common.types.fills.impl :as types.fills.impl]
|
[app.common.types.fills.impl :as types.fills.impl]
|
||||||
|
[app.common.types.gfonts :as gf]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
;;
|
;;
|
||||||
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
||||||
|
|
||||||
(ns app.common.render-wasm.gfonts
|
(ns app.common.types.gfonts
|
||||||
"A fonts loading macros."
|
"A fonts loading macros."
|
||||||
|
|
||||||
(:require
|
(:require
|
||||||
@ -4,13 +4,159 @@
|
|||||||
;;
|
;;
|
||||||
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
||||||
|
|
||||||
(ns app.common.render-wasm.fallback-fonts
|
(ns app.common.types.gfonts
|
||||||
"Host-agnostic fallback-font knowledge: which scripts/emoji a text uses and
|
"Host-agnostic font knowledge shared by every renderer: the google catalog
|
||||||
which (google) fallback fonts cover them. Pure data + pure fns — no browser
|
baked at compile time from `common/resources/fonts/gfonts.*.json`, the
|
||||||
or Node dependencies — so the workspace (`api.texts`/`api.fonts`) and the
|
font-id/uuid mapping, weight/style variant resolution, and the noto fallback
|
||||||
headless exporter (`app.renderer.wasm`) compute the SAME fallback set from
|
fonts a text's scripts and emoji need. Also the one family bundled with the
|
||||||
the same source. Anything a host must fetch/upload for text to render
|
frontend, which is not a google font but resolves by the same rules.
|
||||||
belongs here, not in host code.")
|
|
||||||
|
Pure data + pure fns — no browser or Node dependencies — so the workspace and
|
||||||
|
the headless exporter resolve the SAME fonts from the same source. Anything a
|
||||||
|
host must fetch or upload for text to render belongs here, not in host code."
|
||||||
|
(:require-macros [app.common.types.gfonts :refer [preload-gfonts]])
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
;; --- GOOGLE FONTS CATALOG
|
||||||
|
|
||||||
|
(def catalog
|
||||||
|
(preload-gfonts "fonts/gfonts.2025.11.28.json"))
|
||||||
|
|
||||||
|
(def ^:private by-id
|
||||||
|
(reduce (fn [m font] (assoc m (:id font) font)) {} catalog))
|
||||||
|
|
||||||
|
(def ^:private by-uuid
|
||||||
|
(reduce (fn [m font] (assoc m (:uuid font) font)) {} catalog))
|
||||||
|
|
||||||
|
(defn gfont-id->uuid
|
||||||
|
"Maps a `gfont-<slug>` id to its (compilation-stable) catalog uuid, or nil."
|
||||||
|
[gfont-id]
|
||||||
|
(:uuid (get by-id gfont-id)))
|
||||||
|
|
||||||
|
;; --- font-id -> wasm uuid
|
||||||
|
|
||||||
|
(def ^:private custom-prefix "custom-")
|
||||||
|
(def ^:private gfont-prefix "gfont-")
|
||||||
|
|
||||||
|
(defn font-id->backend
|
||||||
|
"Which source a content font-id comes from: `:google` for `gfont-<slug>`,
|
||||||
|
`:custom` for `custom-<uuid>`, `:builtin` for everything else (bundled
|
||||||
|
families, but also unknown or malformed ids — the same bucket
|
||||||
|
`font-id->uuid` maps to `uuid/zero`)."
|
||||||
|
[font-id]
|
||||||
|
(cond
|
||||||
|
(not (string? font-id)) :builtin
|
||||||
|
(str/starts-with? font-id gfont-prefix) :google
|
||||||
|
(str/starts-with? font-id custom-prefix) :custom
|
||||||
|
:else :builtin))
|
||||||
|
|
||||||
|
(defn font-id->uuid
|
||||||
|
"Maps a content font-id to the uuid WASM keys fonts by:
|
||||||
|
|
||||||
|
- `gfont-<slug>` -> the catalog uuid,
|
||||||
|
- `custom-<uuid>` -> that uuid,
|
||||||
|
- anything else (builtin, unknown, malformed) -> `uuid/zero`, which WASM
|
||||||
|
resolves to the default font."
|
||||||
|
|
||||||
|
[font-id]
|
||||||
|
(case (font-id->backend font-id)
|
||||||
|
:google (or (gfont-id->uuid font-id) uuid/zero)
|
||||||
|
:custom (or (uuid/parse* (subs font-id (count custom-prefix))) uuid/zero)
|
||||||
|
uuid/zero))
|
||||||
|
|
||||||
|
;; --- proxy urls
|
||||||
|
|
||||||
|
(def ^:private gstatic-prefix
|
||||||
|
"https://fonts.gstatic.com/s")
|
||||||
|
|
||||||
|
(defn gstatic->proxy-url
|
||||||
|
[s base]
|
||||||
|
(let [base (str/rtrim (str base) "/")]
|
||||||
|
(str/replace (str s) gstatic-prefix base)))
|
||||||
|
|
||||||
|
;; --- variant resolution
|
||||||
|
|
||||||
|
(defn closest-variant
|
||||||
|
[variants target-weight target-style]
|
||||||
|
(when-let [target-weight (d/parse-integer target-weight)]
|
||||||
|
(let [result
|
||||||
|
(reduce
|
||||||
|
(fn [closest-match variant]
|
||||||
|
(let [weight (d/parse-integer (:weight variant))
|
||||||
|
distance (abs (- target-weight weight))
|
||||||
|
matches-style? (= target-style (:style variant))
|
||||||
|
current {:variant variant
|
||||||
|
:weight weight
|
||||||
|
:distance distance}]
|
||||||
|
(cond
|
||||||
|
;; Exact match found
|
||||||
|
(and (zero? distance)
|
||||||
|
(if target-style matches-style? true))
|
||||||
|
(reduced current)
|
||||||
|
|
||||||
|
(nil? closest-match) current
|
||||||
|
|
||||||
|
;; Update best match if this variant is closer or equal distance but higher weight
|
||||||
|
(or (< distance (:distance closest-match))
|
||||||
|
(and (= distance (:distance closest-match))
|
||||||
|
(> weight (:weight closest-match))))
|
||||||
|
current
|
||||||
|
|
||||||
|
;; Same weight as the `closest-match` but the style matches `target-style`
|
||||||
|
(and (= weight (:weight closest-match)) matches-style?)
|
||||||
|
current
|
||||||
|
|
||||||
|
:else
|
||||||
|
closest-match)))
|
||||||
|
nil
|
||||||
|
variants)]
|
||||||
|
(:variant result))))
|
||||||
|
|
||||||
|
(defn resolve-ttf-url
|
||||||
|
[font-uuid weight style]
|
||||||
|
(when-let [font (get by-uuid font-uuid)]
|
||||||
|
(let [style (if (zero? style) "normal" "italic")
|
||||||
|
variants (:variants font)]
|
||||||
|
(:ttf-url (or (closest-variant variants weight style)
|
||||||
|
(first variants))))))
|
||||||
|
|
||||||
|
;; --- BUILTIN FONTS
|
||||||
|
;;
|
||||||
|
;; Bundled with the frontend, served from `<public-uri>/fonts/`. Shared so the
|
||||||
|
;; workspace and the exporter upload the same TTF for a given weight/style.
|
||||||
|
|
||||||
|
(def local-fonts
|
||||||
|
[{:id "sourcesanspro"
|
||||||
|
:name "Source Sans Pro"
|
||||||
|
:family "sourcesanspro"
|
||||||
|
:variants
|
||||||
|
[{:id "200" :name "200" :weight "200" :style "normal" :suffix "extralight" :ttf-url "sourcesanspro-extralight.ttf"}
|
||||||
|
{:id "200italic" :name "200 Italic" :weight "200" :style "italic" :suffix "extralightitalic" :ttf-url "sourcesanspro-extralightitalic.ttf"}
|
||||||
|
{:id "300" :name "300" :weight "300" :style "normal" :suffix "light" :ttf-url "sourcesanspro-light.ttf"}
|
||||||
|
{:id "300italic" :name "300 Italic" :weight "300" :style "italic" :suffix "lightitalic" :ttf-url "sourcesanspro-lightitalic.ttf"}
|
||||||
|
{:id "regular" :name "400" :weight "400" :style "normal" :ttf-url "sourcesanspro-regular.ttf"}
|
||||||
|
{:id "italic" :name "400 Italic" :weight "400" :style "italic" :ttf-url "sourcesanspro-italic.ttf"}
|
||||||
|
{:id "600" :name "600" :weight "600" :style "normal" :suffix "semibold" :ttf-url "sourcesanspro-semibold.ttf"}
|
||||||
|
{:id "600italic" :name "600 Italic" :weight "600" :style "italic" :suffix "semibolditalic" :ttf-url "sourcesanspro-semibolditalic.ttf"}
|
||||||
|
{:id "bold" :name "700" :weight "700" :style "normal" :ttf-url "sourcesanspro-bold.ttf"}
|
||||||
|
{:id "bolditalic" :name "700 Italic" :weight "700" :style "italic" :ttf-url "sourcesanspro-bolditalic.ttf"}
|
||||||
|
{:id "black" :name "900" :weight "900" :style "normal" :ttf-url "sourcesanspro-black.ttf"}
|
||||||
|
{:id "blackitalic" :name "900 Italic" :weight "900" :style "italic" :ttf-url "sourcesanspro-blackitalic.ttf"}]}])
|
||||||
|
|
||||||
|
(defn resolve-ttf-file
|
||||||
|
"Builtin TTF file name for `weight` and `style` (0 normal, 1 italic), by the
|
||||||
|
same nearest-weight rule as the google catalog."
|
||||||
|
[weight style]
|
||||||
|
(let [variants (:variants (first local-fonts))]
|
||||||
|
(:ttf-url (or (closest-variant variants weight (if (zero? style) "normal" "italic"))
|
||||||
|
(first variants)))))
|
||||||
|
|
||||||
|
;; --- FALLBACK FONTS
|
||||||
|
;;
|
||||||
|
;; Which scripts/emoji a text uses and which (google) fallback fonts cover them.
|
||||||
|
|
||||||
(def ^:private emoji-pattern
|
(def ^:private emoji-pattern
|
||||||
#"(?:\uD83C[\uDDE6-\uDDFF]\uD83C[\uDDE6-\uDDFF])|(?:\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDEFF])|(?:\uD83E[\uDD00-\uDDFF])|(?:\uD83D[\uDE80-\uDEFF]|\uD83E[\uDC00-\uDCFF])|(?:\uD83E[\uDE70-\uDFFF])|[\u2600-\u26FF\u2700-\u27BF\u2300-\u23FF\u2B00-\u2BFF]")
|
#"(?:\uD83C[\uDDE6-\uDDFF]\uD83C[\uDDE6-\uDDFF])|(?:\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDEFF])|(?:\uD83E[\uDD00-\uDDFF])|(?:\uD83D[\uDE80-\uDEFF]|\uD83E[\uDC00-\uDCFF])|(?:\uD83E[\uDE70-\uDFFF])|[\u2600-\u26FF\u2700-\u27BF\u2300-\u23FF\u2B00-\u2BFF]")
|
||||||
@ -4,12 +4,12 @@
|
|||||||
;;
|
;;
|
||||||
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
||||||
|
|
||||||
(ns app.common.render-wasm.resources
|
(ns app.common.types.shape.images
|
||||||
"Host-agnostic enumeration of the external resources a scene needs to
|
"Host-agnostic enumeration of the external resources a scene needs to
|
||||||
render: which image bytes its shapes reference. Pure data walking — no
|
render: which image bytes its shapes reference. Pure data walking — no
|
||||||
browser or Node dependencies — so the workspace and the headless exporter
|
browser or Node dependencies — so the workspace and the headless exporter
|
||||||
derive the same set from the same source (sibling of
|
derive the same set from the same source (counterpart of
|
||||||
`app.common.render-wasm.fallback-fonts`, which does the same for fonts)."
|
`app.common.types.gfonts`, which does the same for fonts)."
|
||||||
(:require
|
(:require
|
||||||
[app.common.types.fills :as types.fills]))
|
[app.common.types.fills :as types.fills]))
|
||||||
|
|
||||||
@ -25,15 +25,13 @@
|
|||||||
[app.common.geom.point]
|
[app.common.geom.point]
|
||||||
[app.common.geom.rect]
|
[app.common.geom.rect]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
[app.common.render-wasm.builtin-fonts :as bfonts]
|
|
||||||
[app.common.render-wasm.fallback-fonts :as fbf]
|
|
||||||
[app.common.render-wasm.gfonts :as gf]
|
|
||||||
[app.common.render-wasm.resources :as resources]
|
|
||||||
[app.common.transit :as t]
|
[app.common.transit :as t]
|
||||||
[app.common.types.fills.impl]
|
[app.common.types.fills.impl]
|
||||||
|
[app.common.types.gfonts :as gf]
|
||||||
[app.common.types.objects-map]
|
[app.common.types.objects-map]
|
||||||
[app.common.types.path.impl]
|
[app.common.types.path.impl]
|
||||||
[app.common.types.shape]
|
[app.common.types.shape]
|
||||||
|
[app.common.types.shape.images :as images]
|
||||||
[app.common.uri :as u]
|
[app.common.uri :as u]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
@ -168,8 +166,8 @@
|
|||||||
;;
|
;;
|
||||||
;; The text serializer keeps each font's real uuid, so `wasm/fonts-for-shape`
|
;; The text serializer keeps each font's real uuid, so `wasm/fonts-for-shape`
|
||||||
;; reports it. Custom (team) fonts resolve through the file's font variants,
|
;; reports it. Custom (team) fonts resolve through the file's font variants,
|
||||||
;; google fonts through the shared `app.common.render-wasm.gfonts` catalog; builtin fonts
|
;; google fonts through the shared `app.common.types.gfonts` catalog; builtin
|
||||||
;; through `app.common.render-wasm.builtin-fonts` + the frontend's static `/fonts/`.
|
;; fonts through its bundled family + the frontend's static `/fonts/`.
|
||||||
|
|
||||||
(defn- fetch-font-variants
|
(defn- fetch-font-variants
|
||||||
"Team (custom) font variants for the file, or nil — a failure here degrades
|
"Team (custom) font variants for the file, or nil — a failure here degrades
|
||||||
@ -253,7 +251,7 @@
|
|||||||
(fetch-asset-bytes (:ttf-file-id variant) params)
|
(fetch-asset-bytes (:ttf-file-id variant) params)
|
||||||
|
|
||||||
(= uuid/zero font-uuid)
|
(= uuid/zero font-uuid)
|
||||||
(fetch-builtin-font-bytes (bfonts/resolve-ttf-file weight style))
|
(fetch-builtin-font-bytes (gf/resolve-ttf-file weight style))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(if-let [gurl (gf/resolve-ttf-url font-uuid weight style)]
|
(if-let [gurl (gf/resolve-ttf-url font-uuid weight style)]
|
||||||
@ -278,11 +276,11 @@
|
|||||||
:let [text (:text node)]
|
:let [text (:text node)]
|
||||||
:when (string? text)]
|
:when (string? text)]
|
||||||
text)
|
text)
|
||||||
emoji? (boolean (some fbf/contains-emoji? texts))
|
emoji? (boolean (some gf/contains-emoji? texts))
|
||||||
langs (reduce fbf/collect-used-languages #{} texts)]
|
langs (reduce gf/collect-used-languages #{} texts)]
|
||||||
(distinct
|
(distinct
|
||||||
(cond-> (fbf/add-noto-fonts [] langs)
|
(cond-> (gf/add-noto-fonts [] langs)
|
||||||
emoji? (fbf/add-emoji-font)))))
|
emoji? (gf/add-emoji-font)))))
|
||||||
|
|
||||||
(defn- fetch-fallback-font-bytes
|
(defn- fetch-fallback-font-bytes
|
||||||
"Downloads one fallback font's TTF. Cached by the whole variant, not just
|
"Downloads one fallback font's TTF. Cached by the whole variant, not just
|
||||||
@ -341,11 +339,11 @@
|
|||||||
|
|
||||||
(defn- provision-images!
|
(defn- provision-images!
|
||||||
"Fetches and stores every image the scene references (shape, stroke and
|
"Fetches and stores every image the scene references (shape, stroke and
|
||||||
text-span fills, enumerated by `app.common.render-wasm.resources`). Unlike fonts,
|
text-span fills, enumerated by `app.common.types.shape.images`). Unlike fonts,
|
||||||
the image store is not reset per request, so already-held images are skipped
|
the image store is not reset per request, so already-held images are skipped
|
||||||
and repeated exports of a file reuse them."
|
and repeated exports of a file reuse them."
|
||||||
[scene params]
|
[scene params]
|
||||||
(let [all-ids (resources/scene-image-ids scene)
|
(let [all-ids (images/scene-image-ids scene)
|
||||||
new-ids (remove wasm/image-cached? all-ids)]
|
new-ids (remove wasm/image-cached? all-ids)]
|
||||||
(l/dbg :hint "wasm render: provisioning images"
|
(l/dbg :hint "wasm render: provisioning images"
|
||||||
:total (count all-ids)
|
:total (count all-ids)
|
||||||
|
|||||||
@ -10,8 +10,7 @@
|
|||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.render-wasm.builtin-fonts :as bfonts]
|
[app.common.types.gfonts :as gf]
|
||||||
[app.common.render-wasm.gfonts :as gf]
|
|
||||||
[app.common.types.text :as txt]
|
[app.common.types.text :as txt]
|
||||||
[app.common.uri :as u]
|
[app.common.uri :as u]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
@ -26,10 +25,6 @@
|
|||||||
|
|
||||||
(log/set-level! :warn)
|
(log/set-level! :warn)
|
||||||
|
|
||||||
(def google-fonts gf/catalog)
|
|
||||||
|
|
||||||
(def local-fonts bfonts/local-fonts)
|
|
||||||
|
|
||||||
(defonce fontsdb (l/atom {}))
|
(defonce fontsdb (l/atom {}))
|
||||||
(defonce fonts (l/atom []))
|
(defonce fonts (l/atom []))
|
||||||
|
|
||||||
@ -49,10 +44,10 @@
|
|||||||
fonts (map #(assoc % :backend backend) fonts)]
|
fonts (map #(assoc % :backend backend) fonts)]
|
||||||
(merge db (d/index-by :id fonts))))))
|
(merge db (d/index-by :id fonts))))))
|
||||||
|
|
||||||
(register! :builtin local-fonts)
|
(register! :builtin gf/local-fonts)
|
||||||
|
|
||||||
(when (contains? cf/flags :google-fonts-provider)
|
(when (contains? cf/flags :google-fonts-provider)
|
||||||
(register! :google google-fonts))
|
(register! :google gf/catalog))
|
||||||
|
|
||||||
(defn get-font-data [id]
|
(defn get-font-data [id]
|
||||||
(get @fontsdb id))
|
(get @fontsdb id))
|
||||||
@ -382,7 +377,7 @@
|
|||||||
"Find the closest font weight variant in `font` for `target-weight` with optional `target-style` match.
|
"Find the closest font weight variant in `font` for `target-weight` with optional `target-style` match.
|
||||||
When exactly between two weights, choose the higher one.
|
When exactly between two weights, choose the higher one.
|
||||||
|
|
||||||
The algorithm lives in `app.common.render-wasm.gfonts` so the headless exporter resolves the
|
The algorithm lives in `app.common.types.gfonts` so the headless exporter resolves the
|
||||||
same variant for the same text."
|
same variant for the same text."
|
||||||
[font target-weight target-style]
|
[font target-weight target-style]
|
||||||
(gf/closest-variant (:variants font []) target-weight target-style))
|
(gf/closest-variant (:variants font []) target-weight target-style))
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
[app.common.render-wasm.wasm :as wasm]
|
[app.common.render-wasm.wasm :as wasm]
|
||||||
[app.common.types.color :as clr]
|
[app.common.types.color :as clr]
|
||||||
[app.common.types.fills :as types.fills]
|
[app.common.types.fills :as types.fills]
|
||||||
|
[app.common.types.gfonts :as gf]
|
||||||
[app.common.types.path :as path]
|
[app.common.types.path :as path]
|
||||||
[app.common.types.path.impl :as path.impl]
|
[app.common.types.path.impl :as path.impl]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
@ -1289,8 +1290,8 @@
|
|||||||
langs)
|
langs)
|
||||||
|
|
||||||
(let [text (apply str (map :text spans))
|
(let [text (apply str (map :text spans))
|
||||||
emoji? (if emoji? emoji? (t/contains-emoji? text))
|
emoji? (if emoji? emoji? (gf/contains-emoji? text))
|
||||||
langs (t/collect-used-languages langs text)]
|
langs (gf/collect-used-languages langs text)]
|
||||||
|
|
||||||
;; FIXME: this should probably be somewhere else
|
;; FIXME: this should probably be somewhere else
|
||||||
(when fallback-fonts-only? (t/write-shape-text spans paragraph text))
|
(when fallback-fonts-only? (t/write-shape-text spans paragraph text))
|
||||||
@ -1301,8 +1302,8 @@
|
|||||||
|
|
||||||
(let [updated-fonts
|
(let [updated-fonts
|
||||||
(-> #{}
|
(-> #{}
|
||||||
(cond-> ^boolean emoji? (f/add-emoji-font))
|
(cond-> ^boolean emoji? (gf/add-emoji-font))
|
||||||
(f/add-noto-fonts langs))
|
(gf/add-noto-fonts langs))
|
||||||
fallback-fonts (filter #(get % :is-fallback) updated-fonts)]
|
fallback-fonts (filter #(get % :is-fallback) updated-fonts)]
|
||||||
|
|
||||||
(if fallback-fonts-only? updated-fonts fallback-fonts))))))
|
(if fallback-fonts-only? updated-fonts fallback-fonts))))))
|
||||||
|
|||||||
@ -9,10 +9,9 @@
|
|||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.render-wasm.fallback-fonts :as fbf]
|
|
||||||
[app.common.render-wasm.gfonts :as gf]
|
|
||||||
[app.common.render-wasm.helpers :as h]
|
[app.common.render-wasm.helpers :as h]
|
||||||
[app.common.render-wasm.wasm :as wasm]
|
[app.common.render-wasm.wasm :as wasm]
|
||||||
|
[app.common.types.gfonts :as gf]
|
||||||
[app.common.types.text :as txt]
|
[app.common.types.text :as txt]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
@ -40,16 +39,6 @@
|
|||||||
(def ^:private default-line-height 1.2)
|
(def ^:private default-line-height 1.2)
|
||||||
(def ^:private default-letter-spacing 0.0)
|
(def ^:private default-letter-spacing 0.0)
|
||||||
|
|
||||||
(defn- font-backend
|
|
||||||
[font-id]
|
|
||||||
(cond
|
|
||||||
(str/starts-with? font-id "gfont-")
|
|
||||||
:google
|
|
||||||
(str/starts-with? font-id "custom-")
|
|
||||||
:custom
|
|
||||||
:else
|
|
||||||
:builtin))
|
|
||||||
|
|
||||||
(defn- font-db-data
|
(defn- font-db-data
|
||||||
[font-id font-variant-id font-weight-fallback font-style-fallback]
|
[font-id font-variant-id font-weight-fallback font-style-fallback]
|
||||||
(let [font (fonts/get-font-data font-id)
|
(let [font (fonts/get-font-data font-id)
|
||||||
@ -75,7 +64,7 @@
|
|||||||
"regular")))
|
"regular")))
|
||||||
|
|
||||||
(defn ^:private font-id->asset-id [font-id font-variant-id font-weight font-style]
|
(defn ^:private font-id->asset-id [font-id font-variant-id font-weight font-style]
|
||||||
(case (font-backend font-id)
|
(case (gf/font-id->backend font-id)
|
||||||
:google
|
:google
|
||||||
font-id
|
font-id
|
||||||
:custom
|
:custom
|
||||||
@ -174,7 +163,7 @@
|
|||||||
|
|
||||||
(defn- font-id->ttf-url
|
(defn- font-id->ttf-url
|
||||||
[font-id asset-id font-variant-id font-weight font-style]
|
[font-id asset-id font-variant-id font-weight font-style]
|
||||||
(case (font-backend font-id)
|
(case (gf/font-id->backend font-id)
|
||||||
:google
|
:google
|
||||||
(google-font-ttf-url font-id font-variant-id font-weight font-style)
|
(google-font-ttf-url font-id font-variant-id font-weight font-style)
|
||||||
:custom
|
:custom
|
||||||
@ -377,7 +366,3 @@
|
|||||||
(defn store-fonts
|
(defn store-fonts
|
||||||
[fonts]
|
[fonts]
|
||||||
(keep (fn [font] (store-font font)) fonts))
|
(keep (fn [font] (store-font font)) fonts))
|
||||||
|
|
||||||
(def add-emoji-font fbf/add-emoji-font)
|
|
||||||
(def noto-fonts fbf/noto-fonts)
|
|
||||||
(def add-noto-fonts fbf/add-noto-fonts)
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
(ns app.render-wasm.api.texts
|
(ns app.render-wasm.api.texts
|
||||||
(:require
|
(:require
|
||||||
[app.common.render-wasm.fallback-fonts :as fbf]
|
|
||||||
[app.common.render-wasm.text-content :as tc]
|
[app.common.render-wasm.text-content :as tc]
|
||||||
[app.render-wasm.api.fonts :as f]))
|
[app.render-wasm.api.fonts :as f]))
|
||||||
|
|
||||||
@ -17,9 +16,3 @@
|
|||||||
(tc/write-shape-text! spans paragraph text
|
(tc/write-shape-text! spans paragraph text
|
||||||
{:normalize-paragraph f/normalize-paragraph-font
|
{:normalize-paragraph f/normalize-paragraph-font
|
||||||
:normalize-span f/normalize-span-font}))
|
:normalize-span f/normalize-span-font}))
|
||||||
|
|
||||||
;; Emoji/script detection lives in the host-agnostic
|
|
||||||
;; `app.common.render-wasm.fallback-fonts`; kept re-exported here for existing
|
|
||||||
;; workspace callers.
|
|
||||||
(def contains-emoji? fbf/contains-emoji?)
|
|
||||||
(def collect-used-languages fbf/collect-used-languages)
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user