Commit Graph
4974 Commits
Author SHA1 Message Date
dotCooCoo 1eaf421e69 Cutting Tools: Banner Hems and Grommets marks a banner's hems and grommet holes
Object › Plug-ins › Cutting Tools › Banner Hems and Grommets…
(`addon.cutting.banner`) marks where a printed banner is hemmed and where
its grommets go. The banner is the bounds of the selected art, or each
artboard when nothing is selected. On each chosen side the command adds a
hem line a set depth inside the edge (1 in by default) and grommet marks a
set inset inside it (0.5 in): one at each end of the side and the rest
spread evenly, as few as keep them at most a set spacing apart (24 in). A
corner that two chosen sides share gets one grommet. Each grommet mark is a
circle (27 pt by default) with a cross through its center. The marks are
stroked in a chosen swatch ([Registration] by default) with no fill, in one
group named Banner Marks per banner on top of the current layer, selected,
as one undo step. While a clip group is edited in isolation, the group goes
outside the outermost clip group, as Weed Box and Lines does.

The command refuses every side off, no hem and no grommets, and bad params
with a message. It also refuses a hem or inset that reaches the far edge of
the banner when one of two opposite sides is chosen, or that meets the marks
of the opposite side when both are chosen, an inset of more than half a
chosen side's length, and more than 100000 grommets. The dialog has Preview.

The grommets' distance from the edge is `grommetInset`, so the dialog shows
it as Grommet Inset with its own catalog rows. The new Diameter catalog
rows also translate the Diameter field of the `shape.flare` command dialog,
which had no rows.

Docs: docs/mcp.md (Cutting Tools add-on), docs/target-app-parity.md and
ROADMAP.
2026-11-13 12:49:43 -08:00
dotCooCoo 4dc5c4f719 Live Shapes: object.setLiveShape refuses unknown keys and invalid sides or radius
object.setLiveShape was in params::UNCHECKED, so a key it does not read,
such as `path`, ran the command with the key ignored. Its description
names every key it reads, so it now goes through check_keys like other
commands, for MCP, scripts and command.batch steps. Each of `items` takes
`id` and `corners` alone; another key is refused as
`items[0].corner`, with the nearest accepted key. Saved steps still
replay leniently.

A `sides` that is not a whole number from 3 to 1000 (2.5, -1, 2, 1001)
and a negative `radius` are now errors naming the key, and leave the
document and its undo history as they were; a radius of 0 still
removes rounding. The Transform panel's Sides field takes 3 to 1000,
the Corners dialog's radius field stops at 0, and the panels' corner
radius fields send 0 for a negative value typed in.
2026-11-13 12:46:15 -08:00
dotCooCoo 804177bddd Offset Path: work out the offsets of several selected paths on several threads
Offset Path took each selected path in turn, so offsetting many paths, or a
group of many paths, used one CPU. It now reads the selected paths first,
works out their offsets on as many threads as the computer has logical CPUs
(one thread on the web and for a single path), and then makes the new
objects one after another in the selection's order on the editing thread.

The new objects, their ids, their stacking, the selection, the messages and
the undo step are the same as before. When several paths fail, the error is
the one for the first of them in the selection's order, and the paths after
a failure that have not started yet are skipped.
2026-11-13 12:41:24 -08:00
dotCooCoo 25857b2bce Type: right-to-left lines with tabs are measured as they are drawn
A right-to-left line draws the text before a tab after it, but its width
was measured in the order of its text. A right-aligned line such as
"אב גד<tab>הו זח" with a stop at 150 pt in a 300 pt frame ended 5.59 pt
past the frame's right edge, and right, center and decimal stops aligned
the text before the tab instead of the text drawn after it.

- The line's width is measured in drawing order, each tab up to its stop
  from where the glyphs drawn before it end. Right, Auto and centered
  lines are aligned by that width. Stops are still measured from the
  left edge.
