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];