npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

simple-super-doc

v0.11.6

Published

Faithful DOCX renderer with exposed typed IR for building document editors

Readme

simple-super-doc

Browser-only TypeScript library that parses a .docx file into a typed intermediate representation (IR) and renders it to HTML. Built for document editors and viewers that need a faithful, inspectable model of the document.

▶️ Try it live — drag and drop any .docx in your browser to see it rendered. Nothing is uploaded; parsing and rendering happen entirely on your machine.

  • Typed IRparse() returns a DocxDocument of Blocks (paragraphs, tables) with computed styles, lists, images, and page size. Inspect or transform it before rendering.
  • HTML renderingrender() paints the IR into a container element.
  • No DOM at parse time — parsing is pure; only render() touches the DOM.
  • Security — text is written with textContent, never innerHTML; hyperlink hrefs and image sources are scheme-sanitized (only http(s)/mailto/tel/ relative links and data:image/http(s) images), so a malicious .docx can't inject a javascript: or other unsafe URL.

Install

npm install simple-super-doc

Usage

import { parse, render } from 'simple-super-doc'

const buf = await file.arrayBuffer()       // a .docx File/Blob
const doc = await parse(buf)               // typed DocxDocument IR
render(doc, document.getElementById('view')!)

// Optional: show tracked changes (deletions struck through, insertions
// underlined) instead of the accepted/final view.
render(doc, view, { showRevisions: true })

parse throws DocxParseError (code: 'INVALID_ZIP' | 'MISSING_ENTRY') for non-docx or malformed input.

What it covers

  • Text & styles — bold/italic/underline/strikethrough, super/subscript, font size/family, color, highlight and shading, paragraph alignment (including OOXML justified both), spacing, indentation and borders, and the full style cascade (docDefaults → named style → direct formatting).
  • Structure — numbered and bulleted lists (nested), tables with gridSpan/vMerge, column widths, cell margins and cell borders (resolved by cascade: table style → tblBorderstcBorders, so borders that come only from a table style like TableGrid are drawn), and block/run ordering recovered from the raw XML (paragraphs, tables, mid-paragraph hyperlinks and tracked changes keep their real sequence — in the body and inside cells).
  • Images — inline and anchored DrawingML (a:blip) and legacy VML (w:pict/v:imagedata), as base64 data URLs; external/linked images (r:link) render from their URL. (EMF/WMF metafiles are skipped — browsers can't display them.)
  • Content controls & wrappersw:sdt (structured document tags / form controls), w:smartTag and w:customXml are transparent: their content (block or inline, possibly nested) renders in place instead of being dropped.
  • Headers & footers — default headerReference/footerReference, rendered in the page margins on every page; distinct per section, with a section that declares none inheriting the previous section's (OOXML semantics); distinct first-page (w:titlePg) and even-page (w:evenAndOddHeaders) variants.
  • Page breaksw:pageBreakBefore and explicit <w:br w:type="page"/>.
  • Tab stops — right/center/decimal stops with dot/hyphen/underscore leaders (table-of-contents rows render as Title …… 12).
  • FieldsPAGE and NUMPAGES resolve live (robust to \* MERGEFORMAT switches); other fields (DATE, REF, PAGEREF, TOC, HYPERLINK, …) render their cached result. Both the complex fldChar form (whether split across runs or packed into a single run, as Google Docs exports) and the compact w:fldSimple form are supported.
  • Footnotes & endnotes — footnotes at the bottom of the referencing page; endnotes on a final page.
  • Tracked changes — deletions, insertions, and moves (w:moveFrom/ w:moveTo), shown or hidden via the showRevisions render option.
  • Right-to-leftw:bidi paragraphs and w:rtl runs (Arabic/Hebrew) render with dir="rtl"; Unicode text (incl. CJK) is preserved as-is.
  • Equations — OMML (m:oMath) renders as its linear text (inline, display and in table cells); 2D layout is out of scope (see below).
  • Multiple sections — per-section page size and orientation (e.g. a landscape table page between portrait pages), per-section headers/footers, with continuous page numbering. Each section is routed to the plain or full-page-background path by its own content, so template documents may mix page sizes across sections.

Page-aware rendering

Some templates paginate themselves with full-page background images (a cover slide, framed body pages, a closing slide) but carry no explicit page breaks — no <w:br w:type="page"/> and no <w:lastRenderedPageBreak/>. Word/LibreOffice decide the pages purely in their layout engine.

When render() detects a full-page background (a behindDoc=1 anchor), it reconstructs the pages with a two-pass approach and a set of heuristics. These live in src/renderer/layout.ts as pure, unit-tested functions (test/layout-heuristics.test.ts):

  • Two-pass measurement — each block is measured in a hidden container (Pass 1), then content is distributed into page-sized boxes (Pass 2). This needs a real layout engine, so it runs in the browser, not jsdom.
  • Empty paragraphs are spacers — they collapse to 0px in a browser but occupy a line in Word, so empty paragraphs get a min line height (EMPTY_LINE_EM). Their vertical rhythm positions things like a cover's customer-name field.
  • Section headings force a new page — a heading is large text (fontSize >= 24) or a wide-short "text-as-image" banner; a small icon that precedes a text heading travels with it. Each framed section starts a page.
  • Watermarks overlay, not flow — large floating images (decorative frames/watermarks) are absolutely positioned behind the text and contribute zero pagination height, instead of pushing content onto extra pages.
  • Background regions by image order — floating backgrounds are positioned by absolute page coordinates, so the order of distinct background images drives which page each covers (cover → page 0, body frame → pages 1+), not their XML position.
  • Full-bleed slides — a page whose only content is a near-full-width image is drawn edge-to-edge; blank pages are dropped.

These thresholds are tuned for proposal/letter-style templates. A document without a full-page background skips all of this and renders as a normal flow.

Limitations & non-goals

This is a faithful but simple HTML renderer, not a Word layout engine. The following are intentionally out of scope:

  • Floating layout — text wrapping around floating images, multi-column layouts, shapes, charts, and SmartArt are not laid out. (Inline images and tables are supported.) Text inside a text box (w:txbxContent, DrawingML or VML) is recovered into the flow so it is never lost, but the box is not floated or positioned.
  • 2D math layout — OMML equations (m:oMath) are recovered as their linear text (e.g. A=πr2), inline and in order, so the content is never lost, but fractions, superscripts and radicals are not laid out two-dimensionally.
  • Pixel-exact pagination — without Word's line-breaking and layout engine, page breaks are reconstructed by two-pass DOM measurement and heuristics. Pagination is close but not guaranteed to match Word/LibreOffice line for line. For a byte-faithful page image, convert the .docx to PDF.
  • Comments — review comments are parsed away (treated as noise); only tracked-change insertions/deletions are surfaced (via showRevisions).

Development

npm run typecheck    # tsc --noEmit
npm test             # vitest (node) — parser + pure layout heuristics
npm run test:browser # Playwright (headless Chromium) — page-aware rendering
npm run build        # esm + d.ts into dist/
npm run build:demo   # IIFE bundle into demo/ for the drag-and-drop demo

The two-pass, page-aware pagination (table splitting, per-page footnotes) needs a real layout engine, so it's covered by browser tests in test/browser/ rather than the node suite. Install the browser once with npx playwright-core install chromium.

License

MIT