Commit Graph
7607 Commits
Author SHA1 Message Date
Brandon Thomas 70e32e449b Merge pull request #5289 from storytold/fix/sec-dxf-export-budgets
DXF export: bound curve flattening work and expanded symbol art
2026-11-03 00:19:04 -05:00
Brandon Thomas b1faef0081 Merge pull request #5271 from dotCooCoo/fix/projective-scale-invariant-checks
Geometry: a projective map's horizon and invertibility checks do not depend on its scale
2026-11-03 00:19:01 -05:00
Brandon Thomas 6d10ae13a3 Merge pull request #5242 from rahul05ranjan/i18n-sv-complete
i18n(sv): translate the 22 strings the report listed as missing
2026-11-03 00:18:57 -05:00
Brandon Thomas e3a1d0999f Merge pull request #5240 from rahul05ranjan/i18n-ru-complete
i18n(ru): translate the 130 strings the report listed as missing
2026-11-03 00:18:54 -05:00
Brandon Thomas c6131cf798 Merge pull request #5243 from dotCooCoo/fix/svg-round-trip-edge-ellipse
SVG: an exported ellipse imports as the four-anchor ellipse it was written from
2026-11-03 00:18:50 -05:00
Brandon Thomas 57e0e55bba Merge pull request #5244 from dotCooCoo/feat/select-same-text-fill-stroke
Select: Same › Text Fill & Stroke Color selects type whose fill and stroke both match
2026-11-03 00:18:46 -05:00
Brandon Thomas a47ebfc3ce Merge pull request #5245 from dotCooCoo/fix/inline-graphic-manual-kerning
Type: an inline graphic takes its character's manual kerning
2026-11-03 00:18:42 -05:00
Brandon Thomas 0116c2522e Merge pull request #5250 from dotCooCoo/fix/pdf-spot-reserved-names
PDF export: spot inks named All or None print as spot inks
2026-11-03 00:18:39 -05:00
Brandon Thomas a824ea9ea1 Merge pull request #5260 from dotCooCoo/fix/document-info-counts-each-object-once
Document Info: Objects counts each object once
2026-11-03 00:18:35 -05:00
Brandon Thomas cc1d906c4c Merge pull request #5259 from dotCooCoo/fix/tcy-style-offsets-in-range
Type: Tate-chu-yoko offsets in character style definitions stay in range
2026-11-03 00:18:32 -05:00
Brandon Thomas e62d40e500 Merge pull request #5269 from dotCooCoo/fix/file-format-doc-one-editing-paragraph
Docs: file-format-parity keeps one .ai editing-data paragraph
2026-11-03 00:18:28 -05:00
Brandon Thomas ef37b37213 Merge pull request #5314 from JonRC/fix/eps-gcheck-simple-objects
Fix #5312: EPS files written by Ghostscript's eps2write open
2026-11-03 00:18:24 -05:00
Brandon Thomas 1b503c618b Merge pull request #5279 from Silvertree2010/fix/h5763-svg-export-writes-invalid-xml-wh
Drop invalid XML characters from SVG link URLs and aria-labels
2026-11-03 00:18:21 -05:00
Rahulandrahul05ranjan b191900855 i18n(sv): drop the rows MENU_KEEP_AS_IS forbids (gate fix)
The gate failed kept_menu_names_are_menu_labels_left_in_english: sv is in
KEEPS_MENU_NAMES, so the MENU_KEEP_AS_IS names (VectorCraft, OpenType, the
workspace names, the perspective-grid presets) must stay in English. The
coverage report had listed them as missing; the 20 rows are removed so the
names fall back to English again. The two genuinely-missing strings
(Font Size:, digits Hindi) stay.

Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
2026-11-03 10:18:58 +05:30
Rahulandrahul05ranjan 32d59f7239 i18n(ru): drop the rows MENU_KEEP_AS_IS forbids (gate fix)
The gate failed kept_menu_names_are_menu_labels_left_in_english: ru is in
KEEPS_MENU_NAMES, so the MENU_KEEP_AS_IS names (VectorCraft, OpenType, the
workspace names, the perspective-grid presets) must stay in English. The
coverage report had listed them as missing; the 20 rows are removed so the
names fall back to English again. The 110 genuinely-missing strings stay.

Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
2026-11-03 10:18:51 +05:30
Dylan M. Taylor 68602ac344 Merge pull request #3990 from dotCooCoo/feat/native-save-precision
Native format: document.save can write path coordinates at a fixed precision
2026-11-02 23:29:28 -05:00
JonRC 5e3d35c0e0 contributors: add my name for the About window 2026-11-03 01:07:42 -03:00
JonRC e44f5a1b77 Fix #5312: EPS files written by Ghostscript's eps2write open
`gcheck` and `scheck` answered false for every operand. The PLRM (3rd
ed., `gcheck`) says they are true for a simple object, and false only
for a composite one in local VM. The eps2write prolog copies its
initial graphics state into global VM and leaves a value alone only
when `gcheck` is true. So the integers in the `currenthalftone`
dictionary took the copy path, left the stack one item off, and the
next `put` failed with a typecheck in `cp2g`.

