rtf-parser

A streaming Node.js parser that turns RTF documents into a structured tree of paragraphs and styled text spans.

Library
npm
v1.3.3
51stars
ISC

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
29/100Needs Attention
Development Activity0
Maintenance0
Community44
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
43/100Fair
Architecture60
Code Quality25
Innovation45
Learning Curve40

rtf-parser is a small Node.js library for reading RTF (Rich Text Format) input and converting it into a plain JavaScript object model — an RTFDocument made up of RTFParagraph and RTFSpan nodes carrying font, color, alignment, and character-set information. It exists purely as a building block: the parsed document isn’t useful on its own, but gives downstream tools a stable structure to walk when converting RTF into HTML, plain text, or another format.

The library is built as two piped Node streams — a tokenizing Transform stream that turns raw RTF bytes into typed tokens, and an interpreting Writable stream that tracks nested {...} group scopes and dispatches each RTF control word to build up the document tree. It supports a wide range of RTF 1.x features (multiple code pages, Unicode escapes, bold/italic/underline/strikethrough, alignment, indentation, font and color tables) while gracefully degrading on unsupported constructs like tables and stylesheets.

What You Get

  • A streaming, two-stage tokenizer + interpreter architecture that can parse RTF incrementally from a Readable stream, a string, or piped input
  • An RTFDocument/RTFParagraph/RTFSpan object model with cascading style inheritance (bold, italic, underline, strikethrough, alignment, indentation, sub/superscript)
  • Character-set handling for 25+ code pages plus Unicode escape sequences, via iconv-lite decoding
  • Font table and color table parsing so spans carry resolved font names and RGB colors rather than raw indices
  • Graceful degradation on unsupported RTF constructs (tables, stylesheets) instead of hard failures

Common Use Cases

  • Converting legacy RTF documents (from Word, email clients, or older desktop apps) into HTML for web display
  • Extracting plain text from RTF attachments in an email or document-processing pipeline
  • Building a custom RTF-to-Markdown or RTF-to-JSON converter on top of the parsed document tree
  • Migrating archives of RTF-formatted content into a structured CMS or database

Under The Hood

Architecture The library is a two-stage streaming pipeline. rtf-parser.js implements RTFParser, a Transform stream that walks raw RTF text character-by-character through a small finite-state machine (parseText, parseEscapes, parseControlSymbol, parseControlWord, parseControlWordParam, parseHexChar), emitting typed tokens (text, control-word, group-start, group-end, hexchar, ignorable, end-paragraph, error). Those tokens feed into RTFInterpreter (rtf-interpreter.js), a Writable stream that maintains a groupStack of RTFGroup instances mirroring RTF’s nested {...} scoping, dynamically dispatching each control word to a ctrl$<name> method to mutate document state. The data model (rtf-document.js, rtf-paragraph.js, rtf-span.js, rtf-group.js) uses a parent-pointer chain so get/getStyle cascade unset properties up through enclosing groups to the document root, and index.js composes the two streams behind simple parse/parse.string/parse.stream entry points with shared error handling. Style resolution logic is spread across RTFGroup.addContent, RTFDocument.addContent, and RTFInterpreter.finisher, so changing the cascading-style model would touch all three.

Tech Stack Plain CommonJS Node.js with no build step or TypeScript. Runtime dependencies are minimal: iconv-lite (^0.4.15) decodes hex-escaped and code-page text into proper Unicode, and readable-stream (^2.2.2) provides a consistent Stream implementation across older Node versions. Dev tooling — standard (zero-config linting run via pretest), tap (referenced by the test script), weallbehave/weallcontribute (community doc scaffolding), and standard-version (changelog/release automation) — reflects a mid-2010s Node library setup; there’s no CI config or bundler present.

Code Quality No test/ directory exists in the repository despite package.json’s test script invoking tap test/, so there is no verifiable automated test coverage in this snapshot. Control-word dispatch uses string-built method names ('ctrl$' + name) rather than an explicit switch or map, which is compact but harder to statically audit. Error handling is inconsistent: most errors flow through proper error events on the parser/interpreter streams, but one code path (_write in rtf-interpreter.js) calls process.emit('error', ...) for unknown token types — process isn’t the stream’s own EventEmitter, so that particular failure class is effectively swallowed rather than surfaced to callers. No type annotations (plain JS, no JSDoc/TypeScript), though standard’s lint rules are enforced via pretest.

API Design The public surface is small and pragmatic: parse(cb) returns a writable stream you can pipe into, parse.string(str, cb) and parse.stream(readableStream, cb) cover the common cases directly, and the resulting RTFDocument is a plain object with content/style arrays rather than a heavy class hierarchy — easy to walk without learning a bespoke API. The trade-offs are dated ergonomics: callback-only (no Promise or async-iterator surface), no shipped TypeScript types, and documentation limited to the README’s feature list with no dedicated examples directory or docs site.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search