diff --git a/src/pdf/font.rs b/src/pdf/font.rs index 14086f1..04b62f3 100644 --- a/src/pdf/font.rs +++ b/src/pdf/font.rs @@ -1,15 +1,21 @@ use crate::{ pdf::{ object::{ - IsPdfNull, PdfDictionary, PdfName, PdfObject, PdfObjectDirect, PdfRectangle, PdfStream, - PdfString, + IsPdfNull, PdfArray, PdfDictionary, PdfMatrix, PdfName, PdfNameOrInteger, PdfObject, + PdfObjectDirect, PdfRectangle, PdfStream, PdfString, + }, + parse::{ + GetPdfInputPosition, PdfInputPosition, PdfInputPositionNoCompare, PdfParse, + PdfParseError, }, - parse::{PdfParse, PdfParseError}, pdf_parse, }, - util::DagDebugState, + util::{ArcOrRef, DagDebugState}, }; -use std::{borrow::Cow, fmt, sync::Arc}; +use std::{borrow::Cow, collections::BTreeMap, fmt, sync::Arc}; + +mod tables; +mod type_1_parse; pdf_parse! { #[pdf(transparent)] @@ -108,7 +114,7 @@ pdf_parse! { #[pdf(name = "MissingWidth")] pub missing_width: Option, #[pdf(name = "FontFile")] - pub font_file: Option, + pub font_file: Option>, #[pdf(name = "FontFile2")] pub font_file2: Option, #[pdf(name = "FontFile3")] @@ -403,7 +409,7 @@ impl PdfFontType1 { Self::Other(v) => Some(&v.font_descriptor), } } - pub fn encoding(&self) -> &PdfObjectDirect { + pub fn encoding(&self) -> &Option { match self { Self::Standard(v) => &v.encoding, Self::Other(v) => &v.encoding, @@ -435,14 +441,19 @@ impl PdfParse for PdfFontType1 { } fn parse(object: PdfObject) -> Result { let object = object.into(); - let PdfObjectDirect::Dictionary(object) = object else { - return Arc::::parse(object.into()).map(Self::Other); - }; - if let Ok(_) = PdfStandardFontName::parse(object.get_or_null(b"BaseFont".as_slice())) { - Arc::::parse(object.into()).map(Self::Standard) + let font = if let PdfObjectDirect::Dictionary(object) = object { + if let Ok(_) = PdfStandardFontName::parse(object.get_or_null(b"BaseFont".as_slice())) { + Self::Standard(PdfParse::parse(object.into())?) + } else { + Self::Other(PdfParse::parse(object.into())?) + } } else { - Arc::::parse(object.into()).map(Self::Other) + Self::Other(PdfParse::parse(object.into())?) + }; + if let Some(font_file) = font.font_descriptor().and_then(|v| v.font_file.as_ref()) { + font_file.decoded_data().as_ref()?; } + Ok(font) } } @@ -456,7 +467,7 @@ pub struct PdfFontType1Common { pub last_char: Option, pub widths: Option>, pub font_descriptor: Option, - pub encoding: PdfObjectDirect, + pub encoding: Option, pub to_unicode: Option, pub rest: PdfDictionary, } @@ -515,8 +526,7 @@ pdf_parse! { #[pdf(name = "FontDescriptor")] pub font_descriptor: Option, #[pdf(name = "Encoding")] - // TODO - pub encoding: PdfObjectDirect, + pub encoding: Option, #[pdf(name = "ToUnicode")] pub to_unicode: Option, #[pdf(flatten)] @@ -609,8 +619,7 @@ pdf_parse! { #[pdf(name = "FontDescriptor")] pub font_descriptor: PdfFontDescriptor, #[pdf(name = "Encoding")] - // TODO - pub encoding: PdfObjectDirect, + pub encoding: Option, #[pdf(name = "ToUnicode")] pub to_unicode: Option, #[pdf(flatten)] @@ -681,3 +690,235 @@ impl PdfFontType1Other { } } } + +pdf_parse! { + #[pdf(name)] + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] + pub enum PdfSimpleFontEncodingPredefined { + #[pdf(name = "MacRomanEncoding")] + MacRomanEncoding, + #[pdf(name = "MacExpertEncoding")] + MacExpertEncoding, + #[pdf(name = "WinAnsiEncoding")] + WinAnsiEncoding, + } +} + +impl PdfSimpleFontEncodingPredefined { + pub const fn table(self) -> PdfSimpleFontEncodingTable { + match self { + Self::MacRomanEncoding => PdfSimpleFontEncodingTable::MAC_ROMAN, + Self::MacExpertEncoding => PdfSimpleFontEncodingTable::MAC_EXPERT, + Self::WinAnsiEncoding => PdfSimpleFontEncodingTable::WIN_ANSI, + } + } +} + +pdf_parse! { + #[pdf(name)] + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] + pub enum PdfSimpleFontEncodingDictionaryType { + #[pdf(name = "Encoding")] + #[default] + Encoding, + } +} + +pdf_parse! { + #[pdf] + #[derive(Clone, Debug)] + pub struct PdfSimpleFontEncodingDictionary { + #[pdf(name = "Type")] + pub ty: Option, + #[pdf(name = "BaseEncoding")] + pub base_encoding: Option, + #[pdf(name = "Differences")] + pub differences: Option, + #[pdf(flatten)] + pub rest: PdfDictionary, + } +} + +impl PdfSimpleFontEncodingDictionary { + pub fn table( + &self, + default_table: impl FnOnce() -> PdfSimpleFontEncodingTable, + ) -> PdfSimpleFontEncodingTable { + let Self { + ty: _, + base_encoding, + differences, + rest: _, + } = self; + let mut retval = base_encoding + .map(|v| v.table()) + .unwrap_or_else(default_table); + if let Some(differences) = differences { + retval = differences.table(retval); + } + retval + } +} + +#[derive(Clone, Debug)] +pub struct PdfSimpleFontEncodingDifferences { + pos: PdfInputPositionNoCompare, + map: Arc>, +} + +impl PdfSimpleFontEncodingDifferences { + pub fn new(pos: impl Into, map: Arc>) -> Self { + Self { + pos: pos.into(), + map, + } + } + pub fn pos(&self) -> PdfInputPosition { + self.pos.0 + } + pub fn map(&self) -> &Arc> { + &self.map + } + pub fn table(&self, base_table: PdfSimpleFontEncodingTable) -> PdfSimpleFontEncodingTable { + let mut retval = base_table; + let table: &mut [_; 0x100] = ArcOrRef::make_mut(&mut retval.table); + for (&byte, name) in self.map.iter() { + table[usize::from(byte)] = PdfSimpleFontEncodingTableEntry { + name: Some(name.clone()), + presumed_unicode: None, + }; + } + retval + } +} + +impl GetPdfInputPosition for PdfSimpleFontEncodingDifferences { + fn get_pdf_input_position(&self) -> PdfInputPosition { + self.pos.0 + } +} + +impl IsPdfNull for PdfSimpleFontEncodingDifferences { + fn is_pdf_null(&self) -> bool { + false + } +} + +impl PdfParse for PdfSimpleFontEncodingDifferences { + fn type_name() -> Cow<'static, str> { + Cow::Borrowed("PdfSimpleFontEncodingDifferences") + } + fn parse(object: PdfObject) -> Result { + let array = PdfArray::parse(object)?; + let pos = array.pos(); + let mut map = BTreeMap::new(); + let mut next_byte = None::; + for i in array.iter() { + let i = PdfNameOrInteger::parse(i.clone())?; + match i { + PdfNameOrInteger::Name(name) => { + let pos = name.pos(); + let byte = next_byte.ok_or(PdfParseError::IntegerOutOfRange { pos })?; + next_byte = byte.checked_add(1); + map.insert(byte, name); + } + PdfNameOrInteger::Integer(v) => next_byte = Some(u8::parse(v.into())?), + } + } + Ok(Self { + pos: pos.into(), + map: Arc::new(map), + }) + } +} + +#[derive(Clone, Default, Debug)] +pub struct PdfSimpleFontEncodingTableEntry { + pub name: Option, + pub presumed_unicode: Option<&'static str>, +} + +impl PdfSimpleFontEncodingTableEntry { + pub const fn new_static( + name: Option<&'static [u8]>, + presumed_unicode: Option<&'static str>, + ) -> Self { + Self { + name: match name { + Some(name) => Some(PdfName::new_static(name)), + None => None, + }, + presumed_unicode, + } + } +} + +#[derive(Clone, Debug)] +pub struct PdfSimpleFontEncodingTable { + pub table: ArcOrRef<'static, [PdfSimpleFontEncodingTableEntry; 0x100]>, +} + +#[derive(Clone, Debug)] +pub enum PdfSimpleFontEncoding { + Predefined(PdfSimpleFontEncodingPredefined), + Dictionary(PdfSimpleFontEncodingDictionary), +} + +impl PdfSimpleFontEncoding { + pub fn table( + &self, + default_table: impl FnOnce() -> PdfSimpleFontEncodingTable, + ) -> PdfSimpleFontEncodingTable { + match self { + PdfSimpleFontEncoding::Predefined(v) => v.table(), + PdfSimpleFontEncoding::Dictionary(v) => v.table(default_table), + } + } +} + +impl IsPdfNull for PdfSimpleFontEncoding { + fn is_pdf_null(&self) -> bool { + false + } +} + +impl PdfParse for PdfSimpleFontEncoding { + fn type_name() -> Cow<'static, str> { + Cow::Borrowed("PdfSimpleFontEncoding") + } + fn parse(object: PdfObject) -> Result { + let object = PdfObjectDirect::from(object); + match object { + PdfObjectDirect::Name(v) => Ok(Self::Predefined(PdfParse::parse(v.into())?)), + PdfObjectDirect::Dictionary(v) => Ok(Self::Dictionary(PdfParse::parse(v.into())?)), + _ => Err(PdfParseError::InvalidType { + pos: object.pos(), + ty: object.type_name(), + expected_ty: "PdfSimpleFontEncoding", + }), + } + } +} + +#[derive(Clone, Debug)] +#[non_exhaustive] +pub struct PdfFontType1Program { + pub encoding: Option]>>, + pub font_bbox: Option, + pub font_info: Option, + pub font_matrix: Option, + pub font_name: Option, +} + +#[derive(Clone, Debug)] +pub struct PdfFontType1FontInfo { + pub family_name: Option, + pub full_name: Option, + pub notice: Option, + pub weight: Option, + pub version: Option, + pub italic_angle: Option, + pub is_fixed_pitch: Option, + pub underline_position: Option, + pub underline_thickness: Option, +} diff --git a/src/pdf/font/tables.rs b/src/pdf/font/tables.rs new file mode 100644 index 0000000..fcb8218 --- /dev/null +++ b/src/pdf/font/tables.rs @@ -0,0 +1,1067 @@ +use crate::{ + pdf::font::{PdfSimpleFontEncodingTable, PdfSimpleFontEncodingTableEntry}, + util::ArcOrRef, +}; + +macro_rules! opt_lit { + (None) => { + None + }; + ($v:literal) => { + Some($v) + }; +} + +macro_rules! array_from_fn_0x100 { + (|$i:ident| $value:expr) => { + array_from_fn_0x100!(@step1 |$i| $value; + [ + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, + ]; + [ + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + ] + ) + }; + (@step1 |$i:ident| $value:expr; [$($low_digits:literal,)*]; $high_digits:tt) => { + array_from_fn_0x100!(@step2 |$i| $value; [$($low_digits + $high_digits,)*]) + }; + (@step2 |$i:ident| $value:expr; [$($low_digits:literal + [$($high_digits:literal,)*],)*]) => { + [$($({ + let $i = $low_digits + $high_digits; + $value + },)*)*] + }; +} + +macro_rules! builtin_simple_font_encoding_table { + ( + $vis:vis const $const_name:ident; + $(#[default = ($default_name:literal, $default_presumed_unicode:literal)])? + [ + $(($byte:literal, $name:tt, $presumed_unicode:tt),)* + ] + ) => { + impl PdfSimpleFontEncodingTable { + $vis const $const_name: Self = { + const TABLE: [PdfSimpleFontEncodingTableEntry; 0x100] = { + #[allow(unused_mut, unused_assignments)] + const DEFAULT: (Option<&[u8]>, Option<&str>) = { + let mut value: (Option<&[u8]>, Option<&str>) = (None, None); + $(value = (Some($default_name), Some($default_presumed_unicode));)? + value + }; + let mut encoding = [DEFAULT; 0x100]; + $(encoding[$byte] = (opt_lit!($name), opt_lit!($presumed_unicode));)* + array_from_fn_0x100!(|i| PdfSimpleFontEncodingTableEntry::new_static(encoding[i].0, encoding[i].1)) + }; + Self { table: ArcOrRef::Ref(&TABLE)} + }; + } + }; +} + +builtin_simple_font_encoding_table! { + pub const MAC_ROMAN; + [ + (0o040, b"space", " "), + (0o041, b"exclam", "!"), + (0o042, b"quotedbl", "\""), + (0o043, b"numbersign", "#"), + (0o044, b"dollar", "$"), + (0o045, b"percent", "%"), + (0o046, b"ampersand", "&"), + (0o047, b"quotesingle", "\'"), + (0o050, b"parenleft", "("), + (0o051, b"parenright", ")"), + (0o052, b"asterisk", "*"), + (0o053, b"plus", "+"), + (0o054, b"comma", ","), + (0o055, b"hyphen", "-"), + (0o056, b"period", "."), + (0o057, b"slash", "/"), + (0o060, b"zero", "0"), + (0o061, b"one", "1"), + (0o062, b"two", "2"), + (0o063, b"three", "3"), + (0o064, b"four", "4"), + (0o065, b"five", "5"), + (0o066, b"six", "6"), + (0o067, b"seven", "7"), + (0o070, b"eight", "8"), + (0o071, b"nine", "9"), + (0o072, b"colon", ":"), + (0o073, b"semicolon", ";"), + (0o074, b"less", "<"), + (0o075, b"equal", "="), + (0o076, b"greater", ">"), + (0o077, b"question", "?"), + (0o100, b"at", "@"), + (0o101, b"A", "A"), + (0o102, b"B", "B"), + (0o103, b"C", "C"), + (0o104, b"D", "D"), + (0o105, b"E", "E"), + (0o106, b"F", "F"), + (0o107, b"G", "G"), + (0o110, b"H", "H"), + (0o111, b"I", "I"), + (0o112, b"J", "J"), + (0o113, b"K", "K"), + (0o114, b"L", "L"), + (0o115, b"M", "M"), + (0o116, b"N", "N"), + (0o117, b"O", "O"), + (0o120, b"P", "P"), + (0o121, b"Q", "Q"), + (0o122, b"R", "R"), + (0o123, b"S", "S"), + (0o124, b"T", "T"), + (0o125, b"U", "U"), + (0o126, b"V", "V"), + (0o127, b"W", "W"), + (0o130, b"X", "X"), + (0o131, b"Y", "Y"), + (0o132, b"Z", "Z"), + (0o133, b"bracketleft", "["), + (0o134, b"backslash", "\\"), + (0o135, b"bracketright", "]"), + (0o136, b"asciicircum", "^"), + (0o137, b"underscore", "_"), + (0o140, b"grave", "`"), + (0o141, b"a", "a"), + (0o142, b"b", "b"), + (0o143, b"c", "c"), + (0o144, b"d", "d"), + (0o145, b"e", "e"), + (0o146, b"f", "f"), + (0o147, b"g", "g"), + (0o150, b"h", "h"), + (0o151, b"i", "i"), + (0o152, b"j", "j"), + (0o153, b"k", "k"), + (0o154, b"l", "l"), + (0o155, b"m", "m"), + (0o156, b"n", "n"), + (0o157, b"o", "o"), + (0o160, b"p", "p"), + (0o161, b"q", "q"), + (0o162, b"r", "r"), + (0o163, b"s", "s"), + (0o164, b"t", "t"), + (0o165, b"u", "u"), + (0o166, b"v", "v"), + (0o167, b"w", "w"), + (0o170, b"x", "x"), + (0o171, b"y", "y"), + (0o172, b"z", "z"), + (0o173, b"braceleft", "{"), + (0o174, b"bar", "|"), + (0o175, b"braceright", "}"), + (0o176, b"asciitilde", "~"), + (0o200, b"Adieresis", "\u{c4}"), + (0o201, b"Aring", "\u{c5}"), + (0o202, b"Ccedilla", "\u{c7}"), + (0o203, b"Eacute", "\u{c9}"), + (0o204, b"Ntilde", "\u{d1}"), + (0o205, b"Odieresis", "\u{d6}"), + (0o206, b"Udieresis", "\u{dc}"), + (0o207, b"aacute", "\u{e1}"), + (0o210, b"agrave", "\u{e0}"), + (0o211, b"acircumflex", "\u{e2}"), + (0o212, b"adieresis", "\u{e4}"), + (0o213, b"atilde", "\u{e3}"), + (0o214, b"aring", "\u{e5}"), + (0o215, b"ccedilla", "\u{e7}"), + (0o216, b"eacute", "\u{e9}"), + (0o217, b"egrave", "\u{e8}"), + (0o220, b"ecircumflex", "\u{ea}"), + (0o221, b"edieresis", "\u{eb}"), + (0o222, b"iacute", "\u{ed}"), + (0o223, b"igrave", "\u{ec}"), + (0o224, b"icircumflex", "\u{ee}"), + (0o225, b"idieresis", "\u{ef}"), + (0o226, b"ntilde", "\u{f1}"), + (0o227, b"oacute", "\u{f3}"), + (0o230, b"ograve", "\u{f2}"), + (0o231, b"ocircumflex", "\u{f4}"), + (0o232, b"odieresis", "\u{f6}"), + (0o233, b"otilde", "\u{f5}"), + (0o234, b"uacute", "\u{fa}"), + (0o235, b"ugrave", "\u{f9}"), + (0o236, b"ucircumflex", "\u{fb}"), + (0o237, b"udieresis", "\u{fc}"), + (0o240, b"dagger", "\u{2020}"), + (0o241, b"degree", "\u{b0}"), + (0o242, b"cent", "\u{a2}"), + (0o243, b"sterling", "\u{a3}"), + (0o244, b"section", "\u{a7}"), + (0o245, b"bullet", "\u{2022}"), + (0o246, b"paragraph", "\u{b6}"), + (0o247, b"germandbls", "\u{df}"), + (0o250, b"registered", "\u{ae}"), + (0o251, b"copyright", "\u{a9}"), + (0o252, b"trademark", "\u{2122}"), + (0o253, b"acute", "\u{b4}"), + (0o254, b"dieresis", "\u{a8}"), + (0o256, b"AE", "\u{c6}"), + (0o257, b"Oslash", "\u{d8}"), + (0o261, b"plusminus", "\u{b1}"), + (0o264, b"yen", "\u{a5}"), + (0o265, b"mu", "\u{3bc}"), + (0o273, b"ordfeminine", "\u{aa}"), + (0o274, b"ordmasculine", "\u{ba}"), + (0o276, b"ae", "\u{e6}"), + (0o277, b"oslash", "\u{f8}"), + (0o300, b"questiondown", "\u{bf}"), + (0o301, b"exclamdown", "\u{a1}"), + (0o302, b"logicalnot", "\u{ac}"), + (0o304, b"florin", "\u{192}"), + (0o307, b"guillemotleft", "\u{ab}"), + (0o310, b"guillemotright", "\u{bb}"), + (0o311, b"ellipsis", "\u{2026}"), + (0o312, b"space", "\u{a0}"), + (0o313, b"Agrave", "\u{c0}"), + (0o314, b"Atilde", "\u{c3}"), + (0o315, b"Otilde", "\u{d5}"), + (0o316, b"OE", "\u{152}"), + (0o317, b"oe", "\u{153}"), + (0o320, b"endash", "\u{2013}"), + (0o321, b"emdash", "\u{2014}"), + (0o322, b"quotedblleft", "\u{201c}"), + (0o323, b"quotedblright", "\u{201d}"), + (0o324, b"quoteleft", "\u{2018}"), + (0o325, b"quoteright", "\u{2019}"), + (0o326, b"divide", "\u{f7}"), + (0o330, b"ydieresis", "\u{ff}"), + (0o331, b"Ydieresis", "\u{178}"), + (0o332, b"fraction", "\u{2044}"), + (0o333, b"currency", "\u{a4}"), + (0o334, b"guilsinglleft", "\u{2039}"), + (0o335, b"guilsinglright", "\u{203a}"), + (0o336, b"fi", "\u{fb01}"), + (0o337, b"fl", "\u{fb02}"), + (0o340, b"daggerdbl", "\u{2021}"), + (0o341, b"periodcentered", "\u{b7}"), + (0o342, b"quotesinglbase", "\u{201a}"), + (0o343, b"quotedblbase", "\u{201e}"), + (0o344, b"perthousand", "\u{2030}"), + (0o345, b"Acircumflex", "\u{c2}"), + (0o346, b"Ecircumflex", "\u{ca}"), + (0o347, b"Aacute", "\u{c1}"), + (0o350, b"Edieresis", "\u{cb}"), + (0o351, b"Egrave", "\u{c8}"), + (0o352, b"Iacute", "\u{cd}"), + (0o353, b"Icircumflex", "\u{ce}"), + (0o354, b"Idieresis", "\u{cf}"), + (0o355, b"Igrave", "\u{cc}"), + (0o356, b"Oacute", "\u{d3}"), + (0o357, b"Ocircumflex", "\u{d4}"), + (0o361, b"Ograve", "\u{d2}"), + (0o362, b"Uacute", "\u{da}"), + (0o363, b"Ucircumflex", "\u{db}"), + (0o364, b"Ugrave", "\u{d9}"), + (0o365, b"dotlessi", "\u{131}"), + (0o366, b"circumflex", "\u{2c6}"), + (0o367, b"tilde", "\u{2dc}"), + (0o370, b"macron", "\u{af}"), + (0o371, b"breve", "\u{2d8}"), + (0o372, b"dotaccent", "\u{2d9}"), + (0o373, b"ring", "\u{2da}"), + (0o374, b"cedilla", "\u{b8}"), + (0o375, b"hungarumlaut", "\u{2dd}"), + (0o376, b"ogonek", "\u{2db}"), + (0o377, b"caron", "\u{2c7}"), + ] +} + +builtin_simple_font_encoding_table! { + pub const STANDARD; + [ + (0o040, b"space", " "), + (0o041, b"exclam", "!"), + (0o042, b"quotedbl", "\""), + (0o043, b"numbersign", "#"), + (0o044, b"dollar", "$"), + (0o045, b"percent", "%"), + (0o046, b"ampersand", "&"), + (0o047, b"quoteright", "\u{2019}"), + (0o050, b"parenleft", "("), + (0o051, b"parenright", ")"), + (0o052, b"asterisk", "*"), + (0o053, b"plus", "+"), + (0o054, b"comma", ","), + (0o055, b"hyphen", "-"), + (0o056, b"period", "."), + (0o057, b"slash", "/"), + (0o060, b"zero", "0"), + (0o061, b"one", "1"), + (0o062, b"two", "2"), + (0o063, b"three", "3"), + (0o064, b"four", "4"), + (0o065, b"five", "5"), + (0o066, b"six", "6"), + (0o067, b"seven", "7"), + (0o070, b"eight", "8"), + (0o071, b"nine", "9"), + (0o072, b"colon", ":"), + (0o073, b"semicolon", ";"), + (0o074, b"less", "<"), + (0o075, b"equal", "="), + (0o076, b"greater", ">"), + (0o077, b"question", "?"), + (0o100, b"at", "@"), + (0o101, b"A", "A"), + (0o102, b"B", "B"), + (0o103, b"C", "C"), + (0o104, b"D", "D"), + (0o105, b"E", "E"), + (0o106, b"F", "F"), + (0o107, b"G", "G"), + (0o110, b"H", "H"), + (0o111, b"I", "I"), + (0o112, b"J", "J"), + (0o113, b"K", "K"), + (0o114, b"L", "L"), + (0o115, b"M", "M"), + (0o116, b"N", "N"), + (0o117, b"O", "O"), + (0o120, b"P", "P"), + (0o121, b"Q", "Q"), + (0o122, b"R", "R"), + (0o123, b"S", "S"), + (0o124, b"T", "T"), + (0o125, b"U", "U"), + (0o126, b"V", "V"), + (0o127, b"W", "W"), + (0o130, b"X", "X"), + (0o131, b"Y", "Y"), + (0o132, b"Z", "Z"), + (0o133, b"bracketleft", "["), + (0o134, b"backslash", "\\"), + (0o135, b"bracketright", "]"), + (0o136, b"asciicircum", "^"), + (0o137, b"underscore", "_"), + (0o140, b"quoteleft", "\u{2018}"), + (0o141, b"a", "a"), + (0o142, b"b", "b"), + (0o143, b"c", "c"), + (0o144, b"d", "d"), + (0o145, b"e", "e"), + (0o146, b"f", "f"), + (0o147, b"g", "g"), + (0o150, b"h", "h"), + (0o151, b"i", "i"), + (0o152, b"j", "j"), + (0o153, b"k", "k"), + (0o154, b"l", "l"), + (0o155, b"m", "m"), + (0o156, b"n", "n"), + (0o157, b"o", "o"), + (0o160, b"p", "p"), + (0o161, b"q", "q"), + (0o162, b"r", "r"), + (0o163, b"s", "s"), + (0o164, b"t", "t"), + (0o165, b"u", "u"), + (0o166, b"v", "v"), + (0o167, b"w", "w"), + (0o170, b"x", "x"), + (0o171, b"y", "y"), + (0o172, b"z", "z"), + (0o173, b"braceleft", "{"), + (0o174, b"bar", "|"), + (0o175, b"braceright", "}"), + (0o176, b"asciitilde", "~"), + (0o241, b"exclamdown", "\u{a1}"), + (0o242, b"cent", "\u{a2}"), + (0o243, b"sterling", "\u{a3}"), + (0o244, b"fraction", "\u{2044}"), + (0o245, b"yen", "\u{a5}"), + (0o246, b"florin", "\u{192}"), + (0o247, b"section", "\u{a7}"), + (0o250, b"currency", "\u{a4}"), + (0o251, b"quotesingle", "\'"), + (0o252, b"quotedblleft", "\u{201c}"), + (0o253, b"guillemotleft", "\u{ab}"), + (0o254, b"guilsinglleft", "\u{2039}"), + (0o255, b"guilsinglright", "\u{203a}"), + (0o256, b"fi", "\u{fb01}"), + (0o257, b"fl", "\u{fb02}"), + (0o261, b"endash", "\u{2013}"), + (0o262, b"dagger", "\u{2020}"), + (0o263, b"daggerdbl", "\u{2021}"), + (0o264, b"periodcentered", "\u{b7}"), + (0o266, b"paragraph", "\u{b6}"), + (0o267, b"bullet", "\u{2022}"), + (0o270, b"quotesinglbase", "\u{201a}"), + (0o271, b"quotedblbase", "\u{201e}"), + (0o272, b"quotedblright", "\u{201d}"), + (0o273, b"guillemotright", "\u{bb}"), + (0o274, b"ellipsis", "\u{2026}"), + (0o275, b"perthousand", "\u{2030}"), + (0o277, b"questiondown", "\u{bf}"), + (0o301, b"grave", "`"), + (0o302, b"acute", "\u{b4}"), + (0o303, b"circumflex", "\u{2c6}"), + (0o304, b"tilde", "\u{2dc}"), + (0o305, b"macron", "\u{af}"), + (0o306, b"breve", "\u{2d8}"), + (0o307, b"dotaccent", "\u{2d9}"), + (0o310, b"dieresis", "\u{a8}"), + (0o312, b"ring", "\u{2da}"), + (0o313, b"cedilla", "\u{b8}"), + (0o315, b"hungarumlaut", "\u{2dd}"), + (0o316, b"ogonek", "\u{2db}"), + (0o317, b"caron", "\u{2c7}"), + (0o320, b"emdash", "\u{2014}"), + (0o341, b"AE", "\u{c6}"), + (0o343, b"ordfeminine", "\u{aa}"), + (0o350, b"Lslash", "\u{141}"), + (0o351, b"Oslash", "\u{d8}"), + (0o352, b"OE", "\u{152}"), + (0o353, b"ordmasculine", "\u{ba}"), + (0o361, b"ae", "\u{e6}"), + (0o365, b"dotlessi", "\u{131}"), + (0o370, b"lslash", "\u{142}"), + (0o371, b"oslash", "\u{f8}"), + (0o372, b"oe", "\u{153}"), + (0o373, b"germandbls", "\u{df}"), + ] +} + +builtin_simple_font_encoding_table! { + pub const WIN_ANSI; + #[default = (b"bullet", "\u{2022}")] + [ + (0o040, b"space", " "), + (0o041, b"exclam", "!"), + (0o042, b"quotedbl", "\""), + (0o043, b"numbersign", "#"), + (0o044, b"dollar", "$"), + (0o045, b"percent", "%"), + (0o046, b"ampersand", "&"), + (0o047, b"quotesingle", "\'"), + (0o050, b"parenleft", "("), + (0o051, b"parenright", ")"), + (0o052, b"asterisk", "*"), + (0o053, b"plus", "+"), + (0o054, b"comma", ","), + (0o055, b"hyphen", "-"), + (0o056, b"period", "."), + (0o057, b"slash", "/"), + (0o060, b"zero", "0"), + (0o061, b"one", "1"), + (0o062, b"two", "2"), + (0o063, b"three", "3"), + (0o064, b"four", "4"), + (0o065, b"five", "5"), + (0o066, b"six", "6"), + (0o067, b"seven", "7"), + (0o070, b"eight", "8"), + (0o071, b"nine", "9"), + (0o072, b"colon", ":"), + (0o073, b"semicolon", ";"), + (0o074, b"less", "<"), + (0o075, b"equal", "="), + (0o076, b"greater", ">"), + (0o077, b"question", "?"), + (0o100, b"at", "@"), + (0o101, b"A", "A"), + (0o102, b"B", "B"), + (0o103, b"C", "C"), + (0o104, b"D", "D"), + (0o105, b"E", "E"), + (0o106, b"F", "F"), + (0o107, b"G", "G"), + (0o110, b"H", "H"), + (0o111, b"I", "I"), + (0o112, b"J", "J"), + (0o113, b"K", "K"), + (0o114, b"L", "L"), + (0o115, b"M", "M"), + (0o116, b"N", "N"), + (0o117, b"O", "O"), + (0o120, b"P", "P"), + (0o121, b"Q", "Q"), + (0o122, b"R", "R"), + (0o123, b"S", "S"), + (0o124, b"T", "T"), + (0o125, b"U", "U"), + (0o126, b"V", "V"), + (0o127, b"W", "W"), + (0o130, b"X", "X"), + (0o131, b"Y", "Y"), + (0o132, b"Z", "Z"), + (0o133, b"bracketleft", "["), + (0o134, b"backslash", "\\"), + (0o135, b"bracketright", "]"), + (0o136, b"asciicircum", "^"), + (0o137, b"underscore", "_"), + (0o140, b"grave", "`"), + (0o141, b"a", "a"), + (0o142, b"b", "b"), + (0o143, b"c", "c"), + (0o144, b"d", "d"), + (0o145, b"e", "e"), + (0o146, b"f", "f"), + (0o147, b"g", "g"), + (0o150, b"h", "h"), + (0o151, b"i", "i"), + (0o152, b"j", "j"), + (0o153, b"k", "k"), + (0o154, b"l", "l"), + (0o155, b"m", "m"), + (0o156, b"n", "n"), + (0o157, b"o", "o"), + (0o160, b"p", "p"), + (0o161, b"q", "q"), + (0o162, b"r", "r"), + (0o163, b"s", "s"), + (0o164, b"t", "t"), + (0o165, b"u", "u"), + (0o166, b"v", "v"), + (0o167, b"w", "w"), + (0o170, b"x", "x"), + (0o171, b"y", "y"), + (0o172, b"z", "z"), + (0o173, b"braceleft", "{"), + (0o174, b"bar", "|"), + (0o175, b"braceright", "}"), + (0o176, b"asciitilde", "~"), + (0o200, b"Euro", "\u{20ac}"), + (0o202, b"quotesinglbase", "\u{201a}"), + (0o203, b"florin", "\u{192}"), + (0o204, b"quotedblbase", "\u{201e}"), + (0o205, b"ellipsis", "\u{2026}"), + (0o206, b"dagger", "\u{2020}"), + (0o207, b"daggerdbl", "\u{2021}"), + (0o210, b"circumflex", "\u{2c6}"), + (0o211, b"perthousand", "\u{2030}"), + (0o212, b"Scaron", "\u{160}"), + (0o213, b"guilsinglleft", "\u{2039}"), + (0o214, b"OE", "\u{152}"), + (0o216, b"Zcaron", "\u{17d}"), + (0o221, b"quoteleft", "\u{2018}"), + (0o222, b"quoteright", "\u{2019}"), + (0o223, b"quotedblleft", "\u{201c}"), + (0o224, b"quotedblright", "\u{201d}"), + (0o225, b"bullet", "\u{2022}"), + (0o226, b"endash", "\u{2013}"), + (0o227, b"emdash", "\u{2014}"), + (0o230, b"tilde", "\u{2dc}"), + (0o231, b"trademark", "\u{2122}"), + (0o232, b"scaron", "\u{161}"), + (0o233, b"guilsinglright", "\u{203a}"), + (0o234, b"oe", "\u{153}"), + (0o236, b"zcaron", "\u{17e}"), + (0o237, b"Ydieresis", "\u{178}"), + (0o240, b"space", "\u{a0}"), + (0o241, b"exclamdown", "\u{a1}"), + (0o242, b"cent", "\u{a2}"), + (0o243, b"sterling", "\u{a3}"), + (0o244, b"currency", "\u{a4}"), + (0o245, b"yen", "\u{a5}"), + (0o246, b"brokenbar", "\u{a6}"), + (0o247, b"section", "\u{a7}"), + (0o250, b"dieresis", "\u{a8}"), + (0o251, b"copyright", "\u{a9}"), + (0o252, b"ordfeminine", "\u{aa}"), + (0o253, b"guillemotleft", "\u{ab}"), + (0o254, b"logicalnot", "\u{ac}"), + (0o255, b"hyphen", "\u{ad}"), + (0o256, b"registered", "\u{ae}"), + (0o257, b"macron", "\u{af}"), + (0o260, b"degree", "\u{b0}"), + (0o261, b"plusminus", "\u{b1}"), + (0o262, b"twosuperior", "\u{b2}"), + (0o263, b"threesuperior", "\u{b3}"), + (0o264, b"acute", "\u{b4}"), + (0o265, b"mu", "\u{3bc}"), + (0o266, b"paragraph", "\u{b6}"), + (0o267, b"periodcentered", "\u{b7}"), + (0o270, b"cedilla", "\u{b8}"), + (0o271, b"onesuperior", "\u{b9}"), + (0o272, b"ordmasculine", "\u{ba}"), + (0o273, b"guillemotright", "\u{bb}"), + (0o274, b"onequarter", "\u{bc}"), + (0o275, b"onehalf", "\u{bd}"), + (0o276, b"threequarters", "\u{be}"), + (0o277, b"questiondown", "\u{bf}"), + (0o300, b"Agrave", "\u{c0}"), + (0o301, b"Aacute", "\u{c1}"), + (0o302, b"Acircumflex", "\u{c2}"), + (0o303, b"Atilde", "\u{c3}"), + (0o304, b"Adieresis", "\u{c4}"), + (0o305, b"Aring", "\u{c5}"), + (0o306, b"AE", "\u{c6}"), + (0o307, b"Ccedilla", "\u{c7}"), + (0o310, b"Egrave", "\u{c8}"), + (0o311, b"Eacute", "\u{c9}"), + (0o312, b"Ecircumflex", "\u{ca}"), + (0o313, b"Edieresis", "\u{cb}"), + (0o314, b"Igrave", "\u{cc}"), + (0o315, b"Iacute", "\u{cd}"), + (0o316, b"Icircumflex", "\u{ce}"), + (0o317, b"Idieresis", "\u{cf}"), + (0o320, b"Eth", "\u{d0}"), + (0o321, b"Ntilde", "\u{d1}"), + (0o322, b"Ograve", "\u{d2}"), + (0o323, b"Oacute", "\u{d3}"), + (0o324, b"Ocircumflex", "\u{d4}"), + (0o325, b"Otilde", "\u{d5}"), + (0o326, b"Odieresis", "\u{d6}"), + (0o327, b"multiply", "\u{d7}"), + (0o330, b"Oslash", "\u{d8}"), + (0o331, b"Ugrave", "\u{d9}"), + (0o332, b"Uacute", "\u{da}"), + (0o333, b"Ucircumflex", "\u{db}"), + (0o334, b"Udieresis", "\u{dc}"), + (0o335, b"Yacute", "\u{dd}"), + (0o336, b"Thorn", "\u{de}"), + (0o337, b"germandbls", "\u{df}"), + (0o340, b"agrave", "\u{e0}"), + (0o341, b"aacute", "\u{e1}"), + (0o342, b"acircumflex", "\u{e2}"), + (0o343, b"atilde", "\u{e3}"), + (0o344, b"adieresis", "\u{e4}"), + (0o345, b"aring", "\u{e5}"), + (0o346, b"ae", "\u{e6}"), + (0o347, b"ccedilla", "\u{e7}"), + (0o350, b"egrave", "\u{e8}"), + (0o351, b"eacute", "\u{e9}"), + (0o352, b"ecircumflex", "\u{ea}"), + (0o353, b"edieresis", "\u{eb}"), + (0o354, b"igrave", "\u{ec}"), + (0o355, b"iacute", "\u{ed}"), + (0o356, b"icircumflex", "\u{ee}"), + (0o357, b"idieresis", "\u{ef}"), + (0o360, b"eth", "\u{f0}"), + (0o361, b"ntilde", "\u{f1}"), + (0o362, b"ograve", "\u{f2}"), + (0o363, b"oacute", "\u{f3}"), + (0o364, b"ocircumflex", "\u{f4}"), + (0o365, b"otilde", "\u{f5}"), + (0o366, b"odieresis", "\u{f6}"), + (0o367, b"divide", "\u{f7}"), + (0o370, b"oslash", "\u{f8}"), + (0o371, b"ugrave", "\u{f9}"), + (0o372, b"uacute", "\u{fa}"), + (0o373, b"ucircumflex", "\u{fb}"), + (0o374, b"udieresis", "\u{fc}"), + (0o375, b"yacute", "\u{fd}"), + (0o376, b"thorn", "\u{fe}"), + (0o377, b"ydieresis", "\u{ff}"), + ] +} + +builtin_simple_font_encoding_table! { + pub const PDF_DOC; + [ + (0o011, None, "\t"), + (0o012, None, "\n"), + (0o015, None, "\r"), + (0o030, b"breve", "\u{2d8}"), + (0o031, b"caron", "\u{2c7}"), + (0o032, b"circumflex", "\u{2c6}"), + (0o033, b"dotaccent", "\u{2d9}"), + (0o034, b"hungarumlaut", "\u{2dd}"), + (0o035, b"ogonek", "\u{2db}"), + (0o036, b"ring", "\u{2da}"), + (0o037, b"tilde", "\u{2dc}"), + (0o040, b"space", " "), + (0o041, b"exclam", "!"), + (0o042, b"quotedbl", "\""), + (0o043, b"numbersign", "#"), + (0o044, b"dollar", "$"), + (0o045, b"percent", "%"), + (0o046, b"ampersand", "&"), + (0o047, b"quotesingle", "\'"), + (0o050, b"parenleft", "("), + (0o051, b"parenright", ")"), + (0o052, b"asterisk", "*"), + (0o053, b"plus", "+"), + (0o054, b"comma", ","), + (0o055, b"hyphen", "-"), + (0o056, b"period", "."), + (0o057, b"slash", "/"), + (0o060, b"zero", "0"), + (0o061, b"one", "1"), + (0o062, b"two", "2"), + (0o063, b"three", "3"), + (0o064, b"four", "4"), + (0o065, b"five", "5"), + (0o066, b"six", "6"), + (0o067, b"seven", "7"), + (0o070, b"eight", "8"), + (0o071, b"nine", "9"), + (0o072, b"colon", ":"), + (0o073, b"semicolon", ";"), + (0o074, b"less", "<"), + (0o075, b"equal", "="), + (0o076, b"greater", ">"), + (0o077, b"question", "?"), + (0o100, b"at", "@"), + (0o101, b"A", "A"), + (0o102, b"B", "B"), + (0o103, b"C", "C"), + (0o104, b"D", "D"), + (0o105, b"E", "E"), + (0o106, b"F", "F"), + (0o107, b"G", "G"), + (0o110, b"H", "H"), + (0o111, b"I", "I"), + (0o112, b"J", "J"), + (0o113, b"K", "K"), + (0o114, b"L", "L"), + (0o115, b"M", "M"), + (0o116, b"N", "N"), + (0o117, b"O", "O"), + (0o120, b"P", "P"), + (0o121, b"Q", "Q"), + (0o122, b"R", "R"), + (0o123, b"S", "S"), + (0o124, b"T", "T"), + (0o125, b"U", "U"), + (0o126, b"V", "V"), + (0o127, b"W", "W"), + (0o130, b"X", "X"), + (0o131, b"Y", "Y"), + (0o132, b"Z", "Z"), + (0o133, b"bracketleft", "["), + (0o134, b"backslash", "\\"), + (0o135, b"bracketright", "]"), + (0o136, b"asciicircum", "^"), + (0o137, b"underscore", "_"), + (0o140, b"grave", "`"), + (0o141, b"a", "a"), + (0o142, b"b", "b"), + (0o143, b"c", "c"), + (0o144, b"d", "d"), + (0o145, b"e", "e"), + (0o146, b"f", "f"), + (0o147, b"g", "g"), + (0o150, b"h", "h"), + (0o151, b"i", "i"), + (0o152, b"j", "j"), + (0o153, b"k", "k"), + (0o154, b"l", "l"), + (0o155, b"m", "m"), + (0o156, b"n", "n"), + (0o157, b"o", "o"), + (0o160, b"p", "p"), + (0o161, b"q", "q"), + (0o162, b"r", "r"), + (0o163, b"s", "s"), + (0o164, b"t", "t"), + (0o165, b"u", "u"), + (0o166, b"v", "v"), + (0o167, b"w", "w"), + (0o170, b"x", "x"), + (0o171, b"y", "y"), + (0o172, b"z", "z"), + (0o173, b"braceleft", "{"), + (0o174, b"bar", "|"), + (0o175, b"braceright", "}"), + (0o176, b"asciitilde", "~"), + (0o200, b"bullet", "\u{2022}"), + (0o201, b"dagger", "\u{2020}"), + (0o202, b"daggerdbl", "\u{2021}"), + (0o203, b"ellipsis", "\u{2026}"), + (0o204, b"emdash", "\u{2014}"), + (0o205, b"endash", "\u{2013}"), + (0o206, b"florin", "\u{192}"), + (0o207, b"fraction", "\u{2044}"), + (0o210, b"guilsinglleft", "\u{2039}"), + (0o211, b"guilsinglright", "\u{203a}"), + (0o212, b"minus", "\u{2212}"), + (0o213, b"perthousand", "\u{2030}"), + (0o214, b"quotedblbase", "\u{201e}"), + (0o215, b"quotedblleft", "\u{201c}"), + (0o216, b"quotedblright", "\u{201d}"), + (0o217, b"quoteleft", "\u{2018}"), + (0o220, b"quoteright", "\u{2019}"), + (0o221, b"quotesinglbase", "\u{201a}"), + (0o222, b"trademark", "\u{2122}"), + (0o223, b"fi", "\u{fb01}"), + (0o224, b"fl", "\u{fb02}"), + (0o225, b"Lslash", "\u{141}"), + (0o226, b"OE", "\u{152}"), + (0o227, b"Scaron", "\u{160}"), + (0o230, b"Ydieresis", "\u{178}"), + (0o231, b"Zcaron", "\u{17d}"), + (0o232, b"dotlessi", "\u{131}"), + (0o233, b"lslash", "\u{142}"), + (0o234, b"oe", "\u{153}"), + (0o235, b"scaron", "\u{161}"), + (0o236, b"zcaron", "\u{17e}"), + (0o240, b"Euro", "\u{20ac}"), + (0o241, b"exclamdown", "\u{a1}"), + (0o242, b"cent", "\u{a2}"), + (0o243, b"sterling", "\u{a3}"), + (0o244, b"currency", "\u{a4}"), + (0o245, b"yen", "\u{a5}"), + (0o246, b"brokenbar", "\u{a6}"), + (0o247, b"section", "\u{a7}"), + (0o250, b"dieresis", "\u{a8}"), + (0o251, b"copyright", "\u{a9}"), + (0o252, b"ordfeminine", "\u{aa}"), + (0o253, b"guillemotleft", "\u{ab}"), + (0o254, b"logicalnot", "\u{ac}"), + (0o256, b"registered", "\u{ae}"), + (0o257, b"macron", "\u{af}"), + (0o260, b"degree", "\u{b0}"), + (0o261, b"plusminus", "\u{b1}"), + (0o262, b"twosuperior", "\u{b2}"), + (0o263, b"threesuperior", "\u{b3}"), + (0o264, b"acute", "\u{b4}"), + (0o265, b"mu", "\u{3bc}"), + (0o266, b"paragraph", "\u{b6}"), + (0o267, b"periodcentered", "\u{b7}"), + (0o270, b"cedilla", "\u{b8}"), + (0o271, b"onesuperior", "\u{b9}"), + (0o272, b"ordmasculine", "\u{ba}"), + (0o273, b"guillemotright", "\u{bb}"), + (0o274, b"onequarter", "\u{bc}"), + (0o275, b"onehalf", "\u{bd}"), + (0o276, b"threequarters", "\u{be}"), + (0o277, b"questiondown", "\u{bf}"), + (0o300, b"Agrave", "\u{c0}"), + (0o301, b"Aacute", "\u{c1}"), + (0o302, b"Acircumflex", "\u{c2}"), + (0o303, b"Atilde", "\u{c3}"), + (0o304, b"Adieresis", "\u{c4}"), + (0o305, b"Aring", "\u{c5}"), + (0o306, b"AE", "\u{c6}"), + (0o307, b"Ccedilla", "\u{c7}"), + (0o310, b"Egrave", "\u{c8}"), + (0o311, b"Eacute", "\u{c9}"), + (0o312, b"Ecircumflex", "\u{ca}"), + (0o313, b"Edieresis", "\u{cb}"), + (0o314, b"Igrave", "\u{cc}"), + (0o315, b"Iacute", "\u{cd}"), + (0o316, b"Icircumflex", "\u{ce}"), + (0o317, b"Idieresis", "\u{cf}"), + (0o320, b"Eth", "\u{d0}"), + (0o321, b"Ntilde", "\u{d1}"), + (0o322, b"Ograve", "\u{d2}"), + (0o323, b"Oacute", "\u{d3}"), + (0o324, b"Ocircumflex", "\u{d4}"), + (0o325, b"Otilde", "\u{d5}"), + (0o326, b"Odieresis", "\u{d6}"), + (0o327, b"multiply", "\u{d7}"), + (0o330, b"Oslash", "\u{d8}"), + (0o331, b"Ugrave", "\u{d9}"), + (0o332, b"Uacute", "\u{da}"), + (0o333, b"Ucircumflex", "\u{db}"), + (0o334, b"Udieresis", "\u{dc}"), + (0o335, b"Yacute", "\u{dd}"), + (0o336, b"Thorn", "\u{de}"), + (0o337, b"germandbls", "\u{df}"), + (0o340, b"agrave", "\u{e0}"), + (0o341, b"aacute", "\u{e1}"), + (0o342, b"acircumflex", "\u{e2}"), + (0o343, b"atilde", "\u{e3}"), + (0o344, b"adieresis", "\u{e4}"), + (0o345, b"aring", "\u{e5}"), + (0o346, b"ae", "\u{e6}"), + (0o347, b"ccedilla", "\u{e7}"), + (0o350, b"egrave", "\u{e8}"), + (0o351, b"eacute", "\u{e9}"), + (0o352, b"ecircumflex", "\u{ea}"), + (0o353, b"edieresis", "\u{eb}"), + (0o354, b"igrave", "\u{ec}"), + (0o355, b"iacute", "\u{ed}"), + (0o356, b"icircumflex", "\u{ee}"), + (0o357, b"idieresis", "\u{ef}"), + (0o360, b"eth", "\u{f0}"), + (0o361, b"ntilde", "\u{f1}"), + (0o362, b"ograve", "\u{f2}"), + (0o363, b"oacute", "\u{f3}"), + (0o364, b"ocircumflex", "\u{f4}"), + (0o365, b"otilde", "\u{f5}"), + (0o366, b"odieresis", "\u{f6}"), + (0o367, b"divide", "\u{f7}"), + (0o370, b"oslash", "\u{f8}"), + (0o371, b"ugrave", "\u{f9}"), + (0o372, b"uacute", "\u{fa}"), + (0o373, b"ucircumflex", "\u{fb}"), + (0o374, b"udieresis", "\u{fc}"), + (0o375, b"yacute", "\u{fd}"), + (0o376, b"thorn", "\u{fe}"), + (0o377, b"ydieresis", "\u{ff}"), + ] +} + +builtin_simple_font_encoding_table! { + pub const MAC_EXPERT; + [ + (0o040, b"space", " "), + (0o041, b"exclamsmall", "!"), + (0o042, b"Hungarumlautsmall", "\u{2dd}"), + (0o043, b"centoldstyle", "\u{a2}"), + (0o044, b"dollaroldstyle", "$"), + (0o045, b"dollarsuperior", "$"), + (0o046, b"ampersandsmall", "&"), + (0o047, b"Acutesmall", "\u{b4}"), + (0o050, b"parenleftsuperior", "\u{207d}"), + (0o051, b"parenrightsuperior", "\u{207e}"), + (0o052, b"twodotenleader", "\u{2025}"), + (0o053, b"onedotenleader", "\u{2024}"), + (0o054, b"comma", ","), + (0o055, b"hyphen", "-"), + (0o056, b"period", "."), + (0o057, b"fraction", "\u{2044}"), + (0o060, b"zerooldstyle", "0"), + (0o061, b"oneoldstyle", "1"), + (0o062, b"twooldstyle", "2"), + (0o063, b"threeoldstyle", "3"), + (0o064, b"fouroldstyle", "4"), + (0o065, b"fiveoldstyle", "5"), + (0o066, b"sixoldstyle", "6"), + (0o067, b"sevenoldstyle", "7"), + (0o070, b"eightoldstyle", "8"), + (0o071, b"nineoldstyle", "9"), + (0o072, b"colon", ":"), + (0o073, b"semicolon", ";"), + (0o075, b"threequartersemdash", "\u{2014}"), + (0o077, b"questionsmall", "?"), + (0o104, b"Ethsmall", "\u{f0}"), + (0o107, b"onequarter", "\u{bc}"), + (0o110, b"onehalf", "\u{bd}"), + (0o111, b"threequarters", "\u{be}"), + (0o112, b"oneeighth", "\u{215b}"), + (0o113, b"threeeighths", "\u{215c}"), + (0o114, b"fiveeighths", "\u{215d}"), + (0o115, b"seveneighths", "\u{215e}"), + (0o116, b"onethird", "\u{2153}"), + (0o117, b"twothirds", "\u{2154}"), + (0o126, b"ff", "\u{fb00}"), + (0o127, b"fi", "\u{fb01}"), + (0o130, b"fl", "\u{fb02}"), + (0o131, b"ffi", "\u{fb03}"), + (0o132, b"ffl", "\u{fb04}"), + (0o133, b"parenleftinferior", "\u{208d}"), + (0o135, b"parenrightinferior", "\u{208e}"), + (0o136, b"Circumflexsmall", "\u{2c6}"), + (0o137, b"hypheninferior", "-"), + (0o140, b"Gravesmall", "`"), + (0o141, b"Asmall", "a"), + (0o142, b"Bsmall", "b"), + (0o143, b"Csmall", "c"), + (0o144, b"Dsmall", "d"), + (0o145, b"Esmall", "e"), + (0o146, b"Fsmall", "f"), + (0o147, b"Gsmall", "g"), + (0o150, b"Hsmall", "h"), + (0o151, b"Ismall", "i"), + (0o152, b"Jsmall", "j"), + (0o153, b"Ksmall", "k"), + (0o154, b"Lsmall", "l"), + (0o155, b"Msmall", "m"), + (0o156, b"Nsmall", "n"), + (0o157, b"Osmall", "o"), + (0o160, b"Psmall", "p"), + (0o161, b"Qsmall", "q"), + (0o162, b"Rsmall", "r"), + (0o163, b"Ssmall", "s"), + (0o164, b"Tsmall", "t"), + (0o165, b"Usmall", "u"), + (0o166, b"Vsmall", "v"), + (0o167, b"Wsmall", "w"), + (0o170, b"Xsmall", "x"), + (0o171, b"Ysmall", "y"), + (0o172, b"Zsmall", "z"), + (0o173, b"colonmonetary", "\u{20a1}"), + (0o174, b"onefitted", "1"), + (0o175, b"rupiah", "Rp"), + (0o176, b"Tildesmall", "\u{2dc}"), + (0o201, b"asuperior", "a"), + (0o202, b"centsuperior", "\u{a2}"), + (0o207, b"Aacutesmall", "\u{e1}"), + (0o210, b"Agravesmall", "\u{e0}"), + (0o211, b"Acircumflexsmall", "\u{e2}"), + (0o212, b"Adieresissmall", "\u{e4}"), + (0o213, b"Atildesmall", "\u{e3}"), + (0o214, b"Aringsmall", "\u{e5}"), + (0o215, b"Ccedillasmall", "\u{e7}"), + (0o216, b"Eacutesmall", "\u{e9}"), + (0o217, b"Egravesmall", "\u{e8}"), + (0o220, b"Ecircumflexsmall", "\u{ea}"), + (0o221, b"Edieresissmall", "\u{eb}"), + (0o222, b"Iacutesmall", "\u{ed}"), + (0o223, b"Igravesmall", "\u{ec}"), + (0o224, b"Icircumflexsmall", "\u{ee}"), + (0o225, b"Idieresissmall", "\u{ef}"), + (0o226, b"Ntildesmall", "\u{f1}"), + (0o227, b"Oacutesmall", "\u{f3}"), + (0o230, b"Ogravesmall", "\u{f2}"), + (0o231, b"Ocircumflexsmall", "\u{f4}"), + (0o232, b"Odieresissmall", "\u{f6}"), + (0o233, b"Otildesmall", "\u{f5}"), + (0o234, b"Uacutesmall", "\u{fa}"), + (0o235, b"Ugravesmall", "\u{f9}"), + (0o236, b"Ucircumflexsmall", "\u{fb}"), + (0o237, b"Udieresissmall", "\u{fc}"), + (0o241, b"eightsuperior", "\u{2078}"), + (0o242, b"fourinferior", "\u{2084}"), + (0o243, b"threeinferior", "\u{2083}"), + (0o244, b"sixinferior", "\u{2086}"), + (0o245, b"eightinferior", "\u{2088}"), + (0o246, b"seveninferior", "\u{2087}"), + (0o247, b"Scaronsmall", "\u{161}"), + (0o251, b"centinferior", "\u{a2}"), + (0o252, b"twoinferior", "\u{2082}"), + (0o254, b"Dieresissmall", "\u{a8}"), + (0o256, b"Caronsmall", "\u{2c7}"), + (0o257, b"osuperior", "o"), + (0o260, b"fiveinferior", "\u{2085}"), + (0o262, b"commainferior", ","), + (0o263, b"periodinferior", "."), + (0o264, b"Yacutesmall", "\u{fd}"), + (0o266, b"dollarinferior", "$"), + (0o271, b"Thornsmall", "\u{fe}"), + (0o273, b"nineinferior", "\u{2089}"), + (0o274, b"zeroinferior", "\u{2080}"), + (0o275, b"Zcaronsmall", "\u{17e}"), + (0o276, b"AEsmall", "\u{e6}"), + (0o277, b"Oslashsmall", "\u{f8}"), + (0o300, b"questiondownsmall", "\u{bf}"), + (0o301, b"oneinferior", "\u{2081}"), + (0o302, b"Lslashsmall", "\u{142}"), + (0o311, b"Cedillasmall", "\u{b8}"), + (0o317, b"OEsmall", "\u{153}"), + (0o320, b"figuredash", "\u{2012}"), + (0o321, b"hyphensuperior", "-"), + (0o326, b"exclamdownsmall", "\u{a1}"), + (0o330, b"Ydieresissmall", "\u{ff}"), + (0o332, b"onesuperior", "\u{b9}"), + (0o333, b"twosuperior", "\u{b2}"), + (0o334, b"threesuperior", "\u{b3}"), + (0o335, b"foursuperior", "\u{2074}"), + (0o336, b"fivesuperior", "\u{2075}"), + (0o337, b"sixsuperior", "\u{2076}"), + (0o340, b"sevensuperior", "\u{2077}"), + (0o341, b"ninesuperior", "\u{2079}"), + (0o342, b"zerosuperior", "\u{2070}"), + (0o344, b"esuperior", "e"), + (0o345, b"rsuperior", "r"), + (0o346, b"tsuperior", "t"), + (0o351, b"isuperior", "i"), + (0o352, b"ssuperior", "s"), + (0o353, b"dsuperior", "d"), + (0o361, b"lsuperior", "l"), + (0o362, b"Ogoneksmall", "\u{2db}"), + (0o363, b"Brevesmall", "\u{2d8}"), + (0o364, b"Macronsmall", "\u{af}"), + (0o365, b"bsuperior", "b"), + (0o366, b"nsuperior", "\u{207f}"), + (0o367, b"msuperior", "m"), + (0o370, b"commasuperior", ","), + (0o371, b"periodsuperior", "."), + (0o372, b"Dotaccentsmall", "\u{2d9}"), + (0o373, b"Ringsmall", "\u{2da}"), + ] +} diff --git a/src/pdf/font/type_1_parse.rs b/src/pdf/font/type_1_parse.rs new file mode 100644 index 0000000..c557d5a --- /dev/null +++ b/src/pdf/font/type_1_parse.rs @@ -0,0 +1,1423 @@ +use crate::{ + pdf::{ + PdfObjects, + font::{PdfFontType1FontInfo, PdfFontType1Program}, + object::{PdfMatrix, PdfName, PdfRectangle, PdfStreamContents, PdfString, PdfVec2D}, + parse::{ + PdfInputPosition, PdfInputPositionKnown, PdfInputPositionNoCompare, PdfParseError, + }, + }, + util::ArcOrRef, +}; +use std::{ + cell::{Cell, RefCell}, + collections::BTreeMap, + fmt, + num::NonZero, + rc::Rc, + sync::Arc, +}; + +#[derive(Debug)] +enum PsBreakReason { + FoundEExec, + Error(PdfParseError), +} + +fn custom_err>(msg: impl ToString) -> Result { + Err(PdfParseError::Custom(msg.to_string()).into()) +} + +impl From for PsBreakReason { + fn from(value: PdfParseError) -> Self { + Self::Error(value) + } +} + +struct PsFileDecryptedSource { + source: Box>, + decoded: Vec, +} + +impl PsFileDecryptedSource { + fn get(&mut self, index: usize) -> Option { + loop { + if let Some(byte) = self.decoded.get(index) { + return Some(*byte); + } + self.decoded.push(self.source.next()?); + } + } +} + +#[derive(Clone)] +enum PsFileSource { + Bytes(Rc<[u8]>), + Decrypted(Rc>), +} + +impl PsFileSource { + fn get(&self, index: usize) -> Option { + match self { + PsFileSource::Bytes(bytes) => bytes.get(index).copied(), + PsFileSource::Decrypted(src) => src.borrow_mut().get(index), + } + } +} + +#[derive(Clone)] +struct PsFile { + id: u64, + source: PsFileSource, + pos: Rc>, +} + +impl PartialEq for PsFile { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for PsFile {} + +impl PartialOrd for PsFile { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for PsFile { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + +impl fmt::Debug for PsFile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { id, source: _, pos } = self; + f.debug_struct("PsFile") + .field("id", id) + .field("pos", pos) + .finish_non_exhaustive() + } +} + +fn is_whitespace_char(v: u8) -> bool { + matches!(v, b'\0' | b'\t' | b'\n' | b'\x0C' | b'\r' | b' ') +} + +fn is_special_char(v: u8) -> bool { + matches!( + v, + b'(' | b')' | b'<' | b'>' | b'[' | b']' | b'{' | b'}' | b'/' | b'%' + ) +} + +fn is_regular_char(v: u8) -> bool { + !(is_whitespace_char(v) || is_special_char(v)) +} + +struct NotALineEnd; + +#[derive(Clone)] +enum Token { + Integer(i128), + Real(f64), + ArrayStart, + ArrayEnd, + ProcedureStart, + ProcedureEnd, + ExecutableName(Vec), + LiteralName(Vec), + ImmediatelyEvaluatedName(Vec), + String(Vec), +} + +impl fmt::Debug for Token { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Integer(v) => f.debug_tuple("Integer").field(v).finish(), + Self::Real(v) => f.debug_tuple("Real").field(v).finish(), + Self::ArrayStart => write!(f, "ArrayStart"), + Self::ArrayEnd => write!(f, "ArrayEnd"), + Self::ProcedureStart => write!(f, "ProcedureStart"), + Self::ProcedureEnd => write!(f, "ProcedureEnd"), + Self::ExecutableName(name) => write!(f, "ExecutableName({})", name.escape_ascii()), + Self::LiteralName(name) => write!(f, "LiteralName({})", name.escape_ascii()), + Self::ImmediatelyEvaluatedName(name) => { + write!(f, "ImmediatelyEvaluatedName({})", name.escape_ascii()) + } + Self::String(contents) => { + write!(f, "String({})", contents.escape_ascii()) + } + } + } +} + +impl PsFile { + fn new(id: u64, source: PsFileSource, pos: usize, stream_pos: PdfInputPosition) -> Self { + Self { + id, + source, + pos: Rc::new(Cell::new(PdfInputPositionKnown { + pos, + containing_streams_pos: stream_pos.get().map(|v| v.pos), + })), + } + } + fn pos(&self) -> PdfInputPosition { + PdfInputPosition::new(Some(self.pos.get())) + } + fn peek_byte(&self) -> Option { + self.source.get(self.pos.get().pos) + } + fn next_byte(&mut self) -> Option { + if let Some(b) = self.source.get(self.pos.get().pos) { + self.pos.update(|mut pos| { + pos.pos += 1; + pos + }); + Some(b) + } else { + None + } + } + fn try_parse_line_end(&mut self) -> Result<(), NotALineEnd> { + match self.peek_byte().ok_or(NotALineEnd)? { + b'\r' => { + self.next_byte(); + if self.peek_byte() == Some(b'\n') { + self.next_byte(); + } + Ok(()) + } + b'\x0C' | b'\n' => Ok(()), + _ => Err(NotALineEnd), + } + } + fn skip_whitespace(&mut self) { + while let Some(b'\0' | b'\t' | b'\n' | b'\x0C' | b'\r' | b' ') = self.peek_byte() { + self.next_byte(); + } + } + fn skip_comments_and_whitespace(&mut self) { + loop { + self.skip_whitespace(); + let Some(b'%') = self.peek_byte() else { + break; + }; + while self.peek_byte().is_some() { + if let Ok(()) = self.try_parse_line_end() { + break; + } + self.next_byte(); + } + } + } + fn parse_number(mut text: &[u8]) -> Option { + let full_text = text; + let sign = match text { + [sign @ (b'-' | b'+'), rest @ ..] => { + text = rest; + Some(*sign) + } + _ => None, + }; + let mut radix = Some(0u32); + let mut any_digits = false; + while let [digit @ b'0'..=b'9', rest @ ..] = text { + text = rest; + any_digits = true; + radix = radix + .and_then(|v| v.checked_mul(10)) + .and_then(|v| v.checked_add((digit - b'0').into())); + } + if let (Some(radix @ (2..=36)), [b'#', rest @ ..]) = (radix, text) { + text = rest; + if sign.is_some() || text.is_empty() { + return None; + } + let mut value = 0i128; + for &digit in text { + let digit = (digit as char).to_digit(radix)?; + value = value.checked_mul(radix.into())?; + value = value.checked_add(digit.into())?; + } + return Some(Token::Integer(value)); + } + let mut is_real = false; + if let [b'.', rest @ ..] = text { + text = rest; + is_real = true; + while let [b'0'..=b'9', rest @ ..] = text { + text = rest; + any_digits = true; + } + } + if !any_digits { + return None; + } + if let [b'e' | b'E', rest @ ..] = text { + text = rest; + is_real = true; + if let [b'+' | b'-', rest @ ..] = text { + text = rest; + } + let [b'0'..=b'9', ..] = text else { + return None; + }; + while let [b'0'..=b'9', rest @ ..] = text { + text = rest; + } + } + let full_text = str::from_utf8(full_text).ok()?; + if is_real { + Some(Token::Real(full_text.parse().ok()?)) + } else { + Some(Token::Integer(full_text.parse().ok()?)) + } + } + fn parse_string_after_l_paren(&mut self) -> Result { + let mut contents = Vec::new(); + let mut paren_level = NonZero::new(1usize).expect("non-zero"); + while let Some(b) = self.next_byte() { + contents.push(match b { + b'(' => { + paren_level = paren_level.checked_add(1).expect("overflow"); + b + } + b')' => { + let Some(new_paren_level) = NonZero::new(paren_level.get() - 1) else { + return Ok(Token::String(contents)); + }; + paren_level = new_paren_level; + b + } + b'\r' if self.peek_byte() == Some(b'\n') => { + self.next_byte(); + b'\n' + } + b'\r' | b'\n' => b'\n', + b'\\' => { + let pos = self.pos(); + let Some(b) = self.next_byte() else { + return Err(PdfParseError::InvalidStringEscape { pos }); + }; + match b { + b'\r' if self.peek_byte() == Some(b'\n') => { + self.next_byte(); + continue; + } + b'\r' | b'\n' => continue, + b'n' => b'\n', + b'r' => b'\r', + b't' => b'\t', + b'b' => b'\x08', + b'f' => b'\x0C', + b'(' | b')' | b'\\' => b, + b'0'..=b'7' => { + const MAX_OCTAL_DIGITS: usize = 3; + let mut value = b - b'0'; + let mut len = 1; + while len < MAX_OCTAL_DIGITS { + let Some(b @ b'0'..=b'7') = self.peek_byte() else { + break; + }; + value <<= 3; + value |= b - b'0'; + len += 1; + self.next_byte(); + } + value + } + _ => { + return Err(PdfParseError::InvalidStringEscape { pos }); + } + } + } + _ => b, + }); + } + Err(PdfParseError::TruncatedFile { pos: self.pos() }) + } + fn next_token(&mut self) -> Result, PdfParseError> { + self.skip_comments_and_whitespace(); + let Some(first_byte) = self.peek_byte() else { + return Ok(None); + }; + match first_byte { + b'(' => { + self.next_byte(); + self.parse_string_after_l_paren().map(Some) + } + b')' => todo!(), + b'<' => { + todo!("encoded string"); + } + b'>' => todo!(), + b'[' => { + self.next_byte(); + Ok(Some(Token::ArrayStart)) + } + b']' => { + self.next_byte(); + Ok(Some(Token::ArrayEnd)) + } + b'{' => { + self.next_byte(); + Ok(Some(Token::ProcedureStart)) + } + b'}' => { + self.next_byte(); + Ok(Some(Token::ProcedureEnd)) + } + b'/' => { + self.next_byte(); + let is_immediately_evaluated_name = self.peek_byte() == Some(b'/'); + if is_immediately_evaluated_name { + self.next_byte(); + } + let mut name = Vec::new(); + while self.peek_byte().is_some_and(is_regular_char) { + name.extend(self.next_byte()); + } + Ok(Some(if is_immediately_evaluated_name { + Token::ImmediatelyEvaluatedName(name) + } else { + Token::LiteralName(name) + })) + } + _ => { + let mut name = Vec::new(); + name.extend(self.next_byte()); + while self.peek_byte().is_some_and(is_regular_char) { + name.extend(self.next_byte()); + } + if let Some(token) = Self::parse_number(&name) { + Ok(Some(token)) + } else { + Ok(Some(Token::ExecutableName(name))) + } + } + } + } + fn decrypt_for_eexec_helper( + mut self, + new_id: u64, + random_bytes: Option<[u8; 4]>, + next_byte: impl Fn(&mut Self) -> Option + 'static, + ) -> Result { + let read_first_4 = || -> Option<[u8; 4]> { + let b0 = next_byte(&mut self)?; + let b1 = next_byte(&mut self)?; + let b2 = next_byte(&mut self)?; + let b3 = next_byte(&mut self)?; + Some([b0, b1, b2, b3]) + }; + let random_bytes = random_bytes.or_else(read_first_4).ok_or_else(|| { + PdfParseError::Custom("postscript eexec operator: can't read the 4 random bytes".into()) + })?; + let mut r = 55665u16; + let c1 = 52845u16; + let c2 = 22719u16; + let mut decrypt_one = move |cipher: u8| -> u8 { + dbg!(cipher); + let plain = cipher ^ (r >> 8) as u8; + dbg!(plain); + r = (cipher as u16) + .wrapping_add(r) + .wrapping_mul(c1) + .wrapping_add(c2); + dbg!(r); + plain + }; + for b in random_bytes { + decrypt_one(b); + } + let stream_pos = self.pos(); + Ok(Self::new( + new_id, + PsFileSource::Decrypted(Rc::new(RefCell::new(PsFileDecryptedSource { + source: Box::new(std::iter::from_fn(move || { + dbg!(next_byte(&mut self)).map(decrypt_one) + })), + decoded: Vec::new(), + }))), + 0, + stream_pos, + )) + } + fn decrypt_for_eexec(mut self, new_id: u64) -> Result { + while let Some(b' ' | b'\t' | b'\r' | b'\n') = self.peek_byte() { + dbg!(self.next_byte()); + } + let start_pos = self.pos.get(); + let mut read_first_4_binary = || -> Option<[u8; 4]> { + let b0 = self.next_byte()?; + let b1 = self.next_byte()?; + let b2 = self.next_byte()?; + let b3 = self.next_byte()?; + let retval = [b0, b1, b2, b3]; + if retval.iter().all(u8::is_ascii_hexdigit) { + None + } else { + Some(retval) + } + }; + if let Some(random_bytes) = dbg!(read_first_4_binary()) { + self.decrypt_for_eexec_helper(new_id, Some(random_bytes), PsFile::next_byte) + } else { + self.pos.set(start_pos); + let next_byte = |this: &mut Self| { + let mut first_digit = None; + loop { + let byte = this.peek_byte()?; + if matches!(byte, b' ' | b'\t' | b'\r' | b'\n') { + this.next_byte(); + continue; + } + let digit = (byte as char).to_digit(0x10)?; + this.next_byte(); + if let Some(first_digit) = first_digit { + return Some(((first_digit << 4) | digit) as u8); + } else { + first_digit = Some(digit); + } + } + }; + self.decrypt_for_eexec_helper(new_id, None, next_byte) + } + } +} + +#[derive(Clone, Copy, Debug, Default)] +struct PsReal(f64); + +impl PartialOrd for PsReal { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Eq for PsReal {} + +impl PartialEq for PsReal { + fn eq(&self, other: &Self) -> bool { + self.cmp(other).is_eq() + } +} + +impl Ord for PsReal { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + let l = (!self.0.is_nan()).then_some(self.0); + let r = (!other.0.is_nan()).then_some(other.0); + l.partial_cmp(&r).expect("already checked for NaN") + } +} + +macro_rules! make_operator_enum { + ( + enum $enum_name:ident { + $($Variant:ident = $name:literal,)* + } + ) => { + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] + enum $enum_name { + $($Variant,)* + } + + impl $enum_name { + const VARIANTS: &[Self] = &[$(Self::$Variant,)*]; + fn name(self) -> &'static str { + match self { + $(Self::$Variant => $name,)* + } + } + } + }; +} + +make_operator_enum! { + enum PsOperator { + Array = "array", + ArrayStart = "[", + ArrayEnd = "]", + Begin = "begin", + CurrentDict = "currentdict", + CurrentFile = "currentfile", + Def = "def", + Dict = "dict", + Dup = "dup", + FontDirectory = "FontDirectory", + For = "for", + EExec = "eexec", + End = "end", + Exch = "exch", + IfElse = "ifelse", + Index = "index", + Known = "known", + Put = "put", + ReadOnly = "readonly", + } +} + +impl PsOperator { + fn run(self, parser: &mut PsParser) -> Result<(), PsBreakReason> { + match self { + PsOperator::Array => { + let Some(len) = parser.operand_stack.pop().and_then(|v| v.to_int()) else { + return custom_err( + "postscript array operator is missing required integer operand", + ); + }; + let Ok(len) = len.try_into() else { + return custom_err("postscript array operator passed invalid length"); + }; + let array = PsArray::from_elements(parser, vec![PsObject::Null; len]); + parser.operand_stack.push(PsObject::Array(array)); + Ok(()) + } + PsOperator::ArrayStart => { + parser.operand_stack.push(PsObject::Mark); + Ok(()) + } + PsOperator::ArrayEnd => { + let mut elements = Vec::new(); + while let Some(object) = parser.operand_stack.pop() { + match object { + PsObject::Mark => { + elements.reverse(); + let array = PsArray::from_elements(parser, elements); + parser.operand_stack.push(PsObject::Array(array)); + return Ok(()); + } + _ => elements.push(object), + } + } + custom_err("postscript ] operator is missing required mark operand") + } + PsOperator::Begin => { + let Some(PsObject::Dictionary(dict)) = parser.operand_stack.pop() else { + return custom_err( + "postscript begin operator is missing required dictionary operand", + ); + }; + parser.dictionary_stack.push(dict); + Ok(()) + } + PsOperator::CurrentDict => { + let Some(dict) = parser.dictionary_stack.last().cloned() else { + unreachable!(); + }; + parser.operand_stack.push(PsObject::Dictionary(dict)); + Ok(()) + } + PsOperator::CurrentFile => { + parser + .operand_stack + .push(PsObject::File(parser.tokenizer.clone())); + Ok(()) + } + PsOperator::Def => { + let Some(value) = parser.operand_stack.pop() else { + return custom_err("postscript def operator is missing required operand"); + }; + let Some(key) = parser.operand_stack.pop() else { + return custom_err("postscript def operator is missing required operand"); + }; + let Some(dict) = parser.dictionary_stack.last_mut() else { + unreachable!(); + }; + dict.insert(key, value); + Ok(()) + } + PsOperator::Dict => { + let Some(_capacity) = parser.operand_stack.pop().and_then(|v| v.to_int()) else { + return custom_err( + "postscript dict operator is missing required integer operand", + ); + }; + let dict = PsDictionary::new(parser); + parser.operand_stack.push(PsObject::Dictionary(dict)); + Ok(()) + } + PsOperator::Dup => { + let Some(value) = parser.operand_stack.pop() else { + return custom_err("postscript dup operator is missing required operand"); + }; + parser.operand_stack.push(value.clone()); + parser.operand_stack.push(value); + Ok(()) + } + PsOperator::EExec => { + if parser.break_at_eexec { + return Err(PsBreakReason::FoundEExec); + } + let Some(source) = parser.operand_stack.pop() else { + return custom_err("postscript eexec operator is missing required operand"); + }; + let file = match source { + PsObject::String(string) => todo!(), + PsObject::File(file) => file, + _ => { + return custom_err("postscript eexec operator has invalid operand"); + } + }; + dbg!(&parser.dictionary_stack); + dbg!(&parser.operand_stack); + let file = file.decrypt_for_eexec(parser.next_file_id)?; + parser.next_file_id += 1; + struct PutBackTokenizerOnDrop<'a> { + parser: &'a mut PsParser, + old_tokenizer: PsFile, + } + impl Drop for PutBackTokenizerOnDrop<'_> { + fn drop(&mut self) { + self.parser.tokenizer = self.old_tokenizer.clone(); + } + } + let put_back_tokenizer_on_drop = PutBackTokenizerOnDrop { + old_tokenizer: std::mem::replace(&mut parser.tokenizer, file), + parser, + }; + put_back_tokenizer_on_drop.parser.parse_file() + } + PsOperator::End => { + if parser.dictionary_stack.len() <= PsParser::MIN_DICTIONARY_STACK_SIZE { + return custom_err("postscript end operator without corresponding begin"); + } + parser.dictionary_stack.pop(); + Ok(()) + } + PsOperator::Exch => { + let Some([a, b]) = parser.operand_stack.last_chunk_mut() else { + return custom_err("postscript exch operator is missing required operands"); + }; + std::mem::swap(a, b); + Ok(()) + } + PsOperator::FontDirectory => { + parser.operand_stack.push(PsObject::Dictionary( + parser.font_directory.clone().expect("set in PsParser::new"), + )); + Ok(()) + } + PsOperator::For => { + let Some(PsObject::Procedure(proc)) = parser.operand_stack.pop() else { + return custom_err( + "postscript for operator is missing required procedure operand", + ); + }; + let Some(limit) = parser.operand_stack.pop() else { + return custom_err("postscript for operator is missing required limit operand"); + }; + let Some(increment) = parser.operand_stack.pop() else { + return custom_err( + "postscript for operator is missing required increment operand", + ); + }; + let Some(initial) = parser.operand_stack.pop() else { + return custom_err( + "postscript for operator is missing required initial operand", + ); + }; + let PsObject::Integer(initial) = initial else { + todo!("{initial:?}"); + }; + let PsObject::Integer(increment @ (..=-1 | 1..)) = increment else { + todo!("{increment:?}"); + }; + let PsObject::Integer(limit) = limit else { + todo!("{limit:?} {:?}", parser.operand_stack); + }; + let mut counter = initial; + let proc = proc.into_vec(); + loop { + if increment < 0 { + if counter < limit { + break; + } + } else if counter > limit { + break; + } + parser.operand_stack.push(PsObject::Integer(counter)); + parser.run_procedure(&proc)?; + counter = counter.checked_add(increment).ok_or_else(|| { + PdfParseError::Custom("postscript arithmetic overflow".into()) + })?; + } + Ok(()) + } + PsOperator::IfElse => { + let Some(PsObject::Procedure(else_proc)) = parser.operand_stack.pop() else { + return custom_err( + "postscript ifelse operator is missing required procedure operand", + ); + }; + let Some(PsObject::Procedure(then_proc)) = parser.operand_stack.pop() else { + return custom_err( + "postscript ifelse operator is missing required procedure operand", + ); + }; + let Some(PsObject::Boolean(cond)) = parser.operand_stack.pop() else { + return custom_err( + "postscript ifelse operator is missing required bool operand", + ); + }; + if cond { + parser.run_procedure(&then_proc.into_vec())?; + } else { + parser.run_procedure(&else_proc.into_vec())?; + } + Ok(()) + } + PsOperator::Index => { + let Some(index) = parser.operand_stack.pop().and_then(|v| v.to_int()) else { + return custom_err( + "postscript index operator is missing required integer operand", + ); + }; + let Some(object) = index + .try_into() + .ok() + .and_then(|index| parser.operand_stack.iter().nth_back(index).cloned()) + else { + return custom_err("postscript index operator passed invalid integer"); + }; + parser.operand_stack.push(object); + Ok(()) + } + PsOperator::Known => { + let Some(key) = parser.operand_stack.pop() else { + return custom_err("postscript known operator is missing required key operand"); + }; + let Some(PsObject::Dictionary(dictionary)) = parser.operand_stack.pop() else { + return custom_err( + "postscript known operator is missing required dictionary operand", + ); + }; + parser + .operand_stack + .push(PsObject::Boolean(dictionary.get(key).is_some())); + Ok(()) + } + PsOperator::Put => { + let Some(value) = parser.operand_stack.pop() else { + return custom_err("postscript put operator is missing required value operand"); + }; + let Some(key_or_index) = parser.operand_stack.pop() else { + return custom_err( + "postscript put operator is missing required key/index operand", + ); + }; + let Some(container) = parser.operand_stack.pop() else { + return custom_err( + "postscript put operator is missing required container operand", + ); + }; + match container { + PsObject::Array(array) => { + let array = array.rc(); + let mut array = array.borrow_mut(); + let Some(target) = key_or_index + .to_int() + .and_then(|index| array.get_mut(usize::try_from(index).ok()?)) + else { + return custom_err("postscript put operator has invalid index operand"); + }; + *target = value; + Ok(()) + } + PsObject::Dictionary(mut dict) => { + dict.insert(key_or_index, value); + Ok(()) + } + PsObject::String(s) => todo!(), + _ => custom_err("postscript put operator was passed invalid container operand"), + } + } + PsOperator::ReadOnly => { + // TODO: implement permissions + Ok(()) + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] +struct PsDictionaryImpl { + named: BTreeMap, + other: BTreeMap, +} + +#[derive(Clone)] +struct PsDictionary { + id: usize, + weak: std::rc::Weak>, +} + +impl fmt::Debug for PsDictionary { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { id, weak } = self; + if let Some(weak) = weak.upgrade() { + if let Ok(weak) = weak.try_borrow() { + write!(f, "#{id} ")?; + let PsDictionaryImpl { named, other } = &*weak; + return f.debug_map().entries(named).entries(other).finish(); + } + } + f.debug_struct("PsDictionary") + .field("id", id) + .field("weak", &weak) + .finish() + } +} + +impl Ord for PsDictionary { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + +impl PartialOrd for PsDictionary { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Eq for PsDictionary {} + +impl PartialEq for PsDictionary { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl PsDictionary { + fn new(parser: &mut PsParser) -> Self { + Self::from_impl(parser, PsDictionaryImpl::default()) + } + fn rc(&self) -> Rc> { + self.weak.upgrade().expect("still in parser scope") + } + fn from_impl(parser: &mut PsParser, impl_: PsDictionaryImpl) -> Self { + let dict = Rc::new(RefCell::new(impl_)); + let weak = Rc::downgrade(&dict); + let id = parser.dictionaries.len(); + parser.dictionaries.push(dict); + Self { id, weak } + } + fn from_name_value_pairs<'a>( + parser: &mut PsParser, + iter: impl IntoIterator, + ) -> Self { + Self::from_impl( + parser, + PsDictionaryImpl { + named: BTreeMap::from_iter( + iter.into_iter() + .map(|(k, v)| (PsName(k.as_bytes().into()), v)), + ), + other: BTreeMap::new(), + }, + ) + } + fn get_named(&self, key: &PsName) -> Option { + self.rc().borrow().named.get(key).cloned() + } + fn insert(&mut self, key: PsObject, value: PsObject) -> Option { + let this = self.rc(); + let mut this = this.borrow_mut(); + let PsDictionaryImpl { named, other } = &mut *this; + match key { + PsObject::String(s) => named.insert(PsName(s.0.borrow().clone()), value), + PsObject::Name(name) => named.insert(name, value), + _ => other.insert(key, value), + } + } + fn insert_named(&mut self, name: PsName, value: PsObject) -> Option { + self.rc().borrow_mut().named.insert(name, value) + } + fn into_impl(self) -> PsDictionaryImpl { + self.rc().borrow().clone() + } + fn get(&self, key: PsObject) -> Option { + let this = self.rc(); + let this = this.borrow(); + let PsDictionaryImpl { named, other } = &*this; + match key { + PsObject::String(s) => named.get(&PsName(s.0.borrow().clone())).cloned(), + PsObject::Name(name) => named.get(&name).cloned(), + _ => other.get(&key).cloned(), + } + } +} + +#[derive(Clone)] +struct PsArray { + id: usize, + weak: std::rc::Weak>>, +} + +impl fmt::Debug for PsArray { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { id, weak } = self; + if let Some(weak) = weak.upgrade() { + if let Ok(weak) = weak.try_borrow() { + write!(f, "#{id} ")?; + return Vec::fmt(&weak, f); + } + } + f.debug_struct("PsArray") + .field("id", id) + .field("weak", &weak) + .finish() + } +} + +impl Ord for PsArray { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + +impl PartialOrd for PsArray { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Eq for PsArray {} + +impl PartialEq for PsArray { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl PsArray { + fn new(parser: &mut PsParser) -> Self { + Self::from_elements(parser, Vec::new()) + } + fn rc(&self) -> Rc>> { + self.weak.upgrade().expect("still in parser scope") + } + fn from_elements(parser: &mut PsParser, elements: Vec) -> Self { + let array = Rc::new(RefCell::new(elements)); + let weak = Rc::downgrade(&array); + let id = parser.arrays.len(); + parser.arrays.push(array); + Self { id, weak } + } + fn into_vec(self) -> Vec { + self.rc().borrow().clone() + } +} + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] +struct PsName(Vec); + +impl fmt::Debug for PsName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PsName({})", self.0.escape_ascii()) + } +} + +impl From for PdfName { + fn from(value: PsName) -> Self { + PdfName::new(PdfInputPosition::empty(), ArcOrRef::Arc(value.0.into())) + } +} + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] +struct PsString(Rc>>); + +impl fmt::Debug for PsString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PsString({})", self.0.borrow().escape_ascii()) + } +} + +impl From for PdfString { + fn from(value: PsString) -> Self { + PdfString::new( + PdfInputPosition::empty(), + ArcOrRef::Arc(value.0.borrow().as_slice().into()), + ) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +enum PsObject { + Array(PsArray), + Procedure(PsArray), + Dictionary(PsDictionary), + Integer(i128), + Mark, + Name(PsName), + Operator(PsOperator), + Real(PsReal), + String(PsString), + Boolean(bool), + Null, + ExecutableName(PsName), + File(PsFile), +} + +impl PsObject { + fn to_int(&self) -> Option { + match self { + PsObject::Integer(v) => Some(*v), + PsObject::Real(PsReal(v)) => Some(*v as i128), + _ => None, + } + } + fn to_f32(&self) -> Option { + match self { + PsObject::Integer(v) => Some(*v as f32), + PsObject::Real(PsReal(v)) => Some(*v as f32), + _ => None, + } + } +} + +struct PsParser { + tokenizer: PsFile, + operand_stack: Vec, + dictionary_stack: Vec, + dictionaries: Vec>>, + arrays: Vec>>>, + next_file_id: u64, + break_at_eexec: bool, + font_directory: Option, +} + +impl PsParser { + const MIN_DICTIONARY_STACK_SIZE: usize = 3; + fn new(tokenizer: PsFile) -> Self { + let mut retval = Self { + next_file_id: tokenizer.id + 1, + tokenizer, + operand_stack: Vec::with_capacity(16), + dictionary_stack: Vec::with_capacity(8), + dictionaries: Vec::with_capacity(16), + arrays: Vec::with_capacity(16), + break_at_eexec: false, + font_directory: None, + }; + let mut system_dict = PsDictionary::from_name_value_pairs( + &mut retval, + PsOperator::VARIANTS + .iter() + .map(|&op| (op.name(), PsObject::Operator(op))), + ); + system_dict.insert_named(PsName(b"false".into()), PsObject::Boolean(false)); + system_dict.insert_named(PsName(b"true".into()), PsObject::Boolean(true)); + retval.dictionary_stack.push(system_dict); + let dict = PsDictionary::new(&mut retval); + retval.dictionary_stack.push(dict); + let dict = PsDictionary::new(&mut retval); + retval.dictionary_stack.push(dict); + retval.font_directory = Some(PsDictionary::new(&mut retval)); + retval + } + + fn run_name(&mut self, name: &PsName) -> Result<(), PsBreakReason> { + let Some(value) = self + .dictionary_stack + .iter() + .rev() + .find_map(|dict| dict.get_named(name)) + else { + todo!("unimplemented PS operator {name:?}"); + }; + match value { + PsObject::Integer(_) | PsObject::Real(_) | PsObject::Boolean(_) => { + self.operand_stack.push(value); + Ok(()) + } + PsObject::Name(v) => todo!(), + PsObject::ExecutableName(v) => todo!(), + PsObject::Null => todo!(), + PsObject::String(v) => todo!(), + PsObject::Array(v) => todo!(), + PsObject::Dictionary(v) => todo!(), + PsObject::Operator(value) => value.run(self), + PsObject::Mark => todo!(), + PsObject::Procedure(v) => todo!(), + PsObject::File(v) => todo!(), + } + } + + fn parse_procedure(&mut self) -> Result { + self.tokenizer.skip_comments_and_whitespace(); + let mut objects = Vec::new(); + while let Some(token) = self.tokenizer.next_token()? { + objects.push(match token { + Token::Integer(v) => PsObject::Integer(v), + Token::Real(v) => PsObject::Real(PsReal(v)), + Token::ArrayStart => PsObject::ExecutableName(PsName(b"[".into())), + Token::ArrayEnd => PsObject::ExecutableName(PsName(b"]".into())), + Token::ProcedureStart => PsObject::Procedure(self.parse_procedure()?), + Token::ProcedureEnd => return Ok(PsArray::from_elements(self, objects)), + Token::ExecutableName(name) => PsObject::ExecutableName(PsName(name.into())), + Token::LiteralName(name) => PsObject::Name(PsName(name.into())), + Token::ImmediatelyEvaluatedName(_) => todo!("{token:?}"), + Token::String(v) => PsObject::String(PsString(Rc::new(RefCell::new(v)))), + }); + } + custom_err("postscript missing closing }") + } + fn parse_file(&mut self) -> Result<(), PsBreakReason> { + self.tokenizer.skip_comments_and_whitespace(); + while let Some(token) = self.tokenizer.next_token()? { + match token { + Token::Integer(v) => self.operand_stack.push(PsObject::Integer(v)), + Token::Real(v) => self.operand_stack.push(PsObject::Real(PsReal(v))), + Token::ArrayStart => self.run_name(&PsName(b"[".into()))?, + Token::ArrayEnd => self.run_name(&PsName(b"]".into()))?, + Token::ProcedureStart => { + let procedure = self.parse_procedure()?; + self.operand_stack.push(PsObject::Procedure(procedure)) + } + Token::ProcedureEnd => todo!(), + Token::ExecutableName(name) => { + let name = PsName(name.into()); + self.run_name(&name)? + } + Token::LiteralName(name) => { + self.operand_stack.push(PsObject::Name(PsName(name.into()))) + } + Token::ImmediatelyEvaluatedName(_) => todo!("{token:?}"), + Token::String(v) => self + .operand_stack + .push(PsObject::String(PsString(Rc::new(RefCell::new(v))))), + } + } + Ok(()) + } + fn parse_font_encoding( + &mut self, + value: PsArray, + ) -> Result]>, PdfParseError> { + let value = value.rc(); + let value = value.borrow(); + let mut vec = Vec::with_capacity(value.len()); + for entry in value.iter() { + match entry { + PsObject::Name(name) => { + if name.0 == b".notdef" { + vec.push(None); + } else { + vec.push(Some(PdfName::new( + self.tokenizer.pos(), + Arc::from(&*name.0), + ))); + } + } + _ => todo!("{entry:?}"), + } + } + Ok(Arc::from(vec)) + } + fn parse_font_bbox(&mut self, value: PsArray) -> Result { + let value = value.rc(); + let value = value.borrow(); + let mut vec = Vec::new(); + for entry in value.iter() { + let Some(v) = entry.to_f32() else { + return custom_err("postscript invalid FontBBox entry"); + }; + vec.push(v); + } + match <[f32; 4]>::try_from(vec) { + Ok([x1, y1, x2, y2]) => Ok(PdfRectangle::new( + PdfVec2D { + pos: PdfInputPositionNoCompare::empty(), + x: x1, + y: y1, + }, + PdfVec2D { + pos: PdfInputPositionNoCompare::empty(), + x: x2, + y: y2, + }, + )), + Err(_) => custom_err("postscript invalid FontBBox entry"), + } + } + fn parse_font_matrix(&mut self, value: PsArray) -> Result { + let value = value.rc(); + let value = value.borrow(); + let mut vec = Vec::new(); + for entry in value.iter() { + let Some(v) = entry.to_f32() else { + return custom_err("postscript invalid FontBBox entry"); + }; + vec.push(v); + } + match vec.try_into() { + Ok(elements) => Ok(PdfMatrix { + pos: PdfInputPositionNoCompare::empty(), + elements, + }), + Err(_) => custom_err("postscript invalid FontBBox entry"), + } + } + fn parse_font_info_dict( + &mut self, + font_info_dict: PsDictionary, + ) -> Result { + let PsDictionaryImpl { named, other: _ } = font_info_dict.into_impl(); + let mut family_name = None; + let mut full_name = None; + let mut notice = None; + let mut weight = None; + let mut version = None; + let mut italic_angle = None; + let mut is_fixed_pitch = None; + let mut underline_position = None; + let mut underline_thickness = None; + for (key, value) in named { + match (&*key.0, value) { + (b"FamilyName", PsObject::String(string)) => family_name = Some(string.into()), + (b"FullName", PsObject::String(string)) => full_name = Some(string.into()), + (b"Notice", PsObject::String(string)) => notice = Some(string.into()), + (b"Weight", PsObject::String(string)) => weight = Some(string.into()), + (b"version", PsObject::String(string)) => version = Some(string.into()), + (b"ItalicAngle", value) => { + if let Some(value) = value.to_f32() { + italic_angle = Some(value); + } else { + todo!("{value:?}") + } + } + (b"isFixedPitch", PsObject::Boolean(v)) => is_fixed_pitch = Some(v), + (b"UnderlinePosition", value) => { + if let Some(value) = value.to_f32() { + underline_position = Some(value); + } else { + todo!("{value:?}") + } + } + (b"UnderlineThickness", value) => { + if let Some(value) = value.to_f32() { + underline_thickness = Some(value); + } else { + todo!("{value:?}") + } + } + _ => {} + } + } + Ok(PdfFontType1FontInfo { + family_name, + full_name, + notice, + weight, + version, + italic_angle, + is_fixed_pitch, + underline_position, + underline_thickness, + }) + } + fn parse_font_dict( + &mut self, + dict: PsDictionary, + ) -> Result { + let PsDictionaryImpl { named, other } = dict.into_impl(); + let mut encoding = None; + let mut font_bbox = None; + let mut font_info = None; + let mut font_matrix = None; + let mut font_name = None; + for (key, value) in named { + match (&*key.0, value) { + (b"Encoding", PsObject::Array(value)) => { + encoding = Some(self.parse_font_encoding(value)?); + } + (b"FontBBox", PsObject::Array(value) | PsObject::Procedure(value)) => { + font_bbox = Some(self.parse_font_bbox(value)?); + } + (b"FontInfo", PsObject::Dictionary(value)) => { + font_info = Some(self.parse_font_info_dict(value)?); + } + (b"FontMatrix", PsObject::Array(value) | PsObject::Procedure(value)) => { + font_matrix = Some(self.parse_font_matrix(value)?); + } + (b"FontName", PsObject::Name(value)) => { + font_name = Some(value.into()); + } + (b"FontType", _) => { + // TODO + } + (b"PaintType", _) => { + // TODO + } + (_, value) => todo!("{key:?}: {value:?}"), + } + } + for (key, value) in other { + todo!("{key:?}: {value:?}"); + } + Ok(PdfFontType1Program { + encoding, + font_bbox, + font_info, + font_matrix, + font_name, + }) + } + fn parse(mut self) -> Result { + self.break_at_eexec = true; + match self.parse_file() { + Ok(()) => return custom_err("postscript eexec operator not found"), + Err(PsBreakReason::FoundEExec) => {} + Err(PsBreakReason::Error(e)) => return Err(e), + } + let Some(PsObject::File(_)) = self.operand_stack.pop() else { + return custom_err("postscript eexec operand not found"); + }; + let Some(PsObject::Dictionary(dict)) = self.operand_stack.pop() else { + todo!(); + }; + self.parse_font_dict(dict) + } + fn run_procedure(&mut self, proc: &[PsObject]) -> Result<(), PsBreakReason> { + for object in proc { + match object { + PsObject::Array(v) => todo!(), + PsObject::Procedure(v) => todo!(), + PsObject::Dictionary(v) => todo!(), + PsObject::Integer(_) => self.operand_stack.push(object.clone()), + PsObject::Mark => todo!(), + PsObject::Name(_) => self.operand_stack.push(object.clone()), + PsObject::ExecutableName(name) => self.run_name(name)?, + PsObject::Operator(v) => todo!(), + PsObject::Real(v) => todo!(), + PsObject::String(v) => todo!(), + PsObject::Boolean(v) => todo!(), + PsObject::Null => todo!(), + PsObject::File(v) => todo!(), + } + } + Ok(()) + } +} + +impl PdfStreamContents for PdfFontType1Program { + fn parse( + data: &[u8], + stream_pos: PdfInputPosition, + _objects: Arc, + ) -> Result { + PsParser::new(PsFile::new( + 0, + PsFileSource::Bytes(Rc::from(data)), + 0, + stream_pos, + )) + .parse() + } +} diff --git a/src/pdf/object.rs b/src/pdf/object.rs index 0931286..bf91fac 100644 --- a/src/pdf/object.rs +++ b/src/pdf/object.rs @@ -482,6 +482,81 @@ impl PdfParse for PdfStringOrNumber { } } +#[derive(Clone)] +pub enum PdfNameOrInteger { + Name(PdfName), + Integer(PdfInteger), +} + +impl fmt::Debug for PdfNameOrInteger { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Name(v) => v.fmt(f), + Self::Integer(v) => v.fmt(f), + } + } +} + +impl PdfNameOrInteger { + pub fn pos(self) -> PdfInputPosition { + match self { + Self::Name(v) => v.pos(), + Self::Integer(v) => v.pos(), + } + } +} + +impl PdfObjectDirect { + pub fn name_or_integer(&self) -> Option { + match *self { + PdfObjectDirect::Name(ref v) => Some(PdfNameOrInteger::Name(v.clone())), + PdfObjectDirect::Integer(v) => Some(PdfNameOrInteger::Integer(v)), + PdfObjectDirect::Boolean(_) + | PdfObjectDirect::Real(_) + | PdfObjectDirect::String(_) + | PdfObjectDirect::Array(_) + | PdfObjectDirect::Dictionary(_) + | PdfObjectDirect::Stream(_) + | PdfObjectDirect::Null(_) => None, + } + } +} + +impl PdfObjectNonNull { + pub fn name_or_integer(&self) -> Option { + match *self { + PdfObjectNonNull::Name(ref v) => Some(PdfNameOrInteger::Name(v.clone())), + PdfObjectNonNull::Integer(v) => Some(PdfNameOrInteger::Integer(v)), + PdfObjectNonNull::Boolean(_) + | PdfObjectNonNull::Real(_) + | PdfObjectNonNull::String(_) + | PdfObjectNonNull::Array(_) + | PdfObjectNonNull::Dictionary(_) + | PdfObjectNonNull::Stream(_) => None, + } + } +} + +impl IsPdfNull for PdfNameOrInteger { + fn is_pdf_null(&self) -> bool { + false + } +} + +impl PdfParse for PdfNameOrInteger { + fn type_name() -> Cow<'static, str> { + Cow::Borrowed("name or integer") + } + fn parse(object: PdfObject) -> Result { + let object = PdfObjectDirect::from(object); + object.name_or_integer().ok_or(PdfParseError::InvalidType { + pos: object.pos(), + ty: object.type_name(), + expected_ty: "name or integer", + }) + } +} + macro_rules! make_pdf_object { ( $( diff --git a/src/pdf/parse.rs b/src/pdf/parse.rs index 4ec885e..1d57f5e 100644 --- a/src/pdf/parse.rs +++ b/src/pdf/parse.rs @@ -1302,7 +1302,7 @@ macro_rules! pdf_parse { $crate::__std::result::Result::Err($crate::pdf::parse::PdfParseError::InvalidName { pos: name.pos(), name, - expected_ty: $crate::__std::stringify!($Struct), + expected_ty: $crate::__std::stringify!($Enum), }) } } diff --git a/src/pdf/render.rs b/src/pdf/render.rs index 4fb56eb..8ffbcac 100644 --- a/src/pdf/render.rs +++ b/src/pdf/render.rs @@ -40,10 +40,7 @@ use crate::{ IsPdfNull, PdfMatrix, PdfName, PdfNumber, PdfObject, PdfObjectDirect, PdfStringOrNumber, PdfVec2D, }, - parse::{ - GetPdfInputPosition, PdfInputPosition, PdfInputPositionNoCompare, PdfParse, - PdfParseError, - }, + parse::{PdfInputPosition, PdfInputPositionNoCompare, PdfParse, PdfParseError}, }, pdf_parse, }; @@ -929,8 +926,21 @@ impl PdfRenderOperator for PdfOperatorShowTextWithGlyphPositioning { PdfStringOrNumber::String(s) => { for glyph in s.bytes().iter() { let positioning = std::mem::replace(&mut positioning, 0.0); - let encoding = font.encoding(); - todo!("{encoding:?}"); + let Some(encoding) = font.encoding() else { + todo!(); + }; + let table = encoding.table(|| { + let Some(font_encoding) = font + .font_descriptor() + .and_then(|v| v.font_file.as_ref()) + .and_then(|v| v.decoded_data().as_ref().ok()) + .and_then(|v| v.encoding.as_ref()) + else { + todo!() + }; + todo!("{font_encoding:?}"); + }); + todo!("{table:?}"); } } PdfStringOrNumber::Number(number) => positioning = number.as_f32(), diff --git a/src/util.rs b/src/util.rs index 1a4440c..9d576f8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -104,6 +104,65 @@ impl fmt::Display for ArcOrRef<'_, T> { } } +/// a stable alternative to `CloneToUninit` for `Arc` +pub trait ArcFromRef { + /// like `Arc::new(Self::clone(self))` but works for unsized types too + fn arc_from_ref(&self) -> Arc; + /// generic version of `Arc::make_mut` + fn make_mut(this: &mut Arc) -> &mut Self; +} + +impl ArcFromRef for T { + fn arc_from_ref(&self) -> Arc { + Arc::new(Self::clone(self)) + } + fn make_mut(this: &mut Arc) -> &mut Self { + Arc::make_mut(this) + } +} + +impl ArcFromRef for [T] { + fn arc_from_ref(&self) -> Arc { + Arc::from(self) + } + fn make_mut(this: &mut Arc) -> &mut Self { + Arc::make_mut(this) + } +} + +impl ArcFromRef for str { + fn arc_from_ref(&self) -> Arc { + Arc::from(self) + } + fn make_mut(this: &mut Arc) -> &mut Self { + Arc::make_mut(this) + } +} + +impl<'a, T: ?Sized + ArcFromRef> ArcOrRef<'a, T> { + pub fn into_arc(this: Self) -> Arc { + match this { + ArcOrRef::Arc(v) => v, + ArcOrRef::Ref(v) => T::arc_from_ref(v), + } + } + pub fn make_arc(this: &mut Self) -> &mut Arc { + match this { + ArcOrRef::Arc(v) => v, + ArcOrRef::Ref(v) => { + *this = ArcOrRef::Arc(T::arc_from_ref(v)); + let ArcOrRef::Arc(v) = this else { + unreachable!(); + }; + v + } + } + } + pub fn make_mut(this: &mut Self) -> &mut T { + T::make_mut(Self::make_arc(this)) + } +} + trait DagDebugStateSealed {} #[expect(private_bounds)]