Commit Graph
1801 Commits
Author SHA1 Message Date
aiongg db8efbe593 LaTeX: \'{} and \"{} import as the spacing acute and diaeresis, not an apostrophe and a quotation mark (#4170) 2026-10-24 21:05:19 +00:00
aiongg 226b3aa25f Web host bridge: Save As in a format that doesn't keep everything is a copy, saveAs false; no bridge on an opaque origin (#3822) 2026-10-24 21:05:19 +00:00
aiongg 528fcb21a3 Release and CI: the suite's craft-fonts pin (bfcbf714, the same fonts as a71c20e) instead of an app override (#3888) 2026-10-24 21:05:19 +00:00
aiongg 54f68cac11 Integration: #4004's restored layout revision gets its own name beside the outline's rev_before (#4014) 2026-10-24 21:05:19 +00:00
aiongg dd8ea27b99 Merge PR #4039: #3889, #3891: Update Table keeps the styles and columns of a localized table of contents 2026-10-24 21:05:19 +00:00
aiongg dd865648c2 Merge PR #4079: Comments: times in the user's own time zone, in the pane and the balloons (#2322, #2337) 2026-10-24 21:05:18 +00:00
aiongg 8e8d8b4dd2 Merge PR #4172: Read Mode: a Page color menu shows the columns on white, sepia or a dark page 2026-10-24 21:05:18 +00:00
aiongg 0e571c1fc6 Merge PR #4133: #4085: toggle the last highlight colour with the keyboard shortcut 2026-10-24 21:05:13 +00:00
aiongg 6ba3d8d2a7 Merge PR #4123: AutoFormat: Enter twice after a short line makes it a heading 2026-10-24 21:05:05 +00:00
aiongg 540d0b2b34 Merge PR #4101: AutoCorrect: words after a no-break space are corrected 2026-10-24 20:42:03 +00:00
aiongg 4a4877e433 Merge PR #4097: Layout: include exact line-bottom contact for floating tables (#3977) 2026-10-24 20:42:02 +00:00
aiongg 39bf1a6c76 Merge PR #4074: cu20we: fix box float paragraph origins and preserve shape line caps 2026-10-24 20:42:02 +00:00
aiongg 5d9d9f5be4 Merge PR #4004: #4001: A command that changes nothing is not an edit 2026-10-24 20:42:01 +00:00
aiongg 26c4d4b869 Merge PR #4170: Import LaTeX \^{} and \~{} as plain ^ and ~ 2026-10-24 20:38:55 +00:00
aiongg e62af05110 Merge PR #4171: Picture memory limits for 6 audit issues: one decoder, TIFF strips, styles, Remove Background, Windows paste, render cache 2026-10-24 20:38:55 +00:00
aiongg a4ba4b8f0f Merge PR #4147: Reject '+' sign in print page ranges 2026-10-24 20:38:53 +00:00
aiongg d5c2d88935 Merge PR #4129: AutoFormat: border lines and +---+ tables track the characters they delete 2026-10-24 20:38:52 +00:00
aiongg 6498bb20f6 Merge PR #4121: MCP: keep serving when the app isn't open yet (#4078) 2026-10-24 20:38:51 +00:00
aiongg d0a9dde289 Merge PR #4120: Engine: keep cell widths in step after Insert Column; validate a version restore before saving 2026-10-24 20:38:50 +00:00
aiongg 35f0a7c785 Merge PR #4113: AutoFormat: Tab and Backspace at the start of a line set the paragraph's indents 2026-10-24 20:38:50 +00:00
aiongg 39dee609c1 Merge PR #4112: MCP: type_text with paragraphs is one command, not a snapshot of the document per line (#3973) 2026-10-24 20:38:49 +00:00
aiongg 423e897c09 Merge PR #4109: Proofing: web addresses after a no-break space are not marked as misspelled 2026-10-24 20:38:49 +00:00
aiongg a4b02f9a16 Merge PR #4100: autoscroll: make the cursor follow the scroll direction 2026-10-24 20:38:47 +00:00
aiongg b9b6d4be88 Merge PR #4099: Match highlight icon to selected color 2026-10-24 20:38:45 +00:00
aiongg 55fc2bed44 Merge PR #4090: Layout: a document of many pages paid a body-length scan per page 2026-10-24 20:38:44 +00:00
aiongg cb88382b10 Merge PR #3998: Resolve note separator revisions in Accept/Reject All 2026-10-24 20:38:42 +00:00
aiongg cbc8a23cb2 Merge PR #3888: Release: embed the current craft-fonts, with the Georgian face 2026-10-24 20:38:41 +00:00
aiongg a50264aa6a Merge PR #3853: SmartArt and charts: resize handles on the canvas and a Size group on SmartArt Design (#3852) 2026-10-24 20:38:41 +00:00
aiongg 3a205983e2 Merge PR #3836: Layout: a multiple-spaced line stays on the page while its text fits 2026-10-24 20:38:40 +00:00
aiongg cb7426af14 Merge PR #3822: Web: a ?host=parent bridge for pages that keep WordCraft's files 2026-10-24 20:38:40 +00:00
aiongg 5078cc1e51 Merge PR #3818: DOCX: a malformed main document part is an error, not a truncated document (#3599) 2026-10-24 20:38:39 +00:00
LQRD e499ecc5c3 #4085: integrate main and keep the highlighter choice in previews
Merge upstream9d1067fc, preserving both progress-document histories.
Copy the session highlighter choice into command previews, and verify that the
preview toggles the chosen colour without changing the document or undo history.
Document the shared-registry constructor as well as the public constructor.

Validation: complete integrated cargo xtask ci passed all8 gates, including all4
highlight regressions; RUST_TEST_THREADS=1. Local named-function doc audit16/16.
Native Word comparison pending. No new interface strings in this fix.
2026-10-25 01:35:28 +05:00
LQRD d67f5fffcb #4085: check the entire target before removing highlighting
Resolve highlight runs one at a time without the formatting-state query cap.
Pin mixed and inherited tails at 10000/10001/10002 runs, including Undo/Redo.
Document the changed command/session/helpers and explicit palette setter behavior.

Validation: red regression at10001 runs; all3 highlight tests pass after fix.
Full cargo xtask ci passed all8 gates:3963 passed,0 failed,4 existing ignored.
RUST_TEST_THREADS=1. Local named-function doc audit:13/13 documented; CodeRabbit
uses its own analyzer. Native Word comparison remains pending.
2026-10-25 00:55:51 +05:00
VoiceLessQ fc9b55575b Render: the media cache knows a picture's file without keeping it alive, so large files stay cached (#3665)
Review of #4171: cacheable() shrank an oversized picture to half the cache
minus its source size, so a source of 64 MiB or more left no room, and the
cache, which counted the source bytes each entry held, refused the entry.
Such a picture (an uncompressed 5000 × 5000 BMP is 75 MB) was still decoded
on every render. A broken source of 128 MiB or more was refused the same
way, so its decode was retried on every render too.

The cache held each source Arc only to recognise it. Before #293 it keyed
entries by the raw address, which a freed source could hand to new bytes.
A Weak keeps that guarantee, since the allocation itself stays while the
Weak lives, but the bytes are freed with the document. The cache now
counts only what it decoded, and cacheable() no longer depends on the
source size.

Tests: the #3665 test caches its shrunk picture for a source larger than
the whole cache. media_cache_counts_what_it_keeps_and_lets_sources_go
replaces media_cache_counts_the_source_bytes_it_holds, which pinned the
old accounting, and the same-allocation test also checks that a freed
source's address isn't taken for a new one. Counting the source again
fails two tests; keeping a strong reference fails one.
2026-10-24 18:49:17 -01:00
dotCooCoo b400a26aaa Read Mode: a Page color menu shows the columns on white, sepia or a dark page
Word's Read Mode has View › Page Color with None, Sepia and Inverse.
The Read Mode toolbar now has a Page color menu beside Line spacing,
backed by the view.readPageColor command. None draws the columns on
white and does not show the document's page color or page picture.
Sepia draws them on Word's sepia paper (RGB 112, 66, 20 at 80%
transparency over white) and leaves the text's colors alone. Inverse
draws the dark page that View › Switch Modes draws.

The setting is screen only, is not saved, and starts at None on each
entry to Read Mode, as the other reading controls do. In Read Mode it
sets the paper even while Switch Modes is on. Print Layout and the
other views still show the document's page color, and Switch Modes
still darkens them.
2026-10-24 12:22:04 -07:00
galagersharp-code d784f328ea Table: a new column keeps cell widths in step with the grid, and a refused restore saves nothing
insertColumnRight shrank the grid but left each cell's width at the old value,
so the page laid the cells out wider than the grid and the saved w:tcW disagreed
with it; the cells now take their grid columns' widths.

