A PDF/A-2b export at PDF 1.4 with Create PDF Layers and a page longer
than 14,400 pt is written as PDF 1.6, which has PDF layers. The export
decided which layers place their instances' symbol art before it raised
the version, so a hidden layer, written as a PDF layer that is off, kept
its instances' art without the art's live effects.
The version a long page needs is now settled before the document is
baked. The pages come from the artboards, which baking leaves as they
are.
The check before a PNG or JPEG is embedded as it is read the profile's
color space only from a profile of at least 128 bytes, so a gray or RGB
profile cut short after its color space reached the PDF writer. The
color space is now read from bytes 16..20 of any profile that holds
them, and valid_icc rejects the short profile.
In PDF/X-1a, a tagged gray JPEG that the Compression settings keep as
JPEG was decoded and written with Flate. It is now written with its
JPEG data unchanged, in DeviceGray without its profile.
A PNG or JPEG embedded as it is now has its gray or RGB profile checked
with valid_icc, like the images the recoder writes: a malformed profile
is left out, the JPEG keeping its JPEG data and the PNG decoded.
A gray or RGB image is tagged with the ICC profile it carries only when
the profile passes the checks a CMYK image's profile already passes: at
most 16 MiB, the size its header declares, the acsp signature, an input,
display, output or color space profile class, and a tag table that lies
within it. Otherwise the image is written in DeviceGray or DeviceRGB, kept
as it is, resampled or compressed with JPEG.
The PDF/X-1a case of tagged_tiffs_keep_their_icc_profile also exports a
16-bit gray TIFF with a gray profile, kept as it is, resampled and
compressed with JPEG, and checks each written file with the PDF/X-1a
check as well as its image color space.
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.
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.
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.
PDF/X-1a allows device colors only, but a gray TIFF that carried a gray
ICC profile kept it when no color conversion applied, and the export
then failed its PDF/X check. A gray or RGB PNG or JPEG with a profile
went the same way through the PDF writer. In PDF/X-1a, images are now
written without the profile they carry; the other standards and plain
exports keep it.
The image crate's filtered resize filters each channel on its own, which
is correct only for premultiplied pixels, and its thumbnail averages each
channel on its own too. Placed images, linked-image previews, MCP
captures and previews, metafile bitmaps, PDF downsampling and library
thumbnails passed straight RGBA to them, so the color under clear pixels
(usually black) bled into the opaque pixels beside them and darkened
cut-out edges.
vectorcraft_doc::resample scales such images with each pixel's color
weighted by its alpha: filtered resampling premultiplies the pixels in
place and divides them back after, and averaging takes the area-weighted
mean of the pixels each new one covers. Opaque and alpha-free images are
scaled as before. PDF export also multiplies the inks of a CMYK image
with alpha by that alpha before resampling them, and divides them by the
resampled alpha after.
A gradient stop linked to a spot ink that also carries process plates
(a DeviceN stop opened from a PDF) printed only its spot tint: print
separations and Separations Preview left its Cyan, Magenta, Yellow and
Black plates blank. They now print the stop's plates beside the tint.
With spots printed as process, each process plate prints the stop's
plate layered with the spot tint's process equivalent, as a separated
image's spot plates layer. Plates beside no spot link, or beside
Registration, still print nothing of their own.
The color visitor also skipped a freeform gradient's points, so print
plates kept their display colors and Document Color Mode left them
unconverted. The points are now visited as unlinked colors.
A blend between two gradients whose matching stops are tints of one spot
swatch kept the link and interpolated the tint, but every step's stop lost
the process inks (C, M, Y, K) printed with the spot ink, so PDF export wrote
the steps as the spot ink alone.
- Matching stops linked to one swatch interpolate each process ink with the
tint; a stop without process inks counts as none of each, and the ends
keep their own.
- Gradients with different stop counts keep the inks at every resampled
stop, interpolated with the midpoint-adjusted fraction between two stops
of one swatch.
- A solid blended with a gradient brings no process inks of its own: its
stand-in gradient no longer copies the other gradient's.
Stops of different swatches, or linked and unlinked stops, still blend into
unlinked process colors without plates.
A gradient's Separation or DeviceN space, and EPS's %%DocumentCustomColors,
named the spot ink of every linked stop. A stop out of order whose
neighboring stretches have no width, a stop at a tint of 0 without process
plates, and in PDF a stop whose stretches are fully transparent print no
ink, but their spot inks were still declared, so separations got an empty
plate.
- vectorcraft-color: Gradient::drawn_stops (the rule Document Info uses,
moved here) and Gradient::reached_stops (every stop taken as opaque).
- PDF: Separation and DeviceN shadings, the Perceptual spot shadings and
the spot-image warning name only inks of drawn stops at a tint above 0
or with process plates; other stops are written with no ink, so their
process inks and RGB or Lab colors no longer shape the space either.
- EPS: the same with reached stops, since EPS writes stops opaque; a
transparent spot stop keeps its ink.
Output is unchanged when every stop colors part of the gradient and every
spot stop has a tint above 0 or process plates.
A CMYK TIFF written again, a CMYK JPEG that the Compression settings
resample or compress again, and a CMYK image written with its mask as
its soft mask lost the CMYK profile the image embeds. Such an image is
now written in an ICCBased space holding that profile, byte for byte,
when output.profiles is all or taggedSource, and in PDF/A-2b, PDF/X-3
and PDF/X-4 unless profiles is destination. Pixels the Output settings
convert are never tagged with it.
A CMYK JPEG embedded as it is now follows the same rule instead of
always carrying its own ICC_PROFILE segments: with profiles none it is
DeviceCMYK, and PDF/X-1a export no longer refuses a CMYK JPEG that
carries a profile.
A profile tags an image only when it is at most 16 MiB and its declared
size, acsp signature, device class, CMYK color space and tag table are
well formed. Tagged CMYK documents blend transparency in the document's
CMYK profile space, not in an image's own; without that space they
blend in DeviceCMYK, or keep their RGB space in PDF/A.
An instance whose symbol art has live effects is replaced in the export by a group holding the baked art. That group now takes the instance's attributes as well, so a PDF export keeps the instance's URL as a link annotation over the art where its effects draw it.
Hidden instances, instances in hidden groups, on template layers, and
on hidden layers or layers that do not print when the exporter leaves
those out are no longer placed for PDF, print, EPS and DXF. Their art
was baked and counted toward the 16,000,000-object limit for placed
symbol art, so it could leave a visible instance written without its
effects.
bake_document_placing_symbols takes a WrittenLayers value: PDF passes
the layers its settings write, EPS writes layers that do not print but
not hidden ones, and DXF writes both. Print removes the layers that do
not print before baking.
A gray or RGB TIFF that reached PDF export as a TIFF, for example in a
native document or from a script, was decoded to 8-bit RGBA and written
as DeviceRGB, which dropped the ICC profile of its tag 34675, its 16-bit
samples and its gray color space. It is now written in an ICCBased color
space holding that profile, as a tagged PNG is: gray stays gray, 16-bit
samples stay 16-bit from PDF 1.5 on, and the alpha becomes a soft mask
only when some pixel is not opaque.
A gray or RGB image that the Compression settings resample or compress
with JPEG keeps its source profile too, a PNG included. Pixels that the
Output settings convert are written without it, and so is a profile whose
color space differs from the samples'. CMYK images are unchanged.
When PDF, print, EPS and DXF export place a symbol's art to bake its
effects, the art now carries the stain the instance's fill gives it, as
it did before the art was placed. The art is stained first and its
effects apply to the stained colors.
PDF, print, EPS and DXF wrote a symbol instance by placing its symbol's
art as stored, so live effects inside that art were missing: a square
moved 100 pt by a Transform effect stayed in place in the file while the
canvas drew it moved.
bake_document_placing_symbols replaces each instance of a symbol whose
art (or the art of a symbol nested in it) needs baking by that art
placed as the instance places it, then baked. The art is placed before
its effects apply, as on the canvas, so an instance that scales its art
does not scale what its effects do. A dynamic instance's own paint
changes apply before baking. Nesting stops at 32 levels and the placed
art at 16,000,000 objects per export; past them an instance stays an
instance. DXF places 9-slice cells unclipped, as its scene does. The
symbols' own art, SVG and the .ai editing data are unchanged, and HPGL
already baked each placed instance.
PDF export writes a translucent knockout group as a form with its opacity around the /K group. PDF import now reads that pair back as one knockout group with the opacity, blend mode and isolation, instead of a translucent group holding a knockout group.
* PDF import: outlined Indic glyphs keep their own stroke and transparency (#5235)
Follow-up to #6906, for two review findings on how a line kept as
outlines is joined into paths:
- Glyphs were joined when their stroke widths matched; dash, cap, join
and miter limit were ignored, so a dashed glyph took the stroke of the
solid one before it. They join only when the whole stroke matches.
- Translucent glyphs were joined into one fill, where overlapping marks
paint once instead of compositing as the file draws them. Only opaque
glyphs join.
Also records the fix in the PDF row of docs/target-app-parity.md and in
docs/roadmap.md, as AGENTS.md asks.
Tests: outlined_glyphs_stroked_differently_stay_apart and
outlined_translucent_glyphs_stay_apart (both fail without the change).
* PDF import: outlined Indic glyphs under a soft mask stay apart; check kept geometry (#5235)
Review follow-ups on #6957 and #6906:
- Glyphs kept as outlines under a soft mask were joined into one path,
so the mask was applied once to all of them where the file applies it
to each glyph. They now join only when no soft mask is active.
- The fallback tests counted outline nodes only. A new test compares the
kept outlines (element count and bounding box) with the same file
opened as outlines, so a dropped or moved glyph fails it.
Tests: outlined_glyphs_under_a_soft_mask_stay_apart (fails without the
change) and kept_glyphs_are_where_the_file_draws_them.
* PDF import: mask outlined Indic glyphs one by one under a soft mask (#5235)
Review follow-up on #6957. Keeping glyphs out of one shared path was not
enough: each path still went into the soft mask's queue and
flush_masked wrapped them all in one masked group, so a mask that is not
constant was applied once to every glyph, where overlapping glyphs
composite one by one in the file.
With a soft mask active the queued art is now drained before the first
glyph and after each one, so every glyph gets a mask of its own.
Test: outlined_glyphs_under_a_varying_soft_mask_are_masked_one_by_one
checks that each outlined glyph carries its own mask (0 of 3 without
the change).
* #6957: clarify soft-mask exception in glyph grouping docs
---------
Co-authored-by: ruchirguitar <info@jeevantirth.org>
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.
The search for brushed strokes that decides between knockout masks and
a PDF knockout group copied a dynamic symbol's art for every instance
with appearance changes, and looked through the evaluated art of live
objects, without a limit. Nested instances could make it copy and walk
art exponentially before any limit applied.
The search now visits and copies at most MAX_KNOCKOUT_MASK_OBJECTS
objects for a group, counting a dynamic symbol's art before copying it
and a live object's evaluated art before looking through it. When the
limit runs out, the group is written as a PDF knockout group with the
size warning. Each symbol's art with one set of appearance changes is
copied once, and art that holds itself ends the search.
The copies of symbol instances drawn in a knockout group's soft masks
counted toward the objects the export's visible symbol art may hold, so
overlapping instances could use that limit up and leave later visible
instances out. Mask copies now count toward a limit of their own. When a
group's masks would leave out symbol art because that limit runs out,
the masks are discarded and the group is written as a PDF knockout
group, with a warning.
The object count that decides whether a group is drawn through masks
left out the art a brushed stroke expands into. A group in which a mask
would draw a brushed stroke is now written as a PDF knockout group, with
its own warning. The search follows what the export draws: visible
members, opacity masks that are on, symbol and inline graphic art, live
objects and placed documents as evaluated, and pattern tiles, and it
skips strokes without paint, width or a known brush.
A knockout group whose elements need no masks is written as a PDF
knockout group without a warning, so the two engine PDF tests that
expected the old warning now check that case and the brushed one.
With downsampling on, each piece of an image bent by a Warp effect was
resampled for its own placed size, so a warp wrote one image XObject for
each size a piece was placed at (four for a 4 x 4 warp that stretches
across, eight for a colorized one with its soft masks). The pieces are
now resampled for the widest and the highest size any piece places the
image at, and all paint that one image. Pieces off the page count too,
and another placement of the same image keeps its own size.
Without downsampling, a colorized image and a CMYK image with a grid
mask no longer key their cache by placed size, so each is decoded and
encoded once for all the sizes it is placed at, as plain images are.
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.