diff options
Diffstat (limited to 'elpa/pdf-tools-20200512.1524/pdf-tools.el')
-rw-r--r-- | elpa/pdf-tools-20200512.1524/pdf-tools.el | 525 |
1 files changed, 525 insertions, 0 deletions
diff --git a/elpa/pdf-tools-20200512.1524/pdf-tools.el b/elpa/pdf-tools-20200512.1524/pdf-tools.el new file mode 100644 index 0000000..7c9144c --- /dev/null +++ b/elpa/pdf-tools-20200512.1524/pdf-tools.el @@ -0,0 +1,525 @@ +;;; pdf-tools.el --- Support library for PDF documents. -*- lexical-binding:t -*- + +;; Copyright (C) 2013, 2014 Andreas Politz + +;; Author: Andreas Politz <politza@fh-trier.de> +;; Keywords: files, multimedia +;; Package: pdf-tools +;; Version: 1.0 +;; Package-Requires: ((emacs "24.3") (tablist "1.0") (let-alist "1.0.4")) + +;; 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 <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; PDF Tools is, among other things, a replacement of DocView for PDF +;; files. The key difference is, that pages are not prerendered by +;; e.g. ghostscript and stored in the file-system, but rather created +;; on-demand and stored in memory. +;; +;; Note: This package requires external libraries and works currently +;; only on GNU/Linux systems. +;; +;; Note: If you ever update it, you need to restart Emacs afterwards. +;; +;; To activate the package put +;; +;; (pdf-tools-install) +;; +;; somewhere in your .emacs.el . +;; +;; M-x pdf-tools-help RET +;; +;; gives some help on using the package and +;; +;; M-x pdf-tools-customize RET +;; +;; offers some customization options. + +;; Features: +;; +;; * View +;; View PDF documents in a buffer with DocView-like bindings. +;; +;; * Isearch +;; Interactively search PDF documents like any other buffer. (Though +;; there is currently no regexp support.) +;; +;; * Follow links +;; Click on highlighted links, moving to some part of a different +;; page, some external file, a website or any other URI. Links may +;; also be followed by keyboard commands. +;; +;; * Annotations +;; Display and list text and markup annotations (like underline), +;; edit their contents and attributes (e.g. color), move them around, +;; delete them or create new ones and then save the modifications +;; back to the PDF file. +;; +;; * Attachments +;; Save files attached to the PDF-file or list them in a dired buffer. +;; +;; * Outline +;; Use imenu or a special buffer to examine and navigate the PDF's +;; outline. +;; +;; * SyncTeX +;; Jump from a position on a page directly to the TeX source and +;; vice-versa. +;; +;; * Misc +;; + Display PDF's metadata. +;; + Mark a region and kill the text from the PDF. +;; + Search for occurrences of a string. +;; + Keep track of visited pages via a history. + +;;; Code: + +(require 'pdf-view) +(require 'pdf-util) +(require 'pdf-info) +(require 'cus-edit) +(require 'compile) +(require 'cl-lib) +(require 'package) + + + +;; * ================================================================== * +;; * Customizables +;; * ================================================================== * + +(defgroup pdf-tools nil + "Support library for PDF documents." + :group 'data) + +(defgroup pdf-tools-faces nil + "Faces determining the colors used in the pdf-tools package. + +In order to customize dark and light colors use +`pdf-tools-customize-faces', or set `custom-face-default-form' to +'all." + :group 'pdf-tools) + +(defconst pdf-tools-modes + '(pdf-history-minor-mode + pdf-isearch-minor-mode + pdf-links-minor-mode + pdf-misc-minor-mode + pdf-outline-minor-mode + pdf-misc-size-indication-minor-mode + pdf-misc-menu-bar-minor-mode + pdf-annot-minor-mode + pdf-sync-minor-mode + pdf-misc-context-menu-minor-mode + pdf-cache-prefetch-minor-mode + pdf-view-auto-slice-minor-mode + pdf-occur-global-minor-mode + pdf-virtual-global-minor-mode)) + +(defcustom pdf-tools-enabled-modes + '(pdf-history-minor-mode + pdf-isearch-minor-mode + pdf-links-minor-mode + pdf-misc-minor-mode + pdf-outline-minor-mode + pdf-misc-size-indication-minor-mode + pdf-misc-menu-bar-minor-mode + pdf-annot-minor-mode + pdf-sync-minor-mode + pdf-misc-context-menu-minor-mode + pdf-cache-prefetch-minor-mode + pdf-occur-global-minor-mode + ;; pdf-virtual-global-minor-mode + ) + "A list of automatically enabled minor-modes. + +PDF Tools is build as a series of minor-modes. This variable and +the function `pdf-tools-install' merely serve as a convenient +wrapper in order to load these modes in current and newly created +PDF buffers." + :group 'pdf-tools + :type `(set ,@(mapcar (lambda (mode) + `(function-item ,mode)) + pdf-tools-modes))) + +(defcustom pdf-tools-enabled-hook nil + "A hook ran after PDF Tools is enabled in a buffer." + :group 'pdf-tools + :type 'hook) + +(defconst pdf-tools-auto-mode-alist-entry + '("\\.[pP][dD][fF]\\'" . pdf-view-mode) + "The entry to use for `auto-mode-alist'.") + +(defconst pdf-tools-magic-mode-alist-entry + '("%PDF" . pdf-view-mode) + "The entry to use for `magic-mode-alist'.") + +(defun pdf-tools-customize () + "Customize Pdf Tools." + (interactive) + (customize-group 'pdf-tools)) + +(defun pdf-tools-customize-faces () + "Customize PDF Tool's faces." + (interactive) + (let ((buffer (format "*Customize Group: %s*" + (custom-unlispify-tag-name 'pdf-tools-faces)))) + (when (buffer-live-p (get-buffer buffer)) + (with-current-buffer (get-buffer buffer) + (rename-uniquely))) + (customize-group 'pdf-tools-faces) + (with-current-buffer buffer + (set (make-local-variable 'custom-face-default-form) 'all)))) + + +;; * ================================================================== * +;; * Installation +;; * ================================================================== * + +;;;###autoload +(defcustom pdf-tools-handle-upgrades t + "Whether PDF Tools should handle upgrading itself." + :group 'pdf-tools + :type 'boolean) + +(make-obsolete-variable 'pdf-tools-handle-upgrades + "Not used anymore" "0.90") + +(defconst pdf-tools-directory + (or (and load-file-name + (file-name-directory load-file-name)) + default-directory) + "The directory from where this library was first loaded.") + +(defvar pdf-tools-msys2-directory nil) + +(defcustom pdf-tools-installer-os nil + "Specifies which installer to use. + +If nil the installer is chosen automatically. This variable is +useful if you have multiple installers present on your +system (e.g. nix on arch linux)" + :group 'pdf-tools + :type 'string) + +(defun pdf-tools-identify-build-directory (directory) + "Return non-nil, if DIRECTORY appears to contain the epdfinfo source. + +Returns the expanded directory-name of DIRECTORY or nil." + (setq directory (file-name-as-directory + (expand-file-name directory))) + (and (file-exists-p (expand-file-name "autobuild" directory)) + (file-exists-p (expand-file-name "epdfinfo.c" directory)) + directory)) + +(defun pdf-tools-locate-build-directory () + "Attempt to locate a source directory. + +Returns a appropriate directory or nil. See also +`pdf-tools-identify-build-directory'." + (cl-some #'pdf-tools-identify-build-directory + (list default-directory + (expand-file-name "build/server" pdf-tools-directory) + (expand-file-name "server") + (expand-file-name "../server" pdf-tools-directory)))) + +(defun pdf-tools-msys2-directory (&optional noninteractive-p) + "Locate the Msys2 installation directory. + +Ask the user if necessary and NONINTERACTIVE-P is nil. +Returns always nil, unless `system-type' equals windows-nt." + (cl-labels ((if-msys2-directory (directory) + (and (stringp directory) + (file-directory-p directory) + (file-exists-p + (expand-file-name "usr/bin/bash.exe" directory)) + directory))) + (when (eq system-type 'windows-nt) + (setq pdf-tools-msys2-directory + (or pdf-tools-msys2-directory + (cl-some #'if-msys2-directory + (cl-mapcan + (lambda (drive) + (list (format "%c:/msys64" drive) + (format "%c:/msys32" drive))) + (number-sequence ?c ?z))) + (unless (or noninteractive-p + (not (y-or-n-p "Do you have Msys2 installed ? "))) + (if-msys2-directory + (read-directory-name + "Please enter Msys2 installation directory: " nil nil t)))))))) + +(defun pdf-tools-msys2-mingw-bin () + "Return the location of /mingw*/bin." + (when (pdf-tools-msys2-directory) + (let ((arch (intern (car (split-string system-configuration "-" t))))) + (expand-file-name + (format "./mingw%s/bin" (if (eq arch 'x86_64) "64" "32")) + (pdf-tools-msys2-directory))))) + +(defun pdf-tools-find-bourne-shell () + "Locate a usable sh." + (or (and (eq system-type 'windows-nt) + (let* ((directory (pdf-tools-msys2-directory))) + (when directory + (expand-file-name "usr/bin/bash.exe" directory)))) + (executable-find "sh"))) + +(defun pdf-tools-build-server (target-directory + &optional + skip-dependencies-p + force-dependencies-p + callback + build-directory) + "Build the epdfinfo program in the background. + +Install into TARGET-DIRECTORY, which should be a directory. + +If CALLBACK is non-nil, it should be a function. It is called +with the compiled executable as the single argument or nil, if +the build failed. + +Expect sources to be in BUILD-DIRECTORY. If nil, search for it +using `pdf-tools-locate-build-directory'. + +See `pdf-tools-install' for the SKIP-DEPENDENCIES-P and +FORCE-DEPENDENCIES-P arguments. + +Returns the buffer of the compilation process." + + (unless callback (setq callback #'ignore)) + (unless build-directory + (setq build-directory (pdf-tools-locate-build-directory))) + (cl-check-type target-directory file-directory) + (setq target-directory (file-name-as-directory + (expand-file-name target-directory))) + (cl-check-type build-directory (and (not null) file-directory)) + (when (and skip-dependencies-p force-dependencies-p) + (error "Can't simultaneously skip and force dependencies")) + (let* ((compilation-auto-jump-to-first-error nil) + (compilation-scroll-output t) + (shell-file-name (pdf-tools-find-bourne-shell)) + (shell-command-switch "-c") + (process-environment process-environment) + (default-directory build-directory) + (autobuild (shell-quote-argument + (expand-file-name "autobuild" build-directory))) + (msys2-p (equal "bash.exe" (file-name-nondirectory shell-file-name)))) + (unless shell-file-name + (error "No suitable shell found")) + (when msys2-p + (push "BASH_ENV=/etc/profile" process-environment)) + (let ((executable + (expand-file-name + (concat "epdfinfo" (and (eq system-type 'windows-nt) ".exe")) + target-directory)) + (compilation-buffer + (compilation-start + (format "%s -i %s%s%s" + autobuild + (shell-quote-argument target-directory) + (cond + (skip-dependencies-p " -D") + (force-dependencies-p " -d") + (t "")) + (if pdf-tools-installer-os (concat " --os " pdf-tools-installer-os) "")) + t))) + ;; In most cases user-input is required, so select the window. + (if (get-buffer-window compilation-buffer) + (select-window (get-buffer-window compilation-buffer)) + (pop-to-buffer compilation-buffer)) + (with-current-buffer compilation-buffer + (setq-local compilation-error-regexp-alist nil) + (add-hook 'compilation-finish-functions + (lambda (_buffer status) + (funcall callback + (and (equal status "finished\n") + executable))) + nil t) + (current-buffer))))) + + +;; * ================================================================== * +;; * Initialization +;; * ================================================================== * + +;;;###autoload +(defun pdf-tools-install (&optional no-query-p skip-dependencies-p + no-error-p force-dependencies-p) + "Install PDF-Tools in all current and future PDF buffers. + +If the `pdf-info-epdfinfo-program' is not running or does not +appear to be working, attempt to rebuild it. If this build +succeeded, continue with the activation of the package. +Otherwise fail silently, i.e. no error is signaled. + +Build the program (if necessary) without asking first, if +NO-QUERY-P is non-nil. + +Don't attempt to install system packages, if SKIP-DEPENDENCIES-P +is non-nil. + +Do not signal an error in case the build failed, if NO-ERROR-P is +non-nil. + +Attempt to install system packages (even if it is deemed +unnecessary), if FORCE-DEPENDENCIES-P is non-nil. + +Note that SKIP-DEPENDENCIES-P and FORCE-DEPENDENCIES-P are +mutually exclusive. + +Note further, that you can influence the installation directory +by setting `pdf-info-epdfinfo-program' to an appropriate +value (e.g. ~/bin/epdfinfo) before calling this function. + +See `pdf-view-mode' and `pdf-tools-enabled-modes'." + (interactive) + (if (or (pdf-info-running-p) + (ignore-errors (pdf-info-check-epdfinfo) t)) + (pdf-tools-install-noverify) + (let ((target-directory + (or (and (stringp pdf-info-epdfinfo-program) + (file-name-directory + pdf-info-epdfinfo-program)) + pdf-tools-directory))) + (if (or no-query-p + (y-or-n-p "Need to (re)build the epdfinfo program, do it now ?")) + (pdf-tools-build-server + target-directory + skip-dependencies-p + force-dependencies-p + (lambda (executable) + (let ((msg (format + "Building the PDF Tools server %s" + (if executable "succeeded" "failed")))) + (if (not executable) + (funcall (if no-error-p #'message #'error) "%s" msg) + (message "%s" msg) + (setq pdf-info-epdfinfo-program executable) + (let ((pdf-info-restart-process-p t)) + (pdf-tools-install-noverify)))))) + (message "PDF Tools not activated"))))) + +(defun pdf-tools-install-noverify () + "Like `pdf-tools-install', but skip checking `pdf-info-epdfinfo-program'." + (add-to-list 'auto-mode-alist pdf-tools-auto-mode-alist-entry) + (add-to-list 'magic-mode-alist pdf-tools-magic-mode-alist-entry) + ;; FIXME: Generalize this sometime. + (when (memq 'pdf-occur-global-minor-mode + pdf-tools-enabled-modes) + (pdf-occur-global-minor-mode 1)) + (when (memq 'pdf-virtual-global-minor-mode + pdf-tools-enabled-modes) + (pdf-virtual-global-minor-mode 1)) + (add-hook 'pdf-view-mode-hook 'pdf-tools-enable-minor-modes) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (and (not (derived-mode-p 'pdf-view-mode)) + (pdf-tools-pdf-buffer-p) + (buffer-file-name)) + (pdf-view-mode))))) + +(defun pdf-tools-uninstall () + "Uninstall PDF-Tools in all current and future PDF buffers." + (interactive) + (pdf-info-quit) + (setq-default auto-mode-alist + (remove pdf-tools-auto-mode-alist-entry auto-mode-alist)) + (setq-default magic-mode-alist + (remove pdf-tools-magic-mode-alist-entry magic-mode-alist)) + (pdf-occur-global-minor-mode -1) + (pdf-virtual-global-minor-mode -1) + (remove-hook 'pdf-view-mode-hook 'pdf-tools-enable-minor-modes) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (pdf-util-pdf-buffer-p buf) + (pdf-tools-disable-minor-modes pdf-tools-modes) + (normal-mode))))) + +(defun pdf-tools-pdf-buffer-p (&optional buffer) + "Return non-nil if BUFFER contains a PDF document." + (save-current-buffer + (when buffer (set-buffer buffer)) + (save-excursion + (save-restriction + (widen) + (goto-char 1) + (looking-at "%PDF"))))) + +(defun pdf-tools-assert-pdf-buffer (&optional buffer) + (unless (pdf-tools-pdf-buffer-p buffer) + (error "Buffer does not contain a PDF document"))) + +(defun pdf-tools-set-modes-enabled (enable &optional modes) + (dolist (m (or modes pdf-tools-enabled-modes)) + (let ((enabled-p (and (boundp m) + (symbol-value m)))) + (unless (or (and enabled-p enable) + (and (not enabled-p) (not enable))) + (funcall m (if enable 1 -1)))))) + +;;;###autoload +(defun pdf-tools-enable-minor-modes (&optional modes) + "Enable MODES in the current buffer. + +MODES defaults to `pdf-tools-enabled-modes'." + (interactive) + (pdf-util-assert-pdf-buffer) + (pdf-tools-set-modes-enabled t modes) + (run-hooks 'pdf-tools-enabled-hook)) + +(defun pdf-tools-disable-minor-modes (&optional modes) + "Disable MODES in the current buffer. + +MODES defaults to `pdf-tools-enabled-modes'." + (interactive) + (pdf-tools-set-modes-enabled nil modes)) + +(declare-function pdf-occur-global-minor-mode "pdf-occur.el") +(declare-function pdf-virtual-global-minor-mode "pdf-virtual.el") + +;;;###autoload +(defun pdf-tools-help () + (interactive) + (help-setup-xref (list #'pdf-tools-help) + (called-interactively-p 'interactive)) + (with-help-window (help-buffer) + (princ "PDF Tools Help\n\n") + (princ "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n") + (dolist (m (cons 'pdf-view-mode + (sort (copy-sequence pdf-tools-modes) 'string<))) + (princ (format "`%s' is " m)) + (describe-function-1 m) + (terpri) (terpri) + (princ "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n")))) + + +;; * ================================================================== * +;; * Debugging +;; * ================================================================== * + +(defvar pdf-tools-debug nil + "Non-nil, if debugging PDF Tools.") + +(defun pdf-tools-toggle-debug () + (interactive) + (setq pdf-tools-debug (not pdf-tools-debug)) + (when (called-interactively-p 'any) + (message "Toggled debugging %s" (if pdf-tools-debug "on" "off")))) + +(provide 'pdf-tools) + +;;; pdf-tools.el ends here |