Skip to content

feat(doc): per-run character formatting from CHPX#608

Open
andiwand wants to merge 2 commits into
mainfrom
feat/doc-character-formatting
Open

feat(doc): per-run character formatting from CHPX#608
andiwand wants to merge 2 commits into
mainfrom
feat/doc-character-formatting

Conversation

@andiwand

@andiwand andiwand commented Jul 16, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Implements Direct Character Formatting ([MS-DOC] 2.4.6.2) for the legacy .doc decoder: text now renders with per-run bold, italic, underline, strikethrough, font size, font name, font color, and highlight, replacing the flat font_size = 11pt placeholder. This is Open Work §1 from text/AGENTS.md, implemented as planned there.

How

  • Read path: PlcBteChpx (table stream) → 512-byte ChpxFkp pages (WordDocument stream) → per-run Chpx.grpprl SPRMs, applied over a 10pt default (the sprmCHps default of 20 half-points). Font names come from SttbfFfn and are interned so TextStyle::font_name stays valid.
  • Tree shape becomes 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.
  • Equal Chpx byte sequences share one resolved style; adjacent equal-style runs merge. ToggleOperand 0x80/0x81 resolve against the (unmodelled, default-off) style value. sprmCCv COLORREF with fAuto clears the color; sprmCIco/sprmCHighlight use the Ico palette.
  • Field-code hiding (TextCleaner) is now stateful across runs and paragraphs — a field can span both.
  • Not applied (documented in AGENTS.md open work): Pcd.Prm piece 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 — synthetic SttbfFfn.
  • 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.
  • All OldMs.* and the real-file .doc HTML output tests pass.

Note: reference HTML

Rendered .doc HTML changes structurally (styled spans instead of flat 11pt text), e.g. SampleDOCFile_100kb.doc's heading:

-<x-s style="font-size:11pt;">Buy Used Book:
+<x-s style="font-size:14pt;font-weight:bold;"><x-s>Buy Used Book:

The .doc reference outputs under test/data/reference-output/odr-public/output/doc/ need regenerating for the compare-html CI step (flagged separately, same as #606).

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +202 to +203
if (ftc < 0 || static_cast<std::size_t>(ftc) >= font_names.size()) {
throw std::runtime_error("doc: sprmCRgFtc0 font index out of range");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant