mirror of
https://github.com/penpot/penpot.git
synced 2025-06-01 16:21:39 +02:00
Merge pull request #6242 from penpot/ladybenko-10666-builtin-fonts
🎉 Load built-in font and its variants (wasm)
This commit is contained in:
commit
de8e27feb8
7 changed files with 97 additions and 54 deletions
|
@ -33,16 +33,16 @@
|
||||||
:name "Source Sans Pro"
|
:name "Source Sans Pro"
|
||||||
:family "sourcesanspro"
|
:family "sourcesanspro"
|
||||||
:variants
|
:variants
|
||||||
[{:id "200" :name "200" :weight "200" :style "normal" :suffix "extralight"}
|
[{: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"}
|
{: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"}
|
{: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"}
|
{:id "300italic" :name "300 (italic)" :weight "300" :style "italic" :suffix "lightitalic" :ttf-url "sourcesanspro-lightitalic.ttf"}
|
||||||
{:id "regular" :name "regular" :weight "400" :style "normal"}
|
{:id "regular" :name "regular" :weight "400" :style "normal" :ttf-url "sourcesanspro-regular.ttf"}
|
||||||
{:id "italic" :name "italic" :weight "400" :style "italic"}
|
{:id "italic" :name "italic" :weight "400" :style "italic" :ttf-url "sourcesanspro-italic.ttf"}
|
||||||
{:id "bold" :name "bold" :weight "bold" :style "normal"}
|
{:id "bold" :name "bold" :weight "bold" :style "normal" :ttf-url "sourcesanspro-bold.ttf"}
|
||||||
{:id "bolditalic" :name "bold (italic)" :weight "bold" :style "italic"}
|
{:id "bolditalic" :name "bold (italic)" :weight "bold" :style "italic" :ttf-url "sourcesanspro-bolditalic.ttf"}
|
||||||
{:id "black" :name "black" :weight "900" :style "normal"}
|
{:id "black" :name "black" :weight "900" :style "normal" :ttf-url "sourcesanspro-black.ttf"}
|
||||||
{:id "blackitalic" :name "black (italic)" :weight "900" :style "italic"}]}])
|
{:id "blackitalic" :name "black (italic)" :weight "900" :style "italic" :ttf-url "sourcesanspro-blackitalic.ttf"}]}])
|
||||||
|
|
||||||
(defonce fontsdb (l/atom {}))
|
(defonce fontsdb (l/atom {}))
|
||||||
(defonce fonts (l/atom []))
|
(defonce fonts (l/atom []))
|
||||||
|
@ -253,7 +253,6 @@
|
||||||
|
|
||||||
(defn get-variant
|
(defn get-variant
|
||||||
[{:keys [variants] :as font} font-variant-id]
|
[{:keys [variants] :as font} font-variant-id]
|
||||||
(prn "get-variant" font-variant-id fonts)
|
|
||||||
(or (d/seek #(= (:id %) font-variant-id) variants)
|
(or (d/seek #(= (:id %) font-variant-id) variants)
|
||||||
(get-default-variant font)))
|
(get-default-variant font)))
|
||||||
|
|
||||||
|
|
|
@ -34,20 +34,46 @@
|
||||||
[font-id]
|
[font-id]
|
||||||
(uuid/uuid (subs font-id (inc (str/index-of font-id "-")))))
|
(uuid/uuid (subs font-id (inc (str/index-of font-id "-")))))
|
||||||
|
|
||||||
(defn- font-id->uuid [font-id]
|
(defn- font-backend
|
||||||
(if (str/starts-with? font-id "gfont-")
|
[font-id]
|
||||||
(google-font-id->uuid font-id)
|
(cond
|
||||||
(custom-font-id->uuid font-id)))
|
(str/starts-with? font-id "gfont-")
|
||||||
|
:google
|
||||||
|
(str/starts-with? font-id "custom-")
|
||||||
|
:custom
|
||||||
|
:else
|
||||||
|
:builtin))
|
||||||
|
|
||||||
(defn ^:private font-id->ttf-id [font-id font-variant-id]
|
(defn- font-db-data
|
||||||
(if (str/starts-with? font-id "gfont-") font-id
|
[font-id font-variant-id]
|
||||||
(let [font-uuid (custom-font-id->uuid font-id)
|
(let [font (fonts/get-font-data font-id)
|
||||||
matching-font (d/seek (fn [[_ font]]
|
variant (fonts/get-variant font font-variant-id)]
|
||||||
(and (= (:font-id font) font-uuid)
|
variant))
|
||||||
(= (:font-variant-id font) font-variant-id)))
|
|
||||||
(seq @fonts))]
|
(defn- font-id->uuid [font-id]
|
||||||
(when matching-font
|
(case (font-backend font-id)
|
||||||
(:ttf-file-id (second matching-font))))))
|
:google
|
||||||
|
(google-font-id->uuid font-id)
|
||||||
|
:custom
|
||||||
|
(custom-font-id->uuid font-id)
|
||||||
|
:builtin
|
||||||
|
uuid/zero))
|
||||||
|
|
||||||
|
(defn ^:private font-id->asset-id [font-id font-variant-id]
|
||||||
|
(case (font-backend font-id)
|
||||||
|
:google
|
||||||
|
font-id
|
||||||
|
:custom
|
||||||
|
(let [font-uuid (custom-font-id->uuid font-id)
|
||||||
|
matching-font (d/seek (fn [[_ font]]
|
||||||
|
(and (= (:font-id font) font-uuid)
|
||||||
|
(= (:font-variant-id font) font-variant-id)))
|
||||||
|
(seq @fonts))]
|
||||||
|
(when matching-font
|
||||||
|
(:ttf-file-id (second matching-font))))
|
||||||
|
:builtin
|
||||||
|
(let [variant (font-db-data font-id font-variant-id)]
|
||||||
|
(:ttf-url variant))))
|
||||||
|
|
||||||
;; 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
|
||||||
|
@ -78,26 +104,25 @@
|
||||||
|
|
||||||
(defn- google-font-ttf-url
|
(defn- google-font-ttf-url
|
||||||
[font-id font-variant-id]
|
[font-id font-variant-id]
|
||||||
(let [font (fonts/get-font-data font-id)
|
(let [variant (font-db-data font-id font-variant-id)]
|
||||||
variant (fonts/get-variant font font-variant-id)]
|
|
||||||
(if-let [ttf-url (:ttf-url variant)]
|
(if-let [ttf-url (:ttf-url variant)]
|
||||||
(str/replace ttf-url "http://fonts.gstatic.com/s/" (u/join cf/public-uri "/internal/gfonts/font/"))
|
(str/replace ttf-url "http://fonts.gstatic.com/s/" (u/join cf/public-uri "/internal/gfonts/font/"))
|
||||||
(do
|
nil)))
|
||||||
(println "Variant TTF URL not found for" font-id font-variant-id)
|
|
||||||
nil))))
|
|
||||||
|
|
||||||
(defn- font-id->ttf-url
|
(defn- font-id->ttf-url
|
||||||
[font-id font-variant-id]
|
[font-id asset-id font-variant-id]
|
||||||
(if (str/starts-with? font-id "gfont-")
|
(case (font-backend font-id)
|
||||||
;; if font-id is a google font (starts with gfont-), we need to get the ttf url from Google Fonts API.
|
:google
|
||||||
(google-font-ttf-url font-id font-variant-id)
|
(google-font-ttf-url font-id font-variant-id)
|
||||||
;; otherwise, we return the font from our public-uri
|
:custom
|
||||||
(str (u/join cf/public-uri "assets/by-id/" font-id))))
|
(dm/str (u/join cf/public-uri "assets/by-id/" font-id))
|
||||||
|
:builtin
|
||||||
|
(dm/str (u/join cf/public-uri "fonts/" asset-id))))
|
||||||
|
|
||||||
(defn- store-font-id
|
(defn- store-font-id
|
||||||
[font-data asset-id]
|
[font-data asset-id]
|
||||||
(when asset-id
|
(when asset-id
|
||||||
(let [uri (font-id->ttf-url 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))
|
||||||
font-data (assoc font-data :family-id-buffer id-buffer)
|
font-data (assoc font-data :family-id-buffer id-buffer)
|
||||||
font-stored? (not= 0 (h/call wasm/internal-module "_is_font_uploaded"
|
font-stored? (not= 0 (h/call wasm/internal-module "_is_font_uploaded"
|
||||||
|
@ -136,17 +161,16 @@
|
||||||
(let [font-id (dm/get-prop font :font-id)
|
(let [font-id (dm/get-prop font :font-id)
|
||||||
font-variant-id (dm/get-prop font :font-variant-id)
|
font-variant-id (dm/get-prop font :font-variant-id)
|
||||||
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)
|
||||||
|
|
||||||
weight (serialize-font-weight
|
weight (serialize-font-weight raw-weight)
|
||||||
(if-let [weight-match (re-find #"\d+" font-variant-id)]
|
|
||||||
(js/parseInt weight-match)
|
|
||||||
400))
|
|
||||||
|
|
||||||
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"))
|
||||||
asset-id (font-id->ttf-id font-id font-variant-id)
|
asset-id (font-id->asset-id font-id font-variant-id)
|
||||||
font-data {:wasm-id wasm-id
|
font-data {:wasm-id wasm-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}]
|
||||||
|
|
Binary file not shown.
BIN
render-wasm/src/fonts/sourcesanspro-regular.ttf
Normal file
BIN
render-wasm/src/fonts/sourcesanspro-regular.ttf
Normal file
Binary file not shown.
|
@ -1,12 +1,22 @@
|
||||||
use skia_safe::{self as skia, textlayout, Font, FontMgr};
|
use skia_safe::{self as skia, textlayout, Font, FontMgr};
|
||||||
|
|
||||||
use crate::shapes::FontFamily;
|
use crate::shapes::{FontFamily, FontStyle};
|
||||||
|
use crate::uuid::Uuid;
|
||||||
|
|
||||||
const DEFAULT_FONT_BYTES: &[u8] = include_bytes!("../fonts/RobotoMono-Regular.ttf");
|
|
||||||
const EMOJI_FONT_BYTES: &[u8] = include_bytes!("../fonts/NotoColorEmoji-Regular.ttf");
|
const EMOJI_FONT_BYTES: &[u8] = include_bytes!("../fonts/NotoColorEmoji-Regular.ttf");
|
||||||
pub static DEFAULT_FONT: &'static str = "robotomono-regular";
|
|
||||||
pub static DEFAULT_EMOJI_FONT: &'static str = "noto-color-emoji";
|
pub static DEFAULT_EMOJI_FONT: &'static str = "noto-color-emoji";
|
||||||
|
|
||||||
|
const DEFAULT_FONT_BYTES: &[u8] = include_bytes!("../fonts/sourcesanspro-regular.ttf");
|
||||||
|
|
||||||
|
pub fn default_font() -> String {
|
||||||
|
let family = FontFamily::new(default_font_uuid(), 400, FontStyle::Normal);
|
||||||
|
format!("{}", family)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_font_uuid() -> Uuid {
|
||||||
|
Uuid::nil()
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FontStore {
|
pub struct FontStore {
|
||||||
font_mgr: FontMgr,
|
font_mgr: FontMgr,
|
||||||
font_provider: textlayout::TypefaceFontProvider,
|
font_provider: textlayout::TypefaceFontProvider,
|
||||||
|
@ -18,25 +28,19 @@ impl FontStore {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let font_mgr = FontMgr::new();
|
let font_mgr = FontMgr::new();
|
||||||
|
|
||||||
let mut font_provider = skia::textlayout::TypefaceFontProvider::new();
|
let mut font_provider = load_default_provider(&font_mgr);
|
||||||
|
|
||||||
let default_font = font_mgr
|
|
||||||
.new_from_data(DEFAULT_FONT_BYTES, None)
|
|
||||||
.expect("Failed to load font");
|
|
||||||
|
|
||||||
font_provider.register_typeface(default_font, DEFAULT_FONT);
|
|
||||||
|
|
||||||
|
// TODO: Load emoji font lazily
|
||||||
let emoji_font = font_mgr
|
let emoji_font = font_mgr
|
||||||
.new_from_data(EMOJI_FONT_BYTES, None)
|
.new_from_data(EMOJI_FONT_BYTES, None)
|
||||||
.expect("Failed to load font");
|
.expect("Failed to load font");
|
||||||
|
|
||||||
font_provider.register_typeface(emoji_font, DEFAULT_EMOJI_FONT);
|
font_provider.register_typeface(emoji_font, DEFAULT_EMOJI_FONT);
|
||||||
|
|
||||||
let mut font_collection = skia::textlayout::FontCollection::new();
|
let mut font_collection = skia::textlayout::FontCollection::new();
|
||||||
font_collection.set_default_font_manager(FontMgr::from(font_provider.clone()), None);
|
font_collection.set_default_font_manager(FontMgr::from(font_provider.clone()), None);
|
||||||
|
|
||||||
let debug_typeface = font_provider
|
let debug_typeface = font_provider
|
||||||
.match_family_style("robotomono-regular", skia::FontStyle::default())
|
.match_family_style(default_font().as_str(), skia::FontStyle::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let debug_font = skia::Font::new(debug_typeface, 10.0);
|
let debug_font = skia::Font::new(debug_typeface, 10.0);
|
||||||
|
@ -85,3 +89,15 @@ impl FontStore {
|
||||||
self.font_provider.family_names().any(|x| x == serialized)
|
self.font_provider.family_names().any(|x| x == serialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_default_provider(font_mgr: &FontMgr) -> skia::textlayout::TypefaceFontProvider {
|
||||||
|
let mut font_provider = skia::textlayout::TypefaceFontProvider::new();
|
||||||
|
|
||||||
|
let family = FontFamily::new(default_font_uuid(), 400, FontStyle::Normal);
|
||||||
|
let font = font_mgr
|
||||||
|
.new_from_data(DEFAULT_FONT_BYTES, None)
|
||||||
|
.expect("Failed to load font");
|
||||||
|
font_provider.register_typeface(font, family.alias().as_str());
|
||||||
|
|
||||||
|
font_provider
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,10 @@ impl FontFamily {
|
||||||
pub fn new(id: Uuid, weight: u32, style: FontStyle) -> Self {
|
pub fn new(id: Uuid, weight: u32, style: FontStyle) -> Self {
|
||||||
Self { id, style, weight }
|
Self { id, style, weight }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn alias(&self) -> String {
|
||||||
|
format!("{}", self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for FontFamily {
|
impl fmt::Display for FontFamily {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
math::Rect,
|
math::Rect,
|
||||||
render::{DEFAULT_EMOJI_FONT, DEFAULT_FONT},
|
render::{default_font, DEFAULT_EMOJI_FONT},
|
||||||
};
|
};
|
||||||
use skia_safe::{
|
use skia_safe::{
|
||||||
self as skia,
|
self as skia,
|
||||||
|
@ -133,7 +133,7 @@ impl TextLeaf {
|
||||||
style.set_font_size(self.font_size);
|
style.set_font_size(self.font_size);
|
||||||
style.set_font_families(&[
|
style.set_font_families(&[
|
||||||
self.serialized_font_family(),
|
self.serialized_font_family(),
|
||||||
DEFAULT_FONT.to_string(),
|
default_font(),
|
||||||
DEFAULT_EMOJI_FONT.to_string(),
|
DEFAULT_EMOJI_FONT.to_string(),
|
||||||
]);
|
]);
|
||||||
style
|
style
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue