A drop shadow, inner shadow or outer glow with a large size needed
gigabytes for one render: the multithreaded pipeline's offscreen image
grew by three times the blur on every side, and vello pads its own
filter layer by as much again, on single-threaded contexts too. A blur
wider than 1,024 device pixels is now drawn offscreen at a lower
resolution, where it is that wide, and scaled back up. It looks the
same, and both buffers stay close to the window's size.
Review fix from aiongg (72d7ea2e, with the line tests of 6db40bdb), ported
to this branch: Fill::linear_axis gives screen and PDF one gradient axis,
and a box with no extent along the gradient's angle takes its longer side.
Co-authored-by: aiongg <aiongtaigi@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add multi-click text selection, pending caret formatting and clipboard support, text-aware colour controls, guide/grid snapping, and frame threading with loaded-port interactions.
Include the layout, drop-cap and IDML updates carried from #83, with safeguards for generated and linked stories, external text references, local IDs, locked frames and atomic colour edits.
Fixes#23.
Co-authored-by: Sean Hammond <sean.hammond@und.edu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Placed PDFs rendered through hayro 0.7 from crates.io, whose
hayro-interpret divides integers in calculator functions without a
check, does not limit how deep functions nest and samples each mesh
triangle over its whole bounding box. A placed PDF whose calculator
function runs `1 0 idiv` or `-2147483648 -1 idiv` panicked the render.
A chain of 100,000 functions, each stitching function naming the next,
overflowed the stack and aborted the process. Mesh shadings with points
far off the page, ten thousand overlapping triangles or forty thousand
patches ran for over a minute or passed 2 GiB of memory.
[patch.crates-io] now takes hayro, hayro-interpret, hayro-syntax and
hayro-jbig2 from pdfcraft's vendor/ directory at b5467d93, with the
patches its vendor/README.md lists. The crate versions do not change.
The patched hayro-syntax sizes a page with a /UserUnit in points, and
pdf_page_box scales the page's boxes by the same unit, so with Crop to
the frame still shows the chosen box. NOTICE names the new source and
the licenses. The function and mesh tests in
crates/render/tests/placed_pdf.rs follow pdfcraft's tests in
crates/render/src/raster.rs.
LYH Studio episode 6 builds every gradient from C=0 M=100 Y=0 K=0 to
C=100 M=0 Y=0 K=0. InDesign blends a gradient's process stops as inks:
halfway is C 50 M 50, a light lilac. DesignCraft converted both ends to
RGB and blended there, so the middle came out a darker violet (117, 88,
189 instead of 135, 119, 181), and every Episode 6 gradient looked
different. A midpoint between CMYK stops also became an RGB colour, so
the PDF shading turned RGB with a conversion warning.
- color: `Color::lerp` blends CMYK with CMYK (or grey), and grey with
grey, in ink space; anything with RGB still blends in RGB.
- `Gradient::display_stops`: the expanded stops plus samples through
each span that isn't RGB at both ends, for renderers that interpolate
in RGB; the screen renderer uses it.
- pdf: a midpoint's expanded stop now keeps the stops' space; the
colour-space test expects no conversion for CMYK or grey midpoints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LYH Studio episode 6 (8:28) gives the "1969" headline the "New Gradient
Swatch" (magenta → cyan) from the Swatches panel. InDesign runs a gradient
on text across its text frame's box. DesignCraft resolved a character
fill to one solid colour, so a gradient swatch drew no text at all.
- render: a character fill or stroke that names a gradient swatch is
painted with that gradient over the frame's box in text space (type on
a path: the path's box).
- pdf: text runs take a gradient paint: the shading is laid out in the
frame's text space and mapped into each run's origin, so skew and
horizontal scale don't bend it; outlined runs and type on a path too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A gradient swatch on a stroke drew nothing: strokes were painted with a
single resolved colour. LYH Studio episode 6 gives the loop on the right
page (9:13) and, at first by accident, the circle (1:19, "right now it
is on the stroke") a gradient stroke.
- doc: Stroke::gradient_angle / gradient_vector (as Fill's) and
Stroke::as_fill(), the stroke's paint as a fill.
- render and PDF: a gradient stroke is painted like a gradient fill,
across the path's box (dash gaps and arrowheads keep their paints).
- engine: object.gradient {target: stroke} edits the stroke's
gradient (kind, stops, angle, the Gradient Swatch tool's from/to);
scaling moves the stroke's vector with the shape.
- IDML: GradientStrokeStart/Length/Angle round-trip.
- Gradient panel: acts on the stroke when the toolbox's Stroke box is
in front, as InDesign's follows the Fill/Stroke proxy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
InDesign draws a radial gradient in the object's bounding box: on a
non-square frame it's an ellipse reaching the frame's sides, and a
Gradient Swatch tool drag keeps those proportions. LYH Studio episode 6
(3:16) gives the left page's background a radial gradient: magenta in
the middle, cyan out to every edge. DesignCraft drew a circle reaching
only the longer side, so a portrait page stayed purple at its sides.
- doc: `Fill::radial_ellipse(bounds)`: centre and radii (default: the
box's centre and half its width and height; with a tool vector, its
start, and its length measured with the box made square).
- render: the radial paint is that circle, stretched by the paint
transform; PDF: the shading's transform does the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The two ink passes run over the area the spread's transparent objects
(opacity, blend, effects, knockout, nested or anchored) and their
effects' reach can change, not the whole view; the rest is drawn once,
through the ordinary renderer, clipped out of that area. Objects the
area touches join it whole, so no object is drawn partly through each
colour conversion (no seam). A transparent parent-page item still
takes the whole view.
- The ink passes get the render threads (effects keep their backdrop:
with_blended_filters composites the offscreen filters with the blend).
- The view is kept in the 64 MiB cache only when the ink passes ran.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A frame holding items pasted into it (Edit › Paste Into) was drawn by
its own branch in draw_item that skipped every effect: Basic Feather,
Inner Glow, shadows and the rest were stored but never drawn. LYH Studio
episode 14 (11:51) gives the outlined "GET MORE." title, with a photo
pasted into it, an Inner Glow and a Basic Feather.
Such a frame now goes the way of any other object: draw_body draws its
fill, its items clipped to the frame and its stroke, so draw_item_fx
wraps all of it. Its silhouette counts as solid for shadow spread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Text is composed in the box around a frame's path in the frame's own
coordinates. A sheared then rotated frame turns that box past its path's
bounds, which was all a move repainted, so its text left trails behind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The renderer painted the paper, then blended page items with it: black
text in Color Dodge over blank paper dodged the white to white and
vanished, and other modes showed the same way. In InDesign (and in PDF,
where the page group starts transparent) the paper is not a backdrop:
over blank paper an object in any blend mode shows as it is, and only
the objects below it are blended with. On spreads that use a blend mode
the items are now drawn in their own group, laid over the paper once;
other spreads render as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The DestIn mask now covers the whole render target (and the PDF's
exported area), with the gradient kept in item space through the paint
transform, since past the gradient's ends the end opacity carries on: a
child's long drop shadow or thick stroke is faded, not cut off at a box.
feather_bounds counts only shown children and active states, walking
nested groups without recursion.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A gradient feather on a group (Gradient Feather tool over grouped text, as
in LYH Studio's poster tutorial) did nothing on screen: groups never went
through the effects path. In PDF the group's mask was the group's own path
bounds, and a group has no path, so the mask covered a box of zero size and
hid the group.
- Item::feather_bounds: the box the feather spans, the objects' bounds for
a group.
- Render: a feathered group draws its objects in one layer, then fades it
with the same mask as other objects (fx::gradient_feather_mask).
- PDF: the group's mask spans its objects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Separable blend formulas work channel by channel, so the spread is drawn
twice through the ordinary renderer: C, M and Y complements into RGB, K
into a second raster, with the existing transparency groups, masks,
effects and parent-page placement. The inks are converted to sRGB once and
put over the paper. Two passes regardless of how many objects blend;
parent items, nested blends and effect blends (Multiply satin, shadows)
blend in CMYK too, and the pass covers the whole view, so blending
continues past the trim. An unchanged view is reused from a one-frame
cache (64 MiB cap). Spreads with Hue, Saturation, Color or Luminosity keep
the RGB-then-proof approximation. Effect blend layers sit outside their
clips and offscreen filters, so they see the object's backdrop (RGB too).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A character attribute strokeBehindFill paints a text stroke under the fill
at double width, so its full weight lies outside the letters and they keep
their shape. Screen and PNG rendering, PDF export (stroke as outlines
first, then the filled text, so the text is not duplicated; clipped to the
outside of the glyphs when there is no fill), Create Outlines (outside-
aligned stroke), the Properties panel and Character Style Options.
IDML StrokeAlignment on CharacterStyleRange, CharacterStyle and
ParagraphStyle imports and exports as the attribute (OutsideAlignment =
on, CenterAlignment = off).
The canvas drew an underline (and a strikethrough) as one rectangle per
glyph; where two anti-aliased edges met at a fractional pixel they left
a lighter seam between the letters. Contiguous bars of the same colour
and position are merged into one, as the PDF export already does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parent-page text now uses the absolute page index as its cache key, so repeated page numbers and renumbering do not reuse another page's composition.
Validation: targeted canvas and PDF regressions, plus all six cargo xtask ci gates on main after #514 (1,179 native tests passed, 3 ignored; 15 crates passed WASM compilation checks).
Task: #523
file.place sized a placed PDF page (and SVG) from its size rounded to
whole points, while the export draws the page into that size: a page of
91.774 x 613.276 pt became 92 x 613 and was drawn 0.25 % wider and
0.3 % shorter, so pieces laid edge to edge left gaps of up to 1.9 pt.
A 100.4 x 50.4 pt page placed 100.4 pt wide came out 50.2 pt tall:
`Rect { x0: 0.0, y0: 0.0, x1: 100.4, y1: 50.2 }`.
The graphic's natural size is now the exact page (crop box) or SVG size;
the rounded pixel size stays for display and preflight. place.load (the
place cursor) gets the same exact size.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Set warichu as smaller lines inside the parent line.
Type › Warichu stacks the selected run after the line is broken, closes up the rest of the line, and round-trips the IDML warichu properties. The line break itself still sees the full-size run.
* Keep clicks, the caret, and Up/Down on each warichu row.
The glyphs were already stacked inside the parent line, but hit testing, the caret, and vertical movement still used that parent line. Break minimums, row widths, spacing, and tab leaders now follow the small lines, and underlines sit on the small baseline.
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
text_line flipped glyph outlines that the font database already gives
y-down, so each family's preview in the font menu was drawn upside down
below its baseline and cut off at the bottom of the image.
Font menus list Western (and other-script) families first, then
Japanese, Simplified Chinese, Traditional Chinese and Korean families,
each group alphabetical by the name shown, with a separator between
groups. The group comes from the font's own data, strongest signal
first: the meta table's dlng (else slng) languages, family names in a
CJK language (Windows and Macintosh language IDs), OS/2 code pages (JIS,
Chinese Simplified, Korean Wansung, Chinese Traditional, Korean Johab);
a font declaring none of these goes by what it covers (kana, Hangul,
ideographs). Ties go Japanese, Simplified, Traditional, Korean. Loaded faces are classified
once; installed fonts by the font scan, which also reads their OS/2 and
meta tables (and cmap only for fonts declaring nothing), capped.
CJK families show their family name in their language, the English
name on hover; Preferences > Type > Show Font Names in English
(prefs.set {showFontNamesInEnglish}, off by default) shows English
names. Search matches either name; documents store the English name.
The Character panel, Control bar, Properties, style dialogs, Find Font
and Glyphs panel lists all follow this order. The main font menu stays
open while searching or starring (it closed on any click inside).
Font previews don't draw DesignCraft's missing-glyph box for fonts
whose .notdef is empty.