rmtool

package module
v0.0.0-...-18a76ad Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 3, 2021 License: MIT Imports: 16 Imported by: 2

README

reMarkable Tools

Tools for working with the reMarkable notes format and API.

Status: This is very much work in progress. The basic functionality should work but there will be some bugs and missing features. Use at your own risk ;-)

This has been tested with the reMarkable 2 on Linux.

CLI Tool

A command line tool is included which serves as a example on how the API works. It should also be useful on its own:

  • ls lists the content from the device
  • get downloads notes as PDF files
  • put uploads PDF documents to the device
  • pin allows to set or remove bookmarks

The CLI tool uses the reMarkable cloud API.

Parser

The parser supports the v3 format for reMarkable notes.

RM Lines Format

The rm format is the proprietary format used by the reMarkable tablet. The format is used to store drawings made on the tablet.

The format is documented in the unofficial reMarkable Wiki.

In version 5, each .rm file contains one page. Each page consists of several layers which contain the strokes that make up the image.

The file consists of a simple header followed by the data. The data is structured into layers, strokes and dots.

All numeric values are little endian.

Header

The header length is 43 bytes and contains ASCII encoded text:

reMarkable .lines file, version=5

The remaining bytes are filled with whitespace.

Layer

The header is immediately followed by 4 bytes for a 32 bit unsigned integer which gives us the number of layers.

The next 4 bytes are again a 32 bit unsigned integer for the number of strokes in the first layer.

Stroke

Each stroke consists of the stroke data followed by the data for the dots. A stroke has the following attributes:

Size Datatype Description
4 bytes uint32 Brush Type
4 bytes uint32 Color
4 bytes uint32 Padding?
4 bytes float32 Brush Size
4 bytes - unknown (v5 only)
4 bytes uint32 Number of Dots

The data for the individual dots follows immediately after.

The Brush Types refer to the different "pencil" choices available on the tablet. The values are different for the v3 and the v5 format.

Version Brush ID
v3 Paint Brush 0
v3 Pencil 1
v3 Ballpoint 2
v3 Marker 3
v3 Fineliner 4
v3 Highlighter 5
v3 Eraser 6
v3 Mechanical Pencil 7
v3 Eraser 8
v5 Brush 12
v5 Mechanical Pencil 13
v5 Pencil 14
v5 Ballpoint 15
v5 Marker 16
v5 Fineliner 17
v5 Highlighter 18

The Color is either Black (0), Gray (1) or White (2).

The Brush Size is the selected base size of the brush (not to be confused with the effective width of the stroke). Predefined sizes are Small (1.875), Medium (2.0) and Large (2.125).

Dot

Each dot holds the following attributes:

Size Datatype Description
4 bytes float32 X-Coordinate
4 bytes float32 Y-Coordinate
4 bytes float32 Speed
4 bytes float32 Tilt
4 bytes float32 Width
4 bytes float32 Pressure

After the last byte of the dot data is read, The data for the next layer begins, starting with the number of strokes.

The Coordinates range between 0,0 and 1404,1872, the origin is at the top left corner.

Speed is a measure for how fast the stylus is drawn across the surface. Not sure how this would affect something (maybe the density of the stroke?).

The Pressure values ranges from 0.0 to 1.0.

The Width seems to be the effective width of the brush, already accounted for tilt and pressure.

Not quite sure if this is correct. Depending on the Brush type and size, pressure and tilt should determine the actual width of the stroke.

The Tilt value is the angle of the stylus towards the tablet surface. It is given in radians and ranges in two intervals from 0.0 to 1.5708 (0 to 90 degrees) and from 4.7124 to 6.2832 (270 to 360 degrees).

Render

The render package contains methods to render drawings to a bitmap (PNG) or PDF.

Status

This "basically works" but the rendering output does not quite match the original.

  • Some lines are way to thin/weak, others to strong.
  • When Rendering a drawing as an overlay on an existing PDF, the scale and placement of the drawing is off.

API

The api package contains an implementation for the reMarkable cloud API, including a client for websocket notifications.

If the tablet is connected to the API, one can access folders and documents through that API and also upload or modify content. Changes made through the API will by synced to the tablet.


Disclaimer

This is a personal project. It is not associated with the reMarkable company.


Credits

Sources:

Documentation

Index

Constants

View Source
const TrashFolder = "trash"

TrashFolder is the ID whoch is used for the reMArkable trash folder. Items that have been soft-deleted have their parent ID set to this value.

Variables

This section is empty.

Functions

func DefaultSort

func DefaultSort(one, other *Node) bool

DefaultSort is the comparsion function to sort nodes in the content tree with folders before documents and by name (case-insensitive). Pinned notes come before unpinned ones within a folder. The "Trash" folder comes last.

func IsDocument

func IsDocument(n *Node) bool

