Commit Graph
554 Commits
Author SHA1 Message Date
dotCooCoo beef32b8a3 Artboards: an artboard's guide label colors its own guides
Each artboard can carry a guide label: one of the layer colors or a
custom RGB. Guides that belong to an artboard draw in its label color,
and guides without an artboard, or on an artboard without a label, keep
the guide color from the preferences. Selected guides still draw in the
selection color.

- Artboard Options has a Guide Label row (None, the layer colors with
  swatches, or Custom with a color swatch).
- artboard.new and artboard.setProps take guideLabel: null, a layer
  color's name, or #rrggbb. Any other value is an error. The inspect
  summary lists guideLabel when one is set.
- The native format saves the label and skips it when unset; a preset
  index outside the layer colors reads as no label.
- artboard.setProps no longer adds an undo step when nothing changes.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-10 19:37:17 -08:00
dotCooCoo d426c70a92 Preferences: export and import preferences as a JSON file
Part of #6435. An optional VectorCraft feature: the Preferences dialog's
Export Preferences... saves the applied preferences to a JSON file (the
web build downloads it), and Import Preferences... applies such a file and
shows the imported values. They run the new commands prefs.export {path?}
and prefs.import {path | data}.

The file holds every preference prefs.list lists and the eyedropper,
perspectiveWidget, pathfinder and glyphSnap groups, without the four
folder preferences and saved presets. prefs.import reads at most 1 MB and
checks every value first: another format, a newer version, an unknown
preference, a bad value or a folder preference is an error, and nothing
changes. It keeps the open document's units, records no undo step, and is
refused as a command.batch step. prefs.set now rejects a group value that
is not an object.
2026-11-10 18:11:01 -08:00
dotCooCoo 74450ca52b Actions: test Batch folder names that differ only in case or are empty
Batch with Override Export compares output names without case, so
`a.svg` and `sub/A.svg` get the Export for Screens folders `a` and
`A-2`. A source whose name without its extension is empty (` .svg`)
gets the folder `file`, and a later `file.svg` gets `file-2`. Two tests
cover these cases.
2026-11-10 15:29:30 -08:00
BFlat 3cb6f44c9b Merge pull request #6464 from dotCooCoo/fix/issue-6382
Fix #6382: PDF export says how a page longer than 200 inches is written
2026-11-11 04:46:38 +07:00
dotCooCoo a960ba0d08 PDF export: pages that exported before keep exporting
- PDF 1.3 and 1.4 files, PDF/X-1a, PDF/X-3, PDF/X-4 and PDF/A-2b at
  PDF 1.4 among them, write a page longer than 14,400 pt in points
  again, now with a warning that apps that keep to the limit cut it to
  200 in. Only a page that failed with
  "PDF writer error: Limit(TooLargeFloat)" gets the new error: one
  whose size or art needs a real number past 32,767, the most PDF 1.4
  holds. Whole numbers are written as integers, so a page of whole
  points past 32,767 is written as before.
- A page whose side is past 32,767 pt and not a whole number of points
  is refused before the thumbnails are drawn and the transparency is
  flattened.
- A PDF 1.5 file written as PDF 1.6 for a long page keeps PDF 1.5's
  encryption (128-bit RC4), which the Save PDF dialog shows.
- A long page whose short side would be under 3 units at its UserUnit
  (ISO 32000-1, Annex C.2), such as 50 m x 4 mm, gets a smaller
  UserUnit that keeps the short side at 3 units, with a warning.
- The warnings give a page's size rounded up, as the error does.

