The collection's UCS2 CMap is parsed the first time a code needs it

A font whose /ToUnicode maps every code it shows never parses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
This commit is contained in:
mtlcl410
2026-11-06 07:04:15 +08:00
co-authored by Claude Opus 5.5
parent fc28e85fb8
commit a00ff8fa6a
2 changed files with 32 additions and 9 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+31 -8
View File
@@ -6,6 +6,7 @@ use crate::font::{
};
use crate::{CMapResolverFn, CacheKey, FontResolverFn};
use hayro_cmap::{BfString, CMap, CharacterCollection, CidFamily, WritingMode};
use std::sync::OnceLock;
use hayro_syntax::object;
use hayro_syntax::object::Dict;
use hayro_syntax::object::Name;
@@ -44,7 +45,7 @@ pub(crate) struct Type0Font {
to_unicode_is_cid_indexed: bool,
/// PdfCraft patch (84): the character collection's UCS2 CMap (by CID), for the codes the
/// font's own `/ToUnicode` gives nothing for, whether or not its program is embedded.
ucs2: Option<CMap>,
ucs2: Option<Ucs2>,
/// PdfCraft patch: whether the CIDs belong to an Adobe character collection (Japan1, GB1,
/// CNS1, Korea1), whose glyph numbering no substitute other than a CID-keyed CFF font shares.
adobe_collection: bool,
@@ -140,13 +141,10 @@ impl Type0Font {
.as_ref()
.and_then(|cc| cc.family.ucs2_cmap())
.and_then(|name| (cmap_resolver)(name))
.and_then(|data| {
let resolver = cmap_resolver.clone();
CMap::parse(data, move |n| (resolver)(n))
});
.map(|data| Ucs2 { data, resolver: cmap_resolver.clone(), map: OnceLock::new() });
// If there is no ToUnicode map, try to get the UCS2 CMap.
if fallback && to_unicode.is_none() && ucs2.is_some() {
to_unicode = ucs2.take();
if fallback && to_unicode.is_none() && let Some(ucs2_cmap) = ucs2.take().and_then(|u| u.parse()) {
to_unicode = Some(ucs2_cmap);
to_unicode_is_cid_indexed = true;
}
@@ -477,7 +475,7 @@ impl Type0Font {
}
}
// PdfCraft patch (84): then the collection's UCS2 CMap, by the code's CID.
if let Some(ucs2) = &self.ucs2
if let Some(ucs2) = self.ucs2.as_ref().and_then(Ucs2::cmap)
&& let Some(s) = self.code_to_cid(code).and_then(|cid| ucs2.lookup_bf_string(cid))
{
return Some(s);
@@ -487,6 +485,31 @@ impl Type0Font {
}
}
/// PdfCraft patch (84): a collection's UCS2 CMap, parsed the first time a code needs it: a font
/// whose `/ToUnicode` maps every code it shows never parses it.
struct Ucs2 {
data: &'static [u8],
resolver: CMapResolverFn,
map: OnceLock<Option<CMap>>,
}
impl Ucs2 {
fn parse(&self) -> Option<CMap> {
let resolver = self.resolver.clone();
CMap::parse(self.data, move |n| (resolver)(n))
}
fn cmap(&self) -> Option<&CMap> {
self.map.get_or_init(|| self.parse()).as_ref()
}
}
impl std::fmt::Debug for Ucs2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ucs2").field("bytes", &self.data.len()).finish_non_exhaustive()
}
}
impl CacheKey for Type0Font {
fn cache_key(&self) -> u128 {
self.cache_key