Skip to content

fix: close five more open upstream issues and adopt two stalled PRs - #2

Merged
ygjb merged 2 commits into
masterfrom
upstream-issue-sweep
Aug 16, 2026
Merged

fix: close five more open upstream issues and adopt two stalled PRs#2
ygjb merged 2 commits into
masterfrom
upstream-issue-sweep

Conversation

@ygjb

@ygjb ygjb commented Aug 16, 2026

Copy link
Copy Markdown

Sweeps every remaining open defect on iarna/rtf-parser and adopts two stalled upstream pull requests. Where a reporter supplied a reproduction, that exact input is the test.

What this closes

Upstream Was Now
#15 Symbol font (\fcharset2) threw — and it's how Word writes bullets \'b7, list text survives
#15 (emoji) Surrogate pair threw ERR_OUT_OF_RANGE \u55357 \u56842 decodes to one code point
#28 \u61607 threw Signed and unsigned both work
#30 \fcharset238'CP238', which threw CP1250 — \'e8č
#32 "before 89504e470d0a… after" "before after"
#33 \ansicpg1256 rejected as unavailable Accepted — \'c7ا
#37 iconv-lite@0.4.15 (2018), readable-stream@2 iconv-lite@^0.7.3, readable-stream removed

Note how many of these were crashes, not wrong output — a single exotic font table or one out-of-range escape aborted the entire document. Unknown charsets now fall back to CP1252 with a debug note instead of throwing.

Credit where it's owed

  • #28 by @UKayeF (2020) — their diagnosis was correct. Their one-line swap to writeUInt16LE would have broken the signed form the spec actually prescribes (writeUInt16LE(-1) throws), so this normalises negatives first and handles both.
  • #33 by @facue (2022) — adopted as written.
  • #29 by @wwwildcat (2020) — proposed six named character control words. This fork already emits those plus eight more, so the PR is superseded, but they got there five years earlier and the README says so.

Deliberately not done

  • #35 tables — a substantial feature, not a defect. RTF tables (\trowd/\cell/\row/\intbl) need a new node type and real design.
  • #34 — a support question about callback timing, not a parser bug.

Dependency change

readable-stream used only Writable and Transform, both built into node for a decade — so this removes the dependency rather than bumping it. Runtime deps: 2 → 1. Verified with npm ls readable-stream --omit=dev returning empty.

Verification

  • 19 tests / 44 assertions green across both suites (test/encoding.js, test/upstream-issues.js), standard clean, npm test exit 0
  • Re-ran the 315k-character manuscript end-to-end: still extracts to exactly its own {\nofchars314892} header count, 1,966 apostrophes / 1,943 quotes / 308 en dashes, zero replacement characters

Scope note

The Symbol table covers 0xA0–0xFE, where the list glyphs live. The 0x20–0x7E half maps ASCII positions onto Greek and is left unmapped — documented in the source. It never throws; unmapped bytes become U+FFFD.

ygjb and others added 2 commits August 15, 2026 23:18
A sweep of everything still open on iarna/rtf-parser that is a defect rather
than a feature request. Each change carries a regression test using the
reporter's own reproduction where they supplied one.

interpreter: accept both the signed and unsigned forms of \uN
  The spec writes a code unit above 32767 as a negative number; plenty of
  producers emit the unsigned value instead, and writeInt16LE threw
  ERR_OUT_OF_RANGE on those -- an uncaught crash rather than a wrong character.
  A negative value is now normalised to its unsigned counterpart and anything
  outside the UTF-16 range is skipped rather than thrown.
  Adopts upstream PR iarna#28 by @UKayeF, whose diagnosis was right; the naive
  swap to writeUInt16LE in that PR would have broken the SIGNED form instead
  (writeUInt16LE(-1) throws), so this handles both.
  Closes: iarna#28. Also fixes the emoji break reported in iarna#15:
  \u55357 \u56842 is a surrogate pair, and both halves exceed 32767.

interpreter: \fcharset238 is CP1250, and \ansicpg1256 is available
  'CP238' was never a code page and iconv has never known it, so a document
  with an east-European font threw on its first hex escape. 1256 was mapped by
  font (\fcharset178) but rejected as a document code page.
  Adopts upstream PR iarna#33 by @facue. Closes: iarna#30, iarna#33.

interpreter: decode the Symbol font instead of throwing on it
  \fcharset2 is a glyph set, not a code page -- iconv has no 'SYMBOL' encoding,
  so a Symbol font (which is how Word writes bullets) took the whole document
  down. Adds an Adobe Symbol table for the 0xA0..0xFE range where the list
  glyphs live, so \'b7 becomes a real bullet. Any charset iconv does not
  recognise now falls back to CP1252 with a debug note rather than aborting:
  one exotic font table should not cost the reader the entire file.
  Closes: iarna#15 (the reported break and the bullet case; the
  0x20..0x7E Greek half is deliberately left unmapped and documented as such).

interpreter: skip \pict payloads instead of emitting them as text
  RTF stores an image as a hex payload inside {\pict ...}. With no handler it
  was decoded as ordinary text, so a document's content gained a span of raw
  hex -- 'before 89504e470d0a...' -- corrupting text extraction and word
  counts. Marked ignorable, exactly as \stylesheet and \info already are.
  Closes: iarna#32.

deps: drop readable-stream, iconv-lite@^0.7.3
  Only Writable and Transform were used, both built into node for a decade, so
  this REMOVES the dependency rather than bumping it. Runtime dependencies go
  from two to one, and the remaining one is current (0.4.24 dated from 2018).
  Closes: iarna#37.

Not addressed here, deliberately:
  iarna#35 tables -- a real feature, not a defect, and a large one.
  iarna#34 -- a support question about callbacks, not a bug in the parser.
  iarna#29 -- superseded: this fork already emits those six control words plus
  eight more. @wwwildcat got there first and deserves the credit.

Verified: 19 tests / 44 assertions green across both suites, lint clean, and
the 315k-character manuscript still extracts to exactly its \nofchars count
(314,892) with zero replacement characters.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ygjb
ygjb merged commit 3ecbef1 into master Aug 16, 2026
2 checks passed
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.

1 participant