Skip to content

Add v6 .rm format support with multi-color highlights#59

Merged
ddvk merged 3 commits into
ddvk:masterfrom
ademasi:v6-highlights-upstream
Jun 1, 2026
Merged

Add v6 .rm format support with multi-color highlights#59
ddvk merged 3 commits into
ddvk:masterfrom
ademasi:v6-highlights-upstream

Conversation

@ademasi

@ademasi ademasi commented Apr 29, 2026

Copy link
Copy Markdown

Summary

Two commits:

  1. Add v6 .rm format support — parse the firmware 3.0+ tagged-block/CRDT scene tree so geta can export annotations from modern reMarkable documents. Also reads the cPages content layout (CRDT ordering + PDF page redirection) and falls back to deriving the page map from .rm filenames when no other source is available.
  2. Render v6 highlights in their actual color — the v6 parser now keeps the per-highlight color (and the optional color_rgba carried by firmware 3.6+ when color == 9), and the renderer maps it to RGB instead of flattening every highlight to yellow.

Why v6

The current parser handles v3/v5 only. As of firmware 3.0+ the device writes .lines files in the v6 tagged-block format. Without v6 parsing, geta silently produces PDFs with no annotations from any document edited on a modern device.

V6 differs from v3/v5 in several places:

  • Coordinate space for PDF-backed pages is normalized around x = page width / 2; positions are in device pixels at 226 DPI and need scaling to 72-DPI PDF points.
  • Stroke widths come from per-point Width values, not the v3/v5 BrushSize × 6 − 10.8 formula.
  • Highlights are top-level scene items (not strokes) with a list of rectangles per glyph range.
  • Page list has moved from a flat pages[] to cPages with CRDT timestamps and PDF page redirection (redir).

Why per-highlight color

The v6 glyph block carries a PenColor index (yellow=3, green=4, pink=5, etc., matching rmscene's enum) and, on firmware 3.6+, an optional color_rgba packed in BGRA at tag 10. The renderer was hardcoded to RGB(1,1,0) so multi-color highlighting on the device was lost on export. The same helper is now used for the v5 highlighter stroke path so its BrushColor is honored too.

Reference

Test plan

  • go build ./... clean
  • go test ./... passes
  • geta on a v3/v5 notebook — verify no regression
  • geta on a v6 PDF with multi-color highlights — verify yellow/green/pink each render in their own color
  • geta on a v6 notebook with strokes — verify stroke widths and positions look right

🤖 Generated with Claude Code

ademasi and others added 3 commits April 29, 2026 22:00
Parse the v6 tagged-block/CRDT scene tree format used by reMarkable
firmware 3.0+, enabling `geta` to export annotations from modern
documents. V6 PDF-backed pages use DPI-based coordinate scaling
(226→72 DPI) instead of the device-pixel mapping used by v3/v5.
Stroke line widths are derived from per-point width data rather than
the v3/v5 brush-size formula.

Reads the firmware 3.0+ `cPages` content layout with CRDT ordering and
PDF page redirection, plus a last-resort pageMap fallback discovered
from the .rm filenames in the zip.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Highlights on the reMarkable can be yellow, green, pink (and arbitrary
RGB on firmware 3.6+ via the optional color_rgba field), but `geta`
exports flattened every highlight to yellow because the renderer ignored
the per-highlight Color and the parser dropped the optional RGBA tag.

- Extend BrushColor with the missing PenColor codes (Yellow=3, Green=4,
  Pink=5, GreyOverlap=8, HighlightDynamic=9, Green2, Cyan, Magenta,
  Yellow2) to match rmscene's enum
  (https://github.com/ricklupton/rmscene).
- Read the optional tag-10 BGRA-packed RGBA on glyph blocks into a new
  Highlight.ColorRGBA field; firmware 3.6+ uses Color=HighlightDynamic
  with the actual color stored there.
- Resolve highlight colors at render time via a small helper that maps
  the enum to RGB and prefers ColorRGBA when present. Apply the same
  helper to v5 highlighter strokes so their BrushColor is honored too.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Three new test groups guard against regressions introduced by the v6
parser and color-aware highlight rendering:

- TestV3V5NoHighlights: parses the existing test_v3.rm / test_v5.rm
  fixtures and asserts the new Rm.Highlights field stays empty (a
  v6-only concept) and layers are still populated.

- TestBrushColorPenColorEnum: locks down each BrushColor constant to
  rmscene's PenColor integer value so the wire format keeps decoding
  correctly even if constants are reordered later.

- TestHighlightRGB*: covers the renderer's color resolution path —
  explicit color_rgba override (firmware 3.6+), per-enum mapping for
  every named highlight color, fallback to yellow for unknown codes,
  and the legacy v3/v5 Black-default brush color landing on a visible
  grey rather than disappearing.

All seven existing TestGenerate* end-to-end PDF render tests (a3/a4/a5/
letter/rm/tmpl/strange) continue to pass, validating that v3/v5
documents still produce the same rendered output after the v6 changes
to normalized(), the BrushColor switch, and the highlight loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@ddvk ddvk merged commit 434da60 into ddvk:master Jun 1, 2026
1 check 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.

2 participants