- Right, center and decimal stops align the text drawn after the tab.
- Area type breaks a line with a tab where it fits as drawn, and wraps a
  line that fits only in the order of its text. Once the line holds a
  tab, it reads on while the least it can be drawn at fits, then ends at
  the latest break that fits as drawn: up to 256 breaks are measured
  from the latest back, and earlier breaks without a hyphen are
  bisected. Hyphenation points are measured as drawn the same way, so
  the line hyphenates at a point that fits.
- Only lines of horizontal paragraphs with right-to-left text keep a
  list of their break candidates.
- A tab that ends a wrapped right-to-left line is drawn before the line
  at its own width instead of pushing the line to the right.

Docs: docs/target-app-parity.md.
2026-11-13 12:40:40 -08:00
dotCooCoo ab1b7a974a Type: threaded type on a path aligned Justify All is measured by paragraphs
Type on a path whose first paragraph is aligned Justify All places its line
from the start bracket, the same as Left, so a threaded story is now measured
with the paragraphs it needs rather than the whole rest. A 1,500-paragraph
story through 40 paths aligned Justify All was laid out over 2,135,202 bytes
while measuring and is now laid out over 20,583. The frames take the same
text as before.

Center, Right, Auto right to left, hanging punctuation, Dynamic Text and
horizontal right-to-left text are still measured with the whole rest: a
centered or right-aligned line starts where the width of all its glyphs puts
it, and negative tracking can make an advance negative.

Tests compare every frame's share with the whole-rest share for Justify All
with spacing, on closed paths, with negative tracking and with punctuation
ending paragraphs.
2026-11-13 12:40:31 -08:00
Drew Dibble abb1e0d820 Merge remote-tracking branch 'upstream/main' into taper-one-face
# Conflicts:
#	crates/three-d/src/solid/tests.rs
#	docs/target-app-parity.md
#	docs/three-d.md
2026-11-13 13:38:40 -07:00
dotCooCoo 9d9e0c2f8e SVG import: broken nested SVG images do not use up the image allowance
An SVG shown as an image that could not be read (text that isn't XML,
an SVG without a size) still took one of the 256 nested SVG images an
SVG may show, so an image after 256 broken ones was left out. Only
images that decode now count toward that limit; a failed image gives
its place back.

Every nested SVG image read, decoded or not, counts toward new limits
of 1,024 reads and 512 MB of text per import, so a broken image shown
many times through <use> is still read a limited number of times. Past
them, nested SVG images are left out with a warning.

A nested SVG image with text that usvg then refuses no longer warns
that its text isn't imported.
2026-11-13 12:38:00 -08:00
dotCooCoo 1fa71e2ab1 3D: solid-color fills and strokes under colored lights draw their faces in one depth order
Under a light that is not white, an opaque Inflate fill or stroke in a
solid color was drawn as one composite of all its facets, so the
object's solids were drawn whole, one after another: the stroke's faces
covered every face of the fill, nearer or not.

When an object's solids share one order (all opaque with Normal
blending, or one member's under its opacity mask), each facet of such a
solid is now drawn on its own, with the composite's paint, channel
light ramps and seam width, and takes its place in that order as the
faces of a gradient already do. A lone solid, and the solids of an
object with a translucent or blended fill or stroke, keep the one
composite.

A face drawn on its own is about 16 nodes, against about 6 per facet
in the composite of a whole surface. These faces go in one order while
they number at most 30,720 together, counted again after the cuts.
Past that, each such solid is drawn as one composite.

