Commit Graph
13399 Commits
Author SHA1 Message Date
BFlat e56d1c7dce Merge pull request #7407 from storytold/fix/fmt-craft-libs-migration
Fix main CI after the craft-libs migration (fmt, wasm)
2026-11-14 12:46:39 +07:00
dotCooCoo 396796ff9b Type: the Every-line Composer sets the visible lines of a mixed-size paragraph that overflows a shaped frame
A paragraph whose characters differ in line metrics, in area type that is
not a rectangle or that wraps around an object, was broken line by line
with the Single-line Composer when the frame could not hold all of it.
The lines the frame holds are now composed with the whole paragraph: a
line the frame has no room for, and every line after it, is measured as
wide as the span of the last line set in the frame, less its indents.
Those lines overflow, or go on in the next threaded frame, which composes
them again at its own widths. A paragraph whose first line has no room,
and a search that passes its limits, still fall back to Single-line.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 21:46:30 -08:00
dotCooCoo ffc698d89c Merge main into fix/ru-missing-message-rows 2026-11-13 21:45:30 -08:00
dotCooCoo 9a2ce6d433 i18n: Russian translates 114 status and error messages that no catalog had
untranslated_report lists 167 messages under "messages, all 23 languages
checked". ru.tsv gains an @msg row for 114 of them, in one block at the end
of the file: image export and encoding errors, color profiles, color books
and swatch exchange files, binary DXF and DWG import, 3D and Revolve limits,
and script, zip, line and file errors. Placeholders, parameter names and
literal values stay as the English writes them.

The other 53 are left to the open pull requests that translate them
(composite fonts, SVG filters, read roots and graph design).
2026-11-13 21:44:53 -08:00
Dylan M. Taylor c11ee11c1c Merge pull request #7454 from storytold/codex/fix-ci
Fix CI formatting after craft-libs cutover
2026-11-14 00:44:10 -05:00
dotCooCoo b39bfbed4f Export Selection: the picked format's options dialog follows the save panel
document.exportSelection run with no params or {} (a shortcut, or an
automation call) wrote the picked file straight away with each format's
defaults: a PDF used the default preset, so Save PDF's options, such as
writing long pages in points, could not be chosen. It now takes Export
As's route after the save panel: Save PDF without its Artboards
section, SVG Options without its artboards, the raster options, EPS
Options, DXF Options, Text Export Options with Selection Only on, or an
exporter plug-in's parameters. OK writes the selection alone, cropped
to it, with the chosen options. While a dialog is open the call returns
null, and OK returns what file.exportAs returns, without format or
bounds.
With a path or any option it still writes straight away.

- file.exportAs takes exportSelection: true and then writes the
  document that document.exportSelection writes
  (fileio::selection_doc), without artboard choices. The options
  dialogs and the replace-file questions pass it on.
- Nothing selected stops before the save panel.
- SVG Options opened by Export Selection leaves the remembered Use
  Artboards as it was.
- Exports through file.exportAs (Export As and the options dialogs)
  apply Appearance of Black's rich black, as document.export does.

Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md.
2026-11-13 21:44:06 -08:00
dotCooCoo a173ba75d2 Docs: script.run params text puts a space before $.includePath
The $.fileName clause in the script.run params text ended without a space before $.includePath.
2026-11-13 21:36:00 -08:00
dotCooCoo 81b79ae67d Swatches: a gradient stop unlinked from its spot swatch also drops its plates
A gradient stop that loses its link to a spot swatch kept the process
plates it printed with that ink, so it carried a DeviceN mix with no
ink. Paint::map_links, which deleting a swatch, recoloring and color
adjustments go through, now clears a stop's plates whenever it leaves
the stop without a link, as GradientStop::set_color(color, None)
already does. Library gradients and library character styles linked
to a swatch the document lacks unlink their stops the same way.

Docs: docs/target-app-parity.md.
2026-11-13 21:30:14 -08:00
dotCooCoo d57cc3896f Docs: the complete-message language lists match COMPLETE_MESSAGES
The development guide and the progress doc now name all 23 languages in
COMPLETE_MESSAGES: Hindi was missing from the first, and Georgian, Hindi,
Turkish and Catalan from the second. The localization summary adds
Bosnian to the languages marked full that are not in the list.
2026-11-13 21:28:53 -08:00
dotCooCoo f4fdf2755e i18n: Brazilian Portuguese translates 114 messages that no catalog had
The untranslated report listed 167 status and error messages that no
complete-message catalog translated. pt-br.tsv now has an @msg row for
the 114 of them that no open pull request already translates: messages
of the 3D and Revolve effects, the image encoders, color profiles, swatch
exchange files, color books and DXF import. Placeholders, parameter names
and literal values stay as the English writes them.

The other 53 (composite font, SVG filter, read root and graph design
messages) are translated by open #7211, #7378 and #7405; adding them here
too would make bundled_catalogs_are_consistent fail on a duplicate row
once those merge.
2026-11-13 21:27:20 -08:00
dotCooCoo fb48216aa2 Image Trace: clean flat art of 10 to 16 evenly spaced levels traces as flat color
The continuous-tone check exempts a neutral or single-curve picture when
sixteen colors leave at most a fifth of the error of eight, but only
applied the exemption below the 0.1 ratio. Clean gray, sepia, single-hue
and duotone art of 10 to 16 evenly spaced levels lands above it, so it
was smoothed before tracing. The exemption now applies in both branches.

At 16 Colors, 12 sepia and 16 gray or sepia levels crossed by 1 px lines
keep every region the lines cut, and Automatic traces 16 clean sepia
levels in 9 colors instead of 4. Art in several unrelated colors is
unaffected.
2026-11-13 21:23:00 -08:00
dotCooCoo 541059faf2 Path: path.setHandle to the handle's own point leaves a smooth anchor unchanged
path.setHandle checked for a no-op by running the handle edit on a copy
and comparing the whole anchor. On a smooth anchor the edit recomputes
the other handle, so when that handle was not exactly in line, or the
recomputed value rounded differently, a request for the handle's own
point realigned the other handle and recorded an undo step.

The check now compares the requested point with the handle itself, and
still lets independent turn a smooth anchor into a corner.
2026-11-13 21:21:11 -08:00
dotCooCoo 6bd6755283 Effects: a clipping path's geometry effects shape the clip on the canvas
A clipping path with Roughen, Transform or another effect that reshapes
a path drew its own fill and stroke reshaped, but the canvas and raster
export still clipped the art by the plain path. SVG and PDF export bake
the effect into the path before writing the clip path, so the screen
and a saved SVG showed different art.

effects::clip_outline now applies the live geometry effects in the
clipping path before taking its region: the path's or compound path's
own effects, the effects of its fills and strokes (the region is then
the union of the paths they give, as an export bakes them), the effects
of a compound path's paths, of a group's visible members and of a
nested clip group's clipping path, shown or hidden, and a group's own
effects as an export evaluates them. Raster effects such as Gaussian
Blur leave the region as it is. The renderer, the engine commands that
measure a clip region (flatten, arrowheads, cutting) and the writers use
it; the writers receive the baked document, so their output for a path
with effects does not change.

The renderer measures a clip group by the region its clipping path's
effects give it, so a view or tile that shows only the moved part of
the clip still draws the clipped art, and it drops cached clip regions
that hold a plug-in effect when plug-ins change.

Two rules change in every export as well as on the canvas:

- When the clip group, or a group around it, has a geometry effect of
  its own, the clipping path's effects apply first and its gradients
  move with them. #7114 left the clipping path's effects live under the
  group's effect, so they applied after it.
- Type used as a clipping path keeps its own effects when a group's
  effect outlines it. #2073 and #2290 outlined it without them, to
  match the plain clip the canvas drew.
