Last active
November 13, 2024 04:06
-
-
Save krisbalintona/f4554bb8e53c27c246ae5e3c4ff9b342 to your computer and use it in GitHub Desktop.
Code snippet from my Emacs config. Only tested on Linux; requires the pdftk binary. To use, call `krisb-pdf-tools-metadata-modify` interactively a pdf-view buffer, or invoke it non-interactively with a single argument that is the pdf file path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; -*- lexical-binding: t -*- | |
;; UPDATE 2024-11-12: I am currently in development of pdf-meta-edit.el | |
;; (https://github.com/krisbalintona/pdf-meta-edit) which is meant to be a | |
;; featureful package that covers the use-case below and more. | |
;;; Modify PDF metadata | |
;; Emacs wrapper and convenience functions for changing package metadata using | |
;; pdftk. Only tested on Linux; requires the pdftk binary. See | |
;; https://unix.stackexchange.com/a/72457 for more information on the CLI | |
;; commands involved. | |
;; NOTE: The version of the code below should be evaluated in a lexically-scoped | |
;; buffer. If evaluated in a dynamically-scoped buffer, | |
;; `krisb-pdf-tools-metadata-modify' will not be fully functional because of the | |
;; way it is written. | |
;; | |
;; For more information about using lexical- versus dynamic-binding, see | |
;; (info "(elisp) Using Lisp Binding"), or if you are using emacs-30, | |
;; (info "(elisp) Selecting Lisp Dialect"). | |
;; USAGE: | |
;; | |
;; Code snippet from my Emacs config. Only tested on Linux; requires the pdftk | |
;; binary. To use, call `krisb-pdf-tools-metadata-modify` interactively a | |
;; pdf-view buffer, or invoke it non-interactively with a single argument that | |
;; is the pdf file path. | |
;; | |
;; This creates a new buffer that represents the metadata of the pdf. Modify it | |
;; to your desire. This includes changing the bookmarks (i.e. pdf outline), | |
;; labels (i.e. actual pagination, not pdf page numbers), pagination styles | |
;; (e.g. Roman Numeral), and more. | |
;; | |
;; The major mode for this buffer does not do any fontification, but users can | |
;; easily create their own font locking. | |
;; | |
;; There is single local command, `krisb-pdf-tools-metadata-bookmark-section', | |
;; that creates a bookmark section. It is bound to `C-c C-b'. Creating | |
;; local commands that suit your needs is trivial. | |
;; | |
;; When finished editing the package metadata, press `C-c C-c'. This writes the | |
;; metadata to the file and kills the buffer. If you decide you do not want to | |
;; make any changes to the metadata, simply kill the buffer. | |
;; If there is enough interest in this functionality, I can try formalizing the | |
;; code packaging it on MELPA. For now, this snippet is sufficient for my | |
;; needs. | |
;;;###autoload | |
(defun krisb-pdf-tools-metadata-bookmark-section () | |
"Insert bookmark metadata section." | |
(interactive) | |
(save-excursion | |
(insert "\nBookmarkBegin\nBookmarkTitle: \nBookmarkLevel: 1\nBookmarkPageNumber: ")) | |
(move-end-of-line 2)) | |
(defvar-keymap krisb-pdf-tools-metadata-mode-map | |
:doc "Mode map for `krisb-pdf-tools-metadata-mode'." | |
"C-c C-b" #'krisb-pdf-tools-metadata-bookmark-section) | |
(define-derived-mode krisb-pdf-tools-metadata-mode fundamental-mode "Metadata" | |
"Major mode for altering and viewing PDF metadata." | |
:interactive t | |
(use-local-map krisb-pdf-tools-metadata-mode-map)) | |
;;;###autoload | |
(defun krisb-pdf-tools-metadata-modify (pdf-file) | |
"Modify PDF-FILE metadata." | |
(interactive (list (buffer-file-name))) | |
(unless (string= "pdf" (file-name-extension pdf-file)) | |
(user-error "File is not a PDF!")) | |
(unless (executable-find "pdftk") | |
(error "System executable `pdftk' not found. Please install executable on filesystem to proceed")) | |
(let* ((pdf-name (file-name-sans-extension (file-name-nondirectory pdf-file))) | |
(buf-name (concat "*pdf-tools metadata: " pdf-name)) | |
(metadata-file (concat "/tmp/pdf-tools-metadata--" pdf-name)) | |
(temp-pdf (make-temp-file "/tmp/pdf-tools-metadata--temp-pdf")) | |
(metadata-dump-command (concat "pdftk \"" pdf-file "\" dump_data")) | |
(metadata-update-command | |
(concat "pdftk \"" pdf-file "\" update_info \"" metadata-file "\" output \"" temp-pdf "\"")) | |
(commit-func (lambda () | |
"Commit the changes to PDF metadata." | |
(interactive) | |
(with-current-buffer buf-name | |
(widen) | |
(write-region (point-min) (point-max) metadata-file)) | |
(shell-command metadata-update-command "*pdf-tools metadata: CLI output") | |
(kill-buffer buf-name) | |
;; Have to do it this way since `pdftk' does not allow | |
;; having the output file be the input file | |
(rename-file temp-pdf pdf-file t) | |
(message "Updated metadata!")))) | |
(save-buffer) | |
(with-current-buffer (get-buffer-create buf-name) | |
(insert (shell-command-to-string metadata-dump-command)) | |
(goto-char (point-min)) | |
(krisb-pdf-tools-metadata-mode)) | |
(pop-to-buffer buf-name) | |
(define-key krisb-pdf-tools-metadata-mode-map (kbd "C-c C-c") commit-func) | |
(set-buffer-modified-p nil) | |
(message (substitute-command-keys "Press `C-c C-c' when finished editing PDF metadata. To see keybinds, press \\[describe-mode]")))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment