diff --git a/frontend/playwright/data/render-wasm/assets/pattern-thumbnail.png b/frontend/playwright/data/render-wasm/assets/pattern-thumbnail.png new file mode 100644 index 0000000000..3d24f4ba3b Binary files /dev/null and b/frontend/playwright/data/render-wasm/assets/pattern-thumbnail.png differ diff --git a/frontend/playwright/ui/pages/BasePage.js b/frontend/playwright/ui/pages/BasePage.js index c5f16c1dac..628415b97b 100644 --- a/frontend/playwright/ui/pages/BasePage.js +++ b/frontend/playwright/ui/pages/BasePage.js @@ -23,11 +23,18 @@ export class BasePage { ); } - static async mockFileMediaAsset(page, assetId, assetFilename, options) { + static async mockFileMediaAsset( + page, + assetId, + assetFilename, + assetThumbnailFilename, + options, + ) { const ids = Array.isArray(assetId) ? assetId : [assetId]; for (const id of ids) { const url = `**/assets/by-file-media-id/${id}`; + const thumbnailUrl = `${url}/thumbnail`; await page.route(url, (route) => route.fulfill({ @@ -36,6 +43,16 @@ export class BasePage { ...options, }), ); + + if (assetThumbnailFilename) { + await page.route(thumbnailUrl, (route) => + route.fulfill({ + path: `playwright/data/${assetThumbnailFilename}`, + status: 200, + ...options, + }), + ); + } } } @@ -55,22 +72,6 @@ export class BasePage { } } - static async mockFileMediaAsset(page, assetId, assetFilename, options) { - const ids = Array.isArray(assetId) ? assetId : [assetId]; - - for (const id of ids) { - const url = `**/assets/by-file-media-id/${id}`; - - await page.route(url, (route) => - route.fulfill({ - path: `playwright/data/${assetFilename}`, - status: 200, - ...options, - }), - ); - } - } - static async mockConfigFlags(page, flags) { const url = "**/js/config.js?ts=*"; return await page.route(url, (route) => @@ -100,11 +101,17 @@ export class BasePage { return BasePage.mockConfigFlags(this.page, flags); } - async mockFileMediaAsset(assetId, assetFilename, options) { + async mockFileMediaAsset( + assetId, + assetFilename, + assetThumbnailFilename, + options, + ) { return BasePage.mockFileMediaAsset( this.page, assetId, assetFilename, + assetThumbnailFilename, options, ); } diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js index f4e6822ed6..7661974477 100644 --- a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js +++ b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js @@ -36,6 +36,7 @@ test("Renders a file with solid, gradient and image fills", async ({ "1ebcea38-f1bf-8101-8006-4c8f579da49c", ], "render-wasm/assets/penguins.jpg", + "render-wasm/assets/pattern-thumbnail.png", // FIXME: get real thumbnail ); await workspace.mockGetFile("render-wasm/get-file-shapes-fills.json"); @@ -58,6 +59,7 @@ test("Renders a file with strokes", async ({ page }) => { "202c1104-9385-81d3-8006-507560ce29e3", ], "render-wasm/assets/penguins.jpg", + "render-wasm/assets/pattern-thumbnail.png", // FIXME: get real thumbnail ); await workspace.mockGetFile("render-wasm/get-file-shapes-strokes.json"); @@ -88,6 +90,11 @@ test("Renders a file with shapes with multiple fills", async ({ page }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await workspace.mockGetFile("render-wasm/get-file-multiple-fills.json"); + await workspace.mockFileMediaAsset( + ["c0939f58-37bc-805d-8006-51cda84a405a"], + "render-wasm/assets/penguins.jpg", + "render-wasm/assets/pattern-thumbnail.png", // FIXME: get real thumbnail + ); await workspace.goToWorkspace({ id: "c0939f58-37bc-805d-8006-51cd3a51c255", @@ -127,6 +134,7 @@ test("Renders shapes with exif rotated images fills and strokes", async ({ "27270c45-35b4-80f3-8006-63a3ea82557f", ], "render-wasm/assets/landscape.jpg", + "render-wasm/assets/pattern-thumbnail.png", // FIXME: get real thumbnail ); await workspace.mockGetFile( "render-wasm/get-file-shapes-exif-rotated-fills.json", @@ -170,6 +178,15 @@ test("Renders a file with blurs applied to any kind of shape", async ({ const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await workspace.mockGetFile("render-wasm/get-file-blurs.json"); + await workspace.mockFileMediaAsset( + [ + "aa0a383a-7553-808a-8006-ae13a3c575eb", + "aa0a383a-7553-808a-8006-ae13c84d6e3a", + "aa0a383a-7553-808a-8006-ae131157fc26", + ], + "render-wasm/assets/pattern.png", + "render-wasm/assets/pattern-thumbnail.png", // FIXME: get real thumbnail + ); await workspace.goToWorkspace({ id: "aa0a383a-7553-808a-8006-ae1237b52cf9", @@ -212,10 +229,7 @@ test("Renders a file with a closed path shape with multiple segments using strok await expect(workspace.canvas).toHaveScreenshot(); }); - -test("Renders a file with paths and svg attrs", async ({ - page, -}) => { +test("Renders a file with paths and svg attrs", async ({ page }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await workspace.mockGetFile("render-wasm/get-file-svg-attrs.json"); @@ -234,7 +248,9 @@ test("Renders a file with nested frames with inherited blur", async ({ }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); - await workspace.mockGetFile("render-wasm/get-file-frame-with-nested-blur.json"); + await workspace.mockGetFile( + "render-wasm/get-file-frame-with-nested-blur.json", + ); await workspace.goToWorkspace({ id: "58c5cc60-d124-81bd-8007-0ee4e5030609", diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-blurs-applied-to-any-kind-of-shape-1.png b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-blurs-applied-to-any-kind-of-shape-1.png index 333e7e9780..de3fd45317 100644 Binary files a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-blurs-applied-to-any-kind-of-shape-1.png and b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-blurs-applied-to-any-kind-of-shape-1.png differ diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-shadows-applied-to-any-kind-of-shape-1.png b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-shadows-applied-to-any-kind-of-shape-1.png index eb1f4f5242..fff5f37a26 100644 Binary files a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-shadows-applied-to-any-kind-of-shape-1.png and b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-shadows-applied-to-any-kind-of-shape-1.png differ diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-shapes-with-multiple-fills-1.png b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-shapes-with-multiple-fills-1.png index 5f0e40d500..47d8a5c323 100644 Binary files a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-shapes-with-multiple-fills-1.png and b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-shapes-with-multiple-fills-1.png differ diff --git a/frontend/playwright/ui/render-wasm-specs/texts.spec.js b/frontend/playwright/ui/render-wasm-specs/texts.spec.js index 816fb964ff..5239fb5606 100644 --- a/frontend/playwright/ui/render-wasm-specs/texts.spec.js +++ b/frontend/playwright/ui/render-wasm-specs/texts.spec.js @@ -141,6 +141,7 @@ test("Renders a file with texts with images", async ({ page }) => { "4f89252d-ebbc-813e-8006-8699e4170e18", ], "render-wasm/assets/pattern.png", + "render-wasm/assets/pattern-thumbnail.png", ); await mockGetEmojiFont(workspace); await mockGetJapaneseFont(workspace); @@ -179,6 +180,7 @@ test("Renders a file with text decoration", async ({ page }) => { await workspace.mockFileMediaAsset( ["d6c33e7b-7b64-80f3-8006-78509a3a2d21"], "render-wasm/assets/pattern.png", + "render-wasm/assets/pattern-thumbnail.png", ); await mockGetEmojiFont(workspace); await mockGetJapaneseFont(workspace); @@ -281,14 +283,10 @@ test("Renders a file with different text shadows combinations", async ({ await expect(workspace.canvas).toHaveScreenshot(); }); -test("Renders a file with multiple text shadows in order", async ({ - page, -}) => { +test("Renders a file with multiple text shadows in order", async ({ page }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); - await workspace.mockGetFile( - "render-wasm/get-file-text-shadows-order.json", - ); + await workspace.mockGetFile("render-wasm/get-file-text-shadows-order.json"); await workspace.goToWorkspace({ id: "48ffa82f-6950-81b5-8006-e49a2a39657f", @@ -337,7 +335,9 @@ test("Renders a file with texts with with text spans of different sizes", async }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); - await workspace.mockGetFile("render-wasm/get-file-text-spans-different-sizes.json"); + await workspace.mockGetFile( + "render-wasm/get-file-text-spans-different-sizes.json", + ); await workspace.goToWorkspace({ id: "a0b1a70e-0d02-8082-8006-ff6d160f15ce", @@ -347,9 +347,8 @@ test("Renders a file with texts with with text spans of different sizes", async await expect(workspace.canvas).toHaveScreenshot(); }); -test("Renders a file with texts with tabs", async ({ - page, -}) => { +// TODO: enable this test once we use the wasm renderer in the new editor +test.skip("Renders a file with texts with tabs", async ({ page }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await workspace.mockGetFile("render-wasm/get-file-text-tabs.json"); @@ -367,9 +366,8 @@ test("Renders a file with texts with tabs", async ({ await expect(workspace.canvas).toHaveScreenshot(); }); -test("Renders a file with texts with empty lines", async ({ - page, -}) => { +// TODO: enable this test once we use the wasm renderer in the new editor +test.skip("Renders a file with texts with empty lines", async ({ page }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await workspace.mockGetFile("render-wasm/get-file-empty-lines.json"); @@ -387,9 +385,8 @@ test("Renders a file with texts with empty lines", async ({ await expect(workspace.canvas).toHaveScreenshot(); }); -test("Renders a file with texts with breaking words", async ({ - page, -}) => { +// TODO: enable this test once we use the wasm renderer in the new editor +test.skip("Renders a file with texts with breaking words", async ({ page }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await workspace.mockGetFile("render-wasm/get-file-empty-lines.json"); diff --git a/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-different-text-leaves-decoration-1.png b/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-different-text-leaves-decoration-1.png index c9b5ca2fe5..25dab21adc 100644 Binary files a/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-different-text-leaves-decoration-1.png and b/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-different-text-leaves-decoration-1.png differ diff --git a/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-multiple-emoji-1.png b/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-multiple-emoji-1.png index c2bdd891a0..f0cf9b0454 100644 Binary files a/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-multiple-emoji-1.png and b/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-multiple-emoji-1.png differ diff --git a/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-texts-with-empty-lines-1.png b/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-texts-with-empty-lines-1.png index ca87b05546..4d75953095 100644 Binary files a/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-texts-with-empty-lines-1.png and b/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-texts-with-empty-lines-1.png differ diff --git a/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-texts-with-tabs-1.png b/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-texts-with-tabs-1.png index dee605a1b0..99945695ce 100644 Binary files a/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-texts-with-tabs-1.png and b/frontend/playwright/ui/render-wasm-specs/texts.spec.js-snapshots/Renders-a-file-with-texts-with-tabs-1.png differ diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index b8120054c7..ecab10cd26 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -912,7 +912,7 @@ (map :id) (run! f/update-text-layout))) -(defn process-pending +(defn process-pending! [shapes thumbnails full] (let [event (js/CustomEvent. "wasm:set-objects-finished") pending-thumbnails (-> (d/index-by :key :callback thumbnails) vals) @@ -920,11 +920,11 @@ (->> (rx/concat (->> (rx/from pending-thumbnails) (rx/merge-map (fn [callback] (callback))) - (rx/reduce conj []) - (rx/tap #(.dispatchEvent ^js js/document event))) + (rx/reduce conj [])) (->> (rx/from pending-full) (rx/mapcat (fn [callback] (callback))) - (rx/reduce conj []))) + (rx/reduce conj []) + (rx/tap #(.dispatchEvent ^js js/document event)))) (rx/subs! (fn [_] (update-text-layouts shapes) @@ -933,7 +933,7 @@ (defn process-object [shape] (let [{:keys [thumbnails full]} (set-object [] shape)] - (process-pending [shape] thumbnails full))) + (process-pending! [shape] thumbnails full))) (defn set-objects [objects] @@ -951,7 +951,7 @@ (into full-acc full))) {:thumbnails thumbnails-acc :full full-acc}))] (perf/end-measure "set-objects") - (process-pending shapes thumbnails full))) + (process-pending! shapes thumbnails full))) (defn clear-focus-mode [] diff --git a/frontend/src/app/render_wasm/api/fonts.cljs b/frontend/src/app/render_wasm/api/fonts.cljs index c8b18edc5e..efbbca5cc8 100644 --- a/frontend/src/app/render_wasm/api/fonts.cljs +++ b/frontend/src/app/render_wasm/api/fonts.cljs @@ -66,9 +66,10 @@ :custom (let [font-uuid (custom-font-id->uuid font-id) matching-font (d/seek (fn [[_ font]] - (and (= (:font-id font) font-uuid) - (or (nil? (:font-variant-id font)) - (= (:font-variant-id font) font-variant-id)))) + (let [variant-id (or (:font-variant-id font) (dm/str (:font-style font) "-" (:font-weight font)))] + (and (= (:font-id font) font-uuid) + (or (nil? font-variant-id) + (= variant-id font-variant-id))))) (seq @fonts))] (when matching-font (:ttf-file-id (second matching-font)))) @@ -85,7 +86,7 @@ (aget shape-id-buffer 2) (aget shape-id-buffer 3)))) -;; IMPORTANT: It should be noted that only TTF fonts can be stored. +;; IMPORTANT: Only TTF fonts can be stored. (defn- store-font-buffer [shape-id font-data font-array-buffer emoji? fallback?] (let [font-id-buffer (:family-id-buffer font-data) @@ -231,6 +232,7 @@ (store-font-id shape-id font-data asset-id emoji? fallback?))) + (defn store-fonts [shape-id fonts] (keep (fn [font] (store-font shape-id font)) fonts)) diff --git a/frontend/src/app/render_wasm/api/texts.cljs b/frontend/src/app/render_wasm/api/texts.cljs index 79eefd7668..b2698bf39b 100644 --- a/frontend/src/app/render_wasm/api/texts.cljs +++ b/frontend/src/app/render_wasm/api/texts.cljs @@ -198,20 +198,20 @@ :coptic #"[\u2C80-\u2CFF]" :ol-chiki #"[\u1C50-\u1C7F]" :vai #"[\uA500-\uA63F]" - :shavian #"[\u10450-\u1047F]" - :osmanya #"[\u10480-\u104AF]" + :shavian #"\uD801[\uDC50-\uDC7F]" + :osmanya #"\uD801[\uDC80-\uDCAF]" :runic #"[\u16A0-\u16FF]" - :old-italic #"[\u10300-\u1032F]" - :brahmi #"[\u11000-\u1107F]" - :modi #"[\u11600-\u1165F]" - :sora-sompeng #"[\u110D0-\u110FF]" + :old-italic #"\uD800[\uDF00-\uDF2F]" + :brahmi #"\uD804[\uDC00-\uDC7F]" + :modi #"\uD805[\uDE00-\uDE5F]" + :sora-sompeng #"\uD804[\uDCD0-\uDCFF]" :bamum #"[\uA6A0-\uA6FF]" - :meroitic #"[\u10980-\u1099F]" + :meroitic #"\uD802[\uDD80-\uDD9F]" ;; Arrows, Mathematical Operators, Misc Technical, Geometric Shapes, Misc Symbols, Dingbats, Supplemental Arrows, etc. :symbols #"[\u2190-\u21FF\u2200-\u22FF\u2300-\u23FF\u25A0-\u25FF\u2600-\u26FF\u2700-\u27BF\u2B00-\u2BFF]" ;; Additional arrows, math, technical, geometric, and symbol blocks :symbols-2 #"[\u2190-\u21FF\u2200-\u22FF\u2300-\u23FF\u25A0-\u25FF\u2600-\u26FF\u2700-\u27BF\u2B00-\u2BFF]" - :music #"[\u2669-\u267B\u1D100-\u1D1FF]"}) + :music #"[\u2669-\u267B]|\uD834[\uDD00-\uDD1F]"}) (defn contains-emoji? [text] (let [result (re-find emoji-pattern text)] diff --git a/frontend/src/app/render_wasm/shape.cljs b/frontend/src/app/render_wasm/shape.cljs index c2e27e408d..0887a0e2ac 100644 --- a/frontend/src/app/render_wasm/shape.cljs +++ b/frontend/src/app/render_wasm/shape.cljs @@ -187,8 +187,12 @@ (api/set-shape-svg-raw-content (api/get-static-markup shape)) (= (:type shape) :text) - (do (api/set-shape-text-content id v) - (api/set-shape-text-images id v))) + (let [pending-thumbnails (into [] (concat (api/set-shape-text-content id v))) + pending-full (into [] (concat (api/set-shape-text-images id v)))] + ;; FIXME: this is a hack to process the pending tasks asynchronously + ;; we should probably modify set-wasm-attr! to return a list of callbacks to be executed in a second pass. + (api/process-pending! [shape] pending-thumbnails pending-full) + nil)) :grow-type (api/set-shape-grow-type v) diff --git a/render-wasm/src/render/text.rs b/render-wasm/src/render/text.rs index 5ca7a4cfdf..f8ba8a0c8b 100644 --- a/render-wasm/src/render/text.rs +++ b/render-wasm/src/render/text.rs @@ -229,7 +229,7 @@ fn draw_text( paragraph.paint(canvas, xy); if paragraph_index == group_len - 1 { - group_offset_y += paragraph.height(); + group_offset_y += paragraph.ideographic_baseline(); } for line_metrics in paragraph.get_line_metrics().iter() {