diff --git a/crates/pdf-cmap/src/to_unicode.rs b/crates/pdf-cmap/src/to_unicode.rs index e069476..cbca2b2 100644 --- a/crates/pdf-cmap/src/to_unicode.rs +++ b/crates/pdf-cmap/src/to_unicode.rs @@ -10,6 +10,15 @@ pub struct ToUnicodeCMap(HashMap>); impl ToUnicodeCMap { /// Parse the optional `/ToUnicode` CMap from a font dictionary. + /// + /// # Paramaters + /// + /// - `dictionary`: The PDF font dictionary that may contain `/ToUnicode`. + /// - `objects`: The resolver used to dereference indirect PDF objects. + /// + /// # Returns + /// + /// The parsed ToUnicode CMap when a readable `/ToUnicode` stream is present. pub fn from_dictionary( dictionary: &Dictionary, objects: &dyn ObjectResolver, diff --git a/crates/pdf-font/src/fallback.rs b/crates/pdf-font/src/fallback.rs index 1651471..fa47cfd 100644 --- a/crates/pdf-font/src/fallback.rs +++ b/crates/pdf-font/src/fallback.rs @@ -14,6 +14,8 @@ pub(crate) struct FallbackFontProgram { } impl FallbackFontProgram { + const NOTO_SANS_CJK_JP_REGULAR: &[u8] = include_bytes!("../assets/NotoSansCJKjp-Regular.otf"); + /// Select fallback font bytes and metadata for a font dictionary. pub(crate) fn from_dictionary( dictionary: &Dictionary, @@ -22,8 +24,17 @@ impl FallbackFontProgram { let flags = FontFlags::from_dictionary(dictionary, objects)?; let standard14 = Standard14Font::from_dictionary(dictionary, objects, flags); let is_cjk = is_cjk_cid_font(dictionary, objects)?; + let font_file = if is_cjk { + Self::NOTO_SANS_CJK_JP_REGULAR + } else { + standard14.fallback_font_bytes() + }; - Ok(fallback_program(flags, standard14, is_cjk)) + Ok(Self { + font_file, + standard14, + flags, + }) } } @@ -47,7 +58,7 @@ pub(crate) fn fallback_true_type_from_dictionary( let encoding = Encoding::from_dictionary(dictionary, objects) .ok() .flatten(); - let to_unicode = to_unicode_cmap(dictionary, objects)?; + let to_unicode = ToUnicodeCMap::from_dictionary(dictionary, objects)?; Ok(TrueTypeFont { font_file: fallback.font_file.into(), @@ -71,56 +82,20 @@ pub(crate) fn fallback_true_type_from_dictionary_best_effort( let flags = FontFlags::from_dictionary(dictionary, objects).unwrap_or_default(); let is_cjk = is_cjk_cid_font(dictionary, objects).unwrap_or(false); let standard14 = Standard14Font::from_dictionary(dictionary, objects, flags); - let fallback = fallback_program(flags, standard14, is_cjk); - let mut font = TrueTypeFont::from_bytes(fallback.font_file, Some(fallback.standard14)); - font.flags = fallback.flags; - - font -} - -/// Build the fallback font program descriptor from already-decided inputs. -/// -/// `standard14` selects the Standard 14 identity for simple-font fallback, -/// while `is_cjk` switches the program bytes to the bundled CJK fallback for -/// CID fonts that declare a supported CJK ordering. -fn fallback_program( - flags: FontFlags, - standard14: Standard14Font, - is_cjk: bool, -) -> FallbackFontProgram { let font_file = if is_cjk { - include_bytes!("../assets/NotoSansCJKjp-Regular.otf").as_slice() + FallbackFontProgram::NOTO_SANS_CJK_JP_REGULAR } else { standard14.fallback_font_bytes() }; - - FallbackFontProgram { + let fallback = FallbackFontProgram { font_file, standard14, flags, - } -} + }; + let mut font = TrueTypeFont::from_bytes(fallback.font_file, Some(fallback.standard14)); + font.flags = fallback.flags; -/// Parse an optional ToUnicode CMap from a font dictionary. -/// -/// # Paramaters -/// -/// - `dictionary`: The PDF font dictionary that may contain `/ToUnicode`. -/// - `objects`: The resolver used to dereference indirect PDF objects. -/// -/// # Returns -/// -/// The parsed ToUnicode CMap when a readable `/ToUnicode` stream is present. -fn to_unicode_cmap( - dictionary: &Dictionary, - objects: &dyn ObjectResolver, -) -> Result, FontError> { - dictionary - .get("ToUnicode") - .and_then(|e| e.try_stream(objects).ok()) - .map(|s| ToUnicodeCMap::try_from(s.raw_data())) - .transpose() - .map_err(FontError::from) + font } /// Detect whether a CID font dictionary uses a known CJK CID ordering. diff --git a/crates/pdf-font/src/glyph_widths_map.rs b/crates/pdf-font/src/glyph_widths_map.rs index 6f88321..05e903a 100644 --- a/crates/pdf-font/src/glyph_widths_map.rs +++ b/crates/pdf-font/src/glyph_widths_map.rs @@ -1,9 +1,12 @@ use pdf_object::{ - error::ObjectError, object_resolver::ObjectResolver, object_variant::ObjectVariant, + dictionary::Dictionary, error::ObjectError, object_resolver::ObjectResolver, + object_variant::ObjectVariant, }; use std::collections::BTreeMap; use thiserror::Error; +use crate::error::FontError; + /// Errors that can occur during GlyphWidthsMap parsing from a /W array. #[derive(Debug, Error, Clone, PartialEq)] pub enum GlyphWidthsMapError { @@ -58,6 +61,20 @@ pub struct GlyphWidthsMap { } impl GlyphWidthsMap { + /// Parse the optional `/W` width map from a descendant CIDFont dictionary. + pub fn from_dictionary( + dictionary: &Dictionary, + objects: &dyn ObjectResolver, + ) -> Result, FontError> { + dictionary + .get("W") + .map(|value| { + let widths = value.try_array(objects)?; + Self::from_array(widths, objects).map_err(FontError::from) + }) + .transpose() + } + /// Parses a PDF /W array into a `GlyphWidthsMap`. /// /// The /W array can contain entries of the form: diff --git a/crates/pdf-font/src/true_type_font.rs b/crates/pdf-font/src/true_type_font.rs index 5d7fe9d..55c820b 100644 --- a/crates/pdf-font/src/true_type_font.rs +++ b/crates/pdf-font/src/true_type_font.rs @@ -74,12 +74,7 @@ impl TrueTypeFont { .flatten() .or_else(|| Self::default_simple_encoding(program.flags, program.standard14)); - // Parse optional ToUnicode CMap stream. - let to_unicode = dictionary - .get("ToUnicode") - .and_then(|e| e.try_stream(objects).ok()) - .map(|s| ToUnicodeCMap::try_from(s.raw_data())) - .transpose()?; + let to_unicode = ToUnicodeCMap::from_dictionary(dictionary, objects)?; Ok(Self { font_file: program.font_file, diff --git a/crates/pdf-font/src/type0_font.rs b/crates/pdf-font/src/type0_font.rs index a4838d5..2408850 100644 --- a/crates/pdf-font/src/type0_font.rs +++ b/crates/pdf-font/src/type0_font.rs @@ -170,7 +170,7 @@ impl<'a> Type0DescendantFont<'a> { dictionary, subtype: CidFontSubType::from_dictionary(dictionary, objects)?, default_width, - widths: widths_map(dictionary, objects)?, + widths: GlyphWidthsMap::from_dictionary(dictionary, objects)?, }) } } @@ -210,29 +210,6 @@ fn descendant_font_dictionary<'a>( .map_err(FontError::from) } -/// Parse explicit CID width overrides from a descendant font dictionary. -/// -/// # Paramaters -/// -/// - `dictionary`: The descendant CIDFont dictionary. -/// - `objects`: The resolver used to dereference indirect PDF objects. -/// -/// # Returns -/// -/// The parsed `/W` width map when the dictionary contains width overrides. -fn widths_map( - dictionary: &Dictionary, - objects: &dyn ObjectResolver, -) -> Result, FontError> { - dictionary - .get("W") - .map(|obj| { - let widths = obj.try_array(objects)?; - GlyphWidthsMap::from_array(widths, objects).map_err(FontError::from) - }) - .transpose() -} - /// Read or synthesize the font program for a Type0 descendant font. /// /// # Paramaters diff --git a/crates/pdf-font/src/type1_font.rs b/crates/pdf-font/src/type1_font.rs index 364778f..658fb66 100644 --- a/crates/pdf-font/src/type1_font.rs +++ b/crates/pdf-font/src/type1_font.rs @@ -46,12 +46,7 @@ impl Type1Font { let encoding = Encoding::from_dictionary(dictionary, objects)?.unwrap_or_default(); - // Parse optional ToUnicode CMap stream. - let to_unicode = dictionary - .get("ToUnicode") - .and_then(|e| e.try_stream(objects).ok()) - .map(|s| ToUnicodeCMap::try_from(s.raw_data())) - .transpose()?; + let to_unicode = ToUnicodeCMap::from_dictionary(dictionary, objects)?; Ok(Self { font_file, diff --git a/crates/pdf-font/src/type3_font.rs b/crates/pdf-font/src/type3_font.rs index 82f082c..90aa7f1 100644 --- a/crates/pdf-font/src/type3_font.rs +++ b/crates/pdf-font/src/type3_font.rs @@ -58,12 +58,7 @@ impl Type3Font { char_procs.insert(name.to_owned(), content_stream); } - // Parse optional ToUnicode CMap stream. - let to_unicode = dictionary - .get("ToUnicode") - .and_then(|e| e.try_stream(objects).ok()) - .map(|s| ToUnicodeCMap::try_from(s.raw_data())) - .transpose()?; + let to_unicode = ToUnicodeCMap::from_dictionary(dictionary, objects)?; Ok(Type3Font { font_matrix,