Commit Graph
486 Commits
Author SHA1 Message Date
BFlat ab959b95a8 Merge pull request #7369 from dotCooCoo/fix/ai-hidden-ruler-guide
.ai import: hidden ruler guides open as ruler guides with their own state
2026-11-14 13:43:11 +07:00
BFlat ea24f694cf Merge pull request #7345 from dotCooCoo/fix/ai-twist-straight-guide-reach
.ai import: straight compound paths whose guides reach past them keep the drawn look under Twist
2026-11-14 09:32:51 +07:00
dotCooCoo 1e4b6c4e22 Merge main into fix/ai-hidden-ruler-guide 2026-11-13 17:56:13 -08:00
dotCooCoo cfa76d674f .ai import: hidden ruler guides open as ruler guides with their own state
A straight `(N) *` guide path that runs across the canvas or an artboard
opens as a ruler guide, but a hidden one (`1 Xw`) stayed a guide path.
It now opens as a ruler guide too, and every ruler guide made from such a
path keeps the path's hide, lock and name, so its Layers row shows them.
Before, a locked guide opened unlocked and a named one lost its name.

Hidden or locked guide paths that are short, slanted, grouped or `(S) *`
stay guide paths, and commented copies (`%_`) are still left out.

Docs: docs/target-app-parity.md, docs/file-format-parity.md.
2026-11-13 17:55:31 -08:00
BFlat bf3cf422e2 Merge pull request #7311 from dotCooCoo/fix/ai-graphs-keep-data
PDF/.ai import: graphs keep their data and settings when their records read whole
2026-11-14 08:39:57 +07:00
dotCooCoo be8d614b0c .ai import: straight compound paths whose guides reach past them keep the drawn look under Twist
A straight compound path with Twist was read live up to 180° whatever
guides it held, and the live Twist was measured from bounds that take in
the guide members. A straight compound path with Twist whose guide
members reach past its other members now opens as its drawn look
instead of live, and so does a compound path made only of guides.
Compound paths whose guides lie inside their other members, a Twist of
0°, and single paths keep opening live.

The `.ai` writer keeps a graphic-style link only where the reader reads
the style live, so such an object is now written without the link, with
the existing style-link warning.
2026-11-13 16:35:37 -08:00
BFlat f14aa09bbf Merge pull request #7226 from dotCooCoo/fix/legacy-ai-type-kerning
PDF/.ai import: legacy type keeps manual kerning, discretionary hyphens and language
2026-11-14 07:34:10 +07:00
dotCooCoo 11ecbe4e9a Merge main into fix/legacy-ai-type-kerning 2026-11-13 15:00:02 -08:00
dotCooCoo aa5d142bd8 .ai save: the editing copy keeps text wrap Offset and Invert Wrap
A PDF-compatible .ai saved with Include Editing Data for Other Apps
wrote no text wrap options, so a wrap object opened from the file no
longer wrapped type. An object with only wrap options got no
dictionary at all.

The writer now writes each wrap object's options in its dictionary as
`AI11TextWrapOffset` (`/Real`) and `AI11TextWrapInvert` (`/Bool`, 1 or
0), the entries `text_wrap` in crates/eps/src/import/ai/read/document.rs
reads, beside the object's name, variables and opacity mask.

- The offset is the one in the art's space, so on a large canvas it is
  written at 1/10 and read back at its size. An offset that is not
  finite is written as the default 6 pt, and one past the -1000 to
  1000 pt the reader keeps as the nearer end of that range.
