diff --git a/frontend/src/app/main/data/workspace/wasm_text.cljs b/frontend/src/app/main/data/workspace/wasm_text.cljs index fd814b45f5..ee262123cf 100644 --- a/frontend/src/app/main/data/workspace/wasm_text.cljs +++ b/frontend/src/app/main/data/workspace/wasm_text.cljs @@ -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] diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index bbe548835c..64e509e7f5 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -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 diff --git a/frontend/src/app/render_wasm/api/fonts.cljs b/frontend/src/app/render_wasm/api/fonts.cljs index 0e939634f9..c520923f74 100644 --- a/frontend/src/app/render_wasm/api/fonts.cljs +++ b/frontend/src/app/render_wasm/api/fonts.cljs @@ -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] diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index b571aea098..e8aa0640f7 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -346,6 +346,14 @@ pub extern "C" fn use_shape(a: u32, b: u32, c: u32, d: u32) { }); } +#[no_mangle] +pub extern "C" fn touch_shape(a: u32, b: u32, c: u32, d: u32) { + with_state_mut!(state, { + let shape_id = uuid_from_u32_quartet(a, b, c, d); + state.touch_shape(shape_id); + }); +} + #[no_mangle] pub extern "C" fn set_parent(a: u32, b: u32, c: u32, d: u32) { with_state_mut!(state, { diff --git a/render-wasm/src/shapes/modifiers.rs b/render-wasm/src/shapes/modifiers.rs index b9cc7201bc..1af06713a7 100644 --- a/render-wasm/src/shapes/modifiers.rs +++ b/render-wasm/src/shapes/modifiers.rs @@ -414,7 +414,7 @@ pub fn propagate_modifiers( &mut reflown, &modifiers, ) - }, + } } } diff --git a/render-wasm/src/wasm/fonts.rs b/render-wasm/src/wasm/fonts.rs index 43d8d5e3f7..b4a604e0e5 100644 --- a/render-wasm/src/wasm/fonts.rs +++ b/render-wasm/src/wasm/fonts.rs @@ -31,21 +31,17 @@ impl From for FontStyle { #[no_mangle] pub extern "C" fn store_font( - a1: u32, - b1: u32, - c1: u32, - d1: u32, - a2: u32, - b2: u32, - c2: u32, - d2: u32, + a: u32, + b: u32, + c: u32, + d: u32, weight: u32, style: u8, is_emoji: bool, is_fallback: bool, ) { with_state_mut!(state, { - let id = uuid_from_u32_quartet(a2, b2, c2, d2); + let id = uuid_from_u32_quartet(a, b, c, d); let font_bytes = mem::bytes(); let font_style = RawFontStyle::from(style); @@ -57,9 +53,6 @@ pub extern "C" fn store_font( .add(family, &font_bytes, is_emoji, is_fallback); mem::free_bytes(); - - let shape_id = uuid_from_u32_quartet(a1, b1, c1, d1); - state.touch_shape(shape_id); }); } diff --git a/render-wasm/src/wasm/text.rs b/render-wasm/src/wasm/text.rs index df2e72f841..e4617575aa 100644 --- a/render-wasm/src/wasm/text.rs +++ b/render-wasm/src/wasm/text.rs @@ -384,6 +384,7 @@ pub extern "C" fn update_shape_text_layout_for(a: u32, b: u32, c: u32, d: u32) { if let Some(shape) = state.shapes.get_mut(&shape_id) { update_text_layout(shape); } + state.touch_shape(shape_id); }); }