Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions crates/pdf-cmap/src/to_unicode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ pub struct ToUnicodeCMap(HashMap<u16, Vec<char>>);

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,
Expand Down
63 changes: 19 additions & 44 deletions crates/pdf-font/src/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
})
}
}

Expand All @@ -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(),
Expand All @@ -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<Option<ToUnicodeCMap>, 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.
Expand Down
19 changes: 18 additions & 1 deletion crates/pdf-font/src/glyph_widths_map.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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<Option<Self>, 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:
Expand Down
7 changes: 1 addition & 6 deletions crates/pdf-font/src/true_type_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
25 changes: 1 addition & 24 deletions crates/pdf-font/src/type0_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?,
})
}
}
Expand Down Expand Up @@ -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<Option<GlyphWidthsMap>, 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
Expand Down
7 changes: 1 addition & 6 deletions crates/pdf-font/src/type1_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 1 addition & 6 deletions crates/pdf-font/src/type3_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading