1 //! This module allows you to use rusttype to provide the font operations that harfbuzz needs.
2
3 use crate::common::Tag;
4 pub use rusttype::Error;
5 use rusttype::Font as RTFont;
6 use rusttype::{Codepoint, GlyphId, Scale};
7
8 use crate::face;
9 use crate::font;
10 use crate::font::{Font, FontFuncs, Glyph as GlyphIndex, GlyphExtents, Position};
11
12 use std;
13 use std::fmt::Debug;
14 use std::str::FromStr;
15
16 // Work around weird rusttype scaling by reading the hhea table.
get_font_height(font: &font::Font<'_>) -> Result<i32, Error>17 fn get_font_height(font: &font::Font<'_>) -> Result<i32, Error> {
18 let face = font.face();
19 let tag = Tag::from_str("hhea").unwrap();
20 let hhea_table = face.table_with_tag(tag).ok_or(Error::IllFormed)?;
21 if hhea_table.len() >= 8 {
22 unsafe {
23 let ascent_ptr = (&hhea_table)[4..6].as_ptr() as *const i16;
24 let ascent = i16::from_be(*ascent_ptr);
25 let descent_ptr = (&hhea_table)[6..8].as_ptr() as *const i16;
26 let descent = i16::from_be(*descent_ptr);
27 Ok(ascent as i32 - descent as i32)
28 }
29 } else {
30 Err(Error::IllFormed)
31 }
32 }
33
rusttype_font_from_face<'a>(face: &face::Face<'a>) -> Result<RTFont<'a>, Error>34 fn rusttype_font_from_face<'a>(face: &face::Face<'a>) -> Result<RTFont<'a>, Error> {
35 // It is unfortunate that we have to copy the face data here.
36 let font_blob = face.face_data().as_ref().to_owned();
37 let index = face.index();
38 let collection = rusttype::FontCollection::from_bytes(font_blob)?;
39 collection.font_at(index as usize)
40 }
41
rusttype_scale_from_hb_font(font: &font::Font<'_>) -> Result<Scale, Error>42 fn rusttype_scale_from_hb_font(font: &font::Font<'_>) -> Result<Scale, Error> {
43 let font_height = get_font_height(font)? as f32;
44 let em_scale = font.scale();
45 let x_scale = em_scale.0 as f32;
46 let y_scale = em_scale.1 as f32;
47 Ok(Scale {
48 x: font_height * x_scale / y_scale,
49 y: font_height,
50 })
51 }
52
53 struct ScaledRusttypeFont<'a> {
54 font: rusttype::Font<'a>,
55 scale: Scale,
56 }
57
58 impl<'a> Debug for ScaledRusttypeFont<'a> {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 f.debug_struct("ScaledRusttypeFont")
61 .field("scale", &self.scale)
62 .finish()
63 }
64 }
65
66 impl<'a> ScaledRusttypeFont<'a> {
from_hb_font<'b>(hb_font: &font::Font<'b>) -> Result<ScaledRusttypeFont<'b>, Error>67 fn from_hb_font<'b>(hb_font: &font::Font<'b>) -> Result<ScaledRusttypeFont<'b>, Error> {
68 let font = rusttype_font_from_face(&hb_font.face())?;
69 let scale = rusttype_scale_from_hb_font(hb_font)?;
70 Ok(ScaledRusttypeFont { font, scale })
71 }
72 }
73
74 impl<'a> FontFuncs for ScaledRusttypeFont<'a> {
get_glyph_h_advance(&self, _: &Font<'_>, glyph: GlyphIndex) -> Position75 fn get_glyph_h_advance(&self, _: &Font<'_>, glyph: GlyphIndex) -> Position {
76 let glyph = self.font.glyph(GlyphId(glyph));
77 let glyph = glyph.scaled(self.scale);
78 glyph.h_metrics().advance_width.round() as Position
79 }
get_glyph_extents(&self, _: &Font<'_>, glyph: GlyphIndex) -> Option<GlyphExtents>80 fn get_glyph_extents(&self, _: &Font<'_>, glyph: GlyphIndex) -> Option<GlyphExtents> {
81 let glyph = self.font.glyph(GlyphId(glyph));
82 let glyph = glyph.scaled(self.scale);
83 glyph.exact_bounding_box().map(|bbox| GlyphExtents {
84 x_bearing: bbox.min.x.round() as i32,
85 y_bearing: bbox.min.y.round() as i32,
86 width: (bbox.max.x - bbox.min.x).round() as i32,
87 height: (bbox.max.y - bbox.min.y).round() as i32,
88 })
89 }
get_nominal_glyph(&self, _: &font::Font<'_>, unicode: char) -> Option<GlyphIndex>90 fn get_nominal_glyph(&self, _: &font::Font<'_>, unicode: char) -> Option<GlyphIndex> {
91 let glyph = self.font.glyph(Codepoint(unicode as u32));
92 Some(glyph.id().0)
93 }
94 }
95
96 use std::sync::Arc;
97
98 /// Creates a new HarfBuzz `Font` object that uses RustType to provide font data.
99 ///
100 /// # Examples
101 ///
102 /// Create a basic font that uses rusttype font funcs:
103 /// ```
104 /// use std::fs;
105 /// use std::sync::Arc;
106 ///
107 /// use harfbuzz_rs::rusttype::create_harfbuzz_rusttype_font;
108 ///
109 /// let path = "testfiles/SourceSansVariable-Roman.ttf";
110 /// let bytes: Arc<[u8]> = fs::read(path).unwrap().into();
111 /// let font = create_harfbuzz_rusttype_font(bytes, 0);
112 /// ```
create_harfbuzz_rusttype_font( bytes: impl Into<Arc<[u8]>>, index: u32, ) -> Result<crate::Owned<Font<'static>>, Error>113 pub fn create_harfbuzz_rusttype_font(
114 bytes: impl Into<Arc<[u8]>>,
115 index: u32,
116 ) -> Result<crate::Owned<Font<'static>>, Error> {
117 let bytes = bytes.into();
118 let face = crate::Face::new(bytes.clone(), index);
119 let mut font = Font::new(face);
120
121 let rt_font = rusttype::FontCollection::from_bytes(bytes)?.font_at(index as usize)?;
122 let scaled_font = ScaledRusttypeFont {
123 font: rt_font,
124 scale: rusttype_scale_from_hb_font(&font)?,
125 };
126 font.set_font_funcs(scaled_font);
127
128 Ok(font)
129 }
130
131 /// Extends the harfbuzz font to allow setting RustType as font funcs provider.
132 #[deprecated(since = "0.4.0")]
133 pub trait SetRustTypeFuncs {
134 /// Let a font use rusttype's font API for getting information like the
135 /// advance width of some glyph or its extents.
136 ///
137 /// # Deprecated
138 ///
139 /// This function is deprecated because it doesn't fit well with the design
140 /// of RustType (Calling this method requires to make a copy of the font
141 /// data used). You should use `create_harfbuzz_rusttype_font` instead.
142 #[deprecated(since = "0.4.0")]
set_rusttype_funcs(&mut self) -> Result<(), Error>143 fn set_rusttype_funcs(&mut self) -> Result<(), Error>;
144 }
145
146 #[allow(deprecated)]
147 impl<'a> SetRustTypeFuncs for Font<'a> {
set_rusttype_funcs(&mut self) -> Result<(), Error>148 fn set_rusttype_funcs(&mut self) -> Result<(), Error> {
149 let font_data = ScaledRusttypeFont::from_hb_font(self)?;
150 self.set_font_funcs(font_data);
151 Ok(())
152 }
153 }
154