Skip to content

Commit f585ef3

Browse files
authored
Merge pull request #253
pdf-font: centralize font map and fallback parsing
2 parents 230ea19 + 47aed15 commit f585ef3

7 files changed

Lines changed: 50 additions & 87 deletions

File tree

crates/pdf-cmap/src/to_unicode.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ pub struct ToUnicodeCMap(HashMap<u16, Vec<char>>);
1010

1111
impl ToUnicodeCMap {
1212
/// Parse the optional `/ToUnicode` CMap from a font dictionary.
13+
///
14+
/// # Paramaters
15+
///
16+
/// - `dictionary`: The PDF font dictionary that may contain `/ToUnicode`.
17+
/// - `objects`: The resolver used to dereference indirect PDF objects.
18+
///
19+
/// # Returns
20+
///
21+
/// The parsed ToUnicode CMap when a readable `/ToUnicode` stream is present.
1322
pub fn from_dictionary(
1423
dictionary: &Dictionary,
1524
objects: &dyn ObjectResolver,

crates/pdf-font/src/fallback.rs

Lines changed: 19 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ pub(crate) struct FallbackFontProgram {
1414
}
1515

1616
impl FallbackFontProgram {
17+
const NOTO_SANS_CJK_JP_REGULAR: &[u8] = include_bytes!("../assets/NotoSansCJKjp-Regular.otf");
18+
1719
/// Select fallback font bytes and metadata for a font dictionary.
1820
pub(crate) fn from_dictionary(
1921
dictionary: &Dictionary,
@@ -22,8 +24,17 @@ impl FallbackFontProgram {
2224
let flags = FontFlags::from_dictionary(dictionary, objects)?;
2325
let standard14 = Standard14Font::from_dictionary(dictionary, objects, flags);
2426
let is_cjk = is_cjk_cid_font(dictionary, objects)?;
27+
let font_file = if is_cjk {
28+
Self::NOTO_SANS_CJK_JP_REGULAR
29+
} else {
30+
standard14.fallback_font_bytes()
31+
};
2532

26-
Ok(fallback_program(flags, standard14, is_cjk))
33+
Ok(Self {
34+
font_file,
35+
standard14,
36+
flags,
37+
})
2738
}
2839
}
2940

@@ -47,7 +58,7 @@ pub(crate) fn fallback_true_type_from_dictionary(
4758
let encoding = Encoding::from_dictionary(dictionary, objects)
4859
.ok()
4960
.flatten();
50-
let to_unicode = to_unicode_cmap(dictionary, objects)?;
61+
let to_unicode = ToUnicodeCMap::from_dictionary(dictionary, objects)?;
5162

5263
Ok(TrueTypeFont {
5364
font_file: fallback.font_file.into(),
@@ -71,56 +82,20 @@ pub(crate) fn fallback_true_type_from_dictionary_best_effort(
7182
let flags = FontFlags::from_dictionary(dictionary, objects).unwrap_or_default();
7283
let is_cjk = is_cjk_cid_font(dictionary, objects).unwrap_or(false);
7384
let standard14 = Standard14Font::from_dictionary(dictionary, objects, flags);
74-
let fallback = fallback_program(flags, standard14, is_cjk);
75-
let mut font = TrueTypeFont::from_bytes(fallback.font_file, Some(fallback.standard14));
76-
font.flags = fallback.flags;
77-
78-
font
79-
}
80-
81-
/// Build the fallback font program descriptor from already-decided inputs.
82-
///
83-
/// `standard14` selects the Standard 14 identity for simple-font fallback,
84-
/// while `is_cjk` switches the program bytes to the bundled CJK fallback for
85-
/// CID fonts that declare a supported CJK ordering.
86-
fn fallback_program(
87-
flags: FontFlags,
88-
standard14: Standard14Font,
89-
is_cjk: bool,
90-
) -> FallbackFontProgram {
9185
let font_file = if is_cjk {
92-
include_bytes!("../assets/NotoSansCJKjp-Regular.otf").as_slice()
86+
FallbackFontProgram::NOTO_SANS_CJK_JP_REGULAR
9387
} else {
9488
standard14.fallback_font_bytes()
9589
};
96-
97-
FallbackFontProgram {
90+
let fallback = FallbackFontProgram {
9891
font_file,
9992
standard14,
10093
flags,
101-
}
102-
}
94+
};
95+
let mut font = TrueTypeFont::from_bytes(fallback.font_file, Some(fallback.standard14));
96+
font.flags = fallback.flags;
10397

104-
/// Parse an optional ToUnicode CMap from a font dictionary.
105-
///
106-
/// # Paramaters
107-
///
108-
/// - `dictionary`: The PDF font dictionary that may contain `/ToUnicode`.
109-
/// - `objects`: The resolver used to dereference indirect PDF objects.
110-
///
111-
/// # Returns
112-
///
113-
/// The parsed ToUnicode CMap when a readable `/ToUnicode` stream is present.
114-
fn to_unicode_cmap(
115-
dictionary: &Dictionary,
116-
objects: &dyn ObjectResolver,
117-
) -> Result<Option<ToUnicodeCMap>, FontError> {
118-
dictionary
119-
.get("ToUnicode")
120-
.and_then(|e| e.try_stream(objects).ok())
121-
.map(|s| ToUnicodeCMap::try_from(s.raw_data()))
122-
.transpose()
123-
.map_err(FontError::from)
98+
font
12499
}
125100

126101
/// Detect whether a CID font dictionary uses a known CJK CID ordering.

crates/pdf-font/src/glyph_widths_map.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use pdf_object::{
2-
error::ObjectError, object_resolver::ObjectResolver, object_variant::ObjectVariant,
2+
dictionary::Dictionary, error::ObjectError, object_resolver::ObjectResolver,
3+
object_variant::ObjectVariant,
34
};
45
use std::collections::BTreeMap;
56
use thiserror::Error;
67

8+
use crate::error::FontError;
9+
710
/// Errors that can occur during GlyphWidthsMap parsing from a /W array.
811
#[derive(Debug, Error, Clone, PartialEq)]
912
pub enum GlyphWidthsMapError {
@@ -58,6 +61,20 @@ pub struct GlyphWidthsMap {
5861
}
5962

6063
impl GlyphWidthsMap {
64+
/// Parse the optional `/W` width map from a descendant CIDFont dictionary.
65+
pub fn from_dictionary(
66+
dictionary: &Dictionary,
67+
objects: &dyn ObjectResolver,
68+
) -> Result<Option<Self>, FontError> {
69+
dictionary
70+
.get("W")
71+
.map(|value| {
72+
let widths = value.try_array(objects)?;
73+
Self::from_array(widths, objects).map_err(FontError::from)
74+
})
75+
.transpose()
76+
}
77+
6178
/// Parses a PDF /W array into a `GlyphWidthsMap`.
6279
///
6380
/// The /W array can contain entries of the form:

crates/pdf-font/src/true_type_font.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,7 @@ impl TrueTypeFont {
7474
.flatten()
7575
.or_else(|| Self::default_simple_encoding(program.flags, program.standard14));
7676

77-
// Parse optional ToUnicode CMap stream.
78-
let to_unicode = dictionary
79-
.get("ToUnicode")
80-
.and_then(|e| e.try_stream(objects).ok())
81-
.map(|s| ToUnicodeCMap::try_from(s.raw_data()))
82-
.transpose()?;
77+
let to_unicode = ToUnicodeCMap::from_dictionary(dictionary, objects)?;
8378

8479
Ok(Self {
8580
font_file: program.font_file,

crates/pdf-font/src/type0_font.rs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ impl<'a> Type0DescendantFont<'a> {
170170
dictionary,
171171
subtype: CidFontSubType::from_dictionary(dictionary, objects)?,
172172
default_width,
173-
widths: widths_map(dictionary, objects)?,
173+
widths: GlyphWidthsMap::from_dictionary(dictionary, objects)?,
174174
})
175175
}
176176
}
@@ -210,29 +210,6 @@ fn descendant_font_dictionary<'a>(
210210
.map_err(FontError::from)
211211
}
212212

213-
/// Parse explicit CID width overrides from a descendant font dictionary.
214-
///
215-
/// # Paramaters
216-
///
217-
/// - `dictionary`: The descendant CIDFont dictionary.
218-
/// - `objects`: The resolver used to dereference indirect PDF objects.
219-
///
220-
/// # Returns
221-
///
222-
/// The parsed `/W` width map when the dictionary contains width overrides.
223-
fn widths_map(
224-
dictionary: &Dictionary,
225-
objects: &dyn ObjectResolver,
226-
) -> Result<Option<GlyphWidthsMap>, FontError> {
227-
dictionary
228-
.get("W")
229-
.map(|obj| {
230-
let widths = obj.try_array(objects)?;
231-
GlyphWidthsMap::from_array(widths, objects).map_err(FontError::from)
232-
})
233-
.transpose()
234-
}
235-
236213
/// Read or synthesize the font program for a Type0 descendant font.
237214
///
238215
/// # Paramaters

crates/pdf-font/src/type1_font.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,7 @@ impl Type1Font {
4646

4747
let encoding = Encoding::from_dictionary(dictionary, objects)?.unwrap_or_default();
4848

49-
// Parse optional ToUnicode CMap stream.
50-
let to_unicode = dictionary
51-
.get("ToUnicode")
52-
.and_then(|e| e.try_stream(objects).ok())
53-
.map(|s| ToUnicodeCMap::try_from(s.raw_data()))
54-
.transpose()?;
49+
let to_unicode = ToUnicodeCMap::from_dictionary(dictionary, objects)?;
5550

5651
Ok(Self {
5752
font_file,

crates/pdf-font/src/type3_font.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,7 @@ impl Type3Font {
5858
char_procs.insert(name.to_owned(), content_stream);
5959
}
6060

61-
// Parse optional ToUnicode CMap stream.
62-
let to_unicode = dictionary
63-
.get("ToUnicode")
64-
.and_then(|e| e.try_stream(objects).ok())
65-
.map(|s| ToUnicodeCMap::try_from(s.raw_data()))
66-
.transpose()?;
61+
let to_unicode = ToUnicodeCMap::from_dictionary(dictionary, objects)?;
6762

6863
Ok(Type3Font {
6964
font_matrix,

0 commit comments

Comments
 (0)