- Art the writer draws for an object (a blend's steps, a symbol
  instance written as its art, a brushed stroke's pieces, pattern
  tiles, an object's drawn look) is written without the options of the
  objects it is made from. The object itself and a symbol's own art
  keep theirs.
- A clipping path written as its fills and its strokes has its options
  once.

Docs: docs/file-format-parity.md, docs/target-app-parity.md.
2026-11-13 14:48:16 -08:00
dotCooCoo 2fc344cb8c PDF/.ai import: graphs keep their data and settings when their records read whole
A legacy .ai graph (%_Gs ... %_GS) opened as plain art, with its table,
type, axes and settings left out. A grouped column, stacked column, line
or area graph now keeps them on its group when every record has the
shape and values the version 7 specification's appendix A gives, the
table holds at least one number, the drawn art strokes the plot's left
and bottom edges, and each series group is tagged once as a whole
column, holds only paths and paints them with one fill and one stroke.
The saved art stays as it is on open; Graph Data redraws the graph from
its data afterward, with each series' fill and stroke kept.

Any other graph (pie and scatter graphs, designs, customizations, record
shapes of later versions, tables past 10,000 categories, 256 series or
2,600,000 cells) keeps its art as plain objects with the existing note.

Saving a graph to .ai, in either form, now warns that its data and
settings are not written.
2026-11-13 14:15:19 -08:00
dotCooCoo 69deae55cf .ai import: straight paths twisted past 180° keep the drawn look
A path or compound path with straight segments and nothing stroked was
read with its Twist live at any angle. It is now read live up to 180°
either way, a limit VectorCraft sets, and one twisted further keeps the
file's drawn look, as curves and groups already do past 90°.

The .ai writer keeps a graphic-style link only where the reader reads the
style live, so a straight object twisted past 180° is now written without
the link, with the existing style-link warning.

Docs: docs/file-format-parity.md, docs/target-app-parity.md.
2026-11-13 11:21:08 -08:00
dotCooCoo 78d3f6e1a5 PDF/.ai import: legacy type keeps manual kerning, discretionary hyphens and language
Legacy .ai type (version 7) now reads the operators it used to skip:

- `autoKern kernValue Tk` (and `TK` in overflow text) sets the manual
  kerning of the character before the pair, or at the start of a line
  the space before the line's first character; autoKern 1 sets the
  pair back to automatic. Values are kept to -1000..10000.
- `T+` and `T-` (discretionary hyphens) become soft hyphens.
- `languageID XL` sets the characters' language (ids 0 to 11).
- A visible text object read while a line breaking value (`TG`) or
  Japanese layout rules (`Xu`, `Tu`) are set opens with a warning,
  once per file for each.

Final-form positions (Tc, Tw, Tm, Td, T*, TR) stay ignored.

Legacy .ai saves now write manual kerning as `0 value Tk` after each
character that has it and before a paragraph's first character with
space before it, and soft hyphens as `T+`; manual kerning is no longer
listed among the attributes left out. The writer's prolog defines Tk
and T+.

Docs: docs/file-format-parity.md, docs/target-app-parity.md.
2026-11-13 10:12:57 -08:00
jmverges 24bcdb5116 Merge remote-tracking branch 'upstream/main' into fix/page-check-typed-envelopes
# Conflicts:
#	crates/eps/src/import/ai.rs
#	docs/file-format-parity.md
#	docs/target-app-parity.md
2026-11-13 16:22:41 +00:00
jmvergesandClaude Opus 5.5 67d947a3c6 .ai and EPS import: weigh each placement of placed files on its own for envelopes of type
Two findings in the review of #7146, in how layers whose differing
objects come in as the page's art treat envelopes of type:

- The envelopes of type the page holds against the layers were weighed
  on the layers as read only, and that choice was also applied at the
  placed files' other placement, which can move a placed file over or
  under an envelope. Each placement is now weighed on its own. The
  places of those left live, for layers that open alone, still come
  from the layers as read: a .ai that opens alone has no other
  placement.
- A placement that draws as the page with only the held envelopes put
  back was dropped, as nothing in it differs for its objects to come in
  as the page's art. It is now used as it is, as layers that draw as the
  page with every live envelope's drawn art put back already are. The
  try with every envelope of type put back keeps its order: objects
  that differ come in as the page's art, as before.

Part of #7141.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 14:53:39 +00:00
jmvergesandClaude Opus 5.5 05f38616e0 .ai and EPS import: envelopes of type stay live where other art differs from the page
When the layers with their live envelopes didn't draw as the page, and
didn't with the drawn art of every live envelope put back either, every
envelope of type got its drawn art back wherever the layers were still
used: the editing data trusted, the objects that differ coming in as
the page's art, or a .ai whose layers would open alone opening alone.
Art that differed anywhere on the page cost every envelope of type its
editable type, though putting its drawn art back changed nothing.

Now each envelope of type is weighed in the page check's own drawing,
on the pixels that it and its drawn art ink (the two drawn together on
a layer of their own, without the rest of the layers). It keeps its
drawn art and hidden type where the layers with it differ from the page
in more of those pixels than its own check allows against its drawn art
(10%, or 3% with all their neighbours), or where the envelopes that stay
live would together differ there in more of a page than half of what
the page check allows (a budget of its own, the same size as the one
their own checks share). Art differing on its pixels still holds it.

It also keeps its drawn art, as before, where the layers don't draw it
where it is and as itself, on the page or off it: under a clip or an
opacity mask, in a group with an appearance of its own, or in another
envelope, a repeat or a blend. Another envelope on no page stays live,
except on a page that holds all the art (an EPS's art box), where one
reaching past the page keeps its drawn art. Past 12 on the pages, all
keep it.

A .ai whose layers would open alone still opens alone: its envelopes of
type are settled again as they were with the page, in the same order
and with the same checks, so each that opens live draws as it was
weighed, and those the page held get their drawn art back. Where the
differing objects are to come in as the page's art and can't with
envelopes of type live (an object that holds type isn't replaced),
every envelope of type gets its drawn art back and that is tried again,
as before. Where putting back the drawn art of every live envelope
makes the layers draw as the page, it is still put back.

On 224 of our own files (used locally), envelopes of type that open
live go from 174 to 186 in 8 files, none fewer; no file changes between
its layers and its PDF part.

Part of #3179.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 14:04:38 +00: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
BFlat d5699579de Merge pull request #7017 from mockydevs/fix/pdf-empty-actualtext-art-6971
PDF import: glyphs in a span with an empty ActualText come in as outlines
2026-11-13 14:16:32 +07:00
BFlat 53ff632a26 Merge pull request #7042 from dotCooCoo/feat/pict-open-place
File formats: PICT pictures open and place
2026-11-13 13:18:26 +07:00
Don Omondi 74deb4aade Merge remote-tracking branch 'upstream/main' into fix/pdf-empty-actualtext-art-6971
# Conflicts:
#	docs/file-format-parity.md
#	docs/roadmap.md
#	docs/target-app-parity.md
2026-11-13 00:46:18 -05: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 a992a14997 Merge main into feat/pict-open-place 2026-11-12 20:41:24 -08:00
Don Omondi b0cc3eb13a Merge remote-tracking branch 'upstream/main' into fix/pdf-empty-actualtext-art-6971 2026-11-12 23:29:01 -05: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
dotCooCoo 71da62dab2 File formats: PICT pictures open and place
File > Open and File > Place read QuickDraw pictures, versions 1 and 2,
from .pct, .pic and .pict files, with or without the file's 512 bytes
of application data; a version 2 picture also opens by its content.
The reader plays the opcodes of Apple's Opcodes in Pictures table on
the metafile player: lines, rectangles, rounded rectangles, ovals,
arcs, polygons and regions become paths, text becomes point type in
the font the picture names, bitmaps, pixel maps and QuickTime JPEG
images become images, and the clip region clips what follows it.
Place puts the picture in one group clipped to its frame. What isn't
read comes back as warnings, and a picture cut short keeps what came
before it. PICT is not written.

Images whose transform flattens them no longer leave their pixel data
in the document. Formats whose signature starts the file are detected
before PICT, which is known by bytes 522 or 10.

Docs: docs/mcp.md, docs/file-format-parity.md, docs/target-app-parity.md.
2026-11-12 19:52:06 -08:00
Don Omondi 8ba280ec44 Merge remote-tracking branch 'upstream/main' into fix/pdf-empty-actualtext-art-6971
# Conflicts:
#	docs/file-format-parity.md
#	docs/roadmap.md
#	docs/target-app-parity.md
2026-11-12 22:40:11 -05:00
dotCooCoo f6d8af3ed6 Merge main into feat/dcs-linked-spot-plates 2026-11-12 19:09:51 -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
Don Omondi b6f2277bee Merge remote-tracking branch 'upstream/main' into fix/pdf-empty-actualtext-art-6971
# Conflicts:
#	docs/file-format-parity.md
2026-11-12 20:39:33 -05: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
Don Omondi d5f0fe3a3f PDF import: glyphs in a span with an empty ActualText come in as outlines (#6971) 2026-11-12 19:15:40 -05: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 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
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
BFlat f4767e89ff Merge pull request #6945 from dotCooCoo/feat/svg-variable-data
SVG: Save As SVG can include the document's variables and data sets
2026-11-13 03:28:53 +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 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 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
dotCooCoo 1132cf0c79 SVG: Save As SVG can include the document's variables and data sets
SVG Options gets an Include Variable Data checkbox (the `variableData`
option, off by default). With it on, the saved SVG carries the document's
text variables, its data sets' text values and each text variable's bound
type object as a `variableSets` element, in the layout Load Variable
Library already reads. A bound object is named by its element id in a
`docRef="id('...')"` attribute; an object without an id gets one.

Opening such an SVG reads the variable data back: the text variables, the
data sets (none active) and the bindings, so choosing a data set changes
the type again. Variable data that Load Variable Library would refuse is
left out with a warning, and the art opens. Markup that only looks like
the element (in comments, CDATA sections, processing instructions or the
document type declaration) is skipped.

Variables of other kinds, names XML can't hold, more than 256 text
variables or 1,000 data sets, and variable data over 8 MiB or 2,000,000
XML nodes are left out of the file, each with a warning. Type written as
outlines opens as art, so its variables are written unbound, with a
warning.
2026-11-12 10:18:22 -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
jmverges e2827d20c1 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 18:02:53 +00:00
BFlat 8c44d414cd Merge pull request #6922 from dotCooCoo/fix/file-format-editing-data-paragraph
Docs: the file-format doc's editing-copy paragraph and summary rows appear once
2026-11-13 00:50:05 +07:00
dotCooCoo 7dd161cc49 Place: DCS files link with their spot plates as spot swatches and separate on their own inks
File › Place with Link reads a DCS 1.0 or 2.0 file (an EPS file whose
header names one EPS program per printing plate) as one linked image of
its plates. Each spot plate adds a spot swatch in the CMYK values the
file gives, and the clipping path the file's composite is drawn through
clips the image. document.open, file.place without link or from bytes,
and links.embed refuse a DCS file and change nothing.

- The screen, composite print and exports show the plates mixed into
  CMYK. Separations Preview and print separations show each plate on
  its own ink; a spot plate prints as its CMYK values when spots print
  as process.
- links.list answers the format DCS, and with dcsTransparency marks the
  DCS images that are transparent or overlap transparent objects (the
  Links panel menu's Show DCS Transparency Interactions).
- swatch.unused leaves out a spot swatch while a DCS image has a plate
  of its ink.
- links.update and links.relink read the plates and the clipping path
  again, and the link shows as modified when one of its plate files
  changes or is gone.
- The plates go on the first plate's pixel grid, grown to hold every
  plate where it prints; a plate of another size or position is
  resampled onto it.
- A file may name 64 plates, a plate file may be 1 GB, and the plates
  together may hold 512 MB of ink amounts. A file naming more plates is
  an error.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/file-format-parity.md.
2026-11-12 09:30:20 -08:00
makesomethingshit 777fb2fa42 Merge upstream/main into fix/6865-ai-oversized-artboard 2026-11-13 02:19:20 +09:00