Skip to content

feat: v6 file format support + remove rmapi/unipdf dependencies#441

Open
joagonca wants to merge 25 commits intoddvk:masterfrom
joagonca:master
Open

feat: v6 file format support + remove rmapi/unipdf dependencies#441
joagonca wants to merge 25 commits intoddvk:masterfrom
joagonca:master

Conversation

@joagonca
Copy link
Copy Markdown
Contributor

@joagonca joagonca commented Apr 22, 2026

Summary

This PR adds native support for the reMarkable v6 file format (Paper Pro, software 3.0+) and removes the rmapi and unipdf dependencies.

What's changed

v6 file format support

  • Detect .rm file version (v3/v5/v6) by reading the 43-byte header before attempting to parse
  • Route v6 files to the rmc-go library for native in-process PDF rendering via Cairo
  • Route v5/v3 files to the existing rendering pipeline (now Cairo-based, see below)
  • Multi-page v6 document export with correct page ordering via content.json
  • New files: exporter/rmc_go.go, exporter/version.go, exporter/version_test.go

Remove rmapi + unipdf dependencies

  • github.com/juruen/rmapi replaced by:
    • internal/archive — pure-Go reMarkable .zip archive reader
    • internal/encoding/rm — pure-Go .rm binary format decoder (v3/v5)
  • github.com/unidoc/unipdf/v3 replaced by:
    • github.com/ungerik/go-cairo — Cairo-based PDF surface rendering via CGo
    • github.com/pdfcpu/pdfcpu — pure-Go PDF merging (annotations onto background)
  • Removed exporter/license.go (the go:linkname hack to inject a UniPDF community license key)
  • RenderRmapi() renamed to RenderPDF() throughout

Build changes

  • Cairo is now a required build dependency (libcairo2-dev, pkg-config)
  • CGO_ENABLED=1 and -tags cairo are the default for all build targets
  • pdf_stub.go provides a clear error message if built without Cairo
  • Docker base image: scratchdebian:bookworm-slim (needed for libcairo2 runtime)
  • CI workflows updated to install Cairo before building
  • New dependency: github.com/joagonca/rmc-go v1.1.1
  • Replaced github.com/danjacques/gofslockgithub.com/juju/fslock

Other changes

  • Removed schema v4 hash tree support (hardcoded to v3)
  • Removed HASH_SCHEMA_VERSION environment variable
  • Removed embedded MQTT broker and screen sharing infrastructure
  • Simplified UI: removed TanStack Table, URL-based document navigation
  • Hardened RemoveDocument() against path traversal (applies common.Sanitize() consistently)

New dependency: rmc-go

rmc-go is a Go library wrapping the reMarkable .rm v6 format renderer. It uses Cairo for native PDF/SVG generation, supporting colour and the new pen types introduced with the Paper Pro.

Testing

  • go build -tags cairo ./cmd/rmfakecloud/
  • go test -tags cairo ./... ✅ (all existing + new version detection tests)
  • go vet -tags cairo ./...

Breaking changes

  • Cairo (libcairo2-dev) is now a required build dependency
  • Docker images are larger (~80MB base instead of scratch)
  • HASH_SCHEMA_VERSION env var no longer supported
  • MQTT-based screen sharing removed (was not functional remotely)

Made CGo build more compliant
Resolve minor conflicts in go.mod (add rmc-go + juju/fslock deps),
go.sum (take v6_rmc version), and blobstore.go (add imports).
Remove rmapi and unipdf dependencies, replacing with:
- internal/archive: pure-Go reMarkable archive reader
- internal/encoding/rm: pure-Go .rm binary format decoder
- pdf_cairo.go: Cairo-based PDF renderer (replaces unipdf)
- pdfcpu: PDF merging (replaces unipdf)

Conflict resolutions:
- Dockerfile/Makefile: Cairo is now the mandatory default build
- go.mod: combined deps from both branches, Go 1.25.1
- blobstore.go: keep v6 routing logic, RenderRmapi -> RenderPDF
- documents.go: keep v6 routing logic, RenderRmapi -> RenderPDF
- imports: rmapi/archive -> internal/archive everywhere
…slock

Two callsites in blobstore.go still used the old fslock.Lock()/Handle API.
Migrate to fslock.New() + LockWithTimeout() consistent with v6_rmc.
Apply common.Sanitize(id) at function entry, consistent with
ExportDocument. Removes filepath.Base wrapping since getPathFromUser
already applies sanitizeFileName. Addresses CodeQL path traversal
warnings.
Bring in 20 upstream commits:
- feat: passcode reset for rm1/rm2
- feat: rmdoc import/export
- fix: generation-0 root missing
- fix: SupportedFileTypes for 3.25
- fix: /pair connect paths
- fix: RMAPI_HOST typo
- fix(ci): changelog append
- docs: 3.25/3.26 compat, mTLS guide, setup cleanups

README conflict resolved: keep our v6+doc-rendering rows, add upstream passcode row.
meta := sanitizedID + storage.MetadataFileExt
fullPath := fs.getPathFromUser(uid, meta)
err = os.Rename(fullPath, path.Join(trashDir, meta))
err = os.Rename(fullPath, filepath.Join(trashDir, meta))
meta := sanitizedID + storage.MetadataFileExt
fullPath := fs.getPathFromUser(uid, meta)
err = os.Rename(fullPath, path.Join(trashDir, meta))
err = os.Rename(fullPath, filepath.Join(trashDir, meta))
@joagonca
Copy link
Copy Markdown
Contributor Author

I've merged both unipdf and v6 implementations, fast forwarded to the current state of this repo, and did this huge PR in an attempt to have these changes accepted upstream with the "least effort". In quotes, because I know that it is a lot of changes.

Also, I've validated the CodeQL suggestions, and I don't think the still pending ones are valid :)

But open to the discussion.

@rmitchellscott
Copy link
Copy Markdown
Collaborator

rmitchellscott commented Apr 22, 2026

Why are you removing working features?

Removed schema v4 hash tree support (hardcoded to v3)
Removed HASH_SCHEMA_VERSION environment variable
Removed embedded MQTT broker and screen sharing infrastructure

@joagonca
Copy link
Copy Markdown
Contributor Author

joagonca commented Apr 23, 2026

My bad - they aren't removed. This was 'written' after doing the merge of both v6 and remove_unipdf branches, but before bringing in the upstream changes (since they differed in time, this has caused some git confusion).

I've compared files from both upstream and this PR, and they're practically identical. So no features were removed. I just forgot to update the description after cherry-picking the changes, as rebase didn't work out of the box.

EDIT: The only visible difference to upstream, is the presence of soheilhy/cmux in go.mod. But when I run go mod tidy on my machine, that one is removed.

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.

3 participants