1// font-kit/src/sources/mem.rs
2//
3// Copyright © 2018 The Pathfinder Project Developers.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! A source that keeps fonts in memory.
12
13use crate::error::{FontLoadingError, SelectionError};
14use crate::family_handle::FamilyHandle;
15use crate::family_name::FamilyName;
16use crate::font::Font;
17use crate::handle::Handle;
18use crate::properties::Properties;
19use crate::source::Source;
20use std::any::Any;
21
22/// A source that keeps fonts in memory.
23#[allow(missing_debug_implementations)]
24pub struct MemSource {
25 families: Vec<FamilyEntry>,
26}
27
28impl MemSource {
29 /// Creates a new empty memory source.
30 pub fn empty() -> MemSource {
31 MemSource { families: vec![] }
32 }
33
34 /// Creates a new memory source that contains the given set of font handles.
35 ///
36 /// The fonts referenced by the handles are eagerly loaded into memory.
37 pub fn from_fonts<I>(fonts: I) -> Result<MemSource, FontLoadingError>
38 where
39 I: Iterator<Item = Handle>,
40 {
41 let mut families = vec![];
42 for handle in fonts {
43 add_font(handle, &mut families)?;
44 }
45 families.sort_by(|a, b| a.family_name.cmp(&b.family_name));
46 Ok(MemSource { families })
47 }
48
49 /// Add an existing font handle to a `MemSource`.
50 ///
51 /// Returns the font that was just added.
52 ///
53 /// Note that adding fonts to an existing `MemSource` is slower than creating a new one from a
54 /// `Handle` iterator, since this method sorts after every addition, rather than once at the
55 /// end.
56 pub fn add_font(&mut self, handle: Handle) -> Result<Font, FontLoadingError> {
57 let font = add_font(handle, &mut self.families)?;
58 self.families
59 .sort_by(|a, b| a.family_name.cmp(&b.family_name));
60 Ok(font)
61 }
62
63 /// Add a number of existing font handles to a `MemSource`.
64 ///
65 /// Note that adding fonts to an existing `MemSource` is slower than creating a new one from a
66 /// `Handle` iterator, because extra unnecessary sorting with occur with every call to this
67 /// method.
68 pub fn add_fonts(
69 &mut self,
70 handles: impl Iterator<Item = Handle>,
71 ) -> Result<(), FontLoadingError> {
72 for handle in handles {
73 add_font(handle, &mut self.families)?;
74 }
75 self.families
76 .sort_by(|a, b| a.family_name.cmp(&b.family_name));
77 Ok(())
78 }
79
80 /// Returns paths of all fonts installed on the system.
81 pub fn all_fonts(&self) -> Result<Vec<Handle>, SelectionError> {
82 Ok(self
83 .families
84 .iter()
85 .map(|family| family.font.clone())
86 .collect())
87 }
88
89 /// Returns the names of all families installed on the system.
90 pub fn all_families(&self) -> Result<Vec<String>, SelectionError> {
91 let mut families = vec![];
92 for family in &self.families {
93 if families.last() == Some(&family.family_name) {
94 continue;
95 }
96
97 families.push(family.family_name.clone());
98 }
99
100 Ok(families)
101 }
102
103 /// Looks up a font family by name and returns the handles of all the fonts in that family.
104 ///
105 /// FIXME(pcwalton): Case-insensitive comparison.
106 pub fn select_family_by_name(&self, family_name: &str) -> Result<FamilyHandle, SelectionError> {
107 let mut first_family_index = self
108 .families
109 .binary_search_by(|family| (&*family.family_name).cmp(family_name))
110 .map_err(|_| SelectionError::NotFound)?;
111
112 while first_family_index > 0
113 && self.families[first_family_index - 1].family_name == family_name
114 {
115 first_family_index -= 1
116 }
117 let mut last_family_index = first_family_index;
118 while last_family_index + 1 < self.families.len()
119 && self.families[last_family_index + 1].family_name == family_name
120 {
121 last_family_index += 1
122 }
123
124 let families = &self.families[first_family_index..(last_family_index + 1)];
125 Ok(FamilyHandle::from_font_handles(
126 families.iter().map(|family| family.font.clone()),
127 ))
128 }
129
130 /// Selects a font by PostScript name, which should be a unique identifier.
131 ///
132 /// The default implementation, which is used by the DirectWrite and the filesystem backends,
133 /// does a brute-force search of installed fonts to find the one that matches.
134 pub fn select_by_postscript_name(
135 &self,
136 postscript_name: &str,
137 ) -> Result<Handle, SelectionError> {
138 self.families
139 .iter()
140 .filter(|family_entry| family_entry.postscript_name == postscript_name)
141 .map(|family_entry| family_entry.font.clone())
142 .next()
143 .ok_or(SelectionError::NotFound)
144 }
145
146 /// Performs font matching according to the CSS Fonts Level 3 specification and returns the
147 /// handle.
148 #[inline]
149 pub fn select_best_match(
150 &self,
151 family_names: &[FamilyName],
152 properties: &Properties,
153 ) -> Result<Handle, SelectionError> {
154 <Self as Source>::select_best_match(self, family_names, properties)
155 }
156}
157
158impl Source for MemSource {
159 #[inline]
160 fn all_fonts(&self) -> Result<Vec<Handle>, SelectionError> {
161 self.all_fonts()
162 }
163
164 #[inline]
165 fn all_families(&self) -> Result<Vec<String>, SelectionError> {
166 self.all_families()
167 }
168
169 fn select_family_by_name(&self, family_name: &str) -> Result<FamilyHandle, SelectionError> {
170 self.select_family_by_name(family_name)
171 }
172
173 fn select_by_postscript_name(&self, postscript_name: &str) -> Result<Handle, SelectionError> {
174 self.select_by_postscript_name(postscript_name)
175 }
176
177 #[inline]
178 fn as_any(&self) -> &dyn Any {
179 self
180 }
181
182 #[inline]
183 fn as_mut_any(&mut self) -> &mut dyn Any {
184 self
185 }
186}
187
188/// Adds a font, but doesn't sort. Returns the font that was created to check for validity.
189fn add_font(handle: Handle, families: &mut Vec<FamilyEntry>) -> Result<Font, FontLoadingError> {
190 let font: Font = Font::from_handle(&handle)?;
191 if let Some(postscript_name: String) = font.postscript_name() {
192 families.push(FamilyEntry {
193 family_name: font.family_name(),
194 postscript_name: postscript_name,
195 font: handle,
196 })
197 }
198 Ok(font)
199}
200
201struct FamilyEntry {
202 family_name: String,
203 postscript_name: String,
204 font: Handle,
205}
206