Finding a placed PDF's Bounding Box crop rendered its page at 2048
pixels and took the bounds of the painted pixels. PDF export measures
every such crop that import left unmeasured, so exporting a 17-page
document that places one complex illustration took about 4 s longer
than before crops were supported. The box is now the union of the
page's painted paths (strokes included), glyphs and images, each cut
to its clip, collected by interpreting the page without rasterizing
it: the same box, exact instead of to the nearest pixel, in
milliseconds.
Test: content_crop_box_is_measured_without_rendering_the_page.
IDML import found every Bounding Box crop by rendering the PDF page at
2048 pixels, once per placement, so opening a 17-page document that
places one Illustrator file 16 times took about 54 s of CPU. Import now
measures the box only for a graphic without GraphicBounds; drawing finds
it through designcraft_render::shown_box (cached per file, page and crop)
and PDF export through with_pdf_boxes.
- table.setCell starts each chosen edge from the stroke rendering draws there (the winning
copy of a shared edge, the row/column pattern, or the table border) and writes the result
to both copies; table.getCellStroke reports drawn edges, counting each shared edge once.
compose::table::cell_side_strokes shares the resolution with the renderer.
- Commands accept dash patterns of 1-64 lengths, each 0 or 0.1-10000 pt. Screen and PDF
draw a dash or dot pattern that would repeat more than 20000 times along a path as solid.
- Stripes, wavy, hash and gap colour extend by half the weight at table corners.
- Cell Options: an edge change reseeds every stroke field and tolerates a non-object seed.
- Edge proxy: a triple-click decides from the choice before the first click.
Conflicts: compose lib.rs imports (both; Composer unused), idml tests.rs (both test modules), ui-egui
panels/mod.rs (next's ruler origin with the branch's graphic wrap for Direct Selection).
A placed graphic carries its own text wrap (Graphic.wrap). IDML import
reads TextWrapPreference on the Image/PDF element and export writes it
back there. Layout treats it as an obstacle around the part of the
graphic its frame shows, plus the offsets, and also collects wraps of
items inside groups. object.textWrap acts on the graphic when the
selection is the frame's content, and the Text Wrap panel shows its wrap.
Conflicts: ui-egui dialogs.rs (next's numberingSection arm and the branch's cellOptions arm; the branch's
copies of the dialog test helpers already on next dropped, its cell options test kept); idml tests.rs and
the i18n tables appended at the end on both sides (both kept; the branch's Gap Tint: and Tint: rows that
next already had dropped).
CellStroke and cell/table style edge attributes carry gap colour and gap tint
(IDML ...EdgeStrokeGapColor/GapTint, ...BorderStrokeGapColor/GapTint). The table
renderer and PDF export draw every stroke type on cell edges, with the gap colour
under dashes, dots and stripes.
table.setCell validates stroke values, takes gapColor/gapTint and an edges list
(top/left/bottom/right are the target range's outer sides; innerHorizontal and
innerVertical the lines between its rows and columns). table.getCellStroke reports
the shown values of the chosen edges, null where they differ.
Conflict: ROADMAP.md: next's FONT1-3 lines with the branch's CJK1 line
Conflict: docs/cjk.md: the branch's rows, with next's aki below and Japanese composer text kept in the leading model row
Conflict: crates/fonts/src/fontdb.rs: both fields (family/style aliases and the ICF cache)
Conflict: crates/compose/src/ruby.rs: the branch's ruby module; its PlacedGlyph (and kenten.rs's) carry next's rendered_char
Conflict: crates/compose/src/lib.rs: modules both; rtl and para_block both; the branch's grid placement (gyoudori, grid references) replaces next's snap, its paragraph-gyoudori span uses next's line_metrics with mark leading and #330's aki below advance; a wrap push moves the line by whole grid lines and its grid end with it; next's trimmed spaces and drop cap, then the branch's ruby and kenten annotation
Conflict: crates/compose/src/shape.rs: next's pieces loop and StyleRuns with the branch's auto tate-chu-yoko split (after marks join their base) and fit, and Upright mode in every shape_segment call; next's label fallback passes the mode on
Conflict: crates/compose/src/tests.rs: both sets of tests appended at the end
Conflict: crates/idml/src/export.rs: next's metadata and text variable writers, then the branch's CJK settings writers
Conflict: crates/idml/src/tests.rs: both tests
Conflict: crates/ui-egui/src/dialogs.rs: the branch's Ruby dialog default section (next's duplicate characterStyleOptions default dropped); next's section arms with the branch's style_cjk::section_ui
Conflict: crates/compose/src/lib.rs: imports from both (ListAlign, ListCounter; ParaBox beside next's hit_note_with); the branch's list labels (ListCounter, named_labels, label_chars, label alignment, Tab Position stop) with next's list_label marking from prepend_label's count; the branch's align_label before next's warichu placement, trimmed spaces and drop cap; next's rule_color for rules above and below in the branch's Deco literals; the branch's split block end (ends_split, span space after) in place of next's two identical copies; next's drop cap helpers, then the branch's label_chars and align_label, then prepend_label
Conflict: crates/compose/src/tests.rs: both sides' tests
Conflict: crates/idml/src/export.rs: next's para_attrs parent name argument with the branch's with_list_tab (its parent attributes renamed parent_props) for styles and paragraph ranges
Conflict: crates/idml/src/import.rs: next's INDESIGN_CORNER_SIZE and tint doc; the branch's corners() placed before tint's doc comment
Conflict: crates/idml/src/tests.rs: both sides' tests
Conflict: crates/ui-egui/src/dialogs.rs: next's deleteParagraphStyle and the branch's paragraphLocal confirm arms
Conflict: crates/ui-egui/src/panels/glyphs.rs: the branch's grid() split, with next's scroll bar room in its column count
Conflict: ROADMAP.md: next's IDML interchange line with the branch's sentence on placed PDFs, $ID/ font names and packaged links appended; next's INDD line kept
Conflict: crates/engine/src/tests_idml.rs: next's drop cap test and the branch's two placed PDF tests
Conflict: crates/fonts/src/fontdb.rs: next's plausible/measured_top/typo_ascent and the branch's plain_family; next's canonical_family lookups, with plain_family in place of without_format_suffix in style_traits, face (two) and has_family; the branch's has_exact_family dropped (has_family uses canonical_family)
Conflict: crates/idml/src/import.rs: next's char_attrs (tab-split family and composite font names), falling back to names::font_family_in
Borders gain a stroke type (built-in or a document stroke style), cap,
join, gap colour and tint, a shape and size per corner, column or text
width, top and bottom edges, closed boxes in each part of a split
paragraph, and one box for consecutive paragraphs with the same
settings. Shading gains the same corners, edges and width, Clip to
Frame, and Do not Print or Export. Boxes are drawn once the lines are in
place, as filled paths where they aren't plain rectangles.
Span and split paragraphs keep at least their space before and after
from the column text. Lists have levels that number on their own and
restart after a higher level, ^1 to ^9 in the number text, a character
style for the label, a bullet font, left, centre or right alignment at
the line start, and a tab position for the text after the label.
All attributes load from older files with their defaults and
round-trip through IDML, apart from the tab position.
The placed PDF's asset stores its Crop to choice (crop, art, trim, bleed,
media, or the content bounds with visible or all layers) and where that
box sits on the page. The graphic spans the box: the screen, PDF export,
rasterised exports and clipping-path tracing draw only that part of the
page, so a larger frame doesn't reveal the bleed.
IDML export writes the choice as PDFCrop, with GraphicBounds spanning the
box. Import reads PDFCrop back; the engine finds the box on the PDF page
after import (and after relink or update) and maps it onto GraphicBounds,
or gives a graphic without bounds the box's size. Documents saved without
a choice load with the crop box. The Place PDF dialog offers the content
bounds too, and links.list reports the choice.
Upstream main moved 9e4f694 -> 736acff by 10 squashes: #258, #285, #153, #280, #288, #281,
#176, #299, #298, #297. New to next: #258 (datamerge test), #153 (temporary files and release
inputs), #280 (warichu), #176 (PDF image conversion cache), #297 (graphics backend before the
window). Already on next: #285, #288, #281, #299, #298.
Conflict: ROADMAP.md: next's lines; CJK1 takes main's wording (ruby unfinished, warichu set after the line is broken)
Conflict: crates/compose/src/lib.rs: list labels (#298): next's list_label structure; paragraph start / break moves: next's `go` (with #295's sub-columns) under the maintainer's #285 comment; Cursor: next's start/next_page/next_baseline with tops; line placement: #280's warichu::place right after layout_line, before next's trimmed trailing spaces and the drop cap splice (so neither is stacked and the spaces follow the closed-up end_x); caret(): next's drop-cap branch first, then #280's stacked-row metrics
Conflict: crates/compose/src/tests.rs: next's tests kept; #280's tests auto-merged
Conflict: crates/engine/src/cmd/text.rs: both (next's TypingFormat/typing_format, #280's warichu_cmd)
Conflict: crates/idml/src/tests.rs: next's side; #280's warichu round-trip test auto-merged
Conflict: crates/pdf/src/export.rs: #176's content cache and test counter with next's RasterMode (no Exporter `interpolate` field on next, so the maintainer's `mode.interpolate &= self.interpolate` is not carried); test exporter gets next's drawing_items field
Conflict: crates/ui-egui/src/canvas.rs: #280's compose::highlight_quads (its RTL branch equals next's mixed-direction spans) followed by next's drop-cap highlight
Conflict: crates/ui-egui/src/menus.rs: next's formatting_affects_text test kept
Auto-merge duplicates removed: #288's table-style tests and #298's list test in idml tests.rs,
#288's tests in doc styles.rs and #285's docx test (both files equal next's), #281's dialog
tests in dialogs.rs (equals next's) and its menu_button/menu_max_height/clipped_submenu in
menus.rs. contributors/people.toml takes main's (next had a duplicated
[people.emanuelebonaventura] table from the 9e4f694 merge, which broke the ui-egui build
script's TOML parse).
Maintainer edits carried over: #285 break characters and Next Column through one closure
(next's `go`, comment added); #281 viewport-aware menu_button in the app bar and the Paragraph
menu, per-dialog widths clamped to the window (all already on next); #280 warichu after the
last-line rules (layout_line with ends_para/forced_mid) and rules at each warichu row's
baseline (rule_baseline in render and pdf text_rules); #176 CMYK fallback through
lossless(.., mode) and its updated contract tests; #258 test fixes; #153 and #297 as landed.
* 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>
#230 splits LineGlyphs::decos into underlines and strikes (underlines paint before the
glyphs, strikethroughs after). #177's in-flight owner test still read `decos`, so the
render tests did not compile once both were merged. The test's composition has an
underline, so it checks `underlines`.
Conflict: crates/compose/src/shape.rs: inline object on the baseline: #124's rule (its box alone sets the extents, not max with the font's) applied to every metric #83 tracks (ascent, typo_ascent, cap, xh); the above-line arm keeps #83's four fields
Conflict: crates/compose/src/tests.rs: tests only; kept both
Conflict: crates/engine/src/tests.rs: tests only; kept both
Conflict: crates/idml/src/tests.rs: tests only; kept both
Conflict: crates/idml/src/import.rs: kept #230's Underline/StrikeThru colour line (Text Color resets) and #83's tint() helper
Conflict: ROADMAP.md: kept the chain's Text engine line and added #230's FID2 line
Resolve forward story references before importing embedded frames and groups.
Compose embedded stories, retain anonymous-story objects for screen and PDF
rendering, and invalidate layout caches when embedded frame geometry changes.
Measure inline objects by geometry, fill open contours, reject cyclic frame
references, and distinguish unplaced stories from located overset text.
Add synthetic round-trip, composition, preflight, pixel-rendering, and PDF
regressions. Update the roadmap with the supported import and rendering paths.
#83's Keep Lines Together tests change expectation: with the last baseline
allowed on the bottom inset, the first frame holds one more line, so the
10-line paragraph splits 7/3 instead of 6/4 (the keep rule is unchanged).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vertical Type is now the story's direction: the full-descent check reads
FrameSpec::vertical, and the vertical-extent test sets the story vertical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve conflicts with the CJK/Arabic composition work: line metrics keep
the first-baseline tops and add the CJK alignment shift; the CJK line
reference adjustment applies before baseline-grid snapping; layout_line
takes the bidi info and the drop cap is spliced in after it; vertical text
is now the story's direction (FrameSpec::vertical); IDML insets use main's
validated inset_spacing over the object style chain; format's JSON upgrade
(vertical frames) and model upgrade (format v2 defaults) both run.
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.
The fonts in a document's Document Fonts folder are registered under a
font scope that the document holds while it is open (Document::font_scope,
runtime only; DocState keeps the scope alive and closes it when the last
copy of the state goes: file.close, closing a tab, revert, a book
chapter's export, the session ending). Composition, preflight, font.list,
Package and the font menus, Glyphs panel and previews look fonts up
through FontDb::scoped(scope): the document's own faces first, then the
shared ones. Other open documents don't see them, and two documents with
fonts of the same name each set text in their own. Composite fonts'
member fonts and the fallback for kenten marks are looked up the same
way.
Font files stay in memory for the session (FaceRef keeps faces for the
process); a document opened again reuses the faces of its unchanged files
(same path, length and modification time) instead of reading them again.
The composition cache's signature includes the scope; the word cache and
the outline and PDF font caches are keyed by face. Variable font
instances are kept per base face.
A family's installed faces join the shared fonts all at once, and a
face two lookups load at the same time is kept once, so a parallel
fallback lookup never takes a family's bold before its regular has
loaded.
Vertical is a story property: every frame of a thread is vertical or
horizontal together, a frame threaded onto a story takes its direction,
and frames split off by threading or breaking a thread keep theirs.
Type > Story Direction is type.storyDirection {vertical} (checked in the
menu) for the stories of the text selection or selected frames;
object.textFrameOptions {vertical} and frame.create {vertical} set the
frames' stories. Documents that stored Vertical on the frame open with
each story taking its first frame's direction. IDML StoryOrientation is
read into and written from the story, beside its column direction
(StoryDirection), which stays a separate setting.
A frame takes the corner options and Text Frame Options it doesn't write
from its applied object style, the styles that one is based on, then
[None]: IDML frames write only what differs from their style. Each
corner's shape and radius resolve separately, item first; at each level
a per-corner attribute wins over the legacy CornerOption / CornerRadius
pair, so a frame that writes one corner keeps the style's other corners,
and a shape without a radius takes the chain's (12 pt at the root). Fill
and stroke lookups end at [None] too.
Checked, unchanged: corner radii are drawn clamped to half the shorter
side as circular arcs, and inside-aligned strokes are the stroke of the
path inset by half the weight (screen and PDF); tests pin both.
Rasters stay below vello_cpu's u16 limits (a 65535-pixel-wide render
panicked inside it), page renders larger than 2^29 pixels decline instead of
allocating, PNG encoding never panics, and the PDF heading tag can't fail.
Both crates deny the panicking lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>