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