-;;; doc-view.el --- View PDF/PostScript/DVI files in Emacs
+;;; doc-view.el --- View PDF/PostScript/DVI files in Emacs -*- lexical-binding: t -*-
-;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
+
+;; Copyright (C) 2007-2011 Free Software Foundation, Inc.
;;
;; Author: Tassilo Horn <tassilo@member.fsf.org>
;; Maintainer: Tassilo Horn <tassilo@member.fsf.org>
:link '(function-link doc-view)
:version "22.2"
:group 'applications
+ :group 'data
:group 'multimedia
:prefix "doc-view-")
(defcustom doc-view-ghostscript-options
'("-dSAFER" ;; Avoid security problems when rendering files from untrusted
- ;; sources.
+ ;; sources.
"-dNOPAUSE" "-sDEVICE=png16m" "-dTextAlphaBits=4"
"-dBATCH" "-dGraphicsAlphaBits=4" "-dQUIET")
"A list of options to give to ghostscript."
:type 'number
:group 'doc-view)
+(defcustom doc-view-image-width 850
+ "Default image width.
+Has only an effect if imagemagick support is compiled into emacs."
+ :type 'number
+ :group 'doc-view)
+
(defcustom doc-view-dvipdfm-program (executable-find "dvipdfm")
"Program to convert DVI files to PDF.
:type 'file
:group 'doc-view)
+(defcustom doc-view-unoconv-program (executable-find "unoconv")
+ "Program to convert any file type readable by OpenOffice.org to PDF.
+
+Needed for viewing OpenOffice.org (and MS Office) files."
+ :type 'file
+ :group 'doc-view)
+
(defcustom doc-view-ps2pdf-program (executable-find "ps2pdf")
"Program to convert PS files to PDF.
:type 'integer
:group 'doc-view)
-(defcustom doc-view-continuous-mode nil
+(defcustom doc-view-continuous nil
"In Continuous mode reaching the page edge advances to next/previous page.
When non-nil, scrolling a line upward at the bottom edge of the page
moves to the next page, and scrolling a line downward at the top edge
(defun doc-view-new-window-function (winprops)
(let ((ol (image-mode-window-get 'overlay winprops)))
+ (when (and ol (not (overlay-buffer ol)))
+ ;; I've seen `ol' be a dead overlay. I do not yet know how this
+ ;; happened, so maybe the bug is elsewhere, but in the mean time,
+ ;; this seems like a safe approach.
+ (setq ol nil))
(if ol
- (setq ol (copy-overlay ol))
+ (progn
+ (assert (eq (overlay-buffer ol) (current-buffer)))
+ (setq ol (copy-overlay ol)))
(assert (not (get-char-property (point-min) 'display)))
(setq ol (make-overlay (point-min) (point-max) nil t))
(overlay-put ol 'doc-view t))
;; Zoom in/out.
(define-key map "+" 'doc-view-enlarge)
(define-key map "-" 'doc-view-shrink)
+ ;; Fit the image to the window
+ (define-key map "W" 'doc-view-fit-width-to-window)
+ (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)
(define-key map (kbd "C-c C-c") 'doc-view-toggle-display)
;; Open a new buffer with doc's text contents
(define-key map (kbd "C-c C-t") 'doc-view-open-text)
- ;; Reconvert the current document
- (define-key map (kbd "g") 'revert-buffer)
- (define-key map (kbd "r") 'revert-buffer)
+ ;; Reconvert the current document. Don't just use revert-buffer
+ ;; because that resets the scale factor, the page number, ...
+ (define-key map (kbd "g") 'doc-view-revert-buffer)
+ (define-key map (kbd "r") 'doc-view-revert-buffer)
map)
"Keymap used by `doc-view-mode' when displaying a doc as a set of images.")
+(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))
+
+
(easy-menu-define doc-view-menu doc-view-mode-map
"Menu for Doc View mode."
'("DocView"
+ ["Toggle display" doc-view-toggle-display]
+ ("Continuous"
+ ["Off" (setq doc-view-continuous nil)
+ :style radio :selected (eq doc-view-continuous nil)]
+ ["On" (setq doc-view-continuous t)
+ :style radio :selected (eq doc-view-continuous t)]
+ "---"
+ ["Save as Default"
+ (customize-save-variable 'doc-view-continuous doc-view-continuous) t]
+ )
+ "---"
["Set Slice" doc-view-set-slice-using-mouse]
["Set Slice (manual)" doc-view-set-slice]
["Reset Slice" doc-view-reset-slice]
"---"
["Search" doc-view-search]
["Search Backwards" doc-view-search-backward]
- ["Toggle display" doc-view-toggle-display]
))
(defvar doc-view-minor-mode-map
(defmacro doc-view-current-image () `(image-mode-window-get 'image))
(defmacro doc-view-current-slice () `(image-mode-window-get 'slice))
+(defun doc-view-last-page-number ()
+ (length doc-view-current-files))
+
(defun doc-view-goto-page (page)
"View the page given by PAGE."
(interactive "nPage: ")
- (let ((len (length doc-view-current-files))
+ (let ((len (doc-view-last-page-number))
(hscroll (window-hscroll)))
(if (< page 1)
(setq page 1)
doc-view-current-converter-processes)
;; The PNG file hasn't been generated yet.
(doc-view-pdf->png-1 doc-view-buffer-file-name file page
- (lexical-let ((page page)
- (win (selected-window)))
+ (let ((win (selected-window)))
(lambda ()
(and (eq (current-buffer) (window-buffer win))
;; If we changed page in the mean
;; time, don't mess things up.
(eq (doc-view-current-page win) page)
+ ;; Make sure we don't infloop.
+ (file-readable-p file)
(with-selected-window win
- (doc-view-goto-page page))))))))
+ (doc-view-goto-page page))))))))
(overlay-put (doc-view-current-overlay)
'help-echo (doc-view-current-info))))
(defun doc-view-last-page ()
"View the last page."
(interactive)
- (doc-view-goto-page (length doc-view-current-files)))
+ (doc-view-goto-page (doc-view-last-page-number)))
(defun doc-view-scroll-up-or-next-page (&optional arg)
"Scroll page up ARG lines if possible, else goto next page.
-When `doc-view-continuous-mode' is non-nil, scrolling upward
+When `doc-view-continuous' is non-nil, scrolling upward
at the bottom edge of the page moves to the next page.
Otherwise, goto next page only on typing SPC (ARG is nil)."
(interactive "P")
- (if (or doc-view-continuous-mode (null arg))
+ (if (or doc-view-continuous (null arg))
(let ((hscroll (window-hscroll))
(cur-page (doc-view-current-page)))
(when (= (window-vscroll) (image-scroll-up arg))
(defun doc-view-scroll-down-or-previous-page (&optional arg)
"Scroll page down ARG lines if possible, else goto previous page.
-When `doc-view-continuous-mode' is non-nil, scrolling downward
+When `doc-view-continuous' is non-nil, scrolling downward
at the top edge of the page moves to the previous page.
Otherwise, goto previous page only on typing DEL (ARG is nil)."
(interactive "P")
- (if (or doc-view-continuous-mode (null arg))
+ (if (or doc-view-continuous (null arg))
(let ((hscroll (window-hscroll))
(cur-page (doc-view-current-page)))
(when (= (window-vscroll) (image-scroll-down arg))
(defun doc-view-next-line-or-next-page (&optional arg)
"Scroll upward by ARG lines if possible, else goto next page.
-When `doc-view-continuous-mode' is non-nil, scrolling a line upward
+When `doc-view-continuous' is non-nil, scrolling a line upward
at the bottom edge of the page moves to the next page."
(interactive "p")
- (if doc-view-continuous-mode
+ (if doc-view-continuous
(let ((hscroll (window-hscroll))
(cur-page (doc-view-current-page)))
(when (= (window-vscroll) (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.
-When `doc-view-continuous-mode' is non-nil, scrolling a line downward
+When `doc-view-continuous' is non-nil, scrolling a line downward
at the top edge of the page moves to the previous page."
(interactive "p")
- (if doc-view-continuous-mode
+ (if doc-view-continuous
(let ((hscroll (window-hscroll))
(cur-page (doc-view-current-page)))
(when (= (window-vscroll) (image-previous-line arg))
(defun doc-view-kill-proc ()
"Kill the current converter process(es)."
(interactive)
- (while doc-view-current-converter-processes
+ (while (consp doc-view-current-converter-processes)
(ignore-errors ;; Maybe it's dead already?
(kill-process (pop doc-view-current-converter-processes))))
(when doc-view-current-timer
(setq doc-view-current-cache-dir
(file-name-as-directory
(expand-file-name
- (concat (file-name-nondirectory buffer-file-name)
+ (concat (file-name-nondirectory doc-view-buffer-file-name)
"-"
(let ((file doc-view-buffer-file-name))
(with-temp-buffer
;;;###autoload
(defun doc-view-mode-p (type)
- "Return non-nil if image type TYPE is available for `doc-view'.
-Image types are symbols like `dvi', `postscript' or `pdf'."
+ "Return non-nil if document type TYPE is available for `doc-view'.
+Document types are symbols like `dvi', `ps', `pdf', or `odf' (any
+OpenDocument format)."
(and (display-graphic-p)
- (image-type-available-p 'png)
+ (or (image-type-available-p 'imagemagick)
+ (image-type-available-p 'png))
(cond
((eq type 'dvi)
(and (doc-view-mode-p 'pdf)
(eq type 'pdf))
(and doc-view-ghostscript-program
(executable-find doc-view-ghostscript-program)))
+ ((eq type 'odf)
+ (and doc-view-unoconv-program
+ (executable-find doc-view-unoconv-program)
+ (doc-view-mode-p 'pdf)))
(t ;; unknown image type
nil))))
(defun doc-view-enlarge (factor)
"Enlarge the document."
(interactive (list doc-view-shrink-factor))
- (set (make-local-variable 'doc-view-resolution)
- (* factor doc-view-resolution))
- (doc-view-reconvert-doc))
+ (if (eq (plist-get (cdr (doc-view-current-image)) :type)
+ 'imagemagick)
+ ;; ImageMagick supports on-the-fly-rescaling
+ (progn
+ (set (make-local-variable 'doc-view-image-width)
+ (ceiling (* factor doc-view-image-width)))
+ (doc-view-insert-image (plist-get (cdr (doc-view-current-image)) :file)
+ :width doc-view-image-width))
+ (set (make-local-variable 'doc-view-resolution)
+ (ceiling (* factor doc-view-resolution)))
+ (doc-view-reconvert-doc)))
(defun doc-view-shrink (factor)
"Shrink the document."
(interactive (list doc-view-shrink-factor))
(doc-view-enlarge (/ 1.0 factor)))
+(defun doc-view-fit-width-to-window ()
+ "Fit the image width to the window width."
+ (interactive)
+ (let ((win-width (- (nth 2 (window-inside-pixel-edges))
+ (nth 0 (window-inside-pixel-edges))))
+ (slice (doc-view-current-slice)))
+ (if (not slice)
+ (let ((img-width (car (image-display-size
+ (image-get-display-property) t))))
+ (doc-view-enlarge (/ (float win-width) (float img-width))))
+
+ ;; If slice is set
+ (let* ((slice-width (nth 2 slice))
+ (scale-factor (/ (float win-width) (float slice-width)))
+ (new-slice (mapcar (lambda (x) (ceiling (* scale-factor x))) slice)))
+
+ (doc-view-enlarge scale-factor)
+ (setf (doc-view-current-slice) new-slice)
+ (doc-view-goto-page (doc-view-current-page))))))
+
+(defun doc-view-fit-height-to-window ()
+ "Fit the image height to the window height."
+ (interactive)
+ (let ((win-height (- (nth 3 (window-inside-pixel-edges))
+ (nth 1 (window-inside-pixel-edges))))
+ (slice (doc-view-current-slice)))
+ (if (not slice)
+ (let ((img-height (cdr (image-display-size
+ (image-get-display-property) t))))
+ ;; When users call 'doc-view-fit-height-to-window',
+ ;; they might want to go to next page by typing SPC
+ ;; ONLY once. So I used '(- win-height 1)' instead of
+ ;; 'win-height'
+ (doc-view-enlarge (/ (float (- win-height 1)) (float img-height))))
+
+ ;; If slice is set
+ (let* ((slice-height (nth 3 slice))
+ (scale-factor (/ (float (- win-height 1)) (float slice-height)))
+ (new-slice (mapcar (lambda (x) (ceiling (* scale-factor x))) slice)))
+
+ (doc-view-enlarge scale-factor)
+ (setf (doc-view-current-slice) new-slice)
+ (doc-view-goto-page (doc-view-current-page))))))
+
+(defun doc-view-fit-page-to-window ()
+ "Fit the image to the window.
+More specifically, this function enlarges image by:
+
+min {(window-width / image-width), (window-height / image-height)} times."
+ (interactive)
+ (let ((win-width (- (nth 2 (window-inside-pixel-edges))
+ (nth 0 (window-inside-pixel-edges))))
+ (win-height (- (nth 3 (window-inside-pixel-edges))
+ (nth 1 (window-inside-pixel-edges))))
+ (slice (doc-view-current-slice)))
+ (if (not slice)
+ (let ((img-width (car (image-display-size
+ (image-get-display-property) t)))
+ (img-height (cdr (image-display-size
+ (image-get-display-property) t))))
+ (doc-view-enlarge (min (/ (float win-width) (float img-width))
+ (/ (float (- win-height 1)) (float img-height)))))
+ ;; If slice is set
+ (let* ((slice-width (nth 2 slice))
+ (slice-height (nth 3 slice))
+ (scale-factor (min (/ (float win-width) (float slice-width))
+ (/ (float (- win-height 1)) (float slice-height))))
+ (new-slice (mapcar (lambda (x) (ceiling (* scale-factor x))) slice)))
+ (doc-view-enlarge scale-factor)
+ (setf (doc-view-current-slice) new-slice)
+ (doc-view-goto-page (doc-view-current-page))))))
+
(defun doc-view-reconvert-doc ()
"Reconvert the current document.
Should be invoked when the cached images aren't up-to-date."
(doc-view-kill-proc)
;; Clear the old cached files
(when (file-exists-p (doc-view-current-cache-dir))
- (dired-delete-file (doc-view-current-cache-dir) 'always))
+ (delete-directory (doc-view-current-cache-dir) 'recursive))
(doc-view-initiate-display))
(defun doc-view-sentinel (proc event)
(if (and doc-view-dvipdf-program
(executable-find doc-view-dvipdf-program))
(doc-view-start-process "dvi->pdf" doc-view-dvipdf-program
- (list dvi pdf)
- callback)
+ (list dvi pdf)
+ callback)
(doc-view-start-process "dvi->pdf" doc-view-dvipdfm-program
(list "-o" pdf dvi)
callback)))
+(defun doc-view-odf->pdf (odf callback)
+ "Convert ODF to PDF asynchronously and call CALLBACK when finished.
+The converted PDF is put into the current cache directory, and it
+is named like ODF with the extension turned to pdf."
+ (doc-view-start-process "odf->pdf" doc-view-unoconv-program
+ (list "-f" "pdf" "-o" (doc-view-current-cache-dir) odf)
+ callback))
(defun doc-view-pdf/ps->png (pdf-ps png)
"Convert PDF-PS to PNG asynchronously."
(list (format "-r%d" (round doc-view-resolution))
(concat "-sOutputFile=" png)
pdf-ps))
- (lambda ()
- (when doc-view-current-timer
- (cancel-timer doc-view-current-timer)
- (setq doc-view-current-timer nil))
- (doc-view-display (current-buffer) 'force)))
+ (let ((resolution doc-view-resolution))
+ (lambda ()
+ ;; Only create the resolution file when it's all done, so it also
+ ;; serves as a witness that the conversion is complete.
+ (write-region (prin1-to-string resolution) nil
+ (expand-file-name "resolution.el"
+ (doc-view-current-cache-dir))
+ nil 'silently)
+ (when doc-view-current-timer
+ (cancel-timer doc-view-current-timer)
+ (setq doc-view-current-timer nil))
+ (doc-view-display (current-buffer) 'force))))
;; Update the displayed pages as soon as they're done generating.
(when doc-view-conversion-refresh-interval
(setq doc-view-current-timer
;; (almost) consecutive, but since in 99% of the cases, there'll be only
;; a single page anyway, and of the remaining 1%, few cases will have
;; consecutive pages, it's not worth the trouble.
- (lexical-let ((pdf pdf) (png png) (rest (cdr pages)))
+ (let ((rest (cdr pages)))
(doc-view-pdf->png-1
pdf (format png (car pages)) (car pages)
(lambda ()
(doc-view-pdf->png pdf png rest)
;; Yippie, the important pages are done, update the display.
(clear-image-cache)
+ ;; For the windows that have a message (like "Welcome to
+ ;; DocView") display property, clearing the image cache is
+ ;; not sufficient.
+ (dolist (win (get-buffer-window-list (current-buffer) nil 'visible))
+ (with-selected-window win
+ (when (stringp (get-char-property (point-min) 'display))
+ (doc-view-goto-page (doc-view-current-page)))))
;; Convert the rest of the pages.
(doc-view-pdf/ps->png pdf png)))))))
(ps
;; Doc is a PS, so convert it to PDF (which will be converted to
;; TXT thereafter).
- (lexical-let ((pdf (expand-file-name "doc.pdf"
- (doc-view-current-cache-dir)))
- (txt txt)
- (callback callback))
+ (let ((pdf (expand-file-name "doc.pdf"
+ (doc-view-current-cache-dir))))
(doc-view-ps->pdf doc-view-buffer-file-name pdf
(lambda () (doc-view-pdf->txt pdf txt callback)))))
(dvi
(doc-view-pdf->txt (expand-file-name "doc.pdf"
(doc-view-current-cache-dir))
txt callback))
+ (odf
+ ;; Doc is some ODF (or MS Office) doc. This means that a doc.pdf
+ ;; already exists in its cache subdirectory.
+ (doc-view-pdf->txt (expand-file-name "doc.pdf"
+ (doc-view-current-cache-dir))
+ txt callback))
(t (error "DocView doesn't know what to do"))))
(defun doc-view-ps->pdf (ps pdf callback)
;; resets during the redisplay).
(setq doc-view-pending-cache-flush t)
(let ((png-file (expand-file-name "page-%d.png"
- (doc-view-current-cache-dir)))
- (res-file (expand-file-name "resolution.el"
(doc-view-current-cache-dir))))
(make-directory (doc-view-current-cache-dir) t)
- ;; Save the used resolution so that it can be restored when
- ;; reading the cached files.
- (let ((res doc-view-resolution))
- (with-temp-buffer
- (princ res (current-buffer))
- ;; Don't use write-file, so as to avoid prompts for `require-newline',
- ;; or for pre-existing buffers with the same name, ...
- (write-region nil nil res-file nil 'silently)))
(case doc-view-doc-type
(dvi
;; DVI files have to be converted to PDF before Ghostscript can process
;; it.
+ (let ((pdf (expand-file-name "doc.pdf" doc-view-current-cache-dir)))
+ (doc-view-dvi->pdf doc-view-buffer-file-name pdf
+ (lambda () (doc-view-pdf/ps->png pdf png-file)))))
+ (odf
+ ;; ODF files have to be converted to PDF before Ghostscript can
+ ;; process it.
(lexical-let
((pdf (expand-file-name "doc.pdf" doc-view-current-cache-dir))
+ (opdf (expand-file-name (concat (file-name-sans-extension
+ (file-name-nondirectory doc-view-buffer-file-name))
+ ".pdf")
+ doc-view-current-cache-dir))
(png-file png-file))
- (doc-view-dvi->pdf doc-view-buffer-file-name pdf
- (lambda () (doc-view-pdf/ps->png pdf png-file)))))
- (pdf
- (let ((pages (doc-view-active-pages)))
- ;; Convert PDF to PNG images starting with the active pages.
- (doc-view-pdf->png doc-view-buffer-file-name png-file pages)))
+ ;; The unoconv tool only supports a output directory, but no
+ ;; file name. It's named like the input file with the
+ ;; extension replaced by pdf.
+ (doc-view-odf->pdf doc-view-buffer-file-name
+ (lambda ()
+ ;; Rename to doc.pdf
+ (rename-file opdf pdf)
+ (doc-view-pdf/ps->png pdf png-file)))))
+ (pdf
+ (let ((pages (doc-view-active-pages)))
+ ;; Convert PDF to PNG images starting with the active pages.
+ (doc-view-pdf->png doc-view-buffer-file-name png-file pages)))
(t
;; Convert to PNG images.
(doc-view-pdf/ps->png doc-view-buffer-file-name png-file)))))
(setq doc-view-pending-cache-flush nil))
(let ((ol (doc-view-current-overlay))
(image (if (and file (file-readable-p file))
- (apply 'create-image file 'png nil args)))
+ (if (not (fboundp 'imagemagick-types))
+ (apply 'create-image file 'png nil args)
+ (unless (member :width args)
+ (setq args (append args (list :width doc-view-image-width))))
+ (apply 'create-image file 'imagemagick nil args))))
(slice (doc-view-current-slice)))
(setf (doc-view-current-image) image)
(move-overlay ol (point-min) (point-max))
(and (not (member pagefile prev-pages))
(member pagefile doc-view-current-files)))
(with-selected-window win
- (assert (eq (current-buffer) buffer))
- (doc-view-goto-page page))))))))
+ (assert (eq (current-buffer) buffer))
+ (doc-view-goto-page page))))))))
(defun doc-view-buffer-message ()
;; Only show this message initially, not when refreshing the buffer (in which
(message "DocView: please wait till conversion finished.")
(let ((txt (expand-file-name "doc.txt" (doc-view-current-cache-dir))))
(if (file-readable-p txt)
- (find-file 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)))
(doc-view-doc->txt txt 'doc-view-open-text)))))
;;;;; Toggle between editing and viewing
-
(defun doc-view-toggle-display ()
"Toggle between editing a document as text or viewing it."
(interactive)
(setq buffer-read-only nil)
(remove-overlays (point-min) (point-max) 'doc-view t)
(set (make-local-variable 'image-mode-winprops-alist) t)
- ;; Switch to the previously used major mode or fall back to fundamental
- ;; mode.
- (if doc-view-previous-major-mode
- (funcall doc-view-previous-major-mode)
- (fundamental-mode))
+ ;; Switch to the previously used major mode or fall back to
+ ;; normal mode.
+ (doc-view-fallback-mode)
(doc-view-minor-mode 1))
;; Switch to doc-view-mode
(when (and (buffer-modified-p)
;;;; User interface commands and the mode
-;; (put 'doc-view-mode 'mode-class 'special)
+(put 'doc-view-mode 'mode-class 'special)
(defun doc-view-already-converted-p ()
"Return non-nil if the current doc was already converted."
(and (file-exists-p (doc-view-current-cache-dir))
- (> (length (directory-files (doc-view-current-cache-dir) nil "\\.png$")) 0)))
+ ;; Check that the resolution info is there, otherwise it means
+ ;; the conversion is incomplete.
+ (file-readable-p (expand-file-name "resolution.el"
+ (doc-view-current-cache-dir)))
+ (> (length (directory-files (doc-view-current-cache-dir)
+ nil "\\.png\\'"))
+ 0)))
(defun doc-view-initiate-display ()
;; Switch to image display if possible
(progn
(message "DocView: using cached files!")
;; Load the saved resolution
- (let ((res-file (expand-file-name "resolution.el"
- (doc-view-current-cache-dir)))
- (res doc-view-resolution))
- (with-temp-buffer
- (when (file-exists-p res-file)
- (insert-file-contents res-file)
- (setq res (read (current-buffer)))))
- (when (numberp res)
+ (let* ((res-file (expand-file-name "resolution.el"
+ (doc-view-current-cache-dir)))
+ (res
+ (with-temp-buffer
+ (when (file-readable-p res-file)
+ (insert-file-contents res-file)
+ (read (current-buffer))))))
+ (when (numberp res)
(set (make-local-variable 'doc-view-resolution) res)))
(doc-view-display (current-buffer) 'force))
(doc-view-convert-current-doc))
(concat "No PNG support is available, or some conversion utility for "
(file-name-extension doc-view-buffer-file-name)
" files is missing."))
- (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))))
+ (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)))
(defvar bookmark-make-record-function)
(dolist (x l1) (if (memq x l2) (push x l)))
l))
+(defun doc-view-set-doc-type ()
+ "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)
+ ;; 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))))))
+ (content-types
+ (save-excursion
+ (goto-char (point-min))
+ (cond
+ ((looking-at "%!") '(ps))
+ ((looking-at "%PDF") '(pdf))
+ ((looking-at "\367\002") '(dvi))))))
+ (set (make-local-variable 'doc-view-doc-type)
+ (car (or (doc-view-intersection name-types content-types)
+ (when (and name-types content-types)
+ (error "Conflicting types: name says %s but content says %s"
+ name-types content-types))
+ name-types content-types
+ (error "Cannot determine the document type"))))))
+
;;;###autoload
(defun doc-view-mode ()
"Major mode in DocView buffers.
;; The doc is empty or doesn't exist at all, so fallback to
;; another mode. We used to also check file-exists-p, but this
;; returns nil for tar members.
- (let ((auto-mode-alist (remq (rassq 'doc-view-mode auto-mode-alist)
- auto-mode-alist)))
- (normal-mode))
+ (doc-view-fallback-mode)
(let* ((prev-major-mode (if (eq major-mode 'doc-view-mode)
doc-view-previous-major-mode
- major-mode)))
+ (when (not (memq major-mode
+ '(doc-view-mode fundamental-mode)))
+ major-mode))))
(kill-all-local-variables)
(set (make-local-variable 'doc-view-previous-major-mode) prev-major-mode))
;; Figure out the document type.
- (let ((name-types
- (when buffer-file-name
- (cdr (assoc (file-name-extension buffer-file-name)
- '(("dvi" dvi)
- ("pdf" pdf)
- ("epdf" pdf)
- ("ps" ps)
- ("eps" ps))))))
- (content-types
- (save-excursion
- (goto-char (point-min))
- (cond
- ((looking-at "%!") '(ps))
- ((looking-at "%PDF") '(pdf))
- ((looking-at "\367\002") '(dvi))))))
- (set (make-local-variable 'doc-view-doc-type)
- (car (or (doc-view-intersection name-types content-types)
- (when (and name-types content-types)
- (error "Conflicting types: name says %s but content says %s"
- name-types content-types))
- name-types content-types
- (error "Cannot determine the document type")))))
+ (unless doc-view-doc-type
+ (doc-view-set-doc-type))
(doc-view-make-safe-dir doc-view-cache-directory)
;; Handle compressed files, remote files, files inside archives
(set (make-local-variable 'doc-view-buffer-file-name)
(cond
(jka-compr-really-do-compress
+ ;; FIXME: there's a risk of name conflicts here.
(expand-file-name
(file-name-nondirectory
(file-name-sans-extension buffer-file-name))
;; supposed to return nil for things like local files accessed via
;; `su' or via file://...
((let ((file-name-handler-alist nil))
- (not (file-readable-p buffer-file-name)))
+ (not (and buffer-file-name (file-readable-p buffer-file-name))))
+ ;; FIXME: there's a risk of name conflicts here.
(expand-file-name
- (file-name-nondirectory buffer-file-name)
- doc-view-cache-directory))
+ (if buffer-file-name
+ (file-name-nondirectory buffer-file-name)
+ (buffer-name))
+ doc-view-cache-directory))
(t buffer-file-name)))
(when (not (string= doc-view-buffer-file-name buffer-file-name))
(write-region nil nil doc-view-buffer-file-name))
(set (make-local-variable 'mode-line-position)
'(" P" (:eval (number-to-string (doc-view-current-page)))
- "/" (:eval (number-to-string (length doc-view-current-files)))))
+ "/" (:eval (number-to-string (doc-view-last-page-number)))))
;; Don't scroll unless the user specifically asked for it.
(set (make-local-variable 'auto-hscroll-mode) nil)
(set (make-local-variable 'mwheel-scroll-up-function)
(set (make-local-variable 'view-read-only) nil)
(run-mode-hooks 'doc-view-mode-hook)))
+(defun doc-view-fallback-mode ()
+ "Fallback to the previous or next best major mode."
+ (if doc-view-previous-major-mode
+ (funcall doc-view-previous-major-mode)
+ (let ((auto-mode-alist (rassq-delete-all
+ 'doc-view-mode-maybe
+ (rassq-delete-all 'doc-view-mode
+ (copy-alist auto-mode-alist)))))
+ (normal-mode))))
+
+;;;###autoload
+(defun doc-view-mode-maybe ()
+ "Switch to `doc-view-mode' if possible.
+If the required external tools are not available, then fallback
+to the next best mode."
+ (condition-case nil
+ (doc-view-set-doc-type)
+ (error (doc-view-fallback-mode)))
+ (if (doc-view-mode-p doc-view-doc-type)
+ (doc-view-mode)
+ (doc-view-fallback-mode)))
+
;;;###autoload
(define-minor-mode doc-view-minor-mode
"Toggle Doc view minor mode.
;;;; Bookmark integration
-(declare-function bookmark-make-record-default "bookmark"
- (&optional point-only))
+(declare-function bookmark-make-record-default
+ "bookmark" (&optional no-file no-context posn))
(declare-function bookmark-prop-get "bookmark" (bookmark prop))
(declare-function bookmark-default-handler "bookmark" (bmk))
(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)))))
+ (or (get-buffer-window (current-buffer) 0)
+ (selected-window))
+ (doc-view-goto-page page)))))
(provide 'doc-view)
;; Local Variables:
-;; mode: outline-minor
+;; eval: (outline-minor-mode)
;; End:
-;; arch-tag: 5d6e5c5e-095f-489e-b4e4-1ca90a7d79be
;;; doc-view.el ends here