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) => {
240                    self.push_face_info(info);
241                }
242                Err(e) => {
243                    log::warn!(
244                        "Failed to load a font face {} from '{}' cause {}.",
245                        index,
246                        path.display(),
247                        e
248                    )
249                }
250            }
251        }
252    }
253
254    #[cfg(all(feature = "fs", feature = "memmap"))]
258    pub fn load_font_file<P: AsRef<std::path::Path>>(
259        &mut self,
260        path: P,
261    ) -> Result<(), std::io::Error> {
262        self.load_font_file_impl(path.as_ref())
263    }
264
265    #[cfg(all(feature = "fs", feature = "memmap"))]
267    fn load_font_file_impl(&mut self, path: &std::path::Path) -> Result<(), std::io::Error> {
268        let file = std::fs::File::open(path)?;
269        let data: &[u8] = unsafe { &memmap2::MmapOptions::new().map(&file)? };
270
271        self.load_fonts_from_file(path, data);
272        Ok(())
273    }
274
275    #[cfg(all(feature = "fs", not(feature = "memmap")))]
279    pub fn load_font_file<P: AsRef<std::path::Path>>(
280        &mut self,
281        path: P,
282    ) -> Result<(), std::io::Error> {
283        self.load_font_file_impl(path.as_ref())
284    }
285
286    #[cfg(all(feature = "fs", not(feature = "memmap")))]
288    fn load_font_file_impl(&mut self, path: &std::path::Path) -> Result<(), std::io::Error> {
289        let data = std::fs::read(path)?;
290
291        self.load_fonts_from_file(path, &data);
292        Ok(())
293    }
294
295    #[cfg(feature = "fs")]
304    pub fn load_fonts_dir<P: AsRef<std::path::Path>>(&mut self, dir: P) {
305        self.load_fonts_dir_impl(dir.as_ref(), &mut Default::default())
306    }
307
308    #[cfg(feature = "fs")]
309    fn canonicalize(
310        &self,
311        path: std::path::PathBuf,
312        entry: std::fs::DirEntry,
313        seen: &mut std::collections::HashSet<std::path::PathBuf>,
314    ) -> Option<(std::path::PathBuf, std::fs::FileType)> {
315        let file_type = entry.file_type().ok()?;
316        if !file_type.is_symlink() {
317            if !seen.is_empty() {
318                if seen.contains(&path) {
319                    return None;
320                }
321                seen.insert(path.clone());
322            }
323
324            return Some((path, file_type));
325        }
326
327        if seen.is_empty() && file_type.is_dir() {
328            seen.reserve(8192 / std::mem::size_of::<std::path::PathBuf>());
329
330            for (_, info) in self.faces.iter() {
331                let path = match &info.source {
332                    Source::Binary(_) => continue,
333                    Source::File(path) => path.to_path_buf(),
334                    #[cfg(feature = "memmap")]
335                    Source::SharedFile(path, _) => path.to_path_buf(),
336                };
337                seen.insert(path);
338            }
339        }
340
341        let stat = std::fs::metadata(&path).ok()?;
342        if stat.is_symlink() {
343            return None;
344        }
345
346        let canon = std::fs::canonicalize(path).ok()?;
347        if seen.contains(&canon) {
348            return None;
349        }
350        seen.insert(canon.clone());
351        Some((canon, stat.file_type()))
352    }
353
354    #[cfg(feature = "fs")]
356    fn load_fonts_dir_impl(
357        &mut self,
358        dir: &std::path::Path,
359        seen: &mut std::collections::HashSet<std::path::PathBuf>,
360    ) {
361        let fonts_dir = match std::fs::read_dir(dir) {
362            Ok(dir) => dir,
363            Err(_) => return,
364        };
365
366        for entry in fonts_dir.flatten() {
367            let (path, file_type) = match self.canonicalize(entry.path(), entry, seen) {
368                Some(v) => v,
369                None => continue,
370            };
371
372            if file_type.is_file() {
373                match path.extension().and_then(|e| e.to_str()) {
374                    #[rustfmt::skip] Some("ttf") | Some("ttc") | Some("TTF") | Some("TTC") |
376                    Some("otf") | Some("otc") | Some("OTF") | Some("OTC") => {
377                        if let Err(e) = self.load_font_file(&path) {
378                            log::warn!("Failed to load '{}' cause {}.", path.display(), e);
379                        }
380                    },
381                    _ => {}
382                }
383            } else if file_type.is_dir() {
384                self.load_fonts_dir_impl(&path, seen);
385            }
386        }
387    }
388
389    #[cfg(feature = "fs")]
400    pub fn load_system_fonts(&mut self) {
401        #[cfg(target_os = "windows")]
402        {
403            let mut seen = Default::default();
404            if let Some(ref system_root) = std::env::var_os("SYSTEMROOT") {
405                let system_root_path = std::path::Path::new(system_root);
406                self.load_fonts_dir_impl(&system_root_path.join("Fonts"), &mut seen);
407            } else {
408                self.load_fonts_dir_impl("C:\\Windows\\Fonts\\".as_ref(), &mut seen);
409            }
410
411            if let Ok(ref home) = std::env::var("USERPROFILE") {
412                let home_path = std::path::Path::new(home);
413                self.load_fonts_dir_impl(
414                    &home_path.join("AppData\\Local\\Microsoft\\Windows\\Fonts"),
415                    &mut seen,
416                );
417                self.load_fonts_dir_impl(
418                    &home_path.join("AppData\\Roaming\\Microsoft\\Windows\\Fonts"),
419                    &mut seen,
420                );
421            }
422        }
423
424        #[cfg(target_os = "macos")]
425        {
426            let mut seen = Default::default();
427            self.load_fonts_dir_impl("/Library/Fonts".as_ref(), &mut seen);
428            self.load_fonts_dir_impl("/System/Library/Fonts".as_ref(), &mut seen);
429            if let Ok(dir) = std::fs::read_dir("/System/Library/AssetsV2") {
431                for entry in dir {
432                    let entry = match entry {
433                        Ok(entry) => entry,
434                        Err(_) => continue,
435                    };
436                    if entry
437                        .file_name()
438                        .to_string_lossy()
439                        .starts_with("com_apple_MobileAsset_Font")
440                    {
441                        self.load_fonts_dir_impl(&entry.path(), &mut seen);
442                    }
443                }
444            }
445            self.load_fonts_dir_impl("/Network/Library/Fonts".as_ref(), &mut seen);
446
447            if let Ok(ref home) = std::env::var("HOME") {
448                let home_path = std::path::Path::new(home);
449                self.load_fonts_dir_impl(&home_path.join("Library/Fonts"), &mut seen);
450            }
451        }
452
453        #[cfg(target_os = "redox")]
455        {
456            let mut seen = Default::default();
457            self.load_fonts_dir_impl("/ui/fonts".as_ref(), &mut seen);
458        }
459
460        #[cfg(all(unix, not(any(target_os = "macos", target_os = "android"))))]
462        {
463            #[cfg(feature = "fontconfig")]
464            {
465                if !self.load_fontconfig() {
466                    log::warn!("Fallback to loading from known font dir paths.");
467                    self.load_no_fontconfig();
468                }
469            }
470
471            #[cfg(not(feature = "fontconfig"))]
472            {
473                self.load_no_fontconfig();
474            }
475        }
476    }
477
478
479    #[cfg(all(
481        unix,
482        feature = "fs",
483        not(any(target_os = "macos", target_os = "android"))
484    ))]
485    fn load_no_fontconfig(&mut self) {
486        let mut seen = Default::default();
487        self.load_fonts_dir_impl("/usr/share/fonts/".as_ref(), &mut seen);
488        self.load_fonts_dir_impl("/usr/local/share/fonts/".as_ref(), &mut seen);
489
490        if let Ok(ref home) = std::env::var("HOME") {
491            let home_path = std::path::Path::new(home);
492            self.load_fonts_dir_impl(&home_path.join(".fonts"), &mut seen);
493            self.load_fonts_dir_impl(&home_path.join(".local/share/fonts"), &mut seen);
494        }
495    }
496
497    #[cfg(all(
499        unix,
500        feature = "fontconfig",
501        not(any(target_os = "macos", target_os = "android"))
502    ))]
503    fn load_fontconfig(&mut self) -> bool {
504        use std::path::Path;
505
506        let mut fontconfig = fontconfig_parser::FontConfig::default();
507        let home = std::env::var("HOME");
508
509        if let Ok(ref config_file) = std::env::var("FONTCONFIG_FILE") {
510            let _ = fontconfig.merge_config(Path::new(config_file));
511        } else {
512            let xdg_config_home = if let Ok(val) = std::env::var("XDG_CONFIG_HOME") {
513                Some(val.into())
514            } else if let Ok(ref home) = home {
515                Some(Path::new(home).join(".config"))
518            } else {
519                None
520            };
521
522            let read_global = match xdg_config_home {
523                Some(p) => fontconfig
524                    .merge_config(&p.join("fontconfig/fonts.conf"))
525                    .is_err(),
526                None => true,
527            };
528
529            if read_global {
530                let _ = fontconfig.merge_config(Path::new("/etc/fonts/local.conf"));
531            }
532            let _ = fontconfig.merge_config(Path::new("/etc/fonts/fonts.conf"));
533        }
534
535        for fontconfig_parser::Alias {
536            alias,
537            default,
538            prefer,
539            accept,
540        } in fontconfig.aliases
541        {
542            let name = prefer
543                .get(0)
544                .or_else(|| accept.get(0))
545                .or_else(|| default.get(0));
546
547            if let Some(name) = name {
548                match alias.to_lowercase().as_str() {
549                    "serif" => self.set_serif_family(name),
550                    "sans-serif" => self.set_sans_serif_family(name),
551                    "sans serif" => self.set_sans_serif_family(name),
552                    "monospace" => self.set_monospace_family(name),
553                    "cursive" => self.set_cursive_family(name),
554                    "fantasy" => self.set_fantasy_family(name),
555                    _ => {}
556                }
557            }
558        }
559
560        if fontconfig.dirs.is_empty() {
561            return false;
562        }
563
564        let mut seen = Default::default();
565        for dir in fontconfig.dirs {
566            let path = if dir.path.starts_with("~") {
567                if let Ok(ref home) = home {
568                    Path::new(home).join(dir.path.strip_prefix("~").unwrap())
569                } else {
570                    continue;
571                }
572            } else {
573                dir.path
574            };
575            self.load_fonts_dir_impl(&path, &mut seen);
576        }
577
578        true
579    }
580
581    pub fn push_face_info(&mut self, mut info: FaceInfo) -> ID {
588        ID(self.faces.insert_with_key(|k| {
589            info.id = ID(k);
590            info
591        }))
592    }
593
594    pub fn remove_face(&mut self, id: ID) {
602        self.faces.remove(id.0);
603    }
604
605    #[inline]
607    pub fn is_empty(&self) -> bool {
608        self.faces.is_empty()
609    }
610
611    #[inline]
617    pub fn len(&self) -> usize {
618        self.faces.len()
619    }
620
621    pub fn set_serif_family<S: Into<String>>(&mut self, family: S) {
623        self.family_serif = family.into();
624    }
625
626    pub fn set_sans_serif_family<S: Into<String>>(&mut self, family: S) {
628        self.family_sans_serif = family.into();
629    }
630
631    pub fn set_cursive_family<S: Into<String>>(&mut self, family: S) {
633        self.family_cursive = family.into();
634    }
635
636    pub fn set_fantasy_family<S: Into<String>>(&mut self, family: S) {
638        self.family_fantasy = family.into();
639    }
640
641    pub fn set_monospace_family<S: Into<String>>(&mut self, family: S) {
643        self.family_monospace = family.into();
644    }
645
646    pub fn family_name<'a>(&'a self, family: &'a Family) -> &'a str {
650        match family {
651            Family::Name(name) => name,
652            Family::Serif => self.family_serif.as_str(),
653            Family::SansSerif => self.family_sans_serif.as_str(),
654            Family::Cursive => self.family_cursive.as_str(),
655            Family::Fantasy => self.family_fantasy.as_str(),
656            Family::Monospace => self.family_monospace.as_str(),
657        }
658    }
659
660    pub fn query(&self, query: &Query) -> Option<ID> {
662        for family in query.families {
663            let name = self.family_name(family);
664            let candidates: Vec<_> = self
665                .faces
666                .iter()
667                .filter(|(_, face)| face.families.iter().any(|family| family.0 == name))
668                .map(|(_, info)| info)
669                .collect();
670
671            if !candidates.is_empty() {
672                if let Some(index) = find_best_match(&candidates, query) {
673                    return Some(candidates[index].id);
674                }
675            }
676        }
677
678        None
679    }
680
681    #[inline]
685    pub fn faces(&self) -> impl Iterator<Item = &FaceInfo> + '_ {
686        self.faces.iter().map(|(_, info)| info)
687    }
688
689    pub fn face(&self, id: ID) -> Option<&FaceInfo> {
694        self.faces.get(id.0)
695    }
696
697    pub fn face_source(&self, id: ID) -> Option<(Source, u32)> {
699        self.face(id).map(|info| (info.source.clone(), info.index))
700    }
701
702    pub fn with_face_data<P, T>(&self, id: ID, p: P) -> Option<T>
722    where
723        P: FnOnce(&[u8], u32) -> T,
724    {
725        let (src, face_index) = self.face_source(id)?;
726        src.with_data(|data| p(data, face_index))
727    }
728
729    #[cfg(all(feature = "fs", feature = "memmap"))]
742    pub unsafe fn make_shared_face_data(
743        &mut self,
744        id: ID,
745    ) -> Option<(std::sync::Arc<dyn AsRef<[u8]> + Send + Sync>, u32)> {
746        let face_info = self.faces.get(id.0)?;
747        let face_index = face_info.index;
748
749        let old_source = face_info.source.clone();
750
751        let (path, shared_data) = match &old_source {
752            Source::Binary(data) => {
753                return Some((data.clone(), face_index));
754            }
755            Source::File(ref path) => {
756                let file = std::fs::File::open(path).ok()?;
757                let shared_data = std::sync::Arc::new(memmap2::MmapOptions::new().map(&file).ok()?)
758                    as std::sync::Arc<dyn AsRef<[u8]> + Send + Sync>;
759                (path.clone(), shared_data)
760            }
761            Source::SharedFile(_, data) => {
762                return Some((data.clone(), face_index));
763            }
764        };
765
766        let shared_source = Source::SharedFile(path.clone(), shared_data.clone());
767
768        self.faces.iter_mut().for_each(|(_, face)| {
769            if matches!(&face.source, Source::File(old_path) if old_path == &path) {
770                face.source = shared_source.clone();
771            }
772        });
773
774        Some((shared_data, face_index))
775    }
776
777    #[cfg(all(feature = "fs", feature = "memmap"))]
781    pub fn make_face_data_unshared(&mut self, id: ID) {
782        let face_info = match self.faces.get(id.0) {
783            Some(face_info) => face_info,
784            None => return,
785        };
786
787        let old_source = face_info.source.clone();
788
789        let shared_path = match old_source {
790            #[cfg(all(feature = "fs", feature = "memmap"))]
791            Source::SharedFile(path, _) => path,
792            _ => return,
793        };
794
795        let new_source = Source::File(shared_path.clone());
796
797        self.faces.iter_mut().for_each(|(_, face)| {
798            if matches!(&face.source, Source::SharedFile(path, ..) if path == &shared_path) {
799                face.source = new_source.clone();
800            }
801        });
802    }
803}
804
805#[derive(Clone, Debug)]
811pub struct FaceInfo {
812    pub id: ID,
814
815    pub source: Source,
820
821    pub index: u32,
823
824    pub families: Vec<(String, Language)>,
837
838    pub post_script_name: String,
844
845    pub style: Style,
847
848    pub weight: Weight,
850
851    pub stretch: Stretch,
853
854    pub monospaced: bool,
856}
857
858#[derive(Clone)]
864pub enum Source {
865    Binary(alloc::sync::Arc<dyn AsRef<[u8]> + Sync + Send>),
867
868    #[cfg(feature = "fs")]
870    File(std::path::PathBuf),
871
872    #[cfg(all(feature = "fs", feature = "memmap"))]
874    SharedFile(
875        std::path::PathBuf,
876        std::sync::Arc<dyn AsRef<[u8]> + Sync + Send>,
877    ),
878}
879
880impl core::fmt::Debug for Source {
881    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
882        match self {
883            Self::Binary(arg0) => f
884                .debug_tuple("SharedBinary")
885                .field(&arg0.as_ref().as_ref())
886                .finish(),
887            #[cfg(feature = "fs")]
888            Self::File(arg0) => f.debug_tuple("File").field(arg0).finish(),
889            #[cfg(all(feature = "fs", feature = "memmap"))]
890            Self::SharedFile(arg0, arg1) => f
891                .debug_tuple("SharedFile")
892                .field(arg0)
893                .field(&arg1.as_ref().as_ref())
894                .finish(),
895        }
896    }
897}
898
899impl Source {
900    fn with_data<P, T>(&self, p: P) -> Option<T>
901    where
902        P: FnOnce(&[u8]) -> T,
903    {
904        match &self {
905            #[cfg(all(feature = "fs", not(feature = "memmap")))]
906            Source::File(ref path) => {
907                let data = std::fs::read(path).ok()?;
908
909                Some(p(&data))
910            }
911            #[cfg(all(feature = "fs", feature = "memmap"))]
912            Source::File(ref path) => {
913                let file = std::fs::File::open(path).ok()?;
914                let data = unsafe { &memmap2::MmapOptions::new().map(&file).ok()? };
915
916                Some(p(data))
917            }
918            Source::Binary(ref data) => Some(p(data.as_ref().as_ref())),
919            #[cfg(all(feature = "fs", feature = "memmap"))]
920            Source::SharedFile(_, ref data) => Some(p(data.as_ref().as_ref())),
921        }
922    }
923}
924
925#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Hash)]
929pub struct Query<'a> {
930    pub families: &'a [Family<'a>],
934
935    pub weight: Weight,
939
940    pub stretch: Stretch,
944
945    pub style: Style,
949}
950
951#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
954pub enum Family<'a> {
955    Name(&'a str),
963
964    Serif,
966
967    SansSerif,
971
972    Cursive,
975
976    Fantasy,
979
980    Monospace,
982}
983
984#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
986pub struct Weight(pub u16);
987
988impl Default for Weight {
989    #[inline]
990    fn default() -> Weight {
991        Weight::NORMAL
992    }
993}
994
995impl Weight {
996    pub const THIN: Weight = Weight(100);
998    pub const EXTRA_LIGHT: Weight = Weight(200);
1000    pub const LIGHT: Weight = Weight(300);
1002    pub const NORMAL: Weight = Weight(400);
1004    pub const MEDIUM: Weight = Weight(500);
1006    pub const SEMIBOLD: Weight = Weight(600);
1008    pub const BOLD: Weight = Weight(700);
1010    pub const EXTRA_BOLD: Weight = Weight(800);
1012    pub const BLACK: Weight = Weight(900);
1014}
1015
1016#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1018pub enum Style {
1019    Normal,
1021    Italic,
1023    Oblique,
1025}
1026
1027impl Default for Style {
1028    #[inline]
1029    fn default() -> Style {
1030        Style::Normal
1031    }
1032}
1033
1034fn parse_face_info(source: Source, data: &[u8], index: u32) -> Result<FaceInfo, LoadError> {
1035    let raw_face = ttf_parser::RawFace::parse(data, index).map_err(|_| LoadError::MalformedFont)?;
1036    let (families, post_script_name) = parse_names(&raw_face).ok_or(LoadError::UnnamedFont)?;
1037    let (mut style, weight, stretch) = parse_os2(&raw_face);
1038    let (monospaced, italic) = parse_post(&raw_face);
1039
1040    if style == Style::Normal && italic {
1041        style = Style::Italic;
1042    }
1043
1044    Ok(FaceInfo {
1045        id: ID::dummy(),
1046        source,
1047        index,
1048        families,
1049        post_script_name,
1050        style,
1051        weight,
1052        stretch,
1053        monospaced,
1054    })
1055}
1056
1057fn parse_names(raw_face: &ttf_parser::RawFace) -> Option<(Vec<(String, Language)>, String)> {
1058    const NAME_TAG: ttf_parser::Tag = ttf_parser::Tag::from_bytes(b"name");
1059    let name_data = raw_face.table(NAME_TAG)?;
1060    let name_table = ttf_parser::name::Table::parse(name_data)?;
1061
1062    let mut families = collect_families(ttf_parser::name_id::TYPOGRAPHIC_FAMILY, &name_table.names);
1063
1064    if families.is_empty() {
1066        families = collect_families(ttf_parser::name_id::FAMILY, &name_table.names);
1067    }
1068
1069    if families.len() > 1 {
1071        if let Some(index) = families
1072            .iter()
1073            .position(|f| f.1 == Language::English_UnitedStates)
1074        {
1075            if index != 0 {
1076                families.swap(0, index);
1077            }
1078        }
1079    }
1080
1081    if families.is_empty() {
1082        return None;
1083    }
1084
1085    let post_script_name = name_table
1086        .names
1087        .into_iter()
1088        .find(|name| {
1089            name.name_id == ttf_parser::name_id::POST_SCRIPT_NAME && name.is_supported_encoding()
1090        })
1091        .and_then(|name| name_to_unicode(&name))?;
1092
1093    Some((families, post_script_name))
1094}
1095
1096fn collect_families(name_id: u16, names: &ttf_parser::name::Names) -> Vec<(String, Language)> {
1097    let mut families = Vec::new();
1098    for name in names.into_iter() {
1099        if name.name_id == name_id && name.is_unicode() {
1100            if let Some(family) = name_to_unicode(&name) {
1101                families.push((family, name.language()));
1102            }
1103        }
1104    }
1105
1106    if !families
1108        .iter()
1109        .any(|f| f.1 == Language::English_UnitedStates)
1110    {
1111        for name in names.into_iter() {
1112            if name.name_id == name_id && name.is_mac_roman() {
1113                if let Some(family) = name_to_unicode(&name) {
1114                    families.push((family, name.language()));
1115                    break;
1116                }
1117            }
1118        }
1119    }
1120
1121    families
1122}
1123
1124fn name_to_unicode(name: &ttf_parser::name::Name) -> Option<String> {
1125    if name.is_unicode() {
1126        let mut raw_data: Vec<u16> = Vec::new();
1127        for c in ttf_parser::LazyArray16::<u16>::new(name.name) {
1128            raw_data.push(c);
1129        }
1130
1131        String::from_utf16(&raw_data).ok()
1132    } else if name.is_mac_roman() {
1133        let mut raw_data = Vec::with_capacity(name.name.len());
1135        for b in name.name {
1136            raw_data.push(MAC_ROMAN[*b as usize]);
1137        }
1138
1139        String::from_utf16(&raw_data).ok()
1140    } else {
1141        None
1142    }
1143}
1144
1145fn parse_os2(raw_face: &ttf_parser::RawFace) -> (Style, Weight, Stretch) {
1146    const OS2_TAG: ttf_parser::Tag = ttf_parser::Tag::from_bytes(b"OS/2");
1147    let table = match raw_face
1148        .table(OS2_TAG)
1149        .and_then(ttf_parser::os2::Table::parse)
1150    {
1151        Some(table) => table,
1152        None => return (Style::Normal, Weight::NORMAL, Stretch::Normal),
1153    };
1154
1155    let style = match table.style() {
1156        ttf_parser::Style::Normal => Style::Normal,
1157        ttf_parser::Style::Italic => Style::Italic,
1158        ttf_parser::Style::Oblique => Style::Oblique,
1159    };
1160
1161    let weight = table.weight();
1162    let stretch = table.width();
1163
1164    (style, Weight(weight.to_number()), stretch)
1165}
1166
1167fn parse_post(raw_face: &ttf_parser::RawFace) -> (bool, bool) {
1168    const POST_TAG: ttf_parser::Tag = ttf_parser::Tag::from_bytes(b"post");
1172    let data = match raw_face.table(POST_TAG) {
1173        Some(v) => v,
1174        None => return (false, false),
1175    };
1176
1177    let monospaced = data.get(12..16) != Some(&[0, 0, 0, 0]);
1179
1180    let italic = data.get(4..8) != Some(&[0, 0, 0, 0]);
1182
1183    (monospaced, italic)
1184}
1185
1186trait NameExt {
1187    fn is_mac_roman(&self) -> bool;
1188    fn is_supported_encoding(&self) -> bool;
1189}
1190
1191impl NameExt for ttf_parser::name::Name<'_> {
1192    #[inline]
1193    fn is_mac_roman(&self) -> bool {
1194        use ttf_parser::PlatformId::Macintosh;
1195        const MACINTOSH_ROMAN_ENCODING_ID: u16 = 0;
1197
1198        self.platform_id == Macintosh && self.encoding_id == MACINTOSH_ROMAN_ENCODING_ID
1199    }
1200
1201    #[inline]
1202    fn is_supported_encoding(&self) -> bool {
1203        self.is_unicode() || self.is_mac_roman()
1204    }
1205}
1206
1207#[inline(never)]
1210fn find_best_match(candidates: &[&FaceInfo], query: &Query) -> Option<usize> {
1211    debug_assert!(!candidates.is_empty());
1212
1213    let mut matching_set: Vec<usize> = (0..candidates.len()).collect();
1215
1216    let matches = matching_set
1218        .iter()
1219        .any(|&index| candidates[index].stretch == query.stretch);
1220    let matching_stretch = if matches {
1221        query.stretch
1223    } else if query.stretch <= Stretch::Normal {
1224        let stretch = matching_set
1226            .iter()
1227            .filter(|&&index| candidates[index].stretch < query.stretch)
1228            .min_by_key(|&&index| {
1229                query.stretch.to_number() - candidates[index].stretch.to_number()
1230            });
1231
1232        match stretch {
1233            Some(&matching_index) => candidates[matching_index].stretch,
1234            None => {
1235                let matching_index = *matching_set.iter().min_by_key(|&&index| {
1236                    candidates[index].stretch.to_number() - query.stretch.to_number()
1237                })?;
1238
1239                candidates[matching_index].stretch
1240            }
1241        }
1242    } else {
1243        let stretch = matching_set
1245            .iter()
1246            .filter(|&&index| candidates[index].stretch > query.stretch)
1247            .min_by_key(|&&index| {
1248                candidates[index].stretch.to_number() - query.stretch.to_number()
1249            });
1250
1251        match stretch {
1252            Some(&matching_index) => candidates[matching_index].stretch,
1253            None => {
1254                let matching_index = *matching_set.iter().min_by_key(|&&index| {
1255                    query.stretch.to_number() - candidates[index].stretch.to_number()
1256                })?;
1257
1258                candidates[matching_index].stretch
1259            }
1260        }
1261    };
1262    matching_set.retain(|&index| candidates[index].stretch == matching_stretch);
1263
1264    let style_preference = match query.style {
1266        Style::Italic => [Style::Italic, Style::Oblique, Style::Normal],
1267        Style::Oblique => [Style::Oblique, Style::Italic, Style::Normal],
1268        Style::Normal => [Style::Normal, Style::Oblique, Style::Italic],
1269    };
1270    let matching_style = *style_preference.iter().find(|&query_style| {
1271        matching_set
1272            .iter()
1273            .any(|&index| candidates[index].style == *query_style)
1274    })?;
1275
1276    matching_set.retain(|&index| candidates[index].style == matching_style);
1277
1278    let weight = query.weight.0;
1283
1284    let matching_weight = if matching_set
1285        .iter()
1286        .any(|&index| candidates[index].weight.0 == weight)
1287    {
1288        Weight(weight)
1289    } else if (400..450).contains(&weight)
1290        && matching_set
1291            .iter()
1292            .any(|&index| candidates[index].weight.0 == 500)
1293    {
1294        Weight::MEDIUM
1296    } else if (450..=500).contains(&weight)
1297        && matching_set
1298            .iter()
1299            .any(|&index| candidates[index].weight.0 == 400)
1300    {
1301        Weight::NORMAL
1303    } else if weight <= 500 {
1304        let idx = matching_set
1306            .iter()
1307            .filter(|&&index| candidates[index].weight.0 <= weight)
1308            .min_by_key(|&&index| weight - candidates[index].weight.0);
1309
1310        match idx {
1311            Some(&matching_index) => candidates[matching_index].weight,
1312            None => {
1313                let matching_index = *matching_set
1314                    .iter()
1315                    .min_by_key(|&&index| candidates[index].weight.0 - weight)?;
1316                candidates[matching_index].weight
1317            }
1318        }
1319    } else {
1320        let idx = matching_set
1322            .iter()
1323            .filter(|&&index| candidates[index].weight.0 >= weight)
1324            .min_by_key(|&&index| candidates[index].weight.0 - weight);
1325
1326        match idx {
1327            Some(&matching_index) => candidates[matching_index].weight,
1328            None => {
1329                let matching_index = *matching_set
1330                    .iter()
1331                    .min_by_key(|&&index| weight - candidates[index].weight.0)?;
1332                candidates[matching_index].weight
1333            }
1334        }
1335    };
1336    matching_set.retain(|&index| candidates[index].weight == matching_weight);
1337
1338    matching_set.into_iter().next()
1342}
1343
1344#[rustfmt::skip]
1348const MAC_ROMAN: &[u16; 256] = &[
1349    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
1350    0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
1351    0x0010, 0x2318, 0x21E7, 0x2325, 0x2303, 0x0015, 0x0016, 0x0017,
1352    0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
1353    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
1354    0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
1355    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
1356    0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
1357    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
1358    0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
1359    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
1360    0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
1361    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
1362    0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
1363    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
1364    0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
1365    0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
1366    0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
1367    0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
1368    0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
1369    0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
1370    0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
1371    0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
1372    0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
1373    0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
1374    0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
1375    0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
1376    0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
1377    0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
1378    0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
1379    0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC,
1380    0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7,
1381];