Commit Graph
380 Commits
Author SHA1 Message Date
dotCooCoo df155042d4 Merge main into feat/image-color-mask 2026-11-12 17:40:13 -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
dotCooCoo 15ffb4ec01 Merge main into feat/export-notes-loss-first 2026-11-12 12:45:09 -08:00
dotCooCoo fe34c2b818 Merge main into feat/custom-arrowheads 2026-11-12 09:23:20 -08:00
dotCooCoo 8d27e53115 Merge main into feat/image-color-mask 2026-11-12 08:56:33 -08:00
BFlat 5f3c7f5425 Merge pull request #6906 from ruchirguitar/fix/pdf-indic-text-5235
PDF import: Indic type that would not draw as the file does keeps the file's glyphs (#5235)
2026-11-12 23:52:49 +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
dotCooCoo e54519d6bd Merge main into feat/image-color-mask 2026-11-12 07:51:12 -08:00
ruchirguitar f18fb4e92d PDF import: Indic type that would not draw as the file does keeps the file's glyphs (#5235)
Gujarati text from a PDF came in jumbled even with its fonts installed:

- A glyph with no advance (a vowel sign or anusvara set over its letter)
  was taken for vertical type and opened as an object of its own, which
  type then drew on a dotted circle. In a font the file writes
  horizontally it now stays on its line.
- A line read glyph by glyph is not always the text that was set: the
  file draws a vowel sign before the consonant it follows, and may not
  name a conjunct. A line with characters of U+0900 to U+0DFF is now set
  again and its glyphs compared with the ink the file drew; when they
  differ the line keeps the file's glyphs as outlines, with a note.

Tests: crates/pdf/src/tests/reshaped.rs (two fail without the change).
2026-11-12 20:56:16 +05:30
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
dotCooCoo 72fb8fe81d Image Tools: masked images count as see-through in legacy .ai and pattern tiles
- The legacy .ai and the check for see-through images in pattern tiles
  read a color-masked image's masked pixels, as the flattener does. A
  legacy .ai flattens a masked opaque image with the art under it, or
  leaves it out with `transparentImages: "omit"`, and a pattern tile that
  holds one is expanded before PDF/X-1a, PDF/X-3 and legacy .ai output
  flatten it.
- Printed separations check whether a colorize paint shows on the masked
  pixels, as the canvas does, rather than on the image's own pixels.
- A saved mask that leaves out `contiguous`, `tolerance` or `softness`
  reads them as the command's defaults: true, 10 and 10.
- Remove Background is unchanged on this branch. Its refusal of a
  grayscale image colorized with a process color is left for a separate
  pull request.

Tests: legacy_ai_flattens_a_masked_image_as_a_see_through_one,
a_pattern_tile_with_a_masked_image_counts_as_see_through,
pdf_x1a_and_x3_flatten_the_hidden_pixels (PDF/X-3 added),
separations_colorize_masked_pixels_whose_own_pixels_are_a_preview,
a_record_without_settings_reads_the_defaults, and the dialog's query on a
linked image at its preview.
2026-11-12 06:27:59 -08:00
dotCooCoo 0779da299b Image Tools: Color Mask hides chosen colors of an image while its pixels stay
Object › Plug-ins › Image Tools gains Color Mask… (`addon.image.colorMask`),
Remove Color Mask and Apply Color Mask. Color Mask hides colors of the
selected images without changing their pixels. It matches colors as Remove
Background does: the edge color, the corner color or picked colors, with
Contiguous, Match, Tolerance and Softness. The mask is kept with the image
object and saved with the document: its settings, and a PNG of the image's
pixels with the hidden colors made clear, at the source's depth with its
color profile and resolution. It is a VectorCraft addition.

- The canvas (clip groups and white-shape opacity masks included),
  printing and separations, PDF (soft masks of CMYK images included), SVG,
  EPS, the raster formats, the legacy .ai and .ai editing data, EMF/WMF,
  DXF and the flattener draw the masked pixels. A linked image stays
  linked, and an output that would refer to its file writes the masked
  pixels instead.
