Commit Graph

2504 Commits

Author SHA1 Message Date
Gert Pellin 7b0164e4e7 ICC profiles written here have their tags 4-byte aligned
moxcms's encode packs tagged elements back to back, so the built-in
sRGB profile embedded in JPEG, TIFF and PSD exports was 634 bytes with
`cprt` at offset 558, where ICC.1 §7.1.2 requires every tagged element
on a 4-byte boundary (stricter readers reject the files). encode now
lays the tag data out again with each element padded, updating the tag
offsets, the size and the profile ID (zeroed, "not computed"); tags
that share data keep sharing it. Reported to moxcms as
awxkee/moxcms#198.

Fixes #1584
2026-10-13 10:52:38 +07:00
bflatastic 670946ef75 Palette index: rustfmt 2026-10-13 10:52:38 +07:00
Ayanprogrammer11 cca3a63e4a Palette nearest-colour lookup: branch-and-bound index over the linear scan
Indexed export (GIF, PNG-8, BMP, TIFF) finds each pixel's nearest
palette entry with a linear scan, and the k-means refinement in median
cut does the same once per cell per pass. With the 256-entry palettes
this code produces, that is 256 distance evaluations per query.

PaletteIndex is an exact nearest-neighbour kd-tree over the palette,
balanced at the widest weighted axis, with tight channel bounds in every
subtree so branch-and-bound can skip whole boxes. Small palettes (12 or
fewer entries) keep the scan, which is cheaper than tree traversal.

Results match the linear scan exactly, including ties: an equal-distance
entry with a smaller index still wins. The bound uses the same weighted
squared f32 form as dist() rather than a transformed space, so rounding
cannot change which entry is chosen.

Three tests: randomized parity against the original scan across palette
sizes spanning the cutoff, three weight vectors, duplicate colours and
fractional query points; explicit tie-breaking cases; and an opt-in
benchmark comparing 100k queries at both, behind -- --ignored.
2026-10-13 10:52:38 +07:00
bflatastic bdb9963c73 Select Behind: one test of what may be hit for both walks, and random documents checked against the repeated hit test 2026-10-13 10:52:38 +07:00
Ayanprogrammer11 507c6e0e63 Select Behind: test each object once instead of re-hitting the whole document
objects_at() found Select Behind targets by running hit_test_skipping()
in a loop, skipping everything found so far. Each round is a full tree
traversal, so a stack of n overlapping paths costs O(n^2) hit tests
before the loop finds no further object.

The fast path walks each editable top-level layer once. Every selectable
object's subtree is visited only until its first hit, and a HashSet
records found ids so subtrees already covered by an earlier hit are not
searched again. Sublayers recurse the same way, so a stack of n paths
costs one traversal total.

The original traversal stays in charge where Select Behind semantics
differ from the plain front-to-back order: isolation (scope), and Edit
Contents, which exposes objects nested inside a top-level group. Non-
layer roots in malformed files also fall back. objects_at_repeated()
keeps the old loop for those cases.

Three tests: a 256-path stack asserts front-to-back order and
completeness; a mixed document (sublayer, groups, clipping group,
outline and path-only options) asserts the one-pass result equals the
repeated-traversal result; an envelope in Edit Contents asserts the
fallback preserves the original order.
2026-10-13 10:52:38 +07:00
bflatastic dc64aaa41b Commands: ids stand in for the selection where the params description names the key, checked over every command 2026-10-13 10:52:38 +07:00
Gert Pellin dfc1b846cc Commands: objects given as ids stand in for the selection
Commands whose description takes `ids` (operate on these objects;
default: the selection) failed with "nothing selected" when objects
were given but none was selected: execute checked their `enabled`
predicate against the selection, with an exception for edit.clear only.
Of the 220 commands mentioning ids, 48 were refused so; their run
already used the ids. execute now evaluates `enabled` with the given
ids selected and puts the selection back, so each predicate keeps its
meaning (Make with Top Object still needs two objects).

Fixes #1575
2026-10-13 10:52:38 +07:00
Gert Pellin a169693817 Warp › Arc: bend in the object's real proportions
warp_point worked on box coordinates normalised to -1..1 on both axes:
Arc's radius was in x units but its edges were offset in y units, so a
box that isn't square bent into no circular arc (skewed sides from
Bend 10 %), and at 100 % the radius (2/π) was smaller than the
half-height, folding the inner edge over. warp_point now takes the
box's aspect along the warp; Arc keeps the centre line's length on a
circular arc in real proportions, with concentric edges, and an inner
edge that would pass the centre stops there. Other styles are as they
were; a square is bent as before.

Fixes #1578
2026-10-13 10:52:37 +07:00
bflatastic db85d465c8 SVG import: flowed text with a region too large to place is left out with a warning 2026-10-13 10:52:37 +07:00
Gert Pellin e794c1ac63 SVG import: Inkscape flowed text (flowRoot) becomes area type
<flowRoot> (SVG 1.2 flowed text, which Inkscape writes for text typed
into a frame) was dropped without a word: usvg doesn't draw it. The
text pass now reads it as area type: the first <rect> of its
<flowRegion> is the frame (with the rect's transform), each <flowPara>
a paragraph (white space collapsed), styled through the same cascade
as <text>. Another kind of region leaves the text out with a warning.

Fixes #1568
2026-10-13 10:52:37 +07:00
Gert Pellin de8d5c96ab Pathfinder: Trim, Merge and Crop remove strokes
The public Pathfinder documentation says Trim and Crop remove any
strokes, and Merge removes them and merges same-coloured fills. The
pieces kept every stroke, and Merge, grouping by the whole style, left
same-filled objects with different strokes apart. The three now drop
stroke layers before styles are compared.

Fixes #1570
2026-10-13 10:52:37 +07:00
bflatastic d44615753a Docs: ROADMAP 2026-10-13 adds #1492–#1583; Average and Join corrected to missing, Polish row count 2026-10-13 10:52:37 +07:00
BFlat 0573d3cdfb
Merge pull request #1596 from storytold/fix/1561-symbol-percentages
SVG import: percentage lengths in a symbol with a viewBox are of the viewBox
2026-10-13 10:26:19 +07:00
BFlat f0f3cb4eb1
Merge pull request #1595 from storytold/u/trace-strokes
Image Trace: stroked centre lines lie in the middle of their ink and are as wide as it
2026-10-13 10:14:01 +07:00
bflatastic 5fe916a425 SVG import: percentage lengths in a symbol with a viewBox are of the viewBox (#1561) 2026-10-13 09:58:14 +07:00
bflatastic 8e7b9ba436 Image Trace: stroked centre lines lie in the middle of their ink and are as wide as it
Create: Strokes (the Outline preset, or the option) traced lines along the pixel skeleton: right to a pixel,
with a width counted in pixels, and fitted by a separate, older fitter.

- Each skeleton point is moved to the middle of the line's ink, read from the image across the line: every
  pixel counts as much ink as its colour is the line's rather than the paper's, the ink's centroid is the
  middle and its sum the width (an anti-aliased or blurred line says both to a fraction of a pixel).
- The stroke width is the median of those widths, no longer a whole number of pixels.
- Each end goes where the ink ends, less the half width a round cap adds.
- Centre lines are fitted as outlines are: a straight line is two anchors, corners stay corners, Snap Curves To
  Lines levels them. The old Douglas-Peucker and simplify fitter, and the crate's pathops dependency, go.

Outline on the thin-line inputs, all sizes and variants (docs/image-trace-quality.md), before -> after:
- painted outline 0.310 -> 0.055 px off on average; 81 -> 14 anchors for five lines
- centre line 0.295 -> 0.052 px from the true centre line; stroke width 0.13 -> 0.02 px off; 18.4 -> 2.7
  anchors a stroke (blurred, noisy and scanned lines: 2.0 each; aliased: 4.6)
- largest distance 1.84 -> 2.94 px: the square ends of a 10 px line under a round cap
Presets without Create: Strokes are unchanged.
2026-10-13 09:36:47 +07:00
BFlat cbcee3bd54
Merge pull request #1593 from storytold/u/trace-upright
Image Trace: Snap Curves To Lines levels lines a degree or two askew
2026-10-13 09:36:14 +07:00
BFlat 0810a15f6e
Merge pull request #1566 from marcoarnulfo/fix/new-symbol-options-dialog
Symbols panel: New Symbol asks for Symbol Options before making the symbol
2026-10-13 09:31:54 +07:00
BFlat db7e568809
Merge pull request #1567 from excited-raccoon/css-class-dedup-hashmap
Replace linear scan in CSS class dedup with a HashMap lookup
2026-10-13 09:31:36 +07:00
BFlat 9d72686d95
Merge pull request #1565 from switch87/fix/1557-css-transform-font
SVG import: CSS transforms in CSS syntax move objects; the font shorthand styles type
2026-10-13 09:31:20 +07:00
BFlat d79fb145c2
Merge pull request #1558 from marcoarnulfo/fix/zigzag-regular-waveform
Zig Zag: ridges are spaced evenly along each segment's length, so a straight line gets a regular wave
2026-10-13 09:31:04 +07:00
BFlat 6a16627de6
Merge pull request #1583 from storytold/fix/1534-guides
Fix #1534: guide paths take the guide colour and style, and a dragged selection snaps to ruler guides with Smart Guides on
2026-10-13 09:09:09 +07:00
BFlat acc1c9a85d
Merge pull request #1582 from storytold/fix/1533-join-coincident
Fix #1533: Join merges the selected anchors that lie on one another (after Average) into one
2026-10-13 09:08:58 +07:00
BFlat 8fbf724cf6
Merge pull request #1580 from storytold/fix/1539-shortcut-menuless
Keyboard Shortcuts: the commands outside the menus that run without params are listed too (#1539)
2026-10-13 09:08:47 +07:00
BFlat 02ed637c68
Merge pull request #1544 from switch87/fix/1542-clip-covers-page
PDF/EPS import: a clip only counts as covering the page when it does
2026-10-13 09:08:21 +07:00
BFlat 9d2b074d12
Merge pull request #1543 from marcoarnulfo/fix/guide-objects-locked
Guides: Lock Guides locks the paths made into guides too
2026-10-13 09:08:05 +07:00
BFlat 632ea2a9f3
Merge pull request #1541 from switch87/fix/1536-effect-raster-paint-transform
Render: effect rasters drawn after a pattern fill keep their place and size
2026-10-13 09:07:48 +07:00
BFlat 523fd6e185
Merge pull request #1549 from marcoarnulfo/fix/duplicate-shortcut
Keyboard Shortcuts: Duplicate is listed, so it can be given a shortcut
2026-10-13 09:07:31 +07:00
bflatastic 612082e26e Image Trace: Snap Curves To Lines levels lines a degree or two askew
The documented option "replaces slightly curved lines with straight lines and snaps lines near 0 or 90 degrees
to absolute 0 or 90 degrees" (helpx, Image Trace panel options); ours did the first half only.

- With Snap Curves To Lines, a straight piece at least 2 px long and within 2.5 degrees of level or upright is
  made exactly so, by moving its ends across it to their mean (the page gives no angle: 2.5 degrees is our
  choice). The pieces next to a moved end follow it.
- A junction other regions share stays where it is, so abutting regions still have one curve along their edge;
  a line between a junction and a free end is levelled to the junction.
- The harness gains askew inputs (a rectangle, a plus sign and two lines, 1 to 2 degrees off level and upright)
  and an "upright %" column: the share of the outline length that is exactly level or upright.

On the clean askew inputs (docs/image-trace-quality.md): Black and White Logo and Technical Drawing, the presets
with the option on, go from 0% to 91% and 95% of their outline exactly level or upright. Their outlines are then
0.49 and 0.59 px from the art as drawn where they were 0.05 (4.2 px at the far end of a 300 px side turned 1.5
degrees): that distance is what the option is for. Presets without the option are unchanged.
2026-10-13 09:07:13 +07:00
bflatastic fea316911d Symbols panel: Alt-click on New Symbol skips Symbol Options 2026-10-13 08:53:06 +07:00
Ayanprogrammer11 3af08505f8 Replace linear scan in CSS class dedup with a HashMap lookup
Export::class() deduplicated declaration blocks with
classes.iter().position(), an O(n) scan per style, making exports with
many distinct styles (e.g. large palettes) quadratic overall. Track the
1-based class number per declaration in a HashMap alongside the
first-use-ordered Vec, so each lookup is O(1) while output stays
byte-identical.

Also adds a regression test covering 256 distinct declarations with
recurrence of early styles, for both InternalCss and StyleEntities
styling.
2026-10-13 08:53:06 +07:00
Gert Pellin 2c01a42b35 SVG import: CSS transforms in CSS syntax move objects; the font shorthand styles type
usvg takes a `transform` CSS property as a presentation attribute that
overrides the element's own, but reads its value as SVG: CSS units
(`translate(100px, 0)`, `rotate(45deg)`) don't parse, and the object
was left with no transform at all. Before usvg runs, transform
declarations in `style` attributes and `<style>` rules are now written
in SVG syntax (lengths in px, angles in deg/rad/grad/turn, translateX/Y,
scaleX/Y and skew as SVG's functions); values that can't be are left.

The text pass's own cascade now expands the `font` shorthand into its
longhands, as usvg does for the rest of the drawing, so text styled
with `font: italic bold 30px …` keeps its size, weight and style.

Refs #1557
2026-10-13 08:53:06 +07:00
bflatastic 45334df881 Zig Zag: tests of the ridge spacing on a circle and on an uneven curve 2026-10-13 08:53:06 +07:00
Marco Arnulfo ee97225c19 Symbols panel: New Symbol asks for Symbol Options before making the symbol (fixes #1560) 2026-10-13 08:53:06 +07:00
Marco Arnulfo 6085197bf4 Zig Zag: ridges are spaced evenly along each segment's length, so a straight line gets a regular wave (fixes #1554) 2026-10-13 08:53:05 +07:00
bflatastic 50911d8608 Fix #1534: a dragged selection snaps to ruler guides with Smart Guides on too 2026-10-13 08:51:43 +07:00
bflatastic 6939eff2f9 Fix #1534: paths made into guides take the guide colour and style of Preferences 2026-10-13 08:51:43 +07:00
Gert Pellin 9652d1d6b2 PDF/EPS import: a clip only counts as covering the page when it does
Both readers skipped a clip of at most six straight segments whose
bounding box contained the page. A diamond touching the page edges has
the page as its bounding box, so its clip was dropped and everything it
cuts away showed. geom::hit::polygon_covers_rect answers it properly
(one convex polygon containing the page's corners); any other path is
kept as a clip.

Fixes #1542
2026-10-13 08:51:40 +07:00
Marco Arnulfo 79b5d73ca7 Guides: Lock Guides locks the paths made into guides too (part of #1534) 2026-10-13 08:51:38 +07:00
Gert Pellin 05fdd15b45 Render: effect rasters drawn after a pattern fill keep their place and size
fx.rs draws blur, shadow and glow rasters as image paints that map 1:1
onto their rect, but only set the geometry transform: the paint
transform a pattern fill (or a radial or freeform gradient) left on the
context still applied, so in supersampled renders (raster exports) the
effect came out tiny and displaced. Both draws now reset the paint
transform first.

Fixes #1536
2026-10-13 08:51:37 +07:00
bflatastic 0a16b62a22 Fix #1533: Join merges the selected anchors that lie on one another (after Average) into one 2026-10-13 08:51:34 +07:00
bflatastic 20f5174710 Keyboard Shortcuts: the commands outside the menus that run without params are listed too (#1539) 2026-10-13 08:51:32 +07:00
Marco Arnulfo 00195e8d28 Keyboard Shortcuts: Duplicate is listed, so it can be given a shortcut (fixes #1539) 2026-10-13 08:51:30 +07:00
BFlat 05e321f4e0
Merge pull request #1574 from storytold/u/trace-options
Image Trace: Ignore Color, and the Auto Color and Outline presets
2026-10-13 08:47:04 +07:00
BFlat 85770d3afb
Merge pull request #1553 from switch87/fix/1552-type42-em
EPS/PS import: Type 42 fonts set type at their size
2026-10-13 08:27:05 +07:00
BFlat 421443b8ac
Merge pull request #1551 from switch87/fix/1550-pdf-alpha-softmask-images
PDF import: images in an alpha soft mask count by their alpha
2026-10-13 08:26:49 +07:00
Gert Pellin e2a63a8baf EPS/PS import: Type 42 fonts set type at their size
Interp::font took a font's FontMatrix times 1000 as the matrix of one
em, the Type 1 convention (glyphs in 1000-unit space). A Type 42 font's
glyph space is one unit an em, its FontMatrix usually the identity, so
type in Type 42 fonts (cairo, Inkscape, LibreOffice) came in 1000 times
too large and off the page. FontType 42 now takes the matrix as it is.

Fixes #1552
2026-10-13 08:11:27 +07:00
bflatastic 44d170f059 PDF import: pattern swatches read inside an alpha mask keep their images; a luminosity mask test 2026-10-13 08:11:27 +07:00
Gert Pellin fce90e3144 PDF import: images in an alpha soft mask count by their alpha
An alpha soft mask's vector art is whitened so its luminance is its
alpha, but images kept their colour: cairo (and so Inkscape) draws a
mask as an image whose alpha is in its own SMask, often black pixels,
so the mask came in empty. While reading an alpha mask's art the
importer now reads images white with their own alpha (under their own
cache key, without the JPEG and CMYK shortcuts), and stencil masks
white; a luminosity mask nested inside reads them as they are.

Fixes #1550
2026-10-13 08:11:27 +07:00
bflatastic 761cc2371a Image Trace: Ignore Color, and the Auto Color and Outline presets
Against the documented panel (https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/image-trace-panel-options.html):

- Ignore Color: "Ignores a specific color while tracing. Use the color picker to selectively ignore a color."
  The panel's Ignore White is now Ignore Color with a colour button beside it (white until another colour is
  picked); `imageTrace.make` takes `ignoreColor: "#rrggbb"` beside `ignoreWhite`, and leaves out the palette
  colours within about 30 levels of it. Documents saved before have no such setting and ignore white as they
  did. Flat Logo mode leaves out white only.
- Auto Color ("Creates a posterized image from a photo or artwork"): Color mode with the Automatic palette.
- Outline ("Simplifies the image to black outlines"): Black and White with Create: Strokes and no fills.
- The Outline preset's name is translated as a preset's name, not as the Outline view's.

Measured (docs/image-trace-quality.md): Auto Color on flat art is 0.140 px off on average with 4.0 paths (16
Colors: 0.141, 4.2). Outline on the line inputs, the first measure of Create: Strokes: 0.310 px off on average,
1.84 at worst, 81 anchors for 5 lines (Line Art's filled outlines: 0.055, 0.96, 46).
2026-10-13 08:04:32 +07:00