Commit Graph
8213 Commits
Author SHA1 Message Date
BFlat be99b8895d Merge pull request #7155 from dotCooCoo/fix/hindi-message-terms
i18n: Hindi messages keep their English meaning and the catalog's terms
2026-11-13 23:04:40 +07:00
BFlat 508c1b658e Merge pull request #7096 from dotCooCoo/feat/ai-save-envelopes
PDF/.ai save: envelopes stay editable when the file is reopened
2026-11-13 23:04:30 +07:00
BFlat d26413c3fb Merge pull request #7117 from dotCooCoo/fix/smooth-tool-smooths
Smooth tool: each drag smooths the path under it, and repeated drags keep smoothing
2026-11-13 23:04:21 +07:00
BFlat de93bef121 Merge pull request #7101 from dotCooCoo/fix/eyedropper-series-opacity
Eyedropper: a pick that leaves a series mark's row without paint replaces the stored transparency
2026-11-13 23:04:12 +07:00
BFlat f525bd742e Merge pull request #7147 from dotCooCoo/fix/script-file-name
Scripts: $.fileName names the file of the code that reads it
2026-11-13 23:04:04 +07:00
BFlat dd8f8ed2bd Merge pull request #7115 from dotCooCoo/fix/settle-keeps-preview-warnings
Edit: settling an interaction keeps the warning of the preview it applies
2026-11-13 23:03:55 +07:00
BFlat ada78a07ba Merge pull request #7114 from dotCooCoo/feat/group-effect-after-members
Effects: a group's geometry effects reshape what its members draw
2026-11-13 23:03:46 +07:00
BFlat 0cc04afd57 Merge pull request #7161 from dotCooCoo/fix/script-xml-heap-limit
Scripts: E4X operations count their temporary storage against the heap limit
2026-11-13 23:03:38 +07:00
BFlat 2d91c860a7 Merge pull request #7087 from dotCooCoo/feat/every-line-composer-shaped-area
Type: the Every-line Composer sets mixed sizes in shaped and wrapped area type
2026-11-13 23:03:29 +07:00
BFlat c68b2775e4 Merge pull request #7125 from dotCooCoo/fix/plugin-install-checks-first
Plug-ins: plugin.install writes nothing to the store until the module is checked
2026-11-13 23:02:12 +07:00
BFlat f239944df0 Merge pull request #7107 from dotCooCoo/fix/measured-arclen-coincident-quad
Paths: a quadratic segment whose points coincide measures zero length instead of NaN
2026-11-13 23:02:03 +07:00
BFlat b17a62be34 Merge pull request #7109 from dotCooCoo/fix/painted-fx-brush-reach
Effects: culling a painted piece leaves room for its own brush art
2026-11-13 23:01:55 +07:00
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 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
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
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
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 ccb14182e9 Plug-ins: plugin.install approves from the grant it checks again, and others wait for its store writes
Without a plug-in store, plugin.install read the process grant once
for its later check and again for its approval. A grant another session
recorded between the two reads approved the module; when that session
revoked it before the install, the check saw the grant it first read
and the install recorded the revoked grant again. The approval now comes
from the first read.

plugin.install published the plug-in before it wrote the module and its
grant to the Plug-ins folder. A plugin.remove in another session in
between removed the plug-in, its file and its grant, and the install then
wrote both back, so the plug-in loaded with its grant at the next launch.
A session attaching the folder in between loaded the old version, which
kept running while the folder held the new one. plugin.remove, loads of
the stored plug-ins and loads of a plug-ins folder now wait until an
install has written the folder, and an install waits for them.

Docs: docs/plugins.md, docs/target-app-parity.md.
2026-11-13 06:56:24 -08:00
dotCooCoo b1c684e912 Scripts: E4X operations count their temporary storage against the heap limit
Assigning to a named child counts its list of the matching children, and
assigning through an empty XMLList counts the chain of targets it climbs,
before either changes the tree. copy(), toXMLString(), normalize(), the
`..` selector and the depth check on insertion hold the stack they keep
for each open level of the tree. Inserting an XMLList, and assigning one
to an item of an XMLList, hold their copy of the list's items.

copy() reads attributes in place, and toXMLString() reads attributes and
namespace declarations in place, instead of copying the lists. copy()
charges the declarations it copies before it copies them. Deleting by
name or assigning an attribute compacts the remaining links in place
instead of building a second list.

toXMLString() holds the set of prefixes a namespace lookup passes, and
picks a new nsN prefix in one pass over the declarations in scope
instead of rescanning them for each candidate.

The parser holds its open elements and their names, the text it gathers,
each tag's attribute values, the sets that check declarations and
attribute names, and its one copy of an element's namespaces, and it
charges the strings of the namespace declarations it makes. The
allowance it holds from the source's length covers the copy a value
takes as it becomes a string.

Comparing XML values, converting them to strings, assigning an XMLList
to an attribute, and reading a name, a qualifier, a namespace URI or
embedded text from a string built by joining strings flatten it with
flat_charged. An attribute name read from a string reserves its copy,
and an element name shares the string.

Docs: docs/target-app-parity.md.
2026-11-13 06:54:23 -08:00
dotCooCoo 5d5b8aa53b i18n: Hindi messages keep their English meaning and the catalog's terms
193 Hindi rows outside the plug-in messages said something other than
their English: "a number of points" read as "many digits", swatches as
clocks, crop as a harvest, resolution as a resolve, and saving used
words the glossary replaces. Others left English prose in place,
dropped a clause ("to keep them all", "what follows it") or ran the
sentence backwards.

- Rows use सेव, स्वॉच, क्रॉप, रिज़ॉल्यूशन, पाथ and टाइप (for a text
  object) as docs/localization-hi.md sets them.
- Values a command reads (enum values, preset names, OpenType tags)
  stay as the English writes them.
- A test checks those terms and the point unit in every Hindi row, and
  another pins rows whose quantity or clause was lost.
- docs/localization-hi.md, docs/localization-parity.md and
  docs/target-app-parity.md carry the new counts and a wording note.
2026-11-13 06:43:32 -08:00
jmvergesandClaude Opus 5.5 e11155c692 Envelopes: a top object's split goes by the longer of handles or bends
The previous commit took the spans from how far each piece bends away
from the split whenever both handles there were within ONE_CURVE of the
anchor. A curve that barely moves at the split but doesn't bend there
(a straight one, say) has short handles and no bend, so that gave 0/0
and the side fell back to the polygon shares. Now the spans come from
whichever pair is the longer: the handles, or the bends (which go with
the squares of the spans). The check of every piece against the
rebuilt curve is unchanged.

Test: a_split_curve_keeps_the_top_object_patch_where_it_barely_moves_and_runs_straight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 14:42:07 +00:00
dotCooCoo 75c74557bb Scripts: $.fileName names the file of the code that reads it
$.fileName gave the file being run, so a function defined in a file
that #include inserted, in a file $.evalFile ran, or in an earlier run
of a named engine reported the calling file instead of its own.

- vectorcraft_js: HostItem::SiteGetter gives a getter the CallSite
  (source tag, line, column) of the code that reads it.
- $.fileName maps that place through the run's source maps, or the
  maps a named engine kept, to the file the code is in. It is empty in
  code from a script given as source and in kept code whose map was
  dropped. Code that eval or Function compiled still gets the file
  being run.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 06:19:23 -08:00
dotCooCoo 4cf010c130 Flatten: grouping sweeps skip boxes that a box already holds or cannot join
The sweep filtered by transparency kept every open box in one tree, so
a box without transparency met every open box without transparency it
overlapped, though such pairs never join. Both sweeps closed an open
box early only when a later box held it, so in a nest of boxes that
share their left and top edges and get smaller to the right and
bottom, each box met every earlier box around it. One see-through
object inside 512 nested opaque objects took 130,817 meets in the
unfiltered sweep and 131,328 in the filtered one.

The filtered sweep now keeps open see-through boxes and open opaque
boxes in separate trees. An opaque box looks only in the see-through
tree; a see-through box looks in both.

In both sweeps, a box inside an open box it met, ending no further
right, is not opened when that box joins everything it overlaps (any
box without the filter, a see-through box with it). Whatever later
overlaps the inner box overlaps the outer one.

The nest above now takes 512 meets in each sweep, and a nest of 512
boxes alone takes 511 in the unfiltered sweep where it took 130,816.
The groups are unchanged.
2026-11-13 05:42:03 -08:00
dotCooCoo 19ab993479 EPS: an image drawn more than once writes its samples once
An image placed several times in an EPS file (the pieces of an envelope
or a Warp effect, copies of one image, a symbol's instances) wrote its
whole ASCII85 data at every placement: 200 copies for one envelope at
the default fidelity, enough for a large photo to pass the 256 MB page
limit and lose pieces.

The samples of such an image are now written once in the setup as
VCim<i>, and each placement reads them from there:
- Level 3: a ReusableStreamDecode stream, rewound with resetfile and
  read through a new FlateDecode filter at each placement.
- Level 2: an array of strings of at most 49,152 compressed bytes, fed
  to RunLengthDecode by a procedure.

An image placed once is written where it is drawn, byte for byte as
before. The page limit counts a repeated image's samples once, and a
colorized image is reused per ink.
2026-11-13 05:38:06 -08:00
dotCooCoo 130f914868 Type: a threaded frame's empty last paragraph keeps a format given to it
A paragraph format given to the empty paragraph after a frame's last
paragraph break was lost when the story flowed again: the paragraph is
shared with the next frame's first paragraph, and the next frame's old
style replaced it, in that frame and in the frames after it.

- The thread reflow also notes which frames' last paragraph changed
  style, and story_paras_restyled keeps that style against the next
  frame's unedited first paragraph, across unedited empty frames too.
  When the same edit formats the next frame's first paragraph, that
  format applies, as before.
- story_paras keeps its behavior for its other callers.
- Tests cover area type and type on a path on either side, a next frame
  holding text, three frames, undo and redo, a format that changes
  nothing, and numbered list counts.
2026-11-13 05:27:02 -08:00
dotCooCoo 6f3ffeb393 Plug-ins: plugin.install writes nothing to the store until the module is checked
plugin.install wrote the module and its grant to the Plug-ins folder,
attached the plug-in's settings and cache and turned a disabled plug-in
on before the registry's last approval check. When another session
revoked the grant, or recorded one for other bytes, after the command
recorded its own grant and before that check, the install was refused
but left the new module and grant in the folder for the next launch.
A plug-in another session installed under the same id between the
command's checks and the install took no part in the decision, so the
install replaced it without `replace: true`.

- registry::install_checked records the grant (or forgets it) and
  installs the plug-in as one step, after a caller check that sees the
  plug-in installed under the id and its grant; a refusal changes
  nothing.
- plugin.install commits through it before any store write, refusing
  with the existing messages when another plug-in now needs `replace`
  or the grant its approval came from changed. The store writes, the
  put-back on a failed write and the disabled-list update now run after
  the commit; their logic is unchanged.
- The grant call-site audit also counts registry::install_checked.

Docs: docs/plugins.md, docs/target-app-parity.md.
2026-11-13 05:25:16 -08:00
dotCooCoo 6b55d7ca3e Eyedropper: a whole pick that removes a series mark's painted row replaces the stored transparency
A whole pick from a source with no fill row, or no stroke row, removes
that row from the target. When the target is a graph series mark whose
row had paint, the pick now replaces the row opacity and blend mode the
graph keeps for that kind of row in the series. A row that had no paint
before the pick still leaves the kept values as they are, and a pick of
some attributes from such a source is unchanged.
2026-11-13 04:50:59 -08:00
dotCooCoo ba2a6fc1f3 Type: a further span beside a wrap object is measured across its line's height
Text wrap can split a line into spans that share a baseline. The spans
after the first were measured across the height of the line set in the
first one, so a taller line set in a later span could run into a wrap
object the shorter line stayed clear of. Each further span is now
measured again across the height of the line that goes in it. The line
gets the part that is free, and a span with no free part, or with a part
narrowed to less than the line's first word, is left empty. The
Every-line Composer and the line loop both place lines through
`Pen::place`, so they agree.

The Every-line Composer also keeps a line start whose line found no room
while a longer line from it would be taller. A taller line steps down
the frame by its own leading and can find room the shorter line's steps
missed.

Tests: a_further_span_is_measured_across_the_height_of_its_line,
composition_tries_a_longer_line_after_one_that_cannot_be_set.
2026-11-13 04:48:43 -08:00
dotCooCoo 349034dda2 Smooth tool: each drag smooths the path under it, and repeated drags keep smoothing
Part of #2224.

A Smooth tool drag only refitted the anchors in its reach within
Fidelity, so a wobble wider than Fidelity stayed, and a second drag over
the same place changed nothing. Repeated refits also drifted away from
the path, and a drag over a whole closed path left a corner at its first
anchor.

- path.smoothRegion now smooths the stretch in reach along its length
  with a Gaussian whose spread is a third of the reach, then refits it
  within Fidelity. The run's outer end points and handles stay fixed.
- Each refit stays within twice the distance the smoothing moved the
  path (at least a tenth of Fidelity), so repeated drags do not drift
  (SimplifyOptions::smooth_moved_only).
- A whole closed path is smoothed across its start as a loop, so a
  bump at the first anchor is smoothed like any other and no corner is
  left. Object > Path > Smooth smooths a closed path across its start
  the same way.
2026-11-13 04:44:12 -08:00
jmvergesandClaude Opus 5.5 6ed3935e59 Envelopes: a top object's side split at a cusp still follows its curve
Split where the curve stops (a cusp), both handles at the split lie on
the anchor, so their lengths say nothing of how the curve's parameter
divides between the two pieces, and the side fell back to the polygon
shares. When both handles lie within ONE_CURVE of the anchor, the
spans now come from how far each piece bends away from it (the second
derivatives, in proportion to the squares of the spans). The existing
check of every piece against the rebuilt curve is unchanged.

Test: a_split_curve_keeps_the_top_object_patch_when_split_at_a_cusp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 12:43:47 +00:00
dotCooCoo e8c3dace32 Effects: a group's geometry effects reshape what its members draw
A group's or layer's geometry effects now run after its members' own:
the geometry effects of each visible path and compound path in it and
of their fills and strokes, and a nested group's geometry and
Pathfinder effects, are applied first, at any depth, and the group's
effects reshape the result. A symbol instance's geometry effects run
after those of its symbol's members in the same way. Before, the group
reshaped the members' outlines and their own effects ran afterwards:
a 100 pt square with its own 10 pt Offset Path in a group with a 50%
Transform drew 70 pt wide, and it now draws 60 pt wide. Existing
documents with such groups draw differently.

The canvas, export (including groups that keep their type), Expand
Appearance and the art that Pathfinder operations and compound shapes
take from such a group use the same order. The reply of
effect.expandAppearance names such a group and no longer names the
members whose effects the group's art already holds.

The renderer now drops the art it keeps for a group when a plug-in is
installed or removed and a member of the group has a plug-in effect.

Docs: docs/target-app-parity.md. Not yet: the own geometry effects of
images, symbol instances, paths with a linked opacity mask or a 3D
effect, and nested groups with a 3D effect in a group still run after
the group's.
2026-11-13 04:41:58 -08:00
dotCooCoo f918c91c13 Edit: settling an interaction keeps the warning of the preview it applies
With Real-time Drawing off, a command that ends a drag first (Undo, Redo,
Clear History, a plug-in command and the others) applies the drag's
deferred preview, but dropped the warning that preview gave, which
releasing the drag shows in the status bar.

- The applied preview's warning is now kept while a top-level command or
  the tool's actions run. A command's result carries it as
  `previewWarning`, and as `warning` when the command has no warning of
  its own, so the status bar shows it.
- Typing kept as its own step when a tool's drag begins, or when Cmd
  lends the Selection tool, shows the warning in the status bar.
- The Cmd-lend path no longer ignores an error from keeping the typing.

Docs: docs/mcp.md (undo/redo row), docs/target-app-parity.md.
2026-11-13 04:36:38 -08:00
dotCooCoo d956d2165f SVG: an absolutely placed right-to-left chunk is anchored by the edges of its glyphs
A text chunk placed by an absolute x was measured from the pen of its
first character to the farthest glyph end. The first character of a
right-to-left chunk is drawn at its right, so a chunk of several such
characters was measured as that one character: anchored by middle or
end, or starting a line beside absolutely placed characters, it opened
off its position by the width of its other characters.

- A chunk's left and right edges are the smallest glyph start and the
  largest glyph end over all its characters, as SVG 2 anchors a chunk
  (https://www.w3.org/TR/SVG2/text.html#TextLayoutAlgorithm). The first
  character's pen is placed at its own distance from the left edge.
- The layout that measures the chunks has the direction the imported
  text gets: left to right unless the text's direction is rtl. Text
  starting with a digit and holding right-to-left letters was measured
  right to left.
- The chunks are measured in the first of the layouts that kern the
  characters onto their x, so anchored text is laid out once less.
2026-11-13 04:32:32 -08:00
dotCooCoo 88c02aa293 Effects: culling a painted piece leaves room for its own brush art
A live-effect plug-in that paints its pieces draws each piece's brushed
stroke along the piece's path at the piece's stroke width. Culling left
room for brush art from the object's own stroke width around the
pieces, so brush art of a moved or wider piece was cut off at the edge
of the view. Culling now leaves 16 x the piece's stroke width + 8 pt
past each brushed piece, and does so for a symbol instance whose art
holds such a path (measured once per symbol) and for a blend, envelope
or repeat that holds one. Object > Rasterize measures the brush art
drawn along each piece and leaves out the object's own brush art, which
is not drawn.

Groups and layers now leave room for their members' brush art. The
cull bounds of a group or layer united its members' bounds without the
room each brushed member leaves, so the painted bounds of a group, and
the area redrawn when it changes, left out brush art past its members'
strokes. That applied to plain brushed paths as well as painted pieces.
Each member is now measured as the renderer measures it when it draws
the group, in the members of a group with a fill, stroke or adjustment
of its own and in a group with crop marks or Vector Halftone too.
2026-11-13 04:31:38 -08:00
dotCooCoo 2bd02ecb2a Paths: a quadratic segment whose points coincide measures zero length instead of NaN
measured_arclen returned kurbo's arc length unchecked. For a quadratic
segment whose three points coincide, kurbo 0.13.1's QuadBez::arclen
multiplies 0 by infinity and returns NaN. measured_arclen now returns 0
when the measurement is not finite, the same value it gives a segment it
does not measure. Finite results are unchanged.
2026-11-13 04:29:09 -08:00
dotCooCoo 727a26d5a6 Path ops: Offset Path sweeps long, low paths along their length
Offset Path now turns the overlap sweep the way Outline Stroke and
Unite do. When the estimate for its fill, the ring stroked around it
and the outline of its open subpaths is under two thirds as large for
the parts turned a quarter turn, the faces are found on the turned
parts, the faces within the offset distance of the path are dropped by
testing their points turned back, and the result is turned back.

On an open path of 10,000 anchors in rows of 1,000, offsetting by 2 pt
takes 1.0 s of processor time instead of 1.8 s in a release build.
The offset of a long, low path has as many subpaths as the offset of
the same path stood on end, and the area where the two differ is no
larger than the refit precision times the outline's length.
2026-11-13 04:26:14 -08:00
dotCooCoo 2170ddcac4 Eyedropper: a pick that leaves a series mark's row without paint replaces the stored transparency
An Eyedropper pick that includes transparency now counts a fill or stroke
row that had paint before the pick and has none after it when it stores
the series' row opacity and blend mode on the graph, as graphic styles
already do. Before, such a pick left the opacity and blend mode another
mark of the series had stored, and the series was drawn with them again
after its data changed. A row that had no paint before the pick still
leaves the stored values as they are.
2026-11-13 04:05:56 -08:00
jmvergesandClaude Opus 5.5 728fd9ef14 Envelopes: a top object's side that is one split curve follows its parameter
A top-object envelope gives each piece of a side a share of the content
in proportion to its control polygon's length. When anchors split a
curve without changing its shape, the pieces' polygons don't divide the
side where the curve's own parameter does, so the envelope bent its
contents differently although its outline was the same: Add Anchor
Points on a top object moved the art, and a top object whose .ai mesh
saves each side's curve split into thirds drew its type up to about
half a point off its drawn art, which follows the whole curve's
parameter.

Coons::from_top_object now checks whether a side's pieces, from corner
to corner, are all pieces of one cubic. The pieces' spans of that
curve's parameter come from the handle lengths either side of each
split, the curve from the outer ends and handles, and every piece must
lie within 0.02 pt of the curve's piece over its span (each control
point). Such a side runs along that curve, by its parameter. Any other
side keeps the polygon shares, including a side only partly made of one
curve and a smooth side drawn in many pieces that isn't one curve as a
whole. Corners found from the outline can still move when anchors are
added, as before. Gradient meshes made from a path are unchanged.

Existing top-object envelopes with such sides redraw.
a_split_curve_moves_the_top_object_share_to_its_new_anchor, which
pinned the earlier behaviour, becomes a_split_curve_keeps_the_top_object_patch.

Tests: a_split_curve_keeps_the_top_object_patch,
a_top_object_joins_a_split_curve_saved_with_rounded_coordinates,
a_top_object_keeps_smoothly_joined_curves_apart,
a_top_object_side_partly_of_one_curve_keeps_the_polygon_shares and
a_top_object_side_of_many_arcs_stays_on_them (vectorcraft-doc),
envelope_top_object_keeps_its_look_when_anchors_are_added
(vectorcraft-engine) and
a_top_object_whose_top_is_one_curve_split_in_thirds_opens_live
(vectorcraft-eps).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 11:18:40 +00:00
dotCooCoo 737d9bc5b8 PDF/.ai save: envelopes stay editable when the file is reopened
A PDF-compatible .ai saved with Include Editing Data for Other Apps now
writes warp, mesh and top-object envelopes as live envelope records in
its editing copy: the grid or contents, Envelope Options, Fidelity,
Edit Contents, a warp's settings and the contents' box, beside the
distorted art, in the form the .ai envelope reader reads. The file
opens with the same live envelopes.

Envelopes the reader would open in another shape or with other
settings are written as their distorted art with a warning: type in
the contents, a turned frame, an old warp map, a mesh laid out another
way round, a top object whose sides do not pair up, a Fidelity that is
not a whole number, bounds margins kept with Distort Appearance off,
envelopes in opacity masks, pattern tiles, clipped art or another
envelope's contents, on a large canvas, and after the first 256.
2026-11-13 02:28:11 -08:00
BFlat 1dbdbce91f Merge pull request #7080 from dotCooCoo/feat/trace-create-strokes
Image Trace: Create Strokes makes a wide shape's thin features strokes and keeps its body a fill
2026-11-13 17:03:37 +07:00
BFlat 54f0c1b99b Merge pull request #7003 from mockydevs/fix/graphic-styles-list-height-2992
Graphic Styles: the list fills a taller panel above its footer
2026-11-13 17:03:22 +07:00
dotCooCoo 2129d00868 Dimension tool: a point on a Smart Guide stays on the guide
A Linear pick that the Smart Guides left where the pointer already was,
for example straight below the first point on the vertical construction
guide through it, or exactly in line with an object's center, was taken
as not snapped. The pick then moved to the closest point of a segment
within 8 pixels while the guide still showed it on the guide. The same
happened to a point already on a grid crossing with Snap to Grid on, or
on a whole pixel with Snap to Pixel on.

The pick now keeps the snapped point whenever the snap shows guides or
grid or pixel snapping is on. With Snap to Grid or Snap to Pixel on, a
pick off an anchor is no longer moved from the grid or the whole pixel
onto a segment. A point that no
anchor, guide, grid or pixel snapping places still lands on the closest
point of a segment. An anchor within 8 pixels, a segment within the
Smart Guides snapping tolerance and a nearby guide crossing still take
the point as before.

Docs: docs/dimensions.md, docs/target-app-parity.md.
2026-11-13 01:48:09 -08:00
dotCooCoo 6c773585fa Type: the Every-line Composer sets mixed sizes in shaped and wrapped area type
A paragraph whose characters differ in line metrics (type size, font,
leading, vertical scale, or an inline graphic taller than the text) was
broken with the Single-line Composer in area type of a shape other than
a rectangle, or with text wrap. The every-line composer now composes it
there too.

- The composer takes each line's width from a measure that follows
  where the lines above it went (`composer::Measure`,
  `compose_with`). The fixed widths by line number are one such measure.
- In a frame whose lines are not all as wide, each line the composer
  tries is placed from the pen the lines above it left, at its own
  tallest metrics, as the line loop places it (`layout/shaped.rs`).
  Starts with the same placement are shared, and each start keeps the
  placements it was tried with.
- A line too long for its room does not end the search from its start
  while a glyph after it could make the line taller and set it lower,
  where the frame may be wider.
- The line loop places a composed line in such a frame at the line's
  tallest metrics, so it gets the room the composer measured for it.
- The search for one paragraph stops after 1,000,000 lines tried or
  2,000,000 baseline steps. The searches of all the paragraphs of one
  frame share a budget of 2,000,000 lines and 4,000,000 steps, kept
  apart from the line loop's own steps. A paragraph whose search stops
  is broken line by line. A paragraph the frame has no room for is
  broken line by line too, and overflows as before.

Tests: crates/text/src/tests/combos.rs (a stepped frame, a frame that
widens, text wrap with two spans on a baseline, overflow, a long
paragraph) and crates/text/src/layout/wrapping_tests.rs (the composer
with a measure, and the shared search budget).
2026-11-13 01:24:33 -08:00
dotCooCoo 345cd021dc i18n: Dutch translates status and error messages
Dutch was not in COMPLETE_MESSAGES, so with the interface in Dutch
script errors, plug-in manifest and permission errors, parameter
schema errors, 3D effect refusals, file-reader errors and data-set
messages showed in English.

- nl.tsv gains 557 `@msg` rows and one `@msgplural` row: the messages
  the German catalog translates and Dutch lacked. Most are one block
  with a header comment on their conventions, in the order de.tsv
  holds them; the rest sit next to the related existing rows.
  Backticked names, command ids, double-quoted values and bare true,
  false and null stay as the English writes them.
- nl joins COMPLETE_MESSAGES, so the message tests that iterate the
  list check Dutch too, and the untranslated report lists Dutch's
  missing messages. It lists the same set for Dutch as for German.
- The registry comment, docs/development.md, the Dutch row and
  introduction of docs/localization-parity.md, and the interface
  languages line of docs/target-app-parity.md list Dutch among the
  languages that translate messages.
2026-11-13 01:23:19 -08:00