Commit Graph
981 Commits
Author SHA1 Message Date
BFlat 20ae675b9b Merge pull request #7622 from dotCooCoo/fix/graph-label-restyle-keeps-plot
Graphs: restyling the labels leaves the plot where it is at the next redraw
2026-11-15 04:22:27 +07:00
BFlat 88b83a8689 Merge pull request #7546 from dotCooCoo/fix/svg-raster-keeps-resolution
SVG import: a raster stored again as PNG keeps its resolution
2026-11-15 04:22:10 +07:00
BFlat 1a8614f00c Merge pull request #7550 from dotCooCoo/fix/graph-wedge-legend-black-kept
Graphs: a black fill set on wedge legend labels stays black on dark wedges
2026-11-15 03:22:47 +07:00
BFlat 85ce363b93 Merge pull request #7548 from dotCooCoo/fix/objects-on-path-widgets-flatten-once
Objects on Path: the widgets flatten the group's path once per lookup
2026-11-15 03:22:27 +07:00
BFlat 46a7dd24f6 Merge pull request #7646 from dotCooCoo/fix/nine-slice-symbol-lookups
Symbols: measuring and picking in 9-slice instances reuse one set of symbol boxes
2026-11-15 03:20:12 +07:00
dotCooCoo df257c2d53 Merge main into fix/svg-raster-keeps-resolution 2026-11-14 12:12:06 -08:00
dotCooCoo a512bf5ce6 Image resolution: a WebP chunk length past the end of the file reads no resolution
The WebP chunk walk added the chunk length to the offset without a
checked add, so a length near u32::MAX overflowed on 32-bit targets
(wasm32) with overflow checks on. It now reads the chunk through the
same checked slice helper as the PNG, JPEG and TIFF readers.
2026-11-14 11:37:16 -08:00
dotCooCoo 3534dd7343 Symbols: measuring and picking in 9-slice instances reuse one set of symbol boxes
Measuring an instance of a symbol with 9-slice guides looked its map up
through Document::symbol_slice_map, which scanned the symbol list and
measured the art box again in new quick boxes, and NineSliceMap::place
and most_placed made new quick boxes of their own. Each of those boxes
built the symbol name index over the whole list on its first lookup, so
every 9-slice instance measured read every symbol once or more. Picking
an object inside a 9-slice instance's art with Direct Selection did the
same for each object of the art that holds a symbol instance.

- SymbolBoxes cuts the guides through the art box it has measured
  (SymbolBoxes::slice_map), and places and counts the art with itself as
  the boxes, in the mode quick boxes use. Art with no 9-slice map is
  moved by the instance's transform without looking the map up again.
- Document::symbol_slice_map, place_symbol_art,
  place_symbol_art_unclipped and edited_nine_slice use one set of quick
  boxes for each call.
- symbol_part_at and symbol_part_outline keep one set of quick boxes
  for the instance's art.

The new tests count the symbols read. Measuring 200 instances of a
symbol with guides and 200 of a symbol with guides holding it, among
2,002 symbols, read 1,205,204 symbols before; now each set of boxes
reads the list once. Picking in a 9-slice instance whose art holds 20
instances read 84,042 symbols among 2,001; now it reads the list at
most twice.
2026-11-14 10:32:20 -08:00
BFlat e76f8d2c88 Merge pull request #7501 from dotCooCoo/fix/export-bakes-symbol-art-effects
Export: live effects in symbol art apply in PDF, EPS, print and DXF
2026-11-15 00:41:46 +07:00
BFlat 63129de08a Merge pull request #7595 from dotCooCoo/fix/image-scaling-alpha
Images: scaled-down images keep their color beside clear pixels
2026-11-15 00:41:27 +07:00
BFlat 3d895d9ac7 Merge pull request #7448 from dotCooCoo/fix/reverse-path-profiles
Paths: Reverse Path Direction turns the stroke's width profile and pen records with the path
2026-11-15 00:40:08 +07:00
dotCooCoo d3789f2b0c Graphs: outlined labels and opened .ai graphs keep the plot in place at the next redraw
- A graph opened from a .ai file now saves `placedArt` too. Moving or
  restyling one of its labels before the first redraw no longer moves or
  stretches the plot.
- Type > Create Outlines on a graph's labels now adjusts the saved bounds,
  so the outlines do not read as a move at the next redraw. A move of
  the graph made before the outlining is still followed.
- A `placedArt` with no width or height falls back to the bounds of all
  of the art, as a graph saved without it does.
- `art_bounds` moves to the doc crate so the .ai reader can use it.
- docs/mcp.md describes `placedArt`, `placed` and when a redraw follows
  a move.
- Tests: a legend across the top, a legend after a right value axis and
  legend labels of several lines keep the plot in place through label
  size changes; moving a category label on its own; outlining labels
  before and after a move; a flat `placedArt`; a label moved in an
  opened .ai graph.
2026-11-14 08:47:03 -08:00
dotCooCoo 4c67b8f73c Graphs: swatch edits reach the kept black legend fill of graphs that draw it
Only a pie with Legends in Wedges picks its legend labels' fill, white on
dark wedges. Paint visits and swatch mappings now leave its default black
legend fill out only for such a pie; other graphs draw the kept fill, so
it is visited and mapped like the other label fills. A color mode change
still leaves the default black legend fill as the default, so turning on
Legends in Wedges later keeps the white labels on dark wedges.
2026-11-14 08:02:17 -08:00
dotCooCoo 5c9717474f Graphs: restyling the labels leaves the plot where it is at the next redraw
The next redraw of a graph compared the bounds of all its generated art,
labels included, with the bounds stored at the last generation, and took
any difference for a move or scale of the graph. Label edits that change
the size of the type (a new size, an indent, a legend size) changed those
bounds without a move, so the plot shifted and stretched when the graph
was drawn again.

The spec now also stores the bounds of the art without type
(`placedArt`), and moves and scales are measured on those. Graphs saved
without them are measured on the whole bounds as before. Moving one
label with Direct Selection no longer moves or stretches the plot at the
next redraw.
2026-11-14 08:00:54 -08:00
dotCooCoo a3f65c677a Merge main into fix/graph-wedge-legend-black-kept 2026-11-14 07:24:01 -08:00
dotCooCoo 96a8a0c6d6 Merge main into fix/svg-raster-keeps-resolution 2026-11-14 07:23:45 -08:00
BFlat edefbd0e7d Merge pull request #7449 from dotCooCoo/fix/links-stored-brush-art
Links: placed images in stored brush art get Links rows
2026-11-14 21:55:20 +07:00
BFlat b541d97a06 Merge pull request #7586 from dotCooCoo/fix/blend-spot-stop-plates
Blend: steps between gradients linked to the same spot swatch keep its process plates
2026-11-14 21:54:47 +07:00
BFlat 35ac921b86 Merge pull request #7589 from dotCooCoo/chore/guides-shared-one-pass-helpers
Guides: document and layer guide edits share one set of one-pass helpers
2026-11-14 21:53:41 +07:00
BFlat 8863c41560 Merge pull request #7581 from dotCooCoo/fix/pdf-cmyk-image-keeps-icc
PDF export: CMYK images that keep their numbers carry their ICC profile
2026-11-14 21:53:33 +07:00
BFlat 5762ec729b Merge pull request #7598 from dotCooCoo/fix/symbol-art-bounds-nested
Symbols: a symbol nested in symbol art is measured by its own art
2026-11-14 21:53:07 +07:00
BFlat 64dd5eb174 Merge pull request #7418 from dotCooCoo/fix/reflected-dimension-line
Dimension tool: a reflected aligned dimension has its line on the reflected side
2026-11-14 21:52:10 +07:00
BFlat 508a0b0180 Merge pull request #7597 from dotCooCoo/fix/separations-spot-stop-plates
Print: a spot gradient stop prints its process plates on the separations
2026-11-14 21:51:47 +07:00
dotCooCoo 20529f1ffb Merge main into fix/graph-wedge-legend-black-kept 2026-11-14 06:08:51 -08:00
dotCooCoo 80854eb4b8 Merge main into fix/export-bakes-symbol-art-effects 2026-11-14 06:00:09 -08:00
BFlat 3c15649730 Merge pull request #7427 from dotCooCoo/fix/live-paint-expand-drawn-stroke
Live Paint: Expand keeps the faces and edges that draw
2026-11-14 20:37:59 +07:00
BFlat 9800d48e58 Merge pull request #7571 from dotCooCoo/fix/cmyk-tiff-alpha-16-bit
TIFF: a 16-bit CMYK or gray TIFF with alpha converts at 16 bits
2026-11-14 20:37:26 +07:00
dotCooCoo b3c2fdab21 Merge main into fix/svg-raster-keeps-resolution 2026-11-14 05:11:34 -08:00
dotCooCoo bc24ad5352 Symbols: a symbol nested in symbol art is measured by its own art
Document::symbol_art_bounds measured a symbol's art without the
document, so an instance of another symbol in that art counted as the
20 x 20 square. It now measures the art through SymbolBoxes, so a nested
instance takes its own symbol's art, and an inline graphic of the outer
symbol is sized by it and follows it when that symbol changes.

