1 use super::{FontData, FontFamily, FontStyle, LayoutBox};
2 use ab_glyph::{Font, FontRef, ScaleFont};
3 use core::fmt::{self, Display};
4 use once_cell::sync::Lazy;
5 use std::collections::HashMap;
6 use std::error::Error;
7 use std::sync::RwLock;
8
9 struct FontMap {
10 map: HashMap<String, FontRef<'static>>,
11 }
12 impl FontMap {
new() -> Self13 fn new() -> Self {
14 Self {
15 map: HashMap::with_capacity(4),
16 }
17 }
insert(&mut self, style: FontStyle, font: FontRef<'static>) -> Option<FontRef<'static>>18 fn insert(&mut self, style: FontStyle, font: FontRef<'static>) -> Option<FontRef<'static>> {
19 self.map.insert(style.as_str().to_string(), font)
20 }
21 // fn get(&self, style: FontStyle) -> Option<&FontRef<'static>> {
22 // self.map.get(style.as_str())
23 // }
get_fallback(&self, style: FontStyle) -> Option<&FontRef<'static>>24 fn get_fallback(&self, style: FontStyle) -> Option<&FontRef<'static>> {
25 self.map
26 .get(style.as_str())
27 .or_else(|| self.map.get(FontStyle::Normal.as_str()))
28 }
29 }
30
31 static FONTS: Lazy<RwLock<HashMap<String, FontMap>>> = Lazy::new(|| RwLock::new(HashMap::new()));
32 pub struct InvalidFont {
33 _priv: (),
34 }
35
36 // Note for future contributors: There is nothing fundamental about the static reference requirement here.
37 // It would be reasonably easy to add a function which accepts an owned buffer,
38 // or even a reference counted buffer, instead.
39 /// Register a font in the fonts table.
40 ///
41 /// The `name` parameter gives the name this font shall be referred to
42 /// in the other APIs, like `"sans-serif"`.
43 ///
44 /// Unprovided font styles for a given name will fallback to `FontStyle::Normal`
45 /// if that is available for that name, when other functions lookup fonts which
46 /// are registered with this function.
47 ///
48 /// The `bytes` parameter should be the complete contents
49 /// of an OpenType font file, like:
50 /// ```ignore
51 /// include_bytes!("FiraGO-Regular.otf")
52 /// ```
register_font( name: &str, style: FontStyle, bytes: &'static [u8], ) -> Result<(), InvalidFont>53 pub fn register_font(
54 name: &str,
55 style: FontStyle,
56 bytes: &'static [u8],
57 ) -> Result<(), InvalidFont> {
58 let font = FontRef::try_from_slice(bytes).map_err(|_| InvalidFont { _priv: () })?;
59 let mut lock = FONTS.write().unwrap();
60 lock.entry(name.to_string())
61 .or_insert_with(FontMap::new)
62 .insert(style, font);
63 Ok(())
64 }
65
66 #[derive(Clone)]
67 pub struct FontDataInternal {
68 font_ref: FontRef<'static>,
69 }
70
71 #[derive(Debug, Clone)]
72 pub enum FontError {
73 /// No idea what the problem is
74 Unknown,
75 /// No font data available for the requested family and style.
76 FontUnavailable,
77 }
78 impl Display for FontError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 // Since it makes literally no difference to how we'd format
81 // this, just delegate to the derived Debug formatter.
82 write!(f, "{:?}", self)
83 }
84 }
85 impl Error for FontError {}
86
87 impl FontData for FontDataInternal {
88 // TODO: can we rename this to `Error`?
89 type ErrorType = FontError;
new(family: FontFamily<'_>, style: FontStyle) -> Result<Self, Self::ErrorType>90 fn new(family: FontFamily<'_>, style: FontStyle) -> Result<Self, Self::ErrorType> {
91 Ok(Self {
92 font_ref: FONTS
93 .read()
94 .unwrap()
95 .get(family.as_str())
96 .and_then(|fam| fam.get_fallback(style))
97 .ok_or(FontError::FontUnavailable)?
98 .clone(),
99 })
100 }
101 // TODO: ngl, it makes no sense that this uses the same error type as `new`
estimate_layout(&self, size: f64, text: &str) -> Result<LayoutBox, Self::ErrorType>102 fn estimate_layout(&self, size: f64, text: &str) -> Result<LayoutBox, Self::ErrorType> {
103 let pixel_per_em = size / 1.24;
104 // let units_per_em = self.font_ref.units_per_em().unwrap();
105 let font = self.font_ref.as_scaled(size as f32);
106
107 let mut x_pixels = 0f32;
108
109 let mut prev = None;
110 for c in text.chars() {
111 let glyph_id = font.glyph_id(c);
112 let size = font.h_advance(glyph_id);
113 x_pixels += size;
114 if let Some(pc) = prev {
115 x_pixels += font.kern(pc, glyph_id);
116 }
117 prev = Some(glyph_id);
118 }
119
120 Ok(((0, 0), (x_pixels as i32, pixel_per_em as i32)))
121 }
draw<E, DrawFunc: FnMut(i32, i32, f32) -> Result<(), E>>( &self, pos: (i32, i32), size: f64, text: &str, mut draw: DrawFunc, ) -> Result<Result<(), E>, Self::ErrorType>122 fn draw<E, DrawFunc: FnMut(i32, i32, f32) -> Result<(), E>>(
123 &self,
124 pos: (i32, i32),
125 size: f64,
126 text: &str,
127 mut draw: DrawFunc,
128 ) -> Result<Result<(), E>, Self::ErrorType> {
129 let font = self.font_ref.as_scaled(size as f32);
130 let mut draw = |x: i32, y: i32, c| {
131 let (base_x, base_y) = pos;
132 draw(base_x + x, base_y + y, c)
133 };
134 let mut x_shift = 0f32;
135 let mut prev = None;
136 for c in text.chars() {
137 if let Some(pc) = prev {
138 x_shift += font.kern(font.glyph_id(pc), font.glyph_id(c));
139 }
140 prev = Some(c);
141 let glyph = font.scaled_glyph(c);
142 if let Some(q) = font.outline_glyph(glyph) {
143 let rect = q.px_bounds();
144 let y_shift = ((size as f32) / 2.0 + rect.min.y) as i32;
145 let x_shift = x_shift as i32;
146 let mut buf = vec![];
147 q.draw(|x, y, c| buf.push((x, y, c)));
148 for (x, y, c) in buf {
149 draw(x as i32 + x_shift, y as i32 + y_shift, c).map_err(|_e| {
150 // Note: If ever `plotters` adds a tracing or logging crate,
151 // this would be a good place to use it.
152 FontError::Unknown
153 })?;
154 }
155 }
156 x_shift += font.h_advance(font.glyph_id(c));
157 }
158 Ok(Ok(()))
159 }
160 }
161