mirror of
https://github.com/penpot/penpot.git
synced 2025-06-14 13:51:38 +02:00
Merge pull request #6609 from penpot/elenatorro-11213-fix-language-font-fallback
🎉 Implement font fallback to support multiple languages
This commit is contained in:
commit
5a7d9e3f18
8 changed files with 180 additions and 39 deletions
|
@ -614,7 +614,8 @@
|
||||||
(let [paragraph-set (first (dm/get-prop content :children))
|
(let [paragraph-set (first (dm/get-prop content :children))
|
||||||
paragraphs (dm/get-prop paragraph-set :children)
|
paragraphs (dm/get-prop paragraph-set :children)
|
||||||
fonts (fonts/get-content-fonts content)
|
fonts (fonts/get-content-fonts content)
|
||||||
emoji? (atom false)]
|
emoji? (atom false)
|
||||||
|
languages (atom #{})]
|
||||||
(loop [index 0]
|
(loop [index 0]
|
||||||
(when (< index (count paragraphs))
|
(when (< index (count paragraphs))
|
||||||
(let [paragraph (nth paragraphs index)
|
(let [paragraph (nth paragraphs index)
|
||||||
|
@ -623,12 +624,14 @@
|
||||||
(let [text (apply str (map :text leaves))]
|
(let [text (apply str (map :text leaves))]
|
||||||
(when (and (not @emoji?) (t/contains-emoji? text))
|
(when (and (not @emoji?) (t/contains-emoji? text))
|
||||||
(reset! emoji? true))
|
(reset! emoji? true))
|
||||||
|
(swap! languages into (t/get-languages text))
|
||||||
(t/write-shape-text leaves paragraph text))
|
(t/write-shape-text leaves paragraph text))
|
||||||
(recur (inc index))))))
|
(recur (inc index))))))
|
||||||
(let [fonts (if @emoji?
|
(let [updated-fonts
|
||||||
(f/add-emoji-font fonts)
|
(-> fonts
|
||||||
fonts)]
|
(cond-> emoji? (f/add-emoji-font))
|
||||||
(f/store-fonts fonts))))
|
(f/add-noto-fonts @languages))]
|
||||||
|
(f/store-fonts updated-fonts))))
|
||||||
|
|
||||||
(defn set-shape-text
|
(defn set-shape-text
|
||||||
[content]
|
[content]
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
[app.render-wasm.helpers :as h]
|
[app.render-wasm.helpers :as h]
|
||||||
[app.render-wasm.wasm :as wasm]
|
[app.render-wasm.wasm :as wasm]
|
||||||
[app.util.http :as http]
|
[app.util.http :as http]
|
||||||
[app.util.webapi :as wapi]
|
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[goog.object :as gobj]
|
[goog.object :as gobj]
|
||||||
|
@ -77,7 +76,7 @@
|
||||||
|
|
||||||
;; IMPORTANT: It should be noted that only TTF fonts can be stored.
|
;; IMPORTANT: It should be noted that only TTF fonts can be stored.
|
||||||
(defn- store-font-buffer
|
(defn- store-font-buffer
|
||||||
[font-data font-array-buffer emoji?]
|
[font-data font-array-buffer emoji? fallback?]
|
||||||
(let [id-buffer (:family-id-buffer font-data)
|
(let [id-buffer (:family-id-buffer font-data)
|
||||||
size (.-byteLength font-array-buffer)
|
size (.-byteLength font-array-buffer)
|
||||||
ptr (h/call wasm/internal-module "_alloc_bytes" size)
|
ptr (h/call wasm/internal-module "_alloc_bytes" size)
|
||||||
|
@ -91,17 +90,17 @@
|
||||||
(aget id-buffer 3)
|
(aget id-buffer 3)
|
||||||
(:weight font-data)
|
(:weight font-data)
|
||||||
(:style font-data)
|
(:style font-data)
|
||||||
emoji?)
|
emoji?
|
||||||
|
fallback?)
|
||||||
true))
|
true))
|
||||||
|
|
||||||
(defn- store-font-url
|
(defn- store-font-url
|
||||||
[font-data font-url emoji?]
|
[font-data font-url emoji? fallback?]
|
||||||
(->> (http/send! {:method :get
|
(->> (http/send! {:method :get
|
||||||
:uri font-url
|
:uri font-url
|
||||||
:response-type :blob})
|
:response-type :buffer})
|
||||||
(rx/map :body)
|
(rx/map (fn [{:keys [body]}]
|
||||||
(rx/mapcat wapi/read-file-as-array-buffer)
|
(store-font-buffer font-data body emoji? fallback?)))))
|
||||||
(rx/map (fn [array-buffer] (store-font-buffer font-data array-buffer emoji?)))))
|
|
||||||
|
|
||||||
(defn- google-font-ttf-url
|
(defn- google-font-ttf-url
|
||||||
[font-id font-variant-id]
|
[font-id font-variant-id]
|
||||||
|
@ -121,7 +120,7 @@
|
||||||
(dm/str (u/join cf/public-uri "fonts/" asset-id))))
|
(dm/str (u/join cf/public-uri "fonts/" asset-id))))
|
||||||
|
|
||||||
(defn- store-font-id
|
(defn- store-font-id
|
||||||
[font-data asset-id emoji?]
|
[font-data asset-id emoji? fallback?]
|
||||||
(when asset-id
|
(when asset-id
|
||||||
(let [uri (font-id->ttf-url (:font-id font-data) asset-id (:font-variant-id font-data))
|
(let [uri (font-id->ttf-url (:font-id font-data) asset-id (:font-variant-id font-data))
|
||||||
id-buffer (uuid/get-u32 (:wasm-id font-data))
|
id-buffer (uuid/get-u32 (:wasm-id font-data))
|
||||||
|
@ -133,7 +132,8 @@
|
||||||
(aget id-buffer 3)
|
(aget id-buffer 3)
|
||||||
(:weight font-data)
|
(:weight font-data)
|
||||||
(:style font-data)))]
|
(:style font-data)))]
|
||||||
(when-not font-stored? (store-font-url font-data uri emoji?)))))
|
(when-not font-stored?
|
||||||
|
(store-font-url font-data uri emoji? fallback?)))))
|
||||||
|
|
||||||
(defn serialize-font-style
|
(defn serialize-font-style
|
||||||
[font-style]
|
[font-style]
|
||||||
|
@ -165,13 +165,13 @@
|
||||||
|
|
||||||
(defn store-font
|
(defn store-font
|
||||||
[font]
|
[font]
|
||||||
(let [font-id (dm/get-prop font :font-id)
|
(let [font-id (get font :font-id)
|
||||||
font-variant-id (dm/get-prop font :font-variant-id)
|
font-variant-id (get font :font-variant-id)
|
||||||
|
emoji? (get font :is-emoji false)
|
||||||
|
fallback? (get font :is-fallback false)
|
||||||
wasm-id (font-id->uuid font-id)
|
wasm-id (font-id->uuid font-id)
|
||||||
raw-weight (or (:weight (font-db-data font-id font-variant-id)) 400)
|
raw-weight (or (:weight (font-db-data font-id font-variant-id)) 400)
|
||||||
|
|
||||||
weight (serialize-font-weight raw-weight)
|
weight (serialize-font-weight raw-weight)
|
||||||
|
|
||||||
style (serialize-font-style (cond
|
style (serialize-font-style (cond
|
||||||
(str/includes? font-variant-id "italic") "italic"
|
(str/includes? font-variant-id "italic") "italic"
|
||||||
:else "normal"))
|
:else "normal"))
|
||||||
|
@ -180,9 +180,8 @@
|
||||||
:font-id font-id
|
:font-id font-id
|
||||||
:font-variant-id font-variant-id
|
:font-variant-id font-variant-id
|
||||||
:style style
|
:style style
|
||||||
:weight weight}
|
:weight weight}]
|
||||||
emoji? (dm/get-prop font :emoji?)]
|
(store-font-id font-data asset-id emoji? fallback?)))
|
||||||
(store-font-id font-data asset-id emoji?)))
|
|
||||||
|
|
||||||
(defn store-fonts
|
(defn store-fonts
|
||||||
[fonts]
|
[fonts]
|
||||||
|
@ -195,4 +194,59 @@
|
||||||
:font-variant-id "regular"
|
:font-variant-id "regular"
|
||||||
:style 0
|
:style 0
|
||||||
:weight 400
|
:weight 400
|
||||||
:emoji? true}))
|
:is-emoji true}))
|
||||||
|
|
||||||
|
(def noto-fonts
|
||||||
|
{:japanese {:font-id "gfont-noto-sans-jp" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:chinese {:font-id "gfont-noto-sans-sc" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:korean {:font-id "gfont-noto-sans-kr" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:arabic {:font-id "gfont-noto-sans-arabic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:cyrillic {:font-id "gfont-noto-sans-cyrillic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:greek {:font-id "gfont-noto-sans-greek" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:hebrew {:font-id "gfont-noto-sans-hebrew" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:thai {:font-id "gfont-noto-sans-thai" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:devanagari {:font-id "gfont-noto-sans-devanagari" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:tamil {:font-id "gfont-noto-sans-tamil" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:latin-ext {:font-id "gfont-noto-sans" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:vietnamese {:font-id "gfont-noto-sans" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:armenian {:font-id "gfont-noto-sans-armenian" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:bengali {:font-id "gfont-noto-sans-bengali" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:cherokee {:font-id "gfont-noto-sans-cherokee" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:ethiopic {:font-id "gfont-noto-sans-ethiopic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:georgian {:font-id "gfont-noto-sans-georgian" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:gujarati {:font-id "gfont-noto-sans-gujarati" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:gurmukhi {:font-id "gfont-noto-sans-gurmukhi" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:khmer {:font-id "gfont-noto-sans-khmer" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:lao {:font-id "gfont-noto-sans-lao" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:malayalam {:font-id "gfont-noto-sans-malayalam" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:myanmar {:font-id "gfont-noto-sans-myanmar" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:sinhala {:font-id "gfont-noto-sans-sinhala" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:telugu {:font-id "gfont-noto-sans-telugu" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:tibetan {:font-id "gfont-noto-sans-tibetan" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:javanese {:font-id "noto-sans-javanese" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:kannada {:font-id "noto-sans-kannada" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:oriya {:font-id "noto-sans-oriya" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:mongolian {:font-id "noto-sans-mongolian" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:syriac {:font-id "noto-sans-syriac" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:tifinagh {:font-id "noto-sans-tifinagh" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:coptic {:font-id "noto-sans-coptic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:ol-chiki {:font-id "noto-sans-ol-chiki" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:vai {:font-id "noto-sans-vai" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:shavian {:font-id "noto-sans-shavian" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:osmanya {:font-id "noto-sans-osmanya" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:runic {:font-id "noto-sans-runic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:old-italic {:font-id "noto-sans-old-italic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:brahmi {:font-id "noto-sans-brahmi" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:modi {:font-id "noto-sans-modi" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:sora-sompeng {:font-id "noto-sans-sora-sompeng" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:bamum {:font-id "noto-sans-bamum" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||||
|
:meroitic {:font-id "noto-sans-meroitic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}})
|
||||||
|
|
||||||
|
|
||||||
|
(defn add-noto-fonts [fonts languages]
|
||||||
|
(reduce (fn [acc lang]
|
||||||
|
(if-let [font (get noto-fonts lang)]
|
||||||
|
(conj acc font)
|
||||||
|
acc))
|
||||||
|
fonts
|
||||||
|
languages))
|
||||||
|
|
|
@ -148,7 +148,62 @@
|
||||||
|
|
||||||
(h/call wasm/internal-module "_set_shape_text_content"))
|
(h/call wasm/internal-module "_set_shape_text_content"))
|
||||||
|
|
||||||
(def emoji-pattern #"[\uD83C-\uDBFF][\uDC00-\uDFFF]")
|
(def ^:private emoji-pattern #"[\uD83C-\uDBFF][\uDC00-\uDFFF]")
|
||||||
|
|
||||||
(defn contains-emoji? [s]
|
(def ^:private unicode-ranges
|
||||||
(boolean (re-find emoji-pattern s)))
|
{:japanese #"[\u3040-\u30FF\u31F0-\u31FF\uFF66-\uFF9F]"
|
||||||
|
:chinese #"[\u4E00-\u9FFF\u3400-\u4DBF]"
|
||||||
|
:korean #"[\uAC00-\uD7AF]"
|
||||||
|
:arabic #"[\u0600-\u06FF\u0750-\u077F\u0870-\u089F\u08A0-\u08FF]"
|
||||||
|
:cyrillic #"[\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F]"
|
||||||
|
:greek #"[\u0370-\u03FF\u1F00-\u1FFF]"
|
||||||
|
:hebrew #"[\u0590-\u05FF\uFB1D-\uFB4F]"
|
||||||
|
:thai #"[\u0E00-\u0E7F]"
|
||||||
|
:devanagari #"[\u0900-\u097F\uA8E0-\uA8FF]"
|
||||||
|
:tamil #"[\u0B80-\u0BFF]"
|
||||||
|
:latin-ext #"[\u0100-\u017F\u0180-\u024F]"
|
||||||
|
:vietnamese #"[\u1EA0-\u1EF9]"
|
||||||
|
:armenian #"[\u0530-\u058F\uFB13-\uFB17]"
|
||||||
|
:bengali #"[\u0980-\u09FF]"
|
||||||
|
:cherokee #"[\u13A0-\u13FF]"
|
||||||
|
:ethiopic #"[\u1200-\u137F]"
|
||||||
|
:georgian #"[\u10A0-\u10FF]"
|
||||||
|
:gujarati #"[\u0A80-\u0AFF]"
|
||||||
|
:gurmukhi #"[\u0A00-\u0A7F]"
|
||||||
|
:khmer #"[\u1780-\u17FF\u19E0-\u19FF]"
|
||||||
|
:lao #"[\u0E80-\u0EFF]"
|
||||||
|
:malayalam #"[\u0D00-\u0D7F]"
|
||||||
|
:myanmar #"[\u1000-\u109F\uAA60-\uAA7F]"
|
||||||
|
:sinhala #"[\u0D80-\u0DFF]"
|
||||||
|
:telugu #"[\u0C00-\u0C7F]"
|
||||||
|
:tibetan #"[\u0F00-\u0FFF]"
|
||||||
|
:javanese #"[\uA980-\uA9DF]"
|
||||||
|
:kannada #"[\u0C80-\u0CFF]"
|
||||||
|
:oriya #"[\u0B00-\u0B7F]"
|
||||||
|
:mongolian #"[\u1800-\u18AF]"
|
||||||
|
:syriac #"[\u0700-\u074F]"
|
||||||
|
:tifinagh #"[\u2D30-\u2D7F]"
|
||||||
|
:coptic #"[\u2C80-\u2CFF]"
|
||||||
|
:ol-chiki #"[\u1C50-\u1C7F]"
|
||||||
|
:vai #"[\uA500-\uA63F]"
|
||||||
|
:shavian #"[\u10450-\u1047F]"
|
||||||
|
:osmanya #"[\u10480-\u104AF]"
|
||||||
|
:runic #"[\u16A0-\u16FF]"
|
||||||
|
:old-italic #"[\u10300-\u1032F]"
|
||||||
|
:brahmi #"[\u11000-\u1107F]"
|
||||||
|
:modi #"[\u11600-\u1165F]"
|
||||||
|
:sora-sompeng #"[\u110D0-\u110FF]"
|
||||||
|
:bamum #"[\uA6A0-\uA6FF]"
|
||||||
|
:meroitic #"[\u10980-\u1099F]"})
|
||||||
|
|
||||||
|
|
||||||
|
(defn contains-emoji? [text]
|
||||||
|
(boolean (re-find emoji-pattern text)))
|
||||||
|
|
||||||
|
(defn get-languages [text]
|
||||||
|
(reduce-kv (fn [result lang pattern]
|
||||||
|
(if (re-find pattern text)
|
||||||
|
(conj result lang)
|
||||||
|
result))
|
||||||
|
#{}
|
||||||
|
unicode-ranges))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use skia_safe::{self as skia, textlayout, Font, FontMgr};
|
use skia_safe::{self as skia, textlayout, Font, FontMgr};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::shapes::{FontFamily, FontStyle};
|
use crate::shapes::{FontFamily, FontStyle};
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
|
@ -21,6 +22,7 @@ pub struct FontStore {
|
||||||
font_provider: textlayout::TypefaceFontProvider,
|
font_provider: textlayout::TypefaceFontProvider,
|
||||||
font_collection: textlayout::FontCollection,
|
font_collection: textlayout::FontCollection,
|
||||||
debug_font: Font,
|
debug_font: Font,
|
||||||
|
fallback_fonts: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontStore {
|
impl FontStore {
|
||||||
|
@ -41,6 +43,7 @@ impl FontStore {
|
||||||
font_provider,
|
font_provider,
|
||||||
font_collection,
|
font_collection,
|
||||||
debug_font,
|
debug_font,
|
||||||
|
fallback_fonts: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +64,7 @@ impl FontStore {
|
||||||
family: FontFamily,
|
family: FontFamily,
|
||||||
font_data: &[u8],
|
font_data: &[u8],
|
||||||
is_emoji: bool,
|
is_emoji: bool,
|
||||||
|
is_fallback: bool,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if self.has_family(&family) {
|
if self.has_family(&family) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -80,6 +84,11 @@ impl FontStore {
|
||||||
|
|
||||||
self.font_provider.register_typeface(typeface, font_name);
|
self.font_provider.register_typeface(typeface, font_name);
|
||||||
self.font_collection.clear_caches();
|
self.font_collection.clear_caches();
|
||||||
|
|
||||||
|
if is_fallback {
|
||||||
|
self.fallback_fonts.insert(alias);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +96,10 @@ impl FontStore {
|
||||||
let serialized = format!("{}", family);
|
let serialized = format!("{}", family);
|
||||||
self.font_provider.family_names().any(|x| x == serialized)
|
self.font_provider.family_names().any(|x| x == serialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_fallback(&self) -> &HashSet<String> {
|
||||||
|
&self.fallback_fonts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_default_provider(font_mgr: &FontMgr) -> skia::textlayout::TypefaceFontProvider {
|
fn load_default_provider(font_mgr: &FontMgr) -> skia::textlayout::TypefaceFontProvider {
|
||||||
|
|
|
@ -240,7 +240,7 @@ pub fn merge_fills(fills: &[Fill], bounding_box: Rect) -> skia::Paint {
|
||||||
if let Some(shader) = shader {
|
if let Some(shader) = shader {
|
||||||
combined_shader = match combined_shader {
|
combined_shader = match combined_shader {
|
||||||
Some(existing_shader) => Some(skia::shaders::blend(
|
Some(existing_shader) => Some(skia::shaders::blend(
|
||||||
skia::Blender::mode(skia::BlendMode::Overlay),
|
skia::Blender::mode(skia::BlendMode::DstOver),
|
||||||
existing_shader,
|
existing_shader,
|
||||||
shader,
|
shader,
|
||||||
)),
|
)),
|
||||||
|
|
|
@ -7,10 +7,11 @@ use skia_safe::{
|
||||||
paint::Paint,
|
paint::Paint,
|
||||||
textlayout::{FontCollection, ParagraphBuilder, ParagraphStyle},
|
textlayout::{FontCollection, ParagraphBuilder, ParagraphStyle},
|
||||||
};
|
};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::FontFamily;
|
use super::FontFamily;
|
||||||
use crate::shapes::{self, merge_fills, set_paint_fill, Stroke, StrokeKind};
|
use crate::shapes::{self, merge_fills, set_paint_fill, Stroke, StrokeKind};
|
||||||
use crate::utils::uuid_from_u32;
|
use crate::utils::{get_fallback_fonts, uuid_from_u32};
|
||||||
use crate::wasm::fills::parse_fills_from_bytes;
|
use crate::wasm::fills::parse_fills_from_bytes;
|
||||||
use crate::Uuid;
|
use crate::Uuid;
|
||||||
|
|
||||||
|
@ -94,6 +95,7 @@ impl TextContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_paragraphs(&self, fonts: &FontCollection) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
pub fn to_paragraphs(&self, fonts: &FontCollection) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||||
|
let fallback_fonts = get_fallback_fonts();
|
||||||
let mut paragraph_group = Vec::new();
|
let mut paragraph_group = Vec::new();
|
||||||
let paragraphs = self
|
let paragraphs = self
|
||||||
.paragraphs
|
.paragraphs
|
||||||
|
@ -102,7 +104,7 @@ impl TextContent {
|
||||||
let paragraph_style = p.paragraph_to_style();
|
let paragraph_style = p.paragraph_to_style();
|
||||||
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
||||||
for leaf in &p.children {
|
for leaf in &p.children {
|
||||||
let text_style = leaf.to_style(p, &self.bounds); // FIXME
|
let text_style = leaf.to_style(p, &self.bounds, fallback_fonts); // FIXME
|
||||||
let text = leaf.apply_text_transform();
|
let text = leaf.apply_text_transform();
|
||||||
builder.push_style(&text_style);
|
builder.push_style(&text_style);
|
||||||
builder.add_text(&text);
|
builder.add_text(&text);
|
||||||
|
@ -121,6 +123,7 @@ impl TextContent {
|
||||||
bounds: &Rect,
|
bounds: &Rect,
|
||||||
fonts: &FontCollection,
|
fonts: &FontCollection,
|
||||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||||
|
let fallback_fonts = get_fallback_fonts();
|
||||||
let mut paragraph_group = Vec::new();
|
let mut paragraph_group = Vec::new();
|
||||||
let stroke_paints = get_text_stroke_paints(stroke, bounds);
|
let stroke_paints = get_text_stroke_paints(stroke, bounds);
|
||||||
|
|
||||||
|
@ -130,7 +133,8 @@ impl TextContent {
|
||||||
let paragraph_style = paragraph.paragraph_to_style();
|
let paragraph_style = paragraph.paragraph_to_style();
|
||||||
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
||||||
for leaf in ¶graph.children {
|
for leaf in ¶graph.children {
|
||||||
let stroke_style = leaf.to_stroke_style(paragraph, &stroke_paint);
|
let stroke_style =
|
||||||
|
leaf.to_stroke_style(paragraph, &stroke_paint, fallback_fonts);
|
||||||
let text: String = leaf.apply_text_transform();
|
let text: String = leaf.apply_text_transform();
|
||||||
builder.push_style(&stroke_style);
|
builder.push_style(&stroke_style);
|
||||||
builder.add_text(&text);
|
builder.add_text(&text);
|
||||||
|
@ -336,6 +340,7 @@ impl TextLeaf {
|
||||||
&self,
|
&self,
|
||||||
paragraph: &Paragraph,
|
paragraph: &Paragraph,
|
||||||
content_bounds: &Rect,
|
content_bounds: &Rect,
|
||||||
|
fallback_fonts: &HashSet<String>,
|
||||||
) -> skia::textlayout::TextStyle {
|
) -> skia::textlayout::TextStyle {
|
||||||
let mut style = skia::textlayout::TextStyle::default();
|
let mut style = skia::textlayout::TextStyle::default();
|
||||||
|
|
||||||
|
@ -359,14 +364,18 @@ impl TextLeaf {
|
||||||
3 => skia::textlayout::TextDecoration::OVERLINE,
|
3 => skia::textlayout::TextDecoration::OVERLINE,
|
||||||
_ => skia::textlayout::TextDecoration::NO_DECORATION,
|
_ => skia::textlayout::TextDecoration::NO_DECORATION,
|
||||||
});
|
});
|
||||||
// FIXME
|
|
||||||
|
// FIXME fix decoration styles
|
||||||
style.set_decoration_color(paint.color());
|
style.set_decoration_color(paint.color());
|
||||||
|
|
||||||
style.set_font_families(&[
|
let mut font_families = vec![
|
||||||
self.serialized_font_family(),
|
self.serialized_font_family(),
|
||||||
default_font(),
|
default_font(),
|
||||||
DEFAULT_EMOJI_FONT.to_string(),
|
DEFAULT_EMOJI_FONT.to_string(),
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
font_families.extend(fallback_fonts.iter().cloned());
|
||||||
|
style.set_font_families(&font_families);
|
||||||
|
|
||||||
style
|
style
|
||||||
}
|
}
|
||||||
|
@ -375,8 +384,9 @@ impl TextLeaf {
|
||||||
&self,
|
&self,
|
||||||
paragraph: &Paragraph,
|
paragraph: &Paragraph,
|
||||||
stroke_paint: &Paint,
|
stroke_paint: &Paint,
|
||||||
|
fallback_fonts: &HashSet<String>,
|
||||||
) -> skia::textlayout::TextStyle {
|
) -> skia::textlayout::TextStyle {
|
||||||
let mut style = self.to_style(paragraph, &Rect::default());
|
let mut style = self.to_style(paragraph, &Rect::default(), fallback_fonts);
|
||||||
style.set_foreground_paint(stroke_paint);
|
style.set_foreground_paint(stroke_paint);
|
||||||
style.set_font_size(self.font_size);
|
style.set_font_size(self.font_size);
|
||||||
style.set_letter_spacing(paragraph.letter_spacing);
|
style.set_letter_spacing(paragraph.letter_spacing);
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::skia::Image;
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
use crate::with_state;
|
use crate::with_state;
|
||||||
use crate::STATE;
|
use crate::STATE;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
pub fn uuid_from_u32_quartet(a: u32, b: u32, c: u32, d: u32) -> Uuid {
|
pub fn uuid_from_u32_quartet(a: u32, b: u32, c: u32, d: u32) -> Uuid {
|
||||||
let hi: u64 = ((a as u64) << 32) | b as u64;
|
let hi: u64 = ((a as u64) << 32) | b as u64;
|
||||||
|
@ -25,3 +26,8 @@ pub fn uuid_from_u32(id: [u32; 4]) -> Uuid {
|
||||||
pub fn get_image(image_id: &Uuid) -> Option<&Image> {
|
pub fn get_image(image_id: &Uuid) -> Option<&Image> {
|
||||||
with_state!(state, { state.render_state().images.get(image_id) })
|
with_state!(state, { state.render_state().images.get(image_id) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: move to a different place ?
|
||||||
|
pub fn get_fallback_fonts() -> &'static HashSet<String> {
|
||||||
|
with_state!(state, { state.render_state().fonts().get_fallback() })
|
||||||
|
}
|
||||||
|
|
|
@ -14,19 +14,19 @@ pub extern "C" fn store_font(
|
||||||
weight: u32,
|
weight: u32,
|
||||||
style: u8,
|
style: u8,
|
||||||
is_emoji: bool,
|
is_emoji: bool,
|
||||||
|
is_fallback: bool,
|
||||||
) {
|
) {
|
||||||
with_state!(state, {
|
with_state!(state, {
|
||||||
let id = uuid_from_u32_quartet(a, b, c, d);
|
let id = uuid_from_u32_quartet(a, b, c, d);
|
||||||
let font_bytes = mem::bytes();
|
let font_bytes = mem::bytes();
|
||||||
|
|
||||||
let family = FontFamily::new(id, weight, style.into());
|
let family = FontFamily::new(id, weight, style.into());
|
||||||
let res = state
|
let _ = state
|
||||||
.render_state()
|
.render_state()
|
||||||
.fonts_mut()
|
.fonts_mut()
|
||||||
.add(family, &font_bytes, is_emoji);
|
.add(family, &font_bytes, is_emoji, is_fallback);
|
||||||
|
|
||||||
if let Err(msg) = res {
|
mem::free_bytes();
|
||||||
eprintln!("{}", msg);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue