diff options
| -rw-r--r-- | lisp/pdf-annot.el | 83 | ||||
| -rw-r--r-- | lisp/pdf-cache.el | 5 | ||||
| -rw-r--r-- | lisp/pdf-isearch.el | 165 | ||||
| -rw-r--r-- | lisp/pdf-links.el | 96 | ||||
| -rw-r--r-- | lisp/pdf-occur.el | 6 | ||||
| -rw-r--r-- | lisp/pdf-sync.el | 19 | ||||
| -rw-r--r-- | lisp/pdf-util.el | 137 | ||||
| -rw-r--r-- | lisp/pdf-view.el | 241 |
8 files changed, 461 insertions, 291 deletions
diff --git a/lisp/pdf-annot.el b/lisp/pdf-annot.el index 0abad53..4c87e24 100644 --- a/lisp/pdf-annot.el +++ b/lisp/pdf-annot.el @@ -1012,7 +1012,8 @@ other annotations." `("white" "steel blue" 0.35 ,@edges)) :map (pdf-view-apply-hotspot-functions window page size) - :width (car size)))) + :width (car size)) + (when pdf-view-roll-minor-mode page))) (pdf-util-scroll-to-edges (pdf-util-scale-relative-to-pixel (car edges))))))) @@ -1099,8 +1100,8 @@ Return the new annotation." (pdf-annot-activate-annotation a)) a)) -(defun pdf-annot-add-text-annotation (pos &optional icon property-alist) - "Add a new text annotation at POS in the selected window. +(defun pdf-annot-add-text-annotation (pos &optional icon property-alist page) + "Add a new text annotation at POS on PAGE in the selected window. POS should be a image position object or a cons \(X . Y\), both being image coordinates. @@ -1128,6 +1129,9 @@ Return the new annotation." (list posn))) (pdf-util-assert-pdf-window) (when (posnp pos) + (setq page (or page + (when pdf-view-roll-minor-mode + (1+ (/ (posn-point pos) 4))))) (setq pos (posn-object-x-y pos))) (let ((isize (pdf-view-image-size)) (x (car pos)) @@ -1152,7 +1156,8 @@ Return the new annotation." property-alist (cdr (assq 'text pdf-annot-default-annotation-properties)) (cdr (assq t pdf-annot-default-annotation-properties)) - `((color . ,(car pdf-annot-color-history)))))))) + `((color . ,(car pdf-annot-color-history)))) + page)))) (defun pdf-annot-mouse-add-text-annotation (ev) "Add a text annotation using the mouse. @@ -1168,11 +1173,12 @@ EV describes the captured mouse event." "Click where a new text annotation should be added ...")) (event-start ev)))) -(defun pdf-annot-add-markup-annotation (list-of-edges type &optional color +(defun pdf-annot-add-markup-annotation (region type &optional color property-alist) "Add a new markup annotation in the selected window. -LIST-OF-EDGES determines the marked up area and should be a list +REGION determines the marked up area and should be a cons cell +\(PAGE . LIST-OF-EDGES\) where LIST-OF-EDGES should be list of \(LEFT TOP RIGHT BOT\), each value a relative coordinate. TYPE should be one of `squiggly', `underline', `strike-out' or @@ -1195,7 +1201,7 @@ Return the new annotation." (pdf-util-assert-pdf-window) (pdf-annot-add-annotation type - list-of-edges + (cdr region) (pdf-annot-merge-alists (and color `((color . ,color))) property-alist @@ -1204,7 +1210,7 @@ Return the new annotation." (when pdf-annot-color-history `((color . ,(car pdf-annot-color-history)))) '((color . "#ffff00"))) - (pdf-view-current-page))) + (car region))) (defun pdf-annot-add-squiggly-markup-annotation (list-of-edges &optional color property-alist) @@ -1554,6 +1560,66 @@ At any given point of time, only one annotation can be in edit mode." (error "No annotation at this position")) (pdf-annot-edit-contents a))) +(defun pdf-annot-edit (annot) + "Activate ANNOT, for editing. + +Interactively, annot is read via `pdf-annot-read-annot'. +This function displays characters around the annots in the current +page and starts reading characters (ignoring case). After a +sufficient number of characters have been read, the corresponding +annot's annot is invoked. Additionally, SPC may be used to +scroll the current page." + (interactive + (list (or (pdf-annot-read-annot "Activate annot (SPC scrolls): ") + (error "No annot selected")))) + (pdf-annot-activate-annotation annot)) + +;; TODO 'merge' this function with `pdf-links-read-link-action' into a single +;; universal 'read-action' function (in `pdf-util'?) +(defun pdf-annot-read-annot (prompt) + "Using PROMPT, interactively read an annot-action. + +See `pdf-annot-edit' for the interface." + (pdf-util-assert-pdf-window) + (let* ((annots (pdf-annot-getannots (pdf-view-current-page) nil nil)) + (keys (pdf-links-read-link-action--create-keys + (length annots))) + (key-strings (mapcar (apply-partially 'apply 'string) + keys)) + (alist (cl-mapcar 'cons keys annots)) + (size (pdf-view-image-size)) + (colors (pdf-util-face-colors + 'pdf-links-read-link pdf-view-dark-minor-mode)) + (args (list + :foreground (car colors) + :background (cdr colors) + :formats + `((?c . ,(lambda (_edges) (pop key-strings))) + (?P . ,(number-to-string + (max 1 (* (cdr size) + pdf-links-convert-pointsize-scale))))) + :commands pdf-links-read-link-convert-commands + :apply (pdf-util-scale-relative-to-pixel + (mapcar (lambda (l) (cdr (assq 'edges l))) + annots))))) + ;; (print (plist-get args :apply)) + (unless annots + (error "No annots on this page")) + (unwind-protect + (let ((image-data + (pdf-cache-get-image + (pdf-view-current-page) + (car size) (car size) 'pdf-annot-read-annot))) + (unless image-data + (setq image-data (apply 'pdf-util-convert-page args )) + (pdf-cache-put-image + (pdf-view-current-page) + (car size) image-data 'pdf-annot-read-annot)) + (pdf-view-display-image + (create-image image-data (pdf-view-image-type) t) + (when pdf-view-roll-minor-mode (pdf-view-current-page))) + (pdf-links-read-link-action--read-chars prompt alist)) + (pdf-view-redisplay)))) ;; * ================================================================== * @@ -1596,6 +1662,7 @@ Currently supported properties are page, type, label, date and contents." (defvar pdf-annot-list-mode-map (let ((km (make-sparse-keymap))) + (define-key km (kbd "e") 'pdf-annot-edit) (define-key km (kbd "C-c C-f") #'pdf-annot-list-follow-minor-mode) (define-key km (kbd "SPC") #'pdf-annot-list-display-annotation-from-id) km)) diff --git a/lisp/pdf-cache.el b/lisp/pdf-cache.el index a4f0d60..57c824f 100644 --- a/lisp/pdf-cache.el +++ b/lisp/pdf-cache.el @@ -456,10 +456,7 @@ WINDOW and IMAGE-WIDTH decide the page and scale of the final image." (image-size (pdf-view-create-page page)) (pdf-util-debug (message "Prefetched page %s." page)) - ;; Avoid max-lisp-eval-depth - (run-with-timer - 0.001 nil - #'pdf-cache--prefetch-pages window image-width))))))) + (pdf-cache--prefetch-pages window image-width))))))) (condition-case err (pdf-info-renderpage page image-width) (error diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index f5468eb..9ffc2e4 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -33,7 +33,7 @@ (require 'let-alist) ;;; Code: - +(defvar pdf-isearch--hl-matches-tick 0) ;; * ================================================================== * @@ -249,42 +249,55 @@ This is a Isearch interface function." (when (> (length string) 0) (let ((same-search-p (pdf-isearch-same-search-p)) (oldpage pdf-isearch-current-page) - (matches (pdf-isearch-search-page string)) + (pages (or (image-mode-window-get 'displayed-pages (selected-window)) + (list (pdf-view-current-page)))) + matches next-match) - ;; matches is a list of list of edges ((x0 y1 x1 y2) ...), - ;; sorted top to bottom ,left to right. Coordinates are in image - ;; space. - (unless isearch-forward - (setq matches (reverse matches))) - (when pdf-isearch-filter-matches-function - (setq matches (funcall pdf-isearch-filter-matches-function matches))) + (dolist (page pages) + (let ((page-matches (pdf-isearch-search-page string page))) + ;; matches is a list of list of edges ((x0 y1 x1 y2) ...), + ;; sorted top to bottom ,left to right. Coordinates are in image + ;; space. + (unless isearch-forward + (setq page-matches (reverse page-matches))) + (when pdf-isearch-filter-matches-function + (setq page-matches (funcall pdf-isearch-filter-matches-function page-matches))) + (push page-matches matches))) ;; Where to go next ? (setq pdf-isearch-current-page (pdf-view-current-page) pdf-isearch-current-matches matches next-match (pdf-isearch-next-match oldpage pdf-isearch-current-page - pdf-isearch-current-match matches + pdf-isearch-current-match (car matches) same-search-p isearch-forward) pdf-isearch-current-parameter (list string isearch-regexp isearch-case-fold-search isearch-word)) + (cl-callf nreverse matches) (cond (next-match (setq pdf-isearch-current-match next-match) - (pdf-isearch-hl-matches next-match matches) + (pdf-isearch-hl-matches next-match matches nil pages) (pdf-isearch-focus-match next-match) ;; Don't get off track. (when (or (and (bobp) (not isearch-forward)) (and (eobp) isearch-forward)) - (goto-char (1+ (/ (buffer-size) 2)))) + (unless pdf-view-roll-minor-mode + (goto-char (1+ (/ (buffer-size) 2))))) ;; Signal success to isearch. + ;; Moving the point is for `pdf-roll'. It ensures that + ;; `re-search-forward' takes us back to the starting point. Otherwise + ;; every call to `isearch-repeat' will increment/decrement the point + ;; and that causes recentering. (if isearch-forward - (re-search-forward ".") + (progn (unless (bobp) (forward-char -1)) + (re-search-forward ".")) + (unless (eobp) (forward-char 1)) (re-search-backward "."))) ((and (not pdf-isearch-narrow-to-page) - (not (pdf-isearch-empty-match-p matches))) + (not (pdf-isearch-empty-match-p pdf-isearch-current-matches))) (let ((next-page (pdf-isearch-find-next-matching-page string pdf-isearch-current-page t))) (when next-page @@ -306,12 +319,14 @@ This is a Isearch interface function." pdf-isearch-current-matches matches pdf-isearch-current-match match pdf-isearch-current-page page) - (pdf-view-goto-page pdf-isearch-current-page) - (when pdf-isearch-current-match - (pdf-isearch-hl-matches - pdf-isearch-current-match - pdf-isearch-current-matches)) + (if pdf-isearch-current-match + (pdf-isearch-hl-matches + pdf-isearch-current-match + pdf-isearch-current-matches + nil (image-mode-window-get 'displayed-pages (selected-window))) + (when pdf-view-roll-minor-mode + (pdf-view-redisplay))) (image-set-window-hscroll hscroll) (image-set-window-vscroll vscroll)))) @@ -347,7 +362,8 @@ This is a Isearch interface function." pdf-isearch-current-match nil pdf-isearch-current-matches nil pdf-isearch-current-parameter nil) - (goto-char (1+ (/ (buffer-size) 2)))) + (unless pdf-view-roll-minor-mode + (goto-char (1+ (/ (buffer-size) 2))))) (defun pdf-isearch-same-search-p (&optional ignore-search-string-p) "Return non-nil, if search parameter have not changed. @@ -385,8 +401,12 @@ there was no previous search, this function returns t." (defun pdf-isearch-redisplay () "Redisplay the current highlighting." - (pdf-isearch-hl-matches pdf-isearch-current-match - pdf-isearch-current-matches)) + (pdf-isearch-hl-matches + pdf-isearch-current-match + pdf-isearch-current-matches + nil + (or (image-mode-window-get 'displayed-pages (selected-window)) + (list (pdf-view-current-page))))) (defun pdf-isearch-update () "Update search and redisplay, if necessary." @@ -412,13 +432,14 @@ there was no previous search, this function returns t." (message "%s" msg)))) (defun pdf-isearch-empty-match-p (matches) - (and matches + (let ((all-matches (apply #'append matches))) + (and all-matches (cl-every (lambda (match) (cl-every (lambda (edges) (cl-every 'zerop edges)) match)) - matches))) + all-matches)))) (defun pdf-isearch-occur () "Run `occur' using the last search string or regexp." @@ -564,10 +585,10 @@ is no such page." (= incr 8)) ;;Don't bother right away. (setq reporter (apply - 'make-progress-reporter "Searching" - (if isearch-forward - (list (car pages) (pdf-cache-number-of-pages) nil 0) - (list 1 (cdr pages) nil 0))))) + 'make-progress-reporter "Searching" + (if isearch-forward + (list (car pages) (pdf-cache-number-of-pages) nil 0) + (list 1 (cdr pages) nil 0))))) (when reporter (progress-reporter-update reporter (if isearch-forward @@ -674,18 +695,18 @@ it is assumed to be ordered with respect to FORWARD-P." (let ((matched (apply 'pdf-util-edges-union match))) (pdf-util-with-edges (matched) (cl-loop for next in matches do - (let ((edges (apply 'pdf-util-edges-union next))) - (pdf-util-with-edges (edges) - (when (if forward-p - (or (>= edges-top matched-bot) - (and (or (>= edges-top matched-top) - (>= edges-bot matched-bot)) - (>= edges-right matched-right))) - (or (<= edges-bot matched-top) - (and (or (<= edges-bot matched-bot) - (<= edges-top matched-top)) - (<= edges-left matched-left)))) - (cl-return next)))))))) + (let ((edges (apply 'pdf-util-edges-union next))) + (pdf-util-with-edges (edges) + (when (if forward-p + (or (>= edges-top matched-bot) + (and (or (>= edges-top matched-top) + (>= edges-bot matched-bot)) + (>= edges-right matched-right))) + (or (<= edges-bot matched-top) + (and (or (<= edges-bot matched-bot) + (<= edges-top matched-top)) + (<= edges-left matched-left)))) + (cl-return next)))))))) @@ -716,41 +737,45 @@ MATCH-BG LAZY-FG LAZY-BG\)." (car lazy) (cdr lazy))))))) -(defvar pdf-isearch--hl-matches-tick 0) - -(defun pdf-isearch-hl-matches (current matches &optional occur-hack-p) +(defun pdf-isearch-hl-matches (current matches &optional occur-hack-p pages) "Highlighting edges CURRENT and MATCHES." (cl-check-type current pdf-isearch-match) - (cl-check-type matches (list-of pdf-isearch-match)) + (cl-check-type matches (list-of (list-of pdf-isearch-match))) (cl-destructuring-bind (fg1 bg1 fg2 bg2) (pdf-isearch-current-colors) - (let* ((width (car (pdf-view-image-size))) - (page (pdf-view-current-page)) - (window (selected-window)) + (let* ((window (selected-window)) + (pages (or pages + (image-mode-window-get 'displayed-pages (selected-window)) + (list (pdf-view-current-page)))) (buffer (current-buffer)) - (tick (cl-incf pdf-isearch--hl-matches-tick)) - (pdf-info-asynchronous - (lambda (status data) - (when (and (null status) - (eq tick pdf-isearch--hl-matches-tick) - (buffer-live-p buffer) - (window-live-p window) - (eq (window-buffer window) - buffer)) - (with-selected-window window - (when (and (derived-mode-p 'pdf-view-mode) - (or isearch-mode - occur-hack-p) - (eq page (pdf-view-current-page))) - (pdf-view-display-image - (pdf-view-create-image data :width width)))))))) - (pdf-info-renderpage-text-regions - page width t nil nil - `(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative - current)) - `(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative - (apply 'append - (remove current matches)))))))) + (tick (cl-incf pdf-isearch--hl-matches-tick))) + (dolist (page pages) + (let* ((width (car (pdf-view-image-size nil window page))) + (pdf-info-asynchronous + (lambda (status data) + (when (and (null status) + (eq tick pdf-isearch--hl-matches-tick) + (buffer-live-p buffer) + (window-live-p window) + (eq (window-buffer window) + buffer)) + (with-selected-window window + (when (and (derived-mode-p 'pdf-view-mode) + (or isearch-mode occur-hack-p + (memq last-command '(isearch-repeat-forward isearch-repeat-backward))) + (or (eq page (pdf-view-current-page)) + (memq page (image-mode-window-get 'displayed-pages window)))) + (pdf-view-display-image + (pdf-view-create-image data :width width) + page window))))))) + (pdf-info-renderpage-text-regions + page width t nil nil + `(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative + (when (eq page (pdf-view-current-page window)) + current))) + `(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative + (apply 'append + (remove current (pop matches))))))))))) ;; * ================================================================== * diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index d54981e..3542085 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -29,6 +29,10 @@ (require 'let-alist) (require 'org) + + +(declare-function pdf-roll-page-overlay "pdf-roll") +(declare-function pdf-roll-displayed-pages "pdf-roll") ;;; Code: @@ -103,7 +107,7 @@ do something with it." ;;;###autoload (define-minor-mode pdf-links-minor-mode - "Handle links in PDF documents.\\<pdf-links-minor-mode-map> + "Handle links in PDF documents. If this mode is enabled, most links in the document may be activated by clicking on them or by pressing \\[pdf-links-action-perform] and selecting @@ -151,7 +155,7 @@ links via \\[pdf-links-isearch-link]. (nreverse hotspots))) (defun pdf-links-action-to-string (link) - "Return a string representation of ACTION." + "Return a string representation of action for LINK." (let-alist link (concat (cl-case .type @@ -208,17 +212,10 @@ scroll the current page." (with-selected-window window (when (derived-mode-p 'pdf-view-mode) (when (> .page 0) - (pdf-view-goto-page .page)) + (pdf-view-goto-page .page window)) (when .top - ;; Showing the tooltip delays displaying the page for - ;; some reason (sit-for/redisplay don't help), do it - ;; later. - (run-with-idle-timer 0.001 nil - (lambda () - (when (window-live-p window) - (with-selected-window window - (when (derived-mode-p 'pdf-view-mode) - (pdf-util-tooltip-arrow .top))))))))))) + (when (derived-mode-p 'pdf-view-mode) + (pdf-util-tooltip-arrow .top))))))) (uri (funcall pdf-links-browse-uri-function .uri)) (t @@ -231,44 +228,47 @@ scroll the current page." See `pdf-links-action-perform' for the interface." (pdf-util-assert-pdf-window) - (let* ((links (pdf-cache-pagelinks - (pdf-view-current-page))) + (let* ((win (selected-window)) + (pages (if pdf-view-roll-minor-mode + (reverse (image-mode-window-get 'displayed-pages win)) + (list (pdf-view-current-page)))) + (links (mapcar #'pdf-cache-pagelinks pages)) (keys (pdf-links-read-link-action--create-keys - (length links))) - (key-strings (mapcar (apply-partially 'apply 'string) - keys)) - (alist (cl-mapcar 'cons keys links)) - (size (pdf-view-image-size)) + (apply #'+ (mapcar #'length links)))) + (alist (cl-mapcar 'cons keys (apply #'append links))) (colors (pdf-util-face-colors - 'pdf-links-read-link pdf-view-dark-minor-mode)) - (args (list - :foreground (car colors) - :background (cdr colors) - :formats - `((?c . ,(lambda (_edges) (pop key-strings))) - (?P . ,(number-to-string - (max 1 (* (cdr size) - pdf-links-convert-pointsize-scale))))) - :commands pdf-links-read-link-convert-commands - :apply (pdf-util-scale-relative-to-pixel - (mapcar (lambda (l) (cdr (assq 'edges l))) - links))))) - (unless links - (error "No links on this page")) - (unwind-protect - (let ((image-data - (pdf-cache-get-image - (pdf-view-current-page) - (car size) (car size) 'pdf-links-read-link-action))) - (unless image-data - (setq image-data (apply 'pdf-util-convert-page args )) - (pdf-cache-put-image - (pdf-view-current-page) - (car size) image-data 'pdf-links-read-link-action)) - (pdf-view-display-image - (create-image image-data (pdf-view-image-type) t)) - (pdf-links-read-link-action--read-chars prompt alist)) - (pdf-view-redisplay)))) + 'pdf-links-read-link pdf-view-dark-minor-mode))) + (if (not links) + (error "No links on displayed pages") + (unwind-protect + (progn + (dolist (page pages) + (let* ((image (or (overlay-get (pdf-roll-page-overlay page win) 'display) + (pdf-view-current-image))) + (image (or (assoc 'image image) image)) + (height (cdr (image-size image t))) + (orig-image (create-image (plist-get (cdr image) :data) + (pdf-view-image-type) t))) + (pdf-view-display-image + (create-image (pdf-util-convert-image + orig-image + :foreground (car colors) + :background (cdr colors) + :formats + `((?c . ,(lambda (_edges) (apply #'string (pop keys)))) + (?P . ,(number-to-string + (max 1 (* height + pdf-links-convert-pointsize-scale))))) + :commands pdf-links-read-link-convert-commands + :apply (pdf-util-scale + (mapcar (lambda (l) (cdr (assq 'edges l))) + (pop links)) + (image-size orig-image t))) + (pdf-view-image-type) t + :height height) + page win))) + (pdf-links-read-link-action--read-chars prompt alist)) + (pdf-view-redisplay))))) (defun pdf-links-read-link-action--read-chars (prompt alist) (catch 'done diff --git a/lisp/pdf-occur.el b/lisp/pdf-occur.el index 47bf2d2..ea5fa17 100644 --- a/lisp/pdf-occur.el +++ b/lisp/pdf-occur.el @@ -629,9 +629,9 @@ matches linked with PAGE." (setq pdf-occur-number-of-matches 0) (setq pdf-occur-search-pages-left (apply #'+ (mapcar (lambda (elt) - (1+ (- (cdr (nth 1 elt)) - (car (nth 1 elt))))) - batches))))) + (1+ (- (cdr (nth 1 elt)) + (car (nth 1 elt))))) + batches))))) diff --git a/lisp/pdf-sync.el b/lisp/pdf-sync.el index 68de3b9..18fd6e0 100644 --- a/lisp/pdf-sync.el +++ b/lisp/pdf-sync.el @@ -277,15 +277,18 @@ Has no effect if `pdf-sync-backward-use-heuristic' is nil." (xy (posn-object-x-y posn))) (unless image (error "Outside of image area")) - (pdf-sync-backward-search (car xy) (cdr xy)))) + (pdf-sync-backward-search + (car xy) (cdr xy) + (and (bound-and-true-p pdf-view-roll-minor-mode) + (/ (+ (posn-point posn) 3) 4))))) -(defun pdf-sync-backward-search (x y) - "Go to the source corresponding to image coordinates X, Y. +(defun pdf-sync-backward-search (x y &optional page) + "Go to the source corresponding to image coordinates X, Y on PAGE. Try to find the exact position, if `pdf-sync-backward-use-heuristic' is non-nil." (cl-destructuring-bind (source finder) - (pdf-sync-backward-correlate x y) + (pdf-sync-backward-correlate x y page) (pop-to-buffer (or (find-buffer-visiting source) (find-file-noselect source)) pdf-sync-backward-display-action) @@ -293,8 +296,8 @@ Try to find the exact position, if (funcall finder) (run-hooks 'pdf-sync-backward-hook))) -(defun pdf-sync-backward-correlate (x y) - "Find the source corresponding to image coordinates X, Y. +(defun pdf-sync-backward-correlate (x y &optional page) + "Find the source corresponding to image coordinates X, Y on PAGE. Returns a list \(SOURCE FINDER\), where SOURCE is the name of the TeX file and FINDER a function of zero arguments which, when @@ -303,7 +306,7 @@ point to the correct position." (pdf-util-assert-pdf-window) (let ((size (pdf-view-image-size)) - (page (pdf-view-current-page))) + (page (or page (pdf-view-current-page)))) (setq x (/ x (float (car size))) y (/ y (float (cdr size)))) (let-alist (pdf-info-synctex-backward-search page x y) @@ -656,7 +659,7 @@ Needs to have `pdf-sync-backward-debug-minor-mode' enabled." buffer pdf-sync-forward-display-action) (pdf-util-assert-pdf-window) (when page - (pdf-view-goto-page page) + (pdf-view-goto-page page (selected-window)) (when y1 (let ((top (* y1 (cdr (pdf-view-image-size))))) (pdf-util-tooltip-arrow (round top)))))) diff --git a/lisp/pdf-util.el b/lisp/pdf-util.el index 5c0e50d..8886de3 100644 --- a/lisp/pdf-util.el +++ b/lisp/pdf-util.el @@ -28,6 +28,7 @@ (require 'pdf-macs) (require 'cl-lib) (require 'format-spec) +(require 'image-mode) (require 'faces) ;; These functions are only used after a PdfView window was asserted, @@ -40,6 +41,7 @@ (declare-function image-set-window-vscroll "image-mode") (declare-function image-set-window-hscroll "image-mode") +(defvar pdf-view-roll-minor-mode) ;; * ================================================================== * @@ -161,7 +163,7 @@ See also `pdf-util-scale'." The result depends on the currently displayed page in WINDOW. See also `pdf-util-scale'." - (pdf-util-assert-pdf-window window) + (when displayed-p (pdf-util-assert-pdf-window window)) (pdf-util-scale-to list-of-pixel-edges (pdf-view-image-size displayed-p window) @@ -314,15 +316,19 @@ depending on the input." "Return the visible region of the image in WINDOW. Returns a list of pixel edges." - (pdf-util-assert-pdf-window) + (when displayed-p (pdf-util-assert-pdf-window window)) (let* ((edges (window-inside-pixel-edges window)) (isize (pdf-view-image-size displayed-p window)) (offset (if displayed-p `(0 . 0) (pdf-view-image-offset window))) - (hscroll (* (window-hscroll window) + (hscroll (* (if displayed-p + (window-hscroll window) + (or (image-mode-window-get 'hscroll window) 0)) (frame-char-width (window-frame window)))) - (vscroll (window-vscroll window t)) + (vscroll (if displayed-p + (window-vscroll window t) + (or (image-mode-window-get 'vscroll window) 0))) (x0 (+ hscroll (car offset))) (y0 (+ vscroll (cdr offset))) (x1 (min (car isize) @@ -386,40 +392,46 @@ needed. Note: For versions of emacs before 27 this will return lines instead of pixels. This is because of a change that occurred to `image-mode' in 27." (pdf-util-assert-pdf-window) - (let* ((win (window-inside-pixel-edges)) - (image-height (cdr (pdf-view-image-size t))) - (image-top (window-vscroll nil t)) - (edges (pdf-util-translate - edges - (pdf-view-image-offset) t))) - (pdf-util-with-edges (win edges) - (let* ((context-pixel (or context-pixel - (* next-screen-context-lines - (frame-char-height)))) - ;;Be careful not to modify edges. - (edges-top (- edges-top context-pixel)) - (edges-bot (+ edges-bot context-pixel)) - (vscroll - (cond ((< edges-top image-top) - (max 0 (if eager-p - (- edges-bot win-height) - edges-top))) - ((> (min image-height - edges-bot) - (+ image-top win-height)) - (min (- image-height win-height) - (if eager-p - edges-top - (- edges-bot win-height))))))) - - - (when vscroll - (round - ;; `image-set-window-vscroll' changed in version 27 to using - ;; pixels, not lines. - (if (version< emacs-version "27") - (/ vscroll (float (frame-char-height))) - vscroll))))))) + (if pdf-view-roll-minor-mode + (max 0 (- (nth 1 edges) + (or context-pixel + (* next-screen-context-lines (frame-char-height))))) + (let* ((win (window-inside-pixel-edges)) + (image-height (cdr (pdf-view-image-size + (unless pdf-view-roll-minor-mode + t)))) + (image-top (window-vscroll nil t)) + (edges (pdf-util-translate + edges + (pdf-view-image-offset) t))) + (pdf-util-with-edges (win edges) + (let* ((context-pixel (or context-pixel + (* next-screen-context-lines + (frame-char-height)))) + ;;Be careful not to modify edges. + (edges-top (- edges-top context-pixel)) + (edges-bot (+ edges-bot context-pixel)) + (vscroll + (cond ((< edges-top image-top) + (max 0 (if eager-p + (- edges-bot win-height) + edges-top))) + ((> (min image-height + edges-bot) + (+ image-top win-height)) + (min (- image-height win-height) + (if eager-p + edges-top + (- edges-bot win-height))))))) + + + (when vscroll + (round + ;; `image-set-window-vscroll' changed in version 27 to using + ;; pixels, not lines. + (if (version< emacs-version "27") + (/ vscroll (float (frame-char-height))) + vscroll)))))))) (defun pdf-util-scroll-to-edges (edges &optional eager-p) "Scroll window such that image EDGES are visible. @@ -639,10 +651,9 @@ string." (cdr (pdf-view-image-offset)) (window-vscroll nil t) (frame-char-height)))) - (when (overlay-get (pdf-view-current-overlay) 'before-string) - (let* ((e (window-inside-pixel-edges)) - (xw (pdf-util-with-edges (e) e-width))) - (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2)))) + (let* ((e (window-inside-pixel-edges)) + (xw (pdf-util-with-edges (e) e-width))) + (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2))) (pdf-util-tooltip-in-window (propertize " " 'display (propertize @@ -787,8 +798,8 @@ respective sequence." (cl-macrolet ((make-matrix (rows columns) `(apply #'vector - (cl-loop for i from 1 to ,rows - collect (make-vector ,columns nil)))) + (cl-loop for i from 1 to ,rows + collect (make-vector ,columns nil)))) (mset (matrix row column newelt) `(aset (aref ,matrix ,row) ,column ,newelt)) (mref (matrix row column) @@ -803,21 +814,21 @@ respective sequence." (if (equal a b) 1 -1))))) (cl-loop for i from 0 to len1 do - (mset d i 0 (- i))) + (mset d i 0 (- i))) (cl-loop for j from 0 to len2 do - (mset d 0 j (if suffix-p 0 (- j)))) + (mset d 0 j (if suffix-p 0 (- j)))) (cl-loop for i from 1 to len1 do - (cl-loop for j from 1 to len2 do - (let ((max (max - (1- (mref d (1- i) j)) - (+ (mref d i (1- j)) - (if (and prefix-p (= i len1)) 0 -1)) - (+ (mref d (1- i) (1- j)) - (funcall similarity-fn - (elt seq1 (1- i)) - (elt seq2 (1- j))))))) - (mset d i j max)))) + (cl-loop for j from 1 to len2 do + (let ((max (max + (1- (mref d (1- i) j)) + (+ (mref d i (1- j)) + (if (and prefix-p (= i len1)) 0 -1)) + (+ (mref d (1- i) (1- j)) + (funcall similarity-fn + (elt seq1 (1- i)) + (elt seq2 (1- j))))))) + (mset d i j max)))) (let ((i len1) (j len2) @@ -1042,8 +1053,8 @@ Returns the convert process." (set-process-sentinel proc callback)) proc))) -(defun pdf-util-convert-page (&rest specs) - "Convert image of current page according to SPECS. +(defun pdf-util-convert-image (image &rest specs) + "Convert IMAGE page according to SPECS. Return the converted PNG image as a string. See also `pdf-util-convert'." @@ -1053,7 +1064,7 @@ Return the converted PNG image as a string. See also (out-file (make-temp-file "pdf-util-convert" nil ".png"))) (unwind-protect (let ((image-data - (plist-get (cdr (pdf-view-current-image)) :data))) + (plist-get (cdr image) :data))) (with-temp-file in-file (set-buffer-multibyte nil) (set-buffer-file-coding-system 'binary) @@ -1066,6 +1077,14 @@ Return the converted PNG image as a string. See also (when (file-exists-p out-file) (delete-file out-file))))) +(defun pdf-util-convert-page (&rest specs) + "Convert image of current page according to SPECS. + +Return the converted PNG image as a string. See also +`pdf-util-convert'." + + (pdf-util-assert-pdf-window) + (apply #'pdf-util-convert-image (pdf-view-current-image) specs)) (defun pdf-util-convert--create-commands (spec) (let ((fg "red") diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 620e526..d1ff32f 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -36,6 +36,18 @@ (declare-function cua-copy-region "cua-base") (declare-function pdf-tools-pdf-buffer-p "pdf-tools") + +(declare-function pdf-roll-scroll-forward "pdf-roll") +(declare-function pdf-roll-scroll-backward "pdf-roll") +(declare-function pdf-roll-next-page "pdf-roll") +(declare-function pdf-roll-redisplay "pdf-roll") +(declare-function pdf-roll-pre-redisplay "pdf-roll") +(declare-function pdf-roll-page-overlay "pdf-roll") +(declare-function pdf-roll-page-at-current-pos "pdf-roll") +(declare-function pdf-roll-display-image "pdf-roll") + +(defvar pdf-view-roll-minor-mode nil) + ;; * ================================================================== * ;; * Customizations @@ -252,7 +264,7 @@ Must be one of `glyph', `word', or `line'." ;; * ================================================================== * (defvar-local pdf-view-active-region nil - "The active region as a list of edges. + "The active region as a cons cell of page and list of edges. Edge values are relative coordinates.") @@ -551,7 +563,8 @@ PNG images in Emacs buffers." 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))) + (buffer-file-name) + (buffer-file-name (buffer-base-buffer)))) (defun pdf-view--write-contents-function () "Function for `write-contents-functions' to save the buffer." @@ -734,7 +747,10 @@ windows." (setf (pdf-view-current-page window) page) (run-hooks 'pdf-view-change-page-hook)) (when (window-live-p window) - (pdf-view-redisplay window)) + (image-set-window-vscroll 0) + (if pdf-view-roll-minor-mode + (pdf-roll-pre-redisplay window) + (pdf-view-redisplay window))) (when changing-p (pdf-view-deactivate-region) (force-mode-line-update) @@ -860,7 +876,7 @@ to previous page only on typing DEL (ARG is nil)." (image-set-window-hscroll hscroll))) (image-scroll-down arg))) -(defun pdf-view-next-line-or-next-page (&optional 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 @@ -878,7 +894,13 @@ at the bottom edge of the page moves to the next page." (image-set-window-hscroll hscroll))) (image-next-line arg))) -(defun pdf-view-previous-line-or-previous-page (&optional arg) +(defun pdf-view-next-line-or-next-page (&optional arg) + (interactive "p") + (if pdf-view-roll-minor-mode + (dotimes (_ (or arg 1)) (pdf-roll-scroll-forward)) + (pdf-view--next-line-or-next-page arg))) + +(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 @@ -896,6 +918,12 @@ at the top edge of the page moves to the previous page." (image-set-window-hscroll hscroll))) (image-previous-line arg))) +(defun pdf-view-previous-line-or-previous-page (&optional arg) + (interactive "p") + (if pdf-view-roll-minor-mode + (dotimes (_ (or arg 1)) (pdf-roll-scroll-backward)) + (pdf-view--previous-line-or-previous-page arg))) + (defun pdf-view-goto-label (label) "Go to the page corresponding to LABEL. @@ -1119,17 +1147,23 @@ See also `pdf-view-use-imagemagick'." :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. +(defun pdf-view-image-size (&optional displayed-p window page) + "Return the size in pixel of the current image in WINDOW. 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))) +image. These values may be different, if slicing is used. + +If PAGE is non-nil return its size instead of current page." + (let ((display-prop (if pdf-view-roll-minor-mode + (progn (setq window (if (windowp window) window (selected-window))) + (setq page (or page (pdf-view-current-page window))) + (unless (memq page (image-mode-window-get 'displayed-pages window)) + (pdf-view-display-page page window)) + (overlay-get (pdf-roll-page-overlay page window) 'display)) + (image-get-display-property)))) + (if displayed-p + (image-display-size display-prop t) + (image-size display-prop t)))) (defun pdf-view-image-offset (&optional window) ;; TODO: add WINDOW to docstring. @@ -1149,47 +1183,49 @@ It is equal to \(LEFT . TOP\) of the current slice in pixel." "Display page PAGE in WINDOW." (setf (pdf-view-window-needs-redisplay window) nil) (pdf-view-display-image - (pdf-view-create-page page window) - window)) + (pdf-view-create-page page window) page window)) -(defun pdf-view-display-image (image &optional window inhibit-slice-p) +(defun pdf-view-display-image (image page &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) + (if pdf-view-roll-minor-mode + (pdf-roll-display-image + image page (or window (selected-window)) inhibit-slice-p) + (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." @@ -1210,12 +1246,18 @@ If WINDOW is t, redisplay pages in all windows." (setf (pdf-view-window-needs-redisplay window) t))))) (force-mode-line-update))) +(defun pdf-view-redisplay (&optional window) + (if pdf-view-roll-minor-mode + (pdf-roll-redisplay window) + (pdf-view--redisplay window))) + (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) + (when (cl-some (lambda (page) (memq page pages)) + (or (image-mode-window-get 'displayed-pages window) + (list (pdf-view-current-page window)))) (pdf-view-redisplay window)))) (defun pdf-view-maybe-redisplay-resized-windows () @@ -1261,7 +1303,7 @@ If WINDOW is t, redisplay pages in all windows." ;; `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) + (image-mode-window-put 'overlay ol) ;; Clean up some overlays. (dolist (ov (overlays-in (point-min) (point-max))) (when (and (windowp (overlay-get ov 'window)) @@ -1406,7 +1448,7 @@ current theme's colors." (pdf-util-assert-pdf-buffer) (pdf-cache-clear-images) (when get-theme - (pdf-view-set-theme-background)) + (pdf-view-set-theme-background)) (pdf-view-redisplay t)) (define-minor-mode pdf-view-themed-minor-mode @@ -1487,7 +1529,7 @@ supersede hotspots in lower ones." (setq deactivate-mark nil)) (defun pdf-view-active-region (&optional deactivate-p) - "Return the active region, a list of edges. + "Return the active region, as a cons cell of page number and list of edges. Deactivate the region if DEACTIVATE-P is non-nil." (pdf-view-assert-active-region) @@ -1537,9 +1579,14 @@ Stores the region in `pdf-view-active-region'." (setq begin-inside-image-p nil) (posn-x-y pos))) (abs-begin (posn-x-y pos)) + (page (if pdf-view-roll-minor-mode + (/ (+ 3 (posn-point pos)) 4) + (pdf-view-current-page))) + (margin (frame-char-height)) (selection-style (or selection-style pdf-view-selection-style)) pdf-view-continuous region) + (setq pdf-view-active-region (list page)) (when (pdf-util-track-mouse-dragging (event 0.05) (let* ((pos (event-start event)) (end (posn-object-x-y pos)) @@ -1579,23 +1626,33 @@ Stores the region in `pdf-view-active-region'." (+ (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))))) + (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)))) + (y (cdr (posn-x-y pos))) + (dy (- y (cdr abs-begin)))) (setq region (pdf-util-scale-pixel-to-relative iregion)) (pdf-view-display-region - (cons region pdf-view-active-region) + (cons page (cons region (cdr pdf-view-active-region))) rectangle-p selection-style) - (pdf-util-scroll-to-edges iregion))))) - (setq pdf-view-active-region - (append pdf-view-active-region - (list region))) + (if pdf-view-roll-minor-mode + (cond + ((and (> dy 0) (< (- (window-text-height window t) y) margin)) + (pdf-roll-scroll-forward + (min margin + (or (nth 3 (pos-visible-in-window-p (posn-point pos) window t)) 0)))) + ((and (< dy 0) (< (- y (window-header-line-height window)) margin)) + (pdf-roll-scroll-backward + (min margin + (or (nth 2 (pos-visible-in-window-p (posn-point pos) window t)) 0))))) + (pdf-util-scroll-to-edges iregion)))))) + (cl-callf append (cdr pdf-view-active-region) (list region)) (pdf-view--push-mark)))) (defun pdf-view-mouse-extend-region (event) @@ -1622,18 +1679,19 @@ This is more useful for commands like (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)) + (page (car region)) (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)) + `(,(car colors) ,(cdr colors) 0.35 ,@(cdr region))) (pdf-info-renderpage-text-regions page width nil selection-style nil - `(,(car colors) ,(cdr colors) ,@region))) - :width width)))) + `(,(car colors) ,(cdr colors) ,@(cdr region)))) + :width width) + (when pdf-view-roll-minor-mode page)))) (defun pdf-view-kill-ring-save () "Copy the region to the `kill-ring'." @@ -1648,7 +1706,7 @@ This is more useful for commands like (interactive) (pdf-view-deactivate-region) (setq pdf-view-active-region - (list (list 0 0 1 1))) + (cons (pdf-view-current-page) (list (list 0 0 1 1)))) (pdf-view--push-mark) (pdf-view-display-region)) @@ -1658,10 +1716,10 @@ This is more useful for commands like (mapcar (lambda (edges) (pdf-info-gettext - (pdf-view-current-page) + (car pdf-view-active-region) edges pdf-view-selection-style)) - pdf-view-active-region)) + (cdr pdf-view-active-region))) (defun pdf-view-extract-region-image (regions &optional page size output-buffer no-display-p) @@ -1683,11 +1741,11 @@ the `convert' program is used." (interactive (list (if (pdf-view-active-region-p) (pdf-view-active-region t) - '((0 0 1 1))))) + '(,(pdf-view-current-page) (0 0 1 1))))) (unless page - (setq page (pdf-view-current-page))) + (setq page (car regions))) (unless size - (setq size (pdf-view-image-size))) + (setq size (pdf-view-image-size nil nil page))) (unless output-buffer (setq output-buffer (get-buffer-create "*PDF image*"))) (let* ((images (mapcar (lambda (edges) @@ -1699,7 +1757,7 @@ the `convert' program is used." :crop-to edges) nil file nil 'no-message) file)) - regions)) + (cdr regions))) result) (unwind-protect (progn @@ -1758,23 +1816,25 @@ the selection styles." The optional, boolean args exclude certain attributes." (or pdf-view--bookmark-to-restore - (let ((displayed-p (eq (current-buffer) - (window-buffer)))) + (let ((win (car (cl-find-if #'window-live-p image-mode-winprops-alist + :key #'car-safe)))) (cons (buffer-name) - (append (bookmark-make-record-default nil t 1) + (append (bookmark-make-record-default + nil t (if pdf-view-roll-minor-mode (point) 1)) `(,(unless no-page - (cons 'page (pdf-view-current-page))) + (cons 'page (pdf-view-current-page win))) ,(unless no-slice - (cons 'slice (and displayed-p - (pdf-view-current-slice)))) + (cons 'slice (and win (pdf-view-current-slice win)))) ,(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))) + (and win + (let* ((edges (pdf-util-image-displayed-edges + win (eq (window-buffer win) (current-buffer))))) (pdf-util-scale-pixel-to-relative - (cons (car edges) (cadr edges)) nil t))))) + (cons (car edges) (cadr edges)) nil + (eq (current-buffer) (window-buffer)) win))))) (handler . pdf-view-bookmark-jump-handler))))))) ;;;###autoload @@ -1813,7 +1873,6 @@ See also `pdf-view-bookmark-make-record'." (when-let ((origin (bookmark-prop-get pdf-view--bookmark-to-restore 'origin)) (size (pdf-view-image-size t win))) - (image-set-window-hscroll (round (/ (* (car origin) (car size)) (frame-char-width)))) |
