Skip to content

v7.5.0

Choose a tag to compare

@github-actions github-actions released this 26 Jul 22:10

v7.5.0: 🧮 Equations From Every Format & ODF Page Breaks

officeParser v7.5.0 makes equations a first-class part of the AST across every format that can carry them, and closes two ODF gaps: paragraph styling that never reached the text, and page breaks that were never emitted.

Thanks to @njoqi and @benhid, whose reports and sample documents drove this release.

Warning

Behavior changes

  • Equations are now code nodes, not text. ODF equations previously came through as plain text nodes holding an ad hoc notation ((1)/(2)). They are now code nodes carrying CodeMetadata.math, with LaTeX in node.text (\frac{1}{2}) — matching what Markdown already produced. ast.toText() output changes accordingly.
  • Redundant heading emphasis is no longer emitted. A heading whose every run is bold used to render as # **Heading** in Markdown, and in RTF/HTML as an inner font size that overrode the heading's own. Uniform bold/size inside a heading or table header row is now dropped in favour of the element's own styling. Partial emphasis (# Normal **Bold** Normal) is untouched.
  • metadata.styleMap flags are tri-state. A style that explicitly turns formatting off (ODF's fo:font-weight="normal") now appears as false rather than being absent. On styleMap, absent means the style is silent and the property inherits; false means it is explicitly off. Code resolving inheritance itself must test === undefined, not truthiness. Content nodes never carry false.

🌟 What's New

1. Equations, From Every Format, Normalized to LaTeX (#97)

Equations were not merely missing before this release — they were silently corrupted. Office documents ship them in two markups: OOXML's <m:oMath> (DOCX, PPTX) and MathML <math> (ODF embedded objects, HTML, EPUB3). Neither was handled, so both fell through to a generic "concatenate the descendant text" fallback:

Before Now
½ + ⅛ + ¹⁄₃₂ 12+ 18+132 \frac12+ \frac18+\frac1{32}
3/42 342 \frac3{42}
(2³×2⁷)² (23×27)2 {(2^3×2^7)}^2
f(x) = ⅓x³ + … fx= 13x3+… f(x)= \frac13x^3+…
R \mathbb{R}

1/2 → 12 and 3/42 → 342 produce plausible-looking numbers, so nothing downstream could tell the value was wrong. In one reporter's maths exam paper, 37 of 121 equations were affected. PowerPoint was worse in a quieter way: its run loop dispatches on <a:r>, and an <m:oMath> is a sibling of the runs rather than one of them, so PPTX equations were dropped entirely.

Every format now converges on one node:

Code Node (type: 'code')
├── text: '\frac{1}{2}'          // LaTeX, whatever the source markup was
└── metadata: { math: 'inline' | 'block' }

Fractions, sub/superscripts, radicals, delimiters, n-ary operators, named functions, accents, bars, matrices and math alphabets are all preserved. A document's own <annotation encoding="application/x-tex"> is used verbatim in preference to anything reconstructed from the presentation markup. Because everything is LaTeX, a formula now survives a docx → md → docx round trip instead of degrading at each hop.

2. includeBreakNodes Now Works for ODF (#104)

It was implemented only in WordParser, and the docs said "DOCX only". The reason was that ODF has no inline break element to find — page and column breaks live on the paragraph style as fo:break-before / fo:break-after. Those now emit break nodes, and <text:soft-page-break/> maps onto the same lastRenderedPage type DOCX uses.

Note

DOCX writes breaks inline, so they land as children of the paragraph. ODF scopes them to the paragraph style, so they are emitted as siblings around it.

3. ODF Paragraph Styles Reach the Text (#104)

A paragraph style carrying fo:font-weight, fo:font-size or fo:color was parsed into the style table and referenced from the node's metadata — but the runs inside were built with empty formatting, so every generator emitted the text unstyled. The formatting sat visible in the AST and was simply never applied.

Runs now inherit the paragraph's formatting. That made a second gap load-bearing: styles that explicitly turn formatting off were not recorded at all. LibreOffice writes fo:font-weight="normal" whenever you un-bold part of a bold-styled paragraph, and with nothing recorded, that span had no way to override what it now inherited. Off-states are recorded, and a span carrying one clears the inherited value.


🔧 Also Fixed

  • Every table subtree was rendered twice (#105). The Markdown generator walked a node's children to build its output, then the table processor discarded that and traversed the rows and cells again. The two traversals compounded with nesting depth, so conversion time grew quadratically — a 184 KB document of nested tables took 7.5s. Footnotes inside table cells were collected on both passes and appeared twice. Time now grows linearly with input size.
  • Text inside PowerPoint grouped shapes was silently dropped (#106). traverseSpTree looked for a nested <p:spTree> inside each <p:grpSp>. A group does not contain one — per the schema it has the same content model as spTree itself — so the lookup always failed and the group's contents were skipped. Nested groups are covered too.
  • Security: an ODF table row carrying a large table:number-rows-repeated with no cells bypassed the per-document cell budget entirely, so a 733-byte file could exhaust memory and crash Node. Zero-cell rows are now charged against the same budget as every other repeat.

🛠 Getting Started

npm install officeparser@7.5.0

🔗 Full Changelog: View v7.5.0 details
🔗 Documentation & Visualizer: officeparser.harshankur.com