mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-24 18:36:58 -06:00
sonar-fix/enforce-https
12
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3d8f8144d9 |
File: recover unsaved work after a crash (#518) (#586)
* M10: retain local crash recovery on current main * i18n: Backstage Recover in every catalog Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
575c5f98df |
.doc: open password-protected Word 97–2003 files (RC4, RC4 CryptoAPI, XOR) (#1079) (#1087)
docbin decrypts XOR-obfuscated, RC4 and RC4 CryptoAPI files after verifying the password against the stored verifier; the engine reuses the .docx password prompt errors. Written from [MS-DOC] and [MS-OFFCRYPTO]; synthetic tests only. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
eb54d88ec0 |
Mail merge: .xls recipients, legacy encodings, Filter and Sort (#778) (#885)
* #778: .xls and legacy-encoding recipient lists; filter and sort recipients - Recipient lists: legacy Excel 97-2003 .xls (BIFF8 reader on the cfb crate already in the tree); Excel 5.0/95 and password-protected .xls refused with a clear message; a CSV named .xls reads as text, an HTML one gets an error. .xls added to the picker filters (desktop, web, built-in browser) and drop handling. - Text lists (and plain-text import) detect legacy encodings when not UTF-8/UTF-16: GBK/GB18030, Big5, Shift_JIS, EUC-KR, Windows code pages (encoding_rs + chardetng, MIT/Apache); an `encoding` param overrides. - mailings.filterRecipients / mailings.sortRecipients: field, comparison (equal, notEqual, less, greater, lessOrEqual, greaterOrEqual, blank, notBlank, contains, notContains), value, And/Or joins; sort keys. Preview, Finish & Merge and labels use only the matching records in sort order. Edit Recipient List edits every record and keeps the filter. - Recipient List dialog: Filter and Sort section and a merge-order column; catalog rows; strings in every catalog; docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * i18n: Hungarian and French strings for this change Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Review fixes for #885: total order for recipient sort, Bulgarian strings - compare_cells now ranks numbers (compared numerically) before text (compared case-insensitively), so it is a total order; a column mixing 9, 10 and 1a could make slice::sort_by panic. Regression test sorts mixed columns. - Bulgarian catalog: the 24 Filter and Sort strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * vi: mail merge Filter and Sort strings Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
4b450f1b00 |
Open and save password-protected Word documents (#55) (#281)
* Clipboard pane: collect the last 24 copies and cuts and paste any of them again
Copy and Cut now also keep each item (rich fragment plus plain text) in a
session list of up to 24, newest first. Items over ~4 MB (estimated) are
skipped and the list stays under ~24 MB in total; copying the same thing
twice in a row adds it once. The system clipboard is untouched.
New commands: edit.clipboardPane (toggle the pane, also the Clipboard
group's dialog launcher), edit.clipboardItems (list with previews),
edit.pasteClipboardItem {index}, edit.pasteAllClipboard,
edit.deleteClipboardItem {index} and edit.clearClipboard. Pasting goes
through the normal paste path, so it is one undo step and respects Track
Changes. The pane lists the items with a short preview; clicking one pastes
it at the caret, and each has Delete, plus Paste All and Clear All.
Parity: 378 -> 379 of 428 features.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Layout > Arrange > Group: group pictures, shapes and text boxes into one object
Shift+click (or Ctrl+click) adds pictures, shapes and text boxes to the
selection; Group (arrange.group) combines the selected floating objects
into one group object that moves, wraps and resizes as one, with its
members keeping their places and scaling with the group. Ungroup
(arrange.ungroup) splits it back, where the group showed them, and
leaves them selected so Group puts them back together.
- Model: InlineObject::Group with the members' coordinate space (like
DrawingML a:chExt), so resizing the group scales its members.
- Layout draws the members inside the group's frame; hit testing
selects the group; text boxes in a group show their text.
- DOCX writes wpg:wgp with a:chOff/a:chExt and each member's a:xfrm,
and reads Word's groups (including inside mc:AlternateContent and
nested groups, flattened).
- select.addObject makes multi-selection scriptable (pos or Selection
Pane index).
Parity 378 -> 379 of 428 (88.6%); floor raised to 88.5%.
Fixes #265
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Paragraph: Asian Typography settings, with kinsoku and mid-word Latin wrapping in layout (#269)
Paragraphs gain the Asian Typography flags (line-breaking rules, wrapping
Latin text in the middle of a word, hanging punctuation, compressing
punctuation at the start of a line, and automatic space between Asian and
Latin text and between Asian text and numbers). They inherit through styles,
round-trip through .docx pPr, are set by the new para.asianTypography
command and appear on a new Asian Typography tab of the Paragraph dialog.
Layout applies the kinsoku lists (no line starts with closing East Asian
punctuation or ends with opening punctuation), breaks between any East Asian
characters when kinsoku is off, and breaks Latin words anywhere when word
wrap is off. Text without East Asian characters breaks as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Manage Styles: list, preview, delete and show or hide styles
Home › Styles › Manage Styles (styles.manage) opens a dialog from the
Styles pane listing the document's paragraph, character, linked and table
styles, sorted as recommended or alphabetically, with a preview line and a
description (type, formatting it sets, what it is based on). From there:
Modify… and New Style… reuse the existing dialogs and come back, Delete
removes custom styles, and two checkboxes show the style in the Styles
gallery (w:qFormat) or hide it from style lists (w:semiHidden).
styles.manage returns the list for scripts (name, type, builtIn,
inGallery, hidden, basedOn, priority, own formatting). styles.delete now
moves paragraphs, runs and tables that used the style to its base (in
every story), rebases styles built on it and takes a custom linked
character half with it; built-in styles are refused and it is undoable.
New styles.setVisibility {style, gallery?, hidden?} sets the flags, which
round-trip through .docx.
Fixes #266
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Create and modify your own table styles, saved in the .docx (#146)
New Table Style and Modify Table Style (Table Design > Table Styles) make
real table styles in the document's style sheet: name, based on, and
formatting for the whole table, the header row and banded rows (borders,
fill, bold, font colour). The table at the caret takes the new style, it
shows in the gallery, and other tools that read table styles by name
(pandoc's custom-style, for one) see it.
- engine: table.newStyle / table.modifyStyle with documented params
(regions take borders, fill, bold, italic, colour, size, font; bandSize;
para); table.style only accepts table styles now.
- doc/layout: table styles gain header-row and row-band cell borders,
row-band character formatting and rows per band, applied by layout.
- docx: tblStyleRowBandSize and firstRow/band1Horz tcBorders and rPr are
read and written, so custom styles round-trip.
- ui: a compact dialog from the Styles menu next to the gallery; table
style tiles redraw when a style changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Table styles: every region, a live preview, delete, custom styles first (#146)
Follow-up to the custom table styles work: the New/Modify Table Style
dialog now covers every region a table style formats, shows what the
style will look like, and table styles can be deleted.
- engine: table.newStyle / table.modifyStyle take lastRow (total row),
firstColumn, lastColumn and bandedColumns regions with the same props
as the others. New table.deleteStyle removes a custom table style:
built-in ones are refused, styles based on it are re-based on its own
base with its formatting folded in (so they look the same), and tables
using it fall back to Table Grid. Undoable like any command.
- doc/layout: table styles gain fill and cell borders for the first and
last column and total row, and fill, text and borders for column
bands; layout applies them in the standard order (column bands, row
bands, first/last column, header/total row).
- docx: firstCol/lastCol/lastRow/band1Vert shading and cell borders are
read and written.
- ui: the dialog's "Apply formatting to" lists all seven regions and a
small live preview table shows the pending changes; Styles menu gets
Delete Table Style (disabled for built-ins); the gallery shows the
document's own table styles first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Paste pictures from the system clipboard (screenshots, Copy Image, copied files) (#45)
egui's paste event only carries text, so a clipboard holding just a picture
sent no paste at all on Ctrl+V, and the ribbon/context-menu Paste only knew
WordCraft's own copy ("the clipboard is empty").
Desktop: the app reads the clipboard's picture through a new
Services::clipboard_picture (arboard, already in the build via egui-winit):
copied files first, else image pixels. Mod+V whose release arrives without a
paste event, and Paste from the ribbon or menus, insert that picture through
insert.picture (one undo step); text naming copied picture files inserts the
pictures. Pixel sizes are hostile: at most 16,000 px a side and 50 Mpx, the
buffer must match, files at most 200 MB.
Web: a document paste listener reads image files from the paste event when
there's no text and delivers them through the inbox like dropped pictures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Shape Format: Shape Effects — shadow, glow and soft edges (#275)
Shapes and text boxes get an effects model (outer shadow with colour,
transparency, blur, distance and angle; glow with colour, size and
transparency; soft-edge radius), the shape.effects command (presets,
custom values or null per effect, applied to every selected shape,
undoable, hostile numbers clamped), screen and PDF rendering with the
same banded approximation of a blur, DOCX a:effectLst read/write
(outerShdw, glow, softEdge) with a matching wp:effectExtent, and a
contextual Shape Format tab with Shape Fill, Shape Outline and Shape
Effects (Shadow, Glow, Soft Edges submenus) plus Arrange.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Open and save password-protected Word documents (#55)
Opening a password-protected .docx used to fail as a broken zip and showed
a blank document. Encrypted packages (an OLE compound file with
EncryptionInfo + EncryptedPackage) are now detected and decrypted with
ECMA-376 agile encryption, written from the public [MS-OFFCRYPTO] spec:
password key derivation with spinCount, verifier check, data-integrity
HMAC, AES-CBC in 4096-byte segments (AES-128/192/256 with SHA-1/256/384/512
on read; AES-256 + SHA-512, spinCount 100000 on save). Standard (Office
2007) and certificate encryption give a clear "not supported" error.
- docx: crypt module (decrypt, encrypt, is_encrypted, read_with_password),
PasswordRequired / WrongPassword / Encryption errors; hostile files are
bounded (stream caps, spinCount <= 10,000,000, checked lengths).
- engine: file.open/file.save take "password"; file.encrypt
{"password": string|null} sets or removes the save password (pure, so it
never lands in Repeat or recorded macros); a document opened with a
password is saved with it again; file.info reports "encrypted".
Passwords are held in a zeroizing type whose Debug prints ***.
- ui: a password prompt when a user-opened file needs one (scripts get the
error), File > Info > Protect Document with Encrypt with Password (typed
twice) and Remove Password; web downloads are encrypted too.
- deps: aes, sha1, sha2, hmac, base64, getrandom (wasm_js in the browser),
zeroize.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Table styles: Table Grid and Normal Table can't be deleted even when a .docx marks them custom
A .docx can set w:customStyle on any style, which made Word's built-in table styles look custom
and deletable. table.deleteStyle and the ribbon's Delete Table Style now also recognise them by
id and name, so no table is left on a style that's gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Manage Styles: deleting a table style runs Table Design's table style deletion
styles.delete on a table style now calls table_style::delete (#263): its tables take Table Grid,
styles based on it keep their look, and Word's built-in table styles are refused even when a
.docx marks them custom (the Manage Styles list reports them built-in too). Document::restyle no
longer retargets tables' own styles, so there is one place that does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Paste pictures: Mod+Shift+V (Paste Formatting) no longer pastes a clipboard picture
The key-release fallback that pastes the system clipboard's picture treated every Mod+V release as
Paste. It now requires plain Mod+V or the Paste key (no Shift, no Alt), with a unit test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Asian typography: test that kinsoku keeps a manual line break before a closing bracket
Manual line breaks end the line by cluster kind, not by break opportunity, so kinsoku's filter
can't drop one before 」; pin that with a layout test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Encrypted saves: the data-integrity HMAC key is hashSize bytes
MS-OFFCRYPTO §2.3.4.14 makes the HMAC salt as long as the hash (64 bytes for SHA-512); it was
16. Reading already accepted both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: password dialog and File › Info strings in every catalog
Rows for the Password and Encrypt with Password dialogs, the Protect Document section of
File › Info and the password status messages in zh-hans, zh-hant, ja, pt-br, es, uk, sr and
sr-latn.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Open and save password-protected Word documents (#55)
Opening a password-protected .docx used to fail as a broken zip and showed
a blank document. Encrypted packages (an OLE compound file with
EncryptionInfo + EncryptedPackage) are now detected and decrypted with
ECMA-376 agile encryption, written from the public [MS-OFFCRYPTO] spec:
password key derivation with spinCount, verifier check, data-integrity
HMAC, AES-CBC in 4096-byte segments (AES-128/192/256 with SHA-1/256/384/512
on read; AES-256 + SHA-512, spinCount 100000 on save). Standard (Office
2007) and certificate encryption give a clear "not supported" error.
- docx: crypt module (decrypt, encrypt, is_encrypted, read_with_password),
PasswordRequired / WrongPassword / Encryption errors; hostile files are
bounded (stream caps, spinCount <= 10,000,000, checked lengths).
- engine: file.open/file.save take "password"; file.encrypt
{"password": string|null} sets or removes the save password (pure, so it
never lands in Repeat or recorded macros); a document opened with a
password is saved with it again; file.info reports "encrypted".
Passwords are held in a zeroizing type whose Debug prints ***.
- ui: a password prompt when a user-opened file needs one (scripts get the
error), File > Info > Protect Document with Encrypt with Password (typed
twice) and Remove Password; web downloads are encrypted too.
- deps: aes, sha1, sha2, hmac, base64, getrandom (wasm_js in the browser),
zeroize.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Reland: catalog coverage 390/431 (90.5%) in ROADMAP and target-app parity
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Manage Styles: mark table styles with ⊞, which the interface fonts have (▦ has no glyph, #305's check)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
|
||
|
|
58ae1d5685 |
Use the browser clock for now_iso on wasm (comment and tracked-change dates) (#214)
Fixes #188. Co-authored-by: Fabio Lussi <198260595+itrycodinganditsbad@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
663904a2e2 |
Import Word 97-2003 .doc files (#28) (#145)
* D0+D1: open Word 97-2003 .doc — container, FIB, piece table text (#28) New wordcraft-docbin crate (L2) reads the OLE2 container via the cfb crate (MIT) and parses the FIB and piece table from the [MS-DOC] specification: text pieces decode as UTF-16LE or Windows-1252 (table moved to wordcraft-doc::encoding), subdocument CP ranges, and Word's special characters map to the model's (tabs, breaks, hyphens). .doc/.dot registered in OPEN_EXTS and the I/O dispatch. Encrypted and Word 6/95 files give clear errors; every length is checked and proptest truncation/flips never panic. * D2: .doc formatting — stylesheet, fonts, CHPX/PAPX FKPs, sprms (#28) Sprm decoder with operand-size rules (incl. sprmTDefTable/sprmPChgTabs exceptions and the Prm0 isprm table), bin tables + ChpxFkp/PapxFkp page lookup, STSH styles (StdfBase layout validated against real Word files: bchUpe == cbStd, sti 0 = Normal) and the SttbfFfn font table in both extended and self-delimiting flavours. The CP walk now splits runs on formatting changes and resolves paragraph styles: bold/italic/strike/ underline/caps/hidden, colours (Ico palette + COLORREF), half-point sizes, super/subscript, fonts, highlights; alignment, indents, spacing, keep options, outline levels. Unknown sprms are skipped leniently; an env-gated corpus test opens real files (styles + text asserted). * D3: .doc structure — sections, headers/footers, tables, lists (#28) - Sections: PlcfSed/Sepx with 16-bit measurement sprms (sprmSXaPage & co), explicit sprmSBOrientation with a width/height fallback; even/odd/first header and footer stories from Plcfhdd become document parts. - Tables: sprmTDefTable grids (operand length fixed to cb+1 per the spec), horizontal merges (sprmTMerge), vertical merges (sprmTVertMerge), cell shading (sprmTSetShd / sprmTDefTableShd80), cell widths, valign, row height/header/cant-split, and table borders (sprmTTableBorders80 Brc80s). - Lists: PlfLst (LSTF + appended LVLs) and PlfLfo (LFO + LFOLVL overrides) resolved into the numbering model; sprmPIlvl/sprmPIlfo mark paragraphs. - Combined fixture (intro + table + list + 2 sections + header) round-trips through Document JSON; hostile proptests now mutate the structured fixture. * D4: .doc notes, fields, bookmarks and inline pictures (#28) - Footnotes/endnotes: PlcffndRef/PlcfEndRef reference CPs paired with the PlcffndTxt/PlcfEndTxt story ranges; each story walks into a Footnote or Endnote part and the 0x02 anchor becomes a NoteRef object. - Fields: 0x13/0x14/0x15 state machine in the walker — instruction text is hidden, the cached result kept; HYPERLINK fields keep their result as linked runs, everything else becomes one Field object. - Bookmarks: SttbfBkmk names + Plcfbkf/Plcfbkl CPs become BookmarkStart/ BookmarkEnd objects at their anchors. - Pictures: sprmCPicLocation points into the Data stream; the PICF header gives the display size (dxaGoal/dyaGoal scaled by mx/my) and the payload is format-sniffed (PNG/JPEG/GIF/BMP; metafiles are dropped with a warn). - Hostile proptests now truncate/flip a fixture with all of the above. * D5: .doc import hardening, caps and roadmap (#28) - Cap inline pictures at 5 000 per document. - Engine test: .doc/.dot bytes dispatch through the Word 97-2003 reader (garbage must error, never panic or open empty). - ROADMAP: Word 97-2003 import row (60% — text, formatting, styles, sections, headers/footers, tables, lists, notes, fields, bookmarks, raster pictures). Real-corpus validation stays opt-in: WORDCRAFT_DOCBIN_FIXTURES=<dir> cargo test -p wordcraft-docbin corpus_files_open. * Corpus fixes: compressed-piece FC space, PAPX query point, none-level labels (#28) Validated against two real files (kept locally under plan/word/fixtures, gitignored): a 1 MB Lorem-ipsum sample and a 2003 styles/lists/table sample. - FKP bin tables and pages address the WordDocument stream in true byte offsets; a compressed piece stores its base fc doubled (byte = fc/2), so fc_of_cp now unwinds the doubling. Before, every PAPX/CHPX lookup in compressed (cp1252) files landed on the wrong entry — tables collapsed to one row, lists and heading styles were lost. - The paragraph-properties query now uses the paragraph mark's own FC (inside the paragraph) instead of the FC after it, which picked up the next paragraph's FKP entry. - Fixture papx entries re-keyed to paragraph-start CPs, the real rgfc convention. - Counters::next_label returns nothing for NumFormat::None levels (Word's numFmt none shows no label; also fixes a latent docx-reader case). - Symbol/Wingdings bullet chars map to Unicode glyphs (• ▪ ○ ●). With this, both corpus files render headings, styles, nested and numbered lists, the tables and the PAGE field correctly; metafile-wrapped images and OLE objects stay dropped by policy (logged). * docbin: bound the text walk by real text; dedupe and cap pictures (#28) Hostile CP numbers no longer drive the work: - header/footnote/endnote story ranges from the Table stream are clamped to their subdocument (ccpHdd/ccpFtn/ccpEdn) with checked base additions, and every walk stops at the last piece's CP; a story spanning [0, 4e9) used to push billions of U+FFFD; - characters are found by binary search over the pieces (PieceTable::char_at), and an unreadable stretch (gap, truncated or out-of-stream piece) is skipped in one step rather than one U+FFFD per CP; the old per-character scan was O(chars x pieces); - the piece CPs are made non-decreasing at parse, so inverted pieces are empty and binary search stays sound; surrogate pairs now decode; - note references are looked up by binary search; the style's CharProps is borrowed, not cloned, per character; - pictures are interned by fc_pic (5000 anchors on one picture share one media entry and one copy) and the bytes copied per document are capped at 256 MB. Each fix has a crafted-bytes regression test; against the previous code the story, piece-claim, last-piece and many-pieces tests hang (killed at 60 s) and the shared picture test copies the picture once per anchor (80 GB). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docbin: checked offset arithmetic in lists, sections and table rows (#28) - list.rs: PlfLst/PlfLfo/LVL offsets (at + 2 + cLst*28, at + 4 + lfoMac*16, off += len, ...) saturate, which matters on 32-bit targets (wasm32) where fc + n can overflow usize; the LFO override path no longer indexes abstracts directly. - sections.rs: the Sepx range (fc_sepx + 2 + cb) is checked; Plcfhdd is read only as far as the stories a capped section count can use (it used to reserve lcb/4 entries). - table.rs: sprmTDyaRowHeight = i16::MIN negated in i16 (overflow panic in debug builds); now -(v as f32). Regression tests: the row height test failed before (overflow panic with debug assertions, a 0 pt height without); the list test pins the saturating offsets (it only overflowed on 32-bit targets). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Format the merged OPEN_EXTS list Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
a45c8a41de |
DOCX: keep a .docm's macros and signatures on save; right content types for .docm/.dotx/.dotm (#172)
* DOCX: keep a .docm's macros on save; write .docm/.dotx/.dotm content types Saving a .docm deleted its VBA project and wrote the plain-docx main content type under the .docm name, which Word rejects; .dotx was written as a document, and .dotm could be opened but not saved. io_ext sent every Word extension to wordcraft_docx::write, which always emitted a .docx package, and the reader never kept the VBA parts. - wordcraft_docx::Flavor (docx/docm/dotx/dotm) picks the main part's content type; write_as(doc, flavor) writes it, write() stays .docx. - The reader keeps word/vbaProject.bin (and the vbaData.xml it relates to) as opaque bytes in Document::passthrough, the mechanism custom.xml already uses. Macro content is never parsed or run. The writer emits them, with their relationships and content types, only for .docm/.dotm; a .docx/.dotx leaves them out cleanly (as Word does) and the open document still holds them for a later Save As .docm. - Empty projects, missing or malformed VBA data, duplicate or odd relationships never panic and never stop the document from opening; part sizes stay under the existing package limits. - .dotm joins the open/save lists, and saving to .docm/.dotx/.dotm binds the session to that file like .docx. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * DOCX: carry vbaData.xml as opaque bytes, never parse it The first cut gated word/vbaData.xml through xml::parse on read and on write. That parser rejects UTF-16 (a valid XML encoding), so a UTF-16 vbaData.xml was silently dropped and the saved .docm kept vbaProject.bin without its VBA data; and it is lenient (no end-name check, auto-closes at EOF), so malformed data passed the gate anyway. WordCraft is a passthrough for macro parts: it should emit exactly what the user's file had. Now vbaProject.bin and vbaData.xml are both carried byte for byte (bounded by the reader's existing size limits); only the relationship part is read, to find the VBA data. A relationship to a missing VBA data part keeps the project and writes no dangling link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * DOCX: read UTF-16 XML parts instead of dropping them xml::parse rejected any part with a UTF-16 BOM, and Package::rels swallowed that error as "no relationships". A UTF-16 word/_rels/vbaProject.bin.rels (valid per OPC) therefore hid vbaData.xml, and saving the .docm dropped it and its relationship. parse now transcodes UTF-16 LE/BE with a BOM to UTF-8 (lossy, bounded at 1.5x, never panics on odd lengths or unpaired surrogates), so every part benefits. Unreadable relationship parts are now logged rather than silently treated as empty; the macro project is still kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Web Save keeps the opened Word format (.docm stays .docm) Browser Save (file.save without a path) always downloaded <stem>.docx, so an opened .docm/.dotm was written as a plain .docx and lost its macros on an ordinary Save. Save and the Save As suggestion now default to the opened file's extension when it is a Word package (.docx/.docm/.dotx/.dotm, via engine io::is_word_package), else .docx. Macros are only dropped on an explicit conversion to a macro-free name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * DOCX: carry every part a VBA project relates to, so signatures survive A signed .docm/.dotm lost its VBA signature on save: the reader kept only vbaProject.bin and vbaData.xml, and the writer rebuilt vbaProject.bin.rels with the wordVbaData relationship alone. Under a "signed macros only" policy the macros then stop running, although the project bytes (which the signature covers) were unchanged. Generalised instead of special-casing signature types: the reader now keeps every internal part vbaProject.bin.rels points to (VBA data, the legacy/Agile/V3 signatures, anything else) as opaque bytes, with its relationship type and its content type from [Content_Types].xml, in a small manifest in Document::passthrough. .docm/.dotm re-emit them all; .docx/.dotx still drop the whole VBA set. Bounded at 64 parts; targets outside word/, relationship parts, the project itself, missing parts and parts the writer produces itself are skipped, never shadowed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * DOCX: match VBA part names case-insensitively; keep colon names relative Two edge cases in carrying a VBA project's related parts: - The reader's word/ prefix check was case-sensitive, so a relationship to /WORD/vbaData.xml (the same part under OPC's case-insensitive part names, and found by Package::get) was skipped and lost on save. The directory is now matched case-insensitively and normalised to word/, the name the writer uses. - Stripping word/ from /word/sig:legacy.bin gave Target="sig:legacy.bin", which parses as a URI scheme rather than a package part. A target whose first segment has a colon is now written as "./sig:legacy.bin"; ordinary names keep the plain form Word already accepts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c0ccda288c |
Find/Replace skips text deleted under Track Changes (#89); a failed command keeps undo history (#153)
* Find/Replace: skip text deleted under Track Changes With Track Changes on, Replace marks the match deleted and leaves it in the paragraph text, then inserts the replacement in front of it. The search helper matched the raw paragraph text, so the next step found that same deleted "cat" right after the caret and replaced it again: four Replaces on "one cat two cat" gave "one dogdogdogdog two cat" and `remaining` never dropped. `search` (used by Find, Find Next/Previous, Replace, its `remaining` count and Replace All) now skips any match that overlaps a run marked deleted, so deleted text is never found, as in Word. A match that would run across deleted text is skipped too: that string is not in the document as it now reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Undo: a failed command keeps the redo stack and the oldest undo step `run` checkpoints a mutating command before running it, and the checkpoint clears the redo stack (and, once the history is at its limit, drops the oldest step). When the command then failed, the error path put back the document, selection and typing group and truncated the history, but the cleared redo stack stayed cleared. Insert, Undo, then any command that fails on its parameters (`text.insert {}`, `para.align {value:"bogus"}`) made the undone work unrecoverable. At the limit, truncating to the old length also kept the failed command's empty step and lost the oldest real one. The checkpoint now hands back what it removed, and the error path restores both. join_next is deliberately left consumed: the drag that set it re-arms it every frame, and restoring it would merge the next unrelated command into the drag's undo step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Find/Replace: find live text next to a tracked deletion The previous fix rejected any regex match that overlapped tracked-deleted text, but `find_iter` had already consumed those positions, so a valid match overlapping the rejected one was never tried. "aaa" with the first "a" deleted reads "aa", yet Find "aa" found nothing (raw 0..2 was rejected and the scan resumed at 2) and Replace All replaced nothing. Searching the raw text also let deleted characters decide word boundaries: deleting the space in "cat fish" still matched whole-word "fish". `search` now builds each paragraph as it reads, without deleted runs, and searches each stretch of live text separately with `regex_automata::Input::range`. A match stays within one stretch, as before, and `\b`, `^` and `$` see the neighbouring live text, so a deletion neither creates nor hides a word boundary. `regex::Regex` cannot bound a search's end without slicing the haystack, which turns the slice end into a word boundary; regex-automata is the engine `regex` already uses (0.4.18, already in the lockfile), so no new code is built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Undo: a failed command restores history changed by nested commands The previous fix had the checkpoint report what it evicted and put that back on failure, but it only saw the outer command's own checkpoint. Commands run nested commands, and a nested one that succeeds checkpoints too. `file.inspect {"remove":["comments","revisions"]}` in a comments-only document runs `review.deleteComment` (allowed) and then `review.acceptAll` (refused). With 499 undo steps, the nested checkpoint evicted the oldest step and the rollback, truncating to 499, kept the outer command's empty step instead. At 500, only one of the two evictions came back. Nested undo, redo and version restore change the stacks in ways a length cannot describe either. Undo steps are now `Arc<Undo>`, and `run` snapshots both stacks whole before a mutating command and puts them back on failure, so the history is exact whatever ran inside. The snapshot copies one pointer per step, under 1,000 refcount bumps and an 8 KB allocation at MAX_UNDO, beside the two document clones every mutating command already makes. Undo and Redo are not mutating commands, so a top-level Undo still moves its document out of the step without cloning it. Only an undo nested inside a mutating command (a macro) clones, because the snapshot still holds the step. This replaces the eviction-return plumbing, so `checkpoint` is back to its original form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: compare undo timelines without comment timestamps failed_command_restores_history_changed_by_nested_commands builds the same session twice and compares their documents. Comments are stamped with the time to the second, so when the clock crossed a second between the two builds the documents differed and the test failed (about 1 run in 12). Clear the stamp in the compared copies; the test still checks every other part of each document in the timeline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Undo: wrap the typing-parity undo step in Arc after merging main Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
72262a2ea9 |
M3/M6/M12: PDF export (krilla), TXT/MD/HTML/RTF/ODT import+export, citations & bibliography, index, table of figures, cross-references, tools (repeat, macros, AutoCorrect, compare, accessibility, inspector, protection, versions, Quick Parts)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c29182e8bc |
M1: DOCX read/write (OOXML) wired into open/save; floating objects; picture & shape tools; mail merge, envelopes, labels; contextual spacing; export without proofing marks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d1a0ed39c0 |
M7.1: proofing — spelling with suggestions, grammar checks, squiggles, F7, right-click menu, thesaurus; web app; hyphenation dictionary
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b8dd46ccb5 |
M0.7: engine — session, undo, 200+ commands, Word feature catalog, samples
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |