Conflict: crates/doc/src/edit.rs: next's thread_frames (append_story); every rev += 1 is rev.wrapping_add(1)
Conflict: crates/engine/src/cmd/fonts.rs: next's replace (the local-formatting pass moved out of this hunk)
Conflict: crates/engine/src/cmd/text.rs: next's format_chars (apply_char_attrs, typing format); rev += 1 is rev.wrapping_add(1)
Conflict: crates/compose/src/tests.rs: next's baseline-fit and keep tests, then the branch's OS/2 offset doc and code-page constant
Conflict: crates/fonts/src/tests_vertical.rs: testing imports from both (with_base, with_code_pages)
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.
A font's em box is its BASE em box; else, for a CJK font (its font
menu group isn't Western: meta or name languages, CJK code pages, else
kana, Hangul or ideograph coverage), the OS/2 typo ascender when the
typo ascender and descender span exactly one em; else 0.5 em + cap
height / 2; else 0.88 em. Latin fonts with a one-em typo box, such as
DejaVu Serif, centre on the cap height.
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.
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.
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.
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
Conflict resolutions:
- crates/engine/src/cmd/layout.rs: next's start_number, chapter_numbering and
layer.move validation kept; the branch's set_layer added after them;
spread_rotation_input_tests and layer_options_tests both kept.
- crates/engine/src/cmd/style.rs: next's delete_tests and the branch's
swatch_options_tests kept as separate modules.
- crates/ui-egui/src/dialogs.rs: both module declarations (style_cjk,
options) and both confirm arms (deleteCharacterStyle, swatchOptions /
colorGroupOptions / layerSettings) kept.
- crates/ui-egui/src/i18n.rs, i18n/{ar,it,ja,pt_br,uk}.rs: both sides' rows
appended; no key was duplicated.
- crates/ui-egui/src/panels/layers.rs: #378's drag-and-drop row
(Sense::click_and_drag, deferred cmds, payloads) kept; the branch's
accessibility name, double-click -> app.layerOptionsDialog (after the
eye/lock/triangle/selection-square branches, before activate) and the
Layer Options for "<name>"... context entry added through cmds.
- crates/ui-egui/src/panels/swatches.rs: next's apply_swatch kept, with the
branch's single-click-only guard so a double-click opens Swatch Options.
Integration:
- Document::rename_swatch_refs also renames table cell stroke gap colours
(CellStroke and CellStrokeAttrs gap_color), kenten and ruby fill/stroke
colours (CharAttrs kenten_fill, kenten_stroke, ruby_fill, ruby_stroke)
and paragraph border gap colours (ParaAttrs border_gap_color).
- The swatch rename test covers a table's cell gap colour, kenten fill,
ruby stroke and border gap colour.
- The Layer Options double-click test checks that the double-click does
not reorder layers.
- German rows for the 29 new shared keys in i18n/de.rs.
- swatch.options: rename, Name with Color Value, process/spot, colour mode
and values, tint, in one undo step; special swatches keep their names,
and a taken or empty name changes nothing.
- Document::rename_swatch_refs points every use at a renamed swatch:
tints, colour groups, inks, paragraph/character/object/cell/table
styles, item fills, strokes, gaps, effects and column rules, text
colours, rules, shading, borders, table cells and the footnote rule.
- layer.set takes every Layer field (show guides, suppress wrap when
hidden), a layer colour name, and rejects empty or duplicate names and
unknown colours.
- Swatches panel: double-click or Swatch Options… opens the dialog;
colour groups get Color Group Options…. Layers panel: double-click or
Layer Options for "name"… opens Layer Options (app.layerOptionsDialog).
- 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.
Combines contour-wrap (wrap side, contour type, multi-segment lines) with
the placed-graphic and nested wraps already on next.
- crates/compose/src/lib.rs: exclusion() and the new graphic_exclusion()
share wrap_exclusion() (offsets, spine sides resolved per page, contour).
wrap_obstacles() builds every obstacle through them: the item's own wrap,
the graphic's wrap around its shown bounds, and children of groups and
pasted-into frames with their own transform (depth <= 64). Contour type
Bounding Box follows the shown part of the graphic, other types the frame
path. wrap_slot() returns all free segments (free_slots) and steps 1 pt
at a time beside a contour; Jump Object stays in wrap_slot only. The line
loop keeps grid gyoudori, Jump Object leading steps and grid snapping for
a line's first segment, and carries the other segments (with the line's
grid end) to the next lines; estimate_slots() lists every segment through
wrap_slot(). hit_line() breaks ties between segments by x, used by hit()
next to the drop-cap lookup.
- crates/compose/src/tests.rs: both sides' tests; the hostile-geometry
test builds Exclusion with side and contour.
- crates/engine/src/cmd/object.rs: object.textWrap is text_wrap() with
mode/side/contour (unknown values are errors) and content?, applied to
the frame's or the graphic's wrap.
- crates/idml/src/import.rs, export.rs: ContourOption/@ContourType is read
in the shared text_wrap() and written by text_wrap_pref() as a child of
TextWrapPreference after Properties, so graphic wraps carry it too.
- crates/idml/src/tests.rs: both sides' tests. tests/wrap.rs gains a test
of a contour, right-side wrap on a placed graphic shown in part.
- crates/ui-egui/src/panels/mod.rs: the Text Wrap panel's side and contour
combos read the targeted wrap (tw), the graphic's when content is selected.
Lines beside an object that lets text through on both sides are set in
segments, left then right, and the paragraph runs on from one to the
next. One-sided wraps (left, right, spine sides, largest area) block the
other side. A contour wrap follows the object's outline band by band,
with the offset around it; graphic frames follow their contour type
(bounding box of the graphic, frame path, same as clipping; detected
edges, alpha channel and image paths follow the frame path). The Text
Wrap panel sets the wrap side and contour type through object.textWrap,
and IDML import and export keep ContourOption.
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 right-to-left bound spread lists its right page first, parent spreads
included. The parent page shown behind a document page was picked by list
index (first = left), so every page of an imported right-to-left document
showed the other side's parent items. Pick it by position instead: the
leftmost parent page for a left page, the rightmost for a right page.
New parent spreads in a right-to-left document list their pages right
first, and changing the binding reorders existing parent spreads, so IDML
export writes them in binding order. Spread bleed on the canvas and in
spread PDF export takes the outer edges by position as well.
Kanji numbering style (一, 二, 三, …, written digit by digit: 10 is 一〇)
for sections, footnotes, endnotes and lists. IDML PageNumberStyle and
FootnoteNumberingStyle read and write Kanji and the leading-zero styles;
the list format "一, 二, 三, 四..." maps to it.
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: fonts fontdb.rs (the branch's em box with its from-BASE flag beside next's ICF cache); idml
tests.rs (the branch's copies of tests already on next, and the mojikumi test #129 replaced, dropped;
the Em Box Height test kept). German entry for Em Box Height.
The first baseline sits the em box top below the frame's top inset: the
font's BASE em box (idtp, else ideo + 1 em), else 0.88 em, for the
largest size on the first line. IDML FirstBaselineOffset="EmboxHeight"
imports and exports as itself; the footnote options list it and the
frame and footnote option commands take emboxHeight.
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
Drop cap style through IDML as a Dropcap nested style, spaces after the cap, one-line caps, nested styles after the cap, own nested-style lists clearing an inherited drop cap style (cells and notes too), cleared styles round-tripping, tabs in right-to-left drop caps.
Conflict: ROADMAP.md: next's INDD, Interface language and Show CJK Features lines (the PR's main-merge resolution, already on next)
Conflict: crates/compose/src/lib.rs: next's warichu placement followed by the trimmed trailing spaces
Conflict: crates/compose/src/tests.rs: next's side; the PR's side repeats helpers and tests next already has
Conflict: crates/idml/src/import.rs: next's range_format
Conflict: crates/idml/src/tests.rs: next's side, with the PR's drop_caps_import_and_round_trip and its new drop_cap_fixture and nested-style clearing tests in place of next's older drop cap test
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.
IDML has no drop cap style attribute. Import reads the nested style list's leading "through 1 Dropcap" entry as the drop cap style; export writes the drop cap style as that entry. The drop cap style counts as that entry when composing, so nested styles after it start after the drop cap.
Preferences > Units & Increments > Origin sets DocSettings.rulerOrigin
(document.preferences {rulerOrigin: spread|page|spine}, IDML
RulerOrigin). Rulers and the X/Y fields (Control, Properties, Info,
transform.set) measure from that origin plus the zero point; with a page
origin the horizontal ruler restarts on each page and an object measures
from the page it is on.
Cmd-drag (Ctrl) from the ruler corner adds a horizontal and a vertical
spread guide through the drop point. The live preview while dragging the
zero point is kept on the pasteboard like the committed value.
Translations for Origin, Spine and Lock/Unlock Zero Point.
Conflict: ROADMAP.md: this branch's Text engine, IDML interchange and Right-to-left lines; main's FID2, PDF export and Interface language lines
Conflict: crates/compose/src/lib.rs: list labels as main's number_label/bullet_label/empty-paragraph arms, kept in this branch's list_label form that marks label glyphs; main's layout_line call (ends_para, forced_mid) and warichu::place, then the drop cap splice; rule above uses main's first-line column/end_x span with this branch's rule_color; caret(): drop-cap branch, then main's stacked-row metrics
Conflict: crates/compose/src/tests.rs: both sides' tests
Conflict: crates/engine/src/cmd/text.rs: type.para doc lists the drop cap fields and main's rule fields
Conflict: crates/fonts/src/fontdb.rs: both (plausible/measured_top/typo_ascent and without_format_suffix)
Conflict: crates/idml/src/import.rs: main's table style regions, borders and alternating fill/stroke attrs (this branch's alt_fills helper dropped, unused); underline/strikethrough colour from main with this branch's tint(); frame_options takes the object-style chain's TextFramePreference elements, the frame's column rule colour resolved after it
Conflict: crates/idml/src/tests.rs: main's tests, plus this branch's added tests and helpers
Conflict: crates/ui-egui/src/canvas.rs: main's compose::highlight_quads, then this branch's drop-cap highlight
The zero point is document state (DocSettings.zeroPoint, zeroPointLocked):
an offset from the top-left corner of each spread's pages, so every
spread measures from the same relative place and split views share it.
view.zeroPoint {at, reset} and view.lockZeroPoint {on} set it; moving it
is not an undo step and undo/redo keep it. Values are kept on the
pasteboard, from commands, files and IDML.
Rulers and the X/Y fields (Control, Properties, Info, transform.set)
measure from it. IDML reads and writes Document ZeroPoint.
Dragging from the ruler corner places it where released, with crosshair
lines while dragging; a click that stays in the corner does nothing; a
double-click resets it; the corner's context menu locks it.