swash/
font.rs

1use super::cache::CacheKey;
2use super::internal::{raw_data, RawFont};
3use super::Tag;
4
5/// Reference to the content of a font file.
6#[derive(Copy, Clone)]
7pub struct FontDataRef<'a> {
8    data: &'a [u8],
9    len: usize,
10}
11
12impl<'a> FontDataRef<'a> {
13    /// Creates font data from the specified bytes. Returns `None` if the bytes
14    /// cannot trivially be determined to represent a font.
15    pub fn new(data: &'a [u8]) -> Option<Self> {
16        if !raw_data::is_font(data, 0) && !raw_data::is_collection(data) {
17            None
18        } else {
19            Some(Self {
20                data,
21                len: raw_data::count(data) as usize,
22            })
23        }
24    }
25
26    /// Returns true if the data represents a font collection.
27    pub fn is_collection(&self) -> bool {
28        raw_data::is_collection(self.data)
29    }
30
31    /// Returns the underlying data.
32    pub fn data(&self) -> &'a [u8] {
33        self.data
34    }
35
36    /// Returns the number of available fonts.
37    pub fn len(&self) -> usize {
38        self.len
39    }
40
41    /// Returns true if there are no available fonts.
42    pub fn is_empty(&self) -> bool {
43        self.len == 0
44    }
45
46    /// Returns the font at the specified index.
47    pub fn get(&self, index: usize) -> Option<FontRef<'a>> {
48        FontRef::from_offset(self.data, raw_data::offset(self.data, index as u32)?)
49    }
50
51    /// Returns an iterator over the available fonts.
52    pub fn fonts(&self) -> Fonts<'a> {
53        Fonts {
54            data: *self,
55            pos: 0,
56        }
57    }
58}
59
60/// Reference to a font.
61///
62/// This struct encapsulates the data required to access font resources and
63/// uniquely identify the font in various caches. Font files can be organized
64/// into collections of multiple fonts, so along with a reference to the actual
65/// content of the file, we also store a byte offset to the header of the
66/// selected font (although most fonts are not collections so this is almost always
67/// zero). Note that internal references in the font are stored relative to the
68/// base of the file, so the entire file must be kept in memory and it is an error
69/// to slice the data at the offset.
70///
71/// # Getting started
72/// As a primer, let's write a function to read a font file, construct a font reference
73/// and print the [`Attributes`](super::Attributes) and all of the associated
74/// [`LocalizedString`](super::LocalizedString)s (including names, copyright information
75/// and other metadata).
76///
77/// ```
78/// fn print_localized_strings(font_path: &str) -> Option<()> {
79///     use swash::FontRef;
80///     // Read the full font file
81///     let font_data = std::fs::read(font_path).ok()?;
82///     // Create a font reference for the first font in the file
83///     let font = FontRef::from_index(&font_data, 0)?;
84///     // Print the font attributes (stretch, weight and style)
85///     println!("{}", font.attributes());
86///     // Iterate through the localized strings
87///     for string in font.localized_strings() {
88///         // Print the string identifier and the actual value
89///         println!("[{:?}] {}", string.id(), string.to_string());
90///     }
91///     Some(())
92/// }
93/// ```
94///
95/// # Owning your fonts
96/// The [`FontRef`] struct is designed to be agnostic with regard to the font management
97/// policy of higher level crates and applications, and as such, contains borrowed
98/// data and is intended to be used as a transient resource. If you're using this
99/// library, you'll likely want to create your own type to represent a font.
100/// Regardless of the complexity of your management strategy, the basic pattern remains
101/// the same, so we'll build a simple `Font` struct here that can load fonts from a
102/// file using a basic `Vec<u8>` as a backing store.
103/// ```
104/// use swash::{Attributes, CacheKey, Charmap, FontRef};
105///
106/// pub struct Font {
107///     // Full content of the font file
108///     data: Vec<u8>,
109///     // Offset to the table directory
110///     offset: u32,
111///     // Cache key
112///     key: CacheKey,
113/// }
114///
115/// impl Font {
116///     pub fn from_file(path: &str, index: usize) -> Option<Self> {
117///         // Read the full font file
118///         let data = std::fs::read(path).ok()?;
119///         // Create a temporary font reference for the first font in the file.
120///         // This will do some basic validation, compute the necessary offset
121///         // and generate a fresh cache key for us.
122///         let font = FontRef::from_index(&data, index)?;
123///         let (offset, key) = (font.offset, font.key);
124///         // Return our struct with the original file data and copies of the
125///         // offset and key from the font reference
126///         Some(Self { data, offset, key })
127///     }
128///
129///     // As a convenience, you may want to forward some methods.
130///     pub fn attributes(&self) -> Attributes {
131///         self.as_ref().attributes()
132///     }
133///
134///     pub fn charmap(&self) -> Charmap {
135///         self.as_ref().charmap()
136///     }
137///
138///     // Create the transient font reference for accessing this crate's
139///     // functionality.
140///     pub fn as_ref(&self) -> FontRef {
141///         // Note that you'll want to initialize the struct directly here as
142///         // using any of the FontRef constructors will generate a new key which,
143///         // while completely safe, will nullify the performance optimizations of
144///         // the caching mechanisms used in this crate.
145///         FontRef {
146///             data: &self.data,
147///             offset: self.offset,
148///             key: self.key
149///         }
150///     }
151/// }
152/// ```
153/// In the example above, it's trivial to replace the `Vec<u8>` with an
154/// `Rc<Vec<u8>>` for a reference counted version or an `Arc<Vec<u8>>` for fonts
155/// that are shareable across threads. You may also consider memory mapping
156/// your font data, particularly for larger fonts (hello Apple Color Emoji!).
157///
158#[derive(Copy, Clone)]
159pub struct FontRef<'a> {
160    /// Full content of a file containing the font.
161    pub data: &'a [u8],
162    /// Offset to the table directory of the font.
163    pub offset: u32,
164    /// Key for identifying a font in various caches.
165    pub key: CacheKey,
166}
167
168impl<'a> FontRef<'a> {
169    /// Creates a new font from the specified font data and the index of the
170    /// desired font. Returns `None` if the data does not represent a font file
171    /// or the index is out of bounds.
172    pub fn from_index(data: &'a [u8], index: usize) -> Option<Self> {
173        FontDataRef::new(data)?.get(index)
174    }
175
176    /// Creates a new font from the specified font data and offset to the
177    /// table directory. Returns `None` if the offset is out of bounds or the
178    /// data at the offset does not represent a table directory.
179    pub fn from_offset(data: &'a [u8], offset: u32) -> Option<Self> {
180        if !raw_data::is_font(data, offset) {
181            None
182        } else {
183            Some(Self {
184                data,
185                offset,
186                key: CacheKey::new(),
187            })
188        }
189    }
190}
191
192impl<'a> RawFont<'a> for FontRef<'a> {
193    fn data(&self) -> &'a [u8] {
194        self.data
195    }
196
197    fn offset(&self) -> u32 {
198        self.offset
199    }
200}
201
202/// Iterator over a collection of fonts.
203pub struct Fonts<'a> {
204    data: FontDataRef<'a>,
205    pos: usize,
206}
207
208impl<'a> Iterator for Fonts<'a> {
209    type Item = FontRef<'a>;
210
211    fn size_hint(&self) -> (usize, Option<usize>) {
212        let remaining = self.data.len - self.pos;
213        (remaining, Some(remaining))
214    }
215
216    fn nth(&mut self, n: usize) -> Option<Self::Item> {
217        let pos = self.pos.checked_add(n)?;
218        self.pos = pos.checked_add(1)?;
219        self.data.get(pos)
220    }
221
222    fn next(&mut self) -> Option<Self::Item> {
223        if self.pos >= self.data.len {
224            None
225        } else {
226            let pos = self.pos;
227            self.pos += 1;
228            self.data.get(pos)
229        }
230    }
231}
232
233impl<'a> ExactSizeIterator for Fonts<'a> {
234    fn len(&self) -> usize {
235        self.data.len - self.pos
236    }
237}
238
239impl<'a> IntoIterator for FontDataRef<'a> {
240    type IntoIter = Fonts<'a>;
241    type Item = FontRef<'a>;
242
243    fn into_iter(self) -> Self::IntoIter {
244        self.fonts()
245    }
246}
247
248/// Source that can provide table data by tag.
249pub trait TableProvider {
250    /// Returns the table for the specified tag.
251    fn table_by_tag(&self, tag: Tag) -> Option<&[u8]>;
252}
253
254impl<'a> TableProvider for FontRef<'a> {
255    fn table_by_tag(&self, tag: Tag) -> Option<&[u8]> {
256        self.table_data(tag)
257    }
258}
259
260impl<'a> TableProvider for &'a FontRef<'a> {
261    fn table_by_tag(&self, tag: Tag) -> Option<&[u8]> {
262        self.table_data(tag)
263    }
264}