Giter VIP home page Giter VIP logo

doq's Introduction

doq logo

doq

doq (pronounced as doc-HUE) is an add-on for Mozilla's excellent PDF.js PDF viewer.

It adds a reader mode where you can change the colors of the rendered PDF files, to make it more comfortable to the eyes while reading. It works similar to the reader mode in web browsers and theme options in eBook readers (except that it cannot change the fonts or reflow text).

doq was inspired by Safari's Reader View, and many terminal color schemes.

Screenshot with a light theme applied

Screenshot with a dark theme applied

Usage

If you simply want to use doq to read PDFs, try doqment. It is a browser extension that bundles doq with PDF.js and opens all your PDF links. You can skip the rest of this section.

doq is written as a native ES6 module; there is no bundled version. Hence it runs only in modern browsers that supports import and export.

Installation

For your own deployment of the PDF.js viewer:

  1. Download the latest version (or clone this repo)
  2. Copy the addon/ and lib/ directories to some directory on your server
    (e.g. /pdfjs-dist/addons/doq/)
  3. Include addon/doq.js as a module in the viewer.html of your deployment:
<script type="module" src="/path/to/addon/doq.js"></script>
  1. The global add-on object can be accessed later as window.DOQ.

Alternatively, to simply import as an ES6 module: import doq from "path/to/lib/doq.js".

Please check the exported functions of lib/api.js for the API for both the global add-on object and the module import.

The add-on targets the default generic viewer of PDF.js. It should also work in custom viewers built on top of that. Feel free to open an issue if it breaks in your viewer.

Defining colors

Color schemes are defined in lib/colors.json, which you can extend. Only 6-digit RGB hex codes are currently supported.

Each color scheme can have up to three tones. background and foreground will replace the white and black colors in the document respectively; they also define the gradient to which the rest of the greyscale gets interpolated. Other colors map to their nearest color among accents, which can be specified per tone and/or per scheme. They too get mapped to the gradient if no accents are supplied.

Included by default are the Firefox, Safari Reader View themes and the Solarized color scheme.

Reader options

Deployments can configure the following options by writing key-value pairs directly to doq.options in Local storage (example follows):

  • autoReader [Boolean]: Whether to automatically apply the last-used reader theme at launch. Default true.
  • dynamicTheme [Boolean]: Whether to save separate last-used preferences for OS light/dark themes. Default true.
/* Options have to be set before loading doq */
const doqOptions = { dynamicTheme: false };
localStorage.setItem("doq.options", JSON.stringify(doqOptions));

Features

