diff options
Diffstat (limited to 'elpa/pdf-tools-20200512.1524/pdf-view.el')
-rw-r--r-- | elpa/pdf-tools-20200512.1524/pdf-view.el | 1676 |
1 files changed, 0 insertions, 1676 deletions
diff --git a/elpa/pdf-tools-20200512.1524/pdf-view.el b/elpa/pdf-tools-20200512.1524/pdf-view.el deleted file mode 100644 index 433f5da..0000000 --- a/elpa/pdf-tools-20200512.1524/pdf-view.el +++ /dev/null @@ -1,1676 +0,0 @@ -;;; pdf-view.el --- View PDF documents. -*- lexical-binding:t -*- - -;; Copyright (C) 2013 Andreas Politz - -;; Author: Andreas Politz <politza@fh-trier.de> -;; Keywords: files, doc-view, pdf - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; Functions related to viewing PDF documents. - -;;; Code: - -(require 'image-mode) -(require 'pdf-util) -(require 'pdf-info) -(require 'pdf-cache) -(require 'jka-compr) -(require 'bookmark) -(require 'password-cache) - -(declare-function cua-copy-region "cua-base") - - -;; * ================================================================== * -;; * Customizations -;; * ================================================================== * - -(defgroup pdf-view nil - "View PDF documents." - :group 'pdf-tools) - -(defcustom pdf-view-display-size 'fit-width - "The desired size of displayed pages. - -This may be one of `fit-height', `fit-width', `fit-page' or a -number as a scale factor applied to the document's size. Any -other value behaves like `fit-width'." - :group 'pdf-view - :type '(choice number - (const fit-height) - (const fit-width) - (const fit-page))) - -(make-variable-buffer-local 'pdf-view-display-size) - -(defcustom pdf-view-resize-factor 1.25 - "Fractional amount of resizing of one resize command." - :group 'pdf-view - :type 'number) - -(defcustom pdf-view-continuous t - "In Continuous mode reaching the page edge advances to next/previous page. - -When non-nil, scrolling a line upward at the bottom edge of the page -moves to the next page, and scrolling a line downward at the top edge -of the page moves to the previous page." - :type 'boolean - :group 'pdf-view) - -(defcustom pdf-view-bounding-box-margin 0.05 - "Fractional margin used for slicing with the bounding-box." - :group 'pdf-view - :type 'number) - -(defcustom pdf-view-use-imagemagick nil - "Whether imagemagick should be used for rendering. - -This variable has no effect, if imagemagick was not compiled into -Emacs or if imagemagick is the only way to display PNG images. -FIXME: Explain dis-/advantages of imagemagick and png." - :group 'pdf-view - :type 'boolean) - -(defcustom pdf-view-use-scaling nil - "Whether images should be allowed to be scaled for rendering. - -This variable affects both the reuse of higher-resolution images -as lower-resolution ones by down-scaling the image. As well as -the rendering of higher-resolution for high-resolution displays, -if available. - -It has no effect, unless either the imagemagick or image-io -image-format is available." - :group 'pdf-view - :type 'boolean) - -(defface pdf-view-region - '((((background dark)) (:inherit region)) - (((background light)) (:inherit region))) - "Face used to determine the colors of the region." - :group 'pdf-view - :group 'pdf-tools-faces) - -(defface pdf-view-rectangle - '((((background dark)) (:inherit highlight)) - (((background light)) (:inherit highlight))) - "Face used to determine the colors of the highlighted rectangle." - :group 'pdf-view - :group 'pdf-tools-faces) - -(defcustom pdf-view-midnight-colors '("#839496" . "#002b36" ) - "Colors used when `pdf-view-midnight-minor-mode' is activated. - -This should be a cons \(FOREGROUND . BACKGROUND\) of colors." - :group 'pdf-view - :type '(cons (color :tag "Foreground") - (color :tag "Background"))) - -(defcustom pdf-view-change-page-hook nil - "Hook run after changing to another page, but before displaying it. - -See also `pdf-view-before-change-page-hook' and -`pdf-view-after-change-page-hook'." - :group 'pdf-view - :type 'hook) - -(defcustom pdf-view-before-change-page-hook nil - "Hook run before changing to another page. - -See also `pdf-view-change-page-hook' and -`pdf-view-after-change-page-hook'." - :group 'pdf-view - :type 'hook) - -(defcustom pdf-view-after-change-page-hook nil - "Hook run after changing to and displaying another page. - -See also `pdf-view-change-page-hook' and -`pdf-view-before-change-page-hook'." - :group 'pdf-view - :type 'hook) - -(defcustom pdf-view-use-dedicated-register t - "Whether to use dedicated register for PDF positions. - -If this is non-nil, the commands `pdf-view-position-to-register' -and `pdf-view-jump-to-register' use the buffer-local variable -`pdf-view-register-alist' to store resp. retrieve marked -positions. Otherwise the common variable `register-alist' is -used." - :group 'pdf-view - :type 'boolean) - -(defcustom pdf-view-image-relief 0 - "Add a shadow rectangle around the page's image. - -See :relief property in Info node `(elisp) Image Descriptors'." - :group 'pdf-view - :type '(integer :tag "Pixel") - :link '(info-link "(elisp) Image Descriptors")) - -(defcustom pdf-view-max-image-width 4800 - "Maximum width of any image displayed in pixel." - :group 'pdf-view - :type '(integer :tag "Pixel")) - -(defcustom pdf-view-use-unicode-ligther t - "Whether to use unicode symbols in the mode-line - -On some systems finding a font which supports those symbols can -take some time. If you don't want to spend that time waiting and -don't care for a nicer looking mode-line, set this variable to -nil. - -Note, that this option has only an effect when this library is -loaded." - :group 'pdf-view - :type 'boolean) - -(defcustom pdf-view-incompatible-modes - '(linum-mode linum-relative-mode helm-linum-relative-mode - nlinum-mode nlinum-hl-mode nlinum-relative-mode yalinum-mode) - "A list of modes incompatible with `pdf-view-mode'. - -Issue a warning, if one of them is active in a PDF buffer." - :group 'pdf-view - :type '(repeat symbol)) - - -;; * ================================================================== * -;; * Internal variables and macros -;; * ================================================================== * - -(defvar-local pdf-view-active-region nil - "The active region as a list of edges. - -Edge values are relative coordinates.") - -(defvar-local pdf-view--have-rectangle-region nil - "Non-nil if the region is currently rendered as a rectangle. - -This variable is set in `pdf-view-mouse-set-region' and used in -`pdf-view-mouse-extend-region' to determine the right choice -regarding display of the region in the later function.") - -(defvar-local pdf-view--buffer-file-name nil - "Local copy of remote file or nil.") - -(defvar-local pdf-view--server-file-name nil - "The servers notion of this buffer's filename.") - -(defvar-local pdf-view--next-page-timer nil - "Timer used in `pdf-view-next-page-command'.") - -(defvar-local pdf-view--hotspot-functions nil - "Alist of hotspot functions.") - -(defvar-local pdf-view-register-alist nil - "Local, dedicated register for PDF positions.") - -(defmacro pdf-view-current-page (&optional window) - ;;TODO: write documentation! - `(image-mode-window-get 'page ,window)) - -(defmacro pdf-view-current-overlay (&optional window) - ;;TODO: write documentation! - `(image-mode-window-get 'overlay ,window)) - -(defmacro pdf-view-current-image (&optional window) - ;;TODO: write documentation! - `(image-mode-window-get 'image ,window)) - -(defmacro pdf-view-current-slice (&optional window) - ;;TODO: write documentation! - `(image-mode-window-get 'slice ,window)) - -(defmacro pdf-view-current-window-size (&optional window) - ;;TODO: write documentation! - `(image-mode-window-get 'window-size ,window)) - -(defmacro pdf-view-window-needs-redisplay (&optional window) - `(image-mode-window-get 'needs-redisplay ,window)) - -(defun pdf-view-current-pagelabel (&optional window) - (nth (1- (pdf-view-current-page window)) (pdf-info-pagelabels))) - -(defun pdf-view-active-region-p nil - "Return t if there are active regions." - (not (null pdf-view-active-region))) - -(defmacro pdf-view-assert-active-region () - "Signal an error if there are no active regions." - `(unless (pdf-view-active-region-p) - (error "The region is not active"))) - -(defconst pdf-view-have-image-mode-pixel-vscroll - (>= emacs-major-version 27) - "Whether image-mode scrolls vertically by pixels.") - - -;; * ================================================================== * -;; * Major Mode -;; * ================================================================== * - -(defvar pdf-view-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map image-mode-map) - (define-key map (kbd "Q") 'kill-this-buffer) - ;; Navigation in the document - (define-key map (kbd "n") 'pdf-view-next-page-command) - (define-key map (kbd "p") 'pdf-view-previous-page-command) - (define-key map (kbd "<next>") 'forward-page) - (define-key map (kbd "<prior>") 'backward-page) - (define-key map [remap forward-page] 'pdf-view-next-page-command) - (define-key map [remap backward-page] 'pdf-view-previous-page-command) - (define-key map (kbd "SPC") 'pdf-view-scroll-up-or-next-page) - (define-key map (kbd "S-SPC") 'pdf-view-scroll-down-or-previous-page) - (define-key map (kbd "DEL") 'pdf-view-scroll-down-or-previous-page) - (define-key map (kbd "C-n") 'pdf-view-next-line-or-next-page) - (define-key map (kbd "<down>") 'pdf-view-next-line-or-next-page) - (define-key map (kbd "C-p") 'pdf-view-previous-line-or-previous-page) - (define-key map (kbd "<up>") 'pdf-view-previous-line-or-previous-page) - (define-key map (kbd "M-<") 'pdf-view-first-page) - (define-key map (kbd "M->") 'pdf-view-last-page) - (define-key map [remap goto-line] 'pdf-view-goto-page) - (define-key map (kbd "M-g l") 'pdf-view-goto-label) - (define-key map (kbd "RET") 'image-next-line) - ;; Zoom in/out. - (define-key map "+" 'pdf-view-enlarge) - (define-key map "=" 'pdf-view-enlarge) - (define-key map "-" 'pdf-view-shrink) - (define-key map "0" 'pdf-view-scale-reset) - ;; Fit the image to the window - (define-key map "W" 'pdf-view-fit-width-to-window) - (define-key map "H" 'pdf-view-fit-height-to-window) - (define-key map "P" 'pdf-view-fit-page-to-window) - ;; Slicing the image - (define-key map (kbd "s m") 'pdf-view-set-slice-using-mouse) - (define-key map (kbd "s b") 'pdf-view-set-slice-from-bounding-box) - (define-key map (kbd "s r") 'pdf-view-reset-slice) - ;; Reconvert - (define-key map (kbd "C-c C-c") 'doc-view-mode) - (define-key map (kbd "g") 'revert-buffer) - (define-key map (kbd "r") 'revert-buffer) - ;; Region - (define-key map [down-mouse-1] 'pdf-view-mouse-set-region) - (define-key map [M-down-mouse-1] 'pdf-view-mouse-set-region-rectangle) - (define-key map [C-down-mouse-1] 'pdf-view-mouse-extend-region) - (define-key map [remap kill-region] 'pdf-view-kill-ring-save) - (define-key map [remap kill-ring-save] 'pdf-view-kill-ring-save) - (define-key map [remap mark-whole-buffer] 'pdf-view-mark-whole-page) - ;; Other - (define-key map (kbd "C-c C-d") 'pdf-view-dark-minor-mode) - (define-key map (kbd "m") 'pdf-view-position-to-register) - (define-key map (kbd "'") 'pdf-view-jump-to-register) - - (define-key map (kbd "C-c C-i") 'pdf-view-extract-region-image) - ;; Rendering - (define-key map (kbd "C-c C-r m") 'pdf-view-midnight-minor-mode) - (define-key map (kbd "C-c C-r p") 'pdf-view-printer-minor-mode) - map) - "Keymap used by `pdf-view-mode' when displaying a doc as a set of images.") - -(define-derived-mode pdf-view-mode special-mode "PDFView" - "Major mode in PDF buffers. - -PDFView Mode is an Emacs PDF viewer. It displays PDF files as -PNG images in Emacs buffers." - :group 'pdf-view - :abbrev-table nil - :syntax-table nil - ;; Setup a local copy for remote files. - (when (and (or jka-compr-really-do-compress - (let ((file-name-handler-alist nil)) - (not (and buffer-file-name - (file-readable-p buffer-file-name))))) - (pdf-tools-pdf-buffer-p)) - (let ((tempfile (pdf-util-make-temp-file - (concat (if buffer-file-name - (file-name-nondirectory - buffer-file-name) - (buffer-name)) - "-")))) - (write-region nil nil tempfile nil 'no-message) - (setq-local pdf-view--buffer-file-name tempfile))) - ;; Decryption needs to be done before any other function calls into - ;; pdf-info.el (e.g. from the mode-line during redisplay during - ;; waiting for process output). - (pdf-view-decrypt-document) - - ;; Setup scroll functions - (if (boundp 'mwheel-scroll-up-function) ; not --without-x build - (setq-local mwheel-scroll-up-function - #'pdf-view-scroll-up-or-next-page)) - (if (boundp 'mwheel-scroll-down-function) - (setq-local mwheel-scroll-down-function - #'pdf-view-scroll-down-or-previous-page)) - - ;; Clearing overlays - (add-hook 'change-major-mode-hook - (lambda () - (remove-overlays (point-min) (point-max) 'pdf-view t)) - nil t) - (remove-overlays (point-min) (point-max) 'pdf-view t) ;Just in case. - - ;; Setup other local variables. - (setq-local mode-line-position - '(" P" (:eval (number-to-string (pdf-view-current-page))) - ;; Avoid errors during redisplay. - "/" (:eval (or (ignore-errors - (number-to-string (pdf-cache-number-of-pages))) - "???")))) - (setq-local auto-hscroll-mode nil) - (setq-local pdf-view--server-file-name (pdf-view-buffer-file-name)) - ;; High values of scroll-conservatively seem to trigger - ;; some display bug in xdisp.c:try_scrolling . - (setq-local scroll-conservatively 0) - (setq-local cursor-type nil) - (setq-local buffer-read-only t) - (setq-local view-read-only nil) - (setq-local bookmark-make-record-function - 'pdf-view-bookmark-make-record) - (setq-local revert-buffer-function #'pdf-view-revert-buffer) - ;; No auto-save at the moment. - (setq-local buffer-auto-save-file-name nil) - ;; Disable image rescaling. - (when (boundp 'image-scaling-factor) - (setq-local image-scaling-factor 1)) - ;; No undo at the moment. - (unless buffer-undo-list - (buffer-disable-undo)) - ;; Enable transient-mark-mode, so region deactivation when quitting - ;; will work. - (setq-local transient-mark-mode t) - ;; In Emacs >= 24.4, `cua-copy-region' should have been advised when - ;; loading pdf-view.el so as to make it work with - ;; pdf-view-mode. Disable cua-mode if that is not the case. - ;; FIXME: cua-mode is a global minor-mode, but setting cua-mode to - ;; nil seems to do the trick. - (when (and (bound-and-true-p cua-mode) - (version< emacs-version "24.4")) - (setq-local cua-mode nil)) - - (add-hook 'window-configuration-change-hook - 'pdf-view-redisplay-some-windows nil t) - (add-hook 'deactivate-mark-hook 'pdf-view-deactivate-region nil t) - (add-hook 'write-contents-functions - 'pdf-view--write-contents-function nil t) - (add-hook 'kill-buffer-hook 'pdf-view-close-document nil t) - (pdf-view-add-hotspot-function - 'pdf-view-text-regions-hotspots-function -9) - - ;; Keep track of display info - (add-hook 'image-mode-new-window-functions - 'pdf-view-new-window-function nil t) - (image-mode-setup-winprops) - - ;; Issue a warning in the future about incompatible modes. - (run-with-timer 1 nil (lambda (buffer) - (when (buffer-live-p buffer) - (pdf-view-check-incompatible-modes buffer))) - (current-buffer))) - -(unless (version< emacs-version "24.4") - (defun cua-copy-region--pdf-view-advice (&rest _) - "If the current buffer is in `pdf-view' mode, call -`pdf-view-kill-ring-save'." - (when (eq major-mode 'pdf-view-mode) - (pdf-view-kill-ring-save) - t)) - - (advice-add 'cua-copy-region - :before-until - #'cua-copy-region--pdf-view-advice)) - -(defun pdf-view-check-incompatible-modes (&optional buffer) - "Check BUFFER for incompatible modes, maybe issue a warning." - (with-current-buffer (or buffer (current-buffer)) - (let ((modes - (cl-remove-if-not - (lambda (mode) (and (symbolp mode) - (boundp mode) - (symbol-value mode))) - pdf-view-incompatible-modes))) - (when modes - (display-warning - 'pdf-view - (format "These modes are incompatible with `pdf-view-mode', - please deactivate them (or customize pdf-view-incompatible-modes): %s" - (mapconcat #'symbol-name modes ","))))))) - -(defun pdf-view-decrypt-document () - "Read a password, if the document is encrypted and open it." - (interactive) - (when (pdf-info-encrypted-p) - (let ((prompt (format "Enter password for `%s': " - (abbreviate-file-name - (buffer-file-name)))) - (key (concat "/pdf-tools" (buffer-file-name))) - (i 3) - password) - (while (and (> i 0) - (pdf-info-encrypted-p)) - (setq i (1- i)) - (setq password (password-read prompt key)) - (setq prompt "Invalid password, try again: ") - (ignore-errors (pdf-info-open nil password))) - (pdf-info-open nil password) - (password-cache-add key password))) - nil) - -(defun pdf-view-buffer-file-name () - "Return the local filename of the PDF in the current buffer. - -This may be different from variable `buffer-file-name' when -operating on a local copy of a remote file." - (or pdf-view--buffer-file-name - (buffer-file-name))) - -(defun pdf-view--write-contents-function () - "Function for `write-contents-functions' to save the buffer." - (when (pdf-util-pdf-buffer-p) - (let ((tempfile (pdf-info-save pdf-view--server-file-name))) - (unwind-protect - (progn - ;; Order matters here: We need to first copy the new - ;; content (tempfile) to the PDF, and then close the PDF. - ;; Since while closing the file (and freeing its resources - ;; in the process), it may be immediately reopened due to - ;; redisplay happening inside the pdf-info-close function - ;; (while waiting for a response from the process.). - (copy-file tempfile (buffer-file-name) t) - (pdf-info-close pdf-view--server-file-name) - - (when pdf-view--buffer-file-name - (copy-file tempfile pdf-view--buffer-file-name t)) - (clear-visited-file-modtime) - (set-buffer-modified-p nil) - (setq pdf-view--server-file-name - (pdf-view-buffer-file-name)) - t) - (when (file-exists-p tempfile) - (delete-file tempfile)))))) - -(defun pdf-view-revert-buffer (&optional ignore-auto noconfirm) - "Revert buffer while preserving current modes. - -Optional parameters IGNORE-AUTO and NOCONFIRM are defined as in -`revert-buffer'." - (interactive (list (not current-prefix-arg))) - ;; Bind to default so that we can use pdf-view-revert-buffer as - ;; revert-buffer-function. A binding of nil is needed in Emacs 24.3, but in - ;; later versions the semantics that nil means the default function should - ;; not relied upon. - (let ((revert-buffer-function (when (fboundp #'revert-buffer--default) - #'revert-buffer--default)) - (after-revert-hook - (cons #'pdf-info-close - after-revert-hook))) - (prog1 - (revert-buffer ignore-auto noconfirm 'preserve-modes) - (pdf-view-redisplay t)))) - -(defun pdf-view-close-document () - "Return immediately after closing document. - -This function always succeeds. See also `pdf-info-close', which -does not return immediately." - (when (pdf-info-running-p) - (let ((pdf-info-asynchronous 'ignore)) - (ignore-errors - (pdf-info-close))))) - - -;; * ================================================================== * -;; * Scaling -;; * ================================================================== * - -(defun pdf-view-fit-page-to-window () - "Fit PDF to window. - -Choose the larger of PDF's height and width, and fits that -dimension to window." - (interactive) - (setq pdf-view-display-size 'fit-page) - (image-set-window-vscroll 0) - (image-set-window-hscroll 0) - (pdf-view-redisplay t)) - -(defun pdf-view-fit-height-to-window () - "Fit PDF height to window." - (interactive) - (setq pdf-view-display-size 'fit-height) - (image-set-window-vscroll 0) - (pdf-view-redisplay t)) - -(defun pdf-view-fit-width-to-window () - "Fit PDF size to window." - (interactive) - (setq pdf-view-display-size 'fit-width) - (image-set-window-hscroll 0) - (pdf-view-redisplay t)) - -(defun pdf-view-enlarge (factor) - "Enlarge PDF by FACTOR. - -When called interactively, uses the value of -`pdf-view-resize-factor'. - -For example, (pdf-view-enlarge 1.25) increases size by 25%." - (interactive - (list (float pdf-view-resize-factor))) - (let* ((size (pdf-view-image-size)) - (pagesize (pdf-cache-pagesize - (pdf-view-current-page))) - (scale (/ (float (car size)) - (float (car pagesize))))) - (setq pdf-view-display-size - (* factor scale)) - (pdf-view-redisplay t))) - -(defun pdf-view-shrink (factor) - "Shrink PDF by FACTOR. - -When called interactively, uses the value of -`pdf-view-resize-factor'. - -For example, (pdf-view-shrink 1.25) decreases size by 20%." - (interactive - (list (float pdf-view-resize-factor))) - (pdf-view-enlarge (/ 1.0 factor))) - -(defun pdf-view-scale-reset () - "Reset PDF to its default set." - (interactive) - (setq pdf-view-display-size 1.0) - (pdf-view-redisplay t)) - - - -;; * ================================================================== * -;; * Moving by pages and scrolling -;; * ================================================================== * - -(defun pdf-view-goto-page (page &optional window) - "Go to PAGE in PDF. - -If optional parameter WINDOW, go to PAGE in all `pdf-view' -windows." - (interactive - (list (if current-prefix-arg - (prefix-numeric-value current-prefix-arg) - (read-number "Page: ")))) - (unless (and (>= page 1) - (<= page (pdf-cache-number-of-pages))) - (error "No such page: %d" page)) - (unless window - (setq window - (if (pdf-util-pdf-window-p) - (selected-window) - t))) - (save-selected-window - ;; Select the window for the hooks below. - (when (window-live-p window) - (select-window window)) - (let ((changing-p - (not (eq page (pdf-view-current-page window))))) - (when changing-p - (run-hooks 'pdf-view-before-change-page-hook) - (setf (pdf-view-current-page window) page) - (run-hooks 'pdf-view-change-page-hook)) - (when (window-live-p window) - (pdf-view-redisplay window)) - (when changing-p - (pdf-view-deactivate-region) - (force-mode-line-update) - (run-hooks 'pdf-view-after-change-page-hook)))) - nil) - -(defun pdf-view-next-page (&optional n) - "View the next page in the PDF. - -Optional parameter N moves N pages forward." - (interactive "p") - (pdf-view-goto-page (+ (pdf-view-current-page) - (or n 1)))) - -(defun pdf-view-previous-page (&optional n) - "View the previous page in the PDF. - -Optional parameter N moves N pages backward." - (interactive "p") - (pdf-view-next-page (- (or n 1)))) - -(defun pdf-view-next-page-command (&optional n) - "View the next page in the PDF. - -Optional parameter N moves N pages forward. - -This command is a wrapper for `pdf-view-next-page' that will -indicate to the user if they are on the last page and more." - (declare (interactive-only pdf-view-next-page)) - (interactive "p") - (unless n (setq n 1)) - (when (> (+ (pdf-view-current-page) n) - (pdf-cache-number-of-pages)) - (user-error "Last page")) - (when (< (+ (pdf-view-current-page) n) 1) - (user-error "First page")) - (let ((pdf-view-inhibit-redisplay t)) - (pdf-view-goto-page - (+ (pdf-view-current-page) n))) - (force-mode-line-update) - (sit-for 0) - (when pdf-view--next-page-timer - (cancel-timer pdf-view--next-page-timer) - (setq pdf-view--next-page-timer nil)) - (if (or (not (input-pending-p)) - (and (> n 0) - (= (pdf-view-current-page) - (pdf-cache-number-of-pages))) - (and (< n 0) - (= (pdf-view-current-page) 1))) - (pdf-view-redisplay) - (setq pdf-view--next-page-timer - (run-with-idle-timer 0.001 nil 'pdf-view-redisplay (selected-window))))) - -(defun pdf-view-previous-page-command (&optional n) - "View the previous page in the PDF. - -Optional parameter N moves N pages backward. - -This command is a wrapper for `pdf-view-previous-page'." - (declare (interactive-only pdf-view-previous-page)) - (interactive "p") - (with-no-warnings - (pdf-view-next-page-command (- (or n 1))))) - -(defun pdf-view-first-page () - "View the first page." - (interactive) - (pdf-view-goto-page 1)) - -(defun pdf-view-last-page () - "View the last page." - (interactive) - (pdf-view-goto-page (pdf-cache-number-of-pages))) - -(defun pdf-view-scroll-up-or-next-page (&optional arg) - "Scroll page up ARG lines if possible, else go to the next page. - -When `pdf-view-continuous' is non-nil, scrolling upward at the -bottom edge of the page moves to the next page. Otherwise, go to -next page only on typing SPC (ARG is nil)." - (interactive "P") - (if (or pdf-view-continuous (null arg)) - (let ((hscroll (window-hscroll)) - (cur-page (pdf-view-current-page))) - (when (or (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll) - (image-scroll-up arg)) - ;; Workaround rounding/off-by-one issues. - (memq pdf-view-display-size - '(fit-height fit-page))) - (pdf-view-next-page) - (when (/= cur-page (pdf-view-current-page)) - (image-bob) - (image-bol 1)) - (image-set-window-hscroll hscroll))) - (image-scroll-up arg))) - -(defun pdf-view-scroll-down-or-previous-page (&optional arg) - "Scroll page down ARG lines if possible, else go to the previous page. - -When `pdf-view-continuous' is non-nil, scrolling downward at the -top edge of the page moves to the previous page. Otherwise, go -to previous page only on typing DEL (ARG is nil)." - (interactive "P") - (if (or pdf-view-continuous (null arg)) - (let ((hscroll (window-hscroll)) - (cur-page (pdf-view-current-page))) - (when (or (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll) - (image-scroll-down arg)) - ;; Workaround rounding/off-by-one issues. - (memq pdf-view-display-size - '(fit-height fit-page))) - (pdf-view-previous-page) - (when (/= cur-page (pdf-view-current-page)) - (image-eob) - (image-bol 1)) - (image-set-window-hscroll hscroll))) - (image-scroll-down arg))) - -(defun pdf-view-next-line-or-next-page (&optional arg) - "Scroll upward by ARG lines if possible, else go to the next page. - -When `pdf-view-continuous' is non-nil, scrolling a line upward -at the bottom edge of the page moves to the next page." - (interactive "p") - (if pdf-view-continuous - (let ((hscroll (window-hscroll)) - (cur-page (pdf-view-current-page))) - (when (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll) - (image-next-line arg)) - (pdf-view-next-page) - (when (/= cur-page (pdf-view-current-page)) - (image-bob) - (image-bol 1)) - (image-set-window-hscroll hscroll))) - (image-next-line 1))) - -(defun pdf-view-previous-line-or-previous-page (&optional arg) - "Scroll downward by ARG lines if possible, else go to the previous page. - -When `pdf-view-continuous' is non-nil, scrolling a line downward -at the top edge of the page moves to the previous page." - (interactive "p") - (if pdf-view-continuous - (let ((hscroll (window-hscroll)) - (cur-page (pdf-view-current-page))) - (when (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll) - (image-previous-line arg)) - (pdf-view-previous-page) - (when (/= cur-page (pdf-view-current-page)) - (image-eob) - (image-bol 1)) - (image-set-window-hscroll hscroll))) - (image-previous-line arg))) - -(defun pdf-view-goto-label (label) - "Go to the page corresponding to LABEL. - -Usually, the label of a document's page is the same as its -displayed page number." - (interactive - (list (let ((labels (pdf-info-pagelabels))) - (completing-read "Goto label: " labels nil t)))) - (let ((index (cl-position label (pdf-info-pagelabels) :test 'equal))) - (unless index - (error "No such label: %s" label)) - (pdf-view-goto-page (1+ index)))) - - -;; * ================================================================== * -;; * Slicing -;; * ================================================================== * - -(defun pdf-view-set-slice (x y width height &optional window) - ;; TODO: add WINDOW to docstring. - "Set the slice of the pages that should be displayed. - -X, Y, WIDTH and HEIGHT should be relative coordinates, i.e. in -\[0;1\]. To reset the slice use `pdf-view-reset-slice'." - (unless (equal (pdf-view-current-slice window) - (list x y width height)) - (setf (pdf-view-current-slice window) - (mapcar (lambda (v) - (max 0 (min 1 v))) - (list x y width height))) - (pdf-view-redisplay window))) - -(defun pdf-view-set-slice-using-mouse () - "Set the slice of the images that should be displayed. - -Set the slice by pressing `mouse-1' at its top-left corner and -dragging it to its bottom-right corner. See also -`pdf-view-set-slice' and `pdf-view-reset-slice'." - (interactive) - (let ((size (pdf-view-image-size)) - x y w h done) - (while (not done) - (let ((e (read-event - (concat "Press mouse-1 at the top-left corner and " - "drag it to the bottom-right corner!")))) - (when (eq (car e) 'drag-mouse-1) - (setq x (car (posn-object-x-y (event-start e)))) - (setq y (cdr (posn-object-x-y (event-start e)))) - (setq w (- (car (posn-object-x-y (event-end e))) x)) - (setq h (- (cdr (posn-object-x-y (event-end e))) y)) - (setq done t)))) - (apply 'pdf-view-set-slice - (pdf-util-scale - (list x y w h) - (cons (/ 1.0 (float (car size))) - (/ 1.0 (float (cdr size)))))))) - -(defun pdf-view-set-slice-from-bounding-box (&optional window) - ;; TODO: add WINDOW to docstring. - "Set the slice from the page's bounding-box. - -The result is that the margins are almost completely cropped, -much more accurate than could be done manually using -`pdf-view-set-slice-using-mouse'. - -See also `pdf-view-bounding-box-margin'." - (interactive) - (let* ((bb (pdf-cache-boundingbox (pdf-view-current-page window))) - (margin (max 0 (or pdf-view-bounding-box-margin 0))) - (slice (list (- (nth 0 bb) - (/ margin 2.0)) - (- (nth 1 bb) - (/ margin 2.0)) - (+ (- (nth 2 bb) (nth 0 bb)) - margin) - (+ (- (nth 3 bb) (nth 1 bb)) - margin)))) - (apply 'pdf-view-set-slice - (append slice (and window (list window)))))) - -(defun pdf-view-reset-slice (&optional window) - ;; TODO: add WINDOW to doctring. - "Reset the current slice. - -After calling this function the whole page will be visible -again." - (interactive) - (when (pdf-view-current-slice window) - (setf (pdf-view-current-slice window) nil) - (pdf-view-redisplay window)) - nil) - -(define-minor-mode pdf-view-auto-slice-minor-mode - "Automatically slice pages according to their bounding boxes. - -See also `pdf-view-set-slice-from-bounding-box'." - nil nil nil - (pdf-util-assert-pdf-buffer) - (cond - (pdf-view-auto-slice-minor-mode - (dolist (win (get-buffer-window-list nil nil t)) - (when (pdf-util-pdf-window-p win) - (pdf-view-set-slice-from-bounding-box win))) - (add-hook 'pdf-view-change-page-hook - 'pdf-view-set-slice-from-bounding-box nil t)) - (t - (remove-hook 'pdf-view-change-page-hook - 'pdf-view-set-slice-from-bounding-box t)))) - - -;; * ================================================================== * -;; * Display -;; * ================================================================== * - -(defvar pdf-view-inhibit-redisplay nil) -(defvar pdf-view-inhibit-hotspots nil) - -(defun pdf-view-image-type () - "Return the image type that should be used. - -The return value is either `imagemagick' (if available and wanted -or if png is not available) or `png'. - -Signal an error, if neither `imagemagick' nor `png' is available. - -See also `pdf-view-use-imagemagick'." - (cond ((and pdf-view-use-imagemagick - (fboundp 'imagemagick-types)) - 'imagemagick) - ((image-type-available-p 'image-io) - 'image-io) - ((image-type-available-p 'png) - 'png) - ((fboundp 'imagemagick-types) - 'imagemagick) - (t - (error "PNG image supported not compiled into Emacs")))) - -(defun pdf-view-use-scaling-p () - "Return t if scaling should be used." - (and (memq (pdf-view-image-type) - '(imagemagick image-io)) - pdf-view-use-scaling)) - -(defmacro pdf-view-create-image (data &rest props) - ;; TODO: add DATA and PROPS to docstring. - "Like `create-image', but with set DATA-P and TYPE arguments." - (declare (indent 1) (debug t)) - (let ((image-data (make-symbol "data"))) - `(let ((,image-data ,data)) - (apply #'create-image ,image-data (pdf-view-image-type) t ,@props - (cl-list* - :relief (or pdf-view-image-relief 0) - (when (and (eq (framep-on-display) 'mac) - (= (pdf-util-frame-scale-factor) 2)) - (list :data-2x ,image-data))))))) - -(defun pdf-view-create-page (page &optional window) - "Create an image of PAGE for display on WINDOW." - (let* ((size (pdf-view-desired-image-size page window)) - (data (pdf-cache-renderpage - page (car size) - (if (not (pdf-view-use-scaling-p)) - (car size) - (* 2 (car size))))) - (hotspots (pdf-view-apply-hotspot-functions - window page size))) - (pdf-view-create-image data - :width (car size) - :map hotspots - :pointer 'arrow))) - -(defun pdf-view-image-size (&optional displayed-p window) - ;; TODO: add WINDOW to docstring. - "Return the size in pixel of the current image. - -If DISPLAYED-P is non-nil, return the size of the displayed -image. These values may be different, if slicing is used." - (if displayed-p - (with-selected-window (or window (selected-window)) - (image-display-size - (image-get-display-property) t)) - (image-size (pdf-view-current-image window) t))) - -(defun pdf-view-image-offset (&optional window) - ;; TODO: add WINDOW to docstring. - "Return the offset of the current image. - -It is equal to \(LEFT . TOP\) of the current slice in pixel." - (let* ((slice (pdf-view-current-slice window))) - (cond - (slice - (pdf-util-scale-relative-to-pixel - (cons (nth 0 slice) (nth 1 slice)) - window)) - (t - (cons 0 0))))) - -(defun pdf-view-display-page (page &optional window) - "Display page PAGE in WINDOW." - (setf (pdf-view-window-needs-redisplay window) nil) - (pdf-view-display-image - (pdf-view-create-page page window) - window)) - -(defun pdf-view-display-image (image &optional window inhibit-slice-p) - ;; TODO: write documentation! - (let ((ol (pdf-view-current-overlay window))) - (when (window-live-p (overlay-get ol 'window)) - (let* ((size (image-size image t)) - (slice (if (not inhibit-slice-p) - (pdf-view-current-slice window))) - (displayed-width (floor - (if slice - (* (nth 2 slice) - (car (image-size image))) - (car (image-size image)))))) - (setf (pdf-view-current-image window) image) - (move-overlay ol (point-min) (point-max)) - ;; In case the window is wider than the image, center the image - ;; horizontally. - (overlay-put ol 'before-string - (when (> (window-width window) - displayed-width) - (propertize " " 'display - `(space :align-to - ,(/ (- (window-width window) - displayed-width) 2))))) - (overlay-put ol 'display - (if slice - (list (cons 'slice - (pdf-util-scale slice size 'round)) - image) - image)) - (let* ((win (overlay-get ol 'window)) - (hscroll (image-mode-window-get 'hscroll win)) - (vscroll (image-mode-window-get 'vscroll win))) - ;; Reset scroll settings, in case they were changed. - (if hscroll (set-window-hscroll win hscroll)) - (if vscroll (set-window-vscroll - win vscroll pdf-view-have-image-mode-pixel-vscroll))))))) - -(defun pdf-view-redisplay (&optional window) - "Redisplay page in WINDOW. - -If WINDOW is t, redisplay pages in all windows." - (unless pdf-view-inhibit-redisplay - (if (not (eq t window)) - (pdf-view-display-page - (pdf-view-current-page window) - window) - (dolist (win (get-buffer-window-list nil nil t)) - (pdf-view-display-page - (pdf-view-current-page win) - win)) - (when (consp image-mode-winprops-alist) - (dolist (window (mapcar #'car image-mode-winprops-alist)) - (unless (or (not (window-live-p window)) - (eq (current-buffer) - (window-buffer window))) - (setf (pdf-view-window-needs-redisplay window) t))))) - (force-mode-line-update))) - -(defun pdf-view-redisplay-pages (&rest pages) - "Redisplay PAGES in all windows." - (pdf-util-assert-pdf-buffer) - (dolist (window (get-buffer-window-list nil nil t)) - (when (memq (pdf-view-current-page window) - pages) - (pdf-view-redisplay window)))) - -(defun pdf-view-maybe-redisplay-resized-windows () - "Redisplay some windows needing redisplay." - (unless (or (numberp pdf-view-display-size) - (pdf-view-active-region-p) - (> (minibuffer-depth) 0)) - (dolist (window (get-buffer-window-list nil nil t)) - (let ((stored (pdf-view-current-window-size window)) - (size (cons (window-width window) - (window-height window)))) - (unless (equal size stored) - (setf (pdf-view-current-window-size window) size) - (unless (or (null stored) - (and (eq pdf-view-display-size 'fit-width) - (eq (car size) (car stored))) - (and (eq pdf-view-display-size 'fit-height) - (eq (cdr size) (cdr stored)))) - (pdf-view-redisplay window))))))) - -(defun pdf-view-redisplay-some-windows () - (pdf-view-maybe-redisplay-resized-windows) - (dolist (window (get-buffer-window-list nil nil t)) - (when (pdf-view-window-needs-redisplay window) - (pdf-view-redisplay window)))) - -(defun pdf-view-new-window-function (winprops) - ;; TODO: write documentation! - ;; (message "New window %s for buf %s" (car winprops) (current-buffer)) - (cl-assert (or (eq t (car winprops)) - (eq (window-buffer (car winprops)) (current-buffer)))) - (let ((ol (image-mode-window-get 'overlay winprops))) - (if ol - (progn - (setq ol (copy-overlay ol)) - ;; `ol' might actually be dead. - (move-overlay ol (point-min) (point-max))) - (setq ol (make-overlay (point-min) (point-max) nil t)) - (overlay-put ol 'pdf-view t)) - (overlay-put ol 'window (car winprops)) - (unless (windowp (car winprops)) - ;; It's a pseudo entry. Let's make sure it's not displayed (the - ;; `window' property is only effective if its value is a window). - (cl-assert (eq t (car winprops))) - (delete-overlay ol)) - (image-mode-window-put 'overlay ol winprops) - ;; Clean up some overlays. - (dolist (ov (overlays-in (point-min) (point-max))) - (when (and (windowp (overlay-get ov 'window)) - (not (window-live-p (overlay-get ov 'window)))) - (delete-overlay ov))) - (when (and (windowp (car winprops)) - (null (image-mode-window-get 'image winprops))) - ;; We're not displaying an image yet, so let's do so. This - ;; happens when the buffer is displayed for the first time. - (with-selected-window (car winprops) - (pdf-view-goto-page - (or (image-mode-window-get 'page t) 1)))))) - -(defun pdf-view-desired-image-size (&optional page window) - ;; TODO: write documentation! - (let* ((pagesize (pdf-cache-pagesize - (or page (pdf-view-current-page window)))) - (slice (pdf-view-current-slice window)) - (width-scale (/ (/ (float (pdf-util-window-pixel-width window)) - (or (nth 2 slice) 1.0)) - (float (car pagesize)))) - (height (- (nth 3 (window-inside-pixel-edges window)) - (nth 1 (window-inside-pixel-edges window)) - 1)) - (height-scale (/ (/ (float height) - (or (nth 3 slice) 1.0)) - (float (cdr pagesize)))) - (scale width-scale)) - (if (numberp pdf-view-display-size) - (setq scale (float pdf-view-display-size)) - (cl-case pdf-view-display-size - (fit-page - (setq scale (min height-scale width-scale))) - (fit-height - (setq scale height-scale)) - (t - (setq scale width-scale)))) - (let ((width (floor (* (car pagesize) scale))) - (height (floor (* (cdr pagesize) scale)))) - (when (> width (max 1 (or pdf-view-max-image-width width))) - (setq width pdf-view-max-image-width - height (* height (/ (float pdf-view-max-image-width) width)))) - (cons (max 1 width) (max 1 height))))) - -(defun pdf-view-text-regions-hotspots-function (page size) - "Return a list of hotspots for text regions on PAGE using SIZE. - -This will display a text cursor, when hovering over them." - (local-set-key [pdf-view-text-region t] - 'pdf-util-image-map-mouse-event-proxy) - (mapcar (lambda (region) - (let ((e (pdf-util-scale region size 'round))) - `((rect . ((,(nth 0 e) . ,(nth 1 e)) - . (,(nth 2 e) . ,(nth 3 e)))) - pdf-view-text-region - (pointer text)))) - (pdf-cache-textregions page))) - -(define-minor-mode pdf-view-dark-minor-mode - "Mode for PDF documents with dark background. - -This tells the various modes to use their face's dark colors." - nil nil nil - (pdf-util-assert-pdf-buffer) - ;; FIXME: This should really be run in a hook. - (when (bound-and-true-p pdf-isearch-active-mode) - (with-no-warnings - (pdf-isearch-redisplay) - (pdf-isearch-message - (if pdf-view-dark-minor-mode "dark mode" "light mode"))))) - -(define-minor-mode pdf-view-printer-minor-mode - "Display the PDF as it would be printed." - nil " Prn" nil - (pdf-util-assert-pdf-buffer) - (let ((enable (lambda () - (pdf-info-setoptions :render/printed t)))) - (cond - (pdf-view-printer-minor-mode - (add-hook 'after-save-hook enable nil t) - (add-hook 'after-revert-hook enable nil t)) - (t - (remove-hook 'after-save-hook enable t) - (remove-hook 'after-revert-hook enable t)))) - (pdf-info-setoptions :render/printed pdf-view-printer-minor-mode) - (pdf-cache-clear-images) - (pdf-view-redisplay t)) - -(define-minor-mode pdf-view-midnight-minor-mode - "Apply a color-filter appropriate for past midnight reading. - -The colors are determined by the variable -`pdf-view-midnight-colors', which see. " - - nil " Mid" nil - (pdf-util-assert-pdf-buffer) - ;; FIXME: Maybe these options should be passed stateless to pdf-info-renderpage ? - (let ((enable (lambda () - (pdf-info-setoptions - :render/foreground (or (car pdf-view-midnight-colors) "black") - :render/background (or (cdr pdf-view-midnight-colors) "white") - :render/usecolors t)))) - (cond - (pdf-view-midnight-minor-mode - (add-hook 'after-save-hook enable nil t) - (add-hook 'after-revert-hook enable nil t) - (funcall enable)) - (t - (remove-hook 'after-save-hook enable t) - (remove-hook 'after-revert-hook enable t) - (pdf-info-setoptions :render/usecolors nil)))) - (pdf-cache-clear-images) - (pdf-view-redisplay t)) - -(when pdf-view-use-unicode-ligther - ;; This check uses an implementation detail, which hopefully gets the - ;; right answer. - (and (fontp (char-displayable-p ?⎙)) - (setcdr (assq 'pdf-view-printer-minor-mode minor-mode-alist) - (list " ⎙" ))) - (and (fontp (char-displayable-p ?🌙)) - (setcdr (assq 'pdf-view-midnight-minor-mode minor-mode-alist) - (list " 🌙" )))) - - -;; * ================================================================== * -;; * Hotspot handling -;; * ================================================================== * - -(defun pdf-view-add-hotspot-function (fn &optional layer) - "Register FN as a hotspot function in the current buffer, using LAYER. - -FN will be called in the PDF buffer with the page-number and the -image size \(WIDTH . HEIGHT\) as arguments. It should return a -list of hotspots applicable to the the :map image-property. - -LAYER determines the order: Functions in a higher LAYER will -supersede hotspots in lower ones." - (push (cons (or layer 0) fn) - pdf-view--hotspot-functions)) - -(defun pdf-view-remove-hotspot-function (fn) - "Unregister FN as a hotspot function in the current buffer." - (setq pdf-view--hotspot-functions - (cl-remove fn pdf-view--hotspot-functions - :key 'cdr))) - -(defun pdf-view-sorted-hotspot-functions () - ;; TODO: write documentation! - (mapcar 'cdr (cl-sort (copy-sequence pdf-view--hotspot-functions) - '> :key 'car))) - -(defun pdf-view-apply-hotspot-functions (window page image-size) - ;; TODO: write documentation! - (unless pdf-view-inhibit-hotspots - (save-selected-window - (when window (select-window window)) - (apply 'nconc - (mapcar (lambda (fn) - (funcall fn page image-size)) - (pdf-view-sorted-hotspot-functions)))))) - - -;; * ================================================================== * -;; * Region -;; * ================================================================== * - -(defun pdf-view--push-mark () - ;; TODO: write documentation! - (let (mark-ring) - (push-mark-command nil)) - (setq deactivate-mark nil)) - -(defun pdf-view-active-region (&optional deactivate-p) - "Return the active region, a list of edges. - -Deactivate the region if DEACTIVATE-P is non-nil." - (pdf-view-assert-active-region) - (prog1 - pdf-view-active-region - (when deactivate-p - (pdf-view-deactivate-region)))) - -(defun pdf-view-deactivate-region () - "Deactivate the region." - (interactive) - (when pdf-view-active-region - (setq pdf-view-active-region nil) - (deactivate-mark) - (pdf-view-redisplay t))) - -(defun pdf-view-mouse-set-region (event &optional allow-extend-p - rectangle-p) - "Select a region of text using the mouse with mouse event EVENT. - -Allow for stacking of regions, if ALLOW-EXTEND-P is non-nil. - -Create a rectangular region, if RECTANGLE-P is non-nil. - -Stores the region in `pdf-view-active-region'." - (interactive "@e") - (setq pdf-view--have-rectangle-region rectangle-p) - (unless (and (eventp event) - (mouse-event-p event)) - (signal 'wrong-type-argument (list 'mouse-event-p event))) - (unless (and allow-extend-p - (or (null (get this-command 'pdf-view-region-window)) - (equal (get this-command 'pdf-view-region-window) - (selected-window)))) - (pdf-view-deactivate-region)) - (put this-command 'pdf-view-region-window - (selected-window)) - (let* ((window (selected-window)) - (pos (event-start event)) - (begin-inside-image-p t) - (begin (if (posn-image pos) - (posn-object-x-y pos) - (setq begin-inside-image-p nil) - (posn-x-y pos))) - (abs-begin (posn-x-y pos)) - pdf-view-continuous - region) - (when (pdf-util-track-mouse-dragging (event 0.05) - (let* ((pos (event-start event)) - (end (posn-object-x-y pos)) - (end-inside-image-p - (and (eq window (posn-window pos)) - (posn-image pos)))) - (when (or end-inside-image-p - begin-inside-image-p) - (cond - ((and end-inside-image-p - (not begin-inside-image-p)) - ;; Started selection outside the image, setup begin. - (let* ((xy (posn-x-y pos)) - (dxy (cons (- (car xy) (car begin)) - (- (cdr xy) (cdr begin)))) - (size (pdf-view-image-size t))) - (setq begin (cons (max 0 (min (car size) - (- (car end) (car dxy)))) - (max 0 (min (cdr size) - (- (cdr end) (cdr dxy))))) - ;; Store absolute position for later. - abs-begin (cons (- (car xy) - (- (car end) - (car begin))) - (- (cdr xy) - (- (cdr end) - (cdr begin)))) - begin-inside-image-p t))) - ((and begin-inside-image-p - (not end-inside-image-p)) - ;; Moved outside the image, setup end. - (let* ((xy (posn-x-y pos)) - (dxy (cons (- (car xy) (car abs-begin)) - (- (cdr xy) (cdr abs-begin)))) - (size (pdf-view-image-size t))) - (setq end (cons (max 0 (min (car size) - (+ (car begin) (car dxy)))) - (max 0 (min (cdr size) - (+ (cdr begin) (cdr dxy))))))))) - (let ((iregion (if rectangle-p - (list (min (car begin) (car end)) - (min (cdr begin) (cdr end)) - (max (car begin) (car end)) - (max (cdr begin) (cdr end))) - (list (car begin) (cdr begin) - (car end) (cdr end))))) - (setq region - (pdf-util-scale-pixel-to-relative iregion)) - (pdf-view-display-region - (cons region pdf-view-active-region) - rectangle-p) - (pdf-util-scroll-to-edges iregion))))) - (setq pdf-view-active-region - (append pdf-view-active-region - (list region))) - (pdf-view--push-mark)))) - -(defun pdf-view-mouse-extend-region (event) - "Extend the currently active region with mouse event EVENT." - (interactive "@e") - (pdf-view-mouse-set-region - event t pdf-view--have-rectangle-region)) - -(defun pdf-view-mouse-set-region-rectangle (event) - "Like `pdf-view-mouse-set-region' but displays as a rectangle. - -EVENT is the mouse event. - -This is more useful for commands like -`pdf-view-extract-region-image'." - (interactive "@e") - (pdf-view-mouse-set-region event nil t)) - -(defun pdf-view-display-region (&optional region rectangle-p) - ;; TODO: write documentation! - (unless region - (pdf-view-assert-active-region) - (setq region pdf-view-active-region)) - (let ((colors (pdf-util-face-colors - (if rectangle-p 'pdf-view-rectangle 'pdf-view-region) - (bound-and-true-p pdf-view-dark-minor-mode))) - (page (pdf-view-current-page)) - (width (car (pdf-view-image-size)))) - (pdf-view-display-image - (pdf-view-create-image - (if rectangle-p - (pdf-info-renderpage-highlight - page width nil - `(,(car colors) ,(cdr colors) 0.35 ,@region)) - (pdf-info-renderpage-text-regions - page width nil nil - `(,(car colors) ,(cdr colors) ,@region))))))) - -(defun pdf-view-kill-ring-save () - "Copy the region to the `kill-ring'." - (interactive) - (pdf-view-assert-active-region) - (let* ((txt (pdf-view-active-region-text))) - (pdf-view-deactivate-region) - (kill-new (mapconcat 'identity txt "\n")))) - -(defun pdf-view-mark-whole-page () - "Mark the whole page." - (interactive) - (pdf-view-deactivate-region) - (setq pdf-view-active-region - (list (list 0 0 1 1))) - (pdf-view--push-mark) - (pdf-view-display-region)) - -(defun pdf-view-active-region-text () - "Return the text of the active region as a list of strings." - (pdf-view-assert-active-region) - (mapcar - (apply-partially 'pdf-info-gettext (pdf-view-current-page)) - pdf-view-active-region)) - -(defun pdf-view-extract-region-image (regions &optional page size - output-buffer no-display-p) - ;; TODO: what is "resp."? Avoid contractions. - "Create a PNG image of REGIONS. - -REGIONS should have the same form as `pdf-view-active-region', -which see. PAGE and SIZE are the page resp. base-size of the -image from which the image-regions will be created; they default -to `pdf-view-current-page' resp. `pdf-view-image-size'. - -Put the image in OUTPUT-BUFFER, defaulting to \"*PDF region -image*\" and display it, unless NO-DISPLAY-P is non-nil. - -In case of multiple regions, the resulting image is constructed -by joining them horizontally. For this operation (and this only) -the `convert' program is used." - - (interactive - (list (if (pdf-view-active-region-p) - (pdf-view-active-region t) - '((0 0 1 1))))) - (unless page - (setq page (pdf-view-current-page))) - (unless size - (setq size (pdf-view-image-size))) - (unless output-buffer - (setq output-buffer (get-buffer-create "*PDF image*"))) - (let* ((images (mapcar (lambda (edges) - (let ((file (make-temp-file "pdf-view")) - (coding-system-for-write 'binary)) - (write-region - (pdf-info-renderpage - page (car size) - :crop-to edges) - nil file nil 'no-message) - file)) - regions)) - result) - (unwind-protect - (progn - (if (= (length images) 1) - (setq result (car images)) - (setq result (make-temp-file "pdf-view")) - ;; Join the images horizontally with a gap of 10 pixel. - (pdf-util-convert - "-noop" ;; workaround limitations of this function - result - :commands `("(" - ,@images - "-background" "white" - "-splice" "0x10+0+0" - ")" - "-gravity" "Center" - "-append" - "+gravity" - "-chop" "0x10+0+0") - :apply '((0 0 0 0)))) - (with-current-buffer output-buffer - (let ((inhibit-read-only t)) - (erase-buffer)) - (set-buffer-multibyte nil) - (insert-file-contents-literally result) - (image-mode) - (unless no-display-p - (pop-to-buffer (current-buffer))))) - (dolist (f (cons result images)) - (when (file-exists-p f) - (delete-file f)))))) - -;; * ================================================================== * -;; * Bookmark + Register Integration -;; * ================================================================== * - -(defun pdf-view-bookmark-make-record (&optional no-page no-slice no-size no-origin) - ;; TODO: add NO-PAGE, NO-SLICE, NO-SIZE, NO-ORIGIN to the docstring. - "Create a bookmark PDF record. - -The optional, boolean args exclude certain attributes." - (let ((displayed-p (eq (current-buffer) - (window-buffer)))) - (cons (buffer-name) - (append (bookmark-make-record-default nil t 1) - `(,(unless no-page - (cons 'page (pdf-view-current-page))) - ,(unless no-slice - (cons 'slice (and displayed-p - (pdf-view-current-slice)))) - ,(unless no-size - (cons 'size pdf-view-display-size)) - ,(unless no-origin - (cons 'origin - (and displayed-p - (let ((edges (pdf-util-image-displayed-edges nil t))) - (pdf-util-scale-pixel-to-relative - (cons (car edges) (cadr edges)) nil t))))) - (handler . pdf-view-bookmark-jump-handler)))))) - -;;;###autoload -(defun pdf-view-bookmark-jump-handler (bmk) - "The bookmark handler-function interface for bookmark BMK. - -See also `pdf-view-bookmark-make-record'." - (let ((page (bookmark-prop-get bmk 'page)) - (slice (bookmark-prop-get bmk 'slice)) - (size (bookmark-prop-get bmk 'size)) - (origin (bookmark-prop-get bmk 'origin)) - (file (bookmark-prop-get bmk 'filename)) - (show-fn-sym (make-symbol "pdf-view-bookmark-after-jump-hook"))) - (fset show-fn-sym - (lambda () - (remove-hook 'bookmark-after-jump-hook show-fn-sym) - (unless (derived-mode-p 'pdf-view-mode) - (pdf-view-mode)) - (with-selected-window - (or (get-buffer-window (current-buffer) 0) - (selected-window)) - (when size - (setq-local pdf-view-display-size size)) - (when slice - (apply 'pdf-view-set-slice slice)) - (when (numberp page) - (pdf-view-goto-page page)) - (when origin - (let ((size (pdf-view-image-size t))) - (image-set-window-hscroll - (round (/ (* (car origin) (car size)) - (frame-char-width)))) - (image-set-window-vscroll - (round (/ (* (cdr origin) (cdr size)) - (if pdf-view-have-image-mode-pixel-vscroll - 1 - (frame-char-height)))))))))) - (add-hook 'bookmark-after-jump-hook show-fn-sym) - (set-buffer (or (find-buffer-visiting file) - (find-file-noselect file))))) - -(defun pdf-view-bookmark-jump (bmk) - "Switch to bookmark BMK. - -This function is like `bookmark-jump', but it always uses the -selected window for display and does not run any hooks. Also, it -works only with bookmarks created by -`pdf-view-bookmark-make-record'." - - (let* ((file (bookmark-prop-get bmk 'filename)) - (buffer (or (find-buffer-visiting file) - (find-file-noselect file)))) - (switch-to-buffer buffer) - (let (bookmark-after-jump-hook) - (pdf-view-bookmark-jump-handler bmk) - (run-hooks 'bookmark-after-jump-hook)))) - -(defun pdf-view-registerv-make () - "Create a PDF register entry of the current position." - (registerv-make - (pdf-view-bookmark-make-record nil t t) - :print-func 'pdf-view-registerv-print-func - :jump-func 'pdf-view-bookmark-jump - :insert-func (lambda (bmk) - (insert (format "%S" bmk))))) - -(defun pdf-view-registerv-print-func (bmk) - "Print a textual representation of bookmark BMK. - -This function is used as the `:print-func' property with -`registerv-make'." - (let* ((file (bookmark-prop-get bmk 'filename)) - (buffer (find-buffer-visiting file)) - (page (bookmark-prop-get bmk 'page)) - (origin (bookmark-prop-get bmk 'origin))) - (princ (format "PDF position: %s, page %d, %d%%" - (if buffer - (buffer-name buffer) - file) - (or page 1) - (if origin - (round (* 100 (cdr origin))) - 0))))) - -(defmacro pdf-view-with-register-alist (&rest body) - "Setup the proper binding for `register-alist' in BODY. - -This macro may not work as desired when it is nested. See also -`pdf-view-use-dedicated-register'." - (declare (debug t) (indent 0)) - (let ((dedicated-p (make-symbol "dedicated-p"))) - `(let* ((,dedicated-p pdf-view-use-dedicated-register) - (register-alist - (if ,dedicated-p - pdf-view-register-alist - register-alist))) - (unwind-protect - (progn ,@body) - (when ,dedicated-p - (setq pdf-view-register-alist register-alist)))))) - -(defun pdf-view-position-to-register (register) - "Store current PDF position in register REGISTER. - -See also `point-to-register'." - (interactive - (list (pdf-view-with-register-alist - (register-read-with-preview "Position to register: ")))) - (pdf-view-with-register-alist - (set-register register (pdf-view-registerv-make)))) - -(defun pdf-view-jump-to-register (register &optional delete return-register) - ;; TODO: add RETURN-REGISTER to the docstring. - "Move point to a position stored in a REGISTER. - -Optional parameter DELETE is defined as in `jump-to-register'." - (interactive - (pdf-view-with-register-alist - (list - (register-read-with-preview "Jump to register: ") - current-prefix-arg - (and (or pdf-view-use-dedicated-register - (local-variable-p 'register-alist)) - (characterp last-command-event) - last-command-event)))) - (pdf-view-with-register-alist - (let ((return-pos (and return-register - (pdf-view-registerv-make)))) - (jump-to-register register delete) - (when return-register - (set-register return-register return-pos))))) - -(provide 'pdf-view) - -;;; pdf-view.el ends here |