July 26, 2016

Hacking MuPDF - custom background colors

My PDF reader of choice is Xpdf, but I recently started to use more and more MuPDF. It has much better rendering, vi-like keys for navigation, zoomable fullscreen and more (speed aside, since Xpdf is fast enough anyway).

Still, some stuff are missing:

  1. reading TOC (Table of Contents) is non-existent - this is essential for me when I read a largish book or book without proper links in content part. Because of that, I keep Xpdf still around.
  2. setting paper color is missing - when I take a long reading session, I usually invert colors which makes intensive white background color as black and more pleasant to read. However, paper, or background surrounding document, is still gray which can be distracting on large monitors.

Xpdf solves this by providing -papercolor <color> option and another option I like - inverting from command line. The later is useful for setting PDF reader to start with inverted colors always (with alias or shell script), but that is for different post.

So, to make MuPDF usable for my setup, I hacked it a little bit by adding two additional command line options, -D <color> and -P <color>. In short, -D will setup shade color (shade are small lines around document to give embossed impression) and -P will setup paper color.

Please note that this patch works only for X11 version of MuPDF.

As images are more useful than empty babbling, here are shots of Paul Graham's On Lisp sample page on stock MuPDF version:

mupdf-default

default view


mupdf-inverted

inverted view


As you can see, even inverted view is a bit distracting. With applied patch (shown at end of this post) and MuPDF started with new options

mupdf-x11 -D black -P black onlisp.pdf

we have this:

mupdf-normal

default view


mupdf-inverted

inverted view - my ultimate goal

Of course, you can use different colors for your taste, as long as they are X11 color names.

For those questioning what shade color is, let's see the same document with red color of shade and dark background:

mupdf-x11 -D red -P black onlisp.pdf

mupdf-inverted-shade


Note that Flickr's embedded images are crap, so this is a link to full album for viewing pleasure.

Patch

Here is uncomplicated patch created from fresh MuPDF code:

diff --git a/platform/x11/x11_main.c b/platform/x11/x11_main.c
index 21e3e84..3bce5aa 100644
--- a/platform/x11/x11_main.c
+++ b/platform/x11/x11_main.c
@@ -96,6 +96,8 @@ static char copyutf8[1024 * 48] = "";
 static Time copytime;
 static char *filename;
 static char message[1024] = "";
+static char *shadecolor = 0;
+static char *papercolor = 0;
 
 static pdfapp_t gapp;
 static int closing = 0;
@@ -129,6 +131,14 @@ static void showmessage(pdfapp_t *app, int timeout, char *msg)
 	}
 }
 
+static int colorasrgb(const char *name, XColor *color) {
+	XColor dummy;
+	if(XAllocNamedColor(xdpy, DefaultColormap(xdpy, xscr), name, color, &dummy))
+		return 1;
+	fprintf(stderr, "mupdf: couldn't allocate color '%s'", name);
+	return 0;
+}
+
 void winerror(pdfapp_t *app, char *msg)
 {
 	fprintf(stderr, "mupdf: error: %s\n", msg);
@@ -224,13 +234,17 @@ static void winopen(void)
 	xcwait = XCreateFontCursor(xdpy, XC_watch);
 	xccaret = XCreateFontCursor(xdpy, XC_xterm);
 
-	xbgcolor.red = 0x7000;
-	xbgcolor.green = 0x7000;
-	xbgcolor.blue = 0x7000;
+	if (!papercolor || !colorasrgb(papercolor, &xbgcolor)) {
+		xbgcolor.red = 0x7000;
+		xbgcolor.green = 0x7000;
+		xbgcolor.blue = 0x7000;
+	}
 
-	xshcolor.red = 0x4000;
-	xshcolor.green = 0x4000;
-	xshcolor.blue = 0x4000;
+	if (!shadecolor || !colorasrgb(shadecolor, &xshcolor)) {
+		xshcolor.red = 0x4000;
+		xshcolor.green = 0x4000;
+		xshcolor.blue = 0x4000;
+	}
 
 	XAllocColor(xdpy, DefaultColormap(xdpy, xscr), &xbgcolor);
 	XAllocColor(xdpy, DefaultColormap(xdpy, xscr), &xshcolor);
@@ -806,6 +820,8 @@ static void usage(void)
 	fprintf(stderr, "\t-H -\tpage height for EPUB layout\n");
 	fprintf(stderr, "\t-S -\tfont size for EPUB layout\n");
 	fprintf(stderr, "\t-U -\tuser style sheet for EPUB layout\n");
+	fprintf(stderr, "\t-D -\tshade color\n");
+	fprintf(stderr, "\t-P -\tpaper color\n");
 	exit(1);
 }
 
@@ -836,7 +852,7 @@ int main(int argc, char **argv)
 
 	pdfapp_init(ctx, &gapp);
 
-	while ((c = fz_getopt(argc, argv, "p:r:A:C:W:H:S:U:")) != -1)
+	while ((c = fz_getopt(argc, argv, "p:r:A:C:W:H:S:U:D:P:")) != -1)
 	{
 		switch (c)
 		{
@@ -854,6 +870,8 @@ int main(int argc, char **argv)
 		case 'H': gapp.layout_h = fz_atof(fz_optarg); break;
 		case 'S': gapp.layout_em = fz_atof(fz_optarg); break;
 		case 'U': gapp.layout_css = fz_optarg; break;
+		case 'P': papercolor = fz_optarg; break;
+		case 'D': shadecolor = fz_optarg; break;
 		default: usage();
 		}
 	}

Save it, apply it, recompile MuPDF and enjoy.

Tags: programming