feat(doc): per-run character formatting from CHPX#608
Conversation
Implement Direct Character Formatting ([MS-DOC] 2.4.6.2): read the PlcBteChpx and its ChpxFkp pages, split the decoded piece text at every formatting-run boundary, and emit styled span elements carrying bold, italic, underline, strike, font size, font name (SttbfFfn), font color (sprmCCv/sprmCIco) and highlight (sprmCHighlight). Replaces the flat 11pt placeholder; unformatted text now defaults to 10pt (the sprmCHps default). Paragraphs store the style of their first run so empty paragraphs keep their height. Equal Chpx byte sequences share one resolved style and adjacent equal-style runs merge. Field-code hiding is now stateful across runs and paragraphs. Pcd.Prm modifications and STSH style layering remain open (documented in AGENTS.md). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9Z3kyUaHNkJacAEggevHH
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 91404264d3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (ftc < 0 || static_cast<std::size_t>(ftc) >= font_names.size()) { | ||
| throw std::runtime_error("doc: sprmCRgFtc0 font index out of range"); |
There was a problem hiding this comment.
Allow zero-entry font tables for sprmCRgFtc0
When a valid document has an empty SttbfFfn, sprmCRgFtc0 is still allowed to carry operand 0 and should just fall back to the default/style-sheet font. This guard treats font_names.size() == 0 as out of range even for ftc == 0, so such documents now throw while reading CHPX instead of rendering text with no explicit font_name; allow that case and leave the font unset.
Useful? React with 👍 / 👎.
Per [MS-DOC] 2.6.1 an sprmCRgFtc0 operand of 0 is valid when SttbfFfn has no entries; the style-sheet default font (unmodelled) applies, so leave the font unset instead of throwing. Genuinely out-of-range indexes still throw. Addresses Codex review feedback on PR #608. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AEvWWwe1EyeWH7ANHoAvPH
🤖 Generated with Claude Code
Implements Direct Character Formatting ([MS-DOC] 2.4.6.2) for the legacy
.docdecoder: text now renders with per-run bold, italic, underline, strikethrough, font size, font name, font color, and highlight, replacing the flatfont_size = 11ptplaceholder. This is Open Work §1 fromtext/AGENTS.md, implemented as planned there.How
PlcBteChpx(table stream) → 512-byteChpxFkppages (WordDocument stream) → per-runChpx.grpprlSPRMs, applied over a 10pt default (thesprmCHpsdefault of 20 half-points). Font names come fromSttbfFfnand are interned soTextStyle::font_namestays valid.root → paragraph → span → text: the piece decode walks style-uniform chunks (runs are keyed by stream offset, so a boundary inside a 2-byte UTF-16 CP is pushed past it), each run becomes a styled span, and each paragraph stores the style of its first run so empty paragraphs keep their height.Chpxbyte sequences share one resolved style; adjacent equal-style runs merge.ToggleOperand0x80/0x81 resolve against the (unmodelled, default-off) style value.sprmCCvCOLORREFwithfAutoclears the color;sprmCIco/sprmCHighlightuse theIcopalette.TextCleaner) is now stateful across runs and paragraphs — a field can span both.Pcd.Prmpiece modifications and STSH style layering — style-sheet-derived properties fall back to defaults.Tests
OldMs.doc_apply_character_sprms— every modelled SPRM, toggle semantics, cvAuto reset, unknown fixed/variable SPRM skipping, malformed-grpprl throws.OldMs.doc_read_font_names— syntheticSttbfFfn.OldMs.doc_character_formatting— end-to-end over a synthetic inline-bytes.doc(FIB + piece table +ChpxFkp): span splitting, defaults, bold + font-name run, paragraph style.OldMs.*and the real-file.docHTML output tests pass.Note: reference HTML
Rendered
.docHTML changes structurally (styled spans instead of flat 11pt text), e.g.SampleDOCFile_100kb.doc's heading:The
.docreference outputs undertest/data/reference-output/odr-public/output/doc/need regenerating for thecompare-htmlCI step (flagged separately, same as #606).