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}