diff --git a/ROADMAP.md b/ROADMAP.md index a16e5a51..73325f4d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -135,7 +135,7 @@ grade by behaviour against `plan/illustrator/`, not by whether a menu item exist - **Fonts:** a font is found by any of its names (family and style in every language of its name table, legacy family/style pairs, PostScript name), so documents from other apps open with the font they name; Find Font and `text.fonts` say for each font whether it was found exactly, by another name, substituted or missing, and how many glyphs are missing. - **Files (M4):** - `.vectorcraft` (lossless JSON, compressed, atomic saves, save down to v1), Save As with a format chooser, Save a Copy, Revert, templates, Data Recovery after a crash, background save and export. - - SVG/SVGZ in and out with SVG Options, Preserve Editing, symbols, filters, rich text and physical units; PDF and PDF-compatible `.ai` in and out (layers, masks, editable text, spot colours, presets, security, marks and bleed, ICC output intents, raster effects, subset fonts, PDF/X, PDF layers, overprint, page thumbnails, fast web view, PDF 1.3). + - SVG/SVGZ in and out with SVG Options, Preserve Editing, symbols, filters, rich text and physical units; PDF and PDF-compatible `.ai` in and out (layers, masks, editable text with type set along a curve coming back as type on a path, spot colours, presets, security, marks and bleed, ICC output intents, raster effects, subset fonts, PDF/X, PDF layers, overprint, page thumbnails, fast web view, PDF 1.3). - EPS and DXF in and out, EMF/WMF in and out; PNG (with PNG-8), JPEG, WebP, GIF, TIFF, BMP, Targa and layered PSD export. EPS import runs Illustrator's AGM/CoolType prologs (resource categories, `resourceforall`, `clipsave`/`cliprestore`, subarrays and substrings that share storage) and falls back to its palette TIFF preview. DXF TEXT justified Fit or Aligned spans its two points (stretched, or scaled as a whole). - Place and the Links panel, Package, File Info, Print with print presets, PostScript output and print tiling, slices and Save for Web, Export for Screens and Asset Export, CSS Properties, PNG/PDF/SVG/text clipboard flavours. - **Performance:** 20k shapes + 1k texts render in 27 ms per full-retina frame (7.8 ms zoomed), 7× faster than the first version. The UI thread never blocks. The web build is 7.1 MB gzipped. diff --git a/crates/pdf/src/import_text.rs b/crates/pdf/src/import_text.rs index 0e2d478e..5b0110ce 100644 --- a/crates/pdf/src/import_text.rs +++ b/crates/pdf/src/import_text.rs @@ -156,13 +156,17 @@ impl TextLine { self.last_dir.dot(self.at.dir) < 0.9986 } - /// A smooth path through the glyphs' baseline origins and the end of the last glyph - /// (Catmull-Rom through the points, as cubic Béziers). + /// A smooth path through the glyphs' baseline origins and the end of the last glyph, an em + /// further (Catmull-Rom through the points, as cubic Béziers). fn baseline_path(&self) -> Option { let mut pts = self.baseline.clone(); + // On past the end of the last glyph by an em, so rounding in the spacing doesn't push it + // off the end of the path (where type on a path stops). pts.push(self.next); + pts.push(self.next + self.last_dir * self.at.size); let first = *pts.first()?; - if pts.len() < 3 { + // Finite placements far out can still overflow once an em is added: no path then. + if pts.len() < 3 || !pts.iter().all(|p| p.is_finite()) { return None; } let mut bp = BezPath::new(); diff --git a/crates/pdf/src/tests_import_fidelity.rs b/crates/pdf/src/tests_import_fidelity.rs index 6b7120ec..7b5ec3bd 100644 --- a/crates/pdf/src/tests_import_fidelity.rs +++ b/crates/pdf/src/tests_import_fidelity.rs @@ -383,6 +383,8 @@ fn glyphs_turned_along_a_curve_become_type_on_a_path() { let vectorcraft_doc::TextKind::OnPath { path, .. } = &t[0].kind else { panic!("type on a path: {:?}", t[0].kind) }; let b = path.bounds().unwrap(); assert!(b.height() > 15.0, "the path curves: {b:?}"); + // Every glyph fits on the path, the last one too. + assert_eq!(vectorcraft_text::layout(vectorcraft_text::FontDb::global(), &t[0]).glyphs.len(), 7); // Straight type with a slight rotation stays one point type object. let straight = texts(&open(&one_page("BT /F1 18 Tf 0.98481 0.17365 -0.17365 0.98481 20 40 Tm (Straight) Tj ET", HELVETICA, &[]))); assert_eq!(straight.len(), 1);