🐛 Prefer nonblank font name candidates on upload

A name record can be present but blank. In that case the blank
preferredFamily/preferredSubfamily shadowed a valid
fontFamily/fontSubfamily, so the metadata was discarded and the
filename fallback was used.

Select the first nonblank candidate with the new
first-nonblank-string helper before parsing, which keeps a valid
second candidate. The blank-candidate selection behaviour is adapted
from #11928 by CHOI JUNSOO.

Closes #10436

AI-assisted-by: muse-spark-1.3-contributor
Signed-off-by: CHOI JUNSOO <junsoo1172@gmail.com>
This commit is contained in:
CHOI JUNSOO 2026-09-28 14:09:59 +00:00 committed by Andrey Antukh
parent 273a968c62
commit d1cddb5312
4 changed files with 40 additions and 5 deletions

View File

@ -136,6 +136,11 @@
"italic"
"normal")))
(defn first-nonblank-string
"Return the first nonblank string from the candidates, or nil."
[& candidates]
(first (filter #(and (string? %) (not (str/blank? %))) candidates)))
(defn font-weight->name
[weight]
(case (long weight)

View File

@ -89,6 +89,18 @@
(t/testing "falls back to the base name when everything is stripped"
(t/is (= "BoldItalic" (media/parse-font-family "BoldItalic")))))
(t/deftest test-first-nonblank-string
(t/testing "returns the first nonblank string"
(t/is (= "Bold" (media/first-nonblank-string " " "Bold")))
(t/is (= "Bold" (media/first-nonblank-string "Bold" "Light")))
(t/is (= "Bold" (media/first-nonblank-string nil "Bold")))
(t/is (= "Inter" (media/first-nonblank-string "" "Inter"))))
(t/testing "returns nil when every candidate is blank or not a string"
(t/is (nil? (media/first-nonblank-string nil " ")))
(t/is (nil? (media/first-nonblank-string)))
(t/is (nil? (media/first-nonblank-string 42 " ")))))
(t/deftest test-strip-image-extension
(t/testing "removes extension from supported image files"
(t/is (= (media/strip-image-extension "foo.png") "foo"))

View File

@ -144,10 +144,15 @@
[{:keys [font type name data] :as _params}]
(if font
;; Font was parsed with opentype.js (ttf, otf, woff)
(let [family (or (.getEnglishName ^js font "preferredFamily")
(.getEnglishName ^js font "fontFamily"))
variant (or (.getEnglishName ^js font "preferredSubfamily")
(.getEnglishName ^js font "fontSubfamily"))
(let [;; Select the first nonblank name candidate: a blank
;; preferredFamily/preferredSubfamily must not shadow a valid
;; fontFamily/fontSubfamily.
family (cm/first-nonblank-string
(.getEnglishName ^js font "preferredFamily")
(.getEnglishName ^js font "fontFamily"))
variant (cm/first-nonblank-string
(.getEnglishName ^js font "preferredSubfamily")
(.getEnglishName ^js font "fontSubfamily"))
;; Vertical metrics determine the baseline in a text and the space between lines of
;; text. For historical reasons, there are three pairs of ascender/descender

View File

@ -142,4 +142,17 @@
(t/is (= "Pretendard" (:font-family result)))
(t/is (= 600 (:font-weight result)))
(t/is (= "normal" (:font-style result)))
(t/is (false? (:height-warning? result))))))
(t/is (false? (:height-warning? result)))))
(t/testing "a blank first candidate does not shadow a valid second one"
(let [result (df/prepare-font-entry
{:font (fake-font {"preferredFamily" " "
"preferredSubfamily" " "
"fontSubfamily" "Bold"})
:type "font/ttf"
:name "Roboto-Light.ttf"
:data (js/Uint8Array. 4)})]
(t/is (= "Roboto" (:font-family result)))
(t/is (= 700 (:font-weight result)))
(t/is (= "normal" (:font-style result)))
(t/is (= "Bold" (:variant-name result))))))