-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpdf-tools-pages.el
More file actions
150 lines (124 loc) · 5.76 KB
/
pdf-tools-pages.el
File metadata and controls
150 lines (124 loc) · 5.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
;;; pdf-tools-pages.el --- Extract and delete pages in PDF documents -*- lexical-binding: t -*-
;; Copyright (C) 2024-2026 Pablo Stafforini
;; Author: Pablo Stafforini
;; URL: https://github.com/benthamite/pdf-tools-pages
;; Version: 0.2
;; Package-Requires: ((emacs "26.1") (pdf-tools "1.0"))
;; This file is NOT part of GNU Emacs.
;; 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:
;; Simple `pdf-tools' extension to extract and delete pages in PDF documents.
;;; Code:
(require 'cl-lib)
(require 'pdf-tools)
;;;; Variables
(defvar-local pdf-tools-pages-selected-pages '()
"List of pages selected for extraction.
This variable is buffer-local so that each PDF buffer maintains its own
independent page selection.")
;;;; Functions
;;;;; Ensure
(defun pdf-tools-pages-ensure-pdf-view-mode ()
"Signal an error unless the current buffer is in `pdf-view-mode'."
(unless (derived-mode-p 'pdf-view-mode)
(user-error "This command can only be used in a `pdf-view-mode' buffer")))
(defun pdf-tools-pages-ensure-qpdf ()
"Signal an error unless `qpdf' is installed and available."
(unless (executable-find "qpdf")
(user-error "This package requires `qpdf' (https://github.com/qpdf/qpdf)")))
(defun pdf-tools-pages-ensure-selection ()
"Signal an error unless there are pages selected."
(unless pdf-tools-pages-selected-pages
(user-error "No pages selected")))
;;;;; Selection
;;;###autoload
(defun pdf-tools-pages-select-dwim ()
"Add current page to the selection, or remove it if already included.
After toggling, advance to the next page if not on the last page."
(interactive)
(pdf-tools-pages-ensure-pdf-view-mode)
(pdf-tools-pages-ensure-qpdf)
(if (member (pdf-view-current-page) pdf-tools-pages-selected-pages)
(pdf-tools-pages-remove-page)
(pdf-tools-pages-add-page))
(when (< (pdf-view-current-page) (pdf-cache-number-of-pages))
(pdf-view-next-page)))
(defun pdf-tools-pages-add-page ()
"Add current page number to list of selected pages."
(let ((page (pdf-view-current-page)))
(unless (member page pdf-tools-pages-selected-pages)
(push page pdf-tools-pages-selected-pages)
(setq pdf-tools-pages-selected-pages
(sort pdf-tools-pages-selected-pages #'<))))
(message (concat "Page added. " (pdf-tools-pages-get-current-selection))))
(defun pdf-tools-pages-remove-page ()
"Remove current page number from list of selected pages."
(setq pdf-tools-pages-selected-pages
(delq (pdf-view-current-page) pdf-tools-pages-selected-pages))
(message (concat "Page removed. "
(if pdf-tools-pages-selected-pages
(pdf-tools-pages-get-current-selection)
"There are currently no selected pages."))))
(defun pdf-tools-pages-get-current-selection ()
"Return the current selection of pages as a string."
(format "Current selection: %s." pdf-tools-pages-selected-pages))
;;;###autoload
(defun pdf-tools-pages-clear-page-selection ()
"Clear the list of pages selected in `pdf-tools-pages-selected-pages'."
(interactive)
(setq pdf-tools-pages-selected-pages '())
(message "Page selection cleared."))
;;;;; Extraction & deletion
;;;###autoload
(defun pdf-tools-pages-extract-selected-pages (file)
"Save pages selected in `pdf-tools-pages-selected-pages' to FILE."
(interactive "FOutput file: ")
(pdf-tools-pages-ensure-pdf-view-mode)
(pdf-tools-pages-ensure-selection)
(pdf-tools-pages-execute-qpdf pdf-tools-pages-selected-pages file)
(pdf-tools-pages-clear-page-selection))
;;;###autoload
(defun pdf-tools-pages-delete-selected-pages ()
"Delete pages selected in `pdf-tools-pages-selected-pages' from current file."
(interactive)
(pdf-tools-pages-ensure-pdf-view-mode)
(pdf-tools-pages-ensure-selection)
(when (yes-or-no-p (concat (pdf-tools-pages-get-current-selection)
" Delete selected pages from current PDF? "))
(let ((pages-to-keep (cl-set-difference
(number-sequence 1 (pdf-cache-number-of-pages))
pdf-tools-pages-selected-pages)))
(pdf-tools-pages-execute-qpdf pages-to-keep)
(revert-buffer nil t)
(pdf-tools-pages-clear-page-selection)
(message "Selected pages deleted."))))
(defun pdf-tools-pages-execute-qpdf (pages &optional output-file)
"Execute qpdf command on PAGES with optional OUTPUT-FILE.
If OUTPUT-FILE is nil, modify the input file in place.
Uses `call-process' to avoid shell injection vulnerabilities."
(let* ((input-file (buffer-file-name))
(page-spec (mapconcat #'number-to-string pages ","))
(args (if output-file
(list input-file "--pages" "." page-spec "--"
(expand-file-name output-file))
(list input-file "--pages" "." page-spec "--"
"--replace-input")))
(exit-code (apply #'call-process "qpdf" nil nil nil args)))
;; qpdf exit code 0 = success, 3 = warnings (operation succeeded).
;; Any other code indicates failure.
(unless (memq exit-code '(0 3))
(user-error "qpdf failed (exit code %d); check *Messages* for details"
exit-code))))
(provide 'pdf-tools-pages)
;;; pdf-tools-pages.el ends here