same_para_style_spacing uses deserialize_explicit_value, so Some(None) round-trips through
serde. Test: SameParaStyleSpacing as a Properties element on the based-on Normal style closes
the gap between consecutive headings; SetIgnore keeps the usual spacing.
Conflict: crates/compose/src/lib.rs: same-style gap replaces the pending space; otherwise next's text_above span space-before rule
Conflict: crates/doc/src/attrs.rs: same_para_style_spacing before the drop-cap fields and their doc
Conflict: crates/compose/src/tests.rs: both sides' tests
Conflict: crates/idml/src/tests.rs: both sides' tests
A paragraph range inside a tracked change takes the change into its character
attributes, so the mark holds for its character ranges however their format is
built.
A column, frame or page break character that tracked changes delete, or a
hidden condition hides, no longer sends the next paragraph on. Deleted
paragraphs between a live break and the next paragraph don't stop the break.
Tests cover a deleted paragraph range inside a change, a deleted frame break in
both nestings, and a live break before a deleted paragraph.
Conflict: crates/idml/src/import.rs: the branch's `over.change` line moved into next's `range_format()`, which takes the enclosing format for it (its CrossReferenceSource caller passes it too); next's `TextVariableInstance` arm kept beside the branch's `Change` arm, with next's ignore list
Conflict: crates/idml/src/export.rs: kept next's metadata, text-variable and CJK settings helpers and the branch's `change_type`
Conflict: crates/idml/src/tests.rs: kept next's `$ID/` font test and the branch's tracked-change test
Conflict: crates/idml/src/import.rs: next's with_style kept after attr_or_style (#555 has no counterpart there); the anchored arm merged as next's header (TextFrame, no has_text guard) with #555's body
Conflict: ROADMAP.md: both progress lines
Conflict: docs/gaps.md: next's header, clipping paths added to its change list
Conflict: docs/target-app-parity.md: next's header, clipping paths added to its change list
Conflict: crates/doc/src/item.rs: both Graphic methods (next's shown_bounds, the branch's clip_region)
Conflict: crates/idml/src/names.rs: both imports (ClippingKind, ContourType)
Conflict: crates/idml/src/export.rs: both pushes, TextWrapPreference first, then the clipping settings
Conflict: crates/idml/src/import.rs: both lets (next's wrap, the branch's clip)
Conflict: crates/engine/src/cmd/file.rs: next's size and transform, plus clip: None
Conflict: crates/engine/src/cmd/datamerge/fill.rs: next's Graphic with wrap, plus clip: None
Conflict: crates/engine/src/cmd/mathexpr.rs: next's Graphic with wrap, plus clip: None
Conflict: crates/pdf/src/tests.rs: both tests
#558 keeps an AppliedFont family exactly as written apart from line breaks; next routes the value through names::font_family_in (for InDesign's $ID/ prefix), which trimmed it. font_family_in strips CR/LF and the $ID/ prefix and keeps spaces; a blank or $ID/-only value is still no font. #558's applied_font_keeps_spaces_and_font_style_nothing_inherits fails without this.
The keep-both resolution of tests.rs in the #558 merge (0ff65578) put #558's test after next's last test without its closing brace, which git had shared between the two sides.
Conflict: ROADMAP.md: both progress lines
Conflict: crates/fonts/src/fontdb.rs: next's style_traits/traits_of/linked_style, canonical_family lookup, family_kind and postscript_face; #558's alternate_names (as written, then trimmed) now chains plain_family ($ID/ prefix and format suffix) and is the fallback in style_traits, face (canonical_family, then substitute_family) and has_family
Conflict: crates/idml/src/import.rs: #558's CR/LF-only stripping (next's f.trim() dropped), next's composite_names lookup (by the trimmed name) and names::font_family_in; #558's FontStyle Nothing → unset
Conflict: crates/idml/src/tests.rs: both tests
A placed graphic keeps its clipping path (Detect Edges, Alpha Channel,
Photoshop Path or User-Modified Path) in graphic space, with the
options IDML carries. IDML import reads ClippingPathSettings and its
saved ClippingPathGeometry (anchors capped, non-finite subpaths
dropped); export writes them back. The canvas and PDF export clip the
image to the path inside the frame clip; an inverted path shows the
image outside it. Without saved geometry nothing changes.
object.clippingPathOptions reports a graphic's clipping path, inverts
it or removes it (Object > Clipping Path > None).
Fixes#450.
Main's squashes of #510, #528, #420, #538 and #356 equal their PR heads; next has
all of them except the two maintainer merge commits a01bfcde (#538, ROADMAP only)
and 139e7c2e (#356: import-warning alert, malformed-URI assertion).
Conflict: ROADMAP.md: next's log lines (main's #538 line already in next)
Conflict: crates/doc/src/assets.rs: next's side (later same_file comment, Asset fields)
Conflict: crates/engine/src/cmd/datamerge/fill.rs: next's side (Asset with pdf_crop)
Conflict: crates/engine/src/cmd/file.rs: next's side (INDD description, Asset ..Default, crop box sizes)
Conflict: crates/engine/src/cmd/interchange.rs: next's side (INDD conversion warnings, resolve_pdf_crops, grep_style_warnings)
Conflict: crates/engine/src/cmd/links.rs: next's side (Asset ..Default)
Conflict: crates/engine/src/cmd/mathexpr.rs: next's side (Asset ..Default)
Conflict: crates/engine/src/cmd/media.rs: next's side (Asset ..Default)
Conflict: crates/engine/src/cmd/style.rs: next's side
Conflict: crates/engine/src/cmd/table.rs: next's side (Asset ..Default)
Conflict: crates/format/src/lib.rs: next's side (pdf_crop fields and tests)
Conflict: crates/idml/src/import.rs: next's side (linked_assets keyed by path, page and crop; CJK settings)
Conflict: crates/idml/src/tests.rs: both (main's file:/a%bé assertion added)
Match IDML part names leniently, report missing parts, and show import warnings when opening a file. Cover the malformed URI crash with a regression test.
Validation: all six cargo xtask ci gates passed on the current main merge candidate; 1,176 native tests passed and all 15 WASM packages checked.
Hosted FreeBSD tests and Windows ARM64 packaging and install/run checks passed.
Task: #356
Co-authored-by: skylartaylor <26588250+skylartaylor@users.noreply.github.com>
Share duplicate file data while keeping distinct link paths independent, and leave unused assets out of saved documents.
Validation: all six cargo xtask ci gates passed on the merge candidate; 1,170 native tests passed and all 15 WASM packages checked. Independent regression checks cover updates, relinking, embedding, undo and save/reopen.
Task: #420
Two linked files with identical bytes were merged into one asset, so updating, relinking or embedding one changed both placements and saves dropped one path. Assets now match only with the same link; identical bytes still share one buffer in memory.
Rotate Spread is the rotation part of the Spread element's ItemTransform.
Import maps the three quarter-turn matrices to view_rotation on the spread's
first page and treats anything else as no rotation; pages and items keep
their unrotated spread coordinates. Export writes the rotation part for
document spreads; parent spreads stay identity.
Conflict: crates/doc/src/page.rs: next's Spread::parent_page_for_side is the one parent-page choice (single-sided pages take the first page); the branch's parent_page_index is dropped and its IDML import caller (crates/idml/src/import.rs) uses parent_page_for_side
Conflict: crates/doc/src/build.rs: the branch's parent_page_of/parent_page/parent_guides/margin sync on next's parent_page_for_side; tests both
Conflict: crates/engine/src/cmd/layout.rs: test modules both
Conflict: crates/engine/src/tests_idml.rs: tests both
Conflict: crates/format/src/lib.rs: next's upgrade and duplicate-asset merge, then the branch's mark_differing_margins_local, in both loaders
Conflict: ROADMAP.md: both log lines
Conflict: docs/target-app-parity.md: next's snapping line plus the branch's parent-page guides
wrap-boundary gives TextWrap a custom path (so it is no longer Copy) and drops it when
object.textWrap sets an offset or contour; next lets object.textWrap edit a placed graphic's
own wrap. The command drops the boundary of the wrap it edits, and next's graphic-wrap IDML
test clones the wrap it compares.
Conflict: crates/compose/src/lib.rs: the custom boundary goes into next's shared wrap_exclusion(); free_slots keeps next's JumpObject handling outside it and takes the branch's combined BoundingBox/Contour arm
Conflict: crates/engine/src/cmd/object.rs: params doc has next's content flag and the branch's custom-boundary note
Conflict: crates/engine/src/tests.rs: both tests
Conflict: crates/idml/src/export.rs: the boundary goes into next's text_wrap_pref()
Conflict: crates/idml/src/import.rs: next's object-style-aware text_wrap() plus the branch's boundary parsing for the item's own wrap; pdf_page_in and wrap_path_of both kept
Conflict: crates/idml/src/tests.rs: both sides' tests
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.
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.
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
Import reads GradientFeatherSetting (Applied, Type, Angle, Length,
GradientStart, first and last OpacityGradientStop) and
DirectionalFeatherSetting (Applied and the four widths) on page items.
Export writes both when the effect is on.
A page item takes every value it lacks from its applied object style and
the styles that one is based on, attribute by attribute: corner shapes and
radii, TransparencySetting (blending, drop shadow, feather), text wrap,
text frame options, the baseline frame grid and frame fitting. Nested
elements merge level by level. Effects whose category the applied style
turns off come from the item alone.
Export writes the column rule attributes on every frame, so a frame keeps
its own rule settings over those of its object style on import.
Fixes#389.
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.