//! PostScript encodings. //! //! This maps font specific character codes to string ids. //! //! See "Glyph Organization" at //! for an explanation of how charsets, encodings and glyphs are related. use super::charset::Charset; use crate::{ ps::{encoding::PredefinedEncoding, string::Sid}, FontData, GlyphId, ReadError, }; #[doc(inline)] pub use super::v1::{EncodingRange1 as Range1, EncodingSupplement as Supplement}; /// Mapping from character codes to string ids. /// /// See "Encodings" at . #[derive(Clone)] pub enum Encoding<'a> { Predefined(PredefinedEncoding), Custom(CustomEncoding<'a>), } impl<'a> Encoding<'a> { /// Parses an encoding at the given offset. /// /// Special offsets 0 and 1 are parsed as the predefined standard and /// expert encodings, respectively. pub fn new(data: &'a [u8], offset: usize) -> Result { match offset { 0 => Ok(Self::Predefined(PredefinedEncoding::Standard)), 1 => Ok(Self::Predefined(PredefinedEncoding::Expert)), _ => CustomEncoding::new(data.get(offset..).ok_or(ReadError::OutOfBounds)?) .map(Self::Custom), } } /// Maps a character code to a glyph identifier. pub fn map(&self, charset: &Charset, code: u8) -> Option { match self { Self::Predefined(predefined) => charset.glyph_id(predefined.sid(code)?).ok(), Self::Custom(custom) => custom.map(charset, code), } } } /// Custom mapping from character codes to string ids. #[derive(Clone)] pub enum CustomEncoding<'a> { /// Sequence of character codes where the string id is equal to the index /// of the code plus one. Format0(&'a [u8], &'a [Supplement]), /// Sequence of ranges mapping character codes to string ids. Format1(&'a [Range1], &'a [Supplement]), } impl<'a> CustomEncoding<'a> { /// Parses a custom encoding from the given data. pub fn new(data: &'a [u8]) -> Result { let mut cursor = FontData::new(data).cursor(); let header = cursor.read::()?; let has_supplement = header & 0x80 != 0; // Macro because a closure cannot borrow cursor mutably macro_rules! read_supplement { () => { if has_supplement { let count = cursor.read::()?; cursor.read_array::(count as usize)? } else { &[] } }; } let format = header & 0x7F; match format { 0 => { let n_codes = cursor.read::()?; let codes = cursor.read_array(n_codes as usize)?; let supp = read_supplement!(); Ok(Self::Format0(codes, supp)) } 1 => { let n_ranges = cursor.read::()?; let ranges = cursor.read_array(n_ranges as usize)?; let supp = read_supplement!(); Ok(Self::Format1(ranges, supp)) } _ => Err(ReadError::InvalidFormat(format as _)), } } /// Maps a character code to a glyph identifier. pub fn map(&self, charset: &Charset, code: u8) -> Option { let read_sup = |sup: &[Supplement]| { sup.iter() .find(|s| s.code == code) .and_then(|s| charset.glyph_id(Sid::new(s.glyph.get())).ok()) }; match self { Self::Format0(codes, sup) => read_sup(sup).or_else(|| { codes .iter() .position(|c| *c == code) // notdef is implicit so add one .map(|gid| GlyphId::new(gid as u32 + 1)) }), Self::Format1(ranges, sup) => read_sup(sup).or_else(|| { let mut gid = 1u32; for range in ranges.iter() { let end = range.first.saturating_add(range.n_left); if (range.first..=end).contains(&code) { gid += (code - range.first) as u32; return Some(GlyphId::new(gid)); } gid += range.n_left as u32 + 1; } None }), } } } #[cfg(test)] mod tests { use super::*; #[test] fn predefined_standard() { let encoding = Encoding::Predefined(PredefinedEncoding::Standard); let charset = iso_adobe_charset(); for code in 0..=255 { let gid = encoding.map(&charset, code); assert_eq!( gid.unwrap(), charset .glyph_id(PredefinedEncoding::Standard.sid(code).unwrap()) .unwrap() ); } } #[test] fn predefined_expert() { let encoding = Encoding::Predefined(PredefinedEncoding::Expert); let charset = iso_expert_charset(); for code in 0..=255 { let gid = encoding.map(&charset, code); assert_eq!( gid.unwrap(), charset .glyph_id(PredefinedEncoding::Expert.sid(code).unwrap()) .unwrap() ); } } #[test] fn custom_format_0() { let codes = [3, 8, 9, 10, 11]; let encoding = Encoding::Custom(CustomEncoding::Format0(&codes, &[])); let charset = iso_adobe_charset(); for (i, code) in codes.into_iter().enumerate() { assert_eq!( encoding.map(&charset, code).unwrap(), GlyphId::new(i as u32 + 1) ); } } #[test] fn custom_format_1() { let ranges = [(51, 4), (250, 5)].map(|(first, n_left)| Range1 { first, n_left }); let encoding = Encoding::Custom(CustomEncoding::Format1(&ranges, &[])); let charset = iso_adobe_charset(); for code in 0..=255 { let gid = encoding.map(&charset, code); let expected = match code { 51..=55 => Some(code as u32 - 50), 250..=255 => Some(code as u32 - 250 + 6), _ => None, }; assert_eq!(gid, expected.map(GlyphId::new)); } } #[test] fn supplemental() { // map 40 -> z and 122 -> parenleft let supplement = [(40, 91), (122, 9)].map(|(code, glyph)| Supplement { code, glyph: glyph.into(), }); let encoding = Encoding::Custom(CustomEncoding::Format0(&[], &supplement)); let charset = iso_adobe_charset(); assert_eq!(encoding.map(&charset, 40).unwrap().to_u32(), 91); assert_eq!(encoding.map(&charset, 122).unwrap().to_u32(), 9); assert_eq!( charset .string_id(91u32.into()) .unwrap() .resolve_standard() .unwrap(), b"z" ); assert_eq!( charset .string_id(9u32.into()) .unwrap() .resolve_standard() .unwrap(), b"parenleft" ); } fn iso_adobe_charset() -> Charset<'static> { Charset::new(Default::default(), 0, 256).unwrap() } fn iso_expert_charset() -> Charset<'static> { Charset::new(Default::default(), 1, 256).unwrap() } }