Commit Graph

1819 Commits

Author SHA1 Message Date
Nigel Ihza Firdausy d47857a20f fix(layers): validate explicit Layer Options targets
Reapply on current upstream main.
2026-10-11 07:43:13 +07:00
BFlat c9236ed3f7
Merge pull request #1084 from storytold/fix/1068-ai-artboard-layout
#1068: a .ai read as its PDF part keeps its artboard layout
2026-10-11 07:35:59 +07:00
bflatastic 2480a505cb #1068: a .ai read as its PDF part keeps its artboard layout
When a .ai's layers can't be read from its editing data yet, it opens as
its PDF part, which lists the artboards as pages; those were laid out in
a row, so a grid of artboards (or any other arrangement) was lost. The
editing data's artboards are now read even then (ai::artboards, as far
as the reader gets) and, when there are as many as pages and of the same
sizes, each page goes where its artboard is, with the art on it (the art
of a page: the objects mostly on it, else nearest it). Otherwise the
pages stay in a row as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:29:16 +07:00
BFlat 231a97dd51
Merge pull request #1074 from dotCooCoo/feat/align-compound-key-stays
Align: a compound path or selected group that contains the key object stays in place
2026-10-11 07:18:50 +07:00
dotCooCoo 1819921e03 Align: a compound path or selected group that contains the key object stays in place
Align and Distribute Spacing move a selected compound path or group as
one object, and they found the key object only when its own id was
among the objects they move. When the key was inside a compound path
or a selected group, aligning to the key moved that compound path or
group like any other object, and the key moved with it. In that case,
Distribute Spacing with a spacing held the first object still instead
of the key.

The key object can end up inside a selected group or a compound path.
Dragging its row into a group in the Layers panel (layer.move) keeps
the selection and the key. A Selection-tool Shift-click on that group,
when it is unselected and directly in a layer, then adds it to the
selection (select.toggle) and keeps the key. Other ways of adding the
group keep the key as well: a Shift-click in the Layers panel's
selection column or on the group's target circle, a Group Selection
click that hits the key itself, select.add, and select.anchors or
select.anchorsMany in add or toggle mode. In isolation mode, a
Selection-tool click on one of several selected objects in the
isolated group makes that object the key. In an isolated compound
path, the same click on the outline of a selected member makes that
member the key.

Align to the key object and Distribute Spacing with a spacing now keep
in place the selected object that is the key or contains it. Both
commands look for that object among the key and its ancestors, so a
group around the key's group also stays in place. Aligning to the key
still lines the others up with the key's own bounds.

