From c02ac7ce9db90f1772658beae4dd0c51debf0bdd Mon Sep 17 00:00:00 2001 From: Elias Pipping Date: Sat, 1 Sep 2012 18:15:11 +0200 Subject: [PATCH 2/2] Add DjVu support --- lisp/doc-view.el | 135 ++++++++++++++++++++++++++++++++++++++++++------------- lisp/files.el | 2 +- 2 files changed, 106 insertions(+), 31 deletions(-) diff --git a/lisp/doc-view.el b/lisp/doc-view.el index 8e1b309..ea2023e 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el @@ -138,6 +138,7 @@ ;;; Code: (eval-when-compile (require 'cl-lib)) +(require 'cl) (require 'dired) (require 'image-mode) (require 'jka-compr) @@ -145,7 +146,7 @@ ;;;; Customization Options (defgroup doc-view nil - "In-buffer viewer for PDF, PostScript and DVI files." + "In-buffer viewer for PDF, PostScript, DVI, and DJVU files." :link '(function-link doc-view) :version "22.2" :group 'applications @@ -171,6 +172,18 @@ function) :group 'doc-view) +(defcustom doc-view-ddjvu-program "ddjvu" + "Program to convert DjVu files to PNG." + :type 'file + :group 'doc-view) + +(defcustom doc-view-djvu->png-converter-invocation + 'doc-view-djvu->png-converter-invocation-ddjvu + "Called to convert a DJVU file into a PNG file" + :type '(radio (function-item doc-view-djvu->png-converter-invocation-ddjvu :doc "Use ddjvu") + function) + :group 'doc-view) + (defcustom doc-view-ps->png-converter-invocation 'doc-view-ps->png-converter-invocation-ghostscript "Called to convert a PS file into a PNG file" @@ -332,6 +345,17 @@ the (uncompressed, extracted) file residing in "The type of document in the current buffer. Can be `dvi', `pdf', or `ps'.") +(defvar doc-view-single-page-converter nil + "Called to convert a single page of the document to a PNG file.") + +(defvar doc-view-image-type nil + "The type of image in the current buffer. +Can be `png' or `tiff'.") + +(defvar doc-view-image-file-extension nil + "The file extension of the image type in the current buffer. +Can be `png' or `tif'.") + ;;;; DocView Keymaps (defvar doc-view-mode-map @@ -470,24 +494,25 @@ Can be `dvi', `pdf', or `ps'.") ;; We used to find the file name from doc-view-current-files but ;; that's not right if the pages are not generated sequentially ;; or if the page isn't in doc-view-current-files yet. - (let ((file (expand-file-name (format "page-%d.png" page) + (let ((file (expand-file-name (format "page-%d.%s" page doc-view-image-file-extension) (doc-view-current-cache-dir)))) (doc-view-insert-image file :pointer 'arrow) (set-window-hscroll (selected-window) hscroll) (when (and (not (file-exists-p file)) 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 - (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)))))))) + (funcall doc-view-single-page-converter + doc-view-buffer-file-name file page + (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)))))))) (overlay-put (doc-view-current-overlay) 'help-echo (doc-view-current-info)))) @@ -663,7 +688,7 @@ Document types are symbols like `dvi', `ps', `pdf', or `odf' (any OpenDocument format)." (and (display-graphic-p) (or (image-type-available-p 'imagemagick) - (image-type-available-p 'png)) + (image-type-available-p 'png)) ; FIXME: do not check for PNG with djvu (cond ((eq type 'dvi) (and (doc-view-mode-p 'pdf) @@ -680,6 +705,8 @@ OpenDocument format)." (and doc-view-unoconv-program (executable-find doc-view-unoconv-program) (doc-view-mode-p 'pdf))) + ((eq type 'djvu) + (executable-find doc-view-ddjvu-program)) ; FIXME: check for TIFF with djvu (t ;; unknown image type nil)))) @@ -850,6 +877,16 @@ Should be invoked when the cached images aren't up-to-date." (defalias 'doc-view-ps->png-converter-invocation-ghostscript 'doc-view-pdf->png-converter-invocation-ghostscript) +(defun doc-view-djvu->png-converter-invocation-ddjvu (resolution djvu png &optional page) + `((command . ,doc-view-ddjvu-program) + (arguments . ("-format=tiff" + ,(format "-scale=%d" resolution) ; ddjvu only accepts the range 1-999 + ;; -eachpage was only added after djvulibre-3.5.25.3! + ,@(unless page '("-eachpage")) + ,@(if page `(,(format "-page=%d" page))) + ,djvu + ,png)))) + (defun doc-view-pdf->png-converter-invocation-mupdf (resolution pdf png &optional page) `((command . ,doc-view-pdfdraw-program) (arguments . (,(concat "-o" png) @@ -867,10 +904,12 @@ is named like ODF with the extension turned to pdf." (defun doc-view-pdf/ps->png (pdf-ps png) "Convert PDF-PS to PNG asynchronously." - (let ((invocation (case doc-view-doc-type - (pdf (funcall doc-view-pdf->png-converter-invocation + (let ((invocation (pcase doc-view-doc-type + (`pdf (funcall doc-view-pdf->png-converter-invocation (round doc-view-resolution) pdf-ps png)) - (t (funcall doc-view-ps->png-converter-invocation + (`djvu (funcall doc-view-djvu->png-converter-invocation + (round doc-view-resolution) pdf-ps png)) + (_ (funcall doc-view-ps->png-converter-invocation (round doc-view-resolution) pdf-ps png))))) (doc-view-start-process "pdf/ps->png" (cdr (assoc 'command invocation)) @@ -904,9 +943,19 @@ Call CALLBACK with no arguments when done." (cdr (assoc 'arguments invocation)) callback))) +(defun doc-view-djvu->png-1 (djvu png page callback) + "Convert a PAGE of a DJVU file to PNG asynchronously. +Call CALLBACK with no arguments when done." + (let ((invocation (funcall doc-view-djvu->png-converter-invocation + (round doc-view-resolution) djvu png page))) + (doc-view-start-process + "djvu->png" (cdr (assoc 'command invocation)) + (cdr (assoc 'arguments invocation)) + callback))) + (declare-function clear-image-cache "image.c" (&optional filter)) -(defun doc-view-pdf->png (pdf png pages) +(defun doc-view-document->png (pdf png pages) "Convert a PDF file to PNG asynchronously. Start by converting PAGES, and then the rest." (if (null pages) @@ -916,11 +965,11 @@ Start by converting PAGES, and then the rest." ;; a single page anyway, and of the remaining 1%, few cases will have ;; consecutive pages, it's not worth the trouble. (let ((rest (cdr pages))) - (doc-view-pdf->png-1 - pdf (format png (car pages)) (car pages) + (funcall doc-view-single-page-converter + pdf (format png (car pages)) (car pages) (lambda () (if rest - (doc-view-pdf->png pdf png rest) + (doc-view-document->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 @@ -928,8 +977,8 @@ Start by converting PAGES, and then the rest." ;; 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))))) + (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))))))) @@ -999,7 +1048,7 @@ Those files are saved in the directory given by the function ;; preserves the horizontal/vertical scroll settings (which are otherwise ;; resets during the redisplay). (setq doc-view-pending-cache-flush t) - (let ((png-file (expand-file-name "page-%d.png" + (let ((png-file (expand-file-name (concat "page-%d." doc-view-image-file-extension) (doc-view-current-cache-dir)))) (make-directory (doc-view-current-cache-dir) t) (pcase doc-view-doc-type @@ -1028,7 +1077,10 @@ Those files are saved in the directory given by the function (`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))) + (doc-view-document->png doc-view-buffer-file-name png-file pages))) + (`djvu + (let ((pages (doc-view-active-pages))) + (doc-view-document->png doc-view-buffer-file-name png-file pages))) (_ ;; Convert to PNG images. (doc-view-pdf/ps->png doc-view-buffer-file-name png-file))))) @@ -1171,7 +1223,7 @@ ARGS is a list of image descriptors." (let ((ol (doc-view-current-overlay)) (image (if (and file (file-readable-p file)) (if (not (fboundp 'imagemagick-types)) - (apply 'create-image file 'png nil args) + (apply 'create-image file doc-view-image-type nil args) (unless (member :width args) (setq args (append args (list :width doc-view-image-width)))) (apply 'create-image file 'imagemagick nil args)))) @@ -1221,12 +1273,12 @@ have the page we want to view." (let ((prev-pages doc-view-current-files)) (setq doc-view-current-files (sort (directory-files (doc-view-current-cache-dir) t - "page-[0-9]+\\.png" t) + (concat "page-[0-9]+\\." doc-view-image-file-extension) t) 'doc-view-sort)) (dolist (win (or (get-buffer-window-list buffer nil t) (list t))) (let* ((page (doc-view-current-page win)) - (pagefile (expand-file-name (format "page-%d.png" page) + (pagefile (expand-file-name (format "page-%d.%s" page doc-view-image-file-extension) (doc-view-current-cache-dir)))) (when (or force (and (not (member pagefile prev-pages)) @@ -1421,7 +1473,7 @@ If BACKWARD is non-nil, jump to the previous match." (file-readable-p (expand-file-name "resolution.el" (doc-view-current-cache-dir))) (> (length (directory-files (doc-view-current-cache-dir) - nil "\\.png\\'")) + nil (concat "\\." doc-view-image-file-extension "\\'"))) 0))) (defun doc-view-initiate-display () @@ -1494,6 +1546,8 @@ If BACKWARD is non-nil, jump to the previous match." ("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) @@ -1508,7 +1562,8 @@ If BACKWARD is non-nil, jump to the previous match." (cond ((looking-at "%!") '(ps)) ((looking-at "%PDF") '(pdf)) - ((looking-at "\367\002") '(dvi)))))) + ((looking-at "\367\002") '(dvi)) + ((looking-at "AT&TFORM") '(djvu)))))) (set (make-local-variable 'doc-view-doc-type) (car (or (doc-view-intersection name-types content-types) (when (and name-types content-types) @@ -1517,6 +1572,24 @@ If BACKWARD is non-nil, jump to the previous match." name-types content-types (error "Cannot determine the document type")))))) +(defun doc-view-set-up-single-converter () + "Find the right single-page converter for the current document type" + (set (make-local-variable 'doc-view-single-page-converter) + (pcase doc-view-doc-type + (`djvu 'doc-view-djvu->png-1) + (_ 'doc-view-pdf->png-1)))) + +(defun doc-view-prepare-for-images () + "Figure out the appropriate image type for the current document type" + (multiple-value-bind (type extension) + (pcase doc-view-doc-type + (`djvu (values 'tiff "tif")) + (_ (values 'png "png"))) + (set (make-local-variable 'doc-view-image-type) + type) + (set (make-local-variable 'doc-view-image-file-extension) + extension))) + ;;;###autoload (defun doc-view-mode () "Major mode in DocView buffers. @@ -1549,6 +1622,8 @@ toggle between displaying the document or editing it as text. ;; Figure out the document type. (unless doc-view-doc-type (doc-view-set-doc-type)) + (doc-view-set-up-single-converter) + (doc-view-prepare-for-images) (doc-view-make-safe-dir doc-view-cache-directory) ;; Handle compressed files, remote files, files inside archives diff --git a/lisp/files.el b/lisp/files.el index 62ad96c..c52ef28 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2355,7 +2355,7 @@ ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|7Z\\)\\'" . archive-mode) ("\\.\\(diffs?\\|patch\\|rej\\)\\'" . diff-mode) ("\\.\\(dif\\|pat\\)\\'" . diff-mode) ; for MSDOG ("\\.[eE]?[pP][sS]\\'" . ps-mode) - ("\\.\\(?:PDF\\|DVI\\|OD[FGPST]\\|DOCX?\\|XLSX?\\|PPTX?\\|pdf\\|dvi\\|od[fgpst]\\|docx?\\|xlsx?\\|pptx?\\)\\'" . doc-view-mode-maybe) + ("\\.\\(?:PDF\\|DVI\\|OD[FGPST]\\|DOCX?\\|XLSX?\\|PPTX?\\|pdf\\|dvi\\|djvu\\|od[fgpst]\\|docx?\\|xlsx?\\|pptx?\\)\\'" . doc-view-mode-maybe) ("configure\\.\\(ac\\|in\\)\\'" . autoconf-mode) ("\\.s\\(v\\|iv\\|ieve\\)\\'" . sieve-mode) ("BROWSE\\'" . ebrowse-tree-mode) -- 1.8.0.2