Skip to content

fix: writer honours lineSpacingRule, allCaps and smallCaps - #4

Merged
dskripchenko merged 2 commits into
dskripchenko:mainfrom
One-Learning-Community:fix/writer-line-rule-caps
Oct 20, 2026
Merged

dskripchenko merged 2 commits into
dskripchenko:mainfrom
One-Learning-Community:fix/writer-line-rule-caps

Conversation

@serpentblade

Copy link
Copy Markdown
Contributor

Summary

Two commits, each separable (each passes the full suite on its own):

  1. fix: emit w:lineRule from ParagraphStyle::$lineSpacingRule: the writer wrote w:lineRule="exact" for every line spacing. It now writes auto, exact or atLeast as the style says. An unset or unknown rule still writes exact.
  2. fix: emit w:caps and w:smallCaps for RunStyle: RunStyle::$allCaps and $smallCaps were never written to DOCX.

No API changes. Both are model fields that already existed and that the reader already fills in; only the writer ignored them.

Motivation

Both show up on a plain read → write round-trip through the library itself:

  • Line spacing. OoxmlPropertyParser stores the rule next to w:line (auto when the attribute is missing, the schema default), and the HTML serializer already treats a missing rule as auto. The writer was the one place that ignored it. Word's default 1.15 spacing (<w:spacing w:line="276" w:lineRule="auto"/>) came back as w:line="276" w:lineRule="exact", a fixed 13.8 pt line height, so any text larger than about 11 pt was clipped at the top.
  • Caps. The reader parses <w:caps/>/<w:smallCaps/> and the serializer writes text-transform: uppercase / font-variant: small-caps, but a heading set in small caps came back from read → write in plain lowercase.

Notes

  • Default kept: lineSpacingRule = null writes exact, as before. DocumentBuilder's spacing(line: ...) and the HTML converter's line-height (both set only lineSpacingTwips) produce the same bytes as today.
  • <w:caps/> and <w:smallCaps/> go after i/iCs and before strike, their position in EG_RPrBase.
  • w:rPr order: the existing order (u before strike, vertAlign before color) does not match the order EG_RPrBase lists its elements in, but the Transitional schema in scripts/conformance declares EG_RPrBase as an unbounded xsd:choice, so any order validates. I left it alone. Happy to send a reorder separately if you'd like it to match the Strict ordering.
  • A sample using the new output (auto and atLeast spacing; bold, italic, caps, small caps, strike in one run) passes scripts/conformance/xsd-check.sh.
  • Not included: style-level (styles.xml) line spacing and caps. StylesXmlBuilder writes neither yet; that is a separate change. The HTML converter also does not parse text-transform/font-variant back into RunStyle.

Tests

composer install && vendor/bin/phpunit: OK (408 tests, 1027 assertions); before: OK (398 tests, 1009 assertions). After each commit: 405, 408, all OK. composer stan: no errors.

  • tests/Writer/LineSpacingRuleTest.php: each rule is written as given, unset/unknown write exact, and a DOCX with line="276" lineRule="auto" (and with no lineRule) comes back from read → write as auto. The round-trip cases fail on main.
  • tests/Writer/CapsTest.php: emission, position after iCs and before strike, and write → read.

🤖 Generated with Claude Code

serpentblade and others added 2 commits October 19, 2026 15:13
The writer wrote w:lineRule="exact" for every line spacing and never read
lineSpacingRule. The reader sets the rule (auto when the attribute is
missing, per the schema default), so read -> write turned Word's 1.15
spacing (line=276 auto) into a fixed 13.8pt line height.

auto, exact and atLeast are now written as given. An unset or unknown
rule stays exact, so callers that set only lineSpacingTwips (the builder's
spacing(line:), the HTML converter) get the same output as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RunStyle::$allCaps and $smallCaps were parsed by the reader and
serialized to HTML (text-transform / font-variant), but the writer never
emitted them, so a read -> write round-trip lost the formatting.

Both are written in their CT_RPr position, after i/iCs and before strike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dskripchenko
dskripchenko merged commit 654da76 into dskripchenko:main Oct 20, 2026
8 checks passed
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.

2 participants