mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
✨ Choose closest font weight for token weight when no matching weight is found
This commit is contained in:
committed by
Andrés Moya
parent
31e37f352d
commit
09b9383a0b
@@ -333,10 +333,7 @@
|
||||
(txt/is-paragraph-node? node)))
|
||||
update-fn (fn [node _]
|
||||
(let [font (fonts/get-font-data (:font-id node))
|
||||
font-variant-id (or
|
||||
(fonts/find-variant font font-variant)
|
||||
;; When variant with matching weight but not with matching style (italic) is found, use that one
|
||||
(fonts/find-variant font (dissoc font-variant :style)))]
|
||||
font-variant-id (fonts/find-closest-variant font (:weight font-variant) (:style font-variant))]
|
||||
(if font-variant-id
|
||||
(-> node
|
||||
(d/txt-merge (assoc font-variant :font-variant-id (:id font-variant-id)))
|
||||
|
||||
@@ -270,6 +270,45 @@
|
||||
(let [props (keys variant-data)]
|
||||
(d/seek #(= (select-keys % props) variant-data) variants)))
|
||||
|
||||
(defn find-closest-variant
|
||||
"Find the closest font weight variant in `font` for `target-weight` with optional `target-style` match.
|
||||
When exactly between two weights, choose the higher one."
|
||||
[font target-weight target-style]
|
||||
(when-let [target-weight (d/parse-integer target-weight)]
|
||||
(let [variants (:variants font [])
|
||||
result
|
||||
(reduce
|
||||
(fn [closest-match variant]
|
||||
(let [weight (d/parse-integer (:weight variant))
|
||||
distance (abs (- target-weight weight))
|
||||
matches-style? (= target-style (:style variant))
|
||||
current {:variant variant
|
||||
:weight weight
|
||||
:distance distance}]
|
||||
(cond
|
||||
;; Exact match found
|
||||
(and (zero? distance)
|
||||
(if target-style matches-style? true))
|
||||
(reduced current)
|
||||
|
||||
(nil? closest-match) current
|
||||
|
||||
;; Update best match if this variant is closer or equal distance but higher weight
|
||||
(or (< distance (:distance closest-match))
|
||||
(and (= distance (:distance closest-match))
|
||||
(> weight (:weight closest-match))))
|
||||
current
|
||||
|
||||
;; Same weight as the `closest-match` but the style matches `target-style`
|
||||
(and (= weight (:weight closest-match)) matches-style?)
|
||||
current
|
||||
|
||||
:else
|
||||
closest-match)))
|
||||
nil
|
||||
variants)]
|
||||
(:variant result))))
|
||||
|
||||
;; Font embedding functions
|
||||
(defn get-node-fonts
|
||||
"Extracts the fonts used by some node"
|
||||
|
||||
126
frontend/test/frontend_tests/fonts_test.cljs
Normal file
126
frontend/test/frontend_tests/fonts_test.cljs
Normal file
@@ -0,0 +1,126 @@
|
||||
;; 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 INC
|
||||
|
||||
(ns frontend-tests.fonts-test
|
||||
(:require
|
||||
[app.main.fonts :as fonts]
|
||||
[cljs.test :as t :include-macros true]))
|
||||
|
||||
(def sample-font
|
||||
{:id "sourcesanspro"
|
||||
:name "Source Sans Pro"
|
||||
:family "sourcesanspro"
|
||||
:variants
|
||||
[{:id "200"
|
||||
:name "200"
|
||||
:weight "200"
|
||||
:style "normal"
|
||||
:suffix "extralight"
|
||||
:ttf-url "sourcesanspro-extralight.ttf"}
|
||||
{:id "200italic"
|
||||
:name "200 Italic"
|
||||
:weight "200"
|
||||
:style "italic"
|
||||
:suffix "extralightitalic"
|
||||
:ttf-url "sourcesanspro-extralightitalic.ttf"}
|
||||
{:id "300"
|
||||
:name "300"
|
||||
:weight "300"
|
||||
:style "normal"
|
||||
:suffix "light"
|
||||
:ttf-url "sourcesanspro-light.ttf"}
|
||||
{:id "300italic"
|
||||
:name "300 Italic"
|
||||
:weight "300"
|
||||
:style "italic"
|
||||
:suffix "lightitalic"
|
||||
:ttf-url "sourcesanspro-lightitalic.ttf"}
|
||||
{:id "regular"
|
||||
:name "400"
|
||||
:weight "400"
|
||||
:style "normal"
|
||||
:ttf-url "sourcesanspro-regular.ttf"}
|
||||
{:id "italic"
|
||||
:name "400 Italic"
|
||||
:weight "400"
|
||||
:style "italic"
|
||||
:ttf-url "sourcesanspro-italic.ttf"}
|
||||
{:id "bold"
|
||||
:name "700"
|
||||
:weight "700"
|
||||
:style "normal"
|
||||
:ttf-url "sourcesanspro-bold.ttf"}
|
||||
{:id "bolditalic"
|
||||
:name "700 Italic"
|
||||
:weight "700"
|
||||
:style "italic"
|
||||
:ttf-url "sourcesanspro-bolditalic.ttf"}
|
||||
{:id "black"
|
||||
:name "900"
|
||||
:weight "900"
|
||||
:style "normal"
|
||||
:ttf-url "sourcesanspro-black.ttf"}
|
||||
{:id "blackitalic"
|
||||
:name "900 Italic"
|
||||
:weight "900"
|
||||
:style "italic"
|
||||
:ttf-url "sourcesanspro-blackitalic.ttf"}]
|
||||
:backend :builtin})
|
||||
|
||||
(t/deftest find-closest-weight-variant-test
|
||||
(t/testing "finds exact weight match"
|
||||
(let [result (fonts/find-closest-variant sample-font "400" nil)]
|
||||
(t/is (= "400" (:weight result)))
|
||||
(t/is (= "normal" (:style result)))))
|
||||
|
||||
(t/testing "finds exact weight match with style"
|
||||
(let [result (fonts/find-closest-variant sample-font "400" "italic")]
|
||||
(t/is (= "400" (:weight result)))
|
||||
(t/is (= "italic" (:style result)))))
|
||||
|
||||
(t/testing "chooses higher weight when exactly between two weights"
|
||||
(let [result (fonts/find-closest-variant sample-font "350" nil)]
|
||||
(t/is (= "400" (:weight result)))))
|
||||
|
||||
(t/testing "finds exact weight match with style"
|
||||
(let [result (fonts/find-closest-variant sample-font "350" "italic")]
|
||||
(t/is (= "400" (:weight result)))
|
||||
(t/is (= "italic" (:style result)))))
|
||||
|
||||
(t/testing "finds closest weight below minimum available"
|
||||
(let [result (fonts/find-closest-variant sample-font "0" nil)]
|
||||
(t/is (= "200" (:weight result)))))
|
||||
|
||||
(t/testing "finds closest weight above maximum available"
|
||||
(let [result (fonts/find-closest-variant sample-font "1000" nil)]
|
||||
(t/is (= "900" (:weight result)))))
|
||||
|
||||
(t/testing "keeps the closest weight match when style is not found"
|
||||
(let [font {:id "sourcesanspro"
|
||||
:name "Source Sans Pro"
|
||||
:family "sourcesanspro"
|
||||
:variants
|
||||
[{:id "200italic"
|
||||
:name "200 Italic"
|
||||
:weight "200"
|
||||
:style "italic"
|
||||
:suffix "extralightitalic"
|
||||
:ttf-url "sourcesanspro-extralightitalic.ttf"}
|
||||
{:id "300"
|
||||
:name "300"
|
||||
:weight "300"
|
||||
:style "normal"
|
||||
:suffix "light"
|
||||
:ttf-url "sourcesanspro-light.ttf"}
|
||||
{:id "300italic"
|
||||
:name "300 Italic"
|
||||
:weight "300"
|
||||
:style "italic"
|
||||
:suffix "lightitalic"
|
||||
:ttf-url "sourcesanspro-lightitalic.ttf"}]}
|
||||
result (fonts/find-closest-variant font "200" nil)]
|
||||
(t/is (= "200" (:weight result)))
|
||||
(t/is (= "italic" (:style result))))))
|
||||
Reference in New Issue
Block a user