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
22 changes: 20 additions & 2 deletions crates/pdf-font/src/encoding.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::{borrow::Cow, convert::TryFrom};

use pdf_object::{object_resolver::ObjectResolver, object_variant::ObjectVariant};
use pdf_object::{
dictionary::Dictionary, object_resolver::ObjectResolver, object_variant::ObjectVariant,
};

use crate::error::FontError;

Expand Down Expand Up @@ -127,8 +129,24 @@ impl Encoding {
}

impl Encoding {
/// Parse the optional `/Encoding` entry from a font dictionary.
pub fn from_dictionary(
dictionary: &pdf_object::dictionary::Dictionary,
dictionary: &Dictionary,
objects: &dyn ObjectResolver,
) -> Result<Option<Self>, FontError> {
dictionary
.get("Encoding")
.map(|value| match objects.resolve_object(value)? {
ObjectVariant::Dictionary(encoding_dictionary) => {
Self::from_encoding_dictionary(encoding_dictionary, objects)
}
other => Self::from_base_encoding(FontEncoding::from(other.try_str(objects)?)),
})
.transpose()
}

fn from_encoding_dictionary(
dictionary: &Dictionary,
objects: &dyn ObjectResolver,
) -> Result<Self, FontError> {
let mut encoding = match dictionary.get("BaseEncoding") {
Expand Down
70 changes: 8 additions & 62 deletions crates/pdf-font/src/fallback.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
use pdf_cmap::ToUnicodeCMap;
use pdf_object::{
dictionary::Dictionary, object_lookup::ObjectLookupExt, object_resolver::ObjectResolver,
object_variant::ObjectVariant,
};
use pdf_object::{dictionary::Dictionary, object_resolver::ObjectResolver};

use crate::{
cid_system_info::CidOrdering,
encoding::{Encoding, FontEncoding},
error::FontError,
flags::FontFlags,
simple_font_glyph_map::SimpleFontGlyphWidthsMap,
standard14::Standard14Font,
cid_system_info::CidOrdering, encoding::Encoding, error::FontError, flags::FontFlags,
simple_font_glyph_map::SimpleFontGlyphWidthsMap, standard14::Standard14Font,
true_type_font::TrueTypeFont,
};

Expand All @@ -26,7 +19,7 @@ impl FallbackFontProgram {
dictionary: &Dictionary,
objects: &dyn ObjectResolver,
) -> Result<Self, FontError> {
let flags = descriptor_flags(dictionary, objects)?;
let flags = FontFlags::from_dictionary(dictionary, objects)?;
let standard14 = Standard14Font::from_dictionary(dictionary, objects, flags);
let is_cjk = is_cjk_cid_font(dictionary, objects)?;

Expand All @@ -51,7 +44,9 @@ pub(crate) fn fallback_true_type_from_dictionary(
) -> Result<TrueTypeFont, FontError> {
let fallback = FallbackFontProgram::from_dictionary(dictionary, objects)?;
let widths = SimpleFontGlyphWidthsMap::from_dictionary(dictionary, objects)?;
let encoding = simple_font_encoding(dictionary, objects);
let encoding = Encoding::from_dictionary(dictionary, objects)
.ok()
.flatten();
let to_unicode = to_unicode_cmap(dictionary, objects)?;

Ok(TrueTypeFont {
Expand All @@ -73,7 +68,7 @@ pub(crate) fn fallback_true_type_from_dictionary_best_effort(
dictionary: &Dictionary,
objects: &dyn ObjectResolver,
) -> TrueTypeFont {
let flags = descriptor_flags(dictionary, objects).unwrap_or_default();
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);
Expand Down Expand Up @@ -106,55 +101,6 @@ fn fallback_program(
}
}

/// Read font descriptor flags from a font dictionary.
///
/// # Paramaters
///
/// - `dictionary`: The PDF font dictionary that may contain `/FontDescriptor`.
/// - `objects`: The resolver used to dereference indirect PDF objects.
///
/// # Returns
///
/// The parsed descriptor flags, or empty flags when the descriptor or `/Flags`
/// entry is absent.
pub(crate) fn descriptor_flags(
dictionary: &Dictionary,
objects: &dyn ObjectResolver,
) -> Result<FontFlags, FontError> {
let Some(descriptor) = dictionary.optional_dictionary("FontDescriptor", objects)? else {
return Ok(FontFlags::empty());
};

Ok(descriptor
.get("Flags")
.and_then(|obj| obj.try_number::<u32>(objects).ok())
.map(FontFlags::from_bits_truncate)
.unwrap_or_default())
}

/// Parse a simple font encoding from a font dictionary.
///
/// # Paramaters
///
/// - `dictionary`: The PDF font dictionary that may contain `/Encoding`.
/// - `objects`: The resolver used to dereference indirect PDF objects.
///
/// # Returns
///
/// The parsed encoding when `/Encoding` is present and supported.
fn simple_font_encoding(dictionary: &Dictionary, objects: &dyn ObjectResolver) -> Option<Encoding> {
dictionary.get("Encoding").and_then(|enc_obj| {
let resolved = objects.resolve_object(enc_obj).ok()?;
match resolved {
ObjectVariant::Dictionary(d) => Encoding::from_dictionary(d, objects).ok(),
_ => {
let base = FontEncoding::from(resolved.try_str(objects).ok()?);
Encoding::from_base_encoding(base).ok()
}
}
})
}

/// Parse an optional ToUnicode CMap from a font dictionary.
///
/// # Paramaters
Expand Down
23 changes: 23 additions & 0 deletions crates/pdf-font/src/flags.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use bitflags::bitflags;
use pdf_object::{
dictionary::Dictionary, object_lookup::ObjectLookupExt, object_resolver::ObjectResolver,
};

use crate::error::FontError;

bitflags! {
/// Font descriptor flags as defined in ISO 32000-1, Table 123.
Expand All @@ -20,3 +25,21 @@ bitflags! {
const FORCE_BOLD = 1 << 18; // spec bit 19
}
}

impl FontFlags {
/// Read font descriptor flags from a font dictionary.
pub(crate) fn from_dictionary(
dictionary: &Dictionary,
objects: &dyn ObjectResolver,
) -> Result<Self, FontError> {
let Some(descriptor) = dictionary.optional_dictionary("FontDescriptor", objects)? else {
return Ok(Self::empty());
};

Ok(descriptor
.get("Flags")
.and_then(|value| value.try_number::<u32>(objects).ok())
.map(Self::from_bits_truncate)
.unwrap_or_default())
}
}
51 changes: 12 additions & 39 deletions crates/pdf-font/src/true_type_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@ use std::collections::HashMap;
use pdf_cmap::ToUnicodeCMap;
use pdf_object::{
dictionary::Dictionary, object_lookup::ObjectLookupExt, object_resolver::ObjectResolver,
object_variant::ObjectVariant,
};

use crate::{
encoding::{Encoding, FontEncoding},
error::FontError,
fallback::FallbackFontProgram,
flags::FontFlags,
font_data::FontData,
simple_font_glyph_map::SimpleFontGlyphWidthsMap,
encoding::Encoding, error::FontError, fallback::FallbackFontProgram, flags::FontFlags,
font_data::FontData, simple_font_glyph_map::SimpleFontGlyphWidthsMap,
standard14::Standard14Font,
};

Expand All @@ -37,8 +32,8 @@ pub struct TrueTypeFont {
pub flags: FontFlags,
}

struct TrueTypeFontProgram {
font_file: FontData,
pub(crate) struct TrueTypeFontProgram {
pub(crate) font_file: FontData,
standard14: Option<Standard14Font>,
flags: FontFlags,
}
Expand Down Expand Up @@ -66,26 +61,17 @@ impl TrueTypeFont {
dictionary: &Dictionary,
objects: &dyn ObjectResolver,
) -> Result<Self, FontError> {
let program = Self::read_font_program(dictionary, objects)?;
let program = Self::read_font_file(dictionary, objects)?;
// Read the `/Widths` entry.
let widths = SimpleFontGlyphWidthsMap::from_dictionary(dictionary, objects)?;

// Read optional `/Encoding` entry — either a name (base encoding) or a
// dictionary (with optional BaseEncoding + Differences). Errors are
// treated as absent encoding rather than propagated, since TrueType fonts
// often omit or mis-specify this entry.
let encoding: Option<Encoding> = dictionary
.get("Encoding")
.and_then(|enc_obj| {
let resolved = objects.resolve_object(enc_obj).ok()?;
match resolved {
ObjectVariant::Dictionary(d) => Encoding::from_dictionary(d, objects).ok(),
_ => {
let base = FontEncoding::from(resolved.try_str(objects).ok()?);
Encoding::from_base_encoding(base).ok()
}
}
})
let encoding = Encoding::from_dictionary(dictionary, objects)
.ok()
.flatten()
.or_else(|| Self::default_simple_encoding(program.flags, program.standard14));

// Parse optional ToUnicode CMap stream.
Expand Down Expand Up @@ -156,28 +142,15 @@ impl TrueTypeFont {
///
/// # Returns
///
/// Returns the font file bytes as [`FontData`] or a [`FontError`] if
/// reading or decompressing the font stream fails or if the font dictionary or its
/// `/FontDescriptor` entry is invalid.
/// Returns the resolved font program and its fallback metadata, or a
/// [`FontError`] if reading the font dictionary or stream fails.
pub(crate) fn read_font_file(
dictionary: &Dictionary,
objects: &dyn ObjectResolver,
) -> Result<(FontData, FontFlags), FontError> {
let program = Self::read_font_program(dictionary, objects)?;
Ok((program.font_file, program.flags))
}

fn read_font_program(
dictionary: &Dictionary,
objects: &dyn ObjectResolver,
) -> Result<TrueTypeFontProgram, FontError> {
if let Some(descriptor) = dictionary.optional_dictionary("FontDescriptor", objects)? {
let flags = descriptor
.get("Flags")
.and_then(|obj| obj.try_number::<u32>(objects).ok())
.map(FontFlags::from_bits_truncate)
.unwrap_or_default();
let flags = FontFlags::from_dictionary(dictionary, objects)?;

if let Some(descriptor) = dictionary.optional_dictionary("FontDescriptor", objects)? {
if let Some(stream) = descriptor.optional_stream("FontFile2", objects)? {
return Ok(TrueTypeFontProgram {
font_file: FontData::shared(stream.shared_data()),
Expand Down
2 changes: 1 addition & 1 deletion crates/pdf-font/src/type0_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ fn read_type0_font_program(
match subtype {
CidFontSubType::Type0 => read_cid_font_type0_program(dictionary, objects),
CidFontSubType::Type2 => Ok(Type0FontProgram {
font_file: TrueTypeFont::read_font_file(dictionary, objects)?.0,
font_file: TrueTypeFont::read_font_file(dictionary, objects)?.font_file,
program_format: Type0FontProgramFormat::TrueType {
cid_to_unicode: false,
},
Expand Down
22 changes: 2 additions & 20 deletions crates/pdf-font/src/type1_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@ use std::{collections::HashMap, sync::Arc};
use pdf_cmap::ToUnicodeCMap;
use pdf_object::{
dictionary::Dictionary, object_lookup::ObjectLookupExt, object_resolver::ObjectResolver,
object_variant::ObjectVariant,
};

use crate::{
cff_builder::build_cff_font,
encoding::{Encoding, FontEncoding},
error::FontError,
font_data::FontData,
cff_builder::build_cff_font, encoding::Encoding, error::FontError, font_data::FontData,
simple_font_glyph_map::SimpleFontGlyphWidthsMap,
};

Expand Down Expand Up @@ -48,21 +44,7 @@ impl Type1Font {
// Read the `/Widths` entry.
let widths = SimpleFontGlyphWidthsMap::from_dictionary(dictionary, objects)?;

// Read optional `/Encoding` entry. This is either a name or a dictionary.
let encoding = if let Some(enc_obj) = dictionary.get("Encoding") {
let enc_obj = objects.resolve_object(enc_obj)?;
match enc_obj {
ObjectVariant::Dictionary(enc_dictionary) => {
Encoding::from_dictionary(enc_dictionary, objects)?
}
_ => {
let base = FontEncoding::from(enc_obj.try_str(objects)?);
Encoding::from_base_encoding(base)?
}
}
} else {
Encoding::default()
};
let encoding = Encoding::from_dictionary(dictionary, objects)?.unwrap_or_default();

// Parse optional ToUnicode CMap stream.
let to_unicode = dictionary
Expand Down
17 changes: 2 additions & 15 deletions crates/pdf-font/src/type3_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@ use pdf_content_stream::{ContentStream, ContentStreamIdAllocator};
use pdf_graphics::{rect::Rect, transform::Transform};
use pdf_object::{
dictionary::Dictionary, object_lookup::ObjectLookupExt, object_resolver::ObjectResolver,
object_variant::ObjectVariant,
};

use crate::{
encoding::{Encoding, FontEncoding},
error::FontError,
};
use crate::{encoding::Encoding, error::FontError};

/// Represents a Type 3 font in a PDF document.
///
Expand Down Expand Up @@ -52,16 +48,7 @@ impl Type3Font {
bottom,
};

// Read optional `/Encoding` entry. This is either a name or a dictionary.
let encoding = dictionary
.get("Encoding")
.map(|enc_obj| match objects.resolve_object(enc_obj)? {
ObjectVariant::Dictionary(enc_dictionary) => {
Encoding::from_dictionary(enc_dictionary, objects)
}
other => Encoding::from_base_encoding(FontEncoding::from(other.try_str(objects)?)),
})
.transpose()?;
let encoding = Encoding::from_dictionary(dictionary, objects)?;

let char_proc_dictionary = dictionary.required_dictionary("CharProcs", objects)?;

Expand Down
Loading