mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-09 04:22:20 +00:00
Reach a THSarabunPSK subset's Thai glyphs when its own cmap is Latin-only
A legacy subset can keep a cmap of its own that maps only Latin, so the editor program returned it unchanged and an edit such as a consonant with a tone mark failed although the subset keeps both outlines. Add the PDF's verified Thai and alternate mappings to the editor-only cmap where the program's own lacks them, keeping its existing mappings.
This commit is contained in:
1 parent
d351aa2760
commit
dff6c2cc3e
3 files changed
+36
-2
No files matched your search
@@ -88,7 +88,8 @@ exact program preservation, Unicode/CID/GID mapping and full-face subset complet
|
||||
never exported. See [existing-text editing](../../docs/editing-existing-text.md).
|
||||
|
||||
`pdf::embedded_sfnt_within` reconstructs a missing Unicode cmap for Identity-H TrueType
|
||||
subsets from the PDF's ToUnicode and CID-to-glyph maps. Only the in-memory editor copy changes;
|
||||
subsets from the PDF's ToUnicode and CID-to-glyph maps, and completes a THSarabunPSK subset's
|
||||
Latin-only one with them. Only the in-memory editor copy changes;
|
||||
the PDF's font program stays intact. `pdf::LegacyThaiFont` shapes verified THSarabunPSK
|
||||
positional glyphs into their original PDF codes, retaining the source face and weight. Missing
|
||||
contextual glyphs are refused. `pdf::recovered_thai_map` recognizes narrowly fingerprinted
|
||||
|
||||
+19
-1
@@ -113,7 +113,11 @@ pub fn embedded_sfnt_within(doc: &Document, font: &Dict, budget: &mut DecodeBudg
|
||||
if face.metrics(skrifa::instance::Size::unscaled(), skrifa::instance::LocationRef::default()).units_per_em == 0 {
|
||||
continue;
|
||||
}
|
||||
if face.charmap().mappings().next().is_some() {
|
||||
let own_cmap = face.charmap().mappings().next().is_some();
|
||||
// A legacy THSarabunPSK subset may keep a Latin-only cmap beside its Thai glyphs, which
|
||||
// only the PDF's verified maps reach (`composite_glyph_maps`); other programs keep theirs.
|
||||
let base_font = font.name(b"BaseFont").map(String::from_utf8_lossy).unwrap_or_default();
|
||||
if own_cmap && !base_font.rsplit('+').next().is_some_and(|f| f.starts_with("THSarabunPSK")) {
|
||||
return Some(bytes);
|
||||
}
|
||||
// PDF subsets may remove the Unicode cmap entirely. Rebuild an editor-only
|
||||
@@ -124,6 +128,20 @@ pub fn embedded_sfnt_within(doc: &Document, font: &Dict, budget: &mut DecodeBudg
|
||||
if let Some((map, _)) = composite_glyph_maps(doc, font, &bytes, &metrics, budget) {
|
||||
mappings = map;
|
||||
}
|
||||
if own_cmap {
|
||||
let charmap = face.charmap();
|
||||
if mappings.keys().all(|&u| char::from_u32(u).is_some_and(|c| charmap.map(c).is_some())) {
|
||||
return Some(bytes);
|
||||
}
|
||||
// The program's own mappings stay; the PDF's fill only what they lack.
|
||||
mappings.extend(charmap.mappings().filter(|(_, g)| g.to_u32() != 0).take(65_537).map(|(u, g)| (u, g.to_u32())));
|
||||
let mut merged = bytes.clone();
|
||||
let usable = append_editor_cmap(&mut merged, &mappings).is_some()
|
||||
&& skrifa::FontRef::new(&merged).is_ok_and(|font| font.charmap().mappings().next().is_some());
|
||||
return Some(if usable { merged } else { bytes });
|
||||
}
|
||||
} else if own_cmap {
|
||||
return Some(bytes);
|
||||
} else if let Ok(cmap) = face.cmap() {
|
||||
for code in 0..=255 {
|
||||
let Some(text) = metrics.unicode.text(code) else { continue };
|
||||
|
||||
@@ -583,6 +583,21 @@ mod tests {
|
||||
assert_eq!(shaped.shaped.glyphs.iter().map(|g| g.id.to_u32()).collect::<Vec<_>>(), [214, 352]);
|
||||
}
|
||||
|
||||
/// A legacy subset that kept a Latin-only cmap of its own still reaches its Thai glyphs through
|
||||
/// the PDF's verified maps, and keeps its own mappings.
|
||||
#[test]
|
||||
fn a_latin_only_cmap_does_not_hide_a_legacy_subsets_thai_glyphs() {
|
||||
use skrifa::MetadataProvider as _;
|
||||
let program = without_tables(&thai_ordered_program(), &[b"GSUB", b"GPOS"]);
|
||||
let latin = skrifa::FontRef::new(&program).unwrap().charmap().map('A').unwrap();
|
||||
let mut doc = Document::new_empty();
|
||||
let font = thai_font(&mut doc, &program, INTACT);
|
||||
let shaped = LegacyThaiFont::from_pdf(&doc, &font, 0.25).unwrap().expect("a legacy subset").shape("ก่").unwrap();
|
||||
assert_eq!(shaped.shaped.glyphs.iter().map(|g| g.id.to_u32()).collect::<Vec<_>>(), [214, 352]);
|
||||
let editor = crate::pdf::embedded_sfnt(&doc, &font).unwrap();
|
||||
assert_eq!(skrifa::FontRef::new(&editor).unwrap().charmap().map('A'), Some(latin), "the program's own mappings stay");
|
||||
}
|
||||
|
||||
/// A THSarabunPSK program with its own Thai mark positioning is shaped by that layout; one
|
||||
/// without it only if every Thai glyph it keeps is where the legacy order has that character
|
||||
/// (the legacy shaper skips the positioning check). A subset needs no particular glyph.
|
||||
|
||||
Reference in new issue
Block a user