470 Commits
Author SHA1 Message Date
aiongg 0565eaf380 Integration: #1461 drop cap box moves with vertical justification 2026-10-31 00:33:24 +00:00
aiongg b1d213ba6e Integration: #1566 hyphenation limits count the word after an elision 2026-10-31 00:32:54 +00:00
aiongg bdcce39e56 Integration: #1566 apostrophe elision only in French and Italian 2026-10-31 00:13:39 +00:00
aiongg 89ef0e3d81 Merge PR #1606: Paragraph Shading and Border pages
crates/ui-egui/src/panels/properties.rs, properties/object.rs: main's split module kept; preview_row and paint_corner_shape exported for the shading corner menus
crates/ui-egui/src/panels/properties/tests.rs: both sides' tests kept
crates/ui-egui/src/dialogs/tests.rs: main's tests kept; the border test uses the new corner labels; the shading layout test added
crates/ui-egui/src/i18n/*.rs: union of keys without duplicates
crates/ui-egui/src/dialogs.rs: the corner shape face uses corner_shape_button (no hard-coded colour in the dialog)
2026-10-30 23:17:40 +00:00
aiongg ad21d75988 Merge PR #1566: Hyphenation fr/it 2026-10-30 23:16:19 +00:00
aiongg cc80be16ab Integration: #1461 spacing-limit test checks every paragraph gap against the small limit 2026-10-30 23:16:19 +00:00
aiongg f2389b791c Merge PR #1461: Text Frame Options
crates/ui-egui/src/dialogs.rs: both dialog tests kept
crates/ui-egui/src/i18n/*.rs: union of keys
docs/target-app-parity.md: main's change line plus the Text Frame Options entry
2026-10-30 23:16:09 +00:00
aiongg b9f884f002 Find/Change: Change and Change/Find (PR #1498's own change on main's Find/Change)
Applied as the diff of #1497 to #1498's head; the older copy of #1496 is not taken.
crates/engine/src/cmd/find.rs: PR's found-text Change path; format_only keeps main's cleared-attributes case
crates/ui-egui/src/dialogs/tests.rs: main's tests kept, the PR's Change test added
crates/ui-egui/src/i18n/*.rs: union of keys
docs/target-app-parity.md: main's text plus the Change wording
2026-10-30 23:15:51 +00:00
moppeeer b71f83114f Merge remote-tracking branch 'origin/main' into hyphenation-french-italian
# Conflicts:
#	crates/compose/Cargo.toml
#	crates/compose/src/hyphen.rs
#	docs/gaps.md
#	docs/localization-parity.md
#	docs/typography-parity.md
2026-10-30 21:56:20 +01:00
moppeeer 1fd7cff87e Merge remote-tracking branch 'origin/main' into text-frame-options-layout
# Conflicts:
#	crates/compose/src/tests.rs
2026-10-30 21:53:55 +01:00
aiongg 92327dcc4c Merge track b: swatches, canvas, tools and tables PRs
Conflicts: engine/src/tests.rs and i18n/*.rs: adjacent additions from both sides, both kept.
2026-10-30 20:49:48 +00:00
aiongg 5d9594900f Merge PR #1041: Compose: test anchored objects pushed into a later frame 2026-10-30 19:44:36 +00:00
aiongg 19a8a7ae66 Merge PR #1463: Paragraph rules: Type, Gap Color and Tint, Keep In Frame
crates/ui-egui/src/dialogs/tests.rs: kept both sides' appended tests
2026-10-30 19:44:13 +00:00
Brandon Thomas 56decd96e2 Merge storytold/main into PR #1990 2026-10-29 21:55:23 -04:00
Brandon Thomas e22e6038dd Merge storytold/main into PR #1990
# Conflicts:
#	docs/typography-parity.md
2026-10-29 21:53:38 -04:00
Brandon Thomas 8867f312e9 Merge storytold/main into PR #1972 2026-10-29 21:53:38 -04:00
Brandon Thomas f05a022cb9 Merge pull request #1254 from RoHaX/feat/german-2006-hyphenation
Add German 2006 reform hyphenation
2026-10-29 21:53:14 -04:00
Brandon Thomas 53eeb497c2 Merge pull request #1949 from garrytan/fix/settle-chained-own-story-wraps
compose: settle a story's own anchored wraps by object count, not 4 passes
2026-10-29 21:53:10 -04:00
Brandon Thomas 0a05c1631c Merge pull request #1233 from seanth/running-header-order
[fix] Running headers read a page in text order, not by height
2026-10-29 21:52:08 -04:00
Brandon ThomasandClaude Opus 5.5 cc47975960 Use clamp for the wrap pass bounds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:43:16 -04:00
Brandon ThomasandClaude Opus 5.5 a9256308e3 Keep at least four wrap passes for a story with few anchored objects
A single wrapping object moved by keep options or vertical justification had four passes to
settle before; one-per-object would have given it two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:39:35 -04:00
moppeeer e16e0c4d61 Merge remote-tracking branch 'origin/main' into paragraph-rule-types
# Conflicts:
#	crates/compose/src/lib.rs
#	crates/doc/src/attrs.rs
#	crates/idml/src/export.rs
#	crates/idml/src/import.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
2026-10-29 12:08:24 +01:00
moppeeer 165d8c65ac Merge remote-tracking branch 'origin/main' into paragraph-shading-indesign
# Conflicts:
#	ROADMAP.md
#	crates/doc/src/attrs.rs
#	crates/idml/src/export.rs
#	crates/idml/src/import.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/panels/properties.rs
2026-10-29 11:50:25 +01:00
moppeeer 4145fcbc44 Merge remote-tracking branch 'origin/main' into text-frame-options-layout
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	docs/target-app-parity.md
2026-10-29 11:41:36 +01:00
moppeeer 45751393ef Merge remote-tracking branch 'origin/main' into hyphenation-french-italian 2026-10-29 11:39:17 +01:00
KnottyDyesandClaude Opus 5.5 d7bc6633e7 Compose: honor discretionary hyphens in single-line composition
Recognize U+00AD independently of automatic hyphenation in the greedy breaker, reusing the existing visible hyphen output. Cover both composers, zero-width unbroken soft hyphens, pattern suppression, and tagged and untagged PDF text extraction.

Fixes #1955

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:14:44 -06:00
VoiceLessQ 89ccfbb18c Autoflow: count each forced move once, and only visible breaks (#1209)
fit() counted every break character in the overset text and every
Start Paragraph option. A break followed by a paragraph whose option it
already meets moved the text once but asked for two frames, and breaks
that tracked changes delete or a hidden condition hides asked for
frames that composing never uses. Autoflow added those pages and
removed them again: 200 chapters that end in page breaks and start on
the next page took 61095 ids and 430 ms, now 798 ids and 16 ms.

Each paragraph composing places now counts once, when a visible break
ends the paragraph before it or its start option sends it on; after a
break it is at the top of a frame, where the option only fixes odd or
even page parity. A visible break inside a paragraph counts once more.
previous_visible_break and the new visible_break in designcraft-compose
are public, so the estimate and the composer use one rule.
2026-10-28 23:08:41 -02:00
KnottyDyesandClaude Opus 5.5 ed88f3e4b5 Compose: tables clear the footnote area
Reserve column and spanning footnotes when fitting inline table row groups. Continue rows in the next column or frame when the remaining space is too small, and include cell-local footnotes in measured row height.

Fixes #1901

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:44:34 -06:00
VoiceLessQ ca85be89d2 Autoflow: skip deleted paragraphs and ask for every forced frame at once (#1209)
The start option lookup now reads the next paragraph composing places,
past tracked-deleted ones. Frames that start options and breaks force
are counted and asked for in one round, so a story with a paragraph per
page no longer gets one frame per round.
2026-10-28 22:32:07 -02:00
Claude 94e0e75249 Merge storytold/main (v0.14.0) into running-header-order
The running-header tests move to cmd/variables/tests.rs with upstream's split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa
2026-10-28 18:00:57 +00:00
hslrmnp27 0ecaa95206 T.DE1: merge upstream main while preserving German hyphenation 2026-10-28 14:52:15 +01:00
Dylan M. Taylor 71a579e8da Refactor compose for strict Clippy checks 2026-10-28 11:39:26 +00:00
garrytanandcapy-ai[bot] 4ac4219fd2 compose: settle a story's own anchored wraps by object count, not 4 passes
A story whose custom or above-line anchored objects wrap its own later
lines is composed first without those wraps, then again with each
object where the previous pass placed it. That loop stopped after a
fixed MAX_PASSES = 4 extra compositions. Each object's wrap pushes down
the anchors of the objects after it, so in a chain of such objects the
k-th object only lands where it belongs once the k - 1 wraps before it
are in place. From roughly the fifth chained object on, the loop ran
out before the story settled: the final composition placed the object
in one spot but wrapped the text around where an earlier pass had put
it, and lines printed straight over the object.

Bound the loop by the story's own wrapping objects instead: n + 1
passes for n such objects (inline objects never wrap their own story
and are left out). Composition 0 has no own wraps, so object 1 is
already where it belongs; by induction pass k places object k + 1
correctly, pass n places the last one, and pass n + 1 wraps the text
around it, after which the positions cannot move. The existing settled
check still stops a story early as soon as its objects stop moving,
so stories that settle sooner cost nothing extra. MAX_PASSES stays as
an absolute safety net, raised to 64, for layouts that keep moving
their objects (keep options, vertical justification) and for stories
with very many anchored objects.

Test: chained_anchored_objects_all_wrap_their_own_storys_lines anchors
eight 100 x 60 pt bounding-box objects six lines apart in one story,
each hanging from its anchor line, and checks that every object sits
on its anchor line and every line it hangs over moves clear of it.
Without this change it fails at the seventh object (a line 790 pt down
sits at x 0 inside the object's band at 718 pt); with it, it passes.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-28 10:23:57 +00:00
dotCooCoo 9ef2e64f9f Merge main into the column rule extent fix 2026-10-27 07:16:50 -07:00
hslrmnp27 66447c01ae T.DE1: merge upstream main and resolve update conflicts 2026-10-27 13:33:24 +01:00
aiongg 0982b3da03 refactor(compose): move the border_tests module of table.rs to table/border_tests.rs 2026-10-26 18:18:14 +00:00
aiongg 29d10be435 refactor(compose): move hyphen/mod.rs to hyphen.rs 2026-10-26 18:18:13 +00:00
dotCooCoo 00d0867d50 Merge branch 'main' into fix/column-rules-span-the-text 2026-10-25 21:39:36 -07:00
Dylan M. Taylor 42463ce99d Reduce cognitive complexity in variables, PDF graphics, compose frames and menu shortcuts (#1847)
* refactor(engine): split convert_all's per-story plan into convert_plan

Resolves SonarCloud rust:S3776 on convert_all (cmd/variables.rs).

* refactor(pdf): split graphic_unclipped into per-kind helpers

Resolves SonarCloud rust:S3776 on graphic_unclipped (pdf/src/export.rs).

* refactor(compose): extract wrap exclusions and frame text area from frame_specs_placed

Resolves SonarCloud rust:S3776 on frame_specs_placed (compose/src/lib.rs).

* refactor(ui): extract command shortcut lookup from shortcut_key

Resolves SonarCloud rust:S3776 on shortcut_key (ui-egui/src/menus.rs).
2026-10-25 21:25:52 -04:00
hslrmnp27 85321749ac T.DE1: merge current main and resolve typography documentation conflict
Preserve the German 2006 hyphenation entry alongside the newer optical margin alignment documentation. Merge upstream main at 5024d61ce6.

Validation: cargo xtask ci passed all nine steps; 3271 tests passed, 6 ignored; all four German hyphenation tests passed; WASM check passed for all 16 crates.
2026-10-25 18:39:15 +01:00
moppeeer 41b3b5e569 Merge remote-tracking branch 'origin/main' into paragraph-shading-indesign 2026-10-25 17:13:49 +01:00
moppeeer 9642aa57c7 Merge remote-tracking branch 'origin/main' into text-frame-options-layout
# Conflicts:
#	crates/engine/src/cmd/object.rs
#	crates/ui-egui/src/dialogs.rs
#	docs/target-app-parity.md
2026-10-25 17:08:00 +01:00
dotCooCoo 5f77ea123b Text frames: column rules run from the top line's ascent to the bottom line's descent
A column rule ran from the top of the columns to their bottom, so a frame
that the text only half fills drew its rules down to the empty bottom of
the frame. InDesign draws them from the top line's ascent to the bottom
line's descent. column_rule_rects now takes that extent from the frame's
lines, and the top and bottom insets shorten the rule from there. A frame
without lines draws no rules. Existing documents draw their column rules
at the new length, on screen, in PDF and in PowerPoint export.
2026-10-24 18:54:30 -07:00
skylar taylor-barrick 9fc02f8f7b Merge contributor branch codex/placed-graphic-layer-overrides (PR #1647) into main
Conflict resolutions:
- crates/pdf/src/export.rs: keep main's `graphic(.., hidden)` signature from
  #1241 and report retained graphic-layer overrides that drawing cannot apply.
- docs/gaps.md: keep main's change list and add the graphic-layer entry.
2026-10-24 15:30:03 -06:00
aiongg a16f29a669 Optical margin alignment: lines with a tab hang their edge glyphs, as both line breakers measure them (#1760)
layout_line set a line with a tab or a right indent tab without the optical
margin or CJK edge hangs, while the greedy breaker and the paragraph composer
counted them. A line that fit only because its period hangs was then squeezed
into the measure or set past it.

layout_line now hangs the edge glyphs of every line. Tabs are measured from the
hung start, so tab stops stay where they are, and a right indent tab fills to
the hung end. A tab at a line edge hangs nothing. Lines without tabs lay out as
before.
2026-10-24 14:45:04 -04:00
Brandon Thomas 12c6c721e7 Merge pull request #1753 from Hamza0101/fix/grep-scan-bounds
GREP styles: a scan and the pattern cache are bounded (#1598)
2026-10-24 10:23:12 -04:00
Brandon Thomas 8d6989c508 Merge pull request #1740 from Silvertree2010/fix/h1918-footnote-numbering-overflows-u32
Saturate footnote and endnote numbering at u32::MAX
2026-10-24 10:22:43 -04:00
aiongg 5def4a8c01 Story Optical Margin Alignment with a size, and Ignore Optical Margin (Story panel, engine and IDML) (#1705)
* Type: story Optical Margin Alignment with a size, and Ignore Optical Margin (engine, IDML)

A story's Optical Margin Alignment applies to every paragraph except those
with Ignore Optical Margin; hang amounts scale with the story's size.
story.options sets it, type.para and paragraph styles take
ignoreOpticalMargin. IDML imports and exports StoryPreference
OpticalMarginAlignment/OpticalMarginSize and IgnoreEdgeAlignment. The
per-paragraph opticalMargin switch keeps hanging at the text's own size.

Part of #947.

* Type: the Story panel, and Ignore Optical Margin in the Paragraph panel menu and Paragraph Style Options

Window > Type & Tables > Story sets Optical Margin Alignment and its size
for the stories of the text selection or the selected text frames through
story.options, and is disabled without either. type.selectionAttrs
reports the stories' values as storyOptions, null where they differ.
The Paragraph panel menu toggles ignoreOpticalMargin, and Paragraph Style
Options > Indents and Spacing has an Ignore Optical Margin checkbox.

Part of #947.

* ROADMAP: the story Optical Margin Alignment entry names its default and scope
2026-10-24 10:08:40 -04:00
Brandon ThomasandClaude Opus 5.5 637c32506a GREP styles: empty matches don't count toward the per-paragraph cap
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 10:05:52 -04:00
Brandon ThomasandClaude Opus 5.5 5fca302ff6 Endnotes: saturate the remaining numbering sites at u32::MAX
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 10:01:04 -04:00