Commit Graph
12834 Commits
Author SHA1 Message Date
BFlat c8bf38c6a1 Merge pull request #7092 from dotCooCoo/fix/dimension-guide-line-snap
Dimension tool: a point on a Smart Guide stays on the guide
2026-11-13 23:01:45 +07:00
BFlat 714e3a3821 Merge pull request #7135 from dotCooCoo/fix/docs-paragraph-style-options-rows
Docs: Paragraph Style Options rows name the dialog and its seven sections
2026-11-13 23:01:36 +07:00
BFlat cd1feada31 Merge pull request #7132 from dotCooCoo/fix/ui-docs-brush-libraries
Docs: `ui-parity.md` names all nine built-in brush libraries
2026-11-13 23:01:27 +07:00
BFlat 017db27090 Merge pull request #7131 from dotCooCoo/fix/flatten-grouping-sweep-meets
Flatten: grouping sweeps skip boxes that a box already holds or cannot join
2026-11-13 23:01:18 +07:00
BFlat dc5c3aa444 Merge pull request #7119 from dotCooCoo/perf/pathops-overlap-sweep
Path ops: Offset Path sweeps long, low paths along their length
2026-11-13 23:01:08 +07:00
BFlat 7372d7ee5e Merge pull request #7081 from dotCooCoo/fix/svg-rtl-text-anchor
SVG: right-to-left text with text-anchor start or end opens at its directional edge
2026-11-13 23:00:55 +07:00
BFlat 02445d5f02 Merge pull request #7130 from dotCooCoo/fix/eps-image-samples-once
EPS: an image drawn more than once writes its samples once
2026-11-13 23:00:45 +07:00
BFlat 0455c12162 Merge pull request #7127 from dotCooCoo/fix/thread-empty-last-paragraph-style
Type: a threaded frame's empty last paragraph keeps a format given to it
2026-11-13 23:00:36 +07:00
BFlat e9459df52b Merge pull request #7099 from jmverges/fix/7097-top-object-split-curve
Envelopes: a top object's side that is one split curve follows its parameter
2026-11-13 23:00:21 +07:00
dotCooCoo 4bf1e654e9 Merge main into feat/layers-drag-autoscroll 2026-11-13 07:59:51 -08:00
dotCooCoo 3e53884b7e Artwork Cleanup: remove quadratic work from three path repairs
Three repairs in crates/pathops/src/addons/cleanup.rs grew with the square
of the input. Each now gives the same results as before, bit for bit.

- Collinear anchors: removing an anchor called Vec::remove, so a straight
  run of n anchors took O(n^2) moves. The repair now tests each anchor once
  against the nearest kept neighbors, with the same test order, neighbors
  and stop rule, and moves the kept anchors into place once.
- Nearly Coincident Anchors (near_point_groups): every pair of anchors in
  touching grid cells was compared, so a cluster of A anchors cost O(A^2)
  checks. Groups now grow one anchor at a time and look only at anchors no
  group has taken, by exact position, skipping quarter-cell squares that
  lie out of reach.
- Rejoin Path Pieces (rejoin): every pair of ends within the gap was listed
  and sorted, O(P^2 log P) for P pieces meeting at one place. Each end now
  finds its first partner in the same order (turn, distance, then end
  order), searching by direction among the ends still free, and two ends
  that are each other's first partner meet.

Tests compare each repair with a copy of the old algorithm on generated
and focused inputs, count the work on dense cases (20,000-anchor runs,
4,000-anchor clusters, 2,000 pieces meeting at one point), and check Health
Check, Show and Fix on a 5,001-anchor path.
2026-11-13 07:58:36 -08:00
dotCooCoo 5f45e39108 Area type: Inset Spacing on a shaped frame is measured from the frame's edge
On a shaped frame, Inset Spacing keeps text that far from the frame's
edge, measured from the edge itself rather than along the line. Before,
the inset was trimmed from each end of a line after the frame's edge was
found, so on a sloped edge the text came closer than the inset: a
triangle (0,0), (100,0), (0,100) with a 10 pt inset gave the band
40..60 the span 10..30, where a 10 pt clearance gives 10..25.86. Holes
keep the same clearance.

A rectangular frame keeps its inset from each side as before, a
column's inner edges keep it once, and the gap kept beside a wrap
object along the line is unchanged.
2026-11-13 07:58:15 -08:00
dotCooCoo 7b024451a3 Merge main into fix/tabs-ruler-mixed-selection 2026-11-13 07:56:38 -08:00
dotCooCoo 776f3e7b00 Merge main into fix/export-unused-option-warning 2026-11-13 07:56:06 -08:00
ruchirguitar 66513d47fa feat(pdf): preserve Type 3 font text runs as named groups (#6740) 2026-11-13 21:25:10 +05:30
dotCooCoo 9fad6eb952 Objects on Path: Detach takes the chosen object off the path
With one Objects on Path group selected and an object chosen with its
Select/Move widget, Detach in the Properties panel's Objects on Path
Options section takes that object out of the group
(`object.objectsOnPath.detach {member?}`). The object goes just above
the group in the Layers panel, stays where it is drawn and is selected
alone. The other objects move up the path in their order: Auto Space
spreads them evenly again, and a Space value and the start are kept.
The group and its path stay with one object or none left. Hidden or
locked objects and groups are refused, and Detach is one undo step.
When the group is itself on another group's path, the object goes
above the outer group so it does not join that path.

Docs: docs/mcp.md, docs/target-app-parity.md. Detach and its undo name
have rows in every catalog, and the new message in the complete-message
catalogs.
2026-11-13 07:54:06 -08:00
dotCooCoo b043c3fa5d Merge main into fix/status-linked-files-keeps-notes 2026-11-13 07:52:33 -08:00
dotCooCoo 7e16f59c69 Merge main into fix/font-menu-nested-lock 2026-11-13 07:52:31 -08:00
dotCooCoo a12de6a39a Merge main into feat/guides-layer-rows 2026-11-13 07:51:49 -08:00
dotCooCoo c76e89c6ea Merge main into feat/revolve-graphics 2026-11-13 07:49:32 -08:00
dotCooCoo 80470a58ff Merge main into feat/warichu 2026-11-13 07:48:39 -08:00
dotCooCoo 036079902b Merge main into feat/pixel-snapping-options 2026-11-13 07:46:25 -08:00
dotCooCoo 98ea3231fa Tests: the vectorcraft-mcp tests remove the temp folders they create
The MCP unit tests wrote their files into one folder per process under
the temp dir (vectorcraft-mcp-test-<pid>, vectorcraft-mcp-fileio-<pid>,
testkit's mcp-cancel folder) and nothing removed them. The export-as and
links tests removed their folder at the end, so a failing test left it.

Each test that writes files now holds a TempFolder, a folder of its own
that starts empty and is removed with everything in it when the test
ends, also when it panics, as the findfiles tests' Tree guard does
(crates/engine/src/cmd/findfiles/tests.rs:49). A new test checks that a
leftover folder is emptied and that the folder is removed on drop and
after a panic.
2026-11-13 07:44:41 -08:00
dotCooCoo 39441540f7 Merge main into fix/nested-link-ids 2026-11-13 07:44:19 -08:00
dotCooCoo 5741c7bba0 Color: assigned profiles follow the document in front
The profiles Edit > Assign Profile gives a document stayed the active
color settings after another document came to the front, so a new,
opened or switched-to document drew and converted its colors through the
previous document's profile, and closing the last document left it in
use.

The session now puts the front document's profiles in use (the working
spaces where it has none, or no document is open) after a new or opened
document, a tab switch, a close, File > Revert, every command including
each batch step and Undo or Redo of Assign Profile, a failed batch or
plug-in call, a script's rollback and slice boundaries, a canceled
preview, undo group or undo scope, and for each document Export All Open
and the update of placed documents work in. The active settings are
rebuilt, and the documents redrawn, only when they change.
2026-11-13 07:44:19 -08:00
dotCooCoo 83aedc8871 Merge main into fix/quit-waits-for-clipboard-read 2026-11-13 07:43:55 -08:00
dotCooCoo 358eb7fe7d Cursors: the Smooth entry lists the Paintbrush and Blob Brush Alt cases
The cursor catalog's Smooth entry named only the Smooth tool and the
Pencil with Alt key toggles to Smooth Tool on. The tools also show it for
the Paintbrush with Alt held before a press while a path the Smooth tool
can act on is selected, and for the Blob Brush with Alt held before a
press or before the drag has moved, and through the drag that smooths.

PaintbrushClose and Paintbrush now say that Alt before a press shows the
closing ring only when no such path is selected.

The gesture tests check that the Paintbrush shows Smooth through an
Alt-press smoothing drag, with Alt let go too, and the closing ring for
Alt pressed after a stroke begins with a smoothable path selected.
2026-11-13 07:41:08 -08:00
dotCooCoo 5f78e1fca7 Merge main into fix/simplify-line-keeps-tangent 2026-11-13 07:41:05 -08:00
dotCooCoo 0a692dba46 Merge main into feat/ctb-type-tools-font-fill 2026-11-13 07:36:57 -08:00
dotCooCoo 1c8cec3e7b Merge main into fix/document-info-labels-translated 2026-11-13 07:36:52 -08:00
dotCooCoo fdbe4ea32d Merge main into fix/command-batch-artboards 2026-11-13 07:36:48 -08:00
dotCooCoo dcb69871fc Merge main into feat/custom-toolbar-dock 2026-11-13 07:29:45 -08:00
dotCooCoo fad839f843 Merge main into fix/smooth-fidelity-screen-pixels 2026-11-13 07:29:34 -08:00
dotCooCoo 393078b4f2 .ai export: a .ai of the art's bounds carries no editing data
document.export and document.serialize to .ai with useArtboards: false
wrote the one page of the visible art, but embedded the one-artboard
stand-in as the native document and as the editing copy for other apps.
Reopened, the file replaced the document's artboards with one artboard
around the art.

- The .ai of the art's bounds now leaves out both editing copies and
  returns the warning "Preserve editing was left out: an .ai file of the
  art's bounds would replace the document's artboards when reopened",
  as the PDF of the art's bounds does.
- useArtboards: false with pdfCompatible: false is refused, with or
  without editingData, since the file would have blank pages and no
  editing copy.
- Save, Save As, Save a Copy and exports of the artboards keep both
  copies. Legacy version 7 .ai files are written as before.

Docs: docs/mcp.md, docs/target-app-parity.md, document.export params.
2026-11-13 07:28:12 -08:00
dotCooCoo 4cd998dce0 Place: a file read apart from the session goes only into its document
Session::place_read and Session::place_queue_read placed a file, or
loaded the place cursor, in whichever document was active when the read
finished. A job prepared in one document could land in another one that
was activated meanwhile, or that stayed open after the first was closed.
Replace did not stop it when the other document held an equal object
under the same id.

PlaceJob, QueueJob and QueueRead now keep the uid of the document they
were prepared in. Both calls refuse a read whose document is not the
active one with "the document it was prepared for is no longer active",
before any selection, edit, tool change or journal entry. Switching back
to the original document places the file as before.
2026-11-13 07:28:07 -08:00
dotCooCoo 4b50fdba1c View: the canvas is drawn through the monitor profile on Windows and X11
Preferences > Performance > Use Monitor Profile for Canvas (on by default,
`monitorProfileDisplay`) converts the canvas's art, artboard paper and
transparency grid from sRGB into the ICC profile of the display that shows
most of the main window, after any proof. Arranged panes and the render
worker's frames are converted too; the worker keeps its frame in sRGB.

- Windows: the display's default profile for its standard color mode
  (ColorProfileGetDisplayDefault, GetICMProfileW where it is missing). A
  display with Advanced Color on is left to Windows.
- X11: colord's profile for the RandR output, then the output's
  `_ICC_PROFILE`, then the screen's when only one output is active.
- A Wayland session stays sRGB; macOS converts the window itself.
- A profile that is missing, larger than 1 MiB, not an RGB display
  profile or unreadable leaves the canvas sRGB, with the reason in
  `ui.inspect`'s `displayProfile`.

The conversion is sampled into a 33x33x33 table when a profile is read, on
a background thread. Exports, `ui.render` and the document are unchanged.
2026-11-13 07:27:30 -08:00
BFlat 77970c6924 Merge pull request #7082 from dotCooCoo/fix/smooth-resets-anchor-selection
Smooth tool: smoothing a path deselects its direct-selected anchors
2026-11-13 22:26:18 +07:00
BFlat 45acda45fe Merge pull request #7084 from dotCooCoo/feat/message-rows-id-nl-da-bg
i18n: Dutch translates status and error messages
2026-11-13 22:26:08 +07:00
dotCooCoo 8b990de004 Join: merge a run of coincident anchors in one pass over the path
Join removed each merged anchor from the path's anchor list as it went,
which moved every anchor after it, so a long selected run in front of
many other anchors took time quadratic in the path's length. It also
looked up each anchor's selection in the selected-anchor set.

Join now marks the merged anchors and removes them in one pass at the
end, reading the selection from one flag per anchor. Each corner or
smooth conversion sees the same neighbors as before, so the resulting
anchors are unchanged. Anchor::smooth_between holds the smoothing that
SubPath::smooth_anchor applies.

Tests compare the new merge bit for bit with the old one on 30,000
generated paths, and count the anchors moved when 300,000 selected
anchors in front of 300,000 others merge: each of the others moves
once.
2026-11-13 07:25:49 -08:00
dotCooCoo b6aaea6c97 Graphs: Appearance commands and graphic styles given a series group edit its marks
A graph draws its series groups again with no rows of their own, so a
fill, stroke, opacity or graphic style that a command put on a series
group was lost the next time the graph's data, type or size changed,
and the graph stored nothing. paint.setFill and paint.setStroke already
pass a series group's id on to the paths directly inside it.

- appearance.addFill, addStroke, setItem, removeItem, duplicateItem and
  moveItem, graphicStyle.apply and transparency.set with a fill or
  stroke item now edit the paths directly in a series group given by id
  or picked with Group Selection, and the graph stores what they change.
  A series group with no such path, or with a fill or stroke row of its
  own, is still edited itself, and appearance.moveItem from "contents"
  still moves the group's own Contents row.
- appearance.clear, appearance.reduceToBasic, appearance.showAllHidden
  and graphicStyle.breakLink edit the group as well as its paths.
- graphicStyle.apply without add and appearance.transfer onto a series
  group clear the group's own opacity, blend mode, isolation, knockout,
  effects and style link, and graphicStyle.apply with add unlinks the
  group. A transfer source that is one of the group's paths keeps its
  look, and a source that is the group's only path is an error.
- graphicStyle.redefine passes the style to the paths of a linked series
  group, other than the source and the linked paths whose look was
  edited, and clears and unlinks the group.
- graphicStyle.new, graphicStyle.redefine and appearance.transfer read a
  series group that holds no row, effect, opacity, blend mode,
  isolation, knockout or style link of its own through the topmost path
  directly in it, and a move clears each of those paths. A series group
  that holds one of them is read itself, as before.
- The Graphic Styles panel reads the style link of such a selected
  series group from that path.

Docs: docs/mcp.md, docs/target-app-parity.md. Not yet: the Appearance
panel and appearance.setActiveItem still read the selected group's own
rows, and the effect commands and the paint, stroke and gradient
commands with an item still edit the group itself.

Follows #5501.
2026-11-13 07:25:03 -08:00
BFlat 5fcfb0ce6e Merge pull request #7086 from dotCooCoo/feat/library-charstyle-menu
Libraries: a character style's menu offers Apply, Add to Character Styles, Rename and Delete
2026-11-13 22:24:41 +07:00
dotCooCoo 2361d2cb1b 3D: caps stay last when overlapping faces form a depth cycle
- When the faces that overlap form a cycle of depth orders, the order
  broke the cycle only after drawing every face with nothing to wait
  for, so a cap could be drawn before the walls and they painted over
  it. A face drawn last now keeps its place: no face moves across it.
- A face with a corner at or past the eye, which the perspective
  projection holds just in front of the eye, no longer gets a plane,
  so its depth is not measured on a plane its drawing does not match.
- docs/three-d.md states the masked member rule in Groups as the
  Fills and strokes section does, and docs/target-app-parity.md cites
  the Inflate test by its name.
2026-11-13 07:23:56 -08:00
BFlat 7fb0a5b51b Merge pull request #7088 from dotCooCoo/fix/panel-drags-primary-button
Panels: item drags start only with the primary button, as Layers does
2026-11-13 22:23:19 +07:00
dotCooCoo 0174888904 Contextual Task Bar: a bar without its font controls leaves their wheel
On a canvas too narrow for the font controls, the bar still shows but
leaves them out, and Window > Contextual Task Bar can hide the bar. In
both cases the font family field the bar drew before no longer takes
the mouse wheel, so the turn scrolls the canvas.
2026-11-13 07:22:25 -08:00
dotCooCoo d7740ff587 Images: an image stored again keeps its ICC profile and 16-bit samples
Open and Place store some images again instead of keeping their bytes:
a photo turned by its orientation, a TIFF the image decoder reads, and
a CMYK TIFF with one plane per ink. Those images lost the ICC profile
their file embeds, and 16-bit samples were cut to 8 bits.

- raster_image keeps gray and RGB samples at 8 or 16 bits in the
  stored PNG, with the gray or RGB profile in its iCCP chunk. A
  profile of another color space, such as the CMYK profile of a CMYK
  image read as RGB, is left out. A TIFF's profile is read with the
  tiff decoder, which reads it whatever the image's size.
- stored_cmyk_tiff and turned_cmyk write CMYK8 or CMYK16 as the file
  has it, with the CMYK profile as TIFF tag 34675 (UNDEFINED). A CMYK
  JPEG's profile is put together from all its APP2 segments.
- The docs/target-app-parity.md line on EXIF orientation says 8 or 16
  bits, and a line after it describes the kept profile.
2026-11-13 07:19:15 -08:00
dotCooCoo 08e1b7af80 Image Trace: a background trace applies only to the pixels it traced
A trace made away from its command (the background trace, #3098) counted
as current when the object, its image record and the settings were
unchanged, without looking at the image data. When other pixels came to
sit under the same image key (Rasterize after an undo reuses the key and
the id, Update Link, a replaced blob), the finished trace applied its old
paths to the new image.

The trace's inputs now include the encoded bytes it decodes, compared by
allocation first and then by content. TraceRun::current and the hand-off
in imageTrace.make and imageTrace.makeAndExpand both use them, so a stale
trace is dropped and the command traces the pixels there now. Equal bytes
in another allocation still count as the same image.
2026-11-13 07:18:53 -08:00
dotCooCoo 07522fb4e6 Tests: ui-egui tests remove the temp folders they make
Place, taskbar, opening, Save Options, Missing Fonts and one control
test left their folders in the system temp folder after every run.
Other opening and control tests removed theirs only at the end of a
passing test, so a failing one left its folder.

- A test-only Folder guard (crate::tests_folder) removes its folder
  with everything in it when dropped, also when the test panics, as
  the find-files tests' Tree guard does (#6948).
- The opening and control tests that made their own folders use it.
- Each Place test gets its own folder instead of one shared per-process
  folder, so one test's cleanup cannot remove another's files.
- The Missing Fonts helper no longer removes and remakes the folder
  vectorcraft_testkit::temp_dir made, which keeps it owner-only on Unix.
- vectorcraft_testkit::temp_dir is unchanged.
2026-11-13 07:14:08 -08:00
dotCooCoo 7f18643ada Type: measuring a threaded frame copies its own text once
Each measurement of a frame's share started from a copy of the whole frame, its own runs included, and then replaced the runs. The frame is now copied once without its own text, and each measurement copies that. A test counts the frame text copied to measure frames that already hold text.
2026-11-13 07:12:59 -08:00
dotCooCoo a0e0f09d79 3D and Materials: bound Revolve graphics' work and paint copies, and close the gap at the back
- Each contour of a graphic is cut only at the faces its own bounds reach,
  and the points cut at the faces count toward a limit of 64,000,000.
- The paint copies of a graphic's parts count toward a budget of their own,
  one copy for a part's fill and one for its seam stroke when it takes one.
- A part filled with a pattern, or whose gradient has a transparent stop or
  point, takes no seam stroke.
- Under several lights an opaque solid part also covers its seams, with its
  lit art clipped to a band of the seam stroke's width.
- A line of a graphic that reaches farther than any surface is refused, as a
  curve already was.
- A full turn places each segment by its own middle, so the cut at the back
  falls on the segment edge nearest half a turn from the middle.
- A group's members are measured for graphics only when it has some.
- docs/three-d.md: Revolve shows no Face control, and the new limits.
2026-11-13 07:06:38 -08:00
dotCooCoo 46f5532f23 Actions: script runs and tool edits name a recording limit as they end
While an action records, a script run, a tool's drag or a panel's or
dialog's commit could journal steps past the 10,000-step or 8 MiB limit
of an action set file without any check: the limit showed only when
Stop Playing/Recording left the steps out, and a finished script then
showed "Ran <name>".

The recording is now checked whenever the journal changed: after every
engine command the app runs (a synchronous script.run and a batch
included), when a script run ends, after a tool's result, and on the
next frame for edits committed directly. The edit and the journal
stay, the status bar names the limit at once, later recorded commands
are refused as before, and Stop keeps only the steps that fit. A
journal rewrite is checked again.
2026-11-13 06:59:18 -08:00