2026-11-13 21:18:18 -08:00
dotCooCoo 6b9e7e9fa6 Automation: replies hide a file or missing folder on the way to the recovery folder
When a recovery copy fails because a folder on the way to the recovery
folder is a file or is missing, the error names that folder, not the
recovery folder. Replies to a client confined to read roots hid only
the recovery folder itself, so the full path of that ancestor stayed
visible. A reply now also hides each ancestor of a recovery folder that
it writes as a whole path. The check compares text only and does not
look anything up on disk.

Follows #7287.
2026-11-13 21:16:40 -08:00
dotCooCoo 5d6d04e8ed i18n: translate 124 more status and error messages into Japanese
On main, the untranslated report lists 167 status and error messages
under "messages, all 23 languages checked": no complete-message catalog
has a row for them, so the Japanese interface shows them in English.

- ja.tsv gains 114 @msg rows for the listed messages: image export and
  encoding errors, color profiles, color books and swatch exchange
  files, binary DXF and DWG import, 3D and Revolve limits, and script,
  zip, line and file errors. The 53 listed messages that #7211, #7378
  and #7405 translate are left out.
- It gains 10 @msg rows for messages other catalogs translate and
  Japanese lacked: the Banner Hems and Grommets errors, a parameter
  range and the failed recovery save. The three script and RTF parsing
  errors that #7314 adds are left out.

Placeholders, parameter names, command ids and literal values stay as
the English writes them, and the rows follow the catalog's Japanese
message conventions.
2026-11-13 21:14:31 -08:00
Dylan M. Taylor 2ce569a6a7 Format svg adapter after craft-libs cutover 2026-11-14 05:14:16 +00:00
dotCooCoo df951caea4 Docs: $.fileName names the file being run in code eval or Function compiled
The script module docs, the script.run params text and the evidence line now
say that $.fileName in code that eval or Function compiled gives the file
being run, and the params text says that the empty name applies to code
compiled from a script given as text.
2026-11-13 21:09:02 -08:00
dotCooCoo 7abd11d19d Plug-ins: windows and processes take turns on the Plug-ins folder
Two windows or processes using one Plug-ins folder could interleave their
writes: an install from one could leave its module with the other's grant,
a grant written for one plug-in could drop one written for another, and
plug-in settings and cache changes could lose each other's keys or pass the
cache caps together.

Each change to the folder now runs in one turn on disabled.lock, the file
the disabled list was already locked with:

- plugin.install takes the turn before it reads the stored grants and holds
  it through the module, grant, put-back and disabled-list writes;
  plugin.remove holds it through grant removal and storage deletion.
- Grant writes, the disabled list, loading the folder, and the plug-ins'
  settings and cache changes read and write in a turn.
- When disabled.lock can't be locked, plugin.install and plugin.remove fail
  and change nothing, plugin.setEnabled keeps its change in the process, and
  loading the folder writes nothing to it.

