| 1 | // font-kit/src/source.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 database of installed fonts that can be queried. |
| 12 | |
| 13 | use crate::error::SelectionError; |
| 14 | use crate::family::Family; |
| 15 | use crate::family_handle::FamilyHandle; |
| 16 | use crate::family_name::FamilyName; |
| 17 | use crate::font::Font; |
| 18 | use crate::handle::Handle; |
| 19 | use crate::matching; |
| 20 | use crate::properties::Properties; |
| 21 | use std::any::Any; |
| 22 | |
| 23 | #[cfg (all( |
| 24 | any(target_os = "macos" , target_os = "ios" ), |
| 25 | not(feature = "loader-freetype-default" ) |
| 26 | ))] |
| 27 | pub use crate::sources::core_text::CoreTextSource as SystemSource; |
| 28 | #[cfg (all(target_family = "windows" , not(feature = "source-fontconfig-default" )))] |
| 29 | pub use crate::sources::directwrite::DirectWriteSource as SystemSource; |
| 30 | #[cfg (all( |
| 31 | any( |
| 32 | not(any( |
| 33 | target_os = "android" , |
| 34 | target_os = "macos" , |
| 35 | target_os = "ios" , |
| 36 | target_family = "windows" , |
| 37 | target_arch = "wasm32" , |
| 38 | )), |
| 39 | feature = "source-fontconfig-default" |
| 40 | ), |
| 41 | not(target_env = "ohos" ) |
| 42 | ))] |
| 43 | pub use crate::sources::fontconfig::FontconfigSource as SystemSource; |
| 44 | #[cfg (all(target_os = "android" , not(feature = "source-fontconfig-default" )))] |
| 45 | pub use crate::sources::fs::FsSource as SystemSource; |
| 46 | |
| 47 | #[cfg (target_env = "ohos" )] |
| 48 | pub use crate::sources::fs::FsSource as SystemSource; |
| 49 | |
| 50 | // FIXME(pcwalton): These could expand to multiple fonts, and they could be language-specific. |
| 51 | #[cfg (any(target_family = "windows" , target_os = "macos" , target_os = "ios" ))] |
| 52 | const DEFAULT_FONT_FAMILY_SERIF: &'static str = "Times New Roman" ; |
| 53 | #[cfg (any(target_family = "windows" , target_os = "macos" , target_os = "ios" ))] |
| 54 | const DEFAULT_FONT_FAMILY_SANS_SERIF: &'static str = "Arial" ; |
| 55 | #[cfg (target_env = "ohos" )] |
| 56 | const DEFAULT_FONT_FAMILY_SANS_SERIF: &str = "HarmonyOS Sans" ; |
| 57 | #[cfg (any(target_family = "windows" , target_os = "macos" , target_os = "ios" ))] |
| 58 | const DEFAULT_FONT_FAMILY_MONOSPACE: &'static str = "Courier New" ; |
| 59 | #[cfg (target_env = "ohos" )] |
| 60 | const DEFAULT_FONT_FAMILY_MONOSPACE: &str = "HarmonyOS Sans" ; |
| 61 | #[cfg (any(target_family = "windows" , target_os = "macos" , target_os = "ios" ))] |
| 62 | const DEFAULT_FONT_FAMILY_CURSIVE: &'static str = "Comic Sans MS" ; |
| 63 | #[cfg (target_family = "windows" )] |
| 64 | const DEFAULT_FONT_FAMILY_FANTASY: &'static str = "Impact" ; |
| 65 | #[cfg (any(target_os = "macos" , target_os = "ios" ))] |
| 66 | const DEFAULT_FONT_FAMILY_FANTASY: &'static str = "Papyrus" ; |
| 67 | |
| 68 | #[cfg (not(any(target_family = "windows" , target_os = "macos" , target_os = "ios" )))] |
| 69 | const DEFAULT_FONT_FAMILY_SERIF: &str = "serif" ; |
| 70 | #[cfg (not(any( |
| 71 | target_family = "windows" , |
| 72 | target_os = "macos" , |
| 73 | target_os = "ios" , |
| 74 | target_env = "ohos" |
| 75 | )))] |
| 76 | const DEFAULT_FONT_FAMILY_SANS_SERIF: &str = "sans-serif" ; |
| 77 | #[cfg (not(any( |
| 78 | target_family = "windows" , |
| 79 | target_os = "macos" , |
| 80 | target_os = "ios" , |
| 81 | target_env = "ohos" |
| 82 | )))] |
| 83 | const DEFAULT_FONT_FAMILY_MONOSPACE: &str = "monospace" ; |
| 84 | #[cfg (not(any(target_family = "windows" , target_os = "macos" , target_os = "ios" )))] |
| 85 | const DEFAULT_FONT_FAMILY_CURSIVE: &str = "cursive" ; |
| 86 | #[cfg (not(any(target_family = "windows" , target_os = "macos" , target_os = "ios" )))] |
| 87 | const DEFAULT_FONT_FAMILY_FANTASY: &str = "fantasy" ; |
| 88 | |
| 89 | /// A database of installed fonts that can be queried. |
| 90 | /// |
| 91 | /// This trait is object-safe. |
| 92 | pub trait Source: Any { |
| 93 | /// Returns paths of all fonts installed on the system. |
| 94 | fn all_fonts(&self) -> Result<Vec<Handle>, SelectionError>; |
| 95 | |
| 96 | /// Returns the names of all families installed on the system. |
| 97 | fn all_families(&self) -> Result<Vec<String>, SelectionError>; |
| 98 | |
| 99 | /// Looks up a font family by name and returns the handles of all the fonts in that family. |
| 100 | fn select_family_by_name(&self, family_name: &str) -> Result<FamilyHandle, SelectionError>; |
| 101 | |
| 102 | /// Selects a font by PostScript name, which should be a unique identifier. |
| 103 | /// |
| 104 | /// The default implementation, which is used by the DirectWrite and the filesystem backends, |
| 105 | /// does a brute-force search of installed fonts to find the one that matches. |
| 106 | fn select_by_postscript_name(&self, postscript_name: &str) -> Result<Handle, SelectionError> { |
| 107 | // TODO(pcwalton): Optimize this by searching for families with similar names first. |
| 108 | for family_name in self.all_families()? { |
| 109 | if let Ok(family_handle) = self.select_family_by_name(&family_name) { |
| 110 | if let Ok(family) = Family::<Font>::from_handle(&family_handle) { |
| 111 | for (handle, font) in family_handle.fonts().iter().zip(family.fonts().iter()) { |
| 112 | if let Some(font_postscript_name) = font.postscript_name() { |
| 113 | if font_postscript_name == postscript_name { |
| 114 | return Ok((*handle).clone()); |
| 115 | } |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | Err(SelectionError::NotFound) |
| 122 | } |
| 123 | |
| 124 | // FIXME(pcwalton): This only returns one family instead of multiple families for the generic |
| 125 | // family names. |
| 126 | #[doc (hidden)] |
| 127 | fn select_family_by_generic_name( |
| 128 | &self, |
| 129 | family_name: &FamilyName, |
| 130 | ) -> Result<FamilyHandle, SelectionError> { |
| 131 | match *family_name { |
| 132 | FamilyName::Title(ref title) => self.select_family_by_name(title), |
| 133 | FamilyName::Serif => self.select_family_by_name(DEFAULT_FONT_FAMILY_SERIF), |
| 134 | FamilyName::SansSerif => self.select_family_by_name(DEFAULT_FONT_FAMILY_SANS_SERIF), |
| 135 | FamilyName::Monospace => self.select_family_by_name(DEFAULT_FONT_FAMILY_MONOSPACE), |
| 136 | FamilyName::Cursive => self.select_family_by_name(DEFAULT_FONT_FAMILY_CURSIVE), |
| 137 | FamilyName::Fantasy => self.select_family_by_name(DEFAULT_FONT_FAMILY_FANTASY), |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | /// Performs font matching according to the CSS Fonts Level 3 specification and returns the |
| 142 | /// handle. |
| 143 | #[inline ] |
| 144 | fn select_best_match( |
| 145 | &self, |
| 146 | family_names: &[FamilyName], |
| 147 | properties: &Properties, |
| 148 | ) -> Result<Handle, SelectionError> { |
| 149 | for family_name in family_names { |
| 150 | if let Ok(family_handle) = self.select_family_by_generic_name(family_name) { |
| 151 | let candidates = self.select_descriptions_in_family(&family_handle)?; |
| 152 | if let Ok(index) = matching::find_best_match(&candidates, properties) { |
| 153 | return Ok(family_handle.fonts[index].clone()); |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | Err(SelectionError::NotFound) |
| 158 | } |
| 159 | |
| 160 | #[doc (hidden)] |
| 161 | fn select_descriptions_in_family( |
| 162 | &self, |
| 163 | family: &FamilyHandle, |
| 164 | ) -> Result<Vec<Properties>, SelectionError> { |
| 165 | let mut fields = vec![]; |
| 166 | for font_handle in family.fonts() { |
| 167 | match Font::from_handle(font_handle) { |
| 168 | Ok(font) => fields.push(font.properties()), |
| 169 | Err(e) => log::warn!("Error loading font from handle: {:?}" , e), |
| 170 | } |
| 171 | } |
| 172 | Ok(fields) |
| 173 | } |
| 174 | |
| 175 | /// Accesses this `Source` as `Any`, which allows downcasting back to a concrete type from a |
| 176 | /// trait object. |
| 177 | fn as_any(&self) -> &dyn Any; |
| 178 | |
| 179 | /// Accesses this `Source` as `Any`, which allows downcasting back to a concrete type from a |
| 180 | /// trait object. |
| 181 | fn as_mut_any(&mut self) -> &mut dyn Any; |
| 182 | } |
| 183 | |