From 41d95abc5c1067690d1d5eb111b587055286d233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 20 Oct 2026 14:09:01 +0200 Subject: [PATCH] :bug: Fix math symbols rendered as boxes in the new render engine (#12471) Noto Sans Symbols lacks most arrows and math operators. Fall back to Noto Sans Math for them, and to Noto Sans for letterlike symbols. Closes #12469 AI-assisted-by: claude-opus-5-5 --- common/src/app/common/fonts.cljs | 11 +++++-- common/test/common_tests/fonts_test.cljs | 37 ++++++++++++++++++++++++ common/test/common_tests/runner.cljc | 2 ++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 common/test/common_tests/fonts_test.cljs diff --git a/common/src/app/common/fonts.cljs b/common/src/app/common/fonts.cljs index 237a6b362b..ee96b6c9df 100644 --- a/common/src/app/common/fonts.cljs +++ b/common/src/app/common/fonts.cljs @@ -213,7 +213,12 @@ ;; SMP: Mahjong/Domino/Playing Cards (U+1F000-1F0FF), Supplemental Arrows-C (U+1F800-1F8FF), ;; Legacy Computing Symbols (U+1FB00-1FBFF) :symbols-2 #"[\u2190-\u21FF\u2200-\u22FF\u2300-\u23FF\u25A0-\u25FF\u2600-\u26FF\u2700-\u27BF\u2B00-\u2BFF]|\uD83C[\uDC00-\uDCFF]|\uD83E[\uDC00-\uDCFF\uDF00-\uDFFF]" - :music #"[\u2669-\u267B]|\uD834[\uDD00-\uDD1F]"}) + :music #"[\u2669-\u267B]|\uD834[\uDD00-\uDD1F]" + ;; Arrows, Mathematical Operators, Misc Mathematical Symbols A/B, Supplemental + ;; Arrows A/B, Supplemental Math Operators: the Symbols fonts lack most of them + :math #"[\u2190-\u21FF\u2200-\u22FF\u27C0-\u27FF\u2900-\u2AFF]" + ;; Letterlike Symbols (ℝ, ℕ, ℃, ™, ...) + :letterlike #"[\u2100-\u214F]"}) (defn contains-emoji? [text] (let [result (re-find emoji-pattern text)] @@ -292,7 +297,9 @@ :meroitic {:font-id "gfont-noto-sans-meroitic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true} :symbols {:font-id "gfont-noto-sans-symbols" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true} :symbols-2 {:font-id "gfont-noto-sans-symbols-2" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true} - :music {:font-id "gfont-noto-music" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}}) + :music {:font-id "gfont-noto-music" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true} + :math {:font-id "gfont-noto-sans-math" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true} + :letterlike {:font-id "gfont-noto-sans" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}}) (defn add-noto-fonts [fonts languages] (reduce (fn [acc lang] diff --git a/common/test/common_tests/fonts_test.cljs b/common/test/common_tests/fonts_test.cljs new file mode 100644 index 0000000000..36786ccb7c --- /dev/null +++ b/common/test/common_tests/fonts_test.cljs @@ -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 SUBSIDIARY SL + +(ns common-tests.fonts-test + (:require + [app.common.fonts :as cfnt] + [clojure.test :as t])) + +(defn- fallback-font-ids + [text] + (->> (cfnt/collect-used-languages #{} text) + (cfnt/add-noto-fonts #{}) + (into #{} (map :font-id)))) + +(t/deftest math-symbols-use-noto-sans-math + (t/testing "arrows" + (t/is (contains? (fallback-font-ids "↳") "gfont-noto-sans-math")) + (t/is (contains? (fallback-font-ids "⇒") "gfont-noto-sans-math"))) + (t/testing "mathematical operators" + (t/is (contains? (fallback-font-ids "∀x ∈ A") "gfont-noto-sans-math")) + (t/is (contains? (fallback-font-ids "a ≠ b") "gfont-noto-sans-math"))) + (t/testing "supplemental arrows and operators" + (t/is (contains? (fallback-font-ids "⟹") "gfont-noto-sans-math")) + (t/is (contains? (fallback-font-ids "⤴") "gfont-noto-sans-math")) + (t/is (contains? (fallback-font-ids "⨁") "gfont-noto-sans-math")))) + +(t/deftest letterlike-symbols-use-noto-sans + (t/is (contains? (fallback-font-ids "x ∈ ℝ") "gfont-noto-sans")) + (t/is (contains? (fallback-font-ids "20 ℃") "gfont-noto-sans")) + (t/is (contains? (fallback-font-ids "Penpot™") "gfont-noto-sans"))) + +(t/deftest plain-text-does-not-use-noto-sans-math + (t/is (not (contains? (fallback-font-ids "Hello world") "gfont-noto-sans-math"))) + (t/is (not (contains? (fallback-font-ids "★ ☀") "gfont-noto-sans-math")))) diff --git a/common/test/common_tests/runner.cljc b/common/test/common_tests/runner.cljc index 7a4c4243f4..e78fbf3da9 100644 --- a/common/test/common_tests/runner.cljc +++ b/common/test/common_tests/runner.cljc @@ -10,6 +10,7 @@ #?(:cljs [app.common.logging :as l]) #?(:cljs [clojure.string :as str]) #?(:cljs [clojure.tools.cli :refer [parse-opts]]) + #?(:cljs [common-tests.fonts-test]) #?(:cljs [goog.object :as gobj]) [clojure.test :as t] [common-tests.attrs-test] @@ -123,6 +124,7 @@ 'common-tests.data-test 'common-tests.exceptions-test 'common-tests.files-builder-test + #?(:cljs 'common-tests.fonts-test) 'common-tests.files-changes-test 'common-tests.files-migrations-0025-test 'common-tests.files-migrations-0026-test