Docs: docs/plugins.md, docs/mcp.md, the plugin.install and plugin.remove
params text, docs/target-app-parity.md.
2026-11-13 21:07:28 -08:00
dotCooCoo 3beda53fd0 i18n: French row counts match the branch after the merge of main
The French row of docs/localization-parity.md gave the counts on 92594378a. After main was merged in, fr.tsv holds 9,166 rows, 3,436 of them @msg and 48 @msgplural, and the untranslated report lists 178 missing French messages: 11 for French alone and 167 that main added for every language.
2026-11-13 21:03:26 -08:00
dotCooCoo ab0ae9f880 Type: area type insets stop at the frame's edges, not at an edge its contours share
An edge that two contours of an area type frame share lies inside the
frame, and lines already run across it. Inset Spacing still kept text
clear of it, so a frame of contours that meet along an edge left a gap
of twice the inset at the seam. The inset now applies only beside the
stretches of edges that bound the frame, counted by parity as the line
spans are. A negative text wrap offset no longer shrinks a wrap object
beside an edge two of its contours share.
2026-11-13 21:00:03 -08:00
ruchirguitar 7929c95ce9 #6613: guard web Gujarati selection and address local review 2026-11-14 10:29:25 +05:30
ruchirguitar ea2deffde1 style(svg): format craft-svg delegation in text_within 2026-11-14 10:28:44 +05:30
dotCooCoo c08d42fd58 Web: Data Recovery's lock snapshots use the monotonic clock
The browser build stamped each snapshot of the Web Locks held with
Date.now() and judged its age against Date.now(). After the system
clock was set back, an old snapshot read as fresh, so a tab could
report no lock on an area another tab still held. Snapshots are now
stamped and aged with vectorcraft_ui_egui::now_ms(), and a snapshot
stamped later than now is not trusted.
2026-11-13 20:57:53 -08:00
bflatastic ff9222c1d4 xtask: name wasm crates by package id
craft-svg builds against an older vectorcraft-geom and vectorcraft-pathops by git, so the
graph holds two packages of each name and `cargo check -p vectorcraft-geom` is ambiguous.
The wasm step now passes each workspace crate's package id.
2026-11-14 11:55:41 +07:00
dotCooCoo 15bf65f014 Merge main into fix/issue-6784 2026-11-13 20:54:25 -08:00
dotCooCoo 4416640bf4 3D: Expand as Wireframes outlines each face's own stroke under a later Outline Stroke
Expand as Wireframes reshaped the faces of a 3D effect with the geometry
effects after it, but the faces it built carried no stroke. An Outline
Stroke after the 3D effect then outlined a plain 1 pt stroke with miter
joins, while the canvas outlines the round-joined stroke each face is
drawn with: the seam stroke (half the facet's thickness, 0.05 to 0.75 pt;
0.75 pt on Revolve) or the 0.5 pt wire edge of the Classic Wireframe
surface. The wireframe's faces now carry that stroke, so the expanded
edges lie where the drawn outlines lie.
2026-11-13 20:53:22 -08:00
dotCooCoo 5750843d73 Links: placed images in stored brush art get Links rows
Linked images and placed documents in the art of the stored brushes (a
scatter or art brush's art, a pattern brush's tiles) are listed in the
Links panel after the other stored art, one row per linked or placed
object in each brush tile, with the brush as owner
(`{kind: brush, name, index, tile?}`). Check Links, the missing and
modified files reported on open, the link watch, Find in Folder, Relink
(including Relink All Instances), Update, Embed, Placement Options, Link
Info and Go To act on them, and their tooltip names the brush.

- vectorcraft-doc: `Document::mounted_art` holds art kept outside the
  document's trees while a command looks it up by id; the stored-art
  lookups, link visitors and `art_roots` include it, and
  `number_mounted_art` gives its objects ids of their own.
- vectorcraft-brush: `stored_art::StoredArt` mounts the art of the
  brushes that hold images or placed documents and writes back only the
  entries whose art changed. Entries that do not read as a brush are left
  as stored. `brush::store` keeps ids of their own in that art, so a row
  keeps its id between commands.
- vectorcraft-engine: the links commands, opening a document, saving a
  placed document's source, output of linked files and placed documents,
  color masks after Update or Relink, and Package relinking go through
  the mounted art.

Brush art rows follow the general Links rule as VectorCraft's own
application of it.
2026-11-13 20:51:54 -08:00
dotCooCoo 5b24c0a55e Actions: Batch errors name the step by its place in the commands sent
action.batch drops Open steps before it plays each file or data set, and
returned the per-file error of command.batch unchanged, so `batch step N`
counted from 0 within the filtered steps. A failed step is now named
``action step N (`id`) failed: reason``, N being its place in the
`commands` the caller sent, from 1, with skipped Open steps counted.
The batch's own open, data set choice and destination steps keep their
unnumbered errors, and command.batch messages are unchanged.

A prefs.import step, which command.batch refuses when it reaches that
step, rolling the batch back, is now refused before any file opens
instead of failing each file or data set the batch plays.

