dotemacs

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

pdf-view.el (68399B)


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