mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Fix #3623: a word gap between two sizes reads as a space in PDF import
PDF import measured each gap between two glyphs against the larger of the line's first glyph size and the next glyph's. A word set beside a much larger one, apart by positioning and no space glyph (a browser's margin between two sizes), stayed under a fifth of that em: "≤ 4" at 20 pt and "hours" at 8 pt, 3.4 pt apart, came in as "≤ 4hours". A gap is now measured against the smaller of the two glyphs beside it. Lines in one size read their gaps as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
bflatastic
co-authored by
Claude Opus 5.5
parent
b35a6b1573
commit
6903bb7ef8
@@ -1,6 +1,6 @@
|
||||
//! Text as editable point type: glyphs drawn one after another on the same baseline make one
|
||||
//! point text object of their Unicode text, a run per font, size and paint (a gap wider than a
|
||||
//! fifth of the size reads as a space). Fonts are named from the file's base font name (its
|
||||
//! fifth of the smaller size beside it reads as a space). Fonts are named from the file's base font name (its
|
||||
//! subset prefix dropped, the family matched against the fonts available); a font that isn't
|
||||
//! available keeps its name, and the text shows in the fallback font until it is.
|
||||
//!
|
||||
@@ -124,7 +124,8 @@ struct Gap {
|
||||
at: usize,
|
||||
/// From the end of the glyph before to the start of the next (points).
|
||||
width: f64,
|
||||
/// The larger of the two glyphs' sizes (points).
|
||||
/// The smaller of the two glyphs' sizes (points): a word gap beside a much larger glyph is
|
||||
/// narrow for the large size (#3623).
|
||||
em: f64,
|
||||
/// Along the line, not turned along a curve.
|
||||
on_line: bool,
|
||||
@@ -156,6 +157,8 @@ pub(crate) struct TextLine {
|
||||
visible: Point,
|
||||
/// The last glyph's baseline direction.
|
||||
last_dir: Vec2,
|
||||
/// The last glyph's size (points).
|
||||
last_size: f64,
|
||||
/// Each glyph's baseline origin, then where the last one ends.
|
||||
baseline: Vec<Point>,
|
||||
runs: Vec<(Look, String)>,
|
||||
@@ -207,6 +210,7 @@ impl TextLine {
|
||||
last: at.origin,
|
||||
visible: at.origin,
|
||||
last_dir: at.dir,
|
||||
last_size: at.size,
|
||||
baseline: vec![],
|
||||
runs: vec![],
|
||||
upright: None,
|
||||
@@ -297,7 +301,7 @@ impl TextLine {
|
||||
return false;
|
||||
}
|
||||
if !run.ends_with(' ') && !text.starts_with(' ') {
|
||||
self.gaps.push(Gap { at: bytes, width: gap, em: size, on_line });
|
||||
self.gaps.push(Gap { at: bytes, width: gap, em: self.last_size.min(at.size), on_line });
|
||||
}
|
||||
// A space the file draws between two glyphs spans the gaps on both sides of it.
|
||||
let drawn = self.drawn_space.take();
|
||||
@@ -322,6 +326,7 @@ impl TextLine {
|
||||
}
|
||||
self.last = at.origin;
|
||||
self.last_dir = at.dir;
|
||||
self.last_size = at.size;
|
||||
self.baseline.push(at.origin);
|
||||
self.advances.push(advance);
|
||||
true
|
||||
|
||||
@@ -164,6 +164,27 @@ fn evenly_wide_gaps_stay_spaces() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A word set beside a much larger one, apart by a gap and no space glyph (as a browser prints a
|
||||
/// margin between two sizes), reads as two words: the gap is measured against the smaller size
|
||||
/// (#3623). Set close, the two sizes stay one word.
|
||||
#[test]
|
||||
fn a_gap_between_two_sizes_is_measured_against_the_smaller() {
|
||||
let font = vectorcraft_text::FontDb::global().find_postscript("SourceSans3-Regular").unwrap();
|
||||
let advance = |c: char, size: f32| font.advance(font.glyph_for(c)) as f32 / font.units_per_em() as f32 * size;
|
||||
// 3.4 pt: 0.17 em of the 20 pt "4", 0.43 em of the 8 pt "hours".
|
||||
for (gap, want) in [(3.4_f32, "4 hours"), (0.4, "4hours")] {
|
||||
let lines = [(20.0, 60.0, 20.0, "4"), (20.0 + advance('4', 20.0) + gap, 60.0, 8.0, "hours")];
|
||||
let d = import_with_report(&text_pdf(&lines, false), &ImportOptions::default()).unwrap().document;
|
||||
let t = texts(&d);
|
||||
assert_eq!(t.len(), 1, "gap {gap} pt: {t:?}");
|
||||
assert_eq!(t[0].plain_text(), want, "gap {gap} pt");
|
||||
}
|
||||
// The small size first.
|
||||
let lines = [(20.0, 60.0, 8.0, "ca"), (20.0 + advance('c', 8.0) + advance('a', 8.0) + 3.4, 60.0, 20.0, "4")];
|
||||
let t = texts(&import_with_report(&text_pdf(&lines, false), &ImportOptions::default()).unwrap().document);
|
||||
assert_eq!(t.iter().map(TextObject::plain_text).collect::<Vec<_>>(), ["ca 4"]);
|
||||
}
|
||||
|
||||
/// Embedded font data is untrusted: anything that isn't a CID-keyed CFF is no table, never a panic.
|
||||
#[test]
|
||||
fn cid_tables_ignore_fonts_that_are_not_cid_keyed_cff() {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user