branch: elpa/pdf-tools
commit 2603233d2b3814c75c762783327f1fd633f82549
Author: Vedang Manerikar <[email protected]>
Commit: Vedang Manerikar <[email protected]>

    feat: integrate pdf-view-roll-minor-mode with existing features
    
    Update all pdf-tools features to work correctly when
    pdf-view-roll-minor-mode is active:
    
    - pdf-isearch: Search across all displayed pages simultaneously
    - pdf-links: Link selection works with multiple visible pages
    - pdf-annot: Annotations work on correct page, add pdf-annot-edit
      command for avy-style annotation editing
    - pdf-sync: SyncTeX jumps work with continuous scroll mode
    - pdf-view: Display and navigation functions updated for multi-page
    - pdf-util: Coordinate transformation updates for visible pages
    - pdf-cache: Simplify prefetch (remove timer indirection)
    - pdf-occur: Minor compatibility updates
    
    The mode is experimental. Some features may not interact perfectly
    with roll mode. Enable with M-x pdf-view-roll-minor-mode.
    
    API changes:
    - pdf-view-active-region now includes page information
    - isearch results include page data for multi-page display
    
    Co-authored-by: Daniel Nicolai <[email protected]>
    Co-authored-by: Rahguzar <[email protected]>
    Co-authored-by: Ihor Radchenko <[email protected]>
    Co-authored-by: NightMachinery <[email protected]>
---
 lisp/pdf-annot.el   |  83 ++++++++++++++++--
 lisp/pdf-cache.el   |   5 +-
 lisp/pdf-isearch.el | 165 ++++++++++++++++++++---------------
 lisp/pdf-links.el   |  96 ++++++++++-----------
 lisp/pdf-occur.el   |   6 +-
 lisp/pdf-sync.el    |  19 +++--
 lisp/pdf-util.el    | 137 ++++++++++++++++-------------
 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 0abad53d41a..4c87e24c8ad 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 a4f0d60801e..57c824fcaed 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 f5468eb56a6..9ffc2e41df3 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 d54981e0019..3542085471c 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 47bf2d29d03..ea5fa1740ee 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 68de3b90598..18fd6e00e22 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 5c0e50d6450..8886de3c7a9 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 620e5269d77..d1ff32f9b60 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))))

Reply via email to