* Fix Links panel stalls on network paths
* Fix Links review findings
* Fix Links worker gating and wasm build
A link check blocked on another document's unreachable share no longer
holds back the document now shown: after 200 ms a fresh worker takes
over (at most four alive at once) and the stale result is dropped.
relinked_asset used the native-only fingerprint_from, breaking the
wasm build; it now uses fingerprint_of. The EPS relink and IDML link
reach tests build Windows paths correctly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fix Links worker repaint and graphic read retry
Switching documents before the 200 ms takeover now schedules a repaint
for it: a blocked worker never asks for one, so without other input the
panel kept waiting. A failed graphic read is no longer cached as
missing, so the next check reads again. The legacy EPS fixture clears
its source fingerprint, as documents from before fingerprints have
none, so the test no longer depends on running within two seconds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep touched links OK and poll Links within seconds
Only a new size now marks a graphic Modified from its fingerprint. A
file with the same size but a new time (saved again, copied, restored
or checked out) is compared byte for byte, once per new time, and stays
OK when unchanged.
External changes are polled every two seconds instead of every five
minutes. The next check waits at least as long as the last one took
(up to five minutes), so a slow share stays idle half the time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Wake Links when a worker slot frees; translate check strings
A worker thread asks for a repaint when it ends, so a document waiting
at the four-worker limit gets a worker as soon as a superseded one
returns from its blocked call, without other input.
Rows from a check keep a weak reference to their document. The key
names it by address, which a later document can't reuse while the
allocation is kept.
The Links panel's new strings ("Checking", "Checking links…", "Link
status is unavailable.") use the UI's ellipsis and are translated in
every interface language. The comments on Session::text_link_checks and
on the byte comparison in links::status describe what they do now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Links: rows from the document, statuses from a check that can stop
The panel lists links from the document on the UI thread (no file is
looked at) and fills in the statuses a worker check last found, kept
per document. An edit shows at once, and a check of an earlier edit
still supplies statuses instead of being discarded.
- A check of another document is told to stop and ends before its next
file; only one blocked in a file system call is replaced, so blocked
workers no longer hold their slots for a whole document.
- Checks are timed on the worker, and what they remember passes to the
worker that replaces them.
- A fingerprint is kept only for a file whose time was settled when it
was read, as for linked text. A file found unchanged by its bytes
gets its current size and time stored with the document
(remember_link_files), without marking a saved document as changed.
- Asset::set_link keeps link and fingerprint together; copies made by
Package and Copy Links To carry none.
- The Session holds one LinkListChecks. Update Link lists files only
when a linked story was edited in the layout.
- The unused "Checking links..." caption is removed.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
The IDML layer-variant pass and the PDF/A-2b rasterisation pass both walked only
document-spread items, so a choice on a parent-page placement (or an anchored one) was
replaced by the file's defaults on export, and an external-only IDML export succeeded
when it could not carry the choice. Both now cover every placement through
Document::placed_items, and the choice is cleared where the placement actually is.
Regression: a parent-page placement with an explicit all-on choice keeps all layers in
embedded IDML, external-only IDML rejects it, and PDF/A-2b rasterises it.
Graphic literals (cli perf, doc assets/item, engine datamerge/export/file/links/mathexpr/media/table/sample, epub, format, pdf, pptx, render, tools, ui-egui properties, and tests): name the branch's clip and wrap (#561, #381) and the PR's fill.
crates/doc/src/item.rs: Graphic keeps wrap and gains fill.
crates/idml/src/import.rs: read the image swatch, then the branch's styled FrameFittingOption lookup; the graphic keeps its wrap.
crates/idml/src/tests/assets.rs: keep both new tests.
crates/render/src/lib.rs: keep #362's crop-box sizing of placed PDFs and colour the resulting pixmap.
crates/render/tests/colorize.rs, crates/epub/tests/grouped_reading_order.rs, crates/compose/src/tests.rs: literals name the branch's Asset and Graphic fields.
crates/pdf/src/export.rs: the soft mask interpolates by the standard's raster mode (the branch has no per-exporter interpolate switch).
docs/gaps.md, docs/target-app-parity.md: status lines list both sides' changes; gap text keeps the clipping-path items and adds the image swatch.
Conflicts and how each was resolved:
- crates/idml/src/import.rs: main's linked_assets and insert_asset kept; the
crop is read beside main's page (PDFAttribute as a child or a property), and
linked_assets is keyed by (path, page, PdfCrop) so two crops of one page are
two assets.
- crates/idml/src/export.rs: PDFAttribute with PageNumber, PDFCrop and
TransparentBackground is written for every placed PDF, replacing main's
page > 0 block.
- crates/idml/src/tests.rs: main's file plus the `$ID/` font tests.
- crates/engine/src/cmd/file.rs: main's exact PDF page size, place cursor and
add_asset kept; Crop to stores the choice on the asset (the graphic spans
the box), so the place cursor's frame crop box is gone.
- crates/engine/src/cmd/{book,interchange}.rs: main's confined and reporting
imports, with the PDF crops resolved after them.
- crates/engine/src/cmd/print.rs: main's print_with, exporting with the
measured PDF boxes.
- crates/engine/src/cmd/links.rs: main's text links plus pdfCrop.
- crates/engine/src/cmd/{datamerge/fill,mathexpr,media,table}.rs,
crates/format/src/lib.rs: main's add_asset with the asset defaults.
- crates/engine/src/cmd/package.rs, crates/engine/src/tests_idml.rs: both
sides' tests.
- crates/fonts/src/fontdb.rs: main's style traits, looked up through
plain_family.
- crates/render/src/lib.rs: pdf_crop_box replaces pdf_page_box; main's
UserUnit test checks pdf_crop_box.
- crates/ui-egui/src/dialogs.rs: main's dialog with the content-bounds crops.
Also: Asset::same_file compares the PDF crop, so placing or saving no longer
merges two crops of one page; Asset literals added on main take the defaults;
main's PageNumber test checks the crop and that page 1 is written; new tests
a_pdf_page_placed_with_two_crops_imports_as_two_assets and
pdf_assets_with_different_crops_stay_apart.
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
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.
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.
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