mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 00:12:24 +00:00
EPS/.ai editing data: a ligature glyph named by a private-use character matches its text object
When the page's type is matched with the text objects, page words are also split at private-use characters (from uniXXXX ligature glyph names such as /uniE028 for a "ti" ligature) and each piece is matched as its own word, so "Quality" set with that ligature matches its text object and the layers are read without a false note.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use super::layers::closed_where_ends_meet;
|
||||
use super::slots::{
|
||||
Content, MAX_PAIRS, OUTLINE_REACH, PAGE_TYPE_LEFT_OUT, PAGE_TYPE_UNCHECKED, SlotInfo, assign, covered, extent, join_pieces, letters, same_type,
|
||||
words,
|
||||
word_pieces, words,
|
||||
};
|
||||
use super::{Affine, Arc, Node, NodeId, NodeKind, Point, Rect, Story, TextKind};
|
||||
|
||||
@@ -283,9 +283,10 @@ fn plain_text(n: &Node) -> String {
|
||||
if let NodeKind::Text(t) = &n.kind { t.plain_text() } else { String::new() }
|
||||
}
|
||||
|
||||
/// The words of the page's text object `o`, each as its [`spelling`], the empty ones left out.
|
||||
/// The words of the page's text object `o` (see [`word_pieces`]), each as its [`spelling`], the
|
||||
/// empty ones left out.
|
||||
fn object_words(o: &[Arc<Node>]) -> Vec<String> {
|
||||
o.iter().flat_map(|n| plain_text(n).split_whitespace().map(spelling).collect::<Vec<_>>()).filter(|w| !w.is_empty()).collect()
|
||||
o.iter().flat_map(|n| word_pieces(&plain_text(n)).map(spelling).collect::<Vec<_>>()).filter(|w| !w.is_empty()).collect()
|
||||
}
|
||||
|
||||
/// Each slot's made type as its content, else nothing yet. Made type that shows has where it draws,
|
||||
|
||||
@@ -446,7 +446,21 @@ pub(super) fn letters(s: &str) -> String {
|
||||
/// the page draws a right-to-left paragraph's words in the order they show (" 120.5 mm " as
|
||||
/// "mm 120.5"), its text object keeps them in the order they are typed.
|
||||
pub(super) fn words(s: &str) -> Vec<String> {
|
||||
s.split_whitespace().map(letters).filter(|w| !w.is_empty()).collect()
|
||||
word_pieces(s).map(letters).filter(|w| !w.is_empty()).collect()
|
||||
}
|
||||
|
||||
/// The words of `s`, split at spaces and at private-use characters too. The page's type names a
|
||||
/// font's ligature glyph that has no character of its own (`uniE028`, the Adobe Glyph List
|
||||
/// specification's `uniXXXX` names: <https://github.com/adobe-type-tools/agl-specification>) by a
|
||||
/// private-use character, where its text object has the letters ("Qual\u{E028}y" for
|
||||
/// "Quality"): what is around it is compared, each piece as a word.
|
||||
pub(super) fn word_pieces(s: &str) -> impl Iterator<Item = &str> {
|
||||
s.split(|c: char| c.is_whitespace() || private_use(c)).filter(|w| !w.is_empty())
|
||||
}
|
||||
|
||||
/// Is `c` in one of Unicode's private use areas (Unicode Standard §23.5)?
|
||||
fn private_use(c: char) -> bool {
|
||||
matches!(u32::from(c), 0xE000..=0xF8FF | 0xF0000..=0xFFFFD | 0x100000..=0x10FFFD)
|
||||
}
|
||||
|
||||
/// Does `n` hold type that is where `story` lays one of its lines out: the line, or a piece of it
|
||||
|
||||
@@ -4661,6 +4661,22 @@ fn a_small_label_of_the_page_in_no_text_object_is_noted() {
|
||||
assert!(note.is_some_and(|w| crate::is_loss(w)), "{:?}", r.warnings);
|
||||
}
|
||||
|
||||
/// A ligature the page shows by a glyph its font's encoding names `uniE028` (a private-use
|
||||
/// character, without the letters it stands for) is the letters of its text object: the page's type
|
||||
/// has one, and nothing is noted as left out.
|
||||
#[test]
|
||||
fn a_ligature_glyph_without_letters_is_matched_to_its_text_object() {
|
||||
let doc = text_document(&[("Quality here\r", 0, (8200.0, 8180.0), (0.0, 0.0), 0.0)]);
|
||||
let art = native_with_text(&layer("Art", true, &text_object(0)), &doc);
|
||||
let page = "0 0 0 1 setcmykcolor /Helvetica findfont dup length dict copy \
|
||||
dup /Encoding StandardEncoding 256 array copy dup 127 /uniE028 put put /Lig exch definefont \
|
||||
14 scalefont setfont 58.5 61.5 moveto (Qual\\177y here) show";
|
||||
let r = import(&eps(page, &art)).unwrap();
|
||||
assert_eq!(names(&r.document), ["Art"], "{:?}", r.warnings);
|
||||
assert!(!r.warnings.iter().any(|w| w.contains("so it is left out")), "{:?}", r.warnings);
|
||||
assert_eq!(text_of(&r.document.layers[0]).iter().map(|t| t.plain_text()).collect::<Vec<_>>(), ["Quality here"], "{:?}", r.warnings);
|
||||
}
|
||||
|
||||
/// A line break inside a paragraph (`\x03` in the text document) stays a line break in it.
|
||||
#[test]
|
||||
fn a_soft_return_is_a_line_break_in_its_paragraph() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# File format parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-07 · **Change:** minor (EPS import: CIDFonts only named, over a Unicode CMap, set as live type; EPS import: CMap mappings to base-font codes and glyph names; EPS import: CID-keyed fonts in FontSets (CFF) draw their glyphs; EPS import: `kshow` procedures kern live type; EPS import: embedded Type 1 fonts' glyphs and widths; EPS import: CID-keyed fonts draw their glyphs; EPS import: Type 42 fonts' characters and widths from the font they carry, show operators' spacing kept as tracking; EPS import: an image decoder's own buffers stop counting against the memory budget once it is decoded; EPS import: sampled functions interpolate between their samples; `.ai` and EPS type from the text document keeps its tab stops; it keeps auto leading and hyphenation options, and may show more of the file's last lines in an area frame; `.ai` and EPS import: Art brushes in the older form of their objects come into the Brushes panel; EPS import: gradients drawn as abutting strips come in as one gradient, without seams; EPS import: composite fonts draw their Type 42 glyphs; SVG import: filter and mask regions clip, #5876, #5875; SVG import: Gaussian blurs keep `color-interpolation-filters`, #5877; PDF and `.ai` export: objects' URLs as links, #5785; SVG import: `color-interpolation="linearRGB"` gradients, #5878; `.ai` import: paths with a Calligraphic brush open brushed, #372) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-07 · **Change:** minor (EPS and `.ai` editing data: a ligature the page names by a private-use glyph matches its text object; EPS import: CIDFonts only named, over a Unicode CMap, set as live type; EPS import: CMap mappings to base-font codes and glyph names; EPS import: CID-keyed fonts in FontSets (CFF) draw their glyphs; EPS import: `kshow` procedures kern live type; EPS import: embedded Type 1 fonts' glyphs and widths; EPS import: CID-keyed fonts draw their glyphs; EPS import: Type 42 fonts' characters and widths from the font they carry, show operators' spacing kept as tracking; EPS import: an image decoder's own buffers stop counting against the memory budget once it is decoded; EPS import: sampled functions interpolate between their samples; `.ai` and EPS type from the text document keeps its tab stops; it keeps auto leading and hyphenation options, and may show more of the file's last lines in an area frame; `.ai` and EPS import: Art brushes in the older form of their objects come into the Brushes panel; EPS import: gradients drawn as abutting strips come in as one gradient, without seams; EPS import: composite fonts draw their Type 42 glyphs; SVG import: filter and mask regions clip, #5876, #5875; SVG import: Gaussian blurs keep `color-interpolation-filters`, #5877; PDF and `.ai` export: objects' URLs as links, #5785; SVG import: `color-interpolation="linearRGB"` gradients, #5878; `.ai` import: paths with a Calligraphic brush open brushed, #372) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
Every format Illustrator opens, places, saves or exports, against VectorCraft's format table (`FORMATS`,
|
||||
`OPEN_EXTS`, `PLACE_EXTS` in `crates/engine/src/cmd/fileio/formats.rs`). Illustrator's list comes from its public
|
||||
@@ -320,6 +320,7 @@ Names in the editing data that aren't UTF-8 (layers, objects, artboards, colors,
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-07 | minor | EPS and `.ai` editing data: when the page's type is matched with the text objects, a glyph the page's font encoding names with a private-use character (a ligature such as `uniE028`, which says nothing of the letters it stands for) splits the page's word there, and the pieces are matched as words. A ligature in "Quality" no longer reads "Qualy" against the text object: four users' Illustrator 30 EPS files (used locally, not committed) lose the false "some of the page's type has no text object" note, their renders unchanged |
|
||||
| 2026-11-07 | minor | EPS import: a CID-keyed font over a predefined Unicode CMap the file doesn't define (Adobe's public CMap resources named `Uni…-UCS2`, `-UTF8`, `-UTF16` or `-UTF32`, `-H` or `-V`: [cmap-resources](https://github.com/adobe-type-tools/cmap-resources)), found by its `CIDFont-CMap` name (`/Ryumin-Light-UniJIS-UCS2-H findfont`, the usual name of a CID-keyed font: its CIDFont's and CMap's names joined by a hyphen) or made with `composefont`, sets its codes' characters as live type in the font of its CIDFont's name, and `stringwidth` measures them. Such type was read as Latin-1 codes, or stopped the program at `composefont`. Vertical CMaps are set horizontally, and the show operators' extra spacing isn't kept; CIDFonts over other CMaps the file doesn't carry still draw nothing, with a warning. |
|
||||
| 2026-11-07 | minor | EPS import: CMap mappings to base fonts (`beginbfchar` … `endbfchar`, `beginbfrange` … `endbfrange` after `usefont`: [Adobe Technical Note #5099](https://adobe-type-tools.github.io/font-tech-notes/pdfs/5099.CMapResources.pdf); PLRM 3rd ed., §5.11.4) are read instead of dropped: a code mapped to a character code (a range's codes counting up from its last byte) or to a glyph name (one, or an array of them) draws that glyph of the base font it names, a Type 3, Type 42 or embedded Type 1 font's own glyph or, in a font found by name, its character as type. Such codes took font 0's CID 0 and drew nothing. |
|
||||
| 2026-11-07 | minor | EPS import: a FontSet's Compact Font Format data (`/name n StartData` in the FontSetInit procedure set: PLRM 3rd ed., §5.8.1; [Adobe Technical Note #5176](https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf)) is read instead of skipped: each CID-keyed font in it (a top DICT with `ROS`) is defined as a CIDFont of its name, and type in a CID-keyed font over it draws its glyphs, picked by CID through the charset and drawn from their Type 2 charstrings with their FDSelect subfont's subroutines and widths. Name-keyed fonts in a FontSet are still set as type by name. |
|
||||
|
||||
Reference in New Issue
Block a user