Simple objects now answer true. Composites still answer false, since
every composite here is in local VM.
2026-11-03 01:07:29 -03:00
Dylan M. Taylor ed4a663986 DXF export: charge symbol art as it is placed unclipped, test the segment budget
Count nine-slice symbol art with clips off, as place_symbol_art_unclipped
places it, so art crossing the guides is not over-charged; add a test that
the flattening segment budget stops a path independently of the point
budget; format.
2026-11-02 22:33:39 -05:00
Dylan M. Taylor aaca80c961 Affinity import: bound archive index names and charge failed entry extraction (#5291)
The table chain's records and entry names now have a whole-archive allowance, only the
first name of an id is decoded and kept (no clone into the entry map), and an entry's
size is charged before it is decompressed and stays charged when it fails, with the
failure remembered so a broken entry is not decompressed again.
2026-11-02 21:36:16 -05:00
Dylan M. Taylor f1d4969864 DXF export: bound curve flattening work and expanded symbol art
Flattening a path for DXF capped the points it kept but kept flattening every remaining curve
after the cap, each curve at a fixed tolerance however large it was. Symbol instances placed
inside symbols were cloned and walked once per instance with only the nesting depth bounded, so
a small symbol graph of empty symbols could fan out into billions of visits that write nothing.

- Flatten through vectorcraft_geom::flatten (a bounded number of segments per curve) and stop
  at the first curve past the point or segment budget.
- Charge each symbol instance's art to MAX_SYMBOL_NODES with charge_symbol_art before placing
  it, as the document does elsewhere; once spent, no further instances are expanded.
2026-11-02 20:43:17 -05:00
dotCooCoo eaab849f42 Document Info: count objects that share an id as separate objects
The walk that skips objects it has already visited keys them by address
instead of id, so two objects that share an id in a damaged file are
both counted. An object reached through a selected group and again on
its own still counts once.
2026-11-02 17:01:29 -08:00
dotCooCoo 33f429972b Merge main (11862f61) into feat/native-save-precision 2026-11-02 16:29:45 -08:00
Brandon ThomasandClaude Opus 5.5 11862f6119 Release 0.21.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.21.0
2026-11-02 19:27:35 -05:00
NetterSilverandClaude Haiku 5.5 6ecf286f6b Drop invalid XML characters from SVG link URLs and aria-labels
Found while testing crates/svg/src/export/nodes.rs:492 (Writer::node, `xlink:href` built with xml_escape)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 21:42:27 +00:00
dotCooCoo 2d0fa32d30 Docs: the projection checks compare absolute values
The horizon test compares a point's signed w with the largest absolute
value in the bottom row, and the invertibility test compares the
absolute value of the determinant with the largest absolute value among
its six products, so a negative determinant counts as invertible. A
stored projection that overflows is divided by the largest absolute
value in its bottom row.
2026-11-02 08:53:47 -08:00
dotCooCoo 5f20bbc3e8 Document Info: test that a Repeat object in a selected group is listed once
With Selection Only, a selection that holds a group and the Repeat object
inside it, in either order, lists that Repeat object once.
2026-11-02 08:25:02 -08:00
dotCooCoo b5885e35fa Geometry: a projective map at a small scale maps a far point without overflowing
The power of two that brings a small map's entries up before a point is mapped is now reduced by the power of two of the point's larger coordinate, so no product of an entry and a coordinate reaches 1. A map that mapped a point before the scaling step does so again, and affine_at uses the same entries.
2026-11-02 08:24:29 -08:00
dotCooCoo 0f48ba21e9 Geometry: a projective map at a tiny scale maps points and derivatives as at unit scale
apply and affine_at multiply every entry by the power of two that brings the
largest to at least 1/2 when all entries are below 1/2, so products that would
be subnormal keep their precision. The identity times 5e-324 mapped (0.5, 0.5)
to (0, 0) before. Maps with an entry of 1/2 or more are unchanged.
2026-11-02 08:15:21 -08:00
dotCooCoo ce9002008b Docs: file-format-parity keeps one .ai editing-data paragraph
The editing-data paragraph appeared twelve times, each copy holding one
feature's change. The copies are merged into one paragraph.
2026-11-02 08:10:43 -08:00
dotCooCoo 4494d40c33 Geometry: the inverse of an invertible projective map is kept however small its relative determinant
adjugate_inverse no longer applies the relative determinant test to its result. That test is not symmetric between a map and its inverse: a map whose determinant is 1e-8 of its largest product can have a finite, accurate inverse whose own ratio is near 1e-16, so inverse() was None while is_invertible() was true. The result is now kept when every entry is finite and its determinant, summed from the six products in split form, is not 0.
2026-11-02 07:55:19 -08:00
dotCooCoo d1fb8e540c Geometry: a projective map with entries far apart in size keeps its inverse
The inverse holds every cofactor and the determinant as a mantissa and a
power of two, and forms each entry from their quotient. A map such as
diag(1e-300, 1e300, 1e-300), which counts as invertible, now gets its
inverse diag(1e300, 1e-300, 1e300) instead of none.
2026-11-02 07:45:10 -08:00
dotCooCoo b22912b25a Geometry: the inverse of a projective map with a tiny determinant keeps its w row
When a cofactor of a map with a normal but very small determinant underflows to 0, the
inverse formed from the map itself can have a w row of 0, so every point mapped through it
is refused. The inverse is now kept only when it is itself invertible, and is otherwise
formed from the map divided by a power of two that brings its determinant's products near 1.
2026-11-02 07:37:37 -08:00
dotCooCoo bffb4e6e87 Geometry: a projective map with entries of very different sizes stays invertible
The scale-free determinant test no longer builds a rescaled copy of the map. It
forms each of the six determinant products as a mantissa product and an
exponent sum and compares their sum with the largest of them, so a map such as
diag(1e307, 1e-157, 1e-157), whose rescaled copy overflows, is still invertible
and loads from a document. The rescaled copy is built only to invert a map whose
determinant is not finite or is below the normal range.
2026-11-02 07:21:32 -08:00
dotCooCoo e365c09141 Geometry: a projective map's horizon and invertibility checks do not depend on its scale
A homography means the same map at any nonzero uniform scale of its
entries, but `apply`, `is_invertible` and `inverse` compared w and the
determinant with absolute thresholds. A map stored with small entries
refused every point and could not be inverted, and a map with large
entries drew points past its horizon.

- `apply` refuses a point when w is at most 1e-9 times the largest
  magnitude in the w row.
- A map counts as invertible when its determinant is more than 1e-15
  times the largest of the six products the determinant sums, taken on
  a copy rescaled by a power of two. An inverse whose determinant
  overflows or underflows is taken on that copy.
2026-11-02 07:10:04 -08:00
dotCooCoo 588ff039a9 Type: Tate-chu-yoko offsets in character style definitions stay in range
A character style whose Up/Down or Left/Right offset lies past MAX_COORD
either way now opens with the nearer end, as text runs already do, so
text that takes the style no longer gets the out-of-range offset. An
offset within range is not rewritten.
2026-11-02 06:32:52 -08:00
dotCooCoo 8b249f8275 Document Info: Objects counts each object once
An object with a gradient or a pattern on both its fill and its stroke
counted twice in Objects. It now counts once for each paint kind it uses.

With Selection Only, a selection that holds a group and an object inside
it walked that object twice, so the object counts, the paths' total
length and Repeat Objects counted it twice. The walk now visits each
object once.
2026-11-02 05:06:38 -08:00
dotCooCoo 52d180910d PDF export: spot inks named All or None print as spot inks
In a Separation color space, the colorant name All marks every plate and
None marks no plate (ISO 32000-1:2008, 8.6.6.4). A spot swatch with one of
those names was written under its own name, so its paint printed on every
plate or on none.

PDF export now writes such an ink as All Spot or None Spot, numbered
All Spot 2, All Spot 3 and on when a spot swatch already has that name,
the same rule EPS export uses. The new name is used for fills and strokes,
tint shadings, DeviceN gradients, colorized images and the Lab alternates.
Registration still writes All.
2026-11-02 05:04:46 -08:00
dotCooCoo 5ba035aba4 Type: an inline graphic takes its character's manual kerning
A manual kerning value on an inline graphic's character was stored and
shown but did not move the text after it. The graphic's advance now
includes the value, as a letter's does, and in right-to-left horizontal
type its art moves right by the value, so the space goes between it and
the next character in reading order. Auto and Optical kerning still apply
no font pairs to an inline graphic.
2026-11-02 03:45:45 -08:00
dotCooCoo fa8addaa12 Select: Same › Text Fill & Stroke Color selects type whose fill and stroke both match
Select › Same › Text Fill & Stroke Color (select.same.textFillAndStrokeColor) selects the type
objects with a run of characters whose fill and stroke both match the first character of the
first selected type. Type that matches only the fill or only the stroke, or has the fill in one
run and the stroke in another, is left out. The entry follows Text Stroke Color in the menu and
has a row in every catalog.
2026-11-02 03:37:17 -08:00
Rahul Ranjanandrahul05ranjan 3976989cb7 i18n(sv): translate the 22 strings the report listed as missing
The Swedish catalog lacked 22 interface strings: the workspace names
(Essentials, Painting, Typography, Web, Touch), the 1P/2P/3P view
preset names and the Hindi digit-script name.

Adds them with the vocabulary the catalog already uses (Utskrift,
Teckenstorlek). After this the report lists nothing missing for sv.

Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
2026-11-02 11:27:09 +00:00
dotCooCoo 442ae20e14 SVG: made-up ids skip every id the file names
An id the importer writes into an element no longer matches a style sheet's
#id selector or a <use> link that no element matched before. The ids are
read from the decoded attribute values and text of the file, once.
2026-11-02 03:22:10 -08:00
Rahul Ranjanandrahul05ranjan 2a51404e74 i18n(ru): translate the 130 strings the report listed as missing
The Russian catalog covered 6,126 of the 6,256 interface strings: the
workspace names (Essentials, Painting, Typography…), their Reset
entries, the artboard and dimension annotations, the PDF and export
preset names, the library panel sentences and several control-channel
messages still fell back to English.

Adds the 130 missing entries in the catalog's TSV format with the
Illustrator-style vocabulary the catalog already uses (монтажная
область, выносные линии, плашечные цвета, трассировка). After this the
report lists nothing missing for ru.

Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
2026-11-02 11:17:49 +00:00
dotCooCoo 7c7a127ec8 SVG: a thin ellipse far from the origin imports with standard handles
An ellipse whose radius is smaller than the point tolerance at its coordinates, but at least two
f32 steps, now imports as the four-anchor ellipse instead of usvg's curves with rounded handles.
2026-11-02 03:09:08 -08:00
dotCooCoo b4d3ae41bf SVG: made-up ids skip the file's taken ids once
Each id made up on import starts where the last one stopped instead of at
the number of ids made so far, so a file that already holds many of the
made-up ids does not make every new id try them all again. The ids made
up are the same.
2026-11-02 03:04:04 -08:00
dotCooCoo 85b6c2a5eb SVG: ids made up on import never repeat an id in the file
The import gives an element an id when it needs to find it again in
usvg's tree (links, symbol uses, events, data attributes, and now
circles and ellipses). A made-up id could repeat one the file already
had, such as vectorcraft-id-0, so a <use> or paint reference to that
element showed the other one. Made-up ids now skip every id in the file.

Circles and ellipses in a file whose style sheet has an [id selector get
no made-up id and keep usvg's curves, so the selector matches the same
elements as before.
2026-11-02 02:59:51 -08:00
dotCooCoo 820a750310 SVG: a small circle far from the origin imports as a four-anchor ellipse
The tolerance that compares the points usvg makes of a <circle> or
<ellipse> grew with the center's distance from the origin, so a circle
of radius 400 at x 40,000,000 fell back to eight anchors. The tolerance
is now a few f32 steps at the size of the coordinates, and the on-ellipse
check allows the same steps relative to the smaller radius.
2026-11-02 02:53:34 -08:00
dotCooCoo 60e1600caa SVG: circles and ellipses import as four-anchor ellipses
usvg turns each <circle> and <ellipse> into arcs and then into a number
of cubics that depends on f32 rounding: an ellipse that export wrote as
<ellipse> could come back with eight anchors instead of the four it was
written from. The renderer flattens the two outlines differently, so a
round trip of a flat filled ellipse redrew 158 edge pixels by up to 43
levels, and prop_svg::plain_art_survives_visually failed on main.

Import now records the ids of <circle> and <ellipse> elements in the
prepass and rebuilds each as the ellipse VectorCraft draws: four smooth
anchors on the axes, starting at the right as SVG defines, with handles
KAPPA of the radii long, then transformed as before.

Tests: prop_svg::filled_ellipse_on_the_artboard_edge_survives_visually
(the failing case) and basic_shapes::circles_and_ellipses_import_as_the_ellipse_drawn
fail without the change.
2026-11-02 02:49:36 -08:00
BFlat 4ee0ef231a Merge pull request #5139 from dotCooCoo/fix/ai-os10-distort
.ai import: type keeps its Distort & Transform effects live
2026-11-02 16:54:41 +07:00
BFlat 04f5e6455d Merge pull request #5156 from dotCooCoo/fix/ai-os18-d
.ai import: open the variable library and data sets from the editing data
2026-11-02 16:54:26 +07:00