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
2 changes: 1 addition & 1 deletion crates/pdf-font/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub use font::{
};
pub use font_registry::FontRegistry;
pub use pdf::{
CharProcedures, CidFontKind, CidFontSpec, CidSystemInfo, CidToGidMap,
CharProcedures, CidFontKind, CidFontSpec, CidSystemInfo, CidToGidMap, CidToUnicode,
PDF_GLYPH_SPACE_UNITS_PER_EM, PdfCode, PdfFontDescriptor, PdfMetrics, SimpleEncoding,
SimpleFontSpec, ToUnicodeMap, Type0FontSpec, Type3FontSpec,
};
Expand Down
4 changes: 3 additions & 1 deletion crates/pdf-font/src/pdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use crate::glyph_widths_map::GlyphWidthsMap;
pub use crate::pdf_font_spec::PdfFontSpec;
pub use crate::simple_font_spec::SimpleFontSpec;
pub use crate::standard14::Standard14Font;
pub use crate::type0_font_spec::{CidFontKind, CidFontSpec, CidToGidMap, Type0FontSpec};
pub use crate::type0_font_spec::{
CidFontKind, CidFontSpec, CidToGidMap, CidToUnicode, Type0FontSpec,
};
pub use crate::type3_font_spec::{CharProcedures, Type3FontSpec};
pub use pdf_cmap::{PdfCode, ToUnicodeMap};

Expand Down
15 changes: 10 additions & 5 deletions crates/pdf-font/src/pdf_font_descriptor.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
//! Best-effort font descriptor metadata and CID collection information.

use crate::pdf::{CidSystemInfo, PdfFontDescriptor};
use crate::{
pdf::{CidSystemInfo, PdfFontDescriptor},
type0_font_spec::CidToUnicode,
};
use pdf_cmap::predefined::PredefinedCMap;
use pdf_object_reader::{FromPdfObject, ObjectAccess, ObjectContext, ReadResult};
use std::{collections::HashMap, sync::Arc};
use std::sync::Arc;

impl FromPdfObject for PdfFontDescriptor {
/// Reads a FontDescriptor dictionary, not the enclosing font dictionary, tolerating
Expand Down Expand Up @@ -44,8 +47,8 @@ impl FromPdfObject for CidSystemInfo {
}

impl CidSystemInfo {
/// Finds the existing collection-based Unicode fallback when the ordering is known.
pub(crate) fn cid_to_unicode_map(&self) -> Option<Arc<HashMap<u16, char>>> {
/// Finds the collection-based Unicode fallback when the ordering is known.
pub(crate) fn cid_to_unicode_map(&self) -> Option<CidToUnicode> {
let cmap_name: &[u8] = match self.ordering.as_ref() {
b"Japan1" => b"UniJIS-UCS2-HW-H",
b"GB1" => b"UniGB-UCS2-H",
Expand All @@ -54,6 +57,8 @@ impl CidSystemInfo {
_ => return None,
};
let cmap = PredefinedCMap::from_name(cmap_name).ok()??;
Some(Arc::new(cmap.cid_to_unicode_map()))
Some(CidToUnicode::Collection(Arc::new(
cmap.cid_to_unicode_map(),
)))
}
}
3 changes: 3 additions & 0 deletions crates/pdf-font/src/pdf_font_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ impl PdfFontSpec {
PdfFontSpec::Type1(font)
| PdfFontSpec::MultipleMasterType1(font)
| PdfFontSpec::TrueType(font) => font.standard14.unwrap_or_default(),
PdfFontSpec::Type0(font) => {
standard14::from_base_font_name(&font.base_font).unwrap_or_default()
}
_ => Standard14Font::Helvetica,
}
}
Expand Down
7 changes: 7 additions & 0 deletions crates/pdf-font/src/standard14.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ pub(crate) fn from_flags(flags: FontFlags) -> Standard14Font {
}

impl Standard14Font {
/// Returns whether the TrueType core font behind this identity, such as Arial for Helvetica,
/// keeps the standard Macintosh glyph order. The symbol fonts do not.
#[must_use]
pub fn has_standard_glyph_order(self) -> bool {
!matches!(self, Self::Symbol | Self::ZapfDingbats)
}

/// Returns the advance width, in 1/1000 em, of a named glyph in this font's AFM metrics.
///
/// Courier is monospaced, so every glyph name is 600 units wide. Other fonts return `None`
Expand Down
47 changes: 43 additions & 4 deletions crates/pdf-font/src/type0_font_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ use crate::{
font::{FontSource, GlyphId},
pdf::{CidSystemInfo, PdfFontDescriptor, PdfMetrics, ToUnicodeMap},
pdf_font_program::{CidType0Program, TrueTypeProgram},
standard14::{Standard14Font, from_base_font_name},
};
use pdf_cmap::{Cid, PdfCMap, Type0EncodingCMap};
use pdf_object_reader::{
FromPdfObject, ObjectAccess, ObjectContext, ReadResult, object_variant::ObjectVariant,
};
use read_fonts::{ps::agl, tables::post::DEFAULT_GLYPH_NAMES};
use std::{collections::HashMap, sync::Arc};

/// Descendant subtype used by a Type 0 composite font.
Expand All @@ -36,8 +38,8 @@ pub struct CidFontSpec {
pub metrics: PdfMetrics,
/// CID-to-glyph identifier mapping, used by CIDFontType2 only.
pub cid_to_gid: CidToGidMap,
/// Best-effort CID-to-Unicode mapping for collection-backed font substitution.
pub cid_to_unicode: Option<Arc<HashMap<u16, char>>>,
/// Best-effort CID-to-Unicode mapping for font substitution.
pub cid_to_unicode: Option<CidToUnicode>,
}

/// Normalized Type 0 composite font.
Expand Down Expand Up @@ -75,6 +77,33 @@ impl CidToGidMap {
}
}

/// Best-effort CID-to-Unicode mapping used when a CID font has no `/ToUnicode` entry.
#[derive(Clone)]
pub enum CidToUnicode {
/// Inverted predefined Unicode CMap of a CJK character collection.
Collection(Arc<HashMap<u16, char>>),
/// CIDs are glyph indices in the standard Macintosh glyph order.
///
/// TrueType core fonts such as Arial, Times New Roman, and Courier New keep this order for
/// their first 258 glyphs. A non-embedded Identity CIDFontType2 font naming one of them
/// selects glyphs of that font by index, so its CIDs follow this order.
StandardGlyphOrder,
}

impl CidToUnicode {
/// Returns the Unicode value of `cid`, if known.
#[must_use]
pub fn get(&self, cid: u16) -> Option<char> {
match self {
Self::Collection(map) => map.get(&cid).copied(),
Self::StandardGlyphOrder => DEFAULT_GLYPH_NAMES
.get(usize::from(cid))
.copied()
.and_then(agl::name_to_char),
}
}
}

impl FromPdfObject for CidToGidMap {
/// Decodes a stream into its `u16` table, ignoring a trailing odd byte.
fn from_pdf_object(context: ObjectContext<'_, impl ObjectAccess + ?Sized>) -> ReadResult<Self> {
Expand Down Expand Up @@ -127,8 +156,18 @@ impl FromPdfObject for CidFontSpec {
let system_info = context
.optional::<CidSystemInfo>(b"CIDSystemInfo")?
.unwrap_or_default();
let cid_to_unicode = system_info.cid_to_unicode_map();
let cid_to_gid = context.lenient(b"CIDToGIDMap").unwrap_or_default();
let cid_to_gid: CidToGidMap = context.lenient(b"CIDToGIDMap").unwrap_or_default();
// A substituted TrueType core font without its own glyph map is indexed by CID.
let indexes_core_font = kind == CidFontKind::Type2
&& program.is_none()
&& matches!(cid_to_gid, CidToGidMap::Identity)
&& base_font
.as_deref()
.and_then(from_base_font_name)
.is_some_and(Standard14Font::has_standard_glyph_order);
let cid_to_unicode = system_info
.cid_to_unicode_map()
.or_else(|| indexes_core_font.then_some(CidToUnicode::StandardGlyphOrder));
Ok(Self {
kind,
descriptor,
Expand Down
5 changes: 2 additions & 3 deletions crates/pdf-text-engine/src/pdf_text_decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ fn type0_selector(descendant: &CidFontSpec, cid: Cid) -> GlyphSelector<'static>

/// Resolves extraction Unicode for one composite-font mapping.
///
/// The source-code `/ToUnicode` map is authoritative. A best-effort collection CID map is used only
/// The source-code `/ToUnicode` map is authoritative. The descendant's best-effort CID map is used only
/// when `/ToUnicode` has no entry and the CID fits that map's 16-bit key space.
fn type0_unicode(font: &Type0FontSpec, mapping: CidMapping) -> UnicodeSequence {
font.to_unicode
Expand All @@ -146,8 +146,7 @@ fn type0_unicode(font: &Type0FontSpec, mapping: CidMapping) -> UnicodeSequence {
font.descendant
.cid_to_unicode
.as_ref()?
.get(&cid)
.copied()
.get(cid)
.map(Into::into)
})
.unwrap_or_default()
Expand Down
Loading