diff --git a/CHANGELOG.md b/CHANGELOG.md
index b92dd4f..a830149 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
## rmapi master
+* Added geta and mgeta command to read annotated notebooks
+
* Increased http timeout to 5 minutes to enable upload of larger files
* Add user-agent header to be a good reMarkable citezen
diff --git a/README.md b/README.md
index b8d7d41..6785f2b 100644
--- a/README.md
+++ b/README.md
@@ -25,11 +25,12 @@ documents through a FUSE system. You can read further at the bottom of this file
Install and build the project:
-`go get -u github.com/juruen/rmapi`
+`go get -u github.com/peerdavid/rmapi`
+
## Binary
-You can download an already built version for either Linux or OSX from [releases](https://github.com/juruen/rmapi/releases).
+You can download an already built version for either Linux or OSX from [releases](https://github.com/peerdavid/rmapi/releases).
# API support
@@ -88,6 +89,21 @@ mput /Papers
Use `get path_to_file` to download a file from the cloud to your local computer.
+## [optional] Download a file with annotations
+Use `geta path_to_file` to download a file from the cloud to your local computer.
+This command is only available, if all necessary tools are installed:
+ - convert (Note: Policy must be set to read|write for pdfs: https://stackoverflow.com/questions/42928765/convertnot-authorized-aaaa-error-constitute-c-readimage-453)
+ - rsvg-convert
+ - pdf270
+ - pdftk
+ - python3
+
+
+Note: If there exists a template folder under `%HOME/Templates/Remarkable`,
+then those are used for notebook backgrounds. Otherwise the background is
+simply white.
+
+
## Recursively download directories and files
Use `mget path_to_dir` to recursively download all the files in that directory.
@@ -98,6 +114,18 @@ E.g: download all the files
mget .
```
+## [optional] Recursively download directories and files with annotations
+
+Use `mgeta path_to_dir` to recursively download all the annotated files in that directory.
+Note: This command is only available if the geta command is also available.
+
+E.g: download all the files
+
+```
+mgeta .
+```
+
+
## Create a directoy
Use `mkdir path_to_new_dir` to create a new directory
@@ -148,3 +176,8 @@ If you want to give it a go, you can run:
```bash
rmapi --fuse-mount mount_point
```
+
+
+# Thanks to
+[1] rmapi, https://github.com/peerdavid/rmapi
+[1] rM2svg, https://github.com/reHackable/maxio
diff --git a/api/api.go b/api/api.go
index 53d317c..3abf186 100644
--- a/api/api.go
+++ b/api/api.go
@@ -7,11 +7,11 @@ import (
"io/ioutil"
"os"
- "github.com/juruen/rmapi/filetree"
- "github.com/juruen/rmapi/log"
- "github.com/juruen/rmapi/model"
- "github.com/juruen/rmapi/transport"
- "github.com/juruen/rmapi/util"
+ "github.com/peerdavid/rmapi/filetree"
+ "github.com/peerdavid/rmapi/log"
+ "github.com/peerdavid/rmapi/model"
+ "github.com/peerdavid/rmapi/transport"
+ "github.com/peerdavid/rmapi/util"
)
type ApiCtx struct {
diff --git a/api/auth.go b/api/auth.go
index af2af9f..356220a 100644
--- a/api/auth.go
+++ b/api/auth.go
@@ -6,10 +6,10 @@ import (
"os"
"strings"
- "github.com/juruen/rmapi/config"
- "github.com/juruen/rmapi/log"
- "github.com/juruen/rmapi/model"
- "github.com/juruen/rmapi/transport"
+ "github.com/peerdavid/rmapi/config"
+ "github.com/peerdavid/rmapi/log"
+ "github.com/peerdavid/rmapi/model"
+ "github.com/peerdavid/rmapi/transport"
uuid "github.com/satori/go.uuid"
)
diff --git a/config/config.go b/config/config.go
index 2deb2c3..3adc495 100644
--- a/config/config.go
+++ b/config/config.go
@@ -6,8 +6,8 @@ import (
"os"
"os/user"
- "github.com/juruen/rmapi/log"
- "github.com/juruen/rmapi/model"
+ "github.com/peerdavid/rmapi/log"
+ "github.com/peerdavid/rmapi/model"
"gopkg.in/yaml.v2"
)
diff --git a/config/config_test.go b/config/config_test.go
index 2195584..d2a3954 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -6,7 +6,7 @@ import (
"os"
"testing"
- "github.com/juruen/rmapi/model"
+ "github.com/peerdavid/rmapi/model"
"github.com/stretchr/testify/assert"
)
diff --git a/docs/tutorial-print-macosx.md b/docs/tutorial-print-macosx.md
index 4a4ecb9..b452bee 100644
--- a/docs/tutorial-print-macosx.md
+++ b/docs/tutorial-print-macosx.md
@@ -1,6 +1,6 @@
# How to directly print to your ReMarkable on Mac
-This tuorial wil show you how to leverage [rmapi](https://github.com/juruen/rmapi) and `Automator` to print
+This tuorial wil show you how to leverage [rmapi](https://github.com/peerdavid/rmapi) and `Automator` to print
to your ReMarkable tablet from your Mac using the Cloud API.
This way you won't need to take the extra step of using the desktop app.
@@ -19,7 +19,7 @@ Use `terminal` or `iterm` to get a terminal to run commands from it.
Download `rmapi` with the following command:
```bash
-curl -L https://github.com/juruen/rmapi/releases/download/v0.0.4/rmapi-macosx-v0.0.4.zip -o rmapi.zip
+curl -L https://github.com/peerdavid/rmapi/releases/download/v0.0.4/rmapi-macosx-v0.0.4.zip -o rmapi.zip
```
Alternatively, you can build it from sources.
diff --git a/filetree/filetree.go b/filetree/filetree.go
index 36e72e3..f8a154c 100644
--- a/filetree/filetree.go
+++ b/filetree/filetree.go
@@ -3,8 +3,8 @@ package filetree
import (
"errors"
- "github.com/juruen/rmapi/model"
- "github.com/juruen/rmapi/util"
+ "github.com/peerdavid/rmapi/model"
+ "github.com/peerdavid/rmapi/util"
)
type FileTreeCtx struct {
diff --git a/filetree/filetree_test.go b/filetree/filetree_test.go
index eb13f39..75635cb 100644
--- a/filetree/filetree_test.go
+++ b/filetree/filetree_test.go
@@ -3,7 +3,7 @@ package filetree
import (
"testing"
- "github.com/juruen/rmapi/model"
+ "github.com/peerdavid/rmapi/model"
"github.com/stretchr/testify/assert"
)
diff --git a/filetree/treeutil.go b/filetree/treeutil.go
index 15bf663..c07418f 100644
--- a/filetree/treeutil.go
+++ b/filetree/treeutil.go
@@ -3,7 +3,7 @@ package filetree
import (
"strings"
- "github.com/juruen/rmapi/model"
+ "github.com/peerdavid/rmapi/model"
)
const (
diff --git a/fusefs/file.go b/fusefs/file.go
index fe3e03c..172808d 100644
--- a/fusefs/file.go
+++ b/fusefs/file.go
@@ -6,7 +6,7 @@ import (
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
- "github.com/juruen/rmapi/log"
+ "github.com/peerdavid/rmapi/log"
)
type fuseFile struct {
diff --git a/fusefs/node.go b/fusefs/node.go
index e77b275..6c249e4 100644
--- a/fusefs/node.go
+++ b/fusefs/node.go
@@ -5,13 +5,13 @@ import (
"os"
"time"
- "github.com/juruen/rmapi/config"
- "github.com/juruen/rmapi/log"
- "github.com/juruen/rmapi/model"
+ "github.com/peerdavid/rmapi/config"
+ "github.com/peerdavid/rmapi/log"
+ "github.com/peerdavid/rmapi/model"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
- "github.com/juruen/rmapi/api"
+ "github.com/peerdavid/rmapi/api"
)
type fuseFs struct {
diff --git a/main.go b/main.go
index 1c4b187..3d809b3 100644
--- a/main.go
+++ b/main.go
@@ -5,10 +5,10 @@ import (
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
- "github.com/juruen/rmapi/api"
- "github.com/juruen/rmapi/fusefs"
- "github.com/juruen/rmapi/log"
- "github.com/juruen/rmapi/shell"
+ "github.com/peerdavid/rmapi/api"
+ "github.com/peerdavid/rmapi/fusefs"
+ "github.com/peerdavid/rmapi/log"
+ "github.com/peerdavid/rmapi/shell"
)
const AUTH_RETRIES = 3
diff --git a/shell/fs_completer.go b/shell/fs_completer.go
index 0e396ed..559fe6b 100644
--- a/shell/fs_completer.go
+++ b/shell/fs_completer.go
@@ -7,7 +7,7 @@ import (
"path"
"strings"
- "github.com/juruen/rmapi/log"
+ "github.com/peerdavid/rmapi/log"
)
func prefixToDir(s []string) string {
diff --git a/shell/geta.go b/shell/geta.go
new file mode 100644
index 0000000..7a3c510
--- /dev/null
+++ b/shell/geta.go
@@ -0,0 +1,255 @@
+package shell
+
+import (
+ "errors"
+ "fmt"
+ "time"
+ "os/exec"
+ "strings"
+ "archive/zip"
+ "github.com/peerdavid/rmapi/log"
+ "os"
+ "io"
+ "github.com/abiosoft/ishell"
+ "path/filepath"
+ "github.com/peerdavid/rmapi/model"
+)
+
+
+func verifyGetaCmdTools() [] string {
+ var missingTools [] string
+
+ missingTools = verifyCommand("python3", missingTools)
+ missingTools = verifyCommand("convert", missingTools)
+ missingTools = verifyCommand("rsvg-convert", missingTools)
+ missingTools = verifyCommand("pdftk", missingTools)
+ missingTools = verifyCommand("pdf270", missingTools)
+
+ return missingTools
+}
+
+
+// See also https://siongui.github.io/2018/03/16/go-check-if-command-exists/
+func verifyCommand(name string, missingTools [] string) [] string {
+ cmd := exec.Command("/bin/sh", "-c", "command -v "+name)
+ if err := cmd.Run(); err != nil {
+ return append(missingTools, name)
+ }
+ return missingTools
+}
+
+
+func getaCmd(ctx *ShellCtxt) *ishell.Cmd {
+ return &ishell.Cmd{
+ Name: "geta",
+ Help: "copy remote file to local with annotations.",
+ Completer: createEntryCompleter(ctx),
+ Func: func(c *ishell.Context) {
+ // Parse cmd args
+ if len(c.Args) == 0 {
+ c.Err(errors.New("missing source file"))
+ return
+ }
+ srcName := c.Args[0]
+
+ // Download document as zip
+ node, err := ctx.api.Filetree.NodeByPath(srcName, ctx.node)
+ if err != nil || node.IsDirectory() {
+ c.Err(errors.New("file doesn't exist"))
+ return
+ }
+
+ err = getAnnotatedDocument(ctx, node, "")
+ if err != nil {
+ c.Err(err)
+ return
+ }
+
+ fmt.Println("OK")
+ },
+ }
+}
+
+
+func getAnnotatedDocument(ctx *ShellCtxt, node *model.Node, path string) error {
+ zipFile := fmt.Sprintf(".%s.zip", node.Name())
+
+ // Set output name and output file name
+ output := node.Name()
+ if(path != ""){
+ output = fmt.Sprintf("%s/%s", path, node.Name())
+ }
+ outputFileName := fmt.Sprintf("%s.pdf", output)
+
+ // Parse last modified time of document on api
+ modifiedClientTime, err := time.Parse(time.RFC3339Nano, node.Document.ModifiedClient)
+ if err != nil {
+ // If we could not parse the time correctly, we still continue
+ // with the execution such that the pdf is downloaded...
+ log.Warning.Println("Could not parse modified time. Overwrite existing file.")
+ modifiedClientTime = time.Now().Local()
+ }
+
+ // If document has not changed since last update skip pdf convertion
+ outputFile, err := os.Stat(outputFileName)
+ if !os.IsNotExist(err) {
+ outputFileModTime := outputFile.ModTime()
+ if(outputFileModTime.Equal(modifiedClientTime)){
+ log.Trace.Println("Nothing changed since last download. Skip. ")
+ cleanup("", zipFile);
+ return nil
+ }
+ }
+
+ // Download document if content has changed
+ err = ctx.api.FetchDocument(node.Document.ID, zipFile)
+ if err != nil {
+ return errors.New(fmt.Sprintf("Failed to download file %s with %s", node.Name(), err.Error()))
+ }
+
+ // Unzip document
+ tmpFolder := fmt.Sprintf(".%s", node.Document.ID)
+ _, err = unzip(zipFile, tmpFolder)
+ if err != nil {
+ cleanup(tmpFolder, zipFile);
+ return err
+ }
+
+ // Currently we don't support the annotation of epub files
+ if(isEpub(tmpFolder, node)){
+ cleanup(tmpFolder, zipFile);
+ return errors.New("Could not annotate epub files.");
+ }
+
+ // If pdf is annotated convert it, otherwise move the pdf file
+ if(documentIsAnnotated(tmpFolder, node)){
+ exportPdf := os.Getenv("GOPATH") + "/src/github.com/peerdavid/rmapi/tools/exportAnnotatedPdf"
+ rM2svg := os.Getenv("GOPATH") + "/src/github.com/peerdavid/rmapi/tools/rM2svg"
+ output, err := exec.Command(
+ "/bin/bash",
+ exportPdf,
+ tmpFolder,
+ node.Document.ID,
+ output,
+ rM2svg).CombinedOutput()
+
+ log.Trace.Println(fmt.Sprintf("%s", output))
+
+ if err != nil {
+ cleanup(tmpFolder, zipFile);
+ return err
+ }
+
+ } else {
+ log.Trace.Println("Document is not annotated. Move original file.")
+
+ // Two cases exist: Pdf file or a notebook. The second case
+ // Could not occur because a notebook has always at least one page.
+ // Therefore we know that its a pdf file.
+ plainPdfFile := fmt.Sprintf("%s/%s.pdf", tmpFolder, node.Document.ID)
+ err := os.Rename(plainPdfFile, outputFileName)
+ if err != nil {
+ log.Error.Println("Could not move pdf file.")
+ cleanup(tmpFolder, zipFile);
+ return err
+ }
+ }
+
+ // Set creation time
+ err = os.Chtimes(outputFileName, modifiedClientTime, modifiedClientTime)
+ if err != nil {
+ fmt.Println("(Warning) Could not set modified time of pdf file.")
+ }
+
+ // Cleanup
+ cleanup(tmpFolder, zipFile);
+ return nil
+}
+
+
+func cleanup(tmpFolder string, zipFile string) {
+ _, err := os.Stat(tmpFolder);
+ if(!os.IsNotExist(err)){
+ os.RemoveAll(tmpFolder)
+ }
+
+ _, err = os.Stat(zipFile);
+ if(!os.IsNotExist(err)){
+ os.Remove(zipFile)
+ }
+}
+
+
+// From https://golangcode.com/unzip-files-in-go/
+func unzip(src string, dest string) ([]string, error) {
+
+ var filenames []string
+
+ r, err := zip.OpenReader(src)
+ if err != nil {
+ return filenames, err
+ }
+ defer r.Close()
+
+ for _, f := range r.File {
+
+ rc, err := f.Open()
+ if err != nil {
+ return filenames, err
+ }
+ defer rc.Close()
+
+ // Store filename/path for returning and using later on
+ fpath := filepath.Join(dest, f.Name)
+
+ // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE
+ if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
+ return filenames, fmt.Errorf("%s: illegal file path", fpath)
+ }
+
+ filenames = append(filenames, fpath)
+
+ if f.FileInfo().IsDir() {
+
+ // Make Folder
+ os.MkdirAll(fpath, os.ModePerm)
+
+ } else {
+
+ // Make File
+ if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
+ return filenames, err
+ }
+
+ outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
+ if err != nil {
+ return filenames, err
+ }
+
+ _, err = io.Copy(outFile, rc)
+
+ // Close the file without defer to close before next iteration of loop
+ outFile.Close()
+
+ if err != nil {
+ return filenames, err
+ }
+
+ }
+ }
+ return filenames, nil
+}
+
+
+func documentIsAnnotated(tmpFolder string, node *model.Node) bool {
+ annotationFolder := fmt.Sprintf("%s/%s", tmpFolder, node.Document.ID)
+ _, err := os.Stat(annotationFolder);
+ return !os.IsNotExist(err)
+}
+
+
+func isEpub(tmpFolder string, node *model.Node) bool {
+ annotationFolder := fmt.Sprintf("%s/%s.epub", tmpFolder, node.Document.ID)
+ _, err := os.Stat(annotationFolder);
+ return !os.IsNotExist(err)
+}
\ No newline at end of file
diff --git a/shell/mget.go b/shell/mget.go
index 2308efa..6589868 100644
--- a/shell/mget.go
+++ b/shell/mget.go
@@ -7,8 +7,8 @@ import (
"path"
"github.com/abiosoft/ishell"
- "github.com/juruen/rmapi/filetree"
- "github.com/juruen/rmapi/model"
+ "github.com/peerdavid/rmapi/filetree"
+ "github.com/peerdavid/rmapi/model"
)
func mgetCmd(ctx *ShellCtxt) *ishell.Cmd {
diff --git a/shell/mgeta.go b/shell/mgeta.go
new file mode 100644
index 0000000..e569bf0
--- /dev/null
+++ b/shell/mgeta.go
@@ -0,0 +1,67 @@
+package shell
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path"
+
+ "github.com/abiosoft/ishell"
+ "github.com/peerdavid/rmapi/filetree"
+ "github.com/peerdavid/rmapi/model"
+)
+
+func mgetaCmd(ctx *ShellCtxt) *ishell.Cmd {
+ return &ishell.Cmd{
+ Name: "mgeta",
+ Help: "recursively copy remote directory to local with annotations",
+ Completer: createDirCompleter(ctx),
+ Func: func(c *ishell.Context) {
+ if len(c.Args) == 0 {
+ c.Err(errors.New(("missing source dir")))
+ return
+ }
+
+ srcName := c.Args[0]
+
+ node, err := ctx.api.Filetree.NodeByPath(srcName, ctx.node)
+
+ if err != nil || node.IsFile() {
+ c.Err(errors.New("directory doesn't exist"))
+ return
+ }
+
+ visitor := filetree.FileTreeVistor{
+ func(currentNode *model.Node, currentPath []string) bool {
+ idxDir := 0
+ if srcName == "." && len(currentPath) > 0 {
+ idxDir = 1
+ }
+
+ dst := "./" + filetree.BuildPath(currentPath[idxDir:], currentNode.Name())
+ dir := path.Dir(dst)
+ os.MkdirAll(dir, 0766)
+
+ if currentNode.IsDirectory() {
+ return filetree.ContinueVisiting
+ }
+
+ c.Printf("downloading [%s]...", dst)
+
+ err = getAnnotatedDocument(ctx, currentNode, fmt.Sprintf("%s", dir))
+
+ if err == nil {
+ c.Println(" OK")
+ return filetree.ContinueVisiting
+ }
+
+ c.Err(errors.New(fmt.Sprintf("Failed to downlaod file %s", currentNode.Name())))
+
+ return filetree.ContinueVisiting
+ },
+ }
+
+ filetree.WalkTree(node, visitor)
+ },
+ }
+}
diff --git a/shell/mput.go b/shell/mput.go
index b5097b5..84f5a97 100644
--- a/shell/mput.go
+++ b/shell/mput.go
@@ -8,7 +8,7 @@ import (
"strings"
"github.com/abiosoft/ishell"
- "github.com/juruen/rmapi/util"
+ "github.com/peerdavid/rmapi/util"
)
func mputCmd(ctx *ShellCtxt) *ishell.Cmd {
diff --git a/shell/put.go b/shell/put.go
index a5b0423..3342b88 100644
--- a/shell/put.go
+++ b/shell/put.go
@@ -5,7 +5,7 @@ import (
"fmt"
"github.com/abiosoft/ishell"
- "github.com/juruen/rmapi/util"
+ "github.com/peerdavid/rmapi/util"
)
func putCmd(ctx *ShellCtxt) *ishell.Cmd {
diff --git a/shell/rmfs_completer.go b/shell/rmfs_completer.go
index f472647..8ad9333 100644
--- a/shell/rmfs_completer.go
+++ b/shell/rmfs_completer.go
@@ -5,8 +5,8 @@ import (
"path"
"strings"
- "github.com/juruen/rmapi/log"
- "github.com/juruen/rmapi/model"
+ "github.com/peerdavid/rmapi/log"
+ "github.com/peerdavid/rmapi/model"
)
func prefixToNodeDir(ctx *ShellCtxt, s []string) (*model.Node, string) {
diff --git a/shell/shell.go b/shell/shell.go
index df97b21..668ba80 100644
--- a/shell/shell.go
+++ b/shell/shell.go
@@ -3,10 +3,12 @@ package shell
import (
"fmt"
"os"
+ "strings"
"github.com/abiosoft/ishell"
- "github.com/juruen/rmapi/api"
- "github.com/juruen/rmapi/model"
+ "github.com/peerdavid/rmapi/api"
+ "github.com/peerdavid/rmapi/model"
+ "github.com/peerdavid/rmapi/log"
)
type ShellCtxt struct {
@@ -49,6 +51,18 @@ func RunShell(apiCtx *api.ApiCtx) error {
shell.AddCmd(versionCmd(ctx))
shell.AddCmd(statCmd(ctx))
+ // Add geta and mgeta if all necessary tools are installed
+ missingTools := verifyGetaCmdTools()
+ hasAllToolsForGetaInstalled := len(missingTools) <= 0
+ if(hasAllToolsForGetaInstalled){
+ shell.AddCmd(getaCmd(ctx))
+ shell.AddCmd(mgetaCmd(ctx))
+ } else {
+ log.Warning.Println(fmt.Sprintf("Commands geta and mgeta are disabled" +
+ " because the following tools are not installed: \n %v",
+ strings.Join(missingTools, "\n ")))
+ }
+
setCustomCompleter(shell)
if len(os.Args) > 1 {
diff --git a/shell/version.go b/shell/version.go
index 8476de5..7355de1 100644
--- a/shell/version.go
+++ b/shell/version.go
@@ -2,7 +2,7 @@ package shell
import (
"github.com/abiosoft/ishell"
- "github.com/juruen/rmapi/version"
+ "github.com/peerdavid/rmapi/version"
)
func versionCmd(ctx *ShellCtxt) *ishell.Cmd {
diff --git a/tools/exportAnnotatedPdf b/tools/exportAnnotatedPdf
new file mode 100755
index 0000000..2f3fe71
--- /dev/null
+++ b/tools/exportAnnotatedPdf
@@ -0,0 +1,104 @@
+#!/bin/bash
+#
+# Script to annotate remarkable notebooks. Based on the work of:
+# Copyright © 2018, Eric S Fraga, e.fraga@ucl.ac.uk
+# $Id: rmannotated.sh,v 1.12 2018/10/07 15:19:09 ucecesf Exp $
+#
+# Changed by peer david for the usage of rmapi
+#
+
+
+# Parse input and set defaults
+base=$1
+id=$2
+output=$3
+rM2svg=$4
+templatesdir="${HOME}/Templates/Remarkable"
+
+# Open folder that contains the .rm files
+cd ${base}
+
+# Write annotated pdf
+doc="${id}.pdf"
+if [ -f ${doc} ]
+then
+ # Get width and height of pdf
+ width=$(pdfinfo ${doc} | grep 'Page size:' | awk '{print $3}')
+ height=$(pdfinfo ${doc} | grep 'Page size:' | awk '{print $5}')
+
+ # comparing floating point numbers in bash directly is not possible
+ if (( $(echo "$height >= $width" |bc -l) )); then
+ rotated="no"
+ else
+ rotated="yes"
+ swap=$width
+ width=$height
+ height=$swap
+ fi
+
+ usetemplates="no"
+else
+ rotate="no"
+ width=1404
+ height=1872
+
+ if [ -d "${templatesdir}" ];
+ then
+ usetemplates="yes"
+ else
+ usetemplates="no"
+ fi
+fi
+
+# Create svg files
+$rM2svg -c -i ${id} --width ${width} --height ${height} -o ""
+
+# Read possible templates
+if [ "$usetemplates" == "yes" ];
+then
+ readarray -t templates < "$id.pagedata"
+fi
+
+# Create a pdf for every svg file
+page=0
+for svg in *.svg
+do
+ p=$(basename $svg .svg)
+
+ if [ "$usetemplates" == "yes" ] && [ "${page}" -lt "${#templates[@]}" ];
+ then
+ # Convert template to pdf
+ if [ ! -f "${templatesdir}/${templates[$page]}.pdf" ]
+ then
+ convert "${templatesdir}/${templates[$page]}.png" "${templatesdir}/${templates[$page]}.pdf"
+ fi
+
+ # Convert notes to pdf
+ rsvg-convert -f pdf -o "${id}_$p_tmp.pdf" "$p.svg"
+ #inkscape "$p.svg" --without-gui --export-pdf="${id}_$p_tmp.pdf"
+
+ # Merge template and notes
+ pdftk "${id}_$p_tmp.pdf" background "${templatesdir}/${templates[$page]}.pdf" output "${id}_$p.pdf"
+ rm "${id}_$p_tmp.pdf"
+ else
+ rsvg-convert -f pdf -o ${id}_$p.pdf $p.svg
+ #inkscape "$p.svg" --without-gui --export-pdf="${id}_$p.pdf"
+ fi
+
+ if [ "$rotated" == "yes" ]
+ then
+ pdf270 ${id}_$p.pdf 2>/dev/null
+ mv ${id}_$p-rotated270.pdf ${id}_$p.pdf
+ fi
+ page=$((page+1))
+done
+
+pdftk ${id}_*.pdf cat output ${id}_annotations.pdf
+
+if [ -f ${doc} ]; then
+ pdftk ${doc} multistamp ${id}_annotations.pdf output ../"${output}.pdf"
+else
+ mv ${id}_annotations.pdf ../"${output}.pdf"
+fi
+
+rm ${id}_*.pdf
\ No newline at end of file
diff --git a/tools/rM2svg b/tools/rM2svg
new file mode 100755
index 0000000..f70b019
--- /dev/null
+++ b/tools/rM2svg
@@ -0,0 +1,263 @@
+#!/usr/bin/env python3
+#
+# Script for converting a reMarkable tablet lines file to an SVG
+# image. Originally from
+#
+# https://github.com/lschwetlick/maxio/tree/master/tools
+#
+# but hacked to allow for specification of desired size of resulting
+# SVG image in terms of width and height. Log of changes at the end
+# of the file.
+#
+# Changed by Eric S Fraga; see log of changes at end.
+# Changed by Peer David; see log of changes at end.
+import sys
+import struct
+import os.path
+import argparse
+
+
+__prog_name__ = "rM2svg"
+__version__ = "0.0.1beta $Revision: 1.7 $"
+
+
+# Size
+default_x_width = 1404
+default_y_width = 1872
+
+# Mappings
+stroke_colour={
+ 0 : "black",
+ 1 : "grey",
+ 2 : "white",
+}
+'''stroke_width={
+ 0x3ff00000 : 2,
+ 0x40000000 : 4,
+ 0x40080000 : 8,
+}'''
+
+
+def main():
+ parser = argparse.ArgumentParser(prog=__prog_name__)
+ parser.add_argument('--height',
+ help='Desired height of image',
+ type=float,
+ default=default_y_width)
+ parser.add_argument('--width',
+ help='Desired width of image',
+ type=float,
+ default=default_x_width)
+ parser.add_argument("-i",
+ "--input",
+ help=".lines input file",
+ required=True,
+ metavar="FILENAME",
+ #type=argparse.FileType('r')
+ )
+ parser.add_argument("-o",
+ "--output",
+ help="prefix for output files",
+ required=True,
+ metavar="NAME",
+ #type=argparse.FileType('w')
+ )
+ parser.add_argument("-c",
+ "--coloured_annotations",
+ help="Colour annotations for document markup.",
+ action='store_true',
+ )
+ parser.add_argument('--version',
+ action='version',
+ version='%(prog)s {version}'.format(version=__version__))
+ args = parser.parse_args()
+
+ if not os.path.exists(args.input):
+ parser.error('The file "{}" does not exist!'.format(args.input))
+
+ if args.coloured_annotations:
+ global stroke_colour
+ stroke_colour = {
+ 0: "darkblue",
+ 1: "gray",
+ 2: "white",
+ 3: "yellow"
+ }
+
+ lines2svg(args.input, args.output, args.coloured_annotations,
+ args.width, args.height)
+
+
+def abort(msg):
+ print(msg, file=sys.stderr)
+ sys.exit(1)
+
+
+def lines2svg(path, output_name, coloured_annotations=False,
+ x_width=default_x_width, y_width=default_y_width):
+
+ if output_name.endswith(".svg"):
+ output_name = output_name[:-4]
+
+ used_pages = []
+ # Iterate through pages (There is at least one)
+ for f in os.listdir(path):
+ if(not f.endswith(".rm")):
+ continue
+
+ input_file = "%s/%s" % (path, f)
+ page = int(f[:-3])
+ used_pages.append(page)
+
+ with open(input_file, 'rb') as f:
+ data = f.read()
+ offset = 0
+
+ # Is this a reMarkable .lines file?
+ expected_header=b'reMarkable .lines file, version=3 '
+ if len(data) < len(expected_header) + 4:
+ abort('File too short to be a valid file')
+
+ fmt = '<{}sI'.format(len(expected_header))
+ header, nlayers = struct.unpack_from(fmt, data, offset); offset += struct.calcsize(fmt)
+ if header != expected_header or nlayers < 1:
+ abort('Not a valid reMarkable file: '.format(header, nlayers))
+
+ output = open("{}{:05}.svg".format(output_name, page+1), 'w')
+ output.write('') # END page
+ output.close()
+
+
+ # For every intermediate page that is empty create a blank page
+ if len(used_pages) == 0:
+ return
+
+ for page in range(max(used_pages)+2): # Note: last page is iterated by pdftk, so also add blank there
+ if page in used_pages:
+ continue
+
+ output = open("{}{:05}.svg".format(output_name, page+1), 'w')
+ output.write('') # END page
+ output.close()
+
+
+if __name__ == "__main__":
+ main()
+
+#
+# revision 1.8
+# Changed for usage of rmapi
+#
+# $Log: rM2svg,v $
+# Revision 1.7 2018/09/26 11:08:52 ucecesf
+# Summary: allow for floating point numbers for geometry
+#
+# revision 1.6
+# date: 2018/09/24 06:18:19; author: ucecesf; state: Exp; lines: +14 -9
+# Summary: adjust for different page width/height ratios
+#
+# revision 1.5
+# date: 2018/09/23 18:21:43; author: ucecesf; state: Exp; lines: +15 -5
+# Summary: added height and width arguments
+#
+# revision 1.4
+# date: 2018/09/23 18:08:05; author: ucecesf; state: Exp; lines: +1 -1
+# Summary: added revision info
+#
+# revision 1.3
+# date: 2018/09/20 17:39:21; author: ucecesf; state: Exp; lines: +2 -0
+# Summary: adjusted pen stroke width for new transformation
+#
+# revision 1.2
+# date: 2018/09/20 17:24:22; author: ucecesf; state: Exp; lines: +6 -4
+# Summary: transformation aiming at standard A4 LaTeX PDF document
+#
+# Line widths etc. still need adjusting.
+#
+# revision 1.1
+# date: 2018/09/14 08:56:07; author: ucecesf; state: Exp;
+# Initial revision
\ No newline at end of file
diff --git a/transport/transport.go b/transport/transport.go
index 243ec4d..b5a0d29 100644
--- a/transport/transport.go
+++ b/transport/transport.go
@@ -11,9 +11,9 @@ import (
"strings"
"time"
- "github.com/juruen/rmapi/log"
- "github.com/juruen/rmapi/model"
- "github.com/juruen/rmapi/util"
+ "github.com/peerdavid/rmapi/log"
+ "github.com/peerdavid/rmapi/model"
+ "github.com/peerdavid/rmapi/util"
)
type AuthType int
diff --git a/util/util.go b/util/util.go
index decfa80..c3a706f 100644
--- a/util/util.go
+++ b/util/util.go
@@ -8,7 +8,7 @@ import (
"path"
"strings"
- "github.com/juruen/rmapi/model"
+ "github.com/peerdavid/rmapi/model"
)
func DocPathToName(p string) string {
diff --git a/util/zipdoc.go b/util/zipdoc.go
index 657b28f..7c9fa84 100644
--- a/util/zipdoc.go
+++ b/util/zipdoc.go
@@ -7,12 +7,13 @@ import (
"io/ioutil"
"strings"
- "github.com/juruen/rmapi/log"
+ "github.com/peerdavid/rmapi/log"
)
type zipDocumentContent struct {
ExtraMetadata map[string]string `json:"extraMetadata"`
FileType string `json:"fileType"`
+ PageCount int `json:"pageCount"`
LastOpenedPage int `json:"lastOpenedPage"`
LineHeight int `json:"lineHeight"`
Margins int `json:"margins"`
@@ -110,6 +111,7 @@ func createZipContent(ext string) (string, error) {
make(map[string]string),
ext,
0,
+ 0,
-1,
180,
1,
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 2b98fa9..213d6ab 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -98,5 +98,5 @@
"revisionTime": "2018-01-09T11:43:31Z"
}
],
- "rootPath": "github.com/juruen/rmapi"
+ "rootPath": "github.com/peerdavid/rmapi"
}