Commit Graph
14082 Commits
Author SHA1 Message Date
dotCooCoo c347abf499 TIFF: CMYK files with unnamed extra samples past their ExtraSamples values open opaque
A CMYK TIFF with two or more samples past its inks and its ExtraSamples
values, or with no ExtraSamples tag, which the decoder gives no color
type, now opens opaque when none of those values names an alpha. It
failed to open before; one that names an alpha already read with it.
2026-11-14 12:05:50 -08:00
dotCooCoo 74dc87f922 Asset Export: panel export tests use temporary folders removed on drop
The four panel tests that export into a folder made it under the system
temporary folder and removed it at the end, so a failing assertion left
the files behind. They now use vectorcraft_testkit::TempDir, which
removes the folder when the test ends or panics.
2026-11-14 12:02:10 -08:00
dotCooCoo 9d0a4eb731 Shape Builder: rewrap the shape_builder_shapes doc comment 2026-11-14 11:47:28 -08:00
dotCooCoo 3a834f705a TIFF: refuse files of over 64 samples a pixel from their tags
The 64-sample limit is now checked against the file's SamplesPerPixel
before anything is decoded. It was checked against the samples the
decoder gives, which is five for any CMYK file whose first ExtraSamples
value is alpha, so a small file with 65,535 samples a pixel was read and
the decoder took a row of every sample (1 GiB for a 16-bit file 8,192
pixels wide) before reading its data.

- The row the decoder reads each strip or tile into, when the file has
  more samples a pixel than it gives, counts in the allowance.
- Files whose samples aren't unsigned integers are refused from their
  tags; no reader takes them.
- The same checks run before CMYK inks and plates are decoded.
- The copy that puts a planar file's samples pixel by pixel is allocated
  once, at its length, so the reader holds no more than it counts.
2026-11-14 11:44:57 -08:00
dotCooCoo ebc82ca0d5 Links: Sort by Size in the panel menu lists the smallest file first
The Links panel menu gets Sort by Size beside Sort by Name, Kind and
Status, and links.list takes sort: "size". A row's size is the file size
Link Info shows: the file's size now, else the size the document keeps
of it. Rows without a size (embedded images, a missing file whose size
is not kept) come last, and equal sizes go by name. The size is read
only when sorting by it, and under automation read roots a file outside
them is not read.

Every catalog with Sort by Status gets a Sort by Size row; the Ukrainian
Sort by Status row, which read "Sort by:", now says "Sort by Status".
2026-11-14 11:43:39 -08:00
dotCooCoo 9317ee0653 PDF: 16-bit TIFF test checks the written samples
The tagged TIFF test now compares the decoded image data with the source
for a 16-bit RGB TIFF whose high and low sample bytes differ: big-endian
16-bit samples at PDF 1.7, and the samples reduced to 8 bits at PDF 1.4.
A byte-swapped or truncated stream no longer passes.
2026-11-14 11:43:26 -08:00
dotCooCoo 6a078a0030 Blend: dashes on a closed key reversed from its own start read back from its end
A closed key that winds the other way is reversed keeping its start anchor.
Its dash offset was computed as if the reversed path ran back from position
0 instead of from the end of the subpath, so the dashes shifted by the
subpath length modulo the pattern period. The offset now uses the full
length in that case, and the dash test covers a closed key 40 long with a
7.5 period.
2026-11-14 11:41:09 -08:00
dotCooCoo a512bf5ce6 Image resolution: a WebP chunk length past the end of the file reads no resolution
The WebP chunk walk added the chunk length to the offset without a
checked add, so a length near u32::MAX overflowed on 32-bit targets
(wasm32) with overflow checks on. It now reads the chunk through the
same checked slice helper as the PNG, JPEG and TIFF readers.
2026-11-14 11:37:16 -08:00
ncic badf89be39 Merge remote-tracking branch 'upstream/main' into pencil-no-guides
# Conflicts:
#	docs/mcp.md
#	docs/target-app-parity.md
2026-11-14 20:32:09 +01:00
dotCooCoo a067279ee9 Docs: legacy .ai Insert Aki warning applies only to type written as text
With outlineText the legacy .ai save writes the type as outlines, which keep
the Insert Aki spacing, and does not warn that the values are left out.
2026-11-14 11:31:49 -08:00
dotCooCoo 8e74183c20 Merge main into fix/svg-use-chain-bounded 2026-11-14 11:29:20 -08:00
dotCooCoo a19646b2f3 Asset Export: Create Sub-folders, Open Location after Export and Format Settings in the panel menu
Create Sub-folders and Open Location after Export are check items that set
the settings the panel shares with Export for Screens, and the panel's
Export follows them. Create Sub-folders is checked as the dialog opens on
it: on for a document without export settings, and on for remembered
folders per row, format or item, which the panel's Export now uses too.
Format Settings... opens Export for Screens on the Assets tab showing the
first row's format settings.

assets.settings.set takes openLocation (kept in document.exportSettings,
not in presets), refuses a subfolders or openLocation value that is not
true, false or null, and with subfolders false given alone also ends a
remembered folder per format or item. Export for Screens reads remembered
empty folders as none, as an export does.
2026-11-14 11:26:20 -08:00
bflatastic 79572cbf6c Revert "Cut over roxmltree, png, tiff, psd, and trace codecs to craft-libs (#7638)"
This reverts commit bada8e1710. #7638 merged with its gate failing: besides six unformatted
files, render and trace don't compile against craft-libs e2dc681 (u64::from(usize) in
render/src/paint.rs, a private craft-libs function, and a call with one argument too many in
trace/src/opaque.rs), so main's CI fails. The tree is that of 0adbdce6cb, whose CI passed.
2026-11-15 02:26:07 +07:00
dotCooCoo 7ef603db40 Live Paint: bucket scopes follow the stroke an edge draws
The Live Paint Bucket's connected and samePaint scopes kept their own
copies of the face flood and the edge walk. They read an edge's stored
stroke paint, so an edge that is fully transparent or whose stroke is
hidden stopped a face flood and matched as stroked. The face flood also
required outlines within 0.0001 pt of an edge, which a curved face that
the planar map refits can miss.

The engine now calls the shared flood_faces and connected_edges that
Live Paint Selection uses (#7342), and compares edges by lp_stroke_paint,
the paint of the topmost stroke that draws. The tool records that same
paint as the match for a double-click or triple-click on an edge. The
params text and docs/mcp.md describe the drawn stroke paint.
2026-11-14 11:24:56 -08:00
dotCooCoo 8ea413f054 SVG import: measure what an SVG expands to before the style cascade reads it
The depth check and the prepass read each element's declarations through
a cascade that keeps a copy of them per element, so one 100 kB style
sheet rule on 16,000 groups still took 1.5 GB before the SVG was refused.
The SVG and each SVG image it shows are now measured before that cascade
runs. Copies nested past the depth cap, and loops of <use> references,
are found as too deep: an SVG is refused with the depth error, and an SVG
image is left out with the depth warning, as before.
2026-11-14 11:19:42 -08:00
dotCooCoo 0b1e1e99a1 Effects: Effect › SVG Filters lists default filters
Effect › SVG Filters and Apply SVG Filter list 14 default filters in
every document, before the document's own. The set, the names and the
values are VectorCraft's own, written with SVG 1.1 filter primitives
(crates/effects/src/svg_filter/defaults.rs).

A document holds none of the defaults until one is applied or edited
there. svgFilter.apply with a default's name copies it into the document
in the same undo step, and svgFilter.define with replace naming a default
adds the edited copy under that name. The copy is listed in the default's
place. svgFilter.delete refuses a default the document holds no copy of,
and deleting the copy lists the default again.

svgFilter.list also returns the defaults. svgFilter.apply takes index and
item to put the filter in the place of an applied SVG filter, which the
dialog's OK uses when it is opened for an applied effect; an index that
is not an applied SVG filter, null included, is an error.
2026-11-14 11:19:22 -08:00
dotCooCoo f0c920fa8e Shape Builder: leave out Live Paint faces and edges with an opacity of 0
A visible, unlocked Live Paint face or edge whose own opacity is 0 draws
nothing, as Expand and the edge stroke check already treat it, but the
Shape Builder still took it as an input. It now leaves such faces and
edges out; hidden and locked parts keep their current handling.
2026-11-14 11:15:41 -08:00
dotCooCoo 1bd269b4ed Type: Smart Punctuation replaces ligatures, extra spaces and fractions and reports results
Type > Smart Punctuation... now opens its own dialog in place of the
generic parameter form. It has the seven replacement options, Replace In
(Selected Text Only or Entire Document) and Report Results, which lists
how many of each chosen replacement were made after OK. The dialog opens
on the options confirmed last.

- type.smartPunctuation takes fiLigatures, flLigatures, spaces and
  fractions, all off by default, so existing calls behave as before.
- Ligatures write U+FB00 to U+FB04, longest match first. Expert
  Fractions writes the one-character fractions Unicode encodes, and
  skips one with a digit, a slash or a decimal point next to it, across
  a change of style too. Either is written only where the run's font
  has a glyph for it.
- Smart Spaces keeps one space after a period, across runs. An inline
  graphic separates the text on either side of it.
- The reply adds a replaced object with a count per kind. Nothing to
  replace still records no undo step.

Docs: docs/mcp.md, docs/type-parity.md.
2026-11-14 11:09:09 -08:00
dotCooCoo b374d1f2c0 EPS import: cshow over a descendant without a matrix sees it at its drawn scale
When a Type 2 CIDFont or a Type 42 descendant of a composite font has no FontMatrix, the
font a cshow procedure sees as current now takes the identity as its default, as glyph
measuring and drawing do, instead of the 1/1000 default of other fonts.
2026-11-14 11:03:23 -08:00
dotCooCoo 87b861c480 TIFF: extra-sample reads count the planar copy and RGBA pixels in the allowance
A CMYK or grayscale TIFF with extra samples is now read only when its
decoded samples, the copy that puts a planar file's samples pixel by
pixel and the RGBA pixels together fit the decoding allowance; the
check counted the decoded samples alone, so a small compressed file
could make the reader hold about twice the allowance plus the RGBA
image.

- The decoder may decode no more than the counted samples, and reads no
  strip or tile of more compressed bytes than the file holds.
- A pixel of more than 64 samples is not read (VectorCraft's own limit).
2026-11-14 10:50:41 -08:00
Dylan M. Taylor bada8e1710 Cut over roxmltree, png, tiff, psd, and trace codecs to craft-libs (#7638) 2026-11-14 13:49:50 -05:00
dotCooCoo 54f4c256eb Type: a tabbed right-to-left line ending at a final soft hyphen draws no hyphen
Where a tabbed line with glyphs stepping back stops reading at its break limit on a soft
hyphen that ends the text, it now ends there without a hyphen, as a line that reads to the
end of the text does.
2026-11-14 10:49:21 -08:00
dotCooCoo 6b9564c60d PDF: PDF/X-1a test covers a tagged gray JPEG
The PDF/X-1a case of tagged_tiffs_keep_their_icc_profile also exports a
gray JPEG with a gray profile, kept as it is, resampled and compressed
with JPEG, and checks that it is written in DeviceGray without the
profile. The comments on decoded_image, recode and the test say that
PDF/X-1a allows no ICC-based color spaces.
2026-11-14 10:48:45 -08:00
dotCooCoo 69ce720e46 Live Paint: Live Paint Selection does nothing on a triple-click
main's Live Paint Selection double-click (#7342) replaced the tool's
catch-all pointer arm with an explicit DoubleClick arm, so the
TripleClick kind this branch adds left the match non-exhaustive. The
tool now takes no action on TripleClick: the third click's press and
release already select what a click selects, as before the merge.

The merge also took main's import list in the engine's Live Paint
commands, which no longer named Paint while this branch's Merge code
still uses it; Paint is imported again.
2026-11-14 10:44:26 -08:00
datatimp 8d3f8d1ce5 Merge branch 'main' of https://github.com/storytold/vectorcraft into fix/7340-guide-row-clicks 2026-11-14 13:36:34 -05:00
dotCooCoo 576660020e Merge main into fix/later-span-taller-line-clears 2026-11-14 10:34:32 -08:00
dotCooCoo 3f3bccde77 Merge main into fix/svg-use-chain-bounded 2026-11-14 10:33:52 -08:00
dotCooCoo 2c5f3b9d20 SVG import: refuse an SVG whose use copies and styles expand past the limits
usvg copies the element a <use> shows, and every copy gets its own copy of
the declarations its style attribute and matching style sheet rules give
it. A chain of <use>s that doubles at each level made usvg build those
copies before the import's object limit was checked: a 101 kB file whose
rectangle has a 100 kB style took 4.7 GB at 14 levels, and one 100 kB
style sheet rule on 16,000 groups took 1.5 GB without any <use>. A plain
chain stopped at usvg's own 1,000,000-node limit with a parse error.

The import now measures, before usvg reads the SVG, how many elements it
expands to and how many bytes of style values those elements copy, each
element measured once and each <use> counted as one addition. References
follow usvg's rules: first element of an id, href before xlink:href, and
the self and back references usvg leaves out. An SVG past 999,999
elements (500,000 on the web) is refused as too large, a loop of <use>
references usvg would copy without end is refused too, and one past 64 MB
of copied style values is refused with the new SvgError::StylesTooLarge.
SVG images count toward the same 64 MB as the SVG showing them, an image
past either limit refuses the whole import, and an image that doesn't
decode does not count.
2026-11-14 10:33:00 -08:00
dotCooCoo 3534dd7343 Symbols: measuring and picking in 9-slice instances reuse one set of symbol boxes
Measuring an instance of a symbol with 9-slice guides looked its map up
through Document::symbol_slice_map, which scanned the symbol list and
measured the art box again in new quick boxes, and NineSliceMap::place
and most_placed made new quick boxes of their own. Each of those boxes
built the symbol name index over the whole list on its first lookup, so
every 9-slice instance measured read every symbol once or more. Picking
an object inside a 9-slice instance's art with Direct Selection did the
same for each object of the art that holds a symbol instance.

- SymbolBoxes cuts the guides through the art box it has measured
  (SymbolBoxes::slice_map), and places and counts the art with itself as
  the boxes, in the mode quick boxes use. Art with no 9-slice map is
  moved by the instance's transform without looking the map up again.
- Document::symbol_slice_map, place_symbol_art,
  place_symbol_art_unclipped and edited_nine_slice use one set of quick
  boxes for each call.
- symbol_part_at and symbol_part_outline keep one set of quick boxes
  for the instance's art.

The new tests count the symbols read. Measuring 200 instances of a
symbol with guides and 200 of a symbol with guides holding it, among
2,002 symbols, read 1,205,204 symbols before; now each set of boxes
reads the list once. Picking in a 9-slice instance whose art holds 20
instances read 84,042 symbols among 2,001; now it reads the list at
most twice.
2026-11-14 10:32:20 -08:00
dotCooCoo b12d4794a0 Merge main into fix/eps-cshow-composite-codes 2026-11-14 10:29:48 -08:00
dotCooCoo 924aa258d0 Merge main into fix/shape-builder-open-path-drawn-fill 2026-11-14 10:29:06 -08:00
dotCooCoo 59d238d682 Merge main into fix/tabbed-rtl-negative-tracking-break-bound
# Conflicts:
#	crates/text/src/layout/breaking.rs
#	crates/text/src/layout/wrapping_tests.rs
2026-11-14 10:25:44 -08:00
dotCooCoo 1c4cc88e90 Merge main into feat/live-paint-bucket-multi-click
# Conflicts:
#	crates/tools/src/builder/live_paint.rs
2026-11-14 10:19:24 -08:00
BFlat 0adbdce6cb Merge pull request #7621 from ruchirguitar/feat/symbols-item-context-menu 2026-11-15 01:14:51 +07:00
dotCooCoo 3be00f4bd3 Blend: a gradient steps where the other does only at a step of its own
When two gradients with different stop counts are blended, the second stop at the other
gradient's step took the last stop within the step tolerance, so two close but distinct stops
became a hard step. It now takes the last stop of a step of its own there, and otherwise paints
what the gradient paints at that place.
2026-11-14 10:14:38 -08:00
dotCooCoo 82d675e0b2 TIFF: the decoder's default buffer limit is 256 MiB
The doc comments of with_room and of the large-file test, and the
progress-doc line, gave the tiff decoder's default decoding buffer
(268,435,456 bytes) as 256 MB.
2026-11-14 10:10:54 -08:00
dotCooCoo d2e36f689b Docs: PDF/X-1a writes tagged gray images without their profile
The progress-doc line for gray and RGB image profiles in PDF export now
states that PDF/X-1a, which allows no ICC-based color spaces, writes a
gray image with a gray profile in DeviceGray without the profile, and
converts RGB images to CMYK as before.
2026-11-14 10:10:37 -08:00
dotCooCoo 66f989c27b Effects: the member limit's doc comment says a compound path with fill or stroke effects becomes a group of pieces 2026-11-14 10:09:26 -08:00
dotCooCoo 7cbbf97b74 Merge main into fix/pdf-art-bounds-printing-layers 2026-11-14 10:03:22 -08:00
dotCooCoo 22ec066bb7 Blend: a turned hue gradient that reflects keeps its swatch links
A hue gradient that reflects unrolls through its curve, whose stops have no swatch links, so a
gradient along a closed key started elsewhere lost its links. The links now come from the
gradient's own stops unrolled as a Classic gradient's, in order, so stops at one place and the
stops where two runs meet keep their own links.
2026-11-14 10:03:08 -08:00
dotCooCoo 7ff186bd31 Type: a selected character's alternate glyphs show beside it on the canvas
With a Type tool and exactly one character selected, the canvas shows up
to five of that character's alternate glyphs in a row beside it. A click
runs the command the Glyphs panel's double-click runs, as one undo step;
an arrow at the end opens the Glyphs panel on Alternates for Current
Selection when the character has more. Preferences > Type > Show
Character Alternates (showCharacterAlternates, on by default) turns the
row off.
2026-11-14 10:00:04 -08:00
dotCooCoo 951f689e8f Export: an HP-GL plot of the art's bounds measures images by the art their effects add
An image or a gradient mesh with a visible Crop Marks effect, or with a
Vector Halftone effect that makes dots, is measured in a plot of the art's
bounds. A halftone that makes no dots, such as one past the effect's cell
limit, leaves the image out as before.
2026-11-14 09:59:27 -08:00
dotCooCoo 9d8407b3de Metafile import: metric WMFs are read at 96 pixels an inch, and only round solid strokes join
A WMF without a usable placeable box that draws in a metric map mode
(MM_LOMETRIC to MM_TWIPS) is read at 96 pixels an inch, the scale
craft_metafile plays those modes at, so a one-inch MM_TWIPS picture opens
at 72 pt instead of 4.8 pt. The mode that more of the drawing records the
reader plays are drawn in decides, with SAVEDC and RESTOREDC followed as
the reader follows them; a file that draws nothing goes by its last mode.

Opaque strokes drawn one after another now join into one path only when
the pen is solid with round caps and round joins. A dashed stroke keeps
its own dash phase, and flat or square caps and miter or bevel joins
stay as the separate strokes drew them.
2026-11-14 09:58:42 -08:00
dotCooCoo f5312d795f TIFF: stored CMYK TIFFs with alpha keep their inks; wide and large files read
A document saved with a CMYK TIFF whose alpha follows another extra
sample keeps it as a CMYK image with its ink amounts, so EPS and PDF
export write the same inks as before; Open and Place still read such a
file with its alpha.

- A CMYK file with two or more samples past its inks and its
  ExtraSamples values, which the decoder gives no color type, reads the
  alpha that one of those values names, and its Orientation tag is read.
- A TIFF with extra samples whose samples take more than the decoder's
  256 MB buffer reads, up to the decoding allowance.
- A file read as black-is-zero gray is read in place instead of copied.
2026-11-14 09:58:26 -08:00
dotCooCoo 7e390fe079 Merge main into fix/3d-masked-cap-graphic-once 2026-11-14 09:55:12 -08:00
dotCooCoo a3ba3eeb38 Merge main into fix/3d-shadow-builds-once 2026-11-14 09:52:39 -08:00
dotCooCoo 7e84899786 Merge main into fix/graph-series-group-opacity 2026-11-14 09:52:39 -08:00
dotCooCoo e1da80a2c8 Merge main into fix/learn-panel-commands 2026-11-14 09:52:25 -08:00
dotCooCoo eafeff8632 Merge main into fix/hindi-label-senses 2026-11-14 09:52:16 -08:00
dotCooCoo 22f4b18021 Merge main into fix/tr-message-rows 2026-11-14 09:52:07 -08:00