file.versions with a restore also saved when the restore was refused (protected
document, no such version, or a restore that is not a non-negative integer):
the restore is checked before anything is saved, a non-integer restore is
refused instead of being read as absent, and an index that does not fit in
usize names no version rather than wrapping to a small one on a 32-bit target.
2026-10-25 03:03:08 +08:00
VoiceLessQ 7a7a05c7f0 Render: a picture too large for the media cache is kept smaller, not decoded on every render (#3665)
pixmap_for offered every decode to the media cache, and the cache
refuses an entry larger than its whole budget (128 MB, source bytes
included). A picture between that and the 512 MB decode budget (about
34 to 67 M pixels, which can be a small PNG) was decoded, thrown away
and decoded again each time a page holding it was drawn: while typing,
scrolling or zooming.

Such a picture is now averaged over square blocks of whole pixels down
to half the cache before it is stored. The pixels are premultiplied, so
edges keep their colour. Pictures the cache already held keep their
full size. Pages draw a picture scaled to its frame, so it lands in the
same place; a tiled page background now takes its tile size from the
picture's natural size instead of the pixmap's.

Measured with a 7000 x 7000 PNG on a page, dev profile (workspace
opt-level 1): every render took 0.27 to 0.28 s before. Now the first
takes 0.43 s and the next ones 0.3 to 0.5 ms. Peak RSS stays 399 MB,
the decode's own.
2026-10-24 18:01:45 -01:00
VoiceLessQ 5929374704 Windows paste: clipboard pictures are checked from their header before anything decodes them (#3623)
On Windows the paste path asked arboard (3.6.1) for decoded pixels.
arboard decodes the registered "PNG" format and CF_DIBV5 with no image
limits, so a clipboard item of a few dozen bytes whose header declares
100,000 x 100,000 pixels made the decoder allocate tens of GB. The
allocation failure aborted the app outside Session::run's guard, before
paste_picture's size checks ever saw the picture.

On Windows the app now reads the clipboard itself (clipboard-win, the
raw read capped at 200 MB):
- "PNG" goes to Insert Picture as encoded bytes, once its header shows
  a size a paste takes (16,000 a side, 50 M pixels).
- Otherwise CF_DIBV5 or CF_DIB is probed through
  wordcraft_geom::picture::probe_dib, checked the same way, then decoded
  within the decode budget into pixels.

probe_dib reads a DIB in the layout the image crate expects: a 32-bit
BI_RGB DIB with an alpha mask becomes bit fields, so its alpha is kept
as arboard did. A V4 or V5 bit-field DIB without the copy of its colour
masks after the header (arboard writes one) gets that copy, because the
decoder skips 12 bytes there and would otherwise read into the pixels.

Linux keeps using arboard, which decodes the clipboard's PNG within the
image crate's default 512 MB limit. macOS keeps it too: arboard decodes
the pasteboard's TIFF there, which can still reach the JPEG strip
decoder of #3645; that path is untouched here because it can't be built
or run on this machine.

cargo check and clippy for x86_64-pc-windows-gnu pass on the
Windows-only code; it hasn't been run on Windows.
2026-10-24 18:01:45 -01:00
VoiceLessQ 8c9b8faa59 Remove Background and the Shadow style stay bounded on big pictures (#3647)
Remove Background pushed the four neighbours of every pixel it cleared
before looking at them, so its stack grew with the picture. It now
fills a row span at a time and keeps one seed for each run of matching
pixels beside a filled span, besides the border's. A uniform 2000 x
2000 picture now peaks at the border's 8,000 seeds. The region it
clears is the same: background reachable from the border, colour kept.

The Shadow style blurred a padded copy of a plain rectangle with a
Gaussian of sigma max(w, h) / 80, a kernel that grows with the picture,
through several full-size buffers. The blur of a rectangle is the
product of its sides' blurs along each axis, so the shadow is now drawn
from two profiles, each one difference of the kernel's running sums
(the same kernel image::imageops::blur uses). A test compares it with
the old blur: no channel differs by more than 2.

Measured on a uniform picture, dev profile, wall time and peak RSS of
the test process:
- Shadow, 4000 x 4000: 15.4 s and 414 MB before, 0.31 s and 203 MB now
- Remove Background, 6000 x 6000: 1.02 s and 1,028 MB before,
  0.54 s and 325 MB now (the picture and its RGBA copy alone: 289 MB)

The input is already held to the decode budget (#3644).
2026-10-24 18:01:45 -01:00
VoiceLessQ 856938d559 Picture Styles and Picture Border check the size they draw before allocating it (#3611)
adjust_media capped only the decoded input. The frame, shadow and
default styles then sized their bitmaps from it with unchecked u32 sums.
A thin 1 x 2,000,000 PNG of a few KB and Thick Frame asked for about
1.8 TB. The failed allocation aborted the app outside Session::run's
panic guard, so every window's unsaved work was lost.
picture.border {"width":200} on it asked for 3.2 GB.

Picture operations are now fallible. Every bitmap a style or border
draws on comes from canvas(), which refuses a size the renderer couldn't
draw either (two RGBA buffers within the 512 MB decode budget) before
allocating, and the margins are added with checked arithmetic. The edit
then fails with "picture is too large to edit" and the picture stays as
it was.
2026-10-24 18:01:45 -01:00
VoiceLessQ c99eb50067 TIFF pictures with JPEG strips are never decoded, and aren't inserted (#3645)
The tiff crate (0.11.3, through the image crate's tiff feature) decodes
a JPEG-compressed strip (compression 7) with a zune-jpeg decoder of its
own and default options: up to 16384 x 16384 a strip, whatever size the
TIFF declares. Neither the image crate's limits nor the budget reach it.

The picture probe now reads the first image's compression from the
TIFF's IFD and decodes only none, CCITT Group 4, LZW, Deflate and
PackBits. Anything else, old-style JPEG (6) included, is refused before
a decoder is made. Drawing, PDF export, wrap outlines, the crop dialog
and picture edits all ask the probe, so such a TIFF in an opened
document gets the grey box.

Insert Picture and Change Picture read only the header's size, so they
took such a TIFF. They now refuse it and say why.
2026-10-24 18:01:45 -01:00
VoiceLessQ d7429d217a Pictures: one decoder checks the header against the budget, progressive JPEG coefficients included (#3644)
Document pictures now decode through wordcraft_geom::picture (behind a
new `image` feature of the geom crate). It reads the header, refuses the
picture before anything is allocated when the decode's peak would pass
the budget, then decodes with the header's size and the budget as
limits.

The peak is the larger of three moments: the decoder's output beside
what it holds while decoding, the output beside its RGBA8 conversion,
and that beside a premultiplied copy. A progressive JPEG's decoder
(zune-jpeg) holds an i16 for every coefficient of every component, in
whole MCUs, for the whole picture; the SOF header gives that size. A
7900 x 7900 progressive JPEG without subsampling needs 9 bytes a pixel
(561 MB), past the 512 MB budget, although two RGBA buffers (499 MB)
fit. With the fix reverted, the test's header-only file of that size
was decoded and the test process peaked at 542 MiB resident.

- Render and PDF export ask the probe in their wrappers (decode_pixmap,
  load_image), so a picture past the budget gets the grey box or is
  left out, as before.
- The crop dialog and every picture edit decoded with load_from_memory
  and looked at the size afterwards. They decode through the probe now,
  and so do wrap outlines and the RTF re-encode.
- A picture edit has the drawing budget instead of an 80 M pixel cap
  checked after decoding. A picture past that budget (about 67 M RGBA
  pixels) can't be drawn either, so no edit that could be seen is lost.
2026-10-24 18:01:45 -01:00
Laurence Manuel 5b37a27b47 #4087: normalize the remembered highlight action 2026-10-25 02:59:52 +08:00
Laurence Manuel cae5efffa5 #4087: clear the highlight bar for No Color 2026-10-25 02:51:01 +08:00
Laurence Manuel fd6bb9696f #4087: match highlight icon to selected color 2026-10-25 02:50:34 +08:00
dotCooCoo e4f170dd67 Lists: Increase Indent and Demote take a list level past the last one to the last level
A paragraph whose list level is past the last one goes to the last level instead of overflowing, for Tab before a later line, Increase Indent and Demote.
2026-10-24 11:47:50 -07:00
NetterSilverandClaude Haiku 5.5 ee558313b0 Import LaTeX \^{} and \~{} as plain ^ and ~
Found while testing crates/formats/src/latex.rs:2212-2216 (accent command handling, fallback for an empty argument)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 18:45:44 +00:00
aiongg 9d1067fc0e Merge pull request #3562 from FedeOlivares/spanish-proofing
#3360, #3553: Spanish proofing (es-ES, es-MX, es-419, es-UY)
2026-10-24 14:43:59 -04:00
galagersharp-code b86373974e MCP: a client that starts before WordCraft keeps its server, and a malformed --connect fails at startup
Connecting eagerly made the server fail when the app wasn't open yet, and a
client marks a failed MCP server failed for its whole session: an unavailable
app now leaves a lazy remote that connects on the first call, warns about a
missing control key only once connected, and says which state it is in.
--connect addresses are checked before the server starts, so 127.0.0.1:notaport
reports a numeric port instead of retrying on every tool call.
2026-10-25 02:36:19 +08:00
dotCooCoo 4280a13a5b AutoFormat: Tab before a later line of a style-numbered list item takes it down a level
Tab at the start of a later line of a list item numbered by its style now takes the item down a level, as it does for an item numbered directly, instead of setting a left indent. A list level past the last one goes to the last level instead of overflowing.
2026-10-24 11:18:25 -07:00