Docs: docs/three-d.md, docs/target-app-parity.md.
2026-11-13 12:34:42 -08:00
BFlat c9b2119552 Merge pull request #7176 from dotCooCoo/feat/monitor-profile-windows-linux
View: the canvas is drawn through the monitor profile on Windows and X11
2026-11-14 03:24:26 +07:00
BFlat 3ec0cdec71 Merge pull request #7245 from dotCooCoo/fix/not-files-real-params
Plug-ins: file-key exemptions name only params a command takes
2026-11-14 03:24:13 +07:00
BFlat f05cb5e7a4 Merge pull request #7223 from dotCooCoo/fix/inline-graphics-in-symbol-art
Type: inline graphics in symbol, mask and pattern art follow their symbols
2026-11-14 03:24:03 +07:00
BFlat cc0413ee34 Merge pull request #7217 from dotCooCoo/fix/brush-repeated-names-numbered
Brushes: repeated brush names are numbered when a document loads
2026-11-14 03:23:54 +07:00
BFlat 50084b789f Merge pull request #7243 from dotCooCoo/fix/smooth-segment-under-drag
Smooth: a drag along a segment smooths it when its anchors are out of reach
2026-11-14 03:23:45 +07:00
BFlat a3ab152a0a Merge pull request #7246 from dotCooCoo/fix/objects-on-path-flatten-once
Objects on Path: each path is flattened once per placement or transform
2026-11-14 03:23:37 +07:00
BFlat 9107dad274 Merge pull request #7194 from dotCooCoo/fix/item-3829
Envelopes: a partly transparent image keeps its alpha where its warp pieces overlap
2026-11-14 03:23:28 +07:00
BFlat 00fba7ae24 Merge pull request #7121 from dotCooCoo/fix/graph-series-paint-other-commands
Graphs: stroke, gradient and recolor commands update a series' stored paint
2026-11-14 03:21:58 +07:00
BFlat cf6c666d25 Merge pull request #6980 from addibble/seam-round-joins
3D effects: every facet's seam has round joins
2026-11-14 03:21:49 +07:00
dotCooCoo add71548db Merge main into fix/blob-brush-preview-bounded 2026-11-13 12:15:59 -08:00
dotCooCoo dd5a9e7cbc Text Wrap: Invert Wrap intersects a line's spans in one pass
With Invert Wrap, each piece of a line's band intersected the line's
spans so far with the spans the wrap shape holds over that piece by
comparing every pair of them. A wrap shape of many parts (a comb or a
zigzag) gives a line many spans, and with an offset the band is cut
into up to 128 pieces, so a dense shape made layout quadratic in the
number of parts for every line.

