Skip to content

feat: parse w:wordWrap on paragraph properties - #3

Merged
developer0hye merged 1 commit into
fix/parse-tolerancefrom
feat/word-wrap
Aug 5, 2026
Merged

feat: parse w:wordWrap on paragraph properties#3
developer0hye merged 1 commit into
fix/parse-tolerancefrom
feat/word-wrap

Conversation

@developer0hye

Copy link
Copy Markdown
Owner

What

Parse w:pPr/w:wordWrap into ParagraphProperty::word_wrap.

Why

w:wordWrap is Word's switch between word-level and character-level line breaking for East Asian text: w:val="1" keeps a Hangul eojeol whole, w:val="0" allows a break at any syllable. The reader dropped the element, so a consumer had no way to distinguish a paragraph that explicitly asks for character-level breaking from one that says nothing and leaves the choice to the style chain.

Needed by developer0hye/office2pdf#730, where the value is measured to be the exact discriminator — including overriding the style chain, so it cannot be inferred from pStyle.

Note on the false case

The flags around it (keepNext, keepLines, pageBreakBefore, widowControl) record only the true case:

if read_bool(&attributes) {
    p.widow_control = Some(true);
}

That is fine where 0 and absent mean the same thing. Here they do not — 0 is the setting that carries the instruction — so this arm records both:

p.word_wrap = Some(read_bool(&attributes));

read_bool already returns true for a bare <w:wordWrap/>, which matches what OOXML means by the element with no w:val.

Tests

Two, in the existing reader::paragraph_property test module and following the style of the shading test there:

  • w:val="0" survives as Some(false)
  • a bare <w:wordWrap/> and w:val="1" both give Some(true), and an absent element gives None, so the three states stay distinct

cargo test -p docx-rs --lib passes 275. The 20 failures in tests/reader.rs are present on fix/parse-tolerance before this change as well — identical count with and without it — so they are untouched by this PR.

Scope follows the shading PR (#2): reader and struct only, no writer.

w:wordWrap is Word's switch between word-level and character-level line
breaking for East Asian text: w:val="1" keeps a Hangul eojeol whole,
w:val="0" allows a break at any syllable. The reader dropped it, so a
consumer could not tell a paragraph asking for character-level breaking
from one that says nothing (office2pdf#730).

Unlike the neighbouring flags, which record only the true case, this one
records both: w:val="0" is the meaningful setting, and collapsing it to
None is indistinguishable from the property being absent. read_bool
already yields true for a bare <w:wordWrap/>, which is what OOXML means
by it.

Signed-off-by: Yonghye Kwon <developer.0hye@gmail.com>
Signed-off-by: developer0hye <developer.0hye@gmail.com>
@developer0hye

Copy link
Copy Markdown
Owner Author

CI status, for the record before merging.

  • Clippy, DCO, build: pass.
  • test: the 308 lib tests pass, including the two added here. The job is red on the 20 tests/reader.rs integration tests, which fail identically on fix/parse-tolerance without this change — I measured both locally (0 passed; 20 failed either way) and the branch's own CI has been failing since 2026-07-21, before this work.
  • build-wasm: fails on tsconfig.node.json(4,25): error TS5108: Option 'moduleResolution=node10' has been removed, a TypeScript toolchain drift unrelated to Rust changes.

Neither red job is caused by this PR, and neither is in scope for it. Merging on that basis.

@developer0hye
developer0hye merged commit ec09b7e into fix/parse-tolerance Aug 5, 2026
5 of 9 checks passed
pull Bot pushed a commit to RadaKichenin/office2pdf that referenced this pull request Aug 5, 2026
w:wordWrap is Word's switch between word-level and character-level
Hangul breaking: w:val="1" keeps an eojeol whole, w:val="0" allows a
break at any syllable. It was not parsed, so a paragraph explicitly
asking for character-level breaking still got the word-level rule developer0hye#626
applies to every non-justified paragraph.

The value now reaches ParagraphStyle and paragraph_eojeol_wrap consults
it before anything the paragraph inherits, because Word's own probes
show the property overrides the style chain -- a ListParagraph with
w:val="0" breaks mid-eojeol although the style alone would not.

This needed a dependency field first: docx-rs had no word_wrap anywhere,
added upstream in developer0hye/docx-rs#3 and merged into the pinned
branch.

Adding the field pushed TaggedElement::ListParagraph past clippy's
large_enum_variant threshold (232 bytes against Plain's 24), so its
Paragraph is boxed.

Related: developer0hye#730
Signed-off-by: developer0hye <developer.0hye@gmail.com>
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