- Copy and paste, placing a document and placed documents carry the masked
  pixels with the image, under the names they are given there.
- The dialog opens on the selected image's mask settings, with a Preview.
  Without settings, the command returns them and changes nothing.
- Setting, removing and applying a mask are each one undo step, and no
  step is added when nothing changes. Apply makes the masked pixels the
  image's own and embeds a linked image. A mask that hides nothing is only
  taken off, and the image keeps its pixels and its link.
- When an image's own pixels are replaced (Update Link, Relink, Crop
  Image, a recolor, a linked file read again on open), its mask is made
  again for the new pixels, in every art tree and in opacity-mask art.
  Crop Image cuts the image's own pixels. When the new pixels can't be
  masked, the mask keeps its settings and hides nothing.
- Objects in `ids` that are not images, CMYK images, linked images that
  show their preview, images past the decode allowance, masks that would
  hide every pixel, and grayscale images colorized with a process color
  are refused. The Eyedropper, Object Mosaic, Create Gradient Mesh, Crop
  Image's trim and Image Trace read the image's own pixels.
- An image is selected by a click anywhere in its frame, as before.

Remove Background now also refuses a grayscale image colorized with a
process color. Its result has an alpha channel, and a process color is
refused on a grayscale image with transparency.

Docs: docs/mcp.md (Image Tools add-on › Color Mask),
docs/target-app-parity.md.
2026-11-12 05:17:01 -08:00
dotCooCoo 5e80810605 Merge main into feat/custom-arrowheads 2026-11-12 05:00:21 -08:00
Taha-Mahmoodi 3eb9e0271c Merge remote-tracking branch 'origin/main' into fix/import-document-title
# Conflicts:
#	docs/file-format-parity.md
#	docs/gaps.md
#	docs/target-app-parity.md
2026-11-12 15:18:59 +04:30
BFlat 4c2912100e Merge pull request #6795 from dotCooCoo/fix/pdfx-check-skips-strings
PDF/X: names inside a string or a comment no longer fail the standard check
2026-11-12 14:16:50 +07:00
dotCooCoo 264eeec475 Merge main into feat/custom-arrowheads 2026-11-11 22:30:03 -08:00
dotCooCoo c1f7e2eeaf Stroke: custom arrowheads from an arrowheads file join the arrowhead menus
The Stroke panel menu's Edit Arrowheads… (stroke.arrowheads.edit) opens
Arrowheads.vectorcraft next to the preferences, creating it with one
example head when it is missing. Each symbol in the file is one head,
listed by name after the built-in heads in both arrowhead menus: it
points right, its registration point is the tip, and its height is drawn
as wide as the built-in heads. The file is read at startup and again
each time VectorCraft saves it; stroke.arrowheads.reload and
stroke.arrowheads.list read and list it.

Arrowhead gets a Custom variant, appended after the built-in names, that
carries the head's name and outline, so a document that uses one draws,
saves, reopens and exports it without the arrowheads file. stroke.set
takes {"custom": name} or the {"Custom": {name, path}} form that
document.inspect reports.

Part of #5421.
2026-11-11 21:52:46 -08:00
BFlat 4eb0915795 Merge pull request #6781 from dotCooCoo/feat/diamond-gradient
Gradients: diamond gradients run the stops from the center out to a square
2026-11-12 12:40:46 +07:00
dotCooCoo a07cdf87f0 PDF/X: names inside a string or a comment no longer fail the standard check
The check that a PDF/X-1a or PDF/X-3 file has no transparency read names
in literal strings and comments as syntax. The file identifier is a
base64 hash of the file, so a file could now and then hold `/BM` or
`/Lab` there and its export was refused as having blend modes or Lab
colors. The check now leaves out the text of literal strings and
comments as well as stream data.
2026-11-11 21:29:50 -08:00
dotCooCoo 66d21471ae Gradients: diamond gradients run the stops from the center out to a square
A new gradient kind, `diamond`, runs its stops from the center (the
start) out to a square with a corner at the end of the vector; the
aspect stretches the square across the vector into a rhombus. The
Gradient panel's Type row has a Diamond button, the Gradient tool draws
the square with the aspect handle and center ring, and a gradient
fitted to an object covers its box.

- Model and canvas: the level is |x| + |y| in the square's frame; a
  square with no area (no vector, an aspect of 0, numbers out of range)
  paints the last stop. The canvas draws it as pixels, out to a
  stroke's miters, caps and arrowheads. Transforms keep the vector and
  take the mapped size across it as the aspect.
- Commands: `paint.setFill`, `paint.editGradient` and
  `paint.setGradientGeom` take `kind: "diamond"`; a negative aspect or
  a focal point is an error. A conic or diamond gradient that a plug-in
  returns with a focal point loses the focal point.
- Gradient tool: dragging the aspect handle of a radial or diamond
  gradient whose file gave it a negative aspect now sets a positive
  aspect; only a conic's negative aspect mirrors it.
- Export: PDF, SVG, EPS and legacy .ai write four linear gradients, one
  for each quarter of the square, clipped to the shape (PDF as axial
  shadings). PDF and SVG write an image for translucent stops, a
  dithered fill or a flat square, and for the Perceptual method unless
  PDF writes the stops as spot color shadings. SVG writes a diamond on
  type characters as a radial gradient through the corners, and so
  does CSS. A swatch library saved as CSS gets four corner layers, a
  value for the `background` property (a circle when the swatch is
  turned by an angle that is not a multiple of 90 degrees). The .ai
  editing data holds a radial gradient through the corners. The
  document exports give a warning.
- SVG export: a stroke drawn as an image, as conic wedges or as diamond
  quarters now covers the miters, caps and arrowheads that reach past
  its paint box.
- PDF import reads a function-based shading whose color is the same
  along each square about the origin of its space as a diamond.
- Object > Expand gives nested squares or a mesh of rings.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-11 20:27:48 -08:00
dotCooCoo b403fe230f Merge main into fix/pdfa-14-long-pages 2026-11-11 18:43:27 -08:00
BFlat a118925177 Merge pull request #6668 from storytold/fix/sec-pdf-scan-budget
PDF: note fonts once per walk, place repeated rasters without converting again, check image headers before embedding
2026-11-12 09:23:42 +07:00
BFlat 9a4be17f0c Merge pull request #6680 from dotCooCoo/fix/pdf-points-option-pdfa
PDF: the in-points option leaves PDF/A pages within the standard's size limit
2026-11-12 09:13:17 +07:00
dotCooCooandGert Pellin 1b744eae52 Gradients: conic gradients sweep the stops once around the center
A fourth gradient kind, conic (also accepted as `angle` or `conical`),
sweeps the stops once around the center (the gradient's start),
clockwise on the page from the vector, with a seam where the last stop
meets the first. A negative aspect mirrors the sweep, and reflecting an
object turns its sweep the other way, also for a conic without a
vector or with a flat extent.

- Gradient panel: a Conic button in the Type row; the Aspect Ratio
  field works for conic gradients and keeps a mirrored sweep mirrored.
- Gradient tool: the extent ellipse, aspect handle and center ring of a
  radial gradient, without a focal point.
- Rendering, swatch chips, transforms, warps, plug-in round trips, and
  Object > Expand into wedges or a mesh of columns around the center.
- Commands: a negative `aspect` is an error for linear and radial
  gradients, and so is a `focal` point for a conic gradient.
- PDF export writes a function-based shading whose calculator function
  turns the angle into the ramp, and PDF import reads a function-based
  shading that turns around the origin of its space back as a conic
  gradient, also when that origin lies outside the painted area. A
  PDF/A file, or a conic with more than 97 stops, gets an image
  instead.
- SVG export writes 360 solid wedges clipped to the shape for opaque
  stops and an image otherwise, and a linear gradient on characters.
- CSS export writes `conic-gradient(from ...)`, with the stops mirrored
  for a mirrored sweep.
- EPS and legacy .ai export write 360 solid wedges; the .ai editing data
  holds a linear gradient along the vector with the same stops.
- .af files: conical gradients open as conic gradients.
- An export that writes the gradient in another form gives a warning.

Supersedes #2326 and includes switch87's code from it.

Co-authored-by: Gert Pellin <gert@pellin.be>
2026-11-11 15:51:11 -08:00
dotCooCoo 7bd559cb1d Docs: PDF/A files at PDF 1.4 write long pages in points
A PDF/A file at PDF 1.4 has no UserUnit, so a page longer than 14,400 pt is written in points with or without longPagesInPoints. The param text, docs/mcp.md and the exporter comment now say that only PDF/A files at PDF 1.5 and later keep the UserUnit.
2026-11-11 14:47:18 -08:00
dotCooCoo 2da0dcbba7 PDF: a PDF/A-2b file at PDF 1.4 writes long pages as PDF 1.6 with a UserUnit
PDF/A-2 limits page boundaries to 14,400 units (ISO 19005-2, 6.1.13), and
PDF 1.4 has no UserUnit. A PDF/A-2b export at PDF 1.4 with a page longer
than 14,400 pt was written in points, with a MediaBox over 14,400 units,
whether or not longPagesInPoints was on.

Such a file is now written as PDF 1.6 with a UserUnit and a warning that
names the version, as PDF 1.5 files already are. PDF/A-2 files may declare
PDF 1.0 to 1.7. Pages past 32,767 pt that aren't whole numbers of points
are written too, since the refusal applies only to PDF 1.3 and 1.4 files.
Files whose pages are all 14,400 pt or shorter stay PDF 1.4.
2026-11-11 14:43:24 -08:00
dotCooCoo dc98b59444 PDF: the in-points option leaves PDF/A pages within the standard's size limit
longPagesInPoints now applies only to files without a standard. PDF/A-2 limits page
boundaries to 14,400 units (ISO 19005-2, 6.1.13), so a PDF/A file with a page longer than
200 in keeps its UserUnit whether the option is on or off. The Save PDF dialog disables the
checkbox for PDF/A as it does for PDF/X.

Follows #6643.
2026-11-11 13:45:46 -08:00
Dylan M. Taylor da8c37aa8c PDF: note fonts once per walk, place repeated rasters without converting again, check image headers before embedding 2026-11-11 21:06:08 +00:00
BFlat 541f8beae7 Merge pull request #6643 from dotCooCoo/feat/pdf-large-pages-in-points
PDF: an option writes pages longer than 200 in at full size in points
2026-11-12 02:05:54 +07:00
Dylan M. Taylor 44dd908c95 PDF scan: note each font resource dictionary once per page walk 2026-11-11 18:32:07 +00:00
dotCooCoo 3074560b07 PDF: an option writes pages longer than 200 in at full size in points
The Save PDF dialog's General section gets "Write pages longer than 200 in
at full size in points" (longPagesInPoints in document.exportPdf,
document.export, presets and document.pdfSettings), off by default. When it
is on, a page with a side longer than 14,400 pt is written with its media
box in points and no /UserUnit at every PDF version, and a PDF 1.5 file
stays PDF 1.5. The export's warning for such a page then says it is written
in points. With the option off, and for pages of 14,400 pt or less, the
file is the same as before.

Docs: docs/mcp.md, docs/target-app-parity.md. The new label has a row in
every catalog that lists the dialog's other options.
2026-11-11 10:17:24 -08:00
dotCooCoo 5ac7d97d85 Guides: ruler guides are document objects on their layers
Ruler guides were a list kept beside the layers. Each guide is now an
object of its layer or sublayer, always directly in it and above the
layer's art. These behave as on main:

- Guides draw on screen only. Exports (SVG, PDF, PNG, PSD), Fit All,
  Select All, the art bounds, clicks and marquees, and a layer's
  Pathfinder and Transform effects leave them out. The Layers panel
  lists them as guide rows of their layer, the highest guide.list
  index first.
- Deleting a layer deletes its guides, and merging layers keeps their
  guides in the merged layer.
- The guide commands number guides as guide.list does, in the order
  they were made.

New: each guide takes an id from the document's object ids. An object
command given a guide's id fails with an error, and node.move cannot
put a guide into a group or out of its layers. A guide made while an
opacity mask, a symbol or a pattern is edited moves to the top
visible, unlocked layer when editing ends.

File format: native files still save the guides in the document's
guides list, in every file version, so older builds read them. Each
entry may now also carry the guide's id, name, locked and hidden
flags; older builds ignore those fields. Opening a file puts each
saved guide on its layer (on the top visible, unlocked layer when the
file names none or a layer it does not have), and each saved guide
counts against the object limit for opening a file. A ruler guide node
that a file holds below a layer's art moves up to the layer's guides,
and one anywhere else (in a group or another object, beside the
layers, or in a symbol) is removed. Ruler guides read from an .ai file
become guide objects on the layer they were on, numbered in the order
the file lists them.
2026-11-11 09:52:28 -08:00
Taha-Mahmoodi 97ee84744c Merge remote-tracking branch 'origin/main' into fix/import-document-title
# Conflicts:
#	docs/file-format-parity.md
2026-11-11 13:41:49 +04:30
jmvergesandClaude Opus 5.5 df50bbf0f3 Type: tracking and manual kerning take the Horizontal Scale
In horizontal type, tracking and manual kerning are now thousandths of
the em as the Horizontal Scale draws it: at 50%, a tracking of 100 adds
0.05 em after each character. They were added at the full em while the
glyph advances took the scale, so narrow tracked type drifted from the
page of users' .ai files letter by letter. That page writes such type's
character spacing as the tracking itself in a text space as wide as the
scale, which PDF scales by it (ISO 32000-1 9.4.4). Vertical type keeps
the full em: its advances down the column don't all take the scale.

vectorcraft_text::spacing_scale gives the factor. Shaping, inline
graphics and the Optical pairs use it, and so do the conversions from
points back to thousandths of an em: the automatic kerning the Character
panel reads and Touch Type's limits (glyph_em), Combine Point Text, and
PDF import, whose tracking is now the file's Tc. Scale Text Attributes
now keeps tracked type where a horizontal stretch drew it.
SVG export writes letter-spacing in thousandths of the font size it
writes (the vertical scale), so uniformly scaled type keeps its spacing
in a viewer.

Type with both a Horizontal Scale other than 100% and tracking or manual
kerning lays out differently than before, in every document.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 07:16:55 +00:00
Taha-Mahmoodi 92f51896a1 Merge remote-tracking branch 'origin/main' into fix/import-document-title
# Conflicts:
#	docs/file-format-parity.md
#	docs/gaps.md
#	docs/target-app-parity.md
2026-11-11 11:16:43 +04:30
dotCooCoo beef32b8a3 Artboards: an artboard's guide label colors its own guides
Each artboard can carry a guide label: one of the layer colors or a
custom RGB. Guides that belong to an artboard draw in its label color,
and guides without an artboard, or on an artboard without a label, keep
the guide color from the preferences. Selected guides still draw in the
selection color.

- Artboard Options has a Guide Label row (None, the layer colors with
  swatches, or Custom with a color swatch).
- artboard.new and artboard.setProps take guideLabel: null, a layer
  color's name, or #rrggbb. Any other value is an error. The inspect
  summary lists guideLabel when one is set.
