Commit Graph
691 Commits
Author SHA1 Message Date
BFlat 508c1b658e Merge pull request #7096 from dotCooCoo/feat/ai-save-envelopes
PDF/.ai save: envelopes stay editable when the file is reopened
2026-11-13 23:04:30 +07:00
BFlat 02445d5f02 Merge pull request #7130 from dotCooCoo/fix/eps-image-samples-once
EPS: an image drawn more than once writes its samples once
2026-11-13 23:00:45 +07:00
dotCooCoo 19ab993479 EPS: an image drawn more than once writes its samples once
An image placed several times in an EPS file (the pieces of an envelope
or a Warp effect, copies of one image, a symbol's instances) wrote its
whole ASCII85 data at every placement: 200 copies for one envelope at
the default fidelity, enough for a large photo to pass the 256 MB page
limit and lose pieces.

The samples of such an image are now written once in the setup as
VCim<i>, and each placement reads them from there:
- Level 3: a ReusableStreamDecode stream, rewound with resetfile and
  read through a new FlateDecode filter at each placement.
- Level 2: an array of strings of at most 49,152 compressed bytes, fed
  to RunLengthDecode by a procedure.

An image placed once is written where it is drawn, byte for byte as
before. The page limit counts a repeated image's samples once, and a
colorized image is reused per ink.
2026-11-13 05:38:06 -08:00
jmvergesandClaude Opus 5.5 728fd9ef14 Envelopes: a top object's side that is one split curve follows its parameter
A top-object envelope gives each piece of a side a share of the content
in proportion to its control polygon's length. When anchors split a
curve without changing its shape, the pieces' polygons don't divide the
side where the curve's own parameter does, so the envelope bent its
contents differently although its outline was the same: Add Anchor
Points on a top object moved the art, and a top object whose .ai mesh
saves each side's curve split into thirds drew its type up to about
half a point off its drawn art, which follows the whole curve's
parameter.

Coons::from_top_object now checks whether a side's pieces, from corner
to corner, are all pieces of one cubic. The pieces' spans of that
curve's parameter come from the handle lengths either side of each
split, the curve from the outer ends and handles, and every piece must
lie within 0.02 pt of the curve's piece over its span (each control
point). Such a side runs along that curve, by its parameter. Any other
side keeps the polygon shares, including a side only partly made of one
curve and a smooth side drawn in many pieces that isn't one curve as a
whole. Corners found from the outline can still move when anchors are
added, as before. Gradient meshes made from a path are unchanged.

Existing top-object envelopes with such sides redraw.
a_split_curve_moves_the_top_object_share_to_its_new_anchor, which
pinned the earlier behaviour, becomes a_split_curve_keeps_the_top_object_patch.

Tests: a_split_curve_keeps_the_top_object_patch,
a_top_object_joins_a_split_curve_saved_with_rounded_coordinates,
a_top_object_keeps_smoothly_joined_curves_apart,
a_top_object_side_partly_of_one_curve_keeps_the_polygon_shares and
a_top_object_side_of_many_arcs_stays_on_them (vectorcraft-doc),
envelope_top_object_keeps_its_look_when_anchors_are_added
(vectorcraft-engine) and
a_top_object_whose_top_is_one_curve_split_in_thirds_opens_live
(vectorcraft-eps).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 11:18:40 +00:00
dotCooCoo 737d9bc5b8 PDF/.ai save: envelopes stay editable when the file is reopened
A PDF-compatible .ai saved with Include Editing Data for Other Apps now
writes warp, mesh and top-object envelopes as live envelope records in
its editing copy: the grid or contents, Envelope Options, Fidelity,
Edit Contents, a warp's settings and the contents' box, beside the
distorted art, in the form the .ai envelope reader reads. The file
opens with the same live envelopes.