Screenshot of the doq toolbar

  • Reader mode: applies the selected theme to the document's background, text and (optionally) to lines and other shapes.

  • Blend images: make images in the document blend with the new background (or text color in the case of dark themes).

  • Invert mode: to simply invert the (original) document colors if that is all you need (and you don't mind negative images).

  • Intelligent application: doq does not blindly change text color, but tries to ensure the legibility of the text against the background in which it is rendered.

  • Color-science aware: doq does color transformations in the perceptually-uniform CIELAB color space.

  • Accessibility: the add-on toolbar is designed, following WCAG guidelines, to be well accessible to keyboard/screen-reader users.

  • Remember preferences: doq loads the last used settings at launch, and also updates them dynamically, based on the OS theme in use.

Performance

doq recalculates the colors when the page is being rendered by PDF.js. This incurs a small overhead, slightly reducing the renderer's performance. doq tries to minimize this overhead with many optimizations (like caching the calculation results) so that speed improves after the initial render. External benchmark tests are welcome.

This does not apply to the quick-invert mode as no processing is done there; it is implemented as a simple CSS filter, and hence, is fast.

(I guess this can be avoided altogether by implementing the logic directly within the PDF.js library by modifying the source. But that requires digging into PDF.js internals and also building and testing the entire thing, which I have zero experience with. Hence I chose the add-on route.)

Why doq?

Same reason that led Ethan Schoonover in developing the Solarized color scheme: ergonomics for eyes. It is best summarized by this quote from the Solarized Readme:

Black text on white from a computer display is akin to reading a book in direct sunlight and tires the eye.

PDFs are perhaps the single largest source of "black text on white" out there that are not amenable to modification. Designed to look the same everywhere, the PDF format, unlike EPUB or plain text, does not offer the flexibility of choosing its appearance while viewing. Yet it is the most popular document format in existence. Not much talk about ergonomics.

Another point is accessibility. The ability to adjust the colors of documents can be immensly helpful to people with color vision deficiencies or other low-vision conditions. Document creators are usually blind to such concerns.

The Web is heeding the call, with major browsers now having reader modes, and more and more websites providing dark/night/low-contrast versions on their own. But I could find scarcely any efforts in that direction in the domain of PDF viewing. None of the viewers I tried offered any simple way to change the PDF's appearance. In the end I decided to create a tool on my own.

OK, but why PDF.js?

Perhaps a web app is still not the best tool to view a PDF document; but they seem to be getting there. With modern browsers, PDF.js does a decent job, and is FireFox's built-in PDF viewer. Being familiar with web and JS, I saw it as the tool that I could quickly extend and develop my solution for, without needing to pore over thousands of lines of code of a low-level PDF library. It requires no additional software and is automatically cross-platform, meaning I could have my solution immediately available on my smartphone also, without much additional coding.

The limitations do bug me sometimes. I would be delighted to see a doq-like feature added to other popular PDF viewers also. I plan to work towards that goal in future if time permits.
Eye ergonomics matter.

Suggestions and contributions are welcome!


This project started out slightly differently; versions 1.x are now legacy. If interested, see v2.0 release notes for an overview of what changed, and why.

doq's People

Contributors

shivaprsd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

doq's Issues

Node.js integration and NPM package

An NPM package would be useful to devs wishing to integrate doq in their projects.

I am not familiar with the Node.js platform, so help in this direction is most welcome.

Separate preferences for system light and dark modes

Would be a nice feature to have. Safari reader view already has it.

Need to decide whether this should apply to only the color scheme and tone, or all prefs. Also need to monitor for change in the mode by the user and update the reader accordingly.

Reader mode breaks printing

Issue

When Reader mode is on, printing the document halts abruptly and immediately, without giving any error message.

Reason

Upon printing, PDF.js redraws the pages on a bigger offscreen canvas. Since saveCanvas() does not save imageData of unattached canvases, the old canvas data is used by getCanvasColor(), causing an invalid argument to be passed to the Color constructor.

Random text goes invisible at times

Screenshot of random missing text

The text looks garbled, but it is actually random letters missing. They are still present in the textLayer, so I strongly suspect they are somehow being rendered in the background color instead of the foreground.

Difficult to debug since I can't reliably reproduce the issue. It sometimes seem to happen after a quick window resize. This could be possibly related to the canvasData object not getting cleared and churning outdated data (as happened in #2).

Corrupted settings can crash doq on launch

bug: doq stopped working with previously corrupted settings

Suddenly the doq stopped working for me in Firefox. However, it still worked on Chromium.

After some debugging, I've found that the cause was a previously stored setting scheme that not matched the colorSchemes anymore.

Steps to Reproduce

I've compared the following pages:

  1. Example viewer linked from the doqment repository front page.
  2. Showing the same PDF of the example viewer in another Firefox tab using plain pdf.js without doqment extension installed.

Debugger Output

viewer.html
Uncaught (in promise) TypeError: scheme is undefined
    updateColorScheme https://shivaprsd.github.io/doq-demo/addon/doq.js:283
    updateReaderState https://shivaprsd.github.io/doq-demo/addon/doq.js:277
    load https://shivaprsd.github.io/doq-demo/addon/doq.js:83
    doqInit https://shivaprsd.github.io/doq-demo/addon/doq.js:16
doq.js:283:9
PDF 110dd61fd57444010b1ab5ff38782f0f [1.4 pdfeTeX-1.21a / TeX] (PDF.js: 2.13.216) app.js:1530:12

Call Stack

  updateColorScheme(index) {                   // index == 3
    const scheme = this.colorSchemes[index];   // this.colorSchemes == [{"Safari"}, {"Solarized"}] == 2 items
    if (!scheme.tones || !scheme.tones.length) // scheme === undefined --> Exception above
      return;
    ...
  },

Property Values

I've got these value inspecting variables inside the DevTools.

this.preferences

{
    flags: {
      ​​imagesOn: true,
      ​​shapesOn: true
    },
    ​​scheme: 3,  // <- does'nt matches this.colorSchemes
    ​tone: "2",
    theme: "dark"
}

localStorage.getItem('doq.preferences.dark')

{"scheme":3,"tone":"2","flags":{"shapesOn":true,"imagesOn":true}}

this.colorSchemes

Array [ {…}, {…} ]
    0: Object { name: "Safari", tones: (4) […], colors: [] }
        colors: Array []
        name: "Safari"
        tones: Array(4) [ {…}, {…}, {…}, … ]
            ​​0: Object { name: "White", background: "#FFFFFF", foreground: "#1B1B1B", … }
            ​​​1: Object { name: "Sepia", background: "#F8F1E3", foreground: "#4F321C", … }
            ​​​2: Object { name: "Gray", background: "#4A4A4D", foreground: "#D7D7D8", … }
            ​​​3: Object { name: "Night", background: "#121212", foreground: "#B0B0B0", … }
            ​​​length: 4
    ​​​1: Object { name: "Solarized", tones: (2) […], accents: (8) […], … }
        accents: Array(8) [ "#B58900", "#CB4B16", "#DC322F", … ]
        colors: Array(8) [ {…}, {…}, {…}, … ]
        name: "Solarized"
        tones: Array [ {…}, {…} ]
            ​​​0: Object { name: "Light", background: "#FDF6E3", foreground: "#657B83", … }
            ​​​1: Object { name: "Dark", background: "#002B36", foreground: "#839496", … }
            ​​​length: 2
    ​​​length: 2

Patch to fix the issue

This makes the code more reliable on bad/corrupted settings:

  updateColorScheme(index) {
    const scheme = this.colorSchemes[index];
--    if (!scheme.tones || !scheme.tones.length)
++    if (!scheme || !scheme.tones || !scheme.tones.length)
      return;
    ...
  },

pdfjsLib is not defined while using with PDF.js source code

version 4.5.136

  const config = getAddonConfig();
  DOQ.colorSchemes.forEach(scheme => {
    config.schemeSelector.appendChild(new Option(scheme.name));
  });

  /* Legacy PDF.js support */
  const pdfjsVer = pdfjsLib.version.split(".").map(Number);
  if (pdfjsVer[0] < 3) {
    if (pdfjsVer[0] < 2 || pdfjsVer[1] < 7) {
      console.warn("doq: unsupported PDF.js version " + pdfjsLib.version);
    }
    config.viewReader.classList.add("pdfjsLegacy");
    config.readerToolbar.classList.add("pdfjsLegacy");
  }
  DOQ.config = config;
}

Implement support for Canvas gradients and patterns

Currently they are left unhandled, because I have not figured out exactly how to handle them.

Gradient

Can have any number of color stops, some of which can be closely spaced. It is not clear how all of them should map to the given color scheme.

Note that the CanvasGradient object is opaque when it reaches getReaderStyle(). The colors are available only in its addColorStop() method. So it needs a different treatment altogether.

Pattern

Is an image, hence should be governed by the image-mode settings. This means we need to modify the composite operation during a fill/stroke operation. Current wrapping paradigm allows modifying only one property at a time; this would need to be changed.


I am also not sure whether they are common enough in PDFs to warrant the added complexity. I plan to evaluate the requirement as I go along reading PDFs with doq.

Image mode messes with thumbnail rendering

messed up thumbnails

PDF.js renders thumbnails as scaled down images of the corresponding page canvases. getReaderCompOp() changes the composite operation of that render when images are turned on.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.