- The native format saves the label and skips it when unset; a preset
  index outside the layer colors reads as no label.
- artboard.setProps no longer adds an undo step when nothing changes.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-10 19:37:17 -08:00
BFlat 3cb6f44c9b Merge pull request #6464 from dotCooCoo/fix/issue-6382
Fix #6382: PDF export says how a page longer than 200 inches is written
2026-11-11 04:46:38 +07:00
dotCooCoo a960ba0d08 PDF export: pages that exported before keep exporting
- PDF 1.3 and 1.4 files, PDF/X-1a, PDF/X-3, PDF/X-4 and PDF/A-2b at
  PDF 1.4 among them, write a page longer than 14,400 pt in points
  again, now with a warning that apps that keep to the limit cut it to
  200 in. Only a page that failed with
  "PDF writer error: Limit(TooLargeFloat)" gets the new error: one
  whose size or art needs a real number past 32,767, the most PDF 1.4
  holds. Whole numbers are written as integers, so a page of whole
  points past 32,767 is written as before.
- A page whose side is past 32,767 pt and not a whole number of points
  is refused before the thumbnails are drawn and the transparency is
  flattened.
- A PDF 1.5 file written as PDF 1.6 for a long page keeps PDF 1.5's
  encryption (128-bit RC4), which the Save PDF dialog shows.
- A long page whose short side would be under 3 units at its UserUnit
  (ISO 32000-1, Annex C.2), such as 50 m x 4 mm, gets a smaller
  UserUnit that keeps the short side at 3 units, with a warning.
- The warnings give a page's size rounded up, as the error does.