Envelopes the reader would open in another shape or with other
settings are written as their distorted art with a warning: type in
the contents, a turned frame, an old warp map, a mesh laid out another
way round, a top object whose sides do not pair up, a Fidelity that is
not a whole number, bounds margins kept with Distort Appearance off,
envelopes in opacity masks, pattern tiles, clipped art or another
envelope's contents, on a large canvas, and after the first 256.
2026-11-13 02:28:11 -08:00
BFlat d118fcb17c Merge pull request #7046 from dotCooCoo/fix/issue-3349
.ai import: graphic styles of one paint open with their fill and stroke
2026-11-13 16:04:36 +07:00
BFlat dd8446f3a4 Merge pull request #7054 from dotCooCoo/feat/nine-slice-guides-outside-art
Symbols: 9-slice guides may lie outside the symbol's art
2026-11-13 15:03:59 +07:00
BFlat 499903878e Merge pull request #7055 from dotCooCoo/feat/mojikumi-presets
Type: the Mojikumi Set menu offers the predefined spacing sets
2026-11-13 13:17:36 +07:00
dotCooCoo ecb994540c .ai import: a one-paint style's gradient paints the side its flag names
A gradient instance in a one-paint style marked both the fill and the
stroke as not read, so a style with a solid fill and a stroke gradient
was left out even when its `Xd` turns on only the fill. The instance's
flag (`1 Bb` for the stroke) now says which side the gradient paints, as
the object reader reads it; a gradient outside an instance still marks
both.
2026-11-12 21:36:18 -08:00
BFlat 6c3022d6e1 Merge pull request #7008 from jmverges/fix/6983-import-font-exact-postscript-name
.ai and EPS import: type takes the face of its exact PostScript name and version
2026-11-13 12:27:09 +07:00
dotCooCoo a1f6eaed77 .ai import: a one-paint style reads the last fill and stroke paint
A one-paint style took a gradient (`Bg`) after a solid stroke color as
the solid stroke, and left out a style whose solid fill color came after
a pattern or a gradient. Each later paint operator now replaces the one
before it on its own side, and a gradient marks both sides as not read,
since its operands do not say which one it paints.
2026-11-12 21:25:12 -08:00
dotCooCoo 7b6b6af7b1 Type: the Mojikumi Set menu offers the predefined spacing sets
The Paragraph panel's Mojikumi Set lists None, YakumonoHankaku,
GyomatsuYakumonoHankaku, GyomatsuYakumonoZenkaku and YakumonoZenkaku,
then Line-end Punctuation Half Width, and text.setFormat {mojikumi}
takes yakumonoHankaku, gyomatsuYakumonoHankaku, gyomatsuYakumonoZenkaku
and yakumonoZenkaku besides none and lineEndHalf.

The spacing of each set is VectorCraft's own rule, built on the help
page's one-line description of each set:
- YakumonoHankaku sets every bracket, comma and full stop half width,
  takes a quarter em off each side of a middle dot, and puts a quarter
  em between Japanese and Latin letters or digits.
- GyomatsuYakumonoHankaku keeps punctuation full width inside a line,
  sets an opening bracket starting a line flush and a closing mark
  ending it half width, and takes the quarter em after a middle dot
  ending a line off.
- GyomatsuYakumonoZenkaku keeps punctuation full width and adds the
  quarter em between Japanese and Latin text.
- YakumonoZenkaku sets characters as None does.

Both composers and vertical type measure each line as it is set. A
right-to-left paragraph, whose line ends are measured untrimmed, also
draws them untrimmed, and a right-to-left line that ends between a
Japanese and a Latin character is measured without the quarter em the
set adds there. With a Desired glyph scaling other than 100%, the
space a set takes off before an opening bracket or middle dot, and the
Japanese-Latin space recorded at a line's end, scale with the glyph.

The sets save with the document and paragraph styles; new type and
Reset Panel keep Line-end Punctuation Half Width. SVG export lays type
with middle dots out again for its positioned spans. A .ai save
reports a paragraph with one of the new sets as left out.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/type-parity.md,
docs/gaps.md.
2026-11-12 21:07:44 -08:00
dotCooCoo ec892d4218 .ai import: use American spelling in the one-paint style comments 2026-11-12 20:50:30 -08:00
dotCooCoo 26ab0f2523 Symbols: 9-slice guides may lie outside the symbol's art
A 9-slice guide can now be placed outside the symbol's art, by dragging
it past the art's edge in symbol editing mode, by symbol.options
nineSliceGuides, or by a Symbol Options field below 0% or above 100%.
A scaled instance then scales by the cells its art falls in.

- Each guide is a fraction from its edge in -16..16, and the two guides
  of an axis keep their order (left + right < 1, top + bottom < 1).
- A guide past an edge cuts the art at that edge, so that side's outer
  cell holds no art. With both guides of an axis past opposite edges,
  the art scales as a whole along it. With both past the same edge, the
  art keeps its size in the outer cell at the other edge.
- Symbol Options shows the guides with three decimals, and OK keeps a
  field that was not changed at its exact value.
