diff options
author | Simon Parri <simonparri@ganzeria.com> | 2025-08-14 10:03:14 -0500 |
---|---|---|
committer | Simon Parri <simonparri@ganzeria.com> | 2025-08-14 10:03:14 -0500 |
commit | d66ee8f9ab3c8966ea95aa80b39c5578afee9d0a (patch) | |
tree | f9534054abb3711a4cb834c07f0206d6c64d4758 | |
download | pdf-view-reader-0.50.tar.gz pdf-view-reader-0.50.zip |
Add version 0.50v0.50
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | pdf-view-reader.el | 203 |
2 files changed, 204 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b25c15b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/pdf-view-reader.el b/pdf-view-reader.el new file mode 100644 index 0000000..f5b1265 --- /dev/null +++ b/pdf-view-reader.el @@ -0,0 +1,203 @@ +;;; pdf-view-reader.el --- Quality-of-life utils for using PDF-Tools as a PDF viewer -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Simon Parri + +;; Author: Simon Parri <simonparri@ganzeria.com> +;; Keywords: multimedia, convenience +;; Version: 0.50 +;; Package-Requires: ((emacs "24.3") (compat "29.1") (pdf-tools "1")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This package comes in 3 parts: + +;; 1. pdf-view-desktop :: `desktop-mode' integration for `pdf-tools'. Call +;; `pdf-view-desktop-setup' to install the appropriate hooks. + +;; 2. pdf-view-pages :: Dual-page mode for `pdf-tools'. Enable +;; `pdf-view-pages-mode' mode to get behavior similar to `follow-mode' for +;; normal buffers or call `pdf-view-book' to toggle both +;; `pdf-view-pages-mode' and a dual-page window configuration. + +;; 3. pdf-view-offset :: Page offset for `pdf-tools'. Useful for when the +;; page numbers on the PDF pages don't match the real page numbers. +;; Enabling `pdf-view-offset-mode' will prompt you for the page number +;; printed on the page, and will calculate and set `pdf-view-offset' +;; appropriately. `pdf-view-offset-set' will instead prompt you for the +;; offset directly, and will also enable `pdf-view-offset-mode' afterwards. + +;;; Code: + +(require 'cl-lib) +(require 'pdf-tools) + +;; Desktop +(defun pdf-view--save-buffer (_desktop) + ;; This is an alist just in case we want more information here + ;; later. As long as `pdf-view-current-page' is a macro, keep + ;; this expansion up to date. + "`desktop-mode' function to save `pdf-view-mode' buffers." + `((page . ,(image-mode-window-get 'page)))) +(defun pdf-view--restore-buffer (file buffer misc) + "`desktop-mode' function to restore `pdf-view-mode' buffers. + +See `(elisp) Desktop Save Mode' for meanings of FILE, BUFFER and MISC." + (when-let ((buf (desktop-restore-file-buffer file buffer misc))) + (with-current-buffer buf + (pdf-view-goto-page (cdr (assq 'page misc))) + buf))) + +;;;###autoload +(defun pdf-view-desktop-setup () + "Set up `desktop-mode' integration for `pdf-view-mode'." + (add-to-list 'desktop-buffer-mode-handlers + '(pdf-view-mode . pdf-view--restore-buffer)) + (add-hook 'pdf-view-mode-hook + (lambda () + (setq desktop-save-buffer + #'pdf-view--save-buffer)))) + +;; Multi-page +;; TODO: Should use advice? + + (defun pdf-view-pages-windows (&optional buffer) + (let ((b (or buffer (current-buffer)))) + (with-current-buffer b + (unless (derived-mode-p 'pdf-view-mode) + (error "Buffer is not a pdf-view buffer: %s" b)) + (seq-filter (lambda (x) (eq (window-buffer x) b)) + (window-list))))) + +(cl-macrolet ((m (dir page) + `(defun ,(intern (format "pdf-view-pages-%s" dir)) (&optional n) + (let ((ww (pdf-view-pages-windows))) + (dolist (w ww) + (with-selected-window w + (ignore-errors + (pdf-view-next-page ,page)))))))) + (m next (or n (length ww))) + (m previous (- (or n (length ww))))) + +(cl-macrolet ((m (dir) + `(defun ,(intern (format "pdf-view-pages-%s-command" dir)) (n) + (declare (interactive-only t)) + (interactive "P" pdf-view-pages-mode) + (,(intern (format "pdf-view-pages-%s" dir)) n)))) + (m next) + (m previous)) + +(defun pdf-view-pages-goto-page (n) + "Go to page N in the current window, scrolling others by the same amount." + (interactive "NPage: " pdf-view-pages-mode) + (let ((s (pdf-view-current-page))) + (dolist (w (pdf-view-pages-windows)) + (with-selected-window w + (let ((d (- (pdf-view-current-page) s))) + (pdf-view-goto-page (+ n d))))))) + +(defvar-keymap pdf-view-pages-mode-map + "<remap> <pdf-view-next-page-command>" #'pdf-view-pages-next-command + "<remap> <pdf-view-previous-page-command>" #'pdf-view-pages-previous-command + "<remap> <pdf-view-scroll-up-or-next-page>" #'pdf-view-pages-next-command + "<remap> <pdf-view-scroll-down-or-previous-page>" #'pdf-view-pages-previous-command + "<remap> <pdf-view-goto-page>" #'pdf-view-pages-goto-page) + +(define-minor-mode pdf-view-pages-mode + "Minor mode for a multi-page view in `pdf-view-mode'." + :lighter " Pages" + :interactive (pdf-view-mode)) + +(defun pdf-view-book-layout-p (win) + "Return t if WIN has nearby windows such that the layout is book-like. + +Book-like means that WIN displays the same buffer as the window to the +right, and that the buffer in question has `pdf-view-pages-mode' +enabled." + (let* ((b (window-buffer win)) + (pdf? + (with-current-buffer b + (derived-mode-p 'pdf-view-mode))) + (pages? + (with-current-buffer b + pdf-view-pages-mode)) + (ow (window-in-direction 'right))) + (and ow pdf? pages? + (eq b (window-buffer ow)) + ow))) + +(defun pdf-view-book (&optional win) + "Set up WIN (or current window, if nil) in a book-like layout. + +Book-like means that WIN will display the same buffer as the window to +the right (which will be created), and that the buffer in question will +have `pdf-view-pages-mode' enabled." + (interactive nil pdf-view-mode) + (let ((win (or win (selected-window)))) + (if-let ((ow (pdf-view-book-layout-p win))) + (progn + (delete-window ow) + (pdf-view-pages-mode -1)) + (with-selected-window (split-window-right) + (call-interactively #'pdf-view-next-page-command)) + (pdf-view-pages-mode 1)) + (with-selected-window win + (pdf-view-fit-height-to-window)))) + +;; Offset +;; TODO: Should use advice? + +(defvar-local pdf-view-offset 0 + "Current page offset.") +(add-to-list 'desktop-locals-to-save 'pdf-view-offset) + +(defun pdf-view-offset-goto-page (n) + "Go to page N, offset according to `pdf-view-offset'." + (interactive "NPage: " pdf-view-offset-mode) + (funcall (if pdf-view-pages-mode + #'pdf-view-pages-goto-page + #'pdf-view-goto-page) + (+ n pdf-view-offset))) + +(defun pdf-view-offset-first-page () + "Go to page 0, taking `pdf-view-offset' into account." + (interactive nil pdf-view-offset-mode) + (pdf-view-offset-goto-page 0)) + +(defvar-keymap pdf-view-offset-mode-map + "<remap> <pdf-view-goto-page>" #'pdf-view-offset-goto-page + "<remap> <goto-line>" #'pdf-view-offset-goto-page + "<remap> <pdf-view-first-page>" #'pdf-view-offset-first-page) + +(define-minor-mode pdf-view-offset-mode + "Minor mode to have an offset for PDF pages in `pdf-view-mode'." + :lighter (:eval (format " Off(%d)" pdf-view-offset)) + :interactive (pdf-view-mode) + (if pdf-view-offset-mode + (thread-last + (read-number "Current page: " (pdf-view-current-page)) + (- (pdf-view-current-page)) + (setq pdf-view-offset)) + (setq pdf-view-offset 0))) + +(defun pdf-view-offset-set (n) + "Set offset for `pdf-view-offset-mode' to N." + (interactive "NOffset: " pdf-view-mode) + (unless pdf-view-offset-mode + (pdf-view-offset-mode 1)) + (setq pdf-view-offset n)) + +(provide 'pdf-view-reader) +;;; pdf-view-reader.el ends here |