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