- Native files keep the guides exactly. SVG and .ai write such an
  instance as its placed art, without guides.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/gaps.md.
2026-11-12 20:40:05 -08:00
dotCooCoo 29649a9d35 .ai import: graphic styles of one paint open with their fill and stroke
A style written as one paint (`/SimpleStyle` with its paint operators)
was read as a style with no fill and no stroke. A graphic style library
listed it, and applying it cleared the object's fill and stroke.

Such a style now reads as the fill and stroke its `stroke fill Xd`
turns on, with the stroke's width, caps, joins, miter limit and dashes,
and its `Xy` transparency. A style whose paint is not read here (a
gradient or pattern fill, a pattern stroke, no `Xd`) is left out of the
library instead of coming in empty.

Part of #3349.
2026-11-12 20:34:42 -08:00
BFlat f062dd7ed2 Merge pull request #6932 from dotCooCoo/feat/dcs-linked-spot-plates
Place: DCS files link with their spot plates as spot swatches and separate on their own inks
2026-11-13 11:27:13 +07:00
jmverges 2ac96a5ed5 Merge remote-tracking branch 'upstream/main' into fix/ai-import-font-match
# Conflicts:
#	docs/file-format-parity.md
2026-11-13 03:55:56 +00:00
BFlat e899f3fdb4 Merge pull request #6881 from dotCooCoo/feat/image-color-mask
Image Tools: Color Mask hides chosen colors of an image without changing its pixels
2026-11-13 10:27:04 +07:00
dotCooCoo f6d8af3ed6 Merge main into feat/dcs-linked-spot-plates 2026-11-12 19:09:51 -08:00
dotCooCoo 99f896f9b8 Place: DCS links follow their plate files, clips and document swatches
- The link watch and links.updateChanged look at a DCS file's plate files
  of their own, so a changed plate file is read again.
- links.update and links.relink apply the file's clipping path to the
  images in symbols, graph designs, patterns and masks too. When the new
  file has no clipping path, a clip group holding the old file's clipping
  path loses that path and stays as a plain group of the image, keeping
  its id, name, visibility and transparency. The image keeps that path with its plates to tell it from a
  clipping mask made in the document; a clipping path given another fill
  rule in the document stays. An even-odd clipping path in the file is
  kept as the same area under the nonzero rule.
- links.update and links.relink return the swatch warnings file.place
  gives, including two files that add one ink in two colors.
- links.relink with copy fails for a DCS file with plate files of its own.
- file.place answers the image id of a clipped DCS placement as image,
  and checks page and crop for a DCS file as it does for an EPS file.
- Print separations and Separations Preview take a spot plate's color
  from the document's swatch of its ink, and a reduced image's plates
  keep their coverage.
- Show DCS Transparency Interactions counts a transparent DCS image and
  characters filled or stroked with a transparent gradient.
- %%PlateFile names keep their spaces, and header bytes that are not
  UTF-8 are read as Latin-1.
- A plate program that draws more than one image, art besides its image,
  or its image through a clipping path is an error naming the plate
  instead of printing the wrong ink.
- Nested clipping paths around the composite clip the image together.
- A kept image with more plates than are read, or more TIFF directories
  than its composite and plates, is the CMYK image of its composite
  only, not a set of plates cut short.
- swatch.unused counts a spot or process swatch as used only while an
  image the art shows has a spot plate of its name.
2026-11-12 18:43:04 -08:00
jmverges e988ea6dfd Merge remote-tracking branch 'upstream/main' into fix/type-layout-envelopes
# Conflicts:
#	docs/file-format-parity.md
2026-11-13 01:58:54 +00:00
jmverges f94eecd439 Merge remote-tracking branch 'upstream/main' into fix/ai-import-font-match
# Conflicts:
#	docs/file-format-parity.md
2026-11-13 01:55:35 +00:00
dotCooCoo df155042d4 Merge main into feat/image-color-mask 2026-11-12 17:40:13 -08:00
BFlat 7430676ff0 Merge pull request #7000 from dotCooCoo/test/envelope-ink-type
Tests: envelope ink tests set type in a test face and check its outlines
2026-11-13 08:30:52 +07:00
jmvergesandClaude Opus 5.5 bb50d438c8 .ai import: type from the text document takes the face of its exact PostScript name and recorded version (#6983)
Type read from a text document's font record was set in whichever installed
face first answered to the record's PostScript name (ignoring case and
punctuation), and its family was then swapped for any available family
spelled alike but for case and punctuation (`FontDb::find_family`): a record
naming `SampleInk`, the PostScript name of `Sample/Ink`, set its type in
`SAMPLE INK`. Copies of one family and style under other PostScript names or
versions were not told apart either, so the type could set at other widths
than the file's own page.

- `named_font` / `document_font` (crates/eps/src/import/text.rs) give the
  family and style of the installed face of exactly that PostScript name,
  else one that differs only in case or punctuation, and name its version
  when the family and style alone resolve to another installed copy.
- The font record's key 5, its version string (name ID 5), is read and picks
  that version among the faces of the name. Each record is resolved once.
- `FontDb::postscript_faces` lists every installed face a PostScript name
  answers to, loading their families; the import prefers among them one of
  exactly that name.
- Type a PostScript program sets (EPS import) and legacy .ai type take the
  face of their font's name the same way.
- The .ai save writes a static font's version (`/5`) when its PostScript name
  alone would give the reader another installed copy (another version, or the
  same name in another family), keeps the version in the style it compares,
  and checks that the type comes back in the same family, style and face,
  taking the record's version as the reader does (up to 256 bytes), so such
  type round-trips without a note.

Measured on 224 users' files (used locally, never committed): 16 files set
some type in another face; 12 render differently against their own page,
10 closer and 2 further by 0.03% of the page; envelopes of type that open
live go from 154 to 159, none fewer in any file; no file opens another way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 00:33:13 +00:00
jmverges a8fc5275b6 Merge remote-tracking branch 'upstream/main' into fix/remaining-type-envelopes
# Conflicts:
#	docs/target-app-parity.md
2026-11-13 00:25:12 +00:00
dotCooCoo b1a35f8428 Tests: envelope ink tests set type in a test face and check its outlines
The three envelope ink tests set "Type" in the default family and
unwrapped ink_bounds(). Without bundled fonts, craft-fonts or system
fonts (vectorcraft-effects' tests on a bare Linux host), no face draws
and the effects test panicked at the unwrap.

Each test now adds the Heighttest Sans face built from the bundled
Source Sans 3 (test-fonts feature, now also on vectorcraft-effects'
dev-dependency), sets the text in it, and computes the expected box
from the layout's glyph outlines. It checks that envelope_ink gives
that box as well as the before and after registration boxes, so a
change to the registered measure fails the test.
2026-11-12 16:23:10 -08:00
BFlat 48d8e16444 Merge pull request #6941 from dotCooCoo/fix/ai-twist-curves
.ai import: Twist stays live on curves and groups with nothing stroked
2026-11-13 06:11:06 +07:00
jmverges 2c911f44f8 Merge remote-tracking branch 'upstream/main' into fix/remaining-type-envelopes
# Conflicts:
#	docs/target-app-parity.md
2026-11-12 22:55:36 +00:00
BFlat efaa9afb9a Merge pull request #6917 from dotCooCoo/fix/ai-look-stroke-warning
.ai save: a stroke the object's art style keeps no longer warns
2026-11-13 05:24:11 +07:00
BFlat 9861e7f927 Merge pull request #6920 from dotCooCoo/feat/export-notes-loss-first
Save and Export: notes of what the file loses come before notes on options
2026-11-13 05:23:53 +07:00
jmverges 4aef07a117 Merge remote-tracking branch 'upstream/main' into fix/type-layout-envelopes
# Conflicts:
#	crates/eps/src/import/ate.rs
#	crates/eps/src/import/tests_native.rs
#	docs/file-format-parity.md
2026-11-12 22:05:35 +00:00
jmverges 73fce692d9 Merge remote-tracking branch 'upstream/main' into fix/remaining-type-envelopes
# Conflicts:
#	docs/file-format-parity.md
2026-11-12 21:55:11 +00:00
dotCooCoo 312d4a3914 Merge main into feat/image-color-mask 2026-11-12 13:30:24 -08:00
BFlat da84e53b30 Merge pull request #6864 from jmverges/fix/6856-ai-type-pattern-paint
.ai import: type filled or stroked with a pattern keeps its pattern
2026-11-13 04:25:16 +07:00
dotCooCoo 18aed1da4e Merge main into feat/dcs-linked-spot-plates 2026-11-12 12:45:18 -08:00
dotCooCoo 15ffb4ec01 Merge main into feat/export-notes-loss-first 2026-11-12 12:45:09 -08:00
dotCooCoo b7793e839f Merge main into fix/ai-look-stroke-warning 2026-11-12 12:40:51 -08:00
BFlat bfd7b3dc00 Merge pull request #6944 from dotCooCoo/feat/bunri-kinshi
Type: Bunri-Kinshi keeps inseparable character pairs on one line
2026-11-13 03:28:08 +07:00
BFlat 653ecc8216 Merge pull request #6935 from dotCooCoo/fix/eps-linear-time-test-steps
EPS tests: the reader's long tests check counts and shared state instead of a 20 s limit
2026-11-13 03:27:59 +07:00
jmverges b783bb13ad Merge remote-tracking branch 'upstream/main' into fix/ai-type-pattern-paint
# Conflicts:
#	docs/file-format-parity.md
2026-11-12 20:19:19 +00:00
jmverges 36719e24f9 Merge remote-tracking branch 'upstream/main' into fix/type-layout-envelopes
# Conflicts:
#	docs/file-format-parity.md
2026-11-12 20:14:54 +00:00
jmverges 3ae2b6926a Merge remote-tracking branch 'upstream/main' into fix/remaining-type-envelopes
# Conflicts:
#	docs/file-format-parity.md
#	docs/target-app-parity.md
2026-11-12 20:11:07 +00:00
BFlat 41f384bffc Merge pull request #6921 from dotCooCoo/fix/ai-save-cmyk-equivalents-stable
.ai save: RGB and named colors keep their CMYK equivalents when a reopened file is saved
2026-11-13 02:47:37 +07:00
BFlat 86f8a6dcbd Merge pull request #6927 from dotCooCoo/fix/roughen-size-zero
Effects: Roughen at size 0 keeps the outline
2026-11-13 02:46:39 +07:00
BFlat d22d3ced48 Merge pull request #6931 from dotCooCoo/fix/eps-cshow-kshow-restore-font
EPS import: cshow and kshow procedures see the font PLRM gives them
2026-11-13 02:46:10 +07:00
BFlat e263a9c7d6 Merge pull request #6895 from makesomethingshit/fix/6865-ai-oversized-artboard
Fix #6865: Keep oversized artboards when opening editing data
2026-11-13 02:45:43 +07:00
dotCooCoo 40f4c920e7 Type: Bunri-Kinshi keeps inseparable character pairs on one line
Paragraph panel menu > Bunri-Kinshi (with the East Asian options) and
text.setFormat {bunriKinshi} turn it on per paragraph. A line then
never breaks inside two em dashes, two horizontal ellipses, two two-dot
leaders or the two halves of a vertical kana repeat mark, the pairs W3C
JLREQ lists for class cl-08 (https://www.w3.org/TR/jlreq/#cl-08), nor
inside a chain of em dashes. Both composers, vertical type, Kinsoku
Shori Type, hyphenation and No Break follow it, and a pair wider than
the line stays whole on it. Justification and letter spacing add no
space inside a pair.

The setting works only with a Kinsoku Set: with Kinsoku Set None the
paragraph keeps the value without effect and the menu item is
unavailable. It is off by default, saved with the paragraph only when
on, carried by paragraph styles (Paragraph Style Options > East Asian
Composition Settings), and turned off by Reset Panel. A value that is
not true or false is refused. The .ai editing data has no field for it,
so saving reports a paragraph with it on and a pair in it as left out.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-12 10:26:11 -08:00
dotCooCoo 5f5058f425 .ai import: Twist stays live on curves and groups with nothing stroked
Twist was read as a live effect only on an unstroked path or compound
path whose segments were all straight, and on those at any angle.

- A curved path or compound path, or a group, with nothing stroked and
  no guide in it now opens with its Twist live when the twist is at
  most 90° either way. Larger twists on these keep the file's drawn
  look. The 90° limit is VectorCraft's own.
- Paths and compound paths with straight segments and nothing stroked
  keep their Twist live at any angle, as before, guides included.
- Stroked objects keep the drawn look, as before.
- .ai save keeps a link to a Twist graphic style on the objects the
  reader reads it on, and leaves it out, with the style-link warning,
  on the others.

Docs: docs/file-format-parity.md, docs/target-app-parity.md.
2026-11-12 10:22:29 -08:00
jmverges d23972104b Merge remote-tracking branch 'upstream/main' into fix/ai-type-pattern-paint
# Conflicts:
#	docs/file-format-parity.md
2026-11-12 18:02:58 +00:00