read_fonts/model/font/
format.rs1use crate::{FileRef, FontRead};
4
5#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
7pub enum FontFormat {
8 Sfnt(u32),
16 Type1,
18 Cff(u32),
22}
23
24impl FontFormat {
25 pub fn new(data: &[u8]) -> Option<Self> {
27 if let Ok(file) = FileRef::new(data) {
28 let format = match file {
29 FileRef::Collection(collection) => Self::Sfnt(collection.len()),
30 FileRef::Font(_) => Self::Sfnt(1),
31 };
32 Some(format)
33 } else if check_type1(data) {
34 Some(Self::Type1)
35 } else if let Ok(cff) = crate::ps::cff::v1::Cff::read(data.into()) {
36 Some(Self::Cff(cff.top_dicts().count() as u32))
37 } else {
38 None
39 }
40 }
41
42 pub fn is_sfnt(&self) -> bool {
44 matches!(self, Self::Sfnt(_))
45 }
46
47 pub fn num_fonts(&self) -> u32 {
49 match self {
50 Self::Sfnt(n) | Self::Cff(n) => *n,
51 _ => 1,
52 }
53 }
54}
55
56fn check_type1(data: &[u8]) -> bool {
57 fn check(data: &[u8]) -> bool {
58 data.starts_with(b"%!PS-AdobeFont") || data.starts_with(b"%!FontType")
59 }
60 check(data) || data.get(6..).map(check).unwrap_or(false)
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use crate::{FontRef, TableProvider};
67
68 #[test]
69 fn check_formats() {
70 let pure_cff = FontRef::new(font_test_data::MATERIAL_ICONS_SUBSET)
71 .unwrap()
72 .cff()
73 .unwrap()
74 .offset_data()
75 .as_bytes();
76 use FontFormat::*;
77 #[rustfmt::skip]
78 let pairs = [
79 (font_test_data::CANTARELL_VF_TRIMMED, Sfnt(1)),
80 (font_test_data::TINOS_SUBSET, Sfnt(1)),
81 (pure_cff, Cff(1)),
82 (font_test_data::ttc::TTC, Sfnt(2)),
83 (font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA, Type1),
84 ];
85 for (data, expected_format) in pairs {
86 assert_eq!(FontFormat::new(data).unwrap(), expected_format);
87 }
88 assert!(FontFormat::new(b"I'm not a font").is_none());
89 }
90}