Commit Graph
1889 Commits
Author SHA1 Message Date
dotCooCoo e4a066f12a Appearance: effect.move's reply follows an explicit toItem to the first target 2026-11-02 12:04:10 +07:00
dotCooCoo 468102e101 Appearance: test effect.move's reply when the first selected object has no effect to move 2026-11-02 12:04:10 +07:00
dotCooCoo d57d0b8e67 Appearance: effect.move reports the effect's index in the object whose row it names
With the Appearance panel's active row, the row belongs to the first
selected object, which need not paint first. effect.move reported where
the effect landed in the first target in paint order; it now reports
where it landed in the first selected object for the active row, and in
the first target for an explicit fromItem, as effect.apply does.
2026-11-02 12:04:09 +07:00
dotCooCoo 53e24b5fdc Export for Screens: a folder named with dotless ı is not merged into i
Export for Screens compared names and folders by upper-casing each
character, which turned dotless ı into I. A row with the folder ı then
wrote into the folder i, and an artboard named ı was numbered as a
second i. NTFS keeps ı apart from i and I, and so does Unicode case
folding outside Turkic languages, so fold now leaves ı as it is.
2026-11-02 12:04:08 +07:00
dotCooCoo beb89ebec4 .ai import: vertical type keeps its orientation and Tate-chu-yoko runs
Squashed from #5086 onto main.
2026-11-02 12:04:08 +07:00
dotCooCoo f46427167c .ai import: area type keeps its rows, columns, gutters and first baseline
Area type made from the editing data's text document came in with one
row, one column, the 18 pt gutter and the Ascent first baseline,
whatever its frame kept. Each area frame now keeps its rows and columns,
the gutter between rows and the gutter between columns, and its first
baseline (Fixed, Cap Height, Leading or x Height) with its minimum,
scaled with the art's units. Each frame of a thread keeps its own, and a
key that is missing or can't be read leaves that option as it was. A
first baseline the frame keeps is no longer replaced by the fixed one
that matches the file's first line.

AreaOptions gets row_gutter (rowGutter, left out while unset), which
layout spaces rows by; text.areaOptions takes and reports rowGutter, a
gutter given alone sets both, and the Area Type Options dialog shows
Row Gutter.
2026-11-02 12:04:07 +07:00
dotCooCoo d4f5004580 .ai import: area type flows around its text wrap objects
Fixes #2763. An object whose editing-data dictionary holds the text wrap
options keeps them (offset in points, 6 pt by default, clamped to
±1000 pt; Invert Wrap), and the objects after the text in a version 7
wraparound group (`*w` … `*W`) are wrap objects at the default options.
Area type below a wrap object in its layer flows around it before the
layers are compared with the page, and threaded type flows on into its
next frames. A Large Canvas file's offset is scaled with its art.

The wrap resolver moves into `vectorcraft_doc::text::resolve_wrap` for
the import and the edit-time refresh, and builds each wrap object's
outline once per layer. A file whose wrap shapes would copy more than
2,000,000 anchors is not read from its editing data.

Docs: docs/file-format-parity.md.
2026-11-02 12:04:05 +07:00
dotCooCoo 100730d4d3 .ai import: a file whose PDF part can't be read opens from its editing data
Squashed from #5051 onto main.
2026-11-02 12:04:04 +07:00
dotCooCoo 54436b0aa2 i18n: open, save and recovery messages name their counts in the singular or the plural
The status bar said "Opened with 1 note(s)", "Saved <path> with 1 note(s)",
"Discarded 1 recovered document(s)" and "Restored 1 recovered document(s)",
and the Global Edit options showed "Result(s): 1". They now use the singular
for 1 and the plural otherwise, in English through i18n::msg_n and in every
catalog through @msgplural rows (the Results line through an @plural row).

The engine's warnings for linked images and placed documents output as their
previews, and its errors for a page past a placed file's last page or
artboard, also name their counts in the singular or the plural.

The catalogs drop the rows no message reads any more: the plain "(s)" rows
of the changed messages and the @msg row for "Packaged {n} file(s){note}".

