]> code.delx.au - gnu-emacs/blobdiff - lisp/doc-view.el
* lisp/descr-text.el (describe-char-unicode-data): Fix copy/paste errors.
[gnu-emacs] / lisp / doc-view.el
index 6706865e5984154c2e9b91311784ab7c92429ffc..223565cedb6a639d66ba6d152cf73ea58ec6026a 100644 (file)
@@ -1,6 +1,6 @@
 ;;; doc-view.el --- View PDF/PostScript/DVI files in Emacs -*- lexical-binding: t -*-
 
-;; Copyright (C) 2007-2014 Free Software Foundation, Inc.
+;; Copyright (C) 2007-2016 Free Software Foundation, Inc.
 ;;
 ;; Author: Tassilo Horn <tsdh@gnu.org>
 ;; Maintainer: Tassilo Horn <tsdh@gnu.org>
 (require 'dired)
 (require 'image-mode)
 (require 'jka-compr)
+(require 'subr-x)
 
 ;;;; Customization Options
 
@@ -336,7 +337,7 @@ of the page moves to the previous page."
       ;; Don't do it if there's a conversion is running, since in that case, it
       ;; will be done later.
       (with-selected-window (car winprops)
-        (doc-view-goto-page 1)))))
+        (doc-view-goto-page (image-mode-window-get 'page t))))))
 
 (defvar-local doc-view--current-files nil
   "Only used internally.")
@@ -415,7 +416,6 @@ Typically \"page-%s.png\".")
     (define-key map "H"               'doc-view-fit-height-to-window)
     (define-key map "P"               'doc-view-fit-page-to-window)
     ;; Killing the buffer (and the process)
-    (define-key map (kbd "k")         'doc-view-kill-proc-and-buffer)
     (define-key map (kbd "K")         'doc-view-kill-proc)
     ;; Slicing the image
     (define-key map (kbd "s s")       'doc-view-set-slice)
@@ -441,10 +441,23 @@ Typically \"page-%s.png\".")
 
 (defun doc-view-revert-buffer (&optional ignore-auto noconfirm)
   "Like `revert-buffer', but preserves the buffer's current modes."
-  ;; FIXME: this should probably be moved to files.el and used for
-  ;; most/all "g" bindings to revert-buffer.
   (interactive (list (not current-prefix-arg)))
-  (revert-buffer ignore-auto noconfirm 'preserve-modes))
+  (cl-labels ((revert ()
+                      (let (revert-buffer-function)
+                        (revert-buffer ignore-auto noconfirm 'preserve-modes))))
+    (if (and (eq 'pdf doc-view-doc-type)
+             (executable-find "pdfinfo"))
+        ;; We don't want to revert if the PDF file is corrupted which
+        ;; might happen when it it currently recompiled from a tex
+        ;; file.  (TODO: We'd like to have something like that also
+        ;; for other types, at least PS, but I don't know a good way
+        ;; to test if a PS file is complete.)
+        (if (= 0 (call-process (executable-find "pdfinfo") nil nil nil
+                               doc-view--buffer-file-name))
+            (revert)
+          (when (called-interactively-p 'interactive)
+            (message "Can't revert right now because the file is corrupted.")))
+      (revert))))
 
 
 (easy-menu-define doc-view-menu doc-view-mode-map
@@ -613,7 +626,7 @@ at the bottom edge of the page moves to the next page."
            (image-bob)
            (image-bol 1))
          (set-window-hscroll (selected-window) hscroll)))
-    (image-next-line 1)))
+    (image-next-line arg)))
 
 (defun doc-view-previous-line-or-previous-page (&optional arg)
   "Scroll downward by ARG lines if possible, else goto previous page.
@@ -645,25 +658,15 @@ at the top edge of the page moves to the previous page."
     (setq doc-view--current-timer nil))
   (setq mode-line-process nil))
 
-(defun doc-view-kill-proc-and-buffer ()
-  "Kill the current converter process and buffer."
-  (interactive)
-  (doc-view-kill-proc)
-  (when (eq major-mode 'doc-view-mode)
-    (kill-buffer (current-buffer))))
+(define-obsolete-function-alias 'doc-view-kill-proc-and-buffer
+  #'image-kill-buffer "25.1")
 
 (defun doc-view-make-safe-dir (dir)
   (condition-case nil
-      (let ((umask (default-file-modes)))
-       (unwind-protect
-           (progn
-             ;; Create temp files with strict access rights.  It's easy to
-             ;; loosen them later, whereas it's impossible to close the
-             ;; time-window of loose permissions otherwise.
-             (set-default-file-modes #o0700)
-             (make-directory dir))
-         ;; Reset the umask.
-         (set-default-file-modes umask)))
+      ;; Create temp files with strict access rights.  It's easy to
+      ;; loosen them later, whereas it's impossible to close the
+      ;; time-window of loose permissions otherwise.
+      (with-file-modes #o0700 (make-directory dir))
     (file-already-exists
      (when (file-symlink-p dir)
        (error "Danger: %s points to a symbolic link" dir))
@@ -693,14 +696,19 @@ It's a subdirectory of `doc-view-cache-directory'."
     (setq doc-view--current-cache-dir
          (file-name-as-directory
           (expand-file-name
-           (concat (subst-char-in-string ?% ?_ ;; bug#13679
-                     (file-name-nondirectory doc-view--buffer-file-name))
-                   "-"
-                   (let ((file doc-view--buffer-file-name))
-                     (with-temp-buffer
-                       (set-buffer-multibyte nil)
-                       (insert-file-contents-literally file)
-                       (md5 (current-buffer)))))
+           (concat (thread-last
+                        (file-name-nondirectory doc-view--buffer-file-name)
+                      ;; bug#13679
+                      (subst-char-in-string ?% ?_)
+                      ;; arc-mode concatenates archive name and file name
+                      ;; with colon, which isn't allowed on MS-Windows.
+                      (subst-char-in-string ?: ?_))
+                    "-"
+                    (let ((file doc-view--buffer-file-name))
+                      (with-temp-buffer
+                        (set-buffer-multibyte nil)
+                        (insert-file-contents-literally file)
+                        (md5 (current-buffer)))))
             doc-view-cache-directory)))))
 
 ;;;###autoload
@@ -979,6 +987,11 @@ is named like ODF with the extension turned to pdf."
     (doc-view-start-process "odf->pdf" doc-view-odf->pdf-converter-program
                            (list
                             (concat "-env:UserInstallation=file://"
+                                     ;; The URL must be
+                                     ;; file:///C:/tmp/dir on Windows.
+                                     ;; https://wiki.documentfoundation.org/UserProfile.
+                                     (when (eq system-type 'windows-nt)
+                                       "/")
                                     tmp-user-install-dir)
                             "--headless" "--convert-to" "pdf"
                             "--outdir" (doc-view--current-cache-dir) odf)
@@ -1398,18 +1411,28 @@ For now these keys are useful:
   (tooltip-show (doc-view-current-info)))
 
 (defun doc-view-open-text ()
-  "Open a buffer with the current doc's contents as text."
+  "Display the current doc's contents as text."
   (interactive)
   (if doc-view--current-converter-processes
       (message "DocView: please wait till conversion finished.")
     (let ((txt (expand-file-name "doc.txt" (doc-view--current-cache-dir))))
       (if (file-readable-p txt)
-         (let ((name (concat "Text contents of "
-                             (file-name-nondirectory buffer-file-name)))
-               (dir (file-name-directory buffer-file-name)))
-           (with-current-buffer (find-file txt)
-             (rename-buffer name)
-             (setq default-directory dir)))
+         (let ((inhibit-read-only t)
+               (buffer-undo-list t)
+               (dv-bfn doc-view--buffer-file-name))
+           (erase-buffer)
+           (set-buffer-multibyte t)
+           (insert-file-contents txt)
+           (text-mode)
+           (setq-local doc-view--buffer-file-name dv-bfn)
+           (set-buffer-modified-p nil)
+           (doc-view-minor-mode)
+           (add-hook 'write-file-functions
+                     (lambda ()
+                       (when (eq major-mode 'text-mode)
+                         (error "Cannot save text contents of document %s"
+                                buffer-file-name)))
+                     nil t))
        (doc-view-doc->txt txt 'doc-view-open-text)))))
 
 ;;;;; Toggle between editing and viewing
@@ -1421,20 +1444,30 @@ For now these keys are useful:
 (defun doc-view-toggle-display ()
   "Toggle between editing a document as text or viewing it."
   (interactive)
-  (if (eq major-mode 'doc-view-mode)
-      ;; Switch to editing mode
-      (progn
-       (doc-view-kill-proc)
-       (setq buffer-read-only nil)
-       ;; Switch to the previously used major mode or fall back to
-       ;; normal mode.
-       (doc-view-fallback-mode)
-       (doc-view-minor-mode 1))
+  (cond
+   ((eq major-mode 'doc-view-mode)
+    ;; Switch to editing mode
+    (doc-view-kill-proc)
+    (setq buffer-read-only nil)
+    ;; Switch to the previously used major mode or fall back to
+    ;; normal mode.
+    (doc-view-fallback-mode)
+    (doc-view-minor-mode 1))
+   ((eq major-mode 'text-mode)
+    (let ((buffer-undo-list t))
+      ;; We're currently viewing the document's text contents, so switch
+      ;; back to .
+      (setq buffer-read-only nil)
+      (insert-file-contents doc-view--buffer-file-name nil nil nil t)
+      (doc-view-fallback-mode)
+      (doc-view-minor-mode 1)
+      (set-buffer-modified-p nil)))
+   (t
     ;; Switch to doc-view-mode
     (when (and (buffer-modified-p)
               (y-or-n-p "The buffer has been modified.  Save the changes? "))
       (save-buffer))
-    (doc-view-mode)))
+    (doc-view-mode))))
 
 ;;;; Searching
 
@@ -1590,11 +1623,11 @@ If BACKWARD is non-nil, jump to the previous match."
      (concat "No PNG support is available, or some conversion utility for "
             (file-name-extension doc-view--buffer-file-name)
             " files is missing."))
-    (when (and (executable-find doc-view-pdftotext-program)
-              (y-or-n-p
-               "Unable to render file.  View extracted text instead? "))
-      (doc-view-open-text))
-    (doc-view-toggle-display)))
+    (if (and (executable-find doc-view-pdftotext-program)
+            (y-or-n-p
+             "Unable to render file.  View extracted text instead? "))
+       (doc-view-open-text)
+      (doc-view-toggle-display))))
 
 (defvar bookmark-make-record-function)
 
@@ -1621,24 +1654,26 @@ If BACKWARD is non-nil, jump to the previous match."
   "Figure out the current document type (`doc-view-doc-type')."
   (let ((name-types
         (when buffer-file-name
-          (cdr (assoc (file-name-extension buffer-file-name)
-                      '(
-                        ;; DVI
-                        ("dvi" dvi)
-                        ;; PDF
-                        ("pdf" pdf) ("epdf" pdf)
-                        ;; PostScript
-                        ("ps" ps) ("eps" ps)
-                        ;; DjVu
-                        ("djvu" djvu)
-                        ;; OpenDocument formats
-                        ("odt" odf) ("ods" odf) ("odp" odf) ("odg" odf)
-                        ("odc" odf) ("odi" odf) ("odm" odf) ("ott" odf)
-                        ("ots" odf) ("otp" odf) ("otg" odf)
-                        ;; Microsoft Office formats (also handled
-                        ;; by the odf conversion chain)
-                        ("doc" odf) ("docx" odf) ("xls" odf) ("xlsx" odf)
-                        ("ppt" odf) ("pptx" odf))))))
+          (cdr (assoc-string
+                 (file-name-extension buffer-file-name)
+                 '(
+                   ;; DVI
+                   ("dvi" dvi)
+                   ;; PDF
+                   ("pdf" pdf) ("epdf" pdf)
+                   ;; PostScript
+                   ("ps" ps) ("eps" ps)
+                   ;; DjVu
+                   ("djvu" djvu)
+                   ;; OpenDocument formats.
+                   ("odt" odf) ("ods" odf) ("odp" odf) ("odg" odf)
+                   ("odc" odf) ("odi" odf) ("odm" odf) ("ott" odf)
+                   ("ots" odf) ("otp" odf) ("otg" odf)
+                   ;; Microsoft Office formats (also handled by the odf
+                   ;; conversion chain).
+                   ("doc" odf) ("docx" odf) ("xls" odf) ("xlsx" odf)
+                   ("ppt" odf) ("pps" odf) ("pptx" odf) ("rtf" odf))
+                t))))
        (content-types
         (save-excursion
           (goto-char (point-min))
@@ -1669,6 +1704,9 @@ If BACKWARD is non-nil, jump to the previous match."
 ;; desktop.el integration
 
 (defun doc-view-desktop-save-buffer (_desktop-dirname)
+  ;; FIXME: This is wrong, since this info is per-window but we only do it once
+  ;; here for the buffer.  IOW it should be saved via something like
+  ;; `window-persistent-parameters'.
   `((page . ,(doc-view-current-page))
     (slice . ,(doc-view-current-slice))))
 
@@ -1679,10 +1717,16 @@ If BACKWARD is non-nil, jump to the previous match."
   (let ((page  (cdr (assq 'page misc)))
        (slice (cdr (assq 'slice misc))))
     (desktop-restore-file-buffer file name misc)
+    ;; FIXME: We need to run this code after displaying the buffer.
     (with-selected-window (or (get-buffer-window (current-buffer) 0)
                              (selected-window))
+      ;; FIXME: This should be done for all windows restored that show
+      ;; this buffer.  Basically, the page/slice should be saved as
+      ;; window-parameters in the window-state(s) and then restoring this
+      ;; window-state should call us back (to interpret/use those parameters).
       (doc-view-goto-page page)
-      (when slice (apply 'doc-view-set-slice slice)))))
+      (when slice (apply 'doc-view-set-slice slice))
+      (current-buffer))))
 
 (add-to-list 'desktop-buffer-mode-handlers
             '(doc-view-mode . doc-view-restore-desktop-buffer))
@@ -1747,6 +1791,8 @@ toggle between displaying the document or editing it as text.
     (when (not (string= doc-view--buffer-file-name buffer-file-name))
       (write-region nil nil doc-view--buffer-file-name))
 
+    (setq-local revert-buffer-function #'doc-view-revert-buffer)
+
     (add-hook 'change-major-mode-hook
              (lambda ()
                (doc-view-kill-proc)
@@ -1754,9 +1800,7 @@ toggle between displaying the document or editing it as text.
              nil t)
     (add-hook 'clone-indirect-buffer-hook 'doc-view-clone-buffer-hook nil t)
     (add-hook 'kill-buffer-hook 'doc-view-kill-proc nil t)
-    (when (and (boundp 'desktop-save-mode)
-              desktop-save-mode)
-      (setq-local desktop-save-buffer 'doc-view-desktop-save-buffer))
+    (setq-local desktop-save-buffer 'doc-view-desktop-save-buffer)
 
     (remove-overlays (point-min) (point-max) 'doc-view t) ;Just in case.
     ;; Keep track of display info ([vh]scroll, page number, overlay,
@@ -1862,20 +1906,23 @@ See the command `doc-view-mode' for more information on this mode."
          `((page     . ,(doc-view-current-page))
            (handler  . doc-view-bookmark-jump))))
 
-
 ;;;###autoload
 (defun doc-view-bookmark-jump (bmk)
   ;; This implements the `handler' function interface for record type
   ;; returned by `doc-view-bookmark-make-record', which see.
-  (prog1 (bookmark-default-handler bmk)
-    (let ((page (bookmark-prop-get bmk 'page)))
-      (when (not (eq major-mode 'doc-view-mode))
-        (doc-view-toggle-display))
-      (with-selected-window
-       (or (get-buffer-window (current-buffer) 0)
-          (selected-window))
-       (doc-view-goto-page page)))))
-
+  (let ((page (bookmark-prop-get bmk 'page))
+       (show-fn-sym (make-symbol "doc-view-bookmark-after-jump-hook")))
+    (fset show-fn-sym
+         (lambda ()
+           (remove-hook 'bookmark-after-jump-hook show-fn-sym)
+           (when (not (eq major-mode 'doc-view-mode))
+             (doc-view-toggle-display))
+           (with-selected-window
+               (or (get-buffer-window (current-buffer) 0)
+                   (selected-window))
+             (doc-view-goto-page page))))
+    (add-hook 'bookmark-after-jump-hook show-fn-sym)
+    (bookmark-default-handler bmk)))
 
 (provide 'doc-view)