From 656f2d437d31f9ea49e30e436b2631a0bab8947e Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Thu, 22 Apr 2021 09:43:15 -0400 Subject: [PATCH 01/10] Allow alternate colors for the pens This adds `render()` and command line parameters to specify the colors that should be used for the "black" and "white" pen colors. These can be used to, for example, have annotations in red or blue lines to distinguish from the content being annotated. The default colors are, of course, actual black and white. The gray pen is always a 50/50 mix of the black and white pens in the RGB color space. --- rmrl/__main__.py | 18 +++++++++++++++++- rmrl/document.py | 14 ++++++++------ rmrl/render.py | 10 ++++++++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/rmrl/__main__.py b/rmrl/__main__.py index 9f9f4cf..a20e433 100644 --- a/rmrl/__main__.py +++ b/rmrl/__main__.py @@ -15,6 +15,7 @@ import argparse import io +import re import sys import zipfile @@ -29,9 +30,16 @@ def main(): parser.add_argument('--alpha', default=0.3, help="Opacity for template background (0 for no background).") parser.add_argument('--no-expand', action='store_true', help="Don't expand pages to margins on device.") parser.add_argument('--only-annotated', action='store_true', help="Only render pages with annotations.") + parser.add_argument('--black', default='#000000', help="Color for \"black\" pen, in hex notation (#RRGGBB).") + parser.add_argument('--white', default='#FFFFFF', help="Color for \"white\" pen, in hex notation (#RRGGBB).") parser.add_argument('--version', action='version', version=VERSION) args = parser.parse_args() + for color, value in [('White', args.white), ('Black', args.black)]: + if re.search(r'^#[0-9a-f]{6}$', value, re.I) is None: + print("{} color is incorrectly formatted.".format(color), file=sys.stderr) + return 1 + source = args.input if source == '-': # zipfile needs to seek, so we need to read this all in @@ -44,10 +52,18 @@ def main(): stream = render(source, template_alpha=float(args.alpha), expand_pages=not args.no_expand, - only_annotated=args.only_annotated) + only_annotated=args.only_annotated, + black=parse_color(args.black), + white=parse_color(args.white)) fout.write(stream.read()) fout.close() return 0 +def parse_color(hex_string): + color_int = int(hex_string[1:], 16) + return (color_int // 65536 / 255, + color_int // 256 % 256 / 255, + color_int % 256 / 255) + if __name__ == '__main__': sys.exit(main()) diff --git a/rmrl/document.py b/rmrl/document.py index 14a2f91..fa24105 100644 --- a/rmrl/document.py +++ b/rmrl/document.py @@ -29,10 +29,12 @@ class DocumentPage: # A single page in a document - def __init__(self, source, pid, pagenum): + def __init__(self, source, pid, pagenum, black=(0, 0, 0), white=(1, 1, 1)): # Page 0 is the first page! self.source = source self.num = pagenum + self.black = black + self.white = white # On disk, these files are named by a UUID self.rmpath = f'{{ID}}/{pid}.rm' @@ -103,7 +105,7 @@ def load_layers(self): except: name = 'Layer ' + str(i + 1) - layer = DocumentPageLayer(self, name=name) + layer = DocumentPageLayer(self, name=name, black=self.black, white=self.white) layer.strokes = layerstrokes self.layers.append(layer) @@ -152,7 +154,7 @@ def render_to_painter(self, canvas, vector, template_alpha): class DocumentPageLayer: pen_widths = [] - def __init__(self, page, name=None): + def __init__(self, page, name=None, black=(0, 0, 0), white=(1, 1, 1)): self.page = page self.name = name @@ -160,9 +162,9 @@ def __init__(self, page, name=None): #QSettings().value('pane/notebooks/export_pdf_blackink'), #QSettings().value('pane/notebooks/export_pdf_grayink'), #QSettings().value('pane/notebooks/export_pdf_whiteink') - (0, 0, 0), - (0.5, 0.5, 0.5), - (1, 1, 1) + black, + [(b + w) / 2 for b, w in zip(black, white)], + white, ] # Set this from the calling func diff --git a/rmrl/render.py b/rmrl/render.py index 475b1da..68789ff 100644 --- a/rmrl/render.py +++ b/rmrl/render.py @@ -35,7 +35,9 @@ def render(source, *, progress_cb=lambda x: None, expand_pages=True, template_alpha=0.3, - only_annotated=False): + only_annotated=False, + black=(0, 0, 0), + white=(1, 1, 1)): """ Render a source document as a PDF file. @@ -59,6 +61,10 @@ def render(source, *, makes the templates invisible, 1 makes them fully dark. only_annotated: Boolean value (default False) indicating whether only pages with annotations should be output. + black: A tuple of three values (red, green, and blue; 0.0-1.0) giving + the color to use as "black" in the document. Default: (0, 0, 0) + white: A tuple of three values (red, green, and blue; 0.0-1.0) giving + the color to use as "white" in the document. Default: (1, 1, 1) """ vector=True # TODO: Different rendering styles @@ -89,7 +95,7 @@ def render(source, *, changed_pages = [] annotations = [] for i in range(0, len(pages)): - page = document.DocumentPage(source, pages[i], i) + page = document.DocumentPage(source, pages[i], i, black=black, white=white) if source.exists(page.rmpath): changed_pages.append(i) page.render_to_painter(pdf_canvas, vector, template_alpha) From 49cd6c35dbf91d24c020533edd6e5cbbc7ea1415 Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Tue, 8 Jun 2021 09:19:55 -0400 Subject: [PATCH 02/10] Use single quotes to avoid escaping internal double quotes --- rmrl/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rmrl/__main__.py b/rmrl/__main__.py index a20e433..250fd0f 100644 --- a/rmrl/__main__.py +++ b/rmrl/__main__.py @@ -30,8 +30,8 @@ def main(): parser.add_argument('--alpha', default=0.3, help="Opacity for template background (0 for no background).") parser.add_argument('--no-expand', action='store_true', help="Don't expand pages to margins on device.") parser.add_argument('--only-annotated', action='store_true', help="Only render pages with annotations.") - parser.add_argument('--black', default='#000000', help="Color for \"black\" pen, in hex notation (#RRGGBB).") - parser.add_argument('--white', default='#FFFFFF', help="Color for \"white\" pen, in hex notation (#RRGGBB).") + parser.add_argument('--black', default='#000000', help='Color for "black" pen, in hex notation (#RRGGBB).') + parser.add_argument('--white', default='#FFFFFF', help='Color for "white" pen, in hex notation (#RRGGBB).') parser.add_argument('--version', action='version', version=VERSION) args = parser.parse_args() From e1d1d327f60ad684ada85465c9b41feb5b15ea1d Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Tue, 8 Jun 2021 09:40:13 -0400 Subject: [PATCH 03/10] Use the colour library for color management --- pyproject.toml | 1 + rmrl/__main__.py | 7 +++---- rmrl/document.py | 16 ++++++++-------- rmrl/render.py | 17 +++++++++-------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c978964..3166808 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.7" +colour = "^0.1.5" pdfrw = "^0.4" reportlab = "^3.5.59" svglib = "^1.0.1" diff --git a/rmrl/__main__.py b/rmrl/__main__.py index 250fd0f..291b2fb 100644 --- a/rmrl/__main__.py +++ b/rmrl/__main__.py @@ -19,6 +19,8 @@ import sys import zipfile +from colour import Color + from . import render from .constants import VERSION from .sources import ZipSource @@ -60,10 +62,7 @@ def main(): return 0 def parse_color(hex_string): - color_int = int(hex_string[1:], 16) - return (color_int // 65536 / 255, - color_int // 256 % 256 / 255, - color_int % 256 / 255) + return Color(hex_string) if __name__ == '__main__': sys.exit(main()) diff --git a/rmrl/document.py b/rmrl/document.py index fa24105..b67dcda 100644 --- a/rmrl/document.py +++ b/rmrl/document.py @@ -18,6 +18,8 @@ import json import logging +from colour import Color + from reportlab.graphics import renderPDF from svglib.svglib import svg2rlg @@ -29,7 +31,7 @@ class DocumentPage: # A single page in a document - def __init__(self, source, pid, pagenum, black=(0, 0, 0), white=(1, 1, 1)): + def __init__(self, source, pid, pagenum, black=Color('black'), white=Color('white')): # Page 0 is the first page! self.source = source self.num = pagenum @@ -154,17 +156,15 @@ def render_to_painter(self, canvas, vector, template_alpha): class DocumentPageLayer: pen_widths = [] - def __init__(self, page, name=None, black=(0, 0, 0), white=(1, 1, 1)): + def __init__(self, page, name=None, black=Color('black'), white=('white')): self.page = page self.name = name + gray = list(black.range_to(white, 3))[1] self.colors = [ - #QSettings().value('pane/notebooks/export_pdf_blackink'), - #QSettings().value('pane/notebooks/export_pdf_grayink'), - #QSettings().value('pane/notebooks/export_pdf_whiteink') - black, - [(b + w) / 2 for b, w in zip(black, white)], - white, + black.rgb, + gray.rgb, + white.rgb, ] # Set this from the calling func diff --git a/rmrl/render.py b/rmrl/render.py index 68789ff..ed011e0 100644 --- a/rmrl/render.py +++ b/rmrl/render.py @@ -20,6 +20,8 @@ import json import re +from colour import Color + from pdfrw import PdfReader, PdfWriter, PageMerge, PdfDict, PdfArray, PdfName, \ IndirectPdfDict, uncompress, compress @@ -36,10 +38,9 @@ def render(source, *, expand_pages=True, template_alpha=0.3, only_annotated=False, - black=(0, 0, 0), - white=(1, 1, 1)): - """ - Render a source document as a PDF file. + black=Color('black'), + white=Color('white')): + """Render a source document as a PDF file. source: The reMarkable document to be rendered. This may be - A filename or pathlib.Path to a zip file containing the @@ -61,10 +62,10 @@ def render(source, *, makes the templates invisible, 1 makes them fully dark. only_annotated: Boolean value (default False) indicating whether only pages with annotations should be output. - black: A tuple of three values (red, green, and blue; 0.0-1.0) giving - the color to use as "black" in the document. Default: (0, 0, 0) - white: A tuple of three values (red, green, and blue; 0.0-1.0) giving - the color to use as "white" in the document. Default: (1, 1, 1) + black: A colour.Color object giving the color to use as "black" in the + document. Default: Color('black') + white: A colour.Color object giving the color to use as "white" in the + document. Default: Color('white') """ vector=True # TODO: Different rendering styles From 6dfd5763ec7ed6998aed7885078ee3e82dea84c9 Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Tue, 8 Jun 2021 09:58:16 -0400 Subject: [PATCH 04/10] Use a namedtuple to carry the colors around internally --- rmrl/__main__.py | 7 ++----- rmrl/document.py | 18 +++++++----------- rmrl/render.py | 27 ++++++++++++++++++++------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/rmrl/__main__.py b/rmrl/__main__.py index 291b2fb..4514e07 100644 --- a/rmrl/__main__.py +++ b/rmrl/__main__.py @@ -55,14 +55,11 @@ def main(): template_alpha=float(args.alpha), expand_pages=not args.no_expand, only_annotated=args.only_annotated, - black=parse_color(args.black), - white=parse_color(args.white)) + black=args.black, + white=args.white) fout.write(stream.read()) fout.close() return 0 -def parse_color(hex_string): - return Color(hex_string) - if __name__ == '__main__': sys.exit(main()) diff --git a/rmrl/document.py b/rmrl/document.py index b67dcda..858c7a0 100644 --- a/rmrl/document.py +++ b/rmrl/document.py @@ -18,8 +18,6 @@ import json import logging -from colour import Color - from reportlab.graphics import renderPDF from svglib.svglib import svg2rlg @@ -31,12 +29,11 @@ class DocumentPage: # A single page in a document - def __init__(self, source, pid, pagenum, black=Color('black'), white=Color('white')): + def __init__(self, source, pid, pagenum, colors): # Page 0 is the first page! self.source = source self.num = pagenum - self.black = black - self.white = white + self.colors = colors # On disk, these files are named by a UUID self.rmpath = f'{{ID}}/{pid}.rm' @@ -107,7 +104,7 @@ def load_layers(self): except: name = 'Layer ' + str(i + 1) - layer = DocumentPageLayer(self, name=name, black=self.black, white=self.white) + layer = DocumentPageLayer(self, name=name, colors=self.colors) layer.strokes = layerstrokes self.layers.append(layer) @@ -156,15 +153,14 @@ def render_to_painter(self, canvas, vector, template_alpha): class DocumentPageLayer: pen_widths = [] - def __init__(self, page, name=None, black=Color('black'), white=('white')): + def __init__(self, page, colors, name=None): self.page = page self.name = name - gray = list(black.range_to(white, 3))[1] self.colors = [ - black.rgb, - gray.rgb, - white.rgb, + colors.black.rgb, + colors.gray.rgb, + colors.white.rgb, ] # Set this from the calling func diff --git a/rmrl/render.py b/rmrl/render.py index ed011e0..80462f1 100644 --- a/rmrl/render.py +++ b/rmrl/render.py @@ -19,6 +19,7 @@ from pathlib import Path import json import re +from collections import namedtuple from colour import Color @@ -33,13 +34,15 @@ log = logging.getLogger(__name__) +Colors = namedtuple('Colors', ['black', 'white', 'gray']) + def render(source, *, progress_cb=lambda x: None, expand_pages=True, template_alpha=0.3, only_annotated=False, - black=Color('black'), - white=Color('white')): + black='black', + white='white'): """Render a source document as a PDF file. source: The reMarkable document to be rendered. This may be @@ -62,12 +65,15 @@ def render(source, *, makes the templates invisible, 1 makes them fully dark. only_annotated: Boolean value (default False) indicating whether only pages with annotations should be output. - black: A colour.Color object giving the color to use as "black" in the - document. Default: Color('black') - white: A colour.Color object giving the color to use as "white" in the - document. Default: Color('white') + black: A string giving the color to use as "black" in the document. + Can be a color name or a hex string. Default: 'black' + white: A string giving the color to use as "white" in the document. + See `black` parameter for format. Default: 'white' """ + # TODO: Error handling + colors = parse_colors(black, white) + vector=True # TODO: Different rendering styles source = sources.get_source(source) @@ -96,7 +102,7 @@ def render(source, *, changed_pages = [] annotations = [] for i in range(0, len(pages)): - page = document.DocumentPage(source, pages[i], i, black=black, white=white) + page = document.DocumentPage(source, pages[i], i, colors=colors) if source.exists(page.rmpath): changed_pages.append(i) page.render_to_painter(pdf_canvas, vector, template_alpha) @@ -184,6 +190,13 @@ def render(source, *, return stream +def parse_colors(black, white): + black_color = Color(black) + white_color = Color(white) + gray_color = list(black_color.range_to(white_color, 3))[1] + return Colors(black=black_color, white=white_color, gray=gray_color) + + def do_apply_ocg(basepage, rmpage, i, uses_base_pdf, ocgprop, annotations): ocgpage = IndirectPdfDict( Type=PdfName('OCG'), From 28e3de8129e876e0ae6c7bbbf4f587e35de79f16 Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Tue, 8 Jun 2021 10:13:54 -0400 Subject: [PATCH 05/10] Error handling for the pen colors --- rmrl/__main__.py | 32 ++++++++++++++++---------------- rmrl/render.py | 16 ++++++++++++++-- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/rmrl/__main__.py b/rmrl/__main__.py index 4514e07..0063742 100644 --- a/rmrl/__main__.py +++ b/rmrl/__main__.py @@ -23,6 +23,7 @@ from . import render from .constants import VERSION +from .render import InvalidColor from .sources import ZipSource def main(): @@ -32,16 +33,11 @@ def main(): parser.add_argument('--alpha', default=0.3, help="Opacity for template background (0 for no background).") parser.add_argument('--no-expand', action='store_true', help="Don't expand pages to margins on device.") parser.add_argument('--only-annotated', action='store_true', help="Only render pages with annotations.") - parser.add_argument('--black', default='#000000', help='Color for "black" pen, in hex notation (#RRGGBB).') - parser.add_argument('--white', default='#FFFFFF', help='Color for "white" pen, in hex notation (#RRGGBB).') + parser.add_argument('--black', default='black', help='Color for "black" pen.') + parser.add_argument('--white', default='white', help='Color for "white" pen.') parser.add_argument('--version', action='version', version=VERSION) args = parser.parse_args() - for color, value in [('White', args.white), ('Black', args.black)]: - if re.search(r'^#[0-9a-f]{6}$', value, re.I) is None: - print("{} color is incorrectly formatted.".format(color), file=sys.stderr) - return 1 - source = args.input if source == '-': # zipfile needs to seek, so we need to read this all in @@ -51,15 +47,19 @@ def main(): else: fout = sys.stdout.buffer - stream = render(source, - template_alpha=float(args.alpha), - expand_pages=not args.no_expand, - only_annotated=args.only_annotated, - black=args.black, - white=args.white) - fout.write(stream.read()) - fout.close() - return 0 + try: + stream = render(source, + template_alpha=float(args.alpha), + expand_pages=not args.no_expand, + only_annotated=args.only_annotated, + black=args.black, + white=args.white) + fout.write(stream.read()) + fout.close() + return 0 + except InvalidColor as e: + print(str(e), file=sys.stderr) + return 1 if __name__ == '__main__': sys.exit(main()) diff --git a/rmrl/render.py b/rmrl/render.py index 80462f1..81bfc2d 100644 --- a/rmrl/render.py +++ b/rmrl/render.py @@ -36,6 +36,11 @@ Colors = namedtuple('Colors', ['black', 'white', 'gray']) +class InvalidColor(Exception): + """Raised when an invalid string is passed as a color.""" + pass + + def render(source, *, progress_cb=lambda x: None, expand_pages=True, @@ -191,12 +196,19 @@ def render(source, *, def parse_colors(black, white): - black_color = Color(black) - white_color = Color(white) + black_color = parse_color(black, 'black') + white_color = parse_color(white, 'white') gray_color = list(black_color.range_to(white_color, 3))[1] return Colors(black=black_color, white=white_color, gray=gray_color) +def parse_color(color_string, name): + try: + return Color(color_string) + except Exception as e: + raise InvalidColor('"{}" color was passed an invalid string: {}'.format(name, str(e))) + + def do_apply_ocg(basepage, rmpage, i, uses_base_pdf, ocgprop, annotations): ocgpage = IndirectPdfDict( Type=PdfName('OCG'), From 7333eaf09065e3206fdc5a0bb7c481f540345f4d Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Tue, 8 Jun 2021 10:29:23 -0400 Subject: [PATCH 06/10] Add gray parameter --- rmrl/__main__.py | 4 +++- rmrl/render.py | 27 ++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/rmrl/__main__.py b/rmrl/__main__.py index 0063742..3631823 100644 --- a/rmrl/__main__.py +++ b/rmrl/__main__.py @@ -35,6 +35,7 @@ def main(): parser.add_argument('--only-annotated', action='store_true', help="Only render pages with annotations.") parser.add_argument('--black', default='black', help='Color for "black" pen.') parser.add_argument('--white', default='white', help='Color for "white" pen.') + parser.add_argument('--gray', '--grey', default=None, help='Color for "gray" pen.') parser.add_argument('--version', action='version', version=VERSION) args = parser.parse_args() @@ -53,7 +54,8 @@ def main(): expand_pages=not args.no_expand, only_annotated=args.only_annotated, black=args.black, - white=args.white) + white=args.white, + gray=args.gray) fout.write(stream.read()) fout.close() return 0 diff --git a/rmrl/render.py b/rmrl/render.py index 81bfc2d..63e642b 100644 --- a/rmrl/render.py +++ b/rmrl/render.py @@ -47,7 +47,8 @@ def render(source, *, template_alpha=0.3, only_annotated=False, black='black', - white='white'): + white='white', + gray=None): """Render a source document as a PDF file. source: The reMarkable document to be rendered. This may be @@ -74,10 +75,12 @@ def render(source, *, Can be a color name or a hex string. Default: 'black' white: A string giving the color to use as "white" in the document. See `black` parameter for format. Default: 'white' + gray: A string giving the color to use as "gray" in the document. + See `black` parameter for format. Default: None, which means to + pick an average between the "white" and "black" values. """ - # TODO: Error handling - colors = parse_colors(black, white) + colors = parse_colors(black, white, gray) vector=True # TODO: Different rendering styles source = sources.get_source(source) @@ -195,10 +198,24 @@ def render(source, *, return stream -def parse_colors(black, white): +def parse_colors(black, white, gray): black_color = parse_color(black, 'black') white_color = parse_color(white, 'white') - gray_color = list(black_color.range_to(white_color, 3))[1] + + if gray is not None: + # Use the explicit gray value. + gray_color = parse_color(gray, 'gray') + elif black_color.saturation == 0 or white_color.saturation == 0: + # One or the other of the color endpoints is a shade of gray (or + # white or black). Use average in RGB space. This keeps the hue + # from the saturated endpoint and just lets the other endpoint + # either darken or lighten it. + gray_color = Color(rgb=((b + w) / 2 for b, w in zip(black_color.rgb, white_color.rgb))) + else: + # Both "black" and "white" have color elements to them. Use + # Color.range_to, which more or less averages in HSL space. + gray_color = list(black_color.range_to(white_color, 3))[1] + return Colors(black=black_color, white=white_color, gray=gray_color) From bd4c52dec97907672841de60c5cc0ff6bf7821d2 Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Tue, 8 Jun 2021 10:46:48 -0400 Subject: [PATCH 07/10] Add parameter for highlighter color --- rmrl/__main__.py | 6 ++++-- rmrl/constants.py | 3 +++ rmrl/document.py | 5 +++++ rmrl/pens/highlighter.py | 3 ++- rmrl/render.py | 16 ++++++++++------ 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/rmrl/__main__.py b/rmrl/__main__.py index 3631823..c6ed92e 100644 --- a/rmrl/__main__.py +++ b/rmrl/__main__.py @@ -22,7 +22,7 @@ from colour import Color from . import render -from .constants import VERSION +from .constants import VERSION, HIGHLIGHT_DEFAULT_COLOR from .render import InvalidColor from .sources import ZipSource @@ -36,6 +36,7 @@ def main(): parser.add_argument('--black', default='black', help='Color for "black" pen.') parser.add_argument('--white', default='white', help='Color for "white" pen.') parser.add_argument('--gray', '--grey', default=None, help='Color for "gray" pen.') + parser.add_argument('--highlight', '--hilight', '--hl', default=HIGHLIGHT_DEFAULT_COLOR, help='Color for the highlighter.') parser.add_argument('--version', action='version', version=VERSION) args = parser.parse_args() @@ -55,7 +56,8 @@ def main(): only_annotated=args.only_annotated, black=args.black, white=args.white, - gray=args.gray) + gray=args.gray, + highlight=args.highlight) fout.write(stream.read()) fout.close() return 0 diff --git a/rmrl/constants.py b/rmrl/constants.py index 7d1d0f0..1e1f7f0 100644 --- a/rmrl/constants.py +++ b/rmrl/constants.py @@ -23,3 +23,6 @@ TEMPLATE_PATH = xdg_data_home() / 'rmrl' / 'templates' VERSION = pkg_resources.get_distribution('rmrl').version + +HIGHLIGHT_DEFAULT_COLOR = '#FFE949' +HIGHLIGHT_ALPHA = 0.392 diff --git a/rmrl/document.py b/rmrl/document.py index 858c7a0..8e1b5ac 100644 --- a/rmrl/document.py +++ b/rmrl/document.py @@ -161,6 +161,7 @@ def __init__(self, page, colors, name=None): colors.black.rgb, colors.gray.rgb, colors.white.rgb, + colors.highlight.rgb, ] # Set this from the calling func @@ -231,6 +232,10 @@ def paint_strokes(self, canvas, vector): log.error("Unknown pen code %d" % pen) penclass = pens.GenericPen + # Hack to get the right color for the highlighter. + if penclass == pens.HighlighterPen: + color = -1 + qpen = penclass(vector=vector, layer=self, color=self.colors[color]) diff --git a/rmrl/pens/highlighter.py b/rmrl/pens/highlighter.py index ce45cdd..cd7a4f0 100644 --- a/rmrl/pens/highlighter.py +++ b/rmrl/pens/highlighter.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from ..constants import HIGHLIGHT_ALPHA from .generic import GenericPen class HighlighterPen(GenericPen): @@ -28,7 +29,7 @@ def paint_stroke(self, canvas, stroke): canvas.setLineCap(2) # Square canvas.setLineJoin(1) # Round #canvas.setDash ?? for solid line - canvas.setStrokeColor((1.000, 0.914, 0.290), alpha=0.392) + canvas.setStrokeColor(self.color, alpha=HIGHLIGHT_ALPHA) canvas.setLineWidth(stroke.width) path = canvas.beginPath() diff --git a/rmrl/render.py b/rmrl/render.py index 63e642b..205de77 100644 --- a/rmrl/render.py +++ b/rmrl/render.py @@ -29,12 +29,12 @@ from reportlab.pdfgen import canvas from . import document, sources -from .constants import PDFHEIGHT, PDFWIDTH, PTPERPX, SPOOL_MAX +from .constants import PDFHEIGHT, PDFWIDTH, PTPERPX, SPOOL_MAX, HIGHLIGHT_DEFAULT_COLOR log = logging.getLogger(__name__) -Colors = namedtuple('Colors', ['black', 'white', 'gray']) +Colors = namedtuple('Colors', ['black', 'white', 'gray', 'highlight']) class InvalidColor(Exception): """Raised when an invalid string is passed as a color.""" @@ -48,7 +48,8 @@ def render(source, *, only_annotated=False, black='black', white='white', - gray=None): + gray=None, + highlight=HIGHLIGHT_DEFAULT_COLOR): """Render a source document as a PDF file. source: The reMarkable document to be rendered. This may be @@ -78,9 +79,11 @@ def render(source, *, gray: A string giving the color to use as "gray" in the document. See `black` parameter for format. Default: None, which means to pick an average between the "white" and "black" values. + highlight: A string giving the color to use for the highlighter. + See `black` parameter for format. """ - colors = parse_colors(black, white, gray) + colors = parse_colors(black, white, gray, highlight) vector=True # TODO: Different rendering styles source = sources.get_source(source) @@ -198,9 +201,10 @@ def render(source, *, return stream -def parse_colors(black, white, gray): +def parse_colors(black, white, gray, highlight): black_color = parse_color(black, 'black') white_color = parse_color(white, 'white') + highlight_color = parse_color(highlight, 'highlight') if gray is not None: # Use the explicit gray value. @@ -216,7 +220,7 @@ def parse_colors(black, white, gray): # Color.range_to, which more or less averages in HSL space. gray_color = list(black_color.range_to(white_color, 3))[1] - return Colors(black=black_color, white=white_color, gray=gray_color) + return Colors(black=black_color, white=white_color, gray=gray_color, highlight=highlight_color) def parse_color(color_string, name): From f5233957cdf660e7464109fa9e9c739afa26525c Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Tue, 8 Jun 2021 10:52:18 -0400 Subject: [PATCH 08/10] CLI docs about the color formats --- rmrl/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rmrl/__main__.py b/rmrl/__main__.py index c6ed92e..859e609 100644 --- a/rmrl/__main__.py +++ b/rmrl/__main__.py @@ -27,7 +27,8 @@ from .sources import ZipSource def main(): - parser = argparse.ArgumentParser(description="Render a PDF file from a Remarkable document.") + parser = argparse.ArgumentParser(description="Render a PDF file from a Remarkable document.", + epilog='The colors may be specified as hex strings ("#AABBCC", "#ABC") or well-known names ("black", "red"). If no gray color is given, the program will use an average of the white and black colors. A fixed amount of transparency will be applied to the color given for the highlighter.') parser.add_argument('input', help="Filename of zip file, or root-level unpacked file of document. Use '-' to read zip file from stdin.") parser.add_argument('output', nargs='?', default='', help="Filename where PDF file should be written. Omit to write to stdout.") parser.add_argument('--alpha', default=0.3, help="Opacity for template background (0 for no background).") From 82d3271b1213a0b59d6683c791756b1d14d013f4 Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Tue, 8 Jun 2021 11:02:21 -0400 Subject: [PATCH 09/10] Don't need in __main__ anymore --- rmrl/__main__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rmrl/__main__.py b/rmrl/__main__.py index 859e609..e3e5fb8 100644 --- a/rmrl/__main__.py +++ b/rmrl/__main__.py @@ -15,7 +15,6 @@ import argparse import io -import re import sys import zipfile From e55e72f7701792884432deec759e1776d6343750 Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Tue, 8 Jun 2021 14:09:21 -0400 Subject: [PATCH 10/10] Don't need `Color` in __main__ anymore --- rmrl/__main__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/rmrl/__main__.py b/rmrl/__main__.py index e3e5fb8..7df74ad 100644 --- a/rmrl/__main__.py +++ b/rmrl/__main__.py @@ -18,8 +18,6 @@ import sys import zipfile -from colour import Color - from . import render from .constants import VERSION, HIGHLIGHT_DEFAULT_COLOR from .render import InvalidColor