Follows #5002.
2026-11-02 12:04:04 +07:00
dotCooCoo 5e3a0090a2 Swatch libraries: test that a pattern library adds only the picked pattern
Reading a pattern library adds no pattern or swatch to the document. A
pattern added from it undoes and redoes with its swatch, and both are
kept after a save and reopen.
2026-11-02 12:04:03 +07:00
dotCooCoo 284b5b45b6 Swatch libraries: only a built-in pattern library's own swatches draw its pattern tiles 2026-11-02 12:04:03 +07:00
dotCooCoo 6b7513c0a0 Swatch libraries: a pattern swatch is found without copying the document
swatch.library.add looks for the library's pattern among the document's
patterns of that name or a numbered copy of it, instead of importing it
into a copy of the document. Only a swatch that paints with the pattern
unmoved counts as the pattern's swatch, so a swatch whose pattern was
moved in its space is not reused and applied in its place.
2026-11-02 12:04:02 +07:00
dotCooCoo cb7df83217 Swatch libraries: Patterns › Geometric Patterns adds a pattern with its swatch
Window › Swatch Libraries and the Swatches panel's library menu get a
Patterns submenu with Geometric Patterns, a built-in library of eleven
black pattern swatches drawn in code (dots, stripes, a checkerboard, a
grid, diamonds, triangles, crosses and a zigzag).

- Clicking a pattern in its library panel adds the pattern and its
  swatch to the document and applies it to the active fill or stroke
  (Alt: the other one), as one undo step. A second click reuses the
  swatch; a different pattern under the same name keeps its name and
  the library's pattern comes in numbered.
- swatch.library.list lists the pattern libraries after the gradient
  libraries (category "patterns"), and swatch.library.add brings a
  pattern swatch's pattern as pasting art painted with it does.
- The library panel draws each pattern tile from the library's pattern.
- "Geometric Patterns" is translated in every catalog.
2026-11-02 12:04:02 +07:00
dotCooCoo 11adb715b5 Place: a Word .doc document with too many property runs, paragraphs or properties is refused
A bin table that lists more than 100,000 runs of character or of paragraph
properties is refused while its pages are read. Every paragraph now counts
against the 100,000-paragraph limit, hidden and deleted ones and those in
field instructions included, and so does every line break. The properties
of the styles and of the property runs are read from one budget of 16 MiB,
and a document with more is refused.

Text inside a field's instructions is found with a count of the fields
open in their instructions instead of a search of the open fields for each
character.
2026-11-02 12:03:59 +07:00
dotCooCoo a6db4c9112 Place: a Word .doc style sheet that holds fewer styles than it counts is refused
Stshif.cstd counts the LPStd records of the style sheet and must be less than
0x0FFE. A sheet that ended before cstd records was read as far as it went, and
paragraphs in the missing styles were placed with the document defaults; a
cstd of 0x0FFE or more was cut to 0x0FFE. Both are now refused as a damaged
style sheet.
2026-11-02 12:03:58 +07:00
dotCooCoo 22218e9097 Place: a Word .doc paragraph's own indent holds over its style's
A paragraph style can set a logical start or end indent and the paragraph
itself only a physical left or right one, or the other way round. The
indent applied last now holds, after the sides are mapped for the
paragraph's direction; before, the logical indent always held, so a
paragraph's own indent was lost under its style's.
2026-11-02 12:03:58 +07:00
dotCooCoo ecd184510c Place: Word .doc styles are resolved once and damaged property tables are refused
Each style's formatting is now worked out once, on top of the style it
is based on, and each property run's formatting once, so a document with
many hidden paragraphs or long style chains no longer resolves the
chains again for every paragraph and run. Character styles are applied
as a change to the paragraph's formatting.

A bin table whose file positions do not rise, a property page listed
twice, pages whose runs overlap, and styles based on each other in a
loop are refused as damaged, as MS-DOC requires (PlcBteChpx, ChpxFkp,
StdfBase). Property runs refer to their page instead of copying it.
2026-11-02 12:03:57 +07:00
dotCooCoo e130cf3e0c .ai import: Convert to Shape opens live on paths and type
Squashed from #5142 onto main.
2026-11-02 12:03:54 +07:00
dotCooCoo 664fe8a12b .ai import: Stylize effects open with their saved settings
An object whose art style holds Drop Shadow, Inner Glow, Outer Glow,
Feather, Round Corners or Scribble now opens as itself with those
effects live, in their order. Feather is read under the name and key
the files use (`Adobe Fuzzy Mask`, `Radius`).

Each setting is read only when it is written as its kind (`/Int`,
`/Real`, a fill style for a color) with a value in range. An unknown
key or choice, a missing setting, or a Scribble with a color of its own
keeps the drawn look, where the reader used to cast, clamp or default
the value.
2026-11-02 12:03:53 +07:00
dotCooCoo f024f5a5f2 Variables: Export Data Sets marks visibility and image columns with # and @
Squashed from #5141 onto main.
2026-11-02 12:03:52 +07:00
bflatastic 78014a1b43 Variables: Import Data Sets and Load Variable Library record their step through edit_variables 2026-11-02 12:03:49 +07:00
dotCooCoo a6ce22da5c Variables: an import or library load that changes nothing adds no undo step
dataset.import and variable.loadLibrary added an undo step, cleared Redo
and marked a saved document modified even when the variables and data
sets came out as they were, such as the same CSV or library loaded
again. Both now build the new variables on a copy and record a step only
when it differs. variable.loadLibrary's params text and docs/mcp.md say
so.
2026-11-02 12:03:45 +07:00
dotCooCoo d0749dde41 Variables: Update Data Set keeps a row's values when every bound object is locked
Squashed from #5126 onto main.
2026-11-02 12:03:41 +07:00
dotCooCoo d9024f9c31 Variables: a command that changes nothing adds no undo step
Squashed from #5130 onto main.
2026-11-02 12:03:41 +07:00
dotCooCoo 06221ce545 Variables: a CSV header that names one variable twice is refused
dataset.import read two header cells that named one variable (City
and City, or Shown and #Shown) into the same column, so each row kept
only the last non-blank cell and the other value was dropped without a
word. The import is now refused with the two column numbers, and the
message has a row in every complete-message catalog.
2026-11-02 12:03:40 +07:00
dotCooCoo 660102064f Variables: Import Data Sets reports why a cell was refused
dataset.import replaced the reason for a refused cell with "value for
`X` must match its variable kind", so a visibility value such as
maybe and a graph value over 262,144 bytes gave the same message. The
error now keeps the line and gives the reason, and both messages have
a row in every complete-message catalog.
2026-11-02 12:03:38 +07:00
dotCooCoo 1be658c88c Variables: Export Data Sets refuses more data sets or variables than Import Data Sets reads
dataset.export checked only the 1 MiB size, so a document with more than
1000 data sets or 256 variables exported a CSV that dataset.import then
refused. Export now refuses such a document with a message naming the
limit, and the messages have rows in every complete-message catalog.
2026-11-02 12:03:37 +07:00
dotCooCoo adb446049b Variables: Undo and Redo clear a highlight that names no variable
Undo, Redo and a canceled undo group put back an earlier document but
left the Variables panel highlight as it was. After renaming the
highlighted variable and undoing, the highlight still named the new
name: a variable defined later under that name showed highlighted
without a click, and Delete removed it.

Each of these paths now clears a highlight whose variable the restored
document does not define, as Delete Variables and Load Variable Library
already did.
2026-11-02 12:03:36 +07:00
dotCooCoo 0ccc6d9df5 .ai import: paths keep their notes and Show Center
Squashed from #5129 onto main.
2026-11-02 12:03:35 +07:00
dotCooCoo db11446502 .ai import: legacy type keeps its spacing, hanging punctuation and hyphenation
Legacy type (`To` ... `TO`) now reads `min opt max TC` and `min opt max TW`
into the paragraph's letter and word spacing, `hangingQuotes Tq` into Roman
hanging punctuation, and `hyphenate limit lead tail max Th` into hyphenation
on or off, After First, Before Last and the Hyphen Limit (0 when the limit
flag is 0). Values are kept to the ranges the Justification and Hyphenation
options take, and a spacing triple out of order goes back to its default.

Operands of the legacy type attributes are read by their place: an attribute
whose operands are missing, are not numbers, or are flags other than 0 and 1
stays as it was. Before, a value that was not a number shifted the later
operands (`5 /x 7 Ti` set indents of 5, 7 and 0), and a bad `TA` or `Ta`
turned kerning on or set the alignment to left. One long `Tx` no longer goes
past the reader's limit on characters.
2026-11-02 12:03:33 +07:00
Brandon Thomas b676c626e0 Merge pull request #5186 from dotCooCoo/fix/legacy-ai-character-rotation
Legacy .ai: type with rotated characters is written as outlines that keep the rotation
2026-11-01 23:45:34 -05:00
Brandon Thomas d3ef9a6aae Merge pull request #5183 from dotCooCoo/fix/replace-symbol-clears-overrides
Symbols: Replace Symbol clears the old symbol's dynamic overrides
2026-11-01 23:45:26 -05:00
Brandon Thomas 01d371b744 Merge pull request #5182 from dotCooCoo/fix/pdf-spot-gradient-white-process-stop
PDF export: a white stop linked to a process swatch keeps a spot gradient on its ink
2026-11-01 23:45:21 -05:00
dotCooCoo 1186d1f712 Affinity: pressure curves that can't be read count against the document's limit
A curve rejected for having more than 256 segments now counts the most points a curve may have against the document's pressure points, so linking it from many strokes reads it a bounded number of times. Once the points are used up, later strokes import as plain strokes with a warning.
2026-11-01 18:09:53 -08:00
dotCooCoo 880a078053 Legacy .ai: type with rotated characters is written as outlines that keep the rotation
A version 7 text object has no arbitrary-angle character rotation: its
character attributes include baseline shift (Ts), scaling (Tz) and
tracking (Tt), Tv only turns Roman characters 90 degrees in vertical
text, and the positioning operators such as Tm are ignored on reading.
Type with Character Rotation was written as editable text with the
rotation left out, so rotated characters opened upright.

Such type is now written as its glyphs' outlines, the way type 8-bit text
can't hold already is, with the warning "type with rotated characters is
written as outlines, which keep the rotation, and is no longer editable
text". The outlines keep each character's rotation, baseline shift and
scale. Type without rotated characters, and type turned a whole number of
times, is still written as text.

Docs: docs/file-format-parity.md, docs/mcp.md, docs/target-app-parity.md.
2026-11-01 16:35:49 -08:00
dotCooCoo 589c56da22 PDF export: a white stop linked to a process swatch keeps a spot gradient on its ink
A gradient of one spot ink's tints and paper white is written as a
Separation shading in that ink, with each white stop at 0% of it. A
white stop linked to a swatch that is not a spot color (a global White
swatch, for example) did not count as paper white. In an RGB document
the spot ink was then dropped: a Classic gradient was written in the
stops' own colors, and a Perceptual one was exported as an image in
process colors.

Such a stop now counts as paper white in the Separation and DeviceN
paths and in the checks that pick them, as an unlinked white stop does.
A stop linked to a white spot color is still a stop of that ink.
2026-11-01 16:28:40 -08:00
dotCooCoo 2adf5a9a61 Symbols: Replace Symbol clears the old symbol's dynamic overrides
An override on a dynamic symbol instance names an object of its symbol's
art by path. Replace Symbol kept every override whose path the new
symbol's art also had, so a change made to one object of the old symbol
was applied to an unrelated object of the new one. An instance given a
different symbol now drops its overrides; an instance replaced with its
own symbol keeps them. Undo brings them back.
2026-11-01 16:24:33 -08:00
dotCooCoo e441d3ba18 Affinity: pressure curves keep dips between samples and stay within a document-wide limit
- Each segment of a pressure curve is also sampled where its width stops
  rising or falling, so a dip between the regular samples of a long
  curve is kept. The segment's values are scaled into -1..1 before that
  is solved, so huge values in a file can't overflow it.
- A curve counts as full width only within 1e-9 of it, so a curve just
  under full width keeps its profile.
- The pressure points of all strokes in one read are limited to about a
  million, as for characters and table cells. Curves at full width count
  too, and once the limit is reached no more curves are read; the
  strokes past it are imported as plain strokes with a warning.
2026-11-01 16:21:57 -08:00
dotCooCoo b473716e3e Fix #2089: Affinity strokes keep their Width tool profile
An Affinity stroke shaped with the Width tool stores its pressure curve
(LDeP: position along the path against width). The importer warned and
drew the stroke at full width all along.

- The pressure curve is read and sampled into the stroke's width
  profile (2 to 8 points per segment, 256 segments at most, widths up
  to the full weight). A curve at full width all along stays a plain
  stroke; one that can't be read is warned about.
- Brush strokes keep their own warning.
- The pinned Affinity 3 signatures for the five corpus files with such
  strokes are updated.
2026-11-01 14:17:23 -08:00
Brandon Thomas 639c9d6ce9 Merge pull request #5113 from dotCooCoo/feat/recolor-color-sliders
Recolor Artwork: color sliders in a chosen color mode edit the selected row's new color
2026-11-01 15:28:03 -05:00
Brandon Thomas fceaf44fd0 Merge pull request #5138 from dotCooCoo/fix/vars-p6
Variables: a text value on threaded type replaces the whole story
2026-11-01 15:27:58 -05:00
Brandon Thomas 33010204cb Merge pull request #5137 from dotCooCoo/fix/vars-p16
Variables: Lock Variables' description names the refused import
2026-11-01 15:27:53 -05:00
Brandon Thomas 2aa24c1f3a Merge pull request #5136 from dotCooCoo/fix/vars-p5
Variables: a graph variable's data set applies its CSV as graph.setData does
2026-11-01 15:27:48 -05:00
Brandon Thomas 7e5eebc3ed Merge pull request #5135 from dotCooCoo/fix/vars-p15
Variables: Rename reports a missing variable or data set before a taken name
2026-11-01 15:27:45 -05:00
Brandon Thomas 0271e034ce Merge pull request #5123 from dotCooCoo/fix/ai-os22-g6
.ai import: version 7 palettes keep their swatches without names
2026-11-01 15:27:41 -05:00
dotCooCoo 39e69e8446 Tests: move the lock params text test next to the lock test 2026-11-01 11:48:41 -08:00
dotCooCoo b104efc53c Graph data: a dropped empty row adds no blank series
Dropping rows with neither a label nor a value read the data padded to
the widest row, so a dropped row wider than the rest (`,,`) left every
kept row one blank cell wider. The graph then had an extra blank series,
and a right-axis assignment to that index was kept. Each kept row now
keeps its own width. This applies to graph.setData and to a graph
variable's data set.
2026-11-01 10:19:45 -08:00
dotCooCoo 637229931f Paste: Paste in Back keeps a clip group's clipping path at the bottom
With the clipping path of a clip group or a clipping layer selected, or a
member of a compound path that is the clipping path, Paste in Back put the
clipboard's objects below it. The bottom pasted object then became the
clipping path. With nothing selected in a clipping layer, Paste in Back did
the same at the bottom of the layer.

Paste in Back now puts the objects right above the clipping path, as Send to
Back keeps art above it.
2026-11-01 10:18:06 -08:00
dotCooCoo be20929877 Variables: a text value on threaded type replaces the whole story
Applying a data set wrote a text value into the bound frame only, and the
thread's reflow then joined it with the other frames' old text. The art no
longer matched the row it had just applied, a captured row applied back lost
text, and a value bound to a later frame deleted the rest of the story.

- A text value bound to any frame of threaded text replaces the whole story,
  which then flows through the frames again. `overflow` names the story's
  last frame.
- Capture Data Set, Update Data Set and the Variables panel's match check
  read such a frame as the whole story, frames joined in thread order.
- When any frame of the story is locked, the text value is skipped and
  counted in `skipped`, and the story is not read.
- Two text variables bound in one story apply in definition order, as for
  two on one object: the one defined last wins.

Docs: docs/mcp.md.
2026-11-01 08:58:35 -08:00
dotCooCoo e49e102032 Variables: Lock Variables' description names the refused import
variable.lock's params text and the doc comment on Variables::locked said every
dataset command still runs while the variables are locked. dataset.import is
refused when its header would define a variable; both texts now say so, as
docs/mcp.md and dataset.import's params already do.
2026-11-01 08:43:56 -08:00