A page of 14,400 pt or less is written as before. The new warning has
rows in every complete catalog. Docs: docs/mcp.md,
docs/target-app-parity.md and the document.exportPdf params.
2026-11-10 12:31:53 -08:00
ruchirguitarandClaude Opus 5.5 b29f02a42e Test #3084: test that Export for Screens JPGs open in every colour model
Exports RGB, CMYK and grey JPGs at qualities 100, 80 and 50, at 1x and 2x,
and decodes each one. It also checks that only the CMYK files leave out the
JFIF segment (#3140). Covers #2096 too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 19:39:18 +05:30
dotCooCoo 2b7b7503eb Fix #6382: PDF export says how a page longer than 200 inches is written
A page with a side longer than 14,400 pt (200 in, ISO 32000-1 Annex C.2)
keeps its size only with a UserUnit, which PDF 1.6 added. Exports now
report such pages and handle the versions without a UserUnit:

- Each such page adds a warning with its size and its UserUnit. Apps
  that ignore UserUnit show the page that many times smaller.
- A PDF 1.5 file with such a page is written as PDF 1.6, with a warning.
- PDF 1.3 and 1.4 files, PDF/X-1a and PDF/X-3 among them, are refused
  with an error that names the page and the limit, instead of
  "PDF writer error: Limit(TooLargeFloat)".
- PDF/X-4 files at PDF 1.5 or 1.6 keep the page in points, with a
  warning that apps that keep to the limit cut it to 200 in.

The warnings and the error have rows in every complete catalog. Docs:
docs/mcp.md, docs/target-app-parity.md and the document.exportPdf
params.
2026-11-09 21:52:13 -08:00
dotCooCoo 49129c565b Export: exported objects keep the clips, masks and effects around them
Export Selection, Export for Screens by selection or by layer, Asset
Export and CSS Properties' rasterized pictures put the objects on a new
plain layer. An object inside a clip group, or on a clipping layer, was
written without its clip, in SVG, PNG and the other formats, and the
opacity masks, effects, opacity and blend modes of the groups and
layers that held it were dropped too.

These exports now keep each object in the layers and groups that hold
it, and crop the file to the bounds the renderer measures for that art
(a clip group by its clip). Those layers are drawn even when they are
hidden, template or non-printing layers, as the objects were before. A
PSD of the export names its layers after the document's layers instead
of one layer named Selection or after the asset.

document.export with selectedOnly uses the same code: an object of a
blend, envelope, repeat or intertwine is now written on its own instead
of inside a blend of one object, and a Shaper group keeps its source
art. A text export of the selection still holds only the selected type,
not that of a type clipping path around it.
2026-11-09 21:10:08 -08:00
Drew Dibble a9a061c8a9 Merge main into type-3d-redraw
# Conflicts:
#	docs/target-app-parity.md
2026-11-09 16:36:25 -07:00
dotCooCoo faea85f772 Fix #6125: raster exports keep a Screen glow on the bare page
A raster export filled its background into the page before the art, so
blend modes read it as art underneath: an Outer Glow in Screen mode, the
default, turned white on a white background and was missing from PNG and
JPEG files while the canvas showed it. Blending against nothing also
differed from the canvas for Difference and the other modes.

The background now goes under the finished art, in frames and in object
thumbnails. A blend mode reads only the art below it. A white object
under a Difference object still inverts.

Page Isolated Blending no longer changes a raster export on a bare page.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-09 13:02:28 -08:00
Drew Dibble bd779077e5 Merge main into type-3d-redraw
# Conflicts:
#	crates/ui-egui/src/canvas/tests.rs
#	docs/target-app-parity.md
2026-11-09 09:49:25 -07:00
Drew Dibble b9dbd867f6 Canvas: a 3D effect changed on type or a group redraws where its solid was
When an edit changes an object, the canvas draws again only the area
the object painted before and after it, measured by its cull bounds.
Type and groups with Extrude & Bevel, Inflate or Rotate were measured by
their letters or members, not by the solid the effect draws, which
reaches well past them. Making the solid shallower or turning it left
pieces of the old solid on the canvas outside that area until the view
was drawn again.

An object with a 3D effect is now measured by the effect's art, as the
renderer draws it: with its geometry effects, each object in it with
the raster effects of its own fills and strokes and the art its brushed
strokes draw, grown by the reach of the object's own raster effects.
One function measures that art for every bounds the renderer and
exports take, where three copies of it disagreed.

- The renderer measures the art it draws. What it makes to measure an
  object it holds until the frame ends, with what the art read from
  outside the object (fonts, symbols, color settings), and keeps it
  with the art it draws if it draws the object while that is
  unchanged. Art measured but not drawn (culled, off the view) isn't
  kept. It holds at most as much as it keeps for the art it draws
  (64 MB); past that, an object is made again to draw it. An object
  whose 3D effect builds nothing (Revolve on a group, a solid too
  complex) is measured as it is drawn otherwise, as without a renderer.
  FrameStats::three_d_built counts the 3D art the renderer makes.
- The canvas measures what an edit changed through the renderer that
  drew the frame shown (Renderer::changed). When it draws the part on
  its own thread, it draws it with the art it made to measure it; when
  the render worker draws the frame, or nothing in view changed, that
  art is let go. Under Overprint Preview or Separations Preview, which
  draw copies of the objects, the edit is measured afresh. An object
  the frame doesn't draw (hidden, in a hidden group or layer, or
  hidden by the view) is measured as nothing, before and after, so an
  edit to it builds no solid: changed() without a renderer counts it as
  nothing too, where it measured it. The canvas measures the edit
  before it knows where the frame is drawn: when the worker draws it,
  the solids it built to measure it are built again there.