IsDocument is a Node filter that matches only documents (not foldeers).

func IsFolder

func IsFolder(n *Node) bool

IsFolder is a node filter that matches only folders.

func IsPinned

func IsPinned(n *Node) bool

IsPinned is a node filter that matches only pinned items.

func ReadPagedata

func ReadPagedata(r io.Reader) ([]string, error)

func SetLogLevel

func SetLogLevel(level string)

SetLogLevel sets the threshold for logging messages.

Level is one of "debug", "info", "warning" or "error".

func WritePagedata

func WritePagedata(pd []string, w io.Writer) error

Types

type AttachmentReader

type AttachmentReader func() (io.ReadCloser, error)

An AttachmentReader creates a reader for a PDF or EPUB attachment.

It must be supplied when creating documents with attachments. It must be possible to call this function multiple times.

type Content

type Content struct {
	DummyDocument bool `json:"dummyDocument"`

	ExtraMetadata ExtraMetadata `json:"extraMetadata"`
	// FileType is the type of content (i.e. handwritten Notebook or PDF, EPUB).
	FileType FileType `json:"fileType"`
	// Orientation gives the base layout orientation.
	// Individual pages can have a different orientation.
	Orientation Orientation `json:"orientation"`
	// PageCount is the number of pages in this notebooks.
	PageCount int `json:"pageCount"`
	// Pages is a list of page IDs in the correct order.
	Pages []string `json:"pages"`
	// CoverPageNumber is the page that should be used as the cover in the UI.
	CoverPageNumber int `json:"coverPageNumber"`

	// FontName for EPUB, empty to use default (probably a list w/ supported font names)
	FontName string `json:"fontName"`
	// LineHeight always seems to be -1 / 150 / 200 / 100?
	LineHeight LineHeight `json:"lineHeight"`
	// MArgins are the page margins (left/right?) for EPUB and PDF files, default is 100 (180 for PDF?)
	Margins int `json:"margins"`
	// TextAlignment for EPUB, left or justify
	TextAlignment TextAlign `json:"textAlignment"`
	// TextScale for EPUB, default is 1.0,
	TextScale float32   `json:"textScale"`
	Transform Transform `json:"transform"`
}

Content holds the data from the remarkable `.content` file. It describes the content for a notebook, specifically the sequence of pages. Collections have an empty content object.

func NewContent

func NewContent(f FileType) *Content

func (*Content) Validate

func (c *Content) Validate() error

type Document

type Document struct {
	Meta
	// contains filtered or unexported fields
}

A Document is a notebook, PDF or EPUB with all associated metadata and Drawings.

A Document is internally backed by a Repository and can load additional content as it is requested.

func NewEpub

func NewEpub(name, parentID string, r AttachmentReader) *Document

TODO - implement

func NewNotebook

func NewNotebook(name, parentID string) *Document

NewNotebook creates a new document of type "notebook" with a single emtpty page. TODO: template name?

func NewPdf

func NewPdf(name, parentID string, r AttachmentReader) (*Document, error)

NewPdf creates a new document for a PDF file.

The given AttachmentReader should return a Reader for the PDF file. Note that this can return an error as the PDF needs to be read for this.

func ReadDocument

func ReadDocument(r Repository, m Meta) (*Document, error)

ReadDocument is a helper function to read a full Document from a repository entry. TODO make this a method of the repository, transfer implementation to internal/

func (*Document) AttachmentReader

func (d *Document) AttachmentReader() (io.ReadCloser, error)

AttachmentReader returns a reader for an associated PDF or EPUB files according to FileType().

An error is returned if this document has no associated attachment.

func (*Document) CoverPage

func (d *Document) CoverPage() int

CoverPage is the number of the page that should be used as a cover.

func (*Document) CreatePage

func (d *Document) CreatePage() string

CreatePage creates a new page with a drawing and append it to the document. TODO: Orientation? Template?

func (*Document) Drawing

func (d *Document) Drawing(pageID string) (*lines.Drawing, error)

Drawing loads the handwritten drawing for the given pageID.

Note that not all pages have associated drawings. If a page has no drawing, an error of type "Not Found" is returned (use IsNotFound(err) to check for this).

func (*Document) FileType

func (d *Document) FileType() FileType

FileType is one of the supported types of content (Notebook, PDF, EPUB).

func (*Document) Orientation

func (d *Document) Orientation() Orientation

Orientation is the base layout (Portait or Landscape) for this document.

func (*Document) Page

func (d *Document) Page(pageID string) (*Page, error)

Page loads meta data associated with the given pageID.

func (*Document) PageCount

func (d *Document) PageCount() int

PageCount returns the number of pages in this document.

Note that for PDF and EPUB files, the number of drawings can be less than the number of pages.

func (*Document) Pages

func (d *Document) Pages() []string

