chore: sync upstream (206 Commits) #18

Merged
alf merged 206 commits from chore/sync-upstream-20261104 into main 2026-11-04 08:25:38 +00:00
Owner

Upstream-Sync von upstream/main (206 Commits). Fast-Forward.

  • a4017a3c Fonts: bound named instances per file, index axis settings in style strings (#2215)
  • 5309d7d8 Merge pull request #2291 from aiongg/integration-designcraft-20261104-1
  • d30c2b1e Merge pull request #2271
  • c3ea3178 Merge pull request #2250
  • b1f67ee4 Merge pull request #2211
  • 6c2ab93b Merge pull request #2231
  • b46ee479 pdf: placements of one media asset share one embedded file (#2266)
  • a2cc32a8 Apply review fixes
  • 3326d919 Apply review fixes
  • e2c22660 warichu: split long runs in one pass instead of trying every length (#2184)
  • 7faf7029 fix(pdf): indexed membership in page ranges, optional content and PDF/X scans (#2207)
  • 36da81e0 fix(textimport): bound xlsx data sources and make docx and hyphenation parsing linear (#2213)
  • 64c956f0 fix(render): index line style groups, stop futile glyph-cache eviction, read PDF page sizes once (#2217)
  • c4ed9b22 fix: write screenshots, signing certificates and benchmark files in private temp folders (#2219)
  • bcc74f29 fix(app): log message line breaks continue the record instead of starting another (#2220)
Upstream-Sync von `upstream/main` (206 Commits). Fast-Forward. - a4017a3c Fonts: bound named instances per file, index axis settings in style strings (#2215) - 5309d7d8 Merge pull request #2291 from aiongg/integration-designcraft-20261104-1 - d30c2b1e Merge pull request #2271 - c3ea3178 Merge pull request #2250 - b1f67ee4 Merge pull request #2211 - 6c2ab93b Merge pull request #2231 - b46ee479 pdf: placements of one media asset share one embedded file (#2266) - a2cc32a8 Apply review fixes - 3326d919 Apply review fixes - e2c22660 warichu: split long runs in one pass instead of trying every length (#2184) - 7faf7029 fix(pdf): indexed membership in page ranges, optional content and PDF/X scans (#2207) - 36da81e0 fix(textimport): bound xlsx data sources and make docx and hyphenation parsing linear (#2213) - 64c956f0 fix(render): index line style groups, stop futile glyph-cache eviction, read PDF page sizes once (#2217) - c4ed9b22 fix: write screenshots, signing certificates and benchmark files in private temp folders (#2219) - bcc74f29 fix(app): log message line breaks continue the record instead of starting another (#2220)
alf added 206 commits 2026-11-04 08:25:36 +00:00
Opening brackets together, and closing brackets, commas and full stops
together, now touch (JLREQ 3.1.4): 」。 and 「『 no longer keep half an
em between them. A closing bracket and an opening one stay half an em
apart, and other neighbours keep the larger blank, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
The Chinese sets keep the larger blank between two marks, as before:
JLREQ's rule is Japanese, and Chinese styles (Taiwan's centred, full-width
marks among them) set consecutive punctuation their own way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
The status table still said mojikumi tables were kept but not applied,
which #129 changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A four-cornered empty graphic frame draws its X from corner to corner, so a
frame slanted with Direct Selection keeps the X on its corners, as InDesign
draws it. Other shapes still span their bounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The corner-to-corner X is for rectangles (slanted or not): four corner points without handles,
on a Rectangle or Path item. An ellipse frame (four smooth anchors) and a four-sided polygon (a
diamond) would otherwise get a plus through their centre; they keep the X across their bounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	ROADMAP.md
Extend the existing MCP tools instead of adding a second set (#259):

- every tool states destructiveHint and idempotentHint next to
  readOnlyHint/openWorldHint; input schemas are closed
- the #776 unknown-key check walks the schema, so a misspelt key in a
  batch step, a pointer event or mods is -32602 with its path instead
  of being ignored
- script::parse refuses JSON step keys other than command/params
- a panic outside a command (rendering, inspection, the app connection)
  is a tool error and the server keeps running

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	ROADMAP.md
# Conflicts:
#	ROADMAP.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	ROADMAP.md
The panel showed and read them in points while the Effects dialog uses the
document's unit, so a bare 7 typed in the dialog of a picas document was
7 picas (84 pt) and showed as 84 pt in the panel. The panel now uses the
same unit as the dialog, and reads any unit typed (3mm, 5pt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
Integrate updated DesignCraft notices, dependencies and package scripts while keeping Adwaita's MIT notice.
# Conflicts:
#	crates/tools/src/snap.rs
# Conflicts:
#	ROADMAP.md
# Conflicts:
#	ROADMAP.md
# Conflicts:
#	crates/ui-egui/src/panels/properties.rs
# Conflicts:
#	crates/ui-egui/src/widgets.rs
The Control panel's Font Size, Leading and Baseline Shift and Character •••
Baseline Shift still used NumField::number with " pt", which read 13Q as
13 pt. NumField::number now makes a points field for the " pt" suffix, so
no point field can miss it; with_suffix is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013phQ3ekq6uGBEWD6FjFYG2
Moving a point makes a polygon a path (as the engine does for any
point edit), and four handle-free corners took the rectangle's branch:
a diamond's corners crossed as a plus. A path's corners are used only
when both diagonals cross as an X; a test edits a four-sided polygon
with path.moveAnchors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/doc/src/vars.rs:~186 (parse_iso_date, the zone '+'/'-' branch: `z.get(0..2)?.parse::<i64>()` and the minutes parse)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/doc/src/vars.rs:178 (parse_iso_date, `let (h, mi) = (tnum(0, 2)?, tnum(3, 5)?);`)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
`indd` was the one git dependency taken from a branch:

    indd = { git = "https://github.com/storytold/indd-utils", branch = "main" }

`Cargo.lock` pinned a commit, but any `cargo update` -- and `.github/dependabot.yml`
runs them -- pulls whatever is on that branch into a parser of untrusted INDD
files, with no review of what changed. The craft-libs dependencies beside it are
all `rev = "b926b5c0..."`.

Pinned to `7f3060e5bce78d21ea70e0d5eacb96edce15dfbe`, which is the commit the
lock already held: the only change in `Cargo.lock` is the source string,
`?branch=main` to `?rev=7f3060e5...`, so no code moves with this.

Verified: `cargo check --locked -p designcraft-engine` passes, and
`indd_files_go_through_the_converter_and_its_errors_are_open_errors` still
passes, so the converter is the same one.

Audit DC-18.
# Conflicts:
#	crates/ui-egui/src/panels/properties.rs
#	crates/ui-egui/src/widgets.rs
# Conflicts:
#	crates/ui-egui/src/panels/properties.rs
- An option took the next token whatever it was, so `--export --scale` wrote a
  file called `--scale` in the working directory. A token that cannot be a
  value is refused, and where the value is a path the message offers the
  shell's own way out (`./-name`). A negative number can still be a value, and
  `-inf`/`-nan` still reach the check that reports them.
- An argument that is not UTF-8 panicked inside `std::env::args`. It is a
  usage error now, named, since such a path cannot reach the engine anyway.
- `<sub> --help` was read as an unknown option by every parser but `script`'s.
  It answers from the usage block, on stdout, and nothing else happens.

Rebased onto the post-split main, where the fix had to grow one part. Upstream
has since added a check that an export option sits before its `--export`, and
it consumed the following token whatever it was, so `--scale --export out.png`
was reported as the *order* of the options -- advice to move an option that is
already where it belongs -- when what is wrong is that `--scale` has no value.
That check now skips a value only where the next token can be one, and stands
aside entirely when a value is missing so the value error speaks. Its own test,
`run_export_options_come_before_their_export`, still passes.

The CLI's tests moved to tests/it/cli.rs, so the test additions went there.
A page-based export leaves out the text its frames cannot fit, and said
nothing about it: an imported 58-page form lost whole sentences -- a
data-consent clause, a statutory rule, the last row of two tables -- with
`warnings` empty while preflight reported 19 overset stories.

The PDF and deck exports now report it, in the words preflight uses and with
the same count, naming the page and the frame. A reflowable export carries the
whole story, so it has nothing to report.

Carries the review fix: a spread export puts the whole spread on the sheet, so
choosing page 2 of a facing-pages document exports page 3 as well and page 3's
overset is as absent from the file as page 2's would be. The selection is
widened to the spreads it touches first. A page on another spread is still not
reported, so the filter keeps doing what it is for.

Rebased onto the post-split main: the PDF command is in export/pdf.rs, the
deck one in export/formats.rs, and preflight.rs is unchanged by the split. The
helper sits before export_pdf rather than between first_page_ref's doc comment
and its function, which is where the pre-split patch had put it.
Copying a text frame cloned its story but left the objects anchored in
it with the original's item ids. Save does not validate, load runs
Document::check and rejects duplicate ids, so a duplicated or pasted
frame with an inline graphic produced a file that failed to open with
"not a DesignCraft document: duplicate item id".

renumber (Duplicate, Alt-drag copy, Paste, Paste into text, parent item
overrides) and the data merge copy now give every anchored item in the
copied story, its table cells, footnotes and endnotes a fresh id.
Anchored text frames from IDML get their own story copy. Assets are not
adopted again: the story copy already remapped them.

The IDML export tests that relied on duplication to create shared ids
now set the shared id explicitly, so the export guard stays covered.
Files already saved with duplicate ids still do not open.
Found while testing crates/geom/src/stroke_style.rs:24-31 (offset_path: `(MoveTo(p), 0, true) => out.line_to(p)`; also samples/wavy at :58-98)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review caught a consequence of the previous commit. Standing the order check
aside so a missing value could be reported meant the value was reported
later -- after the exports before it had already been written. `run --export
first.png --scale --export second.png` wrote first.png and only then failed,
so an invocation that fails could overwrite a file that was already there.

`values_present` checks every option that takes a value before the order
check and before any export, so the line is refused as a whole. The test
writes a file at the export path first and asserts its bytes are unchanged.
The warnings composed every story once with no page. For a frame on a parent
page that is wrong twice over, and I measured both:

    exportPdf {"pages": [2]}
      ["the story in frame 9: 76 character(s) do not fit and are not in the
        export"]

Page 2 used no parent at all, so nothing of that frame was in its export to be
missing -- and the warning named no page, because a parent frame has no
document page of its own. It now resolves the pages that draw the parent page
and reports only those:

    {"pages": [1]}  ["page 1: 76 character(s) ... frame 9 ..."]
    {"pages": [2]}  []

The second half is page-dependent text. A parent frame 112 pt wide holding
"Continued on page <marker>" fits while the number is one digit and does not
once it is two, so composing it once could not tell:

    {"pages": [1]}   []
    {"pages": [9]}   []
    {"pages": [10]}  ["page 10: 1 character(s) ..."]
    {}               ["pages 10, 11, 12: 1 character(s) ..."]

Each page that shows the parent is composed as that page, and the pages are
grouped by how much they lose, so a story still gets one line.

Also from review: `story.get` now says `overset` is a UTF-8 byte offset into
`text`, not a character index. A test measures the difference on French text,
where the offset runs ahead of the character count.
# Conflicts:
#	crates/ui-egui/src/canvas.rs
# Conflicts:
#	crates/tools/src/snap.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
designcraft-cli thumbnail a.designcraft a.designcraft replaced the
document with PNG bytes. The output is now refused before anything is
read or written when it is the input by the same path, through a
symlink, or on Unix as a hard link (same device and inode).
Found while testing crates/engine/src/script.rs:50-54 (parse, compact `command {json}` branch)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Keep native ZIP, legacy JSON and IDML bytes out of the INDD converter even when a browser supplies a misleading .indd or .indt name. Preserve signature and unknown-data routing to the converter. Check that every browser delivery adds a document before asserting its title.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Merge origin/main at a288a87b. Preserve the font.list progress entry and the content-first routing note. The feature diff remains the same four reviewed files.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Fixes #1950

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/datamerge/parse.rs:212 (parse_record_no, used by select_range)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A Cover Image field with a Choose… button (PNG or JPEG) in the EPUB Export Options dialog; left empty, the first page stays the cover. The three labels are translated; a dialogs test exports with a chosen file and checks the EPUB's cover image and manifest.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`<sub> --help` answered about that subcommand without first asking whether the
subcommand was one, so `designcraft-cli typo --help` printed the whole usage and
succeeded. `typo` on its own was an error and `typo --help` was not, which is
backwards: a script checking the exit status carried straight past a misspelled
command (noted in review).

`is_subcommand` reads the names out of `USAGE` rather than listing them again,
so one added to the usage text cannot be missing here, and `usage_for` now uses
the same line parser instead of its own copy. An unknown name falls through to
the dispatch and fails there, as it already did without `--help`.

Measured over every name the usage lists -- run, commands, describe, script,
app, mcp, perf, bench, validate-pdf, links -- each still answers on stdout and
exits 0, while `typo` and `nonsense` exit 1. The test asserts both halves, so
the check cannot quietly become a block on subcommand help.
tempfile::tempdir() in place of a PID-named folder, cleaned up on a panic too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`validate_pdf` accepts `--standard x4|x1a`, and both its own usage string and the
module docs say so, but the line in the global `USAGE` left it off -- and that
line is exactly what `validate-pdf --help` slices out. So the option was
documented everywhere except the one place a user would look (noted in review).

`subcommand_help_names_the_options_that_subcommand_takes` asserts it, checks the
parser and the help agree by refusing an unknown standard, and does the same for
`run --cmd` and `mcp --automation-read-root` so this is about subcommand help
rather than one line.
Found while testing crates/geom/src/path.rs:~335 (PathData::bounds)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A sheet draws its spread's items without asking which page each one sits on, so
a frame straddling two pages is drawn, in part, on both. The warning chose the
page from the frame's centre alone (`page_at_x(bounds.center().x)`), so a frame
centred on a page the export leaves out had its warning dropped while the part
on an exported page went into the file (noted in review).

`pages_of` gives every page of the spread the frame's bounds overlap, and the
story is kept when any of them is exported.

The page named is one the export has: the centre page when that is exported, so
an ordinary frame reads exactly as before, otherwise the first exported page the
frame reaches. A frame wholly on the pasteboard overlaps no page's span and
falls back to `page_of`, so the two cannot disagree about where such a frame is.

`overset_location` returns the reached pages beside the centre page, which is
what made it worth a named type.

The test exports a frame centred on page 3 and reaching back into page 2, four
ways: the whole document, page 3 alone, page 2 alone -- which used to report
nothing -- and page 4, which the frame never reaches and which still reports
nothing.
Fixes #2116

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A centred dialog window offers its body only last frame's height, and the Color Picker lays itself out on a fixed 566 x 334 canvas, so it opened clipped to its top strip behind a scrollbar: the colour field cut off, no value fields, no hex code, OK and Cancel visible but blind, and the window not resizable. The body's minimum scrolled height now covers the picker's canvas, as it already did for Numbering & Section Options.

Verified locally on a288a87b: fmt, clippy, the picker's own tests; opened on macOS at full height.
The picker opened with R selected every time, so anyone who works in H (the hue slider with the saturation/brightness field) chose it again on every open. The component now lives in the UI preferences (colorPickerComponent, default r, so existing preference files and openers that name a component behave as before) and the picker reads it on open and writes it when a radio button is clicked, as Photoshop's picker keeps its last mode.

Verified locally on a288a87b: fmt, clippy, the picker's tests; tried on macOS.
# Conflicts:
#	ROADMAP.md
Fix parent page resizing in panels and Page Tool
Restore GNOME Wayland window controls without bundled fonts
save writes every asset into the archive with no size limit, but load refused any asset entry over 1 GiB and any archive expanding past 4 GiB, so a document with large placed files saved normally and then couldn't be reopened. The caps guard against archives whose claimed sizes lie; a stored entry can't lie about its size, since it is its bytes in the archive, already in memory, and all stored entries together can't exceed the archive. A stored entry (every placed PNG, JPEG, GIF and WebP, which save stores as is) now opens at its own size and counts for nothing against the budget; deflated entries (PDF, EPS, SVG) and the JSON keep their caps, so a zip bomb is still refused. The limits are a struct the test can shrink.

Verified locally on a288a87b: fmt, clippy, format tests including stored_assets_open_at_any_size_while_deflated_ones_stay_capped.
The composer treated Indent to Here as an invisible zero-width character and nothing else, so inserting it changed nothing. A line holding one now sets the start-side indent of the paragraph's following lines to the character's position, and those lines are broken again with the narrower measure. The indent holds into the next columns and frames; a later Indent to Here in the paragraph replaces it.
Fixes #2173

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #2175

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
links.list decided a placed graphic's status by reading the whole file and comparing it with the asset's bytes whenever the sizes matched, and the Links panel polls it twice a second, so a document of press-size images read every one of them in full on every poll. Image links now have the size-and-time fast path linked text files already had: a file of the remembered size and time isn't read, a new time is read once and remembered (unless the file changed in the last two seconds), a different size is modified without a read. The answer is keyed by the asset's bytes' identity, so a relink, update or undo can never be served a stale one; status() stays as the exact check for callers that want it.

Verified locally on a288a87b: fmt, clippy, engine link tests including the new unchanged_linked_images_are_not_read_again.
The lines after an Indent to Here start at the character, which already sits past the drop cap, but the lines beside the drop cap still added its width on top. They no longer do. Adds tests for the drop cap, for Indent to Here after a tab (the usual hanging number) and for a right-to-left paragraph, and documents how the indent behaves beside a text wrap.
The character was looked up among the line's body glyphs, so one inside
the drop cap was missed, and one in a list number's ^i matched the first
body glyph (after the label's tab) instead of itself. Find it among the
placed glyphs, which include the drop cap's and the label's.
The drop cap is set at the indent and the list number beside it, so the
number's character is the later one on the line and sets the indent.
Pin that with a test, and say so where the character is looked up.
A fontFamily the app doesn't have was applied with a null result, so a
script that asked for Courier Prime exported the PDF in the substitute
and exited 0. The font is still applied, since documents can carry
missing fonts and Find Font and preflight list them, but type.char now
returns {"warnings": ["Missing font: Courier Prime (using Source Sans 3)"]},
or "Missing font style: ..." for a style the family doesn't have, in
preflight's words. Other calls still return null.
The script command threw away the result of each export, so a PDF with
a drop shadow came out with nothing on stderr, while run --export
printed "warning: 1 object(s) with soft effects ... exported as images".
Each warning an export returns now goes to stderr as "warning: ...".
The JSON report on stdout is unchanged.
A relative link names another file once the document is saved elsewhere,
so a status remembered for the old file must not answer for the new one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
type.char: warn when the font family or style isn't available (#2189)
Space dotted paragraph border dots evenly
Keep style export tags and classes on IDML import and export
Text: Indent to Here indents the paragraph's later lines to the character (#2080)
Color Picker: reopens on the component it was left on
Dialogs: the Color Picker opens at its full height (#2129)
Links: an unchanged linked image is not read again on every poll (#2164)
Keep the graphics backend when the window closes before its first frame
Include single-anchor subpaths in PathData::bounds
M3: a Custom text variable defined with a number shows its digits (#2093)
Reject signed record numbers in data merge record ranges
File import: keep native and IDML bytes out of the INDD converter
Keep stroke styles from joining the subpaths of a compound path
Pin the indd dependency by rev, as every other git dependency is (#1599)
Linux: file managers show a .designcraft file's first page through a freedesktop thumbnailer (#1782)
An export says what text its pages had no room for (#1465)
# Conflicts:
#	docs/ui-parity.md
M4: the Pen previews the segment a dragged smooth point shapes and curves the rubber band after one (#1438)
M6: EPUB Export Options names a cover image file (#1934)
The warning from #2190 looked only at the attributes passed. A composite
font made by style.compositeFont.set isn't an installed family, so
applying one warned "Missing font: Mixed" even when its entries were
installed. And a fontStyle-only call, or a family change that kept a
style the new family lacks, said nothing.

After a call that sets or clears fontFamily or fontStyle, type.char now
tallies the fonts the formatted text resolves to, with the code
font.list uses (composite entries per character), and warns about the
missing ones and substitute styles. At a caret it checks the font the
next typing gets. tally_story becomes tally_range over a byte range,
and describe_fonts is the part of used_fonts that type.char reuses.
Integration batch integration-designcraft-20261103-3
# Conflicts:
#	apps/designcraft-cli/src/main.rs
#	apps/designcraft-cli/tests/it/cli.rs
- Footnote references on a line are deduplicated with a set instead of
  searching the references collected so far.
- Nested styles stream the paragraph up to their delimiter with one character
  of lookahead instead of copying the rest of the paragraph for each style,
  and a delimiter's characters are indexed once per style.
- Shaping resolves which nested/GREP style is on top with one sweep over the
  overlay boundaries instead of scanning every overlay for each stretch.
- Column rules group crossing lines by paragraph through an index.
- Table fragments index each row's top once instead of searching the rows
  for every cell.
A cached story copied every wrap neighbour's whole signature and item list into
its entry; stories on one spread share its wrapping items, so entries grew with
all of theirs together. A neighbour's signature now goes in as its hash and its
items are kept once each.

Anchored-frame placement asked for the host's composition once per frame, and
again at every level a host was reached from. Each host is now asked for once
per placement pass, and the cache and the uncached path remember the hosts they
resolved for the whole traversal.
- crates/format/src/lib.rs
* hyphenation: linear heuristic, cache misses outside the lock

The syllable heuristic built each word suffix as a String to test it, which is
quadratic in the word's length; it now uses prefix sums of vowels and UTF-8
lengths. Mapping a word's break points to glyphs scanned the word for each
point; both are indexed once per word. A word not in the hyphenation cache is
worked out without holding the cache's global mutex.

* hyphenation: compare the word ending with ends_with
- crates/engine/src/cmd/file/open.rs
Reject ISO timestamps without ':' between hour and minute
IDML: Q ruler units are Q, as InDesign writes them
- crates/engine/src/cmd/file/open.rs
- crates/format/src/lib.rs
- crates/doc/src/vars.rs
Integration batch integration-designcraft-20261103-5
- crates/engine/src/script.rs
- apps/designcraft-cli/src/main.rs
Integration batch integration-designcraft-20261103-6
Integration batch integration-designcraft-20261103-7
Integration batch integration-designcraft-20261103-8
MCP: complete tool hints and check nested argument keys
Snapping: a drawn or resized edge snaps to the page's centre line, as InDesign's smart guides do
Co-authored-by: haranishi <223786623+haranishi@users.noreply.github.com>
- crates/engine/src/cmd/file/place.rs
- apps/designcraft-cli/src/main.rs
Give anchored objects fresh ids when copying text frames (fixes #1522)
Fill the empty or graphic frame under the pointer when a file is dropped on it (#1646)
cli: the three argument bugs photocraft#7071 lists are live here too
A document opened with new objects going to its top layer even when that layer was hidden and
locked (a dieline), so a placed graphic showed as an empty frame outline. Document::working_layer
picks the topmost layer that is shown and unlocked (the top layer when there is none); DocState::new
and the fallback after the active layer is deleted use it.
* Frames holding pasted-in images are hit inside the frame, take a placed graphic; documents open on a usable layer

A frame with items pasted into it (how a document from InDesign keeps a cropped photo) was hit
through its children like a group, so the whole uncropped image counted: a click on the empty
page beside such a photo selected it, and nothing could be deselected there. It is now hit by
its own area, as it is drawn clipped. Such a frame holding only graphics also takes a placed or
dropped graphic (Item::takes_graphic), which fills it as an empty or graphic frame is filled.

A document opened with new objects going to its top layer even when that layer was hidden and
locked (a dieline), so a placed graphic showed as an empty frame outline. It now opens on the
topmost layer that is shown and unlocked (Document::working_layer).

* Place: a graphic replacing the one a frame shows keeps the frame's crop and zoom

Placing or dropping into a frame that shows a graphic (in it, or as the one graphic pasted into
it) filled the frame afresh, so the crop and zoom chosen for the old picture were lost. The new
graphic now covers the old one's area, centred on it: an edited copy of the same picture lands
exactly where the old one was.

* Move the replace-keeps-crop and open-on-a-usable-layer changes to their own PRs

Review asked for them to be checked against reference app behaviour on their own. This PR keeps
the frame hit area and Item::takes_graphic. Reverts 8ea0bc16 and drops Document::working_layer.

* Move Document::working_layer and its test to #2267
- xtask/src/main.rs
The menu item was enabled whenever a document was open. With no table
target it opened the file picker, and placing the image then failed
because table.placeGraphic needs a table target. The item now takes the
enablement of table.placeGraphic, as Justification… takes that of
type.para.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_012xiyiD63mAukLtyDQFCQKU
Format: a saved document opens whatever the size of its placed images (#2166)
Fixes #2281.

- crates/doc/src/mojikumi.rs
- crates/doc/tests/autocraft_regression.rs
The close-up test checked the half-em line-end set only; the other Japanese
base set closes up the same pairs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mojikumi: test consecutive punctuation under the one-em line-end set too
Placing an image or media with no placement parameters used the selected
item as the frame, and so did a click of the loaded place cursor. On a
line that made a zero-height frame that kept its stroke. All three now
skip items that enclose no area (PathData::encloses_area): lines and
paths whose points all lie on one line get a new frame instead. Frames,
and open paths that enclose an area, take the content as before.

An explicit frame, x, y, spread or width behave as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Include the PDF export warnings in the package result and report. Cover
shadow and feather output in packaged PDFs with a synthetic regression test.

Preserve frame and text-range hyperlinks on rasterized frames, including
inside groups, at their original source bounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The nonprinting stand-in for a rasterized linked frame keeps the frame's
story, alt text and effects for link bounds and text composition. A test
now checks that a tagged PDF of a linked frame differs from the same frame
without the link only by the link annotation: no extra structure elements
or alt text. Note why the stand-in keeps the frame's ItemId.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- crates/geom/src/path.rs
PDF: keep hyperlinks on rasterized frames; report package PDF warnings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Place: implicit image placement preserves lines and open paths
* textimport: cap style names read from files

* Apply review fixes

- crates/textimport/src/tagged.rs

---------

Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
* pptx: table border lookups by position, paragraph lines grouped once

* pptx: look a character up in the face's map, don't expand it

* Apply review fixes

- crates/pptx/src/table.rs

---------

Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
* images: one nested-SVG byte budget per parse

* Apply review fixes

- crates/images/src/lib.rs

---------

Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
* warichu: split long runs in one pass instead of trying every length

The exact split tries every line length for every start, so its work grew with
the square of the run's length, and a document can set warichu on a run of any
length. Runs longer than 512 glyphs are now split where the running advance
reaches each line's share, keeping the break minimums. The glyphs drawn with the
run find their anchor by binary search instead of a scan per glyph.

* address review: warichu split picks the break closest to the target
- crates/compose/src/tests.rs
- crates/epub/src/tests.rs
Integration batch integration-designcraft-20261104-1
Fonts: bound named instances per file, index axis settings in style strings (#2215)
Some checks failed
ci.yml / Fonts: bound named instances per file, index axis settings in style strings (#2215) (pull_request) Failing after 0s
nix.yml / Fonts: bound named instances per file, index axis settings in style strings (#2215) (pull_request) Failing after 0s
packaging-lint.yml / Fonts: bound named instances per file, index axis settings in style strings (#2215) (pull_request) Failing after 0s
sonarcloud.yml / Fonts: bound named instances per file, index axis settings in style strings (#2215) (pull_request) Failing after 0s
ci.yml / Fonts: bound named instances per file, index axis settings in style strings (#2215) (push) Failing after 0s
nix.yml / Fonts: bound named instances per file, index axis settings in style strings (#2215) (push) Failing after 0s
packaging-lint.yml / Fonts: bound named instances per file, index axis settings in style strings (#2215) (push) Failing after 0s
sonarcloud.yml / Fonts: bound named instances per file, index axis settings in style strings (#2215) (push) Failing after 0s
a4017a3cda
* fix(fonts): bound named-instance listing and index variable-style settings

* address review: bound inspected named-instance records file-wide
alf merged commit a4017a3cda into main 2026-11-04 08:25:38 +00:00
alf deleted branch chore/sync-upstream-20261104 2026-11-04 08:25:38 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
iris/designcraft!18
No description provided.