Docs: action.batch params text, docs/mcp.md, docs/target-app-parity.md.
2026-11-13 20:47:50 -08:00
dotCooCoo 774ffbbc21 EPS import: KSC-Johab type reads one-byte ASCII codes
The KSC-Johab CMaps had no code space listed, so type in a font over
KSC-Johab-H or KSC-Johab-V read every code as two bytes, so two ASCII
characters moved the point one em instead of two. The code
space now follows Adobe's KSC-Johab-H CMap: <00>-<80>, <8441>-<D3FE>,
<D831>-<DEFE> and <E031>-<F9FE>
(https://github.com/adobe-type-tools/cmap-resources/blob/master/Adobe-Korea1-2/CMap/KSC-Johab-H).
2026-11-13 20:47:32 -08:00
dotCooCoo 0263b4af13 Merge main into fix/smooth-keeps-closed-start 2026-11-13 20:38:29 -08:00
dotCooCoo 702b863306 .ai import: reads Kinsoku, Kinsoku Shori Type, Burasagari and Bunri-Kinshi
Paragraphs in a modern .ai text document now keep their East Asian line
breaking settings: the Kinsoku set (None, Hard or Soft), Kinsoku Shori
Type, Burasagari and Bunri-Kinshi. Each comes from the paragraph, else
its paragraph style or a style that one is based on, else the document's
defaults, as the other paragraph settings do. A value that cannot be read
takes the paragraph style's value.

A paragraph whose Kinsoku set is a custom set takes its paragraph
style's set, and the import warns that custom Kinsoku sets were left out.
.ai export does not write these settings.
2026-11-13 20:36:17 -08:00
dotCooCoo 0a68935bf2 .ai import: Art brush guides match as fractions of the art too
A path's guide lengths matched the brush's within 0.001, so on very short art guides at 30% and 35% of the art matched. The guides now also match as fractions of the art's length.
2026-11-13 20:35:50 -08:00
dotCooCoo 3ea1524618 Image Trace: Flat Logo traces a gray between black and white as one shape
With a gray among the colors, an anti-aliased edge pixel of black on
white is also a mix of black and gray, or of gray and white, and the
unmixing took the mix with the smallest error, which often held the
gray. A black disc and a gray square on white traced the gray as the
square plus 8 to 24 slivers around the disc, with the colors found or
named exactly.

When another mix of two classes also reproduces an edge pixel, the pixel
now takes it if that mix's classes lie nearer in the flat areas around
the pixel and the pixel has a neighbor on each side of the left-out
class, along the line between the other two. Pixels within a few levels
of a class outside a flat area are checked the same way, and they now
count toward the number of pixel values that decides when a busy image
is averaged down further. A flat area that stands for no class, as with
colors named some levels off, counts as holding the classes of its mix,
and so do the pixels of exactly its color joined to it. Whether a
hard-edged image is softened is still judged from each pixel's best mix.

The slivers are removed around sharp edges and edges blurred by up to
about half a pixel; an edge blurred by a pixel or more, or pixel noise
of a level or more, can still leave some. Gray strokes two and three
pixels wide beside black stay gray, and so does a one-pixel gray line
between black and white of at least 13 joined pixels. These trace as
black and white: a shorter one-pixel line, or one that falls between
pixels or is blurred; a soft-edged gray stroke about two pixels wide
touching black; and most or all of a soft-edged stroke about three
pixels wide touching black, 12 pixels long or less.

Docs: docs/image-trace-adjustments.md, docs/target-app-parity.md.
2026-11-13 20:32:14 -08:00
ruchirguitar f965fd6f4a Merge branch 'upstream/main' into feat/layers-row-context-menu 2026-11-14 09:58:09 +05:30
dotCooCoo e93b672b3b Keyboard Shortcuts: panel menu commands with typed optional params can take a shortcut
An undoable command without a menu path or a default key was listed only
when every field of its params doc ended with `?`. A typed optional field
such as `on?: bool`, `ids?: [id…]` or `artboard?: index=0` counted as
required, so Fractional Widths, System Layout, Align to Pixel Grid, Embed
Image, Update Links, Delete Slices, Switch Orientation, Distribute Spacing
for artboards, Target Contents, Invert and other panel menu commands that
act on the selection could not be given a shortcut.

`runs_without_params` now reads the params doc with
`names_a_required_field`, the parser the editor already uses for menu
commands. The commands the new rule would list that set nothing, fail or
only give data back when a key runs them with no params (the panel
setters such as Fill and Character, the exports and saves without a path,
the undo group's begin and end, Create Dimension) join `NEEDS_A_PARAM`,
so they stay out of the list until the user gives one a key. New Symbol
joins it too, since the Symbols panel runs it through Symbol Options.

docs/mcp.md describes the rule and the full `NEEDS_A_PARAM` list.
2026-11-13 20:24:04 -08:00
dotCooCoo 1fbbb0a4e7 SVG export: shadows in a masked group show in readers that bound it by its shapes
Some SVG readers draw a masked group only inside the bounding box of its
shapes. A drop shadow, glow, blur or feather inside the group paints past
those shapes, so these readers left it out, while others drew it.

A masked group now starts with a rectangle over the mask's region,
unpainted (fill="none") and hidden (visibility="hidden"), when what it
holds has a visible raster effect: on a member at any depth, on a fill or
stroke, in a symbol's art, or in the art a blend or other live object is
written as. The rectangle widens the group's bounding box without drawing
anything, and import leaves it out. The masked groups of a knockout group
get the same rectangle when an element inside them has such an effect.
Other masked art, brushed strokes included, is written as before, and so
is an object whose own effects are written outside its mask.

Tests: a_masked_group_spans_its_region_when_its_art_paints_past_its_shapes
and a_knockout_group_spans_its_masks_when_an_element_paints_past_its_shapes
in crates/svg/tests/it/blend_export.rs.
2026-11-13 20:23:15 -08:00
ruchirguitar 05aef1f2c2 Merge remote-tracking branch 'origin/main' into pr-6613 2026-11-14 09:52:17 +05:30
dotCooCoo fb9d7208d5 Merge main into fix/ko-message-rows 2026-11-13 20:20:23 -08:00
dotCooCoo 155c2f722b Place: Placement Options clip a DCS image inside its file's clipping path
When links.update or links.relink re-read a DCS file and Placement
Options also clipped the image (Fill with Clip, or an aspect change
that overflows), the placement rectangle replaced the file's clipping
path, so plate art outside that path showed and printed.

The file's clip group now stays around the image and the placement's
clip goes in a clip group around it. The clip group holding just the
file's group takes that clip, as one holding just an image does today,
else a new one does, so later updates reuse the same two groups. A
file that loses its clipping path leaves its group as a plain group
inside the placement's clip. The same applies in symbols, graph
designs, patterns and opacity masks.
2026-11-13 20:18:35 -08:00
ruchirguitar 0ff98d0380 feat(layers): implement row context menu and actions
- Add right-click context menu on layer, group, and object rows:
  - Options... / Layer Options... / Options for Selection...
  - Duplicate <name> / Duplicate Selection
  - Delete <name> / Delete Selection
  - Lock Others / Unlock All Layers
  - Hide Others / Show All Layers
  - Locate Object
  - Collect for Export
- Select unselected row on right-click; preserve multiselection if already highlighted
- Dispatch actions via registered engine commands with Undo/Redo tracking
- Add headless egui tests covering selection, dispatch, and state integrity
- Update gaps and ui-parity documentation for G12
2026-11-14 09:47:46 +05:30
dotCooCoo 9b2e21f6ca Live Paint: Merge keeps the visibility, opacity and blending of the parts it carries
Merge copied a face's or edge's appearance to the part it rebuilds in its
place, but not its visibility, opacity or blending mode. A half-transparent
face drawn by its stroke became opaque, and a hidden edge was shown again.
2026-11-13 20:14:37 -08:00
ruchirguitar 6f6746bd72 #6613: reconcile Gujarati catalog and complete current interface coverage 2026-11-14 09:44:24 +05:30
dotCooCoo d06870e414 Docs: DXF export of a non-circular ellipse is listed as a native ELLIPSE
The DXF SPLINE line in docs/target-app-parity.md said a non-circular
ellipse keeps the existing fallback. crates/cad/src/scene.rs writes a
closed four-cubic ellipse, or a circle scaled out of proportion, as a
native ELLIPSE in R13 and later, unless its minor-to-major ratio is
below 1e-6, and only R12 keeps the fallback. The line now says so.
2026-11-13 20:13:42 -08:00
dotCooCoo b1cf6f810b PDF import: a knockout group in a form of its own opacity reads back as one group
PDF export writes a translucent knockout group as a form with its opacity around the /K group. PDF import now reads that pair back as one knockout group with the opacity, blend mode and isolation, instead of a translucent group holding a knockout group.
2026-11-13 20:13:02 -08:00
dotCooCoo 00de304ab0 Live Shapes: saved object.setLiveShape steps with a negative radius or out-of-range sides replay again
Since #7294, object.setLiveShape refuses a negative `radius` and a
`sides` that is not a whole number from 3 to 1000. Session::replay
applied the same checks, so a journal entry or action step recorded
before that change with such a value stopped with a parameter error.

On replay, a negative radius now counts as 0, which is how it drew
before. A whole side count below 3 or above 1000 is brought into that
range, and a fractional or negative one is ignored. Direct calls still
refuse these values.
2026-11-13 20:12:05 -08:00
dotCooCoo f0175cbdcb i18n: French leaves the read-root and graph design messages to their own pull request 2026-11-13 20:09:54 -08:00
dotCooCoo 877eb84f6d Graphs: an unchanged paint write keeps a series' kept gradient
A paint write that leaves a series mark's gradient as the graph drew it
now keeps the series' stored gradient and its unit-box flag in two more
cases:

- a mark with no width or no height, such as a column for a value of 0,
  where the stored gradient was replaced by the mark's gradient in
  document coordinates and no mark took it on the next redraw;
- a gradient kept in document coordinates (a file saved before the unit
  box), which a stroke weight written as it was moved to the unit box,
  so the next redraw placed it on each mark's own box.
2026-11-13 20:09:25 -08:00
dotCooCoo 991c36458a .ai import: an Art brushed path opens live only when its own settings would
A path's Art brush settings matched the brush's within a small tolerance, and only the brush's were checked. A path with a Spacing of 0.0005, or guides that leave no art between them, opened live with the brush's settings. The path's settings now pass the same checks as the brush's, and such a path keeps its drawn look.
2026-11-13 20:03:35 -08:00
dotCooCoo 85e0d59bfa Brushes: Scale Proportionately art grows with the stroke weight
An Art brush set to Scale Proportionately drew its art at the same size
on every stroke weight: the art was scaled across only by how much the
path stretched it along and by the brush's Width. Stretch to Fit Stroke
Length and Stretch Between Guides already multiply by the stroke weight.
Proportional art now does too, so a 3 pt stroke draws it three times as
wide as a 1 pt stroke, still as long as the path. The same applies to
each run with Overlap on and to a Width that follows the pen.

Existing Scale Proportionately art brush strokes at weights other than
1 pt draw scaled by the stroke weight. Strokes at 1 pt draw as before.
2026-11-13 20:00:01 -08:00
dotCooCoo be8fafde78 Type: justification spaces a right-to-left warichu block, and the caret keeps to its block line
A justified line opened no gap before a right-to-left warichu block: the
glyph drawn first after the text before it is not the block's first
glyph, and it was taken as continuing the block. The block now starts at
the first of its glyphs the line draws, and blocks the bidi order
interleaves keep their starts.

The caret next to a character of a block spanned the whole line. It now
spans the band of the character's block line, as the selection does.
2026-11-13 19:58:57 -08:00