Pages returns a list of page IDs on the correct order.

func (*Document) Validate

func (d *Document) Validate() error

func (*Document) Write

func (d *Document) Write(repo Repository, w WriterFunc) error

type ExtraMetadata

type ExtraMetadata struct {
	LastBallpointColor       string
	LastBallpointSize        intStr
	LastBallpointv2Color     string
	LastBallpointv2Size      intStr
	LastBrushColor           string
	LastBrushThicknessScale  intStr
	LastCalligraphyColor     string
	LastCalligraphySize      intStr
	LastClearPageColor       string
	LastClearPageSize        intStr
	LastColor                string
	LastEraseSectionColor    string
	LastEraseSectionSize     intStr
	LastEraserColor          string
	LastEraserSize           intStr
	LastEraserThicknessScale intStr
	LastEraserTool           string //"Eraser"
	LastFinelinerColor       string
	LastFinelinerSize        intStr
	LastFinelinerv2Color     string
	LastFinelinerv2Size      intStr
	LastHighlighterColor     string
	LastHighlighterSize      intStr
	LastHighlighterv2Color   string
	LastHighlighterv2Size    intStr
	LastMarkerColor          string
	LastMarkerSize           intStr
	LastMarkerv2Color        string
	LastMarkerv2Size         intStr
	LastPaintbrushColor      string
	LastPaintbrushSize       intStr
	LastPaintbrushv2Color    string
	LastPaintbrushv2Size     intStr
	LastPen                  string // Ballpointv2
	LastPenColor             string
	LastPenThicknessScale    intStr
	LastPencil               string // SharpPencil
	LastPencilColor          string
	LastPencilSize           intStr
	LastPencilThicknessScale intStr
	LastPencilv2Color        string
	LastPencilv2Size         intStr
	LastReservedPenColor     string
	LastReservedPenSize      intStr
	LastSelectionToolColor   string
	LastSelectionToolSize    intStr
	LastSharpPencilColor     string
	LastSharpPencilSize      intStr
	LastSharpPencilv2Color   string
	LastSharpPencilv2Size    intStr
	LastSolidPenColor        string
	LastSolidPenSize         intStr
	LastTool                 string // Ballpoint
	LastUndefinedColor       string
	LastUndefinedSize        intStr
	LastZoomToolColor        string
	LastZoomToolSize         intStr
	ThicknessScale           intStr
}

func NewExtraMetadata

func NewExtraMetadata() ExtraMetadata

type FileType

type FileType int

FileType are the different types of supported content for a notebook.

const (
	Notebook FileType = iota
	Epub
	Pdf
)

func (FileType) Ext

func (f FileType) Ext() string

func (FileType) MarshalJSON

func (f FileType) MarshalJSON() ([]byte, error)

func (FileType) String

func (f FileType) String() string

func (*FileType) UnmarshalJSON

func (f *FileType) UnmarshalJSON(b []byte) error

type LayerMetadata

type LayerMetadata struct {
	// Name is the display name for this layer.
	Name string `json:"name"`
}

LayerMetadata describes one layer.

func (LayerMetadata) Validate

func (l LayerMetadata) Validate() error

type LineHeight

type LineHeight int
const (
	LineHeightDefault LineHeight = -1
	LineHeightSmall   LineHeight = 100
	LineHeightMedium  LineHeight = 150
	LineHeightLarge   LineHeight = 200
)

type Meta

type Meta interface {
	ID() string
	Version() uint
	Name() string
	SetName(n string)
	Type() NotebookType
	Pinned() bool
	SetPinned(p bool)
	LastModified() time.Time
	Parent() string

	// Validate checks the internal state of this item
	// and returns an error if it is not valid.
	Validate() error
}

Meta is the interface for a single entry (a nodebook or folder) in a Repository. These entries are used to access and change metadata for an item.

The Reader() method can be used to download additional content, i.e. the pages and drawings for a notebook.

type Node

type Node struct {
	Meta
	// ParentNode holds a reference to the parent or nul.
	ParentNode *Node
	// Children is a list of all child nodes.
	Children []*Node
}

Node is the representation for an entry in the content tree. A not can either be a document or a collection (which has child nodes).

func BuildTree

func BuildTree(items []Meta) *Node

BuildTree creates a tree view of all items in the given repository. Returns the root node.

func (*Node) Filtered

func (n *Node) Filtered(match ...NodeFilter) *Node

Filtered returns a new node that is the root of a subtree starting at this node. The subtree will contain only nodes that match the given NodeFilter and the parent folders of the matched nodes.

func (*Node) IsLeaf

func (n *Node) IsLeaf() bool

IsLeaf tells if this is a leaf node (without children).

func (*Node) Path

func (n *Node) Path() []string

Path returns the path components for this node. That is, the names of its parent and grandparent up to the root node.