- The inline resolve order also follows the instances that size a
  symbol's art, so a symbol is measured after the type in the art of
  the symbols it holds is laid out again.
- A symbol whose art reaches itself through an instance and an inline
  graphic is an empty slot. Only instances that the measurement uses
  count, so one a clipping path hides adds no dependency.
- A missing symbol, a symbol without art, a symbol that holds an
  instance of itself, and nesting 32 deep still measure as the square.
- SymbolBoxes finds symbols through a name index built once, and the
  inline resolve pass and inline_art_bounds share one index, instead
  of scanning the symbol list for each lookup.
2026-11-14 05:10:41 -08:00
dotCooCoo 786dcc5581 Images: scaled-down images keep their color beside clear pixels
The image crate's filtered resize filters each channel on its own, which
is correct only for premultiplied pixels, and its thumbnail averages each
channel on its own too. Placed images, linked-image previews, MCP
captures and previews, metafile bitmaps, PDF downsampling and library
thumbnails passed straight RGBA to them, so the color under clear pixels
(usually black) bled into the opaque pixels beside them and darkened
cut-out edges.

vectorcraft_doc::resample scales such images with each pixel's color
weighted by its alpha: filtered resampling premultiplies the pixels in
place and divides them back after, and averaging takes the area-weighted
mean of the pixels each new one covers. Opaque and alpha-free images are
scaled as before. PDF export also multiplies the inks of a CMYK image
with alpha by that alpha before resampling them, and divides them by the
resampled alpha after.
2026-11-14 05:07:39 -08:00
dotCooCoo 98bf61bb53 Merge main into fix/objects-on-path-widgets-flatten-once 2026-11-14 05:00:14 -08:00
dotCooCoo 3597752c63 Merge main into fix/graph-wedge-legend-black-kept 2026-11-14 04:50:46 -08:00
dotCooCoo add6938441 Merge main into fix/links-stored-brush-art 2026-11-14 04:49:13 -08:00
dotCooCoo 1fe52fc5b6 Merge main into fix/separations-spot-stop-plates 2026-11-14 04:47:00 -08:00
dotCooCoo 6ab46b5fd8 Print: a spot gradient stop prints its process plates on the separations
A gradient stop linked to a spot ink that also carries process plates
(a DeviceN stop opened from a PDF) printed only its spot tint: print
separations and Separations Preview left its Cyan, Magenta, Yellow and
Black plates blank. They now print the stop's plates beside the tint.
With spots printed as process, each process plate prints the stop's
plate layered with the spot tint's process equivalent, as a separated
image's spot plates layer. Plates beside no spot link, or beside
Registration, still print nothing of their own.

