Skip to content

v8.1.0

Choose a tag to compare

@github-actions github-actions released this 29 Sep 22:00
· 23 commits to master since this release

v8.1.0: 📐 LaTeX In and Out & Hidden Notes That Stay Hidden

I am pleased to announce the release of officeParser v8.1.0! This release adds LaTeX in both directions. LaTeX documents and whole Overleaf projects now parse into the officeParser AST, so a paper converts to Word, OpenDocument, HTML, Markdown or EPUB in one call. And anything officeParser can parse (Word, PowerPoint, Excel, OpenDocument, PDF, RTF, Markdown, HTML, EPUB, CSV) can be written as LaTeX source that compiles unmodified with pdfLaTeX, XeLaTeX, LuaLaTeX, upLaTeX, pLaTeX and latex (Chinese, Japanese and Korean included), with presentations becoming beamer slide decks and a bundle mode that packages the images for Overleaf. It also keeps <!-- ... --> comments as hidden notes through Markdown, HTML and LaTeX conversions, and makes Markdown and HTML round trips keep what was written: text that looks like markup, bold italic, line breaks, code, links, linked images, tables and whitespace. Word text boxes, content controls, SmartArt and embedded chunks, RTF headers and comments, and PowerPoint comments that were lost are read, and every parser and writer is bounded against crafted documents, with limits that grow with the document so a large one is not cut short. Output changes for many documents: the behavior changes below list each, with what to do about it.

Warning

Behavior changes

Output of the same document

  • Markdown <!-- ... --> is now a hidden comment, not text. It used to come through as visible text in every output (and as &lt;!-- ... --&gt; in regenerated Markdown). It is now a comment node (metadata.sourceSyntax: 'html'): kept by the Markdown, HTML and LaTeX generators, left out of text, CSV, RTF, PDF, DOCX, ODT, EPUB and chunks, and not part of any node's text. A comment on its own line directly after a table row now ends the table, as in GFM. To keep such text visible, escape it as \<!--.
  • Markdown is read as CommonMark reads it, and written so it reads back. Emphasis pairs by CommonMark's rules (***text*** is bold italic, snake_case is not emphasis, *a **b** c* is italic with bold inside); links, emphasis and code may span a line end; * * * is a rule; $$...$$ inside a paragraph is display math; a block indented to a list item's content column after a blank line is the item's content, not code; a link to #id is internal (so ignoreInternalLinks applies). Output escapes text that would read as markup (2\*3, \[1\], \$5, and \# , \- , 1\. where a line starts), keeps one-line code blocks fenced, and puts one blank line between blocks. A soft line break inside a paragraph is merged into the text around it, so a Markdown AST has fewer text nodes.
  • HTML (and EPUB chapters) are read as a browser reads them. Character references decode everywhere (&rsquo;, &#8217;, attribute values too; &amp;quot; stays the literal &quot;); whitespace collapses across element boundaries and &nbsp; is kept; omitted end tags end where a browser ends them, and formatting open at an implied paragraph end carries into the next paragraph (<p><b>a<p>b</b> makes both bold); <o:p>, <!DOCTYPE> and Word's conditional comments are no longer text; <html lang>, the declared charset and <base href> are honoured; a table's <caption> comes before the table; GitHub, Pandoc and EPUB 3 footnote markup becomes notes.
  • Heading ids are GitHub's in every writer. They keep letters of every script (#überblick), leave punctuation out (version-20 for "Version 2.0", where HTML wrote version-2-0) and number repeats (intro, intro-1). Update links you wrote to HTML output's old ids.
  • DOCX line breaks and tabs are now in the output. A Shift+Enter line break (w:br) and a tab were dropped unless includeBreakNodes was set, so words ran together ("Line oneLine two", a table of contents' "Tables2"). They are now always a break node and a \t; ODF's text:line-break is a break node too (it was a \n text node). includeBreakNodes now gates only page, column and last-rendered-page breaks.
  • DOCX, RTF and PPTX content that used to be lost is read. DOCX content controls, text boxes (as blocks after their paragraph), SmartArt (as a list), alternative-format chunks and mc:AlternateContent fallbacks (Word's emoji); RTF headers, footers, comments and text boxes; PPTX comments of both kinds, each with its author, date and the comment it answers (parentId), a paragraph per paragraph. Tracked deletions and moved-away text are no longer read (a moved paragraph came out twice), and RTF headers and footers moved from the body to ast.auxiliary, where DOCX's already were.
  • Spreadsheets and charts. XLSX booleans read TRUE/FALSE (they read 1/0); an ODS table nested in a cell is part of its cell, not a sheet of its own; a chart's text holds its category labels once, before the series (they were repeated before each); an ODS cell with a line break or merged cells keeps them.
  • A note or comment is one node every reference shares. A DOCX footnote cited three times is one node in three notes arrays (it was three copies); writers write it once and refer back (DOCX output uses a NOTEREF field, ODT text:note-ref, HTML gives each reference its own id, chunks hold it at its first reference). Code that walks the AST should expect the same object more than once; JSON.stringify still writes a copy per reference.
  • EPUB attachments are named after their files (cover.jpg, where every chapter numbered its own from image_1.jpeg), one attachment per picture however often it is shown.
  • RAG chunks: a picture's alt text is in its chunk as [Image: alt]; a table's header heads its chunks only when it takes at most half a chunk; metadata.closestHeading and sheetName are cut at 256 characters.
  • Links: //host/path is written as https://host/path in HTML, EPUB and Markdown output, and \\host\share is refused (on Windows it reached a file share with your credentials). CSV: a cell holding ; or a tab is quoted, and one starting with a full-width =+-@ is guarded like =+-@.
  • Output declares the document's language. HTML used to say lang="en" whatever the source; HTML, EPUB and the native PDF engine now write the parsed language.

Limits, errors and processes

  • In Node, pdf.js runs in separate processes (at most one per CPU; further parses wait their turn), each limited to pdfParserConfig.processMemoryMb (1,024 MB). A PDF that needs more rejects with the new PDF_PROCESS_FAILED instead of crashing your process. Set pdfParserConfig.separateProcess: false to run pdf.js in your process as before; where no process can start (a restricted runtime), that happens with a PDF_SEPARATE_PROCESS_UNAVAILABLE warning.
  • PDFs are read within budgets that grow with the file's size: maxTextItems, maxOperators (only operators the parser keeps; past it, images and colours stop, text does not), maxAnnotations, and maxTimeMs, the CPU time the pdf.js process spends (load on your machine does not use it up; pdf.js in your own process has no time limit). Past one, PDF_CONTENT_LIMIT_EXCEEDED names it. A budget accepts Infinity.
  • New document limits, each raisable in decompressionLimits and reported when reached: maxXmlElements (2,000,000; the parse rejects with XML_ELEMENT_LIMIT_EXCEEDED, since each element costs about a kilobyte of heap), maxTableCells now bounds XLSX too, maxRepeatedContent and maxRawContentLength. maxTableCells and maxRepeatedContent grow by one cell and 16 characters per byte of the document, so a large workbook is read whole. Generated output fills at most a million empty grid positions plus 16 per byte (a sheet past it is laid out closer, TABLE_GRID_LIMIT_EXCEEDED), and inlines at most 128 MB of pictures per document, standalone HTML included (IMAGE_NOT_INLINED).
  • New errors in place of generic ones. XML nested past the stack rejects with MAX_NESTING_DEPTH_EXCEEDED (was FILE_CORRUPTED); a generator whose output grows past what a string holds, or given an AST built in code that shares its nodes along too many paths, throws OUTPUT_TOO_LARGE (was the engine's RangeError).
  • A cancelled parse always rejects with AbortError (with ocr: true, an abort during OCR used to resolve without the images' text), and abortSignal now stops pdf.js mid-request.

Configuration and types

  • Configuration mistakes are reported. An option a generator does not recognize (texConfig.bundel), a convert() option at the top level instead of under parseConfig/generatorConfig, and a value an option does not accept each raise a warning in the result's messages, and the CLI prints them without --verbose; the default is used. A parse's csvDelimiter still carries over to CSV output, unless it could start a formula line (it starts with =, +, - or @) or holds a line break, which is now reported and , used.
  • Resolved configs gain keys: htmlParserConfig.preserveComments, texParserConfig, decompressionLimits.maxXmlElements/maxRawContentLength/maxRepeatedContent and the six new pdfParserConfig options. Code that snapshots ast.config will see them.
  • Type unions grew: SupportedFileType and UniversalGeneratorFormat gain 'tex', and OfficeParserConfig.fileType accepts the FileTypeAlias names ('latex', 'zip', ODF templates). An exhaustive switch over those types needs a 'tex' case.
  • A file named .zip is routed by its contents; it used to be rejected as an unsupported extension.

🌟 Key Highlights

1. Read LaTeX: .tex files and Overleaf projects

const ast = await OfficeParser.parseOffice('paper.tex');
const { value: docx } = await ast.to('docx');

// A whole Overleaf project ("Download source"): \input chapters and images come from the zip
const project = await OfficeParser.parseOffice('overleaf-project.zip', { extractAttachments: true });
npx officeparser paper.tex --to=docx --output=paper.docx

The parser understands the LaTeX people actually write: sections and \labels, lists (nested, numbered, task lists), tabular/longtable with \multicolumn/\multirow, figures and captions, math (with your own macros expanded), footnotes and endnotes, links, cross-references resolved to their numbers, citations and thebibliography, code listings, fancyhdr headers and footers, the \maketitle title block (kept as a visible title, so a paper converted to Word still opens with its title and authors), beamer slides with speaker notes, numbered theorems and proofs, babel and polyglossia language switches (the main language becomes the document's language), class front matter such as keywords and IEEE author blocks, and your \newcommands, \newenvironments and \NewDocumentCommands. It reads a document the way pdfLaTeX would compile it: \ifXeTeX-style engine tests, \newif switches and \@ifpackageloaded are decided, so a document written for several engines yields one branch's text, not all of them. Older files in Latin-1 or another legacy encoding decode correctly. Because LaTeX is a programming language, the parser is a bounded interpreter: expansion bombs, self-including files and runaway nesting stop at hard limits, parsing time grows linearly with the document, nothing is executed, and nothing is read outside the project. Three new warnings (LATEX_CONSTRUCT_NOT_INTERPRETED, LATEX_EXPANSION_LIMIT_REACHED, LATEX_FILE_NOT_FOUND) say exactly what could not be read.

2. to('tex'): LaTeX from any document

const ast = await OfficeParser.parseOffice('paper.docx', { extractAttachments: true });

const { value: tex } = await ast.to('tex');                                   // one .tex, images inside
const { value: zip } = await ast.to('tex', { texConfig: { bundle: true } });  // main.tex + images/
npx officeparser paper.docx --extractAttachments --to=tex --texConfig.bundle --output=paper.zip

The output is idiomatic LaTeX rather than a pile of absolute positioning:

Your document has You get
Headings \section ... \subparagraph (\chapter in report/book), with \labels wherever something links to them
Lists Real nested itemize/enumerate, numbering continued across interruptions, task lists as check boxes
Tables Page-breaking longtables with a ruled grid, merged cells (\multicolumn/\multirow), column alignment, cell colours, a repeated header row
Footnotes, endnotes \footnote and \endnote at the point of reference
Equations Real typeset math (every format's equations already arrive as LaTeX)
Code lstlisting with syntax highlighting where listings knows the language, verbatim otherwise
Slides beamer frames with the slide title, speaker notes as \note, and long slides continuing on a new frame
Running headers/footers fancyhdr
A title (Word's Title style) \maketitle, printing only the lines the document shows
Review comments LaTeX % comments: kept for the author, invisible on the page

Only the packages a document uses are loaded, and the output is deterministic. Generated files are clean and watermark-free (no comment headers), populating standard PDF document metadata via \hypersetup{pdfcreator={officeParser}} when compiled to PDF. It compiles unmodified with pdfLaTeX, XeLaTeX, LuaLaTeX, upLaTeX, pLaTeX and latex (the last three through dvipdfmx), on TeX Live 2021 and later. Fonts are chosen per engine: under XeLaTeX and LuaLaTeX, Greek and Cyrillic text is set in Computer Modern Unicode and Chinese, Japanese and Korean text in the Fandol, Harano Aji or UnFonts families TeX Live ships (through xeCJK or luatexja, with proper line breaking), each used only where it is installed. pdfLaTeX, which cannot typeset those scripts, shows a visible [U+XXXX] marker in their place instead of failing.

3. Safe with untrusted documents

LaTeX is a programming language, so a naive converter is an injection vector: a document containing \input{/etc/passwd} could read files on the machine that compiles it. officeParser escapes every piece of text, scheme-checks and percent-encodes every link, reduces image names to safe file names, and refuses to put code in a verbatim block that would close itself early, or that would end a beamer frame early. Equations are the one place content is written as live LaTeX, so they must pass a check that refuses any command able to read or write files, run programs, redefine commands or build a command's name from text; a refused equation is shown as literal text and reported with the new MATH_WRITTEN_AS_TEXT warning.

The parsers were hardened against hostile files in the same release. A crafted XLSX or DOCX could write onto JavaScript's shared Object.prototype for the whole process, changing every later conversion; every map keyed by a document's own names now has no prototype. XLSX sheets of unclosed rows, HTML full of unclosed <script> tags and a dozen other inputs took time in the square of their size (a 2.6 KB spreadsheet took 23 seconds) and are linear now; an EPUB showing one picture thousands of times no longer takes gigabytes, and a few-kilobyte DOCX, XLSX, EPUB or HTML file referring to one large footnote, comment or string thousands of times no longer crashes the process with an out-of-memory error (nor makes writers copy the note at every reference), notes or comments referring to each other no longer take time doubling per level, and one picture shown many times no longer multiplies output past what memory holds; links and pictures to //host or \\host\share can no longer reach a Windows file share from an EPUB or saved HTML file; table spans are bounded; content nested in its own kind (notes, comments, fonts, styles, drawings, chart series, math) is read once rather than once per level; XLSX workbooks, content a document repeats by reference (ODF repeated cells, XLSX shared strings, a style's font or a link target on thousands of runs), includeRawContent output and PDFs each have a limit you can raise (maxTableCells, maxRepeatedContent, maxRawContentLength, and pdfParserConfig.maxTextItems, maxOperators, maxAnnotations and maxTimeMs), and the limits grow with the document's size, so a few kilobytes can no longer ask for hundreds of megabytes of output while a 4 MB workbook of a million cells is read and converted whole; in Node, pdf.js now runs in separate processes (one per CPU at most) under a memory limit (pdfParserConfig.processMemoryMb), so a PDF that inflates into gigabytes inside pdf.js fails the parse instead of crashing your process, its time budget counts the CPU pdf.js spends (so a busy server never cuts a document short), and abortSignal can stop a PDF mid-stream; and the XML reader no longer writes to your console. Parsing is synchronous, so abortSignal cannot stop a parse already running: for untrusted input, parse in a worker you can end.

4. Images, characters and wide tables handled for you

  • Images travel inside the .tex: LaTeX only reads images from files, so each PNG or JPEG goes into the .tex as a filecontents* block that writes it, as a small all-ASCII PDF, beside the file when it compiles. One .tex compiles with its pictures on every engine, with no shell escape or Ghostscript, and officeParser reads the pictures back when it parses that .tex. Prefer separate files? texConfig.bundle: true returns a zip with main.tex and every image under images/, and texConfig.embedImages: false refers to images/ files (the new IMAGES_NOT_BUNDLED warning names them).
  • Characters the fonts lack (check marks, arrows, math symbols, dingbats) get automatic fallbacks, so they print instead of vanishing or stopping the run.
  • Very wide spreadsheets continue below themselves in 16-column bands instead of running off the page.

texConfig also offers documentClass (auto, article, report, book, beamer), standalone: false for a body you can paste into your own document, numberSections, and the familiar format/landscape/margin page settings.

The two directions agree: LaTeX that officeParser writes parses back to the same structure, and a generate, parse, generate cycle is stable after one round, so a document can go Word to LaTeX and back again.

5. Hidden notes stay hidden: <!-- ... --> comments survive conversion

A comment in Markdown used to come through as literal text, so converting to HTML showed it on the page and regenerating Markdown wrote it back as &lt;!-- ... --&gt;. Comments are now kept as hidden notes:

  • Markdown to Markdown is byte-for-byte, for comments on their own lines (even spanning several) and inline ones.
  • HTML gets a real <!-- --> comment, or, for rich-text editors whose DOM parser drops comments, a <span data-html-comment="…"> under htmlConfig.sourceAttributes. Both read back; comments in HTML input are kept when you set htmlParserConfig.preserveComments: true.
  • LaTeX gets % <!-- ... --> lines, invisible when typeset and read back by the LaTeX parser, so Markdown to LaTeX and back keeps them.
  • Every other format (Word, OpenDocument, PDF, EPUB, RTF, CSV, plain text, RAG chunks) leaves them out, so a note you hid never appears in the output.

6. Markdown and HTML round trips keep what you wrote

Editors that save through Markdown (md to HTML to the editor and back) lose nothing on the way now. Text that looks like markup is escaped, so 2*3*4, [1] or $5 stay text; bold italic, consecutive line breaks, code spans and pipes in table cells read back as written; link titles may hold parentheses and link text brackets. An image that is a link, such as a README badge [![build](badge.svg)](https://ci), keeps its target in every format that has links: the new ImageMetadata.link, linkType and linkTitle are read from Markdown, HTML, Word, PowerPoint, OpenDocument, RTF and LaTeX, and written to Markdown, HTML, DOCX, ODT, RTF and LaTeX. A table the Markdown generator has to write as HTML (merged cells, or the commonmark preset) keeps its cells' links, pictures, code, math and line breaks, and is read back with the HTML parser. No-break and em spaces at the edges of paragraphs, headings, list items and cells are kept as text. A document from any format, saved as Markdown, reads back as the same Markdown: pictures, headings and lists on pages and slides stay separate blocks, a PDF's standing endnote is written with the other definitions, and runs differing only in a font Markdown does not write are one span. Markdown is read as CommonMark reads it: 5 * 3 * 2 stays text, * * * is a rule, a quote or admonition can hold a list or heading, a footnote can hold paragraphs and a list, a code span can wrap across lines, and a link target can hold parentheses. And every Markdown construct, closed or not, parses in time proportional to the text.

7. The visualizer converts, too

Drop a .tex file or an Overleaf zip into the visualizer to see its AST and every rendering, including a new LaTeX window (defaulting to clean syntax-highlighted raw mode, with informative guidance on client-side previews). A new Export panel downloads the loaded document in every format officeParser generates (DOCX, ODT, LaTeX, EPUB, PDF, HTML, Markdown, RTF and more), so it doubles as an in-browser converter: LaTeX to Word, Word to LaTeX, and everything in between. The configuration experience is streamlined with responsive collapsible cards for Advanced Parser Settings and Metadata Overrides (keeping essential options prominent while cutting clutter), and Markdown, HTML and CSV uploads parse without picking a file type by hand.


🔧 Also Fixed

  • HTML entities no longer double-decode. Escaped literal text such as &amp;quot; no longer turns into a quote mark on the way in; text and attribute values decode in one pass, the exact inverse of how the generators escape them.

  • Markdown placeholder look-alikes no longer crash the parser. A literal __CODE_BLOCK_0__ line in a document used to throw "undefined" is not valid JSON; it is now ordinary text.

  • Display math written inside a Markdown paragraph is read as math, instead of as stray dollar signs around inline math.

  • convert() lists each warning once. Every generation warning used to appear twice in messages.

  • Stopping OCR mid-image no longer crashes Node. An abort or terminateOcr() landing just as an image was handed to an OCR worker ended the Node process with an uncaught error from Tesseract. Also: a cancelled recognition no longer keeps the process alive for 30 seconds, terminateOcr() during a parse fails the pending images' OCR (OCR_FAILED) instead of leaving the parse waiting, and a recognition longer than autoTerminate is no longer cut short.

  • Fenced code blocks are read as CommonMark reads them. ```c++, ```objective-c, ```js title="a.js", indented fences, and fences inside list items (indented four spaces, as editors write them), quotes and admonitions used to be reflowed into text on the first save.

  • Markdown parsing time grows with the text, not faster. Lines of backticks, unclosed links, spans or comments, unclosed fences, ::: admonitions, MDX components or HTML tables, many lines starting an unclosed [, [^ or *[, and headings of many {# each took from seconds to minutes; a line of hundreds of thousands of inline runs overflowed the call stack. Plain-text output of a long run of blank lines is linear too.

  • Errors reach your onWarning. HTML nested too deeply, an unsupported output format, an invalid style map and PDF generation failures were printed to the console (or sent to the parse's handler) instead of the onWarning you passed. RTF nested thousands of groups deep is refused with MAX_NESTING_DEPTH_EXCEEDED, as deep HTML is, instead of overflowing the stack.

  • HTML output keeps pictures and captions where they belong. A picture in a paragraph is an inline <img> (its block markup split the paragraph), and reading this library's HTML back no longer turns each picture's file-name caption into a paragraph; any other caption is kept.

  • Bookmarks survive HTML and Markdown. A node's second and later ids (a Word table of contents' _Toc targets among them) were dropped when HTML this library wrote was read back, as were the ids of cells, rules, definition lists, admonitions, quotes, equations and pictures; a Markdown <a id> inside a line turned into visible text on the next save. All are read back, and written where a reader looks for them.

  • Sheets keep their content in Markdown and HTML. A sheet with merged cells, or any sheet under the commonmark preset, was written to Markdown as nothing at all; a CSV comment line or a spreadsheet picture became an empty row; a sheet built without cell positions was an empty grid in HTML.

  • RTF output keeps code blocks, equations, definition lists and pictures by URL, which it dropped or ran together.

  • The native PDF engine draws pictures that sit in a paragraph, which is where Word, OpenDocument and Markdown put them; they were missing from its PDFs. Embeds are written as their link.

  • An embed built without a type, or a YouTube video given by its URL alone, keeps its URL in HTML and Markdown output, where it became an empty YouTube block.

  • RAG chunks include a table of one row, whose text reached no chunk, and a footnote in a table's data row, which reached none either; math in a sentence no longer breaks its line.

  • OpenDocument fields and partly formatted spreadsheet cells are read. A date, page or figure number, or cross-reference in an ODF paragraph, a list's header, grouped table rows, and every ODS cell holding a formatted word (which kept only that word) came through incomplete.

  • PowerPoint shapes written with a fallback (an equation, an SVG picture) are read, where they were dropped.

  • RTF: Cyrillic, Greek, Chinese and other text outside the code page no longer comes out with a ? after every character, text in the Japanese, Chinese and Korean code pages decodes, Word's nested and merged tables are read as tables, and headers, footers, comments and text boxes are read (a footnote or comment in a table cell or list item no longer breaks the table or list around it). RTF output writes headers, footers and comments, nested tables as Word does, and any colour (a named one made readers refuse the file).

  • SmartArt text is read in PowerPoint (as a nested bulleted list) and Word, where every SmartArt diagram came through empty.

  • Word documents holding HTML, MHT or RTF chunks are read, in the body, tables, headers, footers, notes and comments. Files from tools such as html-docx-js keep their whole body in such a chunk, and parsed as empty. A chunk that cannot be read is reported (ALT_CHUNK_NOT_READ) and the rest of the document is read.

  • Word content controls and text boxes are read. A table of contents, cover page or form in a content control, a table row or cell wrapped in one, and the text of a text box (where Word puts them, inside a run) were left out of the parse; a text box's paragraphs, lists and tables now follow the paragraph that draws it.

  • A spreadsheet's cell positions cannot exhaust memory. One cell at XFD1048576, or a few thousand cells along a diagonal, made HTML and EPUB output run out of memory and CSV and Markdown output grow with the square of the cells. Such a grid is laid out closer before any writer fills it, with a TABLE_GRID_LIMIT_EXCEEDED warning; the budget grows with the document, so a real sparse sheet (400 columns, 3,000 rows) is written as it stands.

  • HTML code blocks keep syntax-highlighted code: tokens wrapped in <span>s were dropped, and <br> line breaks ran the lines together.

  • Markdown in GitHub-style HTML table cells is read as Markdown, and a table in a <center> or <figure> is a table.

  • A Markdown document starting with a rule keeps its first section, which was read as front matter and lost.

  • Plain-text output puts a definition's term and description on their own lines.

  • An AST node of an unknown type is written as its content by every generator, where HTML and EPUB failed on it and Markdown and RTF wrote the word undefined in its place.

  • MDX components in code samples are kept. Stripping <Callout>-style components also removed them from fenced code and code spans.

  • An ODF picture that is a link is no longer dropped, and HTML with a block inside a paragraph no longer loses the text after it.

  • Inline HTML directly in the body is one paragraph (Hello <b>world</b>! was three), and HTML output writes a code block or display equation beside a paragraph instead of inside it.

  • A Markdown embed label cannot write markup into the Markdown output.

  • terminateOcr() cannot hang on a language download that never finishes; it waits at most the load timeout.

  • A PDF attachment is typed application/pdf, where it came out as application/octet-stream.

  • A decompression limit reached while identifying an archive is reported as that limit, instead of as an unsupported format.

  • Raw HTML in Markdown survives a save. A README's centred logo (<p align="center"><img ...></p>), <details> blocks and inline tags like <kbd> were escaped into visible markup on the first save; they are now read as what a renderer shows.

  • RAG chunks keep pictures' alt text (as [Image: alt]), and a table cell's pictures and notes; a cell of several formatted runs was split into words.

  • An EPUB's pictures are attachments named after their files, once each, where every chapter numbered its own from image_1, so two chapters' pictures shared a name.

  • A footnote's own footnotes are written: HTML and PDF output left them out of the notes.

  • More Markdown read as CommonMark reads it: a change of list marker starts a new list, <me@example.com> is a mail link, an autolink's &amp; is decoded, a --- block is front matter only when it is YAML, and indented code ends at its last line.

  • HTML heading ids are GitHub's, as every other format's are (version-20 for "Version 2.0"), so links written for Markdown headings reach them; &#146;-style references read as the Windows-1252 characters browsers show; and a <pre> is read whole, a shell prompt before its <code> included.

  • Visualizer: the RTF preview shows the document's pictures, and EPUB input offers the HTML parser options that apply to it.

  • Visualizer: a chunk's heading can no longer inject markup into the RAG-chunks window.

  • Visualizer: "Open result in Visualizer" from Template Studio now loads the rendered document completely, so downloads and Reparse refer to it rather than the previous file.

  • Visualizer: Reparse is enabled only when a setting changed, and the Download menus are keyboard and screen-reader accessible.

  • Word line breaks and tabs are kept. A Shift+Enter break and a tab were dropped unless includeBreakNodes was set, so words ran together; they are text now, and an emoji Word stores in a compatibility fallback is read.

  • PowerPoint's classic comments are read, which no version read, and modern comments keep each paragraph and the thread they answer.

  • HTML from Word, GitHub and the web reads as a browser shows it. <o:p>, conditional comments and a leading <!DOCTYPE> no longer come out as text; GitHub, Pandoc and EPUB 3 footnotes are notes (their text was lost); a page's charset (a Windows-1252 "Save as Web Page" file, a UTF-16 one), <html lang>, <title> and <base href> are honoured; a table's caption is kept; a nested table's cells stay in it.

  • EPUB: every chapter is read or reported, a percent-encoded or .xml one included; a DRM-encrypted book is refused with DOCUMENT_DECRYPTION_FAILED rather than read as noise; EPUB output never holds a character XML forbids, and its ids are valid XML names.

  • Internal links resolve in DOCX and ODT output to tables, pictures, equations and list items too, where only paragraphs and headings had bookmarks.

  • OpenDocument: a LibreOffice chart has its data, merged spreadsheet cells keep the columns after them, a cell's paragraphs stay on separate lines, a repeated note reference is the note, and hidden text stays hidden; ODT output writes a cell's formatted runs as one paragraph.

  • More Markdown reads as written: HTML in capitals, a line break in a heading, loose lists numbered 1., several terms per definition, front matter lists, closed headings (## x ##), <angle> link targets, pictures without alt text and the pandoc dialect's admonitions; abbreviations are matched in linear time.

  • CLI: --maxInlineImageBytes and the other generator-only options take effect, where they were reported as unrecognized.


🛠 Getting Started

npm install officeparser@8.1.0

🔗 Full Changelog: View v8.1.0 details
🔗 Documentation & Visualizer: officeparser.harshankur.com