Skip to content

fix(save): underline, strikeout and highlights on rotated pages run along the text (#527) - #533

Merged
DutchSailor merged 2 commits into
mainfrom
fix/527-markup-quads
Oct 6, 2026
Merged

DutchSailor merged 2 commits into
mainfrom
fix/527-markup-quads

Conversation

@DutchSailor

Copy link
Copy Markdown
Collaborator

Closes #527. Builds on #530 (Caret and Replace, #508 part 1): until #530 is merged, the diff of this PR includes its commit; the change of this PR itself is the last commit.

What happened

Text markups (Highlight, Underline, StrikeOut, Squiggly) were saved with each quad axis-aligned in user space in a fixed order, and without an appearance stream. Viewers build the appearance from the quads and take the edge from the first to the second point as the text direction, so on pages with /Rotate 90 or 270 underline and strike lines were drawn across the text as short marks.

What this changes

  • js/pdf/saver/text-markup-dict.js writes the quads in reading direction as the page is displayed (taking /Rotate into account), plus an appearance stream along the text and /OPS_TextDir. It reuses feat(annotations): Caret and Replace annotations in load, save and drawing (#508, part 1) #530's geometry: new quadUnderline and quadSquiggle in js/annotations/corrections/geometry.js, and buildMarkupAppearance in correction-dicts.js (strike midline, underline, squiggle with round joins, highlight as one fill with a multiply blend). /C, /CA, /T, /Contents, /M, /F, popups, /IRT replies and status replies are kept.
  • New js/text/leesrichting.js: markups made on vertical or upside-down text take their reading direction from the selected text.
  • Markups from other programs that the user did not change keep their quads exactly; files saved by older versions with the old order are repaired on /Rotate 90 and 270. On /Rotate 180 the old order cannot be told apart from upside-down text, so it stays as it was before.
  • The line width of underline, strikeout and squiggly is stored (/OPS_LineWidth) and read back; a replacement strike's appearance uses the caret's colour, matching /C.

Verification

  • New and adapted tests in geometry.test.mjs, text-markup-dict.test.mjs, leesrichting.test.mjs (real PDFs through pdf.js on every rotation), bestaande-markeringen.test.mjs (old-order files load to the same model; the earlier byte-for-byte guarantee is replaced on purpose), correction-roundtrip.test.mjs, correction-load.test.mjs and correction-wiring.test.mjs. Each failed first for the reported reason.
  • Render check of a four-page test file (/Rotate 0/90/180/270) in PDFium and MuPDF: underline, strikeout and squiggle run along the text and highlights cover it, for markups made in the app, markups from another program and repaired old-order markups. The old files show exactly the cross marks of the issue.
  • An adversarial review found four defects (no reading direction for vertical text, foreign markups rewritten across the text, replace strike colour, line width lost on reload); all fixed test-first.
  • npm run test:unit 2761 passed, 0 failed; npx vite build and npm run typecheck green.

Known, left alone

🤖 Generated with Claude Code

…awing (#508, part 1)

Text-edit corrections are written and read in the documented PDF structure:
- Insert: a /Caret annotation (blue).
- Delete: a /StrikeOut with /IT /StrikeOutTextEdit (red).
- Replace: a /Caret parent with a /StrikeOut child, linked by /IRT and
  /RT /Group (purple). The loader accepts both parent/child directions.
All of them get appearance streams with quads in reading direction on
every page rotation, so other viewers draw them as the app does.
Struck-out text is kept in OPS_MarkedText, never in /Contents.

In the app a caret is drawn, selected, moved, copied and deleted like
other markups. A Replace pair moves, deletes and undoes as one group,
locked halves are respected, and copies get fresh links and no
duplicate /NM. Existing highlights, underlines, strikeouts and replies
keep loading and saving exactly as before. Labels are added in every
locale.

The keyboard-driven proofreading mode follows in a separate change.
…long the text (#527)

Text markups (Highlight, Underline, StrikeOut, Squiggly) were saved with
quads axis-aligned in user space in a fixed order and without an
appearance stream, so on pages with /Rotate 90 or 270 other viewers drew
underline and strike lines across the text.
- Quads are written in reading direction as the page is displayed, with
  an appearance stream along the text (multiply blend for highlights)
  and /OPS_TextDir, reusing the geometry of the Caret work (#508).
- Markups made on vertical or upside-down text take their reading
  direction from the selected text.
- Untouched markups from other programs keep their quads; files saved by
  older versions are repaired on /Rotate 90 and 270. On /Rotate 180 the
  old order cannot be told apart from upside-down text and stays as it
  was.
- The line width of underline, strikeout and squiggly is kept
  (/OPS_LineWidth), and a replacement strike's appearance uses the
  caret's colour.
@DutchSailor
DutchSailor merged commit 8c7ea4f into main Oct 6, 2026
4 checks passed
DutchSailor added a commit that referenced this pull request Oct 6, 2026
@DutchSailor
DutchSailor deleted the fix/527-markup-quads branch October 6, 2026 06:38
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.

Underline and strikeout on rotated pages show as a short cross mark in other viewers

1 participant