The color visitor also skipped a freeform gradient's points, so print
plates kept their display colors and Document Color Mode left them
unconverted. The points are now visited as unlinked colors.
2026-11-14 04:46:10 -08:00
dotCooCoo 173a057beb Merge main into fix/reverse-path-profiles 2026-11-14 04:39:50 -08:00
dotCooCoo 7f17649731 Guides: document and layer guide edits share one set of one-pass helpers
Document::guides_to_saved, remove_edit_layer, retain_guides and the
artboard guide moves now call the layer helpers in node/ops.rs
(held_guides, strip_held, map_held), which Node::strip_ruler_guides and
Node::map_ruler_guide_positions already use. The copies in
document_guides.rs (picked_in_layers, drop_picked, edit_picked) are
removed. Picked stays for the walks through objects and opacity-mask art.

Results, guide order, copies kept for undo and the number of nodes each
walk looks at are unchanged.
2026-11-14 04:34:05 -08:00
dotCooCoo 3a6b016253 Blend: steps between gradients linked to the same spot swatch keep its process plates
A blend between two gradients whose matching stops are tints of one spot
swatch kept the link and interpolated the tint, but every step's stop lost
the process inks (C, M, Y, K) printed with the spot ink, so PDF export wrote
the steps as the spot ink alone.

- Matching stops linked to one swatch interpolate each process ink with the
  tint; a stop without process inks counts as none of each, and the ends
  keep their own.
- Gradients with different stop counts keep the inks at every resampled
  stop, interpolated with the midpoint-adjusted fraction between two stops
  of one swatch.
- A solid blended with a gradient brings no process inks of its own: its
  stand-in gradient no longer copies the other gradient's.

Stops of different swatches, or linked and unlinked stops, still blend into
unlinked process colors without plates.
2026-11-14 04:25:55 -08:00
dotCooCoo b3fd92ccdc PDF export: CMYK images that keep their numbers carry their ICC profile
A CMYK TIFF written again, a CMYK JPEG that the Compression settings
resample or compress again, and a CMYK image written with its mask as
its soft mask lost the CMYK profile the image embeds. Such an image is
now written in an ICCBased space holding that profile, byte for byte,
when output.profiles is all or taggedSource, and in PDF/A-2b, PDF/X-3
and PDF/X-4 unless profiles is destination. Pixels the Output settings
convert are never tagged with it.

A CMYK JPEG embedded as it is now follows the same rule instead of
always carrying its own ICC_PROFILE segments: with profiles none it is
DeviceCMYK, and PDF/X-1a export no longer refuses a CMYK JPEG that
carries a profile.

A profile tags an image only when it is at most 16 MiB and its declared
size, acsp signature, device class, CMYK color space and tag table are
well formed. Tagged CMYK documents blend transparency in the document's
CMYK profile space, not in an image's own; without that space they
blend in DeviceCMYK, or keep their RGB space in PDF/A.
2026-11-14 03:59:32 -08:00
dotCooCoo fe82f68d6f TIFF: a 16-bit CMYK or gray TIFF with alpha converts at 16 bits
Place, Open and relink read a CMYK or grayscale TIFF with an alpha
channel through extra_samples_tiff_rgba, which cut every 16-bit sample
to its high byte before converting it. A faint ink or a low alpha was
lost: cyan 129 at full alpha showed as white, and an alpha of 129
became fully transparent.

The samples are now normalized at the depth the file stores them, and
only the resulting color and alpha are rounded to 8 bits (a 16-bit
alpha to the nearest 8-bit value). Associated alpha is divided out at
16 bits. A pixel whose associated alpha is zero now has no color,
instead of keeping its stored color. TIFF 6.0's Associated Alpha
Handling section states this arithmetic and the zero-alpha rule for
RGB data; applying them to CMYK and gray samples is VectorCraft's own
rule.
2026-11-14 03:44:41 -08:00
dotCooCoo cde9a27b4f Objects on Path: the widgets flatten the group's path once per lookup
Finding where the Rotate All, Space, Move All and Select/Move widgets
go and testing whether the pointer is over one flattened the group's
path two to three times per widget. Starting a Space, Move All or
Select/Move drag flattened it five times, and one pointer move over the
group could flatten it eleven times. Each lookup now flattens the path
once and shares that measurement across the widgets:

- vectorcraft-doc makes `Measured` public with `stop` and `stops`, and
  adds `attachable`, which returns the path with its measurement.
- `Widgets::of` gives all four widgets for the canvas, `widget_at` gives
  the widget under the pointer, and `WidgetDrag::hit` gives the drag a
  press starts, each from one flattening. The Selection and Objects on
  Path tools and the canvas use them.
- A Space, Move All or Select/Move drag keeps its measurement and
  measures again only when the group's path changes.

Widget places, hits, cursors and drag results are unchanged. The new
`spine-count` feature of vectorcraft-doc exposes the flattening counter
to vectorcraft-tools' tests; it is enabled only as a dev-dependency and
leaves Cargo.lock unchanged.
2026-11-14 03:01:29 -08:00
dotCooCoo 083180075c Graphs: a black fill set on wedge legend labels stays black on dark wedges
On a pie with Legends in Wedges, the graph draws legend labels white on
dark wedges unless they keep a fill of their own, and it read a kept black
fill as no fill of its own. A label recolored from white to black on a dark
wedge went back to white the next time the graph was drawn.

The graph now marks the legend labels' fill as set when a command sets it
(legendFillSet in the graph's label styles, kept through save and open), and
draws a marked fill on every wedge, black included. Undo clears the mark. A
size, font or stroke change does not set it, and neither does a whole-pie
recolor that gives the legend labels different fills; one that gives them
all the same fill sets it. A swatch edit or color conversion leaves the
default black legend fill as it is, and keeps a non-black legend fill from a
file saved without the mark as the labels' own when it turns black.
2026-11-14 02:54:22 -08:00
dotCooCoo e7feeb311d SVG import: a raster stored again as PNG keeps its resolution
A raster that SVG import decodes and stores again (a TIFF, a BMP or any
other format that is not PNG, JPEG, GIF or WebP, or an image whose EXIF
or TIFF orientation turns or flips it) was written as a PNG with no pHYs
chunk, and a turned or planar CMYK TIFF was written without resolution
tags, so the image lost the resolution its file declares.

The converted PNG now carries the source's resolution in a pHYs chunk,
and the CMYK TIFF in its resolution tags, as Open and Place store them.
A quarter turn (orientations 5 to 8) swaps the horizontal and vertical
values. An image that declares no resolution is stored without one, and
an image stored unchanged keeps its own bytes.

The PNG, JPEG, TIFF, WebP and BMP resolution readers and the pHYs writer
move from the engine to vectorcraft-doc, so the SVG importer can use
them; the engine and renderer entry points forward to them unchanged.
2026-11-14 02:40:45 -08:00
dotCooCoo 9834c58b66 Merge main into fix/links-stored-brush-art 2026-11-14 02:26:44 -08:00
dotCooCoo ad31fe9527 Merge main into fix/reverse-path-profiles 2026-11-14 02:21:25 -08:00
dotCooCoo cee68c525a Merge main into fix/reflected-dimension-line 2026-11-14 02:15:24 -08:00
dotCooCoo 997aaa61b9 Merge main into fix/live-paint-expand-drawn-stroke 2026-11-14 02:08:40 -08:00
dotCooCoo df3d012e1f Merge main into fix/export-bakes-symbol-art-effects 2026-11-14 02:07:20 -08:00
dotCooCoo fb13d2045d Merge main into feat/cjk-tsume 2026-11-14 01:56:33 -08:00
BFlat 3c7d72f8bc Merge pull request #7333 from dotCooCoo/fix/graph-series-gradient-per-mark
Graphs: a kept gradient takes the same place on every mark of its series
2026-11-14 16:54:26 +07:00