intersect_intervals now walks the two sorted lists once, stepping past
whichever interval ends first, and gives the same spans in the same
order. Tests compare it with the pairwise intersection on every list of
non-overlapping intervals with ends among seven points, check that two
interleaved lists of 3,000 and 2,000 intervals take at most 5,000
steps, and lay out a line inside a 500-tooth Invert Wrap comb with an
offset.
2026-11-13 12:12:22 -08:00
ncic 1577ef3f76 Docs: record Snap to Point without Smart Guides labels (#7250) 2026-11-13 21:11:54 +01:00
dotCooCoo 89ccf89461 Document Info: spot colors from gradient stops that color nothing are not listed
Spot Color Objects listed every spot color linked from a gradient's stops
once any stop was above opacity 0. A spot stop at opacity 0 between other
clear stops, at the end of the ramp after a clear stop, or meeting an
opaque stop only at one offset was listed though it prints no ink, and so
was an opaque stop out of order whose pairs have no width.

A stop now counts when it bounds a stretch of some width that the
gradient sampler mixes and either stop of that stretch is above opacity
0, or when it is the first or last stop and above opacity 0, since those
color the offsets before and after the others. Stops in any order are
judged by the stretches the sampler uses. A freeform gradient keeps the
old rule, since it draws from its points. Type runs follow the same rule.
The document.info params text, docs/mcp.md and the progress line
describe it.
2026-11-13 12:11:20 -08:00
dotCooCoo fd31331079 Merge main into fix/space-in-text-edit 2026-11-13 12:09:58 -08:00
dotCooCoo dd3438cb7f Live Paint Bucket: Cursor Swatch Preview and arrow keys pick swatches
Live Paint Bucket Options gets a Cursor Swatch Preview checkbox (on at
first, saved with the other options). With the Bucket selected, Left and
Right set the active fill, or the stroke when the Stroke proxy is in
front, to the color swatch before or after it in the Swatches panel,
wrapping around at the ends, with the preview on or off. The selected art
keeps its paint. The preview shows the active paint above the pointer
between the swatches before and after it; a color that is no swatch shows
alone until Right picks the first swatch or Left the last, and a paint
that is not a solid color shows no chip. The chips hide during a fill
drag and while Alt samples with the eyedropper. The fill and the stroke
each remember the swatch picked for them, so swatches of the same color
are each reached in turn.

The Bucket and the Shape Builder now take the arrows for swatches only
without Shift, Alt or Cmd/Ctrl, judged by the modifiers held when the key
went down, so modified arrows keep their shortcuts
(Tool::claims_key_with, Session::tool_claims_key_with).

Docs: docs/mcp.md, docs/target-app-parity.md, tool.options params text.
2026-11-13 12:09:16 -08:00
dotCooCoo f42ec13bd2 PDF: the long-page warning names the in-points option
In a PDF without a standard, the warning for a page longer than 14,400 pt
written with a UserUnit above 1 now ends with "turn on Write pages longer
than 200 in at full size in points". The warning already said that apps
ignoring UserUnit show such a page smaller; it now names the Save PDF
option that writes the page at its size in points instead. PDF/A files,
which keep the UserUnit with the option on, and .ai saves, whose options
do not have it, keep the warning as it was.

The longer message has a row in every complete-message catalog. The
params text of document.exportPdf, docs/mcp.md and the evidence line in
docs/target-app-parity.md describe it.
2026-11-13 12:08:49 -08:00
dotCooCoo 291afd8aa6 Free Distort: the bounding box turns with the warped art
object.distort warped the anchors of paths, groups, compound paths and
clip groups but left their bounding-box angle as it was, so a rotated
path kept its old box after a warp and an upright one stayed square to
the page however far the warp tilted it. Text, images and symbol
instances already took the angle of the warp's affine approximation.

- Each path, group, compound path and clip group now takes the angle
  the warp turns its x axis to at its own center before the warp, the
  members of a warped group and the art of a linked opacity mask each
  at their own. An unlinked mask's art keeps its angle. Mirroring keeps
  the nearer angle and a collapsing warp keeps the old one. Reset
  Bounding Box squares the boxes again.
- Each subtree's bounds are measured once while it is warped.
- Corners that equal the corners of the box being warped change
  nothing and add no undo step.

Docs: object.distort params text, docs/mcp.md, docs/target-app-parity.md.
2026-11-13 12:07:21 -08:00
dotCooCoo fdedfb0e59 Merge main into feat/warichu 2026-11-13 11:59:22 -08:00
dotCooCoo 405f51c952 Merge main into fix/docs-layers-row-menu 2026-11-13 11:55:06 -08:00
dotCooCoo 1567957482 Smooth tool: an open path's end turns with the smoothing when a drag reaches it
When a Smooth tool drag refits the stretch at the first or last anchor
of an open path, the curve there now leaves or arrives along the
smoothed path within the tool's reach instead of along the first or
last segment, so a jagged end no longer keeps its jag. The end point
stays where it is. An end where the smoothed path turns back by 90
degrees or more keeps its segment's direction, and a stretch that ends
inside the path keeps the direction of the part outside the drag.

- pathops: SimplifyOptions gains free_start and free_end, off by
  default; closed subpaths and simplify_part ignore them, so Simplify
  and Object > Path > Smooth give the same output as before.
- engine: path.smoothRegion sets them for the ends of an open path
  that a run's refit reaches.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 11:51:02 -08:00
dotCooCoo b7709a3c1f MCP: SVG export by a confined caller refuses script links outside its read roots
An SVG or SVGZ writes each linked JavaScript file of SVG Interactivity
into a <script xlink:href> element as stored, so a caller with
automation read roots could read a path outside them from the exported
text. With read roots in force, an SVG or SVGZ of a document that links
such a file is now refused with "a linked JavaScript file is outside the
read roots", an error that names no file or folder. No file or folder is
written, and the document keeps the link.

- The check uses the rule document.json already applies to
  svg_scripts: a relative path is taken from the document's folder, a
  file: URL is a file, and another URL is not checked.
- It covers document.serialize, document.export, document.exportSelection,
  document.exportForScreens and assets.export (before the folder is made
  or any row is written), the SVG and SVGZ saves (also a save without a
  path of a document saved as SVG), file.exportAs and the SVG Options
  dialog's Show Code.
- Callers without roots, and the person at the keyboard, export the
  links as stored.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 11:46:18 -08:00
dotCooCoo e1a918636e Document Info: values show in the interface language
The panel showed the values Document Info makes in English in every
language. It now shows the Units, the Raster Effects resolution, each
font's file, substitution and embedding permission, each image's size
and linked file, each Repeat object's kind and counts (in the
language's plural forms) and gradients named by their kind in the
interface language. Font, face, file and swatch names stay as they
are, a gradient swatch named like a kind included.

document.info keeps its English rows and adds the values the panel
builds them from: document.unitId, fontDetails, imageObjects and a
kinds list on each gradientObjects entry. Save… and Package's report
stay in English.
2026-11-13 11:42:24 -08:00
dotCooCoo 51e8938374 Blob Brush and Eraser: the drag outline does a bounded amount of work per frame
The outline of the area a Blob Brush or Eraser drag covers was outlined
again for the whole stroke on every frame, so a long stroke slowed the
canvas as it grew.

- The drag is outlined in pieces of up to 32 segments (one segment each
  with Size set to Pressure), and a frame outlines again only the pieces
  it added segments to. A grid of squares lists the segments near each
  square, and each piece's outline drops the parts inside the rest of
  the area, so the pieces show as one edge.
- One frame takes at most 256 new points and a fixed amount of work;
  Tool::overlays_pending makes the canvas draw again until the rest is
  done with the pointer still. Points not outlined yet show as a thin
  line, and the tip shows at the pointer.
- The settled outline is kept in joined paths of 64 pieces, and a frame
  joins again only the paths that changed. The grid's size is capped.
- The Blob Brush's length label and the Size-by-pressure check keep
  running values instead of rescanning the stroke each frame. With Size
  set to Pressure the outline shows only the edge of the whole area.

The release sends the same path.blob and path.eraseRegion parameters.
2026-11-13 11:40:53 -08:00
dotCooCoo ae113557be Merge main into fix/docs-layers-row-menu 2026-11-13 11:40:52 -08:00
BFlat 08b6cfd3e8 Merge pull request #7227 from dotCooCoo/fix/hindi-variable-and-join-terms
i18n: Hindi uses one word for a variable and जोड़ें for joining paths
2026-11-14 02:40:46 +07:00
BFlat a105e14aef Merge pull request #7220 from dotCooCoo/feat/image-indexed-color
Edit Colors: convert embedded images to indexed color
2026-11-14 02:40:36 +07:00
BFlat 5f8e4c1913 Merge pull request #7240 from dotCooCoo/feat/smooth-reach-overlay
Smooth tool: the drag shows the band the tool smooths
2026-11-14 02:40:24 +07:00
BFlat 6e54257294 Merge pull request #7225 from dotCooCoo/fix/modal-blocks-canvas-touch
Touch: canvas taps, presses and view navigation are ignored while a modal dialog is open
2026-11-14 02:40:15 +07:00
BFlat 90a9e00fdd Merge pull request #7205 from dotCooCoo/fix/svg-interactivity-hide-script-paths
SVG Interactivity: replies name JavaScript files outside the read roots by file name
2026-11-14 02:40:06 +07:00
BFlat 3a7295e588 Merge pull request #7201 from dotCooCoo/fix/revert-drops-pending-place-reads
Place: File › Revert drops reads started for the content it discards
2026-11-14 02:39:57 +07:00
BFlat 95b328dcca Merge pull request #7193 from dotCooCoo/fix/trace-blur-rings
Image Trace: blurred edges no longer trace as rings of in-between colors
2026-11-14 02:39:47 +07:00
BFlat 58dd9e8908 Merge pull request #7230 from dotCooCoo/fix/docinfo-hidden-object-paints
Document Info: hidden objects and objects at an opacity of 0 list no brushes or paints
2026-11-14 02:39:39 +07:00
BFlat 34457735e5 Merge pull request #7231 from dotCooCoo/fix/threaded-type-measures-paths-and-aligned-frames
Type: threaded paths and frames aligned off the top measure the paragraphs they need
2026-11-14 02:39:29 +07:00
BFlat 821e2d0bb5 Merge pull request #7224 from dotCooCoo/fix/close-modified-background-tab-keeps-front
Documents: closing a modified background tab keeps the document in front
2026-11-14 02:39:19 +07:00
BFlat 4dc9c5c5d3 Merge pull request #7218 from dotCooCoo/fix/batch-fit-journal-index
Artboards: a batch's Fit to Artwork Bounds step journals the artboard it fits
2026-11-14 02:39:07 +07:00
BFlat aa6d9acc95 Merge pull request #7228 from dotCooCoo/fix/global-edit-docs-type-image-groups
Docs: Global Edit leaves out groups holding type or images
2026-11-14 02:38:55 +07:00
BFlat 38c0f3ed61 Merge pull request #7100 from dotCooCoo/fix/flatpak-clipboard-images
Clipboard: the Flatpak on GNOME copies and pastes pictures and SVG with other apps
2026-11-14 02:38:46 +07:00
BFlat f0c3646e90 Merge pull request #7198 from dotCooCoo/fix/area-type-band-coverage
Type: area type lines stay inside a frame that pinches in within their band
2026-11-14 02:38:38 +07:00
BFlat 1e385ba06e Merge pull request #7210 from dotCooCoo/fix/envelope-empty-type-box
Envelope: type that draws nothing adds no box next to other content
2026-11-14 02:38:27 +07:00
BFlat 858749d596 Merge pull request #7222 from dotCooCoo/fix/parity-doc-preferences-bullet
Docs: keep one Preferences progress bullet with the off default for Highlight Alternate Glyphs
2026-11-14 02:38:19 +07:00
BFlat b004977dba Merge pull request #7202 from dotCooCoo/fix/raster-effects-skipped-transform-budget
Effects: raster effects before a Transform effect past the budget stay with the art
2026-11-14 02:38:10 +07:00
dotCooCoo 1ed7785bc5 Actions: keep folder preferences out of saved sets and confined replies
A prefs.set step, recorded inside a command.batch or loaded from a
.vcactions file, can hold a folder preference such as scratchPrimary.

- ui.inspect under automation read roots now checks such a step's
  value for {key, value} and each entry of values, also in a batch
  step's commands at any depth, as written and trimmed, including bare
  names, subfolders and file: URLs. An empty value and the scratch disk
  choices Startup and None stay.
- Any other step text written as an absolute path or a file: URL
  outside the read root is left out also when whitespace surrounds it.
- Save Actions refuses a set with a prefs.set step that holds a folder
  value and names the action and the preference instead of writing the
  file. An empty folder and the scratch disk choices save.
- prefs.export leaves out scratchPrimary and scratchSecondary, like the
  other folder preferences. prefs.import refuses a scratch disk set to a
  folder and still reads Startup, None and an empty value.

The folder list, the scratch disk choices and the folder check now live
in one place in the engine. Docs: docs/mcp.md, params text and a
progress line in docs/target-app-parity.md.
2026-11-13 11:37:49 -08:00