1#![cfg_attr(not(feature = "std"), no_std)]
60#![warn(missing_docs)]
61#![warn(missing_debug_implementations)]
62#![warn(missing_copy_implementations)]
63
64extern crate alloc;
65
66#[cfg(not(feature = "std"))]
67use alloc::{
68 string::{String, ToString},
69 vec::Vec,
70};
71
72pub use ttf_parser::Language;
73pub use ttf_parser::Width as Stretch;
74
75use slotmap::SlotMap;
76use tinyvec::TinyVec;
77
78#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Debug, Default)]
93pub struct ID(InnerId);
94
95slotmap::new_key_type! {
96 struct InnerId;
98}
99
100impl ID {
101 #[inline]
105 pub fn dummy() -> Self {
106 Self(InnerId::from(slotmap::KeyData::from_ffi(core::u64::MAX)))
107 }
108}
109
110impl core::fmt::Display for ID {
111 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112 write!(f, "{}", (self.0).0.as_ffi())
113 }
114}
115
116#[derive(Debug)]
118enum LoadError {
119 MalformedFont,
124 UnnamedFont,
126 #[cfg(feature = "std")]
128 IoError(std::io::Error),
129}
130
131#[cfg(feature = "std")]
132impl From<std::io::Error> for LoadError {
133 #[inline]
134 fn from(e: std::io::Error) -> Self {
135 LoadError::IoError(e)
136 }
137}
138
139impl core::fmt::Display for LoadError {
140 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
141 match self {
142 LoadError::MalformedFont => write!(f, "malformed font"),
143 LoadError::UnnamedFont => write!(f, "font doesn't have a family name"),
144 #[cfg(feature = "std")]
145 LoadError::IoError(ref e) => write!(f, "{}", e),
146 }
147 }
148}
149
150#[derive(Clone, Debug)]
152pub struct Database {
153 faces: SlotMap<InnerId, FaceInfo>,
154 family_serif: String,
155 family_sans_serif: String,
156 family_cursive: String,
157 family_fantasy: String,
158 family_monospace: String,
159}
160
161impl Default for Database {
162 fn default() -> Self {
163 Self::new()
164 }
165}
166
167impl Database {
168 #[inline]
178 pub fn new() -> Self {
179 Database {
180 faces: SlotMap::with_key(),
181 family_serif: "Times New Roman".to_string(),
182 family_sans_serif: "Arial".to_string(),
183 family_cursive: "Comic Sans MS".to_string(),
184 #[cfg(not(target_os = "macos"))]
185 family_fantasy: "Impact".to_string(),
186 #[cfg(target_os = "macos")]
187 family_fantasy: "Papyrus".to_string(),
188 family_monospace: "Courier New".to_string(),
189 }
190 }
191
192 pub fn load_font_data(&mut self, data: Vec<u8>) {
196 self.load_font_source(Source::Binary(alloc::sync::Arc::new(data)));
197 }
198
199 pub fn load_font_source(&mut self, source: Source) -> TinyVec<[ID; 8]> {
204 let ids = source.with_data(|data| {
205 let n = ttf_parser::fonts_in_collection(data).unwrap_or(1);
206 let mut ids = TinyVec::with_capacity(n as usize);
207
208 for index in 0..n {
209 match parse_face_info(source.clone(), data, index) {
210 Ok(mut info) => {
211 let id = self.faces.insert_with_key(|k| {
212 info.id = ID(k);
213 info
214 });
215 ids.push(ID(id));
216 }
217 Err(e) => log::warn!(
218 "Failed to load a font face {} from source cause {}.",
219 index,
220 e
221 ),
222 }
223 }
224
225 ids
226 });
227
228 ids.unwrap_or_default()
229 }
230
231 #[cfg(feature = "fs")]
233 fn load_fonts_from_file(&mut self, path: &std::path::Path, data: &[u8]) {
234 let source = Source::File(path.into());
235
236 let n = ttf_parser::fonts_in_collection(data).unwrap_or(1);
237 for index in 0..n {
238 match parse_face_info(source.clone(), data, index) {
239 Ok(info) => self.push_face_info(info),
240 Err(e) => {
241 log::warn!(
242 "Failed to load a font face {} from '{}' cause {}.",
243 index,
244 path.display(),
245 e
246 )
247 }
248 }
249 }
250 }
251
252 #[cfg(all(feature = "fs", feature = "memmap"))]
256 pub fn load_font_file<P: AsRef<std::path::Path>>(
257 &mut self,
258 path: P,
259 ) -> Result<(), std::io::Error> {
260 self.load_font_file_impl(path.as_ref())
261 }
262
263 #[cfg(all(feature = "fs", feature = "memmap"))]
265 fn load_font_file_impl(&mut self, path: &std::path::Path) -> Result<(), std::io::Error> {
266 let file = std::fs::File::open(path)?;
267 let data: &[u8] = unsafe { &memmap2::MmapOptions::new().map(&file)? };
268
269 self.load_fonts_from_file(path, data);
270 Ok(())
271 }
272
273 #[cfg(all(feature = "fs", not(feature = "memmap")))]
277 pub fn load_font_file<P: AsRef<std::path::Path>>(
278 &mut self,
279 path: P,
280 ) -> Result<(), std::io::Error> {
281 self.load_font_file_impl(path.as_ref())
282 }
283
284 #[cfg(all(feature = "fs", not(feature = "memmap")))]
286 fn load_font_file_impl(&mut self, path: &std::path::Path) -> Result<(), std::io::Error> {
287 let data = std::fs::read(path)?;
288
289 self.load_fonts_from_file(path, &data);
290 Ok(())
291 }
292
293 #[cfg(feature = "fs")]
302 pub fn load_fonts_dir<P: AsRef<std::path::Path>>(&mut self, dir: P) {
303 self.load_fonts_dir_impl(dir.as_ref())
304 }
305
306 #[rustfmt::skip] #[cfg(feature = "fs")]
309 fn load_fonts_dir_impl(&mut self, dir: &std::path::Path) {
310 let fonts_dir = match std::fs::read_dir(dir) {
311 Ok(dir) => dir,
312 Err(_) => return,
313 };
314
315 for entry in fonts_dir.flatten() {
316 let path = entry.path();
317 if path.is_file() {
318 match path.extension().and_then(|e| e.to_str()) {
319 Some("ttf") | Some("ttc") | Some("TTF") | Some("TTC") |
320 Some("otf") | Some("otc") | Some("OTF") | Some("OTC") => {
321 if let Err(e) = self.load_font_file(&path) {
322 log::warn!("Failed to load '{}' cause {}.", path.display(), e);
323 }
324 }
325 _ => {}
326 }
327 } else if path.is_dir() {
328 self.load_fonts_dir(path);
330 }
331 }
332 }
333
334 #[cfg(feature = "fs")]
345 pub fn load_system_fonts(&mut self) {
346 #[cfg(target_os = "windows")]
347 {
348 if let Some(ref system_root) = std::env::var_os("SYSTEMROOT") {
349 let system_root_path = std::path::Path::new(system_root);
350 self.load_fonts_dir(system_root_path.join("Fonts"));
351 } else {
352 self.load_fonts_dir("C:\\Windows\\Fonts\\");
353 }
354
355 if let Ok(ref home) = std::env::var("USERPROFILE") {
356 let home_path = std::path::Path::new(home);
357 self.load_fonts_dir(home_path.join("AppData\\Local\\Microsoft\\Windows\\Fonts"));
358 self.load_fonts_dir(home_path.join("AppData\\Roaming\\Microsoft\\Windows\\Fonts"));
359 }
360 }
361
362 #[cfg(target_os = "macos")]
363 {
364 self.load_fonts_dir("/Library/Fonts");
365 self.load_fonts_dir("/System/Library/Fonts");
366 if let Ok(dir) = std::fs::read_dir("/System/Library/AssetsV2") {
368 for entry in dir {
369 let entry = match entry {
370 Ok(entry) => entry,
371 Err(_) => continue,
372 };
373 if entry
374 .file_name()
375 .to_string_lossy()
376 .starts_with("com_apple_MobileAsset_Font")
377 {
378 self.load_fonts_dir(entry.path());
379 }
380 }
381 }
382 self.load_fonts_dir("/Network/Library/Fonts");
383
384 if let Ok(ref home) = std::env::var("HOME") {
385 let home_path = std::path::Path::new(home);
386 self.load_fonts_dir(home_path.join("Library/Fonts"));
387 }
388 }
389
390 #[cfg(target_os = "redox")]
392 {
393 self.load_fonts_dir("/ui/fonts");
394 }
395
396 #[cfg(all(unix, not(any(target_os = "macos", target_os = "android"))))]
398 {
399 #[cfg(feature = "fontconfig")]
400 {
401 self.load_fontconfig();
402 }
403
404 #[cfg(not(feature = "fontconfig"))]
405 {
406 self.load_fonts_dir("/usr/share/fonts/");
407 self.load_fonts_dir("/usr/local/share/fonts/");
408
409 if let Ok(ref home) = std::env::var("HOME") {
410 let home_path = std::path::Path::new(home);
411 self.load_fonts_dir(home_path.join(".fonts"));
412 self.load_fonts_dir(home_path.join(".local/share/fonts"));
413 }
414 }
415 }
416 }
417
418 #[cfg(all(
420 unix,
421 feature = "fontconfig",
422 not(any(target_os = "macos", target_os = "android"))
423 ))]
424 fn load_fontconfig(&mut self) {
425 use std::path::Path;
426
427 let mut fontconfig = fontconfig_parser::FontConfig::default();
428 let home = std::env::var("HOME");
429
430 if let Ok(ref config_file) = std::env::var("FONTCONFIG_FILE") {
431 let _ = fontconfig.merge_config(Path::new(config_file));
432 } else {
433 let xdg_config_home = if let Ok(val) = std::env::var("XDG_CONFIG_HOME") {
434 Some(val.into())
435 } else if let Ok(ref home) = home {
436 Some(Path::new(home).join(".config"))
439 } else {
440 None
441 };
442
443 let read_global = match xdg_config_home {
444 Some(p) => fontconfig
445 .merge_config(&p.join("fontconfig/fonts.conf"))
446 .is_err(),
447 None => true,
448 };
449
450 if read_global {
451 let _ = fontconfig.merge_config(Path::new("/etc/fonts/local.conf"));
452 }
453 let _ = fontconfig.merge_config(Path::new("/etc/fonts/fonts.conf"));
454 }
455
456 for fontconfig_parser::Alias {
457 alias,
458 default,
459 prefer,
460 accept,
461 } in fontconfig.aliases
462 {
463 let name = prefer
464 .get(0)
465 .or_else(|| accept.get(0))
466 .or_else(|| default.get(0));
467
468 if let Some(name) = name {
469 match alias.to_lowercase().as_str() {
470 "serif" => self.set_serif_family(name),
471 "sans-serif" => self.set_sans_serif_family(name),
472 "sans serif" => self.set_sans_serif_family(name),
473 "monospace" => self.set_monospace_family(name),
474 "cursive" => self.set_cursive_family(name),
475 "fantasy" => self.set_fantasy_family(name),
476 _ => {}
477 }
478 }
479 }
480
481 for dir in fontconfig.dirs {
482 let path = if dir.path.starts_with("~") {
483 if let Ok(ref home) = home {
484 Path::new(home).join(dir.path.strip_prefix("~").unwrap())
485 } else {
486 continue;
487 }
488 } else {
489 dir.path
490 };
491 self.load_fonts_dir(path);
492 }
493 }
494
495 pub fn push_face_info(&mut self, mut info: FaceInfo) {
502 self.faces.insert_with_key(|k| {
503 info.id = ID(k);
504 info
505 });
506 }
507
508 pub fn remove_face(&mut self, id: ID) {
516 self.faces.remove(id.0);
517 }
518
519 #[inline]
521 pub fn is_empty(&self) -> bool {
522 self.faces.is_empty()
523 }
524
525 #[inline]
531 pub fn len(&self) -> usize {
532 self.faces.len()
533 }
534
535 pub fn set_serif_family<S: Into<String>>(&mut self, family: S) {
537 self.family_serif = family.into();
538 }
539
540 pub fn set_sans_serif_family<S: Into<String>>(&mut self, family: S) {
542 self.family_sans_serif = family.into();
543 }
544
545 pub fn set_cursive_family<S: Into<String>>(&mut self, family: S) {
547 self.family_cursive = family.into();
548 }
549
550 pub fn set_fantasy_family<S: Into<String>>(&mut self, family: S) {
552 self.family_fantasy = family.into();
553 }
554
555 pub fn set_monospace_family<S: Into<String>>(&mut self, family: S) {
557 self.family_monospace = family.into();
558 }
559
560 pub fn family_name<'a>(&'a self, family: &'a Family) -> &'a str {
564 match family {
565 Family::Name(name) => name,
566 Family::Serif => self.family_serif.as_str(),
567 Family::SansSerif => self.family_sans_serif.as_str(),
568 Family::Cursive => self.family_cursive.as_str(),
569 Family::Fantasy => self.family_fantasy.as_str(),
570 Family::Monospace => self.family_monospace.as_str(),
571 }
572 }
573
574 pub fn query(&self, query: &Query) -> Option<ID> {
576 for family in query.families {
577 let name = self.family_name(family);
578 let candidates: Vec<_> = self
579 .faces
580 .iter()
581 .filter(|(_, face)| face.families.iter().any(|family| family.0 == name))
582 .map(|(_, info)| info)
583 .collect();
584
585 if !candidates.is_empty() {
586 if let Some(index) = find_best_match(&candidates, query) {
587 return Some(candidates[index].id);
588 }
589 }
590 }
591
592 None
593 }
594
595 #[inline]
599 pub fn faces(&self) -> impl Iterator<Item = &FaceInfo> + '_ {
600 self.faces.iter().map(|(_, info)| info)
601 }
602
603 pub fn face(&self, id: ID) -> Option<&FaceInfo> {
608 self.faces.get(id.0)
609 }
610
611 pub fn face_source(&self, id: ID) -> Option<(Source, u32)> {
613 self.face(id).map(|info| (info.source.clone(), info.index))
614 }
615
616 pub fn with_face_data<P, T>(&self, id: ID, p: P) -> Option<T>
636 where
637 P: FnOnce(&[u8], u32) -> T,
638 {
639 let (src, face_index) = self.face_source(id)?;
640 src.with_data(|data| p(data, face_index))
641 }
642
643 #[cfg(all(feature = "fs", feature = "memmap"))]
656 pub unsafe fn make_shared_face_data(
657 &mut self,
658 id: ID,
659 ) -> Option<(std::sync::Arc<dyn AsRef<[u8]> + Send + Sync>, u32)> {
660 let face_info = self.faces.get(id.0)?;
661 let face_index = face_info.index;
662
663 let old_source = face_info.source.clone();
664
665 let (path, shared_data) = match &old_source {
666 Source::Binary(data) => {
667 return Some((data.clone(), face_index));
668 }
669 Source::File(ref path) => {
670 let file = std::fs::File::open(path).ok()?;
671 let shared_data = std::sync::Arc::new(memmap2::MmapOptions::new().map(&file).ok()?)
672 as std::sync::Arc<dyn AsRef<[u8]> + Send + Sync>;
673 (path.clone(), shared_data)
674 }
675 Source::SharedFile(_, data) => {
676 return Some((data.clone(), face_index));
677 }
678 };
679
680 let shared_source = Source::SharedFile(path.clone(), shared_data.clone());
681
682 self.faces.iter_mut().for_each(|(_, face)| {
683 if matches!(&face.source, Source::File(old_path) if old_path == &path) {
684 face.source = shared_source.clone();
685 }
686 });
687
688 Some((shared_data, face_index))
689 }
690
691 #[cfg(all(feature = "fs", feature = "memmap"))]
695 pub fn make_face_data_unshared(&mut self, id: ID) {
696 let face_info = match self.faces.get(id.0) {
697 Some(face_info) => face_info,
698 None => return,
699 };
700
701 let old_source = face_info.source.clone();
702
703 let shared_path = match old_source {
704 #[cfg(all(feature = "fs", feature = "memmap"))]
705 Source::SharedFile(path, _) => path,
706 _ => return,
707 };
708
709 let new_source = Source::File(shared_path.clone());
710
711 self.faces.iter_mut().for_each(|(_, face)| {
712 if matches!(&face.source, Source::SharedFile(path, ..) if path == &shared_path) {
713 face.source = new_source.clone();
714 }
715 });
716 }
717}
718
719#[derive(Clone, Debug)]
725pub struct FaceInfo {
726 pub id: ID,
728
729 pub source: Source,
734
735 pub index: u32,
737
738 pub families: Vec<(String, Language)>,
751
752 pub post_script_name: String,
758
759 pub style: Style,
761
762 pub weight: Weight,
764
765 pub stretch: Stretch,
767
768 pub monospaced: bool,
770}
771
772#[derive(Clone)]
778pub enum Source {
779 Binary(alloc::sync::Arc<dyn AsRef<[u8]> + Sync + Send>),
781
782 #[cfg(feature = "fs")]
784 File(std::path::PathBuf),
785
786 #[cfg(all(feature = "fs", feature = "memmap"))]
788 SharedFile(
789 std::path::PathBuf,
790 std::sync::Arc<dyn AsRef<[u8]> + Sync + Send>,
791 ),
792}
793
794impl core::fmt::Debug for Source {
795 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
796 match self {
797 Self::Binary(arg0) => f
798 .debug_tuple("SharedBinary")
799 .field(&arg0.as_ref().as_ref())
800 .finish(),
801 #[cfg(feature = "fs")]
802 Self::File(arg0) => f.debug_tuple("File").field(arg0).finish(),
803 #[cfg(all(feature = "fs", feature = "memmap"))]
804 Self::SharedFile(arg0, arg1) => f
805 .debug_tuple("SharedFile")
806 .field(arg0)
807 .field(&arg1.as_ref().as_ref())
808 .finish(),
809 }
810 }
811}
812
813impl Source {
814 fn with_data<P, T>(&self, p: P) -> Option<T>
815 where
816 P: FnOnce(&[u8]) -> T,
817 {
818 match &self {
819 #[cfg(all(feature = "fs", not(feature = "memmap")))]
820 Source::File(ref path) => {
821 let data = std::fs::read(path).ok()?;
822
823 Some(p(&data))
824 }
825 #[cfg(all(feature = "fs", feature = "memmap"))]
826 Source::File(ref path) => {
827 let file = std::fs::File::open(path).ok()?;
828 let data = unsafe { &memmap2::MmapOptions::new().map(&file).ok()? };
829
830 Some(p(data))
831 }
832 Source::Binary(ref data) => Some(p(data.as_ref().as_ref())),
833 #[cfg(all(feature = "fs", feature = "memmap"))]
834 Source::SharedFile(_, ref data) => Some(p(data.as_ref().as_ref())),
835 }
836 }
837}
838
839#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Hash)]
843pub struct Query<'a> {
844 pub families: &'a [Family<'a>],
848
849 pub weight: Weight,
853
854 pub stretch: Stretch,
858
859 pub style: Style,
863}
864
865#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
868pub enum Family<'a> {
869 Name(&'a str),
877
878 Serif,
880
881 SansSerif,
885
886 Cursive,
889
890 Fantasy,
893
894 Monospace,
896}
897
898#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
900pub struct Weight(pub u16);
901
902impl Default for Weight {
903 #[inline]
904 fn default() -> Weight {
905 Weight::NORMAL
906 }
907}
908
909impl Weight {
910 pub const THIN: Weight = Weight(100);
912 pub const EXTRA_LIGHT: Weight = Weight(200);
914 pub const LIGHT: Weight = Weight(300);
916 pub const NORMAL: Weight = Weight(400);
918 pub const MEDIUM: Weight = Weight(500);
920 pub const SEMIBOLD: Weight = Weight(600);
922 pub const BOLD: Weight = Weight(700);
924 pub const EXTRA_BOLD: Weight = Weight(800);
926 pub const BLACK: Weight = Weight(900);
928}
929
930#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
932pub enum Style {
933 Normal,
935 Italic,
937 Oblique,
939}
940
941impl Default for Style {
942 #[inline]
943 fn default() -> Style {
944 Style::Normal
945 }
946}
947
948fn parse_face_info(source: Source, data: &[u8], index: u32) -> Result<FaceInfo, LoadError> {
949 let raw_face = ttf_parser::RawFace::parse(data, index).map_err(|_| LoadError::MalformedFont)?;
950 let (families, post_script_name) = parse_names(&raw_face).ok_or(LoadError::UnnamedFont)?;
951 let (mut style, weight, stretch) = parse_os2(&raw_face);
952 let (monospaced, italic) = parse_post(&raw_face);
953
954 if style == Style::Normal && italic {
955 style = Style::Italic;
956 }
957
958 Ok(FaceInfo {
959 id: ID::dummy(),
960 source,
961 index,
962 families,
963 post_script_name,
964 style,
965 weight,
966 stretch,
967 monospaced,
968 })
969}
970
971fn parse_names(raw_face: &ttf_parser::RawFace) -> Option<(Vec<(String, Language)>, String)> {
972 const NAME_TAG: ttf_parser::Tag = ttf_parser::Tag::from_bytes(b"name");
973 let name_data = raw_face.table(NAME_TAG)?;
974 let name_table = ttf_parser::name::Table::parse(name_data)?;
975
976 let mut families = collect_families(ttf_parser::name_id::TYPOGRAPHIC_FAMILY, &name_table.names);
977
978 if families.is_empty() {
980 families = collect_families(ttf_parser::name_id::FAMILY, &name_table.names);
981 }
982
983 if families.len() > 1 {
985 if let Some(index) = families
986 .iter()
987 .position(|f| f.1 == Language::English_UnitedStates)
988 {
989 if index != 0 {
990 families.swap(0, index);
991 }
992 }
993 }
994
995 if families.is_empty() {
996 return None;
997 }
998
999 let post_script_name = name_table
1000 .names
1001 .into_iter()
1002 .find(|name| {
1003 name.name_id == ttf_parser::name_id::POST_SCRIPT_NAME && name.is_supported_encoding()
1004 })
1005 .and_then(|name| name_to_unicode(&name))?;
1006
1007 Some((families, post_script_name))
1008}
1009
1010fn collect_families(name_id: u16, names: &ttf_parser::name::Names) -> Vec<(String, Language)> {
1011 let mut families = Vec::new();
1012 for name in names.into_iter() {
1013 if name.name_id == name_id && name.is_unicode() {
1014 if let Some(family) = name_to_unicode(&name) {
1015 families.push((family, name.language()));
1016 }
1017 }
1018 }
1019
1020 if !families
1022 .iter()
1023 .any(|f| f.1 == Language::English_UnitedStates)
1024 {
1025 for name in names.into_iter() {
1026 if name.name_id == name_id && name.is_mac_roman() {
1027 if let Some(family) = name_to_unicode(&name) {
1028 families.push((family, name.language()));
1029 break;
1030 }
1031 }
1032 }
1033 }
1034
1035 families
1036}
1037
1038fn name_to_unicode(name: &ttf_parser::name::Name) -> Option<String> {
1039 if name.is_unicode() {
1040 let mut raw_data: Vec<u16> = Vec::new();
1041 for c in ttf_parser::LazyArray16::<u16>::new(name.name) {
1042 raw_data.push(c);
1043 }
1044
1045 String::from_utf16(&raw_data).ok()
1046 } else if name.is_mac_roman() {
1047 let mut raw_data = Vec::with_capacity(name.name.len());
1049 for b in name.name {
1050 raw_data.push(MAC_ROMAN[*b as usize]);
1051 }
1052
1053 String::from_utf16(&raw_data).ok()
1054 } else {
1055 None
1056 }
1057}
1058
1059fn parse_os2(raw_face: &ttf_parser::RawFace) -> (Style, Weight, Stretch) {
1060 const OS2_TAG: ttf_parser::Tag = ttf_parser::Tag::from_bytes(b"OS/2");
1061 let table = match raw_face
1062 .table(OS2_TAG)
1063 .and_then(ttf_parser::os2::Table::parse)
1064 {
1065 Some(table) => table,
1066 None => return (Style::Normal, Weight::NORMAL, Stretch::Normal),
1067 };
1068
1069 let style = match table.style() {
1070 ttf_parser::Style::Normal => Style::Normal,
1071 ttf_parser::Style::Italic => Style::Italic,
1072 ttf_parser::Style::Oblique => Style::Oblique,
1073 };
1074
1075 let weight = table.weight();
1076 let stretch = table.width();
1077
1078 (style, Weight(weight.to_number()), stretch)
1079}
1080
1081fn parse_post(raw_face: &ttf_parser::RawFace) -> (bool, bool) {
1082 const POST_TAG: ttf_parser::Tag = ttf_parser::Tag::from_bytes(b"post");
1086 let data = match raw_face.table(POST_TAG) {
1087 Some(v) => v,
1088 None => return (false, false),
1089 };
1090
1091 let monospaced = data.get(12..16) != Some(&[0, 0, 0, 0]);
1093
1094 let italic = data.get(4..8) != Some(&[0, 0, 0, 0]);
1096
1097 (monospaced, italic)
1098}
1099
1100trait NameExt {
1101 fn is_mac_roman(&self) -> bool;
1102 fn is_supported_encoding(&self) -> bool;
1103}
1104
1105impl NameExt for ttf_parser::name::Name<'_> {
1106 #[inline]
1107 fn is_mac_roman(&self) -> bool {
1108 use ttf_parser::PlatformId::Macintosh;
1109 const MACINTOSH_ROMAN_ENCODING_ID: u16 = 0;
1111
1112 self.platform_id == Macintosh && self.encoding_id == MACINTOSH_ROMAN_ENCODING_ID
1113 }
1114
1115 #[inline]
1116 fn is_supported_encoding(&self) -> bool {
1117 self.is_unicode() || self.is_mac_roman()
1118 }
1119}
1120
1121#[inline(never)]
1124fn find_best_match(candidates: &[&FaceInfo], query: &Query) -> Option<usize> {
1125 debug_assert!(!candidates.is_empty());
1126
1127 let mut matching_set: Vec<usize> = (0..candidates.len()).collect();
1129
1130 let matches = matching_set
1132 .iter()
1133 .any(|&index| candidates[index].stretch == query.stretch);
1134 let matching_stretch = if matches {
1135 query.stretch
1137 } else if query.stretch <= Stretch::Normal {
1138 let stretch = matching_set
1140 .iter()
1141 .filter(|&&index| candidates[index].stretch < query.stretch)
1142 .min_by_key(|&&index| {
1143 query.stretch.to_number() - candidates[index].stretch.to_number()
1144 });
1145
1146 match stretch {
1147 Some(&matching_index) => candidates[matching_index].stretch,
1148 None => {
1149 let matching_index = *matching_set.iter().min_by_key(|&&index| {
1150 candidates[index].stretch.to_number() - query.stretch.to_number()
1151 })?;
1152
1153 candidates[matching_index].stretch
1154 }
1155 }
1156 } else {
1157 let stretch = matching_set
1159 .iter()
1160 .filter(|&&index| candidates[index].stretch > query.stretch)
1161 .min_by_key(|&&index| {
1162 candidates[index].stretch.to_number() - query.stretch.to_number()
1163 });
1164
1165 match stretch {
1166 Some(&matching_index) => candidates[matching_index].stretch,
1167 None => {
1168 let matching_index = *matching_set.iter().min_by_key(|&&index| {
1169 query.stretch.to_number() - candidates[index].stretch.to_number()
1170 })?;
1171
1172 candidates[matching_index].stretch
1173 }
1174 }
1175 };
1176 matching_set.retain(|&index| candidates[index].stretch == matching_stretch);
1177
1178 let style_preference = match query.style {
1180 Style::Italic => [Style::Italic, Style::Oblique, Style::Normal],
1181 Style::Oblique => [Style::Oblique, Style::Italic, Style::Normal],
1182 Style::Normal => [Style::Normal, Style::Oblique, Style::Italic],
1183 };
1184 let matching_style = *style_preference.iter().find(|&query_style| {
1185 matching_set
1186 .iter()
1187 .any(|&index| candidates[index].style == *query_style)
1188 })?;
1189
1190 matching_set.retain(|&index| candidates[index].style == matching_style);
1191
1192 let weight = query.weight.0;
1197
1198 let matching_weight = if matching_set
1199 .iter()
1200 .any(|&index| candidates[index].weight.0 == weight)
1201 {
1202 Weight(weight)
1203 } else if (400..450).contains(&weight)
1204 && matching_set
1205 .iter()
1206 .any(|&index| candidates[index].weight.0 == 500)
1207 {
1208 Weight::MEDIUM
1210 } else if (450..=500).contains(&weight)
1211 && matching_set
1212 .iter()
1213 .any(|&index| candidates[index].weight.0 == 400)
1214 {
1215 Weight::NORMAL
1217 } else if weight <= 500 {
1218 let idx = matching_set
1220 .iter()
1221 .filter(|&&index| candidates[index].weight.0 <= weight)
1222 .min_by_key(|&&index| weight - candidates[index].weight.0);
1223
1224 match idx {
1225 Some(&matching_index) => candidates[matching_index].weight,
1226 None => {
1227 let matching_index = *matching_set
1228 .iter()
1229 .min_by_key(|&&index| candidates[index].weight.0 - weight)?;
1230 candidates[matching_index].weight
1231 }
1232 }
1233 } else {
1234 let idx = matching_set
1236 .iter()
1237 .filter(|&&index| candidates[index].weight.0 >= weight)
1238 .min_by_key(|&&index| candidates[index].weight.0 - weight);
1239
1240 match idx {
1241 Some(&matching_index) => candidates[matching_index].weight,
1242 None => {
1243 let matching_index = *matching_set
1244 .iter()
1245 .min_by_key(|&&index| weight - candidates[index].weight.0)?;
1246 candidates[matching_index].weight
1247 }
1248 }
1249 };
1250 matching_set.retain(|&index| candidates[index].weight == matching_weight);
1251
1252 matching_set.into_iter().next()
1256}
1257
1258#[rustfmt::skip]
1262const MAC_ROMAN: &[u16; 256] = &[
1263 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
1264 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
1265 0x0010, 0x2318, 0x21E7, 0x2325, 0x2303, 0x0015, 0x0016, 0x0017,
1266 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
1267 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
1268 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
1269 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
1270 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
1271 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
1272 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
1273 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
1274 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
1275 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
1276 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
1277 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
1278 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
1279 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
1280 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
1281 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
1282 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
1283 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
1284 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
1285 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
1286 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
1287 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
1288 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
1289 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
1290 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
1291 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
1292 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
1293 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC,
1294 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7,
1295];