Commit Graph
4562 Commits
Author SHA1 Message Date
bflatastic 79572cbf6c Revert "Cut over roxmltree, png, tiff, psd, and trace codecs to craft-libs (#7638)"
This reverts commit bada8e1710. #7638 merged with its gate failing: besides six unformatted
files, render and trace don't compile against craft-libs e2dc681 (u64::from(usize) in
render/src/paint.rs, a private craft-libs function, and a call with one argument too many in
trace/src/opaque.rs), so main's CI fails. The tree is that of 0adbdce6cb, whose CI passed.
2026-11-15 02:26:07 +07:00
dotCooCoo 7ef603db40 Live Paint: bucket scopes follow the stroke an edge draws
The Live Paint Bucket's connected and samePaint scopes kept their own
copies of the face flood and the edge walk. They read an edge's stored
stroke paint, so an edge that is fully transparent or whose stroke is
hidden stopped a face flood and matched as stroked. The face flood also
required outlines within 0.0001 pt of an edge, which a curved face that
the planar map refits can miss.

The engine now calls the shared flood_faces and connected_edges that
Live Paint Selection uses (#7342), and compares edges by lp_stroke_paint,
the paint of the topmost stroke that draws. The tool records that same
paint as the match for a double-click or triple-click on an edge. The
params text and docs/mcp.md describe the drawn stroke paint.
2026-11-14 11:24:56 -08:00
dotCooCoo 0b1e1e99a1 Effects: Effect › SVG Filters lists default filters
Effect › SVG Filters and Apply SVG Filter list 14 default filters in
every document, before the document's own. The set, the names and the
values are VectorCraft's own, written with SVG 1.1 filter primitives
(crates/effects/src/svg_filter/defaults.rs).

A document holds none of the defaults until one is applied or edited
there. svgFilter.apply with a default's name copies it into the document
in the same undo step, and svgFilter.define with replace naming a default
adds the edited copy under that name. The copy is listed in the default's
place. svgFilter.delete refuses a default the document holds no copy of,
and deleting the copy lists the default again.

svgFilter.list also returns the defaults. svgFilter.apply takes index and
item to put the filter in the place of an applied SVG filter, which the
dialog's OK uses when it is opened for an applied effect; an index that
is not an applied SVG filter, null included, is an error.
2026-11-14 11:19:22 -08:00
dotCooCoo f0c920fa8e Shape Builder: leave out Live Paint faces and edges with an opacity of 0
A visible, unlocked Live Paint face or edge whose own opacity is 0 draws
nothing, as Expand and the edge stroke check already treat it, but the
Shape Builder still took it as an input. It now leaves such faces and
edges out; hidden and locked parts keep their current handling.
2026-11-14 11:15:41 -08:00
dotCooCoo 1bd269b4ed Type: Smart Punctuation replaces ligatures, extra spaces and fractions and reports results
Type > Smart Punctuation... now opens its own dialog in place of the
generic parameter form. It has the seven replacement options, Replace In
(Selected Text Only or Entire Document) and Report Results, which lists
how many of each chosen replacement were made after OK. The dialog opens
on the options confirmed last.

- type.smartPunctuation takes fiLigatures, flLigatures, spaces and
  fractions, all off by default, so existing calls behave as before.
- Ligatures write U+FB00 to U+FB04, longest match first. Expert
  Fractions writes the one-character fractions Unicode encodes, and
  skips one with a digit, a slash or a decimal point next to it, across
  a change of style too. Either is written only where the run's font
  has a glyph for it.
- Smart Spaces keeps one space after a period, across runs. An inline
  graphic separates the text on either side of it.
- The reply adds a replaced object with a count per kind. Nothing to
  replace still records no undo step.

Docs: docs/mcp.md, docs/type-parity.md.
2026-11-14 11:09:09 -08:00
Dylan M. Taylor bada8e1710 Cut over roxmltree, png, tiff, psd, and trace codecs to craft-libs (#7638) 2026-11-14 13:49:50 -05:00
dotCooCoo 69ce720e46 Live Paint: Live Paint Selection does nothing on a triple-click
main's Live Paint Selection double-click (#7342) replaced the tool's
catch-all pointer arm with an explicit DoubleClick arm, so the
TripleClick kind this branch adds left the match non-exhaustive. The
tool now takes no action on TripleClick: the third click's press and
release already select what a click selects, as before the merge.

The merge also took main's import list in the engine's Live Paint
commands, which no longer named Paint while this branch's Merge code
still uses it; Paint is imported again.
2026-11-14 10:44:26 -08:00
dotCooCoo 924aa258d0 Merge main into fix/shape-builder-open-path-drawn-fill 2026-11-14 10:29:06 -08:00
dotCooCoo 1c4cc88e90 Merge main into feat/live-paint-bucket-multi-click
# Conflicts:
#	crates/tools/src/builder/live_paint.rs
2026-11-14 10:19:24 -08:00
dotCooCoo 7cbbf97b74 Merge main into fix/pdf-art-bounds-printing-layers 2026-11-14 10:03:22 -08:00
dotCooCoo 7ff186bd31 Type: a selected character's alternate glyphs show beside it on the canvas
With a Type tool and exactly one character selected, the canvas shows up
to five of that character's alternate glyphs in a row beside it. A click
runs the command the Glyphs panel's double-click runs, as one undo step;
an arrow at the end opens the Glyphs panel on Alternates for Current
Selection when the character has more. Preferences > Type > Show
Character Alternates (showCharacterAlternates, on by default) turns the
row off.
2026-11-14 10:00:04 -08:00
dotCooCoo 951f689e8f Export: an HP-GL plot of the art's bounds measures images by the art their effects add
An image or a gradient mesh with a visible Crop Marks effect, or with a
Vector Halftone effect that makes dots, is measured in a plot of the art's
bounds. A halftone that makes no dots, such as one past the effect's cell
limit, leaves the image out as before.
2026-11-14 09:59:27 -08:00
dotCooCoo f5312d795f TIFF: stored CMYK TIFFs with alpha keep their inks; wide and large files read
A document saved with a CMYK TIFF whose alpha follows another extra
sample keeps it as a CMYK image with its ink amounts, so EPS and PDF
export write the same inks as before; Open and Place still read such a
file with its alpha.

- A CMYK file with two or more samples past its inks and its
  ExtraSamples values, which the decoder gives no color type, reads the
  alpha that one of those values names, and its Orientation tag is read.
- A TIFF with extra samples whose samples take more than the decoder's
  256 MB buffer reads, up to the decoding allowance.
- A file read as black-is-zero gray is read in place instead of copied.
2026-11-14 09:58:26 -08:00
dotCooCoo 7e84899786 Merge main into fix/graph-series-group-opacity 2026-11-14 09:52:39 -08:00
dotCooCoo 283c13dfd8 Merge main into fix/type-no-op-undo-more 2026-11-14 09:49:48 -08:00
dotCooCoo 769d14ee7a Merge main into feat/arrange-nest 2026-11-14 09:47:03 -08:00
dotCooCoo b554fc3b10 Merge main into perf/flatten-held-guides-one-walk 2026-11-14 09:45:47 -08:00
dotCooCoo f43d16268a Global Edit: rotated copies are matched and take each edit turned by their angle
Select > Start Global Edit now also takes rotated copies of the selected
object or group, in the highlights before the start, in the Global Edit
Options' Results count and in the started edit. An edit reaches each copy
turned by that copy's angle, so an anchor moved to the right on the
selected object moves downward on a copy turned a quarter turn clockwise.

The copy's angle is the one that lays the selected object's anchors and
handles on the copy's in order (a least-squares fit in the unit box),
and the copy is the same shape when each point then lies within a
millionth of the larger side of the copy's box. Reflected copies and
copies stretched along one direction stay unmatched. With Size, a rotated
copy matches when it is the object rotated without scaling. The no-match
message now names only reflected copies, in every catalog that has it.

Docs: docs/mcp.md, docs/gaps.md, docs/ui-parity.md and the evidence list
in docs/target-app-parity.md.
2026-11-14 09:43:51 -08:00
dotCooCoo c51d14262f Merge main into fix/composite-font-lent-document-only 2026-11-14 09:42:51 -08:00
dotCooCoo a49dd999f5 Merge main into fix/convert-to-text-locked-frames 2026-11-14 09:42:27 -08:00
dotCooCoo 3674865d62 Merge main into feat/smooth-keeps-corners 2026-11-14 09:42:14 -08:00
dotCooCoo b4eee0faca Merge main into feat/ai-save-graph-records 2026-11-14 09:42:05 -08:00
dotCooCoo 0bd4a1c331 Merge main into fix/artboard-history-index-wrong-type 2026-11-14 09:41:57 -08:00
BFlat fcfad959ff Merge pull request #7588 from dotCooCoo/fix/mesh-point-color-panel
Color: the panels show the color and opacity of picked gradient mesh points
2026-11-15 00:41:54 +07:00
BFlat 0342616e15 Merge pull request #7459 from dotCooCoo/fix/followup-7304
Sort Objects: an opacity sort that gives marks their own opacities changes nothing
2026-11-15 00:41:36 +07:00
BFlat 53cc597fbd Merge pull request #7554 from dotCooCoo/fix/place-text-utf16-no-bom
Place: text files in UTF-16 without a byte-order mark place as UTF-16
2026-11-15 00:41:11 +07:00
BFlat 9735165c40 Merge pull request #7556 from dotCooCoo/fix/rasterfilters-deterministic
Tests: raster effects draw the same twice while another test assigns a document profile
2026-11-15 00:40:50 +07:00
BFlat 8c5494bccc Merge pull request #7342 from dotCooCoo/feat/live-paint-selection-double-click
Live Paint: a Live Paint Selection double-click selects the connected faces or edges
2026-11-15 00:40:41 +07:00
BFlat d0b37826c0 Merge pull request #7338 from dotCooCoo/fix/colored-light-seams
3D: colored-light facets close their shared edges over an underlay
2026-11-15 00:40:19 +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
BFlat 01e66f0a19 Merge pull request #7477 from dotCooCoo/fix/dimension-second-point-guides
Dimension tool: no construction guides through a first point off the anchors
2026-11-15 00:39:29 +07:00
BFlat d89f593bfa Merge pull request #7380 from ncic/pencil-cursors
Pencil: shows where a stroke continues, connects and closes
2026-11-15 00:38:01 +07:00
dotCooCoo 4509c208d6 Merge main into perf/flatten-held-guides-one-walk 2026-11-14 09:31:15 -08:00
dotCooCoo ed03357502 Merge main into feat/arrange-nest 2026-11-14 09:12:06 -08:00
dotCooCoo 77d4c1cc33 Merge main into fix/graph-series-group-opacity 2026-11-14 09:06:41 -08:00
dotCooCoo ae39914b9f Remove Breaks: a partly selected story's frames are gone through once
When only some frames of a threaded story are selected, Remove Line
Breaks and Remove Paragraph Breaks built the story's frame list again
for every selected frame of it, so the work grew with the square of the
frame count. The command now notes a thread once its story is found not
to be selected in full, and reads that thread's other selected frames
on their own without going through the thread again. Each selected
frame still loses its own breaks, in the order selected, as one undo
step.

A test-only counter in threads.rs counts story_in calls, and
remove_breaks_goes_through_a_partly_named_story_once checks both break
kinds with frames named out of order and twice.
2026-11-14 08:58:57 -08:00
dotCooCoo 10225d82e5 Export: an HP-GL plot of the art's bounds leaves out images in symbol art
The bounds of a plot of the art's bounds no longer count the objects a
plot leaves out (placed images, objects at 0% opacity, unpainted paths)
when they sit in the art of a symbol. An instance whose symbol holds
nothing else is left out, so a document with only such instances is
refused with "nothing to export". The art of a dynamic symbol, and of a
group with effects, fills or strokes of its own, is measured whole.
2026-11-14 08:50:21 -08: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 a4c513f101 Live Paint: Make starts a closed edge where its path starts
The edge built from a closed path that nothing cuts started at the anchor the
planar map gave it, so dashes, a width profile or a brush began at a different
place than on the path. The edge now starts at the anchor where its path
starts. An edge with no anchor there takes its dashes from its own start and
leaves off the profile and brush.

The command description and the progress doc say that dashes aligned to
corners and path ends are not fitted to cut edges.
2026-11-14 08:39:47 -08:00
dotCooCoo da8f6ab721 Smooth tool: a whole closed path with a corner starts nearest its old start
When a drag reaches every anchor of a closed path that has a corner, the
path is cut open at the corner and refitted. The result started at the
corner unless the refit kept an anchor exactly at the old first anchor.
It now starts at its point nearest the old first anchor, at an anchor
there or at a new anchor split into the curve, as the other closed-path
cases do.
2026-11-14 08:31:41 -08:00
dotCooCoo e001227ce1 Selection: effects on a hidden member of a clipping group leave clicks alone
A clipping path that is a group clips by its visible members only. A
geometry effect on a hidden member made clicks inside the clip group
count the group's type by its glyph outlines, where they count it by its
frame when nothing has an effect.

Clicks now ask `effects::reshaped_clip_outline`, which gives a region
only when an object the clipping path clips by has a geometry effect.
`clip_outline` uses the same function.
2026-11-14 08:29:00 -08:00
dotCooCoo 501dad41a1 Merge main into fix/file-folder-param-classification 2026-11-14 08:23:36 -08:00
dotCooCoo 506f48daea .ai save: live graphs are written as graph records
Both .ai writers now write a graph that the .ai reader reads back as the
same graph with its records, so a saved graph opens again live, with its
table, type, axes and settings: %_Gs, the Gb, Gy, Gd, Ga/GA, Gz, Gc and
GC records, the graph's group with each series' group followed by a Go
tag, and %_GS (the published .ai file format specification, section 17
and appendix A). The records are the ones the reader in
crates/eps/src/import/ai/read/graph.rs (#7311) reads, in the form it
reads them.

The shared encoder in crates/eps/src/ai_graph.rs decides first whether
the reader keeps the graph whole: a column, stacked column, line or area
graph with a legend, settings the records carry, a table of 1 to 10,000
categories and 1 to 256 series with finite values, labels of at most 255
bytes without control characters, and art the reader attaches the graph
to. Any other graph is written as its art, with the existing warning, as
is a graph whose art the editing writer's object limit cuts short.
Tick-mark counts of 0 are written as 1, the count the reader keeps for
both.

Bar, stacked bar, scatter, pie and radar graphs, stacked column and area
graphs whose series marks carry a stroke their legend swatch lacks, and
line graphs with markers are still written as their art, with the
warning.
2026-11-14 08:20:05 -08:00
dotCooCoo 46c9f922c2 Type: a document draws only its own composite font definitions
While a document's composite fonts are lent, type naming a composite font
the document does not define is set as in a missing font, even when the
front document or the document a placed document sits in defines one of
that name. A document that defines no composite font gets an empty scope
too, so a document in the back, a tiled window, a thumbnail or an export
made while another document is in front no longer borrows the front
document's definitions. A temporary document drawn while a document draws
keeps that document's fonts, and type laid out with nothing lent still uses
the registered ones.

- FontDb::composite_font looks only in the latest loan of the database.
- lend_composite_fonts lends an empty set at the top level when the
  database holds composite fonts; lend_document_composite_fonts also lends
  one inside another loan, for placed documents, back-document measuring
  and threaded reflow on open.
- A loan that leaves out a registered font gets its own generation, so
  layouts made with that font are not reused under it.
2026-11-14 08:16:02 -08:00
dotCooCoo 261fa40d24 Merge main into fix/convert-to-text-locked-frames 2026-11-14 08:11:13 -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 ddae31bad4 TIFF: alpha after another extra sample places and opens as transparency
A CMYK or grayscale TIFF reads its alpha from the first extra sample whose
ExtraSamples value is 1 or 2, wherever it comes among the extra samples, at
8 and 16 bits, chunky or planar. TIFF 6.0 gives each extra sample its own
ExtraSamples value, in order, and asks readers to skip extra samples they do
not use.

- A CMYK file with alpha after an unspecified extra sample was kept as
  opaque inks; it is now shown as RGB with its alpha.
- A grayscale file with alpha after an unspecified extra sample opened
  opaque.
- A six-sample CMYK file whose ExtraSamples tag lists only an unspecified
  value no longer reads that sample as transparency.
- A white-is-zero grayscale file with an alpha channel opens; it failed to
  read before.
2026-11-14 08:00:43 -08:00
dotCooCoo bbe9480656 Shape Builder: an open path counts as filled by the fill rows that draw
Consider Open Filled Paths as Closed decided whether an open path was
filled from its top fill row only. A path whose top fill is None over a
painted lower fill stayed open, and one whose only painted fill was
hidden or at 0% opacity closed. The region map, merge inputs and the
Clicking Stroke Splits the Path cut now use Appearance::has_drawn_fill,
so any shown, painted fill row above 0% opacity counts, as the canvas
paints it.

The Shape Builder's Live Paint face and edge filters had the same
top-row check; they now use has_drawn_fill and has_drawn_stroke, so a
face or edge is taken when one of its rows draws.
2026-11-14 08:00:35 -08:00
dotCooCoo 10f94b544d MCP: livePaint.info lists every appearance row of a face or edge
livePaint.info reported only the top fill of each face and the top
stroke and weight of each edge, so a lower row that draws under an
unpainted top row was not visible to a client. Each face and edge now
also carries `appearance`: every fill and stroke row, bottom first, as
{index, kind, paint, opacity, visible, width (strokes only), drawn}.
`drawn` follows Expand's rule row by row: the part is shown with an
opacity above 0, and the row is shown, has paint and an opacity above
0, and a stroke has a weight above 0. The existing `fill`, `stroke`,
`width`, `bounds`, `sources`, `gapOptions` and `gaps` fields are
unchanged.

The params text no longer says the group defaults to the selection:
the command takes `group`, or `id` when `group` is left out, and
returns an error without either.
2026-11-14 07:57:56 -08:00