Commit Graph
368 Commits
Author SHA1 Message Date
BFlat ddfa381c91 Merge pull request #7273 from dotCooCoo/fix/knockout-groups-flat-masks
PDF/SVG export: knockout groups nest no deeper with more elements
2026-11-14 16:51:33 +07:00
BFlat e419f931ff Merge pull request #7496 from dotCooCoo/fix/svg-tspan-bidi
SVG import: a nested tspan keeps its direction and unicode-bidi
2026-11-14 16:49:38 +07:00
BFlat 0ca2952093 Merge pull request #7363 from dotCooCoo/fix/stained-instance-export
Symbols: a stained instance keeps its stain in SVG, PDF and the other exports
2026-11-14 13:42:39 +07:00
dotCooCoo a008a0d68e SVG import: a nested tspan keeps its direction and unicode-bidi
A <tspan>, <textPath> or <a> inside a <text> whose unicode-bidi is embed
now gives its characters its direction as their Character Direction. An
element with normal stays in its parent's embedding, unicode-bidi is not
inherited (inherit takes the parent's value), and the <text> keeps its
own paragraph direction. A run that SVG export writes with direction and
unicode-bidi="embed" now opens with its Character Direction again.

Character Direction holds one level of embedding and no override or
isolation, so bidi-override, isolate and isolate-override are imported
as embeddings in their direction, plaintext as an embedding in the
direction of its first strong character, and an embedding inside
another as an embedding of its own, each with an import warning.
unicode-bidi: plaintext on the <text> runs and anchors each paragraph
in the direction of its first strong character.

https://www.w3.org/TR/SVG11/text.html#RelationshipWithBi-directionality
https://www.w3.org/TR/css-writing-modes-3/#unicode-bidi
2026-11-13 22:21:42 -08:00
dotCooCoo bc6c48367e Merge main into fix/knockout-groups-flat-masks 2026-11-13 22:15:33 -08:00
Dylan M. Taylor 2ce569a6a7 Format svg adapter after craft-libs cutover 2026-11-14 05:14:16 +00:00
Dylan M. Taylor e927729f25 SVG: delegate entity expansion bounds, sniffing, and SVGZ to craft-svg 2026-11-14 01:40:55 +00:00
dotCooCoo de8ac51502 Symbols: a stained instance keeps its stain in SVG, PDF and the other exports
A symbol instance colored with the Symbol Stainer lost its stain in
export whenever the canvas's stained art was not used:

- An instance with geometry effects was reshaped for export from the
  symbol's art with only its dynamic changes, so SVG, PDF, EPS, .ai,
  DXF, EMF/WMF and HPGL wrote the unstained colors. A color adjustment
  on an instance recolored the unstained art the same way.
- PDF, EPS, legacy .ai, DXF and EMF/WMF wrote an instance without
  effects from the unstained art.

tint_node, stain and instance_art now live in
vectorcraft_doc::symbol_overrides, which both the effects and the
exporters can use; vectorcraft_brush re-exports them. Baking, color
adjustments and the writers above now use instance_art: dynamic changes
first, then the stain.
2026-11-13 17:24:06 -08:00
dotCooCoo 2ee29aefc6 Merge main into fix/knockout-groups-flat-masks 2026-11-13 16:59:50 -08:00
BFlat 00d2f6d24b Merge pull request #7283 from dotCooCoo/fix/nested-svg-image-count
SVG import: broken nested SVG images do not use up the image allowance
2026-11-14 05:39:43 +07:00
BFlat 4558196e16 Merge pull request #7053 from dotCooCoo/feat/warichu
Type: Warichu stacks selected characters on lines inside their line
2026-11-14 04:32:40 +07:00
dotCooCoo f27324ace5 Merge main into fix/knockout-groups-flat-masks 2026-11-13 13:01:18 -08:00
dotCooCoo 9d9e0c2f8e SVG import: broken nested SVG images do not use up the image allowance
An SVG shown as an image that could not be read (text that isn't XML,
an SVG without a size) still took one of the 256 nested SVG images an
SVG may show, so an image after 256 broken ones was left out. Only
images that decode now count toward that limit; a failed image gives
its place back.

Every nested SVG image read, decoded or not, counts toward new limits
of 1,024 reads and 512 MB of text per import, so a broken image shown
many times through <use> is still read a limited number of times. Past
them, nested SVG images are left out with a warning.

A nested SVG image with text that usvg then refuses no longer warns
that its text isn't imported.
2026-11-13 12:38:00 -08:00
BFlat 9107dad274 Merge pull request #7194 from dotCooCoo/fix/item-3829
Envelopes: a partly transparent image keeps its alpha where its warp pieces overlap
2026-11-14 03:23:28 +07:00
dotCooCoo 88b587baa6 PDF/SVG export: knockout groups nest no deeper with more elements
A knockout group was written as a chain of soft masks, one inside the
next for each element, so a group of about 200 elements made a PDF that
VectorCraft's own PDF import refused (form nesting past 50) and an SVG
that the web build refused as too deep.

- PDF writes a knockout group as one transparency group with /K true,
  with /I true when the group isolates, and PDF import reads it back as
  a knockout group. An element with several paints is drawn as one
  group inside it, so its own fills and strokes do not knock each other
  out.
- Where an element knocks out less than the area it paints (its opacity
  or mask defines its knockout shape, or it paints translucently), each
  element is drawn through one soft mask of the overlapping elements
  above it. Past 65,536 objects in a group's masks, or 2,048 elements,
  the group is a PDF knockout group, with a warning.
- SVG draws each element through one mask of the overlapping elements
  above it, built from one shared copy of each element's shape, so the
  elements stay siblings. Past 65,536 shapes in a group's masks, or
  2,048 elements, the group is written as a plain group, with a warning.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 12:21:39 -08:00
dotCooCoo fdedfb0e59 Merge main into feat/warichu 2026-11-13 11:59:22 -08:00
dotCooCoo 4d4d7ee1a7 Envelopes: opaque pixels of a warped image stay opaque where its pieces overlap
In SVG and PDF output, each piece of a warped image whose pieces can show
through was drawn through a mask of every later piece that reaches it. On a
later piece's antialiased edge the viewer multiplies the earlier piece's
coverage by the mask and draws the later piece over it, so an image with
both opaque and partly transparent pixels came out lighter at its opaque
pixels along the triangles' edges (alpha 184 of 255 in the new SVG test).

A later piece now goes into an earlier piece's mask only when a pixel of
either piece's image within 2 pixels of their overlap is less than opaque
(vectorcraft_effects::WarpSeams). Where two pieces overlap over opaque
pixels, the later one is drawn over the earlier one without a mask.
2026-11-13 10:36:16 -08:00
dotCooCoo 0663c76be1 Envelopes: a partly transparent image keeps its alpha where its warp pieces overlap
An envelope or the Warp effect cuts an image into triangle pieces that
reach a little past their edges, so neighbors overlap and no seams show.
On an image with alpha, the overlap drew that alpha twice: a 50% image
came out about 75% opaque along every cell edge and diagonal.

- The pieces now form a knockout group, and each piece is marked as a
  warp piece (`warp_piece`) whose knockout shape is its whole triangle,
  so where pieces overlap only the top one draws.
- The canvas knocks each piece out by its triangle at full coverage.
- SVG and PDF draw the pieces of a partly transparent image (its pixels,
  opacity, color mask or colorize paint) through one mask each, of the
  later triangles that reach it; pixels on a triangle's antialiased edge
  come out slightly lighter there. Pieces of an opaque image are drawn
  one over another as before.
- SVG writes one copy of the image for pieces cut by opacity masks
  (Preserve Shape Using Transparency) too.
- A cell count past the generated-object budget makes an empty group
  instead of overflowing.

Docs: docs/target-app-parity.md.
2026-11-13 08:38:48 -08:00
dotCooCoo 5f77b65aa2 Gradients: Hue Clockwise and Hue Counterclockwise mix stops around the hue wheel
The Gradient panel's Method menu gets two methods of VectorCraft's own
beside Classic and Perceptual. They mix a linear, radial, conic or
diamond gradient's colors around the hue wheel: hue, saturation and
brightness each change evenly from one stop to the next, and the hue
angle keeps one direction, as the fixed-direction rules of
https://www.w3.org/TR/css-color-4/#hue-interpolation do. Hue Clockwise
lowers the angle (red, magenta, blue), which is clockwise on the color
wheels, and Hue Counterclockwise raises it (red, yellow, green).

Two stops whose hues are less than half a color level apart count as
one hue and keep it, so a color and a tint or shade of it do not turn
the whole wheel. A stop whose RGB numbers are less than half a color
level apart is gray and takes the hue of the stop it mixes with, and
black takes that stop's saturation too.

- `paint.editGradient` and `paint.setFill` take `method: hue-clockwise`
  or `hue-counterclockwise`, in any case, with a space or an underscore
  for the hyphen or without it; other spellings are an error. The native
  file writes `HueClockwise` and `HueCounterclockwise`.
- The canvas and raster exports draw the gradient through stops on its
  curve, within half a color level.
- Reverse turns the method the other way with the stops, and a Reflect
  spread is repeated from the curve's stops, so both keep the colors.
- SVG and PDF write the gradient as an RGB image, CMYK and spot color
  stops included, with the spot warning. EPS writes a shading in process
  colors with a warning for spot stops. A legacy .ai file and the .ai
  editing data get the colors between the stops as stops of their own,
  in process colors, at most 256, with a warning. CSS gets those stops
  too.
- The `method` error names the four values, in every catalog.
- The Method menu is as wide as its longest name in the interface
  language, within the panel.

Docs: docs/mcp.md, docs/plugins.md, docs/target-app-parity.md, ROADMAP.
2026-11-13 08:26:57 -08:00
BFlat 9395fb9cec Merge pull request #7160 from dotCooCoo/fix/svg-image-orientation
SVG import: raster images follow their EXIF or TIFF orientation
2026-11-13 23:16:49 +07:00
dotCooCoo 1ce6431a99 SVG import: raster images follow their EXIF or TIFF orientation
An <image> whose EXIF orientation or TIFF Orientation tag turns or flips
its pixels, linked or as a data URL, is turned before usvg reads it: the
pixels are stored as they are shown, the width and height swap, and the
box fits the turned image under preserveAspectRatio, as CSS
image-orientation: from-image describes. A turned CMYK JPEG or TIFF keeps
its inks as a CMYK TIFF, as does a CMYK TIFF that is not turned.

- Data URLs of type image/tiff, and untyped TIFF bytes, are read.
- TIFFs with extra channels, CCITT Group 3 TIFFs and planar CMYK TIFFs
  are read as Open and Place read them.
- Converted images decode within the decode allowance, and a TIFF that
  claims more strips or tiles than the decoder can count is refused
  before any decoder opens it.
- exif_orientation moves to vectorcraft-doc so Open/Place and SVG import
  share it.

Tests: crates/svg/tests/it/image_orientation.rs.
2026-11-13 06:53:42 -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 7b30424538 Merge main into feat/warichu 2026-11-13 04:06:56 -08:00
dotCooCoo 6e2fe9fddf SVG: right-to-left text with text-anchor start or end opens at its directional edge
SVG 2 defines text-anchor start for right-to-left text as placing the
right side of the text at its position, and end as placing the left
side there (https://www.w3.org/TR/SVG2/text.html#TextAnchoringProperties).
Import placed start at the left edge and end at the right edge whatever
the direction.

- Point type and type on a path swap start and end when the effective
  direction is rtl. On a path, right-to-left text anchored by start now
  ends at startOffset and text anchored by end begins there. Vertical
  type keeps its anchoring.
- Each line and each absolutely placed chunk is anchored by the
  text-anchor and direction of the element holding its first
  character, a tspan's own included. A later chunk no longer takes the
  first chunk's anchor.
- Each line of point type starts at its own x, also when it has the
  same anchor as the line before it or another line holds absolutely
  placed characters. Such lines were drawn at the first line's x.
- A test covers letter-spaced right-to-left lines: the layout keeps the
  tracking of every letter in such a line, so the whole advance sits
  on its anchor.
2026-11-13 01:06:53 -08:00
BFlat dd8446f3a4 Merge pull request #7054 from dotCooCoo/feat/nine-slice-guides-outside-art
Symbols: 9-slice guides may lie outside the symbol's art
2026-11-13 15:03:59 +07:00
dotCooCoo 199a9dbce3 Merge main into feat/warichu 2026-11-12 22:28:38 -08:00
dotCooCoo 226ac437b9 Type: Warichu stacks selected characters on lines inside their line
The Character panel menu has Warichu and Warichu Settings… with Show East
Asian Options. Warichu characters are set at a percentage of their size
and stacked on 1 to 6 lines inside their line: one above the other in
horizontal type and side by side in vertical type. Warichu Settings sets
Lines, Line Gap, Scale and Alignment.

- text.setFormat and text.setRangeStyle take warichu, warichuLines,
  warichuGap, warichuScale and warichuAlign; ui.warichuSettings opens
  the dialog.
- A stretch of warichu characters is one block that stays whole on one
  line. The canvas and every export draw the stacked lines; SVG, PDF and
  the .ai writers write such type as outlines, as they do ruby. Type on a
  path sets the characters at their own size.
- The attributes are saved natively, carried by character styles and
  translated in every complete catalog.

Docs: docs/mcp.md, docs/type-parity.md.
2026-11-12 21:13:21 -08:00
dotCooCoo 7b6b6af7b1 Type: the Mojikumi Set menu offers the predefined spacing sets
The Paragraph panel's Mojikumi Set lists None, YakumonoHankaku,
GyomatsuYakumonoHankaku, GyomatsuYakumonoZenkaku and YakumonoZenkaku,
then Line-end Punctuation Half Width, and text.setFormat {mojikumi}
takes yakumonoHankaku, gyomatsuYakumonoHankaku, gyomatsuYakumonoZenkaku
and yakumonoZenkaku besides none and lineEndHalf.

The spacing of each set is VectorCraft's own rule, built on the help
page's one-line description of each set:
- YakumonoHankaku sets every bracket, comma and full stop half width,
  takes a quarter em off each side of a middle dot, and puts a quarter
  em between Japanese and Latin letters or digits.
- GyomatsuYakumonoHankaku keeps punctuation full width inside a line,
  sets an opening bracket starting a line flush and a closing mark
  ending it half width, and takes the quarter em after a middle dot
  ending a line off.
- GyomatsuYakumonoZenkaku keeps punctuation full width and adds the
  quarter em between Japanese and Latin text.
- YakumonoZenkaku sets characters as None does.

Both composers and vertical type measure each line as it is set. A
right-to-left paragraph, whose line ends are measured untrimmed, also
draws them untrimmed, and a right-to-left line that ends between a
Japanese and a Latin character is measured without the quarter em the
set adds there. With a Desired glyph scaling other than 100%, the
space a set takes off before an opening bracket or middle dot, and the
Japanese-Latin space recorded at a line's end, scale with the glyph.

The sets save with the document and paragraph styles; new type and
Reset Panel keep Line-end Punctuation Half Width. SVG export lays type
with middle dots out again for its positioned spans. A .ai save
reports a paragraph with one of the new sets as left out.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/type-parity.md,
docs/gaps.md.
2026-11-12 21:07:44 -08:00
dotCooCoo 26ab0f2523 Symbols: 9-slice guides may lie outside the symbol's art
A 9-slice guide can now be placed outside the symbol's art, by dragging
it past the art's edge in symbol editing mode, by symbol.options
nineSliceGuides, or by a Symbol Options field below 0% or above 100%.
A scaled instance then scales by the cells its art falls in.

- Each guide is a fraction from its edge in -16..16, and the two guides
  of an axis keep their order (left + right < 1, top + bottom < 1).
- A guide past an edge cuts the art at that edge, so that side's outer
  cell holds no art. With both guides of an axis past opposite edges,
  the art scales as a whole along it. With both past the same edge, the
  art keeps its size in the outer cell at the other edge.
- Symbol Options shows the guides with three decimals, and OK keeps a
  field that was not changed at its exact value.
- Native files keep the guides exactly. SVG and .ai write such an
  instance as its placed art, without guides.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/gaps.md.
2026-11-12 20:40:05 -08:00
dotCooCoo df155042d4 Merge main into feat/image-color-mask 2026-11-12 17:40:13 -08:00
dotCooCoo 705b688f2b Type: Insert Aki keeps a widened glyph's own width and an optical pair's letter
A glyph that justification widens to fill the line keeps its Insert Aki
before it at its width, and its own width, which ligature carets stand
in, now starts where the glyph is drawn. Optical kerning measures a pair
from the letter of a cluster rather than from a combining mark whose
advance holds the cluster's Aki.

The SVG test also checks centered and right-aligned lines with Insert Aki.
2026-11-12 15:48:12 -08:00
dotCooCoo 3c10f9bf71 Merge main into feat/cjk-insert-aki 2026-11-12 14:28:45 -08:00
BFlat 9861e7f927 Merge pull request #6920 from dotCooCoo/feat/export-notes-loss-first
Save and Export: notes of what the file loses come before notes on options
2026-11-13 05:23:53 +07:00
BFlat c2f97d2efa Merge pull request #6961 from dotCooCoo/fix/gradient-repeat-runs
Gradients: a repeated gradient with many stops keeps every run
2026-11-13 05:22:59 +07:00
dotCooCoo 312d4a3914 Merge main into feat/image-color-mask 2026-11-12 13:30:24 -08:00
dotCooCoo c513114674 Gradients: Expand reports a capped gradient as unavailable
- object.expand.info reports no gradient or fill to expand for a
  gradient that repeats in more runs across its box than are written,
  matching object.expand, which leaves it a gradient. For a path that a
  painting live effect draws, it measures each piece the effect returns.
- CSS writes a repeating gradient's stop positions, and a repeating
  radial gradient's radius, with enough decimals that their rounding
  adds up to at most a hundredth of a period across the box (at least
  four for positions and three for the radius).
- docs/mcp.md says that sharp steps and swatch-linked stops are the last
  to be left out rather than always kept, and that the CSS conic limit
  counts stops before midpoints and Perceptual curves add more.
2026-11-12 12:55:54 -08:00
dotCooCoo b9946efb1e Merge main into feat/cjk-insert-aki 2026-11-12 12:46:09 -08:00
dotCooCoo 15ffb4ec01 Merge main into feat/export-notes-loss-first 2026-11-12 12:45:09 -08:00
dotCooCoo 0691e1acbe Gradients: keep every run of a repeated gradient with many stops
When a repeated or reflected gradient needs more stops than a limit
allows, each run now leaves out the stops that change its colors
least, so every run is still written, instead of writing fewer runs
and padding the rest with the last color. Each run keeps its ends,
sharp steps, swatch-linked stops, tint and CMYK ink changes and spans
shaped by an off-center midpoint, and the colors are compared as the
gradient's method mixes them. This applies to gradients along or across
a stroke, CSS conic gradients, and PDF, EPS and .ai export.

- Outline Stroke's mesh fits the gradient to at most 1,024 stops, and
  splits a stroke into several meshes when one would hold more than
  2^20 points.
- Object > Expand leaves a gradient as it is when the box needs more
  than 256 runs, instead of expanding only the first runs.
- CSS writes the stop positions of repeating gradients with four
  decimals, so the period stays in place across the box.
- A swatch chip of a gradient that repeats gets at least four grid
  cells a run along each side (at most one a point, and 128), so it
  does not show one color when the count matches the chip's cells.
- docs/mcp.md states the stop limits and the PDF import sample budget.
2026-11-12 11:30:44 -08:00
dotCooCoo 89748b0360 Type: Insert Aki Left and Right add space before and after a character
Characters take two new attributes, `akiLeft` and `akiRight`: space
before and after each character in reading order, in thousandths of an
em from 0 to 1000, measured as tracking is. `text.setFormat`,
`text.setRangeStyle` and character styles set them; values that are not
numbers or are outside the range are refused, and a file holding one
outside the range opens with the nearer end. Both are saved only when
not 0.

- Layout: Left is the space above a character in vertical type and on
  its right in right-to-left type. Wrapping counts the space, a line's
  end keeps it when aligned, and justification and Optical kerning
  leave it at its set width. A ligature, a letter with its marks and a
  tate-chu-yoko block take the values of their first character.
- With Fractional Widths off or System Layout on, the space before and
  the space after a character are each set in whole points and added
  to its rounded width, so justification keeps them at that width. A
  tate-chu-yoko block rounds the space above and below it the same way.
- Character panel: with Show East Asian Options on, Insert Aki Left and
  Insert Aki Right fields with 0, 250, 500 and 1000 in their menus;
  Reset Panel sets both to 0.
- Character Style Options › Advanced Character Formats shows Insert
  Aki Left and Insert Aki Right, with the Character panel's 0, 250, 500
  and 1000 in their menus; a typed value below 0 or past 1000 sets 0 or
  1000, and Reset Panel unsets both.
- SVG export: live text places each character the space moves, and
  type on a path with Insert Aki is written as outlines.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-12 10:24:38 -08:00
dotCooCoo 1132cf0c79 SVG: Save As SVG can include the document's variables and data sets
SVG Options gets an Include Variable Data checkbox (the `variableData`
option, off by default). With it on, the saved SVG carries the document's
text variables, its data sets' text values and each text variable's bound
type object as a `variableSets` element, in the layout Load Variable
Library already reads. A bound object is named by its element id in a
`docRef="id('...')"` attribute; an object without an id gets one.

Opening such an SVG reads the variable data back: the text variables, the
data sets (none active) and the bindings, so choosing a data set changes
the type again. Variable data that Load Variable Library would refuse is
left out with a warning, and the art opens. Markup that only looks like
the element (in comments, CDATA sections, processing instructions or the
document type declaration) is skipped.

Variables of other kinds, names XML can't hold, more than 256 text
variables or 1,000 data sets, and variable data over 8 MiB or 2,000,000
XML nodes are left out of the file, each with a warning. Type written as
outlines opens as art, so its variables are written unbound, with a
warning.
2026-11-12 10:18:22 -08:00
dotCooCoo 8d27e53115 Merge main into feat/image-color-mask 2026-11-12 08:56:33 -08:00
BFlat e618783377 Merge pull request #6084 from Taha-Mahmoodi/feat/glyph-alternates
Glyphs: alternates keep the word joined (feature values, one-piece shaping, unencoded glyphs)
2026-11-12 23:53:13 +07:00
dotCooCoo 766e291e3c Save and Export: notes of what the file loses come before notes on options
Each writer marks its notes as a loss (something of the document that the
file leaves out or that reaches it changed) or as a note on the options
(an option it didn't apply or applied another way while the art reaches
the file as it looks, or something an option turns off). The PDF, SVG,
EPS, legacy .ai, DXF and HP-GL writers return their notes on the options
in a list of their own (`setting_notes`) beside `warnings`, and the
engine's `Encoded` does the same.

The replies of the save and export commands, and of
`clipboard.exportPdf` and `clipboard.exportPng`, list the losses first,
each kind in the order the writers gave them, across every file that
Export for Screens writes. The status bar names the first note, so an
export to lossy WebP of type in a font that isn't installed now names the
fallback-font note instead of the lossy WebP note. A print to a file
keeps the order its notes are given in.
2026-11-12 08:33:57 -08:00
BFlat 3df161a972 Merge pull request #6903 from dotCooCoo/feat/gradient-repeat
Gradients: repeat or reflect a gradient's stops a set number of times
2026-11-12 23:10:02 +07:00
dotCooCoo e54519d6bd Merge main into feat/image-color-mask 2026-11-12 07:51:12 -08:00
Taha-Mahmoodi 7c6f51e8d6 Merge remote-tracking branch 'origin/main' into feat/glyph-alternates 2026-11-12 19:37:53 +04:30
BFlat 50da206604 Merge pull request #6877 from dotCooCoo/fix/svg-use-text-inherits
SVG import: a <use> of a text passes its text properties on to the text
2026-11-12 22:00:34 +07:00
dotCooCoo 91d705e155 Gradients: repeat or reflect a gradient's stops a set number of times
A linear, radial, conic or diamond gradient has a spread: Pad (the
default, as before), Repeat or Reflect, with a count of 1 to 100
(VectorCraft's own limit). Repeat and Reflect run the stops that many
times along the vector (around the turn for a conic) and keep going past
both ends, Reflect running every other run backwards, as SVG's
spreadMethod does.

The Gradient panel has a Spread menu and a Repeats field, and the
Gradient tool marks where the runs meet. paint.setFill and
paint.editGradient take `spread` and `repeats`; bad values are errors.
Presets, swatches, blends, strokes along or across a path, Outline
Stroke and Object › Expand follow the spread; Expand gives each run the
same whole number of objects, and Outline Stroke's mesh keeps the color
on each side of a sharp step between two stops at one offset.

SVG writes and reads spreadMethod: SVG import now reads a repeating or
reflecting gradient as one that repeats or reflects once, instead of
expanding its stops over the shape. CSS writes repeating gradients.
PDF writes a conic's sweep shading over one run. PDF, EPS, legacy .ai
and the .ai editing data write the other kinds with their stops
repeated across the shape: at most 256 runs and 16,384 stops (256 stops
in .ai files), and for a radial or diamond no farther than 65,536 runs
from its center, with a warning past those. PDF import now finds sharp
steps in any axial or radial shading instead of blurring them.

Docs: docs/mcp.md (Spread), docs/target-app-parity.md, docs/gaps.md.
2026-11-12 06:52:38 -08:00
Taha-Mahmoodi d15b9f7f25 Merge remote-tracking branch 'origin/main' into feat/glyph-alternates 2026-11-12 18:35:54 +04:30