A page of 14,400 pt or less is written as before. The new warning has
rows in every complete catalog. Docs: docs/mcp.md,
docs/target-app-parity.md and the document.exportPdf params.
2026-11-10 12:31:53 -08:00
BFlat 776e468e89 Merge pull request #6242 from annoybot/fix/pdf-gradient-forms-macos
M4: fix PDF gradient placement in transparency groups
2026-11-10 20:05:34 +07:00
BFlat 67190e6737 Merge pull request #6243 from z3r0pix3l/fix/6072-pdf-arabic-ligatures
PDF import: Arabic ligatures keep their letters in order (#6072)
2026-11-10 20:01:04 +07:00
dotCooCoo 2b7b7503eb Fix #6382: PDF export says how a page longer than 200 inches is written
A page with a side longer than 14,400 pt (200 in, ISO 32000-1 Annex C.2)
keeps its size only with a UserUnit, which PDF 1.6 added. Exports now
report such pages and handle the versions without a UserUnit:

- Each such page adds a warning with its size and its UserUnit. Apps
  that ignore UserUnit show the page that many times smaller.
- A PDF 1.5 file with such a page is written as PDF 1.6, with a warning.
- PDF 1.3 and 1.4 files, PDF/X-1a and PDF/X-3 among them, are refused
  with an error that names the page and the limit, instead of
  "PDF writer error: Limit(TooLargeFloat)".
- PDF/X-4 files at PDF 1.5 or 1.6 keep the page in points, with a
  warning that apps that keep to the limit cut it to 200 in.

The warnings and the error have rows in every complete catalog. Docs:
docs/mcp.md, docs/target-app-parity.md and the document.exportPdf
params.
2026-11-09 21:52:13 -08:00
jmvergesandClaude Opus 5.5 ae9ab1bc06 PDF import: DeviceN images of one ink or of process plates keep their inks (#6348)
An image in a DeviceN color space opened as an RGB image, losing its inks.

One of one colorant now opens as a Separation image does since #6347: a
grayscale image of its tints colorized by its ink. One of two to 32
colorants that are all process plates (the ink None may be among them)
opens as a CMYK image, each colorant's tint in its plate, its Decode array
applied (8 or 16 bits a sample). A DeviceN image that mixes a spot ink
with other colorants, such as a duotone, still opens as RGB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 03:09:42 +00:00
dotCooCoo 35db931810 Fix #6316: a group copies each member in place when a member makes more art
A group with a visible member that carries a geometry, Pathfinder or 3D effect, also in an opacity mask, keeps its previous copies. Copies stop at the budget with what each copy's own effects make, and a group's copies count the art of opacity masks.

The PDF and SVG tests for a group's image copies now expect the original on top.
2026-11-09 16:24:13 -08:00
Ahmed Gomaa 323b3385bc Merge remote-tracking branch 'upstream/main' into fix/pdf-import-actualtext 2026-11-09 22:42:50 +02:00
Ahmed Gomaa 8692b090d0 PDF import tests: ActualText geometry fixtures embed Source Sans 3
Three geometry fixtures used unembedded Helvetica, so the reader fell
back to Source Sans 3 widths where Helvetica is not installed (CI) and
fitted the line length instead of placing glyphs exactly; the following
"C" then landed about 3 pt off its untagged comparison. Embed the
bundled Source Sans 3 with matching explicit widths. Every assertion and
the 0.05 pt tolerance are unchanged.
2026-11-09 20:49:36 +02:00
Ahmed Gomaa 9fda083329 Merge remote-tracking branch 'upstream/main' into fix/pdf-import-actualtext 2026-11-09 20:49:32 +02:00
jmvergesandClaude Opus 5.5 ca59598cd7 PDF import: Separation images keep their ink (#6314)
An image in a Separation color space opened as an RGB image: its spot ink
was lost (no plate in Separations Preview), and Fill and Recolor Artwork
didn't reach its color.

It now opens as a grayscale image of its tints (white where it lays no
ink, black at 100 %, its Decode array applied; 1 to 16 bits a sample, or
one gray channel from its JPEG, CCITT, JBIG2 or JPEG 2000 decoder),
colorized by its ink at 100 %: the spot swatch, a process plate's CMYK
color, or Registration. Its mask becomes its opacity mask, as a CMYK
image's does. The ink is found by the image's color space among those the
file defines, once per image. The ink None, an ink whose color can't be
told, images inside an alpha soft mask, inline images and DeviceN images
open as before; samples that can't be read open in RGB with a warning, and
the ink gets no swatch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 18:39:46 +00:00
Ahmed Gomaa 9ffea06f82 Merge remote-tracking branch 'upstream/main' into fix/pdf-export-mark-text 2026-11-09 19:12:24 +02:00
Ahmed Gomaa f2c4743072 Merge remote-tracking branch 'upstream/main' into fix/pdf-import-actualtext 2026-11-09 19:12:14 +02:00
Ahmed Gomaa 9b797c06d1 PDF export: letters with marks are written once in the text layer
A mark glyph's vertical translation started a new draw_glyphs batch, so
krilla received the cluster's source range again and mapped its
characters twice. Copying or searching the exported PDF then found
doubled letters for Arabic harakat in any font, and for every dotted
letter in fonts that draw dots as zero-advance mark glyphs (Noto Sans,
Naskh and Kufi Arabic).

Keep compatible glyphs on the same layout line in one batch and carry
their relative vertical placement as per-glyph y_offset; horizontal
placement is unchanged (next_x - x advances). Font, size, linear
transform, line/column, paint run, list marker and hyphen boundaries
still split batches. Krilla keeps owning cluster mappings and
ActualText. No importer change.

Regressions decode the content stream and ToUnicode maps: one batch per
layout line, no duplicated cluster assignment, one ActualText per
multi-glyph cluster, contours within 0.05 pt of outline export; mixed
scripts, vertical columns, path rotations and PDF/X variants covered.
The reader round-trip stays an ignored test until the importer consumes
ActualText (#6165).

Addresses #6309.
2026-11-09 18:13:14 +02:00