diff options
Diffstat (limited to 'elpa/pdf-tools-20200512.1524/pdf-util.el')
-rw-r--r-- | elpa/pdf-tools-20200512.1524/pdf-util.el | 1346 |
1 files changed, 0 insertions, 1346 deletions
diff --git a/elpa/pdf-tools-20200512.1524/pdf-util.el b/elpa/pdf-tools-20200512.1524/pdf-util.el deleted file mode 100644 index cdb0f26..0000000 --- a/elpa/pdf-tools-20200512.1524/pdf-util.el +++ /dev/null @@ -1,1346 +0,0 @@ -;;; pdf-util.el --- PDF Utility functions. -*- lexical-binding: t -*- - -;; Copyright (C) 2013, 2014 Andreas Politz - -;; Author: Andreas Politz <politza@fh-trier.de> -;; Keywords: files, multimedia - -;; 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: -;; -;;; Todo: -;; - -;;; Code: - -(require 'cl-lib) -(require 'format-spec) -(require 'faces) - -;; These functions are only used after a PdfView window was asserted, -;; which won't succeed, if pdf-view.el isn't loaded. -(declare-function pdf-view-image-size "pdf-view") -(declare-function pdf-view-image-offset "pdf-view") -(declare-function pdf-view-current-image "pdf-view") -(declare-function pdf-view-current-overlay "pdf-view") -(declare-function pdf-cache-pagesize "pdf-cache") - -(declare-function pdf-view-image-type "pdf-view") - - - -;; * ================================================================== * -;; * Compatibility with older Emacssen (< 25.1) -;; * ================================================================== * - -;; The with-file-modes macro is only available in recent Emacs -;; versions. -(eval-when-compile - (unless (fboundp 'with-file-modes) - (defmacro with-file-modes (modes &rest body) - "Execute BODY with default file permissions temporarily set to MODES. -MODES is as for `set-default-file-modes'." - (declare (indent 1) (debug t)) - (let ((umask (make-symbol "umask"))) - `(let ((,umask (default-file-modes))) - (unwind-protect - (progn - (set-default-file-modes ,modes) - ,@body) - (set-default-file-modes ,umask))))))) - -(unless (fboundp 'alist-get) ;;25.1 - (defun alist-get (key alist &optional default remove) - "Get the value associated to KEY in ALIST. -DEFAULT is the value to return if KEY is not found in ALIST. -REMOVE, if non-nil, means that when setting this element, we should -remove the entry if the new value is `eql' to DEFAULT." - (ignore remove) ;;Silence byte-compiler. - (let ((x (assq key alist))) - (if x (cdr x) default)))) - -(require 'register) -(unless (fboundp 'register-read-with-preview) - (defalias 'register-read-with-preview 'read-char - "Compatibility alias for pdf-tools.")) - -;; In Emacs 24.3 window-width does not have a PIXELWISE argument. -(defmacro pdf-util-window-pixel-width (&optional window) - "Return the width of WINDOW in pixel." - (if (< (cdr (subr-arity (symbol-function 'window-body-width))) 2) - (let ((window* (make-symbol "window"))) - `(let ((,window* ,window)) - (* (window-body-width ,window*) - (frame-char-width (window-frame ,window*))))) - `(window-body-width ,window t))) - -;; In Emacs 24.3 image-mode-winprops leads to infinite recursion. -(unless (or (> emacs-major-version 24) - (and (= emacs-major-version 24) - (>= emacs-minor-version 4))) - (require 'image-mode) - (defvar image-mode-winprops-original-function - (symbol-function 'image-mode-winprops)) - (eval-after-load "image-mode" - '(defun image-mode-winprops (&optional window cleanup) - (if (not (eq major-mode 'pdf-view-mode)) - (funcall image-mode-winprops-original-function - window cleanup) - (cond ((null window) - (setq window - (if (eq (current-buffer) (window-buffer)) (selected-window) t))) - ((eq window t)) - ((not (windowp window)) - (error "Not a window: %s" window))) - (when cleanup - (setq image-mode-winprops-alist - (delq nil (mapcar (lambda (winprop) - (let ((w (car-safe winprop))) - (if (or (not (windowp w)) (window-live-p w)) - winprop))) - image-mode-winprops-alist)))) - (let ((winprops (assq window image-mode-winprops-alist))) - ;; For new windows, set defaults from the latest. - (if winprops - ;; Move window to front. - (setq image-mode-winprops-alist - (cons winprops (delq winprops image-mode-winprops-alist))) - (setq winprops (cons window - (copy-alist (cdar image-mode-winprops-alist)))) - ;; Add winprops before running the hook, to avoid inf-loops if the hook - ;; triggers window-configuration-change-hook. - (setq image-mode-winprops-alist - (cons winprops image-mode-winprops-alist)) - (run-hook-with-args 'image-mode-new-window-functions winprops)) - winprops))))) - - - -;; * ================================================================== * -;; * Transforming coordinates -;; * ================================================================== * - - -(defun pdf-util-scale (list-of-edges-or-pos scale &optional rounding-fn) - "Scale LIST-OF-EDGES-OR-POS by SCALE. - -SCALE is a cons (SX . SY), by which edges/positions are scaled. -If ROUNDING-FN is non-nil, it should be a function of one -argument, a real value, returning a rounded -value (e.g. `ceiling'). - -The elements in LIST-OF-EDGES-OR-POS should be either a list -\(LEFT TOP RIGHT BOT\) or a position \(X . Y\). - -LIST-OF-EDGES-OR-POS may also be a single such element. - -Return scaled list of edges if LIST-OF-EDGES-OR-POS was indeed a list, -else return the scaled singleton." - - (let ((have-list-p (listp (car list-of-edges-or-pos)))) - (unless have-list-p - (setq list-of-edges-or-pos (list list-of-edges-or-pos))) - (let* ((sx (car scale)) - (sy (cdr scale)) - (result - (mapcar - (lambda (edges) - (cond - ((consp (cdr edges)) - (let ((e (list (* (nth 0 edges) sx) - (* (nth 1 edges) sy) - (* (nth 2 edges) sx) - (* (nth 3 edges) sy)))) - (if rounding-fn - (mapcar rounding-fn e) - e))) - (rounding-fn - (cons (funcall rounding-fn (* (car edges) sx)) - (funcall rounding-fn (* (cdr edges) sy)))) - (t - (cons (* (car edges) sx) - (* (cdr edges) sy))))) - list-of-edges-or-pos))) - (if have-list-p - result - (car result))))) - -(defun pdf-util-scale-to (list-of-edges from to &optional rounding-fn) - "Scale LIST-OF-EDGES in FROM basis to TO. - -FROM and TO should both be a cons \(WIDTH . HEIGHT\). See also -`pdf-util-scale'." - - (pdf-util-scale list-of-edges - (cons (/ (float (car to)) - (float (car from))) - (/ (float (cdr to)) - (float (cdr from)))) - rounding-fn)) - -(defun pdf-util-scale-pixel-to-points (list-of-pixel-edges - &optional rounding-fn displayed-p window) - "Scale LIST-OF-PIXEL-EDGES to point values. - -The result depends on the currently displayed page in WINDOW. -See also `pdf-util-scale'." - (pdf-util-assert-pdf-window window) - (pdf-util-scale-to - list-of-pixel-edges - (pdf-view-image-size displayed-p window) - (pdf-cache-pagesize (pdf-view-current-page window)) - rounding-fn)) - -(defun pdf-util-scale-points-to-pixel (list-of-points-edges - &optional rounding-fn displayed-p window) - "Scale LIST-OF-POINTS-EDGES to point values. - -The result depends on the currently displayed page in WINDOW. -See also `pdf-util-scale'." - (pdf-util-assert-pdf-window window) - (pdf-util-scale-to - list-of-points-edges - (pdf-cache-pagesize (pdf-view-current-page window)) - (pdf-view-image-size displayed-p window) - rounding-fn)) - -(defun pdf-util-scale-relative-to-points (list-of-relative-edges - &optional rounding-fn window) - "Scale LIST-OF-RELATIVE-EDGES to point values. - -The result depends on the currently displayed page in WINDOW. -See also `pdf-util-scale'." - (pdf-util-assert-pdf-window window) - (pdf-util-scale-to - list-of-relative-edges - '(1.0 . 1.0) - (pdf-cache-pagesize (pdf-view-current-page window)) - rounding-fn)) - -(defun pdf-util-scale-points-to-relative (list-of-points-edges - &optional rounding-fn window) - "Scale LIST-OF-POINTS-EDGES to relative values. - -See also `pdf-util-scale'." - (pdf-util-assert-pdf-window window) - (pdf-util-scale-to - list-of-points-edges - (pdf-cache-pagesize (pdf-view-current-page window)) - '(1.0 . 1.0) - rounding-fn)) - -(defun pdf-util-scale-pixel-to-relative (list-of-pixel-edges - &optional rounding-fn displayed-p window) - "Scale LIST-OF-PIXEL-EDGES to relative values. - -The result depends on the currently displayed page in WINDOW. -See also `pdf-util-scale'." - (pdf-util-assert-pdf-window window) - (pdf-util-scale-to - list-of-pixel-edges - (pdf-view-image-size displayed-p window) - '(1.0 . 1.0) - rounding-fn)) - - -(defun pdf-util-scale-relative-to-pixel (list-of-relative-edges - &optional rounding-fn displayed-p window) - "Scale LIST-OF-EDGES to match SIZE. - -The result depends on the currently displayed page in WINDOW. -See also `pdf-util-scale'." - (pdf-util-assert-pdf-window window) - (pdf-util-scale-to - list-of-relative-edges - '(1.0 . 1.0) - (pdf-view-image-size displayed-p window) - rounding-fn)) - -(defun pdf-util-translate (list-of-edges-or-pos - offset &optional opposite-direction-p) - "Translate LIST-OF-EDGES-OR-POS by OFFSET - -OFFSET should be a cons \(X . Y\), by which to translate -LIST-OF-EDGES-OR-POS. If OPPOSITE-DIRECTION-P is non-nil -translate by \(-X . -Y\). - -See `pdf-util-scale' for the LIST-OF-EDGES-OR-POS argument." - - (let ((have-list-p (listp (car list-of-edges-or-pos)))) - (unless have-list-p - (setq list-of-edges-or-pos (list list-of-edges-or-pos))) - (let* ((ox (if opposite-direction-p - (- (car offset)) - (car offset))) - (oy (if opposite-direction-p - (- (cdr offset)) - (cdr offset))) - (result - (mapcar - (lambda (edges) - (cond - ((consp (cdr edges)) - (list (+ (nth 0 edges) ox) - (+ (nth 1 edges) oy) - (+ (nth 2 edges) ox) - (+ (nth 3 edges) oy))) - (t - (cons (+ (car edges) ox) - (+ (cdr edges) oy))))) - list-of-edges-or-pos))) - (if have-list-p - result - (car result))))) - -(defun pdf-util-edges-transform (region elts &optional to-region-p) - "Translate ELTS according to REGION. - -ELTS may be one edges list or a position or a list thereof. -Translate each from region coordinates to (0 0 1 1) or the -opposite, if TO-REGION-P is non-nil. All coordinates should be -relative. - -Returns the translated list of elements or the single one -depending on the input." - - (when elts - (let ((have-list-p (consp (car-safe elts)))) - (unless have-list-p - (setq elts (list elts))) - (let ((result - (if (null region) - elts - (mapcar (lambda (edges) - (let ((have-pos-p (numberp (cdr edges)))) - (when have-pos-p - (setq edges (list (car edges) (cdr edges) - (car edges) (cdr edges)))) - (pdf-util-with-edges (edges region) - (let ((newedges - (mapcar (lambda (n) - (min 1.0 (max 0.0 n))) - (if to-region-p - `(,(/ (- edges-left region-left) - region-width) - ,(/ (- edges-top region-top) - region-height) - ,(/ (- edges-right region-left) - region-width) - ,(/ (- edges-bot region-top) - region-height)) - `(,(+ (* edges-left region-width) - region-left) - ,(+ (* edges-top region-height) - region-top) - ,(+ (* edges-right region-width) - region-left) - ,(+ (* edges-bot region-height) - region-top)))))) - (if have-pos-p - (cons (car newedges) (cadr newedges)) - newedges))))) - elts)))) - (if have-list-p - result - (car result)))))) - -(defmacro pdf-util-with-edges (list-of-edges &rest body) - "Provide some convenient macros for the edges in LIST-OF-EDGES. - -LIST-OF-EDGES should be a list of variables \(X ...\), each one -holding a list of edges. Inside BODY the symbols X-left, X-top, -X-right, X-bot, X-width and X-height expand to their respective -values." - - (declare (indent 1) (debug (sexp &rest form))) - (unless (cl-every 'symbolp list-of-edges) - (error "Argument should be a list of symbols")) - (let ((list-of-syms - (mapcar (lambda (edge) - (cons edge (mapcar - (lambda (kind) - (intern (format "%s-%s" edge kind))) - '(left top right bot width height)))) - list-of-edges))) - (macroexpand-all - `(cl-symbol-macrolet - ,(apply 'nconc - (mapcar - (lambda (edge-syms) - (let ((edge (nth 0 edge-syms)) - (syms (cdr edge-syms))) - `((,(pop syms) (nth 0 ,edge)) - (,(pop syms) (nth 1 ,edge)) - (,(pop syms) (nth 2 ,edge)) - (,(pop syms) (nth 3 ,edge)) - (,(pop syms) (- (nth 2 ,edge) - (nth 0 ,edge))) - (,(pop syms) (- (nth 3 ,edge) - (nth 1 ,edge)))))) - list-of-syms)) - ,@body)))) - - -;; * ================================================================== * -;; * Scrolling -;; * ================================================================== * - -(defun pdf-util-image-displayed-edges (&optional window displayed-p) - "Return the visible region of the image in WINDOW. - -Returns a list of pixel edges." - (pdf-util-assert-pdf-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) - (frame-char-width (window-frame window)))) - (vscroll (window-vscroll window t)) - (x0 (+ hscroll (car offset))) - (y0 (+ vscroll (cdr offset))) - (x1 (min (car isize) - (+ x0 (- (nth 2 edges) (nth 0 edges))))) - (y1 (min (cdr isize) - (+ y0 (- (nth 3 edges) (nth 1 edges)))))) - (mapcar 'round (list x0 y0 x1 y1)))) - -(defun pdf-util-required-hscroll (edges &optional eager-p context-pixel) - "Return the amount of scrolling necessary, to make image EDGES visible. - -Scroll as little as necessary. Unless EAGER-P is non-nil, in -which case scroll as much as possible. - -Keep CONTEXT-PIXEL pixel of the image visible at the bottom and -top of the window. CONTEXT-PIXEL defaults to 0. - -Return the required hscroll in columns or nil, if scrolling is not -needed." - - (pdf-util-assert-pdf-window) - (unless context-pixel - (setq context-pixel 0)) - (let* ((win (window-inside-pixel-edges)) - (image-width (car (pdf-view-image-size t))) - (image-left (* (frame-char-width) - (window-hscroll))) - (edges (pdf-util-translate - edges - (pdf-view-image-offset) t))) - (pdf-util-with-edges (win edges) - (let* ((edges-left (- edges-left context-pixel)) - (edges-right (+ edges-right context-pixel))) - (if (< edges-left image-left) - (round (/ (max 0 (if eager-p - (- edges-right win-width) - edges-left)) - (frame-char-width))) - (if (> (min image-width - edges-right) - (+ image-left win-width)) - (round (/ (min (- image-width win-width) - (if eager-p - edges-left - (- edges-right win-width))) - (frame-char-width))))))))) - -(defun pdf-util-required-vscroll (edges &optional eager-p context-pixel) - "Return the amount of scrolling necessary, to make image EDGES visible. - -Scroll as little as necessary. Unless EAGER-P is non-nil, in -which case scroll as much as possible. - -Keep CONTEXT-PIXEL pixel of the image visible at the bottom and -top of the window. CONTEXT-PIXEL defaults to an equivalent pixel -value of `next-screen-context-lines'. - -Return the required vscroll in lines or nil, if scrolling is not -needed." - - (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))) - (if (< edges-top image-top) - (round (/ (max 0 (if eager-p - (- edges-bot win-height) - edges-top)) - (float (frame-char-height)))) - (if (> (min image-height - edges-bot) - (+ image-top win-height)) - (round (/ (min (- image-height win-height) - (if eager-p - edges-top - (- edges-bot win-height))) - (float (frame-char-height)))))))))) - -(defun pdf-util-scroll-to-edges (edges &optional eager-p) - "Scroll window such that image EDGES are visible. - -Scroll as little as necessary. Unless EAGER-P is non-nil, in -which case scroll as much as possible." - - (let ((vscroll (pdf-util-required-vscroll edges eager-p)) - (hscroll (pdf-util-required-hscroll edges eager-p))) - (when vscroll - (image-set-window-vscroll vscroll)) - (when hscroll - (image-set-window-hscroll hscroll)))) - - - -;; * ================================================================== * -;; * Temporary files -;; * ================================================================== * - -(defvar pdf-util--base-directory nil - "Base directory for temporary files.") - -(defvar-local pdf-util--dedicated-directory nil - "The relative name of buffer's dedicated directory.") - -(defun pdf-util-dedicated-directory () - "Return the name of a existing dedicated directory. - -The directory is exclusive to the current buffer. It will be -automatically deleted, if Emacs or the current buffer are -killed." - (with-file-modes #o0700 - (unless (and pdf-util--base-directory - (file-directory-p - pdf-util--base-directory) - (not (file-symlink-p - pdf-util--base-directory))) - (add-hook 'kill-emacs-hook - (lambda nil - (when (and pdf-util--base-directory - (file-directory-p pdf-util--base-directory)) - (delete-directory pdf-util--base-directory t)))) - (setq pdf-util--base-directory - (make-temp-file "pdf-tools-" t))) - (unless (and pdf-util--dedicated-directory - (file-directory-p pdf-util--dedicated-directory) - (not (file-symlink-p - pdf-util--base-directory))) - (let ((temporary-file-directory - pdf-util--base-directory)) - (setq pdf-util--dedicated-directory - (make-temp-file (convert-standard-filename - (concat (if buffer-file-name - (file-name-nondirectory - buffer-file-name) - (buffer-name)) - "-")) - t)) - (add-hook 'kill-buffer-hook 'pdf-util-delete-dedicated-directory - nil t))) - pdf-util--dedicated-directory)) - -(defun pdf-util-delete-dedicated-directory () - "Delete current buffer's dedicated directory." - (delete-directory (pdf-util-dedicated-directory) t)) - -(defun pdf-util-expand-file-name (name) - "Expand filename against current buffer's dedicated directory." - (expand-file-name name (pdf-util-dedicated-directory))) - -(defun pdf-util-make-temp-file (prefix &optional dir-flag suffix) - "Create a temporary file in current buffer's dedicated directory. - -See `make-temp-file' for the arguments." - (let ((temporary-file-directory - (pdf-util-dedicated-directory))) - (make-temp-file (convert-standard-filename prefix) dir-flag suffix))) - - -;; * ================================================================== * -;; * Various -;; * ================================================================== * - -(defmacro pdf-util-debug (&rest body) - "Execute BODY only if debugging is enabled." - (declare (indent 0) (debug t)) - `(when (bound-and-true-p pdf-tools-debug) - ,@body)) - -(defun pdf-util-pdf-buffer-p (&optional buffer) - (and (or (null buffer) - (buffer-live-p buffer)) - (save-current-buffer - (and buffer (set-buffer buffer)) - (derived-mode-p 'pdf-view-mode)))) - -(defun pdf-util-assert-pdf-buffer (&optional buffer) - (unless (pdf-util-pdf-buffer-p buffer) - (error "Buffer is not in PDFView mode"))) - -(defun pdf-util-pdf-window-p (&optional window) - (unless (or (null window) - (window-live-p window)) - (signal 'wrong-type-argument (list 'window-live-p window))) - (unless window (setq window (selected-window))) - (and (window-live-p window) - (with-selected-window window - (pdf-util-pdf-buffer-p)))) - -(defun pdf-util-assert-pdf-window (&optional window) - (unless (pdf-util-pdf-window-p window) - (error "Window's buffer is not in PdfView mode"))) - -(defun pdf-util-munch-file (filename &optional multibyte-p) - "Read contents from FILENAME and delete it. - -Return the file's content as a unibyte string, unless MULTIBYTE-P -is non-nil." - (unwind-protect - (with-temp-buffer - (set-buffer-multibyte multibyte-p) - (insert-file-contents-literally filename) - (buffer-substring-no-properties - (point-min) - (point-max))) - (when (and filename - (file-exists-p filename)) - (delete-file filename)))) - -(defun pdf-util-hexcolor (color) - "Return COLOR in hex-format. - -Signal an error, if color is invalid." - (if (string-match "\\`#[[:xdigit:]]\\{6\\}\\'" color) - color - (let ((values (color-values color))) - (unless values - (signal 'wrong-type-argument (list 'color-defined-p color))) - (apply 'format "#%02x%02x%02x" - (mapcar (lambda (c) (lsh c -8)) - values))))) - -(defun pdf-util-highlight-regexp-in-string (regexp string &optional face) - "Highlight all occurrences of REGEXP in STRING using FACE. - -FACE defaults to the `match' face. Returns the new fontified -string." - (with-temp-buffer - (save-excursion (insert string)) - (while (and (not (eobp)) - (re-search-forward regexp nil t)) - (if (= (match-beginning 0) - (match-end 0)) - (forward-char) - (put-text-property - (match-beginning 0) - (point) - 'face (or face 'match)))) - (buffer-string))) - -(defun pdf-util-color-completions () - "Return a fontified list of defined colors." - (let ((color-list (list-colors-duplicates)) - colors) - (dolist (cl color-list) - (dolist (c (reverse cl)) - (push (propertize c 'face `(:background ,c)) - colors))) - (nreverse colors))) - -(defun pdf-util-tooltip-in-window (text x y &optional window) - (let* ((we (window-inside-absolute-pixel-edges window)) - (dx (round (+ x (nth 0 we)))) - (dy (round (+ y (nth 1 we)))) - (tooltip-frame-parameters - `((left . ,dx) - (top . ,dy) - ,@tooltip-frame-parameters))) - (tooltip-show text))) - -(defun pdf-util-tooltip-arrow (image-top &optional timeout) - (pdf-util-assert-pdf-window) - (when (floatp image-top) - (setq image-top - (round (* image-top (cdr (pdf-view-image-size)))))) - (let* (x-gtk-use-system-tooltips ;allow for display property in tooltip - (dx (+ (or (car (window-margins)) 0) - (car (window-fringes)))) - (dy image-top) - (pos (list dx dy dx (+ dy (* 2 (frame-char-height))))) - (vscroll - (pdf-util-required-vscroll pos)) - (tooltip-frame-parameters - `((border-width . 0) - (internal-border-width . 0) - ,@tooltip-frame-parameters)) - (tooltip-hide-delay (or timeout 3))) - (when vscroll - (image-set-window-vscroll vscroll)) - (setq dy (max 0 (- dy - (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)))) - (pdf-util-tooltip-in-window - (propertize - " " 'display (propertize - "\u2192" ;;right arrow - 'display '(height 2) - 'face `(:foreground - "orange red" - :background - ,(if (bound-and-true-p pdf-view-midnight-minor-mode) - (cdr pdf-view-midnight-colors) - "white")))) - dx dy))) - -(defvar pdf-util--face-colors-cache (make-hash-table)) - -(defadvice enable-theme (after pdf-util-clear-faces-cache activate) - (clrhash pdf-util--face-colors-cache)) - -(defun pdf-util-face-colors (face &optional dark-p) - "Return both colors of FACE as a cons. - -Look also in inherited faces. If DARK-P is non-nil, return dark -colors, otherwise light." - (let* ((bg (if dark-p 'dark 'light)) - (spec (list (get face 'face-defface-spec) - (get face 'theme-face) - (get face 'customized-face))) - (cached (gethash face pdf-util--face-colors-cache))) - (cl-destructuring-bind (&optional cspec color-alist) - cached - (or (and color-alist - (equal cspec spec) - (cdr (assq bg color-alist))) - (let* ((this-bg (frame-parameter nil 'background-mode)) - (frame-background-mode bg) - (f (and (not (eq bg this-bg)) - (x-create-frame-with-faces '((visibility . nil)))))) - (with-selected-frame (or f (selected-frame)) - (unwind-protect - (let ((colors - (cons (face-attribute face :foreground nil 'default) - (face-attribute face :background nil 'default)))) - (puthash face `(,(mapcar 'copy-sequence spec) - ((,bg . ,colors) ,@color-alist)) - pdf-util--face-colors-cache) - colors) - (when (and f (frame-live-p f)) - (delete-frame f))))))))) - -(defun pdf-util-window-attach (awindow &optional window) - "Attach AWINDOW to WINDOW. - -This has the following effect. Whenever WINDOW, defaulting to -the selected window, stops displaying the buffer it currently -displays (e.g., by switching buffers or because it was deleted) -AWINDOW is deleted." - (unless window (setq window (selected-window))) - (let ((buffer (window-buffer window)) - (hook (make-symbol "window-attach-hook"))) - (fset hook - (lambda () - (when (or (not (window-live-p window)) - (not (eq buffer (window-buffer window)))) - (remove-hook 'window-configuration-change-hook - hook) - ;; Deleting windows inside wcch may cause errors in - ;; windows.el . - (run-with-timer - 0 nil (lambda (win) - (when (and (window-live-p win) - (not (eq win (selected-window)))) - (delete-window win))) - awindow)))) - (add-hook 'window-configuration-change-hook hook))) - -(defun display-buffer-split-below-and-attach (buf alist) - "Display buffer action using `pdf-util-window-attach'." - (let ((window (selected-window)) - (height (cdr (assq 'window-height alist))) - newwin) - (when height - (when (floatp height) - (setq height (round (* height (frame-height))))) - (setq height (- (max height window-min-height)))) - (setq newwin (window--display-buffer - buf - (split-window-below height) - 'window alist)) - (pdf-util-window-attach newwin window) - newwin)) - -(defun pdf-util-goto-position (line &optional column) - "Goto LINE and COLUMN in the current buffer. - -COLUMN defaults to 0. Widen the buffer, if the position is -outside the current limits." - (let ((pos - (when (> line 0) - (save-excursion - (save-restriction - (widen) - (goto-char 1) - (when (= 0 (forward-line (1- line))) - (when (and column (> column 0)) - (forward-char (1- column))) - (point))))))) - (when pos - (when (or (< pos (point-min)) - (> pos (point-max))) - (widen)) - (goto-char pos)))) - -(defun pdf-util-seq-alignment (seq1 seq2 &optional similarity-fn alignment-type) - "Return an alignment of sequences SEQ1 and SEQ2. - -SIMILARITY-FN should be a function. It is called with two -arguments: One element from SEQ1 and one from SEQ2. It should -return a number determining how similar the elements are, where -higher values mean `more similar'. The default returns 1 if the -elements are equal, else -1. - -ALIGNMENT-TYPE may be one of the symbols `prefix', `suffix', -`infix' or nil. If it is `prefix', trailing elements in SEQ2 may -be ignored. For example the alignment of - -\(0 1\) and \(0 1 2\) - -using prefix matching is 0, since the prefixes are equal and the -trailing 2 is ignored. The other possible values have similar -effects. The default is nil, which means to match the whole -sequences. - -Return a cons \(VALUE . ALIGNMENT\), where VALUE says how similar -the sequences are and ALIGNMENT is a list of \(E1 . E2\), where -E1 is an element from SEQ1 or nil, likewise for E2. If one of -them is nil, it means there is gap at this position in the -respective sequence." - - (cl-macrolet ((make-matrix (rows columns) - (list 'apply (list 'quote 'vector) - (list 'cl-loop 'for 'i 'from 1 'to rows - 'collect (list 'make-vector columns nil)))) - (mset (matrix row column newelt) - (list 'aset (list 'aref matrix row) column newelt)) - (mref (matrix row column) - (list 'aref (list 'aref matrix row) column))) - (let* ((nil-value nil) - (len1 (length seq1)) - (len2 (length seq2)) - (d (make-matrix (1+ len1) (1+ len2))) - (prefix-p (memq alignment-type '(prefix infix))) - (suffix-p (memq alignment-type '(suffix infix))) - (similarity-fn (or similarity-fn - (lambda (a b) - (if (equal a b) 1 -1))))) - - (cl-loop for i from 0 to len1 do - (mset d i 0 (- i))) - (cl-loop for j from 0 to len2 do - (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)))) - - (let ((i len1) - (j len2) - alignment) - (while (or (> i 0) - (> j 0)) - (cond - ((and (> i 0) - (= (mref d i j) - (1- (mref d (1- i) j)))) - (cl-decf i) - (push (cons (elt seq1 i) nil-value) alignment)) - ((and (> j 0) - (= (mref d i j) - (+ (mref d i (1- j)) - (if (or (and (= i 0) suffix-p) - (and (= i len1) prefix-p)) - 0 -1)))) - (cl-decf j) - (push (cons nil-value (elt seq2 j)) alignment)) - (t - (cl-assert (and (> i 0) (> j 0)) t) - (cl-decf i) - (cl-decf j) - (push (cons (elt seq1 i) - (elt seq2 j)) alignment)))) - (cons (mref d len1 len2) alignment))))) - - -(defun pdf-util-pcre-quote (string) - "Escape STRING for use as a PCRE. - -See also `regexp-quote'." - - (let ((to-escape - (eval-when-compile (append "\0\\|()[]{}^$*+?." nil))) - (chars (append string nil)) - escaped) - (dolist (ch chars) - (when (memq ch to-escape) - (push ?\\ escaped)) - (push ch escaped)) - (apply 'string (nreverse escaped)))) - -(defun pdf-util-frame-ppi () - "Return the PPI of the current frame." - (let* ((props (frame-monitor-attributes)) - (px (nthcdr 2 (alist-get 'geometry props))) - (mm (alist-get 'mm-size props)) - (dp (sqrt (+ (expt (nth 0 px) 2) - (expt (nth 1 px) 2)))) - (di (sqrt (+ (expt (/ (nth 0 mm) 25.4) 2) - (expt (/ (nth 1 mm) 25.4) 2))))) - (/ dp di))) - -(defvar pdf-view-use-scaling) - -(defun pdf-util-frame-scale-factor () - "Return the frame scale factor depending on the image type used for display. -When `pdf-view-use-scaling' is non-nil and imagemagick or -image-io are used as the image type for display, return the -backing-scale-factor of the frame if available. If a -backing-scale-factor attribute isn't available, return 2 if the -frame's PPI is larger than 180. Otherwise, return 1." - (if (and pdf-view-use-scaling - (memq (pdf-view-image-type) '(imagemagick image-io)) - (fboundp 'frame-monitor-attributes)) - (or (cdr (assq 'backing-scale-factor (frame-monitor-attributes))) - (if (>= (pdf-util-frame-ppi) 180) - 2 - 1)) - 1)) - - -;; * ================================================================== * -;; * Imagemagick's convert -;; * ================================================================== * - -(defcustom pdf-util-convert-program - ;; Avoid using the MS Windows command convert.exe . - (unless (memq system-type '(ms-dos windows-nt)) - (executable-find "convert")) - "Absolute path to the convert program." - :group 'pdf-tools - :type 'executable) - -(defcustom pdf-util-fast-image-format nil - "An image format appropriate for fast displaying. - -This should be a cons \(TYPE . EXT\) where type is the Emacs -image-type and EXT the appropriate file extension starting with a -dot. If nil, the value is determined automatically. - -Different formats have different properties, with respect to -Emacs loading time, convert creation time and the file-size. In -general, uncompressed formats are faster, but may need a fair -amount of (temporary) disk space." - :group 'pdf-tools - :type '(cons symbol string)) - -(defun pdf-util-assert-convert-program () - (unless (and pdf-util-convert-program - (file-executable-p pdf-util-convert-program)) - (error "The pdf-util-convert-program is unset or non-executable"))) - -(defun pdf-util-image-file-size (image-file) - "Determine the size of the image in IMAGE-FILE. - -Returns a cons \(WIDTH . HEIGHT\)." - (pdf-util-assert-convert-program) - (with-temp-buffer - (when (save-excursion - (= 0 (call-process - pdf-util-convert-program - nil (current-buffer) nil - image-file "-format" "%w %h" "info:"))) - (let ((standard-input (current-buffer))) - (cons (read) (read)))))) - -(defun pdf-util-convert (in-file out-file &rest spec) - "Convert image IN-FILE to OUT-FILE according to SPEC. - -IN-FILE should be the name of a file containing an image. Write -the result to OUT-FILE. The extension of this filename usually -determines the resulting image-type. - -SPEC is a property list, specifying what the convert program -should do with the image. All manipulations operate on a -rectangle, see below. - -SPEC may contain the following keys, respectively values. - -`:foreground' Set foreground color for all following operations. - -`:background' Dito, for the background color. - -`:commands' A list of strings representing arguments to convert -for image manipulations. It may contain %-escape characters, as -follows. - -%f -- Expands to the foreground color. -%b -- Expands to the background color. -%g -- Expands to the geometry of the current rectangle, i.e. WxH+X+Y. -%x -- Expands to the left edge of rectangle. -%X -- Expands to the right edge of rectangle. -%y -- Expands to the top edge of rectangle. -%Y -- Expands to the bottom edge of rectangle. -%w -- Expands to the width of rectangle. -%h -- Expands to the height of rectangle. - -Keep in mind, that every element of this list is seen by convert -as a single argument. - -`:formats' An alist of additional %-escapes. Every element -should be a cons \(CHAR . STRING\) or \(CHAR . FUNCTION\). In -the first case, all occurrences of %-CHAR in the above commands -will be replaced by STRING. In the second case FUNCTION is -called with the current rectangle and it should return the -replacement string. - -`:apply' A list of rectangles \(\(LEFT TOP RIGHT BOT\) ...\) in -IN-FILE coordinates. Each such rectangle triggers one execution -of the last commands given earlier in SPEC. E.g. a call like - -\(pdf-util-convert - image-file out-file - :foreground \"black\" - :background \"white\" - :commands '\(\"-fill\" \"%f\" \"-draw\" \"rectangle %x,%y,%X,%Y\"\) - :apply '\(\(0 0 10 10\) \(10 10 20 20\)\) - :commands '\(\"-fill\" \"%b\" \"-draw\" \"rectangle %x,%y,%X,%Y\"\) - :apply '\(\(10 0 20 10\) \(0 10 10 20\)\)\) - -would draw a 4x4 checkerboard pattern in the left corner of the -image, while leaving the rest of it as it was. - -Returns OUT-FILE. - -See url `http://www.imagemagick.org/script/convert.php'." - (pdf-util-assert-convert-program) - (let* ((cmds (pdf-util-convert--create-commands spec)) - (status (apply 'call-process - pdf-util-convert-program nil - (get-buffer-create "*pdf-util-convert-output*") - nil - `(,in-file ,@cmds ,out-file)))) - (unless (and (numberp status) (= 0 status)) - (error "The convert program exited with error status: %s" status)) - out-file)) - -(defun pdf-util-convert-asynch (in-file out-file &rest spec-and-callback) - "Like `pdf-util-convert', but asynchronous. - -If the last argument is a function, it is installed as the -process sentinel. - -Returns the convert process." - (pdf-util-assert-convert-program) - (let ((callback (car (last spec-and-callback))) - spec) - (if (functionp callback) - (setq spec (butlast spec-and-callback)) - (setq spec spec-and-callback - callback nil)) - (let* ((cmds (pdf-util-convert--create-commands spec)) - (proc - (apply 'start-process "pdf-util-convert" - (get-buffer-create "*pdf-util-convert-output*") - pdf-util-convert-program - `(,in-file ,@cmds ,out-file)))) - (when callback - (set-process-sentinel proc callback)) - proc))) - -(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) - (let ((in-file (make-temp-file "pdf-util-convert" nil ".png")) - (out-file (make-temp-file "pdf-util-convert" nil ".png"))) - (unwind-protect - (let ((image-data - (plist-get (cdr (pdf-view-current-image)) :data))) - (with-temp-file in-file - (set-buffer-multibyte nil) - (set-buffer-file-coding-system 'binary) - (insert image-data)) - (pdf-util-munch-file - (apply 'pdf-util-convert - in-file out-file specs))) - (when (file-exists-p in-file) - (delete-file in-file)) - (when (file-exists-p out-file) - (delete-file out-file))))) - - -(defun pdf-util-convert--create-commands (spec) - (let ((fg "red") - (bg "red") - formats result cmds s) - (while (setq s (pop spec)) - (unless spec - (error "Missing value in convert spec:%s" (cons s spec))) - (cl-case s - (:foreground - (setq fg (pop spec))) - (:background - (setq bg (pop spec))) - (:commands - (setq cmds (pop spec))) - (:formats - (setq formats (append formats (pop spec) nil))) - (:apply - (dolist (m (pop spec)) - (pdf-util-with-edges (m) - (let ((alist (append - (mapcar (lambda (f) - (cons (car f) - (if (stringp (cdr f)) - (cdr f) - (funcall (cdr f) m)))) - formats) - `((?g . ,(format "%dx%d+%d+%d" - m-width m-height - m-left m-top)) - (?x . ,m-left) - (?X . ,m-right) - (?y . ,m-top) - (?Y . ,m-bot) - (?w . ,(- m-right m-left)) - (?h . ,(- m-bot m-top)) - (?f . ,fg) - (?b . ,bg))))) - (dolist (fmt cmds) - (push (format-spec fmt alist) result)))))))) - (nreverse result))) - -;; FIXME: Check code below and document. - -(defun pdf-util-edges-p (obj &optional relative-p) - "Return non-nil, if OBJ look like edges. - -If RELATIVE-P is non-nil, also check that all values <= 1." - - (and (consp obj) - (ignore-errors (= 4 (length obj))) - (cl-every (lambda (x) - (and (numberp x) - (>= x 0) - (or (null relative-p) - (<= x 1)))) - obj))) - -(defun pdf-util-edges-empty-p (edges) - "Return non-nil, if EDGES area is empty." - (pdf-util-with-edges (edges) - (or (<= edges-width 0) - (<= edges-height 0)))) - -(defun pdf-util-edges-inside-p (edges pos &optional epsilon) - (pdf-util-edges-contained-p - edges - (list (car pos) (cdr pos) (car pos) (cdr pos)) - epsilon)) - -(defun pdf-util-edges-contained-p (edges contained &optional epsilon) - (unless epsilon (setq epsilon 0)) - (pdf-util-with-edges (edges contained) - (and (<= (- edges-left epsilon) - contained-left) - (>= (+ edges-right epsilon) - contained-right) - (<= (- edges-top epsilon) - contained-top) - (>= (+ edges-bot epsilon) - contained-bot)))) - -(defun pdf-util-edges-intersection (e1 e2) - (pdf-util-with-edges (edges1 e1 e2) - (let ((left (max e1-left e2-left)) - (top (max e1-top e2-top)) - (right (min e1-right e2-right)) - (bot (min e1-bot e2-bot))) - (when (and (<= left right) - (<= top bot)) - (list left top right bot))))) - -(defun pdf-util-edges-union (&rest edges) - (if (null (cdr edges)) - (car edges) - (list (apply 'min (mapcar 'car edges)) - (apply 'min (mapcar 'cadr edges)) - (apply 'max (mapcar 'cl-caddr edges)) - (apply 'max (mapcar 'cl-cadddr edges))))) - -(defun pdf-util-edges-intersection-area (e1 e2) - (let ((inters (pdf-util-edges-intersection e1 e2))) - (if (null inters) - 0 - (pdf-util-with-edges (inters) - (* inters-width inters-height))))) - -(defun pdf-util-read-image-position (prompt) - "Read a image position using prompt. - -Return the event position object." - (save-selected-window - (let ((ev (pdf-util-read-click-event - (propertize prompt 'face 'minibuffer-prompt))) - (buffer (current-buffer))) - (unless (mouse-event-p ev) - (error "Not a mouse event")) - (let ((posn (event-start ev))) - (unless (and (eq (window-buffer - (posn-window posn)) - buffer) - (eq 'image (car-safe (posn-object posn)))) - (error "Invalid image position")) - posn)))) - -(defun pdf-util-read-click-event (&optional prompt seconds) - (let ((down (read-event prompt seconds))) - (unless (and (mouse-event-p down) - (equal (event-modifiers down) - '(down))) - (error "No a mouse click event")) - (let ((up (read-event prompt seconds))) - (unless (and (mouse-event-p up) - (equal (event-modifiers up) - '(click))) - (error "No a mouse click event")) - up))) - -(defun pdf-util-image-map-mouse-event-proxy (event) - "Set POS-OR-AREA in EVENT to 1 and unread it." - (interactive "e") - (setcar (cdr (cadr event)) 1) - (setq unread-command-events (list event))) - -(defun pdf-util-image-map-divert-mouse-clicks (id &optional buttons) - (dolist (kind '("" "down-" "drag-")) - (dolist (b (or buttons '(2 3 4 5 6))) - (local-set-key - (vector id (intern (format "%smouse-%d" kind b))) - 'pdf-util-image-map-mouse-event-proxy)))) - -(defmacro pdf-util-do-events (event-resolution-unread-p condition &rest body) - "Read EVENTs while CONDITION executing BODY. - -Process at most 1/RESOLUTION events per second. If UNREAD-p is -non-nil, unread the final non-processed event. - -\(FN (EVENT RESOLUTION &optional UNREAD-p) CONDITION &rest BODY\)" - (declare (indent 2) (debug ((symbolp form &optional form) form body))) - (cl-destructuring-bind (event resolution &optional unread-p) - event-resolution-unread-p - (let ((*seconds (make-symbol "seconds")) - (*timestamp (make-symbol "timestamp")) - (*clock (make-symbol "clock")) - (*unread-p (make-symbol "unread-p")) - (*resolution (make-symbol "resolution"))) - `(let* ((,*unread-p ,unread-p) - (,*resolution ,resolution) - (,*seconds 0) - (,*timestamp (float-time)) - (,*clock (lambda (&optional secs) - (when secs - (setq ,*seconds secs - ,*timestamp (float-time))) - (- (+ ,*timestamp ,*seconds) - (float-time)))) - (,event (read-event))) - (while ,condition - (when (<= (funcall ,*clock) 0) - (progn ,@body) - (setq ,event nil) - (funcall ,*clock ,*resolution)) - (setq ,event - (or (read-event nil nil - (and ,event - (max 0 (funcall ,*clock)))) - ,event))) - (when (and ,*unread-p ,event) - (setq unread-command-events - (append unread-command-events - (list ,event)))))))) - -(defmacro pdf-util-track-mouse-dragging (event-resolution &rest body) - "Read mouse movement events executing BODY. - -See also `pdf-util-do-events'. - -This macro should be used inside a command bound to a down-mouse -event. It evaluates to t, if at least one event was processed in -BODY, otherwise nil. In the latter case, the only event (usually -a mouse click event) is unread. - -\(FN (EVENT RESOLUTION) &rest BODY\)" - (declare (indent 1) (debug ((symbolp form) body))) - (let ((ran-once-p (make-symbol "ran-once-p"))) - `(let (,ran-once-p) - (track-mouse - (pdf-util-do-events (,@event-resolution t) - (mouse-movement-p ,(car event-resolution)) - (setq ,ran-once-p t) - ,@body)) - (when (and ,ran-once-p - unread-command-events) - (setq unread-command-events - (butlast unread-command-events))) - ,ran-once-p))) - -(defun pdf-util-remove-duplicates (list) - "Remove duplicates from LIST stably using `equal'." - (let ((ht (make-hash-table :test 'equal)) - result) - (dolist (elt list (nreverse result)) - (unless (gethash elt ht) - (push elt result) - (puthash elt t ht))))) - -(provide 'pdf-util) - -;;; pdf-util.el ends here |