]> git.bundesbrandschatzamt.de Git - emacsconfig.git/commitdiff
add pdf-tools
authorAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Thu, 31 Oct 2019 17:11:34 +0000 (18:11 +0100)
committerAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Thu, 31 Oct 2019 17:19:20 +0000 (18:19 +0100)
52 files changed:
elpa/pdf-tools-20191031.642/README [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/Makefile [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/.gitignore [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/Makefile.am [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/autobuild [new file with mode: 0755]
elpa/pdf-tools-20191031.642/build/server/autogen.sh [new file with mode: 0755]
elpa/pdf-tools-20191031.642/build/server/configure.ac [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/epdfinfo.c [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/epdfinfo.h [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/m4/ax_check_compile_flag.m4 [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/poppler-hack.cc [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/poppler-versions [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/synctex_parser.c [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/synctex_parser.h [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/synctex_parser_local.h [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/synctex_parser_readme.txt [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/synctex_parser_utils.c [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/synctex_parser_utils.h [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/synctex_parser_version.txt [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/.gitignore [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/lib/run-tests [new file with mode: 0755]
elpa/pdf-tools-20191031.642/build/server/test/docker/lib/yes-or-enter [new file with mode: 0755]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/arch.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/centos-7.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/debian-8.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/debian-9.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/fedora-24.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/fedora-25.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/fedora-26.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/gentoo.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/ubuntu-14.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/ubuntu-16.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/build/server/test/docker/templates/ubuntu-17.Dockerfile.in [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-annot.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-cache.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-dev.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-history.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-info.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-isearch.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-links.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-loader.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-misc.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-occur.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-outline.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-sync.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-tools-autoloads.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-tools-pkg.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-tools.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-util.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-view.el [new file with mode: 0644]
elpa/pdf-tools-20191031.642/pdf-virtual.el [new file with mode: 0644]

diff --git a/elpa/pdf-tools-20191031.642/README b/elpa/pdf-tools-20191031.642/README
new file mode 100644 (file)
index 0000000..6738261
--- /dev/null
@@ -0,0 +1,381 @@
+#+TITLE:     PDF Tools README
+#+AUTHOR:    Andreas Politz
+#+EMAIL:     politzaNOSPAMfh-trier.de
+
+[[https://travis-ci.org/politza/pdf-tools.svg?branch%3Dmaster][https://travis-ci.org/politza/pdf-tools.svg?branch=master]]
+[[http://stable.melpa.org/#/pdf-tools][http://stable.melpa.org/packages/pdf-tools-badge.svg]]
+[[http://melpa.org/#/pdf-tools][http://melpa.org/packages/pdf-tools-badge.svg]]
+
+
+
+** About this package
+   PDF Tools is, among other things, a replacement of DocView for PDF
+   files.  The key difference is that pages are not pre-rendered by
+   e.g. ghostscript and stored in the file-system, but rather created
+   on-demand and stored in memory.
+
+   This rendering is performed by a special library named, for
+   whatever reason, poppler, running inside a server program.  This
+   program is called ~epdfinfo~ and its job is to successively
+   read requests from Emacs and produce the proper results, i.e. the
+   PNG image of a PDF page.
+
+   Actually, displaying PDF files is just one part of PDF Tools.
+   Since poppler can provide us with all kinds of information about a
+   document and is also able to modify it, there is a lot more we can
+   do with it. [[http://www.dailymotion.com/video/x2bc1is_pdf-tools-tourdeforce_tech?forcedQuality%3Dhd720][Watch]]
+
+   Please read also about [[#known-problems][known problems.]]
+
+** Features
+   + View :: View PDF documents in a buffer with DocView-like
+             bindings.
+   + Isearch :: Interactively search PDF documents like any other
+                buffer, either for a string or a PCRE.
+   + Occur :: List lines matching a string or regexp in one or more
+              PDF documents.
+   + Follow ::
+    Click on highlighted links, moving to some part of a different
+    page, some external file, a website or any other URI.  Links may
+    also be followed by keyboard commands.
+   + Annotations :: Display and list text and markup annotations (like
+                    underline), edit their contents and attributes
+                    (e.g. color), move them around, delete them or
+                    create new ones and then save the modifications
+                    back to the PDF file.
+   + Attachments :: Save files attached to the PDF-file or list them
+                    in a dired buffer.
+   + Outline :: Use imenu or a special buffer to examine and navigate
+                the PDF's outline.
+   + SyncTeX :: Jump from a position on a page directly to the TeX
+                source and vice versa.
+   + Virtual ::
+    Use a collection of documents as if it were one, big single PDF.
+
+   + Misc ::
+     - Display PDF's metadata.
+     - Mark a region and kill the text from the PDF.
+     - Keep track of visited pages via a history.
+     - Apply a color filter for reading in low light conditions.
+
+** Installation
+   The package may be installed via melpa and it will try to build the
+   server part when it is activated the first time.  Though the next
+   section regarding build-prerequisites is still relevant, the rest
+   of the installation instructions assume a build from within a git
+   repository. (The melpa package has a different directory
+   structure.)
+
+*** Server Prerequisites
+    You'll need GNU Emacs \ge 24.3 and some form of a GNU/Linux OS.
+    Other operating systems are currently not supported (patches
+    welcome).  The following instructions assume a Debian-based
+    system. (The prerequisites may be installed automatically on this
+    kind of systems, see [[#compilation][Compilation]] .)
+
+    First make sure a suitable build-system is installed.  We need at
+    least a C/C++ compiler (both ~gcc~ and ~g++~), ~make~, ~automake~
+    and ~autoconf~.
+
+    Next we need to install a few libraries PDF Tools depends on, some
+    of which are probably already on your system.
+#+begin_src sh
+  $ sudo aptitude install libpng-dev zlib1g-dev
+  $ sudo aptitude install libpoppler-glib-dev
+  $ sudo aptitude install libpoppler-private-dev
+#+end_src
+    On some older Ubuntu systems, the final command will possibly give
+    an error.  This should be no problem, since in some versions this
+    package was contained in the main package ~libpoppler-dev~.  Also
+    note, that ~zlib1g-dev~ was for a long time called ~libz-dev~,
+    which it still may be on your system.
+
+    Debian wheezy comes with libpoppler version 0.18, which is pretty
+    old.  The minimally required version is 0.16, but some features of
+    PDF Tools depend on a more recent version of this library.  See
+    the following table for what they are and what version they
+    require.
+
+    | You want to ...                           | Required version |
+    |-------------------------------------------+------------------|
+    | ... create and modify text annotations.   | \ge 0.19.4       |
+    | ... search case-sensitive.                | \ge 0.22         |
+    | ... create and modify markup annotations. | \ge 0.26         |
+    |-------------------------------------------+------------------|
+
+    In case you decide to install libpoppler from source, make sure
+    to run its configure script with the ~--enable-xpdf-headers~
+    option.
+
+    Finally there is one feature (following links of a PDF document by
+    plain keystrokes) which requires imagemagick's convert utility.
+    This requirement is optional and you may install it like so:
+#+begin_src sh
+  $ sudo aptitude install imagemagick
+#+end_src
+**** Compiling on OS X
+     Although OS X is not officially supported, it has been reported
+     to have been successfully compiled.  You will need to install
+     poppler which you can get with homebrew via
+#+BEGIN_SRC sh
+  $ brew install poppler automake
+#+END_SRC
+
+     You will also have to help ~pkg-config~ find some libraries by
+     setting ~PKG_CONFIG_PATH~, e.g.
+#+BEGIN_SRC sh
+  $ export PKG_CONFIG_PATH=/usr/local/Cellar/zlib/1.2.8/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig
+#+END_SRC
+     or likewise within Emacs using `setenv`.
+
+     After that, compilation should proceed as normal.
+**** FreeBSD
+     Although not officially supported, it has been reported that
+     pdf-tools work well on FreeBSD.  Instead of building pdf-tools, you
+     can install one of the OS packages with, e.g.
+#+BEGIN_SRC sh
+  $ pkg install pdf-tools-emacs26
+#+END_SRC
+     To see the current list of pdf-tools packages for FreeBSD visit
+     [[https://repology.org/metapackages/?search=pdf-tools&inrepo=freebsd][the Repology list]].
+
+     To build pdf-tools from either melpa or directly from the source
+     repository, install the dependencies with
+#+BEGIN_SRC sh
+  $ pkg install autotools gmake poppler-glib
+#+END_SRC
+
+     If you choose not to install from melpa, you must substitute
+    ~gmake~ for ~make~ in the instructions below.
+**** Compiling on Centos
+     It is possible to compile pdf-tools on Centos. Install poppler the dependencies with:
+#+BEGIN_SRC sh
+  $ yum install poppler-devel poppler-glib-devel
+#+END_SRC
+
+**** Compiling on Fedora
+#+BEGIN_SRC sh
+  $ sudo dnf install make automake autoconf gcc gcc-c++ ImageMagick libpng-devel zlib-devel poppler-glib-devel
+#+END_SRC
+
+**** Compiling on Alpine Linux
+#+BEGIN_SRC sh
+  $ apk add build-base g++ gcc automake autoconf libpng-dev glib-dev poppler-dev
+#+END_SRC
+
+**** Compiling on Windows
+     PDF Tools can be built and used on Windows using the MSYS2
+     compiler. This will work with native (not cygwin) Windows builds of
+     emacs. This includes the standard binaries provided by the GNU
+     project, those available as MSYS2 packages and numerous third-party
+     binaries. It has been tested with emacs 25.1. Instructions are
+     provided under [[#compilation-and-installation-on-windows][Compilation and installation on Windows]], below.
+     PDF Tools will successfully compile using Cygwin, but it will not be 
+     able to open PDFs properly due to the way binaries compiled with Cygwin
+     handle file paths.
+
+*** Compilation
+    :PROPERTIES:
+    :CUSTOM_ID: compilation
+    :END:
+    Now it's time to compile the source.
+#+begin_src sh
+    $ cd /path/to/pdf-tools
+    $ make install-server-deps # optional
+    $ make -s
+#+end_src
+    The ~make install-server-deps~ command will try to install all
+    necessary programs and libraries to build the package, though
+    it'll only work, if ~sudo~ and ~apt-get~ are available.
+
+    This should compile the source code and create a Emacs Lisp
+    Package in the root directory of the project. The configure script
+    also tells you at the very end, which features, depending on the
+    libpoppler version, will be available.  These commands should give
+    no error, otherwise you are in trouble.
+**** Compilation and installation on Windows
+      If using the GNU binaries for Windows, support for PNG and zlib
+      must first be installed by copying the appropriate dlls into
+      emacs' ~bin/~ directory. Most third-party binaries come with this
+      already done.
+
+      First, install [[http://www.msys2.org/][install MSYS2]] and update
+      the package database and core packages using the instructions
+      provided. Then, to compile PDF tools itself:
+
+      1. Open msys2 shell
+
+      2. Update and install dependencies, skipping any you already have
+         #+BEGIN_SRC sh
+         $ pacman -Syu
+         $ pacman -S base-devel
+         $ pacman -S mingw-w64-x86_64-toolchain
+         $ pacman -S mingw-w64-x86_64-zlib
+         $ pacman -S mingw-w64-x86_64-libpng
+         $ pacman -S mingw-w64-x86_64-poppler
+         $ pacman -S mingw-w64-x86_64-imagemagick
+         #+END_SRC
+
+      3. Install PDF tools in Emacs, but do not try to compile the
+         server. Instead, get a separate copy of the source somewhere
+         else.
+         #+BEGIN_SRC sh
+         $ git clone https://github.com/politza/pdf-tools
+         #+END_SRC
+
+      4. Open mingw64 shell (*Note:* You must use mingw64.exe and not msys2.exe)
+
+      5. Compile pdf-tools
+         #+BEGIN_SRC sh
+         $ cd /path/to/pdf-tools
+         $ make -s
+         #+END_SRC
+
+      6. This should produce a file ~server/epdfinfo.exe~. Copy this file
+         into the ~pdf-tools/~ installation directory in your Emacs.
+
+      7. Start Emacs and activate the package.
+         #+BEGIN_SRC
+         M-x pdf-tools-install RET
+         #+END_SRC
+
+      8. Test.
+         #+BEGIN_SRC
+         M-x pdf-info-check-epdfinfo RET
+         #+END_SRC
+
+      If this is successful, ~(pdf-tools-install)~ can be added to Emacs'
+      config. Note that libraries from other GNU utilities, such as Git
+      for Windows, may interfere with those needed by PDF Tools.
+      ~pdf-info-check-epdinfo~ will succeed, but errors occur when trying
+      to view a PDF file. This can be fixed by ensuring that the MSYS
+      libraries are always preferred in emacs:
+
+ #+BEGIN_SRC emacs-lisp
+ (setenv "PATH" (concat "C:\\msys64\\mingw64\\bin;" (getenv "PATH")))
+ #+END_SRC
+
+*** ELisp Prerequisites
+    This package depends on the following Elisp packages, which should
+    be installed before installing the Pdf Tools package.
+
+    | Package   | Required version                 |
+    |-----------+----------------------------------|
+    | [[https://elpa.gnu.org/packages/let-alist.html][let-alist]] | >= 1.0.4 (comes with Emacs 25.2) |
+    | [[http://melpa.org/#/tablist][tablist]]   | >= 0.70                          |
+    |-----------+----------------------------------|
+
+*** Installing
+    If ~make~ produced the ELP file ~pdf-tools-${VERSION}.tar~ you are
+    fine.  This package contains all the necessary files for Emacs
+    and may be installed by either using
+#+begin_src sh
+    $ make install-package
+#+end_src
+    or executing the Emacs command
+#+begin_src elisp
+  M-x package-install-file RET pdf-tools-${VERSION}.tar RET
+#+end_src
+
+  To complete the installation process, you need to activate the
+  package by putting
+#+begin_src elisp
+  (pdf-tools-install)
+#+end_src
+  somewhere in your ~.emacs~.  Alternatively, and if you care about
+  start-up time, you may want to use 
+#+begin_src elisp
+  (pdf-loader-install)
+#+end_src
+  instead.  Next you probably want to take a look at the various
+  features of what you've just installed.  The following two commands
+  might be of help for doing so.
+#+begin_src elisp
+  M-x pdf-tools-help RET
+  M-x pdf-tools-customize RET
+#+end_src
+
+*** Updating
+    Some day you might want to update this package via ~git pull~ and
+    then reinstall it.  Sometimes this may fail, especially if
+    Lisp-Macros are involved and the version hasn't changed.  To avoid
+    this kind of problems, you should delete the old package via
+    ~list-packages~, restart Emacs and then reinstall the package.
+
+    This also applies when updating via package and melpa.
+
+** Known problems
+    :PROPERTIES:
+    :CUSTOM_ID: known-problems
+    :END:
+
+*** linum-mode
+    PDF Tools does not work well together with ~linum-mode~ and
+    activating it in a ~pdf-view-mode~, e.g. via ~global-linum-mode~,
+    might make Emacs choke.
+
+*** auto-revert
+    Autorevert works by polling the file-system every
+    ~auto-revert-interval~ seconds, optionally combined with some
+    event-based reverting via [[https://www.gnu.org/software/emacs/manual/html_node/elisp/File-Notifications.html][file notification]].  But this currently
+    does not work reliably, such that Emacs may revert the PDF-buffer
+    while the corresponding file is still being written to (e.g. by
+    LaTeX), leading to a potential error.
+
+    With a recent [[https://www.gnu.org/software/auctex/][auctex]] installation, you might want to put the
+    following somewhere in your dotemacs, which will revert the PDF-buffer
+    *after* the TeX compilation has finished.
+#+BEGIN_SRC emacs-lisp
+  (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer)
+#+END_SRC
+** Some keybindings
+
+| Navigation                                 |                       |
+|--------------------------------------------+-----------------------|
+| Scroll Up / Down by page-full              | ~space~ / ~backspace~ |
+| Scroll Up / Down by line                   | ~C-n~ / ~C-p~         |
+| Scroll Right / Left                        | ~C-f~ / ~C-b~         |
+| Top of Page / Bottom of Page               | ~<~ / ~>~             |
+| Next Page / Previous Page                  | ~n~ / ~p~             |
+| First Page / Last Page                     | ~M-<~ / ~M->~         |
+| Incremental Search Forward / Backward      | ~C-s~ / ~C-r~         |
+| Occur (list all lines containing a phrase) | ~M-s o~               |
+| Jump to Occur Line                         | ~RETURN~              |
+| Pick a Link and Jump                       | ~F~                   |
+| Incremental Search in Links                | ~f~                   |
+| History Back / Forwards                    | ~B~ / ~N~             |
+| Display Outline                            | ~o~                   |
+| Jump to Section from Outline               | ~RETURN~              |
+| Jump to Page                               | ~M-g g~               |
+
+| Display                                  |                 |
+|------------------------------------------+-----------------|
+| Zoom in / Zoom out                       | ~+~ / ~-~       |
+| Fit Height / Fit Width / Fit Page        | ~H~ / ~W~ / ~P~ |
+| Trim margins (set slice to bounding box) | ~s b~           |
+| Reset margins                            | ~s r~           |
+| Reset Zoom                               | 0               |
+
+| Annotations                   |                                                 |
+|-------------------------------+-------------------------------------------------|
+| List Annotations              | ~C-c C-a l~                                     |
+| Jump to Annotations from List | ~SPACE~                                         |
+| Mark Annotation for Deletion  | ~d~                                             |
+| Delete Marked Annotations     | ~x~                                             |
+| Unmark Annotations            | ~u~                                             |
+| Close Annotation List         | ~q~                                             |
+| Add and edit annotations      | via Mouse selection and left-click context menu |
+
+| Syncing with Auctex              |             |
+|----------------------------------+-------------|
+| jump to PDF location from source | ~C-c C-g~   |
+| jump source location from PDF    | ~C-mouse-1~ |
+
+| Miscellaneous                                 |           |
+|-----------------------------------------------+-----------|
+| Refresh File (e.g., after recompiling source) | ~g~       |
+| Print File                                    | ~C-c C-p~ |
+
+# Local Variables:
+# mode: org
+# End:
diff --git a/elpa/pdf-tools-20191031.642/build/Makefile b/elpa/pdf-tools-20191031.642/build/Makefile
new file mode 100644 (file)
index 0000000..1b50fee
--- /dev/null
@@ -0,0 +1,97 @@
+
+EMACS ?= emacs
+# Handle the mess when inside Emacs.
+unexport INSIDE_EMACS          #cask not like this.
+ifeq ($(EMACS), t)
+EMACS = emacs
+endif
+
+emacs = $(EMACS)
+emacs_version = $(shell $(emacs) --batch --eval \
+               '(princ (format "%s.%s" emacs-major-version emacs-minor-version))')
+$(info Using Emacs $(emacs_version))
+
+version=$(shell sed -ne 's/^;\+ *Version: *\([0-9.]\)/\1/p' lisp/pdf-tools.el)
+pkgname=pdf-tools-$(version)
+pkgfile=$(pkgname).tar
+
+.PHONY: all clean distclean bytecompile test check melpa cask-install
+
+all: $(pkgfile)
+
+# Create a elpa package including the server
+$(pkgfile): .cask/$(emacs_version) server/epdfinfo lisp/*.el
+       cask package .
+
+# Compile the Lisp sources
+bytecompile: .cask/$(emacs_version)
+       cask exec $(emacs) --batch -L lisp -f batch-byte-compile lisp/*.el
+
+# Run ERT tests
+test: all
+       PACKAGE_TAR=$(pkgfile) cask exec ert-runner
+
+check: test
+
+# Run the autobuild script tests in docker
+test-autobuild: server-test
+
+# Run all tests
+test-all: test test-autobuild
+
+# Init cask
+.cask/$(emacs_version):
+       cask install
+
+# Run the autobuild script (installing depends and compiling)
+autobuild:
+       cd server && ./autobuild
+
+# Soon to be obsolete targets
+melpa-build: autobuild
+       cp build/epdfinfo .
+install-server-deps: ;
+
+# Create a package like melpa would.
+melpa-package: $(pkgfile)
+       cp $(pkgfile) $(pkgname)-melpa.tar
+       tar -u --transform='s/server/$(pkgname)\/build\/server/' \
+               -f $(pkgname)-melpa.tar \
+               $$(git ls-files server)
+       tar -u --transform='s/Makefile/$(pkgname)\/build\/Makefile/' \
+               -f $(pkgname)-melpa.tar \
+               Makefile
+       tar -u --transform='s/README\.org/$(pkgname)\/README/' \
+               -f $(pkgname)-melpa.tar \
+               README.org
+       -tar --delete $(pkgname)/epdfinfo \
+               -f $(pkgname)-melpa.tar
+
+# Various clean targets
+clean: server-clean
+       rm -f -- $(pkgfile)
+       rm -f -- lisp/*.elc
+       rm -f -- pdf-tools-readme.txt
+       rm -f -- pdf-tools-$(version).entry
+
+distclean: clean server-distclean
+       rm -rf -- .cask
+
+# Server targets
+server/epdfinfo: server/Makefile server/*.[ch]
+       $(MAKE) -C server
+
+server/Makefile: server/configure
+       cd server && ./configure -q
+
+server/configure: server/configure.ac
+       cd server && ./autogen.sh
+
+server-test: server/Makefile
+       $(MAKE) -C server check
+
+server-clean:
+       ! [ -f server/Makefile ] || $(MAKE) -C server clean
+
+server-distclean:
+       ! [ -f server/Makefile ] || $(MAKE) -C server distclean
diff --git a/elpa/pdf-tools-20191031.642/build/server/.gitignore b/elpa/pdf-tools-20191031.642/build/server/.gitignore
new file mode 100644 (file)
index 0000000..a3cc612
--- /dev/null
@@ -0,0 +1,25 @@
+*.o
+.deps/
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache/
+config.h
+config.h.in
+config.log
+config.status
+configure
+depcomp
+epdfinfo
+install-sh
+libsynctex.a
+missing
+stamp-h1
+ar-lib
+compile
+config.h.in~
+.clang_complete
+callgrind.out.*
+config.guess
+config.sub
+TAGS
diff --git a/elpa/pdf-tools-20191031.642/build/server/Makefile.am b/elpa/pdf-tools-20191031.642/build/server/Makefile.am
new file mode 100644 (file)
index 0000000..5a5a7a6
--- /dev/null
@@ -0,0 +1,43 @@
+bin_PROGRAMS = epdfinfo
+epdfinfo_CFLAGS = -Wall $(glib_CFLAGS) $(poppler_glib_CFLAGS) $(poppler_CFLAGS) \
+                       $(png_CFLAGS)
+epdfinfo_CXXFLAGS = -Wall $(epdfinfo_CFLAGS)
+epdfinfo_LDADD =  $(glib_LIBS) $(poppler_glib_LIBS) $(poppler_LIBS) \
+                       $(png_LIBS) libsynctex.a $(zlib_LIBS)
+epdfinfo_SOURCES = epdfinfo.c epdfinfo.h poppler-hack.cc
+
+noinst_LIBRARIES = libsynctex.a
+libsynctex_a_SOURCES = synctex_parser.c synctex_parser_utils.c synctex_parser.h        \
+                       synctex_parser_local.h synctex_parser_utils.h
+libsynctex_a_CFLAGS = -w $(zlib_CFLAGS)
+
+if HAVE_W32
+epdfinfo_LDADD += -lshlwapi
+endif
+
+SYNCTEX_UPSTREAM = svn://tug.org/texlive/tags/texlive-2017.1/Build/source/texk/web2c/synctexdir
+SYNCTEX_FILES = synctex_parser.c               \
+               synctex_parser.h                \
+               synctex_parser_local.h          \
+               synctex_parser_readme.txt       \
+               synctex_parser_utils.c          \
+               synctex_parser_utils.h          \
+               synctex_parser_version.txt
+
+
+check-local:
+       @if $(MAKE) --version 2>&1 | grep -q GNU; then \
+               cd test && $(MAKE) $(AM_MAKEFLAGS); \
+       else \
+               echo "Skipping tests in server/test (requires GNU make)"; \
+       fi
+
+synctex-pull:
+       @if [ -n "$$(git status --porcelain)" ]; then \
+               git status; \
+               echo "Not checking-out files into a dirty work-directory"; \
+               false; \
+       fi
+       for file in $(SYNCTEX_FILES); do \
+               svn export --force $(SYNCTEX_UPSTREAM)/$$file; \
+       done
diff --git a/elpa/pdf-tools-20191031.642/build/server/autobuild b/elpa/pdf-tools-20191031.642/build/server/autobuild
new file mode 100755 (executable)
index 0000000..aa6b54c
--- /dev/null
@@ -0,0 +1,545 @@
+#!/bin/sh
+
+##
+## Installs package dependencies and builds the application.
+##
+
+# Don't exit if some command fails.
+set +e
+# Disalbe file globbing.
+set -f
+
+# Boolean variables are true if non-empty and false otherwise.
+
+# Command to install packages.
+PKGCMD=
+# Args to pass to $PKGCMD.
+PKGARGS=
+# Required packages.
+PACKAGES=
+# Whether package installation requires root permissions.
+PKG_INSTALL_AS_ROOT=true
+# Whether to skip package installation altogether.
+PKG_INSTALL_SKIP=
+# Whether to force package installation, even if it does not seem
+# necessary.
+PKG_INSTALL_FORCE=
+# Only test if the OS is handled by this script.
+DRY_RUN=
+# If and where to install the program.
+INSTALL_DIR=
+# Whether we can install packages.
+OS_IS_HANDLED=true
+# Which OSs installer to use
+OS=
+
+## +-----------------------------------------------------------+
+## * Utility Functions
+## +-----------------------------------------------------------+
+
+usage()
+{
+    cat <<EOF
+usage:$(basename "$0") [--help|-n|-i DIR|[-d -D]|[--os OS]]
+
+    -n       Don't do anything, but check if this OS is handled.
+
+    -i DIR   Install the program in the given directory.
+
+    -d       Force dependency installattion.
+
+    -D       Skip dependency installattion.
+
+    --os OS  Use the given OS's installer
+
+    --help   Display this message.
+
+EOF
+    exit "$1"
+}
+
+# Search for command $1 in PATH. Print its absolute filename.
+which()
+{
+    if [ -z "$1" ]; then
+        return 1
+    fi
+    IFS=:
+    for dir in $PATH; do
+        if [ -x "$dir/$1" ]; then
+            printf "%s" "$dir/$1"
+            unset IFS
+            return 0
+        fi
+    done
+    unset IFS
+    return 1
+}
+
+# Quote $@ for the shell.
+quote()
+{
+    quoted=
+    for arg; do
+        qarg=$(printf "%s" "$arg" | sed -e 's/[|&;<>()$\`"'\''         ]/\\&/g')
+        if [ -z "$quoted" ]; then
+            quoted=$qarg
+        else
+            quoted="$quoted $qarg"
+        fi
+    done
+    printf "%s" "$quoted"
+}
+
+# Attempt to exec $@ as root.
+exec_privileged() {
+    if [ -z "$1" ]; then
+        echo "internal error: command is empty"
+        exit 2
+    fi
+    if [ -w / ]; then
+        "$@"
+    elif which sudo >/dev/null 2>&1; then
+        sudo -- "$@"
+        retval=$?
+        sudo -k
+        return $retval
+    elif which su >/dev/null 2>&1; then
+        su -c "$(quote "$@")"
+    else
+        echo "No such program: sudo or su"
+        exit 1
+    fi
+}
+
+# Test if $1 is in PATH or exit with a failure status.
+assert_program()
+{
+    if ! which "$1" >/dev/null 2>&1; then
+        echo "No such program: $1"
+        exit 1
+    fi
+}
+
+# Source filename $1 and echo variable $2.
+source_var()
+{
+    if ! [ -f "$1" ] || ! [ -r "$1" ] || [ -z "$2" ]; then
+        return 1
+    fi
+    # shellcheck source=/dev/null
+    . "$1"
+    eval "printf '%s\n' \$$2"
+    return 0
+}
+
+exit_success()
+{
+    echo "==========================="
+    echo "   Build succeeded. :O)    "
+    echo "==========================="
+    exit 0
+}
+
+exit_fail()
+{
+    echo "==========================="
+    echo "     Build failed.  ;o(    "
+    echo "==========================="
+    if [ -z "$PKG_INSTALL_FORCE" ]; then
+        echo "Note: maybe try the '-d' option."
+    fi
+    exit 1
+}
+
+# Return 0, if all required packages seem to be installed.
+have_packages_installed()
+{
+    {
+        which pkg-config || return 1
+        if ! [ -f configure ];then
+            which autoreconf || return 1
+            which automake || return 1
+        fi
+        for lib in libpng glib-2.0 poppler poppler-glib zlib; do
+            pkg-config --exists $lib || return 1
+        done
+        which make || return 1
+        which gcc || which cc || return 1
+        which g++ || which c++ || return 1
+        cc $(pkg-config --cflags poppler) -o /dev/null -E - 2>/dev/null <<EOF
+        #include <PDFDocEncoding.h>
+EOF
+        [ $? -eq 0 ] || return 1
+        return 0
+    } >/dev/null 2>&1
+}
+
+handle_options()
+{
+    while [ $# -gt 0 ]; do
+        case $1 in
+            --help) usage 0;;
+            -n) DRY_RUN=true;;
+            -d) PKG_INSTALL_FORCE=true ;;
+            -D) PKG_INSTALL_SKIP=true ;;
+            -i)
+                shift
+                [ $# -gt 0 ] || usage 1
+                if [ "${1%%/}" != "${PWD%%/}" ]; then
+                    INSTALL_DIR=$1
+                fi ;;
+            --os)
+                shift
+                [ $# -gt 0 ] || usage 1
+                OS="$1"
+                ;;
+            *) usage 1 ;;
+        esac
+        shift
+    done
+    if [ -n "$PKG_INSTALL_SKIP" ] && [ -n "$PKG_INSTALL_FORCE" ]; then
+        usage 1
+    fi
+}
+
+## +-----------------------------------------------------------+
+## * OS Functions
+## +-----------------------------------------------------------+
+
+# Archlinux
+os_arch() {
+    if ! [ -e "/etc/arch-release" ]; then
+        return 1;
+    fi
+    PKGCMD=pacman
+    PKGARGS="-S --needed"
+    PACKAGES="base-devel libpng zlib poppler-glib"
+    return 0;
+}
+
+# CentOS
+os_centos() {
+    if ! [ -e "/etc/centos-release" ]; then
+        return 1
+    fi
+    PKGCMD=yum
+    if yum help install-n >/dev/null 2>&1; then
+        PKGARGS=install-n
+    else
+        PKGARGS=install
+    fi
+    PACKAGES="autoconf
+              automake
+              gcc
+              gcc-c++
+              libpng-devel
+              make
+              pkgconfig
+              poppler-devel
+              poppler-glib-devel
+              zlib-devel"
+    return 0
+}
+
+# FreeBSD
+os_freebsd() {
+    if ! which uname >/dev/null 2>&1 || [ "$(uname -s)" != "FreeBSD" ]; then
+        return 1
+    fi
+    PKGCMD=pkg
+    PKGARGS=install
+    PACKAGES="autotools poppler-glib png pkgconf"
+    return 0
+}
+
+# OpenBSD
+os_openbsd() {
+    if ! which uname >/dev/null 2>&1 || [ "$(uname -s)" != "OpenBSD" ]; then
+        return 1
+    fi
+    PKGCMD=pkg_add
+    PKGARGS="-uU"
+    PACKAGES="autoconf-2.69p2 automake-1.15.1 poppler poppler-utils png"
+    export AUTOCONF_VERSION=2.69
+    export AUTOMAKE_VERSION=1.15
+    if whereis clang++ ;then
+        export CXX=clang++
+    elif whereis eg++ ;then
+        export CXX=eg++
+    else
+        export CXX=eg++
+        PACKAGES="${PACKAGES} g++"
+    fi
+    export CXXFLAGS="-std=c++11 -I/usr/local/include/poppler -I/usr/local/include"
+    return 0
+}
+
+# Fedora
+os_fedora() {
+    if ! [ -e "/etc/fedora-release" ]; then
+        return 1
+    fi
+    PKGCMD=dnf
+    PKGARGS=install
+    PACKAGES="autoconf
+              automake
+              gcc
+              gcc-c++
+              libpng-devel
+              make
+              poppler-devel
+              poppler-glib-devel
+              zlib-devel"
+    VERSION=$(source_var /etc/os-release VERSION_ID)
+    if [ -n "$VERSION" ] && [ "$VERSION" -ge 26 ]; then
+        PACKAGES="$PACKAGES pkgconf"
+    else
+        PACKAGES="$PACKAGES pkgconfig"
+    fi
+    return 0
+}
+
+# Debian/Ubuntu
+os_debian() {
+    if ! [ -e "/etc/debian_version" ]; then
+        return 1
+    fi
+    PACKAGES="autoconf
+              automake
+              g++
+              gcc
+              libpng-dev
+              libpoppler-dev
+              libpoppler-glib-dev
+              libpoppler-private-dev
+              libz-dev
+              make
+              pkg-config"
+    PKGCMD=apt-get
+    PKGARGS=install
+    return 0
+}
+
+# Msys2
+os_msys2() {
+    if [ -z "$MSYSTEM" ] || ! [ -r "/etc/profile" ]; then
+        return 1
+    fi
+    case $MSYSTEM in
+        MINGW64)
+            PACKAGES="base-devel
+                      mingw-w64-x86_64-libpng
+                      mingw-w64-x86_64-poppler
+                      mingw-w64-x86_64-toolchain
+                      mingw-w64-x86_64-zlib" ;;
+        MINGW32)
+            PACKAGES="base-devel
+                      mingw-w64-i686-libpng
+                      mingw-w64-i686-poppler
+                      mingw-w64-i686-toolchain
+                      mingw-w64-i686-zlib" ;;
+        MSYS)
+            case $(uname -m) in
+                x86_64)
+                    MSYSTEM=MINGW64 ;;
+                *)
+                    MSYSTEM=MINGW32 ;;
+            esac
+            export MSYSTEM
+            # shellcheck source=/dev/null
+            . /etc/profile
+            eval "exec $(quote "$0" "$@")" ;;
+        *)
+            echo "Unrecognized MSYSTEM value: $MSYSTEM"
+            exit 1 ;;
+    esac
+    PKGCMD=pacman
+    PKGARGS="-S --needed"
+    PKG_INSTALL_AS_ROOT=
+    return 0
+}
+
+# MacOS
+os_macos() {
+    if ! which uname >/dev/null 2>&1 || [ "$(uname -s)" != "Darwin" ]; then
+        return 1
+    elif which brew >/dev/null 2>&1; then
+        PKGCMD=brew
+        PKGARGS=install
+        PACKAGES="pkg-config poppler automake"
+        PKG_INSTALL_AS_ROOT=
+        # homebrew install libffi as keg-only, meaning we need to set
+        # PKG_CONFIG_PATH manually so configure can find it
+        export PKG_CONFIG_PATH="$(brew --prefix libffi)/lib/pkgconfig/"
+    elif which port >/dev/null 2>&1; then
+        PKGCMD=port
+        PKGARGS=install
+        PACKAGES="pkgconfig poppler automake libpng"
+    else
+        return 1
+    fi
+    return 0
+}
+
+# NixOS
+os_nixos() {
+    # Already in the nix-shell.
+    if [ -n "$AUTOBUILD_NIX_SHELL" ]; then
+        return 0
+    fi
+    if ! which nix-shell >/dev/null 2>&1; then
+        return 1
+    fi
+    if [ -n "$DRY_RUN" ]; then
+        return 0
+    fi
+    command="AUTOBUILD_NIX_SHELL=true"
+    command="$command;export AUTOBUILD_NIX_SHELL"
+    command="$command;$(quote "$0" "$@")"
+    exec nix-shell --pure --command "$command" \
+         -p gcc gnumake automake autoconf pkgconfig libpng zlib poppler
+}
+
+# Gentoo
+os_gentoo() {
+    if ! [ -e "/etc/gentoo-release" ]; then
+        return 1
+    fi
+    PKGCMD=emerge
+    PKGARGS=--noreplace
+    PACKAGES="app-text/poppler
+              dev-util/pkgconfig
+              media-libs/libpng
+              sys-devel/autoconf
+              sys-devel/automake
+              sys-devel/gcc
+              sys-devel/make
+              sys-libs/zlib"
+    return 0
+}
+
+# By Parameter --os
+os_argument() {
+    [ -z "$OS" ] && return 1
+    case $OS in
+        macos)   os_macos   "$@";;
+        freebsd) os_freebsd "$@";;
+        arch)    os_arch    "$@";;
+        centos)  os_centos  "$@";;
+        openbsd) os_openbsd "$@";;
+        fedora)  os_fedora  "$@";;
+        debian)  os_debian  "$@";;
+        gentoo)  os_gentoo  "$@";;
+        msys2)   os_msys2   "$@";;
+        nixos)   os_nixos   "$@";;
+        *)       echo "Invalid --os argument: $OS"
+                 exit 1
+    esac || {
+        echo "Unable to install on this system as $OS"
+        exit 1
+    }
+}
+
+## +-----------------------------------------------------------+
+## * Figure out were we are, install deps and build the program
+## +-----------------------------------------------------------+
+
+handle_options "$@"
+
+os_argument "$@" || \
+os_macos    "$@" || \
+os_freebsd  "$@" || \
+os_arch     "$@" || \
+os_centos   "$@" || \
+os_openbsd  "$@" || \
+os_fedora   "$@" || \
+os_debian   "$@" || \
+os_gentoo   "$@" || \
+os_msys2    "$@" || \
+os_nixos    "$@" || \
+{
+    OS_IS_HANDLED=
+    if [ -z "$DRY_RUN" ]; then
+        echo "Failed to recognize this system, trying to continue."
+    fi
+}
+
+if [ -n "$DRY_RUN" ]; then
+    [ -n "$OS_IS_HANDLED" ]
+    exit $?
+fi
+
+if [ -n "$PKGCMD" ];then
+    echo "---------------------------"
+    echo "    Installing packages    "
+    echo "---------------------------"
+    if [ -n "$PKG_INSTALL_SKIP" ]; then
+        echo "Skipping package installation (as requested)"
+    elif [ -z "$PKG_INSTALL_FORCE" ] && have_packages_installed; then
+        echo "Skipping package installation (already installed)"
+    else
+        assert_program "$PKGCMD"
+        echo "$PKGCMD $PKGARGS $PACKAGES"
+        if [ -n "$PKG_INSTALL_AS_ROOT" ]; then
+            exec_privileged $PKGCMD $PKGARGS $PACKAGES
+        else
+            $PKGCMD $PKGARGS $PACKAGES
+        fi
+    fi
+    echo
+fi
+
+echo "---------------------------"
+echo " Configuring and compiling "
+echo "---------------------------"
+
+# Try to be in the correct directory.
+if which dirname >/dev/null 2>&1; then
+    cd "$(dirname "$0")" || {
+        echo "Failed to change into the source directory"
+        exit 1
+    }
+fi
+
+# Create the configure script.
+if ! [ -f ./configure ]; then
+    assert_program autoreconf
+    echo "autoreconf -i"
+    autoreconf -i
+    [ -f ./configure ] || exit_fail
+fi
+
+# Build the program.
+if [ -n "$INSTALL_DIR" ]; then
+    prefix=--bindir=$INSTALL_DIR
+fi
+
+echo "./configure -q $prefix && make -s"
+eval "./configure -q $(quote "$prefix") && make -s || exit_fail"
+echo
+if [ -n "$INSTALL_DIR" ]; then
+    echo "---------------------------"
+    echo "       Installing          "
+    echo "---------------------------"
+    echo make -s install
+    if mkdir -p -- "$INSTALL_DIR" && [ -w "$INSTALL_DIR" ]; then
+        make install || exit_fail
+    else
+        exec_privileged make install || exit_fail
+    fi
+    # Copy dynamic libraries on windows.
+    if [ -f epdfinfo.exe ]; then
+        assert_program awk
+        assert_program ldd
+        for dll in $(ldd epdfinfo.exe | awk '/\/mingw/ {print $3}'); do
+            cp -u "$dll" "$INSTALL_DIR"
+        done
+    fi
+    echo
+fi
+exit_success
+
+# Local Variables:
+# compile-command: "shellcheck autobuild"
+# End:
diff --git a/elpa/pdf-tools-20191031.642/build/server/autogen.sh b/elpa/pdf-tools-20191031.642/build/server/autogen.sh
new file mode 100755 (executable)
index 0000000..0e0824c
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+echo "Running autoreconf..."
+
+autoreconf -i
diff --git a/elpa/pdf-tools-20191031.642/build/server/configure.ac b/elpa/pdf-tools-20191031.642/build/server/configure.ac
new file mode 100644 (file)
index 0000000..ae1255f
--- /dev/null
@@ -0,0 +1,113 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.67])
+AC_INIT([epdfinfo], 1.0, [politzaNOSPAMfh-trier.de])
+AM_INIT_AUTOMAKE([-Wall -Wno-override foreign silent-rules])
+AC_CONFIG_SRCDIR([epdfinfo.h])
+AC_CONFIG_HEADERS([config.h])
+
+# Checks for programs.
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CXX
+AC_PROG_RANLIB
+AM_PROG_AR
+
+# Checks for libraries.
+HAVE_POPPLER_FIND_OPTS="no   (requires poppler-glib >= 0.22)"
+HAVE_POPPLER_ANNOT_WRITE="no   (requires poppler-glib >= 0.19.4)"
+HAVE_POPPLER_ANNOT_MARKUP="no (requires poppler-glib >= 0.26)"
+
+PKG_CHECK_MODULES([png], [libpng])
+PKG_CHECK_MODULES([glib], [glib-2.0])
+PKG_CHECK_MODULES([poppler], [poppler])
+PKG_CHECK_MODULES([poppler_glib], [poppler-glib >= 0.16.0])
+PKG_CHECK_EXISTS([poppler-glib >= 0.19.4], [HAVE_POPPLER_ANNOT_WRITE=yes])
+PKG_CHECK_EXISTS([poppler-glib >= 0.22], [HAVE_POPPLER_FIND_OPTS=yes])
+PKG_CHECK_EXISTS([poppler-glib >= 0.26], [HAVE_POPPLER_ANNOT_MARKUP=yes])
+PKG_CHECK_MODULES([zlib], [zlib])
+
+AC_COMPILE_IFELSE(
+   [AC_LANG_PROGRAM([[
+     #ifndef _WIN32
+       error
+     #endif
+   ]])], [have_w32=true], [have_w32=false])
+AM_CONDITIONAL(HAVE_W32, [test "$have_w32" = true])
+
+if test "$have_w32" = true; then
+  if test "$MSYSTEM" = MINGW32 -o "$MSYSTEM" = MINGW64; then
+  # glib won't work properly on msys2 without it.
+    CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $CFLAGS"
+  fi
+fi
+
+SAVED_CPPFLAGS=$CPPFLAGS
+CPPFLAGS=$poppler_CFLAGS
+AC_LANG_PUSH([C++])
+# Check if we can use the -std=c++11 option.
+m4_include([m4/ax_check_compile_flag.m4])
+AX_CHECK_COMPILE_FLAG([-std=c++11], [HAVE_STD_CXX11=yes])
+
+if test "$HAVE_STD_CXX11" = yes; then
+  CXXFLAGS="-std=c++11 $CXXFLAGS"
+fi
+# Check for private poppler header.
+AC_CHECK_HEADERS([Annot.h PDFDocEncoding.h], [],
+    AC_MSG_ERROR([cannot find necessary  poppler-private header (see README.org)]))
+AC_LANG_POP([C++])
+CPPFLAGS=$SAVED_CPPFLAGS
+
+# Setup compile time features.
+if test "$HAVE_POPPLER_FIND_OPTS" = yes; then
+  AC_DEFINE([HAVE_POPPLER_FIND_OPTS],1,
+    [Define to 1 to enable case sensitive searching (requires poppler-glib >= 0.22).])
+fi
+
+if test "$HAVE_POPPLER_ANNOT_WRITE" = yes; then
+  AC_DEFINE([HAVE_POPPLER_ANNOT_WRITE],1,
+    [Define to 1 to enable writing of annotations (requires poppler-glib >= 0.19.4).])
+fi
+
+if test "$HAVE_POPPLER_ANNOT_MARKUP" = yes; then
+  AC_DEFINE([HAVE_POPPLER_ANNOT_MARKUP],1,
+    [Define to 1 to enable adding of markup annotations (requires poppler-glib >= 0.26).])
+fi
+
+AC_CANONICAL_HOST
+# Checks for header files.
+AC_CHECK_HEADERS([stdlib.h string.h strings.h err.h])
+
+AC_MSG_CHECKING([for error.h])
+SAVED_CFLAGS=$CFLAGS
+CFLAGS="$poppler_CFLAGS $poppler_glib_CFLAGS"
+AC_LANG_PUSH([C])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+        #include <error.h>
+        ],[error (0, 0, "");])],
+        [AC_DEFINE([HAVE_ERROR_H],1, [Define to 1 if error.h is useable.])
+        AC_MSG_RESULT([yes])],
+        AC_MSG_RESULT([no]))
+AC_LANG_POP([C])
+CFLAGS=$SAVED_CFLAGS
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_CHECK_TYPES([ptrdiff_t])
+AC_C_BIGENDIAN
+
+# Checks for library functions.
+AC_FUNC_ERROR_AT_LINE
+AC_FUNC_STRTOD
+AC_CHECK_FUNCS([strcspn strtol getline])
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+
+echo
+echo "Is case-sensitive searching enabled ?     ${HAVE_POPPLER_FIND_OPTS}"
+echo "Is modifying text annotations enabled ?   ${HAVE_POPPLER_ANNOT_WRITE}"
+echo "Is modifying markup annotations enabled ? ${HAVE_POPPLER_ANNOT_MARKUP}"
+echo
diff --git a/elpa/pdf-tools-20191031.642/build/server/epdfinfo.c b/elpa/pdf-tools-20191031.642/build/server/epdfinfo.c
new file mode 100644 (file)
index 0000000..86d9200
--- /dev/null
@@ -0,0 +1,3718 @@
+/* Copyright (C) 2013, 2014  Andreas Politz
+ *
+ * Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <assert.h>
+#ifdef HAVE_ERR_H
+#  include <err.h>
+#endif
+#ifdef HAVE_ERROR_H
+#  include <error.h>
+#endif
+#include <glib.h>
+#include <poppler.h>
+#include <cairo.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <png.h>
+#include <math.h>
+#include <regex.h>
+#include "synctex_parser.h"
+#include "epdfinfo.h"
+
+\f
+/* ================================================================== *
+ * Helper Functions
+ * ================================================================== */
+
+#ifndef HAVE_ERR_H
+/**
+ * Print error message and quit.
+ *
+ * @param eval Return code
+ * @param fmt Formatting string
+ */
+static void
+err(int eval, const char *fmt, ...)
+{
+  va_list args;
+
+  fprintf (stderr, "epdfinfo: ");
+  if (fmt != NULL)
+    {
+      va_start (args, fmt);
+      vfprintf (stderr, fmt, args);
+      va_end (args);
+      fprintf (stderr, ": %s\n", strerror(errno));
+    }
+  else
+    {
+      fprintf (stderr, "\n");
+    }
+
+  fflush (stderr);
+  exit (eval);
+}
+#endif
+
+#ifndef HAVE_GETLINE
+/**
+ * Read one line from a file.
+ *
+ * @param lineptr Pointer to malloc() allocated buffer
+ * @param n Pointer to size of buffer
+ * @param stream File pointer to read from
+ */
+static ssize_t
+getline(char **lineptr, size_t *n, FILE *stream)
+{
+  size_t len = 0;
+  int ch;
+
+  if ((lineptr == NULL) || (n == NULL))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (*lineptr == NULL)
+    {
+      *lineptr = malloc (128);
+      *n = 128;
+    }
+
+  while ((ch = fgetc (stream)) != EOF)
+    {
+      (*lineptr)[len] = ch;
+
+      if (++len >= *n)
+        {
+          *n += 128;
+          *lineptr = realloc (*lineptr, *n);
+        }
+
+      if (ch == '\n')
+        break;
+    }
+  (*lineptr)[len] = '\0';
+
+  if (!len)
+    {
+      len = -1;
+    }
+
+  return len;
+}
+#endif
+
+/**
+ * Free a list of command arguments.
+ *
+ * @param args An array of command arguments.
+ * @param n The length of the array.
+ */
+static void
+free_command_args (command_arg_t *args, size_t n)
+{
+  if (! args)
+    return;
+
+  g_free (args);
+}
+
+/**
+ * Free resources held by document.
+ *
+ * @param doc The document to be freed.
+ */
+static void
+free_document (document_t *doc)
+{
+  if (! doc)
+    return;
+
+  g_free (doc->filename);
+  g_free (doc->passwd);
+  if (doc->annotations.pages)
+    {
+      int npages = poppler_document_get_n_pages (doc->pdf);
+      int i;
+      for (i = 0; i < npages; ++i)
+        {
+          GList *item;
+          GList *annots  = doc->annotations.pages[i];
+          for (item = annots; item; item = item->next)
+            {
+              annotation_t *a = (annotation_t*) item->data;
+              poppler_annot_mapping_free(a->amap);
+              g_free (a->key);
+              g_free (a);
+            }
+          g_list_free (annots);
+        }
+      g_hash_table_destroy (doc->annotations.keys);
+      g_free (doc->annotations.pages);
+    }
+  g_object_unref (doc->pdf);
+  g_free (doc);
+}
+
+/**
+ * Parse a list of whitespace separated double values.
+ *
+ * @param str The input string.
+ * @param values[out] Values are put here.
+ * @param nvalues How many values to parse.
+ *
+ * @return TRUE, if str contained exactly nvalues, else FALSE.
+ */
+static gboolean
+parse_double_list (const char *str, gdouble *values, size_t nvalues)
+{
+  char *end;
+  int i;
+
+  if (! str)
+    return FALSE;
+
+  errno = 0;
+  for (i = 0; i < nvalues; ++i)
+    {
+      gdouble n = g_ascii_strtod (str, &end);
+
+      if (str == end || errno)
+        return FALSE;
+
+      values[i] = n;
+      str = end;
+    }
+
+  if (*end)
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+parse_rectangle (const char *str, PopplerRectangle *r)
+{
+  gdouble values[4];
+
+  if (! r)
+    return FALSE;
+
+  if (! parse_double_list (str, values, 4))
+    return FALSE;
+
+  r->x1 = values[0];
+  r->y1 = values[1];
+  r->x2 = values[2];
+  r->y2 = values[3];
+
+  return TRUE;
+}
+
+static gboolean
+parse_edges_or_position (const char *str, PopplerRectangle *r)
+{
+  return (parse_rectangle (str, r)
+          && r->x1 >= 0 && r->x1 <= 1
+          && r->x2 <= 1
+          && r->y1 >= 0 && r->y1 <= 1
+          && r->y2 <= 1);
+}
+
+static gboolean
+parse_edges (const char *str, PopplerRectangle *r)
+{
+  return (parse_rectangle (str, r)
+          && r->x1 >= 0 && r->x1 <= 1
+          && r->x2 >= 0 && r->x2 <= 1
+          && r->y1 >= 0 && r->y1 <= 1
+          && r->y2 >= 0 && r->y2 <= 1);
+}
+
+/**
+ * Print a string properly escaped for a response.
+ *
+ * @param str The string to be printed.
+ * @param suffix_char Append a newline if NEWLINE, a colon if COLON.
+ */
+static void
+print_response_string (const char *str, enum suffix_char suffix)
+{
+  if (str)
+    {
+      while (*str)
+        {
+          switch (*str)
+            {
+            case '\n':
+              printf ("\\n");
+              break;
+            case '\\':
+              printf ("\\\\");
+              break;
+            case ':':
+              printf ("\\:");
+              break;
+            default:
+              putchar (*str);
+            }
+          ++str;
+        }
+    }
+
+  switch (suffix)
+    {
+    case NEWLINE:
+      putchar ('\n');
+      break;
+    case COLON:
+      putchar (':');
+      break;
+    default: ;
+    }
+}
+
+
+/**
+ * Print a formatted error response.
+ *
+ * @param fmt The printf-like format string.
+ */
+static void
+printf_error_response (const char *fmt, ...)
+{
+  va_list va;
+  puts ("ERR");
+  va_start (va, fmt);
+  vprintf (fmt, va);
+  va_end (va);
+  puts ("\n.");
+  fflush (stdout);
+}
+
+/**
+ * Remove one trailing newline character.  Does nothing, if str does
+ * not end with a newline.
+ *
+ * @param str The string.
+ *
+ * @return str with trailing newline removed.
+ */
+static char*
+strchomp (char *str)
+{
+  size_t length;
+
+  if (! str)
+    return str;
+
+  length = strlen (str);
+  if (str[length - 1] == '\n')
+    str[length - 1] = '\0';
+
+  return str;
+}
+
+/**
+ * Create a new, temporary file and returns it's name.
+ *
+ * @return The filename.
+ */
+static char*
+mktempfile()
+{
+  char *filename = NULL;
+  int tries = 3;
+  while (! filename && tries-- > 0)
+    {
+
+      filename =  tempnam(NULL, "epdfinfo");
+      if (filename)
+        {
+          int fd = open(filename, O_CREAT | O_EXCL | O_RDONLY, S_IRWXU);
+          if (fd > 0)
+            close (fd);
+          else
+            {
+              free (filename);
+              filename = NULL;
+            }
+        }
+    }
+  if (! filename)
+    fprintf (stderr, "Unable to create tempfile");
+
+  return filename;
+}
+
+static void
+image_recolor (cairo_surface_t * surface, const PopplerColor * fg,
+              const PopplerColor * bg)
+{
+  /* uses a representation of a rgb color as follows:
+     - a lightness scalar (between 0,1), which is a weighted average of r, g, b,
+     - a hue vector, which indicates a radian direction from the grey axis,
+     inside the equal lightness plane.
+     - a saturation scalar between 0,1. It is 0 when grey, 1 when the color is
+     in the boundary of the rgb cube.
+   */
+
+  const unsigned int page_width = cairo_image_surface_get_width (surface);
+  const unsigned int page_height = cairo_image_surface_get_height (surface);
+  const int rowstride = cairo_image_surface_get_stride (surface);
+  unsigned char *image = cairo_image_surface_get_data (surface);
+
+  /* RGB weights for computing lightness. Must sum to one */
+  static const double a[] = { 0.30, 0.59, 0.11 };
+
+  const double f = 65535.;
+  const double rgb_fg[] = {
+    fg->red / f, fg->green / f, fg->blue / f
+  };
+  const double rgb_bg[] = {
+    bg->red / f, bg->green / f, bg->blue / f
+  };
+
+  const double rgb_diff[] = {
+    rgb_bg[0] - rgb_fg[0],
+    rgb_bg[1] - rgb_fg[1],
+    rgb_bg[2] - rgb_fg[2]
+  };
+
+  unsigned int y;
+  for (y = 0; y < page_height * rowstride; y += rowstride)
+    {
+      unsigned char *data = image + y;
+
+      unsigned int x;
+      for (x = 0; x < page_width; x++, data += 4)
+       {
+         /* Careful. data color components blue, green, red. */
+         const double rgb[3] = {
+           (double) data[2] / 256.,
+           (double) data[1] / 256.,
+           (double) data[0] / 256.
+         };
+
+         /* compute h, s, l data   */
+         double l = a[0] * rgb[0] + a[1] * rgb[1] + a[2] * rgb[2];
+
+         /* linear interpolation between dark and light with color ligtness as
+          * a parameter */
+         data[2] =
+           (unsigned char) round (255. * (l * rgb_diff[0] + rgb_fg[0]));
+         data[1] =
+           (unsigned char) round (255. * (l * rgb_diff[1] + rgb_fg[1]));
+         data[0] =
+           (unsigned char) round (255. * (l * rgb_diff[2] + rgb_fg[2]));
+       }
+    }
+}
+
+/**
+ * Render a PDF page.
+ *
+ * @param pdf The PDF document.
+ * @param page The page to be rendered.
+ * @param width The desired width of the image.
+ *
+ * @return A cairo_t context encapsulating the rendered image, or
+ *         NULL, if rendering failed for some reason.
+ */
+static cairo_surface_t*
+image_render_page(PopplerDocument *pdf, PopplerPage *page,
+                  int width, gboolean do_render_annotaions,
+                  const render_options_t *options)
+{
+  cairo_t *cr = NULL;
+  cairo_surface_t *surface = NULL;
+  double pt_width, pt_height;
+  int height;
+  double scale = 1;
+
+  if (! page || ! pdf)
+    return NULL;
+
+  if (width < 1)
+    width = 1;
+
+  poppler_page_get_size (page, &pt_width, &pt_height);
+  scale = width / pt_width;
+  height = (int) ((scale * pt_height) + 0.5);
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        width, height);
+
+  if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
+    {
+      fprintf (stderr, "Failed to create cairo surface\n");
+      goto error;
+    }
+
+  cr = cairo_create (surface);
+  if (cairo_status(cr) != CAIRO_STATUS_SUCCESS)
+    {
+      fprintf (stderr, "Failed to create cairo handle\n");
+      goto error;
+    }
+
+  cairo_translate (cr, 0, 0);
+  cairo_scale (cr, scale, scale);
+  /* Render w/o annotations. */
+  if (! do_render_annotaions || (options && options->printed))
+    poppler_page_render_for_printing_with_options
+      (page, cr, POPPLER_PRINT_DOCUMENT);
+  else
+    poppler_page_render (page, cr) ;
+  if (cairo_status(cr) != CAIRO_STATUS_SUCCESS)
+    {
+      fprintf (stderr, "Failed to render page\n");
+      goto error;
+    }
+
+  /* This makes the colors look right. */
+  cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
+  cairo_set_source_rgb (cr, 1., 1., 1.);
+
+  cairo_paint (cr);
+
+  if (options && options->usecolors)
+    image_recolor (surface, &options->fg, &options->bg);
+
+  cairo_destroy (cr);
+
+  return surface;
+
+ error:
+  if (surface != NULL)
+    cairo_surface_destroy (surface);
+  if (cr != NULL)
+    cairo_destroy (cr);
+  return NULL;
+}
+
+/**
+ * Write an image to a filename.
+ *
+ * @param cr The cairo context encapsulating the image.
+ * @param filename The filename to be written to.
+ * @param type The desired image type.
+ *
+ * @return 1 if the image was written successfully, else 0.
+ */
+static gboolean
+image_write (cairo_surface_t *surface, const char *filename, enum image_type type)
+{
+
+  int i, j;
+  unsigned char *data;
+  int width, height;
+  FILE *file = NULL;
+  gboolean success = 0;
+
+  if (! surface ||
+      cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
+    {
+      fprintf (stderr, "Invalid cairo surface\n");
+      return 0;
+    }
+
+  if (! (file = fopen (filename, "wb")))
+    {
+      fprintf (stderr, "Can not open file: %s\n", filename);
+      return 0;
+    }
+
+  cairo_surface_flush (surface);
+  width = cairo_image_surface_get_width (surface);
+  height = cairo_image_surface_get_height (surface);
+  data = cairo_image_surface_get_data (surface);
+
+  switch (type)
+    {
+    case PPM:
+      {
+        unsigned char *buffer = g_malloc (width * height * 3);
+        unsigned char *buffer_p = buffer;
+
+        fprintf (file, "P6\n%d %d\n255\n", width, height);
+        for (i = 0; i < width * height; ++i, data += 4, buffer_p += 3)
+          ARGB_TO_RGB (buffer_p, data);
+        fwrite (buffer, 1, width * height * 3, file);
+        g_free (buffer);
+        success = 1;
+      }
+      break;
+    case PNG:
+      {
+        png_infop info_ptr = NULL;
+        png_structp png_ptr = NULL;
+        unsigned char *row = NULL;
+
+        png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+        if (!png_ptr)
+          goto finalize;
+
+        info_ptr = png_create_info_struct(png_ptr);
+        if (!info_ptr)
+          goto finalize;
+
+        if (setjmp(png_jmpbuf(png_ptr)))
+          goto finalize;
+
+        png_init_io (png_ptr, file);
+        png_set_compression_level (png_ptr, 1);
+        png_set_IHDR (png_ptr, info_ptr, width, height,
+                      8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+                      PNG_COMPRESSION_TYPE_BASE,
+                      PNG_FILTER_TYPE_DEFAULT);
+
+        png_set_filter (png_ptr, PNG_FILTER_TYPE_BASE,
+                        PNG_FILTER_NONE);
+        png_write_info (png_ptr, info_ptr);
+        row = g_malloc (3 * width);
+        for (i = 0; i < height; ++i)
+          {
+            unsigned char *row_p = row;
+            for (j = 0; j < width; ++j, data += 4, row_p += 3)
+              {
+                ARGB_TO_RGB (row_p, data);
+              }
+            png_write_row (png_ptr, row);
+          }
+        png_write_end (png_ptr, NULL);
+        success = 1;
+      finalize:
+        if (png_ptr)
+          png_destroy_write_struct (&png_ptr, &info_ptr);
+        if (row)
+          g_free (row);
+        if (! success)
+          fprintf (stderr, "Error writing png data\n");
+      }
+      break;
+    default:
+      internal_error ("switch fell through");
+    }
+
+  fclose (file);
+  return success;
+}
+
+static void
+image_write_print_response(cairo_surface_t *surface, enum image_type type)
+{
+  char *filename = mktempfile ();
+
+  perror_if_not (filename, "Unable to create temporary file");
+  if (image_write (surface, filename, type))
+    {
+      OK_BEGIN ();
+      print_response_string (filename, NEWLINE);
+      OK_END ();
+    }
+  else
+    {
+      printf_error_response ("Unable to write image");
+    }
+  free (filename);
+ error:
+  return;
+}
+
+static void
+region_print (cairo_region_t *region, double width, double height)
+{
+  int i;
+
+  for (i = 0; i < cairo_region_num_rectangles (region); ++i)
+    {
+      cairo_rectangle_int_t r;
+
+      cairo_region_get_rectangle (region, i, &r);
+      printf ("%f %f %f %f",
+              r.x / width,
+              r.y / height,
+              (r.x + r.width) / width,
+              (r.y + r.height) / height);
+      if (i < cairo_region_num_rectangles (region) - 1)
+        putchar (':');
+    }
+  if (0 == cairo_region_num_rectangles (region))
+    printf ("0.0 0.0 0.0 0.0");
+}
+
+/**
+ * Return a string representation of a PopplerActionType.
+ *
+ * @param type The PopplerActionType.
+ *
+ * @return It's string representation.
+ */
+static const char *
+xpoppler_action_type_string(PopplerActionType type)
+{
+  switch (type)
+    {
+    case POPPLER_ACTION_UNKNOWN: return "unknown";
+    case POPPLER_ACTION_NONE: return "none";
+    case POPPLER_ACTION_GOTO_DEST: return "goto-dest";
+    case POPPLER_ACTION_GOTO_REMOTE: return "goto-remote";
+    case POPPLER_ACTION_LAUNCH: return "launch";
+    case POPPLER_ACTION_URI: return "uri";
+    case POPPLER_ACTION_NAMED: return "goto-dest"; /* actually "named" */
+    case POPPLER_ACTION_MOVIE: return "movie";
+    case POPPLER_ACTION_RENDITION: return "rendition";
+    case POPPLER_ACTION_OCG_STATE: return "ocg-state";
+    case POPPLER_ACTION_JAVASCRIPT: return "javascript";
+    default: return "invalid";
+    }
+}
+
+/**
+ * Return a string representation of a PopplerAnnotType.
+ *
+ * @param type The PopplerAnnotType.
+ *
+ * @return It's string representation.
+ */
+static const char *
+xpoppler_annot_type_string (PopplerAnnotType type)
+{
+  switch (type)
+    {
+    case POPPLER_ANNOT_UNKNOWN: return "unknown";
+    case POPPLER_ANNOT_TEXT: return "text";
+    case POPPLER_ANNOT_LINK: return "link";
+    case POPPLER_ANNOT_FREE_TEXT: return "free-text";
+    case POPPLER_ANNOT_LINE: return "line";
+    case POPPLER_ANNOT_SQUARE: return "square";
+    case POPPLER_ANNOT_CIRCLE: return "circle";
+    case POPPLER_ANNOT_POLYGON: return "polygon";
+    case POPPLER_ANNOT_POLY_LINE: return "poly-line";
+    case POPPLER_ANNOT_HIGHLIGHT: return "highlight";
+    case POPPLER_ANNOT_UNDERLINE: return "underline";
+    case POPPLER_ANNOT_SQUIGGLY: return "squiggly";
+    case POPPLER_ANNOT_STRIKE_OUT: return "strike-out";
+    case POPPLER_ANNOT_STAMP: return "stamp";
+    case POPPLER_ANNOT_CARET: return "caret";
+    case POPPLER_ANNOT_INK: return "ink";
+    case POPPLER_ANNOT_POPUP: return "popup";
+    case POPPLER_ANNOT_FILE_ATTACHMENT: return "file";
+    case POPPLER_ANNOT_SOUND: return "sound";
+    case POPPLER_ANNOT_MOVIE: return "movie";
+    case POPPLER_ANNOT_WIDGET: return "widget";
+    case POPPLER_ANNOT_SCREEN: return "screen";
+    case POPPLER_ANNOT_PRINTER_MARK: return "printer-mark";
+    case POPPLER_ANNOT_TRAP_NET: return "trap-net";
+    case POPPLER_ANNOT_WATERMARK: return "watermark";
+    case POPPLER_ANNOT_3D: return "3d";
+    default: return "invalid";
+    }
+}
+
+/**
+ * Return a string representation of a PopplerAnnotTextState.
+ *
+ * @param type The PopplerAnnotTextState.
+ *
+ * @return It's string representation.
+ */
+static const char *
+xpoppler_annot_text_state_string (PopplerAnnotTextState state)
+{
+  switch (state)
+    {
+    case POPPLER_ANNOT_TEXT_STATE_MARKED: return "marked";
+    case POPPLER_ANNOT_TEXT_STATE_UNMARKED: return "unmarked";
+    case POPPLER_ANNOT_TEXT_STATE_ACCEPTED: return "accepted";
+    case POPPLER_ANNOT_TEXT_STATE_REJECTED: return "rejected";
+    case POPPLER_ANNOT_TEXT_STATE_CANCELLED: return "cancelled";
+    case POPPLER_ANNOT_TEXT_STATE_COMPLETED: return "completed";
+    case POPPLER_ANNOT_TEXT_STATE_NONE: return "none";
+    case POPPLER_ANNOT_TEXT_STATE_UNKNOWN:
+    default: return "unknown";
+    }
+};
+
+static document_t*
+document_open (const epdfinfo_t *ctx, const char *filename,
+               const char *passwd, GError **gerror)
+{
+  char *uri;
+  document_t *doc = g_hash_table_lookup (ctx->documents, filename);
+
+  if (NULL != doc)
+    return doc;
+
+  doc = g_malloc0(sizeof (document_t));
+  uri = g_filename_to_uri (filename, NULL, gerror);
+  if (uri != NULL)
+    doc->pdf = poppler_document_new_from_file(uri, passwd, gerror);
+
+  if (NULL == doc->pdf)
+    {
+      g_free (doc);
+      doc = NULL;
+    }
+  else
+    {
+      doc->filename = g_strdup (filename);
+      doc->passwd = g_strdup (passwd);
+      g_hash_table_insert (ctx->documents, doc->filename, doc);
+    }
+  g_free (uri);
+  return doc;
+}
+
+/**
+ * Split command args into a list of strings.
+ *
+ * @param args The colon separated list of arguments.
+ * @param nargs[out] The number of returned arguments.
+ *
+ * @return The list of arguments, which should be freed by the caller.
+ */
+static char **
+command_arg_split (const char *args, int *nargs)
+{
+  char **list = g_malloc (sizeof (char*) * 16);
+  int i = 0;
+  size_t allocated = 16;
+  char *buffer = NULL;
+  gboolean last = FALSE;
+
+  if (! args)
+    goto theend;
+
+  buffer = g_malloc (strlen (args) + 1);
+
+  while (*args || last)
+    {
+      gboolean esc = FALSE;
+      char *buffer_p = buffer;
+
+      while (*args && (*args != ':' || esc))
+        {
+          if (esc)
+            {
+              if (*args == 'n')
+                {
+                  ++args;
+                  *buffer_p++ = '\n';
+                }
+              else
+                {
+                  *buffer_p++ = *args++;
+                }
+              esc = FALSE;
+            }
+          else if (*args == '\\')
+            {
+              ++args;
+              esc = TRUE;
+            }
+          else
+            {
+              *buffer_p++ = *args++;
+            }
+        }
+
+      *buffer_p = '\0';
+
+      if (i >= allocated)
+        {
+          allocated = 2 * allocated + 1;
+          list = g_realloc (list, sizeof (char*) * allocated);
+        }
+      list[i++] = g_strdup (buffer);
+
+      last = FALSE;
+      if (*args)
+        {
+          ++args;
+          if (! *args)
+            last = TRUE;
+        }
+    }
+
+ theend:
+  g_free (buffer);
+  *nargs = i;
+
+  return list;
+}
+
+static gboolean
+command_arg_parse_arg (const epdfinfo_t *ctx, const char *arg,
+                       command_arg_t *cmd_arg, command_arg_type_t type,
+                       gchar **error_msg)
+{
+  GError *gerror = NULL;
+
+  if (! arg || !cmd_arg)
+    return FALSE;
+
+  switch (type)
+    {
+    case ARG_DOC:
+      {
+        document_t *doc = document_open (ctx, arg, NULL, &gerror);
+        cerror_if_not (doc, error_msg,
+                       "Error opening %s:%s", arg,
+                       gerror ? gerror->message : "Unknown reason");
+
+        cmd_arg->value.doc = doc;
+        break;
+      }
+    case ARG_BOOL:
+      cerror_if_not (! strcmp (arg, "0") || ! strcmp (arg, "1"),
+                     error_msg, "Expected 0 or 1:%s", arg);
+      cmd_arg->value.flag = *arg == '1';
+      break;
+    case ARG_NONEMPTY_STRING:
+      cerror_if_not (*arg, error_msg, "Non-empty string expected");
+      /* fall through */
+    case ARG_STRING:
+      cmd_arg->value.string = arg;
+      break;
+    case ARG_NATNUM:
+      {
+        char *endptr;
+        long n = strtol (arg, &endptr, 0);
+        cerror_if_not (! (*endptr || (n < 0)), error_msg,
+                       "Expected natural number:%s", arg);
+        cmd_arg->value.natnum = n;
+      }
+      break;
+    case ARG_EDGES_OR_POSITION:
+      {
+        PopplerRectangle *r = &cmd_arg->value.rectangle;
+        cerror_if_not (parse_edges_or_position (arg, r),
+                       error_msg,
+                       "Expected a relative position or rectangle: %s", arg);
+      }
+      break;
+    case ARG_EDGES:
+      {
+        PopplerRectangle *r = &cmd_arg->value.rectangle;
+        cerror_if_not (parse_edges (arg, r),
+                       error_msg,
+                       "Expected a relative rectangle: %s", arg);
+      }
+      break;
+    case ARG_EDGE_OR_NEGATIVE:
+    case ARG_EDGE:
+      {
+        char *endptr;
+        double n = strtod (arg, &endptr);
+        cerror_if_not (! (*endptr || (type != ARG_EDGE_OR_NEGATIVE && n < 0.0) || n > 1.0),
+                       error_msg, "Expected a relative edge: %s", arg);
+        cmd_arg->value.edge = n;
+      }
+      break;
+    case ARG_COLOR:
+      {
+        guint r,g,b;
+        cerror_if_not ((strlen (arg) == 7
+                        && 3 == sscanf (arg, "#%2x%2x%2x", &r, &g, &b)),
+                       error_msg, "Invalid color: %s", arg);
+        cmd_arg->value.color.red = r << 8;
+        cmd_arg->value.color.green = g << 8;
+        cmd_arg->value.color.blue = b << 8;
+      }
+      break;
+    case ARG_INVALID:
+    default:
+      internal_error ("switch fell through");
+    }
+
+  cmd_arg->type = type;
+
+  return TRUE;
+ error:
+  if (gerror)
+    {
+      g_error_free (gerror);
+      gerror = NULL;
+    }
+  return FALSE;
+}
+
+/**
+ * Parse arguments for a command.
+ *
+ * @param ctx The epdfinfo context.
+ * @param args A string holding the arguments.  This is either empty
+ *             or the suffix of the command starting at the first
+ *             colon after the command name.
+ * @param len The length of args.
+ * @param cmd The command for which the arguments should be parsed.
+ *
+ * @return
+ */
+static command_arg_t*
+command_arg_parse(epdfinfo_t *ctx, char **args, int nargs,
+                  const command_t *cmd, gchar **error_msg)
+{
+  command_arg_t *cmd_args = g_malloc0 (cmd->nargs * sizeof (command_arg_t));
+  int i;
+
+  if (nargs < cmd->nargs - 1
+      || (nargs == cmd->nargs - 1
+          &&  cmd->args_spec[cmd->nargs - 1] != ARG_REST)
+      || (nargs > cmd->nargs
+          && (cmd->nargs == 0
+              || cmd->args_spec[cmd->nargs - 1] != ARG_REST)))
+    {
+      if (error_msg)
+        {
+          *error_msg =
+            g_strdup_printf ("Command `%s' expects %d argument(s), %d given",
+                             cmd->name, cmd->nargs, nargs);
+        }
+      goto failure;
+    }
+
+  for (i = 0; i < cmd->nargs; ++i)
+    {
+      if (i == cmd->nargs - 1 && cmd->args_spec[i] == ARG_REST)
+        {
+          cmd_args[i].value.rest.args = args + i;
+          cmd_args[i].value.rest.nargs = nargs - i;
+          cmd_args[i].type = ARG_REST;
+        }
+      else if (i >= nargs
+               || ! command_arg_parse_arg (ctx, args[i], cmd_args + i,
+                                           cmd->args_spec[i], error_msg))
+        {
+          goto failure;
+        }
+    }
+
+  return cmd_args;
+
+ failure:
+  free_command_args (cmd_args, cmd->nargs);
+  return NULL;
+}
+
+static void
+command_arg_print(const command_arg_t *arg)
+{
+  switch (arg->type)
+    {
+    case ARG_INVALID:
+      printf ("[invalid]");
+      break;
+    case ARG_DOC:
+      print_response_string (arg->value.doc->filename, NONE);
+      break;
+    case ARG_BOOL:
+      printf ("%d", arg->value.flag ? 1 : 0);
+      break;
+    case ARG_NONEMPTY_STRING:   /* fall */
+    case ARG_STRING:
+      print_response_string (arg->value.string, NONE);
+      break;
+    case ARG_NATNUM:
+      printf ("%ld", arg->value.natnum);
+      break;
+    case ARG_EDGE_OR_NEGATIVE:  /* fall */
+    case ARG_EDGE:
+      printf ("%f", arg->value.edge);
+      break;
+    case ARG_EDGES_OR_POSITION: /* fall */
+    case ARG_EDGES:
+      {
+        const PopplerRectangle *r = &arg->value.rectangle;
+        if (r->x2 < 0 && r->y2 < 0)
+          printf ("%f %f", r->x1, r->y1);
+        else
+          printf ("%f %f %f %f", r->x1, r->y1, r->x2, r->y2);
+        break;
+      }
+    case ARG_COLOR:
+      {
+        const PopplerColor *c = &arg->value.color;
+        printf ("#%.2x%.2x%.2x", c->red >> 8,
+                c->green >> 8, c->blue >> 8);
+        break;
+      }
+    case ARG_REST:
+      {
+        int i;
+        for (i = 0; i < arg->value.rest.nargs; ++i)
+          print_response_string (arg->value.rest.args[i], COLON);
+        if (arg->value.rest.nargs > 0)
+          print_response_string (arg->value.rest.args[i], NONE);
+        break;
+      }
+    default:
+      internal_error ("switch fell through");
+    }
+}
+
+static size_t
+command_arg_type_size(command_arg_type_t type)
+{
+  command_arg_t arg;
+  switch (type)
+    {
+    case ARG_INVALID: return 0;
+    case ARG_DOC: return sizeof (arg.value.doc);
+    case ARG_BOOL: return sizeof (arg.value.flag);
+    case ARG_NONEMPTY_STRING:   /* fall */
+    case ARG_STRING: return sizeof (arg.value.string);
+    case ARG_NATNUM: return sizeof (arg.value.natnum);
+    case ARG_EDGE_OR_NEGATIVE:  /* fall */
+    case ARG_EDGE: return sizeof (arg.value.edge);
+    case ARG_EDGES_OR_POSITION: /* fall */
+    case ARG_EDGES: return sizeof (arg.value.rectangle);
+    case ARG_COLOR: return sizeof (arg.value.color);
+    case ARG_REST: return sizeof (arg.value.rest);
+    default:
+      internal_error ("switch fell through");
+      return 0;
+    }
+}
+
+\f
+/* ------------------------------------------------------------------ *
+ * PDF Actions
+ * ------------------------------------------------------------------ */
+
+static gboolean
+action_is_handled (PopplerAction *action)
+{
+  if (! action)
+    return FALSE;
+
+  switch (action->any.type)
+    {
+    case POPPLER_ACTION_GOTO_REMOTE:
+    case POPPLER_ACTION_GOTO_DEST:
+    case POPPLER_ACTION_NAMED:
+      /* case POPPLER_ACTION_LAUNCH: */
+    case POPPLER_ACTION_URI:
+      return TRUE;
+    default: ;
+    }
+  return FALSE;
+}
+
+static void
+action_print_destination (PopplerDocument *doc, PopplerAction *action)
+{
+  PopplerDest *dest = NULL;
+  gboolean free_dest = FALSE;
+  double width, height, top;
+  PopplerPage *page;
+  int saved_stdin;
+
+  if (action->any.type == POPPLER_ACTION_GOTO_DEST
+      && action->goto_dest.dest->type == POPPLER_DEST_NAMED)
+    {
+      DISCARD_STDOUT (saved_stdin);
+      /* poppler_document_find_dest reports errors to stdout, so
+         discard them. */
+      dest = poppler_document_find_dest
+        (doc, action->goto_dest.dest->named_dest);
+      UNDISCARD_STDOUT (saved_stdin);
+      free_dest = TRUE;
+    }
+  else if (action->any.type == POPPLER_ACTION_NAMED)
+
+    {
+      DISCARD_STDOUT (saved_stdin);
+      dest = poppler_document_find_dest (doc, action->named.named_dest);
+      UNDISCARD_STDOUT (saved_stdin);
+      free_dest = TRUE;
+    }
+
+  else if (action->any.type == POPPLER_ACTION_GOTO_REMOTE)
+    {
+      print_response_string (action->goto_remote.file_name, COLON);
+      dest = action->goto_remote.dest;
+    }
+  else if (action->any.type == POPPLER_ACTION_GOTO_DEST)
+    dest = action->goto_dest.dest;
+
+  if (!dest
+      || dest->type == POPPLER_DEST_UNKNOWN
+      || dest->page_num < 1
+      || dest->page_num > poppler_document_get_n_pages (doc))
+    {
+      printf (":");
+      goto theend;
+    }
+
+  printf ("%d:", dest->page_num);
+
+  if (action->type == POPPLER_ACTION_GOTO_REMOTE
+      || NULL == (page = poppler_document_get_page (doc, dest->page_num - 1)))
+    {
+      goto theend;
+    }
+
+  poppler_page_get_size (page, &width, &height);
+  g_object_unref (page);
+  top = (height - dest->top) / height;
+
+  /* adapted from xpdf */
+  switch (dest->type)
+    {
+    case POPPLER_DEST_XYZ:
+      if (dest->change_top)
+        printf ("%f", top);
+      break;
+    case POPPLER_DEST_FIT:
+    case POPPLER_DEST_FITB:
+    case POPPLER_DEST_FITH:
+    case POPPLER_DEST_FITBH:
+      putchar ('0');
+      break;
+    case POPPLER_DEST_FITV:
+    case POPPLER_DEST_FITBV:
+    case POPPLER_DEST_FITR:
+      printf ("%f", top);
+      break;
+    default: ;
+    }
+
+ theend:
+  if (free_dest)
+    poppler_dest_free (dest);
+}
+
+static void
+action_print (PopplerDocument *doc, PopplerAction *action)
+{
+  if (! action_is_handled (action))
+    return;
+
+  print_response_string (xpoppler_action_type_string (action->any.type), COLON);
+  print_response_string (action->any.title, COLON);
+  switch (action->any.type)
+    {
+    case POPPLER_ACTION_GOTO_REMOTE:
+    case POPPLER_ACTION_GOTO_DEST:
+    case POPPLER_ACTION_NAMED:
+      action_print_destination (doc, action);
+      putchar ('\n');
+      break;
+    case POPPLER_ACTION_LAUNCH:
+      print_response_string (action->launch.file_name, COLON);
+      print_response_string (action->launch.params, NEWLINE);
+      break;
+    case POPPLER_ACTION_URI:
+      print_response_string (action->uri.uri, NEWLINE);
+      break;
+    default:
+      ;
+    }
+}
+
+\f
+/* ------------------------------------------------------------------ *
+ * PDF Annotations and Attachments
+ * ------------------------------------------------------------------ */
+
+/* static gint
+ * annotation_cmp_edges (const annotation_t *a1, const annotation_t *a2)
+ * {
+ *   PopplerRectangle *e1 = &a1->amap->area;
+ *   PopplerRectangle *e2 = &a2->amap->area;
+ *
+ *   return (e1->y1 > e2->y1 ? -1
+ *           : e1->y1 < e2->y1 ? 1
+ *           : e1->x1 < e2->x1 ? -1
+ *           : e1->x1 != e2->x1);
+ * } */
+
+static GList*
+annoation_get_for_page (document_t *doc, gint pn)
+{
+
+  GList *annot_list, *item;
+  PopplerPage *page;
+  gint i = 0;
+  gint npages = poppler_document_get_n_pages (doc->pdf);
+
+  if (pn < 1 || pn > npages)
+    return NULL;
+
+  if (! doc->annotations.pages)
+    doc->annotations.pages = g_malloc0 (npages * sizeof(GList*));
+
+  if (doc->annotations.pages[pn - 1])
+    return doc->annotations.pages[pn - 1];
+
+  if (! doc->annotations.keys)
+    doc->annotations.keys = g_hash_table_new (g_str_hash, g_str_equal);
+
+  page = poppler_document_get_page (doc->pdf, pn - 1);
+  if (NULL == page)
+    return NULL;
+
+  annot_list = poppler_page_get_annot_mapping (page);
+  for (item = annot_list; item; item = item->next)
+    {
+      PopplerAnnotMapping *map = (PopplerAnnotMapping *)item->data;
+      gchar *key = g_strdup_printf ("annot-%d-%d", pn, i);
+      annotation_t *a = g_malloc (sizeof (annotation_t));
+      a->amap = map;
+      a->key = key;
+      doc->annotations.pages[pn - 1] =
+        g_list_prepend (doc->annotations.pages[pn - 1], a);
+      assert (NULL == g_hash_table_lookup (doc->annotations.keys, key));
+      g_hash_table_insert (doc->annotations.keys, key, a);
+      ++i;
+    }
+  g_list_free (annot_list);
+  g_object_unref (page);
+  return doc->annotations.pages[pn - 1];
+}
+
+static annotation_t*
+annotation_get_by_key (document_t *doc, const gchar *key)
+{
+  if (! doc->annotations.keys)
+    return NULL;
+
+  return g_hash_table_lookup (doc->annotations.keys, key);
+}
+
+#ifdef HAVE_POPPLER_ANNOT_MARKUP
+void
+annotation_translate_quadrilateral (PopplerPage *page, PopplerQuadrilateral *q, gboolean inverse)
+{
+  PopplerRectangle cbox;
+  gdouble xs, ys;
+
+  poppler_page_get_crop_box (page, &cbox);
+  xs = MIN (cbox.x1, cbox.x2);
+  ys = MIN (cbox.y1, cbox.y2);
+
+  if (inverse)
+    {
+      xs = -xs; ys = -ys;
+    }
+
+  q->p1.x -= xs, q->p2.x -= xs; q->p3.x -= xs; q->p4.x -= xs;
+  q->p1.y -= ys, q->p2.y -= ys; q->p3.y -= ys; q->p4.y -= ys;
+}
+
+static cairo_region_t*
+annotation_markup_get_text_regions (PopplerPage *page, PopplerAnnotTextMarkup *a)
+{
+  GArray *quads = poppler_annot_text_markup_get_quadrilaterals (a);
+  int i;
+  cairo_region_t *region = cairo_region_create ();
+  gdouble height;
+
+  poppler_page_get_size (page, NULL, &height);
+
+  for (i = 0; i < quads->len; ++i)
+    {
+      PopplerQuadrilateral *q = &g_array_index (quads, PopplerQuadrilateral, i);
+      cairo_rectangle_int_t r;
+
+      annotation_translate_quadrilateral (page, q, FALSE);
+      q->p1.y = height - q->p1.y;
+      q->p2.y = height - q->p2.y;
+      q->p3.y = height - q->p3.y;
+      q->p4.y = height - q->p4.y;
+
+      r.x = (int) (MIN (q->p1.x, MIN (q->p2.x, MIN (q->p3.x, q->p4.x))) + 0.5);
+      r.y = (int) (MIN (q->p1.y, MIN (q->p2.y, MIN (q->p3.y, q->p4.y))) + 0.5);
+      r.width = (int) (MAX (q->p1.x, MAX (q->p2.x, MAX (q->p3.x, q->p4.x))) + 0.5)
+                - r.x;
+      r.height = (int) (MAX (q->p1.y, MAX (q->p2.y, MAX (q->p3.y, q->p4.y))) + 0.5)
+                 - r.y;
+
+      cairo_region_union_rectangle (region, &r);
+    }
+  g_array_unref (quads);
+  return region;
+}
+
+/**
+ * Append quadrilaterals equivalent to region to an array.
+ *
+ * @param page The page of the annotation.  This is used to get the
+ *             text regions and pagesize.
+ * @param region The region to add.
+ * @param garray[in,out] An array of PopplerQuadrilateral, where the
+ *              new quadrilaterals will be appended.
+ */
+static void
+annotation_markup_append_text_region (PopplerPage *page, PopplerRectangle *region,
+                                      GArray *garray)
+{
+  gdouble height;
+  /* poppler_page_get_selection_region is deprecated w/o a
+     replacement.  (poppler_page_get_selected_region returns a union
+     of rectangles.) */
+  GList *regions =
+    poppler_page_get_selection_region (page, 1.0, POPPLER_SELECTION_GLYPH, region);
+  GList *item;
+
+  poppler_page_get_size (page, NULL, &height);
+  for (item = regions; item; item = item->next)
+    {
+      PopplerRectangle *r = item->data;
+      PopplerQuadrilateral q;
+
+      q.p1.x = r->x1;
+      q.p1.y = height - r->y1;
+      q.p2.x = r->x2;
+      q.p2.y = height - r->y1;
+      q.p4.x = r->x2;
+      q.p4.y = height - r->y2;
+      q.p3.x = r->x1;
+      q.p3.y = height - r->y2;
+
+      annotation_translate_quadrilateral (page, &q, TRUE);
+      g_array_append_val (garray, q);
+    }
+  g_list_free (regions);
+}
+
+#endif
+/**
+ * Create a new annotation.
+ *
+ * @param doc The document for which to create it.
+ * @param type The type of the annotation.
+ * @param r The rectangle where annotation will end up on the page.
+ *
+ * @return The new annotation, or NULL, if the annotation type is
+ *         not available.
+ */
+static PopplerAnnot*
+annotation_new (const epdfinfo_t *ctx, document_t *doc, PopplerPage *page,
+                const char *type, PopplerRectangle *r,
+                const command_arg_t *rest, char **error_msg)
+{
+
+  PopplerAnnot *a = NULL;
+  int nargs = rest->value.rest.nargs;
+#ifdef HAVE_POPPLER_ANNOT_MARKUP
+  char * const *args = rest->value.rest.args;
+  int i;
+  GArray *garray = NULL;
+  command_arg_t carg;
+  double width, height;
+  cairo_region_t *region = NULL;
+#endif
+
+  if (! strcmp (type, "text"))
+    {
+      cerror_if_not (nargs == 0, error_msg, "%s", "Too many arguments");
+      return poppler_annot_text_new (doc->pdf, r);
+    }
+
+#ifdef HAVE_POPPLER_ANNOT_MARKUP
+  garray = g_array_new (FALSE, FALSE, sizeof (PopplerQuadrilateral));
+  poppler_page_get_size (page, &width, &height);
+  for (i = 0; i < nargs; ++i)
+    {
+      PopplerRectangle *rr = &carg.value.rectangle;
+
+      error_if_not (command_arg_parse_arg (ctx, args[i], &carg,
+                                           ARG_EDGES, error_msg));
+      rr->x1 *= width; rr->x2 *= width;
+      rr->y1 *= height; rr->y2 *= height;
+      annotation_markup_append_text_region (page, rr, garray);
+    }
+  cerror_if_not (garray->len > 0, error_msg, "%s",
+                 "Unable to create empty markup annotation");
+
+  if (! strcmp (type, "highlight"))
+    a = poppler_annot_text_markup_new_highlight (doc->pdf, r, garray);
+  else if (! strcmp (type, "squiggly"))
+    a = poppler_annot_text_markup_new_squiggly (doc->pdf, r, garray);
+  else if (! strcmp (type, "strike-out"))
+    a = poppler_annot_text_markup_new_strikeout (doc->pdf, r, garray);
+  else if (! strcmp (type, "underline"))
+    a = poppler_annot_text_markup_new_underline (doc->pdf, r, garray);
+  else
+    cerror_if_not (0, error_msg, "Unknown annotation type: %s", type);
+
+#endif
+ error:
+#ifdef HAVE_POPPLER_ANNOT_MARKUP
+  if (garray) g_array_unref (garray);
+  if (region) cairo_region_destroy (region);
+#endif
+  return a;
+}
+
+static gboolean
+annotation_edit_validate (const epdfinfo_t *ctx, const command_arg_t *rest,
+                          PopplerAnnot *annotation, char **error_msg)
+{
+  int nargs = rest->value.rest.nargs;
+  char * const *args = rest->value.rest.args;
+  int i = 0;
+  command_arg_t carg;
+
+  const char* error_fmt =
+    "Can modify `%s' property only for %s annotations";
+
+  while (i < nargs)
+    {
+      command_arg_type_t atype = ARG_INVALID;
+      const char *key = args[i++];
+
+      cerror_if_not (i < nargs, error_msg, "Missing a value argument");
+
+      if (! strcmp (key, "flags"))
+        atype = ARG_NATNUM;
+      else if (! strcmp (key, "color"))
+        atype = ARG_COLOR;
+      else if (! strcmp (key, "contents"))
+        atype = ARG_STRING;
+      else if (! strcmp (key, "edges"))
+        atype = ARG_EDGES_OR_POSITION;
+      else if (! strcmp (key, "label"))
+        {
+          cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg,
+                         error_fmt, key, "markup");
+          atype = ARG_STRING;
+        }
+      else if (! strcmp (key, "opacity"))
+        {
+          cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg,
+                         error_fmt, key, "markup");
+          atype = ARG_EDGE;
+        }
+      else if (! strcmp (key, "popup"))
+        {
+          cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg,
+                         error_fmt, key, "markup");
+          atype = ARG_EDGES;
+        }
+      else if (! strcmp (key, "popup-is-open"))
+        {
+          cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg,
+                         error_fmt, key, "markup");
+          atype = ARG_BOOL;
+        }
+      else if (! strcmp (key, "icon"))
+        {
+          cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg,
+                         error_fmt, key, "text");
+          atype = ARG_STRING;
+        }
+      else if (! strcmp (key, "is-open"))
+        {
+          cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg,
+                         error_fmt, key, "text");
+          atype = ARG_BOOL;
+        }
+      else
+        {
+          cerror_if_not (0, error_msg,
+                         "Unable to modify property `%s'", key);
+        }
+
+      if (! command_arg_parse_arg (ctx, args[i++], &carg, atype, error_msg))
+        return FALSE;
+    }
+
+  return TRUE;
+
+ error:
+  return FALSE;
+}
+
+static void
+annotation_print (const annotation_t *annot, /* const */ PopplerPage *page)
+{
+  double width, height;
+  PopplerAnnotMapping *m;
+  const gchar *key;
+  PopplerAnnot *a;
+  PopplerAnnotMarkup *ma;
+  PopplerAnnotText *ta;
+  PopplerRectangle r;
+  PopplerColor *color;
+  gchar *text;
+  gdouble opacity;
+  cairo_region_t *region = NULL;
+
+  if (! annot || ! page)
+    return;
+
+  m = annot->amap;
+  key = annot->key;
+  a = m->annot;
+  poppler_page_get_size (page, &width, &height);
+
+  r.x1 = m->area.x1;
+  r.x2 = m->area.x2;
+  r.y1 = height - m->area.y2;
+  r.y2 = height - m->area.y1;
+
+#ifdef HAVE_POPPLER_ANNOT_MARKUP
+  if (POPPLER_IS_ANNOT_TEXT_MARKUP (a))
+    {
+      region = annotation_markup_get_text_regions (page, POPPLER_ANNOT_TEXT_MARKUP (a));
+      perror_if_not (region, "%s", "Unable to extract annotation's text regions");
+    }
+#endif
+
+  /* >>> Any Annotation >>> */
+  /* Page */
+  printf ("%d:", poppler_page_get_index (page) + 1);
+  /* Area */
+  printf ("%f %f %f %f:", r.x1 / width, r.y1 / height
+          , r.x2 / width, r.y2 / height);
+
+  /* Type */
+  printf ("%s:", xpoppler_annot_type_string (poppler_annot_get_annot_type (a)));
+  /* Internal Key */
+  print_response_string (key, COLON);
+
+  /* Flags */
+  printf ("%d:", poppler_annot_get_flags (a));
+
+  /* Color */
+  color = poppler_annot_get_color (a);
+  if (color)
+    {
+      /* Reduce 2 Byte to 1 Byte color space  */
+      printf ("#%.2x%.2x%.2x", (color->red >> 8)
+              , (color->green >> 8)
+              , (color->blue >> 8));
+      g_free (color);
+    }
+
+  putchar (':');
+
+  /* Text Contents */
+  text = poppler_annot_get_contents (a);
+  print_response_string (text, COLON);
+  g_free (text);
+
+  /* Modified Date */
+  text = poppler_annot_get_modified (a);
+  print_response_string (text, NONE);
+  g_free (text);
+
+  /* <<< Any Annotation <<< */
+
+  /* >>> Markup Annotation >>> */
+  if (! POPPLER_IS_ANNOT_MARKUP (a))
+    {
+      putchar ('\n');
+      goto theend;
+    }
+
+  putchar (':');
+  ma = POPPLER_ANNOT_MARKUP (a);
+  /* Label */
+  text = poppler_annot_markup_get_label (ma);
+  print_response_string (text, COLON);
+  g_free (text);
+
+  /* Subject */
+  text = poppler_annot_markup_get_subject (ma);
+  print_response_string (text, COLON);
+  g_free (text);
+
+  /* Opacity */
+  opacity = poppler_annot_markup_get_opacity (ma);
+  printf ("%f:", opacity);
+
+  /* Popup (Area + isOpen) */
+  if (poppler_annot_markup_has_popup (ma)
+      && poppler_annot_markup_get_popup_rectangle (ma, &r))
+    {
+      gdouble tmp = r.y1;
+      r.y1 = height - r.y2;
+      r.y2 = height - tmp;
+      printf ("%f %f %f %f:%d:", r.x1 / width, r.y1 / height
+              , r.x2 / width, r.y2 / height
+              , poppler_annot_markup_get_popup_is_open (ma) ? 1 : 0);
+
+    }
+  else
+    printf ("::");
+
+  /* Creation Date */
+  text = xpoppler_annot_markup_get_created (ma);
+  if (text)
+    {
+      print_response_string (text, NONE);
+      g_free (text);
+    }
+
+  /* <<< Markup Annotation <<< */
+
+  /* >>>  Text Annotation >>> */
+  if (POPPLER_IS_ANNOT_TEXT (a))
+    {
+      putchar (':');
+      ta = POPPLER_ANNOT_TEXT (a);
+      /* Text Icon */
+      text = poppler_annot_text_get_icon (ta);
+      print_response_string (text, COLON);
+      g_free (text);
+      /* Text State */
+      printf ("%s:%d",
+              xpoppler_annot_text_state_string (poppler_annot_text_get_state (ta)),
+              poppler_annot_text_get_is_open (ta));
+    }
+#ifdef HAVE_POPPLER_ANNOT_MARKUP
+  /* <<< Text Annotation <<< */
+  else if (POPPLER_IS_ANNOT_TEXT_MARKUP (a))
+    {
+      /* >>> Markup Text Annotation >>> */
+      putchar (':');
+      region_print (region, width, height);
+      /* <<< Markup Text Annotation <<< */
+    }
+#endif
+  putchar ('\n');
+ theend:
+#ifdef HAVE_POPPLER_ANNOT_MARKUP
+ error:
+#endif
+  if (region) cairo_region_destroy (region);
+}
+
+static void
+attachment_print (PopplerAttachment *att, const char *id, gboolean do_save)
+{
+  time_t time;
+
+  print_response_string (id, COLON);
+  print_response_string (att->name, COLON);
+  print_response_string (att->description, COLON);
+  if (att->size + 1 != 0)
+    printf ("%" G_GSIZE_FORMAT ":", att->size);
+  else
+    printf ("-1:");
+  time = (time_t) att->mtime;
+  print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON);
+  time = (time_t) att->ctime;
+  print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON);
+  print_response_string (att->checksum ? att->checksum->str : "" , COLON);
+  if (do_save)
+    {
+      char *filename = mktempfile ();
+      GError *error = NULL;
+      if (filename)
+        {
+          if (! poppler_attachment_save (att, filename, &error))
+            {
+              fprintf (stderr, "Writing attachment failed: %s"
+                       , error ? error->message : "reason unknown");
+              if (error)
+                g_free (error);
+            }
+          else
+            {
+              print_response_string (filename, NONE);
+            }
+          free (filename);
+        }
+    }
+  putchar ('\n');
+}
+
+
+\f
+/* ================================================================== *
+ * Server command implementations
+ * ================================================================== */
+
+/* Name: features
+   Args: None
+   Returns: A list of compile-time features.
+   Errors: None
+*/
+
+const command_arg_type_t cmd_features_spec[] = {};
+
+static void
+cmd_features (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  const char *features[] = {
+#ifdef HAVE_POPPLER_FIND_OPTS
+    "case-sensitive-search",
+#else
+    "no-case-sensitive-search",
+#endif
+#ifdef HAVE_POPPLER_ANNOT_WRITE
+    "writable-annotations",
+#else
+    "no-writable-annotations",
+#endif
+#ifdef HAVE_POPPLER_ANNOT_MARKUP
+    "markup-annotations"
+#else
+    "no-markup-annotations"
+#endif
+  };
+  int i;
+  OK_BEGIN ();
+  for (i = 0; i < G_N_ELEMENTS (features); ++i)
+    {
+      printf ("%s", features[i]);
+      if (i < G_N_ELEMENTS (features) - 1)
+        putchar (':');
+    }
+  putchar ('\n');
+  OK_END ();
+}
+
+
+/* Name: open
+   Args: filename password
+   Returns: Nothing
+   Errors: If file can't be opened or is not a PDF document.
+*/
+
+const command_arg_type_t cmd_open_spec[] =
+  {
+    ARG_NONEMPTY_STRING,        /* filename */
+    ARG_STRING,                 /* password */
+  };
+
+static void
+cmd_open (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  const char *filename = args[0].value.string;
+  const char *passwd = args[1].value.string;
+  GError *gerror = NULL;
+  document_t *doc;
+
+  if (! *passwd)
+    passwd = NULL;
+
+  doc = document_open(ctx, filename, passwd, &gerror);
+  perror_if_not (doc, "Error opening %s:%s", filename,
+                gerror ? gerror->message : "unknown error");
+  OK ();
+
+ error:
+  if (gerror)
+    {
+      g_error_free (gerror);
+      gerror = NULL;
+    }
+}
+
+/* Name: close
+   Args: filename
+   Returns: 1 if file was open, otherwise 0.
+   Errors: None
+*/
+
+const command_arg_type_t cmd_close_spec[] =
+  {
+    ARG_NONEMPTY_STRING         /* filename */
+  };
+
+static void
+cmd_close (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = g_hash_table_lookup(ctx->documents, args->value.string);
+
+  g_hash_table_remove (ctx->documents, args->value.string);
+  free_document (doc);
+  OK_BEGIN ();
+  puts (doc ? "1" : "0");
+  OK_END ();
+}
+
+/* Name: closeall
+   Args: None
+   Returns: Nothing
+   Errors: None
+*/
+static void
+cmd_closeall (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  GHashTableIter iter;
+  gpointer key, value;
+
+  g_hash_table_iter_init (&iter, ctx->documents);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      document_t *doc = (document_t*) value;
+      free_document (doc);
+      g_hash_table_iter_remove (&iter);
+    }
+  OK ();
+}
+
+
+const command_arg_type_t cmd_search_regexp_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM,                 /* first page */
+    ARG_NATNUM,                 /* last page */
+    ARG_NONEMPTY_STRING,        /* regexp */
+    ARG_NATNUM,                 /* compile flags */
+    ARG_NATNUM                  /* match flags */
+  };
+
+static void
+cmd_search_regexp(const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerDocument *doc = args[0].value.doc->pdf;
+  int first = args[1].value.natnum;
+  int last = args[2].value.natnum;
+  const gchar *regexp = args[3].value.string;
+  GRegexCompileFlags cflags = args[4].value.natnum;
+  GRegexMatchFlags mflags = args[5].value.natnum;
+  double width, height;
+  int pn;
+  GError *gerror = NULL;
+  GRegex *re = NULL;
+
+  NORMALIZE_PAGE_ARG (doc, &first, &last);
+
+  re = g_regex_new (regexp, cflags, mflags, &gerror);
+  perror_if_not (NULL == gerror, "Invalid regexp: %s", gerror->message);
+
+  OK_BEGIN ();
+  for (pn = first; pn <= last; ++pn)
+    {
+      PopplerPage *page = poppler_document_get_page(doc, pn - 1);
+      char *text;
+      PopplerRectangle *rectangles = NULL;
+      guint nrectangles;
+      GMatchInfo *match = NULL;
+
+      if (! page)
+        continue;
+
+      text = poppler_page_get_text (page);
+      poppler_page_get_text_layout (page, &rectangles, &nrectangles);
+      poppler_page_get_size (page, &width, &height);
+      g_regex_match (re, text, 0, &match);
+
+      while (g_match_info_matches (match))
+        {
+          const double scale = 100.0;
+          gint start, end, ustart, ulen;
+          gchar *string = NULL;
+          gchar *line = NULL;
+          int i;
+
+          /* Does this ever happen ? */
+          if (! g_match_info_fetch_pos (match, 0, &start, &end))
+            continue;
+
+          string = g_match_info_fetch (match, 0);
+          ustart = g_utf8_strlen (text, start);
+          ulen = g_utf8_strlen (string, -1);
+
+          cairo_region_t *region = cairo_region_create ();
+          /* Merge matched glyph rectangles. Scale them so we're able
+             to use cairo . */
+          if (ulen > 0)
+            {
+              assert (ustart < nrectangles
+                      && ustart + ulen <= nrectangles);
+              line = poppler_page_get_selected_text
+                     (page, POPPLER_SELECTION_LINE, rectangles + ustart);
+
+              for (i = ustart; i < ustart + ulen; ++i)
+                {
+                  PopplerRectangle *r = rectangles + i;
+                  cairo_rectangle_int_t c;
+
+                  c.x = (int) (scale * r->x1 + 0.5);
+                  c.y = (int) (scale * r->y1 + 0.5);
+                  c.width = (int) (scale * (r->x2 - r->x1) + 0.5);
+                  c.height = (int) (scale * (r->y2 - r->y1) + 0.5);
+
+                  cairo_region_union_rectangle (region, &c);
+                }
+
+            }
+
+          printf ("%d:", pn);
+          print_response_string (string, COLON);
+          print_response_string (strchomp (line), COLON);
+          region_print (region, width * scale, height * scale);
+          putchar ('\n');
+          cairo_region_destroy (region);
+          g_free (string);
+          g_free (line);
+          g_match_info_next (match, NULL);
+        }
+      g_free (rectangles);
+      g_object_unref (page);
+      g_free (text);
+      g_match_info_free (match);
+    }
+  OK_END ();
+
+ error:
+  if (re) g_regex_unref (re);
+  if (gerror) g_error_free (gerror);
+}
+
+const command_arg_type_t cmd_regexp_flags_spec[] =
+  {
+  };
+
+static void
+cmd_regexp_flags (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  OK_BEGIN ();
+  printf ("caseless:%d\n", G_REGEX_CASELESS);
+  printf ("multiline:%d\n", G_REGEX_MULTILINE);
+  printf ("dotall:%d\n", G_REGEX_DOTALL);
+  printf ("extended:%d\n", G_REGEX_EXTENDED);
+  printf ("anchored:%d\n", G_REGEX_ANCHORED);
+  printf ("dollar-endonly:%d\n", G_REGEX_DOLLAR_ENDONLY);
+  printf ("ungreedy:%d\n", G_REGEX_UNGREEDY);
+  printf ("raw:%d\n", G_REGEX_RAW);
+  printf ("no-auto-capture:%d\n", G_REGEX_NO_AUTO_CAPTURE);
+  printf ("optimize:%d\n", G_REGEX_OPTIMIZE);
+  printf ("dupnames:%d\n", G_REGEX_DUPNAMES);
+  printf ("newline-cr:%d\n", G_REGEX_NEWLINE_CR);
+  printf ("newline-lf:%d\n", G_REGEX_NEWLINE_LF);
+  printf ("newline-crlf:%d\n", G_REGEX_NEWLINE_CRLF);
+
+  printf ("match-anchored:%d\n", G_REGEX_MATCH_ANCHORED);
+  printf ("match-notbol:%d\n", G_REGEX_MATCH_NOTBOL);
+  printf ("match-noteol:%d\n", G_REGEX_MATCH_NOTEOL);
+  printf ("match-notempty:%d\n", G_REGEX_MATCH_NOTEMPTY);
+  printf ("match-partial:%d\n", G_REGEX_MATCH_PARTIAL);
+  printf ("match-newline-cr:%d\n", G_REGEX_MATCH_NEWLINE_CR);
+  printf ("match-newline-lf:%d\n", G_REGEX_MATCH_NEWLINE_LF);
+  printf ("match-newline-crlf:%d\n", G_REGEX_MATCH_NEWLINE_CRLF);
+  printf ("match-newline-any:%d\n", G_REGEX_MATCH_NEWLINE_ANY);
+
+  OK_END ();
+}
+
+
+const command_arg_type_t cmd_search_string_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM,                 /* first page */
+    ARG_NATNUM,                 /* last page */
+    ARG_NONEMPTY_STRING,        /* search string */
+    ARG_BOOL,                   /* ignore-case */
+  };
+
+static void
+cmd_search_string(const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerDocument *doc = args[0].value.doc->pdf;
+  int first = args[1].value.natnum;
+  int last = args[2].value.natnum;
+  const char *string = args[3].value.string;
+  gboolean ignore_case = args[4].value.flag;
+  GList *list, *item;
+  double width, height;
+  int pn;
+#ifdef HAVE_POPPLER_FIND_OPTS
+  PopplerFindFlags flags = ignore_case ? 0 : POPPLER_FIND_CASE_SENSITIVE;
+#endif
+
+  NORMALIZE_PAGE_ARG (doc, &first, &last);
+  OK_BEGIN ();
+  for (pn = first; pn <= last; ++pn)
+    {
+      PopplerPage *page = poppler_document_get_page(doc, pn - 1);
+
+      if (! page)
+        continue;
+
+#ifdef HAVE_POPPLER_FIND_OPTS
+      list = poppler_page_find_text_with_options(page, string, flags);
+#else
+      list = poppler_page_find_text(page, string);
+#endif
+
+      poppler_page_get_size (page, &width, &height);
+
+      for (item = list; item; item = item->next)
+        {
+          gchar *line, *match;
+          PopplerRectangle *r = item->data;
+          gdouble y1 =  r->y1;
+
+          r->y1 = height - r->y2;
+          r->y2 = height - y1;
+
+          printf ("%d:", pn);
+          line = strchomp (poppler_page_get_selected_text
+                           (page, POPPLER_SELECTION_LINE, r));
+          match = strchomp (poppler_page_get_selected_text
+                           (page, POPPLER_SELECTION_GLYPH, r));
+          print_response_string (match, COLON);
+          print_response_string (line, COLON);
+          printf ("%f %f %f %f\n",
+                  r->x1 / width, r->y1 / height,
+                  r->x2 / width, r->y2 / height);
+          g_free (line);
+          g_free (match);
+          poppler_rectangle_free (r);
+        }
+      g_list_free (list);
+      g_object_unref (page);
+    }
+  OK_END ();
+}
+
+/* Name: metadata
+   Args: filename
+   Returns: PDF's metadata
+   Errors: None
+
+   title author subject keywords creator producer pdf-version create-date mod-date
+
+   Dates are in seconds since the epoche.
+
+*/
+
+const command_arg_type_t cmd_metadata_spec[] =
+  {
+    ARG_DOC,
+  };
+
+static void
+cmd_metadata (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerDocument *doc = args[0].value.doc->pdf;
+  time_t date;
+  gchar *md[6];
+  gchar *title;
+  int i;
+  char *time_str;
+
+  OK_BEGIN ();
+
+  title = poppler_document_get_title (doc);
+  print_response_string (title, COLON);
+  g_free (title);
+
+  md[0] = poppler_document_get_author (doc);
+  md[1] = poppler_document_get_subject (doc);
+  md[2] = poppler_document_get_keywords (doc);
+  md[3] = poppler_document_get_creator (doc);
+  md[4] = poppler_document_get_producer (doc);
+  md[5] = poppler_document_get_pdf_version_string (doc);
+
+  for (i = 0; i < 6; ++i)
+    {
+      print_response_string (md[i], COLON);
+      g_free (md[i]);
+    }
+
+  date = poppler_document_get_creation_date (doc);
+  time_str = strchomp (ctime (&date));
+  print_response_string (time_str ? time_str : "", COLON);
+  date = poppler_document_get_modification_date (doc);
+  time_str = strchomp (ctime (&date));
+  print_response_string (time_str ? time_str : "", NEWLINE);
+  OK_END ();
+}
+
+/* Name: outline
+   Args: filename
+
+   Returns: The documents outline (or index) as a, possibly empty,
+   list of records:
+
+   tree-level ACTION
+
+   See cmd_pagelinks for how ACTION is constructed.
+
+   Errors: None
+*/
+
+static void
+cmd_outline_walk (PopplerDocument *doc, PopplerIndexIter *iter, int depth)
+{
+  do
+    {
+      PopplerIndexIter *child;
+      PopplerAction *action = poppler_index_iter_get_action (iter);
+
+      if (! action)
+        continue;
+
+      if (action_is_handled (action))
+        {
+          printf ("%d:", depth);
+          action_print (doc, action);
+        }
+
+      child = poppler_index_iter_get_child (iter);
+      if (child)
+        {
+          cmd_outline_walk (doc, child, depth + 1);
+        }
+      poppler_action_free (action);
+      poppler_index_iter_free (child);
+    } while (poppler_index_iter_next (iter));
+}
+
+const command_arg_type_t cmd_outline_spec[] =
+  {
+    ARG_DOC,
+  };
+
+static void
+cmd_outline (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerIndexIter *iter = poppler_index_iter_new (args->value.doc->pdf);
+  OK_BEGIN ();
+  if (iter)
+    {
+      cmd_outline_walk (args->value.doc->pdf, iter, 1);
+      poppler_index_iter_free (iter);
+    }
+  OK_END ();
+}
+
+/* Name: quit
+   Args: None
+   Returns: Nothing
+   Errors: None
+
+   Close all documents and exit.
+*/
+
+
+const command_arg_type_t cmd_quit_spec[] = {};
+
+static void
+cmd_quit (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  cmd_closeall (ctx, args);
+  exit (EXIT_SUCCESS);
+}
+
+/* Name: number-of-pages
+   Args: filename
+   Returns: The number of pages.
+   Errors: None
+*/
+
+
+const command_arg_type_t cmd_number_of_pages_spec[] =
+  {
+    ARG_DOC
+  };
+
+static void
+cmd_number_of_pages (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  int npages = poppler_document_get_n_pages (args->value.doc->pdf);
+  OK_BEGIN ();
+  printf ("%d\n", npages);
+  OK_END ();
+}
+
+/* Name: pagelinks
+   Args: filename page
+   Returns: A list of linkmaps:
+
+   edges ACTION ,
+
+   where ACTION is one of
+
+   'goto-dest' title page top
+   'goto-remote' title filename page top
+   'uri' title URI
+   'launch' title program arguments
+
+   top is desired vertical position, filename is the target PDF of the
+   `goto-remote' link.
+
+   Errors: None
+*/
+
+
+const command_arg_type_t cmd_pagelinks_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM                  /* page number */
+  };
+
+static void
+cmd_pagelinks(const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerDocument *doc = args[0].value.doc->pdf;
+  PopplerPage *page = NULL;
+  int pn = args[1].value.natnum;
+  double width, height;
+  GList *link_map = NULL, *item;
+
+  page = poppler_document_get_page (doc, pn - 1);
+  perror_if_not (page, "No such page %d", pn);
+  poppler_page_get_size (page, &width, &height);
+  link_map = poppler_page_get_link_mapping (page);
+
+  OK_BEGIN ();
+  for (item = g_list_last (link_map); item; item = item->prev)
+    {
+
+      PopplerLinkMapping *link = item->data;
+      PopplerRectangle *r = &link->area;
+      gdouble y1 = r->y1;
+      /* LinkMappings have a different gravity. */
+      r->y1 = height - r->y2;
+      r->y2 = height - y1;
+
+      if (! action_is_handled (link->action))
+        continue;
+
+      printf ("%f %f %f %f:",
+              r->x1 / width, r->y1 / height,
+              r->x2 / width, r->y2 / height);
+      action_print (doc, link->action);
+    }
+  OK_END ();
+ error:
+  if (page) g_object_unref (page);
+  if (link_map) poppler_page_free_link_mapping (link_map);
+}
+
+/* Name: gettext
+   Args: filename page edges selection-style
+   Returns: The selection's text.
+   Errors: If page is out of range.
+
+   For the selection-style argument see getselection command.
+*/
+
+
+const command_arg_type_t cmd_gettext_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM,                 /* page number */
+    ARG_EDGES,                  /* selection */
+    ARG_NATNUM                  /* selection-style */
+  };
+
+static void
+cmd_gettext(const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerDocument *doc = args[0].value.doc->pdf;
+  int pn = args[1].value.natnum;
+  PopplerRectangle r = args[2].value.rectangle;
+  int selection_style = args[3].value.natnum;
+  PopplerPage *page = NULL;
+  double width, height;
+  gchar *text = NULL;
+
+  switch (selection_style)
+    {
+    case POPPLER_SELECTION_GLYPH: break;
+    case POPPLER_SELECTION_LINE: break;
+    case POPPLER_SELECTION_WORD: break;
+    default: selection_style = POPPLER_SELECTION_GLYPH;
+    }
+
+  page = poppler_document_get_page (doc, pn - 1);
+  perror_if_not (page, "No such page %d", pn);
+  poppler_page_get_size (page, &width, &height);
+  r.x1 = r.x1 * width;
+  r.x2 = r.x2 * width;
+  r.y1 = r.y1 * height;
+  r.y2 = r.y2 * height;
+  /* printf ("%f %f %f %f , %f %f\n", r.x1, r.y1, r.x2, r.y2, width, height); */
+  text = poppler_page_get_selected_text (page, selection_style, &r);
+
+  OK_BEGIN ();
+  print_response_string (text, NEWLINE);
+  OK_END ();
+
+ error:
+  g_free (text);
+  if (page) g_object_unref (page);
+}
+
+/* Name: getselection
+   Args: filename page edges selection-selection_style
+   Returns: The selection's text.
+   Errors: If page is out of range.
+
+   selection-selection_style should be as follows.
+
+   0 (POPPLER_SELECTION_GLYPH)
+       glyph is the minimum unit for selection
+
+   1 (POPPLER_SELECTION_WORD)
+       word is the minimum unit for selection
+
+   2 (POPPLER_SELECTION_LINE)
+       line is the minimum unit for selection
+*/
+
+
+const command_arg_type_t cmd_getselection_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM,                 /* page number */
+    ARG_EDGES,       /* selection */
+    ARG_NATNUM                  /* selection-style */
+  };
+
+static void
+cmd_getselection (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerDocument *doc = args[0].value.doc->pdf;
+  int pn = args[1].value.natnum;
+  PopplerRectangle r = args[2].value.rectangle;
+  int selection_style = args[3].value.natnum;
+  gdouble width, height;
+  cairo_region_t *region = NULL;
+  PopplerPage *page = NULL;
+  int i;
+
+  switch (selection_style)
+    {
+    case POPPLER_SELECTION_GLYPH: break;
+    case POPPLER_SELECTION_LINE: break;
+    case POPPLER_SELECTION_WORD: break;
+    default: selection_style = POPPLER_SELECTION_GLYPH;
+    }
+
+  page = poppler_document_get_page (doc, pn - 1);
+  perror_if_not (page, "No such page %d", pn);
+  poppler_page_get_size (page, &width, &height);
+
+  r.x1 = r.x1 * width;
+  r.x2 = r.x2 * width;
+  r.y1 = r.y1 * height;
+  r.y2 = r.y2 * height;
+
+  region = poppler_page_get_selected_region (page, 1.0, selection_style, &r);
+
+  OK_BEGIN ();
+  for (i = 0; i < cairo_region_num_rectangles (region); ++i)
+    {
+      cairo_rectangle_int_t r;
+
+      cairo_region_get_rectangle (region, i, &r);
+      printf ("%f %f %f %f\n",
+              r.x / width,
+              r.y / height,
+              (r.x + r.width) / width,
+              (r.y + r.height) / height);
+    }
+  OK_END ();
+
+ error:
+  if (region) cairo_region_destroy (region);
+  if (page) g_object_unref (page);
+}
+
+/* Name: pagesize
+   Args: filename page
+   Returns: width height
+   Errors: If page is out of range.
+*/
+
+
+const command_arg_type_t cmd_pagesize_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM                  /* page number */
+  };
+
+static void
+cmd_pagesize(const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerDocument *doc = args[0].value.doc->pdf;
+  int pn = args[1].value.natnum;
+  PopplerPage *page = NULL;
+  double width, height;
+
+
+  page = poppler_document_get_page (doc, pn - 1);
+  perror_if_not (page, "No such page %d", pn);
+  poppler_page_get_size (page, &width, &height);
+
+  OK_BEGIN ();
+  printf ("%f:%f\n", width, height);
+  OK_END ();
+
+ error:
+  if (page) g_object_unref (page);
+}
+
+/* Annotations */
+
+/* Name: getannots
+   Args: filename firstpage lastpage
+   Returns: The list of annotations of this page.
+
+   For all annotations
+
+   page edges type key flags color contents mod-date
+
+   ,where
+
+   name is a document-unique name,
+   flags is PopplerAnnotFlag bitmask,
+   color is 3-byte RGB hex number and
+
+   Then
+
+   label subject opacity popup-edges popup-is-open create-date
+
+   if this is a markup annotation and additionally
+
+   text-icon text-state
+
+   for markup text annotations.
+
+   Errors: If page is out of range.
+*/
+
+
+const command_arg_type_t cmd_getannots_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM,                 /* first page */
+    ARG_NATNUM                  /* last page */
+  };
+
+static void
+cmd_getannots(const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerDocument *doc = args[0].value.doc->pdf;
+  gint first = args[1].value.natnum;
+  gint last = args[2].value.natnum;
+  GList *list;
+  gint pn;
+
+  first = MAX(1, first);
+  if (last <= 0)
+    last = poppler_document_get_n_pages (doc);
+  else
+    last = MIN(last, poppler_document_get_n_pages (doc));
+
+  OK_BEGIN ();
+  for (pn = first; pn <= last; ++pn)
+    {
+      GList *annots = annoation_get_for_page (args->value.doc, pn);
+      PopplerPage *page = poppler_document_get_page (doc, pn - 1);
+
+      if (! page)
+        continue;
+
+      for (list = annots; list; list = list->next)
+        {
+          annotation_t *annot = (annotation_t *)list->data;
+          annotation_print (annot, page);
+        }
+      g_object_unref (page);
+    }
+  OK_END ();
+}
+
+/* Name: getannot
+   Args: filename name
+   Returns: The annotation for name, see cmd_getannots.
+   Errors: If no annotation named ,name' exists.
+*/
+
+
+const command_arg_type_t cmd_getannot_spec[] =
+  {
+    ARG_DOC,
+    ARG_NONEMPTY_STRING,        /* annotation's key */
+  };
+
+static void
+cmd_getannot (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args->value.doc;
+  const gchar *key = args[1].value.string;
+  PopplerPage *page = NULL;
+  annotation_t *a = annotation_get_by_key (doc, key);
+  gint index;
+
+  perror_if_not (a, "No such annotation: %s", key);
+  index = poppler_annot_get_page_index (a->amap->annot);
+  if (index >= 0)
+    page = poppler_document_get_page (doc->pdf, index);
+  perror_if_not (page, "Unable to get page %d", index + 1);
+
+  OK_BEGIN ();
+  annotation_print (a, page);
+  OK_END ();
+
+ error:
+  if (page) g_object_unref (page);
+}
+
+/* Name: getannot_attachment
+   Args: filename name [output-filename]
+   Returns: name description size mtime ctime output-filename
+   Errors: If no annotation named ,name' exists or output-filename is
+   not writable.
+*/
+
+
+const command_arg_type_t cmd_getattachment_from_annot_spec[] =
+  {
+    ARG_DOC,
+    ARG_NONEMPTY_STRING,        /* annotation's name */
+    ARG_BOOL                    /* save attachment */
+  };
+
+static void
+cmd_getattachment_from_annot (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args->value.doc;
+  const gchar *key = args[1].value.string;
+  gboolean do_save = args[2].value.flag;
+  PopplerAttachment *att = NULL;
+  annotation_t *a = annotation_get_by_key (doc, key);
+  gchar *id = NULL;
+
+  perror_if_not (a, "No such annotation: %s", key);
+  perror_if_not (POPPLER_IS_ANNOT_FILE_ATTACHMENT (a->amap->annot),
+                "Not a file annotation: %s", key);
+  att = poppler_annot_file_attachment_get_attachment
+        (POPPLER_ANNOT_FILE_ATTACHMENT (a->amap->annot));
+  perror_if_not (att, "Unable to get attachment: %s", key);
+  id = g_strdup_printf ("attachment-%s", key);
+
+  OK_BEGIN ();
+  attachment_print (att, id, do_save);
+  OK_END ();
+
+ error:
+  if (att) g_object_unref (att);
+  if (id) g_free (id);
+}
+
+
+/* document-level attachments */
+const command_arg_type_t cmd_getattachments_spec[] =
+  {
+    ARG_DOC,
+    ARG_BOOL,        /* save attachments */
+  };
+
+static void
+cmd_getattachments (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args->value.doc;
+  gboolean do_save = args[1].value.flag;
+  GList *item;
+  GList *attmnts = poppler_document_get_attachments (doc->pdf);
+  int i;
+
+  OK_BEGIN ();
+  for (item = attmnts, i = 0; item; item = item->next, ++i)
+    {
+      PopplerAttachment *att = (PopplerAttachment*) item->data;
+      gchar *id = g_strdup_printf ("attachment-document-%d", i);
+
+      attachment_print (att, id, do_save);
+      g_object_unref (att);
+      g_free (id);
+    }
+  g_list_free (attmnts);
+
+  OK_END ();
+}
+
+#ifdef HAVE_POPPLER_ANNOT_WRITE
+
+const command_arg_type_t cmd_addannot_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM,                 /* page number */
+    ARG_STRING,                 /* type */
+    ARG_EDGES_OR_POSITION,      /* edges or position (uses default size) */
+    ARG_REST,                  /* markup regions */
+  };
+
+static void
+cmd_addannot (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+
+  document_t *doc = args->value.doc;
+  gint pn = args[1].value.natnum;
+  const char *type_string = args[2].value.string;
+  PopplerRectangle r = args[3].value.rectangle;
+  int i;
+  PopplerPage *page = NULL;
+  double width, height;
+  PopplerAnnot *pa;
+  PopplerAnnotMapping *amap;
+  annotation_t *a;
+  gchar *key;
+  GList *annotations;
+  gdouble y2;
+  char *error_msg = NULL;
+
+  page = poppler_document_get_page (doc->pdf, pn - 1);
+  perror_if_not (page, "Unable to get page %d", pn);
+  poppler_page_get_size (page, &width, &height);
+  r.x1 *= width; r.x2 *= width;
+  r.y1 *= height; r.y2 *= height;
+  if (r.y2 < 0)
+    r.y2 = r.y1 + 24;
+  if (r.x2 < 0)
+    r.x2 = r.x1 + 24;
+  y2 = r.y2;
+  r.y2 = height - r.y1;
+  r.y1 = height - y2;
+
+  pa = annotation_new (ctx, doc, page, type_string, &r, &args[4], &error_msg);
+  perror_if_not (pa, "Creating annotation failed: %s",
+                 error_msg ? error_msg : "Reason unknown");
+  amap = poppler_annot_mapping_new ();
+  amap->area = r;
+  amap->annot = pa;
+  annotations = annoation_get_for_page (doc, pn);
+
+  i = g_list_length (annotations);
+  key = g_strdup_printf ("annot-%d-%d", pn, i);
+  while (g_hash_table_lookup (doc->annotations.keys, key))
+    {
+      g_free (key);
+      key = g_strdup_printf ("annot-%d-%d", pn, ++i);
+    }
+  a = g_malloc (sizeof (annotation_t));
+  a->amap = amap;
+  a->key = key;
+  doc->annotations.pages[pn - 1] =
+    g_list_prepend (annotations, a);
+  g_hash_table_insert (doc->annotations.keys, key, a);
+  poppler_page_add_annot (page, pa);
+  OK_BEGIN ();
+  annotation_print (a, page);
+  OK_END ();
+
+ error:
+  if (page) g_object_unref (page);
+  if (error_msg) g_free (error_msg);
+}
+
+
+const command_arg_type_t cmd_delannot_spec[] =
+  {
+    ARG_DOC,
+    ARG_NONEMPTY_STRING         /* Annotation's key */
+  };
+
+static void
+cmd_delannot (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args->value.doc;
+  const gchar *key = args[1].value.string;
+  PopplerPage *page = NULL;
+  annotation_t *a = annotation_get_by_key (doc, key);
+  gint pn;
+
+  perror_if_not (a, "No such annotation: %s", key);
+  pn = poppler_annot_get_page_index (a->amap->annot) + 1;
+  if (pn >= 1)
+    page = poppler_document_get_page (doc->pdf, pn - 1);
+  perror_if_not (page, "Unable to get page %d", pn);
+  poppler_page_remove_annot (page, a->amap->annot);
+  doc->annotations.pages[pn - 1] =
+    g_list_remove (doc->annotations.pages[pn - 1], a);
+  g_hash_table_remove (doc->annotations.keys, a->key);
+  poppler_annot_mapping_free(a->amap);
+  OK ();
+
+ error:
+  if (a)
+    {
+      g_free (a->key);
+      g_free (a);
+    }
+  if (page) g_object_unref (page);
+}
+
+const command_arg_type_t cmd_editannot_spec[] =
+  {
+    ARG_DOC,
+    ARG_NONEMPTY_STRING,        /* annotation key */
+    ARG_REST                    /* (KEY VALUE ...) */
+  };
+
+static void
+cmd_editannot (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args->value.doc;
+  const char *key = args[1].value.string;
+  int nrest_args = args[2].value.rest.nargs;
+  char * const *rest_args = args[2].value.rest.args;
+  annotation_t *a = annotation_get_by_key (doc, key);
+  PopplerAnnot *pa;
+  PopplerPage *page = NULL;
+  int i = 0;
+  gint index;
+  char *error_msg = NULL;
+  command_arg_t carg;
+  const char *unexpected_parse_error = "Internal error while parsing arg `%s'";
+
+  perror_if_not (a, "No such annotation: %s", key);
+  pa = a->amap->annot;
+  perror_if_not (annotation_edit_validate (ctx, &args[2], pa, &error_msg),
+                 "%s", error_msg);
+  index = poppler_annot_get_page_index (pa);
+  page = poppler_document_get_page (doc->pdf, index);
+  perror_if_not (page, "Unable to get page %d for annotation", index);
+
+  for (i = 0; i < nrest_args; ++i)
+    {
+      const char *key = rest_args[i++];
+
+      if (! strcmp (key, "flags"))
+        {
+          perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
+                                                 ARG_NATNUM, NULL),
+                         unexpected_parse_error, rest_args[i]);
+          poppler_annot_set_flags (pa, carg.value.natnum);
+        }
+      else if (! strcmp (key, "color"))
+        {
+          perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
+                                                 ARG_COLOR, NULL),
+                         unexpected_parse_error, rest_args[i]);
+          poppler_annot_set_color (pa, &carg.value.color);
+        }
+      else if (! strcmp (key, "contents"))
+        {
+          perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
+                                                 ARG_STRING, NULL),
+                         unexpected_parse_error, rest_args[i]);
+          poppler_annot_set_contents (pa, carg.value.string);
+        }
+      else if (! strcmp (key, "edges"))
+        {
+          PopplerRectangle *area = &a->amap->area;
+          gdouble width, height;
+          PopplerRectangle r;
+
+          perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
+                                                 ARG_EDGES_OR_POSITION, NULL),
+                         unexpected_parse_error, rest_args[i]);
+          r = carg.value.rectangle;
+          poppler_page_get_size (page, &width, &height);
+
+          /* Translate Gravity and maybe keep the width and height. */
+          if (r.x2 < 0)
+            area->x2 +=  (r.x1 * width) - area->x1;
+          else
+            area->x2 = r.x2 * width;
+
+          if (r.y2 < 0)
+            area->y1 -=  (r.y1 * height) - (height - area->y2);
+          else
+            area->y1 = height - (r.y2 * height);
+
+          area->x1 = r.x1 * width;
+          area->y2 = height - (r.y1 * height);
+
+          xpoppler_annot_set_rectangle (pa, area);
+        }
+      else if (! strcmp (key, "label"))
+        {
+          PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa);
+          perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
+                                                 ARG_STRING, NULL),
+                         unexpected_parse_error, rest_args[i]);
+          poppler_annot_markup_set_label (ma, carg.value.string);
+        }
+      else if (! strcmp (key, "opacity"))
+        {
+          PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa);
+          perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
+                                                 ARG_EDGE, NULL),
+                         unexpected_parse_error, rest_args[i]);
+          poppler_annot_markup_set_opacity (ma, carg.value.edge);
+        }
+      else if (! strcmp (key, "popup"))
+        {
+          PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa);
+          perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
+                                                 ARG_EDGES, NULL),
+                         unexpected_parse_error, rest_args[i]);
+          poppler_annot_markup_set_popup (ma, &carg.value.rectangle);
+        }
+      else if (! strcmp (key, "popup-is-open"))
+        {
+          PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa);
+          perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
+                                                 ARG_BOOL, NULL),
+                         unexpected_parse_error, rest_args[i]);
+          poppler_annot_markup_set_popup_is_open (ma, carg.value.flag);
+        }
+      else if (! strcmp (key, "icon"))
+        {
+          PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa);
+          perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
+                                                 ARG_STRING, NULL),
+                         unexpected_parse_error, rest_args[i]);
+          poppler_annot_text_set_icon (ta, carg.value.string);
+        }
+      else if (! strcmp (key, "is-open"))
+        {
+          PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa);
+          perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
+                                                 ARG_BOOL, NULL),
+                         unexpected_parse_error, rest_args[i]);
+          poppler_annot_text_set_is_open (ta, carg.value.flag);
+        }
+      else
+        {
+          perror_if_not (0, "internal error: annotation property validation failed");
+        }
+    }
+
+  OK_BEGIN ();
+  annotation_print (a, page);
+  OK_END ();
+
+ error:
+  if (error_msg) g_free (error_msg);
+  if (page) g_object_unref (page);
+}
+
+const command_arg_type_t cmd_save_spec[] =
+  {
+    ARG_DOC,
+  };
+
+static void
+cmd_save (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args->value.doc;
+  char *filename = mktempfile ();
+  GError *gerror = NULL;
+  gchar *uri;
+  gboolean success = FALSE;
+
+  if (!filename)
+    {
+      printf_error_response ("Unable to create temporary file");
+      return;
+    }
+
+  uri = g_filename_to_uri (filename, NULL, &gerror);
+
+  if (uri)
+    {
+      success = poppler_document_save (doc->pdf, uri, &gerror);
+      g_free (uri);
+    }
+  if (! success)
+    {
+      printf_error_response ("Error while saving %s:%s"
+                    , filename, gerror ? gerror->message : "?");
+      if (gerror)
+        g_error_free (gerror);
+      return;
+    }
+  OK_BEGIN ();
+  print_response_string (filename, NEWLINE);
+  OK_END ();
+}
+
+#endif  /* HAVE_POPPLER_ANNOT_WRITE */
+
+
+const command_arg_type_t cmd_synctex_forward_search_spec[] =
+  {
+    ARG_DOC,
+    ARG_NONEMPTY_STRING,        /* source file */
+    ARG_NATNUM,                 /* line number */
+    ARG_NATNUM                  /* column number */
+  };
+
+static void
+cmd_synctex_forward_search (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args[0].value.doc;
+  const char *source = args[1].value.string;
+  int line = args[2].value.natnum;
+  int column = args[3].value.natnum;
+  synctex_scanner_t scanner = NULL;
+  synctex_node_t node;
+  float x1, y1, x2, y2;
+  PopplerPage *page = NULL;
+  double width, height;
+  int pn;
+
+  scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1);
+  perror_if_not (scanner, "Unable to create synctex scanner,\
+ did you run latex with `--synctex=1' ?");
+
+  perror_if_not (synctex_display_query (scanner, source, line, column)
+                && (node = synctex_next_result (scanner)),
+                "Destination not found");
+
+  pn = synctex_node_page (node);
+  page = poppler_document_get_page(doc->pdf, pn - 1);
+  perror_if_not (page, "Page not found");
+  x1 =  synctex_node_box_visible_h (node);
+  y1 =  synctex_node_box_visible_v (node)
+        - synctex_node_box_visible_height (node);
+  x2 = synctex_node_box_visible_width (node) + x1;
+  y2 = synctex_node_box_visible_depth (node)
+       + synctex_node_box_visible_height (node) + y1;
+  poppler_page_get_size (page, &width, &height);
+  x1 /= width;
+  y1 /= height;
+  x2 /= width;
+  y2 /= height;
+
+  OK_BEGIN ();
+  printf("%d:%f:%f:%f:%f\n", pn, x1, y1, x2, y2);
+  OK_END ();
+
+ error:
+  if (page) g_object_unref (page);
+  if (scanner) synctex_scanner_free (scanner);
+}
+
+
+const command_arg_type_t cmd_synctex_backward_search_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM,                 /* page number */
+    ARG_EDGE,                   /* x */
+    ARG_EDGE                    /* y */
+  };
+
+static void
+cmd_synctex_backward_search (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args[0].value.doc;
+  int pn = args[1].value.natnum;
+  double x = args[2].value.edge;
+  double y = args[3].value.edge;
+  synctex_scanner_t scanner = NULL;
+  const char *filename;
+  PopplerPage *page = NULL;
+  synctex_node_t node;
+  double width, height;
+  int line, column;
+
+  scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1);
+  perror_if_not (scanner, "Unable to create synctex scanner,\
+ did you run latex with `--synctex=1' ?");
+
+  page = poppler_document_get_page(doc->pdf, pn - 1);
+  perror_if_not (page, "Page not found");
+  poppler_page_get_size (page, &width, &height);
+  x = x * width;
+  y = y * height;
+
+  if (! synctex_edit_query (scanner, pn, x, y)
+      || ! (node = synctex_next_result (scanner))
+      || ! (filename =
+            synctex_scanner_get_name (scanner, synctex_node_tag (node))))
+    {
+      printf_error_response ("Destination not found");
+      goto error;
+    }
+
+  line = synctex_node_line (node);
+  column = synctex_node_column (node);
+
+  OK_BEGIN ();
+  print_response_string (filename, COLON);
+  printf("%d:%d\n", line, column);
+  OK_END ();
+
+ error:
+  if (page) g_object_unref (page);
+  if (scanner) synctex_scanner_free (scanner);
+}
+
+
+const command_arg_type_t cmd_renderpage_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM,                 /* page number */
+    ARG_NATNUM,                 /* width */
+    ARG_REST,                   /* commands */
+  };
+
+static void
+cmd_renderpage (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args[0].value.doc;
+  int pn = args[1].value.natnum;
+  int width = args[2].value.natnum;
+  int nrest_args = args[3].value.rest.nargs;
+  char * const *rest_args = args[3].value.rest.args;
+  PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1);
+  cairo_surface_t *surface = NULL;
+  cairo_t *cr = NULL;
+  command_arg_t rest_arg;
+  gchar *error_msg = NULL;
+  double pt_width, pt_height;
+  PopplerColor fg = { 0, 0, 0 };
+  PopplerColor bg = { 65535, 0, 0 };
+  double alpha = 1.0;
+  double line_width = 1.5;
+  PopplerRectangle cb = {0.0, 0.0, 1.0, 1.0};
+  int i = 0;
+
+  perror_if_not (page, "No such page %d", pn);
+  poppler_page_get_size (page, &pt_width, &pt_height);
+  surface = image_render_page (doc->pdf, page, width, 1,
+                               &doc->options.render);
+  perror_if_not (surface, "Failed to render page %d", pn);
+
+  if (! nrest_args)
+    goto theend;
+
+  cr = cairo_create (surface);
+  cairo_scale (cr, width / pt_width, width / pt_width);
+
+  while (i < nrest_args)
+    {
+      const char* keyword;
+
+      perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg,
+                                            ARG_STRING, &error_msg),
+                     "%s", error_msg);
+      keyword = rest_arg.value.string;
+      ++i;
+
+      perror_if_not (i < nrest_args, "Keyword is `%s' missing an argument",
+                     keyword);
+
+      if (! strcmp (keyword, ":foreground")
+          || ! strcmp (keyword, ":background"))
+        {
+          perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg,
+                                                ARG_COLOR, &error_msg),
+                         "%s", error_msg);
+          ++i;
+          if (! strcmp (keyword, ":foreground"))
+            fg = rest_arg.value.color;
+          else
+            bg = rest_arg.value.color;
+
+        }
+      else if (! strcmp (keyword, ":alpha"))
+        {
+          perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg,
+                                                ARG_EDGE, &error_msg),
+                         "%s", error_msg);
+          ++i;
+          alpha = rest_arg.value.edge;
+        }
+      else if (! strcmp (keyword, ":crop-to")
+               || ! strcmp (keyword, ":highlight-region")
+               || ! strcmp (keyword, ":highlight-text")
+               || ! strcmp (keyword, ":highlight-line"))
+        {
+          PopplerRectangle *r;
+          perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg,
+                                                ARG_EDGES, &error_msg),
+                         "%s", error_msg);
+
+          ++i;
+          r = &rest_arg.value.rectangle;
+
+          if (! strcmp (keyword, ":crop-to"))
+            {
+              gdouble w = (cb.x2 - cb.x1);
+              gdouble h = (cb.y2 - cb.y1);
+              gdouble x1 = cb.x1;
+              gdouble y1 = cb.y1;
+
+              cb.x1 = r->x1 * w + x1;
+              cb.x2 = r->x2 * w + x1;
+              cb.y1 = r->y1 * h + y1;
+              cb.y2 = r->y2 * h + y1;
+
+            }
+          else
+            {
+              r->x1 = pt_width * r->x1 * (cb.x2 - cb.x1) + pt_width * cb.x1;
+              r->x2 = pt_width * r->x2 * (cb.x2 - cb.x1) + pt_width * cb.x1;
+              r->y1 = pt_height * r->y1 * (cb.y2 - cb.y1) + pt_height * cb.y1;
+              r->y2 = pt_height * r->y2 * (cb.y2 - cb.y1) + pt_height * cb.y1;
+
+              if (! strcmp (keyword, ":highlight-region"))
+                {
+                  const double deg = M_PI / 180.0;
+                  double rad;
+
+                  r->x1 += line_width / 2;
+                  r->x2 -= line_width / 2;
+                  r->y1 += line_width / 2;
+                  r->y2 -= line_width / 2;
+
+                  rad = MIN (5, MIN (r->x2 - r->x1, r->y2 - r->y1) / 2.0);
+
+                  cairo_move_to (cr, r->x1 , r->y1 + rad);
+                  cairo_arc (cr, r->x1 + rad, r->y1 + rad, rad, 180 * deg, 270 * deg);
+                  cairo_arc (cr, r->x2 - rad, r->y1 + rad, rad, 270 * deg, 360 * deg);
+                  cairo_arc (cr, r->x2 - rad, r->y2 - rad, rad, 0 * deg, 90 * deg);
+                  cairo_arc (cr, r->x1 + rad, r->y2 - rad, rad, 90 * deg, 180 * deg);
+                  cairo_close_path (cr);
+
+                  cairo_set_source_rgba (cr,
+                                         bg.red / 65535.0,
+                                         bg.green / 65535.0,
+                                         bg.blue / 65535.0, alpha);
+                  cairo_fill_preserve (cr);
+                  cairo_set_source_rgba (cr,
+                                         fg.red / 65535.0,
+                                         fg.green / 65535.0,
+                                         fg.blue / 65535.0, 1.0);
+                  cairo_set_line_width (cr, line_width);
+                  cairo_stroke (cr);
+                }
+              else
+                {
+                  gboolean is_single_line = ! strcmp (keyword, ":highlight-line");
+
+                  if (is_single_line)
+                    {
+                      gdouble m = r->y1 + (r->y2 - r->y1) / 2;
+
+                      /* Make the rectangle flat, otherwise poppler frequently
+                         renders neighboring lines.*/
+                      r->y1 = m;
+                      r->y2 = m;
+                    }
+
+                  poppler_page_render_selection (page, cr, r, NULL,
+                                                 POPPLER_SELECTION_GLYPH, &fg, &bg);
+                }
+            }
+        }
+      else
+        perror_if_not (0, "Unknown render command: %s", keyword);
+    }
+  if (cb.x1 != 0 || cb.y1 != 0 || cb.x2 != 1 || cb.y2 != 1)
+    {
+      int height = cairo_image_surface_get_height (surface);
+      cairo_rectangle_int_t r = {(int) (width * cb.x1 + 0.5),
+                                 (int) (height * cb.y1 + 0.5),
+                                 (int) (width * (cb.x2 - cb.x1) + 0.5),
+                                 (int) (height * (cb.y2 - cb.y1) + 0.5)};
+      cairo_surface_t *nsurface =
+        cairo_image_surface_create (CAIRO_FORMAT_ARGB32, r.width, r.height);
+      perror_if_not (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS,
+                     "%s", "Failed to create cairo surface");
+      cairo_destroy (cr);
+      cr = cairo_create (nsurface);
+      perror_if_not (cairo_status (cr) == CAIRO_STATUS_SUCCESS,
+                     "%s", "Failed to create cairo context");
+      cairo_set_source_surface (cr, surface, -r.x, -r.y);
+      cairo_paint (cr);
+      cairo_surface_destroy (surface);
+      surface = nsurface;
+    }
+
+ theend:
+  image_write_print_response (surface, PNG);
+
+ error:
+  if (error_msg) g_free (error_msg);
+  if (cr) cairo_destroy (cr);
+  if (surface) cairo_surface_destroy (surface);
+  if (page) g_object_unref (page);
+}
+
+const command_arg_type_t cmd_boundingbox_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM,                 /* page number */
+    /* region */
+  };
+
+static void
+cmd_boundingbox (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args[0].value.doc;
+  int pn = args[1].value.natnum;
+  PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1);
+  cairo_surface_t *surface = NULL;
+  int width, height;
+  double pt_width, pt_height;
+  unsigned char *data, *data_p;
+  PopplerRectangle bbox;
+  int i, j;
+
+  perror_if_not (page, "No such page %d", pn);
+  poppler_page_get_size (page, &pt_width, &pt_height);
+  surface = image_render_page (doc->pdf, page, (int) pt_width, 1,
+                               &doc->options.render);
+
+  perror_if_not (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS,
+                "Failed to render page");
+
+  width = cairo_image_surface_get_width (surface);
+  height = cairo_image_surface_get_height (surface);
+  data = cairo_image_surface_get_data (surface);
+
+  /* Determine the bbox by comparing each pixel in the 4 corner
+     stripes with the origin. */
+  for (i = 0; i < width; ++i)
+    {
+      data_p = data + 4 * i;
+      for (j = 0; j < height; ++j, data_p += 4 * width)
+        {
+          if (! ARGB_EQUAL (data, data_p))
+            break;
+        }
+      if (j < height)
+        break;
+    }
+  bbox.x1 = i;
+
+  for (i = width - 1; i > -1; --i)
+    {
+      data_p = data + 4 * i;
+      for (j = 0; j < height; ++j, data_p += 4 * width)
+        {
+          if (! ARGB_EQUAL (data, data_p))
+            break;
+        }
+      if (j < height)
+        break;
+    }
+  bbox.x2 = i + 1;
+
+  for (i = 0; i < height; ++i)
+    {
+      data_p = data + 4 * i * width;
+      for (j = 0; j < width; ++j, data_p += 4)
+        {
+          if (! ARGB_EQUAL (data, data_p))
+            break;
+        }
+      if (j < width)
+        break;
+    }
+  bbox.y1 = i;
+
+  for (i = height - 1; i > -1; --i)
+    {
+      data_p = data + 4 * i * width;
+      for (j = 0; j < width; ++j, data_p += 4)
+        {
+          if (! ARGB_EQUAL (data, data_p))
+            break;
+        }
+      if (j < width)
+        break;
+    }
+  bbox.y2 = i + 1;
+
+  OK_BEGIN ();
+  if (bbox.x1 >= bbox.x2 || bbox.y1 >= bbox.y2)
+    {
+      /* empty page */
+      puts ("0:0:1:1");
+    }
+  else
+    {
+      printf ("%f:%f:%f:%f\n",
+              bbox.x1 / width,
+              bbox.y1 / height,
+              bbox.x2 / width,
+              bbox.y2 / height);
+    }
+  OK_END ();
+
+ error:
+  if (surface) cairo_surface_destroy (surface);
+  if (page) g_object_unref (page);
+}
+
+const command_arg_type_t cmd_charlayout_spec[] =
+  {
+    ARG_DOC,
+    ARG_NATNUM,                 /* page number */
+    ARG_EDGES_OR_POSITION,      /* region or position */
+  };
+
+static void
+cmd_charlayout(const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerDocument *doc = args[0].value.doc->pdf;
+  int pn = args[1].value.natnum;
+  PopplerRectangle region = args[2].value.rectangle;
+  double width, height;
+  PopplerPage *page = poppler_document_get_page(doc, pn - 1);
+  char *text = NULL;
+  char *text_p;
+  PopplerRectangle *rectangles = NULL;
+  guint nrectangles;
+  int i;
+  gboolean have_position = region.y2 < 0;
+
+  perror_if_not (page, "No such page %d", pn);
+
+  text = poppler_page_get_text (page);
+  text_p = text;
+  poppler_page_get_text_layout (page, &rectangles, &nrectangles);
+  poppler_page_get_size (page, &width, &height);
+  region.x1 *= width;
+  region.x2 *= width;
+  region.y1 *= height;
+  region.y2 *= height;
+
+  OK_BEGIN ();
+  for (i = 0; i < nrectangles && *text_p; ++i)
+    {
+      PopplerRectangle *r = &rectangles[i];
+      char *nextc = g_utf8_offset_to_pointer (text_p, 1);
+
+      if ((have_position
+           && region.x1 >= r->x1
+           && region.x1 <= r->x2
+           && region.y1 >= r->y1
+           && region.y1 <= r->y2)
+          || (! have_position
+              && r->x1 >= region.x1
+              && r->y1 >= region.y1
+              && r->x2 <= region.x2
+              && r->y2 <= region.y2))
+        {
+          char endc = *nextc;
+
+          printf ("%f %f %f %f:",
+                  r->x1 / width, r->y1 / height,
+                  r->x2 / width, r->y2 / height);
+          *nextc = '\0';
+          print_response_string (text_p, NEWLINE);
+          *nextc = endc;
+        }
+      text_p = nextc;
+    }
+  OK_END ();
+
+  g_free (rectangles);
+  g_object_unref (page);
+  g_free (text);
+
+ error:
+  return;
+}
+
+const document_option_t document_options [] =
+  {
+    DEC_DOPT (":render/usecolors", ARG_BOOL, render.usecolors),
+    DEC_DOPT (":render/printed", ARG_BOOL, render.printed),
+    DEC_DOPT (":render/foreground", ARG_COLOR, render.fg),
+    DEC_DOPT (":render/background", ARG_COLOR, render.bg),
+  };
+
+const command_arg_type_t cmd_getoptions_spec[] =
+  {
+    ARG_DOC,
+  };
+
+static void
+cmd_getoptions(const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  document_t *doc = args[0].value.doc;
+  int i;
+  OK_BEGIN ();
+  for (i = 0; i < G_N_ELEMENTS (document_options); ++i)
+    {
+      command_arg_t arg;
+
+      arg.type = document_options[i].type;
+      memcpy (&arg.value,
+              ((char*) &doc->options) + document_options[i].offset,
+              command_arg_type_size (arg.type));
+      print_response_string (document_options[i].name, COLON);
+      command_arg_print (&arg);
+      puts("");
+    }
+  OK_END ();
+}
+
+const command_arg_type_t cmd_setoptions_spec[] =
+  {
+    ARG_DOC,
+    ARG_REST                    /* key value pairs */
+  };
+
+static void
+cmd_setoptions(const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  int i = 0;
+  document_t *doc = args[0].value.doc;
+  int nrest = args[1].value.rest.nargs;
+  char * const *rest = args[1].value.rest.args;
+  gchar *error_msg = NULL;
+  document_options_t opts = doc->options;
+  const size_t nopts = G_N_ELEMENTS (document_options);
+
+  perror_if_not (nrest % 2 == 0, "Even number of key/value pairs expected");
+
+  while (i < nrest)
+    {
+      int j;
+      command_arg_t key, value;
+
+      perror_if_not (command_arg_parse_arg
+                     (ctx, rest[i], &key, ARG_NONEMPTY_STRING, &error_msg),
+                     "%s", error_msg);
+
+      ++i;
+      for (j = 0; j < nopts; ++j)
+        {
+          const document_option_t *dopt = &document_options[j];
+          if (! strcmp (key.value.string, dopt->name))
+            {
+              perror_if_not (command_arg_parse_arg
+                             (ctx, rest[i], &value, dopt->type, &error_msg),
+                             "%s", error_msg);
+              memcpy (((char*) &opts) + dopt->offset,
+                      &value.value, command_arg_type_size (value.type));
+              break;
+            }
+        }
+      perror_if_not (j < nopts, "Unknown option: %s", key.value.string);
+      ++i;
+    }
+  doc->options = opts;
+  cmd_getoptions (ctx, args);
+
+ error:
+  if (error_msg) g_free (error_msg);
+}
+
+const command_arg_type_t cmd_pagelabels_spec[] =
+  {
+    ARG_DOC,
+  };
+
+static void
+cmd_pagelabels(const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  PopplerDocument *doc = args[0].value.doc->pdf;
+  int i;
+
+  OK_BEGIN ();
+  for (i = 0; i < poppler_document_get_n_pages (doc); ++i)
+    {
+      PopplerPage *page = poppler_document_get_page(doc, i);
+      gchar *label = poppler_page_get_label (page);
+
+      print_response_string (label ? label : "", NEWLINE);
+      g_object_unref (page);
+      g_free (label);
+    }
+  OK_END ();
+}
+
+const command_arg_type_t cmd_ping_spec[] =
+  {
+    ARG_STRING                  /* any message */
+  };
+
+static void
+cmd_ping (const epdfinfo_t *ctx, const command_arg_t *args)
+{
+  const gchar *msg = args[0].value.string;
+  OK_BEGIN ();
+  print_response_string (msg, NEWLINE);
+  OK_END ();
+}
+
+\f
+/* ================================================================== *
+ * Main
+ * ================================================================== */
+
+static const command_t commands [] =
+  {
+    /* Basic */
+    DEC_CMD (ping),
+    DEC_CMD (features),
+    DEC_CMD (open),
+    DEC_CMD (close),
+    DEC_CMD (quit),
+    DEC_CMD (getoptions),
+    DEC_CMD (setoptions),
+
+    /* Searching */
+    DEC_CMD2 (search_string, "search-string"),
+    DEC_CMD2 (search_regexp, "search-regexp"),
+    DEC_CMD2 (regexp_flags, "regexp-flags"),
+
+    /* General Informations */
+    DEC_CMD (metadata),
+    DEC_CMD (outline),
+    DEC_CMD2 (number_of_pages, "number-of-pages"),
+    DEC_CMD (pagelinks),
+    DEC_CMD (gettext),
+    DEC_CMD (getselection),
+    DEC_CMD (pagesize),
+    DEC_CMD (boundingbox),
+    DEC_CMD (charlayout),
+
+    /* General Informations */
+    DEC_CMD (metadata),
+    DEC_CMD (outline),
+    DEC_CMD2 (number_of_pages, "number-of-pages"),
+    DEC_CMD (pagelinks),
+    DEC_CMD (gettext),
+    DEC_CMD (getselection),
+    DEC_CMD (pagesize),
+    DEC_CMD (boundingbox),
+    DEC_CMD (charlayout),
+    DEC_CMD (pagelabels),
+
+    /* Annotations */
+    DEC_CMD (getannots),
+    DEC_CMD (getannot),
+#ifdef HAVE_POPPLER_ANNOT_WRITE
+    DEC_CMD (addannot),
+    DEC_CMD (delannot),
+    DEC_CMD (editannot),
+    DEC_CMD (save),
+#endif
+
+    /* Attachments */
+    DEC_CMD2 (getattachment_from_annot, "getattachment-from-annot"),
+    DEC_CMD (getattachments),
+
+    /* Synctex */
+    DEC_CMD2 (synctex_forward_search, "synctex-forward-search"),
+    DEC_CMD2 (synctex_backward_search, "synctex-backward-search"),
+
+    /* Rendering */
+    DEC_CMD (renderpage),
+  };
+
+int main(int argc, char **argv)
+{
+  epdfinfo_t ctx = {0};
+  char *line = NULL;
+  ssize_t read;
+  size_t line_size;
+  const char *error_log = "/dev/null";
+
+#ifdef __MINGW32__
+  error_log = "NUL";
+  _setmode(_fileno(stdin), _O_BINARY);
+  _setmode(_fileno(stdout), _O_BINARY);
+#endif
+
+  if (argc > 2)
+    {
+      fprintf(stderr, "usage: epdfinfo [ERROR-LOGFILE]\n");
+      exit (EXIT_FAILURE);
+    }
+  if (argc == 2)
+    error_log = argv[1];
+
+  if (! freopen (error_log, "a", stderr))
+    err (2, "Unable to redirect stderr");
+
+#if ! GLIB_CHECK_VERSION(2,36,0)
+  g_type_init ();
+#endif
+
+  ctx.documents = g_hash_table_new (g_str_hash, g_str_equal);
+
+  setvbuf (stdout, NULL, _IOFBF, BUFSIZ);
+
+  while ((read = getline (&line, &line_size, stdin)) != -1)
+    {
+      int nargs = 0;
+      command_arg_t *cmd_args = NULL;
+      char **args = NULL;
+      gchar *error_msg = NULL;
+      int i;
+
+      if (read <= 1 || line[read - 1] != '\n')
+        {
+          fprintf (stderr, "Skipped parts of a line: `%s'\n", line);
+          goto next_line;
+        }
+
+      line[read - 1] = '\0';
+      args = command_arg_split (line, &nargs);
+      if (nargs == 0)
+        continue;
+
+      for (i = 0; i < G_N_ELEMENTS (commands);  i++)
+        {
+          if (! strcmp (commands[i].name, args[0]))
+            {
+              if (commands[i].nargs == 0
+                  || (cmd_args = command_arg_parse (&ctx, args + 1, nargs - 1,
+                                                    commands + i, &error_msg)))
+                {
+                  commands[i].execute (&ctx, cmd_args);
+                  if (commands[i].nargs > 0)
+                    free_command_args (cmd_args, commands[i].nargs);
+                }
+              else
+                {
+                  printf_error_response ("%s", error_msg ? error_msg :
+                                         "Unknown error (this is a bug)");
+                }
+              if (error_msg)
+                g_free (error_msg);
+              break;
+            }
+        }
+      if (G_N_ELEMENTS (commands) == i)
+        {
+          printf_error_response ("Unknown command: %s", args[0]);
+        }
+      for (i = 0; i < nargs; ++i)
+        g_free (args[i]);
+      g_free (args);
+    next_line:
+      free (line);
+      line = NULL;
+    }
+
+  if (ferror (stdin))
+    err (2, NULL);
+  exit (EXIT_SUCCESS);
+}
diff --git a/elpa/pdf-tools-20191031.642/build/server/epdfinfo.h b/elpa/pdf-tools-20191031.642/build/server/epdfinfo.h
new file mode 100644 (file)
index 0000000..9f80cfd
--- /dev/null
@@ -0,0 +1,252 @@
+// Copyright (C) 2013, 2014  Andreas Politz
+
+// Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef _EPDF_H_
+#define _EPDF_H_ _EPDF_H_
+#include "config.h"
+#include <glib.h>
+#include <poppler.h>
+#include <png.h>
+
+/* Some library functions print warnings to stdout, inhibit it. */
+#define DISCARD_STDOUT(saved_fd)                \
+  do {                                          \
+    int __fd;                                   \
+    fflush(stdout);                             \
+    saved_fd = dup(1);                          \
+    __fd = open("/dev/null", O_WRONLY);         \
+    dup2(__fd, 1);                              \
+    close(__fd);                                \
+  } while (0)
+
+#define UNDISCARD_STDOUT(saved_fd)              \
+  do {                                          \
+    fflush(stdout);                             \
+    dup2(saved_fd, 1);                          \
+    close(saved_fd);                            \
+  } while (0)
+
+/* Writing responses */
+#define OK_BEGIN()                              \
+  do {                                          \
+    puts("OK");                                 \
+  } while (0)
+
+#define OK_END()                                \
+  do {                                          \
+    puts(".");                                  \
+    fflush (stdout);                            \
+  } while (0)
+
+#define OK()                                    \
+  do {                                          \
+    puts ("OK\n.");                             \
+    fflush (stdout);                            \
+  } while (0)
+
+/* Dealing with image data. */
+#ifdef WORDS_BIGENDIAN
+#define ARGB_TO_RGB(rgb, argb)                  \
+  do {                                          \
+    rgb[0] = argb[1];                           \
+    rgb[1] = argb[2];                           \
+    rgb[2] = argb[3];                           \
+  } while (0)
+
+#define ARGB_EQUAL(argb1, argb2)                \
+  (argb1[1] == argb2[1]                         \
+   && argb1[2] == argb2[2]                      \
+   && argb1[3] == argb2[3])
+
+#else
+#define ARGB_TO_RGB(rgb, argb)                  \
+  do {                                          \
+    rgb[0] = argb[2];                           \
+    rgb[1] = argb[1];                           \
+    rgb[2] = argb[0];                           \
+  } while (0)
+
+#define ARGB_EQUAL(argb1, argb2)                \
+  (argb1[0] == argb2[0]                         \
+   && argb1[1] == argb2[1]                      \
+   && argb1[2] == argb2[2])
+#endif
+
+#define NORMALIZE_PAGE_ARG(doc, first, last)                    \
+  *first = MAX(1, *first);                                      \
+  if (*last <= 0)                                               \
+    *last = poppler_document_get_n_pages (doc);                 \
+  else                                                          \
+    *last = MIN(*last, poppler_document_get_n_pages (doc));
+
+/* png_jmpbuf is supposed to be not available in older versions of
+   libpng. */
+#ifndef png_jmpbuf
+#  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
+#endif
+
+#ifndef HAVE_ERROR_H
+#  define error(status, errno, fmt, args...)                    \
+  do {                                                          \
+    int error = (errno);                                        \
+    fflush (stdout);                                            \
+    fprintf (stderr, "%s: " fmt, PACKAGE_NAME, ## args);        \
+    if (error)                                                  \
+      fprintf (stderr, ": %s", strerror (error));               \
+    fprintf (stderr, "\n");                                     \
+    exit (status);                                              \
+  } while (0)
+#endif
+
+#define internal_error(fmt, args...)                            \
+  error (2, 0, "internal error in %s: " fmt, __func__, ## args)
+
+#define error_if_not(expr)                      \
+  if (! (expr)) goto error;
+
+#define perror_if_not(expr, fmt, args...)       \
+  do {                                          \
+    if (! (expr))                               \
+      {                                         \
+        printf_error_response ((fmt), ## args); \
+        goto error;                             \
+      }                                         \
+  } while (0)
+
+#define cerror_if_not(expr, error_msg, fmt, args...)            \
+  do {                                                          \
+    if (! (expr))                                               \
+      {                                                         \
+        if (error_msg)                                          \
+          *(error_msg) = g_strdup_printf((fmt), ## args);       \
+        goto error;                                             \
+      }                                                         \
+  } while (0)
+
+/* Declare commands */
+#define DEC_CMD(name)                            \
+  {#name, cmd_ ## name, cmd_ ## name ## _spec,  \
+   G_N_ELEMENTS (cmd_ ## name ## _spec)}
+
+#define DEC_CMD2(command, name)                          \
+  {name, cmd_ ## command, cmd_ ## command ## _spec,     \
+   G_N_ELEMENTS (cmd_ ## command ## _spec)}
+
+/* Declare option */
+#define DEC_DOPT(name, type, sname)                      \
+  {name, type, offsetof (document_options_t, sname)}
+
+enum suffix_char { NONE, COLON, NEWLINE};
+
+enum image_type { PPM, PNG };
+
+typedef struct
+{
+  PopplerAnnotMapping *amap;
+  gchar *key;
+} annotation_t;
+
+typedef enum
+  {
+    ARG_INVALID = 0,
+    ARG_DOC,
+    ARG_BOOL,
+    ARG_STRING,
+    ARG_NONEMPTY_STRING,
+    ARG_NATNUM,
+    ARG_EDGE,
+    ARG_EDGE_OR_NEGATIVE,
+    ARG_EDGES,
+    ARG_EDGES_OR_POSITION,
+    ARG_COLOR,
+    ARG_REST
+  } command_arg_type_t;
+
+typedef struct
+{
+  const char *name;
+  command_arg_type_t type;
+  size_t offset;
+} document_option_t;
+
+typedef struct
+{
+  PopplerColor bg, fg;
+  gboolean usecolors;
+  gboolean printed;
+} render_options_t;
+
+typedef struct
+{
+  render_options_t render;
+} document_options_t;
+
+typedef struct
+{
+  PopplerDocument *pdf;
+  char *filename;
+  char *passwd;
+  struct
+  {
+    GHashTable *keys;             /* key => page */
+    GList **pages;                /* page array  */
+  } annotations;
+  document_options_t options;
+} document_t;
+
+typedef struct
+{
+  command_arg_type_t type;
+  union
+  {
+    gboolean flag;
+    const char *string;
+    long natnum;
+    document_t *doc;
+    gdouble edge;
+    PopplerColor color;
+    PopplerRectangle rectangle;
+#ifdef HAVE_POPPLER_ANNOT_MARKUP
+    PopplerQuadrilateral quadrilateral;
+#endif
+    struct
+    {
+      char * const *args;
+      int nargs;
+    } rest;
+  } value;
+} command_arg_t;
+
+typedef struct
+{
+  GHashTable *documents;
+} epdfinfo_t;
+
+typedef struct
+{
+  const char *name;
+  void (* execute) (const epdfinfo_t *ctxt, const command_arg_t *args);
+  const command_arg_type_t *args_spec;
+  int nargs;
+} command_t;
+
+/* Defined in poppler-hack.cc */
+#ifdef HAVE_POPPLER_ANNOT_WRITE
+extern void xpoppler_annot_set_rectangle (PopplerAnnot*, PopplerRectangle*);
+#endif
+extern gchar *xpoppler_annot_markup_get_created (PopplerAnnotMarkup*);
+#endif  /* _EPDF_H_ */
diff --git a/elpa/pdf-tools-20191031.642/build/server/m4/ax_check_compile_flag.m4 b/elpa/pdf-tools-20191031.642/build/server/m4/ax_check_compile_flag.m4
new file mode 100644 (file)
index 0000000..dcabb92
--- /dev/null
@@ -0,0 +1,74 @@
+# ===========================================================================
+#  https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+#   Check whether the given FLAG works with the current language's compiler
+#   or gives an error.  (Warnings, however, are ignored)
+#
+#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+#   success/failure.
+#
+#   If EXTRA-FLAGS is defined, it is added to the current language's default
+#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
+#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
+#   force the compiler to issue an error when a bad flag is given.
+#
+#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+#   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidodNOSPAMgmx.de>
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmansNOSPAMgmail.com>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 5
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+  ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+  _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+  AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+    [AS_VAR_SET(CACHEVAR,[yes])],
+    [AS_VAR_SET(CACHEVAR,[no])])
+  _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+  [m4_default([$2], :)],
+  [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/elpa/pdf-tools-20191031.642/build/server/poppler-hack.cc b/elpa/pdf-tools-20191031.642/build/server/poppler-hack.cc
new file mode 100644 (file)
index 0000000..2451101
--- /dev/null
@@ -0,0 +1,122 @@
+// Copyright (C) 2013, 2014  Andreas Politz
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#include <config.h>
+#include <PDFDocEncoding.h>
+#include <Annot.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <poppler-features.h>
+
+extern "C"
+{
+
+GType poppler_annot_get_type (void) G_GNUC_CONST;
+GType poppler_annot_markup_get_type (void) G_GNUC_CONST;
+
+#define POPPLER_TYPE_ANNOT (poppler_annot_get_type ())
+#define POPPLER_ANNOT(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), POPPLER_TYPE_ANNOT, PopplerAnnot))
+#define POPPLER_IS_ANNOT_MARKUP(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POPPLER_TYPE_ANNOT_MARKUP))
+#define POPPLER_TYPE_ANNOT_MARKUP (poppler_annot_markup_get_type ())
+
+#if POPPLER_CHECK_VERSION(0,72,0)
+#define GET_CSTR c_str
+#else
+#define GET_CSTR getCString
+#endif
+
+  struct PopplerAnnot
+  {
+    GObject  parent_instance;
+    Annot   *annot;
+  };
+
+  struct PopplerAnnotMarkup
+  {
+    GObject  parent_instance;
+  };
+
+  struct PopplerRectangle
+  {
+    double x1;
+    double y1;
+    double x2;
+    double y2;
+  };
+
+  // This function does not modify its argument s, but for
+  // compatibility reasons (e.g. getLength in GooString.h before 2015)
+  // with older poppler code, it can't be declared as such.
+  char *_xpoppler_goo_string_to_utf8(/* const */ GooString *s)
+  {
+    char *result;
+
+    if (! s)
+      return NULL;
+
+    if (s->hasUnicodeMarker()) {
+      result = g_convert (s->GET_CSTR () + 2,
+                          s->getLength () - 2,
+                          "UTF-8", "UTF-16BE", NULL, NULL, NULL);
+    } else {
+      int len;
+      gunichar *ucs4_temp;
+      int i;
+
+      len = s->getLength ();
+      ucs4_temp = g_new (gunichar, len + 1);
+      for (i = 0; i < len; ++i) {
+        ucs4_temp[i] = pdfDocEncoding[(unsigned char)s->getChar(i)];
+      }
+      ucs4_temp[i] = 0;
+
+      result = g_ucs4_to_utf8 (ucs4_temp, -1, NULL, NULL, NULL);
+
+      g_free (ucs4_temp);
+    }
+
+    return result;
+  }
+#ifdef HAVE_POPPLER_ANNOT_WRITE
+  // Set the rectangle of an annotation.  It was first added in v0.26.
+  void xpoppler_annot_set_rectangle (PopplerAnnot *a, PopplerRectangle *rectangle)
+  {
+    GooString *state = (GooString*) a->annot->getAppearState ();
+    char *ustate = _xpoppler_goo_string_to_utf8 (state);
+
+    a->annot->setRect (rectangle->x1, rectangle->y1,
+                       rectangle->x2, rectangle->y2);
+    a->annot->setAppearanceState (ustate);
+    g_free (ustate);
+  }
+#endif
+  // This function is in the library, but the enforced date parsing is
+  // incomplete (at least in some versions), because it ignores the
+  // timezone.
+  gchar *xpoppler_annot_markup_get_created (PopplerAnnotMarkup *poppler_annot)
+  {
+    AnnotMarkup *annot;
+    GooString *text;
+
+    g_return_val_if_fail (POPPLER_IS_ANNOT_MARKUP (poppler_annot), NULL);
+
+    annot = static_cast<AnnotMarkup *>(POPPLER_ANNOT (poppler_annot)->annot);
+    text = (GooString*) annot->getDate ();
+
+    return text ? _xpoppler_goo_string_to_utf8 (text) : NULL;
+  }
+}
diff --git a/elpa/pdf-tools-20191031.642/build/server/poppler-versions b/elpa/pdf-tools-20191031.642/build/server/poppler-versions
new file mode 100644 (file)
index 0000000..9d1aea9
--- /dev/null
@@ -0,0 +1,12 @@
+HAVE_POPPLER_ANNOT_WRITE
+0.19.4 solves bug 49080, which potentially corrupts PDF files.
+
+HAVE_POPPLER_FIND_OPTS
+0.22 PopplerFindFlags
+0.22 poppler_page_find_text_with_options
+
+HAVE_POPPLER_ANNOT_SET_RECT
+0.26 Adds function poppler_annot_set_rectangle
+
+HAVE_POPPLER_ANNOT_MARKUP
+0.26 poppler_annot_text_markup_new_{highlight,squiggly,strikeout,underline}
diff --git a/elpa/pdf-tools-20191031.642/build/server/synctex_parser.c b/elpa/pdf-tools-20191031.642/build/server/synctex_parser.c
new file mode 100644 (file)
index 0000000..5b7e8c5
--- /dev/null
@@ -0,0 +1,4414 @@
+/* 
+Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Tue Jun 14 08:23:30 UTC 2011
+
+Version: 1.18
+
+See synctex_parser_readme.txt for more details
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+Acknowledgments:
+----------------
+The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh,
+and significant help from XeTeX developer Jonathan Kew
+
+Nota Bene:
+----------
+If you include or use a significant part of the synctex package into a software,
+I would appreciate to be listed as contributor and see "SyncTeX" highlighted.
+
+Version 1
+Thu Jun 19 09:39:21 UTC 2008
+
+*/
+
+/*  We assume that high level application like pdf viewers will want
+ *  to embed this code as is. We assume that they also have locale.h and setlocale.
+ *  For other tools such as TeXLive tools, you must define SYNCTEX_USE_LOCAL_HEADER,
+ *  when building. You also have to create and customize synctex_parser_local.h to fit your system.
+ *  In particular, the HAVE_LOCALE_H and HAVE_SETLOCALE macros should be properly defined.
+ *  With this design, you should not need to edit this file. */
+
+#   if defined(SYNCTEX_USE_LOCAL_HEADER)
+#       include "synctex_parser_local.h"
+#   else
+#       define HAVE_LOCALE_H 1
+#       define HAVE_SETLOCALE 1
+#       if defined(_MSC_VER) 
+#          define SYNCTEX_INLINE __inline
+#       else
+#          define SYNCTEX_INLINE inline
+#       endif
+#   endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#if defined(HAVE_LOCALE_H)
+#include <locale.h>
+#endif
+
+/*  The data is organized in a graph with multiple entries.
+ *  The root object is a scanner, it is created with the contents on a synctex file.
+ *  Each leaf of the tree is a synctex_node_t object.
+ *  There are 3 subtrees, two of them sharing the same leaves.
+ *  The first tree is the list of input records, where input file names are associated with tags.
+ *  The second tree is the box tree as given by TeX when shipping pages out.
+ *  First level objects are sheets, containing boxes, glues, kerns...
+ *  The third tree allows to browse leaves according to tag and line.
+ */
+
+#include "synctex_parser.h"
+#include "synctex_parser_utils.h"
+
+#define printf(fmt, args...) (fprintf (stderr, (fmt), ## args))
+
+/*  These are the possible extensions of the synctex file */
+const char * synctex_suffix = ".synctex";
+const char * synctex_suffix_gz = ".gz";
+
+/*  each synctex node has a class */
+typedef struct __synctex_class_t _synctex_class_t;
+typedef _synctex_class_t * synctex_class_t;
+
+
+/*  synctex_node_t is a pointer to a node
+ *  _synctex_node is the target of the synctex_node_t pointer
+ *  It is a pseudo object oriented program.
+ *  class is a pointer to the class object the node belongs to.
+ *  implementation is meant to contain the private data of the node
+ *  basically, there are 2 kinds of information: navigation information and
+ *  synctex information. Both will depend on the type of the node,
+ *  thus different nodes will have different private data.
+ *  There is no inheritancy overhead.
+ */
+typedef union _synctex_info_t {
+       int    INT;
+       char * PTR;
+} synctex_info_t;
+
+#   if defined(SYNCTEX_USE_CHARINDEX)
+#       define SYNCTEX_DECLARE_CHARINDEX synctex_charindex_t char_index;
+#       define SYNCTEX_CHARINDEX(NODE) (NODE->char_index)
+#       define SYNCTEX_PRINT_CHARINDEX printf("#%i\n",SYNCTEX_CHARINDEX(node))
+#       define SYNCTEX_DECLARE_CHAR_OFFSET synctex_charindex_t charindex_offset
+#       define SYNCTEX_IMPLEMENT_CHARINDEX(NODE,CORRECTION) NODE->char_index = (synctex_charindex_t)(scanner->charindex_offset+SYNCTEX_CUR-SYNCTEX_START+(CORRECTION));
+#   else
+#       define SYNCTEX_DECLARE_CHARINDEX
+#       define SYNCTEX_CHARINDEX(NODE) 0
+#       define SYNCTEX_PRINT_CHARINDEX printf("\n")
+#       define SYNCTEX_DECLARE_CHAR_OFFSET synctex_charindex_t charindex_offset
+#       define SYNCTEX_IMPLEMENT_CHARINDEX(NODE,CORRECTION)
+#   endif
+
+struct _synctex_node {
+    SYNCTEX_DECLARE_CHARINDEX
+       synctex_class_t class;
+       synctex_info_t * implementation;
+};
+
+/*  Each node of the tree, except the scanner itself belongs to a class.
+ *  The class object is just a struct declaring the owning scanner
+ *  This is a pointer to the scanner as root of the tree.
+ *  The type is used to identify the kind of node.
+ *  The class declares pointers to a creator and a destructor method.
+ *  The log and display fields are used to log and display the node.
+ *  display will also display the child, sibling and parent sibling.
+ *  parent, child and sibling are used to navigate the tree,
+ *  from TeX box hierarchy point of view.
+ *  The friend field points to a method which allows to navigate from friend to friend.
+ *  A friend is a node with very close tag and line numbers.
+ *  Finally, the info field point to a method giving the private node info offset.
+ */
+
+typedef synctex_node_t *(*_synctex_node_getter_t)(synctex_node_t);
+typedef synctex_info_t *(*_synctex_info_getter_t)(synctex_node_t);
+
+struct __synctex_class_t {
+       synctex_scanner_t scanner;
+       int type;
+       synctex_node_t (*new)(synctex_scanner_t scanner);
+       void (*free)(synctex_node_t);
+       void (*log)(synctex_node_t);
+       void (*display)(synctex_node_t);
+       _synctex_node_getter_t parent;
+       _synctex_node_getter_t child;
+       _synctex_node_getter_t sibling;
+       _synctex_node_getter_t friend;
+       _synctex_node_getter_t next_hbox;
+       _synctex_info_getter_t info;
+};
+
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Abstract OBJECTS and METHODS
+#   endif
+
+/*  These macros are shortcuts
+ *  This macro checks if a message can be sent.
+ */
+#   define SYNCTEX_CAN_PERFORM(NODE,SELECTOR)\
+               (NULL!=((((NODE)->class))->SELECTOR))
+
+/*  This macro is some kind of objc_msg_send.
+ *  It takes care of sending the proper message if possible.
+ */
+#   define SYNCTEX_MSG_SEND(NODE,SELECTOR) if (NODE && SYNCTEX_CAN_PERFORM(NODE,SELECTOR)) {\
+               (*((((NODE)->class))->SELECTOR))(NODE);\
+       }
+
+/*  read only safe getter
+ */
+#   define SYNCTEX_GET(NODE,SELECTOR)((NODE && SYNCTEX_CAN_PERFORM(NODE,SELECTOR))?SYNCTEX_GETTER(NODE,SELECTOR)[0]:(NULL))
+
+/*  read/write getter
+ */
+#   define SYNCTEX_GETTER(NODE,SELECTOR)\
+               ((synctex_node_t *)((*((((NODE)->class))->SELECTOR))(NODE)))
+
+#   define SYNCTEX_FREE(NODE) SYNCTEX_MSG_SEND(NODE,free);
+
+/*  Parent getter and setter
+ */
+#   define SYNCTEX_PARENT(NODE) SYNCTEX_GET(NODE,parent)
+#   define SYNCTEX_SET_PARENT(NODE,NEW_PARENT) if (NODE && NEW_PARENT && SYNCTEX_CAN_PERFORM(NODE,parent)){\
+               SYNCTEX_GETTER(NODE,parent)[0]=NEW_PARENT;\
+       }
+
+/*  Child getter and setter
+ */
+#   define SYNCTEX_CHILD(NODE) SYNCTEX_GET(NODE,child)
+#   define SYNCTEX_SET_CHILD(NODE,NEW_CHILD) if (NODE && NEW_CHILD){\
+               SYNCTEX_GETTER(NODE,child)[0]=NEW_CHILD;\
+               SYNCTEX_GETTER(NEW_CHILD,parent)[0]=NODE;\
+       }
+
+/*  Sibling getter and setter
+ */
+#   define SYNCTEX_SIBLING(NODE) SYNCTEX_GET(NODE,sibling)
+#   define SYNCTEX_SET_SIBLING(NODE,NEW_SIBLING) if (NODE && NEW_SIBLING) {\
+               SYNCTEX_GETTER(NODE,sibling)[0]=NEW_SIBLING;\
+               if (SYNCTEX_CAN_PERFORM(NEW_SIBLING,parent) && SYNCTEX_CAN_PERFORM(NODE,parent)) {\
+                       SYNCTEX_GETTER(NEW_SIBLING,parent)[0]=SYNCTEX_GETTER(NODE,parent)[0];\
+               }\
+       }
+/*  Friend getter and setter. A friend is a kern, math, glue or void box node which tag and line numbers are similar.
+ *  This is a first filter on the nodes that avoids testing all of them.
+ *  Friends are used mainly in forward synchronization aka from source to output.
+ */
+#   define SYNCTEX_FRIEND(NODE) SYNCTEX_GET(NODE,friend)
+#   define SYNCTEX_SET_FRIEND(NODE,NEW_FRIEND) if (NODE && NEW_FRIEND){\
+               SYNCTEX_GETTER(NODE,friend)[0]=NEW_FRIEND;\
+       }
+
+/*  Next box getter and setter. The box tree can be traversed from one horizontal box to the other.
+ *  Navigation starts with the deeper boxes.
+ */
+#   define SYNCTEX_NEXT_hbox(NODE) SYNCTEX_GET(NODE,next_hbox)
+#   define SYNCTEX_SET_NEXT_hbox(NODE,NEXT_HBOX) if (NODE && NEXT_HBOX){\
+               SYNCTEX_GETTER(NODE,next_hbox)[0]=NEXT_HBOX;\
+       }
+
+/*  A node is meant to own its child and sibling.
+ *  It is not owned by its parent, unless it is its first child.
+ *  This destructor is for all nodes with children.
+ */
+static void _synctex_free_node(synctex_node_t node) {
+       if (node) {
+               (*((node->class)->sibling))(node);
+               SYNCTEX_FREE(SYNCTEX_SIBLING(node));
+               SYNCTEX_FREE(SYNCTEX_CHILD(node));
+               free(node);
+       }
+       return;
+}
+
+/*  A node is meant to own its child and sibling.
+ *  It is not owned by its parent, unless it is its first child.
+ *  This destructor is for nodes with no child.
+ *  The first sheet is onwned by the scanner.
+ */
+static void _synctex_free_leaf(synctex_node_t node) {
+       if (node) {
+               SYNCTEX_FREE(SYNCTEX_SIBLING(node));
+               free(node);
+       }
+       return;
+}
+#      ifdef   __SYNCTEX_WORK__
+#              include "/usr/include/zlib.h"
+#      else
+#              include <zlib.h>
+#      endif
+
+/*  The synctex scanner is the root object.
+ *  Is is initialized with the contents of a text file or a gzipped file.
+ *  The buffer_? are first used to parse the text.
+ */
+struct __synctex_scanner_t {
+       gzFile file;                  /*  The (possibly compressed) file */
+    SYNCTEX_DECLARE_CHAR_OFFSET;
+       char * buffer_cur;            /*  current location in the buffer */
+       char * buffer_start;          /*  start of the buffer */
+       char * buffer_end;            /*  end of the buffer */
+       char * output_fmt;            /*  dvi or pdf, not yet used */
+       char * output;                /*  the output name used to create the scanner */
+       char * synctex;               /*  the .synctex or .synctex.gz name used to create the scanner */
+       int version;                  /*  1, not yet used */
+       struct {
+               unsigned has_parsed:1;          /*  Whether the scanner has parsed its underlying synctex file. */
+               unsigned reserved:sizeof(unsigned)-1;   /*  alignment */
+       } flags;
+       int pre_magnification;        /*  magnification from the synctex preamble */
+       int pre_unit;                 /*  unit from the synctex preamble */
+       int pre_x_offset;             /*  X offste from the synctex preamble */
+       int pre_y_offset;             /*  Y offset from the synctex preamble */
+       int count;                    /*  Number of records, from the synctex postamble */
+       float unit;                   /*  real unit, from synctex preamble or post scriptum */
+       float x_offset;               /*  X offset, from synctex preamble or post scriptum */
+       float y_offset;               /*  Y Offset, from synctex preamble or post scriptum */
+       synctex_node_t sheet;         /*  The first sheet node, its siblings are the other sheet nodes */
+       synctex_node_t input;         /*  The first input node, its siblings are the other input nodes */
+       int number_of_lists;          /*  The number of friend lists */
+       synctex_node_t * lists_of_friends;/*  The friend lists */
+       _synctex_class_t class[synctex_node_number_of_types]; /*  The classes of the nodes of the scanner */
+};
+
+/*  SYNCTEX_CUR, SYNCTEX_START and SYNCTEX_END are convenient shortcuts
+ */
+#   define SYNCTEX_CUR (scanner->buffer_cur)
+#   define SYNCTEX_START (scanner->buffer_start)
+#   define SYNCTEX_END (scanner->buffer_end)
+
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark OBJECTS, their creators and destructors.
+#   endif
+
+/*  Here, we define the indices for the different informations.
+ *  They are used to declare the size of the implementation.
+ *  For example, if one object uses SYNCTEX_HORIZ_IDX is its size,
+ *  then its info will contain a tag, line, column, horiz but no width nor height nor depth
+ */
+
+/*  The sheet is a first level node.
+ *  It has no parent (the parent is the scanner itself)
+ *  Its sibling points to another sheet.
+ *  Its child points to its first child, in general a box.
+ *  A sheet node contains only one synctex information: the page.
+ *  This is the 1 based page index as given by TeX.
+ */
+/*  The next macros are used to access the node info
+ *  SYNCTEX_INFO(node) points to the first synctex integer or pointer data of node
+ *  SYNCTEX_INFO(node)[index] is the information at index
+ *  for example, the page of a sheet is stored in SYNCTEX_INFO(sheet)[SYNCTEX_PAGE_IDX]
+ */
+#   define SYNCTEX_INFO(NODE) ((*((((NODE)->class))->info))(NODE))
+#   define SYNCTEX_PAGE_IDX 0
+#   define SYNCTEX_PAGE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_PAGE_IDX].INT
+
+/*  This macro defines implementation offsets
+ *  It is only used for pointer values
+ */
+#   define SYNCTEX_MAKE_GET(SYNCTEX_GETTER,OFFSET)\
+static synctex_node_t * SYNCTEX_GETTER (synctex_node_t node) {\
+       return node?(synctex_node_t *)((&((node)->implementation))+OFFSET):NULL;\
+}
+SYNCTEX_MAKE_GET(_synctex_implementation_0,0)
+SYNCTEX_MAKE_GET(_synctex_implementation_1,1)
+SYNCTEX_MAKE_GET(_synctex_implementation_2,2)
+SYNCTEX_MAKE_GET(_synctex_implementation_3,3)
+SYNCTEX_MAKE_GET(_synctex_implementation_4,4)
+SYNCTEX_MAKE_GET(_synctex_implementation_5,5)
+
+typedef struct {
+    SYNCTEX_DECLARE_CHARINDEX
+       synctex_class_t class;
+       synctex_info_t implementation[3+SYNCTEX_PAGE_IDX+1];/*  child, sibling, next box,
+                                *  SYNCTEX_PAGE_IDX */
+} synctex_node_sheet_t;
+
+/*  sheet node creator */
+
+#define DEFINE_synctex_new_NODE(NAME)\
+static synctex_node_t _synctex_new_##NAME(synctex_scanner_t scanner) {\
+       if (scanner) {\
+               synctex_node_t node = _synctex_malloc(sizeof(synctex_node_##NAME##_t));\
+               if (node) {\
+                       SYNCTEX_IMPLEMENT_CHARINDEX(node,0);\
+                       ++SYNCTEX_CUR;\
+                       node->class = scanner->class+synctex_node_type_##NAME;\
+               }\
+               return node;\
+       }\
+       return NULL;\
+}
+DEFINE_synctex_new_NODE(sheet)
+
+static void _synctex_display_sheet(synctex_node_t node);
+static void _synctex_log_sheet(synctex_node_t node);
+
+static _synctex_class_t synctex_class_sheet = {
+       NULL,                       /*  No scanner yet */
+       synctex_node_type_sheet,    /*  Node type */
+       &_synctex_new_sheet,        /*  creator */
+       &_synctex_free_node,        /*  destructor */
+       &_synctex_log_sheet,        /*  log */
+       &_synctex_display_sheet,    /*  display */
+       NULL,                       /*  No parent */
+       &_synctex_implementation_0, /*  child */
+       &_synctex_implementation_1, /*  sibling */
+       NULL,                       /*  No friend */
+       &_synctex_implementation_2, /*  Next hbox */
+       (_synctex_info_getter_t)&_synctex_implementation_3  /*  info */
+};
+
+/*  A box node contains navigation and synctex information
+ *  There are different kind of boxes.
+ *  Only horizontal boxes are treated differently because of their visible size.
+ */
+#   define SYNCTEX_TAG_IDX 0
+#   define SYNCTEX_LINE_IDX (SYNCTEX_TAG_IDX+1)
+#   define SYNCTEX_COLUMN_IDX (SYNCTEX_LINE_IDX+1)
+#   define SYNCTEX_HORIZ_IDX (SYNCTEX_COLUMN_IDX+1)
+#   define SYNCTEX_VERT_IDX (SYNCTEX_HORIZ_IDX+1)
+#   define SYNCTEX_WIDTH_IDX (SYNCTEX_VERT_IDX+1)
+#   define SYNCTEX_HEIGHT_IDX (SYNCTEX_WIDTH_IDX+1)
+#   define SYNCTEX_DEPTH_IDX (SYNCTEX_HEIGHT_IDX+1)
+/*  the corresponding info accessors */
+#   define SYNCTEX_TAG(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_TAG_IDX].INT
+#   define SYNCTEX_LINE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_LINE_IDX].INT
+#   define SYNCTEX_COLUMN(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_COLUMN_IDX].INT
+#   define SYNCTEX_HORIZ(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HORIZ_IDX].INT
+#   define SYNCTEX_VERT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_VERT_IDX].INT
+#   define SYNCTEX_WIDTH(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_WIDTH_IDX].INT
+#   define SYNCTEX_HEIGHT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HEIGHT_IDX].INT
+#   define SYNCTEX_DEPTH(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_DEPTH_IDX].INT
+#   define SYNCTEX_ABS_WIDTH(NODE) ((SYNCTEX_WIDTH(NODE)>0?SYNCTEX_WIDTH(NODE):-SYNCTEX_WIDTH(NODE)))
+#   define SYNCTEX_ABS_HEIGHT(NODE) ((SYNCTEX_HEIGHT(NODE)>0?SYNCTEX_HEIGHT(NODE):-SYNCTEX_HEIGHT(NODE)))
+#   define SYNCTEX_ABS_DEPTH(NODE) ((SYNCTEX_DEPTH(NODE)>0?SYNCTEX_DEPTH(NODE):-SYNCTEX_DEPTH(NODE)))
+
+typedef struct {
+    SYNCTEX_DECLARE_CHARINDEX
+       synctex_class_t class;
+       synctex_info_t implementation[5+SYNCTEX_DEPTH_IDX+1]; /*  parent,child,sibling,friend,next box,
+                                                       *  SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
+                                                               *  SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH */
+} synctex_node_vbox_t;
+
+/*  vertical box node creator */
+DEFINE_synctex_new_NODE(vbox)
+
+static void _synctex_log_vbox(synctex_node_t node);
+static void _synctex_display_vbox(synctex_node_t node);
+
+/*  These are static class objects, each scanner will make a copy of them and setup the scanner field.
+ */
+static _synctex_class_t synctex_class_vbox = {
+       NULL,                       /*  No scanner yet */
+       synctex_node_type_vbox,     /*  Node type */
+       &_synctex_new_vbox,         /*  creator */
+       &_synctex_free_node,        /*  destructor */
+       &_synctex_log_vbox,          /*  log */
+       &_synctex_display_vbox,     /*  display */
+       &_synctex_implementation_0, /*  parent */
+       &_synctex_implementation_1, /*  child */
+       &_synctex_implementation_2, /*  sibling */
+       &_synctex_implementation_3, /*  friend */
+       &_synctex_implementation_4, /*  next hbox */
+       (_synctex_info_getter_t)&_synctex_implementation_5
+};
+
+/*  Horizontal boxes must contain visible size, because 0 width does not mean emptiness.
+ *  They also contain an average of the line numbers of the containing nodes. */
+#   define SYNCTEX_MEAN_LINE_IDX (SYNCTEX_DEPTH_IDX+1)
+#   define SYNCTEX_NODE_WEIGHT_IDX (SYNCTEX_MEAN_LINE_IDX+1)
+#   define SYNCTEX_HORIZ_V_IDX (SYNCTEX_NODE_WEIGHT_IDX+1)
+#   define SYNCTEX_VERT_V_IDX (SYNCTEX_HORIZ_V_IDX+1)
+#   define SYNCTEX_WIDTH_V_IDX (SYNCTEX_VERT_V_IDX+1)
+#   define SYNCTEX_HEIGHT_V_IDX (SYNCTEX_WIDTH_V_IDX+1)
+#   define SYNCTEX_DEPTH_V_IDX (SYNCTEX_HEIGHT_V_IDX+1)
+/*  the corresponding info accessors */
+#   define SYNCTEX_MEAN_LINE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_MEAN_LINE_IDX].INT
+#   define SYNCTEX_NODE_WEIGHT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_NODE_WEIGHT_IDX].INT
+#   define SYNCTEX_HORIZ_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HORIZ_V_IDX].INT
+#   define SYNCTEX_VERT_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_VERT_V_IDX].INT
+#   define SYNCTEX_WIDTH_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_WIDTH_V_IDX].INT
+#   define SYNCTEX_HEIGHT_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HEIGHT_V_IDX].INT
+#   define SYNCTEX_DEPTH_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_DEPTH_V_IDX].INT
+#   define SYNCTEX_ABS_WIDTH_V(NODE) ((SYNCTEX_WIDTH_V(NODE)>0?SYNCTEX_WIDTH_V(NODE):-SYNCTEX_WIDTH_V(NODE)))
+#   define SYNCTEX_ABS_HEIGHT_V(NODE) ((SYNCTEX_HEIGHT_V(NODE)>0?SYNCTEX_HEIGHT_V(NODE):-SYNCTEX_HEIGHT_V(NODE)))
+#   define SYNCTEX_ABS_DEPTH_V(NODE) ((SYNCTEX_DEPTH_V(NODE)>0?SYNCTEX_DEPTH_V(NODE):-SYNCTEX_DEPTH_V(NODE)))
+
+typedef struct {
+    SYNCTEX_DECLARE_CHARINDEX
+       synctex_class_t class;
+       synctex_info_t implementation[5+SYNCTEX_DEPTH_V_IDX+1]; /*parent,child,sibling,friend,next box,
+                                               *  SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
+                                               *  SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH,
+                        *  SYNCTEX_MEAN_LINE,SYNCTEX_NODE_WEIGHT,
+                                               *  SYNCTEX_HORIZ_V,SYNCTEX_VERT_V,SYNCTEX_WIDTH_V,SYNCTEX_HEIGHT_V,SYNCTEX_DEPTH_V*/
+} synctex_node_hbox_t;
+
+/*  horizontal box node creator */
+DEFINE_synctex_new_NODE(hbox)
+
+static void _synctex_display_hbox(synctex_node_t node);
+static void _synctex_log_hbox(synctex_node_t node);
+
+
+static _synctex_class_t synctex_class_hbox = {
+       NULL,                       /*  No scanner yet */
+       synctex_node_type_hbox,     /*  Node type */
+       &_synctex_new_hbox,         /*  creator */
+       &_synctex_free_node,        /*  destructor */
+       &_synctex_log_hbox,    /*  log */
+       &_synctex_display_hbox,     /*  display */
+       &_synctex_implementation_0, /*  parent */
+       &_synctex_implementation_1, /*  child */
+       &_synctex_implementation_2, /*  sibling */
+       &_synctex_implementation_3, /*  friend */
+       &_synctex_implementation_4, /*  next hbox */
+       (_synctex_info_getter_t)&_synctex_implementation_5
+};
+
+/*  This void box node implementation is either horizontal or vertical
+ *  It does not contain a child field.
+ */
+typedef struct {
+    SYNCTEX_DECLARE_CHARINDEX
+       synctex_class_t class;
+       synctex_info_t implementation[3+SYNCTEX_DEPTH_IDX+1]; /*  parent,sibling,friend,
+                         *  SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
+                                         *  SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH*/
+} synctex_node_void_vbox_t;
+
+/*  vertical void box node creator */
+DEFINE_synctex_new_NODE(void_vbox)
+
+static void _synctex_log_void_box(synctex_node_t node);
+static void _synctex_display_void_vbox(synctex_node_t node);
+
+static _synctex_class_t synctex_class_void_vbox = {
+       NULL,                       /*  No scanner yet */
+       synctex_node_type_void_vbox,/*  Node type */
+       &_synctex_new_void_vbox,    /*  creator */
+       &_synctex_free_node,        /*  destructor */
+       &_synctex_log_void_box,     /*  log */
+       &_synctex_display_void_vbox,/*  display */
+       &_synctex_implementation_0, /*  parent */
+       NULL,                       /*  No child */
+       &_synctex_implementation_1, /*  sibling */
+       &_synctex_implementation_2, /*  friend */
+       NULL,                                           /*  No next hbox */
+       (_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+typedef synctex_node_void_vbox_t synctex_node_void_hbox_t;
+
+/*  horizontal void box node creator */
+DEFINE_synctex_new_NODE(void_hbox)
+
+static void _synctex_display_void_hbox(synctex_node_t node);
+
+static _synctex_class_t synctex_class_void_hbox = {
+       NULL,                       /*  No scanner yet */
+       synctex_node_type_void_hbox,/*  Node type */
+       &_synctex_new_void_hbox,    /*  creator */
+       &_synctex_free_node,        /*  destructor */
+       &_synctex_log_void_box,     /*  log */
+       &_synctex_display_void_hbox,/*  display */
+       &_synctex_implementation_0, /*  parent */
+       NULL,                       /*  No child */
+       &_synctex_implementation_1, /*  sibling */
+       &_synctex_implementation_2, /*  friend */
+       NULL,                                           /*  No next hbox */
+       (_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+/*  The medium nodes correspond to kern, glue, penalty and math nodes.
+ *  In LuaTeX, the size of the nodes may have changed.  */
+typedef struct {
+    SYNCTEX_DECLARE_CHARINDEX
+       synctex_class_t class;
+       synctex_info_t implementation[3+SYNCTEX_WIDTH_IDX+1]; /*  parent,sibling,friend,
+                         *  SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
+                                         *  SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH */
+} synctex_node_medium_t;
+
+#define SYNCTEX_IS_BOX(NODE)\
+       ((NODE->class->type == synctex_node_type_vbox)\
+       || (NODE->class->type == synctex_node_type_void_vbox)\
+       || (NODE->class->type == synctex_node_type_hbox)\
+       || (NODE->class->type == synctex_node_type_void_hbox))
+       
+#define SYNCTEX_HAS_CHILDREN(NODE) (NODE && SYNCTEX_CHILD(NODE))
+       
+static void _synctex_log_medium_node(synctex_node_t node);
+
+typedef synctex_node_medium_t synctex_node_math_t;
+
+/*  math node creator */
+DEFINE_synctex_new_NODE(math)
+
+static void _synctex_display_math(synctex_node_t node);
+
+static _synctex_class_t synctex_class_math = {
+       NULL,                       /*  No scanner yet */
+       synctex_node_type_math,     /*  Node type */
+       &_synctex_new_math,         /*  creator */
+       &_synctex_free_leaf,        /*  destructor */
+       &_synctex_log_medium_node,  /*  log */
+       &_synctex_display_math,     /*  display */
+       &_synctex_implementation_0, /*  parent */
+       NULL,                       /*  No child */
+       &_synctex_implementation_1, /*  sibling */
+       &_synctex_implementation_2, /*  friend */
+       NULL,                       /*  No next hbox */
+       (_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+typedef synctex_node_medium_t synctex_node_kern_t;
+
+/*  kern node creator */
+DEFINE_synctex_new_NODE(kern)
+
+static void _synctex_display_kern(synctex_node_t node);
+
+static _synctex_class_t synctex_class_kern = {
+       NULL,                       /*  No scanner yet */
+       synctex_node_type_kern,     /*  Node type */
+       &_synctex_new_kern,         /*  creator */
+       &_synctex_free_leaf,        /*  destructor */
+       &_synctex_log_medium_node,  /*  log */
+       &_synctex_display_kern,     /*  display */
+       &_synctex_implementation_0, /*  parent */
+       NULL,                       /*  No child */
+       &_synctex_implementation_1, /*  sibling */
+       &_synctex_implementation_2, /*  friend */
+       NULL,                       /*  No next hbox */
+       (_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+/*  The small nodes correspond to glue and boundary nodes.  */
+typedef struct {
+    SYNCTEX_DECLARE_CHARINDEX
+       synctex_class_t class;
+       synctex_info_t implementation[3+SYNCTEX_VERT_IDX+1]; /*  parent,sibling,friend,
+                         *  SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
+                                         *  SYNCTEX_HORIZ,SYNCTEX_VERT */
+} synctex_node_small_t;
+
+static void _synctex_log_small_node(synctex_node_t node);
+
+/*  glue node creator */
+typedef synctex_node_small_t synctex_node_glue_t;
+DEFINE_synctex_new_NODE(glue)
+static void _synctex_display_glue(synctex_node_t node);
+
+static _synctex_class_t synctex_class_glue = {
+       NULL,                       /*  No scanner yet */
+       synctex_node_type_glue,     /*  Node type */
+       &_synctex_new_glue,         /*  creator */
+       &_synctex_free_leaf,        /*  destructor */
+       &_synctex_log_medium_node,  /*  log */
+       &_synctex_display_glue,     /*  display */
+       &_synctex_implementation_0, /*  parent */
+       NULL,                       /*  No child */
+       &_synctex_implementation_1, /*  sibling */
+       &_synctex_implementation_2, /*  friend */
+       NULL,                       /*  No next hbox */
+       (_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+/*  boundary node creator */
+typedef synctex_node_small_t synctex_node_boundary_t;
+DEFINE_synctex_new_NODE(boundary)
+
+static void _synctex_display_boundary(synctex_node_t node);
+
+static _synctex_class_t synctex_class_boundary = {
+       NULL,                       /*  No scanner yet */
+       synctex_node_type_boundary,     /*  Node type */
+       &_synctex_new_boundary, /*  creator */
+       &_synctex_free_leaf,        /*  destructor */
+       &_synctex_log_small_node,   /*  log */
+       &_synctex_display_boundary,     /*  display */
+       &_synctex_implementation_0, /*  parent */
+       NULL,                       /*  No child */
+       &_synctex_implementation_1, /*  sibling */
+       &_synctex_implementation_2, /*  friend */
+       NULL,                       /*  No next hbox */
+       (_synctex_info_getter_t)&_synctex_implementation_3
+};
+
+#   define SYNCTEX_NAME_IDX (SYNCTEX_TAG_IDX+1)
+#   define SYNCTEX_NAME(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_NAME_IDX].PTR
+
+/*  Input nodes only know about their sibling, which is another input node.
+ *  The synctex information is the SYNCTEX_TAG and SYNCTEX_NAME*/
+typedef struct {
+    SYNCTEX_DECLARE_CHARINDEX
+       synctex_class_t class;
+       synctex_info_t implementation[1+SYNCTEX_NAME_IDX+1]; /*  sibling,
+                                 *  SYNCTEX_TAG,SYNCTEX_NAME */
+} synctex_input_t;
+
+#   define SYNCTEX_INPUT_MARK "Input:"
+static synctex_node_t _synctex_new_input(synctex_scanner_t scanner) {
+       if (scanner) {
+               synctex_node_t node = _synctex_malloc(sizeof(synctex_input_t));
+               if (node) {
+            SYNCTEX_IMPLEMENT_CHARINDEX(node,strlen(SYNCTEX_INPUT_MARK));
+                       node->class = scanner->class+synctex_node_type_input;
+               }
+               return node;
+       }
+       return NULL;
+}
+
+static void _synctex_free_input(synctex_node_t node){
+       if (node) {
+               SYNCTEX_FREE(SYNCTEX_SIBLING(node));
+               free(SYNCTEX_NAME(node));
+               free(node);
+       }
+}
+
+static void _synctex_display_input(synctex_node_t node);
+static void _synctex_log_input(synctex_node_t node);
+
+static _synctex_class_t synctex_class_input = {
+       NULL,                       /*  No scanner yet */
+       synctex_node_type_input,    /*  Node type */
+       &_synctex_new_input,        /*  creator */
+       &_synctex_free_input,       /*  destructor */
+       &_synctex_log_input,        /*  log */
+       &_synctex_display_input,    /*  display */
+       NULL,                       /*  No parent */
+       NULL,                       /*  No child */
+       &_synctex_implementation_0, /*  sibling */
+       NULL,                       /*  No friend */
+       NULL,                       /*  No next hbox */
+       (_synctex_info_getter_t)&_synctex_implementation_1
+};
+
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Navigation
+#   endif
+synctex_node_t synctex_node_parent(synctex_node_t node)
+{
+       return SYNCTEX_PARENT(node);
+}
+synctex_node_t synctex_node_sheet(synctex_node_t node)
+{
+       while(node && node->class->type != synctex_node_type_sheet) {
+               node = SYNCTEX_PARENT(node);
+       }
+       /*  exit the while loop either when node is NULL or node is a sheet */
+       return node;
+}
+synctex_node_t synctex_node_child(synctex_node_t node)
+{
+       return SYNCTEX_CHILD(node);
+}
+synctex_node_t synctex_node_sibling(synctex_node_t node)
+{
+       return SYNCTEX_SIBLING(node);
+}
+synctex_node_t synctex_node_next(synctex_node_t node) {
+       if (SYNCTEX_CHILD(node)) {
+               return SYNCTEX_CHILD(node);
+       }
+sibling:
+       if (SYNCTEX_SIBLING(node)) {
+               return SYNCTEX_SIBLING(node);
+       }
+       if ((node = SYNCTEX_PARENT(node))) {
+               if (node->class->type == synctex_node_type_sheet) {/*  EXC_BAD_ACCESS? */
+                       return NULL;
+               }
+               goto sibling;
+       }
+       return NULL;
+}
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark CLASS
+#   endif
+
+/*  Public node accessor: the type  */
+synctex_node_type_t synctex_node_type(synctex_node_t node) {
+       if (node) {
+               return (((node)->class))->type;
+       }
+       return synctex_node_type_error;
+}
+
+/*  Public node accessor: the human readable type  */
+const char * synctex_node_isa(synctex_node_t node) {
+static const char * isa[synctex_node_number_of_types] =
+               {"Not a node","input","sheet","vbox","void vbox","hbox","void hbox","kern","glue","math","boundary"};
+       return isa[synctex_node_type(node)];
+}
+
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark SYNCTEX_LOG
+#   endif
+
+#   define SYNCTEX_LOG(NODE) SYNCTEX_MSG_SEND(NODE,log)
+
+/*  Public node logger  */
+void synctex_node_log(synctex_node_t node) {
+       SYNCTEX_LOG(node);
+}
+
+static void _synctex_log_input(synctex_node_t node) {
+       if (node) {
+        printf("%s:%i,%s",synctex_node_isa(node),SYNCTEX_TAG(node),SYNCTEX_NAME(node));
+        printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+    }
+}
+
+static void _synctex_log_sheet(synctex_node_t node) {
+       if (node) {
+               printf("%s:%i",synctex_node_isa(node),SYNCTEX_PAGE(node));
+        SYNCTEX_PRINT_CHARINDEX;
+               printf("SELF:%p",(void *)node);
+               printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+               printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+               printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+               printf(" SYNCTEX_FRIEND:%p",(void *)SYNCTEX_FRIEND(node));
+        printf(" SYNCTEX_NEXT_hbox:%p\n",(void *)SYNCTEX_NEXT_hbox(node));
+       }
+}
+
+static void _synctex_log_small_node(synctex_node_t node) {
+       if (node) {
+        printf("%s:%i,%i:%i,%i",
+            synctex_node_isa(node),
+            SYNCTEX_TAG(node),
+            SYNCTEX_LINE(node),
+            SYNCTEX_HORIZ(node),
+            SYNCTEX_VERT(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        printf("SELF:%p",(void *)node);
+        printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+        printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+        printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+        printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
+    }
+}
+
+static void _synctex_log_medium_node(synctex_node_t node) {
+       if (node) {
+        printf("%s:%i,%i:%i,%i:%i",
+            synctex_node_isa(node),
+            SYNCTEX_TAG(node),
+            SYNCTEX_LINE(node),
+            SYNCTEX_HORIZ(node),
+            SYNCTEX_VERT(node),
+            SYNCTEX_WIDTH(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        printf("SELF:%p",(void *)node);
+        printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+        printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+        printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+        printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
+    }
+}
+
+static void _synctex_log_void_box(synctex_node_t node) {
+       if (node) {
+        printf("%s",synctex_node_isa(node));
+        printf(":%i",SYNCTEX_TAG(node));
+        printf(",%i",SYNCTEX_LINE(node));
+        printf(",%i",0);
+        printf(":%i",SYNCTEX_HORIZ(node));
+        printf(",%i",SYNCTEX_VERT(node));
+        printf(":%i",SYNCTEX_WIDTH(node));
+        printf(",%i",SYNCTEX_HEIGHT(node));
+        printf(",%i",SYNCTEX_DEPTH(node));
+        SYNCTEX_PRINT_CHARINDEX;
+        printf("SELF:%p",(void *)node);
+        printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+        printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+        printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+        printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
+    }
+}
+
+static void _synctex_log_vbox(synctex_node_t node) {
+       if (node) {
+        printf("%s",synctex_node_isa(node));
+        printf(":%i",SYNCTEX_TAG(node));
+        printf(",%i",SYNCTEX_LINE(node));
+        printf(",%i",0);
+        printf(":%i",SYNCTEX_HORIZ(node));
+        printf(",%i",SYNCTEX_VERT(node));
+        printf(":%i",SYNCTEX_WIDTH(node));
+        printf(",%i",SYNCTEX_HEIGHT(node));
+        printf(",%i",SYNCTEX_DEPTH(node));
+        SYNCTEX_PRINT_CHARINDEX;
+        printf("SELF:%p",(void *)node);
+        printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+        printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+        printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+        printf(" SYNCTEX_FRIEND:%p",(void *)SYNCTEX_FRIEND(node));
+        printf(" SYNCTEX_NEXT_hbox:%p\n",(void *)SYNCTEX_NEXT_hbox(node));
+    }
+}
+
+static void _synctex_log_hbox(synctex_node_t node) {
+       if (node) {
+        printf("%s",synctex_node_isa(node));
+        printf(":%i",SYNCTEX_TAG(node));
+        printf(",%i~%i*%i",SYNCTEX_LINE(node),SYNCTEX_MEAN_LINE(node),SYNCTEX_NODE_WEIGHT(node));
+        printf(",%i",0);
+        printf(":%i",SYNCTEX_HORIZ(node));
+        printf(",%i",SYNCTEX_VERT(node));
+        printf(":%i",SYNCTEX_WIDTH(node));
+        printf(",%i",SYNCTEX_HEIGHT(node));
+        printf(",%i",SYNCTEX_DEPTH(node));
+        printf("/%i",SYNCTEX_HORIZ_V(node));
+        printf(",%i",SYNCTEX_VERT_V(node));
+        printf(":%i",SYNCTEX_WIDTH_V(node));
+        printf(",%i",SYNCTEX_HEIGHT_V(node));
+        printf(",%i",SYNCTEX_DEPTH_V(node));
+        SYNCTEX_PRINT_CHARINDEX;
+        printf("SELF:%p",(void *)node);
+        printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
+        printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
+        printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
+        printf(" SYNCTEX_FRIEND:%p",(void *)SYNCTEX_FRIEND(node));
+        printf(" SYNCTEX_NEXT_hbox:%p\n",(void *)SYNCTEX_NEXT_hbox(node));
+    }
+}
+
+#   define SYNCTEX_DISPLAY(NODE) SYNCTEX_MSG_SEND(NODE,display)
+
+void synctex_node_display(synctex_node_t node) {
+       SYNCTEX_DISPLAY(node);
+}
+
+static void _synctex_display_input(synctex_node_t node) {
+    if (node) {
+        printf("....Input:%i:%s",
+            SYNCTEX_TAG(node),
+            SYNCTEX_NAME(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+    }
+}
+
+static void _synctex_display_sheet(synctex_node_t node) {
+       if (node) {
+               printf("....{%i",SYNCTEX_PAGE(node));
+        SYNCTEX_PRINT_CHARINDEX;
+               SYNCTEX_DISPLAY(SYNCTEX_CHILD(node));
+               printf("....}\n");
+               SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+       }
+}
+
+static void _synctex_display_vbox(synctex_node_t node) {
+    if (node) {
+        printf("....[%i,%i:%i,%i:%i,%i,%i",
+            SYNCTEX_TAG(node),
+            SYNCTEX_LINE(node),
+            SYNCTEX_HORIZ(node),
+            SYNCTEX_VERT(node),
+            SYNCTEX_WIDTH(node),
+            SYNCTEX_HEIGHT(node),
+            SYNCTEX_DEPTH(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        SYNCTEX_DISPLAY(SYNCTEX_CHILD(node));
+        printf("....]\n");
+        SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+    }
+}
+
+static void _synctex_display_hbox(synctex_node_t node) {
+    if (node) {
+        printf("....(%i,%i~%i*%i:%i,%i:%i,%i,%i",
+            SYNCTEX_TAG(node),
+            SYNCTEX_LINE(node),
+            SYNCTEX_MEAN_LINE(node),
+            SYNCTEX_NODE_WEIGHT(node),
+            SYNCTEX_HORIZ(node),
+            SYNCTEX_VERT(node),
+            SYNCTEX_WIDTH(node),
+            SYNCTEX_HEIGHT(node),
+            SYNCTEX_DEPTH(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        SYNCTEX_DISPLAY(SYNCTEX_CHILD(node));
+        printf("....)\n");
+        SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+    }
+}
+
+static void _synctex_display_void_vbox(synctex_node_t node) {
+    if (node) {
+        printf("....v%i,%i;%i,%i:%i,%i,%i",
+            SYNCTEX_TAG(node),
+            SYNCTEX_LINE(node),
+            SYNCTEX_HORIZ(node),
+            SYNCTEX_VERT(node),
+            SYNCTEX_WIDTH(node),
+            SYNCTEX_HEIGHT(node),
+            SYNCTEX_DEPTH(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+    }
+}
+
+static void _synctex_display_void_hbox(synctex_node_t node) {
+    if (node) {
+        printf("....h%i,%i:%i,%i:%i,%i,%i",
+            SYNCTEX_TAG(node),
+            SYNCTEX_LINE(node),
+            SYNCTEX_HORIZ(node),
+            SYNCTEX_VERT(node),
+            SYNCTEX_WIDTH(node),
+            SYNCTEX_HEIGHT(node),
+            SYNCTEX_DEPTH(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+    }
+}
+
+static void _synctex_display_glue(synctex_node_t node) {
+    if (node) {
+        printf("....glue:%i,%i:%i,%i",
+            SYNCTEX_TAG(node),
+            SYNCTEX_LINE(node),
+            SYNCTEX_HORIZ(node),
+            SYNCTEX_VERT(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+    }
+}
+
+static void _synctex_display_math(synctex_node_t node) {
+    if (node) {
+        printf("....math:%i,%i:%i,%i",
+            SYNCTEX_TAG(node),
+            SYNCTEX_LINE(node),
+            SYNCTEX_HORIZ(node),
+            SYNCTEX_VERT(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+    }
+}
+
+static void _synctex_display_kern(synctex_node_t node) {
+    if (node) {
+        printf("....kern:%i,%i:%i,%i:%i",
+            SYNCTEX_TAG(node),
+            SYNCTEX_LINE(node),
+            SYNCTEX_HORIZ(node),
+            SYNCTEX_VERT(node),
+            SYNCTEX_WIDTH(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+    }
+}
+
+static void _synctex_display_boundary(synctex_node_t node) {
+    if (node) {
+        printf("....boundary:%i,%i:%i,%i",
+            SYNCTEX_TAG(node),
+            SYNCTEX_LINE(node),
+            SYNCTEX_HORIZ(node),
+            SYNCTEX_VERT(node));
+            SYNCTEX_PRINT_CHARINDEX;
+        SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
+    }
+}
+
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark SCANNER
+#   endif
+
+/*  Here are gathered all the possible status that the next scanning functions will return.
+ *  All these functions return a status, and pass their result through pointers.
+ *  Negative values correspond to errors.
+ *  The management of the buffer is causing some significant overhead.
+ *  Every function that may access the buffer returns a status related to the buffer and file state.
+ *  status >= SYNCTEX_STATUS_OK means the function worked as expected
+ *  status < SYNCTEX_STATUS_OK means the function did not work as expected
+ *  status == SYNCTEX_STATUS_NOT_OK means the function did not work as expected but there is still some material to parse.
+ *  status == SYNCTEX_STATUS_EOF means the function did not work as expected and there is no more material.
+ *  status<SYNCTEX_STATUS_EOF means an error
+ */
+
+/*  When the end of the synctex file has been reached: */
+#   define SYNCTEX_STATUS_EOF 0
+/*  When the function could not return the value it was asked for: */
+#   define SYNCTEX_STATUS_NOT_OK (SYNCTEX_STATUS_EOF+1)
+/*  When the function returns the value it was asked for: */
+#   define SYNCTEX_STATUS_OK (SYNCTEX_STATUS_NOT_OK+1)
+/*  Generic error: */
+#   define SYNCTEX_STATUS_ERROR -1
+/*  Parameter error: */
+#   define SYNCTEX_STATUS_BAD_ARGUMENT -2
+
+#   define SYNCTEX_FILE (scanner->file)
+
+/*  Actually, the minimum buffer size is driven by integer and float parsing.
+ *  ±0.123456789e123
+ */
+#   define SYNCTEX_BUFFER_MIN_SIZE 16
+#   define SYNCTEX_BUFFER_SIZE 32768
+
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Prototypes
+#   endif
+synctex_status_t _synctex_buffer_get_available_size(synctex_scanner_t scanner, size_t * size_ptr);
+synctex_status_t _synctex_next_line(synctex_scanner_t scanner);
+synctex_status_t _synctex_match_string(synctex_scanner_t scanner, const char * the_string);
+synctex_status_t _synctex_decode_int(synctex_scanner_t scanner, int* value_ref);
+synctex_status_t _synctex_decode_string(synctex_scanner_t scanner, char ** value_ref);
+synctex_status_t _synctex_scan_input(synctex_scanner_t scanner);
+synctex_status_t _synctex_scan_preamble(synctex_scanner_t scanner);
+synctex_status_t _synctex_scan_float_and_dimension(synctex_scanner_t scanner, float * value_ref);
+synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_t scanner);
+synctex_status_t _synctex_scan_postamble(synctex_scanner_t scanner);
+synctex_status_t _synctex_setup_visible_box(synctex_node_t box);
+synctex_status_t _synctex_hbox_setup_visible(synctex_node_t node,int h, int v);
+synctex_status_t _synctex_scan_sheet(synctex_scanner_t scanner, synctex_node_t parent);
+synctex_status_t _synctex_scan_nested_sheet(synctex_scanner_t scanner);
+synctex_status_t _synctex_scan_content(synctex_scanner_t scanner);
+int synctex_scanner_pre_x_offset(synctex_scanner_t scanner);
+int synctex_scanner_pre_y_offset(synctex_scanner_t scanner);
+const char * synctex_scanner_get_output_fmt(synctex_scanner_t scanner);
+int _synctex_node_is_box(synctex_node_t node);
+
+/*  Try to ensure that the buffer contains at least size bytes.
+ *  Passing a huge size argument means the whole buffer length.
+ *  Passing a null size argument means return the available buffer length, without reading the file.
+ *  In that case, the return status is always SYNCTEX_STATUS_OK unless the given scanner is NULL,
+ *  in which case, SYNCTEX_STATUS_BAD_ARGUMENT is returned.
+ *  The value returned in size_ptr is the number of bytes now available in the buffer.
+ *  This is a nonnegative integer, it may take the value 0.
+ *  It is the responsibility of the caller to test whether this size is conforming to its needs.
+ *  Negative values may return in case of error, actually
+ *  when there was an error reading the synctex file. */
+synctex_status_t _synctex_buffer_get_available_size(synctex_scanner_t scanner, size_t * size_ptr) {
+       size_t available = 0;
+       if (NULL == scanner || NULL == size_ptr) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+#   define size (* size_ptr)
+       if (size>SYNCTEX_BUFFER_SIZE){
+               size = SYNCTEX_BUFFER_SIZE;
+       }
+       available = SYNCTEX_END - SYNCTEX_CUR; /*  available is the number of unparsed chars in the buffer */
+       if (size<=available) {
+               /*  There are already sufficiently many characters in the buffer */
+               size = available;
+               return SYNCTEX_STATUS_OK;
+       }
+       if (SYNCTEX_FILE) {
+               /*  Copy the remaining part of the buffer to the beginning,
+                *  then read the next part of the file */
+               int already_read = 0;
+#   if defined(SYNCTEX_USE_CHARINDEX)
+        scanner->charindex_offset += SYNCTEX_CUR - SYNCTEX_START;
+#   endif
+               if (available) {
+                       memmove(SYNCTEX_START, SYNCTEX_CUR, available);
+               }
+               SYNCTEX_CUR = SYNCTEX_START + available; /*  the next character after the move, will change. */
+               /*  Fill the buffer up to its end */
+               already_read = gzread(SYNCTEX_FILE,(void *)SYNCTEX_CUR,(int)(SYNCTEX_BUFFER_SIZE - available));
+               if (already_read>0) {
+                       /*  We assume that 0<already_read<=SYNCTEX_BUFFER_SIZE - available, such that
+                        *  SYNCTEX_CUR + already_read = SYNCTEX_START + available  + already_read <= SYNCTEX_START + SYNCTEX_BUFFER_SIZE */
+                       SYNCTEX_END = SYNCTEX_CUR + already_read;
+                       /*  If the end of the file was reached, all the required SYNCTEX_BUFFER_SIZE - available
+                        *  may not be filled with values from the file.
+                        *  In that case, the buffer should stop properly after already_read characters. */
+                       * SYNCTEX_END = '\0';
+                       SYNCTEX_CUR = SYNCTEX_START;
+                       size = SYNCTEX_END - SYNCTEX_CUR; /* == old available + already_read*/
+                       return SYNCTEX_STATUS_OK; /*  May be available is less than size, the caller will have to test. */
+               } else if (0>already_read) {
+                       /*  There is a possible error in reading the file */
+                       int errnum = 0;
+                       const char * error_string = gzerror(SYNCTEX_FILE, &errnum);
+                       if (Z_ERRNO == errnum) {
+                               /*  There is an error in zlib caused by the file system */
+                               _synctex_error("gzread error from the file system (%i)",errno);
+                return SYNCTEX_STATUS_ERROR;
+                       } else if (errnum) {
+                               _synctex_error("gzread error (%i:%i,%s)",already_read,errnum,error_string);
+                return SYNCTEX_STATUS_ERROR;
+                       }
+               }
+        /*  Nothing was read, we are at the end of the file. */
+        gzclose(SYNCTEX_FILE);
+        SYNCTEX_FILE = NULL;
+        SYNCTEX_END = SYNCTEX_CUR;
+        SYNCTEX_CUR = SYNCTEX_START;
+        * SYNCTEX_END = '\0';/*  Terminate the string properly.*/
+        size = SYNCTEX_END - SYNCTEX_CUR;
+        return SYNCTEX_STATUS_EOF; /*  there might be a bit of text left */
+    }
+       /*  We cannot enlarge the buffer because the end of the file was reached. */
+       size = available;
+       return SYNCTEX_STATUS_EOF;
+#   undef size
+}
+
+/*  Used when parsing the synctex file.
+ *  Advance to the next character starting a line.
+ *  Actually, only '\n' is recognized as end of line marker.
+ *  On normal completion, the returned value is the number of unparsed characters available in the buffer.
+ *  In general, it is a positive value, 0 meaning that the end of file was reached.
+ *  -1 is returned in case of error, actually because there was an error while feeding the buffer.
+ *  When the function returns with no error, SYNCTEX_CUR points to the first character of the next line, if any.
+ *  J. Laurens: Sat May 10 07:52:31 UTC 2008
+ */
+synctex_status_t _synctex_next_line(synctex_scanner_t scanner) {
+       synctex_status_t status = SYNCTEX_STATUS_OK;
+       size_t available = 0;
+       if (NULL == scanner) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+infinite_loop:
+       while(SYNCTEX_CUR<SYNCTEX_END) {
+               if (*SYNCTEX_CUR == '\n') {
+                       ++SYNCTEX_CUR;
+                       available = 1;
+                       return _synctex_buffer_get_available_size(scanner, &available);
+               }
+               ++SYNCTEX_CUR;
+       }
+       /*  Here, we have SYNCTEX_CUR == SYNCTEX_END, such that the next call to _synctex_buffer_get_available_size
+        *  will read another bunch of synctex file. Little by little, we advance to the end of the file. */
+       available = 1;
+       status = _synctex_buffer_get_available_size(scanner, &available);
+       if (status<=0) {
+               return status;
+       }
+       goto infinite_loop;
+}
+
+/*  Scan the given string.
+ *  Both scanner and the_string must not be NULL, and the_string must not be 0 length.
+ *  SYNCTEX_STATUS_OK is returned if the string is found,
+ *  SYNCTEX_STATUS_EOF is returned when the EOF is reached,
+ *  SYNCTEX_STATUS_NOT_OK is returned is the string is not found,
+ *  an error status is returned otherwise.
+ *  This is a critical method because buffering renders things more difficult.
+ *  The given string might be as long as the maximum size_t value.
+ *  As side effect, the buffer state may have changed if the given argument string can't fit into the buffer.
+ */
+synctex_status_t _synctex_match_string(synctex_scanner_t scanner, const char * the_string) {
+       size_t tested_len = 0; /*  the number of characters at the beginning of the_string that match */
+       size_t remaining_len = 0; /*  the number of remaining characters of the_string that should match */
+       size_t available = 0;
+       synctex_status_t status = 0;
+       if (NULL == scanner || NULL == the_string) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       remaining_len = strlen(the_string); /*  All the_string should match */
+       if (0 == remaining_len) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       /*  How many characters available in the buffer? */
+       available = remaining_len;
+       status = _synctex_buffer_get_available_size(scanner,&available);
+       if (status<SYNCTEX_STATUS_EOF) {
+               return status;
+       }
+       /*  Maybe we have less characters than expected because the buffer is too small. */
+       if (available>=remaining_len) {
+               /*  The buffer is sufficiently big to hold the expected number of characters. */
+               if (strncmp((char *)SYNCTEX_CUR,the_string,remaining_len)) {
+                       return SYNCTEX_STATUS_NOT_OK;
+               }
+return_OK:
+               /*  Advance SYNCTEX_CUR to the next character after the_string. */
+               SYNCTEX_CUR += remaining_len;
+               return SYNCTEX_STATUS_OK;
+       } else if (strncmp((char *)SYNCTEX_CUR,the_string,available)) {
+                       /*  No need to go further, this is not the expected string in the buffer. */
+                       return SYNCTEX_STATUS_NOT_OK;
+       } else if (SYNCTEX_FILE) {
+               /*  The buffer was too small to contain remaining_len characters.
+                *  We have to cut the string into pieces. */
+               z_off_t offset = 0L;
+               /*  the first part of the string is found, advance the_string to the next untested character. */
+               the_string += available;
+               /*  update the remaining length and the parsed length. */
+               remaining_len -= available;
+               tested_len += available;
+               SYNCTEX_CUR += available; /*  We validate the tested characters. */
+               if (0 == remaining_len) {
+                       /*  Nothing left to test, we have found the given string, we return the length. */
+                       return tested_len;
+               }
+               /*  We also have to record the current state of the file cursor because
+                *  if the_string does not match, all this should be a totally blank operation,
+                *  for which the file and buffer states should not be modified at all.
+                *  In fact, the states of the buffer before and after this function are in general different
+                *  but they are totally equivalent as long as the values of the buffer before SYNCTEX_CUR
+                *  can be safely discarded.  */
+               offset = gztell(SYNCTEX_FILE);
+               /*  offset now corresponds to the first character of the file that was not buffered. */
+               available = SYNCTEX_CUR - SYNCTEX_START; /*  available can be used as temporary placeholder. */
+               /*  available now corresponds to the number of chars that where already buffered and
+                *  that match the head of the_string. If in fine the_string does not match, all these chars must be recovered
+                *  because the buffer contents is completely replaced by _synctex_buffer_get_available_size.
+                *  They were buffered from offset-len location in the file. */
+               offset -= available;
+more_characters:
+               /*  There is still some work to be done, so read another bunch of file.
+                *  This is the second call to _synctex_buffer_get_available_size,
+                *  which means that the actual contents of the buffer will be discarded.
+                *  We will definitely have to recover the previous state in case we do not find the expected string. */
+               available = remaining_len;
+               status = _synctex_buffer_get_available_size(scanner,&available);
+               if (status<SYNCTEX_STATUS_EOF) {
+                       return status; /*  This is an error, no need to go further. */
+               }
+               if (available==0) {
+                       /*  Missing characters: recover the initial state of the file and return. */
+return_NOT_OK:
+                       if (offset != gzseek(SYNCTEX_FILE,offset,SEEK_SET)) {
+                               /*  This is a critical error, we could not recover the previous state. */
+                               _synctex_error("can't seek file");
+                               return SYNCTEX_STATUS_ERROR;
+                       }
+                       /*  Next time we are asked to fill the buffer,
+                        *  we will read a complete bunch of text from the file. */
+                       SYNCTEX_CUR = SYNCTEX_END;
+                       return SYNCTEX_STATUS_NOT_OK;
+               }
+               if (available<remaining_len) {
+                       /*  We'll have to loop one more time. */
+                       if (strncmp((char *)SYNCTEX_CUR,the_string,available)) {
+                               /*  This is not the expected string, recover the previous state and return. */
+                               goto return_NOT_OK;
+                       }
+                       /*  Advance the_string to the first untested character. */
+                       the_string += available;
+                       /*  update the remaining length and the parsed length. */
+                       remaining_len -= available;
+                       tested_len += available;
+                       SYNCTEX_CUR += available; /*  We validate the tested characters. */
+                       if (0 == remaining_len) {
+                               /*  Nothing left to test, we have found the given string. */
+                               return SYNCTEX_STATUS_OK;
+                       }
+                       goto more_characters;
+               }
+               /*  This is the last step. */
+               if (strncmp((char *)SYNCTEX_CUR,the_string,remaining_len)) {
+                       /*  This is not the expected string, recover the previous state and return. */
+                       goto return_NOT_OK;
+               }
+               goto return_OK;
+       } else {
+               /*  The buffer can't contain the given string argument, and the EOF was reached */
+               return SYNCTEX_STATUS_EOF;
+       }
+}
+
+/*  Used when parsing the synctex file.
+ *  Decode an integer.
+ *  First, field separators, namely ':' and ',' characters are skipped
+ *  The returned value is negative if there is an unrecoverable error.
+ *  It is SYNCTEX_STATUS_NOT_OK if an integer could not be parsed, for example
+ *  if the characters at the current cursor position are not digits or
+ *  if the end of the file has been reached.
+ *  It is SYNCTEX_STATUS_OK if an int has been successfully parsed.
+ *  The given scanner argument must not be NULL, on the contrary, value_ref may be NULL.
+ */
+synctex_status_t _synctex_decode_int(synctex_scanner_t scanner, int* value_ref) {
+       char * ptr = NULL;
+       char * end = NULL;
+       int result = 0;
+       size_t available = 0;
+       synctex_status_t status = 0;
+       if (NULL == scanner) {
+                return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       available = SYNCTEX_BUFFER_MIN_SIZE;
+       status = _synctex_buffer_get_available_size(scanner, &available);
+       if (status<SYNCTEX_STATUS_EOF) {
+               return status;/*  Forward error. */
+       }
+       if (available==0) {
+               return SYNCTEX_STATUS_EOF;/*  it is the end of file. */
+       }
+       ptr = SYNCTEX_CUR;
+       if (*ptr==':' || *ptr==',') {
+               ++ptr;
+               --available;
+               if (available==0) {
+                       return SYNCTEX_STATUS_NOT_OK;/*  It is not possible to scan an int */
+               }
+       }
+       result = (int)strtol(ptr, &end, 10);
+       if (end>ptr) {
+               SYNCTEX_CUR = end;
+               if (value_ref) {
+                       * value_ref = result;
+               }
+               return SYNCTEX_STATUS_OK;/*  Successfully scanned an int */
+       }       
+       return SYNCTEX_STATUS_NOT_OK;/*  Could not scan an int */
+}
+
+/*  The purpose of this function is to read a string.
+ *  A string is an array of characters from the current parser location
+ *  and before the next '\n' character.
+ *  If a string was properly decoded, it is returned in value_ref and
+ *  the cursor points to the new line marker.
+ *  The returned string was alloced on the heap, the caller is the owner and
+ *  is responsible to free it in due time.
+ *  If no string is parsed, * value_ref is undefined.
+ *  The maximum length of a string that a scanner can decode is platform dependent, namely UINT_MAX.
+ *  If you just want to blindly parse the file up to the end of the current line,
+ *  use _synctex_next_line instead.
+ *  On return, the scanner cursor is unchanged if a string could not be scanned or
+ *  points to the terminating '\n' character otherwise. As a consequence,
+ *  _synctex_next_line is necessary after.
+ *  If either scanner or value_ref is NULL, it is considered as an error and
+ *  SYNCTEX_STATUS_BAD_ARGUMENT is returned.
+ */
+synctex_status_t _synctex_decode_string(synctex_scanner_t scanner, char ** value_ref) {
+       char * end = NULL;
+       size_t current_size = 0;
+       size_t new_size = 0;
+       size_t len = 0;/*  The number of bytes to copy */
+       size_t available = 0;
+       synctex_status_t status = 0;
+       if (NULL == scanner || NULL == value_ref) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       /*  The buffer must at least contain one character: the '\n' end of line marker */
+       if (SYNCTEX_CUR>=SYNCTEX_END) {
+               available = 1;
+               status = _synctex_buffer_get_available_size(scanner,&available);
+               if (status < 0) {
+                       return status;
+               }
+               if (0 == available) {
+                       return SYNCTEX_STATUS_EOF;
+               }
+       }
+       /*  Now we are sure that there is at least one available character, either because
+        *  SYNCTEX_CUR was already < SYNCTEX_END, or because the buffer has been properly filled. */
+       /*  end will point to the next unparsed '\n' character in the file, when mapped to the buffer. */
+       end = SYNCTEX_CUR;
+       * value_ref = NULL;/*  Initialize, it will be realloc'ed */
+       /*  We scan all the characters up to the next '\n' */
+next_character:
+       if (end<SYNCTEX_END) {
+               if (*end == '\n') {
+                       /*  OK, we found where to stop */
+                       len = end - SYNCTEX_CUR;
+                       if (current_size>UINT_MAX-len-1) {
+                               /*  But we have reached the limit: we do not have current_size+len+1>UINT_MAX.
+                                *  We return the missing amount of memory.
+                                *  This will never occur in practice. */
+                               return UINT_MAX-len-1 - current_size;
+                       }
+                       new_size = current_size+len;
+                       /*  We have current_size+len+1<=UINT_MAX
+                        *  or equivalently new_size<UINT_MAX,
+                        *  where we have assumed that len<UINT_MAX */
+                       if ((* value_ref = realloc(* value_ref,new_size+1)) != NULL) {
+                               if (memcpy((*value_ref)+current_size,SYNCTEX_CUR,len)) {
+                                       (* value_ref)[new_size]='\0'; /*  Terminate the string */
+                                       SYNCTEX_CUR += len;/*  Advance to the terminating '\n' */
+                                       return SYNCTEX_STATUS_OK;
+                               }
+                               free(* value_ref);
+                               * value_ref = NULL;
+                               _synctex_error("could not copy memory (1).");
+                               return SYNCTEX_STATUS_ERROR;
+                       }
+                       _synctex_error("could not allocate memory (1).");
+                       return SYNCTEX_STATUS_ERROR;
+               } else {
+                       ++end;
+                       goto next_character;
+               }
+       } else {
+               /*  end == SYNCTEX_END */
+               len = SYNCTEX_END - SYNCTEX_CUR;
+               if (current_size>UINT_MAX-len-1) {
+                       /*  We have reached the limit. */
+                       _synctex_error("limit reached (missing %i).",current_size-(UINT_MAX-len-1));
+                       return SYNCTEX_STATUS_ERROR;
+               }
+               new_size = current_size+len;
+               if ((* value_ref = realloc(* value_ref,new_size+1)) != NULL) {
+                       if (memcpy((*value_ref)+current_size,SYNCTEX_CUR,len)) {
+                               (* value_ref)[new_size]='\0'; /*  Terminate the string */
+                               SYNCTEX_CUR = SYNCTEX_END;/*  Advance the cursor to the end of the bufer */
+                               return SYNCTEX_STATUS_OK;
+                       }
+                       free(* value_ref);
+                       * value_ref = NULL;
+                       _synctex_error("could not copy memory (2).");
+                       return SYNCTEX_STATUS_ERROR;
+               }
+               /*  Huge memory problem */
+               _synctex_error("could not allocate memory (2).");
+               return SYNCTEX_STATUS_ERROR;
+       }
+}
+
+/*  Used when parsing the synctex file.
+ *  Read an Input record.
+ */
+synctex_status_t _synctex_scan_input(synctex_scanner_t scanner) {
+       synctex_status_t status = 0;
+       size_t available = 0;
+       synctex_node_t input = NULL;
+       if (NULL == scanner) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       status = _synctex_match_string(scanner,SYNCTEX_INPUT_MARK);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       /*  Create a node */
+       input = _synctex_new_input(scanner);
+       if (NULL == input) {
+               _synctex_error("could not create an input node.");
+               return SYNCTEX_STATUS_ERROR;
+       }
+       /*  Decode the synctag  */
+       status = _synctex_decode_int(scanner,&(SYNCTEX_TAG(input)));
+       if (status<SYNCTEX_STATUS_OK) {
+               _synctex_error("bad format of input node.");
+               SYNCTEX_FREE(input);
+               return status;
+       }
+       /*  The next character is a field separator, we expect one character in the buffer. */
+       available = 1;
+       status = _synctex_buffer_get_available_size(scanner, &available);
+       if (status<=SYNCTEX_STATUS_ERROR) {
+               return status;
+       }
+       if (0 == available) {
+               return SYNCTEX_STATUS_EOF;
+       }
+       /*  We can now safely advance to the next character, stepping over the field separator. */
+       ++SYNCTEX_CUR;
+       --available;
+       /*  Then we scan the file name */
+       status = _synctex_decode_string(scanner,&(SYNCTEX_NAME(input)));
+       if (status<SYNCTEX_STATUS_OK) {
+               SYNCTEX_FREE(input);
+               return status;
+       }
+       /*  Prepend this input node to the input linked list of the scanner */
+       SYNCTEX_SET_SIBLING(input,scanner->input);
+       scanner->input = input;
+#   if SYNCTEX_VERBOSE
+        synctex_node_log(input);
+#   endif
+       return _synctex_next_line(scanner);/*  read the line termination character, if any */
+       /*  Now, set up the path */
+}
+
+typedef synctex_status_t (*synctex_decoder_t)(synctex_scanner_t,void *);
+
+synctex_status_t _synctex_scan_named(synctex_scanner_t scanner,const char * name,void * value_ref,synctex_decoder_t decoder);
+
+/*  Used when parsing the synctex file.
+ *  Read one of the settings.
+ *  On normal completion, returns SYNCTEX_STATUS_OK.
+ *  On error, returns SYNCTEX_STATUS_ERROR.
+ *  Both arguments must not be NULL.
+ *  On return, the scanner points to the next character after the decoded object whatever it is.
+ *  It is the responsibility of the caller to prepare the scanner for the next line.
+ */
+synctex_status_t _synctex_scan_named(synctex_scanner_t scanner,const char * name,void * value_ref,synctex_decoder_t decoder) {
+       synctex_status_t status = 0;
+       if (NULL == scanner || NULL == name || NULL == value_ref || NULL == decoder) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+not_found:
+       status = _synctex_match_string(scanner,name);
+       if (status<SYNCTEX_STATUS_NOT_OK) {
+               return status;
+       } else if (status == SYNCTEX_STATUS_NOT_OK) {
+               status = _synctex_next_line(scanner);
+               if (status<SYNCTEX_STATUS_OK) {
+                       return status;
+               }
+               goto not_found;
+       }
+       /*  A line is found, scan the value */
+       return (*decoder)(scanner,value_ref);
+}
+
+/*  Used when parsing the synctex file.
+ *  Read the preamble.
+ */
+synctex_status_t _synctex_scan_preamble(synctex_scanner_t scanner) {
+       synctex_status_t status = 0;
+       if (NULL == scanner) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       status = _synctex_scan_named(scanner,"SyncTeX Version:",&(scanner->version),(synctex_decoder_t)&_synctex_decode_int);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       status = _synctex_next_line(scanner);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       /*  Read all the input records */
+       do {
+               status = _synctex_scan_input(scanner);
+               if (status<SYNCTEX_STATUS_NOT_OK) {
+                       return status;
+               }
+       } while(status == SYNCTEX_STATUS_OK);
+       /*  the loop exits when status == SYNCTEX_STATUS_NOT_OK */
+       /*  Now read all the required settings. */
+       status = _synctex_scan_named(scanner,"Output:",&(scanner->output_fmt),(synctex_decoder_t)&_synctex_decode_string);
+       if (status<SYNCTEX_STATUS_NOT_OK) {
+               return status;
+       }
+       status = _synctex_next_line(scanner);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       status = _synctex_scan_named(scanner,"Magnification:",&(scanner->pre_magnification),(synctex_decoder_t)&_synctex_decode_int);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       status = _synctex_next_line(scanner);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       status = _synctex_scan_named(scanner,"Unit:",&(scanner->pre_unit),(synctex_decoder_t)&_synctex_decode_int);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       status = _synctex_next_line(scanner);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       status = _synctex_scan_named(scanner,"X Offset:",&(scanner->pre_x_offset),(synctex_decoder_t)&_synctex_decode_int);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       status = _synctex_next_line(scanner);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       status = _synctex_scan_named(scanner,"Y Offset:",&(scanner->pre_y_offset),(synctex_decoder_t)&_synctex_decode_int);
+       if (status<SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       return _synctex_next_line(scanner);
+}
+
+/*  parse a float with a dimension */
+synctex_status_t _synctex_scan_float_and_dimension(synctex_scanner_t scanner, float * value_ref) {
+       synctex_status_t status = 0;
+       char * endptr = NULL;
+       float f = 0;
+#ifdef HAVE_SETLOCALE
+       char * loc = setlocale(LC_NUMERIC, NULL);
+#endif
+       size_t available = 0;
+       if (NULL == scanner || NULL == value_ref) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       available = SYNCTEX_BUFFER_MIN_SIZE;
+       status = _synctex_buffer_get_available_size(scanner, &available);
+       if (status<SYNCTEX_STATUS_EOF) {
+               _synctex_error("problem with float.");
+               return status;
+       }
+#ifdef HAVE_SETLOCALE
+       setlocale(LC_NUMERIC, "C");
+#endif
+       f = strtod(SYNCTEX_CUR,&endptr);
+#ifdef HAVE_SETLOCALE
+       setlocale(LC_NUMERIC, loc);
+#endif
+       if (endptr == SYNCTEX_CUR) {
+               _synctex_error("a float was expected.");
+               return SYNCTEX_STATUS_ERROR;
+       }
+       SYNCTEX_CUR = endptr;
+       if ((status = _synctex_match_string(scanner,"in")) >= SYNCTEX_STATUS_OK) {
+               f *= 72.27f*65536;
+       } else if (status<SYNCTEX_STATUS_EOF) {
+report_unit_error:
+               _synctex_error("problem with unit.");
+               return status;
+       } else if ((status = _synctex_match_string(scanner,"cm")) >= SYNCTEX_STATUS_OK) {
+               f *= 72.27f*65536/2.54f;
+       } else if (status<0) {
+               goto report_unit_error;
+       } else if ((status = _synctex_match_string(scanner,"mm")) >= SYNCTEX_STATUS_OK) {
+               f *= 72.27f*65536/25.4f;
+       } else if (status<0) {
+               goto report_unit_error;
+       } else if ((status = _synctex_match_string(scanner,"pt")) >= SYNCTEX_STATUS_OK) {
+               f *= 65536.0f;
+       } else if (status<0) {
+               goto report_unit_error;
+       } else if ((status = _synctex_match_string(scanner,"bp")) >= SYNCTEX_STATUS_OK) {
+               f *= 72.27f/72*65536.0f;
+       }  else if (status<0) {
+               goto report_unit_error;
+       } else if ((status = _synctex_match_string(scanner,"pc")) >= SYNCTEX_STATUS_OK) {
+               f *= 12.0*65536.0f;
+       }  else if (status<0) {
+               goto report_unit_error;
+       } else if ((status = _synctex_match_string(scanner,"sp")) >= SYNCTEX_STATUS_OK) {
+               f *= 1.0f;
+       }  else if (status<0) {
+               goto report_unit_error;
+       } else if ((status = _synctex_match_string(scanner,"dd")) >= SYNCTEX_STATUS_OK) {
+               f *= 1238.0f/1157*65536.0f;
+       }  else if (status<0) {
+               goto report_unit_error;
+       } else if ((status = _synctex_match_string(scanner,"cc")) >= SYNCTEX_STATUS_OK) {
+               f *= 14856.0f/1157*65536;
+       } else if (status<0) {
+               goto report_unit_error;
+       } else if ((status = _synctex_match_string(scanner,"nd")) >= SYNCTEX_STATUS_OK) {
+               f *= 685.0f/642*65536;
+       }  else if (status<0) {
+               goto report_unit_error;
+       } else if ((status = _synctex_match_string(scanner,"nc")) >= SYNCTEX_STATUS_OK) {
+               f *= 1370.0f/107*65536;
+       } else if (status<0) {
+               goto report_unit_error;
+       }
+       *value_ref = f;
+       return SYNCTEX_STATUS_OK;
+}
+
+/*  parse the post scriptum
+ *  SYNCTEX_STATUS_OK is returned on completion
+ *  a negative error is returned otherwise */
+synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_t scanner) {
+       synctex_status_t status = 0;
+       char * endptr = NULL;
+#ifdef HAVE_SETLOCALE
+       char * loc = setlocale(LC_NUMERIC, NULL);
+#endif
+       if (NULL == scanner) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       /*  Scan the file until a post scriptum line is found */
+post_scriptum_not_found:
+       status = _synctex_match_string(scanner,"Post scriptum:");
+       if (status<SYNCTEX_STATUS_NOT_OK) {
+               return status;
+       }
+       if (status == SYNCTEX_STATUS_NOT_OK) {
+               status = _synctex_next_line(scanner);
+               if (status<SYNCTEX_STATUS_EOF) {
+                       return status;
+               } else if (status<SYNCTEX_STATUS_OK) {
+                       return SYNCTEX_STATUS_OK;/*  The EOF is found, we have properly scanned the file */
+               }
+               goto post_scriptum_not_found;
+       }
+       /*  We found the name, advance to the next line. */
+next_line:
+       status = _synctex_next_line(scanner);
+       if (status<SYNCTEX_STATUS_EOF) {
+               return status;
+       } else if (status<SYNCTEX_STATUS_OK) {
+               return SYNCTEX_STATUS_OK;/*  The EOF is found, we have properly scanned the file */
+       }
+       /*  Scanning the information */
+       status = _synctex_match_string(scanner,"Magnification:");
+       if (status == SYNCTEX_STATUS_OK ) {
+#ifdef HAVE_SETLOCALE
+               setlocale(LC_NUMERIC, "C");
+#endif
+               scanner->unit = strtod(SYNCTEX_CUR,&endptr);
+#ifdef HAVE_SETLOCALE
+               setlocale(LC_NUMERIC, loc);
+#endif
+               if (endptr == SYNCTEX_CUR) {
+                       _synctex_error("bad magnification in the post scriptum, a float was expected.");
+                       return SYNCTEX_STATUS_ERROR;
+               }
+               if (scanner->unit<=0) {
+                       _synctex_error("bad magnification in the post scriptum, a positive float was expected.");
+                       return SYNCTEX_STATUS_ERROR;
+               }
+               SYNCTEX_CUR = endptr;
+               goto next_line;
+       }
+       if (status<SYNCTEX_STATUS_EOF){
+report_record_problem:
+               _synctex_error("Problem reading the Post Scriptum records");
+               return status; /*  echo the error. */
+       }
+       status = _synctex_match_string(scanner,"X Offset:");
+       if (status == SYNCTEX_STATUS_OK) {
+               status = _synctex_scan_float_and_dimension(scanner, &(scanner->x_offset));
+               if (status<SYNCTEX_STATUS_OK) {
+                       _synctex_error("problem with X offset in the Post Scriptum.");
+                       return status;
+               }
+               goto next_line;
+       } else if (status<SYNCTEX_STATUS_EOF){
+               goto report_record_problem;
+       }
+       status = _synctex_match_string(scanner,"Y Offset:");
+       if (status==SYNCTEX_STATUS_OK) {
+               status = _synctex_scan_float_and_dimension(scanner, &(scanner->y_offset));
+               if (status<SYNCTEX_STATUS_OK) {
+                       _synctex_error("problem with Y offset in the Post Scriptum.");
+                       return status;
+               }
+               goto next_line;
+       } else if (status<SYNCTEX_STATUS_EOF){
+               goto report_record_problem;
+       }
+       goto next_line;
+}
+
+/*  SYNCTEX_STATUS_OK is returned if the postamble is read
+ *  SYNCTEX_STATUS_NOT_OK is returned if the postamble is not at the current location
+ *  a negative error otherwise
+ *  The postamble comprises the post scriptum section.
+ */
+synctex_status_t _synctex_scan_postamble(synctex_scanner_t scanner) {
+       synctex_status_t status = 0;
+       if (NULL == scanner) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       status = _synctex_match_string(scanner,"Postamble:");
+       if (status < SYNCTEX_STATUS_OK) {
+               return status;
+       }
+count_again:
+       status = _synctex_next_line(scanner);
+       if (status < SYNCTEX_STATUS_OK) {
+               return status;
+       }
+       status = _synctex_scan_named(scanner,"Count:",&(scanner->count),(synctex_decoder_t)&_synctex_decode_int);
+       if (status < SYNCTEX_STATUS_EOF) {
+               return status; /*  forward the error */
+       } else if (status < SYNCTEX_STATUS_OK) { /*  No Count record found */
+               status = _synctex_next_line(scanner); /*  Advance one more line */
+               if (status<SYNCTEX_STATUS_OK) {
+                       return status;
+               }
+               goto count_again;
+       }
+       /*  Now we scan the last part of the SyncTeX file: the Post Scriptum section. */
+       return _synctex_scan_post_scriptum(scanner);
+}
+
+/*  Horizontal boxes also have visible size.
+ *  Visible size are bigger than real size.
+ *  For example 0 width boxes may contain text.
+ *  At creation time, the visible size is set to the values of the real size.
+ */
+synctex_status_t _synctex_setup_visible_box(synctex_node_t box) {
+       if (box) {
+               switch(box->class->type) {
+                       case synctex_node_type_hbox:
+                               if (SYNCTEX_INFO(box) != NULL) {
+                                       SYNCTEX_HORIZ_V(box)  = SYNCTEX_HORIZ(box);
+                                       SYNCTEX_VERT_V(box)   = SYNCTEX_VERT(box);
+                                       SYNCTEX_WIDTH_V(box)  = SYNCTEX_WIDTH(box);
+                                       SYNCTEX_HEIGHT_V(box) = SYNCTEX_HEIGHT(box);
+                                       SYNCTEX_DEPTH_V(box)  = SYNCTEX_DEPTH(box);
+                                       return SYNCTEX_STATUS_OK;
+                               }
+                               return SYNCTEX_STATUS_ERROR;
+               }
+       }
+       return SYNCTEX_STATUS_BAD_ARGUMENT;
+}
+
+/*  This method is sent to an horizontal box to setup the visible size
+ *  Some box have 0 width but do contain text material.
+ *  With this method, one can enlarge the box to contain the given point (h,v).
+ */
+synctex_status_t _synctex_hbox_setup_visible(synctex_node_t node,int h, int v) {
+#      ifdef __DARWIN_UNIX03
+#       pragma unused(v)
+#   endif
+       int itsBtm, itsTop;
+       if (NULL == node || node->class->type != synctex_node_type_hbox) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       if (SYNCTEX_WIDTH_V(node)<0) {
+               itsBtm = SYNCTEX_HORIZ_V(node);
+               itsTop = SYNCTEX_HORIZ_V(node)-SYNCTEX_WIDTH_V(node);
+               if (h<itsBtm) {
+                       SYNCTEX_HORIZ_V(node) = h;
+                       SYNCTEX_WIDTH_V(node) = SYNCTEX_HORIZ_V(node) - itsTop;
+               } else if (h>itsTop) {
+                       SYNCTEX_WIDTH_V(node) = SYNCTEX_HORIZ_V(node) - h;
+               }
+       } else {
+               itsBtm = SYNCTEX_HORIZ_V(node);
+               itsTop = SYNCTEX_HORIZ_V(node)+SYNCTEX_WIDTH_V(node);
+               if (h<itsBtm) {
+                       SYNCTEX_HORIZ_V(node) = h;
+                       SYNCTEX_WIDTH_V(node) = itsTop - SYNCTEX_HORIZ_V(node);
+               } else if (h>itsTop) {
+                       SYNCTEX_WIDTH_V(node) = h - SYNCTEX_HORIZ_V(node);
+               }
+       }
+       return SYNCTEX_STATUS_OK;
+}
+
+/*  Here are the control characters that strat each line of the synctex output file.
+ *  Their values define the meaning of the line.
+ */
+#   define SYNCTEX_CHAR_BEGIN_SHEET '{'
+#   define SYNCTEX_CHAR_END_SHEET   '}'
+#   define SYNCTEX_CHAR_BEGIN_VBOX  '['
+#   define SYNCTEX_CHAR_END_VBOX    ']'
+#   define SYNCTEX_CHAR_BEGIN_HBOX  '('
+#   define SYNCTEX_CHAR_END_HBOX    ')'
+#   define SYNCTEX_CHAR_ANCHOR      '!'
+#   define SYNCTEX_CHAR_VOID_VBOX   'v'
+#   define SYNCTEX_CHAR_VOID_HBOX   'h'
+#   define SYNCTEX_CHAR_KERN        'k'
+#   define SYNCTEX_CHAR_GLUE        'g'
+#   define SYNCTEX_CHAR_MATH        '$'
+#   define SYNCTEX_CHAR_BOUNDARY    'x'
+
+#   define SYNCTEX_RETURN(STATUS) return STATUS;
+
+/*  Used when parsing the synctex file. A '{' character has just been parsed.
+ *  The purpose is to gobble everything until the closing '}'.
+ *  Actually only one nesting depth has been observed when using the clip option
+ *  of \includegraphics option. Here we use arbitrary level of depth.
+ */
+synctex_status_t _synctex_scan_nested_sheet(synctex_scanner_t scanner) {
+    unsigned int depth = 0;
+deeper:
+    ++depth;
+    if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+        _synctex_error("Unexpected end of nested sheet (1).");
+        SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+    }
+scan_next_line:
+    if (SYNCTEX_CUR<SYNCTEX_END) {
+               if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
+                       ++SYNCTEX_CUR;
+                       if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                               _synctex_error("Unexpected end of nested sheet (2).");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+            if (--depth>0) {
+                goto scan_next_line;
+            } else {
+               SYNCTEX_RETURN(SYNCTEX_STATUS_OK);
+            }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_SHEET) {
+                       ++SYNCTEX_CUR;
+                       goto deeper;
+            
+               } else if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+            _synctex_error("Unexpected end of nested sheet (3).");
+            SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+        }
+    }
+    _synctex_error("Unexpected end of nested sheet (4).");
+    SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+}
+
+/*  Used when parsing the synctex file.
+ *  The sheet argument is a newly created sheet node that will hold the contents.
+ *  Something is returned in case of error.
+ */
+synctex_status_t _synctex_scan_sheet(synctex_scanner_t scanner, synctex_node_t sheet) {
+       synctex_node_t parent = sheet;
+       synctex_node_t child = NULL;
+       synctex_node_t sibling = NULL;
+       synctex_node_t box = sheet;
+       int friend_index = 0;
+       synctex_info_t * info = NULL;
+       synctex_status_t status = 0;
+       size_t available = 0;
+       if ((NULL == scanner) || (NULL == sheet)) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       /*  We MUST start with a box, so at this level, the unique possibility is '[', '(' or "}". */
+prepare_loop:
+       if (SYNCTEX_CUR<SYNCTEX_END) {
+               if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_VBOX) {
+scan_vbox:
+                       if ((child = _synctex_new_vbox(scanner)) && (info = SYNCTEX_INFO(child))) {
+#               define SYNCTEX_DECODE_FAILED(WHAT) \
+                                       (_synctex_decode_int(scanner,&(info[WHAT].INT))<SYNCTEX_STATUS_OK)
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad vbox record.");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_CHILD(parent,child);
+                               parent = child;
+                               child = NULL;
+                               goto child_loop;/*  next created node will be a child */
+                       } else {
+                               _synctex_error("Can't create vbox record.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_HBOX) {
+scan_hbox:
+                       if ((child = _synctex_new_hbox(scanner)) && (info = SYNCTEX_INFO(child))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+                                               || _synctex_setup_visible_box(child)<SYNCTEX_STATUS_OK
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad hbox record.");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
+                               SYNCTEX_SET_CHILD(parent,child);
+                               parent = child;
+                               child = NULL;
+                               goto child_loop;/*  next created node will be a child */
+                       } else {
+                               _synctex_error("Can't create hbox record.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
+scan_teehs:
+                       ++SYNCTEX_CUR;
+                       if (NULL == parent || parent->class->type != synctex_node_type_sheet
+                                       || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                               _synctex_error("Unexpected end of sheet.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+#           if SYNCTEX_VERBOSE
+                synctex_node_log(parent);
+#           endif
+                       SYNCTEX_RETURN(SYNCTEX_STATUS_OK);
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_SHEET) {
+                       /*  Addendum to version 1.10 to manage nested sheets  */
+                       ++SYNCTEX_CUR;
+                       if (_synctex_scan_nested_sheet(scanner)<SYNCTEX_STATUS_OK) {
+                               _synctex_error("Unexpected nested sheet.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+                       goto prepare_loop;
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_ANCHOR) {
+scan_anchor:
+                       ++SYNCTEX_CUR;
+                       if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                               _synctex_error("Missing anchor.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+                       goto prepare_loop;
+               } else {
+                       /*  _synctex_error("Ignored record %c\n",*SYNCTEX_CUR); */
+                       ++SYNCTEX_CUR;
+                       if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                               _synctex_error("Unexpected end.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+                       goto prepare_loop;
+               }
+       } else {
+               available = 1;
+               status = _synctex_buffer_get_available_size(scanner,&available);
+                if (status<SYNCTEX_STATUS_OK && available>0){
+                       _synctex_error("Uncomplete sheet(0)");
+                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+               }
+        goto prepare_loop;
+       }
+/*  The child loop means that we go down one level, when we just created a box node,
+ *  the next node created is a child of this box. */
+child_loop:
+       if (SYNCTEX_CUR<SYNCTEX_END) {
+               if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_VBOX) {
+                       goto scan_vbox;
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_VBOX) {
+scan_xobv:
+                       ++SYNCTEX_CUR;
+                       if (NULL != parent && parent->class->type == synctex_node_type_vbox) {
+                               #define SYNCTEX_UPDATE_BOX_FRIEND(NODE)\
+                               friend_index = ((SYNCTEX_INFO(NODE))[SYNCTEX_TAG_IDX].INT+(SYNCTEX_INFO(NODE))[SYNCTEX_LINE_IDX].INT)%(scanner->number_of_lists);\
+                               SYNCTEX_SET_FRIEND(NODE,(scanner->lists_of_friends)[friend_index]);\
+                               (scanner->lists_of_friends)[friend_index] = NODE;
+                               if (NULL == SYNCTEX_CHILD(parent)) {
+                                       /*  only void boxes are friends */
+                                       SYNCTEX_UPDATE_BOX_FRIEND(parent);
+                               }
+                               child = parent;
+                               parent = SYNCTEX_PARENT(child);
+                       } else {
+                               _synctex_error("Unexpected end of vbox, ignored.");
+                       }
+                       if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                               _synctex_error("Uncomplete sheet.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+#           if SYNCTEX_VERBOSE
+                synctex_node_log(child);
+#           endif
+                       goto sibling_loop;
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_HBOX) {
+                       goto scan_hbox;
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_HBOX) {
+scan_xobh:
+                       ++SYNCTEX_CUR;
+                       if ((parent) && parent->class->type == synctex_node_type_hbox) {
+            /*  Update the mean line number */
+                synctex_node_t node = SYNCTEX_CHILD(parent);
+                if (node) {
+                    unsigned int node_weight = 0;
+                    unsigned int cumulated_line_numbers = 0;
+                    do {
+                        if (synctex_node_type(node)==synctex_node_type_hbox) {
+                            if (SYNCTEX_NODE_WEIGHT(node)) {
+                                node_weight += SYNCTEX_NODE_WEIGHT(node);
+                                cumulated_line_numbers += SYNCTEX_MEAN_LINE(node)*SYNCTEX_NODE_WEIGHT(node);
+                            } else {
+                                ++node_weight;
+                                cumulated_line_numbers += SYNCTEX_MEAN_LINE(node);
+                            }
+                        } else {
+                            ++node_weight;
+                            cumulated_line_numbers += SYNCTEX_LINE(node);
+                        }
+                    } while ((node = SYNCTEX_SIBLING(node)));
+                    SYNCTEX_MEAN_LINE(parent)=(cumulated_line_numbers + node_weight/2)/node_weight;
+                    SYNCTEX_NODE_WEIGHT(parent)=node_weight;
+                } else {
+                    SYNCTEX_MEAN_LINE(parent)=SYNCTEX_LINE(parent);
+                    SYNCTEX_NODE_WEIGHT(parent)=1;
+                }
+                               if (NULL == child) {
+                                       /*  Only boxes with no children are friends,
+                                        *  boxes with children are indirectly friends through one of their contained nodes. */
+                                       SYNCTEX_UPDATE_BOX_FRIEND(parent);
+                               }
+                               /*  setting the next horizontal box at the end ensures that a child is recorded before any of its ancestors. */
+                               SYNCTEX_SET_NEXT_hbox(box,parent);
+                               box = parent;
+                               child = parent;
+                               parent = SYNCTEX_PARENT(child);
+                       } else {
+                               _synctex_error("Unexpected end of hbox, ignored.");
+                       }
+                       if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                               _synctex_error("Uncomplete sheet.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+#           if SYNCTEX_VERBOSE
+                synctex_node_log(child);
+#           endif
+                       goto sibling_loop;
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_VBOX) {
+                       if (NULL != (child = _synctex_new_void_vbox(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(child))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad void vbox record.");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_CHILD(parent,child);
+                               #define SYNCTEX_UPDATE_FRIEND(NODE)\
+                               friend_index = (info[SYNCTEX_TAG_IDX].INT+info[SYNCTEX_LINE_IDX].INT)%(scanner->number_of_lists);\
+                               SYNCTEX_SET_FRIEND(NODE,(scanner->lists_of_friends)[friend_index]);\
+                               (scanner->lists_of_friends)[friend_index] = NODE;
+                               SYNCTEX_UPDATE_FRIEND(child);
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("Can't create vbox record.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_HBOX) {
+                       if (NULL != (child = _synctex_new_void_hbox(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(child))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad void hbox record.");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_CHILD(parent,child);
+                               SYNCTEX_UPDATE_FRIEND(child);
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("Can't create void hbox record.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_KERN) {
+                       if (NULL != (child = _synctex_new_kern(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(child))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad kern record.");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_CHILD(parent,child);
+                               SYNCTEX_UPDATE_FRIEND(child);
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)-SYNCTEX_WIDTH(child),SYNCTEX_VERT(child));
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("Can't create kern record.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_GLUE) {
+                       if (NULL != (child = _synctex_new_glue(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(child))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad glue record.");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_CHILD(parent,child);
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               SYNCTEX_UPDATE_FRIEND(child);
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("Can't create glue record.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_MATH) {
+                       if (NULL != (child = _synctex_new_math(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(child))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad math record.");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_CHILD(parent,child);
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               SYNCTEX_UPDATE_FRIEND(child);
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("Can't create math record.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BOUNDARY) {
+                       if (NULL != (child = _synctex_new_boundary(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(child))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad boundary record.");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_CHILD(parent,child);
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               SYNCTEX_UPDATE_FRIEND(child);
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("Can't create math record.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
+                       goto scan_teehs;
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_SHEET) {
+                       /*  Addendum to version 1.10 to manage nested sheets  */
+                       ++SYNCTEX_CUR;
+                       if (_synctex_scan_nested_sheet(scanner)<SYNCTEX_STATUS_OK) {
+                               _synctex_error("Unexpected nested sheet.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+                       goto child_loop;
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_ANCHOR) {
+                       goto scan_anchor;
+               } else {
+                       /*  _synctex_error("Ignored record %c\n",*SYNCTEX_CUR); */
+                       ++SYNCTEX_CUR;
+                       if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                               _synctex_error("Unexpected end.");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+                       goto child_loop;
+               }
+       } else {
+               available = 1;
+               status = _synctex_buffer_get_available_size(scanner,&available);
+                if (status<SYNCTEX_STATUS_OK && available>0){
+                       _synctex_error("Uncomplete sheet(0)");
+                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+               } else {
+                       goto child_loop;
+               }
+       }
+/*  The vertical loop means that we are on the same level, for example when we just ended a box.
+ *  If a node is created now, it will be a sibling of the current node, sharing the same parent. */
+sibling_loop:
+       if (SYNCTEX_CUR<SYNCTEX_END) {
+               if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_VBOX) {
+                       if (NULL != (sibling = _synctex_new_vbox(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(sibling))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad vbox record (2).");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_SIBLING(child,sibling);
+                               parent = sibling;
+                               child = NULL;
+                               goto child_loop;
+                       } else {
+                               _synctex_error("Can't create vbox record (2).");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_VBOX) {
+                       goto scan_xobv;
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_HBOX) {
+                       if (NULL != (sibling = _synctex_new_hbox(scanner)) &&
+                                       NULL != (info = SYNCTEX_INFO(sibling))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+                                               || _synctex_setup_visible_box(sibling)<SYNCTEX_STATUS_OK
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad hbox record (2).");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_SIBLING(child,sibling);
+                               child = sibling;
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               parent = child;
+                               child = NULL;
+                               goto child_loop;
+                       } else {
+                               _synctex_error("Can't create hbox record (2).");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_HBOX) {
+                       goto scan_xobh;
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_VBOX) {
+                       if (NULL != (sibling = _synctex_new_void_vbox(scanner)) &&
+                                       NULL != (info = SYNCTEX_INFO(sibling))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad void vbox record (2).");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_SIBLING(child,sibling);
+                               child = sibling;
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               SYNCTEX_UPDATE_FRIEND(child);
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("can't create void vbox record (2).");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_HBOX) {
+                       if (NULL != (sibling = _synctex_new_void_hbox(scanner)) &&
+                                       NULL != (info = SYNCTEX_INFO(sibling))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad void hbox record (2).");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_SIBLING(child,sibling);
+                               child = sibling;
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               SYNCTEX_UPDATE_FRIEND(child);
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("can't create void hbox record (2).");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_KERN) {
+                       if (NULL != (sibling = _synctex_new_kern(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(sibling))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad kern record (2).");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_SIBLING(child,sibling);
+                               child = sibling;
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               SYNCTEX_UPDATE_FRIEND(child);
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)-SYNCTEX_WIDTH(child),SYNCTEX_VERT(child));
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("Can't create kern record (2).");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_GLUE) {
+                       if (NULL != (sibling = _synctex_new_glue(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(sibling))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad glue record (2).");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_SIBLING(child,sibling);
+                               child = sibling;
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               SYNCTEX_UPDATE_FRIEND(child);
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("Can't create glue record (2).");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_MATH) {
+                       if (NULL != (sibling = _synctex_new_math(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(sibling))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad math record (2).");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_SIBLING(child,sibling);
+                               child = sibling;
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               SYNCTEX_UPDATE_FRIEND(child);
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("Can't create math record (2).");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BOUNDARY) {
+                       if (NULL != (sibling = _synctex_new_boundary(scanner))
+                                       && NULL != (info = SYNCTEX_INFO(sibling))) {
+                               if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX)
+                                               || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX)
+                                               || _synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                                       _synctex_error("Bad boundary record (2).");
+                                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                               }
+                               SYNCTEX_SET_SIBLING(child,sibling);
+                               child = sibling;
+#               if SYNCTEX_VERBOSE
+                    synctex_node_log(child);
+#               endif
+                               SYNCTEX_UPDATE_FRIEND(child);
+                               _synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
+                               goto sibling_loop;
+                       } else {
+                               _synctex_error("Can't create boundary record (2).");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
+                       goto scan_teehs;
+               } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_ANCHOR) {
+                       ++SYNCTEX_CUR;
+                       if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                               _synctex_error("Missing anchor (2).");
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+                       goto sibling_loop;
+               } else {
+                       ++SYNCTEX_CUR;
+                       /* _synctex_error("Ignored record %c(2)\n",*SYNCTEX_CUR); */
+                       if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+                               SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+                       }
+                       goto sibling_loop;
+               }
+       } else {
+               available = 1;
+               status = _synctex_buffer_get_available_size(scanner,&available);
+               if (status<SYNCTEX_STATUS_OK && available>0){
+                       goto sibling_loop;
+               } else {
+                       _synctex_error("Uncomplete sheet(2)");
+                       SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
+               }
+       }
+#   undef SYNCTEX_DECODE_FAILED
+}
+
+#   define SYNCTEX_APPEND_SHEET(SCANNER,SHEET) if (SCANNER->sheet) {\
+        synctex_node_t last_sheet = SCANNER->sheet;\
+        synctex_node_t next_sheet = NULL;\
+        while ((next_sheet = SYNCTEX_SIBLING(last_sheet))) {\
+            last_sheet = next_sheet;\
+        }\
+               SYNCTEX_SET_SIBLING(last_sheet,SHEET);\
+    } else {\
+        SCANNER->sheet = SHEET;\
+    }
+
+/*  Used when parsing the synctex file
+ */
+synctex_status_t _synctex_scan_content(synctex_scanner_t scanner) {
+       synctex_node_t sheet = NULL;
+       synctex_status_t status = 0;
+       if (NULL == scanner) {
+               return SYNCTEX_STATUS_BAD_ARGUMENT;
+       }
+       /*  set up the lists of friends */
+       if (NULL == scanner->lists_of_friends) {
+               scanner->number_of_lists = 1024;
+               scanner->lists_of_friends = (synctex_node_t *)_synctex_malloc(scanner->number_of_lists*sizeof(synctex_node_t));
+               if (NULL == scanner->lists_of_friends) {
+                       _synctex_error("malloc:2");
+                       return SYNCTEX_STATUS_ERROR;
+               }
+       }
+       /*  Find where this section starts */
+content_not_found:
+       status = _synctex_match_string(scanner,"Content:");
+       if (status<SYNCTEX_STATUS_EOF) {
+               return status;
+       }
+       if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
+               _synctex_error("Uncomplete Content.");
+               return SYNCTEX_STATUS_ERROR;
+       }
+       if (status == SYNCTEX_STATUS_NOT_OK) {
+               goto content_not_found;
+       }
+next_sheet:
+       if (*SYNCTEX_CUR != SYNCTEX_CHAR_BEGIN_SHEET) {
+               status = _synctex_scan_postamble(scanner);
+               if (status < SYNCTEX_STATUS_EOF) {
+                       _synctex_error("Bad content.");
+                       return status;
+               }
+               if (status<SYNCTEX_STATUS_OK) {
+                       status = _synctex_next_line(scanner);
+                       if (status < SYNCTEX_STATUS_OK) {
+                               _synctex_error("Bad content.");
+                               return status;
+                       }
+                       goto next_sheet;
+               }
+               return SYNCTEX_STATUS_OK;
+       }
+       /*  Create a new sheet node */
+       sheet = _synctex_new_sheet(scanner);
+       status = _synctex_decode_int(scanner,&(SYNCTEX_PAGE(sheet)));
+       if (status<SYNCTEX_STATUS_OK) {
+               _synctex_error("Missing sheet number.");
+bail:
+               SYNCTEX_FREE(sheet);
+               return SYNCTEX_STATUS_ERROR;
+       }
+       status = _synctex_next_line(scanner);
+       if (status<SYNCTEX_STATUS_OK) {
+               _synctex_error("Uncomplete file.");
+               goto bail;
+       }
+       status = _synctex_scan_sheet(scanner,sheet);
+       if (status<SYNCTEX_STATUS_OK) {
+               _synctex_error("Bad sheet content.");
+               goto bail;
+       }
+       SYNCTEX_APPEND_SHEET(scanner,sheet);
+       sheet = NULL;
+       /*  Now read the list of Inputs between 2 sheets. */
+       do {
+               status = _synctex_scan_input(scanner);
+               if (status<SYNCTEX_STATUS_EOF) {
+                       _synctex_error("Bad input section.");
+                       goto bail;
+               }
+       }
+       while(status >= SYNCTEX_STATUS_OK);
+       goto next_sheet;
+}
+
+int _synctex_open(const char * output, const char * build_directory, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_modeRef);
+
+/*  Where the synctex scanner is created. */
+synctex_scanner_t synctex_scanner_new_with_output_file(const char * output, const char * build_directory, int parse) {
+       gzFile file = NULL;
+       char * synctex = NULL;
+       synctex_scanner_t scanner = NULL;
+       synctex_io_mode_t io_mode = 0;
+       /*  Here we assume that int are smaller than void * */
+       if (sizeof(int)>sizeof(void*)) {
+               _synctex_error("INTERNAL INCONSISTENCY: int's are unexpectedly bigger than pointers, bailing out.");
+               return NULL;
+       }
+       /*  We ensure that SYNCTEX_BUFFER_SIZE < UINT_MAX, I don't know if it makes sense... */
+       if (SYNCTEX_BUFFER_SIZE >= UINT_MAX) {
+               _synctex_error("Internal inconsistency, bad SYNCTEX_BUFFER_SIZE (1)");
+               return NULL;
+       }
+       /*  for integers: */
+       if (SYNCTEX_BUFFER_SIZE < SYNCTEX_BUFFER_MIN_SIZE) {
+               _synctex_error("Internal inconsistency, bad SYNCTEX_BUFFER_SIZE (2)");
+               return NULL;
+       }
+       /*  now open the synctex file */
+       if (_synctex_open(output,build_directory,&synctex,&file,synctex_ADD_QUOTES,&io_mode) || !file) {
+               if (_synctex_open(output,build_directory,&synctex,&file,synctex_DONT_ADD_QUOTES,&io_mode) || !file) {
+                       return NULL;
+               }
+       }
+       scanner = (synctex_scanner_t)_synctex_malloc(sizeof(_synctex_scanner_t));
+       if (NULL == scanner) {
+               _synctex_error("malloc problem");
+               free(synctex);
+               gzclose(file);
+               return NULL;
+       }
+       /*  make a private copy of output for the scanner */
+       if (NULL == (scanner->output = (char *)malloc(strlen(output)+1))){
+               _synctex_error("!  synctex_scanner_new_with_output_file: Memory problem (2), scanner's output is not reliable.");
+       } else if (scanner->output != strcpy(scanner->output,output)) {
+               _synctex_error("!  synctex_scanner_new_with_output_file: Copy problem, scanner's output is not reliable.");
+       }
+       scanner->synctex = synctex;/*  Now the scanner owns synctex */
+       SYNCTEX_FILE = file;
+       return parse? synctex_scanner_parse(scanner):scanner;
+}
+
+/*     This functions opens the file at the "output" given location.
+ *  It manages the problem of quoted filenames that appear with pdftex and filenames containing the space character.
+ *  In TeXLive 2008, the synctex file created with pdftex did contain unexpected quotes.
+ *     This function will remove them if possible.
+ *  All the reference arguments will take a value on return. They must be non NULL.
+ *     0 on success, non 0 on error. */
+static int __synctex_open(const char * output, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_mode_ref) {
+       if (synctex_name_ref && file_ref && io_mode_ref) {
+        /*  1 local variables that uses dynamic memory */
+        char * synctex_name = NULL;
+        gzFile the_file = NULL;
+        char * quoteless_synctex_name = NULL;
+               size_t size = 0;
+        synctex_io_mode_t io_mode = *io_mode_ref;
+               const char * mode = _synctex_get_io_mode_name(io_mode);
+               /*  now create the synctex file name */
+               size = strlen(output)+strlen(synctex_suffix)+strlen(synctex_suffix_gz)+1;
+               synctex_name = (char *)malloc(size);
+               if (NULL == synctex_name) {
+                       _synctex_error("!  __synctex_open: Memory problem (1)\n");
+                       return 1;
+               }
+               /*  we have reserved for synctex enough memory to copy output (including its 2 eventual quotes), both suffices,
+                *  including the terminating character. size is free now. */
+               if (synctex_name != strcpy(synctex_name,output)) {
+                       _synctex_error("!  __synctex_open: Copy problem\n");
+return_on_error:
+                       free(synctex_name);
+                       free(quoteless_synctex_name);
+                       return 2;
+               }
+               /*  remove the last path extension if any */
+               _synctex_strip_last_path_extension(synctex_name);
+               if (!strlen(synctex_name)) {
+                       goto return_on_error;           
+               }
+               /*  now insert quotes. */
+               if (add_quotes) {
+                       char * quoted = NULL;
+                       if (_synctex_copy_with_quoting_last_path_component(synctex_name,&quoted,size) || (NULL == quoted)) {
+                               /*      There was an error or quoting does not make sense: */
+                               goto return_on_error;
+                       }
+                       quoteless_synctex_name = synctex_name;
+                       synctex_name = quoted;
+               }
+               /*      Now add to synctex_name the first path extension. */
+               if (synctex_name != strcat(synctex_name,synctex_suffix)){
+                       _synctex_error("!  __synctex_open: Concatenation problem (can't add suffix '%s')\n",synctex_suffix);
+                       goto return_on_error;
+               }
+               /*      Add to quoteless_synctex_name as well, if relevant. */
+               if (quoteless_synctex_name && (quoteless_synctex_name != strcat(quoteless_synctex_name,synctex_suffix))){
+                       free(quoteless_synctex_name);
+                       quoteless_synctex_name = NULL;
+               }
+               if (NULL == (the_file = gzopen(synctex_name,mode))) {
+                       /*  Could not open this file */
+                       if (errno != ENOENT) {
+                               /*  The file does exist, this is a lower level error, I can't do anything. */
+                               _synctex_error("could not open %s, error %i\n",synctex_name,errno);
+                               goto return_on_error;
+                       }
+                       /*  Apparently, there is no uncompressed synctex file. Try the compressed version */
+                       if (synctex_name != strcat(synctex_name,synctex_suffix_gz)){
+                               _synctex_error("!  __synctex_open: Concatenation problem (can't add suffix '%s')\n",synctex_suffix_gz);
+                               goto return_on_error;
+                       }
+                       io_mode |= synctex_io_gz_mask;
+                       mode = _synctex_get_io_mode_name(io_mode); /* the file is a compressed and is a binary file, this caused errors on Windows */
+                       /*      Add the suffix to the quoteless_synctex_name as well. */
+                       if (quoteless_synctex_name && (quoteless_synctex_name != strcat(quoteless_synctex_name,synctex_suffix_gz))){
+                               free(quoteless_synctex_name);
+                               quoteless_synctex_name = NULL;
+                       }
+                       if (NULL == (the_file = gzopen(synctex_name,mode))) {
+                               /*  Could not open this file */
+                               if (errno != ENOENT) {
+                                       /*  The file does exist, this is a lower level error, I can't do anything. */
+                                       _synctex_error("Could not open %s, error %i\n",synctex_name,errno);
+                               }
+                               goto return_on_error;
+                       }
+               }
+               /*      At this point, the file is properly open.
+                *  If we are in the add_quotes mode, we change the file name by removing the quotes. */
+               if (quoteless_synctex_name) {
+                       gzclose(the_file);
+                       if (rename(synctex_name,quoteless_synctex_name)) {
+                               _synctex_error("Could not rename %s to %s, error %i\n",synctex_name,quoteless_synctex_name,errno);
+                               /*      We could not rename, reopen the file with the quoted name. */
+                               if (NULL == (the_file = gzopen(synctex_name,mode))) {
+                                       /*  No luck, could not re open this file, something has happened meanwhile */
+                                       if (errno != ENOENT) {
+                                               /*  The file does not exist any more, it has certainly be removed somehow
+                         *  this is a lower level error, I can't do anything. */
+                                               _synctex_error("Could not open again %s, error %i\n",synctex_name,errno);
+                                       }
+                                       goto return_on_error;
+                               }
+                       } else {
+                /*  The file has been successfully renamed */
+                               if (NULL == (the_file = gzopen(quoteless_synctex_name,mode))) {
+                                       /*  Could not open this file */
+                                       if (errno != ENOENT) {
+                                               /*  The file does exist, this is a lower level error, I can't do anything. */
+                                               _synctex_error("Could not open renamed %s, error %i\n",quoteless_synctex_name,errno);
+                                       }
+                                       goto return_on_error;
+                               }
+                               /*  The quote free file name should replace the old one:*/
+                               free(synctex_name);
+                               synctex_name = quoteless_synctex_name;
+                               quoteless_synctex_name = NULL;
+                       }
+               }
+        /*  The operation is successfull, return the arguments by value.    */
+        * file_ref = the_file;
+        * io_mode_ref = io_mode;
+        * synctex_name_ref = synctex_name;
+               return 0;
+       }
+       return 3;       /*      Bad parameter.  */
+}
+
+/*     Opens the ouput file, taking into account the eventual build_directory.
+ *     0 on success, non 0 on error. */
+int _synctex_open(const char * output, const char * build_directory, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_mode_ref) {
+#      define synctex_name (*synctex_name_ref)
+#      define the_file (*file_ref)
+       int result = __synctex_open(output,synctex_name_ref,file_ref,add_quotes,io_mode_ref);
+       if ((result || !*file_ref) && build_directory && strlen(build_directory)) {
+               char * build_output;
+               const char *lpc;
+               size_t size;
+               synctex_bool_t is_absolute;
+               build_output = NULL;
+               lpc = _synctex_last_path_component(output);
+               size = strlen(build_directory)+strlen(lpc)+2;   /*  One for the '/' and one for the '\0'.   */
+               is_absolute = _synctex_path_is_absolute(build_directory);
+               if (!is_absolute) {
+                       size += strlen(output);
+               }
+               if ((build_output = (char *)malloc(size))) {
+                       if (is_absolute) {
+                               build_output[0] = '\0';
+                       } else {
+                               if (build_output != strcpy(build_output,output)) {
+                                       free(build_output);
+                                       return -4;
+                               }
+                               build_output[lpc-output]='\0';
+                       }
+                       if (build_output == strcat(build_output,build_directory)) {
+                               /*      Append a path separator if necessary. */
+                               if (!SYNCTEX_IS_PATH_SEPARATOR(build_output[strlen(build_directory)-1])) {
+                                       if (build_output != strcat(build_output,"/")) {
+                                               free(build_output);
+                                               return -2;
+                                       }
+                               }
+                               /*      Append the last path component of the output. */
+                               if (build_output != strcat(build_output,lpc)) {
+                                       free(build_output);
+                                       return -3;
+                               }
+                               result = __synctex_open(build_output,synctex_name_ref,file_ref,add_quotes,io_mode_ref);
+                               free(build_output);
+                               return result;
+                       }
+                       free(build_output);
+               }
+               return -1;
+       }
+       return result;
+#      undef synctex_name
+#      undef the_file
+}
+
+/*  The scanner destructor
+ */
+void synctex_scanner_free(synctex_scanner_t scanner) {
+       if (NULL == scanner) {
+               return;
+       }
+       if (SYNCTEX_FILE) {
+               gzclose(SYNCTEX_FILE);
+               SYNCTEX_FILE = NULL;
+       }
+       SYNCTEX_FREE(scanner->sheet);
+       SYNCTEX_FREE(scanner->input);
+       free(SYNCTEX_START);
+       free(scanner->output_fmt);
+       free(scanner->output);
+       free(scanner->synctex);
+       free(scanner->lists_of_friends);
+       free(scanner);
+}
+
+/*  Where the synctex scanner parses the contents of the file. */
+synctex_scanner_t synctex_scanner_parse(synctex_scanner_t scanner) {
+       synctex_status_t status = 0;
+       if (!scanner || scanner->flags.has_parsed) {
+               return scanner;
+       }
+       scanner->flags.has_parsed=1;
+       scanner->pre_magnification = 1000;
+       scanner->pre_unit = 8192;
+       scanner->pre_x_offset = scanner->pre_y_offset = 578;
+       /*  initialize the offset with a fake unprobable value,
+        *  If there is a post scriptum section, this value will be overriden by the real life value */
+       scanner->x_offset = scanner->y_offset = 6.027e23f;
+#   define DEFINE_synctex_scanner_class(NAME)\
+       scanner->class[synctex_node_type_##NAME] = synctex_class_##NAME;\
+       (scanner->class[synctex_node_type_##NAME]).scanner = scanner
+    DEFINE_synctex_scanner_class(sheet);
+    DEFINE_synctex_scanner_class(input);
+    DEFINE_synctex_scanner_class(hbox);
+    DEFINE_synctex_scanner_class(void_hbox);
+    DEFINE_synctex_scanner_class(vbox);
+    DEFINE_synctex_scanner_class(void_vbox);
+    DEFINE_synctex_scanner_class(kern);
+    DEFINE_synctex_scanner_class(glue);
+    DEFINE_synctex_scanner_class(math);
+    DEFINE_synctex_scanner_class(boundary);
+       SYNCTEX_START = (char *)malloc(SYNCTEX_BUFFER_SIZE+1); /*  one more character for null termination */
+       if (NULL == SYNCTEX_START) {
+               _synctex_error("malloc error");
+               synctex_scanner_free(scanner);
+               return NULL;
+       }
+       SYNCTEX_END = SYNCTEX_START+SYNCTEX_BUFFER_SIZE;
+       /*  SYNCTEX_END always points to a null terminating character.
+        *  Maybe there is another null terminating character between SYNCTEX_CUR and SYNCTEX_END-1.
+        *  At least, we are sure that SYNCTEX_CUR points to a string covering a valid part of the memory. */
+       *SYNCTEX_END = '\0';
+       SYNCTEX_CUR = SYNCTEX_END;
+#   if defined(SYNCTEX_USE_CHARINDEX)
+    scanner->charindex_offset = -SYNCTEX_BUFFER_SIZE;
+#   endif
+       status = _synctex_scan_preamble(scanner);
+       if (status<SYNCTEX_STATUS_OK) {
+               _synctex_error("SyncTeX Error: Bad preamble\n");
+bailey:
+               synctex_scanner_free(scanner);
+               return NULL;
+       }
+       status = _synctex_scan_content(scanner);
+       if (status<SYNCTEX_STATUS_OK) {
+               _synctex_error("SyncTeX Error: Bad content\n");
+               goto bailey;
+       }
+       /*  Everything is finished, free the buffer, close the file */
+       free((void *)SYNCTEX_START);
+       SYNCTEX_START = SYNCTEX_CUR = SYNCTEX_END = NULL;
+       gzclose(SYNCTEX_FILE);
+       SYNCTEX_FILE = NULL;
+       /*  Final tuning: set the default values for various parameters */
+       /*  1 pre_unit = (scanner->pre_unit)/65536 pt = (scanner->pre_unit)/65781.76 bp
+        * 1 pt = 65536 sp */
+       if (scanner->pre_unit<=0) {
+               scanner->pre_unit = 8192;
+       }
+       if (scanner->pre_magnification<=0) {
+               scanner->pre_magnification = 1000;
+       }
+       if (scanner->unit <= 0) {
+               /*  no post magnification */
+               scanner->unit = scanner->pre_unit / 65781.76;/*  65781.76 or 65536.0*/
+       } else {
+               /*  post magnification */
+               scanner->unit *= scanner->pre_unit / 65781.76;
+       }
+       scanner->unit *= scanner->pre_magnification / 1000.0;
+       if (scanner->x_offset > 6e23) {
+               /*  no post offset */
+               scanner->x_offset = scanner->pre_x_offset * (scanner->pre_unit / 65781.76);
+               scanner->y_offset = scanner->pre_y_offset * (scanner->pre_unit / 65781.76);
+       } else {
+               /*  post offset */
+               scanner->x_offset /= 65781.76f;
+               scanner->y_offset /= 65781.76f;
+       }
+       return scanner;
+       #undef SYNCTEX_FILE
+}
+
+/*  Scanner accessors.
+ */
+int synctex_scanner_pre_x_offset(synctex_scanner_t scanner){
+       return scanner?scanner->pre_x_offset:0;
+}
+int synctex_scanner_pre_y_offset(synctex_scanner_t scanner){
+       return scanner?scanner->pre_y_offset:0;
+}
+int synctex_scanner_x_offset(synctex_scanner_t scanner){
+       return scanner?scanner->x_offset:0;
+}
+int synctex_scanner_y_offset(synctex_scanner_t scanner){
+       return scanner?scanner->y_offset:0;
+}
+float synctex_scanner_magnification(synctex_scanner_t scanner){
+       return scanner?scanner->unit:1;
+}
+void synctex_scanner_display(synctex_scanner_t scanner) {
+       if (NULL == scanner) {
+               return;
+       }
+       printf("The scanner:\noutput:%s\noutput_fmt:%s\nversion:%i\n",scanner->output,scanner->output_fmt,scanner->version);
+       printf("pre_unit:%i\nx_offset:%i\ny_offset:%i\n",scanner->pre_unit,scanner->pre_x_offset,scanner->pre_y_offset);
+       printf("count:%i\npost_magnification:%f\npost_x_offset:%f\npost_y_offset:%f\n",
+               scanner->count,scanner->unit,scanner->x_offset,scanner->y_offset);
+       printf("The input:\n");
+       SYNCTEX_DISPLAY(scanner->input);
+       if (scanner->count<1000) {
+               printf("The sheets:\n");
+               SYNCTEX_DISPLAY(scanner->sheet);
+               printf("The friends:\n");
+               if (scanner->lists_of_friends) {
+                       int i = scanner->number_of_lists;
+                       synctex_node_t node;
+                       while(i--) {
+                               printf("Friend index:%i\n",i);
+                               node = (scanner->lists_of_friends)[i];
+                               while(node) {
+                                       printf("%s:%i,%i\n",
+                                               synctex_node_isa(node),
+                                               SYNCTEX_TAG(node),
+                                               SYNCTEX_LINE(node)
+                                       );
+                                       node = SYNCTEX_FRIEND(node);
+                               }
+                       }
+               }
+       } else {
+               printf("SyncTeX Warning: Too many objects\n");
+       }
+}
+/*  Public*/
+const char * synctex_scanner_get_name(synctex_scanner_t scanner,int tag) {
+       synctex_node_t input = NULL;
+       if (NULL == scanner) {
+               return NULL;
+       }
+       input = scanner->input;
+       do {
+               if (tag == SYNCTEX_TAG(input)) {
+                       return (SYNCTEX_NAME(input));
+               }
+       } while((input = SYNCTEX_SIBLING(input)) != NULL);
+       return NULL;
+}
+
+int _synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name);
+int _synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name) {
+       synctex_node_t input = NULL;
+       if (NULL == scanner) {
+               return 0;
+       }
+       input = scanner->input;
+       do {
+               if (_synctex_is_equivalent_file_name(name,(SYNCTEX_NAME(input)))) {
+                       return SYNCTEX_TAG(input);
+               }
+       } while((input = SYNCTEX_SIBLING(input)) != NULL);
+    //  2011 version
+    name = _synctex_base_name(name);
+       input = scanner->input;
+       do {
+               if (_synctex_is_equivalent_file_name(name,_synctex_base_name(SYNCTEX_NAME(input)))) {
+            synctex_node_t other_input = input;
+            while((other_input = SYNCTEX_SIBLING(other_input)) != NULL) {
+                if (_synctex_is_equivalent_file_name(name,_synctex_base_name(SYNCTEX_NAME(other_input)))
+                    && (strlen(SYNCTEX_NAME(input))!=strlen(SYNCTEX_NAME(other_input))
+                        || strncmp(SYNCTEX_NAME(other_input),SYNCTEX_NAME(input),strlen(SYNCTEX_NAME(input))))) {
+                    // There is a second possible candidate
+                    return 0;
+                }
+            }
+                       return SYNCTEX_TAG(input);
+               }
+       } while((input = SYNCTEX_SIBLING(input)) != NULL);
+       return 0;
+}
+
+int synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name) {
+       size_t char_index = strlen(name);
+       if ((scanner = synctex_scanner_parse(scanner)) && (0 < char_index)) {
+               /*  the name is not void */
+               char_index -= 1;
+               if (!SYNCTEX_IS_PATH_SEPARATOR(name[char_index])) {
+                       /*  the last character of name is not a path separator */
+                       int result = _synctex_scanner_get_tag(scanner,name);
+                       if (result) {
+                               return result;
+                       } else {
+                               /*  the given name was not the one known by TeX
+                                *  try a name relative to the enclosing directory of the scanner->output file */
+                               const char * relative = name;
+                               const char * ptr = scanner->output;
+                               while((strlen(relative) > 0) && (strlen(ptr) > 0) && (*relative == *ptr))
+                               {
+                                       relative += 1;
+                                       ptr += 1;
+                               }
+                               /*  Find the last path separator before relative */
+                               while(relative > name) {
+                                       if (SYNCTEX_IS_PATH_SEPARATOR(*(relative-1))) {
+                                               break;
+                                       }
+                                       relative -= 1;
+                               }
+                               if ((relative > name) && (result = _synctex_scanner_get_tag(scanner,relative))) {
+                                       return result;
+                               }
+                               if (SYNCTEX_IS_PATH_SEPARATOR(name[0])) {
+                                       /*  No tag found for the given absolute name,
+                                        *  Try each relative path starting from the shortest one */
+                                       while(0<char_index) {
+                                               char_index -= 1;
+                                               if (SYNCTEX_IS_PATH_SEPARATOR(name[char_index])
+                                                               && (result = _synctex_scanner_get_tag(scanner,name+char_index+1))) {
+                                                       return result;
+                                               }
+                                       }
+                               }
+                       }
+                       return result;
+               }
+       }
+       return 0;
+}
+synctex_node_t synctex_scanner_input(synctex_scanner_t scanner) {
+       return scanner?scanner->input:NULL;
+}
+const char * synctex_scanner_get_output_fmt(synctex_scanner_t scanner) {
+       return NULL != scanner && scanner->output_fmt?scanner->output_fmt:"";
+}
+const char * synctex_scanner_get_output(synctex_scanner_t scanner) {
+       return NULL != scanner && scanner->output?scanner->output:"";
+}
+const char * synctex_scanner_get_synctex(synctex_scanner_t scanner) {
+       return NULL != scanner && scanner->synctex?scanner->synctex:"";
+}
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Public node attributes
+#   endif
+int synctex_node_h(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       return SYNCTEX_HORIZ(node);
+}
+int synctex_node_v(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       return SYNCTEX_VERT(node);
+}
+int synctex_node_width(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       return SYNCTEX_WIDTH(node);
+}
+int synctex_node_box_h(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       if (SYNCTEX_IS_BOX(node)) {
+result:
+               return SYNCTEX_HORIZ(node);
+       }
+       if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+               goto result;
+       }
+       return 0;
+}
+int synctex_node_box_v(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       if (SYNCTEX_IS_BOX(node)) {
+result:
+               return SYNCTEX_VERT(node);
+       }
+       if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+               goto result;
+       }
+       return 0;
+}
+int synctex_node_box_width(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       if (SYNCTEX_IS_BOX(node)) {
+result:
+               return SYNCTEX_WIDTH(node);
+       }
+       if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+               goto result;
+       }
+       return 0;
+}
+int synctex_node_box_height(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       if (SYNCTEX_IS_BOX(node)) {
+result:
+               return SYNCTEX_HEIGHT(node);
+       }
+       if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+               goto result;
+       }
+       return 0;
+}
+int synctex_node_box_depth(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       if (SYNCTEX_IS_BOX(node)) {
+result:
+               return SYNCTEX_DEPTH(node);
+       }
+       if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+               goto result;
+       }
+       return 0;
+}
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Public node visible attributes
+#   endif
+float synctex_node_visible_h(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       return SYNCTEX_HORIZ(node)*node->class->scanner->unit+node->class->scanner->x_offset;
+}
+float synctex_node_visible_v(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       return SYNCTEX_VERT(node)*node->class->scanner->unit+node->class->scanner->y_offset;
+}
+float synctex_node_visible_width(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       return SYNCTEX_WIDTH(node)*node->class->scanner->unit;
+}
+float synctex_node_box_visible_h(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       switch(node->class->type) {
+               case synctex_node_type_vbox:
+               case synctex_node_type_void_vbox:
+               case synctex_node_type_void_hbox:
+                       return SYNCTEX_HORIZ(node)*node->class->scanner->unit+node->class->scanner->x_offset;
+               case synctex_node_type_hbox:
+result:
+                       return SYNCTEX_HORIZ_V(node)*node->class->scanner->unit+node->class->scanner->x_offset;
+       }
+       if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+               goto result;
+       }
+       return 0;
+}
+float synctex_node_box_visible_v(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       switch(node->class->type) {
+               case synctex_node_type_vbox:
+               case synctex_node_type_void_vbox:
+               case synctex_node_type_void_hbox:
+                       return SYNCTEX_VERT(node)*node->class->scanner->unit+node->class->scanner->y_offset;
+               case synctex_node_type_hbox:
+result:
+                       return SYNCTEX_VERT_V(node)*node->class->scanner->unit+node->class->scanner->y_offset;
+       }
+       if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+               goto result;
+       }
+       return 0;
+}
+float synctex_node_box_visible_width(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       switch(node->class->type) {
+               case synctex_node_type_vbox:
+               case synctex_node_type_void_vbox:
+               case synctex_node_type_void_hbox:
+                       return SYNCTEX_WIDTH(node)*node->class->scanner->unit;
+               case synctex_node_type_hbox:
+result:
+                       return SYNCTEX_WIDTH_V(node)*node->class->scanner->unit;
+       }
+       if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+               goto result;
+       }
+       return 0;
+}
+float synctex_node_box_visible_height(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       switch(node->class->type) {
+               case synctex_node_type_vbox:
+               case synctex_node_type_void_vbox:
+               case synctex_node_type_void_hbox:
+                       return SYNCTEX_HEIGHT(node)*node->class->scanner->unit;
+               case synctex_node_type_hbox:
+result:
+                       return SYNCTEX_HEIGHT_V(node)*node->class->scanner->unit;
+       }
+       if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+               goto result;
+       }
+       return 0;
+}
+float synctex_node_box_visible_depth(synctex_node_t node){
+       if (!node) {
+               return 0;
+       }
+       switch(node->class->type) {
+               case synctex_node_type_vbox:
+               case synctex_node_type_void_vbox:
+               case synctex_node_type_void_hbox:
+                       return SYNCTEX_DEPTH(node)*node->class->scanner->unit;
+               case synctex_node_type_hbox:
+result:
+                       return SYNCTEX_DEPTH_V(node)*node->class->scanner->unit;
+       }
+       if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
+               goto result;
+       }
+       return 0;
+}
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Other public node attributes
+#   endif
+
+int synctex_node_page(synctex_node_t node){
+       synctex_node_t parent = NULL;
+       if (!node) {
+               return -1;
+       }
+       parent = SYNCTEX_PARENT(node);
+       while(parent) {
+               node = parent;
+               parent = SYNCTEX_PARENT(node);
+       }
+       if (node->class->type == synctex_node_type_sheet) {
+               return SYNCTEX_PAGE(node);
+       }
+       return -1;
+}
+synctex_charindex_t synctex_node_charindex(synctex_node_t node) {
+       return node?SYNCTEX_CHARINDEX(node):0;
+}
+int synctex_node_tag(synctex_node_t node) {
+       return node?SYNCTEX_TAG(node):-1;
+}
+int synctex_node_line(synctex_node_t node) {
+       return node?SYNCTEX_LINE(node):-1;
+}
+int synctex_node_mean_line(synctex_node_t node) {
+       return node?(node->class->type==synctex_node_type_hbox?SYNCTEX_MEAN_LINE(node):SYNCTEX_LINE(node)):-1;
+}
+int synctex_node_child_count(synctex_node_t node) {
+       return node?(node->class->type==synctex_node_type_hbox?SYNCTEX_NODE_WEIGHT(node):0):-1;
+}
+int synctex_node_column(synctex_node_t node) {
+#      ifdef __DARWIN_UNIX03
+#       pragma unused(node)
+#   endif
+       return -1;
+}
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Sheet
+#   endif
+
+synctex_node_t synctex_sheet(synctex_scanner_t scanner,int page) {
+       if (scanner) {
+               synctex_node_t sheet = scanner->sheet;
+               while(sheet) {
+                       if (page == SYNCTEX_PAGE(sheet)) {
+                               return sheet;
+                       }
+                       sheet = SYNCTEX_SIBLING(sheet);
+               }
+       }
+       return NULL;
+}
+
+synctex_node_t synctex_sheet_content(synctex_scanner_t scanner,int page) {
+       if (scanner) {
+               return SYNCTEX_CHILD(synctex_sheet(scanner,page));
+       }
+       return NULL;
+}
+
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Query
+#   endif
+
+synctex_status_t synctex_display_query(synctex_scanner_t scanner,const char * name,int line,int column) {
+#      ifdef __DARWIN_UNIX03
+#       pragma unused(column)
+#   endif
+       int tag = synctex_scanner_get_tag(scanner,name);
+       size_t size = 0;
+       int friend_index = 0;
+       int max_line = 0;
+       synctex_node_t node = NULL;
+       if (tag == 0) {
+               printf("SyncTeX Warning: No tag for %s\n",name);
+               return -1;
+       }
+       free(SYNCTEX_START);
+       SYNCTEX_CUR = SYNCTEX_END = SYNCTEX_START = NULL;
+       max_line = line < INT_MAX-scanner->number_of_lists ? line+scanner->number_of_lists:INT_MAX;
+       while(line<max_line) {
+               /*  This loop will only be performed once for advanced viewers */
+               friend_index = (tag+line)%(scanner->number_of_lists);
+               if ((node = (scanner->lists_of_friends)[friend_index])) {
+                       do {
+                               if ((synctex_node_type(node)>=synctex_node_type_boundary)
+                                       && (tag == SYNCTEX_TAG(node))
+                                               && (line == SYNCTEX_LINE(node))) {
+                                       if (SYNCTEX_CUR == SYNCTEX_END) {
+                                               size += 16;
+                                               SYNCTEX_END = realloc(SYNCTEX_START,size*sizeof(synctex_node_t *));
+                                               SYNCTEX_CUR += SYNCTEX_END - SYNCTEX_START;
+                                               SYNCTEX_START = SYNCTEX_END;
+                                               SYNCTEX_END = SYNCTEX_START + size*sizeof(synctex_node_t *);
+                                       }                       
+                                       *(synctex_node_t *)SYNCTEX_CUR = node;
+                                       SYNCTEX_CUR += sizeof(synctex_node_t);
+                               }
+                       } while ((node = SYNCTEX_FRIEND(node)));
+                       if (SYNCTEX_START == NULL) {
+                               /*  We did not find any matching boundary, retry with glue or kern */
+                               node = (scanner->lists_of_friends)[friend_index];/*  no need to test it again, already done */
+                               do {
+                                       if ((synctex_node_type(node)>=synctex_node_type_kern)
+                                               && (tag == SYNCTEX_TAG(node))
+                                                       && (line == SYNCTEX_LINE(node))) {
+                                               if (SYNCTEX_CUR == SYNCTEX_END) {
+                                                       size += 16;
+                                                       SYNCTEX_END = realloc(SYNCTEX_START,size*sizeof(synctex_node_t *));
+                                                       SYNCTEX_CUR += SYNCTEX_END - SYNCTEX_START;
+                                                       SYNCTEX_START = SYNCTEX_END;
+                                                       SYNCTEX_END = SYNCTEX_START + size*sizeof(synctex_node_t *);
+                                               }                       
+                                               *(synctex_node_t *)SYNCTEX_CUR = node;
+                                               SYNCTEX_CUR += sizeof(synctex_node_t);
+                                       }
+                               } while ((node = SYNCTEX_FRIEND(node)));
+                               if (SYNCTEX_START == NULL) {
+                                       /*  We did not find any matching glue or kern, retry with boxes */
+                                       node = (scanner->lists_of_friends)[friend_index];/*  no need to test it again, already done */
+                                       do {
+                                               if ((tag == SYNCTEX_TAG(node))
+                                                               && (line == SYNCTEX_LINE(node))) {
+                                                       if (SYNCTEX_CUR == SYNCTEX_END) {
+                                                               size += 16;
+                                                               SYNCTEX_END = realloc(SYNCTEX_START,size*sizeof(synctex_node_t *));
+                                                               SYNCTEX_CUR += SYNCTEX_END - SYNCTEX_START;
+                                                               SYNCTEX_START = SYNCTEX_END;
+                                                               SYNCTEX_END = SYNCTEX_START + size*sizeof(synctex_node_t *);
+                                                       }                       
+                                                       *(synctex_node_t *)SYNCTEX_CUR = node;
+                                                       SYNCTEX_CUR += sizeof(synctex_node_t);
+                                               }
+                                       } while((node = SYNCTEX_FRIEND(node)));
+                               }
+                       }
+                       SYNCTEX_END = SYNCTEX_CUR;
+                       /*  Now reverse the order to have nodes in display order, and then keep just a few nodes.
+             *  Order first the best node. */
+                       if ((SYNCTEX_START) && (SYNCTEX_END)) {
+                unsigned int best_match = -1;
+                unsigned int next_match = -1;
+                unsigned int best_weight = 0;
+                synctex_node_t * best_ref   = NULL;
+                               synctex_node_t * start_ref = (synctex_node_t *)SYNCTEX_START;
+                               synctex_node_t * end_ref   = (synctex_node_t *)SYNCTEX_END;
+                               --end_ref;
+                               while (start_ref < end_ref) {
+                                       node = *start_ref;
+                                       *start_ref = *end_ref;
+                                       *end_ref = node;
+                                       ++start_ref;
+                                       --end_ref;
+                               }
+                /*  Now reorder the nodes to put first the one which fits best.
+                 *  The idea is to walk along the list of nodes and pick up the first one
+                 *  which line info is exactly the mean line of its parent, or at least very close.
+                 *  Then we choose among all such node the one with the maximum number of child nodes.
+                 *  Then we switch with the first node.
+                 */
+                best_ref = start_ref = (synctex_node_t *)SYNCTEX_START;
+                node = *start_ref;
+                best_match = abs(SYNCTEX_LINE(node)-SYNCTEX_MEAN_LINE(SYNCTEX_PARENT(node)));
+                end_ref = (synctex_node_t *)SYNCTEX_END;
+                               while (++start_ref<end_ref) {
+                    synctex_node_t parent = NULL;
+                                       node = *start_ref;
+                    parent = SYNCTEX_PARENT(node);
+                    next_match = abs(SYNCTEX_LINE(node)-SYNCTEX_MEAN_LINE(parent));
+                    if (next_match < best_match
+                            || (next_match == best_match && SYNCTEX_NODE_WEIGHT(parent)>best_weight)) {
+                        best_match = next_match;
+                        best_ref = start_ref;
+                        best_weight = SYNCTEX_NODE_WEIGHT(parent);
+                    }
+                               }
+                node = *best_ref;
+                *best_ref = *(synctex_node_t *)SYNCTEX_START;
+                *(synctex_node_t *)SYNCTEX_START = node;
+                               /*  Basically, we keep the first node for each parent.
+                                *  More precisely, we keep only nodes that are not children of
+                                *  their predecessor's parent. */
+                               start_ref = (synctex_node_t *)SYNCTEX_START;
+                               end_ref   = (synctex_node_t *)SYNCTEX_START;
+next_end:
+                               end_ref += 1; /*  we allways have start_ref<= end_ref*/
+                               if (end_ref < (synctex_node_t *)SYNCTEX_END) {
+                                       node = *end_ref;
+                                       while ((node = SYNCTEX_PARENT(node))) {
+                                               if (SYNCTEX_PARENT(*start_ref) == node) {
+                                                       goto next_end;
+                                               }
+                                       }
+                                       start_ref += 1;
+                                       *start_ref = *end_ref;
+                                       goto next_end;
+                               }
+                               start_ref += 1;
+                SYNCTEX_END = (char *)start_ref;
+                SYNCTEX_CUR = NULL;// added on behalf of Jose Alliste
+                               return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t);// added on behalf Jan Sundermeyer
+            }
+                       SYNCTEX_CUR = NULL;
+                       // return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t); removed on behalf Jan Sundermeyer
+               }
+#       if defined(__SYNCTEX_STRONG_DISPLAY_QUERY__)
+               break;
+#       else
+               ++line;
+#       endif
+       }
+       return 0;
+}
+
+synctex_node_t synctex_next_result(synctex_scanner_t scanner) {
+       if (NULL == SYNCTEX_CUR) {
+               SYNCTEX_CUR = SYNCTEX_START;
+       } else {
+               SYNCTEX_CUR+=sizeof(synctex_node_t);
+       }
+       if (SYNCTEX_CUR<SYNCTEX_END) {
+               return *(synctex_node_t*)SYNCTEX_CUR;
+       } else {
+               return NULL;
+       }
+}
+
+/*  This struct records a point in TeX coordinates.*/
+typedef struct {
+       int h;
+       int v;
+} synctex_point_t;
+
+/*  This struct records distances, the left one is positive or 0 and the right one is negative or 0.
+ *  When comparing the locations of 2 different graphical objects on the page, we will have to also record the
+ *  horizontal distance as signed to keep track of the typesetting order.*/
+typedef struct {
+       int left;
+       int right;
+} synctex_distances_t;
+
+typedef struct {
+       synctex_point_t left;
+       synctex_point_t right;
+} synctex_offsets_t;
+
+
+typedef struct {
+       synctex_node_t left;
+       synctex_node_t right;
+} synctex_node_set_t;
+
+/*  The smallest container between two has the smallest width or height.
+ *  This comparison is used when there are 2 overlapping boxes that contain the hit point.
+ *  For ConTeXt, the problem appears at each page.
+ *  The chosen box is the one with the smallest height, then the smallest width. */
+SYNCTEX_INLINE static synctex_node_t _synctex_smallest_container(synctex_node_t node, synctex_node_t other_node);
+
+/*  Returns the distance between the hit point hitPoint=(H,V) and the given node. */
+synctex_bool_t _synctex_point_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible);
+int _synctex_node_distance_to_point(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible);
+
+/*  The best container is the deeper box that contains the hit point (H,V).
+ *  _synctex_eq_deepest_container starts with node whereas
+ *  _synctex_box_child_deepest starts with node's children, if any
+ *  if node is not a box, or a void box, NULL is returned.
+ *  We traverse the node tree in a deep first manner and stop as soon as a result is found. */
+static synctex_node_t _synctex_eq_deepest_container(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible);
+
+/*  Once a best container is found, the closest children are the closest nodes to the left or right of the hit point.
+ *  Only horizontal and vertical offsets are used to compare the positions of the nodes. */
+SYNCTEX_INLINE static int _synctex_eq_get_closest_children_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef, synctex_bool_t visible);
+
+/*  The closest container is the box that is the one closest to the given point.
+ *  The "visible" version takes into account the visible dimensions instead of the real ones given by TeX. */
+SYNCTEX_INLINE static synctex_node_t _synctex_eq_closest_child(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible);
+
+#define SYNCTEX_MASK_LEFT 1
+#define SYNCTEX_MASK_RIGHT 2
+
+synctex_status_t synctex_edit_query(synctex_scanner_t scanner,int page,float h,float v) {
+       synctex_node_t sheet = NULL;
+       synctex_node_t node = NULL; /*  placeholder */
+       synctex_node_t other_node = NULL; /*  placeholder */
+       synctex_point_t hitPoint = {0,0}; /*  placeholder */
+       synctex_node_set_t bestNodes = {NULL,NULL}; /*  holds the best node */
+       synctex_distances_t bestDistances = {INT_MAX,INT_MAX}; /*  holds the best distances for the best node */
+       synctex_node_t bestContainer = NULL; /*  placeholder */
+       if (NULL == (scanner = synctex_scanner_parse(scanner)) || 0 >= scanner->unit) {/*  scanner->unit must be >0 */
+               return 0;
+       }
+       /*  Convert the given point to scanner integer coordinates */
+       hitPoint.h = (h-scanner->x_offset)/scanner->unit;
+       hitPoint.v = (v-scanner->y_offset)/scanner->unit;
+       /*  We will store in the scanner's buffer the result of the query. */
+       free(SYNCTEX_START);
+       SYNCTEX_START = SYNCTEX_END = SYNCTEX_CUR = NULL;
+       /*  Find the proper sheet */
+       sheet = scanner->sheet;
+       while((sheet) && SYNCTEX_PAGE(sheet) != page) {
+               sheet = SYNCTEX_SIBLING(sheet);
+       }
+       if (NULL == sheet) {
+               return -1;
+       }
+       /*  Now sheet points to the sheet node with proper page number */
+       /*  Here is how we work:
+        *  At first we do not consider the visible box dimensions. This will cover the most frequent cases.
+        *  Then we try with the visible box dimensions.
+        *  We try to find a non void box containing the hit point.
+        *  We browse all the horizontal boxes until we find one containing the hit point. */
+       if ((node = SYNCTEX_NEXT_hbox(sheet))) {
+               do {
+                       if (_synctex_point_in_box(hitPoint,node,synctex_YES)) {
+                               /*  Maybe the hitPoint belongs to a contained vertical box. */
+end:
+                               /*  This trick is for catching overlapping boxes */
+                               if ((other_node = SYNCTEX_NEXT_hbox(node))) {
+                                       do {
+                                               if (_synctex_point_in_box(hitPoint,other_node,synctex_YES)) {
+                                                       node = _synctex_smallest_container(other_node,node); 
+                                               }
+                                       } while((other_node = SYNCTEX_NEXT_hbox(other_node)));
+                               }
+                /*  node is the smallest horizontal box that contains hitPoint. */
+                               if ((bestContainer = _synctex_eq_deepest_container(hitPoint,node,synctex_YES))) {
+                                       node = bestContainer;
+                               }
+                               _synctex_eq_get_closest_children_in_box(hitPoint,node,&bestNodes,&bestDistances,synctex_YES);
+                               if (bestNodes.right && bestNodes.left) {
+                                       if ((SYNCTEX_TAG(bestNodes.right)!=SYNCTEX_TAG(bestNodes.left))
+                                                       || (SYNCTEX_LINE(bestNodes.right)!=SYNCTEX_LINE(bestNodes.left))
+                                                               || (SYNCTEX_COLUMN(bestNodes.right)!=SYNCTEX_COLUMN(bestNodes.left))) {
+                                               if ((SYNCTEX_START = malloc(2*sizeof(synctex_node_t)))) {
+                                                       if (bestDistances.left>bestDistances.right) {
+                                                               ((synctex_node_t *)SYNCTEX_START)[0] = bestNodes.right;
+                                                               ((synctex_node_t *)SYNCTEX_START)[1] = bestNodes.left;
+                                                       } else {
+                                                               ((synctex_node_t *)SYNCTEX_START)[0] = bestNodes.left;
+                                                               ((synctex_node_t *)SYNCTEX_START)[1] = bestNodes.right;
+                                                       }
+                                                       SYNCTEX_END = SYNCTEX_START + 2*sizeof(synctex_node_t);
+                                                       SYNCTEX_CUR = NULL;
+                                                       return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t);
+                                               }
+                                               return SYNCTEX_STATUS_ERROR;
+                                       }
+                                       /*  both nodes have the same input coordinates
+                                        *  We choose the one closest to the hit point  */
+                                       if (bestDistances.left>bestDistances.right) {
+                                               bestNodes.left = bestNodes.right;
+                                       }
+                                       bestNodes.right = NULL;
+                               } else if (bestNodes.right) {
+                                       bestNodes.left = bestNodes.right;
+                               } else if (!bestNodes.left){
+                                       bestNodes.left = node;
+                               }
+                               if ((SYNCTEX_START = malloc(sizeof(synctex_node_t)))) {
+                                       * (synctex_node_t *)SYNCTEX_START = bestNodes.left;
+                                       SYNCTEX_END = SYNCTEX_START + sizeof(synctex_node_t);
+                                       SYNCTEX_CUR = NULL;
+                                       return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t);
+                               }
+                               return SYNCTEX_STATUS_ERROR;
+                       }
+               } while ((node = SYNCTEX_NEXT_hbox(node)));
+               /*  All the horizontal boxes have been tested,
+                *  None of them contains the hit point.
+                */
+       }
+       /*  We are not lucky */
+       if ((node = SYNCTEX_CHILD(sheet))) {
+               goto end;
+       }
+       return 0;
+}
+
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Utilities
+#   endif
+
+/*  Rougly speaking, this is:
+ *  node's h coordinate - hitPoint's h coordinate.
+ *  If node is to the right of the hit point, then this distance is positive,
+ *  if node is to the left of the hit point, this distance is negative.*/
+int _synctex_point_h_distance(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible);
+int _synctex_point_h_distance(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) {
+       if (node) {
+               int min,med,max;
+               switch(node->class->type) {
+                       /*  The distance between a point and a box is special.
+                        *  It is not the euclidian distance, nor something similar.
+                        *  We have to take into account the particular layout,
+                        *  and the box hierarchy.
+                        *  Given a box, there are 9 regions delimited by the lines of the edges of the box.
+                        *  The origin being at the top left corner of the page,
+                        *  we also give names to the vertices of the box.
+                        *
+                        *   1 | 2 | 3
+                        *  ---A---B--->
+                        *   4 | 5 | 6
+                        *  ---C---D--->
+                        *   7 | 8 | 9
+                        *     v   v
+                        */
+                       case synctex_node_type_hbox:
+                               /*  getting the box bounds, taking into account negative width, height and depth. */
+                               min = visible?SYNCTEX_HORIZ_V(node):SYNCTEX_HORIZ(node);
+                               max = min + (visible?SYNCTEX_ABS_WIDTH_V(node):SYNCTEX_ABS_WIDTH(node));
+                               /*  We allways have min <= max */
+                               if (hitPoint.h<min) {
+                                       return min - hitPoint.h; /*  regions 1+4+7, result is > 0 */
+                               } else if (hitPoint.h>max) {
+                                       return max - hitPoint.h; /*  regions 3+6+9, result is < 0 */
+                               } else {
+                                       return 0; /*  regions 2+5+8, inside the box, except for vertical coordinates */
+                               }
+                               break;
+                       case synctex_node_type_vbox:
+                       case synctex_node_type_void_vbox:
+                       case synctex_node_type_void_hbox:
+                               /*  getting the box bounds, taking into account negative width, height and depth.
+                                *  For these boxes, no visible dimension available */
+                               min = SYNCTEX_HORIZ(node);
+                               max = min + SYNCTEX_ABS_WIDTH(node);
+                               /*  We allways have min <= max */
+                               if (hitPoint.h<min) {
+                                       return min - hitPoint.h; /*  regions 1+4+7, result is > 0 */
+                               } else if (hitPoint.h>max) {
+                                       return max - hitPoint.h; /*  regions 3+6+9, result is < 0 */
+                               } else {
+                                       return 0; /*  regions 2+5+8, inside the box, except for vertical coordinates */
+                               }
+                               break;
+                       case synctex_node_type_kern:
+                               /*  IMPORTANT NOTICE: the location of the kern is recorded AFTER the move.
+                                *  The distance to the kern is very special,
+                                *  in general, there is no text material in the kern,
+                                *  this is why we compute the offset relative to the closest edge of the kern.*/
+                               max = SYNCTEX_WIDTH(node);
+                               if (max<0) {
+                                       min = SYNCTEX_HORIZ(node);
+                                       max = min - max;
+                               } else {
+                                       min = -max;
+                                       max = SYNCTEX_HORIZ(node);
+                                       min += max;
+                               }
+                               med = (min+max)/2;
+                               /*  positive kern: '.' means text, '>' means kern offset
+                                *      .............
+                                *                   min>>>>med>>>>max
+                                *                                    ...............
+                                *  negative kern: '.' means text, '<' means kern offset
+                                *      ............................
+                                *                 min<<<<med<<<<max
+                                *                 .................................
+                                *  Actually, we do not take into account negative widths.
+                                *  There is a problem for such situation when there is efectively overlapping text.
+                                *  But this should be extremely rare. I guess that in that case, many different choices
+                                *  could be made, one being in contradiction of the other.
+                                *  It means that the best choice should be made according to the situation that occurs
+                                *  most frequently.
+                                */
+                               if (hitPoint.h<min) {
+                                       return min - hitPoint.h + 1; /*  penalty to ensure other nodes are chosen first in case of overlapping ones */
+                               } else if (hitPoint.h>max) {
+                                       return max - hitPoint.h - 1; /*  same kind of penalty */
+                               } else if (hitPoint.h>med) {
+                                       /*  do things like if the node had 0 width and was placed at the max edge + 1*/
+                                       return max - hitPoint.h + 1; /*  positive, the kern is to the right of the hitPoint */
+                               } else {
+                                       return min - hitPoint.h - 1; /*  negative, the kern is to the left of the hitPoint */
+                               }
+                       case synctex_node_type_glue:
+                       case synctex_node_type_math:
+                               return SYNCTEX_HORIZ(node) - hitPoint.h;
+               }
+       }
+       return INT_MAX;/*  We always assume that the node is faraway to the right*/
+}
+/*  Rougly speaking, this is:
+ *  node's v coordinate - hitPoint's v coordinate.
+ *  If node is at the top of the hit point, then this distance is positive,
+ *  if node is at the bottom of the hit point, this distance is negative.*/
+int _synctex_point_v_distance(synctex_point_t hitPoint, synctex_node_t node,synctex_bool_t visible);
+int _synctex_point_v_distance(synctex_point_t hitPoint, synctex_node_t node,synctex_bool_t visible) {
+#      ifdef __DARWIN_UNIX03
+#       pragma unused(visible)
+#   endif
+       if (node) {
+               int min,max;
+               switch(node->class->type) {
+                       /*  The distance between a point and a box is special.
+                        *  It is not the euclidian distance, nor something similar.
+                        *  We have to take into account the particular layout,
+                        *  and the box hierarchy.
+                        *  Given a box, there are 9 regions delimited by the lines of the edges of the box.
+                        *  The origin being at the top left corner of the page,
+                        *  we also give names to the vertices of the box.
+                        *
+                        *   1 | 2 | 3
+                        *  ---A---B--->
+                        *   4 | 5 | 6
+                        *  ---C---D--->
+                        *   7 | 8 | 9
+                        *     v   v
+                        */
+                       case synctex_node_type_hbox:
+                               /*  getting the box bounds, taking into account negative width, height and depth. */
+                               min = SYNCTEX_VERT_V(node);
+                               max = min + SYNCTEX_ABS_DEPTH_V(node);
+                               min -= SYNCTEX_ABS_HEIGHT_V(node);
+                               /*  We allways have min <= max */
+                               if (hitPoint.v<min) {
+                                       return min - hitPoint.v; /*  regions 1+2+3, result is > 0 */
+                               } else if (hitPoint.v>max) {
+                                       return max - hitPoint.v; /*  regions 7+8+9, result is < 0 */
+                               } else {
+                                       return 0; /*  regions 4.5.6, inside the box, except for horizontal coordinates */
+                               }
+                               break;
+                       case synctex_node_type_vbox:
+                       case synctex_node_type_void_vbox:
+                       case synctex_node_type_void_hbox:
+                               /*  getting the box bounds, taking into account negative width, height and depth. */
+                               min = SYNCTEX_VERT(node);
+                               max = min + SYNCTEX_ABS_DEPTH(node);
+                               min -= SYNCTEX_ABS_HEIGHT(node);
+                               /*  We allways have min <= max */
+                               if (hitPoint.v<min) {
+                                       return min - hitPoint.v; /*  regions 1+2+3, result is > 0 */
+                               } else if (hitPoint.v>max) {
+                                       return max - hitPoint.v; /*  regions 7+8+9, result is < 0 */
+                               } else {
+                                       return 0; /*  regions 4.5.6, inside the box, except for horizontal coordinates */
+                               }
+                               break;
+                       case synctex_node_type_kern:
+                       case synctex_node_type_glue:
+                       case synctex_node_type_math:
+                               return SYNCTEX_VERT(node) - hitPoint.v;
+               }
+       }
+       return INT_MAX;/*  We always assume that the node is faraway to the top*/
+}
+
+SYNCTEX_INLINE static synctex_node_t _synctex_smallest_container(synctex_node_t node, synctex_node_t other_node) {
+       float height, other_height;
+       if (SYNCTEX_ABS_WIDTH(node)<SYNCTEX_ABS_WIDTH(other_node)) {
+               return node;
+       }
+       if (SYNCTEX_ABS_WIDTH(node)>SYNCTEX_ABS_WIDTH(other_node)) {
+               return other_node;
+       }
+       height = SYNCTEX_ABS_DEPTH(node) + SYNCTEX_ABS_HEIGHT(node);
+       other_height = SYNCTEX_ABS_DEPTH(other_node) + SYNCTEX_ABS_HEIGHT(other_node);
+       if (height<other_height) {
+               return node;
+       }
+       if (height>other_height) {
+               return other_node;
+    }
+    return node;
+}
+
+synctex_bool_t _synctex_point_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) {
+       if (node) {
+               if (0 == _synctex_point_h_distance(hitPoint,node,visible)
+                               && 0 == _synctex_point_v_distance(hitPoint,node,visible)) {
+                       return synctex_YES;
+               }
+       }
+       return synctex_NO;      
+}
+
+int _synctex_node_distance_to_point(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) {
+#      ifdef __DARWIN_UNIX03
+#       pragma unused(visible)
+#   endif
+       int result = INT_MAX; /*  when the distance is meaning less (sheet, input...)  */
+       if (node) {
+               int minH,maxH,minV,maxV;
+               switch(node->class->type) {
+                       /*  The distance between a point and a box is special.
+                        *  It is not the euclidian distance, nor something similar.
+                        *  We have to take into account the particular layout,
+                        *  and the box hierarchy.
+                        *  Given a box, there are 9 regions delimited by the lines of the edges of the box.
+                        *  The origin being at the top left corner of the page,
+                        *  we also give names to the vertices of the box.
+                        *
+                        *   1 | 2 | 3
+                        *  ---A---B--->
+                        *   4 | 5 | 6
+                        *  ---C---D--->
+                        *   7 | 8 | 9
+                        *     v   v
+                        *  In each region, there is a different formula.
+                        *  In the end we have a continuous distance which may not be a mathematical distance but who cares. */
+                       case synctex_node_type_vbox:
+                       case synctex_node_type_void_vbox:
+                       case synctex_node_type_hbox:
+                       case synctex_node_type_void_hbox:
+                               /*  getting the box bounds, taking into account negative widths. */
+                               minH = SYNCTEX_HORIZ(node);
+                               maxH = minH + SYNCTEX_ABS_WIDTH(node);
+                               minV = SYNCTEX_VERT(node);
+                               maxV = minV + SYNCTEX_ABS_DEPTH(node);
+                               minV -= SYNCTEX_ABS_HEIGHT(node);
+                               /*  In what region is the point hitPoint=(H,V) ? */
+                               if (hitPoint.v<minV) {
+                                       if (hitPoint.h<minH) {
+                                               /*  This is region 1. The distance to the box is the L1 distance PA. */
+                                               result = minV - hitPoint.v + minH - hitPoint.h;/*  Integer overflow? probability epsilon */
+                                       } else if (hitPoint.h<=maxH) {
+                                               /*  This is region 2. The distance to the box is the geometrical distance to the top edge.  */
+                                               result = minV - hitPoint.v;
+                                       } else {
+                                               /*  This is region 3. The distance to the box is the L1 distance PB. */
+                                               result = minV - hitPoint.v + hitPoint.h - maxH;
+                                       }
+                               } else if (hitPoint.v<=maxV) {
+                                       if (hitPoint.h<minH) {
+                                               /*  This is region 4. The distance to the box is the geometrical distance to the left edge.  */
+                                               result = minH - hitPoint.h;
+                                       } else if (hitPoint.h<=maxH) {
+                                               /*  This is region 4. We are inside the box.  */
+                                               result = 0;
+                                       } else {
+                                               /*  This is region 6. The distance to the box is the geometrical distance to the right edge.  */
+                                               result = hitPoint.h - maxH;
+                                       }
+                               } else {
+                                       if (hitPoint.h<minH) {
+                                               /*  This is region 7. The distance to the box is the L1 distance PC. */
+                                               result = hitPoint.v - maxV + minH - hitPoint.h;
+                                       } else if (hitPoint.h<=maxH) {
+                                               /*  This is region 8. The distance to the box is the geometrical distance to the top edge.  */
+                                               result = hitPoint.v - maxV;
+                                       } else {
+                                               /*  This is region 9. The distance to the box is the L1 distance PD. */
+                                               result = hitPoint.v - maxV + hitPoint.h - maxH;
+                                       }
+                               }
+                               break;
+                       case synctex_node_type_kern:
+                               maxH = SYNCTEX_WIDTH(node);
+                               if (maxH<0) {
+                                       minH = SYNCTEX_HORIZ(node);
+                                       maxH = minH - maxH;
+                               } else {
+                                       minH = -maxH;
+                                       maxH = SYNCTEX_HORIZ(node);
+                                       minH += maxH;
+                               }
+                               minV = SYNCTEX_VERT(node);
+                               if (hitPoint.h<minH) {
+                                       if (hitPoint.v>minV) {
+                                               result = hitPoint.v - minV + minH - hitPoint.h;
+                                       } else {
+                                               result = minV - hitPoint.v + minH - hitPoint.h;
+                                       }
+                               } else if (hitPoint.h>maxH) {
+                                       if (hitPoint.v>minV) {
+                                               result = hitPoint.v - minV + hitPoint.h - maxH;
+                                       } else {
+                                               result = minV - hitPoint.v + hitPoint.h - maxH;
+                                       }
+                               } else if (hitPoint.v>minV) {
+                                       result = hitPoint.v - minV;
+                               } else {
+                                       result = minV - hitPoint.v;
+                               }
+                               break;
+                       case synctex_node_type_glue:
+                       case synctex_node_type_math:
+                               minH = SYNCTEX_HORIZ(node);
+                               minV = SYNCTEX_VERT(node);
+                               if (hitPoint.h<minH) {
+                                       if (hitPoint.v>minV) {
+                                               result = hitPoint.v - minV + minH - hitPoint.h;
+                                       } else {
+                                               result = minV - hitPoint.v + minH - hitPoint.h;
+                                       }
+                               } else if (hitPoint.v>minV) {
+                                       result = hitPoint.v - minV + hitPoint.h - minH;
+                               } else {
+                                       result = minV - hitPoint.v + hitPoint.h - minH;
+                               }
+                               break;
+               }
+       }
+       return result;
+}
+
+static synctex_node_t _synctex_eq_deepest_container(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible) {
+       if (node) {
+               synctex_node_t result = NULL;
+               synctex_node_t child = NULL;
+               switch(node->class->type) {
+                       case synctex_node_type_vbox:
+                       case synctex_node_type_hbox:
+                               /*  test the deep nodes first */
+                               if ((child = SYNCTEX_CHILD(node))) {
+                                       do {
+                                               if ((result = _synctex_eq_deepest_container(hitPoint,child,visible))) {
+                                                       return result;
+                                               }
+                                       } while((child = SYNCTEX_SIBLING(child)));
+                               }
+                               /*  is the hit point inside the box? */
+                               if (_synctex_point_in_box(hitPoint,node,visible)) {
+                                       /*  for vboxes we try to use some node inside.
+                                        *  Walk through the list of siblings until we find the closest one.
+                                        *  Only consider siblings with children. */
+                                       if ((node->class->type == synctex_node_type_vbox) && (child = SYNCTEX_CHILD(node))) {
+                                               int bestDistance = INT_MAX;
+                                               do {
+                                                       if (SYNCTEX_CHILD(child)) {
+                                                               int distance = _synctex_node_distance_to_point(hitPoint,child,visible);
+                                                               if (distance < bestDistance) {
+                                                                       bestDistance = distance;
+                                                                       node = child;
+                                                               }
+                                                       }
+                                               } while((child = SYNCTEX_SIBLING(child)));
+                                       }
+                                       return node;
+                               }
+               }
+       }
+       return NULL;
+}
+
+/*  Compares the locations of the hitPoint with the locations of the various nodes contained in the box.
+ *  As it is an horizontal box, we only compare horizontal coordinates. */
+SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_hbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef, synctex_bool_t visible);
+SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_hbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef, synctex_bool_t visible) {
+       int result = 0;
+       if ((node = SYNCTEX_CHILD(node))) {
+               do {
+                       int off7 = _synctex_point_h_distance(hitPoint,node,visible);
+                       if (off7 > 0) {
+                               /*  node is to the right of the hit point.
+                                *  We compare node and the previously recorded one, through the recorded distance.
+                                *  If the nodes have the same tag, prefer the one with the smallest line number,
+                                *  if the nodes also have the same line number, prefer the one with the smallest column. */
+                               if (bestDistancesRef->right > off7) {
+                                       bestDistancesRef->right = off7;
+                                       bestNodesRef->right = node;
+                                       result |= SYNCTEX_MASK_RIGHT;
+                               } else if (bestDistancesRef->right == off7 && bestNodesRef->right) {
+                                       if (SYNCTEX_TAG(bestNodesRef->right) == SYNCTEX_TAG(node)
+                                               && (SYNCTEX_LINE(bestNodesRef->right) > SYNCTEX_LINE(node)
+                                                       || (SYNCTEX_LINE(bestNodesRef->right) == SYNCTEX_LINE(node)
+                                                               && SYNCTEX_COLUMN(bestNodesRef->right) > SYNCTEX_COLUMN(node)))) {
+                                               bestNodesRef->right = node;
+                                               result |= SYNCTEX_MASK_RIGHT;
+                                       }
+                               }
+                       } else if (off7 == 0) {
+                               /*  hitPoint is inside node. */ 
+                               bestDistancesRef->left = bestDistancesRef->right = 0;
+                               bestNodesRef->left = node;
+                               bestNodesRef->right = NULL;
+                               result |= SYNCTEX_MASK_LEFT;
+                       } else { /*  here off7 < 0, hitPoint is to the right of node */
+                               off7 = -off7;
+                               if (bestDistancesRef->left > off7) {
+                                       bestDistancesRef->left = off7;
+                                       bestNodesRef->left = node;
+                                       result |= SYNCTEX_MASK_LEFT;
+                               } else if (bestDistancesRef->left == off7 && bestNodesRef->left) {
+                                       if (SYNCTEX_TAG(bestNodesRef->left) == SYNCTEX_TAG(node)
+                                               && (SYNCTEX_LINE(bestNodesRef->left) > SYNCTEX_LINE(node)
+                                                       || (SYNCTEX_LINE(bestNodesRef->left) == SYNCTEX_LINE(node)
+                                                               && SYNCTEX_COLUMN(bestNodesRef->left) > SYNCTEX_COLUMN(node)))) {
+                                               bestNodesRef->left = node;
+                                               result |= SYNCTEX_MASK_LEFT;
+                                       }
+                               }
+                       }
+               } while((node = SYNCTEX_SIBLING(node)));
+               if (result & SYNCTEX_MASK_LEFT) {
+                       /*  the left node is new, try to narrow the result */
+                       if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->left,visible))) {
+                               bestNodesRef->left = node;
+                       } 
+                       if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->left,visible))) {
+                               bestNodesRef->left = node;
+                       } 
+               }
+               if (result & SYNCTEX_MASK_RIGHT) {
+                       /*  the right node is new, try to narrow the result */
+                       if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->right,visible))) {
+                               bestNodesRef->right = node;
+                       } 
+                       if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->right,visible))) {
+                               bestNodesRef->right = node;
+                       } 
+               }
+       }
+       return result;
+}
+SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_vbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef,synctex_bool_t visible);
+SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_vbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef,synctex_bool_t visible) {
+       int result = 0;
+       if ((node = SYNCTEX_CHILD(node))) {
+               do {
+                       int off7 = _synctex_point_v_distance(hitPoint,node,visible);/*  this is what makes the difference with the h version above */
+                       if (off7 > 0) {
+                               /*  node is to the top of the hit point (below because TeX is oriented from top to bottom.
+                                *  We compare node and the previously recorded one, through the recorded distance.
+                                *  If the nodes have the same tag, prefer the one with the smallest line number,
+                                *  if the nodes also have the same line number, prefer the one with the smallest column. */
+                               if (bestDistancesRef->right > off7) {
+                                       bestDistancesRef->right = off7;
+                                       bestNodesRef->right = node;
+                                       result |= SYNCTEX_MASK_RIGHT;
+                               } else if (bestDistancesRef->right == off7 && bestNodesRef->right) {
+                                       if (SYNCTEX_TAG(bestNodesRef->right) == SYNCTEX_TAG(node)
+                                               && (SYNCTEX_LINE(bestNodesRef->right) > SYNCTEX_LINE(node)
+                                                       || (SYNCTEX_LINE(bestNodesRef->right) == SYNCTEX_LINE(node)
+                                                               && SYNCTEX_COLUMN(bestNodesRef->right) > SYNCTEX_COLUMN(node)))) {
+                                               bestNodesRef->right = node;
+                                               result |= SYNCTEX_MASK_RIGHT;
+                                       }
+                               }
+                       } else if (off7 == 0) {
+                               bestDistancesRef->left = bestDistancesRef->right = 0;
+                               bestNodesRef->left = node;
+                               bestNodesRef->right = NULL;
+                               result |= SYNCTEX_MASK_LEFT;
+                       } else { /*  here off7 < 0 */
+                               off7 = -off7;
+                               if (bestDistancesRef->left > off7) {
+                                       bestDistancesRef->left = off7;
+                                       bestNodesRef->left = node;
+                                       result |= SYNCTEX_MASK_LEFT;
+                               } else if (bestDistancesRef->left == off7 && bestNodesRef->left) {
+                                       if (SYNCTEX_TAG(bestNodesRef->left) == SYNCTEX_TAG(node)
+                                               && (SYNCTEX_LINE(bestNodesRef->left) > SYNCTEX_LINE(node)
+                                                       || (SYNCTEX_LINE(bestNodesRef->left) == SYNCTEX_LINE(node)
+                                                               && SYNCTEX_COLUMN(bestNodesRef->left) > SYNCTEX_COLUMN(node)))) {
+                                               bestNodesRef->left = node;
+                                               result |= SYNCTEX_MASK_LEFT;
+                                       }
+                               }
+                       }
+               } while((node = SYNCTEX_SIBLING(node)));
+               if (result & SYNCTEX_MASK_LEFT) {
+                       /*  the left node is new, try to narrow the result */
+                       if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->left,visible))) {
+                               bestNodesRef->left = node;
+                       } 
+                       if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->left,visible))) {
+                               bestNodesRef->left = node;
+                       } 
+               }
+               if (result & SYNCTEX_MASK_RIGHT) {
+                       /*  the right node is new, try to narrow the result */
+                       if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->right,visible))) {
+                               bestNodesRef->right = node;
+                       } 
+                       if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->right,visible))) {
+                               bestNodesRef->right = node;
+                       } 
+               }
+       }
+       return result;
+}
+SYNCTEX_INLINE static int _synctex_eq_get_closest_children_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t*  bestNodesRef,synctex_distances_t*  bestDistancesRef,synctex_bool_t visible) {
+       if (node) {
+               switch(node->class->type) {
+                       case synctex_node_type_hbox:
+                               return __synctex_eq_get_closest_children_in_hbox(hitPoint, node, bestNodesRef, bestDistancesRef,visible);
+                       case synctex_node_type_vbox:
+                               return __synctex_eq_get_closest_children_in_vbox(hitPoint, node, bestNodesRef, bestDistancesRef,visible);
+               }
+       }
+       return 0;
+}
+
+SYNCTEX_INLINE static synctex_node_t __synctex_eq_closest_child(synctex_point_t hitPoint, synctex_node_t node,int*  distanceRef, synctex_bool_t visible);
+SYNCTEX_INLINE static synctex_node_t __synctex_eq_closest_child(synctex_point_t hitPoint, synctex_node_t node,int*  distanceRef, synctex_bool_t visible) {
+       synctex_node_t best_node = NULL;
+       if ((node = SYNCTEX_CHILD(node))) {
+               do {
+                       int distance = _synctex_node_distance_to_point(hitPoint,node,visible);
+                       synctex_node_t candidate = NULL;
+                       if (distance<=*distanceRef) {
+                               *distanceRef = distance;
+                               best_node = node;
+                       }
+                       switch(node->class->type) {
+                               case synctex_node_type_vbox:
+                               case synctex_node_type_hbox:
+                                       if ((candidate = __synctex_eq_closest_child(hitPoint,node,distanceRef,visible))) {
+                                               best_node = candidate;
+                                       }
+                       }
+               } while((node = SYNCTEX_SIBLING(node)));
+       }
+       return best_node;
+}
+SYNCTEX_INLINE static synctex_node_t _synctex_eq_closest_child(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible) {
+       if (node) {
+               switch(node->class->type) {
+                       case synctex_node_type_hbox:
+                       case synctex_node_type_vbox:
+                       {
+                               int best_distance = INT_MAX;
+                               synctex_node_t best_node = __synctex_eq_closest_child(hitPoint,node,&best_distance,visible);
+                               if ((best_node)) {
+                                       synctex_node_t child = NULL;
+                                       switch(best_node->class->type) {
+                                               case synctex_node_type_vbox:
+                                               case synctex_node_type_hbox:
+                                                       if ((child = SYNCTEX_CHILD(best_node))) {
+                                                               best_distance = _synctex_node_distance_to_point(hitPoint,child,visible);
+                                                               while((child = SYNCTEX_SIBLING(child))) {
+                                                                       int distance = _synctex_node_distance_to_point(hitPoint,child,visible);
+                                                                       if (distance<=best_distance) {
+                                                                               best_distance = distance;
+                                                                               best_node = child;
+                                                                       }
+                                                               }
+                                                       }
+                                       }
+                               }
+                               return best_node;
+                       }
+               }
+       }
+       return NULL;
+}
+
+#      ifdef SYNCTEX_NOTHING
+#       pragma mark -
+#       pragma mark Updater
+#   endif
+
+typedef int (*synctex_fprintf_t)(void *, const char * , ...); /*  print formatted to either FILE *  or gzFile */
+
+#   define SYNCTEX_BITS_PER_BYTE 8
+
+struct __synctex_updater_t {
+    gzFile file;                /*  the foo.synctex or foo.synctex.gz I/O identifier  */
+    synctex_fprintf_t fprintf;  /*  either fprintf or gzprintf */
+    int length;                 /*  the number of chars appended */
+    struct _flags {
+        unsigned int no_gz:1;   /*  Whether zlib is used or not */
+        unsigned int reserved:SYNCTEX_BITS_PER_BYTE*sizeof(int)-1; /*  Align */
+       } flags;
+};
+#   define SYNCTEX_FILE updater->file
+#   define SYNCTEX_NO_GZ ((updater->flags).no_gz)
+#   define SYNCTEX_fprintf (*(updater->fprintf))
+
+synctex_updater_t synctex_updater_new_with_output_file(const char * output, const char * build_directory) {
+       synctex_updater_t updater = NULL;
+       char * synctex = NULL;
+       synctex_io_mode_t io_mode = 0;
+       const char * mode = NULL;
+       /*  prepare the updater, the memory is the only one dynamically allocated */
+       updater = (synctex_updater_t)_synctex_malloc(sizeof(synctex_updater_t));
+       if (NULL == updater) {
+               _synctex_error("!  synctex_updater_new_with_file: malloc problem");
+               return NULL;
+       }
+       if (_synctex_open(output,build_directory,&synctex,&SYNCTEX_FILE,synctex_ADD_QUOTES,&io_mode)
+               && _synctex_open(output,build_directory,&synctex,&SYNCTEX_FILE,synctex_DONT_ADD_QUOTES,&io_mode)) {
+return_on_error:
+               free(updater);
+        updater = NULL;
+               return NULL;
+       }
+       /*  OK, the file exists, we close it and reopen it with the correct mode.
+     *  The receiver is now the owner of the "synctex" variable. */
+       gzclose(SYNCTEX_FILE);
+       SYNCTEX_FILE = NULL;
+       SYNCTEX_NO_GZ = (io_mode&synctex_io_gz_mask)?synctex_NO:synctex_YES;
+    mode = _synctex_get_io_mode_name(io_mode|synctex_io_append_mask);/* either "a" or "ab", depending on the file extension */
+       if (SYNCTEX_NO_GZ) {
+               if (NULL == (SYNCTEX_FILE = (void *)fopen(synctex,mode))) {
+no_write_error:
+                       _synctex_error("!  synctex_updater_new_with_file: Can't append to %s",synctex);
+                       free(synctex);
+                       goto return_on_error;
+               }
+               updater->fprintf = (synctex_fprintf_t)(&fprintf);
+       } else {
+               if (NULL == (SYNCTEX_FILE = (void *)gzopen(synctex,mode))) {
+                       goto no_write_error;
+               }
+               updater->fprintf = (synctex_fprintf_t)(&gzprintf);
+       }
+       printf("SyncTeX: updating %s...",synctex);
+       free(synctex);
+       return updater;
+}
+
+
+void synctex_updater_append_magnification(synctex_updater_t updater, char * magnification){
+       if (NULL==updater) {
+               return;
+       }
+       if (magnification && strlen(magnification)) {
+               updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"Magnification:%s\n",magnification);
+       }
+}
+
+void synctex_updater_append_x_offset(synctex_updater_t updater, char * x_offset){
+       if (NULL==updater) {
+               return;
+       }
+       if (x_offset && strlen(x_offset)) {
+               updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"X Offset:%s\n",x_offset);
+       }
+}
+
+void synctex_updater_append_y_offset(synctex_updater_t updater, char * y_offset){
+       if (NULL==updater) {
+               return;
+       }
+       if (y_offset && strlen(y_offset)) {
+               updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"Y Offset:%s\n",y_offset);
+       }
+}
+
+void synctex_updater_free(synctex_updater_t updater){
+       if (NULL==updater) {
+               return;
+       }
+       if (updater->length>0) {
+               SYNCTEX_fprintf(SYNCTEX_FILE,"!%i\n",updater->length);
+       }
+       if (SYNCTEX_NO_GZ) {
+               fclose((FILE *)SYNCTEX_FILE);
+       } else {
+               gzclose((gzFile)SYNCTEX_FILE);
+       }
+       free(updater);
+       printf("... done.\n");
+       return;
+}
diff --git a/elpa/pdf-tools-20191031.642/build/server/synctex_parser.h b/elpa/pdf-tools-20191031.642/build/server/synctex_parser.h
new file mode 100644 (file)
index 0000000..2c1911e
--- /dev/null
@@ -0,0 +1,363 @@
+/* 
+Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Tue Jun 14 08:23:30 UTC 2011
+
+Version: 1.18
+
+See synctex_parser_readme.txt for more details
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+Acknowledgments:
+----------------
+The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh,
+and significant help from XeTeX developer Jonathan Kew
+
+Nota Bene:
+----------
+If you include or use a significant part of the synctex package into a software,
+I would appreciate to be listed as contributor and see "SyncTeX" highlighted.
+
+Version 1
+Thu Jun 19 09:39:21 UTC 2008
+
+*/
+
+#ifndef __SYNCTEX_PARSER__
+#   define __SYNCTEX_PARSER__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#   define SYNCTEX_VERSION_STRING "1.18"
+
+/*  synctex_node_t is the type for all synctex nodes.
+ *  The synctex file is parsed into a tree of nodes, either sheet, boxes, math nodes... */
+typedef struct _synctex_node *  synctex_node_t;
+
+/*  The main synctex object is a scanner
+ *  Its implementation is considered private.
+ *  The basic workflow is
+ * - create a "synctex scanner" with the contents of a file
+ * - perform actions on that scanner like display or edit queries
+ * - free the scanner when the work is done
+ */
+typedef struct __synctex_scanner_t _synctex_scanner_t;
+typedef _synctex_scanner_t *  synctex_scanner_t;
+
+/*  This is the designated method to create a new synctex scanner object.
+ *  output is the pdf/dvi/xdv file associated to the synctex file.
+ *  If necessary, it can be the tex file that originated the synctex file
+ *  but this might cause problems if the \jobname has a custom value.
+ *  Despite this method can accept a relative path in practice,
+ *  you should only pass a full path name.
+ *  The path should be encoded by the underlying file system,
+ *  assuming that it is based on 8 bits characters, including UTF8,
+ *  not 16 bits nor 32 bits.
+ *  The last file extension is removed and replaced by the proper extension.
+ *  Then the private method _synctex_scanner_new_with_contents_of_file is called.
+ *  NULL is returned in case of an error or non existent file.
+ *  Once you have a scanner, use the synctex_display_query and synctex_edit_query below.
+ *     The new "build_directory" argument is available since version 1.5.
+ *     It is the directory where all the auxiliary stuff is created.
+ *     Sometimes, the synctex output file and the pdf, dvi or xdv files are not created in the same directory.
+ *     This is the case in MikTeX (I will include this into TeX Live).
+ *     This directory path can be nil, it will be ignored then.
+ *     It can be either absolute or relative to the directory of the output pdf (dvi or xdv) file.
+ *     If no synctex file is found in the same directory as the output file, then we try to find one in the build directory.
+ *  Please note that this new "build_directory" is provided as a convenient argument but should not be used.
+ *  In fact, this is implempented as a work around of a bug in MikTeX where the synctex file does not follow the pdf file.
+ *  The new "parse" argument is available since version 1.5. In general, use 1.
+ *  Use 0 only if you do not want to parse the content but just check the existence.
+ */
+synctex_scanner_t synctex_scanner_new_with_output_file(const char * output, const char * build_directory, int parse);
+
+/*  This is the designated method to delete a synctex scanner object.
+ *  Frees all the memory, you must call it when you are finished with the scanner.
+ */
+void synctex_scanner_free(synctex_scanner_t scanner);
+
+/*  Send this message to force the scanner to parse the contents of the synctex output file.
+ *  Nothing is performed if the file was already parsed.
+ *  In each query below, this message is sent, but if you need to access information more directly,
+ *  you must be sure that the parsing did occur.
+ *  Usage:
+ *             if((my_scanner = synctex_scanner_parse(my_scanner))) {
+ *                     continue with my_scanner...
+ *             } else {
+ *                     there was a problem
+ *             }
+ */
+synctex_scanner_t synctex_scanner_parse(synctex_scanner_t scanner);
+
+/*  The main entry points.
+ *  Given the file name, a line and a column number, synctex_display_query returns the number of nodes
+ *  satisfying the contrain. Use code like
+ *
+ *     if(synctex_display_query(scanner,name,line,column)>0) {
+ *         synctex_node_t node;
+ *         while((node = synctex_next_result(scanner))) {
+ *             // do something with node
+ *             ...
+ *         }
+ *     }
+ *
+ *  For example, one can
+ * - highlight each resulting node in the output, using synctex_node_h and synctex_node_v
+ * - highlight all the rectangles enclosing those nodes, using synctex_box_... functions
+ * - highlight just the character using that information
+ *
+ *  Given the page and the position in the page, synctex_edit_query returns the number of nodes
+ *  satisfying the contrain. Use code like
+ *
+ *     if(synctex_edit_query(scanner,page,h,v)>0) {
+ *         synctex_node_t node;
+ *         while(node = synctex_next_result(scanner)) {
+ *             // do something with node
+ *             ...
+ *         }
+ *     }
+ *
+ *  For example, one can
+ * - highlight each resulting line in the input,
+ * - highlight just the character using that information
+ *
+ *  page is 1 based
+ *  h and v are coordinates in 72 dpi unit, relative to the top left corner of the page.
+ *  If you make a new query, the result of the previous one is discarded.
+ *  If one of this function returns a non positive integer,
+ *  it means that an error occurred.
+ *
+ *  Both methods are conservative, in the sense that matching is weak.
+ *  If the exact column number is not found, there will be an answer with the whole line.
+ *
+ *  Sumatra-PDF, Skim, iTeXMac2 and Texworks are examples of open source software that use this library.
+ *  You can browse their code for a concrete implementation.
+ */
+typedef long synctex_status_t;
+synctex_status_t synctex_display_query(synctex_scanner_t scanner,const char *  name,int line,int column);
+synctex_status_t synctex_edit_query(synctex_scanner_t scanner,int page,float h,float v);
+synctex_node_t synctex_next_result(synctex_scanner_t scanner);
+
+/*  Display all the information contained in the scanner object.
+ *  If the records are too numerous, only the first ones are displayed.
+ *  This is mainly for informatinal purpose to help developers.
+ */
+void synctex_scanner_display(synctex_scanner_t scanner);
+
+/*  The x and y offset of the origin in TeX coordinates. The magnification
+   These are used by pdf viewers that want to display the real box size.
+   For example, getting the horizontal coordinates of a node would require
+   synctex_node_box_h(node)*synctex_scanner_magnification(scanner)+synctex_scanner_x_offset(scanner)
+   Getting its TeX width would simply require
+   synctex_node_box_width(node)*synctex_scanner_magnification(scanner)
+   but direct methods are available for that below.
+ */
+int synctex_scanner_x_offset(synctex_scanner_t scanner);
+int synctex_scanner_y_offset(synctex_scanner_t scanner);
+float synctex_scanner_magnification(synctex_scanner_t scanner);
+
+/*  Managing the input file names.
+ *  Given a tag, synctex_scanner_get_name will return the corresponding file name.
+ *  Conversely, given a file name, synctex_scanner_get_tag will retur, the corresponding tag.
+ *  The file name must be the very same as understood by TeX.
+ *  For example, if you \input myDir/foo.tex, the file name is myDir/foo.tex.
+ *  No automatic path expansion is performed.
+ *  Finally, synctex_scanner_input is the first input node of the scanner.
+ *  To browse all the input node, use a loop like
+ *
+ *     if((input_node = synctex_scanner_input(scanner))){
+ *         do {
+ *             blah
+ *         } while((input_node=synctex_node_sibling(input_node)));
+ *     }
+ *
+ *  The output is the name that was used to create the scanner.
+ *  The synctex is the real name of the synctex file,
+ *  it was obtained from output by setting the proper file extension.
+ */
+const char * synctex_scanner_get_name(synctex_scanner_t scanner,int tag);
+int synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name);
+synctex_node_t synctex_scanner_input(synctex_scanner_t scanner);
+const char * synctex_scanner_get_output(synctex_scanner_t scanner);
+const char * synctex_scanner_get_synctex(synctex_scanner_t scanner);
+
+/*  Browsing the nodes
+ *  parent, child and sibling are standard names for tree nodes.
+ *  The parent is one level higher, the child is one level deeper,
+ *  and the sibling is at the same level.
+ *  The sheet of a node is the first ancestor, it is of type sheet.
+ *  A node and its sibling have the same parent.
+ *  A node is the parent of its child.
+ *  A node is either the child of its parent,
+ *  or belongs to the sibling chain of its parent's child.
+ *  The next node is either the child, the sibling or the parent's sibling,
+ *  unless the parent is a sheet.
+ *  This allows to navigate through all the nodes of a given sheet node:
+ *
+ *     synctex_node_t node = sheet;
+ *     while((node = synctex_node_next(node))) {
+ *         // do something with node
+ *     }
+ *
+ *  With synctex_sheet_content, you can retrieve the sheet node given the page.
+ *  The page is 1 based, according to TeX standards.
+ *  Conversely synctex_node_sheet allows to retrieve the sheet containing a given node.
+ */
+synctex_node_t synctex_node_parent(synctex_node_t node);
+synctex_node_t synctex_node_sheet(synctex_node_t node);
+synctex_node_t synctex_node_child(synctex_node_t node);
+synctex_node_t synctex_node_sibling(synctex_node_t node);
+synctex_node_t synctex_node_next(synctex_node_t node);
+synctex_node_t synctex_sheet(synctex_scanner_t scanner,int page);
+synctex_node_t synctex_sheet_content(synctex_scanner_t scanner,int page);
+
+/*  These are the types of the synctex nodes */
+typedef enum {
+       synctex_node_type_error = 0,
+       synctex_node_type_input,
+       synctex_node_type_sheet,
+       synctex_node_type_vbox,
+       synctex_node_type_void_vbox,
+       synctex_node_type_hbox,
+       synctex_node_type_void_hbox,
+       synctex_node_type_kern,
+       synctex_node_type_glue,
+       synctex_node_type_math,
+       synctex_node_type_boundary,
+       synctex_node_number_of_types
+} synctex_node_type_t;
+
+/*  synctex_node_type gives the type of a given node,
+ *  synctex_node_isa gives the same information as a human readable text. */
+synctex_node_type_t synctex_node_type(synctex_node_t node);
+const char * synctex_node_isa(synctex_node_t node);
+
+/*  This is primarily used for debugging purpose.
+ *  The second one logs information for the node and recursively displays information for its next node */
+void synctex_node_log(synctex_node_t node);
+void synctex_node_display(synctex_node_t node);
+
+/*  Given a node, access to the location in the synctex file where it is defined.
+ */
+typedef unsigned int synctex_charindex_t;
+synctex_charindex_t synctex_node_charindex(synctex_node_t node);
+
+/*  Given a node, access to its tag, line and column.
+ *  The line and column numbers are 1 based.
+ *  The latter is not yet fully supported in TeX, the default implementation returns 0 which means the whole line.
+ *  When the tag is known, the scanner of the node will give the corresponding file name.
+ *  When the tag is known, the scanner of the node will give the name.
+ */
+int synctex_node_tag(synctex_node_t node);
+int synctex_node_line(synctex_node_t node);
+int synctex_node_column(synctex_node_t node);
+
+/*  In order to enhance forward synchronization,
+ *  non void horizontal boxes have supplemental cached information.
+ *  The mean line is the average of the line numbers of the included nodes.
+ *  The child count is the number of chidren.
+ */
+int synctex_node_mean_line(synctex_node_t node);
+int synctex_node_child_count(synctex_node_t node);
+
+/*  This is the page where the node appears.
+ *  This is a 1 based index as given by TeX.
+ */
+int synctex_node_page(synctex_node_t node);
+
+/*  For quite all nodes, horizontal, vertical coordinates, and width.
+ *  These are expressed in TeX small points coordinates, with origin at the top left corner.
+ */
+int synctex_node_h(synctex_node_t node);
+int synctex_node_v(synctex_node_t node);
+int synctex_node_width(synctex_node_t node);
+
+/*  For all nodes, dimensions of the enclosing box.
+ *  These are expressed in TeX small points coordinates, with origin at the top left corner.
+ *  A box is enclosing itself.
+ */
+int synctex_node_box_h(synctex_node_t node);
+int synctex_node_box_v(synctex_node_t node);
+int synctex_node_box_width(synctex_node_t node);
+int synctex_node_box_height(synctex_node_t node);
+int synctex_node_box_depth(synctex_node_t node);
+
+/*  For quite all nodes, horizontal, vertical coordinates, and width.
+ *  The visible dimensions are bigger than real ones to compensate 0 width boxes
+ *  that do contain nodes.
+ *  These are expressed in page coordinates, with origin at the top left corner.
+ *  A box is enclosing itself.
+ */
+float synctex_node_visible_h(synctex_node_t node);
+float synctex_node_visible_v(synctex_node_t node);
+float synctex_node_visible_width(synctex_node_t node);
+/*  For all nodes, visible dimensions of the enclosing box.
+ *  A box is enclosing itself.
+ *  The visible dimensions are bigger than real ones to compensate 0 width boxes
+ *  that do contain nodes.
+ */
+float synctex_node_box_visible_h(synctex_node_t node);
+float synctex_node_box_visible_v(synctex_node_t node);
+float synctex_node_box_visible_width(synctex_node_t node);
+float synctex_node_box_visible_height(synctex_node_t node);
+float synctex_node_box_visible_depth(synctex_node_t node);
+
+/*  The main synctex updater object.
+ *  This object is used to append information to the synctex file.
+ *  Its implementation is considered private.
+ *  It is used by the synctex command line tool to take into account modifications
+ *  that could occur while postprocessing files by dvipdf like filters.
+ */
+typedef struct __synctex_updater_t _synctex_updater_t;
+typedef _synctex_updater_t * synctex_updater_t;
+
+/*  Designated initializer.
+ *  Once you are done with your whole job,
+ *  free the updater */
+synctex_updater_t synctex_updater_new_with_output_file(const char * output, const char * directory);
+
+/*  Use the next functions to append records to the synctex file,
+ *  no consistency tests made on the arguments */
+void synctex_updater_append_magnification(synctex_updater_t updater, char *  magnification);
+void synctex_updater_append_x_offset(synctex_updater_t updater, char *  x_offset);
+void synctex_updater_append_y_offset(synctex_updater_t updater, char *  y_offset);
+
+/*  You MUST free the updater, once everything is properly appended */
+void synctex_updater_free(synctex_updater_t updater);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/elpa/pdf-tools-20191031.642/build/server/synctex_parser_local.h b/elpa/pdf-tools-20191031.642/build/server/synctex_parser_local.h
new file mode 100644 (file)
index 0000000..89fd136
--- /dev/null
@@ -0,0 +1,45 @@
+/* 
+Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Tue Jun 14 08:23:30 UTC 2011
+
+Version: 1.18
+
+See synctex_parser_readme.txt for more details
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+*/
+
+/* This local header file is for TEXLIVE, use your own header to fit your system */
+#   include <w2c/c-auto.h> /* for inline && HAVE_xxx */
+/*     No inlining for synctex tool in texlive. */
+#      define SYNCTEX_INLINE
diff --git a/elpa/pdf-tools-20191031.642/build/server/synctex_parser_readme.txt b/elpa/pdf-tools-20191031.642/build/server/synctex_parser_readme.txt
new file mode 100644 (file)
index 0000000..0243f53
--- /dev/null
@@ -0,0 +1,182 @@
+This file is part of the SyncTeX package.
+
+The Synchronization TeXnology named SyncTeX is a new feature
+of recent TeX engines designed by Jerome Laurens.
+It allows to synchronize between input and output, which means to
+navigate from the source document to the typeset material and vice versa.
+More informations on http://itexmac2.sourceforge.net/SyncTeX.html
+
+This package is mainly for developers, it mainly contains the following files:
+
+synctex_parser_readme.txt
+synctex_parser_version.txt
+synctex_parser_utils.c
+synctex_parser_utils.h
+synctex_parser_local.h
+synctex_parser.h
+synctex_parser.c
+
+The file you are reading contains more informations about the SyncTeX parser history.
+
+In order to support SyncTeX in a viewer, it is sufficient to include
+in the source the files synctex_parser.h and synctex_parser.c.
+The synctex parser usage is described in synctex_parser.h header file.
+
+The other files are used by tex engines or by the synctex command line utility:
+
+ChangeLog
+README.txt
+am
+man1
+man5
+synctex-common.h
+synctex-convert.sh
+synctex-e-mem.ch0
+synctex-e-mem.ch1
+synctex-e-rec.ch0
+synctex-e-rec.ch1
+synctex-etex.h
+synctex-mem.ch0
+synctex-mem.ch1
+synctex-mem.ch2
+synctex-pdf-rec.ch2
+synctex-pdftex.h
+synctex-rec.ch0
+synctex-rec.ch1
+synctex-rec.ch2
+synctex-tex.h
+synctex-xe-mem.ch2
+synctex-xe-rec.ch2
+synctex-xe-rec.ch3
+synctex-xetex.h
+synctex.c
+synctex.defines
+synctex.h
+synctex_main.c
+tests
+
+
+Version:
+--------
+This is version 1, which refers to the synctex output file format.
+The files are identified by a build number.
+In order to help developers to automatically manage the version and build numbers
+and download the parser only when necessary, the synctex_parser.version
+is an ASCII text file just containing the current version and build numbers.
+
+History:
+--------
+1.1: Thu Jul 17 09:28:13 UTC 2008
+- First official version available in TeXLive 2008 DVD.
+  Unfortunately, the backwards synchronization is not working properly mainly for ConTeXt users, see below.
+1.2: Tue Sep  2 10:28:32 UTC 2008
+- Correction for ConTeXt support in the edit query.
+  The previous method was assuming that TeX boxes do not overlap,
+  which is reasonable for LaTeX but not for ConTeXt.
+  This assumption is no longer considered.
+1.3: Fri Sep  5 09:39:57 UTC 2008
+- Local variable "read" renamed to "already_read" to avoid conflicts.
+- "inline" compiler directive renamed to "SYNCTEX_INLINE" for code support and maintenance
+- _synctex_error cannot be inlined due to variable arguments (thanks Christiaan Hofman)
+- Correction in the display query, extra boundary nodes are used for a more precise forwards synchronization
+1.4: Fri Sep 12 08:12:34 UTC 2008
+- For an unknown reason, the previous version was not the real 1.3 (as used in iTeXMac2 build 747).
+  As a consequence, a crash was observed.
+- Some typos are fixed.
+1.6: Mon Nov  3 20:20:02 UTC 2008
+- The bug that prevented synchronization with compressed files on windows has been fixed.
+- New interface to allow system specific customization.
+- Note that some APIs have changed.
+1.8: Mer  8 jul 2009 11:32:38 UTC
+Note that version 1.7 was delivered privately.
+- bug fix: synctex was causing a memory leak in pdftex and xetex, thus some processing speed degradation
+- bug fix: the synctex command line tool was broken when updating a .synctex file
+- enhancement: better accuracy of the synchronization process
+- enhancement: the pdf output file and the associated .synctex file no longer need to live in the same directory.
+               The new -d option of the synctex command line tool manages this situation.
+               This is handy when using something like tex -output-directory=DIR ...
+1.9: Wed Nov  4 11:52:35 UTC 2009
+- Various typo fixed
+- OutputDebugString replaced by OutputDebugStringA to deliberately disable unicode preprocessing
+- New conditional created because OutputDebugStringA is only available since Windows 2K professional
+1.10: Sun Jan  10 10:12:32 UTC 2010 
+- Bug fix in synctex_parser.c to solve a synchronization problem with amsmath's gather environment.
+  Concerns the synctex tool.
+1.11: Sun Jan  17 09:12:31 UTC 2010
+- Bug fix in synctex_parser.c, function synctex_node_box_visible_v: 'x' replaced by 'y'.
+  Only 3rd party tools are concerned.
+1.12: Mon Jul 19 21:52:10 UTC 2010
+- Bug fix in synctex_parser.c, function __synctex_open: the io_mode was modified even in case of a non zero return,
+causing a void .synctex.gz file to be created even if it was not expected. Reported by Marek Kasik concerning a bug on evince.
+1.13: Fri Mar 11 07:39:12 UTC 2011
+- Bug fix in synctex_parser.c, better synchronization as suggested by Jan Sundermeyer (near line 3388).
+- Stronger code design in synctex_parser_utils.c, function _synctex_get_name (really neutral behavior).
+  Only 3rd party tools are concerned.
+1.14: Fri Apr 15 19:10:57 UTC 2011
+- taking output_directory into account
+- Replaced FOPEN_WBIN_MODE by FOPEN_W_MODE when opening the text version of the .synctex file.
+- Merging with LuaTeX's version of synctex.c
+1.15: Fri Jun 10 14:10:17 UTC 2011
+This concerns the synctex command line tool and 3rd party developers.
+TeX and friends are not concerned by these changes.
+- Bug fixed in _synctex_get_io_mode_name, sometimes the wrong mode was returned
+- Support for LuaTeX convention of './' file prefixing
+1.16: Tue Jun 14 08:23:30 UTC 2011
+This concerns the synctex command line tool and 3rd party developers.
+TeX and friends are not concerned by these changes.
+- Better forward search (thanks Jose Alliste)
+- Support for LuaTeX convention of './' file prefixing now for everyone, not only for Windows
+1.17: Fri Oct 14 08:15:16 UTC 2011
+This concerns the synctex command line tool and 3rd party developers.
+TeX and friends are not concerned by these changes.
+- synctex_parser.c: cosmetic changes to enhance code readability 
+- Better forward synchronization.
+  The problem occurs for example with LaTeX \item command.
+  The fact is that this command creates nodes at parse time but these nodes are used only
+  after the text material of the \item is displayed on the page. The consequence is that sometimes,
+  forward synchronization spots an irrelevant point from the point of view of the editing process.
+  This was due to some very basic filtering policy, where a somehow arbitrary choice was made when
+  many different possibilities where offered for synchronisation.
+  Now, forward synchronization prefers nodes inside an hbox with as many acceptable spots as possible.
+  This is achieved with the notion of mean line and node weight.
+- Adding support for the new file naming convention with './'
+    + function synctex_ignore_leading_dot_slash_in_path replaces synctex_ignore_leading_dot_slash
+    + function _synctex_is_equivalent_file_name is more permissive
+  Previously, the function synctex_scanner_get_tag would give an answer only when
+  the given file name was EXACTLY one of the file names listed in the synctex file.
+  The we added some changes accepting for example 'foo.tex' instead of './foo.tex'.
+  Now we have an even looser policy for dealing with file names.
+  If the given file name does not match exactly one the file names of the synctex file,
+  then we try to match the base names. If there is only one match of the base names,
+  then it is taken as a match for the whole names.
+  The base name is defined as following:
+      ./foo => foo
+      /my///.////foo => foo
+      /foo => /foo
+      /my//.foo => /my//.foo
+1.17: Tue Mar 13 10:10:03 UTC 2012
+- minor changes, no version changes
+- syntax man pages are fixed as suggested by M. Shimata
+       see mail to tex-liveNOSPAMtug.org titled "syntax.5 has many warnings from groff" and "syntax.1 use invalid macro for mdoc"
+1.17: Tue Jan 14 09:55:00 UTC 2014
+- fixed a segfault, from Sebastian Ramacher
+1.17: Mon Aug 04
+- fixed a memory leak
+1.18: Thu Jun 25 11:36:05 UTC 2015
+- nested sheets now fully supported (does it make sense in TeX)
+- cosmetic changes: uniform indentation
+- suppression of warnings, mainly long/int ones. In short, zlib likes ints when size_t likes longs.
+- CLI synctex tool can build out of TeXLive (modulo appropriate options passed to the compiler)
+
+Acknowledgments:
+----------------
+The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh,
+and significant help from XeTeX developer Jonathan Kew
+
+Nota Bene:
+----------
+If you include or use a significant part of the synctex package into a software,
+I would appreciate to be listed as contributor and see "SyncTeX" highlighted.
+
+Copyright (c) 2008-2014 jerome DOT laurens AT u-bourgogne DOT fr
+
diff --git a/elpa/pdf-tools-20191031.642/build/server/synctex_parser_utils.c b/elpa/pdf-tools-20191031.642/build/server/synctex_parser_utils.c
new file mode 100644 (file)
index 0000000..0e9fd0e
--- /dev/null
@@ -0,0 +1,512 @@
+/* 
+Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Tue Jun 14 08:23:30 UTC 2011
+
+Version: 1.18
+
+See synctex_parser_readme.txt for more details
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+*/
+
+/*  In this file, we find all the functions that may depend on the operating system. */
+
+#include <synctex_parser_utils.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <limits.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <sys/stat.h>
+
+#if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
+#define SYNCTEX_WINDOWS 1
+#endif
+
+#if defined(__OS2__)
+#define SYNCTEX_OS2 1
+#endif
+
+#ifdef _WIN32_WINNT_WINXP
+#define SYNCTEX_RECENT_WINDOWS 1
+#endif
+
+#ifdef SYNCTEX_WINDOWS
+#include <windows.h>
+#include <shlwapi.h> /* Use shlwapi.lib */
+#endif
+
+void *_synctex_malloc(size_t size) {
+       void * ptr = malloc(size);
+       if(ptr) {
+/*  There used to be a switch to use bzero because it is more secure. JL */
+               memset(ptr,0, size);
+       }
+       return (void *)ptr;
+}
+
+int _synctex_error(const char * reason,...) {
+       va_list arg;
+       int result;
+       va_start (arg, reason);
+#      ifdef SYNCTEX_RECENT_WINDOWS
+       {/*     This code is contributed by William Blum.
+        As it does not work on some older computers,
+        the _WIN32 conditional here is replaced with a SYNCTEX_RECENT_WINDOWS one.
+        According to http://msdn.microsoft.com/en-us/library/aa363362(VS.85).aspx
+        Minimum supported client       Windows 2000 Professional
+        Minimum supported server       Windows 2000 Server
+        People running Windows 2K standard edition will not have OutputDebugStringA.
+        JL.*/
+               char *buff;
+               size_t len;
+               OutputDebugStringA("SyncTeX ERROR: ");
+               len = _vscprintf(reason, arg) + 1;
+               buff = (char*)malloc( len * sizeof(char) );
+               result = vsprintf(buff, reason, arg) +strlen("SyncTeX ERROR: ");
+               OutputDebugStringA(buff);
+               OutputDebugStringA("\n");
+               free(buff);
+       }
+#   else
+       result = fprintf(stderr,"SyncTeX ERROR: ");
+       result += vfprintf(stderr, reason, arg);
+       result += fprintf(stderr,"\n");
+#   endif
+       va_end (arg);
+       return result;
+}
+
+/*  strip the last extension of the given string, this string is modified! */
+void _synctex_strip_last_path_extension(char * string) {
+       if(NULL != string){
+               char * last_component = NULL;
+               char * last_extension = NULL;
+#       if defined(SYNCTEX_WINDOWS)
+               last_component = PathFindFileName(string);
+               last_extension = PathFindExtension(string);
+               if(last_extension == NULL)return;
+               if(last_component == NULL)last_component = string;
+               if(last_extension>last_component){/* filter out paths like "my/dir/.hidden" */
+                       last_extension[0] = '\0';
+               }
+#       else
+               char * next = NULL;
+               /*  first we find the last path component */
+               if(NULL == (last_component = strstr(string,"/"))){
+                       last_component = string;
+               } else {
+                       ++last_component;
+                       while((next = strstr(last_component,"/"))){
+                               last_component = next+1;
+                       }
+               }
+#               if defined(SYNCTEX_OS2)
+               /*  On OS2, the '\' is also a path separator. */
+               while((next = strstr(last_component,"\\"))){
+                       last_component = next+1;
+               }
+#               endif /* SYNCTEX_OS2 */
+               /*  then we find the last path extension */
+               if((last_extension = strstr(last_component,"."))){
+                       ++last_extension;
+                       while((next = strstr(last_extension,"."))){
+                               last_extension = next+1;
+                       }
+                       --last_extension;/*  back to the "." */
+                       if(last_extension>last_component){/*  filter out paths like ....my/dir/.hidden"*/
+                               last_extension[0] = '\0';
+                       }
+               }
+#       endif /* SYNCTEX_WINDOWS */
+       }
+}
+
+synctex_bool_t synctex_ignore_leading_dot_slash_in_path(const char ** name_ref)
+{
+    if (SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1])) {
+        do {
+            (*name_ref) += 2;
+            while (SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[0])) {
+                ++(*name_ref);
+            }
+        } while(SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1]));
+        return synctex_YES;
+    }
+    return synctex_NO;
+}
+
+/*  The base name is necessary to deal with the 2011 file naming convention...
+ *  path is a '\0' terminated string
+ *  The return value is the trailing part of the argument,
+ *  just following the first occurrence of the regexp pattern "[^|/|\].[\|/]+".*/
+const char * _synctex_base_name(const char *path) {
+    const char * ptr = path;
+    do {
+        if (synctex_ignore_leading_dot_slash_in_path(&ptr)) {
+            return ptr;
+        }
+        do {
+            if (!*(++ptr)) {
+                return path;
+            }
+        } while (!SYNCTEX_IS_PATH_SEPARATOR(*ptr));
+    } while (*(++ptr));
+    return path;
+}
+
+/*  Compare two file names, windows is sometimes case insensitive... */
+synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs) {
+    /*  Remove the leading regex '(\./+)*' in both rhs and lhs */
+    synctex_ignore_leading_dot_slash_in_path(&lhs);
+    synctex_ignore_leading_dot_slash_in_path(&rhs);
+next_character:
+       if (SYNCTEX_IS_PATH_SEPARATOR(*lhs)) {/*  lhs points to a path separator */
+               if (!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/*  but not rhs */
+                       return synctex_NO;
+               }
+        ++lhs;
+        ++rhs;
+        synctex_ignore_leading_dot_slash_in_path(&lhs);
+        synctex_ignore_leading_dot_slash_in_path(&rhs);
+        goto next_character;
+       } else if (SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/*  rhs points to a path separator but not lhs */
+               return synctex_NO;
+       } else if (SYNCTEX_ARE_PATH_CHARACTERS_EQUAL(*lhs,*rhs)){/*  uppercase do not match */
+               return synctex_NO;
+       } else if (!*lhs) {/*  lhs is at the end of the string */
+               return *rhs ? synctex_NO : synctex_YES;
+       } else if(!*rhs) {/*  rhs is at the end of the string but not lhs */
+               return synctex_NO;
+       }
+       ++lhs;
+       ++rhs;
+       goto next_character;
+}
+
+synctex_bool_t _synctex_path_is_absolute(const char * name) {
+       if(!strlen(name)) {
+               return synctex_NO;
+       }
+#      if defined(SYNCTEX_WINDOWS) || defined(SYNCTEX_OS2)
+       if(strlen(name)>2) {
+               return (name[1]==':' && SYNCTEX_IS_PATH_SEPARATOR(name[2]))?synctex_YES:synctex_NO;
+       }
+       return synctex_NO;
+#      else
+    return SYNCTEX_IS_PATH_SEPARATOR(name[0])?synctex_YES:synctex_NO;
+#      endif
+}
+
+/*  We do not take care of UTF-8 */
+const char * _synctex_last_path_component(const char * name) {
+       const char * c = name+strlen(name);
+       if(c>name) {
+               if(!SYNCTEX_IS_PATH_SEPARATOR(*c)) {
+                       do {
+                               --c;
+                               if(SYNCTEX_IS_PATH_SEPARATOR(*c)) {
+                                       return c+1;
+                               }
+                       } while(c>name);
+               }
+               return c;/* the last path component is the void string*/
+       }
+       return c;
+}
+
+int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size) {
+  const char * lpc;
+  if(src && dest_ref) {
+#              define dest (*dest_ref)
+               dest = NULL;    /*      Default behavior: no change and sucess. */
+               lpc = _synctex_last_path_component(src);
+               if(strlen(lpc)) {
+                       if(strchr(lpc,' ') && lpc[0]!='"' && lpc[strlen(lpc)-1]!='"') {
+                               /*      We are in the situation where adding the quotes is allowed.     */
+                               /*      Time to add the quotes. */
+                               /*  Consistency test: we must have dest+size>dest+strlen(dest)+2
+                                *      or equivalently: strlen(dest)+2<size (see below) */
+                               if(strlen(src)<size) {
+                                       if((dest = (char *)malloc(size+2))) {
+                                               char * dpc = dest + (lpc-src);  /*      dpc is the last path component of dest. */
+                                               if(dest != strncpy(dest,src,size)) {
+                                                       _synctex_error("!  _synctex_copy_with_quoting_last_path_component: Copy problem");
+                                                       free(dest);
+                                                       dest = NULL;/*  Don't forget to reinitialize. */
+                                                       return -2;
+                                               }
+                                               memmove(dpc+1,dpc,strlen(dpc)+1);       /*      Also move the null terminating character. */
+                                               dpc[0]='"';
+                                               dpc[strlen(dpc)+1]='\0';/*      Consistency test */
+                                               dpc[strlen(dpc)]='"';
+                                               return 0;       /*      Success. */
+                                       }
+                                       return -1;      /*      Memory allocation error.        */
+                               }
+                               _synctex_error("!  _synctex_copy_with_quoting_last_path_component: Internal inconsistency");
+                               return -3;
+                       }
+                       return 0;       /*      Success. */
+               }
+               return 0;       /*      No last path component. */
+#              undef dest
+       }
+       return 1; /*  Bad parameter, this value is subject to changes. */
+}
+
+/*  The client is responsible of the management of the returned string, if any. */
+char * _synctex_merge_strings(const char * first,...);
+
+char * _synctex_merge_strings(const char * first,...) {
+       va_list arg;
+       size_t size = 0;
+       const char * temp;
+       /*   First retrieve the size necessary to store the merged string */
+       va_start (arg, first);
+       temp = first;
+       do {
+               size_t len = strlen(temp);
+               if(UINT_MAX-len<size) {
+                       _synctex_error("!  _synctex_merge_strings: Capacity exceeded.");
+                       return NULL;
+               }
+               size+=len;
+       } while( (temp = va_arg(arg, const char *)) != NULL);
+       va_end(arg);
+       if(size>0) {
+               char * result = NULL;
+               ++size;
+               /*  Create the memory storage */
+               if(NULL!=(result = (char *)malloc(size))) {
+                       char * dest = result;
+                       va_start (arg, first);
+                       temp = first;
+                       do {
+                               if((size = strlen(temp))>0) {
+                                       /*  There is something to merge */
+                                       if(dest != strncpy(dest,temp,size)) {
+                                               _synctex_error("!  _synctex_merge_strings: Copy problem");
+                                               free(result);
+                                               result = NULL;
+                                               return NULL;
+                                       }
+                                       dest += size;
+                               }
+                       } while( (temp = va_arg(arg, const char *)) != NULL);
+                       va_end(arg);
+                       dest[0]='\0';/*  Terminate the merged string */
+                       return result;
+               }
+               _synctex_error("!  _synctex_merge_strings: Memory problem");
+               return NULL;
+       }
+       return NULL;    
+}
+
+/*  The purpose of _synctex_get_name is to find the name of the synctex file.
+ *  There is a list of possible filenames from which we return the most recent one and try to remove all the others.
+ *  With two runs of pdftex or xetex we are sure the the synctex file is really the most appropriate.
+ */
+int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref)
+{
+       if(output && synctex_name_ref && io_mode_ref) {
+               /*  If output is already absolute, we just have to manage the quotes and the compress mode */
+               size_t size = 0;
+        char * synctex_name = NULL;
+        synctex_io_mode_t io_mode = *io_mode_ref;
+               const char * base_name = _synctex_last_path_component(output); /*  do not free, output is the owner. base name of output*/
+               /*  Do we have a real base name ? */
+               if(strlen(base_name)>0) {
+                       /*  Yes, we do. */
+                       const char * temp = NULL;
+                       char * core_name = NULL; /*  base name of output without path extension. */
+                       char * dir_name = NULL; /*  dir name of output */
+                       char * quoted_core_name = NULL;
+                       char * basic_name = NULL;
+                       char * gz_name = NULL;
+                       char * quoted_name = NULL;
+                       char * quoted_gz_name = NULL;
+                       char * build_name = NULL;
+                       char * build_gz_name = NULL;
+                       char * build_quoted_name = NULL;
+                       char * build_quoted_gz_name = NULL;
+                       struct stat buf;
+                       time_t the_time = 0;
+                       /*  Create core_name: let temp point to the dot before the path extension of base_name;
+                        *  We start form the \0 terminating character and scan the string upward until we find a dot.
+                        *  The leading dot is not accepted. */
+                       if((temp = strrchr(base_name,'.')) && (size = temp - base_name)>0) {
+                               /*  There is a dot and it is not at the leading position    */
+                               if(NULL == (core_name = (char *)malloc(size+1))) {
+                                       _synctex_error("!  _synctex_get_name: Memory problem 1");
+                                       return -1;
+                               }
+                               if(core_name != strncpy(core_name,base_name,size)) {
+                                       _synctex_error("!  _synctex_get_name: Copy problem 1");
+                                       free(core_name);
+                                       dir_name = NULL;
+                                       return -2;
+                               }
+                               core_name[size] = '\0';
+                       } else {
+                               /*  There is no path extension,
+                                *  Just make a copy of base_name */
+                               core_name = _synctex_merge_strings(base_name);
+                       }
+                       /*  core_name is properly set up, owned by "self". */
+                       /*  creating dir_name. */
+                       size = strlen(output)-strlen(base_name);
+                       if(size>0) {
+                               /*  output contains more than one path component */
+                               if(NULL == (dir_name = (char *)malloc(size+1))) {
+                                       _synctex_error("!  _synctex_get_name: Memory problem");
+                                       free(core_name);
+                                       dir_name = NULL;
+                                       return -1;
+                               }
+                               if(dir_name != strncpy(dir_name,output,size)) {
+                                       _synctex_error("!  _synctex_get_name: Copy problem");
+                                       free(dir_name);
+                                       dir_name = NULL;
+                                       free(core_name);
+                                       dir_name = NULL;
+                                       return -2;
+                               }
+                               dir_name[size] = '\0';
+                       }
+                       /*  dir_name is properly set up. It ends with a path separator, if non void. */
+                       /*  creating quoted_core_name. */
+                       if(strchr(core_name,' ')) {
+                               quoted_core_name = _synctex_merge_strings("\"",core_name,"\"");
+                       }
+                       /*  quoted_core_name is properly set up. */
+                       if(dir_name &&strlen(dir_name)>0) {
+                               basic_name = _synctex_merge_strings(dir_name,core_name,synctex_suffix,NULL);
+                               if(quoted_core_name && strlen(quoted_core_name)>0) {
+                                       quoted_name = _synctex_merge_strings(dir_name,quoted_core_name,synctex_suffix,NULL);
+                               }
+                       } else {
+                               basic_name = _synctex_merge_strings(core_name,synctex_suffix,NULL);
+                               if(quoted_core_name && strlen(quoted_core_name)>0) {
+                                       quoted_name = _synctex_merge_strings(quoted_core_name,synctex_suffix,NULL);
+                               }
+                       }
+                       if(!_synctex_path_is_absolute(output) && build_directory && (size = strlen(build_directory))) {
+                               temp = build_directory + size - 1;
+                               if(_synctex_path_is_absolute(temp)) {
+                                       build_name = _synctex_merge_strings(build_directory,basic_name,NULL);
+                                       if(quoted_core_name && strlen(quoted_core_name)>0) {
+                                               build_quoted_name = _synctex_merge_strings(build_directory,quoted_name,NULL);
+                                       }
+                               } else {
+                                       build_name = _synctex_merge_strings(build_directory,"/",basic_name,NULL);
+                                       if(quoted_core_name && strlen(quoted_core_name)>0) {
+                                               build_quoted_name = _synctex_merge_strings(build_directory,"/",quoted_name,NULL);
+                                       }
+                               }
+                       }
+                       if(basic_name) {
+                               gz_name = _synctex_merge_strings(basic_name,synctex_suffix_gz,NULL);
+                       }
+                       if(quoted_name) {
+                               quoted_gz_name = _synctex_merge_strings(quoted_name,synctex_suffix_gz,NULL);
+                       }
+                       if(build_name) {
+                               build_gz_name = _synctex_merge_strings(build_name,synctex_suffix_gz,NULL);
+                       }
+                       if(build_quoted_name) {
+                               build_quoted_gz_name = _synctex_merge_strings(build_quoted_name,synctex_suffix_gz,NULL);
+                       }
+                       /*  All the others names are properly set up... */
+                       /*  retain the most recently modified file */
+#                      define TEST(FILENAME,COMPRESS_MODE) \
+                       if(FILENAME) {\
+                               if (stat(FILENAME, &buf)) { \
+                                       free(FILENAME);\
+                                       FILENAME = NULL;\
+                               } else if (buf.st_mtime>the_time) { \
+                    the_time=buf.st_mtime; \
+                    synctex_name = FILENAME; \
+                    if (COMPRESS_MODE) { \
+                        io_mode |= synctex_io_gz_mask; \
+                    } else { \
+                        io_mode &= ~synctex_io_gz_mask; \
+                    } \
+                               } \
+                       }
+                       TEST(basic_name,synctex_DONT_COMPRESS);
+                       TEST(gz_name,synctex_COMPRESS);
+                       TEST(quoted_name,synctex_DONT_COMPRESS);
+                       TEST(quoted_gz_name,synctex_COMPRESS);
+                       TEST(build_name,synctex_DONT_COMPRESS);
+                       TEST(build_gz_name,synctex_COMPRESS);
+                       TEST(build_quoted_name,synctex_DONT_COMPRESS);
+                       TEST(build_quoted_gz_name,synctex_COMPRESS);
+#                      undef TEST
+                       /*  Free all the intermediate filenames, except the one that will be used as returned value. */
+#                      define CLEAN_AND_REMOVE(FILENAME) \
+                       if(FILENAME && (FILENAME!=synctex_name)) {\
+                               remove(FILENAME);\
+                               printf("synctex tool info: %s removed\n",FILENAME);\
+                               free(FILENAME);\
+                               FILENAME = NULL;\
+                       }
+                       CLEAN_AND_REMOVE(basic_name);
+                       CLEAN_AND_REMOVE(gz_name);
+                       CLEAN_AND_REMOVE(quoted_name);
+                       CLEAN_AND_REMOVE(quoted_gz_name);
+                       CLEAN_AND_REMOVE(build_name);
+                       CLEAN_AND_REMOVE(build_gz_name);
+                       CLEAN_AND_REMOVE(build_quoted_name);
+                       CLEAN_AND_REMOVE(build_quoted_gz_name);
+#                      undef CLEAN_AND_REMOVE
+            /* set up the returned values */
+            * synctex_name_ref = synctex_name;
+            * io_mode_ref = io_mode;
+                       return 0;
+               }
+               return -1;/*  bad argument */
+       }
+       return -2;
+}
+
+const char * _synctex_get_io_mode_name(synctex_io_mode_t io_mode) {
+    static const char * synctex_io_modes[4] = {"r","rb","a","ab"}; 
+    unsigned index = ((io_mode & synctex_io_gz_mask)?1:0) + ((io_mode & synctex_io_append_mask)?2:0);// bug pointed out by Jose Alliste
+    return synctex_io_modes[index];
+}
diff --git a/elpa/pdf-tools-20191031.642/build/server/synctex_parser_utils.h b/elpa/pdf-tools-20191031.642/build/server/synctex_parser_utils.h
new file mode 100644 (file)
index 0000000..9c76ac5
--- /dev/null
@@ -0,0 +1,152 @@
+/* 
+Copyright (c) 2008, 2009, 2010, 2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Tue Jun 14 08:23:30 UTC 2011
+
+Version: 1.18
+
+See synctex_parser_readme.txt for more details
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+*/
+
+/*  The utilities declared here are subject to conditional implementation.
+ *  All the operating system special stuff goes here.
+ *  The problem mainly comes from file name management: path separator, encoding...
+ */
+
+#      define synctex_bool_t int
+#      define synctex_YES -1
+#      define synctex_ADD_QUOTES -1
+#      define synctex_COMPRESS -1
+#      define synctex_NO 0
+#      define synctex_DONT_ADD_QUOTES 0
+#      define synctex_DONT_COMPRESS 0
+
+#ifndef __SYNCTEX_PARSER_UTILS__
+#   define __SYNCTEX_PARSER_UTILS__
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#      if defined(_WIN32) || defined(__OS2__)
+#       define SYNCTEX_CASE_SENSITIVE_PATH 0
+#              define SYNCTEX_IS_PATH_SEPARATOR(c) ('/' == c || '\\' == c)
+#      else
+#       define SYNCTEX_CASE_SENSITIVE_PATH 1
+#              define SYNCTEX_IS_PATH_SEPARATOR(c) ('/' == c)
+#      endif
+    
+#      if defined(_WIN32) || defined(__OS2__)
+#              define SYNCTEX_IS_DOT(c) ('.' == c)
+#      else
+#              define SYNCTEX_IS_DOT(c) ('.' == c)
+#      endif
+    
+#      if SYNCTEX_CASE_SENSITIVE_PATH
+#              define SYNCTEX_ARE_PATH_CHARACTERS_EQUAL(left,right) (left != right)
+#      else
+#              define SYNCTEX_ARE_PATH_CHARACTERS_EQUAL(left,right) (toupper(left) != toupper(right))
+#      endif
+    
+/*  This custom malloc functions initializes to 0 the newly allocated memory.
+ *  There is no bzero function on windows. */
+void *_synctex_malloc(size_t size);
+
+/*  This is used to log some informational message to the standard error stream.
+ *  On Windows, the stderr stream is not exposed and another method is used.
+ *     The return value is the number of characters printed.   */
+int _synctex_error(const char * reason,...);
+
+/*  strip the last extension of the given string, this string is modified!
+ *  This function depends on the OS because the path separator may differ.
+ *  This should be discussed more precisely. */
+void _synctex_strip_last_path_extension(char * string);
+
+/*  Compare two file names, windows is sometimes case insensitive...
+ *  The given strings may differ stricto sensu, but represent the same file name.
+ *  It might not be the real way of doing things.
+ *  The return value is an undefined non 0 value when the two file names are equivalent.
+ *  It is 0 otherwise. */
+synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs);
+
+/*     Description forthcoming.*/
+synctex_bool_t _synctex_path_is_absolute(const char * name);
+
+/*     Description forthcoming...*/
+const char * _synctex_last_path_component(const char * name);
+
+/*     Description forthcoming...*/
+const char * _synctex_base_name(const char *path);
+
+/*     If the core of the last path component of src is not already enclosed with double quotes ('"')
+ *  and contains a space character (' '), then a new buffer is created, the src is copied and quotes are added.
+ *     In all other cases, no destination buffer is created and the src is not copied.
+ *  0 on success, which means no error, something non 0 means error, mainly due to memory allocation failure, or bad parameter.
+ *  This is used to fix a bug in the first version of pdftex with synctex (1.40.9) for which names with spaces
+ *  were not managed in a standard way.
+ *  On success, the caller owns the buffer pointed to by dest_ref (is any) and
+ *  is responsible of freeing the memory when done.
+ *     The size argument is the size of the src buffer. On return the dest_ref points to a buffer sized size+2.*/
+int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size);
+
+/*  These are the possible extensions of the synctex file */
+extern const char * synctex_suffix;
+extern const char * synctex_suffix_gz;
+
+typedef unsigned int synctex_io_mode_t;
+
+typedef enum {
+       synctex_io_append_mask = 1,
+    synctex_io_gz_mask = synctex_io_append_mask<<1
+} synctex_io_mode_masks_t;
+
+typedef enum {
+       synctex_compress_mode_none = 0,
+       synctex_compress_mode_gz = 1
+} synctex_compress_mode_t;
+
+int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref);
+
+/*  returns the correct mode required by fopen and gzopen from the given io_mode */
+const char * _synctex_get_io_mode_name(synctex_io_mode_t io_mode);
+
+synctex_bool_t synctex_ignore_leading_dot_slash_in_path(const char ** name);
+    
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/elpa/pdf-tools-20191031.642/build/server/synctex_parser_version.txt b/elpa/pdf-tools-20191031.642/build/server/synctex_parser_version.txt
new file mode 100644 (file)
index 0000000..d907c15
--- /dev/null
@@ -0,0 +1 @@
+1.18
\ No newline at end of file
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/.gitignore b/elpa/pdf-tools-20191031.642/build/server/test/docker/.gitignore
new file mode 100644 (file)
index 0000000..a6d9461
--- /dev/null
@@ -0,0 +1,2 @@
+*.Dockerfile
+*.build
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/lib/run-tests b/elpa/pdf-tools-20191031.642/build/server/test/docker/lib/run-tests
new file mode 100755 (executable)
index 0000000..43fe5e5
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+PATH="$(dirname "$0")":$PATH
+
+set -e
+
+yes-or-enter | ./autobuild -i /bin
+yes-or-enter | ./autobuild -i /usr/bin | \
+    grep -q "Skipping package installation (already installed)"
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/lib/yes-or-enter b/elpa/pdf-tools-20191031.642/build/server/test/docker/lib/yes-or-enter
new file mode 100755 (executable)
index 0000000..b19cc5a
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# Step over prompts from the package-manager.
+if [ -f /etc/arch-release ]; then
+    yes '' 
+else
+    yes
+fi
+    
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/Dockerfile.in
new file mode 100644 (file)
index 0000000..083b19a
--- /dev/null
@@ -0,0 +1,4 @@
+ADD . /epdfinfo
+WORKDIR /epdfinfo
+RUN make -s distclean || true
+CMD ["sh", "./test/docker/lib/run-tests"]
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/arch.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/arch.Dockerfile.in
new file mode 100644 (file)
index 0000000..50e6658
--- /dev/null
@@ -0,0 +1,6 @@
+# -*- dockerfile -*-
+FROM base/archlinux
+RUN pacman -Syu --noconfirm --noprogressbar && \
+    pacman -S --noconfirm --noprogressbar poppler-glib base-devel
+
+
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/centos-7.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/centos-7.Dockerfile.in
new file mode 100644 (file)
index 0000000..62f2eb4
--- /dev/null
@@ -0,0 +1,3 @@
+# -*- dockerfile -*-
+FROM centos:7
+RUN yum update -y && yum install -y gcc gcc-c++ poppler-glib-devel
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/debian-8.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/debian-8.Dockerfile.in
new file mode 100644 (file)
index 0000000..adcc6d3
--- /dev/null
@@ -0,0 +1,4 @@
+# -*- dockerfile -*-
+FROM debian:8
+RUN apt-get update -y && apt-get install -y gcc g++ libpoppler-glib-dev
+
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/debian-9.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/debian-9.Dockerfile.in
new file mode 100644 (file)
index 0000000..1f4ece7
--- /dev/null
@@ -0,0 +1,4 @@
+# -*- dockerfile -*-
+FROM debian:9
+RUN apt-get update -y && apt-get install -y gcc g++ libpoppler-glib-dev
+
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/fedora-24.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/fedora-24.Dockerfile.in
new file mode 100644 (file)
index 0000000..a2490ad
--- /dev/null
@@ -0,0 +1,5 @@
+# -*- dockerfile -*-
+FROM fedora:24
+RUN dnf update -y && dnf install -y gcc gcc-c++ poppler-glib-devel
+
+
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/fedora-25.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/fedora-25.Dockerfile.in
new file mode 100644 (file)
index 0000000..6f5f8c2
--- /dev/null
@@ -0,0 +1,5 @@
+# -*- dockerfile -*-
+FROM fedora:25
+RUN dnf update -y && dnf install -y gcc gcc-c++ poppler-glib-devel
+
+
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/fedora-26.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/fedora-26.Dockerfile.in
new file mode 100644 (file)
index 0000000..8c43d1d
--- /dev/null
@@ -0,0 +1,5 @@
+# -*- dockerfile -*-
+FROM fedora:26
+RUN dnf update -y && dnf install -y gcc gcc-c++ poppler-glib-devel
+
+
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/gentoo.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/gentoo.Dockerfile.in
new file mode 100644 (file)
index 0000000..00ef03f
--- /dev/null
@@ -0,0 +1,5 @@
+# -*- dockerfile -*-
+FROM gentoo/stage3-amd64
+RUN emerge --sync && emerge sys-devel/gcc app-text/poppler
+
+
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/ubuntu-14.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/ubuntu-14.Dockerfile.in
new file mode 100644 (file)
index 0000000..1c9aaa7
--- /dev/null
@@ -0,0 +1,4 @@
+# -*- dockerfile -*-
+FROM ubuntu:trusty
+RUN apt-get update -y && apt-get install -y gcc g++ libpoppler-glib-dev
+
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/ubuntu-16.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/ubuntu-16.Dockerfile.in
new file mode 100644 (file)
index 0000000..e7d477e
--- /dev/null
@@ -0,0 +1,4 @@
+# -*- dockerfile -*-
+FROM ubuntu:xenial
+RUN apt-get update -y && apt-get install -y gcc g++ libpoppler-glib-dev
+
diff --git a/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/ubuntu-17.Dockerfile.in b/elpa/pdf-tools-20191031.642/build/server/test/docker/templates/ubuntu-17.Dockerfile.in
new file mode 100644 (file)
index 0000000..bf2dc3d
--- /dev/null
@@ -0,0 +1,4 @@
+# -*- dockerfile -*-
+FROM ubuntu:artful
+RUN apt-get update -y && apt-get install -y gcc g++ libpoppler-glib-dev
+
diff --git a/elpa/pdf-tools-20191031.642/pdf-annot.el b/elpa/pdf-tools-20191031.642/pdf-annot.el
new file mode 100644 (file)
index 0000000..efde0ec
--- /dev/null
@@ -0,0 +1,1790 @@
+;;; pdf-annot.el --- Annotation support for PDF files.  -*- lexical-binding: t -*-
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords:
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+
+(require 'pdf-view)
+(require 'pdf-info)
+(require 'pdf-cache)
+(require 'pdf-misc)
+(require 'facemenu) ;; list-colors-duplicates
+(require 'faces) ;; color-values
+(require 'org)   ;; org-create-formula-image
+(require 'tablist)
+(require 'cl-lib)
+
+\f
+;; * ================================================================== *
+;; * Customizations
+;; * ================================================================== *
+
+(defgroup pdf-annot nil
+  "Annotation support for PDF documents."
+  :group 'pdf-tools)
+
+(defcustom pdf-annot-activate-handler-functions nil
+  "A list of functions to activate a annotation.
+
+The functions on this hook will be called when some annotation is
+activated, usually by a mouse-click.  Each one is called with the
+annotation as a single argument and it should return a non-nil
+value if it has `handled' it.  If no such function exists, the
+default handler `pdf-annot-default-handler' will be
+called.
+
+This hook is meant to allow for custom annotations.  FIXME:
+Implement and describe basic org example."
+  :group 'pdf-annot
+  :type 'hook)
+
+(defcustom pdf-annot-default-text-annotation-properties nil
+  "Alist of initial properties for new text annotations."
+  :group 'pdf-annot
+  :type '(alist :key-type symbol :value-type sexp))
+
+(defcustom pdf-annot-default-markup-annotation-properties nil
+  "Alist of initial properties for new markup annotations."
+  :group 'pdf-annot
+  :type '(alist :key-type symbol :value-type sexp))
+
+(make-obsolete-variable 'pdf-annot-default-text-annotation-properties
+                        'pdf-annot-default-annotation-properties
+                        "0.90")
+
+(make-obsolete-variable 'pdf-annot-default-markup-annotation-properties
+                        'pdf-annot-default-annotation-properties
+                        "0.90")
+
+(defcustom pdf-annot-default-annotation-properties
+  `((t (label . ,user-full-name))
+    (text (icon . "Note")
+          (color . "#ff0000"))
+    (highlight (color . "yellow"))
+    (squiggly (color . "orange"))
+    (strike-out(color . "red"))
+    (underline (color . "blue")))
+  "An alist of initial properties for new annotations.
+
+The alist contains a sub-alist for each of the currently available
+annotation types, i.e. text, highlight, squiggly, strike-out and
+underline.  Additionally a sub-alist with a key of t acts as a default
+entry.
+
+Each of these sub-alists contain default property-values of newly
+added annotations of its respective type.
+
+Some of the most important properties and their types are label
+\(a string\), contents \(a string\), color \(a color\) and, for
+text-annotations only, icon \(one of the standard icon-types, see
+`pdf-annot-standard-text-icons'\).
+
+For example a value of
+
+  \(\(t \(color . \"red\"\)
+      \(label . \"Joe\"\)
+   \(highlight \(color . \"green\"\)\)
+
+would use a green color for highlight and a red one for other
+annotations.  Additionally the label for all annotations is set
+to \"Joe\"."
+
+  :group 'pdf-annot
+  :type (let* ((label '(cons :tag "Label" (const label) string))
+               (contents '(cons :tag "Contents" (const contents) string))
+               (color '(cons :tag "Color" (const color) color))
+               (icon `(cons :tag "Icon"
+                            (const icon)
+                            (choice
+                             ,@(mapcar (lambda (icon)
+                                         `(const ,icon))
+                                       '("Note" "Comment" "Key" "Help" "NewParagraph"
+                                         "Paragraph" "Insert" "Cross" "Circle")))))
+               (other '(repeat
+                        :tag "Other properties"
+                        (cons :tag "Property"
+                              (symbol :tag "Key  ")
+                              (sexp :tag "Value"))))
+               (text-properties
+                `(set ,label ,contents ,color ,icon ,other))
+               (markup-properties
+                `(set ,label ,contents ,color))
+               (all-properties
+                `(set ,label ,contents ,color ,icon ,other)))
+          `(set
+            (cons :tag "All Annotations" (const t) ,all-properties)
+            (cons :tag "Text Annotations" (const text) ,text-properties)
+            (cons :tag "Highlight Annotations" (const highlight) ,markup-properties)
+            (cons :tag "Underline Annotations" (const underline) ,markup-properties)
+            (cons :tag "Squiggly Annotations" (const squiggly) ,markup-properties)
+            (cons :tag "Strike-out Annotations" (const strike-out) ,markup-properties))))
+
+(defcustom pdf-annot-print-annotation-functions
+  '(pdf-annot-print-annotation-latex-maybe)
+  "A alist of functions for printing annotations, e.g. for the tooltip.
+
+The functions receive the annotation as single argument and
+should return either a string or nil.  The first string returned
+will be used.
+
+If all of them return nil, the default function
+`pdf-annot-print-annotation-default' is used."
+  :group 'pdf-annot
+  :type 'hook)
+
+(defcustom pdf-annot-latex-string-predicate
+  (lambda (str)
+    (and str (string-match "\\`[[:space:]\n]*[$\\]" str)))
+  "A predicate for recognizing LaTeX fragments.
+
+It receives a string and should return non-nil, if string is a
+LaTeX fragment."
+  :group 'pdf-annot
+  :type 'function)
+
+(defcustom pdf-annot-latex-header
+  (concat org-format-latex-header
+          "\n\\setlength{\\textwidth}{12cm}")
+  "Header used when latex compiling annotations.
+
+The default value is `org-format-latex-header' + \
+\"\\n\\\\setlength{\\\\textwidth}{12cm}\"."
+  :group 'pdf-annot
+  :type 'string)
+
+(defcustom pdf-annot-tweak-tooltips t
+  "Whether this package should tweak some settings regarding tooltips.
+
+If this variable has a non-nil value,
+
+`x-gtk-use-system-tooltips' is set to nil if appropriate, in
+order to display text properties;
+
+`tooltip-hide-delay' is set to infinity, in order to not being
+annoyed while reading the annotations."
+  :group 'pdf-annot
+  :type 'boolean)
+
+(defcustom pdf-annot-activate-created-annotations nil
+  "Whether to activate (i.e. edit) created annotations."
+  :group 'pdf-annot
+  :type 'boolean)
+
+(defcustom pdf-annot-attachment-display-buffer-action nil
+  "The display action used when displaying attachments."
+  :group 'pdf-annot
+  :type display-buffer--action-custom-type)
+
+(defconst pdf-annot-annotation-types
+  '(3d caret circle file
+       free-text highlight ink line link movie poly-line polygon popup
+       printer-mark screen sound square squiggly stamp strike-out text
+       trap-net underline unknown watermark widget)
+  "Complete list of annotation types.")
+
+(defcustom pdf-annot-list-listed-types
+  (if (pdf-info-markup-annotations-p)
+      (list 'text 'file 'squiggly 'highlight 'underline 'strike-out)
+    (list 'text 'file))
+  "A list of annotation types displayed in the list buffer."
+  :group 'pdf-annot
+  :type `(set ,@(mapcar (lambda (type)
+                          (list 'const type))
+                        pdf-annot-annotation-types)))
+
+\f
+;; * ================================================================== *
+;; * Variables and Macros
+;; * ================================================================== *
+
+(defvar pdf-annot-color-history nil
+  "A list of recently used colors for annotations.")
+
+(defvar-local pdf-annot-modified-functions nil
+  "Functions to call, when an annotation was modified.
+
+A function on this hook should accept one argument: A CLOSURE
+containing inserted, changed and deleted annotations.
+
+It may access theses annotations by calling CLOSURE with one of
+these arguments:
+
+`:inserted' The list of recently added annotations.
+
+`:deleted' The list of recently deleted annotations.
+
+`:changed' The list of recently changed annotations.
+
+`t' The union of recently added, deleted or changed annotations.
+
+`nil' Just returns nil.
+
+Any other argument signals an error.")
+
+(defconst pdf-annot-text-annotation-size '(24 . 24)
+  "The Size of text and file annotations in PDF points.
+
+These values are hard-coded in poppler.  And while the size of
+these annotations may be changed, i.e. the edges property, it has
+no effect on the rendering.")
+
+(defconst pdf-annot-markup-annotation-types
+  '(text link free-text line square
+         circle polygon poly-line highlight underline squiggly
+         strike-out stamp caret ink file sound)
+  "List of defined markup annotation types.")
+
+(defconst pdf-annot-standard-text-icons
+  '("Note" "Comment" "Key" "Help" "NewParagraph"
+    "Paragraph" "Insert" "Cross" "Circle")
+  "A list of standard icon properties for text annotations.")
+
+(defvar pdf-annot-inhibit-modification-hooks nil
+  "Non-nil, if running `pdf-annot-modified-functions' should be
+  inhibited after some annotation has changed.")
+
+(defvar-local pdf-annot-delayed-modified-annotations nil
+  "A plist of not yet propagated modifications.
+
+It contains three entries :change, :delete and :insert.  Each one
+having a list of annotations as value.")
+
+(defvar-local pdf-annot--attachment-file-alist nil
+  "Alist mapping attachment ids to unique relative filenames.")
+
+(defmacro pdf-annot-with-atomic-modifications (&rest body)
+  "Execute BODY joining multiple modifications.
+
+The effect is, that `pdf-annot-modified-functions' will be called
+only once at the end of BODY.
+
+BODY should not modify annotations in a different then the
+current buffer, because that won't run the hooks properly."
+  (declare (indent 0) (debug t))
+  `(unwind-protect
+       (save-current-buffer
+         (let ((pdf-annot-inhibit-modification-hooks t))
+           (progn ,@body)))
+     (pdf-annot-run-modified-hooks)))
+
+\f
+;; * ================================================================== *
+;; * Minor mode
+;; * ================================================================== *
+
+(defcustom pdf-annot-minor-mode-map-prefix (kbd "C-c C-a")
+  "The prefix to use for `pdf-annot-minor-mode-map'.
+
+Setting this after the package was loaded has no effect."
+  :group 'pdf-annot
+  :type 'key-sequence)
+
+(defvar pdf-annot-minor-mode-map
+  (let ((kmap (make-sparse-keymap))
+        (smap (make-sparse-keymap)))
+    (define-key kmap pdf-annot-minor-mode-map-prefix smap)
+    (define-key smap "l" 'pdf-annot-list-annotations)
+    ;; (define-key smap "d" 'pdf-annot-toggle-display-annotations)
+    (define-key smap "a" 'pdf-annot-attachment-dired)
+    (when (pdf-info-writable-annotations-p)
+      (define-key smap "D" 'pdf-annot-delete)
+      (define-key smap "t" 'pdf-annot-add-text-annotation)
+      (when (pdf-info-markup-annotations-p)
+        (define-key smap "m" 'pdf-annot-add-markup-annotation)
+        (define-key smap "s" 'pdf-annot-add-squiggly-markup-annotation)
+        (define-key smap "u" 'pdf-annot-add-underline-markup-annotation)
+        (define-key smap "o" 'pdf-annot-add-strikeout-markup-annotation)
+        (define-key smap "h" 'pdf-annot-add-highlight-markup-annotation)))
+    kmap)
+  "Keymap used for `pdf-annot-minor-mode'.")
+
+(defvar savehist-minibuffer-history-variables)
+
+;;;###autoload
+(define-minor-mode pdf-annot-minor-mode
+  "Support for PDF Annotations.
+
+\\{pdf-annot-minor-mode-map}"
+  nil nil nil
+  (cond
+   (pdf-annot-minor-mode
+    (when pdf-annot-tweak-tooltips
+      (when (boundp 'x-gtk-use-system-tooltips)
+        (setq x-gtk-use-system-tooltips nil))
+      (setq tooltip-hide-delay 3600))
+    (pdf-view-add-hotspot-function 'pdf-annot-hotspot-function 9)
+    (add-hook 'pdf-info-close-document-hook
+              'pdf-annot-attachment-delete-base-directory nil t)
+    (when (featurep 'savehist)
+      (add-to-list 'savehist-minibuffer-history-variables
+                   'pdf-annot-color-history)))
+   (t
+    (pdf-view-remove-hotspot-function 'pdf-annot-hotspot-function)
+    (remove-hook 'pdf-info-close-document-hook
+                 'pdf-annot-attachment-delete-base-directory t)))
+  (pdf-view-redisplay t))
+
+(defun pdf-annot-create-context-menu (a)
+  "Create a appropriate context menu for annotation A."
+  (let ((menu (make-sparse-keymap)))
+    ;; (when (and (bound-and-true-p pdf-misc-menu-bar-minor-mode)
+    ;;            (bound-and-true-p pdf-misc-install-popup-menu))
+    ;;   (set-keymap-parent menu
+    ;;                      (lookup-key pdf-misc-menu-bar-minor-mode-map
+    ;;                                  [menu-bar pdf-tools]))
+    ;;   (define-key menu [sep-99] menu-bar-separator))
+    (when (pdf-info-writable-annotations-p)
+      (define-key menu [delete-annotation]
+        `(menu-item "Delete annotation"
+                    ,(lambda ()
+                       (interactive)
+                       (pdf-annot-delete a)
+                       (message "Annotation deleted"))
+                    :help
+                    "Delete this annotation.")))
+    (define-key menu [goto-annotation]
+      `(menu-item "List annotation"
+                  ,(lambda ()
+                     (interactive)
+                     (pdf-annot-show-annotation a t)
+                     (pdf-annot-list-annotations)
+                     (pdf-annot-list-goto-annotation a))
+                  :help "Find this annotation in the list buffer."))
+    (when (pdf-annot-text-annotation-p a)
+      (define-key menu [change-text-icon]
+        `(menu-item "Change icon"
+                    ,(pdf-annot-create-icon-submenu a)
+                    :help "Change the appearance of this annotation.")))
+    (define-key menu [change-color]
+      `(menu-item "Change color"
+                  ,(pdf-annot-create-color-submenu a)
+                  :help "Change the appearance of this annotation."))
+    (define-key menu [activate-annotation]
+      `(menu-item "Activate"
+                  ,(lambda ()
+                     (interactive)
+                     (pdf-annot-activate-annotation a))
+                  :help "Activate this annotation."))
+    menu))
+
+(defun pdf-annot-create-color-submenu (a)
+  (let ((menu (make-sparse-keymap)))
+    (define-key menu [color-chooser]
+      `(menu-item "Choose ..."
+                  ,(lambda ()
+                     (interactive)
+                     (list-colors-display
+                      nil "*Choose annotation color*"
+                      ;; list-colors-print does not like closures.
+                      (let ((callback (make-symbol "xcallback")))
+                        (fset callback
+                              (lambda (color)
+                                (pdf-annot-put a 'color color)
+                                (setq pdf-annot-color-history
+                                      (cons color
+                                            (remove color pdf-annot-color-history)))
+                                (quit-window t)))
+                        (list 'function callback))))))
+    (dolist (color (butlast (reverse pdf-annot-color-history)
+                            (max 0 (- (length pdf-annot-color-history)
+                                      12))))
+      (define-key menu (vector (intern (format "color-%s" color)))
+        `(menu-item ,color
+                    ,(lambda nil
+                       (interactive)
+                       (pdf-annot-put a 'color color)))))
+    menu))
+
+(defun pdf-annot-create-icon-submenu (a)
+  (let ((menu (make-sparse-keymap)))
+    (dolist (icon (reverse pdf-annot-standard-text-icons))
+      (define-key menu (vector (intern (format "icon-%s" icon)))
+        `(menu-item ,icon
+                    ,(lambda nil
+                       (interactive)
+                       (pdf-annot-put a 'icon icon)))))
+    menu))
+\f
+;; * ================================================================== *
+;; * Annotation Basics
+;; * ================================================================== *
+
+(defun pdf-annot-create (alist &optional buffer)
+  "Create a annotation from ALIST in BUFFER.
+
+ALIST should be a property list as returned by
+`pdf-cache-getannots'.  BUFFER should be the buffer of the
+corresponding PDF document. It defaults to the current buffer."
+
+  (cons `(buffer . ,(or buffer (current-buffer)))
+        alist))
+
+(defun pdf-annot-getannots (&optional pages types buffer)
+  "Return a list of annotations on PAGES of TYPES in BUFFER.
+
+See `pdf-info-normalize-pages' for valid values of PAGES.  TYPES
+may be a symbol or list of symbols denoting annotation types.
+
+PAGES defaults to all pages, TYPES to all types and BUFFER to the
+current buffer."
+
+  (pdf-util-assert-pdf-buffer buffer)
+  (unless buffer
+    (setq buffer (current-buffer)))
+  (unless (listp types)
+    (setq types (list types)))
+  (with-current-buffer buffer
+    (let (result)
+      (dolist (a (pdf-info-getannots pages))
+        (when (or (null types)
+                  (memq (pdf-annot-get a 'type) types))
+          (push (pdf-annot-create a) result)))
+      result)))
+
+(defun pdf-annot-getannot (id &optional buffer)
+  (pdf-annot-create
+   (pdf-info-getannot id buffer)
+   buffer))
+
+(defun pdf-annot-get (a property &optional default)
+  "Get annotation A's value of PROPERTY.
+
+Return DEFAULT, if value is nil."
+  (or (cdr (assq property a)) default))
+
+(defun pdf-annot-put (a property value)
+  "Set annotation A's PROPERTY to VALUE.
+
+Unless VALUE is `equal' to the current value, sets A's buffer's
+modified flag and runs the hook `pdf-annot-modified-functions'.
+
+Signals an error, if PROPERTY is not modifiable.
+
+Returns the modified annotation."
+
+  (declare (indent 2))
+  (unless (equal value (pdf-annot-get a property))
+    (unless (pdf-annot-property-modifiable-p a property)
+      (error "Property `%s' is read-only for this annotation"
+             property))
+    (with-current-buffer (pdf-annot-get-buffer a)
+      (setq a (pdf-annot-create
+               (pdf-info-editannot
+                (pdf-annot-get-id a)
+                `((,property . ,value)))))
+      (set-buffer-modified-p t)
+      (pdf-annot-run-modified-hooks :change a)))
+  a)
+
+(defun pdf-annot-run-modified-hooks (&optional operation &rest annotations)
+  "Run `pdf-annot-modified-functions' using OPERATION on ANNOTATIONS.
+
+OPERATION should be one of nil, :change, :insert or :delete.  If
+nil, annotations should be empty.
+
+Redisplay modified pages.
+
+If `pdf-annot-inhibit-modification-hooks' in non-nil, this just
+saves ANNOTATIONS and does not call the hooks until later, when
+the variable is nil and this function is called again."
+
+  (unless (memq operation '(nil :insert :change :delete))
+    (error "Invalid operation: %s" operation))
+  (when (and (null operation) annotations)
+    (error "Missing operation argument"))
+
+  (when operation
+    (let ((list (plist-get pdf-annot-delayed-modified-annotations operation)))
+      (dolist (a annotations)
+        (cl-pushnew a list :test 'pdf-annot-equal))
+      (setq pdf-annot-delayed-modified-annotations
+            (plist-put pdf-annot-delayed-modified-annotations
+                       operation list))))
+  (unless pdf-annot-inhibit-modification-hooks
+    (let* ((changed (plist-get pdf-annot-delayed-modified-annotations :change))
+           (inserted (mapcar (lambda (a)
+                               (or (car (cl-member a changed :test 'pdf-annot-equal))
+                                   a))
+                             (plist-get pdf-annot-delayed-modified-annotations :insert)))
+           (deleted (plist-get pdf-annot-delayed-modified-annotations :delete))
+           (union (cl-union (cl-union changed inserted :test 'pdf-annot-equal)
+                            deleted :test 'pdf-annot-equal))
+           (closure (lambda (arg)
+                      (cl-ecase arg
+                        (:inserted (copy-sequence inserted))
+                        (:changed (copy-sequence changed))
+                        (:deleted (copy-sequence deleted))
+                        (t (copy-sequence union))
+                        (nil nil))))
+           (pages (mapcar (lambda (a) (pdf-annot-get a 'page)) union)))
+      (when union
+        (unwind-protect
+            (run-hook-with-args
+             'pdf-annot-modified-functions closure)
+          (setq pdf-annot-delayed-modified-annotations nil)
+          (apply 'pdf-view-redisplay-pages pages))))))
+
+(defun pdf-annot-equal (a1 a2)
+  "Return non-nil, if annotations A1 and A2 are equal.
+
+Two annotations are equal, if they belong to the same buffer and
+have identical id properties."
+  (and (eq (pdf-annot-get-buffer a1)
+           (pdf-annot-get-buffer a2))
+       (eq (pdf-annot-get-id a1)
+           (pdf-annot-get-id a2))))
+
+(defun pdf-annot-get-buffer (a)
+  "Return annotation A's buffer."
+  (pdf-annot-get a 'buffer))
+
+(defun pdf-annot-get-id (a)
+  "Return id property of annotation A."
+  (pdf-annot-get a 'id))
+
+(defun pdf-annot-get-type (a)
+  "Return type property of annotation A."
+  (pdf-annot-get a 'type))
+
+(defun pdf-annot-get-display-edges (a)
+  "Return a list of EDGES used for display for annotation A.
+
+This returns a list of \(LEFT TOP RIGHT BOT\) demarking the
+rectangles of the page where A is rendered."
+
+  (or (pdf-annot-get a 'markup-edges)
+      (list (pdf-annot-get a 'edges))))
+
+(defun pdf-annot-delete (a)
+  "Delete annotation A.
+
+Sets A's buffer's modified flag and runs the hook
+`pdf-annot-modified-functions'.
+
+This function always returns nil."
+  (interactive
+   (list (pdf-annot-read-annotation
+          "Click on the annotation you wish to delete")))
+  (with-current-buffer (pdf-annot-get-buffer a)
+    (pdf-info-delannot
+     (pdf-annot-get-id a))
+    (set-buffer-modified-p t)
+    (pdf-annot-run-modified-hooks :delete a))
+  (when (called-interactively-p 'any)
+    (message "Annotation deleted"))
+  nil)
+
+(defun pdf-annot-text-annotation-p (a)
+  (eq 'text (pdf-annot-get a 'type)))
+
+(defun pdf-annot-markup-annotation-p (a)
+  (not (null
+        (memq (pdf-annot-get a 'type)
+              pdf-annot-markup-annotation-types))))
+
+(defun pdf-annot-property-modifiable-p (a property)
+  (or (memq property '(edges color flags contents))
+      (and (pdf-annot-markup-annotation-p a)
+           (memq property '(label opacity popup popup-is-open)))
+      (and (pdf-annot-text-annotation-p a)
+           (memq property '(icon is-open)))))
+
+(defun pdf-annot-activate-annotation (a)
+  (or (run-hook-with-args-until-success
+       'pdf-annot-activate-handler-functions
+       a)
+      (pdf-annot-default-activate-handler a)))
+
+(defun pdf-annot-default-activate-handler (a)
+  (cond
+   ((pdf-annot-has-attachment-p a)
+    (pdf-annot-pop-to-attachment a))
+   (t (pdf-annot-edit-contents a))))
+
+\f
+;; * ================================================================== *
+;; * Handling attachments
+;; * ================================================================== *
+
+(defun pdf-annot-has-attachment-p (a)
+  "Return non-nil if annotation A's has data attached."
+  (eq 'file (pdf-annot-get a 'type)))
+
+(defun pdf-annot-get-attachment (a &optional do-save)
+  "Retrieve annotation A's attachment.
+
+The DO-SAVE argument is given to
+`pdf-info-getattachment-from-annot', which see."
+  (unless (pdf-annot-has-attachment-p a)
+    (error "Annotation has no data attached: %s" a))
+  (pdf-info-getattachment-from-annot
+   (pdf-annot-get-id a)
+   do-save
+   (pdf-annot-get-buffer a)))
+
+(defun pdf-annot-attachment-base-directory ()
+  "Return the base directory for saving attachments."
+  (let ((dir (pdf-util-expand-file-name "attachments")))
+    (unless (file-exists-p dir)
+      (make-directory dir))
+    dir))
+
+(defun pdf-annot-attachment-delete-base-directory ()
+  "Delete all saved attachment files of the current buffer."
+  (setq pdf-annot--attachment-file-alist nil)
+  (delete-directory (pdf-annot-attachment-base-directory) t))
+
+(defun pdf-annot-attachment-unique-filename (attachment)
+  "Return a unique absolute filename for ATTACHMENT."
+  (let* ((filename (or (cdr (assq 'filename attachment))
+                       "attachment"))
+         (id (cdr (assq 'id attachment)))
+         (unique
+          (or (cdr (assoc id pdf-annot--attachment-file-alist))
+              (let* ((sans-ext
+                      (expand-file-name
+                       (concat (file-name-as-directory ".")
+                               (file-name-sans-extension filename))
+                       (pdf-annot-attachment-base-directory)))
+                     (ext (file-name-extension filename))
+                     (newname (concat sans-ext "." ext))
+                     (i 0))
+                (while (rassoc newname pdf-annot--attachment-file-alist)
+                  (setq newname (format "%s-%d.%s" sans-ext (cl-incf i) ext)))
+                (push (cons id newname) pdf-annot--attachment-file-alist)
+                newname)))
+         (directory (file-name-directory unique)))
+    (unless (file-exists-p directory)
+      (make-directory directory t))
+    unique))
+
+
+(defun pdf-annot-attachment-save (attachment &optional regenerate-p)
+  "Save ATTACHMENT's data to a unique filename and return it's name.
+
+If REGENERATE-P is non-nil, copy attachment's file even if the
+copy already exists.
+
+Signal an error, if ATTACHMENT has no, or a non-existing, `file'
+property, i.e. it was retrieved with an unset do-save argument.
+See `pdf-info-getattachments'"
+
+  (let ((datafile (cdr (assq 'file attachment))))
+    (unless (and datafile
+                 (file-exists-p datafile))
+      (error "Attachment's file property is invalid"))
+    (let* ((filename
+            (pdf-annot-attachment-unique-filename attachment)))
+      (when (or regenerate-p
+                (not (file-exists-p filename)))
+        (copy-file datafile filename nil nil t t))
+      filename)))
+
+(defun pdf-annot-find-attachment-noselect (a)
+  "Find annotation A's attachment in a buffer, without selecting it.
+
+Signals an error, if A has no data attached."
+  (let ((attachment (pdf-annot-get-attachment a t)))
+    (unwind-protect
+        (find-file-noselect
+         (pdf-annot-attachment-save attachment))
+      (let ((tmpfile (cdr (assq 'file attachment))))
+        (when (and tmpfile
+                   (file-exists-p tmpfile))
+          (delete-file tmpfile))))))
+
+(defun pdf-annot-attachment-dired (&optional regenerate-p)
+  "List all attachments in a dired buffer.
+
+If REGENERATE-P is non-nil, create attachment's files even if
+they already exist.  Interactively REGENERATE-P is non-nil if a
+prefix argument was given.
+
+Return the dired buffer."
+  (interactive (list current-prefix-arg))
+  (let ((attachments (pdf-info-getattachments t)))
+    (unwind-protect
+        (progn
+          (dolist (a (pdf-annot-getannots nil 'file))
+            (push (pdf-annot-get-attachment a t)
+                  attachments ))
+          (dolist (att attachments)
+            (pdf-annot-attachment-save att regenerate-p))
+          (unless attachments
+            (error "Document has no data attached"))
+          (dired (pdf-annot-attachment-base-directory)))
+      (dolist (att attachments)
+        (let ((tmpfile (cdr (assq 'file att))))
+          (when (and tmpfile (file-exists-p tmpfile))
+            (delete-file tmpfile)))))))
+
+(defun pdf-annot-display-attachment (a &optional display-action select-window-p)
+  "Display file annotation A's data in a buffer.
+
+DISPLAY-ACTION should be a valid `display-buffer' action.  If
+nil, `pdf-annot-attachment-display-buffer-action' is used.
+
+Select the window, if SELECT-WINDOW-P is non-nil.
+
+Return the window attachment is displayed in."
+
+  (interactive
+   (list (pdf-annot-read-annotation
+          "Select a file annotation by clicking on it")))
+  (let* ((buffer (pdf-annot-find-attachment-noselect a))
+         (window (display-buffer
+                  buffer (or display-action
+                             pdf-annot-attachment-display-buffer-action))))
+    (when select-window-p
+      (select-window window))
+    window))
+
+(defun pdf-annot-pop-to-attachment (a)
+  "Display annotation A's attachment in a window and select it."
+  (interactive
+   (list (pdf-annot-read-annotation
+          "Select a file annotation by clicking on it")))
+  (pdf-annot-display-attachment a nil t))
+
+\f
+;; * ================================================================== *
+;; * Interfacing with the display
+;; * ================================================================== *
+
+(defun pdf-annot-image-position (a &optional image-size)
+  "Return the position of annotation A in image coordinates.
+
+IMAGE-SIZE should be a cons \(WIDTH . HEIGHT\) and defaults to
+the page-image of the selected window."
+
+  (unless image-size
+    (pdf-util-assert-pdf-window)
+    (setq image-size (pdf-view-image-size)))
+  (let ((e (pdf-util-scale
+            (pdf-annot-get a 'edges)
+            image-size)))
+    (pdf-util-with-edges (e)
+      `(,e-left . ,e-top))))
+
+(defun pdf-annot-image-set-position (a x y &optional image-size)
+  "Set annotation A's position to X,Y in image coordinates.
+
+See `pdf-annot-image-position' for IMAGE-SIZE."
+
+  (unless image-size
+    (pdf-util-assert-pdf-window)
+    (setq image-size (pdf-view-image-size)))
+  (let* ((edges (pdf-annot-get a 'edges))
+         (x (/ x (float (car image-size))))
+         (y (/ y (float (cdr image-size)))))
+    (pdf-util-with-edges (edges)
+      (let* ((w edges-width)
+             (h edges-height)
+             (x (max 0 (min x (- 1 w))))
+             (y (max 0 (min y (- 1 h)))))
+        (pdf-annot-put a 'edges
+          (list x y -1 -1))))))
+
+(defun pdf-annot-image-size (a &optional image-size)
+  "Return the size of annotation A in image coordinates.
+
+Returns \(WIDTH . HEIGHT\).
+
+See `pdf-annot-image-position' for IMAGE-SIZE."
+  (unless image-size
+    (pdf-util-assert-pdf-window)
+    (setq image-size (pdf-view-image-size)))
+  (let ((edges (pdf-util-scale
+                (pdf-annot-get a 'edges) image-size)))
+    (pdf-util-with-edges (edges)
+      (cons edges-width edges-height))))
+
+(defun pdf-annot-image-set-size (a &optional width height image-size)
+  "Set annotation A's size in image to WIDTH and/or HEIGHT.
+
+See `pdf-annot-image-position' for IMAGE-SIZE."
+  (unless image-size
+    (pdf-util-assert-pdf-window)
+    (setq image-size (pdf-view-image-size)))
+  (let* ((edges (pdf-annot-get a 'edges))
+         (w (and width
+                 (/ width (float (car image-size)))))
+         (h (and height
+                 (/ height (float (cdr image-size))))))
+    (pdf-util-with-edges (edges)
+      (pdf-annot-put a 'edges
+        (list edges-left
+              edges-top
+              (if w (+ edges-left w) edges-right)
+              (if h (+ edges-top h) edges-bot))))))
+
+(defun pdf-annot-at-position (pos)
+  "Return annotation at POS in the selected window.
+
+POS should be an absolute image position as a cons \(X . Y\).
+Alternatively POS may also be an event position, in which case
+`posn-window' and `posn-object-x-y' is used to find the image
+position.
+
+Return nil, if no annotation was found."
+  (let (window)
+    (when (posnp pos)
+      (setq window (posn-window pos)
+            pos (posn-object-x-y pos)))
+    (save-selected-window
+      (when window (select-window window))
+      (let* ((annots (pdf-annot-getannots (pdf-view-current-page)))
+             (size (pdf-view-image-size))
+             (rx (/ (car pos) (float (car size))))
+             (ry (/ (cdr pos) (float (cdr size))))
+             (rpos (cons rx ry)))
+        (or (cl-some (lambda (a)
+                       (and (cl-some
+                             (lambda (e)
+                               (pdf-util-edges-inside-p e rpos))
+                             (pdf-annot-get-display-edges a))
+                            a))
+                     annots)
+            (error "No annotation at this position"))))))
+
+(defun pdf-annot-mouse-move (event &optional annot)
+  "Start moving an annotation at EVENT's position.
+
+EVENT should be a mouse event originating the request and is used
+as a reference point.
+
+ANNOT is the annotation to operate on and defaults to the
+annotation at EVENT's start position.
+
+This function does not return until the operation is completed,
+i.e. a non mouse-movement event is read."
+
+  (interactive "@e")
+  (pdf-util-assert-pdf-window (posn-window (event-start event)))
+  (select-window (posn-window (event-start event)))
+  (let* ((mpos (posn-object-x-y (event-start event)))
+         (a (or annot
+                (pdf-annot-at-position mpos))))
+    (unless a
+      (error "No annotation at this position: %s" mpos))
+    (let* ((apos (pdf-annot-image-position a))
+           (offset (cons (- (car mpos) (car apos))
+                         (- (cdr mpos) (cdr apos))))
+           (window (selected-window))
+           make-pointer-invisible)
+      (when (pdf-util-track-mouse-dragging (ev 0.1)
+              (when (and (eq window (posn-window (event-start ev)))
+                         (eq 'image (car-safe (posn-object (event-start ev)))))
+                (let ((pdf-view-inhibit-hotspots t)
+                      (pdf-annot-inhibit-modification-hooks t)
+                      (pdf-cache-image-inihibit t)
+                      (xy (posn-object-x-y (event-start ev))))
+                  (pdf-annot-image-set-position
+                   a (- (car xy) (car offset))
+                   (- (cdr xy) (cdr offset)))
+                  (pdf-view-redisplay))))
+        (pdf-annot-run-modified-hooks)))
+    nil))
+
+(defun pdf-annot-hotspot-function (page size)
+  "Create image hotspots for page PAGE of size SIZE."
+  (apply 'nconc (mapcar (lambda (a)
+                          (unless (eq (pdf-annot-get a 'type)
+                                      'link)
+                            (pdf-annot-create-hotspots a size)))
+                        (pdf-annot-getannots page))))
+
+(defun pdf-annot-create-hotspots (a size)
+  "Return a list of image hotspots for annotation A."
+  (let ((id (pdf-annot-get-id a))
+        (edges (pdf-util-scale
+                (pdf-annot-get-display-edges a)
+                size 'round))
+        (moveable-p (memq (pdf-annot-get a 'type)
+                          '(file text)))
+        hotspots)
+    (dolist (e edges)
+      (pdf-util-with-edges (e)
+        (push `((rect . ((,e-left . ,e-top) . (,e-right . ,e-bot)))
+                ,id
+                (pointer
+                 hand
+                 help-echo
+                 ,(pdf-annot-print-annotation a)))
+              hotspots)))
+    (pdf-annot-create-hotspot-binding id moveable-p a)
+    hotspots))
+
+;; FIXME: Define a keymap as a template for this. Much cleaner.
+(defun pdf-annot-create-hotspot-binding (id moveable-p annotation)
+  ;; Activating
+  (local-set-key
+   (vector id 'mouse-1)
+   (lambda ()
+     (interactive)
+     (pdf-annot-activate-annotation annotation)))
+  ;; Move
+  (when moveable-p
+    (local-set-key
+     (vector id 'down-mouse-1)
+     (lambda (ev)
+       (interactive "@e")
+       (pdf-annot-mouse-move ev annotation))))
+  ;; Context Menu
+  (local-set-key
+   (vector id 'down-mouse-3)
+   (lambda ()
+     (interactive "@")
+     (popup-menu (pdf-annot-create-context-menu annotation))))
+  ;; Everything else
+  (local-set-key
+   (vector id t)
+   'pdf-util-image-map-mouse-event-proxy))
+
+(defun pdf-annot-show-annotation (a &optional highlight-p window)
+  "Make annotation A visible.
+
+Turn to A's page in WINDOW, and scroll it if necessary.
+
+If HIGHLIGHT-P is non-nil, visually distinguish annotation A from
+other annotations."
+
+  (save-selected-window
+    (when window (select-window window))
+    (pdf-util-assert-pdf-window)
+    (let ((page (pdf-annot-get a 'page))
+          (size (pdf-view-image-size)))
+      (unless (= page (pdf-view-current-page))
+        (pdf-view-goto-page page))
+      (let ((edges (pdf-annot-get-display-edges a)))
+        (when highlight-p
+          (pdf-view-display-image
+           (pdf-view-create-image
+               (pdf-cache-renderpage-highlight
+                page (car size)
+                `("white" "steel blue" 0.35 ,@edges))
+             :map (pdf-view-apply-hotspot-functions
+                   window page size))))
+        (pdf-util-scroll-to-edges
+         (pdf-util-scale-relative-to-pixel (car edges)))))))
+
+(defun pdf-annot-read-annotation (&optional prompt)
+  "Let the user choose a annotation a mouse click using PROMPT."
+  (pdf-annot-at-position
+   (pdf-util-read-image-position
+    (or prompt "Choose a annotation by clicking on it"))))
+
+\f
+;; * ================================================================== *
+;; * Creating annotations
+;; * ================================================================== *
+
+(defun pdf-annot-add-annotation (type edges &optional property-alist page)
+  "Creates and adds a new annotation of type TYPE to the document.
+
+TYPE determines the kind of annotation to add and maybe one of
+`text', `squiggly', `underline', `strike-out' or `highlight'.
+
+EDGES determines where the annotation will appear on the page.
+If type is `text', this should be a single list of \(LEFT TOP
+RIGHT BOT\).  Though, in this case only LEFT and TOP are used,
+since the size of text annotations is fixed. Otherwise EDGES may
+be a list of such elements.  All values should be image relative
+coordinates, i.e. in the range \[0;1\].
+
+PROPERTY-ALIST is a list of annotation properties, which will be
+put on the created annotation.
+
+PAGE determines the page of the annotation. It defaults to the
+page currently displayed in the selected window.
+
+Signal an error, if PROPERTY-ALIST contains non-modifiable
+properties or PAGE is nil and the selected window does not
+display a PDF document or creating annotations of type TYPE is
+not supported.
+
+Set buffers modified flag and calls
+`pdf-annot-activate-annotation' if
+`pdf-annot-activate-created-annotations' is non-nil.
+
+Return the new annotation."
+
+  (unless (memq type (pdf-info-creatable-annotation-types))
+    (error "Unsupported annotation type: %s" type))
+  (unless page
+    (pdf-util-assert-pdf-window)
+    (setq page (pdf-view-current-page)))
+  (unless (consp (car-safe edges))
+    (setq edges (list edges)))
+  (when (and (eq type 'text)
+             (> (length edges) 1))
+    (error "Edges argument should be a single edge-list for text annotations"))
+  (let* ((a (apply 'pdf-info-addannot
+                   page
+                   (if (eq type 'text)
+                       (car edges)
+                     (apply #'pdf-util-edges-union
+                            (apply #'append
+                                   (mapcar
+                                    (lambda (e)
+                                      (pdf-info-getselection page e))
+                                    edges))))
+                   type
+                   nil
+                   (if (not (eq type 'text)) edges)))
+         (id (pdf-annot-get-id a)))
+    (when property-alist
+      (condition-case err
+          (setq a (pdf-info-editannot id property-alist))
+        (error
+         (pdf-info-delannot id)
+         (signal (car err) (cdr err)))))
+    (setq a (pdf-annot-create a))
+    (set-buffer-modified-p t)
+    (pdf-annot-run-modified-hooks :insert a)
+    (when pdf-annot-activate-created-annotations
+      (pdf-annot-activate-annotation a))
+    a))
+
+(defun pdf-annot-add-text-annotation (pos &optional icon property-alist)
+  "Add a new text annotation at POS in the selected window.
+
+POS should be a image position object or a cons \(X . Y\), both
+being image coordinates.
+
+ICON determines how the annotation is displayed and should be
+listed in `pdf-annot-standard-text-icons'.  Any other value is ok
+as well, but will render the annotation invisible.
+
+Adjust X and Y accordingly, if the position would render the
+annotation off-page.
+
+Merge ICON as a icon property with PROPERTY-ALIST and
+`pdf-annot-default-text-annotation-properties' and apply the
+result to the created annotation.
+
+See also `pdf-annot-add-annotation'.
+
+Return the new annotation."
+
+  (interactive
+   (let* ((posn (pdf-util-read-image-position
+                 "Click where a new text annotation should be added ..."))
+          (window (posn-window posn)))
+     (select-window window)
+     (list posn)))
+  (pdf-util-assert-pdf-window)
+  (when (posnp pos)
+    (setq pos (posn-object-x-y pos)))
+  (let ((isize (pdf-view-image-size))
+        (x (car pos))
+        (y (cdr pos)))
+    (unless (and (>= x 0)
+                 (< x (car isize)))
+      (signal 'args-out-of-range (list pos)))
+    (unless (and (>= y 0)
+                 (< y (cdr isize)))
+      (signal 'args-out-of-range (list pos)))
+    (let ((size (pdf-util-scale-points-to-pixel
+                 pdf-annot-text-annotation-size 'round)))
+      (setcar size (min (car size) (car isize)))
+      (setcdr size (min (cdr size) (cdr isize)))
+      (cl-decf x (max 0 (- (+ x (car size)) (car isize))))
+      (cl-decf y (max 0 (- (+ y (cdr size)) (cdr isize))))
+      (pdf-annot-add-annotation
+       'text (pdf-util-scale-pixel-to-relative
+              (list x y -1 -1))
+       (pdf-annot-merge-alists
+        (and icon `((icon . ,icon)))
+        property-alist
+        pdf-annot-default-text-annotation-properties
+        (cdr (assq 'text pdf-annot-default-annotation-properties))
+        (cdr (assq t pdf-annot-default-annotation-properties))
+        `((color . ,(car pdf-annot-color-history))))))))
+
+(defun pdf-annot-mouse-add-text-annotation (ev)
+  (interactive "@e")
+  (pdf-annot-add-text-annotation
+   (if (eq (car-safe ev)
+           'menu-bar)
+       (let (echo-keystrokes)
+         (message nil)
+         (pdf-util-read-image-position
+          "Click where a new text annotation should be added ..."))
+     (event-start ev))))
+
+(defun pdf-annot-add-markup-annotation (list-of-edges type &optional color
+                                                      property-alist)
+  "Add a new markup annotation in the selected window.
+
+LIST-OF-EDGES determines the marked up area and should be a list
+of \(LEFT TOP RIGHT BOT\), each value a relative coordinate.
+
+TYPE should be one of `squiggly', `underline', `strike-out' or
+`highlight'.
+
+Merge COLOR as a color property with PROPERTY-ALIST and
+`pdf-annot-default-markup-annotation-properties' and apply the
+result to the created annotation.
+
+See also `pdf-annot-add-annotation'.
+
+Return the new annotation."
+  (interactive
+   (list (pdf-view-active-region t)
+         (let ((type (completing-read "Markup type (default highlight): "
+                                      '("squiggly" "highlight" "underline" "strike-out")
+                                      nil t)))
+           (if (equal type "") 'highlight (intern type)))
+         (pdf-annot-read-color)))
+  (pdf-util-assert-pdf-window)
+  (pdf-annot-add-annotation
+   type
+   list-of-edges
+   (pdf-annot-merge-alists
+    (and color `((color . ,color)))
+    property-alist
+    pdf-annot-default-markup-annotation-properties
+    (cdr (assq type pdf-annot-default-annotation-properties))
+    (cdr (assq t pdf-annot-default-annotation-properties))
+    (when pdf-annot-color-history
+      `((color . ,(car pdf-annot-color-history))))
+    '((color . "#ffff00")))
+   (pdf-view-current-page)))
+
+(defun pdf-annot-add-squiggly-markup-annotation (list-of-edges
+                                                 &optional color property-alist)
+  "Add a new squiggly annotation in the selected window.
+
+See also `pdf-annot-add-markup-annotation'."
+  (interactive (list (pdf-view-active-region t)))
+  (pdf-annot-add-markup-annotation list-of-edges 'squiggly color property-alist))
+
+(defun pdf-annot-add-underline-markup-annotation (list-of-edges
+                                                  &optional color property-alist)
+  "Add a new underline annotation in the selected window.
+
+See also `pdf-annot-add-markup-annotation'."
+  (interactive (list (pdf-view-active-region t)))
+  (pdf-annot-add-markup-annotation list-of-edges 'underline color property-alist))
+
+(defun pdf-annot-add-strikeout-markup-annotation (list-of-edges
+                                                  &optional color property-alist)
+  "Add a new strike-out annotation in the selected window.
+
+See also `pdf-annot-add-markup-annotation'."
+  (interactive (list (pdf-view-active-region t)))
+  (pdf-annot-add-markup-annotation list-of-edges 'strike-out color property-alist))
+
+(defun pdf-annot-add-highlight-markup-annotation (list-of-edges
+                                                  &optional color property-alist)
+  "Add a new highlight annotation in the selected window.
+
+See also `pdf-annot-add-markup-annotation'."
+  (interactive (list (pdf-view-active-region t)))
+  (pdf-annot-add-markup-annotation list-of-edges 'highlight color property-alist))
+
+(defun pdf-annot-read-color (&optional prompt)
+  "Read and return a color using PROMPT.
+
+Offer `pdf-annot-color-history' as default values."
+  (let* ((defaults (append
+                    (delq nil
+                          (list
+                           (cdr (assq 'color
+                                      pdf-annot-default-markup-annotation-properties))
+                           (cdr (assq 'color
+                                      pdf-annot-default-text-annotation-properties))))
+                    pdf-annot-color-history))
+         (prompt
+          (format "%s%s: "
+                  (or prompt "Color")
+                  (if defaults (format " (default %s)" (car defaults)) "")))
+         (current-completing-read-function completing-read-function)
+         (completing-read-function
+          (lambda (prompt collection &optional predicate require-match
+                          initial-input _hist _def inherit-input-method)
+            (funcall current-completing-read-function
+                     prompt collection predicate require-match
+                     initial-input 'pdf-annot-color-history
+                     defaults
+                     inherit-input-method))))
+    (read-color prompt)))
+
+(defun pdf-annot-merge-alists (&rest alists)
+  "Merge ALISTS into a single one.
+
+Suppresses successive duplicate entries of keys after the first
+occurrence in ALISTS."
+
+  (let (merged)
+    (dolist (elt (apply 'append alists))
+      (unless (assq (car elt) merged)
+        (push elt merged)))
+    (nreverse merged)))
+
+
+\f
+;; * ================================================================== *
+;; * Displaying annotation contents
+;; * ================================================================== *
+
+(defun pdf-annot-print-property (a property)
+  "Pretty print annotation A's property PROPERTY."
+  (let ((value (pdf-annot-get a property)))
+    (cl-case property
+      (color
+       (propertize (or value "")
+                   'face (and value
+                              `(:background ,value))))
+      ((created modified)
+       (let ((date value))
+         (if (null date)
+             "No date"
+           (current-time-string date))))
+      ;; print verbatim
+      (subject
+       (or value "No subject"))
+      (opacity
+       (let ((opacity (or value 1.0)))
+         (format "%d%%" (round (* 100 opacity)))))
+      (t (format "%s" (or value ""))))))
+
+(defun pdf-annot-print-annotation (a)
+  "Pretty print annotation A."
+  (or (run-hook-with-args-until-success
+       'pdf-annot-print-annotation-functions a)
+      (pdf-annot-print-annotation-default a)))
+
+(defun pdf-annot-print-annotation-default (a)
+  "Default pretty printer for annotation A.
+
+The result consists of a header (as printed with
+`pdf-annot-print-annotation-header') a newline and A's contents
+property."
+  (concat
+   (pdf-annot-print-annotation-header a)
+   "\n"
+   (pdf-annot-get a 'contents)))
+
+(defun pdf-annot-print-annotation-header (a)
+  "Emit a suitable header string for annotation A."
+  (let ((header
+         (cond
+          ((eq 'file (pdf-annot-get a 'type))
+           (let ((att (pdf-annot-get-attachment a)))
+             (format "File attachment `%s' of %s"
+                     (or (cdr (assq 'filename att)) "unnamed")
+                     (if (cdr (assq 'size att))
+                         (format "size %s" (file-size-human-readable
+                                            (cdr (assq 'size att))))
+                       "unknown size"))))
+          (t
+           (format "%s"
+                   (mapconcat
+                    'identity
+                    (mapcar
+                     (lambda (property)
+                       (pdf-annot-print-property
+                        a property))
+                     `(subject
+                       label
+                       modified))
+                    ";"))))))
+    (setq header (propertize header 'face 'header-line
+                             'intangible t 'read-only t))
+    ;; This `trick' makes the face apply in a tooltip.
+    (propertize header 'display header)))
+
+(defun pdf-annot-print-annotation-latex-maybe (a)
+  "Maybe print annotation A's content as a LaTeX fragment.
+
+See `pdf-annot-latex-string-predicate'."
+  (when (and (functionp pdf-annot-latex-string-predicate)
+             (funcall pdf-annot-latex-string-predicate
+                      (pdf-annot-get a 'contents)))
+    (pdf-annot-print-annotation-latex a)))
+
+(defun pdf-annot-print-annotation-latex (a)
+  "Print annotation A's content as a LaTeX fragment.
+
+This compiles A's contents as a LaTeX fragment and puts the
+resulting image as a display property on the contents, prefixed
+by a header."
+
+  (let (tempfile)
+    (unwind-protect
+        (with-current-buffer (pdf-annot-get-buffer a)
+          (let* ((page (pdf-annot-get a 'page))
+                 (header (pdf-annot-print-annotation-header a))
+                 (contents (pdf-annot-get a 'contents))
+                 (hash (sxhash (format
+                                "pdf-annot-print-annotation-latex%s%s%s"
+                                page header contents)))
+                 (data (pdf-cache-lookup-image page 0 nil hash))
+                 (org-format-latex-header
+                  pdf-annot-latex-header)
+                 (temporary-file-directory
+                  (pdf-util-expand-file-name "pdf-annot-print-annotation-latex")))
+            (unless (file-directory-p temporary-file-directory)
+              (make-directory temporary-file-directory))
+            (unless data
+              (setq tempfile (make-temp-file "pdf-annot" nil ".png"))
+              ;; FIXME: Why is this with-temp-buffer needed (which it is) ?
+              (with-temp-buffer
+                (org-create-formula-image
+                 contents tempfile org-format-latex-options t))
+              (setq data (pdf-util-munch-file tempfile))
+              (if (and (> (length data) 3)
+                       (equal (substring data 1 4)
+                              "PNG"))
+                  (pdf-cache-put-image page 0 data hash)
+                (setq data nil)))
+            (concat
+             header
+             "\n"
+             (if data
+                 (propertize
+                  contents 'display (pdf-view-create-image data))
+               (propertize
+                contents
+                'display
+                (concat
+                 (propertize "Failed to compile latex fragment\n"
+                             'face 'error)
+                 contents))))))
+      (when (and tempfile
+                 (file-exists-p tempfile))
+        (delete-file tempfile)))))
+
+\f
+;; * ================================================================== *
+;; * Editing annotation contents
+;; * ================================================================== *
+
+(defvar-local pdf-annot-edit-contents--annotation nil)
+(put 'pdf-annot-edit-contents--annotation 'permanent-local t)
+(defvar-local pdf-annot-edit-contents--buffer nil)
+
+(defcustom pdf-annot-edit-contents-setup-function
+  (lambda (a)
+    (let ((mode (if (funcall pdf-annot-latex-string-predicate
+                             (pdf-annot-get a 'contents))
+                    'latex-mode
+                  'text-mode)))
+      (unless (derived-mode-p mode)
+        (funcall mode))))
+  "A function for setting up, e.g. the major-mode, of the edit buffer.
+
+The function receives one argument, the annotation whose contents
+is about to be edited in this buffer.
+
+The default value turns on `latex-mode' if
+`pdf-annot-latex-string-predicate' returns non-nil on the
+annotation's contents and otherwise `text-mode'. "
+  :group 'pdf-annot
+  :type 'function)
+
+(defcustom pdf-annot-edit-contents-display-buffer-action
+  '((display-buffer-reuse-window
+     display-buffer-split-below-and-attach)
+    (inhibit-same-window . t)
+    (window-height . 0.25))
+  "Display action when showing the edit buffer."
+  :group 'pdf-annot
+  :type display-buffer--action-custom-type)
+
+(defvar pdf-annot-edit-contents-minor-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (set-keymap-parent kmap text-mode-map)
+    (define-key kmap (kbd "C-c C-c") 'pdf-annot-edit-contents-commit)
+    (define-key kmap (kbd "C-c C-q") 'pdf-annot-edit-contents-abort)
+    kmap))
+
+(define-minor-mode pdf-annot-edit-contents-minor-mode
+  "Active when editing the contents of annotations."
+  nil nil nil
+  (when pdf-annot-edit-contents-minor-mode
+    (message "%s"
+             (substitute-command-keys
+              "Press \\[pdf-annot-edit-contents-commit] to commit your changes, \\[pdf-annot-edit-contents-abort] to abandon them."))))
+
+(put 'pdf-annot-edit-contents-minor-mode 'permanent-local t)
+
+;; FIXME: Document pdf-annot-edit-* functions below.
+(defun pdf-annot-edit-contents-finalize (do-save &optional do-kill)
+  (when (buffer-modified-p)
+    (cond
+     ((eq do-save 'ask)
+      (save-window-excursion
+        (display-buffer (current-buffer) nil (selected-frame))
+        (when (y-or-n-p "Save changes to this annotation ?")
+          (pdf-annot-edit-contents-save-annotation))))
+     (do-save
+      (pdf-annot-edit-contents-save-annotation)))
+    (set-buffer-modified-p nil))
+  (dolist (win (get-buffer-window-list))
+    (quit-window do-kill win)))
+
+(defun pdf-annot-edit-contents-save-annotation ()
+  (when pdf-annot-edit-contents--annotation
+    (pdf-annot-put pdf-annot-edit-contents--annotation
+        'contents
+      (buffer-substring-no-properties (point-min) (point-max)))
+    (set-buffer-modified-p nil)))
+
+(defun pdf-annot-edit-contents-commit ()
+  (interactive)
+  (pdf-annot-edit-contents-finalize t))
+
+(defun pdf-annot-edit-contents-abort ()
+  (interactive)
+  (pdf-annot-edit-contents-finalize nil t))
+
+(defun pdf-annot-edit-contents-noselect (a)
+  (with-current-buffer (pdf-annot-get-buffer a)
+    (when (and (buffer-live-p pdf-annot-edit-contents--buffer)
+               (not (eq a pdf-annot-edit-contents--annotation)))
+      (with-current-buffer pdf-annot-edit-contents--buffer
+        (pdf-annot-edit-contents-finalize 'ask)))
+    (unless (buffer-live-p pdf-annot-edit-contents--buffer)
+      (setq pdf-annot-edit-contents--buffer
+            (with-current-buffer (get-buffer-create
+                                  (format "*Edit Annotation %s*"
+                                          (buffer-name)))
+              (pdf-annot-edit-contents-minor-mode 1)
+              (current-buffer))))
+    (with-current-buffer pdf-annot-edit-contents--buffer
+      (let ((inhibit-read-only t))
+        (erase-buffer)
+        (save-excursion (insert (pdf-annot-get a 'contents)))
+        (set-buffer-modified-p nil))
+      (setq pdf-annot-edit-contents--annotation a)
+      (funcall pdf-annot-edit-contents-setup-function a)
+      (current-buffer))))
+
+(defun pdf-annot-edit-contents (a)
+  (select-window
+   (display-buffer
+    (pdf-annot-edit-contents-noselect a)
+    pdf-annot-edit-contents-display-buffer-action)))
+
+(defun pdf-annot-edit-contents-mouse (ev)
+  (interactive "@e")
+  (let* ((pos (posn-object-x-y (event-start ev)))
+         (a (and pos (pdf-annot-at-position pos))))
+    (unless a
+      (error "No annotation at this position"))
+    (pdf-annot-edit-contents a)))
+
+
+\f
+;; * ================================================================== *
+;; * Listing annotations
+;; * ================================================================== *
+
+(defcustom pdf-annot-list-display-buffer-action
+  '((display-buffer-reuse-window
+     display-buffer-pop-up-window)
+    (inhibit-same-window . t))
+  "Display action used when displaying the list buffer."
+  :group 'pdf-annot
+  :type display-buffer--action-custom-type)
+
+(defcustom pdf-annot-list-format
+  '((page . 3)
+    (type . 10)
+    (label . 24)
+    (date . 24))
+  "Annotation properties visible in the annotation list.
+
+It should be a list of \(PROPERTIZE. WIDTH\), where PROPERTY is a
+symbol naming one of supported properties to list and WIDTH its
+desired column-width.
+
+Currently supported properties are page, type, label, date and contents."
+  :type '(alist :key-type (symbol))
+  :options '((page (integer :value 3 :tag "Column Width"))
+             (type (integer :value 10 :tag "Column Width" ))
+             (label (integer :value 24 :tag "Column Width"))
+             (date (integer :value 24 :tag "Column Width"))
+             (contents (integer :value 56 :tag "Column Width")))
+  :group 'pdf-annot)
+
+(defcustom pdf-annot-list-highlight-type nil
+  "Whether to highlight \"Type\" column annotation list with annotation color."
+  :group 'pdf-annot
+  :type 'boolean)
+
+(defvar-local pdf-annot-list-buffer nil)
+
+(defvar-local pdf-annot-list-document-buffer nil)
+
+(defvar pdf-annot-list-mode-map
+  (let ((km (make-sparse-keymap)))
+    (define-key km (kbd "C-c C-f") 'pdf-annot-list-follow-minor-mode)
+    (define-key km (kbd "SPC") 'pdf-annot-list-display-annotation-from-id)
+    km))
+
+(defun pdf-annot-property-completions (property)
+  "Return a list of completion candidates for annotation property PROPERTY.
+
+Return nil, if not available."
+  (cl-case property
+    (color (pdf-util-color-completions))
+    (icon (copy-sequence pdf-annot-standard-text-icons))))
+
+(defun pdf-annot-compare-annotations (a1 a2)
+  "Compare annotations A1 and A2.
+
+Return non-nil if A1's page is less than A2's one or if they
+belong to the same page and A1 is displayed above/left of A2."
+  (let ((p1 (pdf-annot-get a1 'page))
+        (p2 (pdf-annot-get a2 'page)))
+    (or (< p1 p2)
+        (and (= p1 p2)
+             (let ((e1 (pdf-util-scale
+                        (car (pdf-annot-get-display-edges a1))
+                        '(1000 . 1000)))
+                   (e2 (pdf-util-scale
+                        (car (pdf-annot-get-display-edges a2))
+                        '(1000 . 1000))))
+               (pdf-util-with-edges (e1 e2)
+                 (or (< e1-top e2-top)
+                     (and (= e1-top e2-top)
+                          (<= e1-left e2-left)))))))))
+
+(defun pdf-annot-list-entries ()
+  (unless (buffer-live-p pdf-annot-list-document-buffer)
+    (error "No PDF document associated with this buffer"))
+  (mapcar 'pdf-annot-list-create-entry
+          (sort (pdf-annot-getannots nil pdf-annot-list-listed-types
+                                     pdf-annot-list-document-buffer)
+                'pdf-annot-compare-annotations)))
+
+(defun pdf-annot--make-entry-formatter (a)
+  (lambda (fmt)
+    (let ((entry-type (car fmt))
+          (entry-width (cdr fmt))
+          ;; Taken from css-mode.el
+          (contrasty-color
+           (lambda (name)
+             (if (> (color-distance name "black") 292485)
+                 "black" "white")))
+          (prune-newlines
+           (lambda (str)
+             (replace-regexp-in-string "\n" " " str t t))))
+      (cl-ecase entry-type
+        (date (pdf-annot-print-property a 'modified))
+        (page (pdf-annot-print-property a 'page))
+        (label (funcall prune-newlines
+                        (pdf-annot-print-property a 'label)))
+        (contents
+         (truncate-string-to-width
+          (funcall prune-newlines
+                   (pdf-annot-print-property a 'contents))
+          entry-width))
+        (type
+         (let ((color (pdf-annot-get a 'color))
+               (type (pdf-annot-print-property a 'type)))
+           (if pdf-annot-list-highlight-type
+               (propertize
+                type 'face
+                `(:background ,color
+                  :foreground ,(funcall contrasty-color color)))
+             type)))))))
+
+(defun pdf-annot-list-create-entry (a)
+  "Create a `tabulated-list-entries' entry for annotation A."
+  (list (pdf-annot-get-id a)
+        (vconcat
+         (mapcar (pdf-annot--make-entry-formatter a)
+                 pdf-annot-list-format))))
+
+(define-derived-mode pdf-annot-list-mode tablist-mode "Annots"
+  (let* ((page-sorter
+          (lambda (a b)
+            (< (string-to-number (aref (cadr a) 0))
+               (string-to-number (aref (cadr b) 0)))))
+         (format-generator
+          (lambda (format)
+            (let ((field (car format))
+                  (width (cdr format)))
+              (cl-case field
+                (page `("Pg." 3 ,page-sorter :read-only t :right-alight t))
+                (t (list
+                    (capitalize (symbol-name field))
+                    width t :read-only t)))))))
+    (setq tabulated-list-entries 'pdf-annot-list-entries
+          tabulated-list-format (vconcat
+                                 (mapcar
+                                  format-generator
+                                  pdf-annot-list-format))
+          tabulated-list-padding 2))
+  (set-keymap-parent pdf-annot-list-mode-map tablist-mode-map)
+  (use-local-map pdf-annot-list-mode-map)
+  (when (assq 'type pdf-annot-list-format)
+    (setq tablist-current-filter
+          `(not (== "Type" "link"))))
+  (tabulated-list-init-header))
+
+(defun pdf-annot-list-annotations ()
+  "List annotations in a dired like buffer.
+
+\\{pdf-annot-list-mode-map}"
+  (interactive)
+  (pdf-util-assert-pdf-buffer)
+  (let ((buffer (current-buffer)))
+    (with-current-buffer (get-buffer-create
+                          (format "*%s's annots*"
+                                  (file-name-sans-extension
+                                   (buffer-name))))
+      (delay-mode-hooks
+        (unless (derived-mode-p 'pdf-annot-list-mode)
+          (pdf-annot-list-mode))
+        (setq pdf-annot-list-document-buffer buffer)
+        (tabulated-list-print)
+        (setq tablist-context-window-function
+              (lambda (id) (pdf-annot-list-context-function id buffer))
+              tablist-operations-function 'pdf-annot-list-operation-function)
+        (let ((list-buffer (current-buffer)))
+          (with-current-buffer buffer
+            (setq pdf-annot-list-buffer list-buffer))))
+      (run-mode-hooks)
+      (pop-to-buffer
+       (current-buffer)
+       pdf-annot-list-display-buffer-action)
+      (tablist-move-to-major-column)
+      (tablist-display-context-window))
+    (add-hook 'pdf-info-close-document-hook
+              'pdf-annot-list-update nil t)
+    (add-hook 'pdf-annot-modified-functions
+              'pdf-annot-list-update nil t)))
+
+(defun pdf-annot-list-goto-annotation (a)
+  (with-current-buffer (pdf-annot-get-buffer a)
+    (unless (and (buffer-live-p pdf-annot-list-buffer)
+                 (get-buffer-window pdf-annot-list-buffer))
+      (pdf-annot-list-annotations))
+    (with-selected-window (get-buffer-window pdf-annot-list-buffer)
+      (goto-char (point-min))
+      (let ((id (pdf-annot-get-id a)))
+        (while (and (not (eobp))
+                    (not (eq id (tabulated-list-get-id))))
+          (forward-line))
+        (unless (eq id (tabulated-list-get-id))
+          (error "Unable to find annotation"))
+        (when (invisible-p (point))
+          (tablist-suspend-filter t))
+        (tablist-move-to-major-column)))))
+
+
+(defun pdf-annot-list-update (&optional _fn)
+  (when (buffer-live-p pdf-annot-list-buffer)
+    (with-current-buffer pdf-annot-list-buffer
+      (unless tablist-edit-column-minor-mode
+        (tablist-revert))
+      (tablist-context-window-update))))
+
+(defun pdf-annot-list-context-function (id buffer)
+  (with-current-buffer (get-buffer-create "*Contents*")
+    (set-window-buffer nil (current-buffer))
+    (let ((inhibit-read-only t))
+      (erase-buffer)
+      (when id
+        (save-excursion
+          (insert
+           (pdf-annot-print-annotation
+            (pdf-annot-getannot id buffer)))))
+      (read-only-mode 1))))
+
+(defun pdf-annot-list-operation-function (op &rest args)
+  (cl-ecase op
+    (supported-operations '(delete find-entry))
+    (delete
+     (cl-destructuring-bind (ids)
+         args
+       (when (buffer-live-p pdf-annot-list-document-buffer)
+         (with-current-buffer pdf-annot-list-document-buffer
+           (pdf-annot-with-atomic-modifications
+             (dolist (a (mapcar 'pdf-annot-getannot ids))
+               (pdf-annot-delete a)))))))
+    (find-entry
+     (cl-destructuring-bind (id)
+         args
+       (unless (buffer-live-p pdf-annot-list-document-buffer)
+         (error "No PDF document associated with this buffer"))
+       (let* ((buffer pdf-annot-list-document-buffer)
+              (a (pdf-annot-getannot id buffer))
+              (pdf-window (save-selected-window
+                            (or (get-buffer-window buffer)
+                                (display-buffer buffer))))
+              window)
+         (with-current-buffer buffer
+           (pdf-annot-activate-annotation a)
+           (setq window (selected-window)))
+         ;; Make it so that quitting the edit window returns to the
+         ;; list window.
+         (unless (memq window (list (selected-window) pdf-window))
+           (let* ((quit-restore
+                   (window-parameter window 'quit-restore)))
+             (when quit-restore
+               (setcar (nthcdr 2 quit-restore) (selected-window))))))))))
+
+(defvar pdf-annot-list-display-annotation--timer nil)
+
+(defun pdf-annot-list-display-annotation-from-id (id)
+  (interactive (list (tabulated-list-get-id)))
+  (when id
+    (unless (buffer-live-p pdf-annot-list-document-buffer)
+      (error "PDF buffer was killed"))
+    (when (timerp pdf-annot-list-display-annotation--timer)
+      (cancel-timer pdf-annot-list-display-annotation--timer))
+    (setq pdf-annot-list-display-annotation--timer
+          (run-with-idle-timer 0.1 nil
+            (lambda (buffer a)
+              (when (buffer-live-p buffer)
+                (with-selected-window
+                    (or (get-buffer-window buffer)
+                        (display-buffer
+                         buffer
+                         '(nil (inhibit-same-window . t))))
+                  (pdf-annot-show-annotation a t))))
+            pdf-annot-list-document-buffer
+            (pdf-annot-getannot id pdf-annot-list-document-buffer)))))
+
+(define-minor-mode pdf-annot-list-follow-minor-mode
+  "" nil nil nil
+  (unless (derived-mode-p 'pdf-annot-list-mode)
+    (error "No in pdf-annot-list-mode."))
+  (cond
+   (pdf-annot-list-follow-minor-mode
+    (add-hook 'tablist-selection-changed-functions
+              'pdf-annot-list-display-annotation-from-id nil t)
+    (let ((id (tabulated-list-get-id)))
+      (when id
+        (pdf-annot-list-display-annotation-from-id id))))
+   (t
+    (remove-hook 'tablist-selection-changed-functions
+                 'pdf-annot-list-display-annotation-from-id t))))
+
+(provide 'pdf-annot)
+;;; pdf-annot.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-cache.el b/elpa/pdf-tools-20191031.642/pdf-cache.el
new file mode 100644 (file)
index 0000000..9ed7241
--- /dev/null
@@ -0,0 +1,456 @@
+;;; pdf-cache.el --- Cache time-critical or frequent epdfinfo queries. -*- lexical-binding:t -*-
+
+;; Copyright (C) 2013  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, doc-view, pdf
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;;; Code:
+;;
+
+(require 'pdf-info)
+(require 'pdf-util)
+
+\f
+;; * ================================================================== *
+;; * Customiazations
+;; * ================================================================== *
+
+(defcustom pdf-cache-image-limit 64
+  "Maximum number of cached PNG images per buffer."
+  :type 'integer
+  :group 'pdf-cache
+  :group 'pdf-view)
+
+(defcustom pdf-cache-prefetch-delay 0.5
+  "Idle time in seconds before prefetching images starts."
+  :group 'pdf-view
+  :type 'number)
+
+(defcustom pdf-cache-prefetch-pages-function
+  'pdf-cache-prefetch-pages-function-default
+  "A function returning a list of pages to be prefetched.
+
+It is called with no arguments in the PDF window and should
+return a list of page-numbers, determining the pages that should
+be prefetched and their order."
+  :group 'pdf-view
+  :type 'function)
+
+\f
+;; * ================================================================== *
+;; * Simple Value cache
+;; * ================================================================== *
+
+(defvar-local pdf-cache--data nil)
+
+(defvar pdf-annot-modified-functions)
+
+(defun pdf-cache--initialize ()
+  (unless pdf-cache--data
+    (setq pdf-cache--data (make-hash-table))
+    (add-hook 'pdf-info-close-document-hook 'pdf-cache-clear-data nil t)
+    (add-hook 'pdf-annot-modified-functions
+              'pdf-cache--clear-data-of-annotations
+              nil t)))
+
+(defun pdf-cache--clear-data-of-annotations (fn)
+  (apply 'pdf-cache-clear-data-of-pages
+         (mapcar (lambda (a)
+                   (cdr (assq 'page a)))
+                 (funcall fn t))))
+
+(defun pdf-cache--data-put (key value &optional page)
+  "Put KEY with VALUE in the cache of PAGE, return value."
+  (pdf-cache--initialize)
+  (puthash page (cons (cons key value)
+                      (assq-delete-all
+                       key
+                       (gethash page pdf-cache--data)))
+           pdf-cache--data)
+  value)
+
+(defun pdf-cache--data-get (key &optional page)
+  "Get value of KEY in the cache of PAGE.
+
+Returns a cons \(HIT . VALUE\), where HIT is non-nil if KEY was
+stored previously for PAGE and VALUE it's value.  Otherwise HIT
+is nil and VALUE undefined."
+  (pdf-cache--initialize)
+  (let ((elt (assq key (gethash page pdf-cache--data))))
+    (if elt
+        (cons t (cdr elt))
+      (cons nil nil))))
+
+(defun pdf-cache--data-clear (key &optional page)
+  (pdf-cache--initialize)
+  (puthash page
+           (assq-delete-all key (gethash page pdf-cache--data))
+           pdf-cache--data)
+  nil)
+
+(defun pdf-cache-clear-data-of-pages (&rest pages)
+  (when pdf-cache--data
+    (dolist (page pages)
+      (remhash page pdf-cache--data))))
+
+(defun pdf-cache-clear-data ()
+  (interactive)
+  (when pdf-cache--data
+    (clrhash pdf-cache--data)))
+
+(defmacro define-pdf-cache-function (command &optional page-arg-p)
+  "Define a simple data cache function.
+
+COMMAND is the name of the command, e.g. number-of-pages.  It
+should have a corresponding pdf-info function.  If PAGE-ARG-P is
+non-nil, define a one-dimensional cache indexed by the page
+number. Otherwise the value is constant for each document, like
+e.g. number-of-pages.
+
+Both args are unevaluated."
+
+  (let ((args (if page-arg-p (list 'page)))
+        (fn (intern (format "pdf-cache-%s" command)))
+        (ifn (intern (format "pdf-info-%s" command)))
+        (doc (format "Cached version of `pdf-info-%s', which see.
+
+Make sure, not to modify it's return value." command)))
+    `(defun ,fn ,args
+       ,doc
+       (let ((hit-value (pdf-cache--data-get ',command ,(if page-arg-p 'page))))
+         (if (car hit-value)
+             (cdr hit-value)
+           (pdf-cache--data-put
+            ',command
+            ,(if page-arg-p
+                 (list ifn 'page)
+               (list ifn))
+            ,(if page-arg-p 'page)))))))
+
+(define-pdf-cache-function pagelinks t)
+(define-pdf-cache-function number-of-pages)
+;; The boundingbox may change if annotations change.
+(define-pdf-cache-function boundingbox t)
+(define-pdf-cache-function textregions t)
+(define-pdf-cache-function pagesize t)
+
+\f
+;; * ================================================================== *
+;; * PNG image LRU cache
+;; * ================================================================== *
+
+(defvar pdf-cache-image-inihibit nil
+  "Non-nil, if the image cache should be bypassed.")
+
+(defvar-local pdf-cache--image-cache nil)
+
+(defmacro pdf-cache--make-image (page width data hash)
+  `(list ,page ,width ,data ,hash))
+(defmacro pdf-cache--image/page (img) `(nth 0 ,img))
+(defmacro pdf-cache--image/width (img) `(nth 1 ,img))
+(defmacro pdf-cache--image/data (img) `(nth 2 ,img))
+(defmacro pdf-cache--image/hash (img) `(nth 3 ,img))
+
+(defun pdf-cache--image-match (image page min-width &optional max-width hash)
+  "Match IMAGE with specs.
+
+IMAGE should be a list as created by `pdf-cache--make-image'.
+
+Return non-nil, if IMAGE's page is the same as PAGE, it's width
+is at least MIN-WIDTH and at most MAX-WIDTH and it's stored
+hash-value is `eql' to HASH."
+  (and (= (pdf-cache--image/page image)
+          page)
+       (or (null min-width)
+           (>= (pdf-cache--image/width image)
+               min-width))
+       (or (null max-width)
+           (<= (pdf-cache--image/width image)
+               max-width))
+       (eql (pdf-cache--image/hash image)
+            hash)))
+
+(defun pdf-cache-lookup-image (page min-width &optional max-width hash)
+  "Return PAGE's cached PNG data as a string or nil.
+
+Does not modify the cache.  See also `pdf-cache-get-image'."
+  (let ((image (car (cl-member
+                     (list page min-width max-width hash)
+                     pdf-cache--image-cache
+                     :test (lambda (spec image)
+                             (apply 'pdf-cache--image-match image spec))))))
+    (and image
+         (pdf-cache--image/data image))))
+
+(defun pdf-cache-get-image (page min-width &optional max-width hash)
+  "Return PAGE's PNG data as a string.
+
+Return an image of at least MIN-WIDTH and, if non-nil, maximum
+width MAX-WIDTH and `eql' hash value.
+
+Remember that image was recently used.
+
+Returns nil, if no matching image was found."
+  (let ((cache pdf-cache--image-cache)
+        image)
+    ;; Find it in the cache.
+    (while (and (setq image (pop cache))
+                (not (pdf-cache--image-match
+                      image page min-width max-width hash))))
+    ;; Remove it and push it to the front.
+    (when image
+      (setq pdf-cache--image-cache
+            (cons image (delq image pdf-cache--image-cache)))
+      (pdf-cache--image/data image))))
+
+(defun pdf-cache-put-image (page width data &optional hash)
+  "Cache image of PAGE with WIDTH, DATA and HASH.
+
+DATA should the string of a PNG image of width WIDTH and from
+page PAGE in the current buffer.  See `pdf-cache-get-image' for
+the HASH argument.
+
+This function always returns nil."
+  (unless pdf-cache--image-cache
+    (add-hook 'pdf-info-close-document-hook 'pdf-cache-clear-images nil t)
+    (add-hook 'pdf-annot-modified-functions
+              'pdf-cache--clear-images-of-annotations nil t))
+  (push (pdf-cache--make-image page width data hash)
+        pdf-cache--image-cache)
+  ;; Forget old image(s).
+  (when (> (length pdf-cache--image-cache)
+           pdf-cache-image-limit)
+    (if (> pdf-cache-image-limit 1)
+        (setcdr (nthcdr (1- pdf-cache-image-limit)
+                        pdf-cache--image-cache)
+                nil)
+      (setq pdf-cache--image-cache nil)))
+  nil)
+
+(defun pdf-cache-clear-images ()
+  "Clear the image cache."
+  (setq pdf-cache--image-cache nil))
+
+(defun pdf-cache-clear-images-if (fn)
+  "Remove images from the cache according to FN.
+
+FN should be function accepting 4 Arguments \(PAGE WIDTH DATA
+HASH\).  It should return non-nil, if the image should be removed
+from the cache."
+  (setq pdf-cache--image-cache
+        (cl-remove-if
+         (lambda (image)
+           (funcall
+            fn
+            (pdf-cache--image/page image)
+            (pdf-cache--image/width image)
+            (pdf-cache--image/data image)
+            (pdf-cache--image/hash image)))
+         pdf-cache--image-cache)))
+
+
+(defun pdf-cache--clear-images-of-annotations (fn)
+  (apply 'pdf-cache-clear-images-of-pages
+         (mapcar (lambda (a)
+                   (cdr (assq 'page a)))
+                 (funcall fn t))))
+
+(defun pdf-cache-clear-images-of-pages (&rest pages)
+  (pdf-cache-clear-images-if
+   (lambda (page &rest _) (memq page pages))))
+
+(defun pdf-cache-renderpage (page min-width &optional max-width)
+  "Render PAGE according to MIN-WIDTH and MAX-WIDTH.
+
+Return the PNG data of an image as a string, such that it's width
+is at least MIN-WIDTH and, if non-nil, at most MAX-WIDTH.
+
+If such an image is not available in the cache, call
+`pdf-info-renderpage' to create one."
+  (if pdf-cache-image-inihibit
+      (pdf-info-renderpage page min-width)
+    (or (pdf-cache-get-image page min-width max-width)
+        (let ((data (pdf-info-renderpage page min-width)))
+          (pdf-cache-put-image page min-width data)
+          data))))
+
+(defun pdf-cache-renderpage-text-regions (page width single-line-p
+                                               &rest selection)
+  "Render PAGE according to WIDTH, SINGLE-LINE-P and SELECTION.
+
+See also `pdf-info-renderpage-text-regions' and
+`pdf-cache-renderpage'."
+  (if pdf-cache-image-inihibit
+      (apply 'pdf-info-renderpage-text-regions
+             page width single-line-p nil selection)
+    (let ((hash (sxhash
+                 (format "%S" (cons 'renderpage-text-regions
+                                    (cons single-line-p selection))))))
+      (or (pdf-cache-get-image page width width hash)
+          (let ((data (apply 'pdf-info-renderpage-text-regions
+                             page width single-line-p nil selection)))
+            (pdf-cache-put-image page width data hash)
+            data)))))
+
+(defun pdf-cache-renderpage-highlight (page width &rest regions)
+  "Highlight PAGE according to WIDTH and REGIONS.
+
+See also `pdf-info-renderpage-highlight' and
+`pdf-cache-renderpage'."
+  (if pdf-cache-image-inihibit
+      (apply 'pdf-info-renderpage-highlight
+             page width nil regions)
+    (let ((hash (sxhash
+                 (format "%S" (cons 'renderpage-highlight
+                                    regions)))))
+      (or (pdf-cache-get-image page width width hash)
+          (let ((data (apply 'pdf-info-renderpage-highlight
+                             page width nil regions)))
+            (pdf-cache-put-image page width data hash)
+            data)))))
+
+\f
+;; * ================================================================== *
+;; * Prefetching images
+;; * ================================================================== *
+
+(defvar-local pdf-cache--prefetch-pages nil
+  "Pages to be prefetched.")
+
+(defvar-local pdf-cache--prefetch-timer nil
+  "Timer used when prefetching images.")
+
+(define-minor-mode pdf-cache-prefetch-minor-mode
+  "Try to load images which will probably be needed in a while."
+  nil nil nil
+  (pdf-cache--prefetch-cancel)
+  (cond
+   (pdf-cache-prefetch-minor-mode
+    (pdf-util-assert-pdf-buffer)
+    (add-hook 'pre-command-hook 'pdf-cache--prefetch-stop nil t)
+    ;; FIXME: Disable the time when the buffer is killed or it's
+    ;; major-mode changes.
+    (setq pdf-cache--prefetch-timer
+          (run-with-idle-timer (or pdf-cache-prefetch-delay 1)
+              t 'pdf-cache--prefetch-start (current-buffer))))
+   (t
+    (remove-hook 'pre-command-hook 'pdf-cache--prefetch-stop t))))
+
+(defun pdf-cache-prefetch-pages-function-default ()
+  (let ((page (pdf-view-current-page)))
+    (pdf-util-remove-duplicates
+     (cl-remove-if-not
+      (lambda (page)
+        (and (>= page 1)
+             (<= page (pdf-cache-number-of-pages))))
+      (append
+       ;; +1, -1, +2, -2, ...
+       (let ((sign 1)
+             (incr 1))
+         (mapcar (lambda (_)
+                   (setq page (+ page (* sign incr))
+                         sign (- sign)
+                         incr (1+ incr))
+                   page)
+                 (number-sequence 1 16)))
+       ;; First and last
+       (list 1 (pdf-cache-number-of-pages))
+       ;; Links
+       (mapcar
+        (apply-partially 'alist-get 'page)
+        (cl-remove-if-not
+         (lambda (link) (eq (alist-get 'type link) 'goto-dest))
+         (pdf-cache-pagelinks
+          (pdf-view-current-page)))))))))
+
+(defun pdf-cache--prefetch-pages (window image-width)
+  (when (and (eq window (selected-window))
+             (pdf-util-pdf-buffer-p))
+    (let ((page (pop pdf-cache--prefetch-pages)))
+      (while (and page
+                  (pdf-cache-lookup-image
+                   page
+                   image-width
+                   (if (not (pdf-view-use-scaling-p))
+                       image-width
+                     (* 2 image-width))))
+        (setq page (pop pdf-cache--prefetch-pages)))
+      (pdf-util-debug
+        (when (null page)
+          (message  "Prefetching done.")))
+      (when page
+        (let* ((buffer (current-buffer))
+               (pdf-info-asynchronous
+                (lambda (status data)
+                  (when (and (null status)
+                             (eq window
+                                 (selected-window))
+                             (eq buffer (window-buffer)))
+                    (with-current-buffer (window-buffer)
+                      (when (derived-mode-p 'pdf-view-mode)
+                        (pdf-cache-put-image
+                         page image-width data)
+                        (image-size (pdf-view-create-page page))
+                        (pdf-util-debug
+                          (message "Prefetched page %s." page))
+                        ;; Avoid max-lisp-eval-depth
+                        (run-with-timer
+                            0.001 nil 'pdf-cache--prefetch-pages window image-width)))))))
+          (condition-case err
+              (pdf-info-renderpage page image-width)
+            (error
+             (pdf-cache-prefetch-minor-mode -1)
+             (signal (car err) (cdr err)))))))))
+
+(defvar pdf-cache--prefetch-started-p nil
+  "Guard against multiple prefetch starts.
+
+Used solely in `pdf-cache--prefetch-start'.")
+
+(defun pdf-cache--prefetch-start (buffer)
+  "Start prefetching images in BUFFER."
+  (when (and pdf-cache-prefetch-minor-mode
+             (not pdf-cache--prefetch-started-p)
+             (pdf-util-pdf-buffer-p)
+             (not isearch-mode)
+             (null pdf-cache--prefetch-pages)
+             (eq (window-buffer) buffer)
+             (fboundp pdf-cache-prefetch-pages-function))
+    (let* ((pdf-cache--prefetch-started-p t)
+           (pages (funcall pdf-cache-prefetch-pages-function)))
+      (setq pdf-cache--prefetch-pages
+            (butlast pages (max 0 (- (length pages)
+                                     pdf-cache-image-limit))))
+      (pdf-cache--prefetch-pages
+       (selected-window)
+       (car (pdf-view-desired-image-size))))))
+
+(defun pdf-cache--prefetch-stop ()
+  "Stop prefetching images in current buffer."
+  (setq pdf-cache--prefetch-pages nil))
+
+(defun pdf-cache--prefetch-cancel ()
+  "Cancel prefetching images in current buffer."
+  (pdf-cache--prefetch-stop)
+  (when pdf-cache--prefetch-timer
+    (cancel-timer pdf-cache--prefetch-timer))
+  (setq pdf-cache--prefetch-timer nil))
+
+(provide 'pdf-cache)
+;;; pdf-cache.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-dev.el b/elpa/pdf-tools-20191031.642/pdf-dev.el
new file mode 100644 (file)
index 0000000..6273b3e
--- /dev/null
@@ -0,0 +1,85 @@
+;;; pdf-dev.el --- Mother's little development helper  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2015  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMhochschule-trier.de>
+;; Keywords:
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; This file is only ment for developers.  The entry point is
+;; pdf-dev-minor-mode, which see.
+
+;;; Code:
+
+(defvar pdf-dev-root-directory
+  (file-name-directory
+   (directory-file-name
+    (file-name-directory load-file-name))))
+
+(defun pdf-dev-reload ()
+  "Reload lisp files from source."
+  (interactive)
+  (let ((default-directory (expand-file-name
+                            "lisp"
+                            pdf-dev-root-directory))
+        loaded)
+    (dolist (file (directory-files default-directory nil "\\`pdf-\\w*\\.el\\'"))
+      (push file loaded)
+      (load-file file))
+    (message "Loaded %s" (mapconcat 'identity loaded " "))))
+
+(define-minor-mode pdf-dev-minor-mode
+  "Make developing pdf-tools easier.
+
+It does the following:
+
+Quits the server and sets `pdf-info-epdfinfo-program' to
+../server/epdfinfo.
+
+Installs a `compilation-finish-functions' which will restart
+epdfinfo after a successful recompilation.
+
+Sets up `load-path' and reloads all PDF Tools lisp files."
+  nil nil nil
+  (let ((lisp-dir (expand-file-name "lisp" pdf-dev-root-directory)))
+    (setq load-path (remove lisp-dir load-path))
+    (cond
+     (pdf-dev-minor-mode
+      (add-hook 'compilation-finish-functions 'pdf-dev-compilation-finished)
+      (add-to-list 'load-path lisp-dir)
+      (setq pdf-info-epdfinfo-program
+            (expand-file-name
+             "epdfinfo"
+             (expand-file-name "server" pdf-dev-root-directory)))
+      (pdf-info-quit)
+      (pdf-dev-reload))
+     (t
+      (remove-hook 'compilation-finish-functions 'pdf-dev-compilation-finished)))))
+
+(defun pdf-dev-compilation-finished (buffer status)
+  (with-current-buffer buffer
+    (when (and (equal status "finished\n")
+               (file-equal-p
+                (expand-file-name "server" pdf-dev-root-directory)
+                default-directory))
+      (message "Restarting epdfinfo server")
+      (pdf-info-quit)
+      (let ((pdf-info-restart-process-p t))
+        (pdf-info-process-assert-running)))))
+
+(provide 'pdf-dev)
+;;; pdf-dev.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-history.el b/elpa/pdf-tools-20191031.642/pdf-history.el
new file mode 100644 (file)
index 0000000..433d7e3
--- /dev/null
@@ -0,0 +1,170 @@
+;;; pdf-history.el --- A simple stack-based history in PDF buffers. -*- lexical-binding: t -*-
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, multimedia
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+(require 'pdf-view)
+(require 'pdf-util)
+
+;;; Code:
+
+(defgroup pdf-history nil
+  "A simple stack-based history."
+  :group 'pdf-tools)
+
+(defvar-local pdf-history-stack nil
+  "The stack of history items.")
+
+(defvar-local pdf-history-index nil
+  "The current index into the `pdf-history-stack'.")
+
+(defvar pdf-history-minor-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (define-key kmap (kbd "B") 'pdf-history-backward)
+    (define-key kmap (kbd "N") 'pdf-history-forward)
+    kmap)
+  "Keymap used in `pdf-history-minor-mode'.")
+
+;;;###autoload
+(define-minor-mode pdf-history-minor-mode
+  "Keep a history of previously visited pages.
+
+This is a simple stack-based history.  Turning the page or
+following a link pushes the left-behind page on the stack, which
+may be navigated with the following keys.
+
+\\{pdf-history-minor-mode-map}"
+  nil nil nil
+  (pdf-util-assert-pdf-buffer)
+  (pdf-history-clear)
+  (cond
+   (pdf-history-minor-mode
+    (pdf-history-push)
+    (add-hook 'pdf-view-after-change-page-hook
+              'pdf-history-before-change-page-hook nil t))
+   (t
+    (remove-hook 'pdf-view-after-change-page-hook
+                 'pdf-history-before-change-page-hook t))))
+
+(defun pdf-history-before-change-page-hook ()
+  "Push a history item, before leaving this page."
+  (when (and pdf-history-minor-mode
+             (not (bound-and-true-p pdf-isearch-active-mode))
+             (pdf-view-current-page))
+    (pdf-history-push)))
+
+(defun pdf-history-push ()
+  "Push the current page on the stack.
+
+This function does nothing, if current stack item already
+represents the current page."
+  (interactive)
+  (let ((item (pdf-history-create-item)))
+    (unless (and pdf-history-stack
+                 (equal (nth pdf-history-index
+                             pdf-history-stack) item))
+      (setq pdf-history-stack
+            (last pdf-history-stack
+                  (- (length pdf-history-stack)
+                     pdf-history-index))
+            pdf-history-index 0)
+      (push item pdf-history-stack))))
+
+(defun pdf-history-clear ()
+  "Remove all history items."
+  (interactive)
+  (setq pdf-history-stack nil
+        pdf-history-index 0)
+  (pdf-history-push))
+
+(defun pdf-history-create-item ()
+  "Create a history item representing the current page."
+  (list
+   (pdf-view-current-page)))
+
+(defun pdf-history-beginning-of-history-p ()
+  "Return t, if at the beginning of the history."
+  (= pdf-history-index 0))
+
+(defun pdf-history-end-of-history-p ()
+  "Return t, if at the end of the history."
+  (= pdf-history-index
+     (1- (length pdf-history-stack))))
+
+(defun pdf-history-backward (n)
+  "Go N-times backward in the history."
+  (interactive "p")
+  (cond
+   ((and (> n 0)
+         (pdf-history-end-of-history-p))
+    (error "End of history"))
+   ((and (< n 0)
+         (pdf-history-beginning-of-history-p))
+    (error "Beginning of history"))
+   ((/= n 0)
+    (let ((i (min (max 0 (+ pdf-history-index n))
+                  (1- (length pdf-history-stack)))))
+      (prog1
+          (- (+ pdf-history-index n) i)
+        (pdf-history-goto i))))
+   (t 0)))
+
+(defun pdf-history-forward (n)
+  "Go N-times forward in the history."
+  (interactive "p")
+  (pdf-history-backward (- n)))
+
+(defun pdf-history-goto (n)
+  "Go to item N in the history."
+  (interactive "p")
+  (when (null pdf-history-stack)
+    (error "The history is empty"))
+  (cond
+   ((>= n (length pdf-history-stack))
+    (error "End of history"))
+   ((< n 0)
+    (error "Beginning of history"))
+   (t
+    (setq pdf-history-index n)
+    (pdf-view-goto-page
+     (car (nth n pdf-history-stack))))))
+
+(defun pdf-history-debug ()
+  "Visualize the history in the header-line."
+  (interactive)
+  (setq header-line-format
+        '(:eval
+          (let ((pages (mapcar 'car pdf-history-stack))
+                (index pdf-history-index)
+                header)
+            (dotimes (i (length pages))
+              (push (propertize
+                     (format "%s" (nth i pages))
+                     'face
+                     (and (= i index) 'match))
+                    header))
+            (concat
+             "(" (format "%d" index) ")  "
+             (mapconcat 'identity (nreverse header) " | "))))))
+
+(provide 'pdf-history)
+
+;;; pdf-history.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-info.el b/elpa/pdf-tools-20191031.642/pdf-info.el
new file mode 100644 (file)
index 0000000..6dd7ff6
--- /dev/null
@@ -0,0 +1,1744 @@
+;;; pdf-info.el --- Extract info from pdf-files via a helper process. -*- lexical-binding: t -*-
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, multimedia
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; This library represents the Lisp side of the epdfinfo server.  This
+;; program works on a command/response basis, but there should be no
+;; need to understand the protocol, since every command has a
+;; corresponding Lisp-function (see below under `High level
+;; interface').
+;;
+;; Most of these functions receive a file-or-buffer argument, which
+;; may be what it says and defaults to the current buffer.  Also, most
+;; functions return some sort of alist, with, in most cases,
+;; straight-forward key-value-pairs.  Though some may be only
+;; understandable in the context of Adobe's PDF spec \(Adobe
+;; PDF32000\) or the poppler documentation (e.g. annotation flags).
+;;
+;; If the poppler library is fairly recent (>= 0.19.4, older versions
+;; have a bug, which may corrupt the document), annotations maybe
+;; modified to a certain degree, deleted and text-annotations created.
+;; The state of these modifications is held in the server.  In order
+;; to realize, annotations retrieved or created are referenced by a
+;; unique symbol.  Saving these changes creates a new file, the
+;; original document is never touched.
+
+;;; Todo:
+;;
+;; + Close documents at some time (e.g. when the buffer is killed)
+;;
+
+;;; Code:
+
+(require 'tq)
+(require 'cl-lib)
+
+
+\f
+;; * ================================================================== *
+;; * Customizations
+;; * ================================================================== *
+
+(defgroup pdf-info nil
+  "Extract infos from pdf-files via a helper process."
+  :group 'pdf-tools)
+
+(defcustom pdf-info-epdfinfo-program
+  (let ((executable (if (eq system-type 'windows-nt)
+                        "epdfinfo.exe" "epdfinfo"))
+        (default-directory
+          (or (and load-file-name
+                   (file-name-directory load-file-name))
+              default-directory)))
+    (cl-labels ((try-directory (directory)
+                  (and (file-directory-p directory)
+                       (file-executable-p (expand-file-name executable directory))
+                       (expand-file-name executable directory))))
+      (or (executable-find executable)
+          ;; This works if epdfinfo is in the same place as emacs and
+          ;; the editor was started with an absolute path, i.e. it is
+          ;; ment for Windows/Msys2.
+          (and (stringp (car-safe command-line-args))
+               (file-name-directory (car command-line-args))
+               (try-directory
+                (file-name-directory (car command-line-args))))
+          ;; If we are running directly from the git repo.
+          (try-directory (expand-file-name "../server"))
+          ;; Fall back to epdfinfo in the directory of this file.
+          (expand-file-name executable))))
+  "Filename of the epdfinfo executable."
+  :group 'pdf-info
+  :type 'file)
+
+(defcustom pdf-info-epdfinfo-error-filename nil
+  "Filename for error output of the epdfinfo executable.
+
+If nil, discard any error messages.  Useful for debugging."
+  :group 'pdf-info
+  :type `(choice (const :tag "None" nil)
+                 ,@(when (file-directory-p "/tmp/")
+                     '((const "/tmp/epdfinfo.log")))
+                 (file)))
+
+(defcustom pdf-info-log nil
+  "Whether to log the communication with the server.
+
+If this is non-nil, all communication with the epdfinfo program
+will be logged to the buffer \"*pdf-info-log*\"."
+  :group 'pdf-info
+  :type 'boolean)
+
+(defcustom pdf-info-log-entry-max 512
+  "Maximum number of characters in a single log entry.
+
+This variable has no effect if `pdf-info-log' is nil."
+  :group 'pdf-info
+  :type 'integer)
+
+(defcustom pdf-info-restart-process-p 'ask
+  "What to do when the epdfinfo server died.
+
+This should be one of
+nil -- do nothing,
+t   -- automatically restart it or
+ask -- ask whether to restart or not.
+
+If it is `ask', the server quits and you answer no, this variable
+is set to nil."
+  :group 'pdf-info
+  :type '(choice (const :tag "Do nothing" nil)
+                 (const :tag "Restart silently" t)
+                 (const :tag "Always ask" ask)))
+
+(defcustom pdf-info-close-document-hook nil
+  "A hook ran after a document was closed in the server.
+
+The hook is run in the documents buffer, if it exists. Otherwise
+in a `with-temp-buffer' form."
+  :group 'pdf-info
+  :type 'hook)
+
+
+\f
+;; * ================================================================== *
+;; * Variables
+;; * ================================================================== *
+
+(defvar pdf-info-asynchronous nil
+  "If non-nil process queries asynchronously.
+
+More specifically the value should be a function of at 2
+arguments \(fn STATUS RESPONSE\), where STATUS is either nil, for
+a successful query, or the symbol error.  RESPONSE is either the
+command's response or the error message.  This does not work
+recursive, i.e. if function wants to make another asynchronous
+query it has to rebind this variable.
+
+Alternatively it may be a list \(FN . ARGS\), in which case FN
+will be invoked like \(apply FN STATUS RESPONSE ARGS\).
+
+Also, all pdf-info functions normally returning a response return
+nil.
+
+This variable should only be let-bound.")
+
+(defconst pdf-info-pdf-date-regexp
+  ;; Adobe PDF32000.book, 7.9.4 Dates
+  (eval-when-compile
+    (concat
+     ;; allow for preceding garbage
+     ;;"\\`"
+     "[dD]:"
+     "\\([0-9]\\{4\\}\\)"          ;year
+     "\\(?:"
+     "\\([0-9]\\{2\\}\\)"          ;month
+     "\\(?:"
+     "\\([0-9]\\{2\\}\\)"          ;day
+     "\\(?:"
+     "\\([0-9]\\{2\\}\\)"          ;hour
+     "\\(?:"
+     "\\([0-9]\\{2\\}\\)"          ;minutes
+     "\\(?:"
+     "\\([0-9]\\{2\\}\\)"          ;seconds
+     "\\)?\\)?\\)?\\)?\\)?"
+     "\\(?:"
+     "\\([+-Zz]\\)"                ;UT delta char
+     "\\(?:"
+     "\\([0-9]\\{2\\}\\)"          ;UT delta hours
+     "\\(?:"
+     "'"
+     "\\([0-9]\\{2\\}\\)"          ;UT delta minutes
+     "\\)?\\)?\\)?"
+     ;; "\\'"
+     ;; allow for trailing garbage
+     )))
+
+(defvar pdf-info--queue t
+  "Internally used transmission-queue for the server.
+
+This variable is initially `t', telling the code starting the
+server, that it never ran.")
+
+\f
+;; * ================================================================== *
+;; * Process handling
+;; * ================================================================== *
+
+(defconst pdf-info-empty-page-data
+  (eval-when-compile
+    (concat
+     "%PDF-1.0\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0"
+     " obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</"
+     "Type/Page/MediaBox[0 0 3 3]>>endobj\nxref\n0 4\n00000000"
+     "0065535 f\n0000000010 00000 n\n0000000053 00000 n\n00000"
+     "00102 00000 n\ntrailer<</Size 4/Root 1 0 R>>\nstartxref\n149\n%EOF"))
+  "PDF data of an empty page.")
+
+(defun pdf-info-process ()
+  "Return the process object or nil."
+  (and pdf-info--queue
+       (not (eq t pdf-info--queue))
+       (tq-process pdf-info--queue)))
+
+(defun pdf-info-check-epdfinfo (&optional interactive-p)
+  "Check if the server should be working properly.
+
+Signal an error if some problem was found.  Message a
+confirmation, if INTERACTIVE-P is non-nil and no problems were
+found.
+
+Returns nil."
+  (interactive "p")
+  (let ((executable pdf-info-epdfinfo-program))
+    (unless (stringp executable)
+      (error "pdf-info-epdfinfo-program is unset or not a string"))
+    (unless (file-executable-p executable)
+      (error "pdf-info-epdfinfo-program is not executable"))
+    (when pdf-info-epdfinfo-error-filename
+      (unless (and (stringp pdf-info-epdfinfo-error-filename)
+                   (file-writable-p pdf-info-epdfinfo-error-filename))
+        (error "pdf-info-epdfinfo-error-filename should contain writable filename")))
+    (let* ((default-directory (expand-file-name "~/"))
+           (cmdfile (make-temp-file "commands"))
+           (pdffile (make-temp-file "empty.pdf"))
+           (tempdir (make-temp-file "tmpdir" t))
+           (process-environment (cons (concat "TMPDIR=" tempdir)
+                                      process-environment)))
+      (unwind-protect
+          (with-temp-buffer
+            (with-temp-file pdffile
+              (set-buffer-multibyte nil)
+              (insert pdf-info-empty-page-data))
+            (with-temp-file cmdfile
+              (insert (format "renderpage:%s:1:100\nquit\n"
+                              (pdf-info-query--escape pdffile))))
+            (unless (= 0 (apply #'call-process
+                                executable cmdfile (current-buffer)
+                                nil (when pdf-info-epdfinfo-error-filename
+                                      (list pdf-info-epdfinfo-error-filename))))
+              (error "Error running `%s': %s"
+                     pdf-info-epdfinfo-program
+                     (buffer-string))))
+        (when (file-exists-p cmdfile)
+          (delete-file cmdfile))
+        (when (file-exists-p pdffile)
+          (delete-file pdffile))
+        (when (file-exists-p tempdir)
+          (delete-directory tempdir t)))))
+  (when interactive-p
+    (message "The epdfinfo program appears to be working."))
+  nil)
+
+(defun pdf-info-process-assert-running (&optional force)
+  "Assert a running process.
+
+If it never ran, i.e. `pdf-info-process' is t, start it
+unconditionally.
+
+Otherwise, if FORCE is non-nil start it, if it is not running.
+Else restart it with respect to the variable
+`pdf-info-restart-process-p', which see.
+
+If getting the process to run fails, this function throws an
+error."
+  (interactive "P")
+  (unless (and (processp (pdf-info-process))
+               (eq (process-status (pdf-info-process))
+                   'run))
+    (when (pdf-info-process)
+      (tq-close pdf-info--queue)
+      (setq pdf-info--queue nil))
+    (unless (or force
+                (eq pdf-info--queue t)
+                (and (eq pdf-info-restart-process-p 'ask)
+                     (not noninteractive)
+                     (y-or-n-p "The epdfinfo server quit, restart it ? "))
+                (and pdf-info-restart-process-p
+                     (not (eq pdf-info-restart-process-p 'ask))))
+
+      (when (eq pdf-info-restart-process-p 'ask)
+        (setq pdf-info-restart-process-p nil))
+      (error "The epdfinfo server quit"))
+    (pdf-info-check-epdfinfo)
+    (let* ((process-connection-type)    ;Avoid 4096 Byte bug #12440.
+           (default-directory "~")
+           (proc (apply #'start-process
+                        "epdfinfo" " *epdfinfo*" pdf-info-epdfinfo-program
+                        (when pdf-info-epdfinfo-error-filename
+                          (list pdf-info-epdfinfo-error-filename)))))
+      (with-current-buffer " *epdfinfo*"
+        (erase-buffer))
+      (set-process-query-on-exit-flag proc nil)
+      (set-process-coding-system proc 'utf-8-unix 'utf-8-unix)
+      (setq pdf-info--queue (tq-create proc))))
+  pdf-info--queue)
+
+(defadvice tq-process-buffer (around bugfix activate)
+  "Fix a bug in trunk where the wrong callback gets called."
+  ;; FIXME: Make me iterative.
+  (let ((tq (ad-get-arg 0)))
+    (if (not (equal (car (process-command (tq-process tq)))
+                    pdf-info-epdfinfo-program))
+        ad-do-it
+      (let ((buffer (tq-buffer tq))
+            done)
+        (when (buffer-live-p buffer)
+          (set-buffer buffer)
+          (while (and (not done)
+                      (> (buffer-size) 0))
+            (setq done t)
+            (if (tq-queue-empty tq)
+                (let ((buf (generate-new-buffer "*spurious*")))
+                  (copy-to-buffer buf (point-min) (point-max))
+                  (delete-region (point-min) (point))
+                  (pop-to-buffer buf nil)
+                  (error "Spurious communication from process %s, see buffer %s"
+                         (process-name (tq-process tq))
+                         (buffer-name buf)))
+              (goto-char (point-min))
+              (when (re-search-forward (tq-queue-head-regexp tq) nil t)
+                (setq done nil)
+                (let ((answer (buffer-substring (point-min) (point)))
+                      (fn (tq-queue-head-fn tq))
+                      (closure (tq-queue-head-closure tq)))
+                  (delete-region (point-min) (point))
+                  (tq-queue-pop tq)
+                  (condition-case-unless-debug err
+                      (funcall fn closure answer)
+                    (error
+                     (message "Error while processing tq callback: %s"
+                              (error-message-string err)))))))))))))
+
+\f
+;; * ================================================================== *
+;; * Sending and receiving
+;; * ================================================================== *
+
+(defun pdf-info-query (cmd &rest args)
+  "Query the server using CMD and ARGS."
+  (pdf-info-process-assert-running)
+  (unless (symbolp cmd)
+    (setq cmd (intern cmd)))
+  (let* ((query (concat (mapconcat 'pdf-info-query--escape
+                                   (cons cmd args) ":") "\n"))
+         (callback
+          (lambda (closure response)
+            (cl-destructuring-bind (status &rest result)
+                (pdf-info-query--parse-response cmd response)
+              (pdf-info-query--log response)
+              (let* (pdf-info-asynchronous)
+                (if (functionp closure)
+                    (funcall closure status result)
+                  (apply (car closure) status result (cdr closure)))))))
+         response status done
+         (closure (or pdf-info-asynchronous
+                      (lambda (s r)
+                        (setq status s response r done t)))))
+    (pdf-info-query--log query t)
+    (tq-enqueue
+     pdf-info--queue query "^\\.\n" closure callback)
+    (unless pdf-info-asynchronous
+      (while (and (not done)
+                  (eq (process-status (pdf-info-process))
+                      'run))
+        (accept-process-output (pdf-info-process) 0.01))
+      (when (and (not done)
+                 (not (eq (process-status (pdf-info-process))
+                          'run))
+                 (not (eq cmd 'quit)))
+        (error "The epdfinfo server quit unexpectedly."))
+      (cond
+       ((null status) response)
+       ((eq status 'error)
+        (error "epdfinfo: %s" response))
+       ((eq status 'interrupted)
+        (error "epdfinfo: Command was interrupted"))
+       (t
+        (error "internal error: invalid response status"))))))
+
+(defun pdf-info-interrupt ()
+  "FIXME: This command does currently nothing."
+  (when (and (processp (pdf-info-process))
+             (eq (process-status (pdf-info-process))
+                 'run))
+    (signal-process (pdf-info-process) 'SIGUSR1)))
+
+(defun pdf-info-query--escape (arg)
+  "Escape ARG for transmission to the server."
+  (if (null arg)
+      (string)
+    (with-current-buffer (get-buffer-create " *pdf-info-query--escape*")
+      (erase-buffer)
+      (insert (format "%s" arg))
+      (goto-char 1)
+      (while (not (eobp))
+        (cond
+         ((memq (char-after) '(?\\ ?:))
+          (insert ?\\))
+         ((eq (char-after) ?\n)
+          (delete-char 1)
+          (insert ?\\ ?n)
+          (backward-char)))
+        (forward-char))
+      (buffer-substring-no-properties 1 (point-max)))))
+
+(defmacro pdf-info-query--read-record ()
+  "Read a single record of the response in current buffer."
+  `(let (records done (beg (point)))
+     (while (not done)
+       (cl-case (char-after)
+         (?\\
+          (delete-char 1)
+          (if (not (eq (char-after) ?n))
+              (forward-char)
+            (delete-char 1)
+            (insert ?\n)))
+         ((?: ?\n)
+          (push (buffer-substring-no-properties
+                 beg (point)) records)
+          (forward-char)
+          (setq beg (point)
+                done (bolp)))
+         (t (forward-char))))
+     (nreverse records)))
+
+(defun pdf-info-query--parse-response (cmd response)
+  "Parse one epdfinfo RESPONSE to CMD.
+
+Returns a cons \(STATUS . RESULT\), where STATUS is one of nil
+for a regular response, error for an error \(RESULT contains the
+error message\) or interrupted, i.e. the command was
+interrupted."
+  (with-current-buffer
+      (get-buffer-create " *pdf-info-query--parse-response*")
+    (erase-buffer)
+    (insert response)
+    (goto-char 1)
+    (cond
+     ((looking-at "ERR\n")
+      (forward-line)
+      (cons 'error (buffer-substring-no-properties
+                    (point)
+                    (progn
+                      (re-search-forward "^\\.\n")
+                      (1- (match-beginning 0))))))
+     ((looking-at "OK\n")
+      (let (result)
+        (forward-line)
+        (while (not (and (= (char-after) ?.)
+                         (= (char-after (1+ (point))) ?\n)))
+          (push (pdf-info-query--read-record) result))
+        (cons nil (pdf-info-query--transform-response
+                   cmd (nreverse result)))))
+     ((looking-at "INT\n")
+      (cons 'interrupted nil))
+     (t
+      (cons 'error "Invalid server response")))))
+
+(defun pdf-info-query--transform-response (cmd response)
+  "Transform a RESPONSE to CMD into a Lisp form."
+  (cl-case cmd
+    (open nil)
+    (close (equal "1" (caar response)))
+    (number-of-pages (string-to-number (caar response)))
+    (charlayout
+     (mapcar (lambda (elt)
+               (cl-assert (= 1 (length (cadr elt))) t)
+               `(,(aref (cadr elt) 0)
+                 ,(mapcar 'string-to-number
+                          (split-string (car elt) " " t))))
+             response))
+    (regexp-flags
+     (mapcar (lambda (elt)
+               (cons (intern (car elt))
+                     (string-to-number (cadr elt))))
+             response))
+    ((search-string search-regexp)
+     (mapcar
+      (lambda (r)
+        `((page . ,(string-to-number (nth 0 r)))
+          (text . ,(let (case-fold-search)
+                     (pdf-util-highlight-regexp-in-string
+                      (regexp-quote (nth 1 r)) (nth 2 r))))
+          (edges . ,(mapcar (lambda (m)
+                              (mapcar 'string-to-number
+                                      (split-string m " " t)))
+                            (cddr (cdr r))))))
+      response))
+    (outline
+     (mapcar (lambda (r)
+               `((depth . ,(string-to-number (pop r)))
+                 ,@(pdf-info-query--transform-action r)))
+             response))
+    (pagelinks
+     (mapcar (lambda (r)
+               `((edges .
+                        ,(mapcar 'string-to-number ;area
+                                 (split-string (pop r) " " t)))
+                 ,@(pdf-info-query--transform-action r)))
+             response))
+    (metadata
+     (let ((md (car response)))
+       (if (= 1 (length md))
+           (list (cons 'title (car md)))
+         (list
+          (cons 'title (pop md))
+          (cons 'author (pop md))
+          (cons 'subject (pop md))
+          (cons 'keywords-raw (car md))
+          (cons 'keywords (split-string (pop md) "[\t\n ]*,[\t\n ]*" t))
+          (cons 'creator (pop md))
+          (cons 'producer (pop md))
+          (cons 'format (pop md))
+          (cons 'created (pop md))
+          (cons 'modified (pop md))))))
+    (gettext
+     (or (caar response) ""))
+    (getselection
+     (mapcar (lambda (line)
+               (mapcar 'string-to-number
+                       (split-string (car line) " " t)))
+             response))
+    (features (mapcar 'intern (car response)))
+    (pagesize
+     (setq response (car response))
+     (cons (round (string-to-number (car response)))
+           (round (string-to-number (cadr response)))))
+    ((getannot editannot addannot)
+     (pdf-info-query--transform-annotation (car response)))
+    (getannots
+     (mapcar 'pdf-info-query--transform-annotation response))
+    (getattachments
+     (mapcar 'pdf-info-query--transform-attachment response))
+    ((getattachment-from-annot)
+     (pdf-info-query--transform-attachment (car response)))
+    (boundingbox
+     (mapcar 'string-to-number (car response)))
+    (synctex-forward-search
+     (let ((list (mapcar 'string-to-number (car response))))
+       `((page . ,(car list))
+         (edges . ,(cdr list)))))
+    (synctex-backward-search
+     `((filename . ,(caar response))
+       (line . ,(string-to-number (cadr (car response))))
+       (column . ,(string-to-number (cadr (cdar response))))))
+    (delannot nil)
+    ((save) (caar response))
+    ((renderpage renderpage-text-regions renderpage-highlight)
+     (pdf-util-munch-file (caar response)))
+    ((setoptions getoptions)
+     (let (options)
+       (dolist (key-value response)
+         (let ((key (intern (car key-value)))
+               (value (cadr key-value)))
+           (cl-case key
+             ((:render/printed :render/usecolors)
+              (setq value (equal value "1"))))
+           (push value options)
+           (push key options)))
+       options))
+    (pagelabels (mapcar 'car response))
+    (ping (caar response))
+    (t response)))
+
+
+(defun pdf-info-query--transform-action (action)
+  "Transform ACTION response into a Lisp form."
+  (let ((type (intern (pop action))))
+    `((type . ,type)
+      (title . ,(pop action))
+      ,@(cl-case type
+          (goto-dest
+           `((page . ,(string-to-number (pop action)))
+             (top . ,(and (> (length (car action)) 0)
+                          (string-to-number (pop action))))))
+          (goto-remote
+           `((filename . ,(pop action))
+             (page . ,(string-to-number (pop action)))
+             (top . ,(and (> (length (car action)) 0)
+                          (string-to-number (pop action))))))
+          (t `((uri . ,(pop action))))))))
+
+(defun pdf-info-query--transform-annotation (a)
+  (cl-labels ((not-empty (s)
+                (if (not (equal s "")) s)))
+    (let (a1 a2 a3)
+      (cl-destructuring-bind (page edges type id flags color contents modified &rest rest)
+          a
+        (setq a1 `((page . ,(string-to-number page))
+                   (edges . ,(mapcar 'string-to-number
+                                     (split-string edges " " t)))
+                   (type . ,(intern type))
+                   (id . ,(intern id))
+                   (flags . ,(string-to-number flags))
+                   (color . ,(not-empty color))
+                   (contents . ,contents)
+                   (modified . ,(pdf-info-parse-pdf-date modified))))
+        (when rest
+          (cl-destructuring-bind (label subject opacity popup-edges popup-is-open created
+                                        &rest rest)
+              rest
+            (setq a2
+                  `((label . ,(not-empty label))
+                    (subject . ,(not-empty subject))
+                    (opacity . ,(let ((o (not-empty opacity)))
+                                  (and o (string-to-number o))))
+                    (popup-edges . ,(let ((p (not-empty popup-edges)))
+                                      (when p
+                                        (mapcar 'string-to-number
+                                                (split-string p " " t)))))
+                    (popup-is-open . ,(equal popup-is-open "1"))
+                    (created . ,(pdf-info-parse-pdf-date (not-empty created)))))
+            (cond
+             ((eq (cdr (assoc 'type a1)) 'text)
+              (cl-destructuring-bind (icon state is-open)
+                  rest
+                (setq a3
+                      `((icon . ,(not-empty icon))
+                        (state . ,(not-empty state))
+                        (is-open . ,(equal is-open "1"))))))
+             ((memq (cdr (assoc 'type a1))
+                    '(squiggly highlight underline strike-out))
+              (setq a3 `((markup-edges
+                          . ,(mapcar (lambda (r)
+                                       (mapcar 'string-to-number
+                                               (split-string r " " t)))
+                                     rest)))))))))
+      (append a1 a2 a3))))
+
+(defun pdf-info-query--transform-attachment (a)
+  (cl-labels ((not-empty (s)
+                (if (not (equal s "")) s)))
+    (cl-destructuring-bind (id filename description size modified
+                               created checksum file)
+        a
+      `((id . ,(intern id))
+        (filename . ,(not-empty filename))
+        (description . ,(not-empty description))
+        (size . ,(let ((n (string-to-number size)))
+                   (and (>= n 0) n)))
+        (modified . ,(not-empty modified))
+        (created . ,(not-empty created))
+        (checksum . ,(not-empty checksum))
+        (file . ,(not-empty file))))))
+
+(defun pdf-info-query--log (string &optional query-p)
+  "Log STRING as query/response, depending on QUERY-P.
+
+This is a no-op, if `pdf-info-log' is nil."
+  (when pdf-info-log
+    (with-current-buffer (get-buffer-create "*pdf-info-log*")
+      (buffer-disable-undo)
+      (let ((pos (point-max))
+            (window (get-buffer-window)))
+        (save-excursion
+          (goto-char (point-max))
+          (unless (bolp)
+            (insert ?\n))
+          (insert
+           (propertize
+            (format-time-string "%H:%M:%S ")
+            'face
+            (if query-p
+                'font-lock-keyword-face
+              'font-lock-function-name-face))
+           (if (and (numberp pdf-info-log-entry-max)
+                    (> (length string)
+                       pdf-info-log-entry-max))
+               (concat (substring string 0 pdf-info-log-entry-max)
+                       "...[truncated]\n")
+             string)))
+        (when (and (window-live-p window)
+                   (= pos (window-point window)))
+          (set-window-point window (point-max)))))))
+
+
+\f
+;; * ================================================================== *
+;; * Utility functions
+;; * ================================================================== *
+
+(defvar doc-view-buffer-file-name)
+(defvar doc-view--buffer-file-name)
+
+(defun pdf-info--normalize-file-or-buffer (file-or-buffer)
+  "Return the PDF file corresponding to FILE-OR-BUFFER.
+
+FILE-OR-BUFFER may be nil, a PDF buffer, the name of a PDF buffer
+or a PDF file."
+  (unless file-or-buffer
+    (setq file-or-buffer
+          (current-buffer)))
+  (when (bufferp file-or-buffer)
+    (unless (buffer-live-p file-or-buffer)
+      (error "Buffer is not live :%s" file-or-buffer))
+    (with-current-buffer file-or-buffer
+      (unless (setq file-or-buffer
+                    (cl-case major-mode
+                      (doc-view-mode
+                       (cond ((boundp 'doc-view-buffer-file-name)
+                              doc-view-buffer-file-name)
+                             ((boundp 'doc-view--buffer-file-name)
+                              doc-view--buffer-file-name)))
+                      (pdf-view-mode (pdf-view-buffer-file-name))
+                      (t (buffer-file-name))))
+        (error "Buffer is not associated with any file :%s" (buffer-name)))))
+  (unless (stringp file-or-buffer)
+    (signal 'wrong-type-argument
+            (list 'stringp 'bufferp 'null file-or-buffer)))
+  ;; is file
+  (when (file-remote-p file-or-buffer)
+    (error "Processing remote files not supported :%s"
+           file-or-buffer))
+  ;; (unless (file-readable-p file-or-buffer)
+  ;;   (error "File not readable :%s" file-or-buffer))
+  (expand-file-name file-or-buffer))
+
+(defun pdf-info-valid-page-spec-p (pages)
+  "The type predicate for a valid page-spec."
+  (not (not (ignore-errors (pdf-info-normalize-page-range pages)))))
+
+(defun pdf-info-normalize-page-range (pages)
+  "Normalize PAGES for sending to the server.
+
+PAGES may be a single page number, a cons \(FIRST . LAST\), or
+nil, which stands for all pages.
+
+The result is a cons \(FIRST . LAST\), where LAST may be 0
+representing the final page."
+  (cond
+   ((natnump pages)
+    (cons pages pages))
+   ((null pages)
+    (cons 1 0))
+   ((and (natnump (car pages))
+         (natnump (cdr pages)))
+    pages)
+   (t
+    (signal 'wrong-type-argument
+            (list 'pdf-info-valid-page-spec-p pages)))))
+
+(defun pdf-info-parse-pdf-date (date)
+  (when (and date
+             (string-match pdf-info-pdf-date-regexp date))
+    (let ((year (match-string 1 date))
+          (month (match-string 2 date))
+          (day (match-string 3 date))
+          (hour (match-string 4 date))
+          (min (match-string 5 date))
+          (sec (match-string 6 date))
+          (ut-char (match-string 7 date))
+          (ut-hour (match-string 8 date))
+          (ut-min (match-string 9 date))
+          (tz 0))
+      (when (or (equal ut-char "+")
+                (equal ut-char "-"))
+        (when ut-hour
+          (setq tz (* 3600 (string-to-number ut-hour))))
+        (when ut-min
+          (setq tz (+ tz (* 60 (string-to-number ut-min)))))
+        (when (equal ut-char "-")
+          (setq tz (- tz))))
+      (encode-time
+       (if sec (string-to-number sec) 0)
+       (if min (string-to-number min) 0)
+       (if hour (string-to-number hour) 0)
+       (if day (string-to-number day) 1)
+       (if month (string-to-number month) 1)
+       (string-to-number year)
+       tz))))
+
+(defmacro pdf-info-compose-queries (let-forms &rest body)
+  "Let-bind each VAR to QUERIES results and evaluate BODY.
+
+All queries in each QUERIES form are run by the server in the
+order they appear and the results collected in a list, which is
+bound to VAR.  Then BODY is evaluated and its value becomes the
+final result of all queries, unless at least one of them provoked
+an error.  In this case BODY is ignored and the error is the
+result.
+
+This macro handles synchronous and asynchronous calls,
+i.e. `pdf-info-asynchronous' is non-nil, transparently.
+
+\(FN \(\(VAR QUERIES\)...\) BODY\)"
+  (declare (indent 1)
+           (debug ((&rest &or
+                          (symbolp &optional form)
+                          symbolp)
+                   body)))
+  (unless (cl-every (lambda (form)
+                      (when (symbolp form)
+                        (setq form (list form)))
+                      (and (consp form)
+                           (symbolp (car form))
+                           (listp (cdr form))))
+                    let-forms)
+    (error "Invalid let-form: %s" let-forms))
+
+  (setq let-forms (mapcar (lambda (form)
+                            (if (symbolp form)
+                                (list form)
+                              form))
+                          let-forms))
+  (let* ((status (make-symbol "status"))
+         (response (make-symbol "response"))
+         (first-error (make-symbol "first-error"))
+         (done (make-symbol "done"))
+         (callback (make-symbol "callback"))
+         (results (make-symbol "results"))
+         (push-fn (make-symbol "push-fn"))
+         (terminal-fn (make-symbol "terminal-fn"))
+         (buffer (make-symbol "buffer")))
+    `(let* (,status
+            ,response ,first-error ,done
+            (,buffer (current-buffer))
+            (,callback pdf-info-asynchronous)
+            ;; Ensure a new alist on every invocation.
+            (,results (mapcar 'copy-sequence
+                              ',(cl-mapcar (lambda (form)
+                                             (list (car form)))
+                                           let-forms)))
+            (,push-fn (lambda (status result var)
+                        ;; Store result in alist RESULTS under key
+                        ;; VAR.
+                        (if status
+                            (unless ,first-error
+                              (setq ,first-error result))
+                          (let ((elt (assq var ,results)))
+                            (setcdr elt (append (cdr elt)
+                                                (list result)))))))
+            (,terminal-fn
+             (lambda (&rest _)
+               ;; Let-bind responses corresponding to their variables,
+               ;; i.e. keys in alist RESULTS.
+               (let (,@(mapcar (lambda (var)
+                                 (list var (list 'cdr (list 'assq (list 'quote var)
+                                                            results))))
+                               (mapcar 'car let-forms)))
+                 (setq ,status (not (not ,first-error))
+                       ,response (or ,first-error
+                                     (with-current-buffer ,buffer
+                                       ,@body))
+                       ,done t)
+                 ;; Maybe invoke the CALLBACK (which was bound to
+                 ;; pdf-info-asynchronous).
+                 (when ,callback
+                   (if (functionp ,callback)
+                       (funcall ,callback ,status ,response)
+                     (apply (car ,callback)
+                       ,status ,response (cdr ,callback))))))))
+       ;; Wrap each query in an asynchronous call, with its VAR as
+       ;; callback argument, so the PUSH-FN can put it in the alist
+       ;; RESULTS.
+       ,@(mapcar (lambda (form)
+                   (list 'let (list
+                               (list 'pdf-info-asynchronous
+                                     (list 'list push-fn (list 'quote (car form)))))
+                         (cadr form)))
+                 let-forms)
+       ;; Request a no-op, just so we know that we are finished.
+       (let ((pdf-info-asynchronous ,terminal-fn))
+         (pdf-info-ping))
+       ;; CALLBACK is the original value of pdf-info-asynchronous.  If
+       ;; nil, this is a synchronous query.
+       (unless ,callback
+         (while (and (not ,done)
+                     (eq (process-status (pdf-info-process))
+                         'run))
+           (accept-process-output (pdf-info-process) 0.01))
+         (when (and (not ,done)
+                    (not (eq (process-status (pdf-info-process))
+                             'run)))
+           (error "The epdfinfo server quit unexpectedly."))
+         (when ,status
+           (error "epdfinfo: %s" ,response))
+         ,response))))
+
+\f
+;; * ================================================================== *
+;; * Buffer local server instances
+;; * ================================================================== *
+
+(put 'pdf-info--queue 'permanent-local t)
+
+(defun pdf-info-make-local-server (&optional buffer force-restart-p)
+  "Create a server instance local to BUFFER.
+
+Does nothing if BUFFER already has a local instance.  Unless
+FORCE-RESTART-P is non-nil, then quit a potential process and
+restart it."
+  (unless buffer
+    (setq buffer (current-buffer)))
+  (with-current-buffer buffer
+    (unless (and
+             (not force-restart-p)
+             (local-variable-p 'pdf-info--queue)
+             (processp (pdf-info-process))
+             (eq (process-status (pdf-info-process))
+                 'run))
+      (when (and (local-variable-p 'pdf-info--queue)
+                 (processp (pdf-info-process)))
+        (tq-close pdf-info--queue))
+      (set (make-local-variable 'pdf-info--queue) nil)
+      (pdf-info-process-assert-running t)
+      (add-hook 'kill-buffer-hook 'pdf-info-kill-local-server nil t)
+      pdf-info--queue)))
+
+(defun pdf-info-kill-local-server (&optional buffer)
+  "Kill the local server in BUFFER.
+
+A No-op, if BUFFER has not running server instance."
+  (save-current-buffer
+    (when buffer
+      (set-buffer buffer))
+    (when (local-variable-p 'pdf-info--queue)
+      (pdf-info-kill)
+      (kill-local-variable 'pdf-info--queue)
+      t)))
+
+(defun pdf-info-local-server-p (&optional buffer)
+  "Return non-nil, if BUFFER has a running server instance."
+  (unless buffer
+    (setq buffer (current-buffer)))
+  (setq buffer (get-buffer buffer))
+  (and (buffer-live-p buffer)
+       (local-variable-p 'pdf-info--queue buffer)))
+
+(defun pdf-info-local-batch-query (producer-fn
+                                   consumer-fn
+                                   sentinel-fn
+                                   args)
+  "Process a set of queries asynchronously in a local instance."
+  (unless (pdf-info-local-server-p)
+    (error "Create a local server first"))
+  (let* ((buffer (current-buffer))
+         (producer-symbol (make-symbol "producer"))
+         (consumer-symbol (make-symbol "consumer"))
+         (producer
+          (lambda (args)
+            (if (null args)
+                (funcall sentinel-fn 'finished buffer)
+              (let ((pdf-info-asynchronous
+                     (apply-partially
+                      (symbol-function consumer-symbol)
+                      args)))
+                (cond
+                 ((pdf-info-local-server-p buffer)
+                  (with-current-buffer buffer
+                    (apply producer-fn (car args))))
+                 (t
+                  (funcall sentinel-fn 'error buffer)))))))
+         (consumer (lambda (args status result)
+                     (if (not (pdf-info-local-server-p buffer))
+                         (funcall sentinel-fn 'error buffer)
+                       (with-current-buffer buffer
+                         (apply consumer-fn status result (car args)))
+                       (funcall (symbol-function producer-symbol)
+                                (cdr args))))))
+    (fset producer-symbol producer)
+    (fset consumer-symbol consumer)
+    (funcall producer args)))
+
+
+\f
+;; * ================================================================== *
+;; * High level interface
+;; * ================================================================== *
+
+(defvar pdf-info-features nil)
+
+(defun pdf-info-features ()
+  "Return a list of symbols describing compile-time features."
+  (or pdf-info-features
+      (setq pdf-info-features
+            (let (pdf-info-asynchronous)
+              (pdf-info-query 'features)))))
+
+(defun pdf-info-writable-annotations-p ()
+  (not (null (memq 'writable-annotations (pdf-info-features)))))
+
+(defun pdf-info-markup-annotations-p ()
+  (not (null (memq 'markup-annotations (pdf-info-features)))))
+
+(defmacro pdf-info-assert-writable-annotations ()
+  `(unless (memq 'writable-annotations (pdf-info-features))
+     (error "Writing annotations is not supported by this version of epdfinfo")))
+
+(defmacro pdf-info-assert-markup-annotations ()
+  `(unless (memq 'markup-annotations (pdf-info-features))
+     (error "Creating markup annotations is not supported by this version of epdfinfo")))
+
+(defun pdf-info-creatable-annotation-types ()
+  (let ((features (pdf-info-features)))
+    (cond
+     ((not (memq 'writable-annotations features)) nil)
+     ((memq 'markup-annotations features)
+      (list 'text 'squiggly 'underline 'strike-out 'highlight))
+     (t (list 'text)))))
+
+(defun pdf-info-open (&optional file-or-buffer password)
+  "Open the document FILE-OR-BUFFER using PASSWORD.
+
+Generally, documents are opened and closed automatically on
+demand, so this function is rarely needed, unless a PASSWORD is
+set on the document.
+
+Manually opened documents are never closed automatically."
+
+  (pdf-info-query
+   'open (pdf-info--normalize-file-or-buffer file-or-buffer)
+   password))
+
+(defun pdf-info-close (&optional file-or-buffer)
+  "Close the document FILE-OR-BUFFER.
+
+Returns t, if the document was actually open, otherwise nil.
+This command is rarely needed, see also `pdf-info-open'."
+  (let* ((pdf (pdf-info--normalize-file-or-buffer file-or-buffer))
+         (buffer (find-buffer-visiting pdf)))
+    (prog1
+        (pdf-info-query 'close pdf)
+      (if (buffer-live-p buffer)
+          (with-current-buffer buffer
+            (run-hooks 'pdf-info-close-document-hook))
+        (with-temp-buffer
+          (run-hooks 'pdf-info-close-document-hook))))))
+
+(defun pdf-info-encrypted-p (&optional file-or-buffer)
+  "Return non-nil if FILE-OR-BUFFER requires a password.
+
+Note: This function returns nil, if the document is encrypted,
+but was already opened (presumably using a password)."
+
+  (condition-case err
+      (pdf-info-open
+       (pdf-info--normalize-file-or-buffer file-or-buffer))
+    (error (or (string-match-p
+                ":Document is encrypted\\'" (cadr err))
+               (signal (car err) (cdr err))))))
+
+(defun pdf-info-metadata (&optional file-or-buffer)
+  "Extract the metadata from the document FILE-OR-BUFFER.
+
+This returns an alist containing some information about the
+document."
+  (pdf-info-query
+   'metadata
+   (pdf-info--normalize-file-or-buffer file-or-buffer)))
+
+(defun pdf-info-search-string (string &optional pages file-or-buffer)
+  "Search for STRING in PAGES of document FILE-OR-BUFFER.
+
+See `pdf-info-normalize-page-range' for valid PAGES formats.
+
+This function returns a list of matches.  Each item is an alist
+containing keys PAGE, TEXT and EDGES, where PAGE and TEXT are the
+matched page resp. line. EDGES is a list containing a single
+edges element \(LEFT TOP RIGHT BOTTOM\). This is for consistency
+with `pdf-info-search-regexp', which may return matches with
+multiple edges.
+
+The TEXT contains `match' face properties on the matched parts.
+
+Search is case-insensitive, unless `case-fold-search' is nil and
+searching case-sensitive is supported by the server."
+
+  (let ((pages (pdf-info-normalize-page-range pages)))
+    (pdf-info-query
+     'search-string
+     (pdf-info--normalize-file-or-buffer file-or-buffer)
+     (car pages)
+     (cdr pages)
+     string
+     (if case-fold-search 1 0))))
+
+(defvar pdf-info-regexp-compile-flags nil
+  "PCRE compile flags.
+
+Don't use this, but the equally named function.")
+
+(defvar pdf-info-regexp-match-flags nil
+  "PCRE match flags.
+
+Don't use this, but the equally named function.")
+
+(defun pdf-info-regexp-compile-flags ()
+  (or pdf-info-regexp-compile-flags
+      (let* (pdf-info-asynchronous
+             (flags (pdf-info-query 'regexp-flags))
+             (match (cl-remove-if-not
+                     (lambda (flag)
+                       (string-match-p
+                        "\\`match-" (symbol-name (car flag))))
+                     flags))
+             (compile (cl-set-difference flags match)))
+        (setq pdf-info-regexp-compile-flags compile
+              pdf-info-regexp-match-flags match)
+        pdf-info-regexp-compile-flags)))
+
+(defun pdf-info-regexp-match-flags ()
+  (or pdf-info-regexp-match-flags
+      (progn
+        (pdf-info-regexp-compile-flags)
+        pdf-info-regexp-match-flags)))
+
+(defvar pdf-info-regexp-flags '(multiline)
+  "Compile- and match-flags for the PCRE engine.
+
+This is a list of symbols denoting compile- and match-flags when
+searching for regular expressions.
+
+You should not change this directly, but rather `let'-bind it
+around a call to `pdf-info-search-regexp'.
+
+Valid compile-flags are:
+
+newline-crlf, newline-lf, newline-cr, dupnames, optimize,
+no-auto-capture, raw, ungreedy, dollar-endonly, anchored,
+extended, dotall, multiline and caseless.
+
+Note that the last one, caseless, is handled special, as it is
+always added if `case-fold-search' is non-nil.
+
+And valid match-flags:
+
+match-anchored, match-notbol, match-noteol, match-notempty,
+match-partial, match-newline-cr, match-newline-lf,
+match-newline-crlf and match-newline-any.
+
+See the glib documentation at url
+`https://developer.gnome.org/glib/stable/glib-Perl-compatible-regular-expressions.html'.")
+
+(defun pdf-info-search-regexp (pcre &optional pages
+                                    no-error
+                                    file-or-buffer)
+  "Search for a PCRE on PAGES of document FILE-OR-BUFFER.
+
+See `pdf-info-normalize-page-range' for valid PAGES formats and
+`pdf-info-search-string' for its return value.
+
+Uses the flags in `pdf-info-regexp-flags', which see.  If
+`case-fold-search' is non-nil, the caseless flag is added.
+
+If NO-ERROR is non-nil, catch errors due to invalid regexps and
+return nil.  If it is the symbol `invalid-regexp', then re-signal
+this kind of error as a `invalid-regexp' error."
+
+  (cl-labels ((orflags (flags alist)
+                (cl-reduce
+                 (lambda (v flag)
+                   (let ((n
+                          (cdr (assq flag alist))))
+                     (if n (logior n v) v)))
+                 (cons 0 flags))))
+    (let ((pages (pdf-info-normalize-page-range pages)))
+      (condition-case err
+          (pdf-info-query
+           'search-regexp
+           (pdf-info--normalize-file-or-buffer file-or-buffer)
+           (car pages)
+           (cdr pages)
+           pcre
+           (orflags `(,(if case-fold-search
+                           'caseless)
+                      ,@pdf-info-regexp-flags)
+                    (pdf-info-regexp-compile-flags))
+           (orflags pdf-info-regexp-flags
+                    (pdf-info-regexp-match-flags)))
+        (error
+         (let ((re
+                (concat "\\`epdfinfo: *Invalid *regexp: *"
+                        ;; glib error
+                        "\\(?:Error while compiling regular expression"
+                        " *%s *\\)?\\(.*\\)")))
+           (if (or (null no-error)
+                   (not (string-match
+                         (format re (regexp-quote pcre))
+                         (cadr err))))
+               (signal (car err) (cdr err))
+             (if (eq no-error 'invalid-regexp)
+                 (signal 'invalid-regexp
+                         (list (match-string 1 (cadr err))))))))))))
+
+(defun pdf-info-pagelinks (page &optional file-or-buffer)
+  "Return a list of links on PAGE in document FILE-OR-BUFFER.
+
+This function returns a list of alists with the following keys.
+EDGES represents the relative bounding-box of the link , TYPE is
+the type of the action, TITLE is a, possibly empty, name for this
+action.
+
+TYPE may be one of
+
+goto-dest -- This is a internal link to some page.  Each element
+contains additional keys PAGE and TOP, where PAGE is the page of
+the link and TOP its vertical position.
+
+goto-remote -- This a external link to some document.  Same as
+goto-dest, with an additional FILENAME of the external PDF.
+
+uri -- A link in form of some URI. Alist contains additional key
+URI.
+
+In the first two cases, PAGE may be 0 and TOP nil, which means
+these data is unspecified."
+  (cl-check-type page natnum)
+  (pdf-info-query
+   'pagelinks
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   page))
+
+(defun pdf-info-number-of-pages (&optional file-or-buffer)
+  "Return the number of pages in document FILE-OR-BUFFER."
+  (pdf-info-query 'number-of-pages
+                  (pdf-info--normalize-file-or-buffer
+                   file-or-buffer)))
+
+(defun pdf-info-outline (&optional file-or-buffer)
+  "Return the PDF outline of document FILE-OR-BUFFER.
+
+This function returns a list of alists like `pdf-info-pagelinks'.
+Additionally every alist has a DEPTH (>= 1) entry with the depth
+of this element in the tree."
+
+  (pdf-info-query
+   'outline
+   (pdf-info--normalize-file-or-buffer file-or-buffer)))
+
+(defun pdf-info-gettext (page edges &optional selection-style
+                              file-or-buffer)
+  "Get text on PAGE according to EDGES.
+
+EDGES should contain relative coordinates.  The selection may
+extend over multiple lines, which works similar to a Emacs
+region. SELECTION-STYLE may be one of glyph, word or line and
+determines the smallest unit of the selected region.
+
+Return the text contained in the selection."
+
+  (pdf-info-query
+   'gettext
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   page
+   (mapconcat 'number-to-string edges " ")
+   (cl-case selection-style
+     (glyph 0)
+     (word 1)
+     (line 2)
+     (t 0))))
+
+(defun pdf-info-getselection (page edges &optional selection-style
+                                   file-or-buffer)
+  "Return the edges of the selection EDGES on PAGE.
+
+Arguments are the same as for `pdf-info-gettext'.  Return a list
+of edges corresponding to the text that would be returned by the
+aforementioned function, when called with the same arguments."
+
+  (pdf-info-query
+   'getselection
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   page
+   (mapconcat 'number-to-string edges " ")
+   (cl-case selection-style
+     (glyph 0)
+     (word 1)
+     (line 2)
+     (t 0))))
+
+(defun pdf-info-textregions (page &optional file-or-buffer)
+  "Return a list of edges describing PAGE's text-layout."
+  (pdf-info-getselection
+   page '(0 0 1 1) 'glyph file-or-buffer))
+
+(defun pdf-info-charlayout (page &optional edges-or-pos file-or-buffer)
+  "Return the layout of characters of PAGE in/at EDGES-OR-POS.
+
+Returns a list of elements \(CHAR . \(LEFT TOP RIGHT BOT\)\) mapping
+character to their corresponding relative bounding-boxes.
+
+EDGES-OR-POS may be a region \(LEFT TOP RIGHT BOT\) restricting
+the returned value to include only characters fully contained in
+it.  Or a cons \(LEFT . TOP\) which means to only include the
+character at this position.  In this case the return value
+contains at most one element."
+
+  ;; FIXME: Actually returns \(CHAR . LEFT ...\).
+
+  (unless edges-or-pos
+    (setq edges-or-pos '(0 0 1 1)))
+  (when (numberp (cdr edges-or-pos))
+    (setq edges-or-pos (list (car edges-or-pos)
+                             (cdr edges-or-pos)
+                             -1 -1)))
+  (pdf-info-query
+   'charlayout
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   page
+   (mapconcat 'number-to-string edges-or-pos " ")))
+
+(defun pdf-info-pagesize (page &optional file-or-buffer)
+  "Return the size of PAGE as a cons \(WIDTH . HEIGHT\)
+
+The size is in PDF points."
+  (pdf-info-query
+   'pagesize
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   page))
+
+(defun pdf-info-running-p ()
+  "Return non-nil, if the server is running."
+  (and (processp (pdf-info-process))
+       (eq (process-status (pdf-info-process))
+           'run)))
+
+(defun pdf-info-quit (&optional timeout)
+  "Quit the epdfinfo server.
+
+This blocks until all outstanding requests are answered.  Unless
+TIMEOUT is non-nil, in which case we wait at most TIMEOUT seconds
+before killing the server."
+  (cl-check-type timeout (or null number))
+  (when (pdf-info-running-p)
+    (let ((pdf-info-asynchronous
+           (if timeout (lambda (&rest _))
+             pdf-info-asynchronous)))
+      (pdf-info-query 'quit)
+      (when timeout
+        (setq timeout (+ (float-time) (max 0 timeout)))
+        (while (and (pdf-info-running-p)
+                    (> timeout (float-time)))
+          (accept-process-output (pdf-info-process) 0.5 nil t)))))
+  (when (processp (pdf-info-process))
+    (tq-close pdf-info--queue))
+  (setq pdf-info--queue nil))
+
+(defun pdf-info-kill ()
+  "Kill the epdfinfo server.
+
+Immediately delete the server process, see also `pdf-info-quit',
+for a more sane way to exit the program."
+  (when (processp (pdf-info-process))
+    (tq-close pdf-info--queue))
+  (setq pdf-info--queue nil))
+
+(defun pdf-info-getannots (&optional pages file-or-buffer)
+  "Return the annotations on PAGE.
+
+See `pdf-info-normalize-page-range' for valid PAGES formats.
+
+This function returns the annotations for PAGES as a list of
+alists.  Each element of this list describes one annotation and
+contains the following keys.
+
+page     - Its page number.
+edges    - Its area.
+type     - A symbol describing the annotation's type.
+id       - A document-wide unique symbol referencing this annotation.
+flags    - Its flags, binary encoded.
+color    - Its color in standard Emacs notation.
+contents - The text of this annotation.
+modified - The last modification date of this annotation.
+
+Additionally, if the annotation is a markup annotation, the
+following keys are present.
+
+label        - The annotation's label.
+subject      - The subject addressed.
+opacity      - The level of relative opacity.
+popup-edges  - The edges of a associated popup window or nil.
+popup-is-open - Whether this window should be displayed open.
+created      - The date this markup annotation was created.
+
+If the annotation is also a markup text annotation, the alist
+contains the following keys.
+
+text-icon  - A string describing the purpose of this annotation.
+text-state - A string, e.g. accepted or rejected." ;FIXME: Use symbols ?
+
+  (let ((pages (pdf-info-normalize-page-range pages)))
+    (pdf-info-query
+     'getannots
+     (pdf-info--normalize-file-or-buffer file-or-buffer)
+     (car pages)
+     (cdr pages))))
+
+(defun pdf-info-getannot (id &optional file-or-buffer)
+  "Return the annotation for ID.
+
+ID should be a symbol, which was previously returned in a
+`pdf-info-getannots' query.  Signal an error, if an annotation
+with ID is not available.
+
+See `pdf-info-getannots' for the kind of return value of this
+function."
+  (pdf-info-query
+   'getannot
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   id))
+
+(defun pdf-info-addannot (page edges type &optional file-or-buffer &rest markup-edges)
+  "Add a new annotation to PAGE with EDGES of TYPE.
+
+FIXME: TYPE may be one of `text', `markup-highlight', ... .
+FIXME: -1 = 24
+See `pdf-info-getannots' for the kind of value of this function
+returns."
+  (pdf-info-assert-writable-annotations)
+  (when (consp file-or-buffer)
+    (push file-or-buffer markup-edges)
+    (setq file-or-buffer nil))
+  (apply
+   'pdf-info-query
+   'addannot
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   page
+   type
+   (mapconcat 'number-to-string edges " ")
+   (mapcar (lambda (me)
+             (mapconcat 'number-to-string me " "))
+           markup-edges)))
+
+(defun pdf-info-delannot (id &optional file-or-buffer)
+  "Delete the annotation with ID in FILE-OR-BUFFER.
+
+ID should be a symbol, which was previously returned in a
+`pdf-info-getannots' query.  Signal an error, if annotation ID
+does not exist."
+  (pdf-info-assert-writable-annotations)
+  (pdf-info-query
+   'delannot
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   id))
+
+(defun pdf-info-mvannot (id edges &optional file-or-buffer)
+  "Move/Resize annotation ID to fit EDGES.
+
+ID should be a symbol, which was previously returned in a
+`pdf-info-getannots' query.  Signal an error, if annotation ID
+does not exist.
+
+EDGES should be a list \(LEFT TOP RIGHT BOT\).  RIGHT and/or BOT
+may also be negative, which means to keep the width
+resp. height."
+  (pdf-info-editannot id `((edges . ,edges)) file-or-buffer))
+
+(defun pdf-info-editannot (id modifications &optional file-or-buffer)
+  "Edit annotation ID, applying MODIFICATIONS.
+
+ID should be a symbol, which was previously returned in a
+`pdf-info-getannots' query.
+
+MODIFICATIONS is an alist of properties and their new values.
+
+The server must support modifying annotations for this to work."
+
+  (pdf-info-assert-writable-annotations)
+  (let ((edits
+         (mapcar
+          (lambda (elt)
+            (cl-case (car elt)
+              (color
+               (list (car elt)
+                     (pdf-util-hexcolor (cdr elt))))
+              (edges
+               (list (car elt)
+                     (mapconcat 'number-to-string (cdr elt) " ")))
+              ((popup-is-open is-open)
+               (list (car elt) (if (cdr elt) 1 0)))
+              (t
+               (list (car elt) (cdr elt)))))
+          modifications)))
+    (apply 'pdf-info-query
+           'editannot
+           (pdf-info--normalize-file-or-buffer file-or-buffer)
+           id
+           (apply 'append edits))))
+
+(defun pdf-info-save (&optional file-or-buffer)
+  "Save FILE-OR-BUFFER.
+
+This saves the document to a new temporary file, which is
+returned and owned by the caller."
+  (pdf-info-assert-writable-annotations)
+  (pdf-info-query
+   'save
+   (pdf-info--normalize-file-or-buffer file-or-buffer)))
+
+(defun pdf-info-getattachment-from-annot (id &optional do-save file-or-buffer)
+  "Return the attachment associated with annotation ID.
+
+ID should be a symbol which was previously returned in a
+`pdf-info-getannots' query, and referencing an attachment of type
+`file', otherwise an error is signaled.
+
+See `pdf-info-getattachments' for the kind of return value of this
+function and the meaning of DO-SAVE."
+
+  (pdf-info-query
+   'getattachment-from-annot
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   id
+   (if do-save 1 0)))
+
+(defun pdf-info-getattachments (&optional do-save file-or-buffer)
+  "Return all document level attachments.
+
+If DO-SAVE is non-nil, save the attachments data to a local file,
+which is then owned by the caller, see below.
+
+This function returns a list of alists, where every element
+contains the following keys.  All values, except for id, may be
+nil, i.e. not present.
+
+id          - A symbol uniquely identifying this attachment.
+filename    - The filename of this attachment.
+description - A description of this attachment.
+size        - The size in bytes.
+modified    - The last modification date.
+created     - The date of creation.
+checksum    - A MD5 checksum of this attachment's data.
+file        - The name of a tempfile containing the data (only present if
+              DO-SAVE is non-nil)."
+
+  (pdf-info-query
+   'getattachments
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   (if do-save 1 0)))
+
+(defun pdf-info-synctex-forward-search (source &optional line column file-or-buffer)
+  "Perform a forward search with synctex.
+
+SOURCE should be a LaTeX buffer or the absolute filename of a
+corresponding file.  LINE and COLUMN represent the position in
+the buffer or file.  Finally FILE-OR-BUFFER corresponds to the
+PDF document.
+
+Returns an alist with entries PAGE and relative EDGES describing
+the position in the PDF document corresponding to the SOURCE
+location."
+
+  (let ((source (if (buffer-live-p (get-buffer source))
+                    (buffer-file-name (get-buffer source))
+                  source)))
+    (pdf-info-query
+     'synctex-forward-search
+     (pdf-info--normalize-file-or-buffer file-or-buffer)
+     source
+     (or line 1)
+     (or column 1))))
+
+(defun pdf-info-synctex-backward-search (page &optional x y file-or-buffer)
+  "Perform a backward search with synctex.
+
+Find the source location corresponding to the coordinates
+\(X . Y\) on PAGE in FILE-OR-BUFFER.
+
+Returns an alist with entries FILENAME, LINE and COLUMN."
+
+
+  (pdf-info-query
+   'synctex-backward-search
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   page
+   (or x 0)
+   (or y 0)))
+
+(defun pdf-info-renderpage (page width &optional file-or-buffer &rest commands)
+  "Render PAGE with width WIDTH.
+
+Return the data of the corresponding PNG image."
+  (when (keywordp file-or-buffer)
+    (push file-or-buffer commands)
+    (setq file-or-buffer nil))
+  (apply 'pdf-info-query
+    'renderpage
+    (pdf-info--normalize-file-or-buffer file-or-buffer)
+    page
+    (* width (pdf-util-frame-scale-factor))
+    (let (transformed)
+      (while (cdr commands)
+        (let ((kw (pop commands))
+              (value (pop commands)))
+          (setq value
+                (cl-case kw
+                  ((:crop-to :highlight-line :highlight-region :highlight-text)
+                   (mapconcat 'number-to-string value " "))
+                  ((:foreground :background)
+                   (pdf-util-hexcolor value))
+                  (:alpha
+                   (number-to-string value))
+                  (otherwise value)))
+          (push kw transformed)
+          (push value transformed)))
+      (when commands
+        (error "Keyword is missing a value: %s" (car commands)))
+      (nreverse transformed))))
+
+(defun pdf-info-renderpage-text-regions (page width single-line-p
+                                              &optional file-or-buffer
+                                              &rest regions)
+  "Highlight text on PAGE with width WIDTH using REGIONS.
+
+REGIONS is a list determining foreground and background color and
+the regions to render. So each element should look like \(FG BG
+\(LEFT TOP RIGHT BOT\) \(LEFT TOP RIGHT BOT\) ... \) . The
+rendering is text-aware.
+
+If SINGLE-LINE-P is non-nil, the edges in REGIONS are each
+supposed to be limited to a single line in the document.  Setting
+this, if applicable, avoids rendering problems.
+
+For the other args see `pdf-info-renderpage'.
+
+Return the data of the corresponding PNG image."
+
+  (when (consp file-or-buffer)
+    (push file-or-buffer regions)
+    (setq file-or-buffer nil))
+
+  (apply 'pdf-info-renderpage
+    page width file-or-buffer
+    (apply 'append
+      (mapcar (lambda (elt)
+                `(:foreground ,(pop elt)
+                  :background ,(pop elt)
+                  ,@(cl-mapcan (lambda (edges)
+                                 `(,(if single-line-p
+                                        :highlight-line
+                                      :highlight-text)
+                                   ,edges))
+                               elt)))
+              regions))))
+
+(defun pdf-info-renderpage-highlight (page width
+                                           &optional file-or-buffer
+                                           &rest regions)
+  "Highlight regions on PAGE with width WIDTH using REGIONS.
+
+REGIONS is a list determining the background color, a alpha value
+and the regions to render. So each element should look like \(FILL-COLOR
+STROKE-COLOR ALPHA \(LEFT TOP RIGHT BOT\) \(LEFT TOP RIGHT BOT\) ... \)
+.
+
+For the other args see `pdf-info-renderpage'.
+
+Return the data of the corresponding PNG image."
+
+  (when (consp file-or-buffer)
+    (push file-or-buffer regions)
+    (setq file-or-buffer nil))
+
+  (apply 'pdf-info-renderpage
+    page width file-or-buffer
+    (apply 'append
+      (mapcar (lambda (elt)
+                `(:background ,(pop elt)
+                  :foreground ,(pop elt)
+                  :alpha ,(pop elt)
+                  ,@(cl-mapcan (lambda (edges)
+                                 `(:highlight-region ,edges))
+                               elt)))
+              regions))))
+
+(defun pdf-info-boundingbox (page &optional file-or-buffer)
+  "Return a bounding-box for PAGE.
+
+Returns a list \(LEFT TOP RIGHT BOT\)."
+
+  (pdf-info-query
+   'boundingbox
+   (pdf-info--normalize-file-or-buffer file-or-buffer)
+   page))
+
+(defun pdf-info-getoptions (&optional file-or-buffer)
+  (pdf-info-query
+   'getoptions
+   (pdf-info--normalize-file-or-buffer file-or-buffer)))
+
+(defun pdf-info-setoptions (&optional file-or-buffer &rest options)
+  (when (symbolp file-or-buffer)
+    (push file-or-buffer options)
+    (setq file-or-buffer nil))
+  (unless (= (% (length options) 2) 0)
+    (error "Missing a option value"))
+  (apply 'pdf-info-query
+    'setoptions
+    (pdf-info--normalize-file-or-buffer file-or-buffer)
+    (let (soptions)
+      (while options
+        (let ((key (pop options))
+              (value (pop options)))
+          (unless (and (keywordp key)
+                       (not (eq key :)))
+            (error "Keyword expected: %s" key))
+          (cl-case key
+            ((:render/foreground :render/background)
+             (push (pdf-util-hexcolor value)
+                   soptions))
+            ((:render/usecolors :render/printed)
+             (push (if value 1 0) soptions))
+            (t (push value soptions)))
+          (push key soptions)))
+      soptions)))
+
+
+
+(defun pdf-info-pagelabels (&optional file-or-buffer)
+  "Return a list of pagelabels.
+
+Returns a list of strings corresponding to the labels of the
+pages in FILE-OR-BUFFER."
+
+  (pdf-info-query
+   'pagelabels
+   (pdf-info--normalize-file-or-buffer file-or-buffer)))
+
+(defun pdf-info-ping (&optional message)
+  "Ping the server using MESSAGE.
+
+Returns MESSAGE, which defaults to \"pong\"."
+  (pdf-info-query 'ping (or message "pong")))
+
+(provide 'pdf-info)
+
+;;; pdf-info.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-isearch.el b/elpa/pdf-tools-20191031.642/pdf-isearch.el
new file mode 100644 (file)
index 0000000..563e721
--- /dev/null
@@ -0,0 +1,831 @@
+;;; pdf-isearch.el --- Isearch in pdf buffers. -*- lexical-binding: t -*-
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, multimedia
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;;; Todo:
+;;
+;; * Add the possibility to limit the search to a range of pages.
+
+(require 'cl-lib)
+(require 'pdf-util)
+(require 'pdf-info)
+(require 'pdf-misc)
+(require 'pdf-view)
+(require 'pdf-cache)
+(require 'let-alist)
+
+;;; Code:
+
+
+\f
+;; * ================================================================== *
+;; * Customizations
+;; * ================================================================== *
+
+(defgroup pdf-isearch nil
+  "Isearch in pdf buffers."
+  :group 'pdf-tools)
+
+(defface pdf-isearch-match
+  '((((background dark)) (:inherit isearch))
+    (((background light)) (:inherit isearch)))
+  "Face used to determine the colors of the current match."
+  :group 'pdf-isearch
+  :group 'pdf-tools-faces)
+
+(defface pdf-isearch-lazy
+  '((((background dark)) (:inherit lazy-highlight))
+    (((background light)) (:inherit lazy-highlight)))
+  "Face used to determine the colors of non-current matches."
+  :group 'pdf-isearch
+  :group 'pdf-tools-faces)
+
+(defface pdf-isearch-batch
+  '((((background dark)) (:inherit match))
+    (((background light)) (:inherit match)))
+  "Face used to determine the colors in `pdf-isearch-batch-mode'."
+  :group 'pdf-isearch
+  :group 'pdf-tools-faces)
+
+(defcustom pdf-isearch-hyphenation-character "-­"
+  "Characters used as hyphens when word searching."
+  :group 'pdf-isearch
+  :type 'string)
+
+(defvar pdf-isearch-search-fun-function nil
+  "Search function used when searching.
+
+Like `isearch-search-fun-function', though it should return a
+function \(FN STRING &optional PAGES\), which in turn should
+return a result like `pdf-info-search-regexp'.")
+
+\f
+;; * ================================================================== *
+;; * Internal Variables
+;; * ================================================================== *
+
+(defvar-local pdf-isearch-current-page nil
+  "The page that is currently searched.")
+
+(defvar-local pdf-isearch-current-match nil
+  "A list ((LEFT TOP RIGHT BOT) ...) of the current match or nil.
+
+A match may contain more than one edges-element, e.g. when regexp
+searching across multiple lines.")
+
+(defvar-local pdf-isearch-current-matches nil
+  "A list of matches of the last search.")
+
+(defvar-local pdf-isearch-current-parameter nil
+  "A list of search parameter \(search-string regex-p case-fold word-search\).")
+
+\f
+;; * ================================================================== *
+;; * Modes
+;; * ================================================================== *
+
+(declare-function pdf-occur "pdf-occur.el")
+(declare-function pdf-sync-backward-search "pdf-sync.el")
+
+(defvar pdf-isearch-minor-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (define-key kmap [remap occur] 'pdf-occur)
+    kmap)
+  "Keymap used in `pdf-isearch-minor-mode'.")
+
+(defvar pdf-isearch-active-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (set-keymap-parent kmap isearch-mode-map)
+    (define-key kmap (kbd "C-d") 'pdf-view-dark-minor-mode)
+    (define-key kmap (kbd "C-b") 'pdf-isearch-batch-mode)
+    (define-key kmap (kbd "M-s o") 'pdf-isearch-occur)
+    (define-key kmap (kbd "M-s s") 'pdf-isearch-sync-backward)
+    kmap)
+  "Keymap used in `pdf-isearch-active-mode'.
+
+This keymap is used, when isearching in PDF buffers.  Its parent
+keymap is `isearch-mode-map'.")
+
+(put 'image-scroll-up 'isearch-scroll t)
+(put 'image-scroll-down 'isearch-scroll t)
+
+(define-minor-mode pdf-isearch-active-mode "" nil nil nil
+  (cond
+   (pdf-isearch-active-mode
+    (set (make-local-variable 'isearch-mode-map)
+         pdf-isearch-active-mode-map)
+    (setq overriding-terminal-local-map
+          isearch-mode-map))
+   (t
+    ;;(setq overriding-terminal-local-map nil) ?
+    (kill-local-variable 'isearch-mode-map))))
+
+;;;###autoload
+(define-minor-mode pdf-isearch-minor-mode
+  "Isearch mode for PDF buffer.
+
+When this mode is enabled \\[isearch-forward], among other keys,
+starts an incremental search in this PDF document.  Since this mode
+uses external programs to highlight found matches via
+image-processing, proceeding to the next match may be slow.
+
+Therefore two isearch behaviours have been defined: Normal isearch and
+batch mode.  The later one is a minor mode
+\(`pdf-isearch-batch-mode'\), which when activated inhibits isearch
+from stopping at and highlighting every single match, but rather
+display them batch-wise.  Here a batch means a number of matches
+currently visible in the selected window.
+
+The kind of highlighting is determined by three faces
+`pdf-isearch-match' \(for the current match\), `pdf-isearch-lazy'
+\(for all other matches\) and `pdf-isearch-batch' \(when in batch
+mode\), which see.
+
+Colors may also be influenced by the minor-mode
+`pdf-view-dark-minor-mode'.  If this is minor mode enabled, each face's
+dark colors, are used (see e.g. `frame-background-mode'), instead
+of the light ones.
+
+\\{pdf-isearch-minor-mode-map}
+While in `isearch-mode' the following keys are available. Note
+that not every isearch command work as expected.
+
+\\{pdf-isearch-active-mode-map}"
+  :group 'pdf-isearch
+  (pdf-util-assert-pdf-buffer)
+  (cond
+   (pdf-isearch-minor-mode
+    (when (boundp 'character-fold-search)
+      (setq-local character-fold-search nil))
+    (set (make-local-variable 'isearch-search-fun-function)
+         (lambda nil 'pdf-isearch-search-function))
+    (set (make-local-variable 'isearch-push-state-function)
+         'pdf-isearch-push-state-function)
+    (set (make-local-variable 'isearch-wrap-function)
+         'pdf-isearch-wrap-function)
+    (set (make-local-variable 'isearch-lazy-highlight) nil)
+    ;; Make our commands work in isearch-mode.
+    (set (make-local-variable 'isearch-allow-scroll) t)
+    (set (make-local-variable 'search-exit-option)
+         ;; This maybe edit or t, but edit would suppress our cmds
+         ;; in isearch-other-meta-char.
+         (not (not search-exit-option)))
+    ;; FIXME: Die Variable imagemagick-render-type entweder an anderer
+    ;; Stelle global setzen oder nur irgendwo auf den
+    ;; Performancegewinn hinweisen.
+    (when (and (boundp 'imagemagick-render-type)
+               (= 0 imagemagick-render-type))
+      ;; This enormously speeds up rendering.
+      (setq imagemagick-render-type 1))
+    (add-hook 'isearch-mode-hook 'pdf-isearch-mode-initialize nil t)
+    (add-hook 'isearch-mode-end-hook 'pdf-isearch-mode-cleanup nil t)
+    (add-hook 'isearch-update-post-hook 'pdf-isearch-update nil t))
+   (t
+    (when (boundp 'character-fold-search)
+      (kill-local-variable 'character-fold-search))
+    (kill-local-variable 'search-exit-option)
+    (kill-local-variable 'isearch-allow-scroll)
+    (kill-local-variable 'isearch-search-fun-function)
+    (kill-local-variable 'isearch-push-state-function)
+    (kill-local-variable 'isearch-wrap-function)
+    (kill-local-variable 'isearch-lazy-highlight)
+    (remove-hook 'isearch-update-post-hook 'pdf-isearch-update t)
+    (remove-hook 'isearch-mode-hook 'pdf-isearch-mode-initialize t)
+    (remove-hook 'isearch-mode-end-hook 'pdf-isearch-mode-cleanup t))))
+
+(define-minor-mode pdf-isearch-batch-mode
+  "Isearch PDF documents batch-wise.
+
+If this mode is enabled, isearching does not stop at every match,
+but rather moves to the next one not currently visible.  This
+behaviour is much faster than ordinary isearch, since far less
+different images have to be displayed."
+  nil nil nil
+  :group 'pdf-isearch
+  (when isearch-mode
+    (pdf-isearch-redisplay)
+    (pdf-isearch-message
+     (if pdf-isearch-batch-mode "batch mode" "isearch mode"))))
+
+
+\f
+;; * ================================================================== *
+;; * Isearch interface
+;; * ================================================================== *
+
+(defvar pdf-isearch-filter-matches-function nil
+  "A function for filtering isearch matches.
+
+The function receives one argument: a list of matches, each
+being a list of edges. It should return a subset of this list.
+Edge coordinates are in image-space.")
+
+(defvar pdf-isearch-narrow-to-page nil
+  "Non-nil, if the search should be limited to the current page.")
+
+(defun pdf-isearch-search-function (string &rest _)
+  "Search for STRING in the current PDF buffer.
+
+This is a Isearch interface function."
+  (when (> (length string) 0)
+    (let ((same-search-p (pdf-isearch-same-search-p))
+          (oldpage pdf-isearch-current-page)
+          (matches (pdf-isearch-search-page string))
+          next-match)
+      ;; matches is a list of list of edges ((x0 y1 x1 y2) ...),
+      ;; sorted top to bottom ,left to right. Coordinates are in image
+      ;; space.
+      (unless isearch-forward
+        (setq matches (reverse matches)))
+      (when pdf-isearch-filter-matches-function
+        (setq matches (funcall pdf-isearch-filter-matches-function matches)))
+      ;; Where to go next ?
+      (setq pdf-isearch-current-page (pdf-view-current-page)
+            pdf-isearch-current-matches matches
+            next-match
+            (pdf-isearch-next-match
+             oldpage pdf-isearch-current-page
+             pdf-isearch-current-match matches
+             same-search-p
+             isearch-forward)
+            pdf-isearch-current-parameter
+            (list string isearch-regexp
+                  isearch-case-fold-search isearch-word))
+      (cond
+       (next-match
+        (setq pdf-isearch-current-match next-match)
+        (pdf-isearch-hl-matches next-match matches)
+        (pdf-isearch-focus-match next-match)
+        ;; Don't get off track.
+        (when (or (and (bobp) (not isearch-forward))
+                  (and (eobp) isearch-forward))
+          (goto-char (1+ (/ (buffer-size) 2))))
+        ;; Signal success to isearch.
+        (if isearch-forward
+            (re-search-forward ".")
+          (re-search-backward ".")))
+       ((and (not pdf-isearch-narrow-to-page)
+             (not (pdf-isearch-empty-match-p matches)))
+        (let ((next-page (pdf-isearch-find-next-matching-page
+                          string pdf-isearch-current-page t)))
+          (when next-page
+            (pdf-view-goto-page next-page)
+            (pdf-isearch-search-function string))))))))
+
+(defun pdf-isearch-push-state-function ()
+  "Push the current search state.
+
+This is a Isearch interface function."
+  (let ((hscroll (window-hscroll))
+        (vscroll (window-vscroll))
+        (parms pdf-isearch-current-parameter)
+        (matches pdf-isearch-current-matches)
+        (match pdf-isearch-current-match)
+        (page pdf-isearch-current-page))
+    (lambda (_state)
+      (setq pdf-isearch-current-parameter parms
+            pdf-isearch-current-matches matches
+            pdf-isearch-current-match match
+            pdf-isearch-current-page page)
+
+      (pdf-view-goto-page pdf-isearch-current-page)
+      (when pdf-isearch-current-match
+        (pdf-isearch-hl-matches
+         pdf-isearch-current-match
+         pdf-isearch-current-matches))
+      (image-set-window-hscroll hscroll)
+      (image-set-window-vscroll vscroll))))
+
+(defun pdf-isearch-wrap-function ()
+  "Go to first or last page.
+
+This is a Isearch interface function."
+  (let ((page (if isearch-forward
+                  1
+                (pdf-cache-number-of-pages))))
+    (unless (or pdf-isearch-narrow-to-page
+                (= page (pdf-view-current-page)))
+      (pdf-view-goto-page page)
+      (let ((next-screen-context-lines 0))
+        (if (= page 1)
+            (image-scroll-down)
+          (image-scroll-up)))))
+  (setq pdf-isearch-current-match nil))
+
+(defun pdf-isearch-mode-cleanup ()
+  "Cleanup after exiting Isearch.
+
+This is a Isearch interface function."
+  (pdf-isearch-active-mode -1)
+  (pdf-view-redisplay))
+
+(defun pdf-isearch-mode-initialize ()
+  "Initialize isearching.
+
+This is a Isearch interface function."
+  (pdf-isearch-active-mode 1)
+  (setq pdf-isearch-current-page (pdf-view-current-page)
+        pdf-isearch-current-match nil
+        pdf-isearch-current-matches nil
+        pdf-isearch-current-parameter nil)
+  (goto-char (1+ (/ (buffer-size) 2))))
+
+(defun pdf-isearch-same-search-p (&optional ignore-search-string-p)
+  "Return non-nil, if search parameter have not changed.
+
+Parameter inspected are `isearch-string' (unless
+IGNORE-SEARCH-STRING-P is t) and `isearch-case-fold-search'.  If
+there was no previous search, this function returns t."
+  (or (null pdf-isearch-current-parameter)
+      (let ((parameter (list isearch-string
+                             isearch-regexp
+                             isearch-case-fold-search
+                             isearch-word)))
+        (if ignore-search-string-p
+            (equal (cdr pdf-isearch-current-parameter)
+                   (cdr parameter))
+          (equal pdf-isearch-current-parameter
+                 parameter)))))
+
+(defun pdf-isearch-next-match (last-page this-page last-match
+                                         all-matches continued-p
+                                         forward-p)
+  "Determine the next match."
+  (funcall (if pdf-isearch-batch-mode
+               'pdf-isearch-next-match-batch
+             'pdf-isearch-next-match-isearch)
+           last-page this-page last-match
+           all-matches continued-p forward-p))
+
+(defun pdf-isearch-focus-match (current-match)
+  "Make the CURRENT-MATCH visible in the window."
+  (funcall (if pdf-isearch-batch-mode
+               'pdf-isearch-focus-match-batch
+             'pdf-isearch-focus-match-isearch)
+           current-match))
+
+(defun pdf-isearch-redisplay ()
+  "Redisplay the current highlighting."
+  (pdf-isearch-hl-matches pdf-isearch-current-match
+                          pdf-isearch-current-matches))
+
+(defun pdf-isearch-update ()
+  "Update search and redisplay, if necessary."
+  (unless (pdf-isearch-same-search-p t)
+    (setq pdf-isearch-current-parameter
+          (list isearch-string isearch-regexp
+                isearch-case-fold-search isearch-word)
+          pdf-isearch-current-matches
+          (pdf-isearch-search-page isearch-string))
+    (pdf-isearch-redisplay)))
+
+(defun pdf-isearch-message (fmt &rest args)
+  "Like `message', but Isearch friendly."
+  (unless args (setq args (list fmt) fmt "%s"))
+  (let ((msg (apply 'format fmt args)))
+    (if (cl-some (lambda (buf)
+                   (buffer-local-value 'isearch-mode buf))
+                 (mapcar 'window-buffer (window-list)))
+        (let ((isearch-message-suffix-add
+               (format " [%s]" msg)))
+          (isearch-message)
+          (sit-for 1))
+      (message "%s" msg))))
+
+(defun pdf-isearch-empty-match-p (matches)
+  (and matches
+       (cl-every
+        (lambda (match)
+          (cl-every (lambda (edges)
+                      (cl-every 'zerop edges))
+                    match))
+        matches)))
+
+(defun pdf-isearch-occur ()
+  "Run `occur' using the last search string or regexp."
+  (interactive)
+  (let ((case-fold-search isearch-case-fold-search)
+        (regexp
+         (cond
+          ((functionp isearch-word)
+           (funcall isearch-word isearch-string))
+          (isearch-word (pdf-isearch-word-search-regexp
+                         isearch-string nil
+                         pdf-isearch-hyphenation-character))
+          (isearch-regexp isearch-string))))
+    (save-selected-window
+      (pdf-occur (or regexp isearch-string) regexp))
+    (isearch-message)))
+
+(defun pdf-isearch-sync-backward ()
+  "Visit the source of the beginning of the current match."
+  (interactive)
+  (pdf-util-assert-pdf-window)
+  (unless pdf-isearch-current-match
+    (user-error "No current or recent match"))
+  (when isearch-mode
+    (isearch-exit))
+  (cl-destructuring-bind (left top _right _bot)
+      (car pdf-isearch-current-match)
+    (pdf-sync-backward-search left top)))
+
+\f
+;; * ================================================================== *
+;; * Interface to epdfinfo
+;; * ================================================================== *
+
+(defun pdf-isearch-search-page (string &optional page)
+  "Search STRING on PAGE in the current window.
+
+Returns a list of edges (LEFT TOP RIGHT BOTTOM) in PDF
+coordinates, sorted top to bottom, then left to right."
+
+  (unless page (setq page (pdf-view-current-page)))
+  (mapcar (lambda (match)
+            (let-alist match
+              (pdf-util-scale-relative-to-pixel .edges 'round)))
+          (let ((case-fold-search isearch-case-fold-search))
+            (funcall (pdf-isearch-search-fun)
+                     string page))))
+
+(defun pdf-isearch-search-fun ()
+  (funcall (or pdf-isearch-search-fun-function
+               'pdf-isearch-search-fun-default)))
+
+(defun pdf-isearch-search-fun-default ()
+  "Return default functions to use for the search."
+  (cond
+   ((eq isearch-word t)
+    (lambda (string &optional pages)
+      ;; Use lax versions to not fail at the end of the word while
+      ;; the user adds and removes characters in the search string
+      ;; (or when using nonincremental word isearch)
+      (let ((lax (not (or isearch-nonincremental
+                          (null (car isearch-cmds))
+                          (eq (length isearch-string)
+                              (length (isearch--state-string
+                                       (car isearch-cmds))))))))
+        (pdf-info-search-regexp
+         (pdf-isearch-word-search-regexp
+          string lax pdf-isearch-hyphenation-character)
+         pages 'invalid-regexp))))
+   (isearch-regexp
+    (lambda (string &optional pages)
+      (pdf-info-search-regexp string pages 'invalid-regexp)))
+   (t
+    'pdf-info-search-string)))
+
+
+(defun pdf-isearch-word-search-regexp (string &optional lax hyphenization-chars)
+  "Return a PCRE which matches words, ignoring punctuation."
+  (let ((hyphenization-regexp
+         (and hyphenization-chars
+              (format "(?:[%s]\\n)?"
+                      (replace-regexp-in-string
+                       "[]^\\\\-]" "\\\\\\&"
+                       hyphenization-chars t)))))
+    (cond
+     ((equal string "") "")
+     ((string-match-p "\\`\\W+\\'" string) "\\W+")
+     (t (concat
+         (if (string-match-p "\\`\\W" string) "\\W+"
+           (unless lax "\\b"))
+         (mapconcat (lambda (word)
+                      (if hyphenization-regexp
+                          (mapconcat
+                           (lambda (ch)
+                             (pdf-util-pcre-quote (string ch)))
+                           (append word nil)
+                           hyphenization-regexp)
+                        (pdf-util-pcre-quote word)))
+                    (split-string string "\\W+" t) "\\W+")
+         (if (string-match-p "\\W\\'" string) "\\W+"
+           (unless lax "\\b")))))))
+
+(defun pdf-isearch-find-next-matching-page (string page &optional interactive-p)
+  "Find STRING after or before page PAGE, according to FORWARD-P.
+
+If INTERACTIVE-P is non-nil, give some progress feedback.
+Returns the page number where STRING was found, or nil if there
+is no such page."
+  ;; Do a exponentially expanding search.
+  (let* ((incr 1)
+         (pages (if isearch-forward
+                    (cons (1+ page)
+                          (1+ page))
+                  (cons (1- page)
+                        (1- page))))
+         (fn (pdf-isearch-search-fun))
+         matched-page
+         reporter)
+
+    (while (and (null matched-page)
+                (or (and isearch-forward
+                         (<= (car pages)
+                             (pdf-cache-number-of-pages)))
+                    (and (not isearch-forward)
+                         (>= (cdr pages) 1))))
+      (let* ((case-fold-search isearch-case-fold-search)
+             (matches (funcall fn string pages)))
+        (setq matched-page
+              (alist-get 'page (if isearch-forward
+                                   (car matches)
+                                 (car (last matches))))))
+      (setq incr (* incr 2))
+      (cond (isearch-forward
+             (setcar pages (1+ (cdr pages)))
+             (setcdr pages (min (pdf-cache-number-of-pages)
+                                (+ (cdr pages) incr))))
+            (t
+             (setcdr pages (1- (car pages)))
+             (setcar pages (max 1 (- (car pages)
+                                     incr)))))
+      (when interactive-p
+        (when (and (not reporter)
+                   (= incr 8)) ;;Don't bother right away.
+          (setq reporter
+                (apply
+                    'make-progress-reporter "Searching"
+                    (if isearch-forward
+                        (list (car pages) (pdf-cache-number-of-pages) nil 0)
+                      (list 1 (cdr pages) nil 0)))))
+        (when reporter
+          (progress-reporter-update
+           reporter (if isearch-forward
+                        (- (cdr pages) page)
+                      (- page (car pages)))))))
+    matched-page))
+
+
+\f
+;; * ================================================================== *
+;; * Isearch Behavior
+;; * ================================================================== *
+
+(defun pdf-isearch-next-match-isearch (last-page this-page last-match
+                                                 matches same-search-p
+                                                 forward)
+  "Default function for choosing the next match.
+
+Implements default isearch behaviour, i.e. it stops at every
+match."
+  (cond
+   ((null last-match)
+    ;; Goto first match from top or bottom of the window.
+    (let* ((iedges (pdf-util-image-displayed-edges))
+           (pos (pdf-util-with-edges (iedges)
+                  (if forward
+                      (list iedges-left iedges-top
+                            iedges-left iedges-top)
+                    (list iedges-right iedges-bot
+                          iedges-right iedges-bot)))))
+      (pdf-isearch-closest-match (list pos) matches forward)))
+   ((not (eq last-page this-page))
+    ;; First match from top-left or bottom-right of the new
+    ;; page.
+    (car matches))
+   (same-search-p
+    ;; Next match after the last one.
+    (if last-match
+        (cadr (member last-match matches))))
+   (matches
+    ;; Next match of new search closest to the last one.
+    (pdf-isearch-closest-match
+     last-match matches forward))))
+
+(defun pdf-isearch-focus-match-isearch (match)
+  "Make the image area in MATCH visible in the selected window."
+  (pdf-util-scroll-to-edges (apply 'pdf-util-edges-union match)))
+
+(defun pdf-isearch-next-match-batch (last-page this-page last-match
+                                               matches same-search-p
+                                               forward-p)
+  "Select the next match, unseen in the current search direction."
+
+  (if (or (null last-match)
+          (not same-search-p)
+          (not (eq last-page this-page)))
+      (pdf-isearch-next-match-isearch
+       last-page this-page last-match matches same-search-p forward-p)
+    (pdf-util-with-edges (match iedges)
+      (let ((iedges (pdf-util-image-displayed-edges)))
+        (car (cl-remove-if
+              ;; Filter matches visible on screen.
+              (lambda (edges)
+                (let ((match (apply 'pdf-util-edges-union edges)))
+                  (and (<= match-right iedges-right)
+                       (<= match-bot iedges-bot)
+                       (>= match-left iedges-left)
+                       (>= match-top iedges-top))))
+              (cdr (member last-match matches))))))))
+
+(defun pdf-isearch-focus-match-batch (match)
+  "Make the image area in MATCH eagerly visible in the selected window."
+  (pdf-util-scroll-to-edges (apply 'pdf-util-edges-union match) t))
+
+(cl-deftype pdf-isearch-match ()
+  `(satisfies
+    (lambda (match)
+      (cl-every (lambda (edges)
+                  (and (consp edges)
+                       (= (length edges) 4)
+                       (cl-every 'numberp edges)))
+                match))))
+
+(cl-deftype list-of (type)
+  `(satisfies
+    (lambda (l)
+      (and (listp l)
+           (cl-every (lambda (x)
+                       (cl-typep x ',type))
+                     l)))))
+
+(defun pdf-isearch-closest-match (match matches
+                                        &optional forward-p)
+  "Find the nearest element to MATCH in MATCHES.
+
+The direction in which to look is determined by FORWARD-P.
+
+MATCH should be a list of edges, MATCHES a list of such element;
+it is assumed to be ordered with respect to FORWARD-P."
+
+
+  (cl-check-type match pdf-isearch-match)
+  (cl-check-type matches (list-of pdf-isearch-match))
+  (let ((matched (apply 'pdf-util-edges-union match)))
+    (pdf-util-with-edges (matched)
+      (cl-loop for next in matches do
+        (let ((edges (apply 'pdf-util-edges-union next)))
+          (pdf-util-with-edges (edges)
+            (when (if forward-p
+                      (or (>= edges-top matched-bot)
+                          (and (or (>= edges-top matched-top)
+                                   (>= edges-bot matched-bot))
+                               (>= edges-right matched-right)))
+                    (or (<= edges-bot matched-top)
+                        (and (or (<= edges-bot matched-bot)
+                                 (<= edges-top matched-top))
+                             (<= edges-left matched-left))))
+              (cl-return next))))))))
+
+
+\f
+;; * ================================================================== *
+;; * Display
+;; * ================================================================== *
+
+
+(defun pdf-isearch-current-colors ()
+  "Return the current color set.
+
+The return value depends on `pdf-view-dark-minor-mode' and
+`pdf-isearch-batch-mode'.  It is a list of four colors \(MATCH-FG
+MATCH-BG LAZY-FG LAZY-BG\)."
+  (let ((dark-p pdf-view-dark-minor-mode))
+    (cond
+     (pdf-isearch-batch-mode
+      (let ((colors (pdf-util-face-colors 'pdf-isearch-batch dark-p)))
+        (list (car colors)
+              (cdr colors)
+              (car colors)
+              (cdr colors))))
+     (t
+      (let ((match (pdf-util-face-colors 'pdf-isearch-match dark-p))
+            (lazy (pdf-util-face-colors 'pdf-isearch-lazy dark-p)))
+        (list (car match)
+              (cdr match)
+              (car lazy)
+              (cdr lazy)))))))
+
+(defvar pdf-isearch--hl-matches-tick 0)
+
+(defun pdf-isearch-hl-matches (current matches &optional occur-hack-p)
+  "Highlighting edges CURRENT and MATCHES."
+  (cl-check-type current pdf-isearch-match)
+  (cl-check-type matches (list-of pdf-isearch-match))
+  (cl-destructuring-bind (fg1 bg1 fg2 bg2)
+      (pdf-isearch-current-colors)
+    (let* ((width (car (pdf-view-image-size)))
+           (page (pdf-view-current-page))
+           (window (selected-window))
+           (buffer (current-buffer))
+           (tick (cl-incf pdf-isearch--hl-matches-tick))
+           (pdf-info-asynchronous
+            (lambda (status data)
+              (when (and (null status)
+                         (eq tick pdf-isearch--hl-matches-tick)
+                         (buffer-live-p buffer)
+                         (window-live-p window)
+                         (eq (window-buffer window)
+                             buffer))
+                (with-selected-window window
+                  (when (and (derived-mode-p 'pdf-view-mode)
+                             (or isearch-mode
+                                 occur-hack-p)
+                             (eq page (pdf-view-current-page)))
+                    (pdf-view-display-image
+                     (pdf-view-create-image data))))))))
+      (pdf-info-renderpage-text-regions
+       page width t nil
+       `(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative
+                      current))
+       `(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative
+                      (apply 'append
+                        (remove current matches))))))))
+
+\f
+;; * ================================================================== *
+;; * Debug
+;; * ================================================================== *
+
+;; The following isearch-search function is debuggable.
+;;
+(when nil
+  (defun isearch-search ()
+    ;; Do the search with the current search string.
+    (if isearch-message-function
+        (funcall isearch-message-function nil t)
+      (isearch-message nil t))
+    (if (and (eq isearch-case-fold-search t) search-upper-case)
+        (setq isearch-case-fold-search
+              (isearch-no-upper-case-p isearch-string isearch-regexp)))
+    (condition-case lossage
+        (let ((inhibit-point-motion-hooks
+               ;; FIXME: equality comparisons on functions is asking for trouble.
+               (and (eq isearch-filter-predicate 'isearch-filter-visible)
+                    search-invisible))
+              (inhibit-quit nil)
+              (case-fold-search isearch-case-fold-search)
+              (retry t))
+          (setq isearch-error nil)
+          (while retry
+            (setq isearch-success
+                  (isearch-search-string isearch-string nil t))
+            ;; Clear RETRY unless the search predicate says
+            ;; to skip this search hit.
+            (if (or (not isearch-success)
+                    (bobp) (eobp)
+                    (= (match-beginning 0) (match-end 0))
+                    (funcall isearch-filter-predicate
+                             (match-beginning 0) (match-end 0)))
+                (setq retry nil)))
+          (setq isearch-just-started nil)
+          (if isearch-success
+              (setq isearch-other-end
+                    (if isearch-forward (match-beginning 0) (match-end 0)))))
+
+      (quit (isearch-unread ?\C-g)
+            (setq isearch-success nil))
+
+      (invalid-regexp
+       (setq isearch-error (car (cdr lossage)))
+       (if (string-match
+            "\\`Premature \\|\\`Unmatched \\|\\`Invalid "
+            isearch-error)
+           (setq isearch-error "incomplete input")))
+
+      (search-failed
+       (setq isearch-success nil)
+       (setq isearch-error (nth 2 lossage)))
+
+      ;; (error
+      ;;  ;; stack overflow in regexp search.
+      ;;  (setq isearch-error (format "%s" lossage)))
+      )
+
+    (if isearch-success
+        nil
+      ;; Ding if failed this time after succeeding last time.
+      (and (isearch--state-success (car isearch-cmds))
+           (ding))
+      (if (functionp (isearch--state-pop-fun (car isearch-cmds)))
+          (funcall (isearch--state-pop-fun (car isearch-cmds))
+                   (car isearch-cmds)))
+      (goto-char (isearch--state-point (car isearch-cmds))))))
+
+
+(provide 'pdf-isearch)
+
+;;; pdf-isearch.el ends here
+
+;; Local Variables:
+;; byte-compile-warnings: (not obsolete)
+;; End:
diff --git a/elpa/pdf-tools-20191031.642/pdf-links.el b/elpa/pdf-tools-20191031.642/pdf-links.el
new file mode 100644 (file)
index 0000000..24baa3c
--- /dev/null
@@ -0,0 +1,376 @@
+;;; pdf-links.el --- Handle PDF links. -*- lexical-binding: t -*-
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, multimedia
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+(require 'pdf-info)
+(require 'pdf-util)
+(require 'pdf-misc)
+(require 'pdf-cache)
+(require 'pdf-isearch)
+(require 'let-alist)
+(require 'org)
+
+;;; Code:
+
+
+\f
+;; * ================================================================== *
+;; * Customizations
+;; * ================================================================== *
+
+(defgroup pdf-links nil
+  "Following links in PDF documents."
+  :group 'pdf-tools)
+
+(defface pdf-links-read-link
+  '((((background dark)) (:background "red" :foreground "yellow"))
+    (((background light)) (:background "red" :foreground "yellow")))
+  "Face used to determine the colors when reading links."
+  ;; :group 'pdf-links
+  :group 'pdf-tools-faces)
+
+(defcustom pdf-links-read-link-convert-commands
+  '(;;"-font" "FreeMono"
+    "-pointsize" "%P"
+    "-undercolor" "%f"
+    "-fill" "%b"
+    "-draw" "text %X,%Y '%c'")
+
+  "The commands for the convert program, when decorating links for reading.
+See `pdf-util-convert' for an explanation of the format.
+
+Aside from the description there, two additional escape chars are
+available.
+
+%P -- The scaled font pointsize, i.e. IMAGE-WIDTH * SCALE (See
+ `pdf-links-convert-pointsize-scale').
+%c -- String describing the current link key (e.g. AA, AB,
+ etc.)."
+  :group 'pdf-links
+  :type '(repeat string)
+  :link '(variable-link pdf-isearch-convert-commands)
+  :link '(url-link "http://www.imagemagick.org/script/convert.php"))
+
+(defcustom pdf-links-convert-pointsize-scale 0.01
+  "The scale factor for the -pointsize convert command.
+
+This determines the relative size of the font, when interactively
+reading links."
+  :group 'pdf-links
+  :type '(restricted-sexp :match-alternatives
+                          ((lambda (x) (and (numberp x)
+                                            (<= x 1)
+                                            (>= x 0))))))
+
+(defcustom pdf-links-browse-uri-function
+  'pdf-links-browse-uri-default
+  "The function for handling uri links.
+
+This function should accept one argument, the URI to follow, and
+do something with it."
+  :group 'pdf-links
+  :type 'function)
+
+\f
+;; * ================================================================== *
+;; * Minor Mode
+;; * ================================================================== *
+
+(defvar pdf-links-minor-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (define-key kmap (kbd "f") 'pdf-links-isearch-link)
+    (define-key kmap (kbd "F") 'pdf-links-action-perform)
+    kmap))
+
+;;;###autoload
+(define-minor-mode pdf-links-minor-mode
+  "Handle links in PDF documents.\\<pdf-links-minor-mode-map>
+
+If this mode is enabled, most links in the document may be
+activated by clicking on them or by pressing \\[pdf-links-action-perform] and selecting
+one of the displayed keys, or by using isearch limited to
+links via \\[pdf-links-isearch-link].
+
+\\{pdf-links-minor-mode-map}"
+
+  nil nil nil
+  :group 'pdf-links
+  (pdf-util-assert-pdf-buffer)
+  (cond
+   (pdf-links-minor-mode
+    (pdf-view-add-hotspot-function 'pdf-links-hotspots-function 0))
+   (t
+    (pdf-view-remove-hotspot-function 'pdf-links-hotspots-function)))
+  (pdf-view-redisplay t))
+
+(defun pdf-links-hotspots-function (page size)
+  "Create hotspots for links on PAGE using SIZE."
+
+  (let ((links (pdf-cache-pagelinks page))
+        (id-fmt "link-%d-%d")
+        (i 0)
+        (pointer 'hand)
+        hotspots)
+    (dolist (l links)
+      (let ((e (pdf-util-scale
+                (cdr (assq 'edges l)) size 'round))
+            (id (intern (format id-fmt page
+                                (cl-incf i)))))
+        (push `((rect . ((,(nth 0 e) . ,(nth 1 e))
+                         . (,(nth 2 e) . ,(nth 3 e))))
+                ,id
+                (pointer
+                 ,pointer
+                 help-echo ,(pdf-links-action-to-string l)))
+              hotspots)
+        (local-set-key
+         (vector id 'mouse-1)
+         (lambda nil
+           (interactive "@")
+           (pdf-links-action-perform l)))
+        (local-set-key
+         (vector id t)
+         'pdf-util-image-map-mouse-event-proxy)))
+    (nreverse hotspots)))
+
+(defun pdf-links-action-to-string (link)
+  "Return a string representation of ACTION."
+  (let-alist link
+    (concat
+     (cl-case .type
+       (goto-dest
+        (if (> .page 0)
+            (format "Goto page %d" .page)
+          "Destination not found"))
+       (goto-remote
+        (if (and .filename (file-exists-p .filename))
+            (format "Goto %sfile '%s'"
+                    (if (> .page 0)
+                        (format "p.%d of " .page)
+                      "")
+                    .filename)
+          (format "Link to nonexistent file '%s'" .filename)))
+       (uri
+        (if (> (length .uri) 0)
+            (format "Link to uri '%s'" .uri)
+          (format "Link to empty uri")))
+       (t (format "Unrecognized link type: %s" .type)))
+     (if (> (length .title) 0)
+         (format " (%s)" .title)))))
+
+;;;###autoload
+(defun pdf-links-action-perform (link)
+  "Follow LINK, depending on its type.
+
+This may turn to another page, switch to another PDF buffer or
+invoke `pdf-links-browse-uri-function'.
+
+Interactively, link is read via `pdf-links-read-link-action'.
+This function displays characters around the links in the current
+page and starts reading characters (ignoring case).  After a
+sufficient number of characters have been read, the corresponding
+link's link is invoked.  Additionally, SPC may be used to
+scroll the current page."
+  (interactive
+   (list (or (pdf-links-read-link-action "Activate link (SPC scrolls): ")
+             (error "No link selected"))))
+  (let-alist link
+    (cl-case .type
+      ((goto-dest goto-remote)
+       (let ((window (selected-window)))
+         (cl-case .type
+           (goto-dest
+            (unless (> .page 0)
+              (error "Link points to nowhere")))
+           (goto-remote
+            (unless (and .filename (file-exists-p .filename))
+              (error "Link points to nonexistent file %s" .filename))
+            (setq window (display-buffer
+                          (or (find-buffer-visiting .filename)
+                              (find-file-noselect .filename))))))
+         (with-selected-window window
+           (when (derived-mode-p 'pdf-view-mode)
+             (when (> .page 0)
+               (pdf-view-goto-page .page))
+             (when .top
+               ;; Showing the tooltip delays displaying the page for
+               ;; some reason (sit-for/redisplay don't help), do it
+               ;; later.
+               (run-with-idle-timer 0.001 nil
+                 (lambda ()
+                   (when (window-live-p window)
+                     (with-selected-window window
+                       (when (derived-mode-p 'pdf-view-mode)
+                         (pdf-util-tooltip-arrow .top)))))))))))
+      (uri
+       (funcall pdf-links-browse-uri-function .uri))
+      (t
+       (error "Unrecognized link type: %s" .type)))
+    nil))
+
+(defun pdf-links-read-link-action (prompt)
+  "Using PROMPT, interactively read a link-action.
+
+See `pdf-links-action-perform' for the interface."
+
+  (pdf-util-assert-pdf-window)
+  (let* ((links (pdf-cache-pagelinks
+                 (pdf-view-current-page)))
+         (keys (pdf-links-read-link-action--create-keys
+                (length links)))
+         (key-strings (mapcar (apply-partially 'apply 'string)
+                              keys))
+         (alist (cl-mapcar 'cons keys links))
+         (size (pdf-view-image-size))
+         (colors (pdf-util-face-colors
+                  'pdf-links-read-link pdf-view-dark-minor-mode))
+         (args (list
+                :foreground (car colors)
+                :background (cdr colors)
+                :formats
+                 `((?c . ,(lambda (_edges) (pop key-strings)))
+                   (?P . ,(number-to-string
+                           (max 1 (* (cdr size)
+                                     pdf-links-convert-pointsize-scale)))))
+                 :commands pdf-links-read-link-convert-commands
+                 :apply (pdf-util-scale-relative-to-pixel
+                         (mapcar (lambda (l) (cdr (assq 'edges l)))
+                                 links)))))
+    (unless links
+      (error "No links on this page"))
+    (unwind-protect
+        (let ((image-data
+               (pdf-cache-get-image
+                (pdf-view-current-page)
+                (car size) (car size) 'pdf-links-read-link-action)))
+          (unless image-data
+            (setq image-data (apply 'pdf-util-convert-page args ))
+            (pdf-cache-put-image
+             (pdf-view-current-page)
+             (car size) image-data 'pdf-links-read-link-action))
+          (pdf-view-display-image
+           (create-image image-data (pdf-view-image-type) t))
+          (pdf-links-read-link-action--read-chars prompt alist))
+      (pdf-view-redisplay))))
+
+(defun pdf-links-read-link-action--read-chars (prompt alist)
+  (catch 'done
+    (let (key)
+      (while t
+        (let* ((chars (append (mapcar 'caar alist)
+                              (mapcar 'downcase (mapcar 'caar alist))
+                              (list ?\s)))
+               (ch (read-char-choice prompt chars)))
+          (setq ch (upcase ch))
+          (cond
+           ((= ch ?\s)
+            (when (= (window-vscroll) (image-scroll-up))
+              (image-scroll-down (window-vscroll))))
+           (t
+            (setq alist (delq nil (mapcar (lambda (elt)
+                                            (and (eq ch (caar elt))
+                                                 (cons (cdar elt)
+                                                       (cdr elt))))
+                                          alist))
+                  key (append key (list ch))
+                  prompt (concat prompt (list ch)))
+            (when (= (length alist) 1)
+              (message nil)
+              (throw 'done (cdar alist))))))))))
+
+(defun pdf-links-read-link-action--create-keys (n)
+  (when (> n 0)
+    (let ((len (1+ (floor (log n 26))))
+          keys)
+      (dotimes (i n)
+        (let (key)
+          (dotimes (_x len)
+            (push (+ (% i 26) ?A) key)
+            (setq i (/ i 26)))
+          (push key keys)))
+      (nreverse keys))))
+
+(defun pdf-links-isearch-link ()
+  (interactive)
+  (let* (quit-p
+         (isearch-mode-end-hook
+          (cons (lambda nil
+                  (setq quit-p isearch-mode-end-hook-quit))
+                isearch-mode-end-hook))
+         (pdf-isearch-filter-matches-function
+          'pdf-links-isearch-link-filter-matches)
+         (pdf-isearch-narrow-to-page t)
+         (isearch-message-prefix-add "(Links)")
+         pdf-isearch-batch-mode)
+    (isearch-forward)
+    (unless (or quit-p (null pdf-isearch-current-match))
+      (let* ((page (pdf-view-current-page))
+             (match (car pdf-isearch-current-match))
+             (size (pdf-view-image-size))
+             (links (sort (cl-remove-if
+                           (lambda (e)
+                             (= 0 (pdf-util-edges-intersection-area (car e) match)))
+                           (mapcar (lambda (l)
+                                     (cons (pdf-util-scale (alist-get 'edges l) size)
+                                           l))
+                                   (pdf-cache-pagelinks page)))
+                          (lambda (e1 e2)
+                            (> (pdf-util-edges-intersection-area
+                                (alist-get 'edges e1) match)
+                               (pdf-util-edges-intersection-area
+                                (alist-get 'edges e2) match))))))
+        (unless links
+          (error "No link found at this position"))
+        (pdf-links-action-perform (car links))))))
+
+(defun pdf-links-isearch-link-filter-matches (matches)
+  (let ((links (pdf-util-scale
+                (mapcar (apply-partially 'alist-get 'edges)
+                        (pdf-cache-pagelinks
+                         (pdf-view-current-page)))
+                (pdf-view-image-size))))
+    (cl-remove-if-not
+     (lambda (m)
+       (cl-some
+        (lambda (edges)
+          (cl-some (lambda (link)
+                     (pdf-util-with-edges (link edges)
+                       (let ((area (min (* link-width link-height)
+                                        (* edges-width edges-height))))
+                         (>  (/  (pdf-util-edges-intersection-area edges link)
+                                 (float area)) 0.5))))
+                   links))
+        m))
+     matches)))
+
+(defun pdf-links-browse-uri-default (uri)
+  "Open the string URI using Org.
+
+Wraps the URI in \[\[ ... \]\] and calls `org-open-link-from-string'
+on the resulting string."
+  (cl-check-type uri string)
+  (message "Opening `%s' with Org" uri)
+  (org-open-link-from-string (format "[[%s]]" uri)))
+
+(provide 'pdf-links)
+
+;;; pdf-links.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-loader.el b/elpa/pdf-tools-20191031.642/pdf-loader.el
new file mode 100644 (file)
index 0000000..b1eab34
--- /dev/null
@@ -0,0 +1,80 @@
+;;; pdf-loader.el --- Minimal PDF Tools loader       -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2017  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMhochschule-trier.de>
+;; Keywords:
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(defconst pdf-loader--auto-mode-alist-item
+  (copy-sequence "\\.[pP][dD][fF]\\'")
+  "The item used in `auto-mode-alist'.")
+
+(defconst pdf-loader--magic-mode-alist-item
+  (copy-sequence "%PDF")
+  "The item used in`magic-mode-alist'.")
+
+
+(declare-function pdf-tools-install "pdf-tools.el")
+
+;;;###autoload
+(defun pdf-loader-install (&optional no-query-p skip-dependencies-p
+                                     no-error-p force-dependencies-p)
+  "Prepare Emacs for using PDF Tools.
+
+This function acts as a replacement for `pdf-tools-install' and
+makes Emacs load and use PDF Tools as soon as a PDF file is
+opened, but not sooner.
+
+The arguments are passed verbatim to `pdf-tools-install', which
+see."
+  (let ((args (list no-query-p skip-dependencies-p
+                    no-error-p force-dependencies-p)))
+    (if (featurep 'pdf-tools)
+        (apply #'pdf-tools-install args)
+      (pdf-loader--install
+       (lambda ()
+         (apply #'pdf-loader--load args))))))
+
+(defun pdf-loader--load (&rest args)
+  (pdf-loader--uninstall)
+  (save-selected-window
+    (pdf-tools-install args)))
+
+(defun pdf-loader--install (loader)
+  (pdf-loader--uninstall)
+  (push (cons pdf-loader--auto-mode-alist-item loader)
+        auto-mode-alist)
+  (push (cons pdf-loader--magic-mode-alist-item loader)
+        magic-mode-alist))
+
+(defun pdf-loader--uninstall ()
+  (let ((elt (assoc pdf-loader--auto-mode-alist-item
+                    auto-mode-alist)))
+    (when elt
+      (setq auto-mode-alist (remove elt auto-mode-alist))))
+  (let ((elt (assoc pdf-loader--magic-mode-alist-item
+                    magic-mode-alist)))
+    (when elt
+      (setq magic-mode-alist (remove elt magic-mode-alist)))))
+
+(provide 'pdf-loader)
+;;; pdf-loader.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-misc.el b/elpa/pdf-tools-20191031.642/pdf-misc.el
new file mode 100644 (file)
index 0000000..6b46d99
--- /dev/null
@@ -0,0 +1,283 @@
+;;; pdf-misc.el --- Miscellaneous commands for PDF buffer.
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, multimedia
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+
+(require 'pdf-view)
+(require 'pdf-util)
+(require 'imenu)
+
+
+
+(defvar pdf-misc-minor-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "I") 'pdf-misc-display-metadata)
+    (define-key map (kbd "C-c C-p") 'pdf-misc-print-document)
+    map)
+  "Keymap used in `pdf-misc-minor-mode'.")
+
+;;;###autoload
+(define-minor-mode pdf-misc-minor-mode
+  "FIXME:  Not documented."
+  nil nil nil)
+
+;;;###autoload
+(define-minor-mode pdf-misc-size-indication-minor-mode
+  "Provide a working size indication in the mode-line."
+  nil nil nil
+  (pdf-util-assert-pdf-buffer)
+  (cond
+   (pdf-misc-size-indication-minor-mode
+    (unless (assq 'pdf-misc-size-indication-minor-mode
+                  mode-line-position)
+      (setq mode-line-position
+            `((pdf-misc-size-indication-minor-mode
+               (:eval (pdf-misc-size-indication)))
+              ,@mode-line-position))))
+   (t
+    (setq mode-line-position
+          (cl-remove 'pdf-misc-size-indication-minor-mode
+                     mode-line-position :key 'car-safe)))))
+
+(defun pdf-misc-size-indication ()
+  "Return size indication string for the mode-line."
+  (let ((top (= (window-vscroll nil t) 0))
+        (bot (>= (+ (- (nth 3 (window-inside-pixel-edges))
+                       (nth 1 (window-inside-pixel-edges)))
+                    (window-vscroll nil t))
+                 (cdr (pdf-view-image-size t)))))
+    (cond
+     ((and top bot) " All")
+     (top " Top")
+     (bot " Bot")
+     (t (format
+         " %d%%%%"
+         (ceiling
+          (* 100 (/ (float (window-vscroll nil t))
+                    (cdr (pdf-view-image-size t))))))))))
+
+(defvar pdf-misc-menu-bar-minor-mode-map (make-sparse-keymap)
+  "The keymap used in `pdf-misc-menu-bar-minor-mode'.")
+
+(easy-menu-define nil pdf-misc-menu-bar-minor-mode-map
+  "Menu for PDF Tools."
+  `("PDF Tools"
+    ["Go Backward" pdf-history-backward
+     :visible (bound-and-true-p pdf-history-minor-mode)
+     :active (and (bound-and-true-p pdf-history-minor-mode)
+                  (not (pdf-history-end-of-history-p)))]
+    ["Go Forward" pdf-history-forward
+     :visible (bound-and-true-p pdf-history-minor-mode)
+     :active (not (pdf-history-end-of-history-p))]
+    ["--" nil
+     :visible (derived-mode-p 'pdf-virtual-view-mode)]
+    ["Next file" pdf-virtual-buffer-forward-file
+     :visible  (derived-mode-p 'pdf-virtual-view-mode)
+     :active (pdf-virtual-document-next-file
+              (pdf-view-current-page))]
+    ["Previous file" pdf-virtual-buffer-backward-file
+     :visible (derived-mode-p 'pdf-virtual-view-mode)
+     :active (not (eq 1 (pdf-view-current-page)))]
+    ["--" nil
+     :visible (bound-and-true-p pdf-history-minor-mode)]
+    ["Add text annotation" pdf-annot-mouse-add-text-annotation
+     :visible (bound-and-true-p pdf-annot-minor-mode)
+     :keys "\\[pdf-annot-add-text-annotation]"]
+    ("Add markup annotation"
+     :active (pdf-view-active-region-p)
+     :visible (and (bound-and-true-p pdf-annot-minor-mode)
+                   (pdf-info-markup-annotations-p))
+     ["highlight" pdf-annot-add-highlight-markup-annotation]
+     ["squiggly" pdf-annot-add-squiggly-markup-annotation]
+     ["underline" pdf-annot-add-underline-markup-annotation]
+     ["strikeout" pdf-annot-add-strikeout-markup-annotation])
+    ["--" nil :visible (bound-and-true-p pdf-annot-minor-mode)]
+    ["Display Annotations" pdf-annot-list-annotations
+     :help "List all annotations"
+     :visible (bound-and-true-p pdf-annot-minor-mode)]
+    ["Display Attachments" pdf-annot-attachment-dired
+     :help "Display attachments in a dired buffer"
+     :visible (featurep 'pdf-annot)]
+    ["Display Metadata" pdf-misc-display-metadata
+     :help "Display information about the document"
+     :visible (featurep 'pdf-misc)]
+    ["Display Outline" pdf-outline
+     :help "Display documents outline"
+     :visible (featurep 'pdf-outline)]
+    "--"
+    ("Render Options"
+     ["Printed Mode" (lambda ()
+                       (interactive)
+                       (pdf-view-printer-minor-mode 'toggle))
+      :style toggle
+      :selected pdf-view-printer-minor-mode
+      :help "Display the PDF as it would be printed."]
+     ["Midnight Mode" (lambda ()
+                        (interactive)
+                        (pdf-view-midnight-minor-mode 'toggle))
+      :style toggle
+      :selected pdf-view-midnight-minor-mode
+      :help "Apply a color-filter appropriate for past midnight reading."])
+    "--"
+    ["Copy region" pdf-view-kill-ring-save
+     :keys "\\[kill-ring-save]"
+     :active (pdf-view-active-region-p)]
+    "--"
+    ["Isearch document" isearch-forward
+     :visible (bound-and-true-p pdf-isearch-minor-mode)]
+    ["Occur document" pdf-occur
+     :visible (featurep 'pdf-occur)]
+    "--"
+    ["Locate TeX source" pdf-sync-backward-search-mouse
+     :visible (and (featurep 'pdf-sync)
+                   (equal last-command-event
+                          last-nonmenu-event))]
+    ["--" nil :visible (and (featurep 'pdf-sync)
+                            (equal last-command-event
+                                   last-nonmenu-event))]
+    ["Print" pdf-misc-print-document
+     :active (and (pdf-view-buffer-file-name)
+                  (file-readable-p (pdf-view-buffer-file-name)))]
+    ["Create image" pdf-view-extract-region-image
+     :help "Create an image of the page or the selected region(s)."]
+    ["Create virtual PDF" pdf-virtual-buffer-create
+     :help "Create a PDF containing all documents in this directory."
+     :visible (bound-and-true-p pdf-virtual-global-minor-mode)]
+    "--"
+    ["Revert buffer" pdf-view-revert-buffer
+     :visible (pdf-info-writable-annotations-p)]
+    "--"
+    ["Customize" pdf-tools-customize]))
+
+;;;###autoload
+(define-minor-mode pdf-misc-menu-bar-minor-mode
+  "Display a PDF Tools menu in the menu-bar."
+  nil nil nil
+  (pdf-util-assert-pdf-buffer))
+
+(defvar pdf-misc-context-menu-minor-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (define-key kmap [down-mouse-3] 'pdf-misc-popup-context-menu)
+    kmap))
+
+;;;###autoload
+(define-minor-mode pdf-misc-context-menu-minor-mode
+  "Provide a right-click context menu in PDF buffers.
+
+\\{pdf-misc-context-menu-minor-mode-map}"
+  nil nil nil
+  (pdf-util-assert-pdf-buffer))
+
+(defun pdf-misc-popup-context-menu (event)
+  "Popup a context menu at position determined by EVENT."
+  (interactive "@e")
+  (popup-menu
+   (cons 'keymap
+         (cddr (lookup-key pdf-misc-menu-bar-minor-mode-map
+                           [menu-bar PDF\ Tools])))))
+
+(defun pdf-misc-display-metadata ()
+  "Display all available metadata in a separate buffer."
+  (interactive)
+  (pdf-util-assert-pdf-buffer)
+  (let* ((buffer (current-buffer))
+         (md (pdf-info-metadata)))
+    (with-current-buffer (get-buffer-create "*PDF-Metadata*")
+      (let* ((inhibit-read-only t)
+             (pad (apply' max (mapcar (lambda (d)
+                                        (length (symbol-name (car d))))
+                                      md)))
+             (fmt (format "%%%ds:%%s\n" pad))
+             window)
+        (erase-buffer)
+        (setq header-line-format (buffer-name buffer)
+              buffer-read-only t)
+        (font-lock-mode 1)
+        (font-lock-add-keywords nil
+          '(("^ *\\(\\(?:\\w\\|-\\)+\\):"
+             (1 font-lock-keyword-face))))
+        (dolist (d md)
+          (let ((key (car d))
+                (val (cdr d)))
+            (cl-case key
+              (keywords
+               (setq val (mapconcat 'identity val ", "))))
+            (let ((beg (+ (length (symbol-name key)) (point) 1))
+                  (fill-prefix
+                   (make-string (1+ pad) ?\s)))
+              (insert (format fmt key val))
+              (fill-region beg (point) )))))
+      (goto-char 1)
+      (display-buffer (current-buffer)))
+    md))
+
+(defgroup pdf-misc nil
+  "Miscellaneous options for PDF documents."
+  :group 'pdf-tools)
+
+(defcustom pdf-misc-print-programm nil
+  "The program used for printing.
+
+It is called with one argument, the PDF file."
+  :group 'pdf-misc
+  :type 'file)
+
+(defcustom pdf-misc-print-programm-args nil
+  "List of additional arguments passed to `pdf-misc-print-program'."
+  :group 'pdf-misc
+  :type '(repeat string))
+
+(defun pdf-misc-print-programm (&optional interactive-p)
+  (or (and pdf-misc-print-programm
+           (executable-find pdf-misc-print-programm))
+      (when interactive-p
+        (let* ((default (car (delq nil (mapcar
+                                        'executable-find
+                                        '("gtklp" "xpp" "gpr")))))
+               buffer-file-name
+               (programm
+                (expand-file-name
+                 (read-file-name
+                  "Print with: " default nil t nil 'file-executable-p))))
+          (when (and programm
+                     (executable-find programm))
+            (when (y-or-n-p "Save choice using customize ?")
+              (customize-save-variable
+               'pdf-misc-print-programm programm))
+            (setq pdf-misc-print-programm programm))))))
+
+(defun pdf-misc-print-document (filename &optional interactive-p)
+  (interactive
+   (list (pdf-view-buffer-file-name) t))
+  (cl-check-type filename (and string file-readable))
+  (let ((programm (pdf-misc-print-programm interactive-p))
+        (args (append pdf-misc-print-programm-args (list filename))))
+    (unless programm
+      (error "No print program available"))
+    (apply #'start-process "printing" nil programm args)
+    (message "Print job started: %s %s"
+             programm (mapconcat #'identity args " "))))
+
+
+(provide 'pdf-misc)
+
+;;; pdf-misc.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-occur.el b/elpa/pdf-tools-20191031.642/pdf-occur.el
new file mode 100644 (file)
index 0000000..f2572e9
--- /dev/null
@@ -0,0 +1,826 @@
+;;; pdf-occur.el --- Display matching lines of PDF documents. -*- lexical-binding: t -*-
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, multimedia
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+(require 'pdf-tools)
+(require 'pdf-view)
+(require 'pdf-util)
+(require 'pdf-info)
+(require 'pdf-isearch)
+(require 'tablist)
+(require 'ibuf-ext)
+(require 'dired)
+(require 'let-alist)
+
+;;; Code:
+
+
+
+\f
+;; * ================================================================== *
+;; * Custom & Variables
+;; * ================================================================== *
+
+(defgroup pdf-occur nil
+  "Display matching lines of PDF documents."
+  :group 'pdf-tools)
+
+(defface pdf-occur-document-face
+  '((default (:inherit font-lock-string-face)))
+  "Face used to highlight documents in the list buffer."
+  :group 'pdf-occur)
+
+(defface pdf-occur-page-face
+  '((default (:inherit font-lock-type-face)))
+  "Face used to highlight page numbers in the list buffer."
+  :group 'pdf-occur)
+
+(defcustom pdf-occur-search-batch-size 16
+  "Maximum number of pages searched in one query.
+
+Lower numbers will make Emacs more responsive when searching at
+the cost of slightly increased search time."
+  :group 'pdf-occur
+  :type 'integer)
+
+(defcustom pdf-occur-prefer-string-search nil
+  "If non-nil, reverse the meaning of the regexp-p prefix-arg."
+  :group 'pdf-occur
+  :type 'boolean)
+
+(defvar pdf-occur-history nil
+  "The history variable for search strings.")
+
+(defvar pdf-occur-search-pages-left nil
+  "The total number of pages left to search.")
+
+(defvar pdf-occur-search-documents nil
+  "The list of searched documents.
+
+Each element should be either the filename of a PDF document or a
+cons \(FILENAME . PAGES\), where PAGES is the list of pages to
+search.  See `pdf-info-normalize-page-range' for it's format.")
+
+(defvar pdf-occur-number-of-matches 0
+  "The number of matches in all searched documents.")
+
+(defvar pdf-occur-search-string nil
+  "The currently used search string, resp. regexp.")
+
+(defvar pdf-occur-search-regexp-p nil
+  "Non-nil, if searching for a regexp.")
+
+(defvar pdf-occur-buffer-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (set-keymap-parent kmap tablist-mode-map)
+    (define-key kmap (kbd "RET") 'pdf-occur-goto-occurrence)
+    (define-key kmap (kbd "C-o") 'pdf-occur-view-occurrence)
+    (define-key kmap (kbd "SPC") 'pdf-occur-view-occurrence)
+    (define-key kmap (kbd "C-c C-f") 'next-error-follow-minor-mode)
+    (define-key kmap (kbd "g") 'pdf-occur-revert-buffer-with-args)
+    (define-key kmap (kbd "K") 'pdf-occur-abort-search)
+    (define-key kmap (kbd "D") 'pdf-occur-tablist-do-delete)
+    (define-key kmap (kbd "x") 'pdf-occur-tablist-do-flagged-delete)
+    (define-key kmap (kbd "A") 'pdf-occur-tablist-gather-documents)
+    kmap)
+  "The keymap used for `pdf-occur-buffer-mode'.")
+
+\f
+;; * ================================================================== *
+;; * High level functions
+;; * ================================================================== *
+
+(define-derived-mode pdf-occur-buffer-mode tablist-mode "PDFOccur"
+  "Major mode for output from `pdf-occur`. \\<pdf-occur-buffer-mode-map>
+
+Some useful keys are:
+
+\\[pdf-occur-abort-search] - Abort the search.
+\\[pdf-occur-revert-buffer-with-args] - Restart the search.
+\\[universal-argument] \\[pdf-occur-revert-buffer-with-args] - Restart search with different regexp.
+\\[universal-argument] \\[universal-argument] \\[pdf-occur-revert-buffer-with-args] - Same, but do a plain string search.
+
+\\[tablist-push-regexp-filter] - Filter matches by regexp on current or prefix-th column.
+\\[tablist-pop-filter] - Remove last added filter.
+
+\\[pdf-occur-tablist-do-delete] - Remove the current file from the search.
+\\[pdf-occur-tablist-gather-documents] - Include marked files from displayed `dired'/`ibuffer' and
+    `pdf-view-mode' buffers in the search.
+
+\\{pdf-occur-buffer-mode-map}"
+  (setq-local next-error-function 'pdf-occur-next-error)
+  (setq-local revert-buffer-function
+              'pdf-occur-revert-buffer)
+  (setq next-error-last-buffer (current-buffer))
+  (setq-local tabulated-list-sort-key nil)
+  (setq-local tabulated-list-use-header-line t)
+  (setq-local tablist-operations-function
+              (lambda (op &rest _)
+                (cl-case op
+                  (supported-operations '(find-entry))
+                  (find-entry
+                   (let ((display-buffer-overriding-action
+                          '(display-buffer-same-window)))
+                     (pdf-occur-goto-occurrence)))))))
+
+;;;###autoload
+(defun pdf-occur (string &optional regexp-p)
+  "List lines matching STRING or PCRE.
+
+Interactively search for a regexp. Unless a prefix arg was given,
+in which case this functions performs a string search.
+
+If `pdf-occur-prefer-string-search' is non-nil, the meaning of
+the prefix-arg is inverted."
+  (interactive
+   (progn
+     (pdf-util-assert-pdf-buffer)
+     (list
+      (pdf-occur-read-string
+       (pdf-occur-want-regexp-search-p))
+      (pdf-occur-want-regexp-search-p))))
+  (pdf-util-assert-pdf-buffer)
+  (pdf-occur-search (list (current-buffer)) string regexp-p))
+
+(defvar ibuffer-filtering-qualifiers)
+;;;###autoload
+(defun pdf-occur-multi-command ()
+  "Perform `pdf-occur' on multiple buffer.
+
+For a programmatic search of multiple documents see
+`pdf-occur-search'."
+  (interactive)
+  (ibuffer)
+  (with-current-buffer "*Ibuffer*"
+    (pdf-occur-ibuffer-minor-mode)
+    (unless (member '(derived-mode . pdf-view-mode)
+                    ibuffer-filtering-qualifiers)
+      (ibuffer-filter-by-derived-mode 'pdf-view-mode))
+    (message
+     "%s"
+     (substitute-command-keys
+      "Mark a bunch of PDF buffers and type \\[pdf-occur-ibuffer-do-occur]"))
+    (sit-for 3)))
+
+(defun pdf-occur-revert-buffer (&rest _)
+  "Restart the search."
+  (pdf-occur-assert-occur-buffer-p)
+  (unless pdf-occur-search-documents
+    (error "No documents to search"))
+  (unless pdf-occur-search-string
+    (error "Nothing to search for"))
+  (let* ((2-columns-p (= 1 (length pdf-occur-search-documents)))
+         (filename-width
+          (min 24
+               (apply 'max
+                 (mapcar 'length
+                         (mapcar 'pdf-occur-abbrev-document
+                                 (mapcar 'car pdf-occur-search-documents))))))
+         (page-sorter (tablist-generate-sorter
+                       (if 2-columns-p 0 1)
+                       '<
+                       'string-to-number)))
+    (setq tabulated-list-format
+          (if 2-columns-p
+              `[("Page" 4 ,page-sorter :right-align t)
+                ("Line" 0 t)]
+            `[("Document" ,filename-width t)
+              ("Page" 4 ,page-sorter :right-align t)
+              ("Line" 0 t)])
+          tabulated-list-entries nil))
+  (tabulated-list-revert)
+  (pdf-occur-start-search
+   pdf-occur-search-documents
+   pdf-occur-search-string
+   pdf-occur-search-regexp-p)
+  (pdf-occur-update-header-line)
+  (setq mode-line-process
+        '(:propertize ":run" face compilation-mode-line-run)))
+
+(defun pdf-occur-revert-buffer-with-args (string &optional regexp-p documents)
+  "Restart the search with modified arguments.
+
+Interactively just restart the search, unless a prefix was given.
+In this case read a new search string.  With `C-u C-u' as prefix
+additionally invert the current state of
+`pdf-occur-search-regexp-p'."
+  (interactive
+   (progn
+     (pdf-occur-assert-occur-buffer-p)
+     (cond
+      (current-prefix-arg
+       (let ((regexp-p
+              (if (equal current-prefix-arg '(16))
+                  (not pdf-occur-search-regexp-p)
+                pdf-occur-search-regexp-p)))
+         (list
+          (pdf-occur-read-string regexp-p)
+          regexp-p)))
+      (t
+       (list pdf-occur-search-string
+             pdf-occur-search-regexp-p)))))
+  (setq pdf-occur-search-string string
+        pdf-occur-search-regexp-p regexp-p)
+  (when documents
+    (setq pdf-occur-search-documents
+          (pdf-occur-normalize-documents documents)))
+  (pdf-occur-revert-buffer))
+
+(defun pdf-occur-abort-search ()
+  "Abort the current search.
+
+This immediately kills the search process."
+  (interactive)
+  (unless (pdf-occur-search-in-progress-p)
+    (user-error "No search in progress"))
+  (pdf-info-kill-local-server)
+  (pdf-occur-search-finished t))
+
+\f
+;; * ================================================================== *
+;; * Finding occurrences
+;; * ================================================================== *
+
+
+(defun pdf-occur-goto-occurrence (&optional no-select-window-p)
+  "Go to the occurrence at point.
+
+If EVENT is nil, use occurrence at current line.  Select the
+PDF's window, unless NO-SELECT-WINDOW-P is non-nil.
+
+FIXME: EVENT not used at the moment."
+  (interactive)
+  (let ((item (tabulated-list-get-id)))
+    (when item
+      (let* ((doc (plist-get item :document))
+             (page (plist-get item :page))
+             (match (plist-get item :match-edges))
+             (buffer (if (bufferp doc)
+                         doc
+                       (or (find-buffer-visiting doc)
+                           (find-file-noselect doc))))
+             window)
+        (if no-select-window-p
+            (setq window (display-buffer buffer))
+          (pop-to-buffer buffer)
+          (setq window (selected-window)))
+        (with-selected-window window
+          (when page
+            (pdf-view-goto-page page))
+          ;; Abuse isearch.
+          (when match
+            (let ((pixel-match
+                   (pdf-util-scale-relative-to-pixel match))
+                  (pdf-isearch-batch-mode t))
+              (pdf-isearch-hl-matches pixel-match nil t)
+              (pdf-isearch-focus-match-batch pixel-match))))))))
+
+(defun pdf-occur-view-occurrence (&optional _event)
+  "View the occurrence at EVENT.
+
+If EVENT is nil, use occurrence at current line."
+  (interactive (list last-nonmenu-event))
+  (pdf-occur-goto-occurrence t))
+
+(defun pdf-occur-next-error (&optional arg reset)
+  "Move to the Nth (default 1) next match in an PDF Occur mode buffer.
+Compatibility function for \\[next-error] invocations."
+  (interactive "p")
+  ;; we need to run pdf-occur-find-match from within the Occur buffer
+  (with-current-buffer
+      ;; Choose the buffer and make it current.
+      (if (next-error-buffer-p (current-buffer))
+          (current-buffer)
+        (next-error-find-buffer
+         nil nil
+         (lambda ()
+           (eq major-mode 'pdf-occur-buffer-mode))))
+    (when (bobp)
+      (setq reset t))
+    (if reset
+        (goto-char (point-min))
+      (beginning-of-line))
+    (when (/= arg 0)
+      (when (eobp)
+        (forward-line -1))
+      (when reset
+        (cl-decf arg))
+      (let ((line (line-number-at-pos))
+            (limit (line-number-at-pos
+                    (if (>= arg 0)
+                        (1- (point-max))
+                      (point-min)))))
+        (when (= line limit)
+          (error "No more matches"))
+        (forward-line
+         (if (>= arg 0)
+             (min arg (- limit line))
+           (max arg (- limit line))))))
+    ;; In case the *Occur* buffer is visible in a nonselected window.
+    (tablist-move-to-major-column)
+    (let ((win (get-buffer-window (current-buffer) t)))
+      (if win (set-window-point win (point))))
+    (pdf-occur-goto-occurrence)))
+
+\f
+;; * ================================================================== *
+;; * Integration with other modes
+;; * ================================================================== *
+
+;;;###autoload
+(define-minor-mode pdf-occur-global-minor-mode
+  "Enable integration of Pdf Occur with other modes.
+
+This global minor mode enables (or disables)
+`pdf-occur-ibuffer-minor-mode' and `pdf-occur-dired-minor-mode'
+in all current and future ibuffer/dired buffer."  nil nil nil
+:global t
+  (let ((arg (if pdf-occur-global-minor-mode 1 -1)))
+    (dolist (buf (buffer-list))
+      (with-current-buffer buf
+        (cond
+         ((derived-mode-p 'dired-mode)
+          (pdf-occur-dired-minor-mode arg))
+         ((derived-mode-p 'ibuffer-mode)
+          (pdf-occur-ibuffer-minor-mode arg)))))
+    (cond
+     (pdf-occur-global-minor-mode
+      (add-hook 'dired-mode-hook 'pdf-occur-dired-minor-mode)
+      (add-hook 'ibuffer-mode-hook 'pdf-occur-ibuffer-minor-mode))
+     (t
+      (remove-hook 'dired-mode-hook 'pdf-occur-dired-minor-mode)
+      (remove-hook 'ibuffer-mode-hook 'pdf-occur-ibuffer-minor-mode)))))
+
+(defvar pdf-occur-ibuffer-minor-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [remap ibuffer-do-occur] 'pdf-occur-ibuffer-do-occur)
+    map)
+  "Keymap used in `pdf-occur-ibuffer-minor-mode'.")
+
+;;;###autoload
+(define-minor-mode pdf-occur-ibuffer-minor-mode
+  "Hack into ibuffer's do-occur binding.
+
+This mode remaps `ibuffer-do-occur' to
+`pdf-occur-ibuffer-do-occur', which will start the PDF Tools
+version of `occur', if all marked buffer's are in `pdf-view-mode'
+and otherwise fallback to `ibuffer-do-occur'."
+
+  nil nil nil)
+
+(defun pdf-occur-ibuffer-do-occur (&optional regexp-p)
+  "Uses `pdf-occur-search', if appropriate.
+
+I.e. all marked buffers are in PDFView mode."
+  (interactive
+   (list (pdf-occur-want-regexp-search-p)))
+  (let* ((buffer (or (ibuffer-get-marked-buffers)
+                     (and (ibuffer-current-buffer)
+                          (list (ibuffer-current-buffer)))))
+         (pdf-only-p (cl-every
+                      (lambda (buf)
+                        (with-current-buffer buf
+                          (derived-mode-p 'pdf-view-mode)))
+                      buffer)))
+    (if (not pdf-only-p)
+        (call-interactively 'ibuffer-do-occur)
+      (let ((regexp (pdf-occur-read-string regexp-p)))
+        (pdf-occur-search buffer regexp regexp-p)))))
+
+(defvar pdf-occur-dired-minor-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [remap dired-do-search] 'pdf-occur-dired-do-search)
+    map)
+  "Keymap used in `pdf-occur-dired-minor-mode'.")
+
+;;;###autoload
+(define-minor-mode pdf-occur-dired-minor-mode
+  "Hack into dired's `dired-do-search' binding.
+
+This mode remaps `dired-do-search' to
+`pdf-occur-dired-do-search', which will start the PDF Tools
+version of `occur', if all marked buffer's are in `pdf-view-mode'
+and otherwise fallback to `dired-do-search'."
+
+  nil nil nil)
+
+(defun pdf-occur-dired-do-search ()
+  "Uses `pdf-occur-search', if appropriate.
+
+I.e. all marked files look like PDF documents."
+  (interactive)
+  (let ((files (dired-get-marked-files)))
+    (if (not (cl-every (lambda (file)
+                         (string-match-p
+                          (car pdf-tools-auto-mode-alist-entry)
+                          file))
+                       files))
+        (call-interactively 'dired-do-search)
+      (let* ((regex-p (pdf-occur-want-regexp-search-p))
+             (regexp (pdf-occur-read-string regex-p)))
+        (pdf-occur-search files regexp regex-p)))))
+
+
+\f
+;; * ================================================================== *
+;; * Search engine
+;; * ================================================================== *
+
+
+(defun pdf-occur-search (documents string &optional regexp-p)
+  "Search DOCUMENTS for STRING.
+
+DOCUMENTS should be a list of buffers (objects, not names),
+filenames or conses \(BUFFER-OR-FILENAME . PAGES\), where PAGES
+determines the scope of the search of the respective document.
+See `pdf-info-normalize-page-range' for it's format.
+
+STRING is either the string to search for or, if REGEXP-P is
+non-nil, a Perl compatible regular expression (PCRE).
+
+Display the occur buffer and start the search asynchronously.
+
+Returns the window where the buffer is displayed."
+
+  (unless documents
+    (error "No documents to search"))
+  (when (or (null string) (= (length string) 0))
+    (error "Not searching for the empty string"))
+  (with-current-buffer (get-buffer-create "*PDF-Occur*")
+    (pdf-occur-buffer-mode)
+    (setq-local pdf-occur-search-documents
+                (pdf-occur-normalize-documents documents))
+    (setq-local pdf-occur-search-string string)
+    (setq-local pdf-occur-search-regexp-p regexp-p)
+    (setq-local pdf-occur-search-pages-left 0)
+    (setq-local pdf-occur-number-of-matches 0)
+    (pdf-occur-revert-buffer)
+    (display-buffer
+     (current-buffer))))
+
+(defadvice tabulated-list-init-header (after update-header activate)
+  "We want our own headers, thank you."
+  (when (derived-mode-p 'pdf-occur-buffer-mode)
+    (save-current-buffer
+      (with-no-warnings (pdf-occur-update-header-line)))))
+
+(defun pdf-occur-create-entry (filename page &optional match)
+  "Create a `tabulated-list-entries' entry for a search result.
+
+If match is nil, create a fake entry for documents w/o any
+matches linked with PAGE."
+  (let* ((text (or (car match) "[No matches]"))
+         (edges (cdr match))
+         (displayed-text
+          (if match
+              (replace-regexp-in-string "\n" "\\n" text t t)
+            (propertize text 'face 'font-lock-warning-face)))
+         (displayed-page
+          (if match
+              (propertize (format "%d" page)
+                          'face 'pdf-occur-page-face)
+            ""))
+         (displayed-document
+          (propertize
+           (pdf-occur-abbrev-document filename)
+           'face 'pdf-occur-document-face))
+         (id `(:document ,filename
+               :page ,page
+               :match-text ,(if match text)
+               :match-edges ,(if match edges))))
+    (list id
+          (if (= (length pdf-occur-search-documents) 1)
+              (vector displayed-page displayed-text)
+            (vector displayed-document
+                    displayed-page
+                    displayed-text)))))
+
+(defun pdf-occur-update-header-line ()
+  (pdf-occur-assert-occur-buffer-p)
+  (save-current-buffer
+    ;;force-mode-line-update seems to sometimes spuriously change the
+    ;;current buffer.
+    (setq header-line-format
+          `(:eval (concat
+                   (if (= (length pdf-occur-search-documents) 1)
+                       (format "%d match%s in document `%s'"
+                               pdf-occur-number-of-matches
+                               (if (/= 1 pdf-occur-number-of-matches) "es" "")
+                               (pdf-occur-abbrev-document
+                                (caar pdf-occur-search-documents)))
+                     (format "%d match%s in %d documents"
+                             pdf-occur-number-of-matches
+                             (if (/= 1 pdf-occur-number-of-matches) "es" "")
+                             (length pdf-occur-search-documents)))
+                   (if (pdf-occur-search-in-progress-p)
+                       (propertize
+                        (concat " ["
+                                (if (numberp pdf-occur-search-pages-left)
+                                    (format "%d pages left"
+                                            pdf-occur-search-pages-left)
+                                  "Searching")
+                                "]")
+                        'face 'compilation-mode-line-run)))))
+    (force-mode-line-update)))
+
+(defun pdf-occur-search-finished (&optional abort-p)
+  (setq pdf-occur-search-pages-left 0)
+  (setq mode-line-process
+        (if abort-p
+            '(:propertize
+              ":aborted" face compilation-mode-line-fail)
+          '(:propertize
+            ":exit" face compilation-mode-line-exit)))
+  (let ((unmatched
+         (mapcar (lambda (doc)
+                   (pdf-occur-create-entry doc 1))
+                 (cl-set-difference
+                  (mapcar 'car
+                          pdf-occur-search-documents)
+                  (mapcar (lambda (elt)
+                            (plist-get (car elt) :document))
+                          tabulated-list-entries)
+                  :test 'equal))))
+    (when (and unmatched
+               (> (length pdf-occur-search-documents) 1))
+      (pdf-occur-insert-entries unmatched)))
+  (tablist-apply-filter)
+  (pdf-occur-update-header-line)
+  (pdf-isearch-message
+   (if abort-p
+       "Search aborted."
+     (format "Occur search finished with %d matches"
+             pdf-occur-number-of-matches))))
+
+(defun pdf-occur-add-matches (filename matches)
+  (pdf-occur-assert-occur-buffer-p)
+  (when matches
+    (let (entries)
+      (dolist (match matches)
+        (let-alist match
+          (push (pdf-occur-create-entry filename .page (cons .text .edges))
+                entries)))
+      (setq entries (nreverse entries))
+      (pdf-occur-insert-entries entries))))
+
+(defun pdf-occur-insert-entries (entries)
+  "Insert tabulated-list ENTRIES at the end."
+  (pdf-occur-assert-occur-buffer-p)
+  (let ((inhibit-read-only t)
+        (end-of-buffer (and (eobp) (not (bobp)))))
+    (save-excursion
+      (goto-char (point-max))
+      (dolist (elt entries)
+        (apply tabulated-list-printer elt))
+      (set-buffer-modified-p nil))
+    (when end-of-buffer
+      (dolist (win (get-buffer-window-list))
+        (set-window-point win (point-max))))
+    (setq tabulated-list-entries
+          (append tabulated-list-entries
+                  entries))))
+
+(defun pdf-occur-search-in-progress-p ()
+  (and (numberp pdf-occur-search-pages-left)
+       (> pdf-occur-search-pages-left 0)))
+
+(defun pdf-occur-start-search (documents string
+                                         &optional regexp-p)
+  (pdf-occur-assert-occur-buffer-p)
+  (pdf-info-make-local-server nil t)
+  (let ((batches (pdf-occur-create-batches
+                  documents (or pdf-occur-search-batch-size 1))))
+    (pdf-info-local-batch-query
+     (lambda (document pages)
+       (if regexp-p
+           (pdf-info-search-regexp string pages nil document)
+         (pdf-info-search-string string pages document)))
+     (lambda (status response document pages)
+       (if status
+           (error "%s" response)
+         (when (numberp pdf-occur-search-pages-left)
+           (cl-decf pdf-occur-search-pages-left
+                    (1+ (- (cdr pages) (car pages)))))
+         (when (cl-member document pdf-occur-search-documents
+                          :key 'car
+                          :test 'equal)
+           (cl-incf pdf-occur-number-of-matches
+                    (length response))
+           (pdf-occur-add-matches document response)
+           (pdf-occur-update-header-line))))
+     (lambda (status buffer)
+       (when (buffer-live-p buffer)
+         (with-current-buffer buffer
+           (pdf-occur-search-finished (eq status 'killed)))))
+     batches)
+    (setq pdf-occur-number-of-matches 0)
+    (setq pdf-occur-search-pages-left
+          (apply '+ (mapcar (lambda (elt)
+                              (1+ (- (cdr (nth 1 elt))
+                                     (car (nth 1 elt)))))
+                            batches)))))
+
+
+\f
+;; * ================================================================== *
+;; * Editing searched documents
+;; * ================================================================== *
+
+(defun pdf-occur-tablist-do-delete (&optional arg)
+  "Delete ARG documents from the search list."
+  (interactive "P")
+  (when (pdf-occur-search-in-progress-p)
+    (user-error "Can't delete while a search is in progress."))
+  (let* ((items (tablist-get-marked-items arg))
+         (documents (cl-remove-duplicates
+                     (mapcar (lambda (entry)
+                               (plist-get (car entry) :document))
+                             items)
+                     :test 'equal)))
+    (unless documents
+      (error "No documents selected"))
+    (when (tablist-yes-or-no-p
+           'Stop\ searching
+           nil (mapcar (lambda (d) (cons nil (vector d)))
+                       documents))
+      (setq pdf-occur-search-documents
+            (cl-remove-if (lambda (elt)
+                            (member (car elt) documents))
+                          pdf-occur-search-documents)
+            tabulated-list-entries
+            (cl-remove-if (lambda (elt)
+                            (when (member (plist-get (car elt) :document)
+                                          documents)
+                              (when (plist-get (car elt) :match-edges)
+                                (cl-decf pdf-occur-number-of-matches))
+                              t))
+                          tabulated-list-entries))
+      (tablist-revert)
+      (pdf-occur-update-header-line)
+      (tablist-move-to-major-column))))
+
+(defun pdf-occur-tablist-do-flagged-delete (&optional interactive)
+  "Stop searching all documents marked with a D."
+  (interactive "p")
+  (let* ((tablist-marker-char ?D))
+    (if (save-excursion
+          (goto-char (point-min))
+          (re-search-forward (tablist-marker-regexp) nil t))
+        (pdf-occur-tablist-do-delete)
+      (or (not interactive)
+          (message "(No deletions requested)")))))
+
+(defun pdf-occur-tablist-gather-documents ()
+  "Gather marked documents in windows.
+
+Examine all dired/ibuffer windows and offer to put marked files
+in the search list."
+  (interactive)
+  (let ((searched (mapcar 'car pdf-occur-search-documents))
+        files)
+    (dolist (win (window-list))
+      (with-selected-window win
+        (cond
+         ((derived-mode-p 'dired-mode)
+          (let ((marked (dired-get-marked-files nil nil nil t)))
+            (when (> (length marked) 1)
+              (when (eq t (car marked))
+                (setq marked (cdr marked)))
+              (setq files
+                    (append files marked nil)))))
+         ((derived-mode-p 'ibuffer-mode)
+          (dolist (fname (mapcar 'buffer-file-name
+                                 (ibuffer-get-marked-buffers)))
+            (when fname
+              (push fname files))))
+         ((and (derived-mode-p 'pdf-view-mode)
+               (buffer-file-name))
+          (push (buffer-file-name) files)))))
+
+    (setq files
+          (cl-sort                      ;Looks funny.
+           (cl-set-difference
+            (cl-remove-duplicates
+             (cl-remove-if-not
+              (lambda (file) (string-match-p
+                              (car pdf-tools-auto-mode-alist-entry)
+                              file))
+              files)
+             :test 'file-equal-p)
+            searched
+            :test 'file-equal-p)
+           'string-lessp))
+    (if (null files)
+        (message "No marked, new PDF files found in windows")
+      (when (tablist-yes-or-no-p
+             'add nil (mapcar (lambda (file)
+                                (cons nil (vector file)))
+                              (cl-sort files 'string-lessp)))
+        (setq pdf-occur-search-documents
+              (append pdf-occur-search-documents
+                      (pdf-occur-normalize-documents files)))
+        (message "Added %d file%s to the list of searched documents%s"
+                 (length files)
+                 (dired-plural-s (length files))
+                 (substitute-command-keys
+                  " - Hit \\[pdf-occur-revert-buffer-with-args]"))))))
+
+\f
+;; * ================================================================== *
+;; * Utilities
+;; * ================================================================== *
+
+(defun pdf-occur-read-string (&optional regexp-p)
+  (read-string
+   (concat
+    (format "List lines %s"
+            (if regexp-p "matching PCRE" "containing string"))
+    (if pdf-occur-search-string
+        (format " (default %s)" pdf-occur-search-string))
+    ": ")
+   nil 'pdf-occur-history pdf-occur-search-string))
+
+(defun pdf-occur-assert-occur-buffer-p ()
+  (unless (derived-mode-p 'pdf-occur-buffer-mode)
+    (error "Not in PDF occur buffer")))
+
+(defun pdf-occur-want-regexp-search-p ()
+  (or (and current-prefix-arg
+           pdf-occur-prefer-string-search)
+      (and (null current-prefix-arg)
+           (not pdf-occur-prefer-string-search))))
+
+;; FIXME: This will be confusing when searching documents with the
+;; same base file-name.
+(defun pdf-occur-abbrev-document (file-or-buffer)
+  (if (bufferp file-or-buffer)
+      (buffer-name file-or-buffer)
+    (let ((abbrev (file-name-nondirectory file-or-buffer)))
+      (if (> (length abbrev) 0)
+          abbrev
+        file-or-buffer))))
+
+(defun pdf-occur-create-batches (documents batch-size)
+  (let (queries)
+    (dolist (d documents)
+      (let* ((file-or-buffer (car d))
+             (pages (pdf-info-normalize-page-range (cdr d)))
+             (first (car pages))
+             (last (if (eq (cdr pages) 0)
+                       (pdf-info-number-of-pages file-or-buffer)
+                     (cdr pages)))
+             (npages (1+ (- last first)))
+             (nbatches (ceiling
+                        (/ (float npages) batch-size))))
+        (dotimes (i nbatches)
+          (push
+           (list file-or-buffer
+                 (cons (+ first (* i batch-size))
+                       (min last (+ first (1- (* (1+ i) batch-size))))))
+           queries))))
+    (nreverse queries)))
+
+(defun pdf-occur-normalize-documents (documents)
+  "Normalize list of documents.
+
+Replaces buffers with their associated filenames \(if
+applicable\) and ensures that every element looks like
+\(FILENAME-OR-BUFFER . PAGES\)."
+  (cl-sort (mapcar (lambda (doc)
+                     (unless (consp doc)
+                       (setq doc (cons doc nil)))
+                     (when (and (bufferp (car doc))
+                                (buffer-file-name (car doc)))
+                       (setq doc (cons (buffer-file-name (car doc))
+                                       (cdr doc))))
+                     (if (stringp (car doc))
+                         (cons (expand-file-name (car doc)) (cdr doc))
+                       doc))
+                   documents)
+           (lambda (a b) (string-lessp
+                          (if (bufferp a) (buffer-name a) a)
+                          (if (bufferp b) (buffer-name b) b)))
+           :key 'car))
+
+(provide 'pdf-occur)
+
+;;; pdf-occur.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-outline.el b/elpa/pdf-tools-20191031.642/pdf-outline.el
new file mode 100644 (file)
index 0000000..e141ca8
--- /dev/null
@@ -0,0 +1,598 @@
+;;; pdf-outline.el --- Outline for PDF buffer -*- lexical-binding: t -*-
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, multimedia
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+(require 'outline)
+(require 'pdf-links)
+(require 'pdf-view)
+(require 'pdf-util)
+(require 'cl-lib)
+(require 'imenu)
+(require 'let-alist)
+
+;;; Code:
+
+;;
+;; User options
+;;
+
+(defgroup pdf-outline nil
+  "Display a navigatable outline of a PDF document."
+  :group 'pdf-tools)
+
+(defcustom pdf-outline-buffer-indent 2
+  "The level of indent in the Outline buffer."
+  :type 'integer
+  :group 'pdf-outline)
+
+(defcustom pdf-outline-enable-imenu t
+  "Whether `imenu' should be enabled in PDF documents."
+  :group 'pdf-outline
+  :type '(choice (const :tag "Yes" t)
+                 (const :tag "No" nil)))
+
+(defcustom pdf-outline-imenu-keep-order t
+  "Whether `imenu' should be advised not to reorder the outline."
+  :group 'pdf-outline
+  :type '(choice (const :tag "Yes" t)
+                 (const :tag "No" nil)))
+
+(defcustom pdf-outline-imenu-use-flat-menus nil
+  "Whether the constructed Imenu should be a list, rather than a tree."
+  :group 'pdf-outline
+  :type '(choice (const :tag "Yes" t)
+                 (const :tag "No" nil)))
+
+(defcustom pdf-outline-display-buffer-action '(nil . nil)
+  "The display action used, when displaying the outline buffer."
+  :group 'pdf-outline
+  :type display-buffer--action-custom-type)
+
+(defcustom pdf-outline-display-labels nil
+  "Whether the outline should display labels instead of page numbers.
+
+Usually a page's label is it's displayed page number."
+  :group 'pdf-outline
+  :type 'boolean)
+
+(defvar pdf-outline-minor-mode-map
+  (let ((km (make-sparse-keymap)))
+    (define-key km (kbd "o") 'pdf-outline)
+    km)
+  "Keymap used for `pdf-outline-minor-mode'.")
+
+(defvar pdf-outline-buffer-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (dotimes (i 10)
+      (define-key kmap (vector (+ i ?0)) 'digit-argument))
+    (define-key kmap "-" 'negative-argument)
+    (define-key kmap (kbd "p") 'previous-line)
+    (define-key kmap (kbd "n") 'next-line)
+    (define-key kmap (kbd "b") 'outline-backward-same-level)
+    (define-key kmap (kbd "d") 'hide-subtree)
+    (define-key kmap (kbd "a") 'show-all)
+    (define-key kmap (kbd "s") 'show-subtree)
+    (define-key kmap (kbd "f") 'outline-forward-same-level)
+    (define-key kmap (kbd "u") 'pdf-outline-up-heading)
+    (define-key kmap (kbd "Q") 'hide-sublevels)
+    (define-key kmap (kbd "<") 'beginning-of-buffer)
+    (define-key kmap (kbd ">") 'pdf-outline-end-of-buffer)
+    (define-key kmap (kbd "TAB") 'outline-toggle-children)
+    (define-key kmap (kbd "RET") 'pdf-outline-follow-link)
+    (define-key kmap (kbd "C-o") 'pdf-outline-display-link)
+    (define-key kmap (kbd "SPC") 'pdf-outline-display-link)
+    (define-key kmap [mouse-1] 'pdf-outline-mouse-display-link)
+    (define-key kmap (kbd "o") 'pdf-outline-select-pdf-window)
+    (define-key kmap (kbd ".") 'pdf-outline-move-to-current-page)
+    ;; (define-key kmap (kbd "Q") 'pdf-outline-quit)
+    (define-key kmap (kbd "C-c C-q") 'pdf-outline-quit-and-kill)
+    (define-key kmap (kbd "q") 'quit-window)
+    (define-key kmap (kbd "M-RET") 'pdf-outline-follow-link-and-quit)
+    (define-key kmap (kbd "C-c C-f") 'pdf-outline-follow-mode)
+    kmap)
+  "Keymap used in `pdf-outline-buffer-mode'.")
+
+;;
+;; Internal Variables
+;;
+
+(define-button-type 'pdf-outline
+  'face nil
+  'keymap nil)
+
+(defvar-local pdf-outline-pdf-window nil
+  "The PDF window corresponding to this outline buffer.")
+
+(defvar-local pdf-outline-pdf-document nil
+  "The PDF filename or buffer corresponding to this outline
+  buffer.")
+
+(defvar-local pdf-outline-follow-mode-last-link nil)
+
+;;
+;; Functions
+;;
+
+;;;###autoload
+(define-minor-mode pdf-outline-minor-mode
+  "Display an outline of a PDF document.
+
+This provides a PDF's outline on the menu bar via imenu.
+Additionally the same outline may be viewed in a designated
+buffer.
+
+\\{pdf-outline-minor-mode-map}"
+  nil nil nil
+  (pdf-util-assert-pdf-buffer)
+  (cond
+   (pdf-outline-minor-mode
+    (when pdf-outline-enable-imenu
+      (pdf-outline-imenu-enable)))
+   (t
+    (when pdf-outline-enable-imenu
+      (pdf-outline-imenu-disable)))))
+
+(define-derived-mode pdf-outline-buffer-mode outline-mode "PDF Outline"
+  "View and traverse the outline of a PDF file.
+
+Press \\[pdf-outline-display-link] to display the PDF document,
+\\[pdf-outline-select-pdf-window] to select it's window,
+\\[pdf-outline-move-to-current-page] to move to the outline item
+of the current page, \\[pdf-outline-follow-link] to goto the
+corresponding page or \\[pdf-outline-follow-link-and-quit] to
+additionally quit the Outline.
+
+\\[pdf-outline-follow-mode] enters a variant of
+`next-error-follow-mode'.  Most `outline-mode' commands are
+rebound to their respective last character.
+
+\\{pdf-outline-buffer-mode-map}"
+  (setq-local outline-regexp "\\( *\\).")
+  (setq-local outline-level
+              (lambda nil (1+ (/ (length (match-string 1))
+                                 pdf-outline-buffer-indent))))
+
+  (toggle-truncate-lines 1)
+  (setq buffer-read-only t)
+  (when (> (count-lines 1 (point-max))
+           (* 1.5 (frame-height)))
+    (hide-sublevels 1))
+  (message "%s"
+           (substitute-command-keys
+            (concat
+             "Try \\[pdf-outline-display-link], "
+             "\\[pdf-outline-select-pdf-window], "
+             "\\[pdf-outline-move-to-current-page] or "
+             "\\[pdf-outline-follow-link-and-quit]"))))
+
+(define-minor-mode pdf-outline-follow-mode
+  "Display links as point moves."
+  nil nil nil
+  (setq pdf-outline-follow-mode-last-link nil)
+  (cond
+   (pdf-outline-follow-mode
+    (add-hook 'post-command-hook 'pdf-outline-follow-mode-pch nil t))
+   (t
+    (remove-hook 'post-command-hook 'pdf-outline-follow-mode-pch t))))
+
+(defun pdf-outline-follow-mode-pch ()
+  (let ((link (pdf-outline-link-at-pos (point))))
+    (when (and link
+               (not (eq link pdf-outline-follow-mode-last-link)))
+      (setq pdf-outline-follow-mode-last-link link)
+      (pdf-outline-display-link (point)))))
+
+;;;###autoload
+(defun pdf-outline (&optional buffer no-select-window-p)
+  "Display an PDF outline of BUFFER.
+
+BUFFER defaults to the current buffer.  Select the outline
+buffer, unless NO-SELECT-WINDOW-P is non-nil."
+  (interactive (list nil (or current-prefix-arg
+                             (consp last-nonmenu-event))))
+  (let ((win
+         (display-buffer
+          (pdf-outline-noselect buffer)
+          pdf-outline-display-buffer-action)))
+    (unless no-select-window-p
+      (select-window win))))
+
+(defun pdf-outline-noselect (&optional buffer)
+  "Create an PDF outline of BUFFER, but don't display it."
+  (save-current-buffer
+    (and buffer (set-buffer buffer))
+    (pdf-util-assert-pdf-buffer)
+    (let* ((pdf-buffer (current-buffer))
+           (pdf-file (pdf-view-buffer-file-name))
+           (pdf-window (and (eq pdf-buffer (window-buffer))
+                            (selected-window)))
+           (bname (pdf-outline-buffer-name))
+           (buffer-exists-p (get-buffer bname))
+           (buffer (get-buffer-create bname)))
+      (with-current-buffer buffer
+        (unless buffer-exists-p
+          (when (= 0 (save-excursion
+                       (pdf-outline-insert-outline pdf-buffer)))
+            (kill-buffer buffer)
+            (error "PDF has no outline"))
+          (pdf-outline-buffer-mode))
+        (set (make-local-variable 'other-window-scroll-buffer)
+             pdf-buffer)
+        (setq pdf-outline-pdf-window pdf-window
+              pdf-outline-pdf-document (or pdf-file pdf-buffer))
+        (current-buffer)))))
+
+(defun pdf-outline-buffer-name (&optional pdf-buffer)
+  (unless pdf-buffer (setq pdf-buffer (current-buffer)))
+  (let ((buf (format "*Outline %s*" (buffer-name pdf-buffer))))
+    ;; (when (buffer-live-p (get-buffer buf))
+    ;;   (kill-buffer buf))
+    buf))
+
+(defun pdf-outline-insert-outline (pdf-buffer)
+  (let ((labels (and pdf-outline-display-labels
+                     (pdf-info-pagelabels pdf-buffer)))
+        (nitems 0))
+    (dolist (item (pdf-info-outline pdf-buffer))
+      (let-alist item
+        (when (eq .type 'goto-dest)
+          (insert-text-button
+           (concat
+            (make-string (* (1- .depth) pdf-outline-buffer-indent) ?\s)
+            .title
+            (if (> .page 0)
+                (format " (%s)"
+                        (if labels
+                            (nth (1- .page) labels)
+                          .page))
+              "(invalid)"))
+           'type 'pdf-outline
+           'help-echo (pdf-links-action-to-string item)
+           'pdf-outline-link item)
+          (newline)
+          (cl-incf nitems))))
+    nitems))
+
+(defun pdf-outline-get-pdf-window (&optional if-visible-p)
+  (save-selected-window
+    (let* ((buffer (cond
+                    ((buffer-live-p pdf-outline-pdf-document)
+                     pdf-outline-pdf-document)
+                    ((bufferp pdf-outline-pdf-document)
+                     (error "PDF buffer was killed"))
+                    (t
+                     (or
+                      (find-buffer-visiting
+                       pdf-outline-pdf-document)
+                      (find-file-noselect
+                       pdf-outline-pdf-document)))))
+           (pdf-window
+            (if (and (window-live-p pdf-outline-pdf-window)
+                     (eq buffer
+                         (window-buffer pdf-outline-pdf-window)))
+                pdf-outline-pdf-window
+              (or (get-buffer-window buffer)
+                  (and (null if-visible-p)
+                       (display-buffer
+                        buffer
+                        '(nil (inhibit-same-window . t))))))))
+      (setq pdf-outline-pdf-window pdf-window))))
+
+\f
+;;
+;; Commands
+;;
+
+(defun pdf-outline-move-to-current-page ()
+  "Move to the item corresponding to the current page.
+
+Open nodes as necessary."
+  (interactive)
+  (let (page)
+    (with-selected-window (pdf-outline-get-pdf-window)
+      (setq page (pdf-view-current-page)))
+    (pdf-outline-move-to-page page)))
+
+(defun pdf-outline-quit-and-kill ()
+  "Quit browsing the outline and kill it's buffer."
+  (interactive)
+  (pdf-outline-quit t))
+
+(defun pdf-outline-quit (&optional kill)
+  "Quit browsing the outline buffer."
+  (interactive "P")
+  (let ((win (selected-window)))
+    (pdf-outline-select-pdf-window t)
+    (quit-window kill win)))
+
+(defun pdf-outline-up-heading (arg &optional invisible-ok)
+  "Like `outline-up-heading', but `push-mark' first."
+  (interactive "p")
+  (let ((pos (point)))
+    (outline-up-heading arg invisible-ok)
+    (unless (= pos (point))
+      (push-mark pos))))
+
+(defun pdf-outline-end-of-buffer ()
+  "Move to the end of the outline buffer."
+  (interactive)
+  (let ((pos (point)))
+    (goto-char (point-max))
+    (when (and (eobp)
+               (not (bobp))
+               (null (button-at (point))))
+      (forward-line -1))
+    (unless (= pos (point))
+      (push-mark pos))))
+
+(defun pdf-outline-link-at-pos (&optional pos)
+  (unless pos (setq pos (point)))
+  (let ((button (or (button-at pos)
+                    (button-at (1- pos)))))
+    (and button
+         (button-get button
+                     'pdf-outline-link))))
+
+(defun pdf-outline-follow-link (&optional pos)
+  "Select PDF window and move to the page corresponding to POS."
+  (interactive)
+  (unless pos (setq pos (point)))
+  (let ((link (pdf-outline-link-at-pos pos)))
+    (unless link
+      (error "Nothing to follow here"))
+    (select-window (pdf-outline-get-pdf-window))
+    (pdf-links-action-perform link)))
+
+(defun pdf-outline-follow-link-and-quit (&optional pos)
+  "Select PDF window and move to the page corresponding to POS.
+
+Then quit the outline window."
+  (interactive)
+  (let ((link (pdf-outline-link-at-pos (or pos (point)))))
+    (pdf-outline-quit)
+    (unless link
+      (error "Nothing to follow here"))
+    (pdf-links-action-perform link)))
+
+(defun pdf-outline-display-link (&optional pos)
+  "Display the page corresponding to the link at POS."
+  (interactive)
+  (unless pos (setq pos (point)))
+  (let ((inhibit-redisplay t)
+        (link (pdf-outline-link-at-pos pos)))
+    (unless link
+      (error "Nothing to follow here"))
+    (with-selected-window (pdf-outline-get-pdf-window)
+      (pdf-links-action-perform link))
+    (force-mode-line-update t)))
+
+(defun pdf-outline-mouse-display-link (event)
+  "Display the page corresponding to the position of EVENT."
+  (interactive "@e")
+  (pdf-outline-display-link
+   (posn-point (event-start event))))
+
+(defun pdf-outline-select-pdf-window (&optional no-create-p)
+  "Display and select the PDF document window."
+  (interactive)
+  (let ((win (pdf-outline-get-pdf-window no-create-p)))
+    (and (window-live-p win)
+         (select-window win))))
+
+(defun pdf-outline-toggle-subtree ()
+  "Toggle hidden state of the current complete subtree."
+  (interactive)
+  (save-excursion
+    (outline-back-to-heading)
+    (if (not (outline-invisible-p (line-end-position)))
+        (hide-subtree)
+      (show-subtree))))
+
+(defun pdf-outline-move-to-page (page)
+  "Move to an outline item corresponding to PAGE."
+  (interactive
+   (list (or (and current-prefix-arg
+                  (prefix-numeric-value current-prefix-arg))
+             (read-number "Page: "))))
+  (goto-char (pdf-outline-position-of-page page))
+  (save-excursion
+    (while (outline-invisible-p)
+      (outline-up-heading 1 t)
+      (show-children)))
+  (save-excursion
+    (when (outline-invisible-p)
+      (outline-up-heading 1 t)
+      (show-children)))
+  (back-to-indentation))
+
+(defun pdf-outline-position-of-page (page)
+  (let (curpage)
+    (save-excursion
+      (goto-char (point-min))
+      (while (and (setq curpage (alist-get 'page (pdf-outline-link-at-pos)))
+                  (< curpage page))
+        (forward-line))
+      (point))))
+
+
+\f
+;;
+;; Imenu Support
+;;
+
+
+;;;###autoload
+(defun pdf-outline-imenu-enable ()
+  "Enable imenu in the current PDF buffer."
+  (interactive)
+  (pdf-util-assert-pdf-buffer)
+  (setq-local imenu-create-index-function
+              (if pdf-outline-imenu-use-flat-menus
+                  'pdf-outline-imenu-create-index-flat
+                'pdf-outline-imenu-create-index-tree))
+  (imenu-add-to-menubar "PDF Outline"))
+
+(defun pdf-outline-imenu-disable ()
+  "Disable imenu in the current PDF buffer."
+  (interactive)
+  (pdf-util-assert-pdf-buffer)
+  (setq-local imenu-create-index-function nil)
+  (local-set-key [menu-bar index] nil)
+  (when (eq pdf-view-mode-map
+            (keymap-parent (current-local-map)))
+    (use-local-map (keymap-parent (current-local-map)))))
+
+
+(defun pdf-outline-imenu-create-item (link &optional labels)
+  (let-alist link
+    (list (format "%s (%s)" .title (if labels
+                                       (nth (1- .page) labels)
+                                     .page))
+          0
+          'pdf-outline-imenu-activate-link
+          link)))
+
+(defun pdf-outline-imenu-create-index-flat ()
+  (let ((labels (and pdf-outline-display-labels
+                     (pdf-info-pagelabels)))
+        index)
+    (dolist (item (pdf-info-outline))
+      (let-alist item
+        (when (eq .type 'goto-dest)
+          (push (pdf-outline-imenu-create-item item labels)
+                index))))
+    (nreverse index)))
+
+
+(defun pdf-outline-imenu-create-index-tree ()
+  (pdf-outline-imenu-create-index-tree-1
+   (pdf-outline-treeify-outline-list
+    (cl-remove-if-not
+     (lambda (type)
+       (eq type 'goto-dest))
+     (pdf-info-outline)
+     :key (apply-partially 'alist-get 'type)))
+   (and pdf-outline-display-labels
+        (pdf-info-pagelabels))))
+
+(defun pdf-outline-imenu-create-index-tree-1 (nodes &optional labels)
+  (mapcar (lambda (node)
+            (let (children)
+              (when (consp (caar node))
+                (setq children (cdr node)
+                      node (car node)))
+              (let ((item
+                     (pdf-outline-imenu-create-item node labels)))
+                (if children
+                    (cons (alist-get 'title node)
+                          (cons item (pdf-outline-imenu-create-index-tree-1
+                                      children labels)))
+                  item))))
+          nodes))
+
+(defun pdf-outline-treeify-outline-list (list)
+  (when list
+    (let ((depth (alist-get 'depth (car list)))
+          result)
+      (while (and list
+                  (>= (alist-get 'depth (car list))
+                      depth))
+        (when (= (alist-get 'depth (car list)) depth)
+          (let ((item (car list)))
+            (when (and (cdr list)
+                       (>  (alist-get 'depth (cadr list))
+                           depth))
+              (setq item
+                    (cons
+                     item
+                     (pdf-outline-treeify-outline-list (cdr list)))))
+            (push item result)))
+        (setq list (cdr list)))
+      (reverse result))))
+
+(defun pdf-outline-imenu-activate-link (&rest args)
+  ;; bug #14029
+  (when (eq (nth 2 args) 'pdf-outline-imenu-activate-link)
+    (setq args (cdr args)))
+  (pdf-links-action-perform (nth 2 args)))
+
+(defadvice imenu--split-menu (around pdf-outline activate)
+  "Advice to keep the original outline order.
+
+ Calls `pdf-outline-imenu--split-menu' instead, if in a PDF
+ buffer and `pdf-outline-imenu-keep-order' is non-nil."
+  (if (not (and (pdf-util-pdf-buffer-p)
+                pdf-outline-imenu-keep-order))
+      ad-do-it
+    (setq ad-return-value
+          (pdf-outline-imenu--split-menu menulist title))))
+
+(defvar imenu--rescan-item)
+(defvar imenu-sort-function)
+(defvar imenu-create-index-function)
+(defvar imenu-max-items)
+
+(defun pdf-outline-imenu--split-menu (menulist title)
+  "Replacement function for `imenu--split-menu'.
+
+This function does not move sub-menus to the top, therefore
+keeping the original outline order of the document.  Also it does
+not call `imenu-sort-function'."
+  (let ((menulist (copy-sequence menulist))
+        keep-at-top)
+    (if (memq imenu--rescan-item menulist)
+        (setq keep-at-top (list imenu--rescan-item)
+              menulist (delq imenu--rescan-item menulist)))
+    (if (> (length menulist) imenu-max-items)
+        (setq menulist
+              (mapcar
+               (lambda (menu)
+                 (cons (format "From: %s" (caar menu)) menu))
+               (imenu--split menulist imenu-max-items))))
+    (cons title
+          (nconc (nreverse keep-at-top) menulist))))
+
+;; bugfix for imenu in Emacs 24.3 and below.
+(when (condition-case nil
+          (progn (imenu--truncate-items '(("" 0))) nil)
+        (error t))
+  (eval-after-load "imenu"
+    '(defun imenu--truncate-items (menulist)
+       "Truncate all strings in MENULIST to `imenu-max-item-length'."
+       (mapc (lambda (item)
+               ;; Truncate if necessary.
+               (when (and (numberp imenu-max-item-length)
+                          (> (length (car item)) imenu-max-item-length))
+                 (setcar item (substring (car item) 0 imenu-max-item-length)))
+               (when (imenu--subalist-p item)
+                 (imenu--truncate-items (cdr item))))
+             menulist))))
+
+
+
+(provide 'pdf-outline)
+
+;;; pdf-outline.el ends here
+
+;; Local Variables:
+;; byte-compile-warnings: (not obsolete)
+;; End:
diff --git a/elpa/pdf-tools-20191031.642/pdf-sync.el b/elpa/pdf-tools-20191031.642/pdf-sync.el
new file mode 100644 (file)
index 0000000..bf40cdc
--- /dev/null
@@ -0,0 +1,781 @@
+;;; pdf-sync.el --- Use synctex to correlate LaTeX-Sources with PDF positions. -*- lexical-binding:t -*-
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, doc-view, pdf
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; The backward search uses a heuristic, which is pretty simple, but
+;; effective: It extracts the text around the click-position in the
+;; PDF, normalizes it's whitespace, deletes certain notorious
+;; character and translates certain other character into their latex
+;; equivalents.  This transformed text is split into a series of
+;; token.  A similar operation is performed on the source code around
+;; the position synctex points at.  These two sequences of token are
+;; aligned with a standard sequence alignment algorithm, resulting in
+;; an alist of matched and unmatched tokens.  This is then used to
+;; find the corresponding word from the PDF file in the LaTeX buffer.
+
+
+(require 'pdf-view)
+(require 'pdf-info)
+(require 'pdf-util)
+(require 'let-alist)
+
+;;; Code:
+
+(defgroup pdf-sync nil
+  "Jump from TeX sources to PDF pages and back."
+  :group 'pdf-tools)
+
+(defcustom pdf-sync-forward-display-pdf-key "C-c C-g"
+  "Key to jump from a TeX buffer to it's PDF file.
+
+This key is added to `TeX-source-correlate-method', when
+command `pdf-sync-minor-mode' is activated and this map is defined."
+  :group 'pdf-sync
+  :type 'key-sequence)
+
+(make-obsolete-variable
+ 'pdf-sync-forward-display-pdf-key
+ "Bound in Auctex's to C-c C-v, if TeX-source-correlate-mode is activate." "1.0")
+
+(defcustom pdf-sync-backward-hook nil
+  "Hook ran after going to a source location.
+
+The hook is run in the TeX buffer."
+  :group 'pdf-sync
+  :type 'hook
+  :options '(pdf-sync-backward-beginning-of-word))
+
+(defcustom pdf-sync-forward-hook nil
+  "Hook ran after displaying the PDF buffer.
+
+The hook is run in the PDF's buffer."
+  :group 'pdf-sync
+  :type 'hook)
+
+(defcustom pdf-sync-forward-display-action nil
+  "Display action used when displaying PDF buffers."
+  :group 'pdf-sync
+  :type 'display-buffer--action-custom-type)
+
+(defcustom pdf-sync-backward-display-action nil
+  "Display action used when displaying TeX buffers."
+  :group 'pdf-sync
+  :type 'display-buffer--action-custom-type)
+
+(defcustom pdf-sync-locate-synctex-file-functions nil
+  "A list of functions for locating the synctex database.
+
+Each function on this hook should accept a single argument: The
+absolute path of a PDF file.  It should return the absolute path
+of the corresponding synctex database or nil, if it was unable to
+locate it."
+  :group 'pdf-sync
+  :type 'hook)
+
+(defvar pdf-sync-minor-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (define-key kmap [double-mouse-1] 'pdf-sync-backward-search-mouse)
+    (define-key kmap [C-mouse-1] 'pdf-sync-backward-search-mouse)
+    kmap))
+
+(defcustom pdf-sync-backward-redirect-functions nil
+  "List of functions which may redirect a backward search.
+
+Functions on this hook should accept three arguments, namely
+SOURCE, LINE and COLUMN, where SOURCE is the absolute filename of
+the source file and LINE and COLUMN denote the position in the
+file.  COLUMN may be negative, meaning unspecified.
+
+These functions should either return nil, if no redirection is
+necessary.  Or a list of the same structure, with some or all (or
+none) values modified.
+
+AUCTeX installs a function here which changes the backward search
+location for synthetic `TeX-region' files back to the equivalent
+position in the original tex file."
+  :group 'pdf-sync
+  :type '(repeat function))
+
+
+;;;###autoload
+(define-minor-mode pdf-sync-minor-mode
+  "Correlate a PDF position with the TeX file.
+\\<pdf-sync-minor-mode-map>
+This works via SyncTeX, which means the TeX sources need to have
+been compiled with `--synctex=1'.  In AUCTeX this can be done by
+setting `TeX-source-correlate-method' to 'synctex \(before AUCTeX
+is loaded\) and enabling `TeX-source-correlate-mode'.
+
+Then \\[pdf-sync-backward-search-mouse] in the PDF buffer will open the
+corresponding TeX location.
+
+If AUCTeX is your preferred tex-mode, this library arranges to
+bind `pdf-sync-forward-display-pdf-key' \(the default is `C-c C-g'\)
+to `pdf-sync-forward-search' in `TeX-source-correlate-map'.  This
+function displays the PDF page corresponding to the current
+position in the TeX buffer.  This function only works together
+with AUCTeX."
+
+  nil nil nil
+  (pdf-util-assert-pdf-buffer))
+
+\f
+;; * ================================================================== *
+;; * Backward search (PDF -> TeX)
+;; * ================================================================== *
+
+(defcustom pdf-sync-backward-use-heuristic t
+  "Whether to apply a heuristic when backward searching.
+
+If nil, just go where Synctex tells us.  Otherwise try to find
+the exact location of the clicked-upon text in the PDF."
+  :group 'pdf-sync
+  :type 'boolean)
+
+(defcustom pdf-sync-backward-text-translations
+  '((88 "X" "sum")
+    (94 "textasciicircum")
+    (126 "textasciitilde")
+    (169 "copyright" "textcopyright")
+    (172 "neg" "textlnot")
+    (174 "textregistered" "textregistered")
+    (176 "textdegree")
+    (177 "pm" "textpm")
+    (181 "upmu" "mu")
+    (182 "mathparagraph" "textparagraph" "P" "textparagraph")
+    (215 "times")
+    (240 "eth" "dh")
+    (915 "Upgamma" "Gamma")
+    (920 "Uptheta" "Theta")
+    (923 "Uplambda" "Lambda")
+    (926 "Upxi" "Xi")
+    (928 "Uppi" "Pi")
+    (931 "Upsigma" "Sigma")
+    (933 "Upupsilon" "Upsilon")
+    (934 "Upphi" "Phi")
+    (936 "Uppsi" "Psi")
+    (945 "upalpha" "alpha")
+    (946 "upbeta" "beta")
+    (947 "upgamma" "gamma")
+    (948 "updelta" "delta")
+    (949 "upvarepsilon" "varepsilon")
+    (950 "upzeta" "zeta")
+    (951 "upeta" "eta")
+    (952 "uptheta" "theta")
+    (953 "upiota" "iota")
+    (954 "upkappa" "varkappa" "kappa")
+    (955 "uplambda" "lambda")
+    (957 "upnu" "nu")
+    (958 "upxi" "xi")
+    (960 "uppi" "pi")
+    (961 "upvarrho" "uprho" "rho")
+    (962 "varsigma")
+    (963 "upvarsigma" "upsigma" "sigma")
+    (964 "uptau" "tau")
+    (965 "upupsilon" "upsilon")
+    (966 "upphi" "phi")
+    (967 "upchi" "chi")
+    (968 "uppsi" "psi")
+    (969 "upomega" "omega")
+    (977 "upvartheta" "vartheta")
+    (981 "upvarphi" "varphi")
+    (8224 "dagger")
+    (8225 "ddagger")
+    (8226 "bullet")
+    (8486 "Upomega" "Omega")
+    (8501 "aleph")
+    (8592 "mapsfrom" "leftarrow")
+    (8593 "uparrow")
+    (8594 "to" "mapsto" "rightarrow")
+    (8595 "downarrow")
+    (8596 "leftrightarrow")
+    (8656 "shortleftarrow" "Leftarrow")
+    (8657 "Uparrow")
+    (8658 "Mapsto" "rightrightarrows" "Rightarrow")
+    (8659 "Downarrow")
+    (8660 "Leftrightarrow")
+    (8704 "forall")
+    (8706 "partial")
+    (8707 "exists")
+    (8709 "varnothing" "emptyset")
+    (8710 "Updelta" "Delta")
+    (8711 "nabla")
+    (8712 "in")
+    (8722 "-")
+    (8725 "setminus")
+    (8727 "*")
+    (8734 "infty")
+    (8743 "wedge")
+    (8744 "vee")
+    (8745 "cap")
+    (8746 "cup")
+    (8756 "therefore")
+    (8757 "because")
+    (8764 "thicksim" "sim")
+    (8776 "thickapprox" "approx")
+    (8801 "equiv")
+    (8804 "leq")
+    (8805 "geq")
+    (8810 "lll")
+    (8811 "ggg")
+    (8814 "nless")
+    (8815 "ngtr")
+    (8822 "lessgtr")
+    (8823 "gtrless")
+    (8826 "prec")
+    (8832 "nprec")
+    (8834 "subset")
+    (8835 "supset")
+    (8838 "subseteq")
+    (8839 "supseteq")
+    (8853 "oplus")
+    (8855 "otimes")
+    (8869 "bot" "perp")
+    (9702 "circ")
+    (9792 "female" "venus")
+    (9793 "earth")
+    (9794 "male" "mars")
+    (9824 "spadesuit")
+    (9827 "clubsuit")
+    (9829 "heartsuit")
+    (9830 "diamondsuit"))
+  "Alist mapping PDF character to a list of LaTeX macro names.
+
+Adding a character here with it's LaTeX equivalent names allows
+the heuristic backward search to find it's location in the source
+file.  These strings should not match
+`pdf-sync-backward-source-flush-regexp'.
+
+Has no effect if `pdf-sync-backward-use-heuristic' is nil."
+  :group 'pdf-sync
+  :type '(alist :key-type character
+                :value-type (repeat string)))
+
+(defconst pdf-sync-backward-text-flush-regexp
+  "[][.·{}|\\]\\|\\C.\\|-\n+"
+  "Regexp of ignored text when backward searching.")
+
+(defconst pdf-sync-backward-source-flush-regexp
+  "\\(?:\\\\\\(?:begin\\|end\\|\\(?:eq\\)?ref\\|label\\|cite\\){[^}]*}\\)\\|[][\\&{}$_]"
+  "Regexp of ignored source when backward searching.")
+
+(defconst pdf-sync-backward-context-limit 64
+  "Number of character to include in the backward search.")
+
+(defun pdf-sync-backward-search-mouse (ev)
+  "Go to the source corresponding to position at event EV."
+  (interactive "@e")
+  (let* ((posn (event-start ev))
+         (image (posn-image posn))
+         (xy (posn-object-x-y posn)))
+    (unless image
+      (error "Outside of image area"))
+    (pdf-sync-backward-search (car xy) (cdr xy))))
+
+(defun pdf-sync-backward-search (x y)
+  "Go to the source corresponding to image coordinates X, Y.
+
+Try to find the exact position, if
+`pdf-sync-backward-use-heuristic' is non-nil."
+  (cl-destructuring-bind (source finder)
+      (pdf-sync-backward-correlate x y)
+    (pop-to-buffer (or (find-buffer-visiting source)
+                       (find-file-noselect source))
+                   pdf-sync-backward-display-action)
+    (push-mark)
+    (funcall finder)
+    (run-hooks 'pdf-sync-backward-hook)))
+
+(defun pdf-sync-backward-correlate (x y)
+  "Find the source corresponding to image coordinates X, Y.
+
+Returns a list \(SOURCE FINDER\), where SOURCE is the name of the
+TeX file and FINDER a function of zero arguments which, when
+called in the buffer of the aforementioned file, will try to move
+point to the correct position."
+
+  (pdf-util-assert-pdf-window)
+  (let ((size (pdf-view-image-size))
+        (page (pdf-view-current-page)))
+    (setq x (/ x (float (car size)))
+          y (/ y (float (cdr size))))
+    (let-alist (pdf-info-synctex-backward-search page x y)
+      (let ((data (list (expand-file-name .filename)
+                        .line .column)))
+        (cl-destructuring-bind (source line column)
+            (or (save-selected-window
+                  (apply 'run-hook-with-args-until-success
+                    'pdf-sync-backward-redirect-functions data))
+                data)
+          (list source
+                (if (not pdf-sync-backward-use-heuristic)
+                    (lambda nil
+                      (pdf-util-goto-position line column))
+                  (let ((context (pdf-sync-backward--get-text-context page x y)))
+                    (lambda nil
+                      (pdf-sync-backward--find-position line column context))))))))))
+
+(defun pdf-sync-backward--find-position (line column context)
+  (pdf-util-goto-position line column)
+  (cl-destructuring-bind (windex chindex words)
+      context
+    (let* ((swords (pdf-sync-backward--get-source-context
+                    nil (* 6 pdf-sync-backward-context-limit)))
+           (similarity-fn (lambda (text source)
+                            (if (if (consp text)
+                                    (member source text)
+                                  (equal text source))
+                                1024 -1024)))
+           (alignment
+            (pdf-util-seq-alignment
+             words swords similarity-fn 'infix)))
+      (setq alignment (cl-remove-if-not 'car (cdr alignment)))
+      (cl-assert (< windex (length alignment)))
+
+      (let ((word (cdr (nth windex alignment))))
+        (unless word
+          (setq chindex 0
+                word (cdr (nth (1+ windex) alignment))))
+        (unless word
+          (setq word (cdr (nth (1- windex) alignment))
+                chindex (length word)))
+        (when word
+          (cl-assert (get-text-property 0 'position word) t)
+          (goto-char (get-text-property 0 'position word))
+          (forward-char chindex))))))
+
+(defun pdf-sync-backward--get-source-context (&optional position limit)
+  (save-excursion
+    (when position (goto-char position))
+    (goto-char (line-beginning-position))
+    (let* ((region
+            (cond
+             ((eq limit 'line)
+              (cons (line-beginning-position)
+                    (line-end-position)))
+
+             ;; Synctex usually jumps to the end macro, in case it
+             ;; does not understand the environment.
+             ((and (fboundp 'LaTeX-find-matching-begin)
+                   (looking-at " *\\\\\\(end\\){"))
+              (cons (or (ignore-errors
+                          (save-excursion
+                            (LaTeX-find-matching-begin)
+                            (forward-line 1)
+                            (point)))
+                        (point))
+                    (point)))
+             ((and (fboundp 'LaTeX-find-matching-end)
+                   (looking-at " *\\\\\\(begin\\){"))
+              (goto-char (line-end-position))
+              (cons (point)
+                    (or (ignore-errors
+                          (save-excursion
+                            (LaTeX-find-matching-end)
+                            (forward-line 0)
+                            (point)))
+                        (point))))
+             (t (cons (point) (point)))))
+           (begin (car region))
+           (end (cdr region)))
+      (when (numberp limit)
+        (let ((delta (- limit (- end begin))))
+          (when (> delta 0)
+            (setq begin (max (point-min)
+                             (- begin (/ delta 2)))
+                  end (min (point-max)
+                           (+ end (/ delta 2)))))))
+      (let ((string (buffer-substring-no-properties begin end)))
+        (dotimes (i (length string))
+          (put-text-property i (1+ i) 'position (+ begin i) string))
+        (nth 2 (pdf-sync-backward--tokenize
+                (pdf-sync-backward--source-strip-comments string)
+                nil
+                pdf-sync-backward-source-flush-regexp))))))
+
+(defun pdf-sync-backward--source-strip-comments (string)
+  "Strip all standard LaTeX comments from string."
+  (with-temp-buffer
+    (save-excursion (insert string))
+    (while (re-search-forward
+            "^\\(?:[^\\\n]\\|\\(?:\\\\\\\\\\)\\)*\\(%.*\\)" nil t)
+      (delete-region (match-beginning 1) (match-end 1)))
+    (buffer-string)))
+
+(defun pdf-sync-backward--get-text-context (page x y)
+  (cl-destructuring-bind (&optional char edges)
+      (car (pdf-info-charlayout page (cons x y)))
+    (when edges
+      (setq x (nth 0 edges)
+            y (nth 1 edges)))
+    (let* ((prefix (pdf-info-gettext page (list 0 0 x y)))
+           (suffix (pdf-info-gettext page (list x y 1 1)))
+           (need-suffix-space-p (memq char '(?\s ?\n)))
+           ;; Figure out whether we missed a space by matching the
+           ;; prefix's suffix with the line's prefix.  Due to the text
+           ;; extraction in poppler, spaces are only inserted
+           ;; inbetween words.  This test may fail, if prefix and line
+           ;; do not overlap, which may happen in various cases, but
+           ;; we don't care.
+           (need-prefix-space-p
+            (and (not need-suffix-space-p)
+                 (memq
+                  (ignore-errors
+                    (aref (pdf-info-gettext page (list x y x y) 'line)
+                          (- (length prefix)
+                             (or (cl-position ?\n prefix :from-end t)
+                                 -1)
+                             1)))
+                  '(?\s ?\n)))))
+      (setq prefix
+            (concat
+             (substring
+              prefix (max 0 (min (1- (length prefix))
+                                 (- (length prefix)
+                                    pdf-sync-backward-context-limit))))
+             (if need-prefix-space-p " "))
+            suffix
+            (concat
+             (if need-suffix-space-p " ")
+             (substring
+              suffix 0 (max 0 (min (1- (length suffix))
+                                   pdf-sync-backward-context-limit)))))
+      (pdf-sync-backward--tokenize
+       prefix suffix
+       pdf-sync-backward-text-flush-regexp
+       pdf-sync-backward-text-translations))))
+
+(defun pdf-sync-backward--tokenize (prefix &optional suffix flush-re translation)
+  (with-temp-buffer
+    (when prefix (insert prefix))
+    (let* ((center (copy-marker (point)))
+           (case-fold-search nil))
+      (when suffix (insert suffix))
+      (goto-char 1)
+      ;; Delete ignored text.
+      (when flush-re
+        (save-excursion
+          (while (re-search-forward flush-re nil t)
+            (replace-match " " t t))))
+      ;; Normalize whitespace.
+      (save-excursion
+        (while (re-search-forward "[ \t\f\n]+" nil t)
+          (replace-match " " t t)))
+      ;; Split words and non-words
+      (save-excursion
+        (while (re-search-forward "[^ ]\\b\\|[^ [:alnum:]]" nil t)
+          (insert-before-markers " ")))
+      ;; Replace character
+      (let ((translate
+             (lambda (string)
+               (or (and (= (length string) 1)
+                        (cdr (assq (aref string 0)
+                                   translation)))
+                   string)))
+            words
+            (windex -1)
+            (chindex 0))
+        (skip-chars-forward " ")
+        (while (and (not (eobp))
+                    (<= (point) center))
+          (cl-incf windex)
+          (skip-chars-forward "^ ")
+          (skip-chars-forward " "))
+        (goto-char center)
+        (when (eq ?\s (char-after))
+          (skip-chars-backward " "))
+        (setq chindex (- (skip-chars-backward "^ ")))
+        (setq words (split-string (buffer-string)))
+        (when translation
+          (setq words (mapcar translate words)))
+        (list windex chindex words)))))
+
+(defun pdf-sync-backward-beginning-of-word ()
+  "Maybe move to the beginning of the word.
+
+Don't move if already at the beginning, or if not at a word
+character.
+
+This function is meant to be put on `pdf-sync-backward-hook', when
+word-level searching is desired."
+  (interactive)
+  (unless (or (looking-at "\\b\\w")
+              (not (looking-back "\\w" (1- (point)))))
+    (backward-word)))
+
+;; * ------------------------------------------------------------------ *
+;; * Debugging backward search
+;; * ------------------------------------------------------------------ *
+
+(defvar pdf-sync-backward-debug-trace nil)
+
+(defun pdf-sync-backward-debug-wrapper (fn-symbol fn &rest args)
+  (cond
+   ((eq fn-symbol 'pdf-sync-backward-search)
+    (setq pdf-sync-backward-debug-trace nil)
+    (apply fn args))
+   (t
+    (let ((retval (apply fn args)))
+      (push `(,args . ,retval)
+            pdf-sync-backward-debug-trace)
+      retval))))
+
+(define-minor-mode pdf-sync-backward-debug-minor-mode
+  "Aid in debugging the backward search."
+  nil nil nil
+  (if (and (fboundp 'advice-add)
+           (fboundp 'advice-remove))
+      (let ((functions
+             '(pdf-sync-backward-search
+               pdf-sync-backward--tokenize
+               pdf-util-seq-alignment)))
+        (cond
+         (pdf-sync-backward-debug-minor-mode
+          (dolist (fn functions)
+            (advice-add fn :around (apply-partially 'pdf-sync-backward-debug-wrapper
+                                                    fn)
+                        `((name . ,(format "%s-debug" fn))))))
+         (t
+          (dolist (fn functions)
+            (advice-remove fn (format "%s-debug" fn))))))
+    (error "Need Emacs version >= 24.4")))
+
+(defun pdf-sync-backward-debug-explain ()
+  "Explain the last backward search.
+
+Needs to have `pdf-sync-backward-debug-minor-mode' enabled."
+
+  (interactive)
+  (unless pdf-sync-backward-debug-trace
+    (error "No last search or `pdf-sync-backward-debug-minor-mode' not enabled."))
+
+  (with-current-buffer (get-buffer-create "*pdf-sync-backward trace*")
+    (cl-destructuring-bind (text source alignment &rest ignored)
+        (reverse pdf-sync-backward-debug-trace)
+      (let* ((fill-column 68)
+             (sep (format "\n%s\n" (make-string fill-column ?-)))
+             (highlight '(:background "chartreuse" :foreground "black"))
+             (or-sep "|")
+             (inhibit-read-only t)
+             (windex (nth 0 (cdr text)))
+             (chindex (nth 1 (cdr text))))
+        (erase-buffer)
+        (font-lock-mode -1)
+        (view-mode 1)
+        (insert (propertize "Text Raw:" 'face 'font-lock-keyword-face))
+        (insert sep)
+        (insert (nth 0 (car text)))
+        (insert (propertize "<|>" 'face highlight))
+        (insert (nth 1 (car text)))
+        (insert sep)
+        (insert (propertize "Text Token:" 'face 'font-lock-keyword-face))
+        (insert sep)
+        (fill-region (point)
+                     (progn
+                       (insert
+                        (mapconcat (lambda (elt)
+                                     (if (consp elt)
+                                         (mapconcat 'identity elt or-sep)
+                                       elt))
+                                   (nth 2 (cdr text)) " "))
+                       (point)))
+        (insert sep)
+
+        (insert (propertize "Source Raw:" 'face 'font-lock-keyword-face))
+        (insert sep)
+        (insert (nth 0 (car source)))
+        (insert sep)
+        (insert (propertize "Source Token:" 'face 'font-lock-keyword-face))
+        (insert sep)
+        (fill-region (point)
+                     (progn (insert (mapconcat 'identity (nth 2 (cdr source)) " "))
+                            (point)))
+        (insert sep)
+
+        (insert (propertize "Alignment:" 'face 'font-lock-keyword-face))
+        (insert (format " (windex=%d, chindex=%d" windex chindex))
+        (insert sep)
+        (save-excursion (newline 2))
+        (let ((column 0)
+              (index 0))
+          (dolist (a (cdr (cdr alignment)))
+            (let* ((source (cdr a))
+                   (text (if (consp (car a))
+                             (mapconcat 'identity (car a) or-sep)
+                           (car a)))
+                   (extend (max (length text)
+                                (length source))))
+              (when (and (not (bolp))
+                         (> (+ column extend)
+                            fill-column))
+                (forward-line 2)
+                (newline 3)
+                (forward-line -2)
+                (setq column 0))
+              (when text
+                (insert (propertize text 'face
+                                    (if (= index windex)
+                                        highlight
+                                      (if source 'match
+                                        'lazy-highlight)))))
+              (move-to-column (+ column extend) t)
+              (insert " ")
+              (save-excursion
+                (forward-line)
+                (move-to-column column t)
+                (when source
+                  (insert (propertize source 'face (if text
+                                                       'match
+                                                     'lazy-highlight))))
+                (move-to-column (+ column extend) t)
+                (insert " "))
+              (cl-incf column (+ 1 extend))
+              (when text (cl-incf index)))))
+        (goto-char (point-max))
+        (insert sep)
+        (goto-char 1)
+        (pop-to-buffer (current-buffer))))))
+
+\f
+;; * ================================================================== *
+;; * Forward search (TeX -> PDF)
+;; * ================================================================== *
+
+(defun pdf-sync-forward-search (&optional line column)
+  "Display the PDF location corresponding to LINE, COLUMN."
+  (interactive)
+  (cl-destructuring-bind (pdf page _x1 y1 _x2 _y2)
+      (pdf-sync-forward-correlate line column)
+    (let ((buffer (or (find-buffer-visiting pdf)
+                      (find-file-noselect pdf))))
+      (with-selected-window (display-buffer
+                             buffer pdf-sync-forward-display-action)
+        (pdf-util-assert-pdf-window)
+        (when page
+         (pdf-view-goto-page page)
+         (when y1
+           (let ((top (* y1 (cdr (pdf-view-image-size)))))
+             (pdf-util-tooltip-arrow (round top))))))
+      (with-current-buffer buffer
+        (run-hooks 'pdf-sync-forward-hook)))))
+
+(defun pdf-sync-forward-correlate (&optional line column)
+  "Find the PDF location corresponding to LINE, COLUMN.
+
+Returns a list \(PDF PAGE X1 Y1 X2 Y2\), where PAGE, X1, Y1, X2
+and Y2 may be nil, if the destination could not be found."
+  (unless (fboundp 'TeX-master-file)
+    (error "This function works only with AUCTeX"))
+  (unless line (setq line (line-number-at-pos)))
+  (unless column (setq column (current-column)))
+
+  (let* ((pdf (expand-file-name
+               (with-no-warnings (TeX-master-file "pdf"))))
+         (sfilename (pdf-sync-synctex-file-name
+                     (buffer-file-name) pdf)))
+    (cons pdf
+         (condition-case error
+             (let-alist (pdf-info-synctex-forward-search
+                         (or sfilename
+                             (buffer-file-name))
+                         line column pdf)
+               (cons .page .edges))
+           (error
+            (message "%s" (error-message-string error))
+            (list nil nil nil nil nil))))))
+
+
+\f
+;; * ================================================================== *
+;; * Dealing with synctex files.
+;; * ================================================================== *
+
+(defun pdf-sync-locate-synctex-file (pdffile)
+  "Locate the synctex database corresponding to PDFFILE.
+
+Returns either the absolute path of the database or nil.
+
+See also `pdf-sync-locate-synctex-file-functions'."
+  (cl-check-type pdffile string)
+  (setq pdffile (expand-file-name pdffile))
+  (or (run-hook-with-args-until-success
+       'pdf-sync-locate-synctex-file-functions pdffile)
+      (pdf-sync-locate-synctex-file-default pdffile)))
+
+(defun pdf-sync-locate-synctex-file-default (pdffile)
+  "The default function for locating a synctex database for PDFFILE.
+
+See also `pdf-sync-locate-synctex-file'."
+  (let ((default-directory
+          (file-name-directory pdffile))
+        (basename (file-name-sans-extension
+                   (file-name-nondirectory pdffile))))
+    (cl-labels ((file-if-exists-p (file)
+                  (and (file-exists-p file)
+                       file)))
+      (or (file-if-exists-p
+           (expand-file-name (concat basename ".synctex.gz")))
+          (file-if-exists-p
+           (expand-file-name (concat basename ".synctex")))
+          ;; Some pdftex quote the basename.
+          (file-if-exists-p
+           (expand-file-name (concat "\"" basename "\"" ".synctex.gz")))
+          (file-if-exists-p
+           (expand-file-name (concat "\"" basename "\"" ".synctex")))))))
+
+(defun pdf-sync-synctex-file-name (filename pdffile)
+  "Find SyncTeX filename corresponding to FILENAME in the context of PDFFILE.
+
+This function consults the synctex.gz database of PDFFILE and
+searches for a filename, which is `file-equal-p' to FILENAME.
+The first such filename is returned, or nil if none was found."
+
+  (when (file-exists-p filename)
+    (setq filename (expand-file-name filename))
+    (let* ((synctex (pdf-sync-locate-synctex-file pdffile))
+           (basename (file-name-nondirectory filename))
+           (regexp (format "^ *Input *: *[^:\n]+ *:\\(.*%s\\)$"
+                           (regexp-quote basename)))
+           (jka-compr-verbose nil))
+      (when (and synctex
+                 (file-readable-p synctex))
+        (with-current-buffer (find-file-noselect synctex :nowarn)
+          (unless (or (verify-visited-file-modtime)
+                      (buffer-modified-p))
+            (revert-buffer :ignore-auto :noconfirm)
+            (goto-char (point-min)))
+          ;; Keep point in front of the found filename. It will
+          ;; probably be queried for again next time.
+          (let ((beg (point))
+                (end (point-max)))
+            (catch 'found
+              (dotimes (_x 2)
+                (while (re-search-forward regexp end t)
+                  (let ((syncname (match-string-no-properties 1)))
+                    (when (and (file-exists-p syncname)
+                               (file-equal-p filename syncname))
+                      (goto-char (point-at-bol))
+                      (throw 'found syncname))))
+                (setq end beg
+                      beg (point-min))
+                (goto-char beg)))))))))
+
+(provide 'pdf-sync)
+;;; pdf-sync.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-tools-autoloads.el b/elpa/pdf-tools-20191031.642/pdf-tools-autoloads.el
new file mode 100644 (file)
index 0000000..dda8580
--- /dev/null
@@ -0,0 +1,387 @@
+;;; pdf-tools-autoloads.el --- automatically extracted autoloads
+;;
+;;; Code:
+(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path))))
+\f
+;;;### (autoloads nil "pdf-annot" "pdf-annot.el" (23994 46397 260173
+;;;;;;  770000))
+;;; Generated autoloads from pdf-annot.el
+
+(autoload 'pdf-annot-minor-mode "pdf-annot" "\
+Support for PDF Annotations.
+
+\\{pdf-annot-minor-mode-map}
+
+\(fn &optional ARG)" t nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-history" "pdf-history.el" (23994 46397
+;;;;;;  259193 628000))
+;;; Generated autoloads from pdf-history.el
+
+(autoload 'pdf-history-minor-mode "pdf-history" "\
+Keep a history of previously visited pages.
+
+This is a simple stack-based history.  Turning the page or
+following a link pushes the left-behind page on the stack, which
+may be navigated with the following keys.
+
+\\{pdf-history-minor-mode-map}
+
+\(fn &optional ARG)" t nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-isearch" "pdf-isearch.el" (23994 46397
+;;;;;;  249007 18000))
+;;; Generated autoloads from pdf-isearch.el
+
+(autoload 'pdf-isearch-minor-mode "pdf-isearch" "\
+Isearch mode for PDF buffer.
+
+When this mode is enabled \\[isearch-forward], among other keys,
+starts an incremental search in this PDF document.  Since this mode
+uses external programs to highlight found matches via
+image-processing, proceeding to the next match may be slow.
+
+Therefore two isearch behaviours have been defined: Normal isearch and
+batch mode.  The later one is a minor mode
+\(`pdf-isearch-batch-mode'), which when activated inhibits isearch
+from stopping at and highlighting every single match, but rather
+display them batch-wise.  Here a batch means a number of matches
+currently visible in the selected window.
+
+The kind of highlighting is determined by three faces
+`pdf-isearch-match' (for the current match), `pdf-isearch-lazy'
+\(for all other matches) and `pdf-isearch-batch' (when in batch
+mode), which see.
+
+Colors may also be influenced by the minor-mode
+`pdf-view-dark-minor-mode'.  If this is minor mode enabled, each face's
+dark colors, are used (see e.g. `frame-background-mode'), instead
+of the light ones.
+
+\\{pdf-isearch-minor-mode-map}
+While in `isearch-mode' the following keys are available. Note
+that not every isearch command work as expected.
+
+\\{pdf-isearch-active-mode-map}
+
+\(fn &optional ARG)" t nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-links" "pdf-links.el" (23994 46397 251008
+;;;;;;  846000))
+;;; Generated autoloads from pdf-links.el
+
+(autoload 'pdf-links-minor-mode "pdf-links" "\
+Handle links in PDF documents.\\<pdf-links-minor-mode-map>
+
+If this mode is enabled, most links in the document may be
+activated by clicking on them or by pressing \\[pdf-links-action-perform] and selecting
+one of the displayed keys, or by using isearch limited to
+links via \\[pdf-links-isearch-link].
+
+\\{pdf-links-minor-mode-map}
+
+\(fn &optional ARG)" t nil)
+
+(autoload 'pdf-links-action-perform "pdf-links" "\
+Follow LINK, depending on its type.
+
+This may turn to another page, switch to another PDF buffer or
+invoke `pdf-links-browse-uri-function'.
+
+Interactively, link is read via `pdf-links-read-link-action'.
+This function displays characters around the links in the current
+page and starts reading characters (ignoring case).  After a
+sufficient number of characters have been read, the corresponding
+link's link is invoked.  Additionally, SPC may be used to
+scroll the current page.
+
+\(fn LINK)" t nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-loader" "pdf-loader.el" (23994 46397 258169
+;;;;;;  584000))
+;;; Generated autoloads from pdf-loader.el
+
+(autoload 'pdf-loader-install "pdf-loader" "\
+Prepare Emacs for using PDF Tools.
+
+This function acts as a replacement for `pdf-tools-install' and
+makes Emacs load and use PDF Tools as soon as a PDF file is
+opened, but not sooner.
+
+The arguments are passed verbatim to `pdf-tools-install', which
+see.
+
+\(fn &optional NO-QUERY-P SKIP-DEPENDENCIES-P NO-ERROR-P FORCE-DEPENDENCIES-P)" nil nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-misc" "pdf-misc.el" (23994 46397 253136
+;;;;;;  199000))
+;;; Generated autoloads from pdf-misc.el
+
+(autoload 'pdf-misc-minor-mode "pdf-misc" "\
+FIXME:  Not documented.
+
+\(fn &optional ARG)" t nil)
+
+(autoload 'pdf-misc-size-indication-minor-mode "pdf-misc" "\
+Provide a working size indication in the mode-line.
+
+\(fn &optional ARG)" t nil)
+
+(autoload 'pdf-misc-menu-bar-minor-mode "pdf-misc" "\
+Display a PDF Tools menu in the menu-bar.
+
+\(fn &optional ARG)" t nil)
+
+(autoload 'pdf-misc-context-menu-minor-mode "pdf-misc" "\
+Provide a right-click context menu in PDF buffers.
+
+\\{pdf-misc-context-menu-minor-mode-map}
+
+\(fn &optional ARG)" t nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-occur" "pdf-occur.el" (23994 46397 248001
+;;;;;;  265000))
+;;; Generated autoloads from pdf-occur.el
+
+(autoload 'pdf-occur "pdf-occur" "\
+List lines matching STRING or PCRE.
+
+Interactively search for a regexp. Unless a prefix arg was given,
+in which case this functions performs a string search.
+
+If `pdf-occur-prefer-string-search' is non-nil, the meaning of
+the prefix-arg is inverted.
+
+\(fn STRING &optional REGEXP-P)" t nil)
+
+(autoload 'pdf-occur-multi-command "pdf-occur" "\
+Perform `pdf-occur' on multiple buffer.
+
+For a programmatic search of multiple documents see
+`pdf-occur-search'.
+
+\(fn)" t nil)
+
+(defvar pdf-occur-global-minor-mode nil "\
+Non-nil if Pdf-Occur-Global minor mode is enabled.
+See the `pdf-occur-global-minor-mode' command
+for a description of this minor mode.
+Setting this variable directly does not take effect;
+either customize it (see the info node `Easy Customization')
+or call the function `pdf-occur-global-minor-mode'.")
+
+(custom-autoload 'pdf-occur-global-minor-mode "pdf-occur" nil)
+
+(autoload 'pdf-occur-global-minor-mode "pdf-occur" "\
+Enable integration of Pdf Occur with other modes.
+
+This global minor mode enables (or disables)
+`pdf-occur-ibuffer-minor-mode' and `pdf-occur-dired-minor-mode'
+in all current and future ibuffer/dired buffer.
+
+\(fn &optional ARG)" t nil)
+
+(autoload 'pdf-occur-ibuffer-minor-mode "pdf-occur" "\
+Hack into ibuffer's do-occur binding.
+
+This mode remaps `ibuffer-do-occur' to
+`pdf-occur-ibuffer-do-occur', which will start the PDF Tools
+version of `occur', if all marked buffer's are in `pdf-view-mode'
+and otherwise fallback to `ibuffer-do-occur'.
+
+\(fn &optional ARG)" t nil)
+
+(autoload 'pdf-occur-dired-minor-mode "pdf-occur" "\
+Hack into dired's `dired-do-search' binding.
+
+This mode remaps `dired-do-search' to
+`pdf-occur-dired-do-search', which will start the PDF Tools
+version of `occur', if all marked buffer's are in `pdf-view-mode'
+and otherwise fallback to `dired-do-search'.
+
+\(fn &optional ARG)" t nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-outline" "pdf-outline.el" (23994 46397
+;;;;;;  261141 728000))
+;;; Generated autoloads from pdf-outline.el
+
+(autoload 'pdf-outline-minor-mode "pdf-outline" "\
+Display an outline of a PDF document.
+
+This provides a PDF's outline on the menu bar via imenu.
+Additionally the same outline may be viewed in a designated
+buffer.
+
+\\{pdf-outline-minor-mode-map}
+
+\(fn &optional ARG)" t nil)
+
+(autoload 'pdf-outline "pdf-outline" "\
+Display an PDF outline of BUFFER.
+
+BUFFER defaults to the current buffer.  Select the outline
+buffer, unless NO-SELECT-WINDOW-P is non-nil.
+
+\(fn &optional BUFFER NO-SELECT-WINDOW-P)" t nil)
+
+(autoload 'pdf-outline-imenu-enable "pdf-outline" "\
+Enable imenu in the current PDF buffer.
+
+\(fn)" t nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-sync" "pdf-sync.el" (23994 46397 262109
+;;;;;;  503000))
+;;; Generated autoloads from pdf-sync.el
+
+(autoload 'pdf-sync-minor-mode "pdf-sync" "\
+Correlate a PDF position with the TeX file.
+\\<pdf-sync-minor-mode-map>
+This works via SyncTeX, which means the TeX sources need to have
+been compiled with `--synctex=1'.  In AUCTeX this can be done by
+setting `TeX-source-correlate-method' to 'synctex (before AUCTeX
+is loaded) and enabling `TeX-source-correlate-mode'.
+
+Then \\[pdf-sync-backward-search-mouse] in the PDF buffer will open the
+corresponding TeX location.
+
+If AUCTeX is your preferred tex-mode, this library arranges to
+bind `pdf-sync-forward-display-pdf-key' (the default is `C-c C-g')
+to `pdf-sync-forward-search' in `TeX-source-correlate-map'.  This
+function displays the PDF page corresponding to the current
+position in the TeX buffer.  This function only works together
+with AUCTeX.
+
+\(fn &optional ARG)" t nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-tools" "pdf-tools.el" (23994 46397 247044
+;;;;;;  318000))
+;;; Generated autoloads from pdf-tools.el
+
+(defvar pdf-tools-handle-upgrades t "\
+Whether PDF Tools should handle upgrading itself.")
+
+(custom-autoload 'pdf-tools-handle-upgrades "pdf-tools" t)
+
+(autoload 'pdf-tools-install "pdf-tools" "\
+Install PDF-Tools in all current and future PDF buffers.
+
+If the `pdf-info-epdfinfo-program' is not running or does not
+appear to be working, attempt to rebuild it.  If this build
+succeeded, continue with the activation of the package.
+Otherwise fail silently, i.e. no error is signaled.
+
+Build the program (if necessary) without asking first, if
+NO-QUERY-P is non-nil.
+
+Don't attempt to install system packages, if SKIP-DEPENDENCIES-P
+is non-nil.
+
+Do not signal an error in case the build failed, if NO-ERROR-P is
+non-nil.
+
+Attempt to install system packages (even if it is deemed
+unnecessary), if FORCE-DEPENDENCIES-P is non-nil.
+
+Note that SKIP-DEPENDENCIES-P and FORCE-DEPENDENCIES-P are
+mutually exclusive.
+
+Note further, that you can influence the installation directory
+by setting `pdf-info-epdfinfo-program' to an appropriate
+value (e.g. ~/bin/epdfinfo) before calling this function.
+
+See `pdf-view-mode' and `pdf-tools-enabled-modes'.
+
+\(fn &optional NO-QUERY-P SKIP-DEPENDENCIES-P NO-ERROR-P FORCE-DEPENDENCIES-P)" t nil)
+
+(autoload 'pdf-tools-enable-minor-modes "pdf-tools" "\
+Enable MODES in the current buffer.
+
+MODES defaults to `pdf-tools-enabled-modes'.
+
+\(fn &optional MODES)" t nil)
+
+(autoload 'pdf-tools-help "pdf-tools" "\
+
+
+\(fn)" t nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-view" "pdf-view.el" (23994 46397 250000
+;;;;;;  807000))
+;;; Generated autoloads from pdf-view.el
+
+(autoload 'pdf-view-bookmark-jump-handler "pdf-view" "\
+The bookmark handler-function interface for bookmark BMK.
+
+See also `pdf-view-bookmark-make-record'.
+
+\(fn BMK)" nil nil)
+
+;;;***
+\f
+;;;### (autoloads nil "pdf-virtual" "pdf-virtual.el" (23994 46397
+;;;;;;  256216 424000))
+;;; Generated autoloads from pdf-virtual.el
+
+(autoload 'pdf-virtual-edit-mode "pdf-virtual" "\
+Major mode when editing a virtual PDF buffer.
+
+\(fn)" t nil)
+
+(autoload 'pdf-virtual-view-mode "pdf-virtual" "\
+Major mode in virtual PDF buffers.
+
+\(fn)" t nil)
+
+(defvar pdf-virtual-global-minor-mode nil "\
+Non-nil if Pdf-Virtual-Global minor mode is enabled.
+See the `pdf-virtual-global-minor-mode' command
+for a description of this minor mode.
+Setting this variable directly does not take effect;
+either customize it (see the info node `Easy Customization')
+or call the function `pdf-virtual-global-minor-mode'.")
+
+(custom-autoload 'pdf-virtual-global-minor-mode "pdf-virtual" nil)
+
+(autoload 'pdf-virtual-global-minor-mode "pdf-virtual" "\
+Enable recognition and handling of VPDF files.
+
+\(fn &optional ARG)" t nil)
+
+(autoload 'pdf-virtual-buffer-create "pdf-virtual" "\
+
+
+\(fn &optional FILENAMES BUFFER-NAME DISPLAY-P)" t nil)
+
+;;;***
+\f
+;;;### (autoloads nil nil ("pdf-cache.el" "pdf-dev.el" "pdf-info.el"
+;;;;;;  "pdf-tools-pkg.el" "pdf-util.el") (23994 46397 257150 14000))
+
+;;;***
+\f
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; End:
+;;; pdf-tools-autoloads.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-tools-pkg.el b/elpa/pdf-tools-20191031.642/pdf-tools-pkg.el
new file mode 100644 (file)
index 0000000..45dcf93
--- /dev/null
@@ -0,0 +1,13 @@
+(define-package "pdf-tools" "20191031.642" "Support library for PDF documents."
+  '((emacs "24.3")
+    (tablist "1.0")
+    (let-alist "1.0.4"))
+  :keywords
+  '("files" "multimedia")
+  :authors
+  '(("Andreas Politz" . "politzaNOSPAMfh-trier.de"))
+  :maintainer
+  '("Andreas Politz" . "politzaNOSPAMfh-trier.de"))
+;; Local Variables:
+;; no-byte-compile: t
+;; End:
diff --git a/elpa/pdf-tools-20191031.642/pdf-tools.el b/elpa/pdf-tools-20191031.642/pdf-tools.el
new file mode 100644 (file)
index 0000000..e512fd1
--- /dev/null
@@ -0,0 +1,525 @@
+;;; pdf-tools.el --- Support library for PDF documents. -*- lexical-binding:t -*-
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, multimedia
+;; Package: pdf-tools
+;; Version: 1.0
+;; Package-Requires: ((emacs "24.3") (tablist "1.0") (let-alist "1.0.4"))
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; PDF Tools is, among other things, a replacement of DocView for PDF
+;; files.  The key difference is, that pages are not prerendered by
+;; e.g. ghostscript and stored in the file-system, but rather created
+;; on-demand and stored in memory.
+;;
+;; Note: This package requires external libraries and works currently
+;; only on GNU/Linux systems.
+;;
+;; Note: If you ever update it, you need to restart Emacs afterwards.
+;;
+;; To activate the package put
+;;
+;; (pdf-tools-install)
+;;
+;; somewhere in your .emacs.el .
+;;
+;; M-x pdf-tools-help RET
+;;
+;; gives some help on using the package and
+;;
+;; M-x pdf-tools-customize RET
+;;
+;; offers some customization options.
+
+;; Features:
+;;
+;; * View
+;;   View PDF documents in a buffer with DocView-like bindings.
+;;
+;; * Isearch
+;;   Interactively search PDF documents like any other buffer. (Though
+;;   there is currently no regexp support.)
+;;
+;; * Follow links
+;;   Click on highlighted links, moving to some part of a different
+;;   page, some external file, a website or any other URI.  Links may
+;;   also be followed by keyboard commands.
+;;
+;; * Annotations
+;;   Display and list text and markup annotations (like underline),
+;;   edit their contents and attributes (e.g. color), move them around,
+;;   delete them or create new ones and then save the modifications
+;;   back to the PDF file.
+;;
+;; * Attachments
+;;   Save files attached to the PDF-file or list them in a dired buffer.
+;;
+;; * Outline
+;;   Use imenu or a special buffer to examine and navigate the PDF's
+;;   outline.
+;;
+;; * SyncTeX
+;;   Jump from a position on a page directly to the TeX source and
+;;   vice-versa.
+;;
+;; * Misc
+;;    + Display PDF's metadata.
+;;    + Mark a region and kill the text from the PDF.
+;;    + Search for occurrences of a string.
+;;    + Keep track of visited pages via a history.
+
+;;; Code:
+
+(require 'pdf-view)
+(require 'pdf-util)
+(require 'pdf-info)
+(require 'cus-edit)
+(require 'compile)
+(require 'cl-lib)
+(require 'package)
+
+
+\f
+;; * ================================================================== *
+;; * Customizables
+;; * ================================================================== *
+
+(defgroup pdf-tools nil
+  "Support library for PDF documents."
+  :group 'data)
+
+(defgroup pdf-tools-faces nil
+  "Faces determining the colors used in the pdf-tools package.
+
+In order to customize dark and light colors use
+`pdf-tools-customize-faces', or set `custom-face-default-form' to
+'all."
+  :group 'pdf-tools)
+
+(defconst pdf-tools-modes
+  '(pdf-history-minor-mode
+    pdf-isearch-minor-mode
+    pdf-links-minor-mode
+    pdf-misc-minor-mode
+    pdf-outline-minor-mode
+    pdf-misc-size-indication-minor-mode
+    pdf-misc-menu-bar-minor-mode
+    pdf-annot-minor-mode
+    pdf-sync-minor-mode
+    pdf-misc-context-menu-minor-mode
+    pdf-cache-prefetch-minor-mode
+    pdf-view-auto-slice-minor-mode
+    pdf-occur-global-minor-mode
+    pdf-virtual-global-minor-mode))
+
+(defcustom pdf-tools-enabled-modes
+  '(pdf-history-minor-mode
+    pdf-isearch-minor-mode
+    pdf-links-minor-mode
+    pdf-misc-minor-mode
+    pdf-outline-minor-mode
+    pdf-misc-size-indication-minor-mode
+    pdf-misc-menu-bar-minor-mode
+    pdf-annot-minor-mode
+    pdf-sync-minor-mode
+    pdf-misc-context-menu-minor-mode
+    pdf-cache-prefetch-minor-mode
+    pdf-occur-global-minor-mode
+    ;; pdf-virtual-global-minor-mode
+    )
+  "A list of automatically enabled minor-modes.
+
+PDF Tools is build as a series of minor-modes.  This variable and
+the function `pdf-tools-install' merely serve as a convenient
+wrapper in order to load these modes in current and newly created
+PDF buffers."
+  :group 'pdf-tools
+  :type `(set ,@(mapcar (lambda (mode)
+                          `(function-item ,mode))
+                        pdf-tools-modes)))
+
+(defcustom pdf-tools-enabled-hook nil
+  "A hook ran after PDF Tools is enabled in a buffer."
+  :group 'pdf-tools
+  :type 'hook)
+
+(defconst pdf-tools-auto-mode-alist-entry
+  '("\\.[pP][dD][fF]\\'" . pdf-view-mode)
+  "The entry to use for `auto-mode-alist'.")
+
+(defconst pdf-tools-magic-mode-alist-entry
+  '("%PDF" . pdf-view-mode)
+  "The entry to use for `magic-mode-alist'.")
+
+(defun pdf-tools-customize ()
+  "Customize Pdf Tools."
+  (interactive)
+  (customize-group 'pdf-tools))
+
+(defun pdf-tools-customize-faces ()
+  "Customize PDF Tool's faces."
+  (interactive)
+  (let ((buffer (format "*Customize Group: %s*"
+                        (custom-unlispify-tag-name 'pdf-tools-faces))))
+    (when (buffer-live-p (get-buffer buffer))
+      (with-current-buffer (get-buffer buffer)
+        (rename-uniquely)))
+    (customize-group 'pdf-tools-faces)
+    (with-current-buffer buffer
+      (set (make-local-variable 'custom-face-default-form) 'all))))
+
+\f
+;; * ================================================================== *
+;; * Installation
+;; * ================================================================== *
+
+;;;###autoload
+(defcustom pdf-tools-handle-upgrades t
+  "Whether PDF Tools should handle upgrading itself."
+  :group 'pdf-tools
+  :type 'boolean)
+
+(make-obsolete-variable 'pdf-tools-handle-upgrades
+                        "Not used anymore" "0.90")
+
+(defconst pdf-tools-directory
+  (or (and load-file-name
+           (file-name-directory load-file-name))
+      default-directory)
+  "The directory from where this library was first loaded.")
+
+(defvar pdf-tools-msys2-directory nil)
+
+(defcustom pdf-tools-installer-os nil
+  "Specifies which installer to use.
+
+If nil the installer is chosen automatically. This variable is
+useful if you have multiple installers present on your
+system (e.g. nix on arch linux)"
+  :group 'pdf-tools
+  :type 'string)
+
+(defun pdf-tools-identify-build-directory (directory)
+  "Return non-nil, if DIRECTORY appears to contain the epdfinfo source.
+
+Returns the expanded directory-name of DIRECTORY or nil."
+  (setq directory (file-name-as-directory
+                   (expand-file-name directory)))
+  (and (file-exists-p (expand-file-name "autobuild" directory))
+       (file-exists-p (expand-file-name "epdfinfo.c" directory))
+       directory))
+
+(defun pdf-tools-locate-build-directory ()
+  "Attempt to locate a source directory.
+
+Returns a appropriate directory or nil.  See also
+`pdf-tools-identify-build-directory'."
+  (cl-some #'pdf-tools-identify-build-directory
+           (list default-directory
+                 (expand-file-name "build/server" pdf-tools-directory)
+                 (expand-file-name "server")
+                 (expand-file-name "../server" pdf-tools-directory))))
+
+(defun pdf-tools-msys2-directory (&optional noninteractive-p)
+  "Locate the Msys2 installation directory.
+
+Ask the user if necessary and NONINTERACTIVE-P is nil.
+Returns always nil, unless `system-type' equals windows-nt."
+  (cl-labels ((if-msys2-directory (directory)
+                (and (stringp directory)
+                     (file-directory-p directory)
+                     (file-exists-p
+                      (expand-file-name "usr/bin/bash.exe" directory))
+                     directory)))
+    (when (eq system-type 'windows-nt)
+      (setq pdf-tools-msys2-directory
+            (or pdf-tools-msys2-directory
+                (cl-some #'if-msys2-directory
+                         (cl-mapcan
+                          (lambda (drive)
+                            (list (format "%c:/msys64" drive)
+                                  (format "%c:/msys32" drive)))
+                          (number-sequence ?c ?z)))
+                (unless (or noninteractive-p
+                            (not (y-or-n-p "Do you have Msys2 installed ? ")))
+                  (if-msys2-directory
+                   (read-directory-name
+                    "Please enter Msys2 installation directory: " nil nil t))))))))
+
+(defun pdf-tools-msys2-mingw-bin ()
+  "Return the location of /mingw*/bin."
+  (when (pdf-tools-msys2-directory)
+    (let ((arch (intern (car (split-string system-configuration "-" t)))))
+    (expand-file-name
+     (format "./mingw%s/bin" (if (eq arch 'x86_64) "64" "32"))
+     (pdf-tools-msys2-directory)))))
+
+(defun pdf-tools-find-bourne-shell ()
+  "Locate a usable sh."
+  (or (and (eq system-type 'windows-nt)
+           (let* ((directory (pdf-tools-msys2-directory)))
+             (when directory
+               (expand-file-name "usr/bin/bash.exe" directory))))
+      (executable-find "sh")))
+
+(defun pdf-tools-build-server (target-directory
+                               &optional
+                               skip-dependencies-p
+                               force-dependencies-p
+                               callback
+                               build-directory)
+  "Build the epdfinfo program in the background.
+
+Install into TARGET-DIRECTORY, which should be a directory.
+
+If CALLBACK is non-nil, it should be a function.  It is called
+with the compiled executable as the single argument or nil, if
+the build falied.
+
+Expect sources to be in BUILD-DIRECTORY.  If nil, search for it
+using `pdf-tools-locate-build-directory'.
+
+See `pdf-tools-install' for the SKIP-DEPENDENCIES-P and
+FORCE-DEPENDENCIES-P arguments.
+
+Returns the buffer of the compilation process."
+
+  (unless callback (setq callback #'ignore))
+  (unless build-directory
+    (setq build-directory (pdf-tools-locate-build-directory)))
+  (cl-check-type target-directory file-directory)
+  (setq target-directory (file-name-as-directory
+                          (expand-file-name target-directory)))
+  (cl-check-type build-directory (and (not null) file-directory))
+  (when (and skip-dependencies-p force-dependencies-p)
+    (error "Can't simultaneously skip and force dependencies"))
+  (let* ((compilation-auto-jump-to-first-error nil)
+         (compilation-scroll-output t)
+         (shell-file-name (pdf-tools-find-bourne-shell))
+         (shell-command-switch "-c")
+         (process-environment process-environment)
+         (default-directory build-directory)
+         (autobuild (shell-quote-argument
+                     (expand-file-name "autobuild" build-directory)))
+         (msys2-p (equal "bash.exe" (file-name-nondirectory shell-file-name))))
+    (unless shell-file-name
+      (error "No suitable shell found"))
+    (when msys2-p
+      (push "BASH_ENV=/etc/profile" process-environment))
+    (let ((executable
+           (expand-file-name
+            (concat "epdfinfo" (and (eq system-type 'windows-nt) ".exe"))
+            target-directory))
+          (compilation-buffer
+           (compilation-start
+            (format "%s -i %s%s%s"
+                    autobuild
+                    (shell-quote-argument target-directory)
+                    (cond
+                     (skip-dependencies-p " -D")
+                     (force-dependencies-p " -d")
+                     (t ""))
+                    (if pdf-tools-installer-os (concat " --os " pdf-tools-installer-os) ""))
+            t)))
+      ;; In most cases user-input is required, so select the window.
+      (if (get-buffer-window compilation-buffer)
+          (select-window (get-buffer-window compilation-buffer))
+        (pop-to-buffer compilation-buffer))
+      (with-current-buffer compilation-buffer
+        (setq-local compilation-error-regexp-alist nil)
+        (add-hook 'compilation-finish-functions
+                  (lambda (_buffer status)
+                    (funcall callback
+                             (and (equal status "finished\n")
+                                  executable)))
+                  nil t)
+        (current-buffer)))))
+
+\f
+;; * ================================================================== *
+;; * Initialization
+;; * ================================================================== *
+
+;;;###autoload
+(defun pdf-tools-install (&optional no-query-p skip-dependencies-p
+                                    no-error-p force-dependencies-p)
+  "Install PDF-Tools in all current and future PDF buffers.
+
+If the `pdf-info-epdfinfo-program' is not running or does not
+appear to be working, attempt to rebuild it.  If this build
+succeeded, continue with the activation of the package.
+Otherwise fail silently, i.e. no error is signaled.
+
+Build the program (if necessary) without asking first, if
+NO-QUERY-P is non-nil.
+
+Don't attempt to install system packages, if SKIP-DEPENDENCIES-P
+is non-nil.
+
+Do not signal an error in case the build failed, if NO-ERROR-P is
+non-nil.
+
+Attempt to install system packages (even if it is deemed
+unnecessary), if FORCE-DEPENDENCIES-P is non-nil.
+
+Note that SKIP-DEPENDENCIES-P and FORCE-DEPENDENCIES-P are
+mutually exclusive.
+
+Note further, that you can influence the installation directory
+by setting `pdf-info-epdfinfo-program' to an appropriate
+value (e.g. ~/bin/epdfinfo) before calling this function.
+
+See `pdf-view-mode' and `pdf-tools-enabled-modes'."
+  (interactive)
+  (if (or (pdf-info-running-p)
+          (ignore-errors (pdf-info-check-epdfinfo) t))
+      (pdf-tools-install-noverify)
+    (let ((target-directory
+           (or (and (stringp pdf-info-epdfinfo-program)
+                    (file-name-directory
+                     pdf-info-epdfinfo-program))
+               pdf-tools-directory)))
+      (if (or no-query-p
+              (y-or-n-p "Need to (re)build the epdfinfo program, do it now ?"))
+        (pdf-tools-build-server
+         target-directory
+         skip-dependencies-p
+         force-dependencies-p
+         (lambda (executable)
+           (let ((msg (format
+                       "Building the PDF Tools server %s"
+                       (if executable "succeeded" "failed"))))
+             (if (not executable)
+                 (funcall (if no-error-p #'message #'error) "%s" msg)
+               (message "%s" msg)
+               (setq pdf-info-epdfinfo-program executable)
+               (let ((pdf-info-restart-process-p t))
+                 (pdf-tools-install-noverify))))))
+        (message "PDF Tools not activated")))))
+
+(defun pdf-tools-install-noverify ()
+  "Like `pdf-tools-install', but skip checking `pdf-info-epdfinfo-program'."
+  (add-to-list 'auto-mode-alist pdf-tools-auto-mode-alist-entry)
+  (add-to-list 'magic-mode-alist pdf-tools-magic-mode-alist-entry)
+  ;; FIXME: Generalize this sometime.
+  (when (memq 'pdf-occur-global-minor-mode
+              pdf-tools-enabled-modes)
+    (pdf-occur-global-minor-mode 1))
+  (when (memq 'pdf-virtual-global-minor-mode
+              pdf-tools-enabled-modes)
+    (pdf-virtual-global-minor-mode 1))
+  (add-hook 'pdf-view-mode-hook 'pdf-tools-enable-minor-modes)
+  (dolist (buf (buffer-list))
+    (with-current-buffer buf
+      (when (and (not (derived-mode-p 'pdf-view-mode))
+                 (pdf-tools-pdf-buffer-p)
+                 (buffer-file-name))
+        (pdf-view-mode)))))
+
+(defun pdf-tools-uninstall ()
+  "Uninstall PDF-Tools in all current and future PDF buffers."
+  (interactive)
+  (pdf-info-quit)
+  (setq-default auto-mode-alist
+    (remove pdf-tools-auto-mode-alist-entry auto-mode-alist))
+  (setq-default magic-mode-alist
+    (remove pdf-tools-magic-mode-alist-entry magic-mode-alist))
+  (pdf-occur-global-minor-mode -1)
+  (pdf-virtual-global-minor-mode -1)
+  (remove-hook 'pdf-view-mode-hook 'pdf-tools-enable-minor-modes)
+  (dolist (buf (buffer-list))
+    (with-current-buffer buf
+      (when (pdf-util-pdf-buffer-p buf)
+        (pdf-tools-disable-minor-modes pdf-tools-modes)
+        (normal-mode)))))
+
+(defun pdf-tools-pdf-buffer-p (&optional buffer)
+  "Return non-nil if BUFFER contains a PDF document."
+  (save-current-buffer
+    (when buffer (set-buffer buffer))
+    (save-excursion
+      (save-restriction
+        (widen)
+        (goto-char 1)
+        (looking-at "%PDF")))))
+
+(defun pdf-tools-assert-pdf-buffer (&optional buffer)
+  (unless (pdf-tools-pdf-buffer-p buffer)
+    (error "Buffer does not contain a PDF document")))
+
+(defun pdf-tools-set-modes-enabled (enable &optional modes)
+  (dolist (m (or modes pdf-tools-enabled-modes))
+    (let ((enabled-p (and (boundp m)
+                          (symbol-value m))))
+      (unless (or (and enabled-p enable)
+                  (and (not enabled-p) (not enable)))
+        (funcall m (if enable 1 -1))))))
+
+;;;###autoload
+(defun pdf-tools-enable-minor-modes (&optional modes)
+  "Enable MODES in the current buffer.
+
+MODES defaults to `pdf-tools-enabled-modes'."
+  (interactive)
+  (pdf-util-assert-pdf-buffer)
+  (pdf-tools-set-modes-enabled t modes)
+  (run-hooks 'pdf-tools-enabled-hook))
+
+(defun pdf-tools-disable-minor-modes (&optional modes)
+  "Disable MODES in the current buffer.
+
+MODES defaults to `pdf-tools-enabled-modes'."
+  (interactive)
+  (pdf-tools-set-modes-enabled nil modes))
+
+(declare-function pdf-occur-global-minor-mode "pdf-occur.el")
+(declare-function pdf-virtual-global-minor-mode "pdf-virtual.el")
+
+;;;###autoload
+(defun pdf-tools-help ()
+  (interactive)
+  (help-setup-xref (list #'pdf-tools-help)
+                   (called-interactively-p 'interactive))
+  (with-help-window (help-buffer)
+    (princ "PDF Tools Help\n\n")
+    (princ "\f~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n")
+    (dolist (m (cons 'pdf-view-mode
+                     (sort (copy-sequence pdf-tools-modes) 'string<)))
+      (princ (format "`%s' is " m))
+      (describe-function-1 m)
+      (terpri) (terpri)
+      (princ "\f~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"))))
+
+\f
+;; * ================================================================== *
+;; * Debugging
+;; * ================================================================== *
+
+(defvar pdf-tools-debug nil
+  "Non-nil, if debugging PDF Tools.")
+
+(defun pdf-tools-toggle-debug ()
+  (interactive)
+  (setq pdf-tools-debug (not pdf-tools-debug))
+  (when (called-interactively-p 'any)
+    (message "Toggled debugging %s" (if pdf-tools-debug "on" "off"))))
+
+(provide 'pdf-tools)
+
+;;; pdf-tools.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-util.el b/elpa/pdf-tools-20191031.642/pdf-util.el
new file mode 100644 (file)
index 0000000..b5f3f55
--- /dev/null
@@ -0,0 +1,1346 @@
+;;; pdf-util.el --- PDF Utility functions. -*- lexical-binding: t -*-
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, multimedia
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;;; Todo:
+;;
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'format-spec)
+(require 'faces)
+
+;; These functions are only used after a PdfView window was asserted,
+;; which won't succeed, if pdf-view.el isn't loaded.
+(declare-function pdf-view-image-size "pdf-view")
+(declare-function pdf-view-image-offset "pdf-view")
+(declare-function pdf-view-current-image "pdf-view")
+(declare-function pdf-view-current-overlay "pdf-view")
+(declare-function pdf-cache-pagesize "pdf-cache")
+
+(declare-function pdf-view-image-type "pdf-view")
+
+
+\f
+;; * ================================================================== *
+;; * Compatibility with older Emacssen (< 25.1)
+;; * ================================================================== *
+
+;; The with-file-modes macro is only available in recent Emacs
+;; versions.
+(eval-when-compile
+  (unless (fboundp 'with-file-modes)
+    (defmacro with-file-modes (modes &rest body)
+      "Execute BODY with default file permissions temporarily set to MODES.
+MODES is as for `set-default-file-modes'."
+      (declare (indent 1) (debug t))
+      (let ((umask (make-symbol "umask")))
+        `(let ((,umask (default-file-modes)))
+           (unwind-protect
+               (progn
+                 (set-default-file-modes ,modes)
+                 ,@body)
+             (set-default-file-modes ,umask)))))))
+
+(unless (fboundp 'alist-get) ;;25.1
+  (defun alist-get (key alist &optional default remove)
+    "Get the value associated to KEY in ALIST.
+DEFAULT is the value to return if KEY is not found in ALIST.
+REMOVE, if non-nil, means that when setting this element, we should
+remove the entry if the new value is `eql' to DEFAULT."
+    (ignore remove) ;;Silence byte-compiler.
+    (let ((x (assq key alist)))
+      (if x (cdr x) default))))
+
+(require 'register)
+(unless (fboundp 'register-read-with-preview)
+  (defalias 'register-read-with-preview 'read-char
+    "Compatibility alias for pdf-tools."))
+
+;; In Emacs 24.3 window-width does not have a PIXELWISE argument.
+(defmacro pdf-util-window-pixel-width (&optional window)
+  "Return the width of WINDOW in pixel."
+  (if (< (cdr (subr-arity (symbol-function 'window-body-width))) 2)
+      (let ((window* (make-symbol "window")))
+        `(let ((,window* ,window))
+           (*  (window-body-width ,window*)
+               (frame-char-width (window-frame ,window*)))))
+    `(window-body-width ,window t)))
+
+;; In Emacs 24.3 image-mode-winprops leads to infinite recursion.
+(unless (or (> emacs-major-version 24)
+            (and (= emacs-major-version 24)
+                 (>= emacs-minor-version 4)))
+  (require 'image-mode)
+  (defvar image-mode-winprops-original-function
+    (symbol-function 'image-mode-winprops))
+  (eval-after-load "image-mode"
+    '(defun image-mode-winprops (&optional window cleanup)
+       (if (not (eq major-mode 'pdf-view-mode))
+           (funcall image-mode-winprops-original-function
+                    window cleanup)
+         (cond ((null window)
+                (setq window
+                      (if (eq (current-buffer) (window-buffer)) (selected-window) t)))
+               ((eq window t))
+               ((not (windowp window))
+                (error "Not a window: %s" window)))
+         (when cleanup
+           (setq image-mode-winprops-alist
+                 (delq nil (mapcar (lambda (winprop)
+                                     (let ((w (car-safe winprop)))
+                                       (if (or (not (windowp w)) (window-live-p w))
+                                           winprop)))
+                                   image-mode-winprops-alist))))
+         (let ((winprops (assq window image-mode-winprops-alist)))
+           ;; For new windows, set defaults from the latest.
+           (if winprops
+               ;; Move window to front.
+               (setq image-mode-winprops-alist
+                     (cons winprops (delq winprops image-mode-winprops-alist)))
+             (setq winprops (cons window
+                                  (copy-alist (cdar image-mode-winprops-alist))))
+             ;; Add winprops before running the hook, to avoid inf-loops if the hook
+             ;; triggers window-configuration-change-hook.
+             (setq image-mode-winprops-alist
+                   (cons winprops image-mode-winprops-alist))
+             (run-hook-with-args 'image-mode-new-window-functions winprops))
+           winprops)))))
+
+
+\f
+;; * ================================================================== *
+;; * Transforming coordinates
+;; * ================================================================== *
+
+
+(defun pdf-util-scale (list-of-edges-or-pos scale &optional rounding-fn)
+  "Scale LIST-OF-EDGES-OR-POS by SCALE.
+
+SCALE is a cons (SX . SY), by which edges/positions are scaled.
+If ROUNDING-FN is non-nil, it should be a function of one
+argument, a real value, returning a rounded
+value (e.g. `ceiling').
+
+The elements in LIST-OF-EDGES-OR-POS should be either a list
+\(LEFT TOP RIGHT BOT\) or a position \(X . Y\).
+
+LIST-OF-EDGES-OR-POS may also be a single such element.
+
+Return scaled list of edges if LIST-OF-EDGES-OR-POS was indeed a list,
+else return the scaled singleton."
+
+  (let ((have-list-p (listp (car list-of-edges-or-pos))))
+    (unless have-list-p
+      (setq list-of-edges-or-pos (list list-of-edges-or-pos)))
+    (let* ((sx (car scale))
+           (sy (cdr scale))
+           (result
+            (mapcar
+             (lambda (edges)
+               (cond
+                ((consp (cdr edges))
+                 (let ((e (list (* (nth 0 edges) sx)
+                                (* (nth 1 edges) sy)
+                                (* (nth 2 edges) sx)
+                                (* (nth 3 edges) sy))))
+                   (if rounding-fn
+                       (mapcar rounding-fn e)
+                     e)))
+                (rounding-fn
+                 (cons (funcall rounding-fn (* (car edges) sx))
+                       (funcall rounding-fn (* (cdr edges) sy))))
+                (t
+                 (cons (* (car edges) sx)
+                       (* (cdr edges) sy)))))
+             list-of-edges-or-pos)))
+      (if have-list-p
+          result
+        (car result)))))
+
+(defun pdf-util-scale-to (list-of-edges from to &optional rounding-fn)
+  "Scale LIST-OF-EDGES in FROM basis to TO.
+
+FROM and TO should both be a cons \(WIDTH . HEIGTH\).  See also
+`pdf-util-scale'."
+
+  (pdf-util-scale list-of-edges
+                  (cons (/ (float (car to))
+                           (float (car from)))
+                        (/ (float (cdr to))
+                           (float (cdr from))))
+                  rounding-fn))
+
+(defun pdf-util-scale-pixel-to-points (list-of-pixel-edges
+                                       &optional rounding-fn displayed-p window)
+  "Scale LIST-OF-PIXEL-EDGES to point values.
+
+The result depends on the currently displayed page in WINDOW.
+See also `pdf-util-scale'."
+  (pdf-util-assert-pdf-window window)
+  (pdf-util-scale-to
+   list-of-pixel-edges
+   (pdf-view-image-size displayed-p window)
+   (pdf-cache-pagesize (pdf-view-current-page window))
+   rounding-fn))
+
+(defun pdf-util-scale-points-to-pixel (list-of-points-edges
+                                       &optional rounding-fn displayed-p window)
+  "Scale LIST-OF-POINTS-EDGES to point values.
+
+The result depends on the currently displayed page in WINDOW.
+See also `pdf-util-scale'."
+  (pdf-util-assert-pdf-window window)
+  (pdf-util-scale-to
+   list-of-points-edges
+   (pdf-cache-pagesize (pdf-view-current-page window))
+   (pdf-view-image-size displayed-p window)
+   rounding-fn))
+
+(defun pdf-util-scale-relative-to-points (list-of-relative-edges
+                                          &optional rounding-fn window)
+  "Scale LIST-OF-RELATIVE-EDGES to point values.
+
+The result depends on the currently displayed page in WINDOW.
+See also `pdf-util-scale'."
+  (pdf-util-assert-pdf-window window)
+  (pdf-util-scale-to
+   list-of-relative-edges
+   '(1.0 . 1.0)
+   (pdf-cache-pagesize (pdf-view-current-page window))
+   rounding-fn))
+
+(defun pdf-util-scale-points-to-relative (list-of-points-edges
+                                          &optional rounding-fn window)
+  "Scale LIST-OF-POINTS-EDGES to relative values.
+
+See also `pdf-util-scale'."
+  (pdf-util-assert-pdf-window window)
+  (pdf-util-scale-to
+   list-of-points-edges
+   (pdf-cache-pagesize (pdf-view-current-page window))
+   '(1.0 . 1.0)
+   rounding-fn))
+
+(defun pdf-util-scale-pixel-to-relative (list-of-pixel-edges
+                                         &optional rounding-fn displayed-p window)
+  "Scale LIST-OF-PIXEL-EDGES to relative values.
+
+The result depends on the currently displayed page in WINDOW.
+See also `pdf-util-scale'."
+  (pdf-util-assert-pdf-window window)
+  (pdf-util-scale-to
+   list-of-pixel-edges
+   (pdf-view-image-size displayed-p window)
+   '(1.0 . 1.0)
+   rounding-fn))
+
+
+(defun pdf-util-scale-relative-to-pixel (list-of-relative-edges
+                                         &optional rounding-fn displayed-p window)
+  "Scale LIST-OF-EDGES to match SIZE.
+
+The result depends on the currently displayed page in WINDOW.
+See also `pdf-util-scale'."
+  (pdf-util-assert-pdf-window window)
+  (pdf-util-scale-to
+   list-of-relative-edges
+   '(1.0 . 1.0)
+   (pdf-view-image-size displayed-p window)
+   rounding-fn))
+
+(defun pdf-util-translate (list-of-edges-or-pos
+                           offset &optional opposite-direction-p)
+  "Translate LIST-OF-EDGES-OR-POS by OFFSET
+
+OFFSET should be a cons \(X . Y\), by which to translate
+LIST-OF-EDGES-OR-POS.  If OPPOSITE-DIRECTION-P is non-nil
+translate by \(-X . -Y\).
+
+See `pdf-util-scale' for the LIST-OF-EDGES-OR-POS argument."
+
+  (let ((have-list-p (listp (car list-of-edges-or-pos))))
+    (unless have-list-p
+      (setq list-of-edges-or-pos (list list-of-edges-or-pos)))
+    (let* ((ox (if opposite-direction-p
+                   (- (car offset))
+                 (car offset)))
+           (oy (if opposite-direction-p
+                   (- (cdr offset))
+                 (cdr offset)))
+           (result
+            (mapcar
+             (lambda (edges)
+               (cond
+                ((consp (cdr edges))
+                 (list (+ (nth 0 edges) ox)
+                       (+ (nth 1 edges) oy)
+                       (+ (nth 2 edges) ox)
+                       (+ (nth 3 edges) oy)))
+                (t
+                 (cons (+ (car edges) ox)
+                       (+ (cdr edges) oy)))))
+             list-of-edges-or-pos)))
+      (if have-list-p
+          result
+        (car result)))))
+
+(defun pdf-util-edges-transform (region elts &optional to-region-p)
+  "Translate ELTS according to REGION.
+
+ELTS may be one edges list or a position or a list thereof.
+Translate each from region coordinates to (0 0 1 1) or the
+opposite, if TO-REGION-P is non-nil.  All coordinates should be
+relative.
+
+Returns the translated list of elements or the single one
+depending on the input."
+
+  (when elts
+    (let ((have-list-p (consp (car-safe elts))))
+      (unless have-list-p
+        (setq elts (list elts)))
+      (let ((result
+             (if (null region)
+                 elts
+               (mapcar (lambda (edges)
+                         (let ((have-pos-p (numberp (cdr edges))))
+                           (when have-pos-p
+                             (setq edges (list (car edges) (cdr edges)
+                                               (car edges) (cdr edges))))
+                           (pdf-util-with-edges (edges region)
+                             (let ((newedges
+                                    (mapcar (lambda (n)
+                                              (min 1.0 (max 0.0 n)))
+                                            (if to-region-p
+                                                `(,(/ (- edges-left region-left)
+                                                      region-width)
+                                                  ,(/ (- edges-top region-top)
+                                                      region-height)
+                                                  ,(/ (- edges-right region-left)
+                                                      region-width)
+                                                  ,(/ (- edges-bot region-top)
+                                                      region-height))
+                                              `(,(+ (* edges-left region-width)
+                                                    region-left)
+                                                ,(+ (* edges-top region-height)
+                                                    region-top)
+                                                ,(+ (* edges-right region-width)
+                                                    region-left)
+                                                ,(+ (* edges-bot region-height)
+                                                    region-top))))))
+                               (if have-pos-p
+                                   (cons (car newedges) (cadr newedges))
+                                 newedges)))))
+                       elts))))
+        (if have-list-p
+            result
+          (car result))))))
+
+(defmacro pdf-util-with-edges (list-of-edges &rest body)
+  "Provide some convenient macros for the edges in LIST-OF-EDGES.
+
+LIST-OF-EDGES should be a list of variables \(X ...\), each one
+holding a list of edges. Inside BODY the symbols X-left, X-top,
+X-right, X-bot, X-width and X-height expand to their respective
+values."
+
+  (declare (indent 1) (debug (sexp &rest form)))
+  (unless (cl-every 'symbolp list-of-edges)
+    (error "Argument should be a list of symbols"))
+  (let ((list-of-syms
+         (mapcar (lambda (edge)
+                   (cons edge (mapcar
+                               (lambda (kind)
+                                 (intern (format "%s-%s" edge kind)))
+                               '(left top right bot width height))))
+                 list-of-edges)))
+    (macroexpand-all
+     `(cl-symbol-macrolet
+          ,(apply 'nconc
+                  (mapcar
+                   (lambda (edge-syms)
+                     (let ((edge (nth 0 edge-syms))
+                           (syms (cdr edge-syms)))
+                       `((,(pop syms) (nth 0 ,edge))
+                         (,(pop syms) (nth 1 ,edge))
+                         (,(pop syms) (nth 2 ,edge))
+                         (,(pop syms) (nth 3 ,edge))
+                         (,(pop syms) (- (nth 2 ,edge)
+                                         (nth 0 ,edge)))
+                         (,(pop syms) (- (nth 3 ,edge)
+                                         (nth 1 ,edge))))))
+                   list-of-syms))
+        ,@body))))
+
+\f
+;; * ================================================================== *
+;; * Scrolling
+;; * ================================================================== *
+
+(defun pdf-util-image-displayed-edges (&optional window displayed-p)
+  "Return the visible region of the image in WINDOW.
+
+Returns a list of pixel edges."
+  (pdf-util-assert-pdf-window)
+  (let* ((edges (window-inside-pixel-edges window))
+         (isize (pdf-view-image-size displayed-p window))
+         (offset (if displayed-p
+                     `(0 . 0)
+                   (pdf-view-image-offset window)))
+         (hscroll (* (window-hscroll window)
+                     (frame-char-width (window-frame window))))
+         (vscroll (window-vscroll window t))
+         (x0 (+ hscroll (car offset)))
+         (y0 (+ vscroll (cdr offset)))
+         (x1 (min (car isize)
+                  (+ x0 (- (nth 2 edges) (nth 0 edges)))))
+         (y1 (min (cdr isize)
+                  (+ y0 (- (nth 3 edges) (nth 1 edges))))))
+    (mapcar 'round (list x0 y0 x1 y1))))
+
+(defun pdf-util-required-hscroll (edges &optional eager-p context-pixel)
+  "Return the amount of scrolling nescessary, to make image EDGES visible.
+
+Scroll as little as necessary.  Unless EAGER-P is non-nil, in
+which case scroll as much as possible.
+
+Keep CONTEXT-PIXEL pixel of the image visible at the bottom and
+top of the window.  CONTEXT-PIXEL defaults to 0.
+
+Return the required hscroll in columns or nil, if scrolling is not
+needed."
+
+  (pdf-util-assert-pdf-window)
+  (unless context-pixel
+    (setq context-pixel 0))
+  (let* ((win (window-inside-pixel-edges))
+         (image-width (car (pdf-view-image-size t)))
+         (image-left (* (frame-char-width)
+                        (window-hscroll)))
+         (edges (pdf-util-translate
+                 edges
+                 (pdf-view-image-offset) t)))
+    (pdf-util-with-edges (win edges)
+      (let* ((edges-left (- edges-left context-pixel))
+             (edges-right (+ edges-right context-pixel)))
+        (if (< edges-left image-left)
+            (round (/ (max 0 (if eager-p
+                                 (- edges-right win-width)
+                               edges-left))
+                      (frame-char-width)))
+          (if (> (min image-width
+                      edges-right)
+                 (+ image-left win-width))
+              (round (/ (min (- image-width win-width)
+                             (if eager-p
+                                 edges-left
+                               (- edges-right win-width)))
+                        (frame-char-width)))))))))
+
+(defun pdf-util-required-vscroll (edges &optional eager-p context-pixel)
+  "Return the amount of scrolling nescessary, to make image EDGES visible.
+
+Scroll as little as necessary.  Unless EAGER-P is non-nil, in
+which case scroll as much as possible.
+
+Keep CONTEXT-PIXEL pixel of the image visible at the bottom and
+top of the window.  CONTEXT-PIXEL defaults to an equivalent pixel
+value of `next-screen-context-lines'.
+
+Return the required vscroll in lines or nil, if scrolling is not
+needed."
+
+  (pdf-util-assert-pdf-window)
+  (let* ((win (window-inside-pixel-edges))
+         (image-height (cdr (pdf-view-image-size t)))
+         (image-top (window-vscroll nil t))
+         (edges (pdf-util-translate
+                 edges
+                 (pdf-view-image-offset) t)))
+    (pdf-util-with-edges (win edges)
+      (let* ((context-pixel (or context-pixel
+                                (* next-screen-context-lines
+                                   (frame-char-height))))
+             ;;Be careful not to modify edges.
+             (edges-top (- edges-top context-pixel))
+             (edges-bot (+ edges-bot context-pixel)))
+        (if (< edges-top image-top)
+            (round (/ (max 0 (if eager-p
+                                 (- edges-bot win-height)
+                               edges-top))
+                      (float (frame-char-height))))
+          (if (> (min image-height
+                      edges-bot)
+                 (+ image-top win-height))
+              (round (/ (min (- image-height win-height)
+                             (if eager-p
+                                 edges-top
+                               (- edges-bot win-height)))
+                        (float (frame-char-height))))))))))
+
+(defun pdf-util-scroll-to-edges (edges &optional eager-p)
+  "Scroll window such that image EDGES are visible.
+
+Scroll as little as necessary.  Unless EAGER-P is non-nil, in
+which case scroll as much as possible."
+
+  (let ((vscroll (pdf-util-required-vscroll edges eager-p))
+        (hscroll (pdf-util-required-hscroll edges eager-p)))
+    (when vscroll
+      (image-set-window-vscroll vscroll))
+    (when hscroll
+      (image-set-window-hscroll hscroll))))
+
+
+\f
+;; * ================================================================== *
+;; * Temporary files
+;; * ================================================================== *
+
+(defvar pdf-util--base-directory nil
+  "Base directory for temporary files.")
+
+(defvar-local pdf-util--dedicated-directory nil
+  "The relative name of buffer's dedicated directory.")
+
+(defun pdf-util-dedicated-directory ()
+  "Return the name of a existing dedicated directory.
+
+The directory is exclusive to the current buffer.  It will be
+automatically deleted, if Emacs or the current buffer are
+killed."
+  (with-file-modes #o0700
+    (unless (and pdf-util--base-directory
+                 (file-directory-p
+                  pdf-util--base-directory)
+                 (not (file-symlink-p
+                       pdf-util--base-directory)))
+      (add-hook 'kill-emacs-hook
+                (lambda nil
+                  (when (and pdf-util--base-directory
+                             (file-directory-p pdf-util--base-directory))
+                    (delete-directory pdf-util--base-directory t))))
+      (setq pdf-util--base-directory
+            (make-temp-file "pdf-tools-" t)))
+    (unless (and pdf-util--dedicated-directory
+                 (file-directory-p pdf-util--dedicated-directory)
+                 (not (file-symlink-p
+                       pdf-util--base-directory)))
+      (let ((temporary-file-directory
+             pdf-util--base-directory))
+        (setq pdf-util--dedicated-directory
+              (make-temp-file (convert-standard-filename
+                              (concat (if buffer-file-name
+                                          (file-name-nondirectory
+                                           buffer-file-name)
+                                        (buffer-name))
+                                      "-"))
+                              t))
+        (add-hook 'kill-buffer-hook 'pdf-util-delete-dedicated-directory
+                  nil t)))
+    pdf-util--dedicated-directory))
+
+(defun pdf-util-delete-dedicated-directory ()
+  "Delete current buffer's dedicated directory."
+  (delete-directory (pdf-util-dedicated-directory) t))
+
+(defun pdf-util-expand-file-name (name)
+  "Expand filename against current buffer's dedicated directory."
+  (expand-file-name name (pdf-util-dedicated-directory)))
+
+(defun pdf-util-make-temp-file (prefix &optional dir-flag suffix)
+  "Create a temporary file in current buffer's dedicated directory.
+
+See `make-temp-file' for the arguments."
+  (let ((temporary-file-directory
+         (pdf-util-dedicated-directory)))
+    (make-temp-file (convert-standard-filename prefix) dir-flag suffix)))
+
+\f
+;; * ================================================================== *
+;; * Various
+;; * ================================================================== *
+
+(defmacro pdf-util-debug (&rest body)
+  "Execute BODY only if debugging is enabled."
+  (declare (indent 0) (debug t))
+  `(when (bound-and-true-p pdf-tools-debug)
+     ,@body))
+
+(defun pdf-util-pdf-buffer-p (&optional buffer)
+  (and (or (null buffer)
+           (buffer-live-p buffer))
+       (save-current-buffer
+         (and buffer (set-buffer buffer))
+         (derived-mode-p 'pdf-view-mode))))
+
+(defun pdf-util-assert-pdf-buffer (&optional buffer)
+  (unless (pdf-util-pdf-buffer-p buffer)
+    (error "Buffer is not in PDFView mode")))
+
+(defun pdf-util-pdf-window-p (&optional window)
+  (unless (or (null window)
+              (window-live-p window))
+    (signal 'wrong-type-argument (list 'window-live-p window)))
+  (unless window (setq window (selected-window)))
+  (and (window-live-p window)
+       (with-selected-window window
+         (pdf-util-pdf-buffer-p))))
+
+(defun pdf-util-assert-pdf-window (&optional window)
+  (unless (pdf-util-pdf-window-p window)
+    (error "Window's buffer is not in PdfView mode")))
+
+(defun pdf-util-munch-file (filename &optional multibyte-p)
+  "Read contents from FILENAME and delete it.
+
+Return the file's content as a unibyte string, unless MULTIBYTE-P
+is non-nil."
+  (unwind-protect
+      (with-temp-buffer
+        (set-buffer-multibyte multibyte-p)
+        (insert-file-contents-literally filename)
+        (buffer-substring-no-properties
+         (point-min)
+         (point-max)))
+    (when (and filename
+               (file-exists-p filename))
+      (delete-file filename))))
+
+(defun pdf-util-hexcolor (color)
+  "Return COLOR in hex-format.
+
+Singal an error, if color is invalid."
+  (if (string-match "\\`#[[:xdigit:]]\\{6\\}\\'" color)
+      color
+    (let ((values (color-values color)))
+      (unless values
+        (signal 'wrong-type-argument (list 'color-defined-p color)))
+      (apply 'format "#%02x%02x%02x"
+             (mapcar (lambda (c) (lsh c -8))
+                     values)))))
+
+(defun pdf-util-highlight-regexp-in-string (regexp string &optional face)
+  "Highlight all occurrences of REGEXP in STRING using FACE.
+
+FACE defaults to the `match' face.  Returns the new fontified
+string."
+  (with-temp-buffer
+    (save-excursion (insert string))
+    (while (and (not (eobp))
+                (re-search-forward regexp nil t))
+      (if (= (match-beginning 0)
+             (match-end 0))
+          (forward-char)
+        (put-text-property
+         (match-beginning 0)
+         (point)
+         'face (or face 'match))))
+    (buffer-string)))
+
+(defun pdf-util-color-completions ()
+  "Return a fontified list of defined colors."
+  (let ((color-list (list-colors-duplicates))
+        colors)
+    (dolist (cl color-list)
+      (dolist (c (reverse cl))
+        (push (propertize c 'face `(:background ,c))
+              colors)))
+    (nreverse colors)))
+
+(defun pdf-util-tooltip-in-window (text x y &optional window)
+  (let* ((we (window-inside-absolute-pixel-edges window))
+         (dx (round (+ x (nth 0 we))))
+         (dy (round (+ y (nth 1 we))))
+         (tooltip-frame-parameters
+          `((left . ,dx)
+            (top . ,dy)
+            ,@tooltip-frame-parameters)))
+    (tooltip-show text)))
+
+(defun pdf-util-tooltip-arrow (image-top &optional timeout)
+  (pdf-util-assert-pdf-window)
+  (when (floatp image-top)
+    (setq image-top
+          (round (* image-top (cdr (pdf-view-image-size))))))
+  (let* (x-gtk-use-system-tooltips ;allow for display property in tooltip
+         (dx (+ (or (car (window-margins)) 0)
+                (car (window-fringes))))
+         (dy image-top)
+         (pos (list dx dy dx (+ dy (* 2 (frame-char-height)))))
+         (vscroll
+          (pdf-util-required-vscroll pos))
+         (tooltip-frame-parameters
+          `((border-width . 0)
+            (internal-border-width . 0)
+            ,@tooltip-frame-parameters))
+         (tooltip-hide-delay (or timeout 3)))
+    (when vscroll
+      (image-set-window-vscroll vscroll))
+    (setq dy (max 0 (- dy
+                       (cdr (pdf-view-image-offset))
+                       (window-vscroll nil t)
+                       (frame-char-height))))
+    (when (overlay-get (pdf-view-current-overlay) 'before-string)
+      (let* ((e (window-inside-pixel-edges))
+             (xw (pdf-util-with-edges (e) e-width)))
+        (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2))))
+    (pdf-util-tooltip-in-window
+     (propertize
+      " " 'display (propertize
+                    "\u2192" ;;right arrow
+                    'display '(height 2)
+                    'face `(:foreground
+                            "orange red"
+                            :background
+                            ,(if (bound-and-true-p pdf-view-midnight-minor-mode)
+                                 (cdr pdf-view-midnight-colors)
+                               "white"))))
+     dx dy)))
+
+(defvar pdf-util--face-colors-cache (make-hash-table))
+
+(defadvice enable-theme (after pdf-util-clear-faces-cache activate)
+  (clrhash pdf-util--face-colors-cache))
+
+(defun pdf-util-face-colors (face &optional dark-p)
+  "Return both colors of FACE as a cons.
+
+Look also in inherited faces.  If DARK-P is non-nil, return dark
+colors, otherwise light."
+  (let* ((bg (if dark-p 'dark 'light))
+         (spec (list (get face 'face-defface-spec)
+                     (get face 'theme-face)
+                     (get face 'customized-face)))
+         (cached (gethash face pdf-util--face-colors-cache)))
+    (cl-destructuring-bind (&optional cspec color-alist)
+        cached
+      (or (and color-alist
+               (equal cspec spec)
+               (cdr (assq bg color-alist)))
+          (let* ((this-bg (frame-parameter nil 'background-mode))
+                 (frame-background-mode bg)
+                 (f (and (not (eq bg this-bg))
+                         (x-create-frame-with-faces '((visibility . nil))))))
+            (with-selected-frame (or f (selected-frame))
+              (unwind-protect
+                  (let ((colors
+                         (cons (face-attribute face :foreground nil 'default)
+                               (face-attribute face :background nil 'default))))
+                    (puthash face `(,(mapcar 'copy-sequence spec)
+                                    ((,bg . ,colors) ,@color-alist))
+                             pdf-util--face-colors-cache)
+                    colors)
+                (when (and f (frame-live-p f))
+                  (delete-frame f)))))))))
+
+(defun pdf-util-window-attach (awindow &optional window)
+  "Attach AWINDOW to WINDOW.
+
+This has the following effect.  Whenever WINDOW, defaulting to
+the selected window, stops displaying the buffer it currently
+displays (e.g., by switching buffers or because it was deleted)
+AWINDOW is deleted."
+  (unless window (setq window (selected-window)))
+  (let ((buffer (window-buffer window))
+        (hook (make-symbol "window-attach-hook")))
+    (fset hook
+          (lambda ()
+            (when (or (not (window-live-p window))
+                      (not (eq buffer (window-buffer window))))
+              (remove-hook 'window-configuration-change-hook
+                           hook)
+              ;; Deleting windows inside wcch may cause errors in
+              ;; windows.el .
+              (run-with-timer
+               0 nil (lambda (win)
+                       (when (and (window-live-p win)
+                                  (not (eq win (selected-window))))
+                         (delete-window win)))
+               awindow))))
+    (add-hook 'window-configuration-change-hook hook)))
+
+(defun display-buffer-split-below-and-attach (buf alist)
+  "Display buffer action using `pdf-util-window-attach'."
+  (let ((window (selected-window))
+        (height (cdr (assq 'window-height alist)))
+        newwin)
+    (when height
+      (when (floatp height)
+        (setq height (round (* height (frame-height)))))
+      (setq height (- (max height window-min-height))))
+    (setq newwin (window--display-buffer
+                  buf
+                  (split-window-below height)
+                  'window alist))
+    (pdf-util-window-attach newwin window)
+    newwin))
+
+(defun pdf-util-goto-position (line &optional column)
+  "Goto LINE and COLUMN in the current buffer.
+
+COLUMN defaults to 0.  Widen the buffer, if the position is
+outside the current limits."
+  (let ((pos
+         (when (> line 0)
+           (save-excursion
+             (save-restriction
+               (widen)
+               (goto-char 1)
+               (when (= 0 (forward-line (1- line)))
+                 (when (and column (> column 0))
+                   (forward-char (1- column)))
+                 (point)))))))
+    (when pos
+      (when (or (< pos (point-min))
+                (> pos (point-max)))
+        (widen))
+      (goto-char pos))))
+
+(defun pdf-util-seq-alignment (seq1 seq2 &optional similarity-fn alignment-type)
+  "Return an alignment of sequences SEQ1 and SEQ2.
+
+SIMILARITY-FN should be a function. It is called with two
+arguments: One element from SEQ1 and one from SEQ2.  It should
+return a number determining how similar the elements are, where
+higher values mean `more similar'.  The default returns 1 if the
+elements are equal, else -1.
+
+ALIGNMENT-TYPE may be one of the symbols `prefix', `suffix',
+`infix' or nil.  If it is `prefix', trailing elements in SEQ2 may
+be ignored. For example the alignment of
+
+\(0 1\) and \(0 1 2\)
+
+using prefix matching is 0, since the prefixes are equal and the
+trailing 2 is ignored.  The other possible values have similar
+effects.  The default is nil, which means to match the whole
+sequences.
+
+Return a cons \(VALUE . ALINGMENT\), where VALUE says how similar
+the sequences are and ALINGMENT is a list of \(E1 . E2\), where
+E1 is an element from SEQ1 or nil, likewise for E2.  If one of
+them is nil, it means there is gap at this position in the
+respective sequence."
+
+  (cl-macrolet ((make-matrix (rows columns)
+                  (list 'apply (list 'quote 'vector)
+                        (list 'cl-loop 'for 'i 'from 1 'to rows
+                              'collect (list 'make-vector columns nil))))
+                (mset (matrix row column newelt)
+                  (list 'aset (list 'aref matrix row) column newelt))
+                (mref (matrix row column)
+                  (list 'aref (list 'aref matrix row) column)))
+    (let* ((nil-value nil)
+           (len1 (length seq1))
+           (len2 (length seq2))
+           (d (make-matrix (1+ len1) (1+ len2)))
+           (prefix-p (memq alignment-type '(prefix infix)))
+           (suffix-p (memq alignment-type '(suffix infix)))
+           (similarity-fn (or similarity-fn
+                              (lambda (a b)
+                                (if (equal a b) 1 -1)))))
+
+      (cl-loop for i from 0 to len1 do
+        (mset d i 0 (- i)))
+      (cl-loop for j from 0 to len2 do
+        (mset d 0 j (if suffix-p 0 (- j))))
+
+      (cl-loop for i from 1 to len1 do
+        (cl-loop for j from 1 to len2 do
+          (let ((max (max
+                      (1- (mref d (1- i) j))
+                      (+ (mref d i (1- j))
+                         (if (and prefix-p (= i len1)) 0 -1))
+                      (+ (mref d (1- i) (1- j))
+                         (funcall similarity-fn
+                                  (elt seq1 (1- i))
+                                  (elt seq2 (1- j)))))))
+            (mset d i j max))))
+
+      (let ((i len1)
+            (j len2)
+            alignment)
+        (while (or (> i 0)
+                   (> j 0))
+          (cond
+           ((and (> i 0)
+                 (= (mref d i j)
+                    (1- (mref d (1- i) j))))
+            (cl-decf i)
+            (push (cons (elt seq1 i) nil-value) alignment))
+           ((and (> j 0)
+                 (= (mref d i j)
+                    (+ (mref d i (1- j))
+                       (if (or (and (= i 0) suffix-p)
+                               (and (= i len1) prefix-p))
+                           0 -1))))
+            (cl-decf j)
+            (push (cons nil-value (elt seq2 j)) alignment))
+           (t
+            (cl-assert (and (> i 0) (> j 0)) t)
+            (cl-decf i)
+            (cl-decf j)
+            (push (cons (elt seq1 i)
+                        (elt seq2 j)) alignment))))
+        (cons (mref d len1 len2) alignment)))))
+
+
+(defun pdf-util-pcre-quote (string)
+  "Escape STRING for use as a PCRE.
+
+See also `regexp-quote'."
+
+  (let ((to-escape
+         (eval-when-compile (append "\0\\|()[]{}^$*+?." nil)))
+        (chars (append string nil))
+        escaped)
+    (dolist (ch chars)
+      (when (memq ch to-escape)
+        (push ?\\ escaped))
+      (push ch escaped))
+    (apply 'string (nreverse escaped))))
+
+(defun pdf-util-frame-ppi ()
+  "Return the PPI of the current frame."
+  (let* ((props (frame-monitor-attributes))
+         (px (nthcdr 2 (alist-get 'geometry props)))
+         (mm (alist-get 'mm-size props))
+         (dp (sqrt (+ (expt (nth 0 px) 2)
+                      (expt (nth 1 px) 2))))
+         (di (sqrt (+ (expt (/ (nth 0 mm) 25.4) 2)
+                      (expt (/ (nth 1 mm) 25.4) 2)))))
+    (/ dp di)))
+
+(defvar pdf-view-use-scaling)
+
+(defun pdf-util-frame-scale-factor ()
+  "Return the frame scale factor depending on the image type used for display.
+When `pdf-view-use-scaling' is non-nil and imagemagick or
+image-io are used as the image type for display, return the
+backing-scale-factor of the frame if available. If a
+backing-scale-factor attribute isn't available, return 2 if the
+frame's PPI is larger than 180. Otherwise, return 1."
+  (if (and pdf-view-use-scaling
+           (memq (pdf-view-image-type) '(imagemagick image-io))
+           (fboundp 'frame-monitor-attributes))
+      (or (cdr (assq 'backing-scale-factor (frame-monitor-attributes)))
+          (if (>= (pdf-util-frame-ppi) 180)
+              2
+            1))
+    1))
+
+\f
+;; * ================================================================== *
+;; * Imagemagick's convert
+;; * ================================================================== *
+
+(defcustom pdf-util-convert-program
+  ;; Avoid using the MS Windows command convert.exe .
+  (unless (memq system-type '(ms-dos windows-nt))
+    (executable-find "convert"))
+  "Absolute path to the convert program."
+  :group 'pdf-tools
+  :type 'executable)
+
+(defcustom pdf-util-fast-image-format nil
+  "An image format appropriate for fast displaying.
+
+This should be a cons \(TYPE . EXT\) where type is the Emacs
+image-type and EXT the appropriate file extension starting with a
+dot. If nil, the value is determined automatically.
+
+Different formats have different properties, with respect to
+Emacs loading time, convert creation time and the file-size.  In
+general, uncompressed formats are faster, but may need a fair
+amount of (temporary) disk space."
+  :group 'pdf-tools
+  :type '(cons symbol string))
+
+(defun pdf-util-assert-convert-program ()
+  (unless (and pdf-util-convert-program
+               (file-executable-p pdf-util-convert-program))
+    (error "The pdf-util-convert-program is unset or non-executable")))
+
+(defun pdf-util-image-file-size (image-file)
+  "Determine the size of the image in IMAGE-FILE.
+
+Returns a cons \(WIDTH . HEIGHT\)."
+  (pdf-util-assert-convert-program)
+  (with-temp-buffer
+    (when (save-excursion
+            (= 0 (call-process
+                  pdf-util-convert-program
+                  nil (current-buffer) nil
+                  image-file "-format" "%w %h" "info:")))
+      (let ((standard-input (current-buffer)))
+        (cons (read) (read))))))
+
+(defun pdf-util-convert (in-file out-file &rest spec)
+  "Convert image IN-FILE to OUT-FILE according to SPEC.
+
+IN-FILE should be the name of a file containing an image.  Write
+the result to OUT-FILE.  The extension of this filename ususally
+determines the resulting image-type.
+
+SPEC is a property list, specifying what the convert programm
+should do with the image.  All manipulations operate on a
+rectangle, see below.
+
+SPEC may contain the following keys, respectively values.
+
+`:foreground' Set foreground color for all following operations.
+
+`:background' Dito, for the background color.
+
+`:commands' A list of strings representing arguments to convert
+for image manipulations.  It may contain %-escape characters, as
+follows.
+
+%f -- Expands to the foreground color.
+%b -- Expands to the background color.
+%g -- Expands to the geometry of the current rectangle, i.e. WxH+X+Y.
+%x -- Expands to the left edge of rectangle.
+%X -- Expands to the right edge of rectangle.
+%y -- Expands to the top edge of rectangle.
+%Y -- Expands to the bottom edge of rectangle.
+%w -- Expands to the width of rectangle.
+%h -- Expands to the height of rectangle.
+
+Keep in mind, that every element of this list is seen by convert
+as a single argument.
+
+`:formats' An alist of additional %-escapes.  Every element
+should be a cons \(CHAR . STRING\) or \(CHAR . FUNCTION\).  In
+the first case, all occurences of %-CHAR in the above commands
+will be replaced by STRING.  In the second case FUNCTION is
+called with the current rectangle and it should return the
+replacement string.
+
+`:apply' A list of rectangles \(\(LEFT TOP RIGHT BOT\) ...\) in
+IN-FILE coordinates. Each such rectangle triggers one execution
+of the last commands given earlier in SPEC. E.g. a call like
+
+\(pdf-util-convert
+       image-file out-file
+       :foreground \"black\"
+       :background \"white\"
+       :commands '\(\"-fill\" \"%f\" \"-draw\" \"rectangle %x,%y,%X,%Y\"\)
+       :apply '\(\(0 0 10 10\) \(10 10 20 20\)\)
+       :commands '\(\"-fill\" \"%b\" \"-draw\" \"rectangle %x,%y,%X,%Y\"\)
+       :apply '\(\(10 0 20 10\) \(0 10 10 20\)\)\)
+
+would draw a 4x4 checkerboard pattern in the left corner of the
+image, while leaving the rest of it as it was.
+
+Returns OUT-FILE.
+
+See url `http://www.imagemagick.org/script/convert.php'."
+  (pdf-util-assert-convert-program)
+  (let* ((cmds (pdf-util-convert--create-commands spec))
+         (status (apply 'call-process
+                        pdf-util-convert-program nil
+                        (get-buffer-create "*pdf-util-convert-output*")
+                        nil
+                        `(,in-file ,@cmds ,out-file))))
+    (unless (and (numberp status) (= 0 status))
+      (error "The convert program exited with error status: %s" status))
+    out-file))
+
+(defun pdf-util-convert-asynch (in-file out-file &rest spec-and-callback)
+  "Like `pdf-util-convert', but asynchronous.
+
+If the last argument is a function, it is installed as the
+process sentinel.
+
+Returns the convert process."
+  (pdf-util-assert-convert-program)
+  (let ((callback (car (last spec-and-callback)))
+        spec)
+    (if (functionp callback)
+        (setq spec (butlast spec-and-callback))
+      (setq spec spec-and-callback
+            callback nil))
+    (let* ((cmds (pdf-util-convert--create-commands spec))
+           (proc
+            (apply 'start-process "pdf-util-convert"
+                   (get-buffer-create "*pdf-util-convert-output*")
+                   pdf-util-convert-program
+                   `(,in-file ,@cmds ,out-file))))
+      (when callback
+        (set-process-sentinel proc callback))
+      proc)))
+
+(defun pdf-util-convert-page (&rest specs)
+  "Convert image of current page according to SPECS.
+
+Return the converted PNG image as a string.  See also
+`pdf-util-convert'."
+
+  (pdf-util-assert-pdf-window)
+  (let ((in-file (make-temp-file "pdf-util-convert" nil ".png"))
+        (out-file (make-temp-file "pdf-util-convert" nil ".png")))
+    (unwind-protect
+        (let ((image-data
+               (plist-get (cdr (pdf-view-current-image)) :data)))
+          (with-temp-file in-file
+            (set-buffer-multibyte nil)
+            (set-buffer-file-coding-system 'binary)
+            (insert image-data))
+          (pdf-util-munch-file
+           (apply 'pdf-util-convert
+                  in-file out-file specs)))
+      (when (file-exists-p in-file)
+        (delete-file in-file))
+      (when (file-exists-p out-file)
+        (delete-file out-file)))))
+
+
+(defun pdf-util-convert--create-commands (spec)
+  (let ((fg "red")
+        (bg "red")
+        formats result cmds s)
+    (while (setq s (pop spec))
+      (unless spec
+        (error "Missing value in convert spec:%s" (cons s spec)))
+      (cl-case s
+        (:foreground
+         (setq fg (pop spec)))
+        (:background
+         (setq bg (pop spec)))
+        (:commands
+         (setq cmds (pop spec)))
+        (:formats
+         (setq formats (append formats (pop spec) nil)))
+        (:apply
+         (dolist (m (pop spec))
+           (pdf-util-with-edges (m)
+             (let ((alist (append
+                           (mapcar (lambda (f)
+                                     (cons (car f)
+                                           (if (stringp (cdr f))
+                                               (cdr f)
+                                             (funcall (cdr f) m))))
+                                   formats)
+                           `((?g . ,(format "%dx%d+%d+%d"
+                                            m-width m-height
+                                            m-left m-top))
+                             (?x . ,m-left)
+                             (?X . ,m-right)
+                             (?y . ,m-top)
+                             (?Y . ,m-bot)
+                             (?w . ,(- m-right m-left))
+                             (?h . ,(- m-bot m-top))
+                             (?f . ,fg)
+                             (?b . ,bg)))))
+               (dolist (fmt cmds)
+                 (push (format-spec fmt alist) result))))))))
+    (nreverse result)))
+
+;; FIXME: Check code below and document.
+
+(defun pdf-util-edges-p (obj &optional relative-p)
+  "Return non-nil, if OBJ look like edges.
+
+If RELATIVE-P is non-nil, also check that all values <= 1."
+
+  (and (consp obj)
+       (ignore-errors (= 4 (length obj)))
+       (cl-every (lambda (x)
+                   (and (numberp x)
+                        (>= x 0)
+                        (or (null relative-p)
+                            (<= x 1))))
+                 obj)))
+
+(defun pdf-util-edges-empty-p (edges)
+  "Return non-nil, if EDGES area is empty."
+  (pdf-util-with-edges (edges)
+    (or (<= edges-width 0)
+        (<= edges-height 0))))
+
+(defun pdf-util-edges-inside-p (edges pos &optional epsilon)
+  (pdf-util-edges-contained-p
+   edges
+   (list (car pos) (cdr pos) (car pos) (cdr pos))
+   epsilon))
+
+(defun pdf-util-edges-contained-p (edges contained &optional epsilon)
+  (unless epsilon (setq epsilon 0))
+  (pdf-util-with-edges (edges contained)
+    (and (<= (- edges-left epsilon)
+             contained-left)
+         (>= (+ edges-right epsilon)
+             contained-right)
+         (<= (- edges-top epsilon)
+             contained-top)
+         (>= (+ edges-bot epsilon)
+             contained-bot))))
+
+(defun pdf-util-edges-intersection (e1 e2)
+  (pdf-util-with-edges (edges1 e1 e2)
+    (let ((left (max e1-left e2-left))
+          (top (max e1-top e2-top))
+          (right (min e1-right e2-right))
+          (bot (min e1-bot e2-bot)))
+      (when (and (<= left right)
+                 (<= top bot))
+        (list left top right bot)))))
+
+(defun pdf-util-edges-union (&rest edges)
+  (if (null (cdr edges))
+      (car edges)
+    (list (apply 'min (mapcar 'car edges))
+          (apply 'min (mapcar 'cadr edges))
+          (apply 'max (mapcar 'cl-caddr edges))
+          (apply 'max (mapcar 'cl-cadddr edges)))))
+
+(defun pdf-util-edges-intersection-area (e1 e2)
+  (let ((inters (pdf-util-edges-intersection e1 e2)))
+    (if (null inters)
+        0
+      (pdf-util-with-edges (inters)
+        (* inters-width inters-height)))))
+
+(defun pdf-util-read-image-position (prompt)
+  "Read a image position using prompt.
+
+Return the event position object."
+  (save-selected-window
+    (let ((ev (pdf-util-read-click-event
+               (propertize prompt 'face 'minibuffer-prompt)))
+          (buffer (current-buffer)))
+      (unless (mouse-event-p ev)
+        (error "Not a mouse event"))
+      (let ((posn (event-start ev)))
+        (unless (and (eq (window-buffer
+                          (posn-window posn))
+                         buffer)
+                     (eq 'image (car-safe (posn-object posn))))
+          (error "Invalid image position"))
+        posn))))
+
+(defun pdf-util-read-click-event (&optional prompt seconds)
+  (let ((down (read-event prompt seconds)))
+    (unless (and (mouse-event-p down)
+                 (equal (event-modifiers down)
+                        '(down)))
+      (error "No a mouse click event"))
+    (let ((up (read-event prompt seconds)))
+      (unless (and (mouse-event-p up)
+                   (equal (event-modifiers up)
+                          '(click)))
+        (error "No a mouse click event"))
+      up)))
+
+(defun pdf-util-image-map-mouse-event-proxy (event)
+  "Set POS-OR-AREA in EVENT to 1 and unread it."
+  (interactive "e")
+  (setcar (cdr (cadr event)) 1)
+  (setq unread-command-events (list event)))
+
+(defun pdf-util-image-map-divert-mouse-clicks (id &optional buttons)
+  (dolist (kind '("" "down-" "drag-"))
+    (dolist (b (or buttons '(2 3 4 5 6)))
+      (local-set-key
+       (vector id (intern (format "%smouse-%d" kind b)))
+       'pdf-util-image-map-mouse-event-proxy))))
+
+(defmacro pdf-util-do-events (event-resolution-unread-p condition &rest body)
+  "Read EVENTs while CONDITION executing BODY.
+
+Process at most 1/RESOLUTION events per second.  If UNREAD-p is
+non-nil, unread the final non-processed event.
+
+\(FN (EVENT RESOLUTION &optional UNREAD-p) CONDITION &rest BODY\)"
+  (declare (indent 2) (debug ((symbolp form &optional form) form body)))
+  (cl-destructuring-bind (event resolution &optional unread-p)
+      event-resolution-unread-p
+    (let ((*seconds (make-symbol "seconds"))
+          (*timestamp (make-symbol "timestamp"))
+          (*clock (make-symbol "clock"))
+          (*unread-p (make-symbol "unread-p"))
+          (*resolution (make-symbol "resolution")))
+      `(let* ((,*unread-p ,unread-p)
+              (,*resolution ,resolution)
+              (,*seconds 0)
+              (,*timestamp (float-time))
+              (,*clock (lambda (&optional secs)
+                         (when secs
+                           (setq ,*seconds secs
+                                 ,*timestamp (float-time)))
+                         (- (+ ,*timestamp ,*seconds)
+                            (float-time))))
+              (,event (read-event)))
+         (while ,condition
+           (when (<= (funcall ,*clock) 0)
+             (progn ,@body)
+             (setq ,event nil)
+             (funcall ,*clock ,*resolution))
+           (setq ,event
+                 (or (read-event nil nil
+                                 (and ,event
+                                      (max 0 (funcall ,*clock))))
+                     ,event)))
+         (when (and ,*unread-p ,event)
+           (setq unread-command-events
+                 (append unread-command-events
+                         (list ,event))))))))
+
+(defmacro pdf-util-track-mouse-dragging (event-resolution &rest body)
+  "Read mouse movement events executing BODY.
+
+See also `pdf-util-do-events'.
+
+This macro should be used inside a command bound to a down-mouse
+event.  It evaluates to t, if at least one event was processed in
+BODY, otherwise nil.  In the latter case, the only event (usually
+a mouse click event) is unread.
+
+\(FN (EVENT RESOLUTION) &rest BODY\)"
+  (declare (indent 1) (debug ((symbolp form) body)))
+  (let ((ran-once-p (make-symbol "ran-once-p")))
+    `(let (,ran-once-p)
+       (track-mouse
+         (pdf-util-do-events (,@event-resolution t)
+             (mouse-movement-p ,(car event-resolution))
+           (setq ,ran-once-p t)
+           ,@body))
+       (when (and ,ran-once-p
+                  unread-command-events)
+         (setq unread-command-events
+               (butlast unread-command-events)))
+       ,ran-once-p)))
+
+(defun pdf-util-remove-duplicates (list)
+  "Remove duplicates from LIST stably using `equal'."
+  (let ((ht (make-hash-table :test 'equal))
+        result)
+    (dolist (elt list (nreverse result))
+      (unless (gethash elt ht)
+        (push elt result)
+        (puthash elt t ht)))))
+
+(provide 'pdf-util)
+
+;;; pdf-util.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-view.el b/elpa/pdf-tools-20191031.642/pdf-view.el
new file mode 100644 (file)
index 0000000..7de1888
--- /dev/null
@@ -0,0 +1,1676 @@
+;;; pdf-view.el --- View PDF documents. -*- lexical-binding:t -*-
+
+;; Copyright (C) 2013  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMfh-trier.de>
+;; Keywords: files, doc-view, pdf
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;  Functions related to viewing PDF documents.
+
+;;; Code:
+
+(require 'image-mode)
+(require 'pdf-util)
+(require 'pdf-info)
+(require 'pdf-cache)
+(require 'jka-compr)
+(require 'bookmark)
+(require 'password-cache)
+
+(declare-function cua-copy-region "cua-base")
+
+\f
+;; * ================================================================== *
+;; * Customizations
+;; * ================================================================== *
+
+(defgroup pdf-view nil
+  "View PDF documents."
+  :group 'pdf-tools)
+
+(defcustom pdf-view-display-size 'fit-width
+  "The desired size of displayed pages.
+
+This may be one of `fit-height', `fit-width', `fit-page' or a
+number as a scale factor applied to the document's size.  Any
+other value behaves like `fit-width'."
+  :group 'pdf-view
+  :type '(choice number
+                 (const fit-height)
+                 (const fit-width)
+                 (const fit-page)))
+
+(make-variable-buffer-local 'pdf-view-display-size)
+
+(defcustom pdf-view-resize-factor 1.25
+  "Fractional amount of resizing of one resize command."
+  :group 'pdf-view
+  :type 'number)
+
+(defcustom pdf-view-continuous t
+  "In Continuous mode reaching the page edge advances to next/previous page.
+
+When non-nil, scrolling a line upward at the bottom edge of the page
+moves to the next page, and scrolling a line downward at the top edge
+of the page moves to the previous page."
+  :type 'boolean
+  :group 'pdf-view)
+
+(defcustom pdf-view-bounding-box-margin 0.05
+  "Fractional margin used for slicing with the bounding-box."
+  :group 'pdf-view
+  :type 'number)
+
+(defcustom pdf-view-use-imagemagick nil
+  "Whether imagemagick should be used for rendering.
+
+This variable has no effect, if imagemagick was not compiled into
+Emacs or if imagemagick is the only way to display PNG images.
+FIXME: Explain dis-/advantages of imagemagick and png."
+  :group 'pdf-view
+  :type 'boolean)
+
+(defcustom pdf-view-use-scaling nil
+  "Whether images should be allowed to be scaled for rendering.
+
+This variable affects both the reuse of higher-resolution images
+as lower-resolution ones by down-scaling the image.  As well as
+the rendering of higher-resolution for high-resolution displays,
+if available.
+
+It has no effect, unless either the imagemagick or image-io
+image-format is available."
+  :group 'pdf-view
+  :type 'boolean)
+
+(defface pdf-view-region
+  '((((background dark)) (:inherit region))
+    (((background light)) (:inherit region)))
+  "Face used to determine the colors of the region."
+  :group 'pdf-view
+  :group 'pdf-tools-faces)
+
+(defface pdf-view-rectangle
+  '((((background dark)) (:inherit highlight))
+    (((background light)) (:inherit highlight)))
+  "Face used to determine the colors of the highlighted rectangle."
+  :group 'pdf-view
+  :group 'pdf-tools-faces)
+
+(defcustom pdf-view-midnight-colors '("#839496" . "#002b36" )
+  "Colors used when `pdf-view-midnight-minor-mode' is activated.
+
+This should be a cons \(FOREGROUND . BACKGROUND\) of colors."
+  :group 'pdf-view
+  :type '(cons (color :tag "Foreground")
+               (color :tag "Background")))
+
+(defcustom pdf-view-change-page-hook nil
+  "Hook run after changing to another page, but before displaying it.
+
+See also `pdf-view-before-change-page-hook' and
+`pdf-view-after-change-page-hook'."
+  :group 'pdf-view
+  :type 'hook)
+
+(defcustom pdf-view-before-change-page-hook nil
+  "Hook run before changing to another page.
+
+See also `pdf-view-change-page-hook' and
+`pdf-view-after-change-page-hook'."
+  :group 'pdf-view
+  :type 'hook)
+
+(defcustom pdf-view-after-change-page-hook nil
+  "Hook run after changing to and displaying another page.
+
+See also `pdf-view-change-page-hook' and
+`pdf-view-before-change-page-hook'."
+  :group 'pdf-view
+  :type 'hook)
+
+(defcustom pdf-view-use-dedicated-register t
+  "Whether to use dedicated register for PDF positions.
+
+If this is non-nil, the commands `pdf-view-position-to-register'
+and `pdf-view-jump-to-register' use the buffer-local variable
+`pdf-view-register-alist' to store resp. retrieve marked
+positions.  Otherwise the common variable `register-alist' is
+used."
+  :group 'pdf-view
+  :type 'boolean)
+
+(defcustom pdf-view-image-relief 0
+  "Add a shadow rectangle around the page's image.
+
+See :relief property in Info node `(elisp) Image Descriptors'."
+  :group 'pdf-view
+  :type '(integer :tag "Pixel")
+  :link '(info-link "(elisp) Image Descriptors"))
+
+(defcustom pdf-view-max-image-width 4800
+  "Maximum width of any image displayed in pixel."
+  :group 'pdf-view
+  :type '(integer :tag "Pixel"))
+
+(defcustom pdf-view-use-unicode-ligther t
+  "Whether to use unicode symbols in the mode-line
+
+On some systems finding a font which supports those symbols can
+take some time.  If you don't want to spend that time waiting and
+don't care for a nicer looking mode-line, set this variable to
+nil.
+
+Note, that this option has only an effect when this library is
+loaded."
+  :group 'pdf-view
+  :type 'boolean)
+
+(defcustom pdf-view-incompatible-modes
+  '(linum-mode linum-relative-mode helm-linum-relative-mode
+              nlinum-mode nlinum-hl-mode nlinum-relative-mode yalinum-mode)
+  "A list of modes incompatible with `pdf-view-mode'.
+
+Issue a warning, if one of them is active in a PDF buffer."
+  :group 'pdf-view
+  :type '(repeat symbol))
+
+\f
+;; * ================================================================== *
+;; * Internal variables and macros
+;; * ================================================================== *
+
+(defvar-local pdf-view-active-region nil
+  "The active region as a list of edges.
+
+Edge values are relative coordinates.")
+
+(defvar-local pdf-view--have-rectangle-region nil
+  "Non-nil if the region is currently rendered as a rectangle.
+
+This variable is set in `pdf-view-mouse-set-region' and used in
+`pdf-view-mouse-extend-region' to determine the right choice
+regarding display of the region in the later function.")
+
+(defvar-local pdf-view--buffer-file-name nil
+  "Local copy of remote file or nil.")
+
+(defvar-local pdf-view--server-file-name nil
+  "The servers notion of this buffer's filename.")
+
+(defvar-local pdf-view--next-page-timer nil
+  "Timer used in `pdf-view-next-page-command'.")
+
+(defvar-local pdf-view--hotspot-functions nil
+  "Alist of hotspot functions.")
+
+(defvar-local pdf-view-register-alist nil
+  "Local, dedicated register for PDF positions.")
+
+(defmacro pdf-view-current-page (&optional window)
+  ;;TODO: write documentation!
+  `(image-mode-window-get 'page ,window))
+
+(defmacro pdf-view-current-overlay (&optional window)
+  ;;TODO: write documentation!
+  `(image-mode-window-get 'overlay ,window))
+
+(defmacro pdf-view-current-image (&optional window)
+  ;;TODO: write documentation!
+  `(image-mode-window-get 'image ,window))
+
+(defmacro pdf-view-current-slice (&optional window)
+  ;;TODO: write documentation!
+  `(image-mode-window-get 'slice ,window))
+
+(defmacro pdf-view-current-window-size (&optional window)
+  ;;TODO: write documentation!
+  `(image-mode-window-get 'window-size ,window))
+
+(defmacro pdf-view-window-needs-redisplay (&optional window)
+  `(image-mode-window-get 'needs-redisplay ,window))
+
+(defun pdf-view-current-pagelabel (&optional window)
+  (nth (1- (pdf-view-current-page window)) (pdf-info-pagelabels)))
+
+(defun pdf-view-active-region-p nil
+  "Return t if there are active regions."
+  (not (null pdf-view-active-region)))
+
+(defmacro pdf-view-assert-active-region ()
+  "Signal an error if there are no active regions."
+  `(unless (pdf-view-active-region-p)
+     (error "The region is not active")))
+
+(defconst pdf-view-have-image-mode-pixel-vscroll
+  (>= emacs-major-version 27)
+  "Whether image-mode scrolls vertically by pixels.")
+
+\f
+;; * ================================================================== *
+;; * Major Mode
+;; * ================================================================== *
+
+(defvar pdf-view-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map image-mode-map)
+    (define-key map (kbd "Q")         'kill-this-buffer)
+    ;; Navigation in the document
+    (define-key map (kbd "n")         'pdf-view-next-page-command)
+    (define-key map (kbd "p")         'pdf-view-previous-page-command)
+    (define-key map (kbd "<next>")    'forward-page)
+    (define-key map (kbd "<prior>")   'backward-page)
+    (define-key map [remap forward-page]  'pdf-view-next-page-command)
+    (define-key map [remap backward-page] 'pdf-view-previous-page-command)
+    (define-key map (kbd "SPC")       'pdf-view-scroll-up-or-next-page)
+    (define-key map (kbd "S-SPC")     'pdf-view-scroll-down-or-previous-page)
+    (define-key map (kbd "DEL")       'pdf-view-scroll-down-or-previous-page)
+    (define-key map (kbd "C-n")       'pdf-view-next-line-or-next-page)
+    (define-key map (kbd "<down>")    'pdf-view-next-line-or-next-page)
+    (define-key map (kbd "C-p")       'pdf-view-previous-line-or-previous-page)
+    (define-key map (kbd "<up>")      'pdf-view-previous-line-or-previous-page)
+    (define-key map (kbd "M-<")       'pdf-view-first-page)
+    (define-key map (kbd "M->")       'pdf-view-last-page)
+    (define-key map [remap goto-line] 'pdf-view-goto-page)
+    (define-key map (kbd "M-g l")     'pdf-view-goto-label)
+    (define-key map (kbd "RET")       'image-next-line)
+    ;; Zoom in/out.
+    (define-key map "+"               'pdf-view-enlarge)
+    (define-key map "="               'pdf-view-enlarge)
+    (define-key map "-"               'pdf-view-shrink)
+    (define-key map "0"               'pdf-view-scale-reset)
+    ;; Fit the image to the window
+    (define-key map "W"               'pdf-view-fit-width-to-window)
+    (define-key map "H"               'pdf-view-fit-height-to-window)
+    (define-key map "P"               'pdf-view-fit-page-to-window)
+    ;; Slicing the image
+    (define-key map (kbd "s m")       'pdf-view-set-slice-using-mouse)
+    (define-key map (kbd "s b")       'pdf-view-set-slice-from-bounding-box)
+    (define-key map (kbd "s r")       'pdf-view-reset-slice)
+    ;; Reconvert
+    (define-key map (kbd "C-c C-c")   'doc-view-mode)
+    (define-key map (kbd "g")         'revert-buffer)
+    (define-key map (kbd "r")         'revert-buffer)
+    ;; Region
+    (define-key map [down-mouse-1] 'pdf-view-mouse-set-region)
+    (define-key map [M-down-mouse-1] 'pdf-view-mouse-set-region-rectangle)
+    (define-key map [C-down-mouse-1] 'pdf-view-mouse-extend-region)
+    (define-key map [remap kill-region] 'pdf-view-kill-ring-save)
+    (define-key map [remap kill-ring-save] 'pdf-view-kill-ring-save)
+    (define-key map [remap mark-whole-buffer] 'pdf-view-mark-whole-page)
+    ;; Other
+    (define-key map (kbd "C-c C-d") 'pdf-view-dark-minor-mode)
+    (define-key map (kbd "m") 'pdf-view-position-to-register)
+    (define-key map (kbd "'") 'pdf-view-jump-to-register)
+
+    (define-key map (kbd "C-c C-i") 'pdf-view-extract-region-image)
+    ;; Rendering
+    (define-key map (kbd "C-c C-r m") 'pdf-view-midnight-minor-mode)
+    (define-key map (kbd "C-c C-r p") 'pdf-view-printer-minor-mode)
+    map)
+  "Keymap used by `pdf-view-mode' when displaying a doc as a set of images.")
+
+(define-derived-mode pdf-view-mode special-mode "PDFView"
+  "Major mode in PDF buffers.
+
+PDFView Mode is an Emacs PDF viewer.  It displays PDF files as
+PNG images in Emacs buffers."
+  :group 'pdf-view
+  :abbrev-table nil
+  :syntax-table nil
+  ;; Setup a local copy for remote files.
+  (when (and (or jka-compr-really-do-compress
+                 (let ((file-name-handler-alist nil))
+                   (not (and buffer-file-name
+                             (file-readable-p buffer-file-name)))))
+             (pdf-tools-pdf-buffer-p))
+    (let ((tempfile (pdf-util-make-temp-file
+                     (concat (if buffer-file-name
+                                 (file-name-nondirectory
+                                  buffer-file-name)
+                               (buffer-name))
+                             "-"))))
+      (write-region nil nil tempfile nil 'no-message)
+      (setq-local pdf-view--buffer-file-name tempfile)))
+  ;; Decryption needs to be done before any other function calls into
+  ;; pdf-info.el (e.g. from the mode-line during redisplay during
+  ;; waiting for process output).
+  (pdf-view-decrypt-document)
+
+  ;; Setup scroll functions
+  (if (boundp 'mwheel-scroll-up-function) ; not --without-x build
+      (setq-local mwheel-scroll-up-function
+                  #'pdf-view-scroll-up-or-next-page))
+  (if (boundp 'mwheel-scroll-down-function)
+      (setq-local mwheel-scroll-down-function
+                  #'pdf-view-scroll-down-or-previous-page))
+
+  ;; Clearing overlays
+  (add-hook 'change-major-mode-hook
+            (lambda ()
+              (remove-overlays (point-min) (point-max) 'pdf-view t))
+            nil t)
+  (remove-overlays (point-min) (point-max) 'pdf-view t) ;Just in case.
+
+  ;; Setup other local variables.
+  (setq-local mode-line-position
+              '(" P" (:eval (number-to-string (pdf-view-current-page)))
+                ;; Avoid errors during redisplay.
+                "/" (:eval (or (ignore-errors
+                                 (number-to-string (pdf-cache-number-of-pages)))
+                               "???"))))
+  (setq-local auto-hscroll-mode nil)
+  (setq-local pdf-view--server-file-name (pdf-view-buffer-file-name))
+  ;; High values of scroll-conservatively seem to trigger
+  ;; some display bug in xdisp.c:try_scrolling .
+  (setq-local scroll-conservatively 0)
+  (setq-local cursor-type nil)
+  (setq-local buffer-read-only t)
+  (setq-local view-read-only nil)
+  (setq-local bookmark-make-record-function
+              'pdf-view-bookmark-make-record)
+  (setq-local revert-buffer-function #'pdf-view-revert-buffer)
+  ;; No auto-save at the moment.
+  (setq-local buffer-auto-save-file-name nil)
+  ;; Disable image rescaling.
+  (when (boundp 'image-scaling-factor)
+    (setq-local image-scaling-factor 1))
+  ;; No undo at the moment.
+  (unless buffer-undo-list
+    (buffer-disable-undo))
+  ;; Enable transient-mark-mode, so region deactivation when quitting
+  ;; will work.
+  (setq-local transient-mark-mode t)
+  ;; In Emacs >= 24.4, `cua-copy-region' should have been advised when
+  ;; loading pdf-view.el so as to make it work with
+  ;; pdf-view-mode. Disable cua-mode if that is not the case.
+  ;; FIXME: cua-mode is a global minor-mode, but setting cua-mode to
+  ;; nil seems to do the trick.
+  (when (and (bound-and-true-p cua-mode)
+             (version< emacs-version "24.4"))
+    (setq-local cua-mode nil))
+
+  (add-hook 'window-configuration-change-hook
+            'pdf-view-redisplay-some-windows nil t)
+  (add-hook 'deactivate-mark-hook 'pdf-view-deactivate-region nil t)
+  (add-hook 'write-contents-functions
+            'pdf-view--write-contents-function nil t)
+  (add-hook 'kill-buffer-hook 'pdf-view-close-document nil t)
+  (pdf-view-add-hotspot-function
+   'pdf-view-text-regions-hotspots-function -9)
+
+  ;; Keep track of display info
+  (add-hook 'image-mode-new-window-functions
+            'pdf-view-new-window-function nil t)
+  (image-mode-setup-winprops)
+
+  ;; Issue a warning in the future about incompatible modes.
+  (run-with-timer 1 nil (lambda (buffer)
+                          (when (buffer-live-p buffer)
+                            (pdf-view-check-incompatible-modes buffer)))
+                 (current-buffer)))
+
+(unless (version< emacs-version "24.4")
+  (defun cua-copy-region--pdf-view-advice (&rest _)
+    "If the current buffer is in `pdf-view' mode, call
+`pdf-view-kill-ring-save'."
+    (when (eq major-mode 'pdf-view-mode)
+      (pdf-view-kill-ring-save)
+      t))
+
+  (advice-add 'cua-copy-region
+             :before-until
+             #'cua-copy-region--pdf-view-advice))
+
+(defun pdf-view-check-incompatible-modes (&optional buffer)
+  "Check BUFFER for incompatible modes, maybe issue a warning."
+  (with-current-buffer (or buffer (current-buffer))
+    (let ((modes
+          (cl-remove-if-not
+           (lambda (mode) (and (symbolp mode)
+                               (boundp mode)
+                               (symbol-value mode)))
+           pdf-view-incompatible-modes)))
+      (when modes
+       (display-warning
+        'pdf-view
+        (format "These modes are incompatible with `pdf-view-mode',
+       please deactivate them (or customize pdf-view-incompatible-modes): %s"
+                (mapconcat #'symbol-name modes ",")))))))
+
+(defun pdf-view-decrypt-document ()
+  "Read a password, if the document is encrypted and open it."
+  (interactive)
+  (when (pdf-info-encrypted-p)
+    (let ((prompt (format "Enter password for `%s': "
+                          (abbreviate-file-name
+                           (buffer-file-name))))
+          (key (concat "/pdf-tools" (buffer-file-name)))
+          (i 3)
+          password)
+      (while (and (> i 0)
+                  (pdf-info-encrypted-p))
+        (setq i (1- i))
+        (setq password (password-read prompt key))
+        (setq prompt "Invalid password, try again: ")
+        (ignore-errors (pdf-info-open nil password)))
+      (pdf-info-open nil password)
+      (password-cache-add key password)))
+  nil)
+
+(defun pdf-view-buffer-file-name ()
+  "Return the local filename of the PDF in the current buffer.
+
+This may be different from variable `buffer-file-name' when
+operating on a local copy of a remote file."
+  (or pdf-view--buffer-file-name
+      (buffer-file-name)))
+
+(defun pdf-view--write-contents-function ()
+  "Function for `write-contents-functions' to save the buffer."
+  (when (pdf-util-pdf-buffer-p)
+    (let ((tempfile (pdf-info-save pdf-view--server-file-name)))
+      (unwind-protect
+          (progn
+            ;; Order matters here: We need to first copy the new
+            ;; content (tempfile) to the PDF, and then close the PDF.
+            ;; Since while closing the file (and freeing its resources
+            ;; in the process), it may be immediately reopened due to
+            ;; redisplay happening inside the pdf-info-close function
+            ;; (while waiting for a response from the process.).
+            (copy-file tempfile (buffer-file-name) t)
+            (pdf-info-close pdf-view--server-file-name)
+
+            (when pdf-view--buffer-file-name
+              (copy-file tempfile pdf-view--buffer-file-name t))
+            (clear-visited-file-modtime)
+            (set-buffer-modified-p nil)
+            (setq pdf-view--server-file-name
+                  (pdf-view-buffer-file-name))
+            t)
+        (when (file-exists-p tempfile)
+          (delete-file tempfile))))))
+
+(defun pdf-view-revert-buffer (&optional ignore-auto noconfirm)
+  "Revert buffer while preseving current modes.
+
+Optional parameters IGNORE-AUTO and NOCONFIRM are defined as in
+`revert-buffer'."
+  (interactive (list (not current-prefix-arg)))
+  ;; Bind to default so that we can use pdf-view-revert-buffer as
+  ;; revert-buffer-function.  A binding of nil is needed in Emacs 24.3, but in
+  ;; later versions the semantics that nil means the default function should
+  ;; not relied upon.
+  (let ((revert-buffer-function (when (fboundp #'revert-buffer--default)
+                                  #'revert-buffer--default))
+        (after-revert-hook
+         (cons #'pdf-info-close
+               after-revert-hook)))
+    (prog1
+        (revert-buffer ignore-auto noconfirm 'preserve-modes)
+      (pdf-view-redisplay t))))
+
+(defun pdf-view-close-document ()
+  "Return immediately after closing document.
+
+This function always suceeds.  See also `pdf-info-close', which
+does not return immediately."
+  (when (pdf-info-running-p)
+    (let ((pdf-info-asynchronous 'ignore))
+      (ignore-errors
+        (pdf-info-close)))))
+
+\f
+;; * ================================================================== *
+;; * Scaling
+;; * ================================================================== *
+
+(defun pdf-view-fit-page-to-window ()
+  "Fit PDF to window.
+
+Choose the larger of PDF's height and width, and fits that
+dimension to window."
+  (interactive)
+  (setq pdf-view-display-size 'fit-page)
+  (image-set-window-vscroll 0)
+  (image-set-window-hscroll 0)
+  (pdf-view-redisplay t))
+
+(defun pdf-view-fit-height-to-window ()
+  "Fit PDF height to window."
+  (interactive)
+  (setq pdf-view-display-size 'fit-height)
+  (image-set-window-vscroll 0)
+  (pdf-view-redisplay t))
+
+(defun pdf-view-fit-width-to-window ()
+  "Fit PDF size to window."
+  (interactive)
+  (setq pdf-view-display-size 'fit-width)
+  (image-set-window-hscroll 0)
+  (pdf-view-redisplay t))
+
+(defun pdf-view-enlarge (factor)
+  "Enlarge PDF by FACTOR.
+
+When called interactively, uses the value of
+`pdf-view-resize-factor'.
+
+For example, (pdf-view-enlarge 1.25) increases size by 25%."
+  (interactive
+   (list (float pdf-view-resize-factor)))
+  (let* ((size (pdf-view-image-size))
+         (pagesize (pdf-cache-pagesize
+                    (pdf-view-current-page)))
+         (scale (/ (float (car size))
+                   (float (car pagesize)))))
+    (setq pdf-view-display-size
+          (* factor scale))
+    (pdf-view-redisplay t)))
+
+(defun pdf-view-shrink (factor)
+  "Shrink PDF by FACTOR.
+
+When called interactively, uses the value of
+`pdf-view-resize-factor'.
+
+For example, (pdf-view-shrink 1.25) decreases size by 20%."
+  (interactive
+   (list (float pdf-view-resize-factor)))
+  (pdf-view-enlarge (/ 1.0 factor)))
+
+(defun pdf-view-scale-reset ()
+  "Reset PDF to its default set."
+  (interactive)
+  (setq pdf-view-display-size 1.0)
+  (pdf-view-redisplay t))
+
+
+\f
+;; * ================================================================== *
+;; * Moving by pages and scrolling
+;; * ================================================================== *
+
+(defun pdf-view-goto-page (page &optional window)
+  "Go to PAGE in PDF.
+
+If optional parameter WINDOW, go to PAGE in all `pdf-view'
+windows."
+  (interactive
+   (list (if current-prefix-arg
+             (prefix-numeric-value current-prefix-arg)
+           (read-number "Page: "))))
+  (unless (and (>= page 1)
+               (<= page (pdf-cache-number-of-pages)))
+    (error "No such page: %d" page))
+  (unless window
+    (setq window
+          (if (pdf-util-pdf-window-p)
+              (selected-window)
+            t)))
+  (save-selected-window
+    ;; Select the window for the hooks below.
+    (when (window-live-p window)
+      (select-window window))
+    (let ((changing-p
+           (not (eq page (pdf-view-current-page window)))))
+      (when changing-p
+        (run-hooks 'pdf-view-before-change-page-hook)
+        (setf (pdf-view-current-page window) page)
+        (run-hooks 'pdf-view-change-page-hook))
+      (when (window-live-p window)
+        (pdf-view-redisplay window))
+      (when changing-p
+        (pdf-view-deactivate-region)
+        (force-mode-line-update)
+        (run-hooks 'pdf-view-after-change-page-hook))))
+  nil)
+
+(defun pdf-view-next-page (&optional n)
+  "View the next page in the PDF.
+
+Optional parameter N moves N pages forward."
+  (interactive "p")
+  (pdf-view-goto-page (+ (pdf-view-current-page)
+                         (or n 1))))
+
+(defun pdf-view-previous-page (&optional n)
+  "View the previous page in the PDF.
+
+Optional parameter N moves N pages backward."
+  (interactive "p")
+  (pdf-view-next-page (- (or n 1))))
+
+(defun pdf-view-next-page-command (&optional n)
+  "View the next page in the PDF.
+
+Optional parameter N moves N pages forward.
+
+This command is a wrapper for `pdf-view-next-page' that will
+indicate to the user if they are on the last page and more."
+  (declare (interactive-only pdf-view-next-page))
+  (interactive "p")
+  (unless n (setq n 1))
+  (when (> (+ (pdf-view-current-page) n)
+           (pdf-cache-number-of-pages))
+    (user-error "Last page"))
+  (when (< (+ (pdf-view-current-page) n) 1)
+    (user-error "First page"))
+  (let ((pdf-view-inhibit-redisplay t))
+    (pdf-view-goto-page
+     (+ (pdf-view-current-page) n)))
+  (force-mode-line-update)
+  (sit-for 0)
+  (when pdf-view--next-page-timer
+    (cancel-timer pdf-view--next-page-timer)
+    (setq pdf-view--next-page-timer nil))
+  (if (or (not (input-pending-p))
+          (and (> n 0)
+               (= (pdf-view-current-page)
+                  (pdf-cache-number-of-pages)))
+          (and (< n 0)
+               (= (pdf-view-current-page) 1)))
+      (pdf-view-redisplay)
+    (setq pdf-view--next-page-timer
+          (run-with-idle-timer 0.001 nil 'pdf-view-redisplay (selected-window)))))
+
+(defun pdf-view-previous-page-command (&optional n)
+  "View the previous page in the PDF.
+
+Optional parameter N moves N pages backward.
+
+This command is a wrapper for `pdf-view-previous-page'."
+  (declare (interactive-only pdf-view-previous-page))
+  (interactive "p")
+  (with-no-warnings
+    (pdf-view-next-page-command (- (or n 1)))))
+
+(defun pdf-view-first-page ()
+  "View the first page."
+  (interactive)
+  (pdf-view-goto-page 1))
+
+(defun pdf-view-last-page ()
+  "View the last page."
+  (interactive)
+  (pdf-view-goto-page (pdf-cache-number-of-pages)))
+
+(defun pdf-view-scroll-up-or-next-page (&optional arg)
+  "Scroll page up ARG lines if possible, else go to the next page.
+
+When `pdf-view-continuous' is non-nil, scrolling upward at the
+bottom edge of the page moves to the next page.  Otherwise, go to
+next page only on typing SPC (ARG is nil)."
+  (interactive "P")
+  (if (or pdf-view-continuous (null arg))
+      (let ((hscroll (window-hscroll))
+            (cur-page (pdf-view-current-page)))
+        (when (or (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll)
+                     (image-scroll-up arg))
+                  ;; Workaround rounding/off-by-one issues.
+                  (memq pdf-view-display-size
+                        '(fit-height fit-page)))
+          (pdf-view-next-page)
+          (when (/= cur-page (pdf-view-current-page))
+            (image-bob)
+            (image-bol 1))
+          (set-window-hscroll (selected-window) hscroll)))
+    (image-scroll-up arg)))
+
+(defun pdf-view-scroll-down-or-previous-page (&optional arg)
+  "Scroll page down ARG lines if possible, else go to the previous page.
+
+When `pdf-view-continuous' is non-nil, scrolling downward at the
+top edge of the page moves to the previous page.  Otherwise, go
+to previous page only on typing DEL (ARG is nil)."
+  (interactive "P")
+  (if (or pdf-view-continuous (null arg))
+      (let ((hscroll (window-hscroll))
+            (cur-page (pdf-view-current-page)))
+        (when (or (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll)
+                     (image-scroll-down arg))
+                  ;; Workaround rounding/off-by-one issues.
+                  (memq pdf-view-display-size
+                        '(fit-height fit-page)))
+          (pdf-view-previous-page)
+          (when (/= cur-page (pdf-view-current-page))
+            (image-eob)
+            (image-bol 1))
+          (set-window-hscroll (selected-window) hscroll)))
+    (image-scroll-down arg)))
+
+(defun pdf-view-next-line-or-next-page (&optional arg)
+  "Scroll upward by ARG lines if possible, else go to the next page.
+
+When `pdf-view-continuous' is non-nil, scrolling a line upward
+at the bottom edge of the page moves to the next page."
+  (interactive "p")
+  (if pdf-view-continuous
+      (let ((hscroll (window-hscroll))
+            (cur-page (pdf-view-current-page)))
+        (when (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll)
+                 (image-next-line arg))
+          (pdf-view-next-page)
+          (when (/= cur-page (pdf-view-current-page))
+            (image-bob)
+            (image-bol 1))
+          (set-window-hscroll (selected-window) hscroll)))
+    (image-next-line 1)))
+
+(defun pdf-view-previous-line-or-previous-page (&optional arg)
+  "Scroll downward by ARG lines if possible, else go to the previous page.
+
+When `pdf-view-continuous' is non-nil, scrolling a line downward
+at the top edge of the page moves to the previous page."
+  (interactive "p")
+  (if pdf-view-continuous
+      (let ((hscroll (window-hscroll))
+            (cur-page (pdf-view-current-page)))
+        (when (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll)
+                 (image-previous-line arg))
+          (pdf-view-previous-page)
+          (when (/= cur-page (pdf-view-current-page))
+            (image-eob)
+            (image-bol 1))
+          (set-window-hscroll (selected-window) hscroll)))
+    (image-previous-line arg)))
+
+(defun pdf-view-goto-label (label)
+  "Go to the page corresponding to LABEL.
+
+Usually, the label of a document's page is the same as its
+displayed page number."
+  (interactive
+   (list (let ((labels (pdf-info-pagelabels)))
+           (completing-read "Goto label: " labels nil t))))
+  (let ((index (cl-position label (pdf-info-pagelabels) :test 'equal)))
+    (unless index
+      (error "No such label: %s" label))
+    (pdf-view-goto-page (1+ index))))
+
+\f
+;; * ================================================================== *
+;; * Slicing
+;; * ================================================================== *
+
+(defun pdf-view-set-slice (x y width height &optional window)
+  ;; TODO: add WINDOW to docstring.
+  "Set the slice of the pages that should be displayed.
+
+X, Y, WIDTH and HEIGHT should be relative coordinates, i.e. in
+\[0;1\].  To reset the slice use `pdf-view-reset-slice'."
+  (unless (equal (pdf-view-current-slice window)
+                 (list x y width height))
+    (setf (pdf-view-current-slice window)
+          (mapcar (lambda (v)
+                    (max 0 (min 1 v)))
+                  (list x y width height)))
+    (pdf-view-redisplay window)))
+
+(defun pdf-view-set-slice-using-mouse ()
+  "Set the slice of the images that should be displayed.
+
+Set the slice by pressing `mouse-1' at its top-left corner and
+dragging it to its bottom-right corner.  See also
+`pdf-view-set-slice' and `pdf-view-reset-slice'."
+  (interactive)
+  (let ((size (pdf-view-image-size))
+        x y w h done)
+    (while (not done)
+      (let ((e (read-event
+                (concat "Press mouse-1 at the top-left corner and "
+                        "drag it to the bottom-right corner!"))))
+        (when (eq (car e) 'drag-mouse-1)
+          (setq x (car (posn-object-x-y (event-start e))))
+          (setq y (cdr (posn-object-x-y (event-start e))))
+          (setq w (- (car (posn-object-x-y (event-end e))) x))
+          (setq h (- (cdr (posn-object-x-y (event-end e))) y))
+          (setq done t))))
+    (apply 'pdf-view-set-slice
+           (pdf-util-scale
+            (list x y w h)
+            (cons (/ 1.0 (float (car size)))
+                  (/ 1.0 (float (cdr size))))))))
+
+(defun pdf-view-set-slice-from-bounding-box (&optional window)
+  ;; TODO: add WINDOW to docstring.
+  "Set the slice from the page's bounding-box.
+
+The result is that the margins are almost completely cropped,
+much more accurate than could be done manually using
+`pdf-view-set-slice-using-mouse'.
+
+See also `pdf-view-bounding-box-margin'."
+  (interactive)
+  (let* ((bb (pdf-cache-boundingbox (pdf-view-current-page window)))
+         (margin (max 0 (or pdf-view-bounding-box-margin 0)))
+         (slice (list (- (nth 0 bb)
+                         (/ margin 2.0))
+                      (- (nth 1 bb)
+                         (/ margin 2.0))
+                      (+ (- (nth 2 bb) (nth 0 bb))
+                         margin)
+                      (+ (- (nth 3 bb) (nth 1 bb))
+                         margin))))
+    (apply 'pdf-view-set-slice
+           (append slice (and window (list window))))))
+
+(defun pdf-view-reset-slice (&optional window)
+  ;; TODO: add WINDOW to doctring.
+  "Reset the current slice.
+
+After calling this function the whole page will be visible
+again."
+  (interactive)
+  (when (pdf-view-current-slice window)
+    (setf (pdf-view-current-slice window) nil)
+    (pdf-view-redisplay window))
+  nil)
+
+(define-minor-mode pdf-view-auto-slice-minor-mode
+  "Automatically slice pages according to their bounding boxes.
+
+See also `pdf-view-set-slice-from-bounding-box'."
+  nil nil nil
+  (pdf-util-assert-pdf-buffer)
+  (cond
+   (pdf-view-auto-slice-minor-mode
+    (dolist (win (get-buffer-window-list nil nil t))
+      (when (pdf-util-pdf-window-p win)
+        (pdf-view-set-slice-from-bounding-box win)))
+    (add-hook 'pdf-view-change-page-hook
+              'pdf-view-set-slice-from-bounding-box nil t))
+   (t
+    (remove-hook 'pdf-view-change-page-hook
+                 'pdf-view-set-slice-from-bounding-box t))))
+
+\f
+;; * ================================================================== *
+;; * Display
+;; * ================================================================== *
+
+(defvar pdf-view-inhibit-redisplay nil)
+(defvar pdf-view-inhibit-hotspots nil)
+
+(defun pdf-view-image-type ()
+  "Return the image type that should be used.
+
+The return value is either `imagemagick' (if available and wanted
+or if png is not available) or `png'.
+
+Signal an error, if neither `imagemagick' nor `png' is available.
+
+See also `pdf-view-use-imagemagick'."
+  (cond ((and pdf-view-use-imagemagick
+              (fboundp 'imagemagick-types))
+         'imagemagick)
+        ((image-type-available-p 'image-io)
+         'image-io)
+        ((image-type-available-p 'png)
+         'png)
+        ((fboundp 'imagemagick-types)
+         'imagemagick)
+        (t
+         (error "PNG image supported not compiled into Emacs"))))
+
+(defun pdf-view-use-scaling-p ()
+  "Return t if scaling should be used."
+  (and (memq (pdf-view-image-type)
+             '(imagemagick image-io))
+       pdf-view-use-scaling))
+
+(defmacro pdf-view-create-image (data &rest props)
+  ;; TODO: add DATA and PROPS to docstring.
+  "Like `create-image', but with set DATA-P and TYPE arguments."
+  (declare (indent 1) (debug t))
+  (let ((image-data (make-symbol "data")))
+    `(let ((,image-data ,data))
+       (apply #'create-image ,image-data (pdf-view-image-type) t ,@props
+              (cl-list*
+               :relief (or pdf-view-image-relief 0)
+               (when (and (eq (framep-on-display) 'mac)
+                          (= (pdf-util-frame-scale-factor) 2))
+                 (list :data-2x ,image-data)))))))
+
+(defun pdf-view-create-page (page &optional window)
+  "Create an image of PAGE for display on WINDOW."
+  (let* ((size (pdf-view-desired-image-size page window))
+         (data (pdf-cache-renderpage
+                page (car size)
+                (if (not (pdf-view-use-scaling-p))
+                    (car size)
+                  (* 2 (car size)))))
+         (hotspots (pdf-view-apply-hotspot-functions
+                    window page size)))
+    (pdf-view-create-image data
+      :width (car size)
+      :map hotspots
+      :pointer 'arrow)))
+
+(defun pdf-view-image-size (&optional displayed-p window)
+  ;; TODO: add WINDOW to docstring.
+  "Return the size in pixel of the current image.
+
+If DISPLAYED-P is non-nil, return the size of the displayed
+image.  These values may be different, if slicing is used."
+  (if displayed-p
+      (with-selected-window (or window (selected-window))
+        (image-display-size
+         (image-get-display-property) t))
+    (image-size (pdf-view-current-image window) t)))
+
+(defun pdf-view-image-offset (&optional window)
+  ;; TODO: add WINDOW to docstring.
+  "Return the offset of the current image.
+
+It is equal to \(LEFT . TOP\) of the current slice in pixel."
+  (let* ((slice (pdf-view-current-slice window)))
+    (cond
+     (slice
+      (pdf-util-scale-relative-to-pixel
+       (cons (nth 0 slice) (nth 1 slice))
+       window))
+     (t
+      (cons 0 0)))))
+
+(defun pdf-view-display-page (page &optional window)
+  "Display page PAGE in WINDOW."
+  (setf (pdf-view-window-needs-redisplay window) nil)
+  (pdf-view-display-image
+   (pdf-view-create-page page window)
+   window))
+
+(defun pdf-view-display-image (image &optional window inhibit-slice-p)
+  ;; TODO: write documentation!
+  (let ((ol (pdf-view-current-overlay window)))
+    (when (window-live-p (overlay-get ol 'window))
+      (let* ((size (image-size image t))
+             (slice (if (not inhibit-slice-p)
+                        (pdf-view-current-slice window)))
+             (displayed-width (floor
+                               (if slice
+                                   (* (nth 2 slice)
+                                      (car (image-size image)))
+                                 (car (image-size image))))))
+        (setf (pdf-view-current-image window) image)
+        (move-overlay ol (point-min) (point-max))
+        ;; In case the window is wider than the image, center the image
+        ;; horizontally.
+        (overlay-put ol 'before-string
+                     (when (> (window-width window)
+                              displayed-width)
+                       (propertize " " 'display
+                                   `(space :align-to
+                                           ,(/ (- (window-width window)
+                                                  displayed-width) 2)))))
+        (overlay-put ol 'display
+                     (if slice
+                         (list (cons 'slice
+                                     (pdf-util-scale slice size 'round))
+                               image)
+                       image))
+        (let* ((win (overlay-get ol 'window))
+               (hscroll (image-mode-window-get 'hscroll win))
+               (vscroll (image-mode-window-get 'vscroll win)))
+          ;; Reset scroll settings, in case they were changed.
+          (if hscroll (set-window-hscroll win hscroll))
+          (if vscroll (set-window-vscroll
+                       win vscroll pdf-view-have-image-mode-pixel-vscroll)))))))
+
+(defun pdf-view-redisplay (&optional window)
+  "Redisplay page in WINDOW.
+
+If WINDOW is t, redisplay pages in all windows."
+  (unless pdf-view-inhibit-redisplay
+    (if (not (eq t window))
+        (pdf-view-display-page
+         (pdf-view-current-page window)
+         window)
+      (dolist (win (get-buffer-window-list nil nil t))
+        (pdf-view-display-page
+         (pdf-view-current-page win)
+         win))
+      (when (consp image-mode-winprops-alist)
+        (dolist (window (mapcar #'car image-mode-winprops-alist))
+          (unless (or (not (window-live-p window))
+                      (eq (current-buffer)
+                          (window-buffer window)))
+            (setf (pdf-view-window-needs-redisplay window) t)))))
+    (force-mode-line-update)))
+
+(defun pdf-view-redisplay-pages (&rest pages)
+  "Redisplay PAGES in all windows."
+  (pdf-util-assert-pdf-buffer)
+  (dolist (window (get-buffer-window-list nil nil t))
+    (when (memq (pdf-view-current-page window)
+                pages)
+      (pdf-view-redisplay window))))
+
+(defun pdf-view-maybe-redisplay-resized-windows ()
+  "Redisplay some windows needing redisplay."
+  (unless (or (numberp pdf-view-display-size)
+              (pdf-view-active-region-p)
+              (> (minibuffer-depth) 0))
+    (dolist (window (get-buffer-window-list nil nil t))
+      (let ((stored (pdf-view-current-window-size window))
+            (size (cons (window-width window)
+                        (window-height window))))
+        (unless (equal size stored)
+          (setf (pdf-view-current-window-size window) size)
+          (unless (or (null stored)
+                      (and (eq pdf-view-display-size 'fit-width)
+                           (eq (car size) (car stored)))
+                      (and (eq pdf-view-display-size 'fit-height)
+                           (eq (cdr size) (cdr stored))))
+            (pdf-view-redisplay window)))))))
+
+(defun pdf-view-redisplay-some-windows ()
+  (pdf-view-maybe-redisplay-resized-windows)
+  (dolist (window (get-buffer-window-list nil nil t))
+    (when (pdf-view-window-needs-redisplay window)
+      (pdf-view-redisplay window))))
+
+(defun pdf-view-new-window-function (winprops)
+  ;; TODO: write documentation!
+  ;; (message "New window %s for buf %s" (car winprops) (current-buffer))
+  (cl-assert (or (eq t (car winprops))
+                 (eq (window-buffer (car winprops)) (current-buffer))))
+  (let ((ol (image-mode-window-get 'overlay winprops)))
+    (if ol
+        (progn
+          (setq ol (copy-overlay ol))
+          ;; `ol' might actually be dead.
+          (move-overlay ol (point-min) (point-max)))
+      (setq ol (make-overlay (point-min) (point-max) nil t))
+      (overlay-put ol 'pdf-view t))
+    (overlay-put ol 'window (car winprops))
+    (unless (windowp (car winprops))
+      ;; It's a pseudo entry.  Let's make sure it's not displayed (the
+      ;; `window' property is only effective if its value is a window).
+      (cl-assert (eq t (car winprops)))
+      (delete-overlay ol))
+    (image-mode-window-put 'overlay ol winprops)
+    ;; Clean up some overlays.
+    (dolist (ov (overlays-in (point-min) (point-max)))
+      (when (and (windowp (overlay-get ov 'window))
+                 (not (window-live-p (overlay-get ov 'window))))
+        (delete-overlay ov)))
+    (when (and (windowp (car winprops))
+               (null (image-mode-window-get 'image winprops)))
+      ;; We're not displaying an image yet, so let's do so.  This
+      ;; happens when the buffer is displayed for the first time.
+      (with-selected-window (car winprops)
+        (pdf-view-goto-page
+         (or (image-mode-window-get 'page t) 1))))))
+
+(defun pdf-view-desired-image-size (&optional page window)
+  ;; TODO: write documentation!
+  (let* ((pagesize (pdf-cache-pagesize
+                    (or page (pdf-view-current-page window))))
+         (slice (pdf-view-current-slice window))
+         (width-scale (/ (/ (float (pdf-util-window-pixel-width window))
+                            (or (nth 2 slice) 1.0))
+                         (float (car pagesize))))
+         (height (- (nth 3 (window-inside-pixel-edges window))
+                    (nth 1 (window-inside-pixel-edges window))
+                    1))
+         (height-scale (/ (/ (float height)
+                             (or (nth 3 slice) 1.0))
+                          (float (cdr pagesize))))
+         (scale width-scale))
+    (if (numberp pdf-view-display-size)
+        (setq scale (float pdf-view-display-size))
+      (cl-case pdf-view-display-size
+        (fit-page
+         (setq scale (min height-scale width-scale)))
+        (fit-height
+         (setq scale height-scale))
+        (t
+         (setq scale width-scale))))
+    (let ((width (floor (* (car pagesize) scale)))
+          (height (floor (* (cdr pagesize) scale))))
+      (when (> width (max 1 (or pdf-view-max-image-width width)))
+        (setq width pdf-view-max-image-width
+              height (* height (/ (float pdf-view-max-image-width) width))))
+      (cons (max 1 width) (max 1 height)))))
+
+(defun pdf-view-text-regions-hotspots-function (page size)
+  "Return a list of hotspots for text regions on PAGE using SIZE.
+
+This will display a text cursor, when hovering over them."
+  (local-set-key [pdf-view-text-region t]
+                 'pdf-util-image-map-mouse-event-proxy)
+  (mapcar (lambda (region)
+            (let ((e (pdf-util-scale region size 'round)))
+              `((rect . ((,(nth 0 e) . ,(nth 1 e))
+                         . (,(nth 2 e) . ,(nth 3 e))))
+                pdf-view-text-region
+                (pointer text))))
+          (pdf-cache-textregions page)))
+
+(define-minor-mode pdf-view-dark-minor-mode
+  "Mode for PDF documents with dark background.
+
+This tells the various modes to use their face's dark colors."
+  nil nil nil
+  (pdf-util-assert-pdf-buffer)
+  ;; FIXME: This should really be run in a hook.
+  (when (bound-and-true-p pdf-isearch-active-mode)
+    (with-no-warnings
+      (pdf-isearch-redisplay)
+      (pdf-isearch-message
+       (if pdf-view-dark-minor-mode "dark mode" "light mode")))))
+
+(define-minor-mode pdf-view-printer-minor-mode
+  "Display the PDF as it would be printed."
+  nil " Prn" nil
+  (pdf-util-assert-pdf-buffer)
+  (let ((enable (lambda ()
+                  (pdf-info-setoptions :render/printed t))))
+    (cond
+     (pdf-view-printer-minor-mode
+      (add-hook 'after-save-hook enable nil t)
+      (add-hook 'after-revert-hook enable nil t))
+     (t
+      (remove-hook 'after-save-hook enable t)
+      (remove-hook 'after-revert-hook enable t))))
+  (pdf-info-setoptions :render/printed pdf-view-printer-minor-mode)
+  (pdf-cache-clear-images)
+  (pdf-view-redisplay t))
+
+(define-minor-mode pdf-view-midnight-minor-mode
+  "Apply a color-filter appropriate for past midnight reading.
+
+The colors are determined by the variable
+`pdf-view-midnight-colors', which see. "
+
+  nil " Mid" nil
+  (pdf-util-assert-pdf-buffer)
+  ;; FIXME: Maybe these options should be passed stateless to pdf-info-renderpage ?
+  (let ((enable (lambda ()
+                  (pdf-info-setoptions
+                   :render/foreground (or (car pdf-view-midnight-colors) "black")
+                   :render/background (or (cdr pdf-view-midnight-colors) "white")
+                   :render/usecolors t))))
+    (cond
+     (pdf-view-midnight-minor-mode
+      (add-hook 'after-save-hook enable nil t)
+      (add-hook 'after-revert-hook enable nil t)
+      (funcall enable))
+     (t
+      (remove-hook 'after-save-hook enable t)
+      (remove-hook 'after-revert-hook enable t)
+      (pdf-info-setoptions :render/usecolors nil))))
+  (pdf-cache-clear-images)
+  (pdf-view-redisplay t))
+
+(when pdf-view-use-unicode-ligther
+  ;; This check uses an implementation detail, which hopefully gets the
+  ;; right answer.
+  (and (fontp (char-displayable-p ?⎙))
+       (setcdr (assq 'pdf-view-printer-minor-mode minor-mode-alist)
+               (list " ⎙" )))
+  (and (fontp (char-displayable-p ?🌙))
+       (setcdr (assq 'pdf-view-midnight-minor-mode minor-mode-alist)
+               (list  " 🌙" ))))
+
+\f
+;; * ================================================================== *
+;; * Hotspot handling
+;; * ================================================================== *
+
+(defun pdf-view-add-hotspot-function (fn &optional layer)
+  "Register FN as a hotspot function in the current buffer, using LAYER.
+
+FN will be called in the PDF buffer with the page-number and the
+image size \(WIDTH . HEIGHT\) as arguments.  It should return a
+list of hotspots applicable to the the :map image-property.
+
+LAYER determines the order: Functions in a higher LAYER will
+supercede hotspots in lower ones."
+  (push (cons (or layer 0) fn)
+        pdf-view--hotspot-functions))
+
+(defun pdf-view-remove-hotspot-function (fn)
+  "Unregister FN as a hotspot function in the current buffer."
+  (setq pdf-view--hotspot-functions
+        (cl-remove fn pdf-view--hotspot-functions
+                   :key 'cdr)))
+
+(defun pdf-view-sorted-hotspot-functions ()
+  ;; TODO: write documentation!
+  (mapcar 'cdr (cl-sort (copy-sequence pdf-view--hotspot-functions)
+                        '> :key 'car)))
+
+(defun pdf-view-apply-hotspot-functions (window page image-size)
+  ;; TODO: write documentation!
+  (unless pdf-view-inhibit-hotspots
+    (save-selected-window
+      (when window (select-window window))
+      (apply 'nconc
+             (mapcar (lambda (fn)
+                       (funcall fn page image-size))
+                     (pdf-view-sorted-hotspot-functions))))))
+
+\f
+;; * ================================================================== *
+;; * Region
+;; * ================================================================== *
+
+(defun pdf-view--push-mark ()
+  ;; TODO: write documentation!
+  (let (mark-ring)
+    (push-mark-command nil))
+  (setq deactivate-mark nil))
+
+(defun pdf-view-active-region (&optional deactivate-p)
+  "Return the active region, a list of edges.
+
+Deactivate the region if DEACTIVATE-P is non-nil."
+  (pdf-view-assert-active-region)
+  (prog1
+      pdf-view-active-region
+    (when deactivate-p
+      (pdf-view-deactivate-region))))
+
+(defun pdf-view-deactivate-region ()
+  "Deactivate the region."
+  (interactive)
+  (when pdf-view-active-region
+    (setq pdf-view-active-region nil)
+    (deactivate-mark)
+    (pdf-view-redisplay t)))
+
+(defun pdf-view-mouse-set-region (event &optional allow-extend-p
+                                        rectangle-p)
+  "Select a region of text using the mouse with mouse event EVENT.
+
+Allow for stacking of regions, if ALLOW-EXTEND-P is non-nil.
+
+Create a rectangular region, if RECTANGLE-P is non-nil.
+
+Stores the region in `pdf-view-active-region'."
+  (interactive "@e")
+  (setq pdf-view--have-rectangle-region rectangle-p)
+  (unless (and (eventp event)
+               (mouse-event-p event))
+    (signal 'wrong-type-argument (list 'mouse-event-p event)))
+  (unless (and allow-extend-p
+               (or (null (get this-command 'pdf-view-region-window))
+                   (equal (get this-command 'pdf-view-region-window)
+                          (selected-window))))
+    (pdf-view-deactivate-region))
+  (put this-command 'pdf-view-region-window
+       (selected-window))
+  (let* ((window (selected-window))
+         (pos (event-start event))
+         (begin-inside-image-p t)
+         (begin (if (posn-image pos)
+                    (posn-object-x-y pos)
+                  (setq begin-inside-image-p nil)
+                  (posn-x-y pos)))
+         (abs-begin (posn-x-y pos))
+         pdf-view-continuous
+         region)
+    (when (pdf-util-track-mouse-dragging (event 0.05)
+            (let* ((pos (event-start event))
+                   (end (posn-object-x-y pos))
+                   (end-inside-image-p
+                    (and (eq window (posn-window pos))
+                         (posn-image pos))))
+              (when (or end-inside-image-p
+                        begin-inside-image-p)
+                (cond
+                 ((and end-inside-image-p
+                       (not begin-inside-image-p))
+                  ;; Started selection ouside the image, setup begin.
+                  (let* ((xy (posn-x-y pos))
+                         (dxy (cons (- (car xy) (car begin))
+                                    (- (cdr xy) (cdr begin))))
+                         (size (pdf-view-image-size t)))
+                    (setq begin (cons (max 0 (min (car size)
+                                                  (- (car end) (car dxy))))
+                                      (max 0 (min (cdr size)
+                                                  (- (cdr end) (cdr dxy)))))
+                          ;; Store absolute position for later.
+                          abs-begin (cons (- (car xy)
+                                             (- (car end)
+                                                (car begin)))
+                                          (- (cdr xy)
+                                             (- (cdr end)
+                                                (cdr begin))))
+                          begin-inside-image-p t)))
+                 ((and begin-inside-image-p
+                       (not end-inside-image-p))
+                  ;; Moved outside the image, setup end.
+                  (let* ((xy (posn-x-y pos))
+                         (dxy (cons (- (car xy) (car abs-begin))
+                                    (- (cdr xy) (cdr abs-begin))))
+                         (size (pdf-view-image-size t)))
+                    (setq end (cons (max 0 (min (car size)
+                                                (+ (car begin) (car dxy))))
+                                    (max 0 (min (cdr size)
+                                                (+ (cdr begin) (cdr dxy)))))))))
+                (let ((iregion (if rectangle-p
+                                   (list (min (car begin) (car end))
+                                         (min (cdr begin) (cdr end))
+                                         (max (car begin) (car end))
+                                         (max (cdr begin) (cdr end)))
+                                 (list (car begin) (cdr begin)
+                                       (car end) (cdr end)))))
+                  (setq region
+                        (pdf-util-scale-pixel-to-relative iregion))
+                  (pdf-view-display-region
+                   (cons region pdf-view-active-region)
+                   rectangle-p)
+                  (pdf-util-scroll-to-edges iregion)))))
+      (setq pdf-view-active-region
+            (append pdf-view-active-region
+                    (list region)))
+      (pdf-view--push-mark))))
+
+(defun pdf-view-mouse-extend-region (event)
+  "Extend the currently active region with mouse event EVENT."
+  (interactive "@e")
+  (pdf-view-mouse-set-region
+   event t pdf-view--have-rectangle-region))
+
+(defun pdf-view-mouse-set-region-rectangle (event)
+  "Like `pdf-view-mouse-set-region' but displays as a rectangle.
+
+EVENT is the mouse event.
+
+This is more useful for commands like
+`pdf-view-extract-region-image'."
+  (interactive "@e")
+  (pdf-view-mouse-set-region event nil t))
+
+(defun pdf-view-display-region (&optional region rectangle-p)
+  ;; TODO: write documentation!
+  (unless region
+    (pdf-view-assert-active-region)
+    (setq region pdf-view-active-region))
+  (let ((colors (pdf-util-face-colors
+                 (if rectangle-p 'pdf-view-rectangle 'pdf-view-region)
+                 (bound-and-true-p pdf-view-dark-minor-mode)))
+        (page (pdf-view-current-page))
+        (width (car (pdf-view-image-size))))
+    (pdf-view-display-image
+     (pdf-view-create-image
+         (if rectangle-p
+             (pdf-info-renderpage-highlight
+              page width nil
+              `(,(car colors) ,(cdr colors) 0.35 ,@region))
+           (pdf-info-renderpage-text-regions
+            page width nil nil
+            `(,(car colors) ,(cdr colors) ,@region)))))))
+
+(defun pdf-view-kill-ring-save ()
+  "Copy the region to the `kill-ring'."
+  (interactive)
+  (pdf-view-assert-active-region)
+  (let* ((txt (pdf-view-active-region-text)))
+    (pdf-view-deactivate-region)
+    (kill-new (mapconcat 'identity txt "\n"))))
+
+(defun pdf-view-mark-whole-page ()
+  "Mark the whole page."
+  (interactive)
+  (pdf-view-deactivate-region)
+  (setq pdf-view-active-region
+        (list (list 0 0 1 1)))
+  (pdf-view--push-mark)
+  (pdf-view-display-region))
+
+(defun pdf-view-active-region-text ()
+  "Return the text of the active region as a list of strings."
+  (pdf-view-assert-active-region)
+  (mapcar
+   (apply-partially 'pdf-info-gettext (pdf-view-current-page))
+   pdf-view-active-region))
+
+(defun pdf-view-extract-region-image (regions &optional page size
+                                              output-buffer no-display-p)
+  ;; TODO: what is "resp."? Avoid contractions.
+  "Create a PNG image of REGIONS.
+
+REGIONS should have the same form as `pdf-view-active-region',
+which see.  PAGE and SIZE are the page resp. base-size of the
+image from which the image-regions will be created; they default
+to `pdf-view-current-page' resp. `pdf-view-image-size'.
+
+Put the image in OUTPUT-BUFFER, defaulting to \"*PDF region
+image*\" and display it, unless NO-DISPLAY-P is non-nil.
+
+In case of multiple regions, the resulting image is constructed
+by joining them horizontally.  For this operation (and this only)
+the `convert' programm is used."
+
+  (interactive
+   (list (if (pdf-view-active-region-p)
+             (pdf-view-active-region t)
+           '((0 0 1 1)))))
+  (unless page
+    (setq page (pdf-view-current-page)))
+  (unless size
+    (setq size (pdf-view-image-size)))
+  (unless output-buffer
+    (setq output-buffer (get-buffer-create "*PDF image*")))
+  (let* ((images (mapcar (lambda (edges)
+                           (let ((file (make-temp-file "pdf-view"))
+                                 (coding-system-for-write 'binary))
+                             (write-region
+                              (pdf-info-renderpage
+                               page (car size)
+                               :crop-to edges)
+                              nil file nil 'no-message)
+                             file))
+                         regions))
+         result)
+    (unwind-protect
+        (progn
+          (if (= (length images) 1)
+              (setq result (car images))
+            (setq result (make-temp-file "pdf-view"))
+            ;; Join the images horizontally with a gap of 10 pixel.
+            (pdf-util-convert
+             "-noop" ;; workaround limitations of this function
+             result
+             :commands `("("
+                         ,@images
+                         "-background" "white"
+                         "-splice" "0x10+0+0"
+                         ")"
+                         "-gravity" "Center"
+                         "-append"
+                         "+gravity"
+                         "-chop" "0x10+0+0")
+             :apply '((0 0 0 0))))
+          (with-current-buffer output-buffer
+            (let ((inhibit-read-only t))
+              (erase-buffer))
+            (set-buffer-multibyte nil)
+            (insert-file-contents-literally result)
+            (image-mode)
+            (unless no-display-p
+              (pop-to-buffer (current-buffer)))))
+      (dolist (f (cons result images))
+        (when (file-exists-p f)
+          (delete-file f))))))
+\f
+;; * ================================================================== *
+;; * Bookmark + Register Integration
+;; * ================================================================== *
+
+(defun pdf-view-bookmark-make-record  (&optional no-page no-slice no-size no-origin)
+  ;; TODO: add NO-PAGE, NO-SLICE, NO-SIZE, NO-ORIGIN to the docstring.
+  "Create a bookmark PDF record.
+
+The optional, boolean args exclude certain attributes."
+  (let ((displayed-p (eq (current-buffer)
+                         (window-buffer))))
+    (cons (buffer-name)
+          (append (bookmark-make-record-default nil t 1)
+                  `(,(unless no-page
+                       (cons 'page (pdf-view-current-page)))
+                    ,(unless no-slice
+                       (cons 'slice (and displayed-p
+                                         (pdf-view-current-slice))))
+                    ,(unless no-size
+                       (cons 'size pdf-view-display-size))
+                    ,(unless no-origin
+                       (cons 'origin
+                             (and displayed-p
+                                  (let ((edges (pdf-util-image-displayed-edges nil t)))
+                                    (pdf-util-scale-pixel-to-relative
+                                     (cons (car edges) (cadr edges)) nil t)))))
+                    (handler . pdf-view-bookmark-jump-handler))))))
+
+;;;###autoload
+(defun pdf-view-bookmark-jump-handler (bmk)
+  "The bookmark handler-function interface for bookmark BMK.
+
+See also `pdf-view-bookmark-make-record'."
+  (let ((page (bookmark-prop-get bmk 'page))
+        (slice (bookmark-prop-get bmk 'slice))
+        (size (bookmark-prop-get bmk 'size))
+        (origin (bookmark-prop-get bmk 'origin))
+        (file (bookmark-prop-get bmk 'filename))
+        (show-fn-sym (make-symbol "pdf-view-bookmark-after-jump-hook")))
+    (fset show-fn-sym
+          (lambda ()
+            (remove-hook 'bookmark-after-jump-hook show-fn-sym)
+            (unless (derived-mode-p 'pdf-view-mode)
+              (pdf-view-mode))
+            (with-selected-window
+                (or (get-buffer-window (current-buffer) 0)
+                    (selected-window))
+              (when size
+                (setq-local pdf-view-display-size size))
+              (when slice
+                (apply 'pdf-view-set-slice slice))
+              (when (numberp page)
+                (pdf-view-goto-page page))
+              (when origin
+                (let ((size (pdf-view-image-size t)))
+                  (image-set-window-hscroll
+                   (round (/ (* (car origin) (car size))
+                             (frame-char-width))))
+                  (image-set-window-vscroll
+                   (round (/ (* (cdr origin) (cdr size))
+                             (if pdf-view-have-image-mode-pixel-vscroll
+                                 1
+                               (frame-char-height))))))))))
+    (add-hook 'bookmark-after-jump-hook show-fn-sym)
+    (set-buffer (or (find-buffer-visiting file)
+                    (find-file-noselect file)))))
+
+(defun pdf-view-bookmark-jump (bmk)
+  "Switch to bookmark BMK.
+
+This function is like `bookmark-jump', but it always uses the
+selected window for display and does not run any hooks.  Also, it
+works only with bookmarks created by
+`pdf-view-bookmark-make-record'."
+
+  (let* ((file (bookmark-prop-get bmk 'filename))
+         (buffer (or (find-buffer-visiting file)
+                     (find-file-noselect file))))
+    (switch-to-buffer buffer)
+    (let (bookmark-after-jump-hook)
+      (pdf-view-bookmark-jump-handler bmk)
+      (run-hooks 'bookmark-after-jump-hook))))
+
+(defun pdf-view-registerv-make ()
+  "Create a PDF register entry of the current position."
+  (registerv-make
+   (pdf-view-bookmark-make-record nil t t)
+   :print-func 'pdf-view-registerv-print-func
+   :jump-func 'pdf-view-bookmark-jump
+   :insert-func (lambda (bmk)
+                  (insert (format "%S" bmk)))))
+
+(defun pdf-view-registerv-print-func (bmk)
+  "Print a textual representation of bookmark BMK.
+
+This function is used as the `:print-func' property with
+`registerv-make'."
+  (let* ((file (bookmark-prop-get bmk 'filename))
+         (buffer (find-buffer-visiting file))
+         (page (bookmark-prop-get bmk 'page))
+         (origin (bookmark-prop-get bmk 'origin)))
+    (princ (format "PDF position: %s, page %d, %d%%"
+                   (if buffer
+                       (buffer-name buffer)
+                     file)
+                   (or page 1)
+                   (if origin
+                       (round (* 100 (cdr origin)))
+                     0)))))
+
+(defmacro pdf-view-with-register-alist (&rest body)
+  "Setup the proper binding for `register-alist' in BODY.
+
+This macro may not work as desired when it is nested.  See also
+`pdf-view-use-dedicated-register'."
+  (declare (debug t) (indent 0))
+  (let ((dedicated-p (make-symbol "dedicated-p")))
+    `(let* ((,dedicated-p pdf-view-use-dedicated-register)
+            (register-alist
+             (if ,dedicated-p
+                 pdf-view-register-alist
+               register-alist)))
+       (unwind-protect
+           (progn ,@body)
+         (when ,dedicated-p
+           (setq pdf-view-register-alist register-alist))))))
+
+(defun pdf-view-position-to-register (register)
+  "Store current PDF position in register REGISTER.
+
+See also `point-to-register'."
+  (interactive
+   (list (pdf-view-with-register-alist
+           (register-read-with-preview "Position to register: "))))
+  (pdf-view-with-register-alist
+    (set-register register (pdf-view-registerv-make))))
+
+(defun pdf-view-jump-to-register (register &optional delete return-register)
+  ;; TODO: add RETURN-REGISTER to the docstring.
+  "Move point to a position stored in a REGISTER.
+
+Optional parameter DELETE is defined as in `jump-to-register'."
+  (interactive
+   (pdf-view-with-register-alist
+     (list
+      (register-read-with-preview "Jump to register: ")
+      current-prefix-arg
+      (and (or pdf-view-use-dedicated-register
+               (local-variable-p 'register-alist))
+           (characterp last-command-event)
+           last-command-event))))
+  (pdf-view-with-register-alist
+    (let ((return-pos (and return-register
+                           (pdf-view-registerv-make))))
+      (jump-to-register register delete)
+      (when return-register
+        (set-register return-register return-pos)))))
+
+(provide 'pdf-view)
+
+;;; pdf-view.el ends here
diff --git a/elpa/pdf-tools-20191031.642/pdf-virtual.el b/elpa/pdf-tools-20191031.642/pdf-virtual.el
new file mode 100644 (file)
index 0000000..5d6458f
--- /dev/null
@@ -0,0 +1,1038 @@
+;;; pdf-virtual.el --- Virtual PDF documents         -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2015  Andreas Politz
+
+;; Author: Andreas Politz <politzaNOSPAMhochschule-trier.de>
+;; Keywords: multimedia, files
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; A virtual PDF is a collection of pages, or parts thereof, of
+;; arbitrary documents in one particular order.  This library acts as
+;; an intermediate between pdf-info.el and all other packages, in
+;; order to transparently make this collection appear as one single
+;; document.
+;;
+;; The trickiest part is to make theses intermediate functions behave
+;; like the pdf-info-* equivalents in both the synchronous and
+;; asynchronous case.
+
+;;; Code:
+(eval-when-compile
+  (unless (or (> emacs-major-version 24)
+              (and (= emacs-major-version 24)
+                   (>= emacs-minor-version 4)))
+    (error "pdf-virtual.el only works with Emacs >= 24.4")))
+
+(require 'let-alist)
+(require 'pdf-info)
+(require 'pdf-util)
+
+\f
+;; * ================================================================== *
+;; * Variables
+;; * ================================================================== *
+
+(defconst pdf-virtual-magic-mode-regexp "^ *;+ *%VPDF\\_>"
+  "A regexp matching the first line in a vpdf file.")
+
+(defvar-local pdf-virtual-document nil
+  "A list representing the virtual document.")
+
+(put 'pdf-virtual-document 'permanent-local t)
+
+(defvar pdf-virtual-adapter-alist nil
+  "Alist of server functions.
+
+Each element looks like \(PDF-VIRTUAL-FN . PDF-INFO-FN\).  This
+list is filled by the macro `pdf-virtual-define-adapter' and used
+to enable/disable the corresponding advices.")
+
+\f
+;; * ================================================================== *
+;; * VPDF datastructure
+;; * ================================================================== *
+
+(defun pdf-virtual-pagespec-normalize (page-spec &optional filename)
+  "Normalize PAGE-SPEC using FILENAME.
+
+PAGE-SPEC should be as described in
+`pdf-virtual-document-create'.  FILENAME is used to determine the
+last page number, if needed.  The `current-buffer', if it is nil.
+
+Returns a list \(\(FIRST . LAST\) . REGION\)\)."
+
+  (let ((page-spec (cond
+                    ((natnump page-spec)
+                     (list (cons page-spec page-spec)))
+                    ((null (car page-spec))
+                     (let ((npages (pdf-info-number-of-pages filename)))
+                       (cons (cons 1 npages)
+                             (cdr page-spec))))
+                    ((natnump (car page-spec))
+                     (cond
+                      ((natnump (cdr page-spec))
+                       (list page-spec))
+                      (t
+                       (cons (cons (car page-spec)
+                                   (car page-spec))
+                             (cdr page-spec)))))
+                    (t page-spec))))
+    (when (equal (cdr page-spec)
+                 '(0 0 1 1))
+      (setq page-spec `((,(caar page-spec) . ,(cdar page-spec)))))
+    page-spec))
+
+(cl-defstruct pdf-virtual-range
+  ;; The PDF's filename.
+  filename
+  ;; First page in this range.
+  first
+  ;; Last page.
+  last
+  ;; The edges selected for these pages.
+  region
+  ;; The page-index corresponding to the first page in this range.
+  index-start)
+
+(cl-defstruct pdf-virtual-document
+  ;; Array of shared pdf-virtual-range structs, one element for each
+  ;; page.
+  page-array
+  ;; An alist mapping filenames to a list of pages.
+  file-map)
+
+(defun pdf-virtual-range-length (page)
+  "Return the number of pages in PAGE."
+  (1+ (- (pdf-virtual-range-last page)
+         (pdf-virtual-range-first page))))
+
+(defun pdf-virtual-document-create (list &optional directory
+                                         file-error-handler)
+  "Create a virtual PDF from LIST using DIRECTORY.
+
+LIST should be a list of elements \(FILENAME . PAGE-SPECS\),
+where FILENAME is a PDF document and PAGE-SPECS is a list of
+PAGE-RANGE and/or \(PAGE-RANGE . EDGES\).  In the later case,
+EDGES should be a list of relative coordinates \(LEFT TOP RIGHT
+BOT\) selecting a region of the page(s) in PAGE-RANGE.  Giving no
+PAGE-SPECs at all is equivalent to all pages of FILENAME.
+
+See `pdf-info-normalize-page-range' for the valid formats of
+PAGE-RANGE.
+"
+
+  (unless (cl-every 'consp list)
+    (error "Every element should be a cons: %s" list))
+  (unless (cl-every 'stringp (mapcar 'car list))
+    (error "The car of every element should be a filename."))
+  (unless (cl-every (lambda (elt)
+                      (cl-every (lambda (page)
+                                  (or (pdf-info-valid-page-spec-p page)
+                                      (and (consp page)
+                                           (pdf-info-valid-page-spec-p (car page))
+                                           (pdf-util-edges-p (cdr page) 'relative))))
+                                elt))
+                    (mapcar 'cdr list))
+    (error
+     "The cdr of every element should be a list of page-specs"))
+  (let* ((doc (pdf-virtual-document--normalize
+               list (or directory default-directory)
+               file-error-handler))
+         (npages 0)
+         document file-map)
+    (while doc
+      (let* ((elt (pop doc))
+             (filename (car elt))
+             (mapelt (assoc filename file-map))
+             (page-specs (cdr elt)))
+        (if mapelt
+            (setcdr mapelt (cons (1+ npages) (cdr mapelt)))
+          (push (list filename (1+ npages)) file-map))
+        (while page-specs
+          (let* ((ps (pop page-specs))
+                 (first (caar ps))
+                 (last (cdar ps))
+                 (region (cdr ps))
+                 (clx (make-pdf-virtual-range
+                       :filename filename
+                       :first first
+                       :last last
+                       :region region
+                       :index-start npages)))
+            (cl-incf npages (1+ (- last first)))
+            (push (make-vector (1+ (- last first)) clx)
+                  document)))))
+    (make-pdf-virtual-document
+     :page-array (apply 'vconcat (nreverse document))
+     :file-map (nreverse
+                (mapcar (lambda (f)
+                          (setcdr f (nreverse (cdr f)))
+                          f)
+                        file-map)))))
+
+(defun pdf-virtual-document--normalize (list &optional directory
+                                            file-error-handler)
+  (unless file-error-handler
+    (setq file-error-handler
+          (lambda (filename err)
+            (signal (car err)
+                    (append (cdr err) (list filename))))))
+  (let ((default-directory
+          (or directory default-directory)))
+    (setq list (cl-remove-if-not
+                (lambda (filename)
+                  (condition-case err
+                      (progn
+                        (unless (file-readable-p filename)
+                          (signal 'file-error
+                                  (list "File not readable: " filename)))
+                        (pdf-info-open filename)
+                        t)
+                    (error
+                     (funcall file-error-handler filename err)
+                     nil)))
+                list
+                :key 'car))
+    (let* ((file-attributes (make-hash-table :test 'equal))
+           (file-equal-p (lambda (f1 f2)
+                           (let ((a1 (gethash f1 file-attributes))
+                                 (a2 (gethash f2 file-attributes)))
+                             (if (and a1 a2)
+                                 (equal a1 a2)
+                               (file-equal-p f1 f2)))))
+           files normalized)
+      ;; Optimize file-equal-p by caching file-attributes, which is slow
+      ;; and would be called quadratic times otherwise.  (We don't want
+      ;; the same file under different names.)
+      (dolist (f (mapcar 'car list))
+        (unless (find-file-name-handler f 'file-equal-p)
+          (puthash f (file-attributes f) file-attributes)))
+      (dolist (elt list)
+        (let ((file (cl-find (car elt) files :test file-equal-p)))
+          (unless file
+            (push (car elt) files)
+            (setq file (car elt)))
+          (let ((pages (mapcar (lambda (p)
+                                 (pdf-virtual-pagespec-normalize p file))
+                               (or (cdr elt) '(nil))))
+                newpages)
+            (while pages
+              (let* ((spec (pop pages))
+                     (first (caar spec))
+                     (last (cdar spec))
+                     (region (cdr spec)))
+                (while (and pages
+                            (eq (1+ last)
+                                (caar (car pages)))
+                            (equal region (cdr (car pages))))
+                  (setq last (cdar (pop pages))))
+                (push `((,first . ,last) . ,region) newpages)))
+            (push (cons file (nreverse newpages))
+                  normalized))))
+      (nreverse normalized))))
+
+(defmacro pdf-virtual-document-defun (name args &optional documentation &rest body)
+  "Define a PDF Document function.
+
+Args are just like for `defun'.  This macro will ensure, that the
+DOCUMENT argument, which should be last, is setup properly in
+case it is nil, i.e. check that the buffer passes
+`pdf-virtual-buffer-assert-p' and use the variable
+`pdf-virtual-document'."
+
+  (declare (doc-string 3) (indent defun)
+           (debug (&define name lambda-list
+                           [&optional stringp]
+                           def-body)))
+  (unless (stringp documentation)
+    (push documentation body)
+    (setq documentation nil))
+  (unless (memq '&optional args)
+    (setq args (append (butlast args)
+                       (list '&optional)
+                       (last args))))
+  (when (memq '&rest args)
+    (error "&rest argument not supported"))
+  (let ((doc-arg (car (last args)))
+        (fn (intern (format "pdf-virtual-document-%s" name))))
+    `(progn
+       (put ',fn 'definition-name ',name)
+       (defun ,fn
+           ,args ,documentation
+           (setq ,doc-arg
+                 (or ,doc-arg
+                     (progn (pdf-virtual-buffer-assert-p)
+                            pdf-virtual-document)))
+           (cl-check-type ,doc-arg pdf-virtual-document)
+           ,@body))))
+
+(pdf-virtual-document-defun filenames (doc)
+  "Return the list of filenames in DOC."
+  (mapcar 'car (pdf-virtual-document-file-map doc)))
+
+(pdf-virtual-document-defun normalize-pages (pages doc)
+  "Normalize PAGES using DOC.
+
+Like `pdf-info-normalize-page-range', except 0 is replaced by
+DOC's last page."
+
+  (setq pages (pdf-info-normalize-page-range pages))
+  (if (eq 0 (cdr pages))
+      `(,(car pages) . ,(pdf-virtual-document-number-of-pages doc))
+    pages))
+
+(pdf-virtual-document-defun page (page doc)
+  "Get PAGE of DOC.
+
+Returns a list \(FILENAME FILE-PAGE REGION\)."
+  (let ((page (car (pdf-virtual-document-pages (cons page page) doc))))
+    (when page
+      (cl-destructuring-bind (filename first-last region)
+          page
+        (list filename (car first-last) region)))))
+
+(pdf-virtual-document-defun pages (pages doc)
+  "Get PAGES of DOC.
+
+PAGES should be a cons \(FIRST . LAST\).  Return a list of
+ranges corresponding to PAGES. Each element has the form
+
+     \(FILENAME \(FILE-FIRT-PAGE . FILE-LAST-PAGE\) REGION\)
+.
+"
+
+  (let ((begin (car pages))
+        (end (cdr pages)))
+    (unless (<= begin end)
+      (error "begin should not exceed end: %s" (cons begin end)))
+    (let ((arr (pdf-virtual-document-page-array doc))
+          result)
+      (when (or (< begin 1)
+                (> end (length arr)))
+        (signal 'args-out-of-range (list 'pages pages)))
+      (while (<= begin end)
+        (let* ((page (aref arr (1- begin)))
+               (filename (pdf-virtual-range-filename page))
+               (offset (- (1- begin)
+                          (pdf-virtual-range-index-start page)))
+               (first (+ (pdf-virtual-range-first page)
+                         offset))
+               (last (min (+ first (- end begin))
+                          (pdf-virtual-range-last page)))
+               (region (pdf-virtual-range-region page)))
+          (push `(,filename (,first . ,last) ,region) result)
+          (cl-incf begin (1+ (- last first)))))
+      (nreverse result))))
+
+(pdf-virtual-document-defun number-of-pages (doc)
+  "Return the number of pages in DOC."
+  (length (pdf-virtual-document-page-array doc)))
+
+(pdf-virtual-document-defun page-of (filename &optional file-page limit doc)
+  "Return a page number displaying FILENAME's page FILE-PAGE in DOC.
+
+If FILE-PAGE is nil, return the first page displaying FILENAME.
+If LIMIT is non-nil, it should be a range \(FIRST . LAST\) in
+which the returned page should fall. This is useful if there are
+more than one page displaying FILE-PAGE. LIMIT is ignored, if
+FILE-PAGE is nil.
+
+Return nil if there is no matching page."
+
+  (if (null file-page)
+      (cadr (assoc filename (pdf-virtual-document-file-map doc)))
+    (let ((pages (pdf-virtual-document-page-array doc)))
+      (catch 'found
+        (mapc
+         (lambda (pn)
+           (while (and (<= pn (length pages))
+                       (equal (pdf-virtual-range-filename (aref pages (1- pn)))
+                              filename))
+             (let* ((page (aref pages (1- pn)))
+                    (first (pdf-virtual-range-first page))
+                    (last (pdf-virtual-range-last page)))
+               (when (and (>= file-page first)
+                          (<= file-page last))
+                 (let ((r (+ (pdf-virtual-range-index-start page)
+                             (- file-page (pdf-virtual-range-first page))
+                             1)))
+                   (when (or (null limit)
+                             (and (>= r (car limit))
+                                  (<= r (cdr limit))))
+                     (throw 'found r))))
+               (cl-incf pn (1+ (- last first))))))
+         (cdr (assoc filename (pdf-virtual-document-file-map doc))))
+        nil))))
+
+(pdf-virtual-document-defun find-matching-page (page predicate
+                                                     &optional
+                                                     backward-p doc)
+  (unless (and (>= page 1)
+               (<= page (length (pdf-virtual-document-page-array doc))))
+    (signal 'args-out-of-range (list 'page page)))
+  (let* ((pages (pdf-virtual-document-page-array doc))
+         (i (1- page))
+         (this (aref pages i))
+         other)
+    (while (and (< i (length pages))
+                (>= i 0)
+                (null other))
+      (setq i
+            (if backward-p
+                (1- (pdf-virtual-range-index-start this))
+              (+ (pdf-virtual-range-length this)
+                 (pdf-virtual-range-index-start this))))
+      (when (and (< i (length pages))
+                 (>= i 0))
+        (setq other (aref pages i))
+        (unless (funcall predicate this other)
+          (setq other nil))))
+    other))
+
+(pdf-virtual-document-defun next-matching-page (page predicate doc)
+  (pdf-virtual-document-find-matching-page page predicate nil doc))
+
+(pdf-virtual-document-defun previous-matching-page (page predicate doc)
+  (declare (indent 1))
+  (pdf-virtual-document-find-matching-page page predicate t doc))
+
+(pdf-virtual-document-defun next-file (page doc)
+  "Return the next page displaying a different file than PAGE.
+
+PAGE should be a page-number."
+  (let ((page (pdf-virtual-document-next-matching-page
+               page
+               (lambda (this other)
+                 (not (equal (pdf-virtual-range-filename this)
+                             (pdf-virtual-range-filename other)))))))
+    (when page
+      (1+ (pdf-virtual-range-index-start page)))))
+
+(pdf-virtual-document-defun previous-file (page doc)
+  "Return the previous page displaying a different file than PAGE.
+
+PAGE should be a page-number."
+  (let ((page (pdf-virtual-document-previous-matching-page
+               page
+               (lambda (this other)
+                 (not (equal (pdf-virtual-range-filename this)
+                             (pdf-virtual-range-filename other)))))))
+    (when page
+      (1+ (pdf-virtual-range-index-start page)))))
+
+\f
+;; * ================================================================== *
+;; * Modes
+;; * ================================================================== *
+
+(defvar pdf-virtual-edit-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map emacs-lisp-mode-map)
+    (define-key map (kbd "C-c C-c") 'pdf-virtual-view-mode)
+    map))
+
+
+;;;###autoload
+(define-derived-mode pdf-virtual-edit-mode emacs-lisp-mode "VPDF-Edit"
+  "Major mode when editing a virtual PDF buffer."
+  (buffer-enable-undo)
+  (setq-local buffer-read-only nil)
+  (unless noninteractive
+    (message (substitute-command-keys "Press \\[pdf-virtual-view-mode] to view."))))
+
+;; FIXME: Provide filename/region from-windows-gathering functions.
+(defvar pdf-virtual-view-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map pdf-view-mode-map)
+    (define-key map (kbd "C-c C-c") 'pdf-virtual-edit-mode)
+    (define-key map [remap backward-paragraph] 'pdf-virtual-buffer-backward-file)
+    (define-key map [remap forward-paragraph] 'pdf-virtual-buffer-forward-file)
+    (define-key map (kbd "C-c C-c") 'pdf-virtual-edit-mode)
+    map))
+
+;;;###autoload
+(define-derived-mode pdf-virtual-view-mode pdf-view-mode "VPDF-View"
+  "Major mode in virtual PDF buffers."
+  (setq-local write-contents-functions nil)
+  (remove-hook 'kill-buffer-hook 'pdf-view-close-document t)
+  (setq-local header-line-format
+              `(:eval (pdf-virtual-buffer-current-file)))
+  (unless noninteractive
+    (message (substitute-command-keys "Press \\[pdf-virtual-edit-mode] to edit."))))
+
+;;;###autoload
+(define-minor-mode pdf-virtual-global-minor-mode
+  "Enable recognition and handling of VPDF files."
+  nil nil nil
+  :global t
+  (let ((elt `(,pdf-virtual-magic-mode-regexp . pdf-virtual-view-mode)))
+    (cond
+     (pdf-virtual-global-minor-mode
+      (add-to-list 'magic-mode-alist elt))
+     (t
+      (setq magic-mode-alist
+            (remove elt magic-mode-alist))))
+    (dolist (elt pdf-virtual-adapter-alist)
+      (let ((fn (car elt))
+            (orig (cdr elt)))
+        (advice-remove orig fn)
+        (when pdf-virtual-global-minor-mode
+          (advice-add orig :around fn))))))
+
+(advice-add 'pdf-virtual-view-mode
+            :around 'pdf-virtual-view-mode-prepare)
+
+;; This needs to run before pdf-view-mode does its thing.
+(defun pdf-virtual-view-mode-prepare (fn)
+  (let (list unreadable)
+    (save-excursion
+      (goto-char 1)
+      (unless (looking-at pdf-virtual-magic-mode-regexp)
+        (pdf-virtual-buffer-assert-p))
+      (setq list (read (current-buffer))))
+    (setq pdf-virtual-document
+          (pdf-virtual-document-create
+           list
+           nil
+           (lambda (filename _error)
+             (push filename unreadable))))
+    (when unreadable
+      (display-warning
+       'pdf-virtual
+       (format "Some documents could not be opened:\n%s"
+               (mapconcat (lambda (f)
+                            (concat " " f))
+                          unreadable "\n"))))
+    (if (= (pdf-virtual-document-number-of-pages) 0)
+        (error "Docüment is empty.")
+      (unless pdf-virtual-global-minor-mode
+        (pdf-virtual-global-minor-mode 1))
+      (funcall fn))))
+
+\f
+;; * ================================================================== *
+;; * Buffer handling
+;; * ================================================================== *
+
+;;;###autoload
+(defun pdf-virtual-buffer-create (&optional filenames buffer-name display-p)
+  (interactive
+   (list (directory-files default-directory nil "\\.pdf\\'")
+         (read-string
+          "Buffer name (default: all.vpdf): " nil nil "all.vpdf") t))
+  (with-current-buffer (generate-new-buffer buffer-name)
+    (insert ";; %VPDF 1.0\n\n")
+    (insert ";; File Format
+;;
+;; FORMAT    ::= ( FILES* )
+;; FILES     ::= ( FILE . PAGE-SPEC* )
+;; PAGE-SPEC ::= PAGE | ( PAGE . REGION )
+;; PAGE      ::= NUMBER | ( FIRST . LAST )
+;; REGION    ::= ( LEFT TOP RIGHT BOT )
+;;
+;; 0 <= X <= 1, forall X in REGION .
+
+")
+    (if (null filenames)
+        (insert "nil\n")
+      (insert "(")
+      (dolist (f filenames)
+        (insert (format "(%S)\n " f)))
+      (delete-char -2)
+      (insert ")\n"))
+    (pdf-virtual-edit-mode)
+    (when display-p
+      (pop-to-buffer (current-buffer)))
+    (current-buffer)))
+
+(defun pdf-virtual-buffer-p (&optional buffer)
+  (save-current-buffer
+    (when buffer (set-buffer buffer))
+    (or (derived-mode-p 'pdf-virtual-view-mode 'pdf-virtual-edit-mode)
+        pdf-virtual-document)))
+
+(defun pdf-virtual-view-window-p (&optional window)
+  (save-selected-window
+    (when window (select-window window))
+    (derived-mode-p 'pdf-virtual-view-mode)))
+
+(defun pdf-virtual-filename-p (filename)
+  (and (stringp filename)
+       (file-exists-p filename)
+       (with-temp-buffer
+         (save-excursion (insert-file-contents filename nil 0 128))
+         (looking-at pdf-virtual-magic-mode-regexp))))
+
+(defun pdf-virtual-buffer-assert-p (&optional buffer)
+  (unless (pdf-virtual-buffer-p buffer)
+    (error "Buffer is not a virtual PDF buffer")))
+
+(defun pdf-virtual-view-window-assert-p (&optional window)
+  (unless (pdf-virtual-view-window-p window)
+    (error "Window's buffer is not in `pdf-virtual-view-mode'.")))
+
+(defun pdf-virtual-buffer-current-file (&optional window)
+  (pdf-virtual-view-window-assert-p window)
+  (pdf-virtual-range-filename
+   (aref (pdf-virtual-document-page-array
+          pdf-virtual-document)
+         (1- (pdf-view-current-page window)))))
+
+(defun pdf-virtual-buffer-forward-file (&optional n interactive-p)
+  (interactive "p\np")
+  (pdf-virtual-view-window-assert-p)
+  (let* ((pn (pdf-view-current-page))
+         (pages (pdf-virtual-document-page-array
+                 pdf-virtual-document))
+         (page (aref pages (1- pn)))
+         (first-filepage (1+ (pdf-virtual-range-index-start page))))
+
+    (when (and (< n 0)
+               (not (= first-filepage pn)))
+      (cl-incf n))
+    (setq pn first-filepage)
+
+    (let (next)
+      (while (and (> n 0)
+                  (setq next (pdf-virtual-document-next-file pn)))
+        (setq pn next)
+        (cl-decf n)))
+    (let (previous)
+      (while (and (< n 0)
+                  (setq previous (pdf-virtual-document-previous-file pn)))
+        (setq pn previous)
+        (cl-incf n)))
+    (when interactive-p
+      (when (< n 0)
+        (message "First file."))
+      (when (> n 0)
+        (message "Last file.")))
+    (pdf-view-goto-page pn)
+    n))
+
+(defun pdf-virtual-buffer-backward-file (&optional n interactive-p)
+  (interactive "p\np")
+  (pdf-virtual-buffer-forward-file (- (or n 1)) interactive-p))
+
+\f
+;; * ================================================================== *
+;; * Helper functions
+;; * ================================================================== *
+
+
+(defmacro pdf-virtual-dopages (bindings pages &rest body)
+  (declare (indent 2) (debug (sexp form &rest form)))
+  (let ((page (make-symbol "page")))
+    `(dolist (,page ,pages)
+       (cl-destructuring-bind ,bindings
+           ,page
+         ,@body))))
+
+(defun pdf-virtual--perform-search (string pages &optional regexp-p no-error)
+  (let* ((pages (pdf-virtual-document-normalize-pages pages))
+         (file-pages (pdf-virtual-document-pages pages)))
+    (pdf-info-compose-queries
+        ((responses
+          (pdf-virtual-dopages (filename pages _region)
+              file-pages
+            (if regexp-p
+                (pdf-info-search-string string pages filename)
+              ;; FIXME: no-error won't work with synchronous calls.
+              (pdf-info-search-regexp string pages no-error filename)))))
+      (let (result)
+        (pdf-virtual-dopages (filename _ region)
+          file-pages
+          (let ((matches (pop responses)))
+            (when region
+              (setq matches
+                    (mapcar
+                     (lambda (m)
+                       (let-alist m
+                         `((edges . ,(pdf-util-edges-transform region .edges t))
+                           ,@m)))
+                     (pdf-virtual--filter-edges
+                      region matches
+                      (apply-partially 'alist-get 'edges)))))
+            (dolist (m matches)
+              (push `((page . ,(pdf-virtual-document-page-of
+                                filename (alist-get 'page m)
+                                pages))
+                      ,@m)
+                    result))))
+        (nreverse result)))))
+
+(defun pdf-virtual--filter-edges (region elts &optional edges-key-fn)
+  (if (null region)
+      elts
+    (cl-remove-if-not
+     (lambda (edges)
+       (or (null edges)
+           (if (consp (car edges))
+               (cl-some (apply-partially 'pdf-util-edges-intersection region) edges)
+             (pdf-util-edges-intersection region edges))))
+     elts
+     :key edges-key-fn)))
+
+(defun pdf-virtual--transform-goto-dest (link filename region)
+  (let-alist link
+    (let ((local-page (pdf-virtual-document-page-of
+                       filename .page)))
+      (if local-page
+          `((type . ,'goto-dest)
+            (title . , .title)
+            (page . ,local-page)
+            (top . ,(car (pdf-util-edges-transform
+                          region (cons .top .top) t))))
+        `((type . ,'goto-remote)
+          (title . , .title)
+          (filename . ,filename)
+          (page . , .page)
+          (top . , .top))))))
+
+\f
+;; * ================================================================== *
+;; * Server adapter
+;; * ================================================================== *
+
+(defmacro pdf-virtual-define-adapter (name arglist &optional doc &rest body)
+  ;; FIXME: Handle &optional + &rest argument.
+  (declare (doc-string 3) (indent 2)
+           (debug (&define name lambda-list
+                           [&optional stringp]
+                           def-body)))
+  (unless (stringp doc)
+    (push doc body)
+    (setq doc nil))
+  (let ((fn (intern (format "pdf-virtual-%s" name)))
+        (base-fn (intern (format "pdf-info-%s" name)))
+        (base-fn-arg (make-symbol "fn"))
+        (true-file-or-buffer (make-symbol "true-file-or-buffer"))
+        (args (cl-remove-if (lambda (elt)
+                              (memq elt '(&optional &rest)))
+                            arglist)))
+    (unless (fboundp base-fn)
+      (error "Base function is undefined: %s" base-fn))
+    (unless (memq 'file-or-buffer arglist)
+      (error "Argument list is missing a `file-or-buffer' argument: %s" arglist))
+    `(progn
+       (put ',fn 'definition-name ',name)
+       (add-to-list 'pdf-virtual-adapter-alist ',(cons fn base-fn))
+       (defun ,fn ,(cons base-fn-arg arglist)
+         ,(format "%sPDF virtual adapter to `%s'.
+
+This function delegates to `%s', unless the FILE-OR-BUFFER
+argument denotes a VPDF document."
+                  (if doc (concat doc "\n\n") "")
+                  base-fn
+                  base-fn)
+         (let ((,true-file-or-buffer
+                (cond
+                 ((or (bufferp file-or-buffer)
+                      (stringp file-or-buffer)) file-or-buffer)
+                 ((or (null file-or-buffer)
+                      ,(not (null (memq '&rest arglist))))
+                  (current-buffer)))))
+           (if (cond
+                ((null ,true-file-or-buffer) t)
+                ((bufferp ,true-file-or-buffer)
+                 (not (pdf-virtual-buffer-p ,true-file-or-buffer)))
+                ((stringp ,true-file-or-buffer)
+                 (not (pdf-virtual-filename-p ,true-file-or-buffer))))
+               (,(if (memq '&rest arglist) 'apply 'funcall) ,base-fn-arg ,@args)
+             (when (stringp ,true-file-or-buffer)
+               (setq ,true-file-or-buffer
+                     (find-file-noselect ,true-file-or-buffer)))
+             (save-current-buffer
+               (when (bufferp ,true-file-or-buffer)
+                 (set-buffer ,true-file-or-buffer))
+               ,@body)))))))
+
+(define-error 'pdf-virtual-unsupported-operation
+  "Operation not supported in VPDF buffer")
+
+(pdf-virtual-define-adapter open (&optional file-or-buffer password)
+  (mapc (lambda (file)
+          (pdf-info-open file password))
+        (pdf-virtual-document-filenames)))
+
+(pdf-virtual-define-adapter close (&optional file-or-buffer)
+  (let ((files (cl-remove-if 'find-buffer-visiting
+                             (pdf-virtual-document-filenames))))
+    (pdf-info-compose-queries
+        ((results (mapc 'pdf-info-close files)))
+      (cl-some 'identity results))))
+
+(pdf-virtual-define-adapter metadata (&optional file-or-buffer)
+  (pdf-info-compose-queries
+      ((md (mapc 'pdf-info-metadata (pdf-virtual-document-filenames))))
+    (apply 'cl-mapcar (lambda (&rest elts)
+                        (cons (caar elts)
+                              (cl-mapcar 'cdr elts)))
+           md)))
+
+(pdf-virtual-define-adapter search-string (string &optional pages file-or-buffer)
+  (pdf-virtual--perform-search
+   string (pdf-virtual-document-normalize-pages pages)))
+
+(pdf-virtual-define-adapter search-regexp (pcre &optional
+                                                pages no-error file-or-buffer)
+  (pdf-virtual--perform-search
+   pcre (pdf-virtual-document-normalize-pages pages) 'regexp no-error))
+
+(pdf-virtual-define-adapter pagelinks (page &optional file-or-buffer)
+  (cl-destructuring-bind (filename ext-page region)
+      (pdf-virtual-document-page page)
+    (pdf-info-compose-queries
+        ((links (pdf-info-pagelinks ext-page filename)))
+      (mapcar
+       (lambda (link)
+         (let-alist link
+           (if (not (eq .type 'goto-dest))
+               link
+             `((edges .  ,(pdf-util-edges-transform region .edges t))
+               ,@(pdf-virtual--transform-goto-dest link filename region)))))
+       (pdf-virtual--filter-edges region (car links) 'car)))))
+
+(pdf-virtual-define-adapter number-of-pages (&optional file-or-buffer)
+  (pdf-info-compose-queries nil (pdf-virtual-document-number-of-pages)))
+
+(pdf-virtual-define-adapter outline (&optional file-or-buffer)
+  (let ((files (pdf-virtual-document-filenames)))
+    (pdf-info-compose-queries
+        ((outlines (mapc 'pdf-info-outline files)))
+      (cl-mapcan
+       (lambda (outline filename)
+         `(((depth . 1)
+            (type . goto-dest)
+            (title . ,filename)
+            (page . ,(pdf-virtual-document-page-of filename))
+            (top . 0))
+           ,@(delq
+              nil
+              (mapcar
+               (lambda (item)
+                 (let-alist item
+                   (if (not (eq .type 'goto-dest))
+                       `((depth . ,(1+ .depth))
+                         ,@item)
+                     (cl-check-type filename string)
+                     (let ((page (pdf-virtual-document-page-of
+                                  filename .page)))
+                       (when page
+                         `((depth . ,(1+ .depth))
+                           ,@(pdf-virtual--transform-goto-dest
+                              item filename
+                              (nth 2 (pdf-virtual-document-page page)))))))))
+               outline))))
+       outlines files))))
+
+(pdf-virtual-define-adapter gettext (page edges &optional
+                                          selection-style file-or-buffer)
+  (cl-destructuring-bind (filename file-page region)
+      (pdf-virtual-document-page page)
+    (let ((edges (pdf-util-edges-transform region edges)))
+      (pdf-info-gettext file-page edges selection-style filename))))
+
+(pdf-virtual-define-adapter getselection (page edges &optional
+                                               selection-style file-or-buffer)
+  (cl-destructuring-bind (filename file-page region)
+      (pdf-virtual-document-page page)
+    (let ((edges (pdf-util-edges-transform region edges)))
+      (pdf-info-compose-queries
+          ((results (pdf-info-getselection file-page edges selection-style filename)))
+        (pdf-util-edges-transform
+         region
+         (pdf-virtual--filter-edges region (car results)) t)))))
+
+(pdf-virtual-define-adapter charlayout (page &optional edges-or-pos file-or-buffer)
+  (cl-destructuring-bind (filename file-page region)
+      (pdf-virtual-document-page page)
+    (let ((edges-or-pos (pdf-util-edges-transform region edges-or-pos)))
+      (pdf-info-compose-queries
+          ((results (pdf-info-charlayout file-page edges-or-pos filename)))
+        (mapcar (lambda (elt)
+                  `(,(car elt)
+                    . ,(pdf-util-edges-transform region (cdr elt) t)))
+                (pdf-virtual--filter-edges region (car results) 'cadr))))))
+
+(pdf-virtual-define-adapter pagesize (page &optional file-or-buffer)
+  (cl-destructuring-bind (filename file-page region)
+      (pdf-virtual-document-page page)
+    (pdf-info-compose-queries
+        ((result (pdf-info-pagesize file-page filename)))
+      (if (null region)
+          (car result)
+        (pdf-util-with-edges (region)
+          (pdf-util-scale
+           (car result) (cons region-width region-height)))))))
+
+(pdf-virtual-define-adapter getannots (&optional pages file-or-buffer)
+  (let* ((pages (pdf-virtual-document-normalize-pages pages))
+         (file-pages (pdf-virtual-document-pages pages)))
+    (pdf-info-compose-queries
+        ((annotations
+          (pdf-virtual-dopages (filename file-pages _region)
+              file-pages
+            (pdf-info-getannots file-pages filename))))
+      (let ((page (car pages))
+            result)
+        (pdf-virtual-dopages (_filename file-pages region)
+            file-pages
+          (dolist (a (pop annotations))
+            (let ((edges (delq nil `(,(cdr (assq 'edges a))
+                                     ,@(cdr (assq 'markup-edges a))))))
+              (when (pdf-virtual--filter-edges region edges)
+                (let-alist a
+                  (setcdr (assq 'page a)
+                          (+ page (- .page (car file-pages))))
+                  (setcdr (assq 'id a)
+                          (intern (format "%s/%d" .id (cdr (assq 'page a)))))
+                  (when region
+                    (when .edges
+                      (setcdr (assq 'edges a)
+                              (pdf-util-edges-transform region .edges t)))
+                    (when .markup-edges
+                      (setcdr (assq 'markup-edges a)
+                              (pdf-util-edges-transform region .markup-edges t))))
+                  (push a result)))))
+          (cl-incf page (1+ (- (cdr file-pages) (car file-pages)))))
+        (nreverse result)))))
+
+(pdf-virtual-define-adapter getannot (id &optional file-or-buffer)
+  (let ((name (symbol-name id))
+        page)
+    (save-match-data
+      (when (string-match "\\(.*\\)/\\([0-9]+\\)\\'" name)
+        (setq id  (intern (match-string 1 name))
+              page (string-to-number (match-string 2 name)))))
+    (if page
+        (cl-destructuring-bind (filename _ _)
+            (pdf-virtual-document-page page)
+          (pdf-info-compose-queries
+              ((result (pdf-info-getannot id filename)))
+            (let ((a (car result)))
+              (cl-destructuring-bind (_ _ region)
+                  (pdf-virtual-document-page page)
+                (setcdr (assq 'page a) page)
+                (let-alist a
+                  (setcdr (assq 'id a)
+                          (intern (format "%s/%d" .id (cdr (assq 'page a)))))
+                  (when region
+                    (when .edges
+                      (setcdr (assq 'edges a)
+                              (pdf-util-edges-transform region .edges t)))
+                    (when .markup-edges
+                      (setcdr (assq 'markup-edges a)
+                              (pdf-util-edges-transform region .markup-edges t))))))
+              a)))
+      (pdf-info-compose-queries nil
+        (error "No such annotation: %s" id)))))
+
+(pdf-virtual-define-adapter addannot (page edges type &optional
+                                           file-or-buffer &rest markup-edges)
+  (signal 'pdf-virtual-unsupported-operation (list 'addannot)))
+
+(pdf-virtual-define-adapter delannot (id &optional file-or-buffer)
+  (signal 'pdf-virtual-unsupported-operation (list 'delannot)))
+
+(pdf-virtual-define-adapter mvannot (id edges &optional file-or-buffer)
+  (signal 'pdf-virtual-unsupported-operation (list 'mvannot)))
+
+(pdf-virtual-define-adapter editannot (id modifications &optional file-or-buffer)
+  (signal 'pdf-virtual-unsupported-operation (list 'editannot)))
+
+(pdf-virtual-define-adapter save (&optional file-or-buffer)
+  (signal 'pdf-virtual-unsupported-operation (list 'save)))
+
+;;(defvar-local pdf-virtual-annotation-mapping nil)
+
+(pdf-virtual-define-adapter getattachment-from-annot
+    (id &optional do-save file-or-buffer)
+  (let ((name (symbol-name id))
+        page)
+    (save-match-data
+      (when (string-match "\\(.*\\)/\\([0-9]+\\)\\'" name)
+        (setq id  (intern (match-string 1 name))
+              page (string-to-number (match-string 2 name)))))
+    (if page
+        (cl-destructuring-bind (filename _ _)
+            (pdf-virtual-document-page page)
+          (pdf-info-getattachment-from-annot id do-save filename))
+      (pdf-info-compose-queries nil
+        (error "No such annotation: %s" id)))))
+
+(pdf-virtual-define-adapter getattachments (&optional do-save file-or-buffer)
+  (pdf-info-compose-queries
+      ((results (mapc
+                 (lambda (f)
+                   (pdf-info-getattachments do-save f))
+                 (pdf-virtual-document-filenames))))
+    (apply 'append results)))
+
+(pdf-virtual-define-adapter synctex-forward-search
+    (source &optional line column file-or-buffer)
+  (signal 'pdf-virtual-unsupported-operation (list 'synctex-forward-search)))
+
+(pdf-virtual-define-adapter synctex-backward-search (page &optional x y file-or-buffer)
+  (cl-destructuring-bind (filename file-page region)
+      (pdf-virtual-document-page page)
+    (cl-destructuring-bind (x &rest y)
+        (pdf-util-edges-transform region (cons x y))
+      (pdf-info-synctex-backward-search file-page x y filename))))
+
+(pdf-virtual-define-adapter renderpage (page width &optional file-or-buffer
+                                             &rest commands)
+  (when (keywordp file-or-buffer)
+    (push file-or-buffer commands)
+    (setq file-or-buffer nil))
+  (cl-destructuring-bind (filename file-page region)
+      (pdf-virtual-document-page page)
+    (when region
+      (setq commands (append (list :crop-to region) commands)
+            width (pdf-util-with-edges (region)
+                    (round (* width (max 1 (/ 1.0 (max 1e-6 region-width))))))))
+    (apply 'pdf-info-renderpage file-page width filename commands)))
+
+(pdf-virtual-define-adapter boundingbox (page &optional file-or-buffer)
+  (cl-destructuring-bind (filename file-page region)
+      (pdf-virtual-document-page page)
+    (pdf-info-compose-queries
+        ((results (unless region (pdf-info-boundingbox file-page filename))))
+      (if region
+          (list 0 0 1 1)
+        (car results)))))
+
+(pdf-virtual-define-adapter pagelabels (&optional file-or-buffer)
+  (signal 'pdf-virtual-unsupported-operation (list 'pagelabels)))
+
+(pdf-virtual-define-adapter setoptions (&optional file-or-buffer &rest options)
+  (when (keywordp file-or-buffer)
+    (push file-or-buffer options)
+    (setq file-or-buffer nil))
+  (pdf-info-compose-queries
+      ((_ (dolist (f (pdf-virtual-document-filenames))
+            (apply 'pdf-info-setoptions f options))))
+    nil))
+
+(pdf-virtual-define-adapter getoptions (&optional file-or-buffer)
+  (signal 'pdf-virtual-unsupported-operation (list 'getoptions)))
+
+(pdf-virtual-define-adapter encrypted-p (&optional file-or-buffer)
+  nil)
+
+(provide 'pdf-virtual)
+;;; pdf-virtual.el ends here