dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

pdf-view.el (65510B)


      1 ;;; pdf-view.el --- View PDF documents. -*- lexical-binding:t -*-
      2 
      3 ;; Copyright (C) 2013  Andreas Politz
      4 
      5 ;; Author: Andreas Politz <politza@fh-trier.de>
      6 ;; Keywords: files, doc-view, pdf
      7 
      8 ;; This program is free software; you can redistribute it and/or modify
      9 ;; it under the terms of the GNU General Public License as published by
     10 ;; the Free Software Foundation, either version 3 of the License, or
     11 ;; (at your option) any later version.
     12 
     13 ;; This program is distributed in the hope that it will be useful,
     14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 ;; GNU General Public License for more details.
     17 
     18 ;; You should have received a copy of the GNU General Public License
     19 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
     20 
     21 ;;; Commentary:
     22 
     23 ;;  Functions related to viewing PDF documents.
     24 
     25 ;;; Code:
     26 
     27 (require 'image-mode)
     28 (require 'pdf-macs)
     29 (require 'pdf-util)
     30 (require 'pdf-info)
     31 (require 'pdf-cache)
     32 (require 'jka-compr)
     33 (require 'bookmark)
     34 (require 'password-cache)
     35 
     36 (declare-function cua-copy-region "cua-base")
     37 (declare-function pdf-tools-pdf-buffer-p "pdf-tools")
     38 
     39 ;; * ================================================================== *
     40 ;; * Customizations
     41 ;; * ================================================================== *
     42 
     43 (defgroup pdf-view nil
     44   "View PDF documents."
     45   :group 'pdf-tools)
     46 
     47 (defcustom pdf-view-display-size 'fit-width
     48   "The desired size of displayed pages.
     49 
     50 This may be one of `fit-height', `fit-width', `fit-page' or a
     51 number as a scale factor applied to the document's size.  Any
     52 other value behaves like `fit-width'."
     53   :group 'pdf-view
     54   :type '(choice number
     55                  (const fit-height)
     56                  (const fit-width)
     57                  (const fit-page)))
     58 
     59 (make-variable-buffer-local 'pdf-view-display-size)
     60 
     61 (defcustom pdf-view-resize-factor 1.25
     62   "Fractional amount of resizing of one resize command."
     63   :group 'pdf-view
     64   :type 'number)
     65 
     66 (defcustom pdf-view-continuous t
     67   "In Continuous mode reaching the page edge advances to next/previous page.
     68 
     69 When non-nil, scrolling a line upward at the bottom edge of the page
     70 moves to the next page, and scrolling a line downward at the top edge
     71 of the page moves to the previous page."
     72   :type 'boolean
     73   :group 'pdf-view)
     74 
     75 (defcustom pdf-view-bounding-box-margin 0.05
     76   "Fractional margin used for slicing with the bounding-box."
     77   :group 'pdf-view
     78   :type 'number)
     79 
     80 (defcustom pdf-view-use-imagemagick nil
     81   "Whether imagemagick should be used for rendering.
     82 
     83 This variable has no effect, if imagemagick was not compiled into
     84 Emacs or if imagemagick is the only way to display PNG images.
     85 FIXME: Explain dis-/advantages of imagemagick and png."
     86   :group 'pdf-view
     87   :type 'boolean)
     88 
     89 (defcustom pdf-view-use-scaling t
     90   "Whether images should be allowed to be scaled for rendering.
     91 
     92 This variable affects both the reuse of higher-resolution images
     93 as lower-resolution ones by down-scaling the image.  As well as
     94 the rendering of higher-resolution for high-resolution displays,
     95 if available."
     96   :group 'pdf-view
     97   :type 'boolean)
     98 
     99 (defface pdf-view-region
    100   '((((background dark)) (:inherit region))
    101     (((background light)) (:inherit region)))
    102   "Face used to determine the colors of the region."
    103   :group 'pdf-view
    104   :group 'pdf-tools-faces)
    105 
    106 (defface pdf-view-rectangle
    107   '((((background dark)) (:inherit highlight))
    108     (((background light)) (:inherit highlight)))
    109   "Face used to determine the colors of the highlighted rectangle."
    110   :group 'pdf-view
    111   :group 'pdf-tools-faces)
    112 
    113 (defcustom pdf-view-midnight-colors '("#839496" . "#002b36" )
    114   "Colors used when command `pdf-view-midnight-minor-mode' is activated.
    115 
    116 This should be a cons \(FOREGROUND . BACKGROUND\) of colors."
    117   :group 'pdf-view
    118   :type '(cons (color :tag "Foreground")
    119                (color :tag "Background")))
    120 
    121 (defcustom pdf-view-change-page-hook nil
    122   "Hook run after changing to another page, but before displaying it.
    123 
    124 See also `pdf-view-before-change-page-hook' and
    125 `pdf-view-after-change-page-hook'."
    126   :group 'pdf-view
    127   :type 'hook)
    128 
    129 (defcustom pdf-view-before-change-page-hook nil
    130   "Hook run before changing to another page.
    131 
    132 See also `pdf-view-change-page-hook' and
    133 `pdf-view-after-change-page-hook'."
    134   :group 'pdf-view
    135   :type 'hook)
    136 
    137 (defcustom pdf-view-after-change-page-hook nil
    138   "Hook run after changing to and displaying another page.
    139 
    140 See also `pdf-view-change-page-hook' and
    141 `pdf-view-before-change-page-hook'."
    142   :group 'pdf-view
    143   :type 'hook)
    144 
    145 (defcustom pdf-view-use-dedicated-register t
    146   "Whether to use dedicated register for PDF positions.
    147 
    148 If this is non-nil, the commands `pdf-view-position-to-register'
    149 and `pdf-view-jump-to-register' use the buffer-local variable
    150 `pdf-view-register-alist' to store resp. retrieve marked
    151 positions.  Otherwise the common variable `register-alist' is
    152 used."
    153   :group 'pdf-view
    154   :type 'boolean)
    155 
    156 (defcustom pdf-view-image-relief 0
    157   "Add a shadow rectangle around the page's image.
    158 
    159 See :relief property in Info node `(elisp) Image Descriptors'."
    160   :group 'pdf-view
    161   :type '(integer :tag "Pixel")
    162   :link '(info-link "(elisp) Image Descriptors"))
    163 
    164 (defcustom pdf-view-max-image-width 4800
    165   "Maximum width of any image displayed in pixel."
    166   :group 'pdf-view
    167   :type '(integer :tag "Pixel"))
    168 
    169 (defcustom pdf-view-use-unicode-ligther t
    170   "Decide whether to use unicode symbols in the mode-line.
    171 
    172 On some systems finding a font which supports those symbols can
    173 take some time.  If you don't want to spend that time waiting and
    174 don't care for a nicer looking mode-line, set this variable to
    175 nil.
    176 
    177 Note, that this option has only an effect when this library is
    178 loaded."
    179   :group 'pdf-view
    180   :type 'boolean)
    181 
    182 (defcustom pdf-view-incompatible-modes
    183   '(linum-mode linum-relative-mode helm-linum-relative-mode
    184 	       nlinum-mode nlinum-hl-mode nlinum-relative-mode yalinum-mode)
    185   "A list of modes incompatible with `pdf-view-mode'.
    186 
    187 Issue a warning, if one of them is active in a PDF buffer."
    188   :group 'pdf-view
    189   :type '(repeat symbol))
    190 
    191 
    192 ;; * ================================================================== *
    193 ;; * Internal variables and macros
    194 ;; * ================================================================== *
    195 
    196 (defvar-local pdf-view-active-region nil
    197   "The active region as a list of edges.
    198 
    199 Edge values are relative coordinates.")
    200 
    201 (defvar-local pdf-view--have-rectangle-region nil
    202   "Non-nil if the region is currently rendered as a rectangle.
    203 
    204 This variable is set in `pdf-view-mouse-set-region' and used in
    205 `pdf-view-mouse-extend-region' to determine the right choice
    206 regarding display of the region in the later function.")
    207 
    208 (defvar-local pdf-view--buffer-file-name nil
    209   "Local copy of remote file or nil.")
    210 
    211 (defvar-local pdf-view--server-file-name nil
    212   "The servers notion of this buffer's filename.")
    213 
    214 (defvar-local pdf-view--next-page-timer nil
    215   "Timer used in `pdf-view-next-page-command'.")
    216 
    217 (defvar-local pdf-view--hotspot-functions nil
    218   "Alist of hotspot functions.")
    219 
    220 (defvar-local pdf-view-register-alist nil
    221   "Local, dedicated register for PDF positions.")
    222 
    223 (defun pdf-view-current-pagelabel (&optional window)
    224   (nth (1- (pdf-view-current-page window)) (pdf-info-pagelabels)))
    225 
    226 (defun pdf-view-active-region-p nil
    227   "Return t if there are active regions."
    228   (not (null pdf-view-active-region)))
    229 
    230 (defmacro pdf-view-assert-active-region ()
    231   "Signal an error if there are no active regions."
    232   `(unless (pdf-view-active-region-p)
    233      (error "The region is not active")))
    234 
    235 (defconst pdf-view-have-image-mode-pixel-vscroll
    236   (>= emacs-major-version 27)
    237   "Whether `image-mode' scrolls vertically by pixels.")
    238 
    239 
    240 ;; * ================================================================== *
    241 ;; * Major Mode
    242 ;; * ================================================================== *
    243 
    244 (defvar pdf-view-mode-map
    245   (let ((map (make-sparse-keymap)))
    246     (set-keymap-parent map image-mode-map)
    247     (define-key map (kbd "Q")         'kill-this-buffer)
    248     ;; Navigation in the document
    249     (define-key map (kbd "n")         'pdf-view-next-page-command)
    250     (define-key map (kbd "p")         'pdf-view-previous-page-command)
    251     (define-key map (kbd "<next>")    'forward-page)
    252     (define-key map (kbd "<prior>")   'backward-page)
    253     (define-key map [remap forward-page]  'pdf-view-next-page-command)
    254     (define-key map [remap backward-page] 'pdf-view-previous-page-command)
    255     (define-key map (kbd "SPC")       'pdf-view-scroll-up-or-next-page)
    256     (define-key map (kbd "S-SPC")     'pdf-view-scroll-down-or-previous-page)
    257     (define-key map (kbd "DEL")       'pdf-view-scroll-down-or-previous-page)
    258     (define-key map (kbd "C-n")       'pdf-view-next-line-or-next-page)
    259     (define-key map (kbd "<down>")    'pdf-view-next-line-or-next-page)
    260     (define-key map [remap next-line] 'pdf-view-next-line-or-next-page)
    261     (define-key map (kbd "C-p")           'pdf-view-previous-line-or-previous-page)
    262     (define-key map (kbd "<up>")          'pdf-view-previous-line-or-previous-page)
    263     (define-key map [remap previous-line] 'pdf-view-previous-line-or-previous-page)
    264     (define-key map (kbd "M-<")                 'pdf-view-first-page)
    265     (define-key map [remap beginning-of-buffer] 'pdf-view-first-page)
    266     (define-key map (kbd "M->")                 'pdf-view-last-page)
    267     (define-key map [remap end-of-buffer]       'pdf-view-last-page)
    268     (define-key map [remap goto-line] 'pdf-view-goto-page)
    269     (define-key map (kbd "M-g l")     'pdf-view-goto-label)
    270     (define-key map (kbd "RET")       'image-next-line)
    271     ;; Zoom in/out.
    272     (define-key map "+"               'pdf-view-enlarge)
    273     (define-key map "="               'pdf-view-enlarge)
    274     (define-key map "-"               'pdf-view-shrink)
    275     (define-key map "0"               'pdf-view-scale-reset)
    276     ;; Fit the image to the window
    277     (define-key map "W"               'pdf-view-fit-width-to-window)
    278     (define-key map "H"               'pdf-view-fit-height-to-window)
    279     (define-key map "P"               'pdf-view-fit-page-to-window)
    280     ;; Slicing the image
    281     (define-key map (kbd "s m")       'pdf-view-set-slice-using-mouse)
    282     (define-key map (kbd "s b")       'pdf-view-set-slice-from-bounding-box)
    283     (define-key map (kbd "s r")       'pdf-view-reset-slice)
    284     ;; Reconvert
    285     (define-key map (kbd "C-c C-c")   'doc-view-mode)
    286     (define-key map (kbd "g")         'revert-buffer)
    287     ;; Region
    288     (define-key map [down-mouse-1] 'pdf-view-mouse-set-region)
    289     (define-key map [M-down-mouse-1] 'pdf-view-mouse-set-region-rectangle)
    290     (define-key map [C-down-mouse-1] 'pdf-view-mouse-extend-region)
    291     (define-key map [remap kill-region] 'pdf-view-kill-ring-save)
    292     (define-key map [remap kill-ring-save] 'pdf-view-kill-ring-save)
    293     (define-key map [remap mark-whole-buffer] 'pdf-view-mark-whole-page)
    294     ;; Other
    295     (define-key map (kbd "C-c C-d") 'pdf-view-dark-minor-mode)
    296     (define-key map (kbd "m") 'pdf-view-position-to-register)
    297     (define-key map (kbd "'") 'pdf-view-jump-to-register)
    298 
    299     (define-key map (kbd "C-c C-i") 'pdf-view-extract-region-image)
    300     ;; Rendering
    301     (define-key map (kbd "C-c C-r m") 'pdf-view-midnight-minor-mode)
    302     (define-key map (kbd "C-c C-r t") 'pdf-view-themed-minor-mode)
    303     (define-key map (kbd "C-c C-r p") 'pdf-view-printer-minor-mode)
    304     map)
    305   "Keymap used by `pdf-view-mode' when displaying a doc as a set of images.")
    306 
    307 (define-derived-mode pdf-view-mode special-mode "PDFView"
    308   "Major mode in PDF buffers.
    309 
    310 PDFView Mode is an Emacs PDF viewer.  It displays PDF files as
    311 PNG images in Emacs buffers."
    312   :group 'pdf-view
    313   :abbrev-table nil
    314   :syntax-table nil
    315   ;; Setup a local copy for remote files.
    316   (when (and (or jka-compr-really-do-compress
    317                  (let ((file-name-handler-alist nil))
    318                    (not (and buffer-file-name
    319                              (file-readable-p buffer-file-name)))))
    320              (pdf-tools-pdf-buffer-p))
    321     (let ((tempfile (pdf-util-make-temp-file)))
    322       (write-region nil nil tempfile nil 'no-message)
    323       (setq-local pdf-view--buffer-file-name tempfile)))
    324   ;; Decryption needs to be done before any other function calls into
    325   ;; pdf-info.el (e.g. from the mode-line during redisplay during
    326   ;; waiting for process output).
    327   (pdf-view-decrypt-document)
    328 
    329   ;; Setup scroll functions
    330   (if (boundp 'mwheel-scroll-up-function) ; not --without-x build
    331       (setq-local mwheel-scroll-up-function
    332                   #'pdf-view-scroll-up-or-next-page))
    333   (if (boundp 'mwheel-scroll-down-function)
    334       (setq-local mwheel-scroll-down-function
    335                   #'pdf-view-scroll-down-or-previous-page))
    336 
    337   ;; Disable pixel-scroll-precision-mode locally if enabled
    338   (if (bound-and-true-p pixel-scroll-precision-mode)
    339       (set (make-local-variable 'pixel-scroll-precision-mode) nil))
    340 
    341   ;; Clearing overlays
    342   (add-hook 'change-major-mode-hook
    343             (lambda ()
    344               (remove-overlays (point-min) (point-max) 'pdf-view t))
    345             nil t)
    346   (remove-overlays (point-min) (point-max) 'pdf-view t) ;Just in case.
    347 
    348   ;; Setup other local variables.
    349   (setq-local mode-line-position
    350               '(" P" (:eval (number-to-string (pdf-view-current-page)))
    351                 ;; Avoid errors during redisplay.
    352                 "/" (:eval (or (ignore-errors
    353                                  (number-to-string (pdf-cache-number-of-pages)))
    354                                "???"))))
    355   (setq-local auto-hscroll-mode nil)
    356   (setq-local pdf-view--server-file-name (pdf-view-buffer-file-name))
    357   ;; High values of scroll-conservatively seem to trigger
    358   ;; some display bug in xdisp.c:try_scrolling .
    359   (setq-local scroll-conservatively 0)
    360   (setq-local cursor-type nil)
    361   (setq-local buffer-read-only t)
    362   (setq-local view-read-only nil)
    363   (setq-local bookmark-make-record-function
    364               'pdf-view-bookmark-make-record)
    365   (setq-local revert-buffer-function #'pdf-view-revert-buffer)
    366   ;; No auto-save at the moment.
    367   (setq-local buffer-auto-save-file-name nil)
    368   ;; Disable image rescaling.
    369   (when (boundp 'image-scaling-factor)
    370     (setq-local image-scaling-factor 1))
    371   ;; No undo at the moment.
    372   (unless buffer-undo-list
    373     (buffer-disable-undo))
    374   ;; Enable transient-mark-mode, so region deactivation when quitting
    375   ;; will work.
    376   (setq-local transient-mark-mode t)
    377 
    378   (add-hook 'window-configuration-change-hook
    379             'pdf-view-redisplay-some-windows nil t)
    380   (add-hook 'deactivate-mark-hook 'pdf-view-deactivate-region nil t)
    381   (add-hook 'write-contents-functions
    382             'pdf-view--write-contents-function nil t)
    383   (add-hook 'kill-buffer-hook 'pdf-view-close-document nil t)
    384   (pdf-view-add-hotspot-function
    385    'pdf-view-text-regions-hotspots-function -9)
    386 
    387   ;; Keep track of display info
    388   (add-hook 'image-mode-new-window-functions
    389             'pdf-view-new-window-function nil t)
    390   (image-mode-setup-winprops)
    391 
    392   ;; Issue a warning in the future about incompatible modes.
    393   (run-with-timer 1 nil (lambda (buffer)
    394                           (when (buffer-live-p buffer)
    395                             (pdf-view-check-incompatible-modes buffer)))
    396 		  (current-buffer)))
    397 
    398 (advice-add 'cua-copy-region
    399 	        :before-until
    400 	        #'cua-copy-region--pdf-view-advice)
    401 
    402 (defun cua-copy-region--pdf-view-advice (&rest _)
    403   "If the current buffer is in `pdf-view' mode, call `pdf-view-kill-ring-save'."
    404   (when (eq major-mode 'pdf-view-mode)
    405     (pdf-view-kill-ring-save)
    406     t))
    407 
    408 (defun pdf-view-check-incompatible-modes (&optional buffer)
    409   "Check BUFFER for incompatible modes, maybe issue a warning."
    410   (with-current-buffer (or buffer (current-buffer))
    411     (let ((modes
    412 	   (cl-remove-if-not
    413 	    (lambda (mode) (and (symbolp mode)
    414 				(boundp mode)
    415 				(symbol-value mode)))
    416 	    pdf-view-incompatible-modes)))
    417       (when modes
    418 	(display-warning
    419 	 'pdf-view
    420 	 (format "These modes are incompatible with `pdf-view-mode',
    421 	please deactivate them (or customize pdf-view-incompatible-modes): %s"
    422 		 (mapconcat #'symbol-name modes ",")))))))
    423 
    424 (defun pdf-view-decrypt-document ()
    425   "Read a password, if the document is encrypted and open it."
    426   (interactive)
    427   (when (pdf-info-encrypted-p)
    428     (let ((prompt (format "Enter password for `%s': "
    429                           (abbreviate-file-name
    430                            (buffer-file-name))))
    431           (key (concat "/pdf-tools" (buffer-file-name)))
    432           (i 3)
    433           password)
    434       (while (and (> i 0)
    435                   (pdf-info-encrypted-p))
    436         (setq i (1- i))
    437         (setq password (password-read prompt key))
    438         (setq prompt "Invalid password, try again: ")
    439         (ignore-errors (pdf-info-open nil password)))
    440       (pdf-info-open nil password)
    441       (password-cache-add key password)))
    442   nil)
    443 
    444 (defun pdf-view-buffer-file-name ()
    445   "Return the local filename of the PDF in the current buffer.
    446 
    447 This may be different from variable `buffer-file-name' when
    448 operating on a local copy of a remote file."
    449   (or pdf-view--buffer-file-name
    450       (buffer-file-name)))
    451 
    452 (defun pdf-view--write-contents-function ()
    453   "Function for `write-contents-functions' to save the buffer."
    454   (when (pdf-util-pdf-buffer-p)
    455     (let ((tempfile (pdf-info-save pdf-view--server-file-name)))
    456       (unwind-protect
    457           (progn
    458             ;; Order matters here: We need to first copy the new
    459             ;; content (tempfile) to the PDF, and then close the PDF.
    460             ;; Since while closing the file (and freeing its resources
    461             ;; in the process), it may be immediately reopened due to
    462             ;; redisplay happening inside the pdf-info-close function
    463             ;; (while waiting for a response from the process.).
    464             (copy-file tempfile (buffer-file-name) t)
    465             (pdf-info-close pdf-view--server-file-name)
    466 
    467             (when pdf-view--buffer-file-name
    468               (copy-file tempfile pdf-view--buffer-file-name t))
    469             (clear-visited-file-modtime)
    470             (set-buffer-modified-p nil)
    471             (setq pdf-view--server-file-name
    472                   (pdf-view-buffer-file-name))
    473             t)
    474         (when (file-exists-p tempfile)
    475           (delete-file tempfile))))))
    476 
    477 (defun pdf-view--after-revert ()
    478   "Reload the local copy in case of a remote file, and close the document."
    479   (when pdf-view--buffer-file-name
    480     (write-region nil nil pdf-view--buffer-file-name nil 'no-message))
    481   (pdf-info-close))
    482 
    483 (defun pdf-view-revert-buffer (&optional ignore-auto noconfirm)
    484   "Revert buffer while preserving current modes.
    485 
    486 Optional parameters IGNORE-AUTO and NOCONFIRM are defined as in
    487 `revert-buffer'."
    488   (interactive (list (not current-prefix-arg)))
    489   ;; Bind to default so that we can use pdf-view-revert-buffer as
    490   ;; revert-buffer-function.  A binding of nil is needed in Emacs 24.3, but in
    491   ;; later versions the semantics that nil means the default function should
    492   ;; not relied upon.
    493   (let ((revert-buffer-function (when (fboundp #'revert-buffer--default)
    494                                   #'revert-buffer--default))
    495         (after-revert-hook
    496          (cons #'pdf-view--after-revert
    497                after-revert-hook)))
    498     (prog1
    499         (revert-buffer ignore-auto noconfirm 'preserve-modes)
    500       (pdf-view-redisplay t))))
    501 
    502 (defun pdf-view-close-document ()
    503   "Return immediately after closing document.
    504 
    505 This function always succeeds.  See also `pdf-info-close', which
    506 does not return immediately."
    507   (when (pdf-info-running-p)
    508     (let ((pdf-info-asynchronous 'ignore))
    509       (ignore-errors
    510         (pdf-info-close)))))
    511 
    512 
    513 ;; * ================================================================== *
    514 ;; * Scaling
    515 ;; * ================================================================== *
    516 
    517 (defun pdf-view-fit-page-to-window ()
    518   "Fit PDF to window.
    519 
    520 Choose the larger of PDF's height and width, and fits that
    521 dimension to window."
    522   (interactive)
    523   (setq pdf-view-display-size 'fit-page)
    524   (image-set-window-vscroll 0)
    525   (image-set-window-hscroll 0)
    526   (pdf-view-redisplay t))
    527 
    528 (defun pdf-view-fit-height-to-window ()
    529   "Fit PDF height to window."
    530   (interactive)
    531   (setq pdf-view-display-size 'fit-height)
    532   (image-set-window-vscroll 0)
    533   (pdf-view-redisplay t))
    534 
    535 (defun pdf-view-fit-width-to-window ()
    536   "Fit PDF size to window."
    537   (interactive)
    538   (setq pdf-view-display-size 'fit-width)
    539   (image-set-window-hscroll 0)
    540   (pdf-view-redisplay t))
    541 
    542 (defun pdf-view-enlarge (factor)
    543   "Enlarge PDF by FACTOR.
    544 
    545 When called interactively, uses the value of
    546 `pdf-view-resize-factor'.
    547 
    548 For example, (pdf-view-enlarge 1.25) increases size by 25%."
    549   (interactive
    550    (list (float pdf-view-resize-factor)))
    551   (let* ((size (pdf-view-image-size))
    552          (pagesize (pdf-cache-pagesize
    553                     (pdf-view-current-page)))
    554          (scale (/ (float (car size))
    555                    (float (car pagesize)))))
    556     (setq pdf-view-display-size
    557           (* factor scale))
    558     (pdf-view-redisplay t)))
    559 
    560 (defun pdf-view-shrink (factor)
    561   "Shrink PDF by FACTOR.
    562 
    563 When called interactively, uses the value of
    564 `pdf-view-resize-factor'.
    565 
    566 For example, (pdf-view-shrink 1.25) decreases size by 20%."
    567   (interactive
    568    (list (float pdf-view-resize-factor)))
    569   (pdf-view-enlarge (/ 1.0 factor)))
    570 
    571 (defun pdf-view-scale-reset ()
    572   "Reset PDF to its default set."
    573   (interactive)
    574   (setq pdf-view-display-size 1.0)
    575   (pdf-view-redisplay t))
    576 
    577 
    578 
    579 ;; * ================================================================== *
    580 ;; * Moving by pages and scrolling
    581 ;; * ================================================================== *
    582 
    583 (defvar pdf-view-inhibit-redisplay nil)
    584 (defvar pdf-view-inhibit-hotspots nil)
    585 
    586 (defun pdf-view-goto-page (page &optional window)
    587   "Go to PAGE in PDF.
    588 
    589 If optional parameter WINDOW, go to PAGE in all `pdf-view'
    590 windows."
    591   (interactive
    592    (list (if current-prefix-arg
    593              (prefix-numeric-value current-prefix-arg)
    594            (read-number "Page: "))))
    595   (unless (and (>= page 1)
    596                (<= page (pdf-cache-number-of-pages)))
    597     (error "No such page: %d" page))
    598   (unless window
    599     (setq window
    600           (if (pdf-util-pdf-window-p)
    601               (selected-window)
    602             t)))
    603   (save-selected-window
    604     ;; Select the window for the hooks below.
    605     (when (window-live-p window)
    606       (select-window window 'norecord))
    607     (let ((changing-p
    608            (not (eq page (pdf-view-current-page window)))))
    609       (when changing-p
    610         (run-hooks 'pdf-view-before-change-page-hook)
    611         (setf (pdf-view-current-page window) page)
    612         (run-hooks 'pdf-view-change-page-hook))
    613       (when (window-live-p window)
    614         (pdf-view-redisplay window))
    615       (when changing-p
    616         (pdf-view-deactivate-region)
    617         (force-mode-line-update)
    618         (run-hooks 'pdf-view-after-change-page-hook))))
    619   nil)
    620 
    621 (defun pdf-view-next-page (&optional n)
    622   "View the next page in the PDF.
    623 
    624 Optional parameter N moves N pages forward."
    625   (interactive "p")
    626   (pdf-view-goto-page (+ (pdf-view-current-page)
    627                          (or n 1))))
    628 
    629 (defun pdf-view-previous-page (&optional n)
    630   "View the previous page in the PDF.
    631 
    632 Optional parameter N moves N pages backward."
    633   (interactive "p")
    634   (pdf-view-next-page (- (or n 1))))
    635 
    636 (defun pdf-view-next-page-command (&optional n)
    637   "View the next page in the PDF.
    638 
    639 Optional parameter N moves N pages forward.
    640 
    641 This command is a wrapper for `pdf-view-next-page' that will
    642 indicate to the user if they are on the last page and more."
    643   (declare (interactive-only pdf-view-next-page))
    644   (interactive "p")
    645   (unless n (setq n 1))
    646   (when (> (+ (pdf-view-current-page) n)
    647            (pdf-cache-number-of-pages))
    648     (user-error "Last page"))
    649   (when (< (+ (pdf-view-current-page) n) 1)
    650     (user-error "First page"))
    651   (let ((pdf-view-inhibit-redisplay t))
    652     (pdf-view-goto-page
    653      (+ (pdf-view-current-page) n)))
    654   (force-mode-line-update)
    655   (sit-for 0)
    656   (when pdf-view--next-page-timer
    657     (cancel-timer pdf-view--next-page-timer)
    658     (setq pdf-view--next-page-timer nil))
    659   (if (or (not (input-pending-p))
    660           (and (> n 0)
    661                (= (pdf-view-current-page)
    662                   (pdf-cache-number-of-pages)))
    663           (and (< n 0)
    664                (= (pdf-view-current-page) 1)))
    665       (pdf-view-redisplay)
    666     (setq pdf-view--next-page-timer
    667           (run-with-idle-timer 0.001 nil 'pdf-view-redisplay (selected-window)))))
    668 
    669 (defun pdf-view-previous-page-command (&optional n)
    670   "View the previous page in the PDF.
    671 
    672 Optional parameter N moves N pages backward.
    673 
    674 This command is a wrapper for `pdf-view-previous-page'."
    675   (declare (interactive-only pdf-view-previous-page))
    676   (interactive "p")
    677   (with-no-warnings
    678     (pdf-view-next-page-command (- (or n 1)))))
    679 
    680 (defun pdf-view-first-page ()
    681   "View the first page."
    682   (interactive)
    683   (pdf-view-goto-page 1))
    684 
    685 (defun pdf-view-last-page ()
    686   "View the last page."
    687   (interactive)
    688   (pdf-view-goto-page (pdf-cache-number-of-pages)))
    689 
    690 (defun pdf-view-scroll-up-or-next-page (&optional arg)
    691   "Scroll page up ARG lines if possible, else go to the next page.
    692 
    693 When `pdf-view-continuous' is non-nil, scrolling upward at the
    694 bottom edge of the page moves to the next page.  Otherwise, go to
    695 next page only on typing SPC (ARG is nil)."
    696   (interactive "P")
    697   (if (or pdf-view-continuous (null arg))
    698       (let ((hscroll (window-hscroll))
    699             (cur-page (pdf-view-current-page))
    700             (win-scroll (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll))
    701             (img-scroll (image-scroll-up arg)))
    702         (when (or
    703                ;; There is no next line for the image to scroll to
    704                (and img-scroll (= win-scroll img-scroll))
    705                ;; Workaround rounding/off-by-one issues.
    706                (memq pdf-view-display-size
    707                      '(fit-height fit-page)))
    708           (pdf-view-next-page)
    709           (when (/= cur-page (pdf-view-current-page))
    710             (image-bob)
    711             (image-bol 1))
    712           (image-set-window-hscroll hscroll)))
    713     (image-scroll-up arg)))
    714 
    715 (defun pdf-view-scroll-down-or-previous-page (&optional arg)
    716   "Scroll page down ARG lines if possible, else go to the previous page.
    717 
    718 When `pdf-view-continuous' is non-nil, scrolling downward at the
    719 top edge of the page moves to the previous page.  Otherwise, go
    720 to previous page only on typing DEL (ARG is nil)."
    721   (interactive "P")
    722   (if (or pdf-view-continuous (null arg))
    723       (let ((hscroll (window-hscroll))
    724             (cur-page (pdf-view-current-page))
    725             (win-scroll (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll))
    726             (img-scroll (image-scroll-down arg)))
    727         (when (or
    728                ;; There is no previous line for the image to scroll to
    729                (and img-scroll (= win-scroll img-scroll))
    730                ;; Workaround rounding/off-by-one issues.
    731                (memq pdf-view-display-size
    732                      '(fit-height fit-page)))
    733           (pdf-view-previous-page)
    734           (when (/= cur-page (pdf-view-current-page))
    735             (image-eob)
    736             (image-bol 1))
    737           (image-set-window-hscroll hscroll)))
    738     (image-scroll-down arg)))
    739 
    740 (defun pdf-view-next-line-or-next-page (&optional arg)
    741   "Scroll upward by ARG lines if possible, else go to the next page.
    742 
    743 When `pdf-view-continuous' is non-nil, scrolling a line upward
    744 at the bottom edge of the page moves to the next page."
    745   (interactive "p")
    746   (if pdf-view-continuous
    747       (let ((hscroll (window-hscroll))
    748             (cur-page (pdf-view-current-page)))
    749         (when (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll)
    750                  (image-next-line arg))
    751           (pdf-view-next-page)
    752           (when (/= cur-page (pdf-view-current-page))
    753             (image-bob)
    754             (image-bol 1))
    755           (image-set-window-hscroll hscroll)))
    756     (image-next-line 1)))
    757 
    758 (defun pdf-view-previous-line-or-previous-page (&optional arg)
    759   "Scroll downward by ARG lines if possible, else go to the previous page.
    760 
    761 When `pdf-view-continuous' is non-nil, scrolling a line downward
    762 at the top edge of the page moves to the previous page."
    763   (interactive "p")
    764   (if pdf-view-continuous
    765       (let ((hscroll (window-hscroll))
    766             (cur-page (pdf-view-current-page)))
    767         (when (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll)
    768                  (image-previous-line arg))
    769           (pdf-view-previous-page)
    770           (when (/= cur-page (pdf-view-current-page))
    771             (image-eob)
    772             (image-bol 1))
    773           (image-set-window-hscroll hscroll)))
    774     (image-previous-line arg)))
    775 
    776 (defun pdf-view-goto-label (label)
    777   "Go to the page corresponding to LABEL.
    778 
    779 Usually, the label of a document's page is the same as its
    780 displayed page number."
    781   (interactive
    782    (list (let ((labels (pdf-info-pagelabels)))
    783            (completing-read "Goto label: " labels nil t))))
    784   (let ((index (cl-position label (pdf-info-pagelabels) :test 'equal)))
    785     (unless index
    786       (error "No such label: %s" label))
    787     (pdf-view-goto-page (1+ index))))
    788 
    789 (defun pdf-view-center-in-window ()
    790   "Center PDF in window horizontally."
    791   (interactive)
    792   (image-set-window-hscroll
    793    (/ (* (- (car (pdf-view-image-size))
    794             (window-pixel-width))
    795          (window-width))
    796       2 (window-pixel-width))) ; convert from pixel to character width
    797   (pdf-view-redisplay t))
    798 
    799 (defun pdf-view-align-left ()
    800   "Align left edge of pdf with left edge of window."
    801   (interactive)
    802   (image-set-window-hscroll 0)
    803   (pdf-view-redisplay t))
    804 
    805 (defun pdf-view-align-right ()
    806   "Align right edge of pdf with right edge of window."
    807   (interactive)
    808   (image-set-window-hscroll
    809    (/ (* (- (car (pdf-view-image-size))
    810             (window-pixel-width))
    811          (window-width))
    812       (window-pixel-width))) ; convert from pixel to character width
    813   (pdf-view-redisplay t))
    814 
    815 
    816 ;; * ================================================================== *
    817 ;; * Slicing
    818 ;; * ================================================================== *
    819 
    820 (defun pdf-view-set-slice (x y width height &optional window)
    821   "Set the slice of the pages that should be displayed in WINDOW.
    822 
    823 WINDOW defaults to `selected-window' if not provided.
    824 X, Y, WIDTH and HEIGHT should be relative coordinates, i.e. in
    825 \[0;1\].  To reset the slice use `pdf-view-reset-slice'."
    826   (unless (equal (pdf-view-current-slice window)
    827                  (list x y width height))
    828     (setf (pdf-view-current-slice window)
    829           (mapcar (lambda (v)
    830                     (max 0 (min 1 v)))
    831                   (list x y width height)))
    832     (pdf-view-redisplay window)))
    833 
    834 (defun pdf-view-set-slice-using-mouse ()
    835   "Set the slice of the images that should be displayed.
    836 
    837 Set the slice by pressing `mouse-1' at its top-left corner and
    838 dragging it to its bottom-right corner.  See also
    839 `pdf-view-set-slice' and `pdf-view-reset-slice'."
    840   (interactive)
    841   (let ((size (pdf-view-image-size))
    842         x y w h done)
    843     (while (not done)
    844       (let ((e (read-event
    845                 (concat "Press mouse-1 at the top-left corner and "
    846                         "drag it to the bottom-right corner!"))))
    847         (when (eq (car e) 'drag-mouse-1)
    848           (setq x (car (posn-object-x-y (event-start e))))
    849           (setq y (cdr (posn-object-x-y (event-start e))))
    850           (setq w (- (car (posn-object-x-y (event-end e))) x))
    851           (setq h (- (cdr (posn-object-x-y (event-end e))) y))
    852           (setq done t))))
    853     (apply 'pdf-view-set-slice
    854            (pdf-util-scale
    855             (list x y w h)
    856             (cons (/ 1.0 (float (car size)))
    857                   (/ 1.0 (float (cdr size))))))))
    858 
    859 (defun pdf-view-set-slice-from-bounding-box (&optional window)
    860   "Set the slice from the page's bounding-box.
    861 
    862 WINDOW defaults to `selected-window' if not provided.
    863 
    864 The result is that the margins are almost completely cropped,
    865 much more accurate than could be done manually using
    866 `pdf-view-set-slice-using-mouse'.
    867 
    868 See also `pdf-view-bounding-box-margin'."
    869   (interactive)
    870   (let* ((bb (pdf-cache-boundingbox (pdf-view-current-page window)))
    871          (margin (max 0 (or pdf-view-bounding-box-margin 0)))
    872          (slice (list (- (nth 0 bb)
    873                          (/ margin 2.0))
    874                       (- (nth 1 bb)
    875                          (/ margin 2.0))
    876                       (+ (- (nth 2 bb) (nth 0 bb))
    877                          margin)
    878                       (+ (- (nth 3 bb) (nth 1 bb))
    879                          margin))))
    880     (apply 'pdf-view-set-slice
    881            (append slice (and window (list window))))))
    882 
    883 (defun pdf-view-reset-slice (&optional window)
    884   "Reset the current slice and redisplay WINDOW.
    885 
    886 WINDOW defaults to `selected-window' if not provided.
    887 
    888 After calling this function the whole page will be visible
    889 again."
    890   (interactive)
    891   (when (pdf-view-current-slice window)
    892     (setf (pdf-view-current-slice window) nil)
    893     (pdf-view-redisplay window))
    894   nil)
    895 
    896 (define-minor-mode pdf-view-auto-slice-minor-mode
    897   "Automatically slice pages according to their bounding boxes.
    898 
    899 See also `pdf-view-set-slice-from-bounding-box'."
    900   :group 'pdf-view
    901   (pdf-util-assert-pdf-buffer)
    902   (cond
    903    (pdf-view-auto-slice-minor-mode
    904     (dolist (win (get-buffer-window-list nil nil t))
    905       (when (pdf-util-pdf-window-p win)
    906         (pdf-view-set-slice-from-bounding-box win)))
    907     (add-hook 'pdf-view-change-page-hook
    908               'pdf-view-set-slice-from-bounding-box nil t))
    909    (t
    910     (progn (remove-hook 'pdf-view-change-page-hook
    911                         'pdf-view-set-slice-from-bounding-box t)
    912            (pdf-view-reset-slice)))))
    913 
    914 
    915 ;; * ================================================================== *
    916 ;; * Display
    917 ;; * ================================================================== *
    918 
    919 (defun pdf-view-image-type ()
    920   "Return the image type that should be used.
    921 
    922 The return value is either `imagemagick' (if available and wanted
    923 or if png is not available) or `png'.
    924 
    925 Signal an error, if neither `imagemagick' nor `png' is available.
    926 
    927 See also `pdf-view-use-imagemagick'."
    928   (cond ((and pdf-view-use-imagemagick
    929               (fboundp 'imagemagick-types))
    930          'imagemagick)
    931         ((image-type-available-p 'image-io)
    932          'image-io)
    933         ((image-type-available-p 'png)
    934          'png)
    935         ((fboundp 'imagemagick-types)
    936          'imagemagick)
    937         (t
    938          (error "PNG image supported not compiled into Emacs"))))
    939 
    940 (defmacro pdf-view-create-image (data &rest props)
    941   ;; TODO: add DATA and PROPS to docstring.
    942   "Like `create-image', but with set DATA-P and TYPE arguments."
    943   (declare (indent 1) (debug t))
    944   (let ((image-data (make-symbol "data")))
    945     `(let ((,image-data ,data))
    946        (apply #'create-image ,image-data (pdf-view-image-type) t ,@props
    947               (cl-list*
    948                :relief (or pdf-view-image-relief 0)
    949                (when (and (eq (framep-on-display) 'mac)
    950                           (= (pdf-util-frame-scale-factor) 2))
    951                  (list :data-2x ,image-data)))))))
    952 
    953 (defun pdf-view-create-page (page &optional window)
    954   "Create an image of PAGE for display on WINDOW."
    955   (let* ((size (pdf-view-desired-image-size page window))
    956          (data (pdf-cache-renderpage
    957                 page (car size)
    958                 (if pdf-view-use-scaling
    959                     (* 2 (car size))
    960                   (car size))))
    961          (hotspots (pdf-view-apply-hotspot-functions
    962                     window page size)))
    963     (pdf-view-create-image data
    964       :width (car size)
    965       :map hotspots
    966       :pointer 'arrow)))
    967 
    968 (defun pdf-view-image-size (&optional displayed-p window)
    969   ;; TODO: add WINDOW to docstring.
    970   "Return the size in pixel of the current image.
    971 
    972 If DISPLAYED-P is non-nil, return the size of the displayed
    973 image.  These values may be different, if slicing is used."
    974   (if displayed-p
    975       (with-selected-window (or window (selected-window))
    976         (image-display-size
    977          (image-get-display-property) t))
    978     (image-size (pdf-view-current-image window) t)))
    979 
    980 (defun pdf-view-image-offset (&optional window)
    981   ;; TODO: add WINDOW to docstring.
    982   "Return the offset of the current image.
    983 
    984 It is equal to \(LEFT . TOP\) of the current slice in pixel."
    985   (let* ((slice (pdf-view-current-slice window)))
    986     (cond
    987      (slice
    988       (pdf-util-scale-relative-to-pixel
    989        (cons (nth 0 slice) (nth 1 slice))
    990        window))
    991      (t
    992       (cons 0 0)))))
    993 
    994 (defun pdf-view-display-page (page &optional window)
    995   "Display page PAGE in WINDOW."
    996   (setf (pdf-view-window-needs-redisplay window) nil)
    997   (pdf-view-display-image
    998    (pdf-view-create-page page window)
    999    window))
   1000 
   1001 (defun pdf-view-display-image (image &optional window inhibit-slice-p)
   1002   ;; TODO: write documentation!
   1003   (let ((ol (pdf-view-current-overlay window)))
   1004     (when (window-live-p (overlay-get ol 'window))
   1005       (let* ((size (image-size image t))
   1006              (slice (if (not inhibit-slice-p)
   1007                         (pdf-view-current-slice window)))
   1008              (displayed-width (floor
   1009                                (if slice
   1010                                    (* (nth 2 slice)
   1011                                       (car (image-size image)))
   1012                                  (car (image-size image))))))
   1013         (setf (pdf-view-current-image window) image)
   1014         (move-overlay ol (point-min) (point-max))
   1015         ;; In case the window is wider than the image, center the image
   1016         ;; horizontally.
   1017         (overlay-put ol 'before-string
   1018                      (when (> (window-width window)
   1019                               displayed-width)
   1020                        (propertize " " 'display
   1021                                    `(space :align-to
   1022                                            ,(/ (- (window-width window)
   1023                                                   displayed-width) 2)))))
   1024         (overlay-put ol 'display
   1025                      (if slice
   1026                          (list (cons 'slice
   1027                                      (pdf-util-scale slice size 'round))
   1028                                image)
   1029                        image))
   1030         (let* ((win (overlay-get ol 'window))
   1031                (hscroll (image-mode-window-get 'hscroll win))
   1032                (vscroll (image-mode-window-get 'vscroll win)))
   1033           ;; Reset scroll settings, in case they were changed.
   1034           (if hscroll (set-window-hscroll win hscroll))
   1035           (if vscroll (set-window-vscroll
   1036                        win vscroll pdf-view-have-image-mode-pixel-vscroll)))))))
   1037 
   1038 (defun pdf-view-redisplay (&optional window)
   1039   "Redisplay page in WINDOW.
   1040 
   1041 If WINDOW is t, redisplay pages in all windows."
   1042   (unless pdf-view-inhibit-redisplay
   1043     (if (not (eq t window))
   1044         (pdf-view-display-page
   1045          (pdf-view-current-page window)
   1046          window)
   1047       (dolist (win (get-buffer-window-list nil nil t))
   1048         (pdf-view-display-page
   1049          (pdf-view-current-page win)
   1050          win))
   1051       (when (consp image-mode-winprops-alist)
   1052         (dolist (window (mapcar #'car image-mode-winprops-alist))
   1053           (unless (or (not (window-live-p window))
   1054                       (eq (current-buffer)
   1055                           (window-buffer window)))
   1056             (setf (pdf-view-window-needs-redisplay window) t)))))
   1057     (force-mode-line-update)))
   1058 
   1059 (defun pdf-view-redisplay-pages (&rest pages)
   1060   "Redisplay PAGES in all windows."
   1061   (pdf-util-assert-pdf-buffer)
   1062   (dolist (window (get-buffer-window-list nil nil t))
   1063     (when (memq (pdf-view-current-page window)
   1064                 pages)
   1065       (pdf-view-redisplay window))))
   1066 
   1067 (defun pdf-view-maybe-redisplay-resized-windows ()
   1068   "Redisplay some windows needing redisplay."
   1069   (unless (or (numberp pdf-view-display-size)
   1070               (pdf-view-active-region-p)
   1071               (> (minibuffer-depth) 0))
   1072     (dolist (window (get-buffer-window-list nil nil t))
   1073       (let ((stored (pdf-view-current-window-size window))
   1074             (size (cons (window-width window)
   1075                         (window-height window))))
   1076         (unless (equal size stored)
   1077           (setf (pdf-view-current-window-size window) size)
   1078           (unless (or (null stored)
   1079                       (and (eq pdf-view-display-size 'fit-width)
   1080                            (eq (car size) (car stored)))
   1081                       (and (eq pdf-view-display-size 'fit-height)
   1082                            (eq (cdr size) (cdr stored))))
   1083             (pdf-view-redisplay window)))))))
   1084 
   1085 (defun pdf-view-redisplay-some-windows ()
   1086   (pdf-view-maybe-redisplay-resized-windows)
   1087   (dolist (window (get-buffer-window-list nil nil t))
   1088     (when (pdf-view-window-needs-redisplay window)
   1089       (pdf-view-redisplay window))))
   1090 
   1091 (defun pdf-view-new-window-function (winprops)
   1092   ;; TODO: write documentation!
   1093   ;; (message "New window %s for buf %s" (car winprops) (current-buffer))
   1094   (cl-assert (or (eq t (car winprops))
   1095                  (eq (window-buffer (car winprops)) (current-buffer))))
   1096   (let ((ol (image-mode-window-get 'overlay winprops)))
   1097     (if ol
   1098         (progn
   1099           (setq ol (copy-overlay ol))
   1100           ;; `ol' might actually be dead.
   1101           (move-overlay ol (point-min) (point-max)))
   1102       (setq ol (make-overlay (point-min) (point-max) nil t))
   1103       (overlay-put ol 'pdf-view t))
   1104     (overlay-put ol 'window (car winprops))
   1105     (unless (windowp (car winprops))
   1106       ;; It's a pseudo entry.  Let's make sure it's not displayed (the
   1107       ;; `window' property is only effective if its value is a window).
   1108       (cl-assert (eq t (car winprops)))
   1109       (delete-overlay ol))
   1110     (image-mode-window-put 'overlay ol winprops)
   1111     ;; Clean up some overlays.
   1112     (dolist (ov (overlays-in (point-min) (point-max)))
   1113       (when (and (windowp (overlay-get ov 'window))
   1114                  (not (window-live-p (overlay-get ov 'window))))
   1115         (delete-overlay ov)))
   1116     (when (and (windowp (car winprops))
   1117                (null (image-mode-window-get 'image winprops)))
   1118       ;; We're not displaying an image yet, so let's do so.  This
   1119       ;; happens when the buffer is displayed for the first time.
   1120       (with-selected-window (car winprops)
   1121         (pdf-view-goto-page
   1122          (or (image-mode-window-get 'page t) 1))))))
   1123 
   1124 (defun pdf-view-desired-image-size (&optional page window)
   1125   ;; TODO: write documentation!
   1126   (let* ((pagesize (pdf-cache-pagesize
   1127                     (or page (pdf-view-current-page window))))
   1128          (slice (pdf-view-current-slice window))
   1129          (width-scale (/ (/ (float (window-body-width window t))
   1130                             (or (nth 2 slice) 1.0))
   1131                          (float (car pagesize))))
   1132          (height (- (nth 3 (window-inside-pixel-edges window))
   1133                     (nth 1 (window-inside-pixel-edges window))
   1134                     1))
   1135          (height-scale (/ (/ (float height)
   1136                              (or (nth 3 slice) 1.0))
   1137                           (float (cdr pagesize))))
   1138          (scale width-scale))
   1139     (if (numberp pdf-view-display-size)
   1140         (setq scale (float pdf-view-display-size))
   1141       (cl-case pdf-view-display-size
   1142         (fit-page
   1143          (setq scale (min height-scale width-scale)))
   1144         (fit-height
   1145          (setq scale height-scale))
   1146         (t
   1147          (setq scale width-scale))))
   1148     (let ((width (floor (* (car pagesize) scale)))
   1149           (height (floor (* (cdr pagesize) scale))))
   1150       (when (> width (max 1 (or pdf-view-max-image-width width)))
   1151         (setq width pdf-view-max-image-width
   1152               height (* height (/ (float pdf-view-max-image-width) width))))
   1153       (cons (max 1 width) (max 1 height)))))
   1154 
   1155 (defun pdf-view-text-regions-hotspots-function (page size)
   1156   "Return a list of hotspots for text regions on PAGE using SIZE.
   1157 
   1158 This will display a text cursor, when hovering over them."
   1159   (local-set-key [pdf-view-text-region t]
   1160                  'pdf-util-image-map-mouse-event-proxy)
   1161   (mapcar (lambda (region)
   1162             (let ((e (pdf-util-scale region size 'round)))
   1163               `((rect . ((,(nth 0 e) . ,(nth 1 e))
   1164                          . (,(nth 2 e) . ,(nth 3 e))))
   1165                 pdf-view-text-region
   1166                 (pointer text))))
   1167           (pdf-cache-textregions page)))
   1168 
   1169 (define-minor-mode pdf-view-dark-minor-mode
   1170   "Mode for PDF documents with dark background.
   1171 
   1172 This tells the various modes to use their face's dark colors."
   1173   :group 'pdf-view
   1174   (pdf-util-assert-pdf-buffer)
   1175   ;; FIXME: This should really be run in a hook.
   1176   (when (bound-and-true-p pdf-isearch-active-mode)
   1177     (with-no-warnings
   1178       (pdf-isearch-redisplay)
   1179       (pdf-isearch-message
   1180        (if pdf-view-dark-minor-mode "dark mode" "light mode")))))
   1181 
   1182 (define-minor-mode pdf-view-printer-minor-mode
   1183   "Display the PDF as it would be printed."
   1184   :group 'pdf-view
   1185   :lighter " Prn"
   1186   (pdf-util-assert-pdf-buffer)
   1187   (let ((enable (lambda ()
   1188                   (pdf-info-setoptions :render/printed t))))
   1189     (cond
   1190      (pdf-view-printer-minor-mode
   1191       (add-hook 'after-save-hook enable nil t)
   1192       (add-hook 'after-revert-hook enable nil t))
   1193      (t
   1194       (remove-hook 'after-save-hook enable t)
   1195       (remove-hook 'after-revert-hook enable t))))
   1196   (pdf-info-setoptions :render/printed pdf-view-printer-minor-mode)
   1197   (pdf-cache-clear-images)
   1198   (pdf-view-redisplay t))
   1199 
   1200 (define-minor-mode pdf-view-midnight-minor-mode
   1201   "Apply a color-filter appropriate for past midnight reading.
   1202 
   1203 The colors are determined by the variable
   1204 `pdf-view-midnight-colors', which see. "
   1205   :group 'pdf-view
   1206   :lighter " Mid"
   1207   (pdf-util-assert-pdf-buffer)
   1208   ;; FIXME: Maybe these options should be passed stateless to pdf-info-renderpage ?
   1209   (let ((enable (lambda ()
   1210                   (pdf-info-setoptions
   1211                    :render/foreground (or (car pdf-view-midnight-colors) "black")
   1212                    :render/background (or (cdr pdf-view-midnight-colors) "white")
   1213                    :render/usecolors t))))
   1214     (cond
   1215      (pdf-view-midnight-minor-mode
   1216       (add-hook 'after-save-hook enable nil t)
   1217       (add-hook 'after-revert-hook enable nil t)
   1218       (funcall enable))
   1219      (t
   1220       (remove-hook 'after-save-hook enable t)
   1221       (remove-hook 'after-revert-hook enable t)
   1222       (pdf-info-setoptions :render/usecolors nil))))
   1223   (pdf-cache-clear-images)
   1224   (pdf-view-redisplay t))
   1225 
   1226 (defun pdf-view-refresh-themed-buffer (&optional get-theme)
   1227   "Refresh the current buffer to activate applied colors.
   1228 
   1229 When GET-THEME is non-nil, also reset the applied colors to the
   1230 current theme's colors."
   1231   (pdf-util-assert-pdf-buffer)
   1232   (pdf-cache-clear-images)
   1233   (when get-theme
   1234 	(pdf-view-set-theme-background))
   1235   (pdf-view-redisplay t))
   1236 
   1237 (defun pdf-view-set-theme-background ()
   1238   "Set the buffer's color filter to correspond to the current Emacs theme."
   1239   (pdf-util-assert-pdf-buffer)
   1240   (pdf-info-setoptions
   1241    :render/foreground (face-foreground 'default nil)
   1242    :render/background (face-background 'default nil)
   1243    :render/usecolors t))
   1244 
   1245 (define-minor-mode pdf-view-themed-minor-mode
   1246   "Synchronize color filter with the present Emacs theme.
   1247 
   1248 The colors are determined by the `face-foreground' and
   1249 `face-background' of the currently active theme."
   1250   :group 'pdf-view
   1251   :lighter " Thm"
   1252   (pdf-util-assert-pdf-buffer)
   1253   (cond
   1254    (pdf-view-themed-minor-mode
   1255     (add-hook 'after-save-hook #'pdf-view-set-theme-background nil t)
   1256     (add-hook 'after-revert-hook #'pdf-view-set-theme-background nil t))
   1257    (t
   1258     (remove-hook 'after-save-hook #'pdf-view-set-theme-background t)
   1259     (remove-hook 'after-revert-hook #'pdf-view-set-theme-background t)
   1260     (pdf-info-setoptions :render/usecolors nil)))
   1261   (pdf-view-refresh-themed-buffer pdf-view-themed-minor-mode))
   1262 
   1263 (when pdf-view-use-unicode-ligther
   1264   ;; This check uses an implementation detail, which hopefully gets the
   1265   ;; right answer.
   1266   (and (fontp (char-displayable-p ?⎙))
   1267        (setcdr (assq 'pdf-view-printer-minor-mode minor-mode-alist)
   1268                (list " ⎙" )))
   1269   (and (fontp (char-displayable-p ?🌙))
   1270        (setcdr (assq 'pdf-view-midnight-minor-mode minor-mode-alist)
   1271                (list  " 🌙" ))))
   1272 
   1273 
   1274 ;; * ================================================================== *
   1275 ;; * Hotspot handling
   1276 ;; * ================================================================== *
   1277 
   1278 (defun pdf-view-add-hotspot-function (fn &optional layer)
   1279   "Register FN as a hotspot function in the current buffer, using LAYER.
   1280 
   1281 FN will be called in the PDF buffer with the page-number and the
   1282 image size \(WIDTH . HEIGHT\) as arguments.  It should return a
   1283 list of hotspots applicable to the the :map image-property.
   1284 
   1285 LAYER determines the order: Functions in a higher LAYER will
   1286 supersede hotspots in lower ones."
   1287   (push (cons (or layer 0) fn)
   1288         pdf-view--hotspot-functions))
   1289 
   1290 (defun pdf-view-remove-hotspot-function (fn)
   1291   "Unregister FN as a hotspot function in the current buffer."
   1292   (setq pdf-view--hotspot-functions
   1293         (cl-remove fn pdf-view--hotspot-functions
   1294                    :key 'cdr)))
   1295 
   1296 (defun pdf-view-sorted-hotspot-functions ()
   1297   ;; TODO: write documentation!
   1298   (mapcar 'cdr (cl-sort (copy-sequence pdf-view--hotspot-functions)
   1299                         '> :key 'car)))
   1300 
   1301 (defun pdf-view-apply-hotspot-functions (window page image-size)
   1302   ;; TODO: write documentation!
   1303   (unless pdf-view-inhibit-hotspots
   1304     (save-selected-window
   1305       (when window (select-window window 'norecord))
   1306       (apply 'nconc
   1307              (mapcar (lambda (fn)
   1308                        (funcall fn page image-size))
   1309                      (pdf-view-sorted-hotspot-functions))))))
   1310 
   1311 
   1312 ;; * ================================================================== *
   1313 ;; * Region
   1314 ;; * ================================================================== *
   1315 
   1316 (defun pdf-view--push-mark ()
   1317   ;; TODO: write documentation!
   1318   (let (mark-ring)
   1319     (push-mark-command nil))
   1320   (setq deactivate-mark nil))
   1321 
   1322 (defun pdf-view-active-region (&optional deactivate-p)
   1323   "Return the active region, a list of edges.
   1324 
   1325 Deactivate the region if DEACTIVATE-P is non-nil."
   1326   (pdf-view-assert-active-region)
   1327   (prog1
   1328       pdf-view-active-region
   1329     (when deactivate-p
   1330       (pdf-view-deactivate-region))))
   1331 
   1332 (defun pdf-view-deactivate-region ()
   1333   "Deactivate the region."
   1334   (interactive)
   1335   (when pdf-view-active-region
   1336     (setq pdf-view-active-region nil)
   1337     (deactivate-mark)
   1338     (pdf-view-redisplay t)))
   1339 
   1340 (defun pdf-view-mouse-set-region (event &optional allow-extend-p
   1341                                         rectangle-p)
   1342   "Select a region of text using the mouse with mouse event EVENT.
   1343 
   1344 Allow for stacking of regions, if ALLOW-EXTEND-P is non-nil.
   1345 
   1346 Create a rectangular region, if RECTANGLE-P is non-nil.
   1347 
   1348 Stores the region in `pdf-view-active-region'."
   1349   (interactive "@e")
   1350   (setq pdf-view--have-rectangle-region rectangle-p)
   1351   (unless (and (eventp event)
   1352                (mouse-event-p event))
   1353     (signal 'wrong-type-argument (list 'mouse-event-p event)))
   1354   (unless (and allow-extend-p
   1355                (or (null (get this-command 'pdf-view-region-window))
   1356                    (equal (get this-command 'pdf-view-region-window)
   1357                           (selected-window))))
   1358     (pdf-view-deactivate-region))
   1359   (put this-command 'pdf-view-region-window
   1360        (selected-window))
   1361   (let* ((window (selected-window))
   1362          (pos (event-start event))
   1363          (begin-inside-image-p t)
   1364          (begin (if (posn-image pos)
   1365                     (posn-object-x-y pos)
   1366                   (setq begin-inside-image-p nil)
   1367                   (posn-x-y pos)))
   1368          (abs-begin (posn-x-y pos))
   1369          pdf-view-continuous
   1370          region)
   1371     (when (pdf-util-track-mouse-dragging (event 0.05)
   1372             (let* ((pos (event-start event))
   1373                    (end (posn-object-x-y pos))
   1374                    (end-inside-image-p
   1375                     (and (eq window (posn-window pos))
   1376                          (posn-image pos))))
   1377               (when (or end-inside-image-p
   1378                         begin-inside-image-p)
   1379                 (cond
   1380                  ((and end-inside-image-p
   1381                        (not begin-inside-image-p))
   1382                   ;; Started selection outside the image, setup begin.
   1383                   (let* ((xy (posn-x-y pos))
   1384                          (dxy (cons (- (car xy) (car begin))
   1385                                     (- (cdr xy) (cdr begin))))
   1386                          (size (pdf-view-image-size t)))
   1387                     (setq begin (cons (max 0 (min (car size)
   1388                                                   (- (car end) (car dxy))))
   1389                                       (max 0 (min (cdr size)
   1390                                                   (- (cdr end) (cdr dxy)))))
   1391                           ;; Store absolute position for later.
   1392                           abs-begin (cons (- (car xy)
   1393                                              (- (car end)
   1394                                                 (car begin)))
   1395                                           (- (cdr xy)
   1396                                              (- (cdr end)
   1397                                                 (cdr begin))))
   1398                           begin-inside-image-p t)))
   1399                  ((and begin-inside-image-p
   1400                        (not end-inside-image-p))
   1401                   ;; Moved outside the image, setup end.
   1402                   (let* ((xy (posn-x-y pos))
   1403                          (dxy (cons (- (car xy) (car abs-begin))
   1404                                     (- (cdr xy) (cdr abs-begin))))
   1405                          (size (pdf-view-image-size t)))
   1406                     (setq end (cons (max 0 (min (car size)
   1407                                                 (+ (car begin) (car dxy))))
   1408                                     (max 0 (min (cdr size)
   1409                                                 (+ (cdr begin) (cdr dxy)))))))))
   1410                 (let ((iregion (if rectangle-p
   1411                                    (list (min (car begin) (car end))
   1412                                          (min (cdr begin) (cdr end))
   1413                                          (max (car begin) (car end))
   1414                                          (max (cdr begin) (cdr end)))
   1415                                  (list (car begin) (cdr begin)
   1416                                        (car end) (cdr end)))))
   1417                   (setq region
   1418                         (pdf-util-scale-pixel-to-relative iregion))
   1419                   (pdf-view-display-region
   1420                    (cons region pdf-view-active-region)
   1421                    rectangle-p)
   1422                   (pdf-util-scroll-to-edges iregion)))))
   1423       (setq pdf-view-active-region
   1424             (append pdf-view-active-region
   1425                     (list region)))
   1426       (pdf-view--push-mark))))
   1427 
   1428 (defun pdf-view-mouse-extend-region (event)
   1429   "Extend the currently active region with mouse event EVENT."
   1430   (interactive "@e")
   1431   (pdf-view-mouse-set-region
   1432    event t pdf-view--have-rectangle-region))
   1433 
   1434 (defun pdf-view-mouse-set-region-rectangle (event)
   1435   "Like `pdf-view-mouse-set-region' but displays as a rectangle.
   1436 
   1437 EVENT is the mouse event.
   1438 
   1439 This is more useful for commands like
   1440 `pdf-view-extract-region-image'."
   1441   (interactive "@e")
   1442   (pdf-view-mouse-set-region event nil t))
   1443 
   1444 (defun pdf-view-display-region (&optional region rectangle-p)
   1445   ;; TODO: write documentation!
   1446   (unless region
   1447     (pdf-view-assert-active-region)
   1448     (setq region pdf-view-active-region))
   1449   (let ((colors (pdf-util-face-colors
   1450                  (if rectangle-p 'pdf-view-rectangle 'pdf-view-region)
   1451                  (bound-and-true-p pdf-view-dark-minor-mode)))
   1452         (page (pdf-view-current-page))
   1453         (width (car (pdf-view-image-size))))
   1454     (pdf-view-display-image
   1455      (pdf-view-create-image
   1456          (if rectangle-p
   1457              (pdf-info-renderpage-highlight
   1458               page width nil
   1459               `(,(car colors) ,(cdr colors) 0.35 ,@region))
   1460            (pdf-info-renderpage-text-regions
   1461             page width nil nil
   1462             `(,(car colors) ,(cdr colors) ,@region)))
   1463        :width width))))
   1464 
   1465 (defun pdf-view-kill-ring-save ()
   1466   "Copy the region to the `kill-ring'."
   1467   (interactive)
   1468   (pdf-view-assert-active-region)
   1469   (let* ((txt (pdf-view-active-region-text)))
   1470     (pdf-view-deactivate-region)
   1471     (kill-new (mapconcat 'identity txt "\n"))))
   1472 
   1473 (defun pdf-view-mark-whole-page ()
   1474   "Mark the whole page."
   1475   (interactive)
   1476   (pdf-view-deactivate-region)
   1477   (setq pdf-view-active-region
   1478         (list (list 0 0 1 1)))
   1479   (pdf-view--push-mark)
   1480   (pdf-view-display-region))
   1481 
   1482 (defun pdf-view-active-region-text ()
   1483   "Return the text of the active region as a list of strings."
   1484   (pdf-view-assert-active-region)
   1485   (mapcar
   1486    (apply-partially 'pdf-info-gettext (pdf-view-current-page))
   1487    pdf-view-active-region))
   1488 
   1489 (defun pdf-view-extract-region-image (regions &optional page size
   1490                                               output-buffer no-display-p)
   1491   ;; TODO: what is "resp."? Avoid contractions.
   1492   "Create a PNG image of REGIONS.
   1493 
   1494 REGIONS should have the same form as `pdf-view-active-region',
   1495 which see.  PAGE and SIZE are the page resp. base-size of the
   1496 image from which the image-regions will be created; they default
   1497 to `pdf-view-current-page' resp. `pdf-view-image-size'.
   1498 
   1499 Put the image in OUTPUT-BUFFER, defaulting to \"*PDF region
   1500 image*\" and display it, unless NO-DISPLAY-P is non-nil.
   1501 
   1502 In case of multiple regions, the resulting image is constructed
   1503 by joining them horizontally.  For this operation (and this only)
   1504 the `convert' program is used."
   1505 
   1506   (interactive
   1507    (list (if (pdf-view-active-region-p)
   1508              (pdf-view-active-region t)
   1509            '((0 0 1 1)))))
   1510   (unless page
   1511     (setq page (pdf-view-current-page)))
   1512   (unless size
   1513     (setq size (pdf-view-image-size)))
   1514   (unless output-buffer
   1515     (setq output-buffer (get-buffer-create "*PDF image*")))
   1516   (let* ((images (mapcar (lambda (edges)
   1517                            (let ((file (make-temp-file "pdf-view"))
   1518                                  (coding-system-for-write 'binary))
   1519                              (write-region
   1520                               (pdf-info-renderpage
   1521                                page (car size)
   1522                                :crop-to edges)
   1523                               nil file nil 'no-message)
   1524                              file))
   1525                          regions))
   1526          result)
   1527     (unwind-protect
   1528         (progn
   1529           (if (= (length images) 1)
   1530               (setq result (car images))
   1531             (setq result (make-temp-file "pdf-view"))
   1532             ;; Join the images horizontally with a gap of 10 pixel.
   1533             (pdf-util-convert
   1534              "-noop" ;; workaround limitations of this function
   1535              result
   1536              :commands `("("
   1537                          ,@images
   1538                          "-background" "white"
   1539                          "-splice" "0x10+0+0"
   1540                          ")"
   1541                          "-gravity" "Center"
   1542                          "-append"
   1543                          "+gravity"
   1544                          "-chop" "0x10+0+0")
   1545              :apply '((0 0 0 0))))
   1546           (with-current-buffer output-buffer
   1547             (let ((inhibit-read-only t))
   1548               (erase-buffer))
   1549             (set-buffer-multibyte nil)
   1550             (insert-file-contents-literally result)
   1551             (image-mode)
   1552             (unless no-display-p
   1553               (pop-to-buffer (current-buffer)))))
   1554       (dolist (f (cons result images))
   1555         (when (file-exists-p f)
   1556           (delete-file f))))))
   1557 
   1558 ;; * ================================================================== *
   1559 ;; * Bookmark + Register Integration
   1560 ;; * ================================================================== *
   1561 
   1562 (defun pdf-view-bookmark-make-record  (&optional no-page no-slice no-size no-origin)
   1563   ;; TODO: add NO-PAGE, NO-SLICE, NO-SIZE, NO-ORIGIN to the docstring.
   1564   "Create a bookmark PDF record.
   1565 
   1566 The optional, boolean args exclude certain attributes."
   1567   (let ((displayed-p (eq (current-buffer)
   1568                          (window-buffer))))
   1569     (cons (buffer-name)
   1570           (append (bookmark-make-record-default nil t 1)
   1571                   `(,(unless no-page
   1572                        (cons 'page (pdf-view-current-page)))
   1573                     ,(unless no-slice
   1574                        (cons 'slice (and displayed-p
   1575                                          (pdf-view-current-slice))))
   1576                     ,(unless no-size
   1577                        (cons 'size pdf-view-display-size))
   1578                     ,(unless no-origin
   1579                        (cons 'origin
   1580                              (and displayed-p
   1581                                   (let ((edges (pdf-util-image-displayed-edges nil t)))
   1582                                     (pdf-util-scale-pixel-to-relative
   1583                                      (cons (car edges) (cadr edges)) nil t)))))
   1584                     (handler . pdf-view-bookmark-jump-handler))))))
   1585 
   1586 ;;;###autoload
   1587 (defun pdf-view-bookmark-jump-handler (bmk)
   1588   "The bookmark handler-function interface for bookmark BMK.
   1589 
   1590 See also `pdf-view-bookmark-make-record'."
   1591   (let ((page (bookmark-prop-get bmk 'page))
   1592         (slice (bookmark-prop-get bmk 'slice))
   1593         (size (bookmark-prop-get bmk 'size))
   1594         (origin (bookmark-prop-get bmk 'origin))
   1595         (file (bookmark-prop-get bmk 'filename))
   1596         (show-fn-sym (make-symbol "pdf-view-bookmark-after-jump-hook")))
   1597     (fset show-fn-sym
   1598           (lambda ()
   1599             (remove-hook 'bookmark-after-jump-hook show-fn-sym)
   1600             (unless (derived-mode-p 'pdf-view-mode)
   1601               (pdf-view-mode))
   1602             (with-selected-window
   1603                 (or (get-buffer-window (current-buffer) 0)
   1604                     (selected-window))
   1605               (when size
   1606                 (setq-local pdf-view-display-size size))
   1607               (when slice
   1608                 (apply 'pdf-view-set-slice slice))
   1609               (when (numberp page)
   1610                 (pdf-view-goto-page page))
   1611               (when origin
   1612                 (let ((size (pdf-view-image-size t)))
   1613                   (image-set-window-hscroll
   1614                    (round (/ (* (car origin) (car size))
   1615                              (frame-char-width))))
   1616                   (image-set-window-vscroll
   1617                    (round (/ (* (cdr origin) (cdr size))
   1618                              (if pdf-view-have-image-mode-pixel-vscroll
   1619                                  1
   1620                                (frame-char-height))))))))))
   1621     (add-hook 'bookmark-after-jump-hook show-fn-sym)
   1622     (set-buffer (or (find-buffer-visiting file)
   1623                     (find-file-noselect file)))))
   1624 
   1625 (defun pdf-view-bookmark-jump (bmk)
   1626   "Switch to bookmark BMK.
   1627 
   1628 This function is like `bookmark-jump', but it always uses the
   1629 selected window for display and does not run any hooks.  Also, it
   1630 works only with bookmarks created by
   1631 `pdf-view-bookmark-make-record'."
   1632 
   1633   (let* ((file (bookmark-prop-get bmk 'filename))
   1634          (buffer (or (find-buffer-visiting file)
   1635                      (find-file-noselect file))))
   1636     (switch-to-buffer buffer)
   1637     (let (bookmark-after-jump-hook)
   1638       (pdf-view-bookmark-jump-handler bmk)
   1639       (run-hooks 'bookmark-after-jump-hook))))
   1640 
   1641 (defun pdf-view-registerv-make ()
   1642   "Create a PDF register entry of the current position."
   1643   (registerv-make
   1644    (pdf-view-bookmark-make-record nil t t)
   1645    :print-func 'pdf-view-registerv-print-func
   1646    :jump-func 'pdf-view-bookmark-jump
   1647    :insert-func (lambda (bmk)
   1648                   (insert (format "%S" bmk)))))
   1649 
   1650 (defun pdf-view-registerv-print-func (bmk)
   1651   "Print a textual representation of bookmark BMK.
   1652 
   1653 This function is used as the `:print-func' property with
   1654 `registerv-make'."
   1655   (let* ((file (bookmark-prop-get bmk 'filename))
   1656          (buffer (find-buffer-visiting file))
   1657          (page (bookmark-prop-get bmk 'page))
   1658          (origin (bookmark-prop-get bmk 'origin)))
   1659     (princ (format "PDF position: %s, page %d, %d%%"
   1660                    (if buffer
   1661                        (buffer-name buffer)
   1662                      file)
   1663                    (or page 1)
   1664                    (if origin
   1665                        (round (* 100 (cdr origin)))
   1666                      0)))))
   1667 
   1668 (defmacro pdf-view-with-register-alist (&rest body)
   1669   "Setup the proper binding for `register-alist' in BODY.
   1670 
   1671 This macro may not work as desired when it is nested.  See also
   1672 `pdf-view-use-dedicated-register'."
   1673   (declare (debug t) (indent 0))
   1674   (let ((dedicated-p (make-symbol "dedicated-p")))
   1675     `(let* ((,dedicated-p pdf-view-use-dedicated-register)
   1676             (register-alist
   1677              (if ,dedicated-p
   1678                  pdf-view-register-alist
   1679                register-alist)))
   1680        (unwind-protect
   1681            (progn ,@body)
   1682          (when ,dedicated-p
   1683            (setq pdf-view-register-alist register-alist))))))
   1684 
   1685 (defun pdf-view-position-to-register (register)
   1686   "Store current PDF position in register REGISTER.
   1687 
   1688 See also `point-to-register'."
   1689   (interactive
   1690    (list (pdf-view-with-register-alist
   1691            (register-read-with-preview "Position to register: "))))
   1692   (pdf-view-with-register-alist
   1693     (set-register register (pdf-view-registerv-make))))
   1694 
   1695 (defun pdf-view-jump-to-register (register &optional delete return-register)
   1696   ;; TODO: add RETURN-REGISTER to the docstring.
   1697   "Move point to a position stored in a REGISTER.
   1698 
   1699 Optional parameter DELETE is defined as in `jump-to-register'."
   1700   (interactive
   1701    (pdf-view-with-register-alist
   1702      (list
   1703       (register-read-with-preview "Jump to register: ")
   1704       current-prefix-arg
   1705       (and (or pdf-view-use-dedicated-register
   1706                (local-variable-p 'register-alist))
   1707            (characterp last-command-event)
   1708            last-command-event))))
   1709   (pdf-view-with-register-alist
   1710     (let ((return-pos (and return-register
   1711                            (pdf-view-registerv-make))))
   1712       (jump-to-register register delete)
   1713       (when return-register
   1714         (set-register return-register return-pos)))))
   1715 
   1716 (provide 'pdf-view)
   1717 
   1718 ;;; pdf-view.el ends here