Batch text layout updates

This commit is contained in:
alonso.torres
2026-02-05 16:46:15 +01:00
committed by Belén Albeza
parent 53c2acb3e6
commit fd3d549f9c
7 changed files with 111 additions and 70 deletions

View File

@@ -10,6 +10,7 @@
This exists to avoid circular deps:
workspace.texts -> workspace.libraries -> workspace.texts"
(:require
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
@@ -17,6 +18,7 @@
[app.main.data.helpers :as dsh]
[app.main.data.workspace.modifiers :as dwm]
[app.render-wasm.api :as wasm.api]
[app.render-wasm.api.fonts :as wasm.fonts]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
@@ -64,7 +66,7 @@
(defn resize-wasm-text-debounce-commit
[]
(ptk/reify ::resize-wasm-text
(ptk/reify ::resize-wasm-text-debounce-commit
ptk/WatchEvent
(watch [_ state _]
(let [ids (get state ::resize-wasm-text-debounce-ids)
@@ -89,10 +91,10 @@
;; are processed at the same time and not one-by-one. This will improve
;; performance because it's better to make only one layout calculation instead
;; of (potentialy) hundreds.
(defn resize-wasm-text-debounce
(defn resize-wasm-text-debounce-inner
[id]
(let [cur-event (js/Symbol)]
(ptk/reify ::resize-wasm-text-debounce
(ptk/reify ::resize-wasm-text-debounce-inner
ptk/UpdateEvent
(update [_ state]
(-> state
@@ -107,20 +109,39 @@
(rx/concat
(rx/merge
(->> stream
(rx/filter (ptk/type? ::resize-wasm-text-debounce))
(rx/debounce 20)
(rx/filter (ptk/type? ::resize-wasm-text-debounce-inner))
(rx/debounce 40)
(rx/take 1)
(rx/delay 200)
(rx/map #(resize-wasm-text-debounce-commit))
(rx/take-until stopper))
(rx/of (resize-wasm-text-debounce id)))
(rx/of (resize-wasm-text-debounce-inner id)))
(rx/of #(dissoc %
::resize-wasm-text-debounce-ids
::resize-wasm-text-debounce-event))))
(rx/empty))))))
(defn resize-wasm-text-debounce
[id]
(ptk/reify ::resize-wasm-text-debounce
ptk/WatchEvent
(watch [_ state _]
(let [page-id (:current-page-id state)
objects (dsh/lookup-page-objects state page-id)
content (dm/get-in objects [id :content])
fonts (wasm.fonts/get-content-fonts content)
fonts-loaded?
(->> fonts
(every?
(fn [font]
(let [font-data (wasm.fonts/make-font-data font)]
(wasm.fonts/font-stored? font-data (:emoji? font-data))))))]
(if (not fonts-loaded?)
(->> (rx/of (resize-wasm-text-debounce id))
(rx/delay 20))
(rx/of (resize-wasm-text-debounce-inner id)))))))
(defn resize-wasm-text-all
"Resize all text shapes (auto-width/auto-height) from a collection of ids."
[ids]

View File

@@ -867,12 +867,12 @@
(set-shape-vertical-align (get content :vertical-align))
(let [fonts (f/get-content-fonts content)
(let [fonts (f/get-content-fonts content)
fallback-fonts (fonts-from-text-content content true)
all-fonts (concat fonts fallback-fonts)
result (f/store-fonts shape-id all-fonts)]
all-fonts (concat fonts fallback-fonts)
result (f/store-fonts all-fonts)]
(f/load-fallback-fonts-for-editor! fallback-fonts)
(h/call wasm/internal-module "_update_shape_text_layout")
(f/update-text-layout shape-id)
result))
(defn set-shape-grow-type

View File

@@ -97,9 +97,8 @@
;; IMPORTANT: Only TTF fonts can be stored.
(defn- store-font-buffer
[shape-id font-data font-array-buffer emoji? fallback?]
[font-data font-array-buffer emoji? fallback?]
(let [font-id-buffer (:family-id-buffer font-data)
shape-id-buffer (uuid/get-u32 shape-id)
size (.-byteLength font-array-buffer)
ptr (h/call wasm/internal-module "_alloc_bytes" size)
heap (gobj/get ^js wasm/internal-module "HEAPU8")
@@ -107,10 +106,6 @@
(.set mem (js/Uint8Array. font-array-buffer))
(h/call wasm/internal-module "_store_font"
(aget shape-id-buffer 0)
(aget shape-id-buffer 1)
(aget shape-id-buffer 2)
(aget shape-id-buffer 3)
(aget font-id-buffer 0)
(aget font-id-buffer 1)
(aget font-id-buffer 2)
@@ -119,24 +114,31 @@
(:style font-data)
emoji?
fallback?)
(update-text-layout shape-id)
true))
;; This variable will store the fonts that are currently being fetched
;; so we don't fetch more than once the same font
(def fetching (atom #{}))
(defn- fetch-font
[shape-id font-data font-url emoji? fallback?]
{:key font-url
:callback #(->> (http/send! {:method :get
:uri font-url
:response-type :buffer})
(rx/map (fn [{:keys [body]}]
(store-font-buffer shape-id font-data body emoji? fallback?)))
(rx/catch (fn [cause]
(log/error :hint "Could not fetch font"
:font-url font-url
:cause cause)
(rx/empty))))})
[font-data font-url emoji? fallback?]
(when-not (contains? @fetching font-url)
(swap! fetching conj font-url)
{:key font-url
:callback
(fn []
(->> (http/send! {:method :get
:uri font-url
:response-type :buffer})
(rx/map (fn [{:keys [body]}]
(swap! fetching disj font-url)
(store-font-buffer font-data body emoji? fallback?)))
(rx/catch (fn [cause]
(swap! fetching disj font-url)
(log/error :hint "Could not fetch font"
:font-url font-url
:cause cause)
(rx/empty)))))}))
(defn- google-font-ttf-url
[font-id font-variant-id font-weight font-style]
@@ -155,22 +157,31 @@
:builtin
(dm/str (u/join cf/public-uri "fonts/" asset-id))))
(defn font-stored?
[font-data emoji?]
(when-let [id-buffer (uuid/get-u32 (:wasm-id font-data))]
(not= 0 (h/call wasm/internal-module "_is_font_uploaded"
(aget id-buffer 0)
(aget id-buffer 1)
(aget id-buffer 2)
(aget id-buffer 3)
(:weight font-data)
(:style font-data)
emoji?))))
(defn- store-font-id
[shape-id font-data asset-id emoji? fallback?]
[font-data asset-id emoji? fallback?]
(when asset-id
(let [uri (font-id->ttf-url (:font-id font-data) asset-id (:font-variant-id font-data) (:weight font-data) (:style-name font-data))
(let [uri (font-id->ttf-url
(:font-id font-data) asset-id
(:font-variant-id font-data)
(:weight font-data)
(:style-name font-data))
id-buffer (uuid/get-u32 (:wasm-id font-data))
font-data (assoc font-data :family-id-buffer id-buffer)
font-stored? (not= 0 (h/call wasm/internal-module "_is_font_uploaded"
(aget id-buffer 0)
(aget id-buffer 1)
(aget id-buffer 2)
(aget id-buffer 3)
(:weight font-data)
(:style font-data)
emoji?))]
font-stored? (font-stored? font-data emoji?)]
(when-not font-stored?
(fetch-font shape-id font-data uri emoji? fallback?)))))
(fetch-font font-data uri emoji? fallback?)))))
(defn serialize-font-style
[font-style]
@@ -280,8 +291,8 @@
"regular"
font-variant-id))
(defn store-font
[shape-id font]
(defn make-font-data
[font]
(let [font-id (get font :font-id)
font-variant-id (get font :font-variant-id)
normalized-variant-id (when font-variant-id
@@ -301,14 +312,21 @@
(str/includes? raw-weight "italic") "italic"
:else font-style-fallback)
variant-id (or (:id font-data) normalized-variant-id)
asset-id (font-id->asset-id font-id variant-id raw-weight style)
font-data {:wasm-id wasm-id
:font-id font-id
:font-variant-id variant-id
:style (serialize-font-style style)
:style-name style
:weight weight}]
(store-font-id shape-id font-data asset-id emoji? fallback?)))
asset-id (font-id->asset-id font-id variant-id raw-weight style)]
{:wasm-id wasm-id
:font-id font-id
:font-variant-id variant-id
:style (serialize-font-style style)
:style-name style
:weight weight
:emoji? emoji?
:fallbck? fallback?
:asset-id asset-id}))
(defn store-font
[font]
(let [{:keys [asset-id emoji? fallback?] :as font-data} (make-font-data font)]
(store-font-id font-data asset-id emoji? fallback?)))
;; FIXME: This is a temporary function to load the fallback fonts for the editor.
;; Once we render the editor content within wasm, we can remove this function.
@@ -341,8 +359,8 @@
#{}))))
(defn store-fonts
[shape-id fonts]
(keep (fn [font] (store-font shape-id font)) fonts))
[fonts]
(keep (fn [font] (store-font font)) fonts))
(defn add-emoji-font
[fonts]