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
66mod ttf_parser;
67
68#[cfg(not(feature = "std"))]
69use alloc::{
70 string::{String, ToString},
71 vec::Vec,
72};
73
74pub use ttf_parser::Language;
75pub use ttf_parser::Width as Stretch;
76
77use slotmap::SlotMap;
78use tinyvec::TinyVec;
79
80#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Debug, Default)]
95pub struct ID(InnerId);
96
97slotmap::new_key_type! {
98 struct InnerId;
100}
101
102impl ID {
103 #[inline]
107 pub fn dummy() -> Self {
108 Self(InnerId::from(slotmap::KeyData::from_ffi(core::u64::MAX)))
109 }
110}
111
112impl core::fmt::Display for ID {
113 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
114 write!(f, "{}", (self.0).0.as_ffi())
115 }
116}
117
118#[derive(Debug)]
120enum LoadError {
121 MalformedFont,
126 UnnamedFont,
128 #[cfg(feature = "std")]
130 IoError(std::io::Error),
131}
132
133#[cfg(feature = "std")]
134impl From<std::io::Error> for LoadError {
135 #[inline]
136 fn from(e: std::io::Error) -> Self {
137 LoadError::IoError(e)
138 }
139}
140
141impl core::fmt::Display for LoadError {
142 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
143 match self {
144 LoadError::MalformedFont => write!(f, "malformed font"),
145 LoadError::UnnamedFont => write!(f, "font doesn't have a family name"),
146 #[cfg(feature = "std")]
147 LoadError::IoError(ref e) => write!(f, "{}", e),
148 }
149 }
150}
151
152#[derive(Clone, Debug)]
154pub struct Database {
155 faces: SlotMap<InnerId, FaceInfo>,
156 family_serif: String,
157 family_sans_serif: String,
158 family_cursive: String,
159 family_fantasy: String,
160 family_monospace: String,
161}
162
163impl Default for Database {
164 fn default() -> Self {
165 Self::new()
166 }
167}
168
169impl Database {
170 #[inline]
180 pub fn new() -> Self {
181 Database {
182 faces: SlotMap::with_key(),
183 family_serif: "Times New Roman".to_string(),
184 family_sans_serif: "Arial".to_string(),
185 family_cursive: "Comic Sans MS".to_string(),
186 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
187 family_fantasy: "Impact".to_string(),
188 #[cfg(any(target_os = "macos", target_os = "ios"))]
189 family_fantasy: "Papyrus".to_string(),
190 family_monospace: "Courier New".to_string(),
191 }
192 }
193
194 pub fn load_font_data(&mut self, data: Vec<u8>) {
198 self.load_font_source(Source::Binary(alloc::sync::Arc::new(data)));
199 }
200
201 pub fn load_font_source(&mut self, source: Source) -> TinyVec<[ID; 8]> {
206 let ids = source.with_data(|data| {
207 let n = ttf_parser::fonts_in_collection(data).unwrap_or(1);
208 let mut ids = TinyVec::with_capacity(n as usize);
209
210 for index in 0..n {
211 match parse_face_info(source.clone(), data, index) {
212 Ok(mut info) => {
213 let id = self.faces.insert_with_key(|k| {
214 info.id = ID(k);
215 info
216 });
217 ids.push(ID(id));
218 }
219 Err(e) => log::warn!(
220 "Failed to load a font face {} from source cause {}.",
221 index,
222 e
223 ),
224 }
225 }
226
227 ids
228 });
229
230 ids.unwrap_or_default()
231 }
232
233 #[cfg(feature = "fs")]
235 fn load_fonts_from_file(&mut self, path: &std::path::Path, data: &[u8]) {
236 let source = Source::File(path.into());
237
238 let n = ttf_parser::fonts_in_collection(data).unwrap_or(1);
239 for index in 0..n {
240 match parse_face_info(source.clone(), data, index) {
241 Ok(info) => {
242 self.push_face_info(info);
243 }
244 Err(e) => {
245 log::warn!(
246 "Failed to load a font face {} from '{}' cause {}.",
247 index,
248 path.display(),
249 e
250 )
251 }
252 }
253 }
254 }
255
256 #[cfg(all(feature = "fs", feature = "memmap"))]
260 pub fn load_font_file<P: AsRef<std::path::Path>>(
261 &mut self,
262 path: P,
263 ) -> Result<(), std::io::Error> {
264 self.load_font_file_impl(path.as_ref())
265 }
266
267 #[cfg(all(feature = "fs", feature = "memmap"))]
269 fn load_font_file_impl(&mut self, path: &std::path::Path) -> Result<(), std::io::Error> {
270 let file = std::fs::File::open(path)?;
271 let data: &[u8] = unsafe { &memmap2::MmapOptions::new().map(&file)? };
272
273 self.load_fonts_from_file(path, data);
274 Ok(())
275 }
276
277 #[cfg(all(feature = "fs", not(feature = "memmap")))]
281 pub fn load_font_file<P: AsRef<std::path::Path>>(
282 &mut self,
283 path: P,
284 ) -> Result<(), std::io::Error> {
285 self.load_font_file_impl(path.as_ref())
286 }
287
288 #[cfg(all(feature = "fs", not(feature = "memmap")))]
290 fn load_font_file_impl(&mut self, path: &std::path::Path) -> Result<(), std::io::Error> {
291 let data = std::fs::read(path)?;
292
293 self.load_fonts_from_file(path, &data);
294 Ok(())
295 }
296
297 #[cfg(feature = "fs")]
306 pub fn load_fonts_dir<P: AsRef<std::path::Path>>(&mut self, dir: P) {
307 self.load_fonts_dir_impl(dir.as_ref(), &mut Default::default())
308 }
309
310 #[cfg(feature = "fs")]
311 fn canonicalize(
312 &self,
313 path: std::path::PathBuf,
314 entry: std::fs::DirEntry,
315 seen: &mut std::collections::HashSet<std::path::PathBuf>,
316 ) -> Option<(std::path::PathBuf, std::fs::FileType)> {
317 let file_type = entry.file_type().ok()?;
318 if !file_type.is_symlink() {
319 if !seen.is_empty() {
320 if seen.contains(&path) {
321 return None;
322 }
323 seen.insert(path.clone());
324 }
325
326 return Some((path, file_type));
327 }
328
329 if seen.is_empty() && file_type.is_dir() {
330 seen.reserve(8192 / std::mem::size_of::<std::path::PathBuf>());
331
332 for (_, info) in self.faces.iter() {
333 let path = match &info.source {
334 Source::Binary(_) => continue,
335 Source::File(path) => path.to_path_buf(),
336 #[cfg(feature = "memmap")]
337 Source::SharedFile(path, _) => path.to_path_buf(),
338 };
339 seen.insert(path);
340 }
341 }
342
343 let stat = std::fs::metadata(&path).ok()?;
344 if stat.is_symlink() {
345 return None;
346 }
347
348 let canon = std::fs::canonicalize(path).ok()?;
349 if seen.contains(&canon) {
350 return None;
351 }
352 seen.insert(canon.clone());
353 Some((canon, stat.file_type()))
354 }
355
356 #[cfg(feature = "fs")]
358 fn load_fonts_dir_impl(
359 &mut self,
360 dir: &std::path::Path,
361 seen: &mut std::collections::HashSet<std::path::PathBuf>,
362 ) {
363 let fonts_dir = match std::fs::read_dir(dir) {
364 Ok(dir) => dir,
365 Err(_) => return,
366 };
367
368 for entry in fonts_dir.flatten() {
369 let (path, file_type) = match self.canonicalize(entry.path(), entry, seen) {
370 Some(v) => v,
371 None => continue,
372 };
373
374 if file_type.is_file() {
375 match path.extension().and_then(|e| e.to_str()) {
376 #[rustfmt::skip] Some("ttf") | Some("ttc") | Some("TTF") | Some("TTC") |
378 Some("otf") | Some("otc") | Some("OTF") | Some("OTC") => {
379 if let Err(e) = self.load_font_file(&path) {
380 log::warn!("Failed to load '{}' cause {}.", path.display(), e);
381 }
382 },
383 _ => {}
384 }
385 } else if file_type.is_dir() {
386 self.load_fonts_dir_impl(&path, seen);
387 }
388 }
389 }
390
391 #[cfg(feature = "fs")]
402 pub fn load_system_fonts(&mut self) {
403 #[cfg(target_os = "windows")]
404 {
405 let mut seen = Default::default();
406 if let Some(ref system_root) = std::env::var_os("SYSTEMROOT") {
407 let system_root_path = std::path::Path::new(system_root);
408 self.load_fonts_dir_impl(&system_root_path.join("Fonts"), &mut seen);
409 } else {
410 self.load_fonts_dir_impl("C:\\Windows\\Fonts\\".as_ref(), &mut seen);
411 }
412
413 if let Ok(ref home) = std::env::var("USERPROFILE") {
414 let home_path = std::path::Path::new(home);
415 self.load_fonts_dir_impl(
416 &home_path.join("AppData\\Local\\Microsoft\\Windows\\Fonts"),
417 &mut seen,
418 );
419 self.load_fonts_dir_impl(
420 &home_path.join("AppData\\Roaming\\Microsoft\\Windows\\Fonts"),
421 &mut seen,
422 );
423 }
424 }
425
426 #[cfg(any(target_os = "macos", target_os = "ios"))]
427 {
428 let mut seen = Default::default();
429 self.load_fonts_dir_impl("/Library/Fonts".as_ref(), &mut seen);
430 self.load_fonts_dir_impl("/System/Library/Fonts".as_ref(), &mut seen);
431 if let Ok(dir) = std::fs::read_dir("/System/Library/AssetsV2") {
433 for entry in dir {
434 let entry = match entry {
435 Ok(entry) => entry,
436 Err(_) => continue,
437 };
438 if entry
439 .file_name()
440 .to_string_lossy()
441 .starts_with("com_apple_MobileAsset_Font")
442 {
443 self.load_fonts_dir_impl(&entry.path(), &mut seen);
444 }
445 }
446 }
447 self.load_fonts_dir_impl("/Network/Library/Fonts".as_ref(), &mut seen);
448
449 if let Ok(ref home) = std::env::var("HOME") {
450 let home_path = std::path::Path::new(home);
451 self.load_fonts_dir_impl(&home_path.join("Library/Fonts"), &mut seen);
452 }
453 }
454
455 #[cfg(target_os = "redox")]
457 {
458 let mut seen = Default::default();
459 self.load_fonts_dir_impl("/ui/fonts".as_ref(), &mut seen);
460 }
461
462 #[cfg(all(unix, not(any(target_os = "macos", target_os = "ios", target_os = "android"))))]
464 {
465 #[cfg(feature = "fontconfig")]
466 {
467 if !self.load_fontconfig() {
468 log::warn!("Fallback to loading from known font dir paths.");
469 self.load_no_fontconfig();
470 }
471 }
472
473 #[cfg(not(feature = "fontconfig"))]
474 {
475 self.load_no_fontconfig();
476 }
477 }
478 }
479
480
481 #[cfg(all(
483 unix,
484 feature = "fs",
485 not(any(target_os = "macos", target_os = "ios", target_os = "android"))
486 ))]
487 fn load_no_fontconfig(&mut self) {
488 let mut seen = Default::default();
489 self.load_fonts_dir_impl("/usr/share/fonts/".as_ref(), &mut seen);
490 self.load_fonts_dir_impl("/usr/local/share/fonts/".as_ref(), &mut seen);
491
492 if let Ok(ref home) = std::env::var("HOME") {
493 let home_path = std::path::Path::new(home);
494 self.load_fonts_dir_impl(&home_path.join(".fonts"), &mut seen);
495 self.load_fonts_dir_impl(&home_path.join(".local/share/fonts"), &mut seen);
496 }
497 }
498
499 #[cfg(all(
501 unix,
502 feature = "fontconfig",
503 not(any(target_os = "macos", target_os = "ios", target_os = "android"))
504 ))]
505 fn load_fontconfig(&mut self) -> bool {
506 use std::path::Path;
507
508 let mut fontconfig = fontconfig_parser::FontConfig::default();
509 let home = std::env::var("HOME");
510
511 if let Ok(ref config_file) = std::env::var("FONTCONFIG_FILE") {
512 let _ = fontconfig.merge_config(Path::new(config_file));
513 } else {
514 let xdg_config_home = if let Ok(val) = std::env::var("XDG_CONFIG_HOME") {
515 Some(val.into())
516 } else if let Ok(ref home) = home {
517 Some(Path::new(home).join(".config"))
520 } else {
521 None
522 };
523
524 let read_global = match xdg_config_home {
525 Some(p) => fontconfig
526 .merge_config(&p.join("fontconfig/fonts.conf"))
527 .is_err(),
528 None => true,
529 };
530
531 if read_global {
532 let _ = fontconfig.merge_config(Path::new("/etc/fonts/local.conf"));
533 }
534 let _ = fontconfig.merge_config(Path::new("/etc/fonts/fonts.conf"));
535 }
536
537 for fontconfig_parser::Alias {
538 alias,
539 default,
540 prefer,
541 accept,
542 } in fontconfig.aliases
543 {
544 let name = prefer
545 .get(0)
546 .or_else(|| accept.get(0))
547 .or_else(|| default.get(0));
548
549 if let Some(name) = name {
550 match alias.to_lowercase().as_str() {
551 "serif" => self.set_serif_family(name),
552 "sans-serif" => self.set_sans_serif_family(name),
553 "sans serif" => self.set_sans_serif_family(name),
554 "monospace" => self.set_monospace_family(name),
555 "cursive" => self.set_cursive_family(name),
556 "fantasy" => self.set_fantasy_family(name),
557 _ => {}
558 }
559 }
560 }
561
562 if fontconfig.dirs.is_empty() {
563 return false;
564 }
565
566 let mut seen = Default::default();
567 for dir in fontconfig.dirs {
568 let path = if dir.path.starts_with("~") {
569 if let Ok(ref home) = home {
570 Path::new(home).join(dir.path.strip_prefix("~").unwrap())
571 } else {
572 continue;
573 }
574 } else {
575 dir.path
576 };
577 self.load_fonts_dir_impl(&path, &mut seen);
578 }
579
580 true
581 }
582
583 pub fn push_face_info(&mut self, mut info: FaceInfo) -> ID {
590 ID(self.faces.insert_with_key(|k| {
591 info.id = ID(k);
592 info
593 }))
594 }
595
596 pub fn remove_face(&mut self, id: ID) {
604 self.faces.remove(id.0);
605 }
606
607 #[inline]
609 pub fn is_empty(&self) -> bool {
610 self.faces.is_empty()
611 }
612
613 #[inline]
619 pub fn len(&self) -> usize {
620 self.faces.len()
621 }
622
623 pub fn set_serif_family<S: Into<String>>(&mut self, family: S) {
625 self.family_serif = family.into();
626 }
627
628 pub fn set_sans_serif_family<S: Into<String>>(&mut self, family: S) {
630 self.family_sans_serif = family.into();
631 }
632
633 pub fn set_cursive_family<S: Into<String>>(&mut self, family: S) {
635 self.family_cursive = family.into();
636 }
637
638 pub fn set_fantasy_family<S: Into<String>>(&mut self, family: S) {
640 self.family_fantasy = family.into();
641 }
642
643 pub fn set_monospace_family<S: Into<String>>(&mut self, family: S) {
645 self.family_monospace = family.into();
646 }
647
648 pub fn family_name<'a>(&'a self, family: &'a Family) -> &'a str {
652 match family {
653 Family::Name(name) => name,
654 Family::Serif => self.family_serif.as_str(),
655 Family::SansSerif => self.family_sans_serif.as_str(),
656 Family::Cursive => self.family_cursive.as_str(),
657 Family::Fantasy => self.family_fantasy.as_str(),
658 Family::Monospace => self.family_monospace.as_str(),
659 }
660 }
661
662 pub fn query(&self, query: &Query) -> Option<ID> {
664 for family in query.families {
665 let name = self.family_name(family);
666 let candidates: Vec<_> = self
667 .faces
668 .iter()
669 .filter(|(_, face)| face.families.iter().any(|family| family.0 == name))
670 .map(|(_, info)| info)
671 .collect();
672
673 if !candidates.is_empty() {
674 if let Some(index) = find_best_match(&candidates, query) {
675 return Some(candidates[index].id);
676 }
677 }
678 }
679
680 None
681 }
682
683 #[inline]
687 pub fn faces(&self) -> impl Iterator<Item = &FaceInfo> + '_ {
688 self.faces.iter().map(|(_, info)| info)
689 }
690
691 pub fn face(&self, id: ID) -> Option<&FaceInfo> {
696 self.faces.get(id.0)
697 }
698
699 pub fn face_source(&self, id: ID) -> Option<(Source, u32)> {
701 self.face(id).map(|info| (info.source.clone(), info.index))
702 }
703
704 pub fn with_face_data<P, T>(&self, id: ID, p: P) -> Option<T>
724 where
725 P: FnOnce(&[u8], u32) -> T,
726 {
727 let (src, face_index) = self.face_source(id)?;
728 src.with_data(|data| p(data, face_index))
729 }
730
731 #[cfg(all(feature = "fs", feature = "memmap"))]
744 pub unsafe fn make_shared_face_data(
745 &mut self,
746 id: ID,
747 ) -> Option<(std::sync::Arc<dyn AsRef<[u8]> + Send + Sync>, u32)> {
748 let face_info = self.faces.get(id.0)?;
749 let face_index = face_info.index;
750
751 let old_source = face_info.source.clone();
752
753 let (path, shared_data) = match &old_source {
754 Source::Binary(data) => {
755 return Some((data.clone(), face_index));
756 }
757 Source::File(ref path) => {
758 let file = std::fs::File::open(path).ok()?;
759 let shared_data = std::sync::Arc::new(memmap2::MmapOptions::new().map(&file).ok()?)
760 as std::sync::Arc<dyn AsRef<[u8]> + Send + Sync>;
761 (path.clone(), shared_data)
762 }
763 Source::SharedFile(_, data) => {
764 return Some((data.clone(), face_index));
765 }
766 };
767
768 let shared_source = Source::SharedFile(path.clone(), shared_data.clone());
769
770 self.faces.iter_mut().for_each(|(_, face)| {
771 if matches!(&face.source, Source::File(old_path) if old_path == &path) {
772 face.source = shared_source.clone();
773 }
774 });
775
776 Some((shared_data, face_index))
777 }
778
779 #[cfg(all(feature = "fs", feature = "memmap"))]
783 pub fn make_face_data_unshared(&mut self, id: ID) {
784 let face_info = match self.faces.get(id.0) {
785 Some(face_info) => face_info,
786 None => return,
787 };
788
789 let old_source = face_info.source.clone();
790
791 let shared_path = match old_source {
792 #[cfg(all(feature = "fs", feature = "memmap"))]
793 Source::SharedFile(path, _) => path,
794 _ => return,
795 };
796
797 let new_source = Source::File(shared_path.clone());
798
799 self.faces.iter_mut().for_each(|(_, face)| {
800 if matches!(&face.source, Source::SharedFile(path, ..) if path == &shared_path) {
801 face.source = new_source.clone();
802 }
803 });
804 }
805}
806
807#[derive(Clone, Debug)]
813pub struct FaceInfo {
814 pub id: ID,
816
817 pub source: Source,
822
823 pub index: u32,
825
826 pub families: Vec<(String, Language)>,
839
840 pub post_script_name: String,
846
847 pub style: Style,
849
850 pub weight: Weight,
852
853 pub stretch: Stretch,
855
856 pub monospaced: bool,
858}
859
860#[derive(Clone)]
866pub enum Source {
867 Binary(alloc::sync::Arc<dyn AsRef<[u8]> + Sync + Send>),
869
870 #[cfg(feature = "fs")]
872 File(std::path::PathBuf),
873
874 #[cfg(all(feature = "fs", feature = "memmap"))]
876 SharedFile(
877 std::path::PathBuf,
878 std::sync::Arc<dyn AsRef<[u8]> + Sync + Send>,
879 ),
880}
881
882impl core::fmt::Debug for Source {
883 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
884 match self {
885 Self::Binary(arg0) => f
886 .debug_tuple("SharedBinary")
887 .field(&arg0.as_ref().as_ref())
888 .finish(),
889 #[cfg(feature = "fs")]
890 Self::File(arg0) => f.debug_tuple("File").field(arg0).finish(),
891 #[cfg(all(feature = "fs", feature = "memmap"))]
892 Self::SharedFile(arg0, arg1) => f
893 .debug_tuple("SharedFile")
894 .field(arg0)
895 .field(&arg1.as_ref().as_ref())
896 .finish(),
897 }
898 }
899}
900
901impl Source {
902 fn with_data<P, T>(&self, p: P) -> Option<T>
903 where
904 P: FnOnce(&[u8]) -> T,
905 {
906 match &self {
907 #[cfg(all(feature = "fs", not(feature = "memmap")))]
908 Source::File(ref path) => {
909 let data = std::fs::read(path).ok()?;
910
911 Some(p(&data))
912 }
913 #[cfg(all(feature = "fs", feature = "memmap"))]
914 Source::File(ref path) => {
915 let file = std::fs::File::open(path).ok()?;
916 let data = unsafe { &memmap2::MmapOptions::new().map(&file).ok()? };
917
918 Some(p(data))
919 }
920 Source::Binary(ref data) => Some(p(data.as_ref().as_ref())),
921 #[cfg(all(feature = "fs", feature = "memmap"))]
922 Source::SharedFile(_, ref data) => Some(p(data.as_ref().as_ref())),
923 }
924 }
925}
926
927#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Hash)]
931pub struct Query<'a> {
932 pub families: &'a [Family<'a>],
936
937 pub weight: Weight,
941
942 pub stretch: Stretch,
946
947 pub style: Style,
951}
952
953#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
956pub enum Family<'a> {
957 Name(&'a str),
965
966 Serif,
968
969 SansSerif,
973
974 Cursive,
977
978 Fantasy,
981
982 Monospace,
984}
985
986#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
988pub struct Weight(pub u16);
989
990impl Default for Weight {
991 #[inline]
992 fn default() -> Weight {
993 Weight::NORMAL
994 }
995}
996
997impl Weight {
998 pub const THIN: Weight = Weight(100);
1000 pub const EXTRA_LIGHT: Weight = Weight(200);
1002 pub const LIGHT: Weight = Weight(300);
1004 pub const NORMAL: Weight = Weight(400);
1006 pub const MEDIUM: Weight = Weight(500);
1008 pub const SEMIBOLD: Weight = Weight(600);
1010 pub const BOLD: Weight = Weight(700);
1012 pub const EXTRA_BOLD: Weight = Weight(800);
1014 pub const BLACK: Weight = Weight(900);
1016}
1017
1018#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1020pub enum Style {
1021 Normal,
1023 Italic,
1025 Oblique,
1027}
1028
1029impl Default for Style {
1030 #[inline]
1031 fn default() -> Style {
1032 Style::Normal
1033 }
1034}
1035
1036fn parse_face_info(source: Source, data: &[u8], index: u32) -> Result<FaceInfo, LoadError> {
1037 let raw_face = ttf_parser::RawFace::parse(data, index).map_err(|_| LoadError::MalformedFont)?;
1038 let (families, post_script_name) = parse_names(&raw_face).ok_or(LoadError::UnnamedFont)?;
1039 let (mut style, weight, stretch) = parse_os2(&raw_face);
1040 let (monospaced, italic) = parse_post(&raw_face);
1041
1042 if style == Style::Normal && italic {
1043 style = Style::Italic;
1044 }
1045
1046 Ok(FaceInfo {
1047 id: ID::dummy(),
1048 source,
1049 index,
1050 families,
1051 post_script_name,
1052 style,
1053 weight,
1054 stretch,
1055 monospaced,
1056 })
1057}
1058
1059fn parse_names(raw_face: &ttf_parser::RawFace) -> Option<(Vec<(String, Language)>, String)> {
1060 const NAME_TAG: ttf_parser::Tag = ttf_parser::Tag::from_bytes(b"name");
1061 let name_data = raw_face.table(NAME_TAG)?;
1062 let name_table = ttf_parser::name::Table::parse(name_data)?;
1063
1064 let mut families = collect_families(ttf_parser::name_id::TYPOGRAPHIC_FAMILY, &name_table.names);
1065
1066 if families.is_empty() {
1068 families = collect_families(ttf_parser::name_id::FAMILY, &name_table.names);
1069 }
1070
1071 if families.len() > 1 {
1073 if let Some(index) = families
1074 .iter()
1075 .position(|f| f.1 == Language::English_UnitedStates)
1076 {
1077 if index != 0 {
1078 families.swap(0, index);
1079 }
1080 }
1081 }
1082
1083 if families.is_empty() {
1084 return None;
1085 }
1086
1087 let post_script_name = name_table
1088 .names
1089 .into_iter()
1090 .find(|name| {
1091 name.name_id == ttf_parser::name_id::POST_SCRIPT_NAME && name.is_supported_encoding()
1092 })
1093 .and_then(|name| name_to_unicode(&name))?;
1094
1095 Some((families, post_script_name))
1096}
1097
1098fn collect_families(name_id: u16, names: &ttf_parser::name::Names) -> Vec<(String, Language)> {
1099 let mut families = Vec::new();
1100 for name in names.into_iter() {
1101 if name.name_id == name_id && name.is_unicode() {
1102 if let Some(family) = name_to_unicode(&name) {
1103 families.push((family, name.language()));
1104 }
1105 }
1106 }
1107
1108 if !families
1110 .iter()
1111 .any(|f| f.1 == Language::English_UnitedStates)
1112 {
1113 for name in names.into_iter() {
1114 if name.name_id == name_id && name.is_mac_roman() {
1115 if let Some(family) = name_to_unicode(&name) {
1116 families.push((family, name.language()));
1117 break;
1118 }
1119 }
1120 }
1121 }
1122
1123 families
1124}
1125
1126fn name_to_unicode(name: &ttf_parser::name::Name) -> Option<String> {
1127 if name.is_unicode() {
1128 let mut raw_data: Vec<u16> = Vec::new();
1129 for c in ttf_parser::LazyArray16::<u16>::new(name.name) {
1130 raw_data.push(c);
1131 }
1132
1133 String::from_utf16(&raw_data).ok()
1134 } else if name.is_mac_roman() {
1135 let mut raw_data = Vec::with_capacity(name.name.len());
1137 for b in name.name {
1138 raw_data.push(MAC_ROMAN[*b as usize]);
1139 }
1140
1141 String::from_utf16(&raw_data).ok()
1142 } else {
1143 None
1144 }
1145}
1146
1147fn parse_os2(raw_face: &ttf_parser::RawFace) -> (Style, Weight, Stretch) {
1148 const OS2_TAG: ttf_parser::Tag = ttf_parser::Tag::from_bytes(b"OS/2");
1149 let table = match raw_face
1150 .table(OS2_TAG)
1151 .and_then(ttf_parser::os2::Table::parse)
1152 {
1153 Some(table) => table,
1154 None => return (Style::Normal, Weight::NORMAL, Stretch::Normal),
1155 };
1156
1157 let style = match table.style() {
1158 ttf_parser::Style::Normal => Style::Normal,
1159 ttf_parser::Style::Italic => Style::Italic,
1160 ttf_parser::Style::Oblique => Style::Oblique,
1161 };
1162
1163 let weight = table.weight();
1164 let stretch = table.width();
1165
1166 (style, Weight(weight.to_number()), stretch)
1167}
1168
1169fn parse_post(raw_face: &ttf_parser::RawFace) -> (bool, bool) {
1170 const POST_TAG: ttf_parser::Tag = ttf_parser::Tag::from_bytes(b"post");
1174 let data = match raw_face.table(POST_TAG) {
1175 Some(v) => v,
1176 None => return (false, false),
1177 };
1178
1179 let monospaced = data.get(12..16) != Some(&[0, 0, 0, 0]);
1181
1182 let italic = data.get(4..8) != Some(&[0, 0, 0, 0]);
1184
1185 (monospaced, italic)
1186}
1187
1188trait NameExt {
1189 fn is_mac_roman(&self) -> bool;
1190 fn is_supported_encoding(&self) -> bool;
1191}
1192
1193impl NameExt for ttf_parser::name::Name<'_> {
1194 #[inline]
1195 fn is_mac_roman(&self) -> bool {
1196 use ttf_parser::PlatformId::Macintosh;
1197 const MACINTOSH_ROMAN_ENCODING_ID: u16 = 0;
1199
1200 self.platform_id == Macintosh && self.encoding_id == MACINTOSH_ROMAN_ENCODING_ID
1201 }
1202
1203 #[inline]
1204 fn is_supported_encoding(&self) -> bool {
1205 self.is_unicode() || self.is_mac_roman()
1206 }
1207}
1208
1209#[inline(never)]
1212fn find_best_match(candidates: &[&FaceInfo], query: &Query) -> Option<usize> {
1213 debug_assert!(!candidates.is_empty());
1214
1215 let mut matching_set: Vec<usize> = (0..candidates.len()).collect();
1217
1218 let matches = matching_set
1220 .iter()
1221 .any(|&index| candidates[index].stretch == query.stretch);
1222 let matching_stretch = if matches {
1223 query.stretch
1225 } else if query.stretch <= Stretch::Normal {
1226 let stretch = matching_set
1228 .iter()
1229 .filter(|&&index| candidates[index].stretch < query.stretch)
1230 .min_by_key(|&&index| {
1231 query.stretch.to_number() - candidates[index].stretch.to_number()
1232 });
1233
1234 match stretch {
1235 Some(&matching_index) => candidates[matching_index].stretch,
1236 None => {
1237 let matching_index = *matching_set.iter().min_by_key(|&&index| {
1238 candidates[index].stretch.to_number() - query.stretch.to_number()
1239 })?;
1240
1241 candidates[matching_index].stretch
1242 }
1243 }
1244 } else {
1245 let stretch = matching_set
1247 .iter()
1248 .filter(|&&index| candidates[index].stretch > query.stretch)
1249 .min_by_key(|&&index| {
1250 candidates[index].stretch.to_number() - query.stretch.to_number()
1251 });
1252
1253 match stretch {
1254 Some(&matching_index) => candidates[matching_index].stretch,
1255 None => {
1256 let matching_index = *matching_set.iter().min_by_key(|&&index| {
1257 query.stretch.to_number() - candidates[index].stretch.to_number()
1258 })?;
1259
1260 candidates[matching_index].stretch
1261 }
1262 }
1263 };
1264 matching_set.retain(|&index| candidates[index].stretch == matching_stretch);
1265
1266 let style_preference = match query.style {
1268 Style::Italic => [Style::Italic, Style::Oblique, Style::Normal],
1269 Style::Oblique => [Style::Oblique, Style::Italic, Style::Normal],
1270 Style::Normal => [Style::Normal, Style::Oblique, Style::Italic],
1271 };
1272 let matching_style = *style_preference.iter().find(|&query_style| {
1273 matching_set
1274 .iter()
1275 .any(|&index| candidates[index].style == *query_style)
1276 })?;
1277
1278 matching_set.retain(|&index| candidates[index].style == matching_style);
1279
1280 let weight = query.weight.0;
1285
1286 let matching_weight = if matching_set
1287 .iter()
1288 .any(|&index| candidates[index].weight.0 == weight)
1289 {
1290 Weight(weight)
1291 } else if (400..450).contains(&weight)
1292 && matching_set
1293 .iter()
1294 .any(|&index| candidates[index].weight.0 == 500)
1295 {
1296 Weight::MEDIUM
1298 } else if (450..=500).contains(&weight)
1299 && matching_set
1300 .iter()
1301 .any(|&index| candidates[index].weight.0 == 400)
1302 {
1303 Weight::NORMAL
1305 } else if weight <= 500 {
1306 let idx = matching_set
1308 .iter()
1309 .filter(|&&index| candidates[index].weight.0 <= weight)
1310 .min_by_key(|&&index| weight - candidates[index].weight.0);
1311
1312 match idx {
1313 Some(&matching_index) => candidates[matching_index].weight,
1314 None => {
1315 let matching_index = *matching_set
1316 .iter()
1317 .min_by_key(|&&index| candidates[index].weight.0 - weight)?;
1318 candidates[matching_index].weight
1319 }
1320 }
1321 } else {
1322 let idx = matching_set
1324 .iter()
1325 .filter(|&&index| candidates[index].weight.0 >= weight)
1326 .min_by_key(|&&index| candidates[index].weight.0 - weight);
1327
1328 match idx {
1329 Some(&matching_index) => candidates[matching_index].weight,
1330 None => {
1331 let matching_index = *matching_set
1332 .iter()
1333 .min_by_key(|&&index| weight - candidates[index].weight.0)?;
1334 candidates[matching_index].weight
1335 }
1336 }
1337 };
1338 matching_set.retain(|&index| candidates[index].weight == matching_weight);
1339
1340 matching_set.into_iter().next()
1344}
1345
1346#[rustfmt::skip]
1350const MAC_ROMAN: &[u16; 256] = &[
1351 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
1352 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
1353 0x0010, 0x2318, 0x21E7, 0x2325, 0x2303, 0x0015, 0x0016, 0x0017,
1354 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
1355 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
1356 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
1357 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
1358 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
1359 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
1360 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
1361 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
1362 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
1363 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
1364 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
1365 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
1366 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
1367 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
1368 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
1369 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
1370 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
1371 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
1372 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
1373 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
1374 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
1375 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
1376 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
1377 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
1378 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
1379 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
1380 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
1381 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC,
1382 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7,
1383];