Skip to content

fix(table): emit numeric pct widths in fiftieths of a percent - #3476

Draft
omartuhintvs wants to merge 1 commit into
dolanmiu:masterfrom
omartuhintvs:fix/percentage-width-fiftieths
Draft

fix(table): emit numeric pct widths in fiftieths of a percent#3476
omartuhintvs wants to merge 1 commit into
dolanmiu:masterfrom
omartuhintvs:fix/percentage-width-fiftieths

Conversation

@omartuhintvs

@omartuhintvs omartuhintvs commented Jul 22, 2026

Copy link
Copy Markdown

What

createTableWidthElement serialises a numeric WidthType.PERCENTAGE size as a percent string (w:w="50%"). This changes it to emit the value in fiftieths of a percent (50% → 2500), which is the form Word itself writes.

Fixes #1457 (also the root cause behind #349, #216, #3015).

Why

For w:tblW / w:tcW, @w:w is ST_MeasurementOrPercent, whose union permits both an ST_Percentage string ("50%") and an ST_UnqualifiedPercentage integer in fiftieths. So the current "50%" output is schema-valid — this is an interoperability issue, not a spec violation:

  • Word writes pct widths as the fiftieths integer.
  • Stricter consumers — Google Docs' .docx importer and Apple's shared OOXML parser (Pages, QuickLook, Mail preview, iCloud) — misread the "50%" string, fall back to tblGrid, and collapse every column to ~1 character wide (text stacked vertically). This is the long-standing "tables broken in Google Docs" report.

Emitting the integer matches Word and renders correctly everywhere, with no visual change in Word/LibreOffice.

Compatibility

  • The rendered percentage is unchanged ({ size: 50, type: PERCENTAGE } still means 50%).
  • Only the numeric branch changes. Callers who deliberately want the literal string can still pass a Percentage (e.g. { size: "50%", type: PERCENTAGE }) — that path is untouched.
  • Note: this does change the emitted XML bytes for existing numeric-percentage callers ("100%"5000). Two existing specs asserted the old string output and are updated. I recognise % was introduced deliberately in Fix: table width in percentage should include '%' #282; happy to gate this behind an option instead if you'd prefer to preserve the exact previous bytes by default.

Tests

  • New src/file/table/table-width.spec.ts covering: numeric pct → fiftieths, fractional rounding, the Percentage-string path still emitting the string, and dxa untouched.
  • Updated the two specs that asserted the old string (table.spec.ts, table-cell-margin.spec.ts).
  • Full suite green: 1023 passed / 197 files. Lint clean on changed files.

Summary by CodeRabbit

  • Bug Fixes
    • Improved table and cell percentage-width formatting for better compatibility with Word and other document editors.
    • Numeric percentage widths now use the correct Word-compatible representation, including proper rounding.
    • Preserved explicitly provided percentage strings and DXA width values.
    • Updated coverage to verify common and edge-case table width scenarios.

createTableWidthElement wrote a numeric PERCENTAGE size as a "50%" string.
Both that string and the fiftieths-of-a-percent integer (50% = 2500) are
valid under ST_MeasurementOrPercent, but Word writes the integer, and
stricter consumers — Google Docs and Apple Pages/QuickLook/Mail — misread
the string form and collapse table columns to one character.

Emit the integer (Math.round(size * 50)) for numeric pct sizes to match
Word's output. Callers who want the literal percent string can still pass a
Percentage (e.g. "50%"), which is left untouched. Adds table-width.spec.ts
and updates the two specs that asserted the old string output.
@omartuhintvs
omartuhintvs marked this pull request as draft July 22, 2026 08:55
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Numeric percentage widths now use WordprocessingML’s fiftieths-of-a-percent integer representation. Table width and cell margin tests were updated, with new coverage for rounding, literal percentage strings, and DXA passthrough.

Changes

Percentage width encoding

Layer / File(s) Summary
Encode numeric percentage widths
src/file/table/table-width.ts
Numeric WidthType.PERCENTAGE values are rounded after multiplication by 50, while literal percentage strings and DXA widths retain their existing handling.
Validate table and cell width output
src/file/table/table-width.spec.ts, src/file/table/table.spec.ts, src/file/table/table-properties/table-cell-margin.spec.ts
Tests cover percentage conversion, fractional rounding, literal "50%" values, DXA passthrough, 100% table widths, and 5% cell margins.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: numeric percentage widths are emitted as fiftieths-of-a-percent values.
Linked Issues check ✅ Passed The PR implements #1457 by emitting fiftieths-of-a-percent integers for numeric percentage widths while keeping string percentages and DXA unchanged.
Out of Scope Changes check ✅ Passed All changes are directly tied to percentage-width formatting and supporting tests; no unrelated edits are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/file/table/table-width.spec.ts (1)

5-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the configured @file/ alias.

This new TypeScript spec imports table-width relatively instead of using the repository’s required path aliases.

Suggested change
-import { WidthType, createTableWidthElement } from "./table-width";
+import { WidthType, createTableWidthElement } from "`@file/table/table-width`";

As per coding guidelines, TypeScript imports should use the @file/, @export/, and @util/ path aliases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/file/table/table-width.spec.ts` at line 5, Update the import in the
table-width spec to use the configured `@file/` alias for table-width instead of a
relative path, while preserving the existing WidthType and
createTableWidthElement imports.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/file/table/table-width.spec.ts`:
- Around line 16-20: Update the rounding test in the percentage-width case to
use a fractional value such as 33.31 whose multiplication by 50 requires
rounding, and change the expected width to 1666. Keep the existing formatter and
percentage configuration unchanged so the assertion specifically verifies
Math.round behavior.

---

Nitpick comments:
In `@src/file/table/table-width.spec.ts`:
- Line 5: Update the import in the table-width spec to use the configured `@file/`
alias for table-width instead of a relative path, while preserving the existing
WidthType and createTableWidthElement imports.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e5dab2f7-e1bc-472c-aa8b-5e8dbe388b9b

📥 Commits

Reviewing files that changed from the base of the PR and between 309b972 and c39ca31.

📒 Files selected for processing (4)
  • src/file/table/table-properties/table-cell-margin.spec.ts
  • src/file/table/table-width.spec.ts
  • src/file/table/table-width.ts
  • src/file/table/table.spec.ts

Comment on lines +16 to +20
it("rounds fractional percentages to the nearest fiftieth", () => {
const tree = new Formatter().format(createTableWidthElement("w:tblW", { size: 33.3, type: WidthType.PERCENTAGE }));

expect(tree).to.deep.equal({ "w:tblW": { _attr: { "w:type": "pct", "w:w": 1665 } } });
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the rounding test exercise rounding.

33.3 * 50 equals 1665, so this test would also pass with truncation or without meaningful rounding. Use a value such as 33.31 and expect 1666 to verify the Math.round behavior.

As per coding guidelines, test files should cover edge cases and option combinations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/file/table/table-width.spec.ts` around lines 16 - 20, Update the rounding
test in the percentage-width case to use a fractional value such as 33.31 whose
multiplication by 50 requires rounding, and change the expected width to 1666.
Keep the existing formatter and percentage configuration unchanged so the
assertion specifically verifies Math.round behavior.

Source: Coding guidelines

@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (309b972) to head (c39ca31).

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #3476   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          311       311           
  Lines         3237      3237           
  Branches       732       732           
=========================================
  Hits          3237      3237           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

WidthType.PERCENTAGE doesn't work for tables in Google Docs or Apple Pages

1 participant