* fix(docx): collect an embedded object's relationship ids in a set on save
* style: rustfmt
---------
Co-authored-by: Martin Ball <martin.ball@ucscaregroup.co.uk>
* Charts: an automatic series colour follows the series' c:idx (#5963)
A series without its own colour took the accent for its place in the
file. Word picks it by the series' c:idx (c:order doesn't matter), so a
chart whose series aren't numbered 0, 1, 2... in file order, such as a
lone series with idx 2 after a deletion, got different colours.
c:idx is read strictly as the unsignedInt the schema says; a missing or
invalid value falls back to the place in the file.
* Charts: the renumbered test helper fails if ser's layout changes (#5963)
---------
Co-authored-by: kiwi4boy <kiwi4boy@gmail.com>
Co-authored-by: Martin Ball <martin.ball@ucscaregroup.co.uk>
## What changed
Word stores paragraph indents (`w:ind` left/start, right/end, hanging, firstLine) in 16 signed bits and paragraph
spacing (`w:spacing` before/after) in 16 unsigned bits. A file that writes a larger plain integer is read by Word
modulo 2^16: `w:left="66256"` is a 720-twip (0.5") indent in Word. We read the full value, so such a paragraph was
indented by about 46 inches and its text was pushed off the page.
- `crates/docx/src/units.rs`: new `tw_i16` / `tw_u16` readers. A plain integer keeps its low 16 bits (two's
complement for indents, so 66256 -> 720 and 65535 -> -1); values with a unit or a fraction read as before.
- `crates/docx/src/read/props.rs` and `read/styles.rs` use them for `w:ind` and `w:spacing` before/after, in direct
and style formatting.
- In-range values read exactly as before.
## Testing
- `crates/docx/tests/it/twips_wrap.rs`: synthetic documents with out-of-range indents and spacing in paragraph and
style properties check the wrapped values; in-range values and values with units are unchanged.
- Unit tests for the two readers; `cargo xtask ci` passes.
Found by a real-file corpus comparison against Word's own PDFs: a file whose indents exceed 16 bits shows its text on
the page in Word and off the page here.
Co-authored-by: tpawley2001 <268951182+tpawley2001@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Martin Ball <martin.ball@ucscaregroup.co.uk>
## What changed
The DOCX writer's content-control planner (`crates/docx/src/write/sdt.rs`) decides whether a control that
covers a whole paragraph is written block-level (`w:sdt` around `w:p`) or run-level (inside the paragraph).
It only treated the control as covering the whole paragraph when nothing but control-end markers followed
its end. Bookmark, comment and permission range markers after the end now count as "nothing" too, so the
control stays block-level and the markers stay at the end of its paragraph.
## Why
Word writes a body-level `_GoBack` bookmark (`<w:bookmarkStart w:name="_GoBack"/><w:bookmarkEnd/>` directly in
`w:body`) in nearly every file it saves. When the last body-level content control was followed by it, the
reader put the bookmark at the end of the control's paragraph, and the writer then turned the control (and
any controls nested in it) into run-level `w:sdt`s inside that paragraph. For a repeating section
(`w15:repeatingSection` / `w15:repeatingSectionItem`), which must wrap whole paragraphs or rows, Microsoft
365 Word then refuses to open the saved file ("the file appears to be corrupted"). Plain nested block
controls changed what they cover the same way.
Found by the real-file corpus: Word reported our saved copy of a form with a repeating section as corrupted.
## How it was tested
- New tests in `crates/docx/tests/it/nested_block_controls.rs` (synthetic packages): a repeating section around
one paragraph, plain nested block controls, nested block controls in a table cell, a run-level control that
must stay in its paragraph, and repeating sections / block controls followed by a body-level bookmark (with
and without data bindings) all round-trip with the controls at the same level and the text kept.
- `cargo xtask ci` passes.
Co-authored-by: tpawley2001 <268951182+tpawley2001@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* #5843: resolve LibreOffice semicolon-separated font fallback lists to first installed family
* #5843: address review comments on font fallback list resolution
* #5843: verify fallback list text on cache hit and accept Times New Roman in tests
* #5843: bound the fallback lists read from a DOCX and reuse their classification
A document could name thousands of fallback families in `w:ascii`/`w:hAnsi`.
Nothing bounded that value, and the name-based rules (roman classification,
Thai family, named substitutes) then scanned every candidate for each shaped
piece, so opening the document took seconds.
`w:ascii` and `w:hAnsi` now keep the leading families within MAX_FONT_NAME
bytes, cut at a `;` so no name is split, as `w:cs`/`w:eastAsia` already bound
their names. The family a real list would pick is kept: a LibreOffice list
names a few families, well under the bound.
The Thai-family and named-substitute answers are worked out once per list and
kept in the fallback-list cache, keyed by the list text, so shaping no longer
rescans candidates for every piece. Resolution still reads every candidate.
A 500-paragraph document whose font names 20,000 candidates laid out in 10.8 s;
bounded on read it takes 17 ms.
Tests:
- a hostile fallback list keeps its leading families, cut on a `;`
- a real LibreOffice list is read whole
- a later Thai or named fallback still classifies, and lists are cached
Text nodes were decoded without line-end normalisation, so a file
saved with Windows line ends kept the CR of each CR LF: a line break
inside w:t read as two spaces. Text and CDATA now go through
quick-xml's xml_content, which reads CR LF and a lone CR as one line
feed; a CR written as stays a CR. Attribute values were already
normalised.
Co-authored-by: Martin Ball <martin.ball@ucscaregroup.co.uk>
A file that turns an inline picture but writes no wp:effectExtent (or a zero
one), as other tools do, makes Word keep the picture's line to its frame and
let the turn overhang it, cut at the line's top and bottom: the frame as it
is in Word 2007 layout (compatibilityMode < 15), with width and height
exchanged from 45 to 135 and 225 to 315 degrees in Word 2013+ layout.
WordCraft always reserved the exact rotated bounds (Float::spin_pad), which
is what Word's own files encode through their effect extents, so the text
after such a picture sat up to 34 pt too low (docxide-pdf
standalone-picture-compat12-* / compat15-angle45/135).
The docx reader now marks such objects (Float::bare_turn, cleared when the
object is turned again); the inline room comes from Float::line_pad, which
display uses too to place the frame in it, and the line clips the picture's
overhang. The writer keeps writing no turn overhang for them, so a save
round-trips. Word-saved pictures are unchanged.
Co-authored-by: Naxvog <4963183+naxvog@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* WC6-07: preserve paragraph text direction
* Resolve gaps summary merge
* WC6-07: retain paragraph direction variants
* WC6-07: preserve strict paragraph direction
* Update lockfile for DOCX test dependency
* Close paragraph direction implementation
* Format paragraph direction imports
Compatibility-mode documents store text boxes as VML only. The reader
ignored v:textbox's text direction, so a header text box turned 270
degrees was drawn level. It now reads it as Word for Windows does:
mso-layout-flow-alt:bottom-to-top is vert270 whatever layout-flow says;
otherwise layout-flow:vertical or vertical-ideographic is vert; anything
else stays horizontal.
Co-authored-by: Naxvog <4963183+naxvog@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Martin Ball <martin.ball@ucscaregroup.co.uk>
A Strict document opened with its text and no shapes at all. Strict puts
shapes, groups and canvases (wsp, wgp, wpc) and their parts in its one
WordprocessingDrawing namespace, which the reader maps to wp:, while the
drawing reader looks for Word 2010's wps:, wpg: and wpc: names. Word also
writes a Strict text box's text in wne:txbxContent.
The XML reader now names those elements as Transitional documents do,
from the element and its parent (xml::element_prefix), and reads a text
box's content from wne:txbxContent and from the schema's own
txbxContent. Saving writes them as Transitional.
Claude-Session: https://claude.ai/code/session_01LvG6E1XTfHtZDbfYwfREhR
Co-authored-by: Filip Strajnar <filip.strajnar@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Martin Ball <martin.ball@ucscaregroup.co.uk>
* #5267: a linked picture shows and follows its link; DOCX keeps it on the drawing
A link put on a picture from its context menu was stored, but nothing showed
it: hovering the picture gave no tip, and Ctrl+click picked the picture for a
copy instead of following the link. Now the hover tip and Ctrl/Cmd+click work
on a linked picture, shape or text box, in line or floating, as on linked text.
DOCX: Word keeps a picture's hyperlink on the drawing (wp:docPr/a:hlinkClick,
ECMA-376 Part 1 20.4.2.5). It is now read (a w:hyperlink around the run wins)
and written beside the w:hyperlink, sharing one relationship. A link to a place
in the document stays the w:hyperlink w:anchor only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #5267: ink and Office 2016 charts take their drawing's link; Ctrl+click over text on a Behind Text picture stays a text click
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #5267: rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #5267: a chart or ink takes the link of the choice it was read from; Ctrl/⌘+click in the docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #5267: the decoy choice in the chart-link fixture needs a namespace no reader knows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #5267: inside a linked text box, hover and Ctrl/⌘+click take the box's link
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #5267: the picture-link test sets the image's fx after merging main
Main gave InlineObject::Image picture effects (fx); the round-trip test this PR adds built
an image without them and no longer compiled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Martin Ball <martin.ball@ucscaregroup.co.uk>
* WC6-06: preserve adjustRightInd in DOCX
* WC6-06: honor grid indent defaults and XML order
* Update lockfile for DOCX test dependency
* Format DOCX ordering assertion
* Keep main lockfile for paragraph property PR
* PR 5384: tighten grid indent docs and regression
* PR 5384: match paragraph check formatting
* docx: keep a paragraph's text alignment
w:textAlignment (ECMA-376 17.3.1.39) was not read, so a save dropped it. Word sizes a line by it when a
list's bullet is larger than the text: without it, those lines grow and every page after them moves.
ParaProps gains text_alignment, read and carried through styles, and written back after w:jc. Kept for
the file only; the layout does not use it yet.
* contributors: add entropybeater
---------
Co-authored-by: Brian <255903314+entropybeater@users.noreply.github.com>
Conflicts kept both sides. Where the two branches met:
- Para.mark: both added it. It stays CharProps (ODT needs it); HTML's
`<p> </p>` converts its Fmt, and to_doc reads it in one place.
- Arrowheads: the PR's line ends (top left to bottom right) with main's
arrowhead styles.
- inline_rect: the PR's depth below the baseline with main's rule room.
- Spacing and Cell literals take main's new fields.
- The zip entry guard (#3660) also covers the encrypted ODT paths, which
parsed a package before it; regression test in tests_more/encryption.rs.
- chunks_exact to as_chunks in odt/frames.rs and odt/path.rs (clippy).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User fields (text:user-field-decl, text:user-field-get: LibreOffice's
report templates, 58 documents of the corpus) were read as custom
document properties and written back as meta:user-defined properties
with text:user-defined fields: the look and values stayed, the kind
didn't. Custom properties now remember being user fields
(CustomProp::odf_user_field) and are written back as
text:user-field-decls with their values and text:user-field-get fields.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
* DOCX: VML horizontal rules take their line
A web page's <hr> pasted into Word is a VML rule: `v:rect` with
`o:hr="t"`, often `width:0`. WordCraft took the width as written and
drew a 1 pt dot where Word draws a line across the page.
Measured on Word with a synthetic document: a rule with no width and
no `o:hrpct` spans the line inside the paragraph's indents; `o:hrpct`
is a share of it in tenths of a percent (500 = half); `o:hrpct="0"`
keeps the shape's own width; `o:hralign` places a shorter one (left,
centre, right); `fillcolor` and `stroked` apply as to any VML shape.
WordCraft now matches each case within 0.1 pt.
- wordcraft-doc: ShapeExtra::hr (HorizontalRule: share, alignment,
Word's standard look).
- wordcraft-docx: `o:hr`, `o:hrpct`, `o:hralign`, `o:hrstd` read; a rule
is written back as the same VML rule (DrawingML has none), and Word
opens the re-saved file with the same lines.
- wordcraft-layout: a rule takes its whole line and is drawn at its
share; measured for a table column it asks for no width, so it
follows its column. (Word draws a rule in a cell at the page's text
width, centred on the cell and past its edges; it stays in the cell
here.)
docs/file-format-parity.md is updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Horizontal rules: only an inline, shown rule is one
Review: the rule arm of the writer ignored the shape's placement, so a
rule imported with position:absolute or visibility:hidden came back
inline and visible. Only a rule in the text and shown is read as a rule
now (a pasted <hr> is one); a positioned or hidden one stays a plain
shape, which keeps both when saved. The writer checks the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Horizontal rules: a see-through fill is kept; column measuring asks for the right width
Review: `write_hr` wrote only the fill's colour, so a rule with `v:fill
opacity` came back opaque after a save; the opacity now goes back as `v:fill`.
Review: measured for a table column, every rule asked for no width, so one of
its own width (`o:hrpct="0"`) could end up clipped by a narrow column; it now
asks for its width. AutoFit to contents measures on a line of its own width
that the rule took for a real line, so a rule sharing its line made the
column as wide as it could go; any measuring width now counts.
`Placed::Shape` boxes its extras: with the rule, `Placed` went past clippy's
large_enum_variant limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Horizontal rules: placed in what the line has left; rotation and flips kept on save
Review: a whole-line rule ignores a first-line indent or a line narrowed by a
float. Measured in Word, it does too: the width stays the share of the
paragraph's width and the rule runs past the margin, or under a float on its
right. What did differ was the placement: Word centres and right-aligns a rule
in what the line has left, with a first-line indent moving the end of that room
along, and a right-aligned rule beside a float was drawn off the page here. The
display now places it that way (`ParaLayout::rule_room`).
Review: a save dropped a rule's rotation and flips; they now go back in its
style, as Word writes them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: VML horizontal-rule revision row at the top of the history
---------
Co-authored-by: Daniele Mori <Dinasty@hotmail.it>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
A page layout's background picture (`style:background-image`, §17.3:
LibreOffice's report templates' world map) was dropped. It is now
WordCraft's page picture (Design › Page Color › Fill Effects) and is
written back with its picture:
- repeated: tiled;
- stretched: stretched;
- not repeated: centred at its own size, a new fill that the screen
renderer and PDF export draw in the middle of the page. DOCX writes
it stretched, as VML has no centred fill.
LibreOffice's report template with the map goes from 11,719 to 3,602
differing pixels, and shows the map after a save.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
* #3660: refuse a package that declares too many zip entries before zip parses them
zip::ZipArchive::new parses and keeps every central directory header
the end record declares before its caller can look at len(). A ZIP64
end record can declare one per 46 bytes of the file: a 47 MB package of
one stored file listed 1,000,000 times takes zip 3.0.0 about half a
second and a large transient allocation, and len() then says 1, since
the copies share a name, so the docx reader's MAX_ENTRIES cap never
fires. ODT and spreadsheet packages had no cap at all.
wordcraft_doc::zip_guard::check finds the end records the way zip finds
them (backwards; the ZIP64 record from the locator's offset on; a plain
record when it has no entries or a central header before it) and refuses
a package declaring more than the reader's limit. The docx package
reader, the ODT reader, the spreadsheet reader (mail-merge data) and the
template-title probe call it first, with 20,000 entries as the limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* formats: keep the extensions doc line on EXTENSIONS
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word's Font › Emphasis mark (a mark over or under each character, used
to stress East Asian text) wasn't modelled. DOCX `w:em` and ODT
`style:text-emphasize` were dropped.
- Model: `CharProps::emphasis` (dot, comma, circle, dot below, or none
to turn off a style's).
- Layout: a mark is drawn over (or under) each character, and the line
is made taller to fit it.
- `format.emphasisMark {"mark": …}` sets it, and Format state reports
it. The catalog lists it under Home › Font.
- DOCX: `w:em` is read, and written in its place in `w:rPr` (after
`w:cs`, before `w:lang`).
- ODT: `style:text-emphasize` maps to it: an accent is the comma, a
disc the dot, a dot below the under-dot.
Also fixes two problems from the previous commit (ODT variables):
- Field updates blanked a SET field's stored result. The layout
already draws nothing for SET, so its result stays as it was, as
`ask_and_set_fields_draw_nothing` pins.
- A clippy lint (manual char comparison) failed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
An alternative format chunk holding a Word document with w:matchSrc keeps its
own styles: a style the document already has comes along under a new id, the
chunk's document defaults (and Word's, where they say nothing) go into its
base styles, and its unstyled paragraphs use its own Normal. Without
w:matchSrc the document's styles win, as before. Adopting another document
now also renumbers table styles and the revisions of table, row and cell
property changes.
A document whose chunk shares style names with it (Normal with an exact line
height, an italic Subtitle) ran a page longer than in Word: the chunk's tables
took the document's styles. It now keeps its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Word brings a document's alternative format chunks (w:altChunk) in where they
stand when it opens the file; WordCraft left them out, so a document made of
one (as generators and mail-merge tools write them) opened empty.
- docx: read_with/ReadOptions with a ChunkImporter for formats other than
WordprocessingML; nested Word documents are read here (at most 4 deep,
256 chunks and 512 MB of chunk bytes a file, XML depth counted on).
- doc: Document::adopt (moved from paste) brings another document's media,
lists, styles, notes, comments and revisions along under new ids; pictures
in groups and charts are relinked. Insert > Text from File uses it and now
keeps the file's pictures and lists.
- engine: chunks in MHT, HTML (in their charset), RTF and plain text go
through the format importers.
- formats: MHT web archives open (MIME, quoted-printable/base64, charsets,
pictures by Content-Location or cid). HTML reads style element rules
(simple selectors, cascade, budgets), paragraph margins, indents, line
height, cell widths and borders; an opened page takes Word's web-page
defaults (Times New Roman 12 pt, single lines, black headings, automatic
spacing). html::decode knows the single-byte Windows code pages.
docxide-pdf suite: croatian_regulations_altchunk goes from one empty page to
Word's 8 pages, SSIM 2.7% -> 78.7%; no other document changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Word draws an arrowhead 2, 3 or 5 times the line's width long and wide
(small, medium, large; `a:headEnd`/`a:tailEnd` `len`/`w`), a line under
2 points as a 2-point one. Every arrowhead was drawn one size and saved
as a medium triangle: the kind, width and length are now read (DrawingML
and VML), kept and written back, and the size is drawn as Word does.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
LibreOffice writes an embedded object's replacement picture in its own
metafile format, which WordCraft can't decode. So an embedded
spreadsheet (`draw:object`, ODF 1.3 §10.4.6) or OLE object
(`draw:object-ole`, §10.4.7) vanished on open, and saving lost it.
Such objects are now kept whole: their files, media type (from the
manifest or the sub-document's body) and replacement picture.
- Display: the replacement picture when WordCraft can show it, else a
placeholder of the object's size drawn in code (ruled for a
spreadsheet).
- ODT save: written back as the same object, with its manifest entries
and original replacement. LibreOffice renders a round-tripped
spreadsheet pixel-identically.
- Document model: the files live in `passthrough` under `odf-object/`,
linked from the picture's `ole` by a marker. The DOCX writer skips
the marker and writes the picture alone.
Flat ODT, a fix to the chart commit: inline sub-documents lacked the
required `office:mimetype`, so the schema rejected flat files holding
charts. Sub-documents now go inline as full `office:document`s (media
type, styles, settings) and are split back into their files on
opening. Replacement pictures and OLE files go inline as base64. A
picture's data is matched to the element that holds it, no longer to
the last `draw:image` before it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
* #4848: corporate authors in bibliographies
A Word source whose author is an organisation (Create Source › Corporate Author)
stores it as `b:Corporate`, which the reader ignored: the source opened with no
author, citations fell back to the title and bibliography entries started with
the year. A bare name typed into Author was split into "Organization, W. H.".
- Source gains `corporate`, read from and written as `b:Corporate` (in place of a
name list: a role holds one or the other, ECMA-376 CT_NameOrCorporateType).
- Citations and bibliography entries write it as it is in every style; it gives
the source's tag and its place in an author-sorted list, and untagged sources
are matched by it.
- APA puts its full stop after a group author ("World Health Organization.
(2021).") instead of relying on the initials for it.
- Create Source has a Corporate Author box under Author; Edit Source opens with
it checked when the source has one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvG6DxS4tKmWSLCiDPvb4G
* Match untagged sources by corporate author before personal author
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* M12: preserve custom-property bookmark links (#4767)
* M12: preserve bookmark links across property renames (#4767)
* M12: align custom-property preservation status dates (#4767)
* docs: keep the gaps.md note in the status line and revision table
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #4696: built-in heading styles have their outline level without w:outlineLvl
Some producers write "heading 1"…"heading 9" in styles.xml without w:outlineLvl. Word
gives those built-in styles their outline level anyway (checked: Word for Windows reports
OutlineLevel 1 and 2, whatever the style id), so their headings show in the Navigation
pane. WordCraft read no level, and the Headings tab stayed empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #4696: review — w:customStyle="on" marks a custom style too
A custom style named "heading 4" with customStyle="on" (ST_OnOff) was taken for Word's
built-in heading and got an outline level.
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>
* #4548: comment ranges inside footnotes and endnotes are kept on read
read_opened read footnotes.xml and endnotes.xml before comments.xml, so
a comment range inside a note looked its id up in an empty comment map
and was dropped. Comments are now read first. read_comments uses nothing
the notes set up.
Regression test: tests/it/note_comments.rs (synthetic markup).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: file-format-parity row for comment ranges in notes (#4548)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Adds the key that Apache POI's Excel-written test file
protected_passtika.xlsx (password "tika") derives, computed from
[MS-OFFCRYPTO] §2.3.4.7 independently of the implementation, so real
Word 2007 files stay pinned.
Also adds the contributor's people.toml entry.
Co-authored-by: Bra1nZ <bra1nz@astraoffice.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>