Skip to content

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

Merged
andiwand merged 7 commits into
mainfrom
feat/doc-character-formatting
Jul 21, 2026
Merged

feat(doc): per-run character formatting from CHPX#608
andiwand merged 7 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).

@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 thread src/odr/internal/oldms/text/doc_helper.cpp Outdated
andiwand added a commit that referenced this pull request Jul 16, 2026
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
andiwand added a commit that referenced this pull request Jul 21, 2026
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
@andiwand
andiwand force-pushed the feat/doc-character-formatting branch from a4042cb to e1e681e Compare July 21, 2026 18:28
andiwand and others added 4 commits July 21, 2026 20:56
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
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
Mirror the xls module: a StyleRegistry owns the resolved character
TextStyles (index 0 = default) and the interned font names, while the
ElementRegistry stores only a per-element style index. CharacterStyles
shrinks to a runs-only CharacterRuns; the SPRM/font-table resolution
moves to doc_style.{hpp,cpp}.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PvbJYsLLST3hkCgkcHBoFb
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PvbJYsLLST3hkCgkcHBoFb
@andiwand
andiwand force-pushed the feat/doc-character-formatting branch from 7070e5e to 46d3a07 Compare July 21, 2026 18:58
andiwand and others added 3 commits July 21, 2026 21:03
Mirror the xls StyleRegistry: set_font_names stores the SttbfFfn table
in a vector that is never modified afterwards, so the font-name strings
cannot move; apply_character_sprms reads them as a span and
read_character_runs takes the fonts from the registry it already gets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PvbJYsLLST3hkCgkcHBoFb
StyleRegistry now takes its font names and resolved styles in the
constructor (mirroring the xls registry); set_font_names/add_style are
gone, read_character_runs collects styles into a plain vector and no
longer depends on the registry, and the default style moved into a named
default_character_style() factory.

Also review cleanups across the branch:
- emplace_back for aggregate push_backs (C++20 parenthesized init)
- intern cache: single try_emplace instead of find + emplace with a
  second std::string(grpprl) allocation
- fixed-size integer types instead of plain int
- drop the local read_u16 in favor of util::byte::from_little_endian
- shared append_u16/append_u32 test helpers in oldms_test_util.hpp
- doc_helper.cpp: definitions moved into the namespace block

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJRDZcksAtKnXyhDTeMkMK
@andiwand
andiwand enabled auto-merge (squash) July 21, 2026 21:48
@andiwand
andiwand merged commit b0bb932 into main Jul 21, 2026
11 checks passed
@andiwand
andiwand deleted the feat/doc-character-formatting branch July 21, 2026 22:17
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