Conflicts:
- engine cmd/file.rs, cmd/interchange.rs: kept next's file.open and
file.openIdml docs (content detection, INDD conversion, package parts)
and added "GREP styles whose pattern can't compile" to their warnings.
open_idml keeps next's conversion and import-report warnings and appends
grep_style_warnings after the font warnings.
- engine cmd/style.rs: style.paragraph.create/edit docs combine next's
startAt and kinsoku notes with the grepStyles rule. create_para and
edit_para keep next's set_json_over, continue_numbering and
explicit_default_kinsoku, then call check_grep_styles. The branch's
grep_style_error, check_grep_styles and grep_style_warnings follow
explicit_default_kinsoku. Kept next's delete_tests and
swatch_options_tests; grep_style_tests goes at the end of the file.
- ui-egui dialogs.rs: kept next's style_section_edit tests and added the
branch's bad-GREP-pattern test.
confirm_paragraph_style_options, grep_error and the dialog status line
merged without conflicts; next had not changed the OK arm they replace.
Conflicts in crates/engine/src/cmd/object.rs:
- Helpers: kept next's opt_u64, siblings_mut(d, spread, parent), reorder,
set_layer_deep and set_layer, then added the branch's GroupPlan,
group_plan, GROUP_SAME_LEVEL and can_group. The branch's
siblings_mut(d, &GroupPlan) is dropped; group() calls next's helper as
siblings_mut(d, plan.spread, &plan.parent).
- Tests: kept next's reorder_tests and the other test modules next added,
followed by the branch's group_tests module.
group() and ungroup() were unchanged on next, so group() is the branch's
version. cmd/mod.rs (CANT_GROUP_SELECTION, selection_reason) and lib.rs
(execute_unguarded) merged without conflicts.
Once style.paragraph.create (or edit with a rename) succeeds, the dialog
switches to editing that style, so when export tagging then fails the
dialog stays open with the error and a second OK edits the style instead
of creating another.
style.paragraph.create, style.paragraph.edit and type.para return the
compile error (bad GREP: ...) for a GREP style pattern that can't compile
and change nothing. Paragraph Style Options shows the error under the
pattern as it is typed, and on OK keeps the dialog open on the GREP Style
section with the command's error.
Opening an IDML or native document keeps such a style and reports a
warning naming the style (or paragraph) and the pattern; composition
keeps skipping it. Adds a lookbehind GREP style test.
Grouping objects that were not top-level items of the first object's
spread panicked (index out of bounds), through object.group, Group on a
content selection, and states.create. Objects nested at different levels
or on different spreads were silently left out of the new group.
Objects that share a spread and a parent group now group into a nested
group in that parent, keeping their stacking order and position. Objects
on different spreads or at different levels return an error and the
document is unchanged, as do objects inside a multi-state object. Group
is disabled in the menu for a selection it can't group; a call that
names its ids still runs whatever is selected.
Fixes#412
PDF export replaces objects with soft effects by images of their
appearance. The image item carried no text wrap, so text recomposed
for the export ran under the object. Each rasterized object with a
wrap now stays in the export copy as a nonprinting stand-in that keeps
the wrap of the frame, its placed graphic and its grouped items, with
its text, form field, button and media removed.
A font's em box is its BASE em box; else it tops at the OS/2 typo
ascender when the typo ascender and descender span exactly one em; else
it is centred on half the cap height (OS/2 sCapHeight, else the H
glyph's top), 0.5 em + cap / 2 above the baseline; else 0.88 em. The Em
Box Height first baseline, vertical text and declared_em_box all use
this one em box; declared_em_box is None only for the 0.88 em fallback.
A font of a pan-CJK family lists the code pages of every language it
covers. With no `meta` languages and no CJK family name, the first group
in menu order won, so a Simplified Chinese face such as "… Noto Sans CJK
SC" was listed under Japanese.
When a font lists several CJK code pages, the region tag in its family
name (JP; SC, CN, GB; TC, HK, TW, MO, HC; KR — a whole word in capitals)
now decides, if the font lists that language. Fonts with one CJK code
page, or none, are grouped as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
Cut, Duplicate, Paste in Place, the Arrange commands and pasting text
run a second command inside themselves, and both recorded an undo
step: the second Undo did nothing. Only the outer command records now.
A command that fails after changing the document records its step
too; a script that stopped at a bad step left changes that could not
be undone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts:
- crates/fonts/src/fontdb.rs, vertical.rs: next's em cache
((top, bottom), declared) with FontFace::declared_em_box and the ICF
cache/icf_box kept; the branch's base_em_box accessor dropped (same
value). em_box's fallback chain takes the branch's let em form.
- crates/compose/src/lib.rs: next's cjk_icf_box and the branch's
alignment_em_box, which reads declared_em_box.
- crates/compose/src/tests.rs: both tests. next applies character
alignment only under the Japanese composers (5dc8383a), so the branch's
smaller_cjk_run_aligns_to_the_line_em_box_centre runs under japanese();
next's cjk_em_center_alignment_moves_small_characters measures centres
on the alignment em box (declared, else 0.88/-0.12 em).
Conflicts:
- crates/pdf/src/text.rs run_text: the branch's two generated_text
branches first, then #392's kashida branch (no text), then the cmap
fallback for other inserted glyphs.
- crates/compose/src/lib.rs: next's kashida PlacedGlyph (with dx) gets
generated_text: None.
- crates/compose/src/shape.rs hyphen_after: next's rendered_char/moji
resets and generated_text = None.
- crates/compose/src/ruby.rs: next's ruby module (#363); the branch's
edit to the old ruby set() has nothing to apply to.
- crates/pdf/src/tests.rs: tests from both.
Also, so the merge compiles: next's ruby (ruby.rs annotate) and kenten
(kenten.rs shape_mark) PlacedGlyph literals take generated_text: None;
they map back through the font like other inserted glyphs. Kanji page
numbers, composite-font note numbers and fallback-face label segments
go through shape_segment's replacement and list labels through
prepend_label, so they carry their generated text.
next's shatai path draws a slanted glyph on its own through tagged_run,
which opens a Span; inside #392's right-to-left line span that nested
marked content and krilla panicked ("can't start marked content twice")
in tagged exports. While a line span is open, the glyph is drawn with
run like the line's other runs. Test:
shatai_glyphs_in_right_to_left_lines_stay_in_the_line_span (panicked
before).
Asset::same_file compares pdf_crop (#362) with type, page and bytes: one
PDF page shown with two crop boxes keeps two assets, so add_asset,
adopt_asset, insert_asset (IDML/INDD import) and the load/save merge
never point a placement at another crop. Test in
identical_files_share_one_asset.
The copy clipping (edit.rs clip_doc) keeps the assets used_assets finds
in it, so graphics anchored in copied text, graphic table cells (#379),
notes and media posters keep their files; it collected only graphics on
the copied items before. Test:
copied_text_frames_keep_the_graphics_anchored_in_their_text.
Conflicts:
- crates/engine/src/cmd/{datamerge/fill.rs, file.rs, mathexpr.rs,
media.rs, table.rs}: the branch's add_asset calls, with next's Asset
fields (pdf_crop, pdf_box) through ..Default::default(); file.place
keeps next's PdfCrop-aware asset (crop_pdf) and adds it with add_asset;
the data merge graphic keeps next's wrap field.
- crates/format/src/lib.rs: load runs next's upgrade(doc, version), then
merge_duplicate_assets (zip and legacy JSON); tests from both, with the
round-trip test placing its asset (saves drop unused ones).
- crates/idml/src/import.rs graphic(): the branch's linked_assets reuse and
insert_asset, with next's PDF page and PdfCrop on the asset and the
graphic's text wrap (#389 integration) in the shared graphic closure.
linked_assets is keyed by (path, page, crop), so placements of one PDF
showing different pages or crop boxes keep separate assets.
Also, so the merge compiles: Asset/Graphic literals in the branch's tests
(crates/doc/src/assets.rs, format, engine links.rs) take next's pdf_crop,
pdf_box and wrap fields.
The merge of idml-feathers left overlay() in crates/idml/src/import.rs and
imports_and_round_trips_text_wrap_side_and_contour_type in tests.rs
without their closing braces (both sides added code at the same place);
this closes them.
Test: feathers_come_from_the_object_style_unless_its_category_turns_them_off
(gradient and directional feathers come from the style through
with_style; EnableGradientFeather="false" in
ObjectStyleObjectEffectsCategorySettings drops the style's gradient
feather; the item's own Applied="false" wins).
Conflicts:
- crates/idml/src/import.rs, free functions: next's effect_flag/overlay
(#389) and the branch's finite/gradient_feather_in both kept. The
feather reads sit inside the TransparencySetting block, which next reads
through with_style, so object style feathers are inherited and their
EnableGradientFeather/EnableDirectionalFeather category flags apply.
- crates/idml/src/tests.rs: tests from both.
A graphic element that names an AppliedObjectStyle reads its
TextWrapPreference through with_style, so mode, side, offsets and
ContourOption come from the style where the graphic sets none. Test:
text_wrap_side_and_contour_come_from_the_style_for_frames_and_placed_graphics.
Also: swatch_ref's doc comment sits on swatch_ref again.
Conflicts:
- crates/idml/src/import.rs, style chain helper: next's object_style_chain
(trims BasedOn, ends with [None], cycle-safe) kept; the branch's
style_chain dropped. with_style and attr_or_style use object_style_chain.
- import.rs corners: next's corner import over the object style chain kept
(per-corner then all-corners at each level, InDesign default radius);
the branch's second corner pass dropped, so corners are read once.
- import.rs text wrap: with_style(e, "TextWrapPreference") feeds next's
shared text_wrap() reader (side, ContourOption, offsets) instead of the
branch's inline TextWrap build.
- import.rs TextFramePreference: next's chain-aware text_frame_options
with column rule colour kept; BaselineFrameGridOption read through
with_style.
- crates/idml/src/tests.rs: test modules from both.
- release.yml: new linux-riscv64 job (cross-compiled on ubuntu-24.04 against
ports.ubuntu.com riscv64 libs, tar.gz only, CLI smoke-tested under QEMU),
ported from GridCraft; the draft release job needs it, so SHA256SUMS covers it.
- packaging/linux/package.sh: CROSS_ARCH/CROSS_TARGET/CROSS_COMPILE/EMULATOR
support, as in GridCraft.
- cargo xtask version set 0.6.0; README, hardware parity and ROADMAP log note
the new platform.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- install_fonts no longer waits for the system font catalog on the first
frame: the built-in faces are installed at once, the scan runs in the
background, and the installed CJK faces are added when it is done.
- The installed faces are read once and shared: changing the interface
language reorders them without copying the font files into egui again.
- fonts: line_metrics no longer sits between feature's doc comment and
feature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
install_fonts caches system_ui_fonts in a OnceLock; the face bytes are
leaked once and borrowed (Cow::Borrowed) by each FontData, so a language
change reorders the faces without re-reading font files or copying them.
SystemFont.bytes is a Cow<'static, [u8]>.
Also: line_metrics no longer sits between feature's doc comment and
feature (crates/fonts), and the canvas texture test's frame closure
drops an unneeded mut.
Lines with right-to-left text are wrapped in a marked span whose
ActualText is the line's text in reading order, so viewers that order
text by position (Preview, PDFKit) no longer return it reversed.
A base letter and its marks (Hebrew points) map glyph by glyph when
each glyph stands for one of the cluster's characters, and are drawn in
text order, so ToUnicode-only extraction reads each letter once with
its points after it. Marks stay in their run, placed by glyph offsets
(compensated for skew), instead of starting runs of their own.
Kashidas inserted by justification carry no text.
Fixes#392.
Conflict: crates/ui-egui/src/theme.rs install_fonts: the PR's
system_ui_fonts/add_system_fallbacks replace next's #126 system-font loop
(fallback_for per CJK sample char into system-ui-{id} faces when
CRAFT_FONTS is empty), so installed faces are not loaded twice. No #126
helper in theme.rs is left unused; FontDb::fallback_for stays (compose,
images use it).
Placing a file that the document already holds reuses its asset (same
bytes, type and PDF page), in file.place, place.load, media and poster
placement, graphic table cells, math, data merge image fields, IDML
import (a linked file is read once) and paste/duplicate between
documents (which also maps ids that collide in the target).
Saving merges identical assets into the first and leaves out assets
nothing references; the open document keeps them for undo. Opening a
file saved with one copy per placement shares the identical bytes as
they are read and merges the assets. Package and Copy Links write only
used files, and links.list lists only used assets.
Refs #391
patch_texture writes full-resolution dirty rects with set_partial; when
upload() has shrunk the canvas texture to max_texture_side (a viewport
wider than the GPU limit), the edit takes a full render instead of a
partial write past the texture's bounds (a wgpu validation error). Test:
edits_patch_the_canvas_only_at_full_resolution.
The content ghost proxy goes through widgets::fit_texture (capped at 4096)
instead of skipping graphics larger than the limit.
patch_texture computed the damaged region in view pixels and wrote it
with set_partial into the canvas texture upload() had sampled down to
the GPU limit: a viewport wider than max_texture_side put the region in
the wrong place or outside the texture (a wgpu validation error on the
first edit). Such a texture is now rendered whole.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
Conflict: crates/ui-egui/src/canvas.rs draw_tool_overlays: the PR's
Overlay::Path arm (draw_path_overlay) and next's Overlay::ContentGhost
arm both kept; draw_path_overlay sits before draw_content_ghost.