Commit Graph
4052 Commits
Author SHA1 Message Date
dotCooCoo ba32ef5b09 .ai import: the editing data reader warns about each image it can't read
An image in a .ai's editing data whose operands had no matrix, fewer
than 11 numbers, or a matrix of other than six numbers was left out
with no warning. So was one whose matrix maps it onto a line or a
point. The reader's warnings did not mention such an image, while the
reader warned "an image VectorCraft can't read was left out" for an
image whose size or bit depth it can't use.

Each of those exits now adds the same warning, as does the one where
the decoded pixels don't make an image.
2026-10-19 00:03:51 +07:00
dotCooCoo f0c2d8c890 PDF and .ai import: editing data with a UTF-16 description is checked against its pages
A PDF or PDF-compatible .ai saved with Preserve Editing carries the
native document as an embedded file, with a hash of its pages at the
end of the file's /Desc. The import read /Desc with from_utf8_lossy and
looked for "hash ". /Desc is a PDF text string, which may be written as
UTF-16BE with a byte order mark (ISO 32000-1 7.9.2.2). Written that
way it holds no UTF-8 "hash ", so the import found no hash, took the
editing data as intact and reopened the native document with no
warning, even after another app had changed the pages.

/Desc is now read with text_string, the importer's decoder for text
strings, so the hash is found in UTF-16BE, UTF-8 and PDFDocEncoding.
Changed pages open as artwork with the "changed in another app"
warning. The embedded file's /UF and /F names are decoded the same
way, so editing data named only in UTF-16BE is found.
A /Desc without a hash is still trusted, as before.
2026-10-19 00:03:51 +07:00
dotCooCoo e5c9054324 Legacy .ai export: an F line follows each %AI5_EndRaster
alongtheblue-sketch took 0.12.0's version 7 output of the SVGs in #2692
and #2830, edited it by hand and opened it in the reference app
(https://github.com/storytold/vectorcraft/pull/2801#issuecomment-6244853548).
Writing the image lines as #2801 does, with the bracketed matrix and
the 60-digit data lines, did not change the result there. With one
line, F, added after %AI5_EndRaster, the reference app opened the image
in its clipping group and the art after it in both files.

The writer now writes F after every %AI5_EndRaster: clipped, grouped
and loose images, flattened images and images in pattern tiles. No path
is open at that point, so the line paints nothing. VectorCraft's reader
opens the files with the same objects as before, and the file's
PostScript prolog draws the same page.
2026-10-19 00:03:50 +07:00
dotCooCoo 9fe0f03de2 i18n: number, object id and date parameter errors are translated
The errors of the parameter checks that many commands share had no
@msg row in any catalog: "`{key}` must be a number, not {v}" from
check_numbers (object.move, object.rotate, object.setBounds and the
other callers), "missing integer `{key}`" from index_param in
cmd/appearance.rs (appearance, effect and freeform gradient commands),
the three object id errors of checked_ids_param and checked_id, and
the Unix seconds error of date_param. A template
matches only the whole text, so the row for "`{key}` must be a
number" did not match the longer check_numbers message. These errors
showed in English in the status bar, and "no such object id {value}"
matched the general "no such object {0}" row and showed half in
English. The scan in complete_languages_translate_every_message does
not read messages built with bad(), so that test did not report them.

Each of the 6 messages now has a row in the 18 catalogs that translate
every status and error message and in Bulgarian, Danish, Indonesian and
Dutch, and "missing number `{key}`" has a row in Danish and Indonesian.
No English string changes.

Docs: docs/target-app-parity.md.
2026-10-19 00:03:48 +07:00
dotCooCoo c0ec14b86d Paths: anchor commands refuse a subpath that is not a whole number
path.curvatureEdit, path.removeAnchor, path.convertAnchor,
path.reshapeSegment, path.split, path.setHandle and path.insertAnchor
read subpath as an unsigned integer and fell back to 0 for any other
value. A subpath of 1.5, -1 or "1" edited subpath 0 and the command
reported success. path.split read anchor and segment with as_u64 too
and treated any other value as absent, so a bad anchor beside a good
segment split at the segment.

These commands now refuse a subpath that is not a whole number of 0 or
more with "missing integer `subpath`", the error path.curvatureEdit,
path.removeAnchor, path.convertAnchor and path.reshapeSegment already
give for a bad anchor or segment, and path.split refuses such an anchor
or segment too. Without a subpath, or with null, they edit subpath 0 as
before. The document and the undo history stay as they were after each
error. The params text of the six commands that listed subpath as
required now marks it optional.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-19 00:03:47 +07:00
dotCooCoo 89a73abc38 Links: a document opened from a mapped drive finds links on its share
craft-docpath follows a network path only on the share the document is
on, and its caller contract asks for a document opened from a mapped
drive to be passed by its UNC path. fileio::document_path passed the
drive path as given, so a document opened as Z:\jobs\doc.vectorcraft
treated a link stored as \\server\share\img\logo.png, on the share
behind Z:, as on another computer: the image was missing, and
document.open, links.check and links.info gave refused: "the path is on
another computer".

On Windows, when a stored path is refused as on another computer and
the document is on a drive, document_path now reads the final path of
the document's folder (std::fs::canonicalize). When that names a share
(\\?\UNC\server\share\...), the path is checked again against the
document's path on that share. Links on other computers stay refused,
and paths that were followed resolve as before. Opening a document,
Link Info, links.check and the linked-file watch all use it. Other
systems are unchanged.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-19 00:03:47 +07:00
dotCooCoo cc8944ad2a Transform Each, Rasterize, Mosaic, Trim Marks and Rearrange: a number given as text is refused
Transform Each, Rasterize, Create Object Mosaic, Create Trim Marks and
Rearrange All Artboards read their optional numbers with f64_or, which
falls back to a default for a value that is not a JSON number.
object.transformEach {"scaleH": "50"} left the selected objects at
their size and reported success. Rasterize did the same with ppi,
Create Object Mosaic did the same with width, height, spacingX and
spacingY, Create Trim Marks did the same with offset, length, bleed and
weight, and Rearrange All Artboards did the same with spacing. Crop
Image cropped to the artboard when rect was a string or another value
that is not an array.

These commands now fail with "`<key>` must be a number, not <value>"
for a value that is not a number or null, as the transform commands
do, and change nothing. Transform Each checks scaleH, scaleV, moveH,
moveV and rotate. Crop Image refuses a rect that is not an array of
four numbers with "rect must be [x, y, width, height]", the message it
already gave for an array of the wrong length; a null rect crops to the
artboard, as a missing one does.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-19 00:03:46 +07:00
dotCooCoo 05264b2801 MCP: document.pdfInfo answers one page for an EPS, a .ps file or a PostScript .ai
document.pdfInfo refused PostScript (an EPS, a .ps file, or a .ai in
PostScript form), mostly with "this is a PostScript file ..., not a
PDF", although document.open and file.place read the same file
through the EPS reader as one page.

It now answers {pages: 1, needsPassword: false, pageInfo: [{width,
height, rotation: 0, boxes: {}}]}. The page is the file's bounding box
(%%HiResBoundingBox when it has one, a US Letter page without one),
read from its comments without running its program. thumbnail: 1 is a
PNG of what that page prints, read by the EPS reader without any
editing data the file carries. A program whose header is only %! is
read as PostScript, as clipboard.importEps reads it.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-19 00:03:45 +07:00
dotCooCoo 372887fc6c EPS and .ai import: images with TIFF predictor 2 draw their samples at every bit depth
The PostScript interpreter that reads EPS and PostScript .ai files
accepts a FlateDecode or LZWDecode filter with Predictor 2 at
BitsPerComponent 1, 2, 4, 8 and 16, but undid the horizontal
differencing only at 8 bits. At the other depths the filter gave back
the stored differences, and the image drew them instead of its
samples.

The predictor is now undone at every depth it accepts, as ISO 32000-1
§7.4.4.4 describes: each component adds the same component of the
pixel to its left, within its bit width, and each row starts again.
16-bit components add as big-endian 16-bit numbers. The padding bits
at the end of a row stay as they are, and 8-bit data takes the same
path as before. The PNG predictors are unchanged.

Docs: docs/target-app-parity.md.
2026-10-19 00:03:43 +07:00
dotCooCoo 841fc0cd51 i18n: Traditional Chinese translates status-bar and error messages
Squashed from #2784 (its merge of main dropped).
2026-10-18 23:50:58 +07:00
dotCooCoo d7e7066251 File formats: Targa (.tga) files open and place
Squashed from #2765 (its merge of main dropped).
2026-10-18 23:50:58 +07:00
focuerandClaude Opus 5.5 dc7216f0e4 web: rustfmt window.vectorcraft
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:50:58 +07:00
focuerandClaude Opus 5.5 9dbab52168 web: window.vectorcraft, the control channel for the page
The same JSON methods as the desktop control server (engine.execute, ui.inspect, ui.screenshot, …),
as promises on window.vectorcraft, like FilmCraft's window.filmcraft: an agent driving the editor in a
browser tab or an <iframe> uses the same commands as the CLI and the MCP server. openFile opens
bytes the way a dropped file opens; captureDownloads keeps saves in the page for readFile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:50:58 +07:00
MortenHusted 36aecff81d Select: select.color reads compound paths and empty type as document.inspect does
A compound path holds its members, so select.color passed over it and
looked at the members instead, whose paint inspect doesn't report: a
black compound path (art with holes, as PDFs bring in) never matched
"#000000". It now matches by its own fill and stroke, and its members
never do. Type without characters reads as the default character fill,
as inspect reports it (CodeRabbit on #2939).
2026-10-18 23:50:58 +07:00
MortenHusted d25289a3be Select: select.color picks objects by fill and stroke colour, no object needed
Scripts had to know an object id before they could select art by colour
(select.same.* and select.magicWand start from one). select.color
{fill?, stroke?} takes colours as paint.setFill does and selects the
objects whose fill and stroke read as them in #rrggbb, as
document.inspect reports colours, with Select > Same's selection rules.

The label is translated in each of the 23 catalogs, which
complete_languages_translate_every_menu_string requires.
2026-10-18 23:50:57 +07:00
dotCooCoo 09a6fbe470 Type: a click away from the text being edited ends editing without making new text
While a Type tool edited text, a click on blank canvas ended the
editing and also placed new point type at the click. Area Type and
Type on a Path did the same with a click on a path.

Such a click now only ends the editing. The typing session is
committed, empty point type that a click placed is discarded as before,
and the Type tool in use stays active; the next click makes new type.
The same applies when the editing began with a double-click from a
selection tool. A click on other type still edits it at once, and a
drag that draws an area-type frame still makes the frame.
2026-10-18 23:50:57 +07:00
dotCooCoo b30d80e056 Fields: arrow keys and the wheel step to the next whole unit, and Ctrl/Cmd steps to the next tenth
In number fields, Up/Down and a wheel notch added a whole step and kept
the fraction, so 5.1 mm stepped up to 6.1 mm. A plain step now goes to
the next whole unit in its direction (5.1 goes up to 6 and down to 5; 5
goes up to 6 and down to 4), and a Ctrl/Cmd step goes to the next tenth,
in the field's own unit. Repeated presses or notches of one size add or
take one step each, and presses and notches are applied in the order
they came. A Shift step still adds or takes ten and keeps the fraction.

The number fields that widgets.rs draws for the panels, the Control bar
and the dialogs (unit, percent, degree and count fields) all use this
step (widgets::step_value).
2026-10-18 23:50:57 +07:00
EricKuckandClaude Fable 5.1 1b8e3d9ae1 Outer Glow grows its solid silhouette by a quarter raster pixel before the blur, as the glows AI files export measure
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 23:50:57 +07:00
bflatastic 12d107cc56 fmt (#2928) 2026-10-18 23:50:57 +07:00
nrq6993-a11yandCursor 2025c357d9 Fix #2719: honor Display Print Size for status-bar 100% zoom
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 23:50:57 +07:00
MortenHusted eba9abc21e CLI, MCP: the raster scale no longer resizes DXF exports
`vectorcraft-cli run/convert --scale` and the MCP export tool's `scale`
are raster pixels per point, but they were passed to every export, and
DXF reads `scale` as drawing units per unit: a 2x PNG next to a DXF
doubled the drawing and left it unitless ($INSUNITS 0). They now go
only to raster formats. DXF's own scale stays reachable through the
format options.
2026-10-18 23:50:57 +07:00
bflatastic 88494319da fmt (#2911) 2026-10-18 23:50:57 +07:00
nrq6993-a11yandCursor bf19ada5e1 Fix #2646: keep undocked shared-dock panels opaque
Floating groups on the canvas now always get a panel-colour backdrop from layout geometry when egui has not registered the area yet, and panel bodies paint their own fill so empty regions do not show through.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 23:48:57 +07:00
nrq6993-a11yandCursor 16dcaa425b Fix #2727: opacity-mask editing no longer dims the canvas
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 23:48:57 +07:00
Higgs32584andClaude Opus 5.5 c5798f446e CMYK profiles carry every tag an ICC output profile requires (#2707)
The built-in CMYK profiles embedded in CMYK exports had only the
perceptual and colorimetric tables. ICC.1 requires an output profile to
also have the saturation tables (A2B2, B2A2) and the gamut tag (gamt).
Windows colour management rejects the profile without them ("Bitmap color
context is not valid"), so Microsoft Photos couldn't show a CMYK TIFF
that embedded it, while the RGB export opened fine.

Write the saturation tables (same as colorimetric) and a 9x9x9 gamut
table (in gamut where a Lab colour separates and returns within dE 3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:48:56 +07:00
Davit MatchakhelidzeandClaude Opus 5.5 4675a1633e i18n(ka): Letter and Word Spacing as in the other Craft apps; "weren't read" in the catalog's usual form
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-18 23:48:55 +07:00
ElPaule d99b04e594 i18n(de): add missing PSD error messages and effect Soft entry 2026-10-18 23:48:54 +07:00
BFlat 0f6154e7b9 Merge pull request #2934 from devius/fonts-georgian-pin
Release: embed the current craft-fonts (adds Noto Sans Georgian, craft-fonts#27)
2026-10-18 23:45:27 +07:00
Davit MatchakhelidzeandClaude Opus 5.5 fc594b51ee Release: build the web version with craft-fonts too (web-craft-fonts)
Without it the shared release workflow builds the web release without CRAFT_FONTS_DIR, so
web-fonts.txt's startup faces (Arabic, Thai, Georgian) are not served (CodeRabbit review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-18 20:29:04 +04:00
BFlat cdba0aac03 Merge pull request #2900 from nrq6993-a11y/stream/vc-issue-2723-reshape-falloff
Fix #2723: Reshape tool falloff follows the path, so nodes stretch progressively
2026-10-18 20:14:02 +07:00
BFlat f44c3e9593 Merge pull request #2895 from Silvertree2010/fix/h2559-unit-named-ft-resolves-to-feet-i
Resolve the "ft" unit suffix to Feet in Unit::named
2026-10-18 20:13:47 +07:00
BFlat c965f49fda Merge pull request #2905 from storytold/fix/2850-pencil-redraw
Pencil: a stroke that starts and ends on a selected path redraws the section between (Edit Selected Paths, #2850)
2026-10-18 20:13:22 +07:00
BFlat a10604bbc6 Merge pull request #2904 from storytold/fix/2860-recent-cards
New Document: Recent and Saved cards keep the orientation they were made in (#2860)
2026-10-18 20:13:13 +07:00
bflatastic 4f526b4e3f Reshape: the lengths along the path are measured once per drag, and the default reach stays the path's size (#2900)
anchor_arc_distance measured every segment again for each anchor, so a reshape cost the square of the anchor count; path.reshape now measures the positions once, and the two helpers only it used are gone. The default radius stays half the subpath's bounding-box diagonal, now measured along the path: half its length reached the far corners of a square, moving art the grab is nowhere near (reshape_moves_the_grabbed_point_and_its_neighbourhood).
2026-10-18 20:12:47 +07:00
NetterSilverandClaude Haiku 5.5 f2601137ab Resolve the "ft" unit suffix to Feet in Unit::named
Found while testing crates/doc/src/lib.rs:274 (Unit::named)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 20:12:47 +07:00
bflatastic 9938a31c9b Pencil: a stroke that starts and ends on a selected path redraws the section between (Edit Selected Paths, #2850)
path.freehand takes redraw: {id, within}: the stroke's ends are placed on the path, anchors added there, and the section between them replaced by the fitted stroke, the shorter way round a closed subpath; a stroke that doesn't end on the path still draws a new one. The Pencil and Paintbrush send it for a stroke that starts on a selected path's outline away from its ends, within Edit Selected Paths' distance.
2026-10-18 20:12:47 +07:00
bflatastic 5f37fccdce New Document: Recent and Saved cards keep the orientation they were made in, so one A3 card is chosen, not two (#2860)
The cards turned to the chosen orientation in every category, so a portrait and a landscape A3 in Recent looked the same and both read as chosen. Only the built-in presets' cards turn now.
2026-10-18 20:12:47 +07:00
nrq6993-a11yandCursor e8e88c14f2 Fix #2723: Reshape falloff uses arc length along the path
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 20:12:47 +07:00
BFlat 2cdb0d2d53 Merge pull request #2893 from dylanmtaylor/chore/drop-competitor-references
docs: drop references to competing commercial software
2026-10-18 19:36:12 +07:00
Dylan M. Taylor fe16f852db docs: clarify readiness audience and tracked features baseline 2026-10-18 08:28:33 -04:00
BFlat 43bb588e5e Merge pull request #2897 from storytold/fix/2879-baselines
Canvas: selected point type's baseline marker runs under each line, centred and right-aligned type too (#2879)
2026-10-18 19:20:48 +07:00
BFlat a57eaa0782 Merge pull request #2896 from storytold/fix/2873-w-weights
SVG export: a Japanese face's numbered weight (W0–W9) is its font-weight, W6 600 (#2873)
2026-10-18 19:20:39 +07:00
BFlat 007df2ee24 Merge pull request #2877 from AJMakesStuff/main
Add interactive Mirror Repeat axis controls
2026-10-18 19:20:05 +07:00
BFlat 0955e6845c Merge pull request #2884 from nrq6993-a11y/stream/vc-issue-2576-font-poll
Fix #2576: poll for fonts that font managers activate while the app is in front
2026-10-18 19:19:49 +07:00
BFlat 4bafe834a6 Merge pull request #2887 from ham9zah/fix/arabic-alternate-glyph-highlight
Part of #394: required shaping is not an alternate glyph
2026-10-18 19:19:33 +07:00
BFlat 87603e4c35 Merge pull request #2876 from ham9zah/fix/selection-clip-group-outlines
G1: test that a group of clipped images shows only its masks and their bounds
2026-10-18 19:19:19 +07:00
BFlat e60882a3a1 Merge pull request #2870 from lewandowskit/pl-terminologia
i18n(pl): Artboard is "obszar kompozycji", Guides "linie pomocnicze"
2026-10-18 19:19:05 +07:00
Davit MatchakhelidzeandClaude Opus 5.5 25a987a94f Release: embed the current craft-fonts (adds Noto Sans Georgian, craft-fonts#27)
The release and FreeBSD workflows pinned craft-fonts 3d9ab3a1, two commits before #27,
so release builds had no Georgian face for the ka interface.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-18 16:09:28 +04:00
bflatastic bb14031aa3 Canvas: selected point type's baseline marker runs under each line, centred and right-aligned type too (#2879)
The marker ran from the anchor to the right edge of the bounds, so centred type showed half a line and right-aligned type none, and rotated type a level line. It now draws each line's baseline in text space (TextObject::baselines, the lines a click selects the type on), turned with the type.
2026-10-18 19:02:52 +07:00
bflatastic eec304dde1 SVG export: a Japanese face's numbered weight (W0–W9) is its font-weight, W6 600 (#2873)
style_weight reads W0–W9 style names (Hiragino, Morisawa…) as their weight classes before the English words, so SVG and CSS export write font-weight 600 for W6, and nearest-style matching weighs them too.
2026-10-18 19:02:52 +07:00