Skip to content

Instantly share code, notes, and snippets.

@owulveryck
Created April 22, 2021 07:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save owulveryck/ae2e071bd5740c96bdc92c7f8f8f7172 to your computer and use it in GitHub Desktop.
Save owulveryck/ae2e071bd5740c96bdc92c7f8f8f7172 to your computer and use it in GitHub Desktop.
Reading the .line format from the reMarkable
package main
import (
"encoding/binary"
"errors"
"fmt"
"image"
"io"
"log"
"os"
"strings"
)
// https://remarkablewiki.com/tech/filesystem?s[]=lines
// and https://plasma.ninja/blog/devices/remarkable/binary/format/2017/12/26/reMarkable-lines-file-format.html
func (p *Page) String() string {
return p.Header
}
type Page struct {
Header string
Layers []*Layer
}
type Layer struct {
Strokes []*Stroke
}
type Stroke struct {
Pen uint32
StrokeColor uint32
PenWidth float32
Segments []*Segment
}
type Segment struct {
P image.Point
Speed float32
Direction float32
Width float32
Pressure float32
}
func main() {
p := &Page{}
err := Read(os.Stdin, p)
if err != nil {
log.Fatal(err)
}
fmt.Println(p)
}
func Read(r io.Reader, p *Page) error {
return p.readFrom(r)
}
// readFrom r and fill p
func (p *Page) readFrom(r io.Reader) error {
var buf struct {
Header [43]byte
NumLayers int32
}
err := binary.Read(r, binary.LittleEndian, &buf)
if err != nil {
return err
}
p.Header = string(buf.Header[:])
if !strings.Contains(p.Header, "version=5") {
return errors.New("only version 5 is supported")
}
p.Layers = make([]*Layer, buf.NumLayers)
for i := 0; i < int(buf.NumLayers); i++ {
p.Layers[i] = &Layer{}
err := p.Layers[i].readFrom(r)
if err != nil {
return err
}
}
return nil
}
// readFrom r and fill l
func (l *Layer) readFrom(r io.Reader) error {
var numStrokes int32
err := binary.Read(r, binary.LittleEndian, &numStrokes)
if err != nil {
return err
}
l.Strokes = make([]*Stroke, numStrokes)
for i := 0; i < int(numStrokes); i++ {
l.Strokes[i] = &Stroke{}
err := l.Strokes[i].readFrom(r)
if err != nil {
return err
}
}
return nil
}
func (s *Stroke) readFrom(r io.Reader) error {
var buf struct {
Pen int32
Color int32
_ int32
Width float32
_ int32
NumSegments int32
}
err := binary.Read(r, binary.LittleEndian, &buf)
if err != nil {
return err
}
s.Pen = uint32(buf.Pen)
s.StrokeColor = uint32(buf.Color)
s.PenWidth = float32(buf.Width)
s.Segments = make([]*Segment, buf.NumSegments)
for i := 0; i < int(buf.NumSegments); i++ {
s.Segments[i] = &Segment{}
err := s.Segments[i].readFrom(r)
if err != nil {
return err
}
}
return nil
}
func (s *Segment) readFrom(r io.Reader) error {
var buf struct {
X float32
Y float32
Speed float32
Direction float32
Width float32
Pressure float32
}
err := binary.Read(r, binary.LittleEndian, &buf)
if err != nil {
return err
}
s.P.X = int(buf.X)
s.P.Y = int(buf.Y)
if !s.P.In(image.Rect(0, 0, 1404, 1872)) {
return fmt.Errorf("point %v is out of bound", s.P)
}
s.Speed = buf.Speed
s.Direction = buf.Direction
s.Width = buf.Width
s.Pressure = buf.Pressure
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment