mirror of
https://github.com/penpot/penpot.git
synced 2026-08-04 20:09:05 +00:00
🔧 Upload builtin font variants in the wasm exporter
This commit is contained in:
parent
1f510ae22c
commit
7de5ade845
37
common/src/app/common/render_wasm/builtin_fonts.cljs
Normal file
37
common/src/app/common/render_wasm/builtin_fonts.cljs
Normal file
@ -0,0 +1,37 @@
|
||||
;; 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)))))
|
||||
@ -25,6 +25,7 @@
|
||||
[app.common.geom.point]
|
||||
[app.common.geom.rect]
|
||||
[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]
|
||||
@ -167,8 +168,8 @@
|
||||
;;
|
||||
;; 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,
|
||||
;; google fonts through the shared `app.common.render-wasm.gfonts` catalog; builtin falls back to
|
||||
;; the bundled default.
|
||||
;; google fonts through the shared `app.common.render-wasm.gfonts` catalog; builtin fonts
|
||||
;; through `app.common.render-wasm.builtin-fonts` + the frontend's static `/fonts/`.
|
||||
|
||||
(defn- fetch-font-variants
|
||||
"Team (custom) font variants for the file, or nil — a failure here degrades
|
||||
@ -189,47 +190,52 @@
|
||||
:uri uri :detail (explain cause) :cause cause)
|
||||
(p/resolved nil))))))
|
||||
|
||||
(defn- fetch-asset-bytes
|
||||
"Downloads a stored asset (font TTF) by id, returning a promise of an
|
||||
ArrayBuffer (or nil)."
|
||||
[asset-id {:keys [token]}]
|
||||
(let [headers (asset-headers token)
|
||||
uri (internal-uri (str "assets/by-id/" asset-id))]
|
||||
(->> (fetch! uri #js {:method "GET" :headers headers})
|
||||
(p/mcat (fn [^js resp]
|
||||
(if (= 200 (.-status resp))
|
||||
(.arrayBuffer resp)
|
||||
(p/resolved nil))))
|
||||
(p/merr (fn [cause]
|
||||
(l/warn :hint "wasm render: font asset fetch failed"
|
||||
:asset-id (str asset-id) :uri uri
|
||||
:detail (explain cause) :cause cause)
|
||||
(p/resolved nil))))))
|
||||
(defn- fetch-ttf-bytes
|
||||
"Downloads a TTF, returning a promise of an ArrayBuffer (or nil). A failure
|
||||
here degrades to fallback fonts, it does not fail the export."
|
||||
([uri] (fetch-ttf-bytes uri #js {:method "GET"}))
|
||||
([uri opts]
|
||||
(->> (fetch! uri opts)
|
||||
(p/mcat (fn [^js resp]
|
||||
(if (= 200 (.-status resp))
|
||||
(.arrayBuffer resp)
|
||||
(p/resolved nil))))
|
||||
(p/merr (fn [cause]
|
||||
(l/warn :hint "wasm render: font fetch failed"
|
||||
:uri uri :detail (explain cause) :cause cause)
|
||||
(p/resolved nil))))))
|
||||
|
||||
(defn- gfont-proxy-url
|
||||
"Rewrites a gstatic ttf url to the local gfonts proxy (same rewrite as the
|
||||
browser's `google-font-ttf-url`)."
|
||||
[ttf-url]
|
||||
(gf/gstatic->proxy-url ttf-url (internal-uri "internal/gfonts/font")))
|
||||
;; TTF bytes cached for the process lifetime, keyed by whatever identifies the
|
||||
;; variant (a gfont id+weight+style, a builtin file name).
|
||||
(defonce ^:private font-bytes* (atom {}))
|
||||
|
||||
(defn- cached-ttf-bytes
|
||||
[cache-key fetch-fn]
|
||||
(if-let [bytes (get @font-bytes* cache-key)]
|
||||
(p/resolved bytes)
|
||||
(->> (fetch-fn)
|
||||
(p/fmap (fn [buf]
|
||||
(when buf (swap! font-bytes* assoc cache-key buf))
|
||||
buf)))))
|
||||
|
||||
(defn- fetch-asset-bytes
|
||||
[asset-id {:keys [token]}]
|
||||
(fetch-ttf-bytes (internal-uri (str "assets/by-id/" asset-id))
|
||||
#js {:method "GET" :headers (asset-headers token)}))
|
||||
|
||||
(defn- fetch-gfont-bytes
|
||||
"Downloads a google font TTF through the local gfonts proxy."
|
||||
[ttf-url]
|
||||
(let [uri (gfont-proxy-url ttf-url)]
|
||||
(->> (fetch! uri #js {:method "GET"})
|
||||
(p/mcat (fn [^js resp]
|
||||
(if (= 200 (.-status resp))
|
||||
(.arrayBuffer resp)
|
||||
(p/resolved nil))))
|
||||
(p/merr (fn [cause]
|
||||
(l/warn :hint "wasm render: gfont fetch failed"
|
||||
:url uri :detail (explain cause) :cause cause)
|
||||
(p/resolved nil))))))
|
||||
(fetch-ttf-bytes (gf/gstatic->proxy-url ttf-url (internal-uri "internal/gfonts/font"))))
|
||||
|
||||
(defn- fetch-builtin-font-bytes
|
||||
[ttf-file]
|
||||
(cached-ttf-bytes ttf-file #(fetch-ttf-bytes (internal-uri (str "fonts/" ttf-file)))))
|
||||
|
||||
(defn- make-resolve-font
|
||||
"Builds a `resolve-font` fn (family map -> promise of TTF bytes). Custom
|
||||
variants first, matching uuid+weight+style then degrading to uuid+weight then
|
||||
uuid; google catalog after that; nil when nothing matches."
|
||||
uuid; the bundled fonts for `uuid/zero`, which is what `font-id->uuid` maps
|
||||
every builtin family to; google catalog otherwise."
|
||||
[variants params]
|
||||
(fn [{:keys [id weight style]}]
|
||||
(let [font-uuid (uuid/from-unsigned-parts (aget id 0) (aget id 1) (aget id 2) (aget id 3))
|
||||
@ -242,8 +248,14 @@
|
||||
(= (:font-weight v) weight)))
|
||||
variants)
|
||||
(d/seek (fn [v] (= (:font-id v) font-uuid)) variants))]
|
||||
(if-let [ttf-id (:ttf-file-id variant)]
|
||||
(fetch-asset-bytes ttf-id params)
|
||||
(cond
|
||||
(:ttf-file-id variant)
|
||||
(fetch-asset-bytes (:ttf-file-id variant) params)
|
||||
|
||||
(= uuid/zero font-uuid)
|
||||
(fetch-builtin-font-bytes (bfonts/resolve-ttf-file weight style))
|
||||
|
||||
:else
|
||||
(if-let [gurl (gf/resolve-ttf-url font-uuid weight style)]
|
||||
(fetch-gfont-bytes gurl)
|
||||
(p/resolved nil))))))
|
||||
@ -272,25 +284,14 @@
|
||||
(cond-> (fbf/add-noto-fonts [] langs)
|
||||
emoji? (fbf/add-emoji-font)))))
|
||||
|
||||
(defonce ^:private fallback-font-bytes* (atom {}))
|
||||
|
||||
(defn- fetch-fallback-font-bytes
|
||||
"Downloads (and caches for the process lifetime) one fallback font's TTF.
|
||||
Keyed by the whole variant, not just `font-id`: `resolve-ttf-url` picks a
|
||||
different TTF per weight/style, so a font-id-only key would serve the first
|
||||
downloaded variant for every other one."
|
||||
"Downloads one fallback font's TTF. Cached by the whole variant, not just
|
||||
`font-id`: `resolve-ttf-url` picks a different TTF per weight/style, so a
|
||||
font-id-only key would serve the first downloaded variant for every other one."
|
||||
[{:keys [font-id weight style]}]
|
||||
(let [cache-key [font-id weight style]]
|
||||
(if-let [bytes (get @fallback-font-bytes* cache-key)]
|
||||
(p/resolved bytes)
|
||||
(let [font-uuid (gf/gfont-id->uuid font-id)
|
||||
ttf-url (some-> font-uuid (gf/resolve-ttf-url weight style))]
|
||||
(if ttf-url
|
||||
(->> (fetch-gfont-bytes ttf-url)
|
||||
(p/fmap (fn [buf]
|
||||
(when buf (swap! fallback-font-bytes* assoc cache-key buf))
|
||||
buf)))
|
||||
(p/resolved nil))))))
|
||||
(if-let [ttf-url (some-> (gf/gfont-id->uuid font-id) (gf/resolve-ttf-url weight style))]
|
||||
(cached-ttf-bytes [font-id weight style] #(fetch-gfont-bytes ttf-url))
|
||||
(p/resolved nil)))
|
||||
|
||||
(defn- provision-fallback-fonts!
|
||||
[scene]
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.logging :as log]
|
||||
[app.common.render-wasm.builtin-fonts :as bfonts]
|
||||
[app.common.render-wasm.gfonts :as gf]
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.uri :as u]
|
||||
@ -27,23 +28,7 @@
|
||||
|
||||
(def google-fonts gf/catalog)
|
||||
|
||||
(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"}]}])
|
||||
(def local-fonts bfonts/local-fonts)
|
||||
|
||||
(defonce fontsdb (l/atom {}))
|
||||
(defonce fonts (l/atom []))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user