mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-14 14:52:22 +00:00
M10: the editor reads a swapped post version as the renderer does (#3962)
CodeRabbit on #3969: symbolic_glyph_texts read `post` straight from the font, so a version written with its halves swapped (00 00 00 02 for 2.0, which the renderer repairs since #2946, vendored hayro-interpret patch (49)) gave the editor no glyph names while the renderer drew the font. The table is now read as the version it means first. The test builds the same symbolic font with the swapped version and gets the same text. Also removes a stray `|` main had left in hayro-interpret's test list, which split it into a sixth cell. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3200a5078d
commit
3fe55aae22
2 files changed
+29
-2
No files matched your search
+15
-2
@@ -1234,7 +1234,8 @@ pub fn glyph_unicode(name: &str) -> Option<char> {
|
||||
/// 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.
|
||||
fn symbolic_glyph_texts(doc: &Document, font: &Dict) -> Vec<(u32, String)> {
|
||||
use skrifa::{GlyphId, raw::TableProvider as _, raw::tables::cmap::PlatformId};
|
||||
use skrifa::GlyphId;
|
||||
use skrifa::raw::{FontData, FontRead as _, TableProvider as _, tables::cmap::PlatformId, tables::post::Post, types::Tag};
|
||||
let Some(descriptor) = dict(doc, font.get(b"FontDescriptor")) else { return Vec::new() };
|
||||
// Flag bit 6, Nonsymbolic (ISO 32000-2 Table 121).
|
||||
let flags = descriptor.get(b"Flags").and_then(|v| doc.resolve(v).as_f64()).unwrap_or(0.0) as i64;
|
||||
@@ -1246,7 +1247,19 @@ fn symbolic_glyph_texts(doc: &Document, font: &Dict) -> Vec<(u32, String)> {
|
||||
let Object::Stream(stream) = object.as_ref() else { return Vec::new() };
|
||||
let Ok(bytes) = stream.decoded_within(16 << 20) else { return Vec::new() };
|
||||
let Ok(face) = skrifa::FontRef::from_index(&bytes, 0) else { return Vec::new() };
|
||||
let (Ok(cmap), Ok(post)) = (face.cmap(), face.post()) else { return Vec::new() };
|
||||
let Ok(cmap) = face.cmap() else { return Vec::new() };
|
||||
// A `post` version written with its 16-bit halves swapped (`00 00 00 02` for 2.0, as ScanSoft
|
||||
// PDF Create! writes it) is read as the version it means, as the renderer reads it (#2946,
|
||||
// vendored hayro-interpret patch (49)); otherwise its names would be lost here alone.
|
||||
let repaired = face.table_data(Tag::new(b"post")).and_then(|data| match data.as_bytes() {
|
||||
[0, 0, 0, n @ 1..=3, rest @ ..] => Some([&[0, *n, 0, 0], rest].concat()),
|
||||
_ => None,
|
||||
});
|
||||
let post = match &repaired {
|
||||
Some(bytes) => Post::read(FontData::new(bytes)),
|
||||
None => face.post(),
|
||||
};
|
||||
let Ok(post) = post else { return Vec::new() };
|
||||
(0..=255u32)
|
||||
.filter_map(|code| {
|
||||
// The records in order, as the renderer's `map_code` reads a symbolic font: a Windows
|
||||
|
||||
@@ -284,6 +284,20 @@ fn a_symbolic_truetype_font_without_an_encoding_reads_its_glyph_names() {
|
||||
};
|
||||
let symbolic = metrics(4, None);
|
||||
assert_eq!(symbolic.decode(&codes), TEXT);
|
||||
// The same font with its `post` version halves swapped (`00 00 00 02`) reads the same,
|
||||
// as the renderer reads it (#2946).
|
||||
let post = (0..count).map(|i| 12 + i * 16).find(|i| &subset[*i..*i + 4] == b"post").unwrap();
|
||||
let at = u32::from_be_bytes(subset[post + 8..post + 12].try_into().unwrap()) as usize;
|
||||
assert_eq!(subset[at..at + 4], [0, 2, 0, 0], "Inter's post is version 2.0");
|
||||
let mut swapped = subset.clone();
|
||||
swapped[at..at + 4].copy_from_slice(&[0, 0, 0, 2]);
|
||||
let mut doc = Document::new_empty();
|
||||
let program = doc.add(Object::Stream(Stream::flate(Dict::new(), &swapped)));
|
||||
let mut descriptor = Dict::new();
|
||||
descriptor.set(b"Flags".to_vec(), Object::Int(4));
|
||||
descriptor.set(b"FontFile2".to_vec(), Object::Ref(program));
|
||||
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.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.
|
||||
|
||||
Reference in new issue
Block a user