From 0c20d09051fd99e6f7ee36603785aa5352dd7baa Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 11:12:23 -0800 Subject: [PATCH] Legacy .ai save: manual kerning of 0 is written as Tk A pair with manual kerning of 0 was saved with no Tk and opened with automatic kerning. It is now written as 0 0 Tk and reads back as manual kerning of 0. --- crates/eps/src/legacy_ai/tests.rs | 21 +++++++++++++++++++++ crates/eps/src/legacy_ai/writer.rs | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/crates/eps/src/legacy_ai/tests.rs b/crates/eps/src/legacy_ai/tests.rs index c6e3c05435..7cec7c50f3 100644 --- a/crates/eps/src/legacy_ai/tests.rs +++ b/crates/eps/src/legacy_ai/tests.rs @@ -1527,6 +1527,27 @@ fn manual_kerning_and_soft_hyphens_are_written_and_read_back() { ); } +/// Manual kerning of 0 is written as `0 0 Tk` and reads back as manual kerning, not as the +/// automatic kerning of a pair with no `Tk`. +#[test] +fn manual_kerning_of_zero_is_written_and_read_back() { + use vectorcraft_doc::{CharStyle, TextObject, TextRun}; + let plain = style(12.0, Paint::solid(Color::BLACK)); + let zero = CharStyle { kerning: Some(0.0), ..plain.clone() }; + let mut t = TextObject::point(vectorcraft_geom::Point::new(20.0, 40.0), "AV", zero); + t.runs.push(TextRun::new("To", plain)); + let mut doc = Document::new(300.0, 200.0); + let layer = doc.layers[0].id; + let n = Node::new(doc.alloc_id(), NodeKind::Text(Box::new(t))); + doc.insert(Some(layer), usize::MAX, n).unwrap(); + let out = write(&doc); + let text = String::from_utf8(out.bytes.clone()).unwrap(); + assert!(text.contains("(A) Tx\n0 0 Tk\n(V) Tx\n0 0 Tk\n"), "{text}"); + let read = texts(&crate::import(&out.bytes).unwrap().document); + let runs: Vec<(String, Option)> = read[0].1.runs.iter().map(|r| (r.text.clone(), r.style.kerning)).collect(); + assert_eq!(runs, [("AV".to_string(), Some(0.0)), ("To".to_string(), None)]); +} + /// Type whose font size measures the cap height is written at the em size it is drawn at, with /// no warning: 14 pt measuring a 0.7 em cap height reads back as 20 pt type. #[test] diff --git a/crates/eps/src/legacy_ai/writer.rs b/crates/eps/src/legacy_ai/writer.rs index 5f749e15c8..19f8ce654f 100644 --- a/crates/eps/src/legacy_ai/writer.rs +++ b/crates/eps/src/legacy_ai/writer.rs @@ -853,9 +853,9 @@ impl<'a> Writer<'a> { self.kern(run.style.kerning_before); } line_start = false; - // Manual kerning after each character, a soft hyphen as a discretionary hyphen - // (§15.4.10.1). - let kerning = run.style.kerning.filter(|k| *k != 0.0); + // Manual kerning after each character (0 included, which differs from automatic + // kerning), a soft hyphen as a discretionary hyphen (§15.4.10.1). + let kerning = run.style.kerning; let mut bytes = vec![]; for c in piece.chars() { if c == '\u{ad}' {