- Fit to Artwork Bounds, the Artboards panel's check for it and
  per-artboard SVG export measure every artboard through one renderer
  (Renderer::art_bounds_on, Renderer::effect_bounds), which keeps each
  object's bounds for the pass, a group holding 3D art or a hidden
  member included, so each solid is built once: 30 extruded type
  objects over 30 artboards take about 2 ms, where measuring their
  letters took under 1 ms and building each solid for each artboard
  would take 70 ms. Objects with brushed strokes, which culling leaves
  room for and exports don't, are measured on each artboard. The
  Artboard tool's canvas menu keeps whether Fit to Artwork Bounds has
  art to fit for the document's revision, where it measured it on
  every frame it was open.
- Other exports, Object › Rasterize and changed() without a renderer
  build the art on each call, as 3D paths and groups already did.
  Exports and Rasterize now take the solid of 3D type, where they
  cropped it to the letters, and the art its brushed strokes draw, not
  the room culling leaves for it. Rasterize on drawn 3D art measures it
  once while the art is kept.
- A shadow after a 3D effect, on the object or on a fill after the
  fill's own 3D effect, reaches past the solid once; it was counted
  twice.
- With Crop Marks or Vector Halftone, which draw other art, the object
  is measured by that art, as before.
- Culling leaves room for a 3D object's brushed strokes as for any
  object's, and the renderer measures a 3D object again when the brush
  library changes: a brush widened past that room would leave the
  object culled.

A document's first frame builds the solid of every 3D object, those off
the view too, to know whether it reaches into the view: 200 extruded
words with 150 off the view take about 12 ms, where measuring their
letters took 6 ms.

Type whose object effects leave it as it is (Revolve builds no art from
type) is measured by its own box grown to hold its glyphs, as plain
type is; it was measured by its box alone, which needn't hold them.
2026-11-08 23:17:50 -07:00
BFlat cc02f7b35e Merge pull request #4379 from dotCooCoo/fix/shape-builder-merge-off-ui-thread
Shape Builder: a large selection merges on another thread
2026-11-09 13:10:54 +08:00
BFlat a32bd3dc92 Merge pull request #6234 from dotCooCoo/fix/save-screens-unknown-options
Save: the save commands and Export for Screens refuse an unknown option key
2026-11-09 12:49:37 +08:00
dotCooCoo fc728b909e Merge main into fix/shape-builder-merge-off-ui-thread 2026-11-08 20:23:21 -08:00
dotCooCoo 3258785476 Merge main into fix/ai-os23-2831 2026-11-08 19:44:24 -08:00
dotCooCoo 67e3d6f13f Merge main into fix/save-screens-unknown-options 2026-11-08 19:13:11 -08:00
dotCooCoo d3a323284c Merge main into fix/ai-os23-2831 2026-11-08 18:52:59 -08:00
dotCooCoo bf4ec4253d Merge main into fix/save-screens-unknown-options 2026-11-08 18:13:32 -08:00
dotCooCoo e064698086 Merge main into feat/jxl-import 2026-11-08 18:04:45 -08:00
dotCooCoo e1798597f3 Fix as_png calls after merging main 2026-11-08 17:43:33 -08:00
dotCooCoo aa74ee433c Merge main into feat/jxl-import 2026-11-08 16:52:56 -08:00
dotCooCoo 918ef940cb Merge main into feat/plugin-importers 2026-11-08 16:45:20 -08:00
dotCooCoo 8579f436f7 Merge main into fix/save-screens-unknown-options 2026-11-08 16:12:24 -08:00
BFlat 9499281b49 Merge pull request #5029 from dotCooCoo/fix/batch-override-export-for-screens-folder-r
Actions: Batch's Override Export sends Export for Screens files to the destination folder
2026-11-09 07:47:42 +08:00
BFlat 7adc2f66a3 Merge pull request #6109 from dotCooCoo/feat/ai-symbol-libraries
.ai import: symbol libraries open with their symbols
2026-11-09 07:47:36 +08:00
dotCooCoo 4c3f0b4c33 Merge main into fix/save-screens-unknown-options 2026-11-08 15:32:45 -08:00
dotCooCoo 421189893f Plug-ins: composite fonts stay out of importer filters; base64 size is checked first
File › Open imports .vccomposite files as composite fonts, so an importer
listing that extension no longer shows it under its own filter or in
document.formats. File › Open gets a Composite fonts filter.

document.open refuses dataBase64 that decodes past the importer input
limit before decoding it. A file whose start shows a built-in format
still opens as that format, as it does by path.
2026-11-08 15:18:31 -08:00
dotCooCoo b8ab981058 Merge main into feat/ai-symbol-libraries 2026-11-08 14:56:51 -08:00
dotCooCoo 4fff063dcc Merge main into fix/export-unknown-options 2026-11-08 14:25:57 -08:00
dotCooCoo 87cd34bbd1 Merge main into fix/shape-builder-merge-off-ui-thread 2026-11-08 14:21:52 -08:00
dotCooCoo 973503a2b1 Batch: Override Export's folder for Export for Screens no longer warns of one shared file
A data set batch whose Override Export gives each data set its own export folder no longer reports that every data set saves over the same file.
2026-11-08 13:20:48 -08:00
dotCooCoo d5bb58aad5 Merge main into feat/plugin-importers 2026-11-08 12:38:22 -08:00
dotCooCoo 9518dc0e3a Merge main into feat/plugin-importers 2026-11-08 12:33:17 -08:00
dotCooCoo 87d8938df1 Merge main into fix/batch-override-export-for-screens-folder-r 2026-11-08 12:18:17 -08:00
dotCooCoo 25ecee0c40 Color: Generic CMYK tints gain as CGATS TR 001 gives for SWOP printing
Generic CMYK's tone curves now pass through the CGATS TR 001 tone value
increase of each ink at 40% and 50% (C 20.0, M 17.6, Y 16.8, K 21.6 at
50%), and its 16 Neugebauer primaries mix linearly, so a single ink's
tints gain as TR 001 gives. The 300% ink limit stays. The primaries, the
black-generation curve and the black point are VectorCraft's own values.

- The black point is 90% black under neutral CMY that fills the ink
  limit: sRGB black separates to C75 M68 Y67 K90 instead of C67 M67 Y67
  K100.
- Black follows a curve over the gray component of the black-free
  solve, raised by how far that solve stays lighter than the target.
  Grays from sRGB 177 up carry no black, and sRGB 128 gray separates to
  C50.5 M42.2 Y42.0 K7.5 instead of C38.8 M26.3 Y29.9 K24.4.
- Display of CMYK fills, swatches and imported CMYK values and the
  separation of RGB and Lab colors use the same model.

Paper white still separates to C0 M0 Y0 K0, but other separations
change, and so does the display of CMYK colors with ink. Tests that held
numbers of the old model are updated.
2026-11-09 03:14:41 +07:00
dotCooCoo c94269ff72 Type: Composite Fonts… sets each class of character in a font of its own
Squashed from #6244 onto main.
2026-11-09 03:08:23 +07:00
dotCooCoo faa7b8ef6a Links: a missing linked file keeps its relative folder when a moved document is saved
Squashed from #6231 onto main.
2026-11-09 03:08:23 +07:00
bflatastic 02d877d043 .ai import: the unread-PDF-part test expects the cut-short note for its large image 2026-11-09 02:54:32 +07:00
dotCooCoo d5fc96327e Merge main into fix/shape-builder-merge-off-ui-thread
# Conflicts:
#	crates/engine/src/lib.rs
#	crates/engine/src/session/documents.rs
2026-11-08 11:33:51 -08:00
dotCooCoo 19f7fb774b Merge main into feat/plugin-importers 2026-11-08 11:30:49 -08:00
dotCooCoo fd3482c272 Merge main into fix/shape-builder-merge-off-ui-thread 2026-11-08 11:28:00 -08:00
SBaksa d329dd05e1 Export for Screens: a leading dot in an artboard name is a dash
A name like .draft would otherwise write a hidden file on Unix-like systems.
2026-11-09 02:03:08 +07:00
SBaksa 0b3ea7c1d2 Export for Screens: an artboard name keeps its dots (#6143)
The artboard and asset file stems came from unique_file_names, which turned
every character except letters, digits, - and _ into a dash, so the dot of
example.artboard became a dash. Export for Screens now uses a variant that keeps
dots (a name of only dots takes the Artboard-N fallback); safe_file_name still
drops trailing periods and guards device names.
Test: an_artboard_name_keeps_its_dots.
2026-11-09 02:03:08 +07:00
dotCooCoo 425cf48a18 Plug-ins: an imported document has no native PDF save setting 2026-11-08 10:38:29 -08:00
dotCooCoo 79351d7063 Merge main into feat/plugin-importers 2026-11-08 10:25:28 -08:00
dotCooCoo 2ecd593e61 Plug-ins: a file past an importer's limit is refused from its size, without reading it
document.open by path reads the first 64 KiB of a file only an importer
reads to look for a built-in format. A larger file is read to the limit
only when its size is within the limit, so an over-limit file costs no
128 MiB read and a built-in file over the limit is not held in memory
twice.
2026-11-08 10:20:53 -08:00
dotCooCoo 01ff8464c4 Merge main into fix/save-screens-unknown-options 2026-11-08 10:08:30 -08:00
dotCooCoo e152973836 Save: saved Export for Screens keys no longer read don't block the export
The Export for Screens dialog and the Asset Export panel open
remembered settings and presets without the row and settings keys the
export no longer reads, and without settings for a format it doesn't
write, so their export and Save Preset are not refused.

A format row and a format's settings take `params`, an exporter
plug-in's own, and `security.passwordRequired`, which remembered
settings hold where PDF passwords were left out, and `settings` takes an
exporter plug-in's format, as before the key check. assets.export and assets.settings.set check the
rows and settings they are given as document.exportForScreens does.
An unknown top-level key of document.exportForScreens gets the
command's usual "the command takes" error.

The MCP save and Export for Screens calls no longer check the params
themselves: Session::execute checks them before anything runs.
2026-11-08 09:51:22 -08:00
dotCooCoo 1ce9e9e9eb Merge main into feat/plugin-importers 2026-11-08 09:41:06 -08:00