Cells and cell styles had no first-baseline setting, so every cell set its first line by the
Ascent offset (the font's typographic ascender). IDML documents that set FirstBaselineOffset on a
cell style (CapHeight is common, often through a table style's body region) composed every row
taller than the document asked for. With a font whose typographic ascender is a full em (Avenir),
each row grew about 2.6 pt and long tables overset, losing their last rows.
- doc: Cell.first_baseline / first_baseline_min; CellStyle.first_baseline / first_baseline_min,
applied by CellStyle::apply_to and carried through resolve_cell_style's BasedOn chain.
- idml: FirstBaselineOffset and MinimumFirstBaselineOffset read and written on cells and cell styles.
- compose: a cell's text frame uses the cell's first baseline offset and minimum.
- engine: table.setCell, style.cell.create/edit take firstBaseline / firstBaselineMin; table.get
reports them; style.cell.create fromSelection captures them.
- tests: import through BasedOn and a table style's body region, a cell's own override, IDML round
trip; composer cap height vs ascent and a fixed minimum; command parameters and validation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merge compiled, but six tests showed where the two branches meant
different things. Fixes, in order of consequence:
- Format version: both branches had independently defined version 2 —
here for retained EPS sources, on main for pinned Keep Lines Together
and drop cap defaults, with an `upgrade` that runs below 2. Writing 1
for documents without EPS then re-ran that upgrade on load and rewrote
their paragraph styles, so saving and reloading changed the document.
EPS now asks for version 3 and everything else stays at 2.
- place.drop into a frame: main turned Auto-Fit off so that resizing a
frame crops rather than refits, and this branch had carried the older
FillProportionally along while moving the geometry out of the edit.
Main's behaviour is the intended one.
- save() compacts unreferenced assets now, so the format test has to
place its EPS for the version sniff to see it at all.
- EPUB entries are named from the asset's file stem (asset_path), not
its bare id: images/logo-<id>.png.
- Command parameters are validated strictly, so the PDF page test sends
each command only the parameters it takes.
- The relink batch's "commits once" check read a process-wide revision
counter, which a test running beside it can advance; the single undo
step is what actually shows it committed once.
The EPS place tests move to cmd/file/eps_place_tests.rs: they write real
files, and main's file-system guard reads src files without seeing inline
#[cfg(test)] modules, but exempts _tests.rs ones.
Four sites already on main that `cargo xtask ci` cannot get past with the
current toolchain; each rewrite is the one clippy suggests and changes no
behaviour. `!x.is_some_and(f)` becomes `x.is_none_or(!f)`, and pdf_bounds
folds `!(a && b) || !c` into `!(a && b && c)` by De Morgan.
Unrelated to the merge they are committed beside; separated so that merge
stays a merge.
Resolve 12 conflicted files. Where both sides changed the same code:
- format: keep main's bounded-read load_zip (MAX_ENTRIES, read_entry) and
this branch's u64 version reporting, so a version above u32::MAX is named
as written rather than clamped. Both sides had fixed the same truncation.
- render: main's natural_size supersedes image_natural_size (it already
reads an EPS bounding box and also handles PDF pages); callers in table
and data merge now use it.
- images: keep the DSC-aware header scan and (atend) trailer lookup, take
main's classic-Mac line endings, and carry its overflow guard into
parse_box. eps_proxy draws main's simple rectangles before falling back
to the placeholder, still over a retained EPS source.
- engine/file: take main's placement shape (add_asset, crop box geometry,
place cursor, finite_rect, object effects) and keep this branch's
validate_placement in both branches; edit() works on a clone and commits
only on Ok, so validating inside it stays all-or-nothing.
- engine/links: keep building the replacement before edit() so a file that
cannot be read leaves the document untouched, and move main's crop_pdf
refresh into relinked_asset, which both relink and update go through.
- engine/export: EPS preview warnings join the font-embedding warnings
rather than shadowing them.
- idml: an EPS element keeps ActualPpi (its pixels are a preview raster);
main's PDF exclusion is unchanged.
Cargo.lock regenerated. Tests and doc strings from both sides kept.
- panels/pages.rs: #635's parent rows (rendered thumbnails, parents by id, the shown parent selected, double-click shows that parent) sit in #1066's parents scroll area, vertical and side by side; Panel Options' parent size sets the parent icon height (Extra Small: blank pages); thumbnail key = (uid, revision, page or parent, height); pages use ThumbOf::Page; #869/#870/#1058 kept (canvas refit after leaving parents, Delete Page/Spread through the delete-pages question, applyParent by id); #1067's narrow-spread label kept; tests: both sides.
- dock.rs: #343's floating-window frame kept, its body goes through scrolled_body so a floating Pages panel keeps its footer.
- dialogs.rs: deletePages, newWorkspace (#1058 and ours) and pagesPanelOptions arms side by side; HEAD's dialog widths plus pagesPanelOptions at 380.
- lib.rs: skip_delete_pages_warning and pages_parents_height/pages_panel both kept.
- i18n.rs, i18n/{ar,de,it,ja,ka,pt_br,uk}.rs: both sides' rows, no duplicate keys.
- ROADMAP.md: the two new lines in the log's style; dropped four 2026-10-14 lines the union merge duplicated.
The always-on mark uses thread::port_center, the position draw_ports and the Selection tool
use, so rotated and short frames get it on their right edge. Frames whose ports the selection
draws get no second mark. text_frames_in and overset_frames drop the unused transforms.
ROADMAP.md: the union merge re-added 13 log lines already present; kept one copy each. The PR's overset line moved into date order and took the group-ports clause; its other two lines describe work from other PRs and were dropped.
docs/ui-parity.md: merged cleanly next to #1033's row.
Holding Cmd/Ctrl with a non-selection tool borrows whichever of Selection and Direct Selection was chosen last, instead of a fixed tool per tool family. The session records the last chosen selection tool; the busy() guard stays.
tooling.rs: kept #366's IME handling (with_ctx passes the session; a press ends the composition first) and #1002's borrowed tool, which pointer, key, overlay, cursor and wants_text reach through gesture_tool().
file.place, place.drop, placed media and Fixed Width text frame columns refuse a rect that overflows to infinity or NaN. Saving refuses a document with non-finite item geometry before writing anything, so a file that won't load is never written.
crates/ui-egui/src/dialogs.rs: the branch's dialog body is laid out differently; the Color Theme body goes into its match beside Text Frame Options, and its OK label into the branch's translated labels.
crates/ui-egui/src/menus.rs: keep the branch's Effects dialog opener and add the Color Theme opener.
crates/ui-egui/src/panels/swatches.rs: keep both menu rows (Swatch Options… from #416, Color Theme…).
crates/tools/src/tests.rs: the PR's Graphic literal names clip, wrap and fill.
docs/agents.md: keep both sections.
ROADMAP.md: the PR's line moves under "Newest first."; its copy of an existing 2026-10-13 line is dropped.