func (*Node) Sort

func (n *Node) Sort(compare NodeComparator)

Sort sorts the subtree starting at this node by the given sort rule. Sorting is in-place.

func (*Node) Walk

func (n *Node) Walk(f func(n *Node) error) error

Walk applies the given function to the subtree starting at this node, (including this node). Returns the first error that is encountered or nil.

type NodeComparator

type NodeComparator func(one, other *Node) bool

NodeComparator is used to sort nodes in a tree. It should return true if "one" comes before "other".

type NodeFilter

type NodeFilter func(n *Node) bool

A NodeFilter is a function that can be used to test whether a node should be included in a filtered subset or not.

func MatchName

func MatchName(s string) NodeFilter

MatchName creates a node filter that matches the given string against the Name of a node. The match is case insensitive and allows partial matches ("doc" matches "My Document").

func MatchPath

func MatchPath(path string) NodeFilter

MatchPath creates a node filter that matches on the path components of a node (case insensitive).

The path to match against is expected to contain the item name, i.e. "foo/bar/baz" will match the item named "baz" in the folder "foo/bar".

type NotebookType

type NotebookType int

NotebookType is used to distinguish betweeen documents and folders.

const (
	DocumentType NotebookType = iota
	CollectionType
)

func (NotebookType) MarshalJSON

func (n NotebookType) MarshalJSON() ([]byte, error)

func (*NotebookType) UnmarshalJSON

func (n *NotebookType) UnmarshalJSON(b []byte) error

type Orientation

type Orientation int

Orientation is the layout of a notebook page. It can be Portrait or Landscape.

const (
	Portrait Orientation = iota
	Landscape
)

func (Orientation) MarshalJSON

func (o Orientation) MarshalJSON() ([]byte, error)

func (Orientation) String

func (o Orientation) String() string

func (*Orientation) UnmarshalJSON

func (o *Orientation) UnmarshalJSON(b []byte) error

type Page

type Page struct {
	// contains filtered or unexported fields
}

Page describes a single page within a document.

func (*Page) HasTemplate

func (p *Page) HasTemplate() bool

HasTemplate tells if this page is associated with a background template. Returns false for the "Blank" template.

func (*Page) Layers

func (p *Page) Layers() []LayerMetadata

Layers is the metadata for the layers in this page.

func (*Page) Number

func (p *Page) Number() uint

Number is the 1-based page number.

func (*Page) Template

func (p *Page) Template() string

Template is the name of the background template. It can be used to look up a graphic file for this template.

type PageMetadata

type PageMetadata struct {
	// Layers is the list of layers for a page.
	Layers []LayerMetadata `json:"layers"`
}

PageMetadata holds the layer information for a single page.

func (PageMetadata) Validate

func (p PageMetadata) Validate() error

type Repository

type Repository interface {
	// List returns a flat list of all entries in the repository.
	// The list is in no particular order - use BuildTree() to recreate the
	// tree structure with folders and subfolders.
	List() ([]Meta, error)

	// Update changes metadata for an entry.
	Update(meta Meta) error

	// Reader creates a reader for one of the components associated with an
	// item, e.g. the drawing for a single page.
	//
	// This function is typically used internally by ReadDocument and friends.
	Reader(id string, version uint, path ...string) (io.ReadCloser, error)

	// PagePrefix returns the filename prefix for page related paths.
	//
	// This function is normally used internally by ReadDocument and friends.
	PagePrefix(pageID string, pageIndex int) string

	// Upload creates the given document in the repository.
	Upload(d *Document) error
}

Repository is the interface for a storage backend.

It can either represent local files copied from the tablet or notes accessed via the Cloud API.

The repository offers methods to work on the metadata of items, allowing operations like rename or bookmark.

type TextAlign

type TextAlign int
const (
	AlignLeft TextAlign = iota
	AlignJustify
)

func (TextAlign) MarshalJSON

func (t TextAlign) MarshalJSON() ([]byte, error)

func (TextAlign) String

func (t TextAlign) String() string

func (*TextAlign) UnmarshalJSON

func (t *TextAlign) UnmarshalJSON(b []byte) error

type Transform

type Transform struct {
	// TODO: these might also be floats
	// never seen anything other than identity transform with values set to 1 or 0
	M11 int `json:"m11"`
	M12 int `json:"m12"`
	M13 int `json:"m13"`
	M21 int `json:"m21"`
	M22 int `json:"m22"`
	M23 int `json:"m23"`
	M31 int `json:"m31"`
	M32 int `json:"m32"`
	M33 int `json:"m33"`
}

func NewTransform

func NewTransform() Transform

type WriterFunc

type WriterFunc func(path ...string) (io.WriteCloser, error)

Directories

Path Synopsis
cmd
internal
fs
pkg
api
fs

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL