use std::ops::Index; use std::fmt::{ Debug, Formatter, Result as FmtResult }; use super::ReadError; use super::core::*; // Glyph Names const STANDARD_MACINTOSH_GLYPH_NAMES: [&str; 258] = [ ".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph", "germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal", "Delta", "guillemotleft", "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve", "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash", "Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute", "yacute", "Thorn", "thorn", "minus", "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf", "onequarter", "threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla", "scedilla", "Cacute", "cacute", "Ccaron", "ccaron", "dcroat", ]; #[derive(Clone, Copy)] pub enum GlyphNames<'a> { Standard, Subset(&'a [u8]), Superset(&'a [u8]), } impl<'a> GlyphNames<'a> { pub fn len(&self) -> u16 { match self { Self::Standard => 258, Self::Subset(x) => x.uint16(0), Self::Superset(x) => x.uint16(0), } } pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn get(&self, index: u16) -> &'a str { match self { Self::Standard => STANDARD_MACINTOSH_GLYPH_NAMES[index as usize], Self::Subset(x) => { let offset = x.int8(2 + index as usize); let index = index as i32 + offset as i32; STANDARD_MACINTOSH_GLYPH_NAMES[index as usize] }, Self::Superset(x) => { let index = x.uint16(2 + index as usize * 2); if index < 258 { return STANDARD_MACINTOSH_GLYPH_NAMES[index as usize]; } let index = index - 258; let mut data = &x[2 + x.uint16(0) as usize * 2..]; for _ in 0..index { let end = 1 + data[0] as usize; data = &data[end..]; } let end = 1 + data[0] as usize; let data = &data[1..end]; std::str::from_utf8(data).unwrap() // TODO this may fail, but the data should actually be ASCII }, } } pub fn iter(&self) -> impl ExactSizeIterator { (0..self.len()).map(|x| self.get(x)) } } impl<'a> Index for GlyphNames<'a> { type Output = str; fn index(&self, index: u16) -> &Self::Output { self.get(index) } } impl Debug for GlyphNames<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.debug_list().entries(self.iter()).finish() } } // Table #[derive(Clone, Copy)] #[repr(transparent)] pub struct PostScriptTable<'a>(&'a [u8]); impl<'a> RandomAccess<'a> for PostScriptTable<'a> { fn bytes(&self) -> &'a [u8] { self.0 } } impl<'a> PostScriptTable<'a> { pub fn version(&self) -> Version16Dot16 { self.version16dot16(0) } pub fn italic_angle(&self) -> Fixed { self.fixed(4) } pub fn underline_position(&self) -> i16 { self.int16(8) } pub fn underline_thickness(&self) -> i16 { self.int16(10) } pub fn is_fixed_pitch(&self) -> u32 { self.uint32(12) } pub fn min_mem_type42(&self) -> u32 { self.uint32(16) } pub fn max_mem_type42(&self) -> u32 { self.uint32(20) } pub fn min_mem_type1(&self) -> u32 { self.uint32(24) } pub fn max_mem_type1(&self) -> u32 { self.uint32(28) } pub fn glyph_names(&self) -> Option> { match self.version() { Version16Dot16(0x0001, 0x0000) => Some(GlyphNames::Standard), Version16Dot16(0x0002, 0x0000) => Some(GlyphNames::Superset(&self.0[32..])), Version16Dot16(0x0002, 0x5000) => Some(GlyphNames::Subset(&self.0[32..])), Version16Dot16(0x0003, 0x0000) => None, _ => unreachable!(), } } } impl Debug for PostScriptTable<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.debug_struct("PostScriptTable") .field("version", &self.version()) .field("italic_angle", &self.italic_angle()) .field("underline_position", &self.underline_position()) .field("underline_thickness", &self.underline_thickness()) .field("is_fixed_pitch", &self.is_fixed_pitch()) .field("min_mem_type42", &self.min_mem_type42()) .field("max_mem_type42", &self.max_mem_type42()) .field("min_mem_type1", &self.min_mem_type1()) .field("max_mem_type1", &self.max_mem_type1()) .field("glyph_names", &self.glyph_names()) .finish() } } impl<'a> TryFrom<&'a [u8]> for PostScriptTable<'a> { type Error = ReadError; fn try_from(value: &'a [u8]) -> Result { if value.len() < 32 { return Err(ReadError::UnexpectedEof); } match value.version16dot16(0) { Version16Dot16(0x0001, 0x0000) => Ok(PostScriptTable(value)), Version16Dot16(0x0002, 0x0000) => { // TODO validate size Ok(PostScriptTable(value)) }, Version16Dot16(0x0002, 0x5000) => { // TODO validate size Ok(PostScriptTable(value)) }, Version16Dot16(0x0003, 0x0000) => Ok(PostScriptTable(value)), x => Err(ReadError::UnsupportedTableVersion16Dot16(x)) } } }