* Content controls: typed text takes the control's formatting, in any language
Text typed in place of a content control's placeholder takes the
control's own run properties (w:sdtPr/w:rPr, ECMA-376 §17.5.2.27), as
in Word: in a protected form filled in Word for Mac, all 17 entered
values carry the style their control's rPr names. WordCraft kept that
rPr only as XML to write back and gave typed text the placeholder's
formatting minus the placeholder style.
It also knew the placeholder style only by its English id,
PlaceholderText. German Word writes Platzhaltertext (the name stays
"Placeholder Text"), so in forms from German Word every entry stayed
grey, on screen and in the saved file, and restoring a placeholder
added a second Placeholder Text style. The style is now found by its
built-in name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Content controls: typing drops a showing placeholder's own style
A control without run properties that shows its placeholder: text typed
over it drops the placeholder run's character style whatever its id
and name. Word localizes only the placeholder style's id, but another
program may localize its name too, and the name lookup then misses it.
Restoring a placeholder and new controls keep the name lookup.
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>
* References: Word sources file import in Manage Sources (#2640)
Manage Sources gains Import… and references.sources an "import": path
parameter: it reads a b:Sources document (Word's own Sources.xml) into the
document's sources with the same reader as a document's own bibliography
part. A tag already here is left alone, an untagged entry is matched by
title and author or gets a tag like a new source, the whole import is one
undo step, and a file that isn't b:Sources is refused. Desktop only; the
web build hides the button.
The docx reader now shares the b:Sources parser (crates/docx/src/
bibliography.rs) with the import, with the same dedupe and 10,000-source
cap; the hostile-input cap test still passes.
Tests: docx unit tests (names, kinds, accessed/DOI fallbacks, repeated
tags, empty entries, wrong root), engine import test (add, undo, second
import, untagged matching, refused file), ui tests (the dialog button asks
for a "sources" file; finish_pick imports), wordcraft-cli end to end
(import, cite, bibliography, save, reopen; the package's customXml
carries the sources). The button's label is in every interface catalogue.
ROADMAP progress log and docs/roadmap.md, gaps.md, file-format-parity.md
and target-app-parity.md get their rows.
* References: review fixes for the sources import (#2640)
- read_sources validates the root namespace instead of the local name alone:
an un-namespaced or unrelated <Sources> is refused, and both bibliography
namespaces read — OOXML's and Word's own 2004/10 (the VBA Source.XML
format), the latter mapped onto the canonical b prefix with parse_ns.
Tests: the 2004/10 namespace is accepted; a no-namespace or unrelated
root is refused.
- The import loop builds the tag and (title, author-or-editor) sets once and
keeps a per-stem counter, with the stem rule shared with auto_tag
(auto_tag_base). Importing thousands of untagged sources no longer costs
O(n^3). Test: untagged sources sharing a stem get Riv21, Riv211, Src, and
a second import adds none.
- docs/target-app-parity.md and docs/file-format-parity.md no longer claim
bibliography sources aren't saved in DOCX: #169 and #532 are closed and
the round trip exists (#790).
* References: doc comments for the sources import helpers (#2640)
Hoist the untagged-source matching helper (by_author) to module level and
document sources, auto_tag_base and sources_ui, so the functions this PR
touches meet CodeRabbit's docstring-coverage pre-merge check.
* References: re-apply the sources import's progress notes (#2640)
The rebase onto current main took upstream's copies of ROADMAP.md,
docs/file-format-parity.md and docs/gaps.md, because the rows this PR
adds conflicted with upstream's own appends. Re-apply them on the current
text: the progress log and revision rows, the bibliography row's
source-format note, and #15's evidence, with each status line's change
note. The other two documents (docs/roadmap.md, docs/target-app-parity.md)
merged cleanly and keep their rows.
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- Math straight in the body or a cell (m:oMathPara/m:oMath without w:p,
allowed by EG_BlockLevelElts) gets its own paragraph instead of vanishing.
- mc:AlternateContent Requires="m" picks the equation, not its fallback.
- Inside an equation, runs in w:hyperlink, w:fldSimple, w:dir, w:bdo and
mc:AlternateContent are kept (EG_PContentMath); depth stays bounded.
- w:sym, w:noBreakHyphen and Symbol-font text in math runs become Unicode.
- Layout: n-ary operators, delimiters, accents and group characters the math
font lacks use a fallback font instead of a missing-glyph box.
- Coverage test reads every ECMA-376 §22.1 element and checks no argument's
text is dropped; tests for body-level math, Requires="m", symbols, hostile
nesting and the font fallback. Parity docs and progress log updated.
Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #2601: Drop Cap Options dialog
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gaps: keep the Drop Cap Options note on the dialog-depth row
* i18n: translate the two Drop Cap disabled reasons in the audited catalogs
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* fix(docx): support self-closing w:compat in settings merge
* Drop the duplicate divyanshu2074 entry in people.toml (already on main)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* gap: nested ink WIP
* gap 2: native ink nested in groups and drawing canvases survives DOCX saves
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 2: review round 1: ungrouping keeps nested ink; no orphan parts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 2: Impact cell matches the restated impact
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 2: only Word ink pins a group against Ungroup
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 2: alternate ink must hold an a:graphic to be written as standalone
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap: nested ink WIP
* gap 2: native ink nested in groups and drawing canvases survives DOCX saves
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 2: review round 1: ungrouping keeps nested ink; no orphan parts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 2: Impact cell matches the restated impact
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 2: only Word ink pins a group against Ungroup
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 2: alternate ink must hold an a:graphic to be written as standalone
Co-Authored-By: Claude Sonnet 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>
* #2240: fonts Word embeds as subsets lay out the text set in them
Embedded fonts were registered under the family inside the font file. Word
embeds a subset ("embed only the characters used") under a made-up family
(___WRD_EMBED_SUB_46) and the text asks for it by the font table's name
(OBRBWW+Century Schoolbook), so it still fell back to a substitute. Each
embedded file now comes with its font table name, and its faces go by that
name when they don't already.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #2240: the black-circle fallback test passes its font's table name
main's new `circle_checks_face_coverage` test (#2582) called `add_document_fonts` with bare font
bytes; with this branch each embedded font comes with the name the document's font table gives
it, so the test passes "Times New Roman" alongside the face it renames to it.
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>
Each picture, and each embedded object or macro part kept for saving, was
copied out of the unzipped package (bytes.to_vec()), so a document with
pictures held them twice while it opened, and the package's copies were
left behind as freed memory. Package now holds its parts as Arc<Vec<u8>>,
the type the document keeps media and passthrough parts in, and hands them
over with get_shared. keep_unmodelled also searches the main part in place
instead of copying it.
A 92 MB document with 20 pictures on macOS 27: the app's footprint once
open 866 -> 772 MB (freed blocks kept 206 -> 118 MB, peak 915-932 ->
821-823 MB); wordcraft-cli info peak 319 -> 231 MB.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Word 2024 saves Strict Open XML with measures written out with their unit:
the issue's file carries `w:spacing w:after="8pt" w:line="13.90pt"
w:lineRule="auto"` in its paragraph defaults. The reader sent `13.90`
straight into the `auto` branch, where a bare number counts 240ths of a
line (240 = single), so 13.90pt became a 13.9 multiple: 191.8 pt lines,
and the issue's document laid out as 10 pages against Word's 1.
Under `lineRule="auto"` a unit-suffixed height now reads through twips, as
Word renders it (ISO 29500 `ST_SignedTwipsMeasure`: 13.90 pt = 278 twips =
the 1.1583 multiple). A bare number still counts 240ths of a line;
`exact`/`atLeast` already read units as points.
Checked with the issue's file: 1 page with baselines on Word's (15.98 pt
line advance against Word's 16.10/15.96; paragraph advance 23.99 against
24.00; table lines 12.65 against 12.60/12.72), and a save/reopen keeps it.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Read w:eastAsianLayout (ISO/IEC 29500-1 §17.3.2.10) into a new
CharProps::east_asian_layout and write it back after w:lang, in runs and
styles. Values outside the schema are left unset. Layout is unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* WIP for #1543
* DOCX: split mixed-direction runs and mark right-to-left pieces w:rtl so Word orders them as WordCraft does (#1543)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* DOCX: right-to-left pieces of split runs say their language (w:lang w:bidi) (#1543)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* DOCX: no phantom tracked formatting on split runs; typed text doesn't inherit run direction (#1543)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Engine: text typed or inserted anywhere takes its direction from its own letters, not the caret's run (#1543)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* track-move-options: WIP
* track-move-options: Moves options in Track Changes Options and w:doNotTrackMoves
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* track-move-options: Moved from Hidden leaves the layout; per-revision hiding
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* track-move-options: Word's mark lists, (none) moves are plain, no borders on collapsed paragraphs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* track-move-options: hr and sv translations for Moved from:/Moved to:
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* DOCX: read every VML shape in a `w:pict`, and a `v:group`'s members (#1643)
`read_vml` took `pict.els().find(..)`, the first `v:*` child that is not a
`v:shapetype`, and read only that one. So a `w:pict` holding several shapes
kept the first and dropped the rest, and a `v:group` was read as a single
shape -- or, with no text box in it, as nothing at all -- losing every member.
Both are gone after a save, since saving writes the model back as DrawingML.
Read all of them instead:
- `read_vml_all` walks every `v:*` child of a `w:pict` and emits one object
each; `read_vml` keeps returning the first for the two places that show a
single drawing (an OLE object's picture, a `mc:Fallback` preview).
- a `v:group` becomes an `InlineObject::Group`, its members mapped from the
space `coordsize`/`coordorigin` give it onto the group's own box, nested
groups flattened into it -- what `group_members` already does for a
DrawingML `wpg:wgp`, so the model, the renderer and the writer need no
change. Nesting is bounded at `MAX_VML_GROUP_DEPTH`, members at
`MAX_GROUP_CHILDREN`.
- `read_vml_shape` is the old per-shape body, taking the group's scale so a
member's `width`/`height` (and a `v:line`'s `from`/`to`) come through in
the group's coordinates.
`boxes()` in the text-box tests now asks for `text_boxes()` rather than
`text_box()`, which only reports an object that is itself a box and so could
not see one inside a group; its assertions are unchanged.
* DOCX: cover a `v:group` holding several text boxes (#1643)
@KNUFFIXX reported the same loss from the other end: an inline `v:group` with
two `_x0000_t202` boxes side by side came through a convert as box A only, in
both a tall-B and a too-short-B variant, against Word reading both.
A text box is the one member a group used to keep -- `vml_textbox` recurses to
the first box that has content -- so this case looked half-working rather than
broken, and it is worth pinning separately from the shape cases. On `main` the
group collapses to a single `Shape { kind: TextBox, w: 400, h: 100 }` wearing
the group's own size and holding box A's story; here both boxes read, keep
their own stories and positions, and survive a save.
Built in code from the reported fixture rather than committing the .docx.
* DOCX: cover the shapetype lookup for a group's member (#1643)
Word writes each `v:shapetype` once, at its first use, as a child of the
`w:pict` -- so a group's member refers to one that is its parent's sibling,
not its own. Reading all shapes means `read_vml_shape` is now called for a
member as well as for a top-level shape, and both need the picture to find it,
which is why `pict` is threaded through the group walk.
Two tests: a member with no geometry and no colours of its own, taking its
diamond from the shapetype's `o:spt` and its fill from its `fillcolor`, and the
same for a shape outside a group, which is the case the lookup was written for.
* Shapes: preserve DrawingML outline dashes
* Shapes: scale dashes to effective path width (#380)
* Keep the outline dash with noFill; cap the raster dashed stroke width at 200 pt
Addresses the two CodeRabbit review notes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Use clamp for the dashed stroke width (clippy)
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>
* Automatic text in a styled text box takes the shape style's font colour
A text box shape whose style has a:fontRef with a colour (Word's
default shape style uses lt1, white on the accent fill) shows its
automatic-colour text in that colour. WordCraft drew it black. Word,
observed with a synthetic document: only text whose resolved colour is
automatic takes the fontRef colour; a run's own colour, a paragraph or
character style colour and a colour from the document defaults all win
over it. A box without a fontRef keeps black.
- wordcraft-doc: TextBody gets `auto_color`.
- wordcraft-docx: read from wps:style/a:fontRef (theme colours resolved
as for SmartArt).
- wordcraft-layout: lines in a text box story resolve automatic colour
to it.
Saving does not write a shape style yet, so the colour is not kept
through a save; the reader and layout parts stand on their own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Remove stray doc-comment lines from the new test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
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>
Some generators write a placeholder Heading 1 and then their own,
under the same w:styleId. The reader kept the first definition and
skipped the rest, so headings came out in the placeholder's font,
size and colour. Word uses the last definition, whole: a synthetic
document with an italic red 20 pt first definition and a bold blue
12 pt second one shows bold blue 12 pt, with nothing of the first
(no italic, no centring), for a custom style and for Heading 1 alike.
The reader now replaces an earlier definition in place instead of
skipping the later one.
Co-authored-by: Daniele Mori <Dinasty@hotmail.it>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/docx/src/write/story.rs:512-530 (InlineObject::NoteRef, the `current_note == Some(..)` branch)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
part that inherits its lines sends it, and the picker only shows No Border checked for it.
- CellProps::valign is Option<VAlign>: a cell's own Top (Align Top, w:vAlign="top") beats a
table style part's vertical alignment, and round-trips through .docx.
- tableStyle edit rejects Part fields at the top level without "part".
- "font": null or "" makes a part's font inherited; clearing the dialog's font box sends it.
- Document ui.dialog's optional "part" in docs/control-protocol.md.
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* #714: table style editor formats every table style part
Modify/New Table Style: "Apply formatting to" any of the 13 ECMA-376
§17.7.6 parts (whole table, header/total row, first/last column,
odd/even row and column bands, four corner cells), each with font,
size, bold, italic, underline, colour, fill, borders (line style,
width, colour; all/none/outside/inside/each edge) and the 9-way cell
alignment, over a live preview.
- doc: TablePart, PartFormat, PartSet; TableStyleParts::part/set_part
(new parts and per-part jc/vAlign in `regions`)
- layout: generic part resolution incl. band2, corner cells, alignment
- docx: every tblStylePr type read/written with pPr jc and tcPr vAlign
- engine: table.newStyle/modifyStyle take ECMA part names, `part`,
`parts`, per-edge `borders`, `underline`, `align`/`vAlign`/`cellAlign`
- ui: dialog moved to dialogs_table_style.rs; ui.dialog takes `part`
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 #887: Bulgarian strings for the table style parts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #714: Vietnamese strings for the table style parts (main's new vi catalog)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* hr: match the catalog's word for banded
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>
* Tables: preserve row table border exceptions
* Tests: shorten the row border regression name
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A field begin marker flagged w:dirty by a producer that couldn't compute
its result (pandoc-style TOC placeholders) lost the flag on save, so Word
no longer refreshed the field on open. Mirror w:fldLock end to end:
FieldState, InlineObject::Field/FieldStart, the reader (fldChar and
fldSimple) and the writer (buffered, range and TOC-spanning begins).
Update Field/Update Table clear it on the fields they compute, like
Word's F9; untouched fields keep it. Round-trip and engine regression
tests, incl. a pin for #878 (imported TOC placeholder replaced in place,
fixed earlier by #617).
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* DOCX: read VML freeforms (v:shape with a path) as freeform shapes
Legacy VML shapes with their own `path` (ECMA-376 Part 4), such as rule
lines in headers, were dropped on open and lost on save. They are now
read as freeforms: m/t/l/r/c/v/x/e commands in the coordsize/coordorigin
space, nf/ns, v:fill and v:stroke opacity; arcs drawn straight, formula
references read as 0. Saved as DrawingML custom geometry.
Tests: header rule read and round-tripped; hostile paths and coordinate
spaces stay finite and never panic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* DOCX: VML freeforms whose path uses formulas aren't drawn
Formulas (@n, from v:formulas) aren't evaluated; reading them as 0 drew stray lines. Parity row states the fill-opacity limit (none at 0, otherwise opaque, as for DrawingML fills).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* DOCX: VML freeforms reuse the watermark's vml_fraction
The watermark reader (#1328, now on main) brought its own vml_fraction;
the freeform reader had an identical copy, which no longer compiles after
the merge. Keep one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
w:asciiTheme="minorBidi" (and majorBidi, and w:cstheme) read as the
theme's Latin font, so a template's titles came out in Calibri where
Word draws Arial. Measured in Word: the slot names the theme's a:cs
font, and an empty a:cs names nothing, so the font inherited from the
style applies.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
WordCraft chose a substitute for a font that isn't installed from its
name alone, so an unknown brand font always came out sans. Word reads the
font table instead: measured with the same missing font, it draws it in
Cambria when w:family is roman and in Calibri when it is swiss or auto.
A report whose font table calls its brand font roman got a sans footer
where Word draws serif.
The font table's roman fonts are read into the settings, and layout looks
a missing one up as Cambria (Caladea where Cambria isn't installed). The
other classes keep the existing substitutes, which already start with
Calibri's metric clone.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
space after and the second's space before, as Word does by default. Word
adds them instead when the document's compatibility options include
w:doNotUseHTMLParagraphAutoSpacing. Measured in Word with five paragraphs
of 12 pt before and after: 24 pt between them with the option, 12 pt
without, the same in compatibility modes 14 and 15. A 60-page report with
the option lost 6 pt per paragraph, about 60 pt on its cover.
The option is read, laid out in the body and in boxes, and saved back.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Follow-up to #1900: the fixture test vml_preset_shapes_are_drawn also
expected the isosceles triangle for VML shape type 6 (#1599).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#1599 made VML spt 6 (rtTriangle) a RightTriangle; #1442's mapping test
still expected the isosceles Triangle, so it failed on main.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
DrawingML's rtTriangle was read as an isosceles triangle, so a cover
page's white corner cut-outs pointed up into a peak instead of cutting a
V into the picture above them. It is now its own shape kind, drawn with
the right angle at the bottom left, wrapped and glued along that outline,
and saved back as rtTriangle. The .doc reader maps msosptRightTriangle (6)
to it too.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The Word XML Document (Flat OPC) reader counted only the parts it kept:
a repeated or nameless part, a child of the package that isn't a part
and anything nested inside one were read without being counted, nesting
had no cap, and a part's inline XML was copied before its size was
checked. Every child of the package now counts against the part limit
and every part's data against the part and package size limits, kept or
not; nesting and the elements of each child are capped as the XML tree
reader caps a part's; and data is checked before it is copied.
Base64 in pkg:binaryData was taken from plain text only, so CDATA and
character references were dropped and a picture written that way lost
bytes. Both now read as the same text, and an entity there is an
invalid base64 error.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>