Commit Graph
2347 Commits
Author SHA1 Message Date
Dylan M. Taylor 260a629e9e Refactor ui canvas for strict Clippy checks 2026-10-28 01:06:48 +00:00
Dylan M. Taylor 1ecb22c67d Refactor render for strict Clippy checks 2026-10-28 01:06:48 +00:00
Dylan M. Taylor 36f6244cd4 Refactor proof for strict Clippy checks 2026-10-28 01:06:48 +00:00
Dylan M. Taylor ffaeda9316 Refactor pdf for strict Clippy checks 2026-10-28 01:06:48 +00:00
Dylan M. Taylor 815d3f0032 Refactor mcp for strict Clippy checks 2026-10-28 01:06:48 +00:00
Dylan M. Taylor 937c7e532d Refactor layout for strict Clippy checks 2026-10-28 01:06:48 +00:00
Dylan M. Taylor 7976368de7 Refactor formats for strict Clippy checks 2026-10-28 01:06:48 +00:00
Dylan M. Taylor 0436139c57 Refactor fonts for strict Clippy checks 2026-10-28 01:06:48 +00:00
Dylan M. Taylor a2435388e5 Refactor engine for strict Clippy checks 2026-10-28 01:06:48 +00:00
Dylan M. Taylor 75d30d69b4 Refactor docx for strict Clippy checks 2026-10-28 01:06:47 +00:00
Dylan M. Taylor 1d3e6d9ca8 Refactor doc for strict Clippy checks 2026-10-28 01:06:47 +00:00
aiongg 4b8b06fe03 Merge pull request #4602 from Silvertree2010/perf/export-speed
Speed up PDF export by about 24 %
2026-10-27 13:41:24 -04:00
NetterSilverandClaude Opus 5.5 de3bfaf049 Keep harfrust shape plans per face
Each shaping call let harfrust compile a new shape plan (the face's
lookups for the run's script, direction, language and features), which
took about as long as shaping a short run. Each thread now keeps up to 64
plans (the oldest dropped to make room), found by face and harfrust's
ShapePlanKey::matches, and passes the plan to the shaper. A kept plan is
only used for the face it was made for: the face id (from one process-wide
counter, so no two faces share it) together with the font bytes' address,
length and face index. The plan is the one the shaper would have made for
the buffer, so the glyphs are the same; tests shape with kept and fresh
plans and compare, and check the per-thread bound.

PDF export of the export_bench document (259 pages), interleaved with main
(A/A within 1 %): median 421 ms -> 319 ms (-24.2 %), measured on a loaded
machine; on a quieter run earlier 294 ms -> 224 ms (-23.8 %). PNG export of
page 1 (which lays out the whole document): about -34 %. The PDF and PNG
bytes are identical to main's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 17:39:37 +01:00
aiongg f8e327b068 Merge pull request #3936 from benandez/fix/3907-section-marks
#3907: continuous sections running off a page go onto the next; empty section marks in a row stack to the page bottom
2026-10-27 12:37:46 -04:00
aiongg 50a8ebe307 Merge pull request #4387 from benandez/fix/3872-followup
#3872 follow-up: font table found by its stored spelling; privacy scrub keeps attribute values well-formed
2026-10-27 12:37:26 -04:00
aiongg f270dbd3b0 Merge pull request #4060 from benandez/fix/3995-thai-test-guard
#3995: Thai one-face fallback tests skip unless a Thai font with Latin is installed
2026-10-27 12:37:05 -04:00
aiongg 436d444ef0 fix(ui): dropdown menus grow back after a search with no matches (#4577)
* ui: craft_ui::ComboBox everywhere and the combo arrow popup on craft_ui::combo::Menu

Repin craft-libs to aiongg/craft-libs 62bb0e1d (dropdown regrow). Ban egui::ComboBox in clippy.toml.

* chore(deps): craft-libs 60d60d48 (dropdown fixes)

* chore(deps): craft-libs 6af63481 (dropdown fixes)

* chore(deps): craft-libs 4d7b82f7 (dropdown fixes)

* chore(deps): craft-libs 92d392cf (#387 merged)
2026-10-27 10:27:21 -04:00
NetterSilverandClaude Opus 5.5 ffa127c615 Skip per-character table lookups for plain text in layout and PDF export
Shortcuts that give the same answers as the lookups they skip:

- is_rtl: nothing before U+0590 is right to left, so Latin, Greek and
  Cyrillic text skips the bidi class search.
- strong_rtl and is_mark: ASCII letters are the only strong ASCII
  characters, and no ASCII character is a mark.
- cluster_starts: in ASCII text every character starts a grapheme except LF
  after CR, and no ASCII character joins a cluster.
- display lines: a glyph run's vectors and text are sized up front, and the
  symbol-font check is made once per run instead of once per cluster.
- PDF glyph runs: the baseline (the y most glyphs share) is only worked
  out for italic runs, the only ones that use it, and a run on one baseline
  returns it without counting.

Tests check each shortcut against the full lookup (every code point for the
bidi classes, every ASCII pair for the clusters).

PDF export of the export_bench document, interleaved with main: median
293 ms -> 241 ms (-18 % with the previous commit). The PDF bytes are
identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 13:39:09 +01:00
NetterSilverandClaude Opus 5.5 8cdc172171 Lay out the paragraph after a kept heading once
A paragraph with keep with next measures the paragraph after it before it is
placed (next_first_line). That measurement called layout_para directly, so
every paragraph following a heading was laid out twice in each layout pass:
once to measure it and once, through the cache, when it was placed.

The measurement now goes through the layout cache under the key
para_labelled uses (Ctx::para_ahead) when its inputs are the same: no
proofing, no typed word, not inside a text box, no inline text boxes that
resize, the body's tracked-deletion view. Otherwise it is laid out on its
own as before. It counts nothing (list labels, equation numbers and the
page-total flag are restored), so pagination is unchanged.

Adds crates/engine/examples/export_bench.rs, which saves a long generated
document (the sample document 200 times, 259 pages) through io::save_bytes
and prints wall times:

  cargo run --release -p wordcraft-engine --example export_bench -- pdf 200 9

PDF export on that document, interleaved with main (Ryzen, 16 threads):
median 286 ms -> 256 ms (-10.6 %). The PDF bytes are identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 13:39:09 +01:00
Martin BallandClaude Opus 5.5 1b485e1f90 #3907: CodeRabbit — a kept paragraph is measured against the next page's body (a different first-page header)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 11:24:50 +00:00
Martin BallandClaude Opus 5.5 751eb4b204 #3907: CodeRabbit — a stacked mark past the page bottom draws no line number either
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:39:26 +00:00
Martin BallandClaude Opus 5.5 5225569d44 #3907: review — an empty mark taking no room, or stacked past the page bottom, draws no list label; an empty mark after a text paragraph closing a section stays quiet
dpkruse's Genuine Steps Certificate: a bulleted text paragraph closes a
two-column continuous section, then an empty bulleted one closes another.
Word draws no bullet for the empty one; it is now quiet (takes no room) and
draws no label, still counted. A stacked mark that doesn't fit keeps its
line for the caret but draws nothing below the margin (CodeRabbit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:04:05 +00:00
Martin BallandClaude Opus 5.5 f2fe7654e9 #3907: review — a kept paragraph low in a continuous section moves to the next page; a page break before a stacked mark keeps the new page's bottom
CodeRabbit (local): keep-lines compared its height with the room below the
mid-page section start, so a paragraph that fits on a new page split instead.
It now compares with the next column, or the next page's body when this is the
last column. A stacked mark whose page break before starts a page no longer
gets the previous page's bottom back.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CX1hMKX28764Lrx5moxs1o
2026-10-27 09:56:44 +00:00
Martin BallandClaude Opus 5.5 7757828295 #3907: continuous sections running off a page go onto the next; empty marks in a row stack to the page bottom
A continuous section moves the column top down below the text above. at_top()
then held at every new section start, so a paragraph that did not fit was
placed anyway, as on an empty page. A run of one-line continuous sections ran
off the bottom of page 1 (100 of them reached 2,480 pt down a 792 pt page).
The overflow checks for paragraphs now use fresh(): at the top with nothing
above on this page. at_top() still drives the spacing rules, unchanged.

Empty paragraphs that close a continuous section right after another
section's mark took no room. Measured in Word on synthetic files ("Start", N
such marks, "End"): 10 put "End" about 9 cm down page 1; 50, 100 and 200 all
put it on the first line of page 2, the same with 24 pt marks or no paragraph
spacing. So each takes its line while it fits, then stays at the page's
bottom. A mark right after text stays quiet, as measured before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CX1hMKX28764Lrx5moxs1o
2026-10-27 09:56:35 +00:00
Martin BallandClaude Opus 5.5 04ebc4ed42 #3995: Thai one-face fallback tests skip unless a Thai font with Latin is installed
Noto Sans Thai and its siblings have no Latin letters or digits, so on a
machine whose only Thai fonts are Noto no face covers the test run and
fallback_for_run rightly returns None. The guard now asks whether the Thai
substitute covers the whole run, which doesn't go through fallback_for_run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CX1hMKX28764Lrx5moxs1o
2026-10-27 09:52:32 +00:00
Martin BallandClaude Opus 5.5 cd683a9fd1 #3872: review — privacy rewrite keeps attribute values as written, escaping only quotes
Unescaping and escaping again turned &#10; and &#9; into raw characters that read
back as spaces. The raw value of well-formed markup holds no `<` and every `&` starts
a reference, so only a `"` from a single-quoted value is replaced with &quot;.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HUodYU9r4mPHed4pPsjHC2
2026-10-27 09:51:52 +00:00
Martin BallandClaude Opus 5.5 932a2e8955 #3872: review — font table found by its stored spelling; privacy rewrite escapes attribute values
- A package storing WORD/FONTTABLE.XML (part names are case-insensitive) loaded no
  embedded fonts and a save dropped the table and every font: lookups used the exact
  constant spelling. They now find the key case-insensitively.
- scrub_private re-pushed attribute values raw (quick-xml push_attribute does not
  escape), so a value read from single quotes became malformed XML. The value is now
  unescaped and escaped again.
- rel_targets matches _rels/ case-insensitively.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HUodYU9r4mPHed4pPsjHC2
2026-10-27 09:51:52 +00:00
aiongg 2cf1d6c7ae Merge pull request #4489 from aiongg/layout
refactor: shared module layout (craft-contrib#20)
2026-10-26 21:32:09 -04:00
skylar taylor-barrick f54ea731cc Merge pull request #4487 from storytold/fix/nextest-0.9.150
ci: require nextest 0.9.150
2026-10-26 17:35:44 -06:00
aiongg 0404b4199a test: file_access scan stops only at inline test modules 2026-10-26 22:10:26 +00:00
aiongg 95ba3d07b4 docs: update paths of moved files 2026-10-26 21:16:49 +00:00
aiongg 835704f870 refactor(engine): move session::ime_state to the crate root as ime 2026-10-26 21:16:49 +00:00
aiongg 39336fe844 refactor(engine): move session::snapshot_types to the crate root as snapshot 2026-10-26 21:16:49 +00:00
aiongg 5a311f4615 refactor(engine): move session::prefs to the crate root as prefs 2026-10-26 21:16:49 +00:00
aiongg 8f1f3c6132 refactor(engine): move session::view to the crate root as view 2026-10-26 21:16:49 +00:00
aiongg e027a4244b refactor(engine): move session::doc_state to the crate root as doc_state 2026-10-26 21:16:49 +00:00
aiongg 3c4d7adc87 refactor(engine): move session::locale to the crate root as locale 2026-10-26 21:16:49 +00:00
aiongg cfa90220d9 refactor(engine): split session.rs into session/ 2026-10-26 21:16:49 +00:00
aiongg 8f10bd1b9f refactor(engine): split cmd/table.rs into table/ 2026-10-26 21:16:49 +00:00
aiongg a82063e50a refactor(engine): split cmd/insert.rs into insert/ 2026-10-26 21:16:49 +00:00
aiongg 342de16153 refactor(engine): split cmd/text.rs into text/ 2026-10-26 21:16:49 +00:00
aiongg d66f34b774 refactor(engine): split cmd/references.rs into references/ 2026-10-26 21:16:49 +00:00
aiongg 0a6c7605f6 refactor(engine): split cmd/para.rs into para/ 2026-10-26 21:16:49 +00:00
aiongg fbc95e1e10 refactor(engine): split cmd/citations.rs into citations/ 2026-10-26 21:16:49 +00:00
aiongg fac4b550c1 refactor(engine): split cmd/mailings.rs into mailings/ 2026-10-26 21:16:49 +00:00
aiongg cc728757f5 refactor(engine): split cmd/review.rs into review/ 2026-10-26 21:16:49 +00:00
aiongg 3d458a08b6 refactor(engine): split cmd/tools.rs into tools/ 2026-10-26 21:16:49 +00:00
aiongg dabc31eb83 refactor(engine): move cmd/tools tests to tools/tests.rs 2026-10-26 21:16:49 +00:00
aiongg 03f5df4bba refactor(engine): split cmd/objects.rs into objects/ 2026-10-26 21:16:49 +00:00
aiongg b8ddca30a1 refactor(docx): split read/chart.rs into read/chart/ 2026-10-26 21:16:49 +00:00