mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-10 13:35:21 +00:00
M10: an unreadable glyph name reads as WinAnsi in the editor too (#3962)
Review on #3969: for a symbolic TrueType font without an encoding, the editor overlaid only the codes whose glyph has a name with a Unicode value on its Standard table. A code whose glyph name has none kept Standard's reading (0xC4 `tilde`, 0x80 nothing), while extraction falls back to WinAnsi (vendored hayro-interpret patch (53)): `Ä`, `€`. Such a code now reads as WinAnsi in the editor as well, so editing and extraction show the same text. vendor/README.md: patch (53) names the function it adds, `EmbeddedKind::symbolic_text`, states the glyph-name rules it shares with the editor, and lists the pdfcraft-fonts test too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
53d0e36533
commit
175d4e4de1
@@ -1232,7 +1232,9 @@ pub fn glyph_unicode(name: &str) -> Option<char> {
|
||||
/// selects it: the code, then at 0xF000, 0xF100 and 0xF200). Such a font's codes need not be
|
||||
/// Standard or WinAnsi: FrameMaker files distilled with Distiller 3 number them in the Macintosh
|
||||
/// glyph order, 29 below ASCII, so `Solid` read as `6ROLG` and typing set the wrong glyphs (#3962).
|
||||
/// Codes without a glyph, or whose glyph has no name with a Unicode value, are left out.
|
||||
/// Codes without a glyph are left out. A code whose glyph has no name with a Unicode value reads
|
||||
/// as WinAnsi, as the renderer reads it (vendored hayro-interpret patch (53)), so the editor and
|
||||
/// extraction agree: `€` for code 0x80, which Standard leaves without a character.
|
||||
fn symbolic_glyph_texts(doc: &Document, font: &Dict) -> Vec<(u32, String)> {
|
||||
use skrifa::GlyphId;
|
||||
use skrifa::raw::{FontData, FontRead as _, TableProvider as _, tables::cmap::PlatformId, tables::post::Post, types::Tag};
|
||||
@@ -1276,7 +1278,11 @@ fn symbolic_glyph_texts(doc: &Document, font: &Dict) -> Vec<(u32, String)> {
|
||||
offsets.iter().find_map(|o| subtable.map_codepoint(code + o).filter(|g| *g != GlyphId::NOTDEF))
|
||||
})?;
|
||||
let glyph = u16::try_from(glyph.to_u32()).ok()?;
|
||||
Some((code, glyph_text(post_name(&post, &custom, glyph)?)?))
|
||||
let winansi = || {
|
||||
let u = crate::encodings::WIN_ANSI.get(usize::try_from(code).ok()?).copied().filter(|u| *u != 0)?;
|
||||
char::from_u32(u).map(String::from)
|
||||
};
|
||||
Some((code, post_name(&post, &custom, glyph).and_then(glyph_text).or_else(winansi)?))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -224,6 +224,10 @@ fn a_symbolic_truetype_font_without_an_encoding_reads_its_glyph_names() {
|
||||
let variant = (1..face.maxp().unwrap().num_glyphs())
|
||||
.find(|&g| post.glyph_name(skrifa::raw::types::GlyphId16::new(g)) == Some("a.1"))
|
||||
.expect("Inter has a.1");
|
||||
// A glyph whose name has no Unicode value: its code reads as WinAnsi, as the renderer reads it.
|
||||
let unnamed = (1..face.maxp().unwrap().num_glyphs())
|
||||
.find(|&g| post.glyph_name(skrifa::raw::types::GlyphId16::new(g)).is_some_and(|n| !n.starts_with('.') && glyph_text(n).is_none()))
|
||||
.expect("Inter has a glyph whose name has no text");
|
||||
// A format 6 Mac cmap over codes 3..=0x57: each letter at its code minus 29.
|
||||
let (first, last) = (3u16, 0x57u16);
|
||||
let mut glyphs = vec![0u16; usize::from(last - first + 1)];
|
||||
@@ -252,8 +256,8 @@ fn a_symbolic_truetype_font_without_an_encoding_reads_its_glyph_names() {
|
||||
.collect();
|
||||
words.iter().flat_map(|w| w.to_be_bytes()).collect::<Vec<u8>>()
|
||||
};
|
||||
// Symbol (3,0): 0xF020 → k, 0xF0C0 → é, 0xF0C1 → a.1, 0xF0C2 → x.
|
||||
let symbol = format4(&[(0xF020, gid('k')), (0xF0C0, gid('é')), (0xF0C1, variant), (0xF0C2, gid('x')), (0xF0C3, gid('w'))]);
|
||||
// Symbol (3,0): 0xF020 → k, 0xF0C0 → é, 0xF0C1 → a.1, 0xF0C2 → x, 0xF0C4 → the unnamed glyph.
|
||||
let symbol = format4(&[(0xF020, gid('k')), (0xF0C0, gid('é')), (0xF0C1, variant), (0xF0C2, gid('x')), (0xF0C3, gid('w')), (0xF0C4, unnamed)]);
|
||||
// Unicode (3,1), after it: 0xC2 → z. The renderer reads the (3,0) record, offsets and
|
||||
// all, before this one, so code 0xC2 is `x`.
|
||||
let unicode = format4(&[(0xC2, gid('z'))]);
|
||||
@@ -315,6 +319,7 @@ fn a_symbolic_truetype_font_without_an_encoding_reads_its_glyph_names() {
|
||||
let f = font(&mut doc, vec![("Subtype", Object::name("TrueType")), ("FontDescriptor", Object::Dict(descriptor))]);
|
||||
assert_eq!(Metrics::from_dict(&doc, &f).decode(&codes), TEXT, "a swapped post version");
|
||||
assert_eq!(symbolic.decode(&[0x20, 0xC0, 0xC1, 0xC2, 0xC3]), "kéaxw");
|
||||
assert_eq!(symbolic.decode(&[0xC4]), "Ä", "a glyph name with no text reads as WinAnsi, not Standard's `tilde`");
|
||||
assert_eq!(symbolic.encode("Solid").as_deref(), Some(&codes[..5]), "typing sets the same glyphs");
|
||||
// A named encoding, or the Nonsymbolic flag, says what the codes are: they read through it.
|
||||
assert_eq!(metrics(4, Some(Object::name("WinAnsiEncoding"))).decode(&codes), "6ROLGVWDWH");
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user