Docs: docs/target-app-parity.md.
2026-10-11 07:18:28 +07:00
BFlat 06226bcb73
Merge pull request #1062 from dotCooCoo/feat/eps-arc-angles
EPS: arc and arcn with huge angles no longer hang the import
2026-10-11 07:15:53 +07:00
BFlat 20eaecd624
Merge pull request #1059 from dotCooCoo/feat/command-sweep-stale-exclusions
Tests: the command sweeps no longer skip junk scale, spacing and artboard values
2026-10-11 07:15:38 +07:00
BFlat 653945c8f8
Merge pull request #1058 from Ni-zav/fix/noissue-saved-view-geometry-s20261010T2146Z-4e82
fix(views): validate saved-view coordinates and rotation
2026-10-11 07:15:22 +07:00
BFlat bfde4dd918
Merge pull request #1057 from Ni-zav/fix/noissue-corner-dialog-explicit-inputs-s20261010T2146Z-4e82
fix(corners): reject malformed explicit dialog targets
2026-10-11 07:15:12 +07:00
BFlat a71348821a
Merge pull request #1056 from Ni-zav/fix/noissue-freeform-line-indices-s20261010T2146Z-4e82
fix(gradient): reject malformed freeform line index lists
2026-10-11 07:15:03 +07:00
BFlat 4f60296e77
Merge pull request #1055 from Ni-zav/fix/noissue-textwrap-explicit-targets-s20261010T2146Z-4e82
fix(textwrap): reject malformed explicit object targets atomically
2026-10-11 07:14:46 +07:00
BFlat 98ccc7980b
Merge pull request #1054 from Ni-zav/fix/1018-all-tools-compact-s20261010T2146Z-4e82
fix(ui): keep All Tools drawer compact on wide screens
2026-10-11 07:14:31 +07:00
BFlat 1639666a34
Merge pull request #1052 from MarekSpodBiedry/issue-1050
Gradient mesh: a fill colour recolours the point last clicked (#1050)
2026-10-11 07:14:14 +07:00
BFlat 47af13c3d4
Merge pull request #1051 from bobtherobot/fix/build-scripts-unchanged-output
Build: generated sources are rewritten only when they change
2026-10-11 07:14:00 +07:00
bflatastic 1da3c0c052 ROADMAP: EPS arcs with huge angles in the progress log (#1062)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:13:38 +07:00
bflatastic b19731b307 ROADMAP: the command sweeps in the progress log (#1059)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:13:38 +07:00
dotCooCoo 2d95ac1694 EPS: arc and arcn with huge angles no longer hang the import
arc raised its second angle by 2π in a loop until it was no longer
below the first, and arcn lowered it the same way. For an angle of
1e18 degrees that loop takes about 3e15 steps: vectorcraft-cli info
was still reading an EPS file with "newpath 100 100 50 1e18 0 arc
stroke" when "timeout 20" stopped it. For 1e308 the loop never ends:
from 2^56 radians (about 4.1e18 degrees) up, adding 2π leaves a double
unchanged. The import fuzz test eps_hostile_programs_never_panic builds
programs from tokens that include arc, arcn, 1e308 and 1e15.

arc now raises the second angle by multiples of 360 until it is at
least the first, and arcn lowers it until it is at most the first, as
the PostScript Language Reference defines them. The code works out both
sweeps from the remainders of the two angles after whole turns, and it
places the first endpoint at the remainder of the first angle. Ordinary
angles give the same arcs as before, apart from rounding in the last
digits. One case changes: when raising (arc) or lowering (arcn) the
second angle by whole turns makes it equal to the first, as in
60 -300 arc or -60 300 arcn, rounding sometimes made the old loop add
another turn and draw a full circle. Those arcs are now empty, as the
PostScript Language Reference defines them.

Tests: 1e18 0 arc, 1e308 0 arc, 0 1e18 arcn and 0 1e308 arcn draw the
arc between the angles' remainders and leave the current point at the
second angle. The test reads each program on a thread of its own and
fails when no result arrives within 20 seconds.
2026-10-11 07:13:38 +07:00
bflatastic 68241453dd Review: New View reads its centre with finite_numbers; ROADMAP (#1058)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:13:37 +07:00
bflatastic 39099dcc57 Review: Corners reuses its messages; i18n; ROADMAP (#1057)
A malformed id gets the existing object-id message, and a listed anchor
that is not a corner the existing "give corners of the path" one, so
only the malformed-list message is new (translated in every catalog
that covers messages).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:13:37 +07:00
dotCooCoo 9ea392e6ee Tests: the command sweeps no longer skip junk scale, spacing and artboard values
The sweeps `every_command_with_junk_params` and
`fuzzed_calls_keep_documents_serializable` skipped every case that set
`object.scale`'s `sx`, `object.transformEach`'s `scaleH` or `scaleV`,
`object.distributeSpacing`'s `spacing`, or `artboard.setProps`'s `x`,
`y`, `width` or `height`. The comment above `KNOWN_BUGS`, the list of
those (command, param) pairs, referred to `#[ignore]`d repro tests, but
`bug_huge_transform_makes_document_unloadable` and
`bug_artboard_huge_props_break_svg` have no `#[ignore]` and run with the
other tests. Since 42df20ca, the commit that added the list,
`Session::edit` returns an error when an edit leaves an artboard or a
selected object with coordinates that are not finite or exceed
`MAX_COORD`, and `object.transformEach` clamps `scaleH` and `scaleV`.

The list and its two checks are removed, so both sweeps run those
cases.
2026-10-11 07:13:37 +07:00
Nigel Ihza Firdausy fffec4a714 fix(views): validate saved-view coordinates and rotation 2026-10-11 07:13:37 +07:00
bflatastic adfe6e3820 Review: one point-index parser for freeform lines; i18n; ROADMAP (#1056)
parse_freeform and paint.freeform.addLine share point_indices, which also
keeps addLine from wrapping a huge index round on 32-bit targets (wasm).
The new messages are in the catalogs that translate every message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:13:36 +07:00
Nigel Ihza Firdausy 935a588aa2 fix(corners): reject malformed explicit dialog targets 2026-10-11 07:13:36 +07:00
bflatastic 212fb9994a Review: one checked ids-or-selection helper; ROADMAP (#1055)
Text Wrap and the variable bindings share checked_ids_or_selection in
cmd/mod.rs instead of each keeping its own copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:13:35 +07:00
Nigel Ihza Firdausy f5111045a9 fix(gradient): reject malformed freeform line index lists 2026-10-11 07:13:35 +07:00
bflatastic c3e0ca3dfa Review: All Tools width from its cells; test order; ROADMAP (#1054)
The drawer width is worked out from the tool cell, and the new test no
longer sits between the existing test and its doc comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:13:34 +07:00
bflatastic 50dbe4c10e ROADMAP: recolouring mesh points in the progress log (#1052)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:13:34 +07:00
Nigel Ihza Firdausy ec41d72e0d fix(textwrap): reject malformed explicit object targets atomically 2026-10-11 07:13:34 +07:00
Nigel Ihza Firdausy 63026402a1 fix(ui): keep All Tools drawer compact on wide screens (#1018) 2026-10-11 07:13:34 +07:00
bflatastic a785b53e7b ROADMAP: generated sources in the progress log (#1051)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:13:33 +07:00
MarekSpodBiedry 1217d58ca1 Gradient mesh: a fill colour recolours the point last clicked (#1050) 2026-10-11 07:13:33 +07:00
Michael Gieson c912ef59d1 Build: generated sources are rewritten only when they change
crates/text/build.rs (craft_fonts.rs) and crates/ui-egui/build.rs (credits.rs) wrote their
output on every run. Where cargo's recorded times lose their sub-second part (an ecryptfs home
directory, as Mint and Ubuntu offer at install), a file written just before its crate compiles
reads as newer than that compile, so the next build compiled the crate and everything above it
a second time: a full app rebuild for nothing after any build-script rerun. Unchanged output is
now left alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 07:13:32 +07:00
BFlat af505b197a
Merge pull request #1076 from storytold/fix/1032-lab-spots
#1032: spot colours defined in Lab keep their Lab values
2026-10-11 07:06:46 +07:00
bflatastic f043a1a392 #1032: spot colours defined in Lab keep their Lab values
A .ai or PDF whose spot colours are defined in Lab opened them wrong:
- the PDF part gives such an ink an ICC Lab profile as its alternate
  space ([/ICCBased profile], 3 components), which was read as RGB, so
  the Lab values were clamped into wildly wrong colours (PANTONE 249 C
  came in yellow). A profile whose Alternate is [/Lab ...], or whose
  header names the Lab data colour space, is now read as Lab;
- the editing data writes them as `c m y k L a b (name) tint 2 Xx`,
  type 2 being Lab, and only the CMYK equivalent was read. Type 2 now
  gives the Lab colour, in the swatches and on the art.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:06:05 +07:00
BFlat d01d7b2ef0
Merge pull request #1060 from dotCooCoo/feat/align-no-op-no-undo
Object: Align, Move, Arrange and other commands that change nothing record no undo step
2026-10-11 07:02:47 +07:00
dotCooCoo dc6e8a4253 Object: Align, Move, Arrange and other commands that change nothing record no undo step
These Object commands edited the document even when nothing changed, so
the call recorded an undo step, emptied Redo and marked the document
modified:

- object.align, object.distributeSpacing and object.distribute (three
  or more objects) when no object moves. With one object selected,
  Object › Align aligns it to its own bounds, so it recorded a step
  every time. Calls on objects that were already aligned or evenly
  spaced also recorded a step, including objects that an earlier Align
  or Distribute left a rounding error away from the position the next
  call computes.
- Move, Rotate, Scale, Shear, Transform and Nudge by an identity
  transform without a copy, Transform Again after one, Set Bounds with
  the current position and size, and a Selection tool drag that ends
  where it started.
- Nudge by 0 with anchors direct-selected or with only ruler guides
  selected, and Transform with nothing selected or with ids that name
  no object, with or without a copy.
- Bring to Front and Bring Forward on objects already in front, Send to
  Back and Send Backward on objects already at the back, and Send to
  Current Layer on objects already on top of the current layer.
- Ungroup, Release Compound Path and Release Clipping Mask with none of
  their objects selected, and Expand Shape with no live shape selected.
- Lock with every selected object already locked, Hide with every one
  already hidden, Unlock All with nothing locked and Show All with
  nothing hidden.
- object.setProps with the values the objects already have, or with no
  object.
- object.setLiveShape with the values the shape already has: a
  polygon's sides, angle, radius or equal sides, or a side length that
  gives exactly its current radius; an ellipse's pie, including Invert
  Pie on a whole ellipse; and a corner radius or kind that the corners
  already have.

These commands now return without editing the document, and Align and
Distribute count a move of 1e-9 pt or less on both axes as none. Such a
call records no undo step, keeps the step to redo and leaves the
document unmodified. In these cases Ungroup no longer resets the
selection, and Release Compound Path, Unlock All and Show All no longer
empty it. A Transform with a copy of no object keeps the selection.
Lock and Hide still deselect the objects. A Nudge by 0 no longer
expands a live shape whose anchors are direct-selected. The replies
have the same form as before. An identity transform still sets what
Transform Again repeats. The journal still records each call, so the
Actions panel records it as before.

When every value given to Set Bounds is the current one, it now applies
the identity transform instead of the transform computed from those
values, which can differ from the identity by a rounding error on a
rotated object or with Use Preview Bounds.

When a drag comes back to its start, the identity preview no longer
edits the document. Session::preview now advances the revision when it
undoes the previous preview, so views redraw that drag.

The undo and redo row of docs/mcp.md now notes that many calls that
change nothing record no undo step, so undo after one undoes the step
before it.
2026-10-11 07:02:28 +07:00
BFlat 327737b21a
Merge pull request #1067 from imachus/pr/ja-proportional-metrics
Type: Proportional Metrics — full-width glyphs on the font's proportional widths, `palt` (#966)
2026-10-11 07:01:17 +07:00
BFlat a7a4d952ea
Merge pull request #1045 from lollinoo/codex/1034-text-scale
Fix font size and horizontal scale after text/group scaling
2026-10-11 07:01:05 +07:00
imachus 621e1a6640 Type: Proportional Metrics — full-width glyphs on the font's proportional widths, `palt` (#966)
A character attribute (Character panel menu with the East Asian options;
`text.setFormat` / `text.setRangeStyle {proportionalMetrics}`), saved only
when on. In horizontal type it applies `palt`; vertical type sets as before
until the vertical shaping change (`vpal`) follows.

Line-end Punctuation Half Width takes nothing more off the punctuation
`palt` re-spaced (the flush opening bracket, consecutive punctuation, the
line-end half width): the segment is shaped once more without `palt`, and
the glyphs whose advance differs are marked. Punctuation the font leaves
full width is trimmed as before. SVG writes `"palt" 1` with the
character's other features.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:00:35 +07:00
lollinoo c5274beb23 #1034: scaled type shows and takes its scaled sizes
After scaling 12 pt type 2x, the Character panel still showed 12 pt and
typing 10 pt gave 20 pt type. Character controls and text commands now
work in document points: the size, leading and baseline shift shown and
set are the stored ones times the text's scale along its em height, and
horizontal scale includes the ratio of the transformed em axes. Rotation,
reflection and shear stay in the transform, so text remains editable.
Groups' text counts, keyboard increments use the same units, Shrink
Text to Fit works in document tenths, and a collapsed transform rejects
dimensional edits without touching the document.

text.getRange takes effective: true for the document-space values.
2026-10-11 07:00:34 +07:00
BFlat 611f6855ef
Merge pull request #1075 from storytold/fix/1063-char-paint
#1063: a fill or stroke chosen with characters selected colours just them
2026-10-11 07:00:07 +07:00
bflatastic 75c587c8a0 #1063: a fill chosen with characters selected colours just them
Choosing a fill or stroke (Color panel, Swatches, the proxies) while the
Type tool had a few characters selected painted every run of the type
object. As in Illustrator, it now goes to the selected characters only:
the runs are split at the selection's ends and the paint is placed on
the runs in between, as one undo step; a stroke gets a 1 pt weight
where it had none. With only a caret, or with explicit ids or an item,
paint.setFill/setStroke behave as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 06:59:36 +07:00
Brandon Thomas 10f1c56a86
Release prep: 0.9.0 and a Linux riscv64 release build (#1071)
- Version 0.8.0 -> 0.9.0 (cargo xtask version set; Cargo.lock refreshed).
- release.yml: new linux-riscv64 job, ported from GridCraft. Cross-compiles
  riscv64gc-unknown-linux-gnu on ubuntu-24.04 against riscv64 -dev packages
  from ports.ubuntu.com (the same set the linux job installs), packages a
  tar.gz, and runs vectorcraft-cli --version under qemu-riscv64. The draft
  release job now needs it, so its tarball is uploaded and in SHA256SUMS.
- packaging/linux/package.sh: CROSS_ARCH / CROSS_TARGET / CROSS_COMPILE /
  EMULATOR support, as in GridCraft.
- docs/releasing.md: list the riscv64 artifact.

Checked locally: cargo check -p vectorcraft -p vectorcraft-cli --target
riscv64gc-unknown-linux-gnu, cargo fmt --check, actionlint, shellcheck.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:47:38 -04:00
BFlat ab49f6af8b
Merge pull request #1070 from storytold/fix/1027-ps-form-ai
#1027: PostScript-form .ai files open through their layers
2026-10-11 06:05:40 +07:00
BFlat 51e24f2944
Merge pull request #1069 from storytold/fix/1025-ai-global-colour-pattern
#1025: .ai editing data keeps sublayers with global colours and pattern fills
2026-10-11 06:05:24 +07:00
bflatastic 2048084396 #1027: PostScript-form .ai files open through their layers
A .ai in PostScript form went only to the PostScript interpreter, which
stopped on the first operator its missing prolog would define
(`Adobe_packedarray`, `Lb`, `Np`): files from Rhino and other CAD apps name
the procsets without including them.

- Such a program is its own editing data: when it has layers
  (`%AI5_BeginLayer`) and no private data, the editing-data reader reads it,
  compared with the page when the program runs, from the layers alone when
  it can't be run. If the layers can't be read either, the error says why.
- The reader takes the legacy ten-operand `Lb`, ignores flatness `i`, and
  uses the bounding box as the page when nothing else says where it is.
- Tests on synthetic files; fuzz fixture and hostile-program layers added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-11 06:04:48 +07:00
bflatastic cee73d03a3 #1025: .ai editing data keeps sublayers with global colours and pattern fills
The editing-data reader stopped on `Xk`/`XK` (global process colours) and
on `p`/`P` (pattern fills), so the file fell back to its PDF part, which has
only the top-level layers.

- `Xk`/`XK` read like `Xx`: a global swatch (not a spot ink), linked at its tint.
- `%AI3_BeginPattern` sections are read by a reader of their own into
  pattern swatches (tile from `(name) llx lly urx ury`, art in pattern space);
  `p`/`P` fill or stroke with them, placed by the operator's matrix. A pattern
  it can't read whole still makes a fill with it unreadable, as before.
- Tests on synthetic editing data; fuzz tokens and fixture extended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 06:04:48 +07:00
BFlat 3ee9543b3f
Merge pull request #1021 from redrun6767/feat/guides-on-layers
Guides: ruler guides as layer objects
2026-10-11 06:04:04 +07:00
bflatastic c183c47df3 Review: guides on hidden or locked layers, and through layer commands
- Guides on a hidden or locked layer leave the selection (Selection::prune,
  run after every edit and selection change), their Layers rows neither
  select nor drag them, and guide.move / guide.remove / guide.setLayer
  refuse them; guide.setLayer refuses a locked target layer, as dragged
  rows are refused there.
- A new guide goes where new art goes (the current layer, else the top
  shown and unlocked one; refused when there is none), so it never lands
  on a hidden layer and vanishes.
- Layer commands keep guides with their layers: Duplicate (and Alt-drag
  copies of rows) copy a layer's guides onto the copy, Merge Selected and
  Flatten Artwork put the merged layers' guides on the merged layer and
  Flatten discards a hidden layer's. One shared doc predicate for "the
  layer a guide is on"; the deleted-layer cleanup moves into the doc.
- Tests: engine tests for the above and their undo, a doc check that an
  old guide reads with no layer and none is written, and a headless
  Layers panel test of guide rows (select, drag to another layer, locked).
- Translations for the new message; docs and a ROADMAP progress line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 06:03:48 +07:00
redrun6767 2d2e0c6992 Docs: ruler guides on layers in the parity docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 06:03:47 +07:00