1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2025-02-03 20:24:29 +00:00

* lisp/doc-view.el: Add support for DjVu.

(doc-view-djvu->png-converter-function): New config var.
(doc-view-single-page-converter-function, doc-view--image-type)
(doc-view--image-file-extension): New vars.
(doc-view-mode): Initialize them.
(doc-view-goto-page): Use them.
(doc-view-mode-p): Add support for ddjvu.
(doc-view-djvu->png-converter-ddjvu, doc-view-djvu->png-1)
(doc-view-set-up-single-converter): New funs.
(doc-view-pdf/ps->png): Extend for djvu.
(doc-view-document->png): Rename from doc-view-pdf->png.
(doc-view-convert-current-doc): Handle djvu.
(doc-view-insert-image, doc-view-display)
(doc-view-already-converted-p): Don't hardcode png.
(doc-view-set-doc-type): Recognize djvu docs.

Fixes: debbugs:13164
This commit is contained in:
Elias Pipping 2013-01-09 16:24:42 -05:00 committed by Stefan Monnier
parent 85f17e350d
commit 2b541f9a8d
2 changed files with 135 additions and 40 deletions

View File

@ -1,3 +1,21 @@
2013-01-09 Elias Pipping <pipping@lavabit.com>
* doc-view.el: Add support for DjVu (bug#13164).
(doc-view-djvu->png-converter-function): New config var.
(doc-view-single-page-converter-function, doc-view--image-type)
(doc-view--image-file-extension): New vars.
(doc-view-mode): Initialize them.
(doc-view-goto-page): Use them.
(doc-view-mode-p): Add support for ddjvu.
(doc-view-djvu->png-converter-ddjvu, doc-view-djvu->png-1)
(doc-view-set-up-single-converter): New funs.
(doc-view-pdf/ps->png): Extend for djvu.
(doc-view-document->png): Rename from doc-view-pdf->png.
(doc-view-convert-current-doc): Handle djvu.
(doc-view-insert-image, doc-view-display)
(doc-view-already-converted-p): Don't hardcode png.
(doc-view-set-doc-type): Recognize djvu docs.
2013-01-09 Elias Pipping <pipping@lavabit.com>
* doc-view.el: Add support for mupdf converter (bug#13164).

View File

@ -145,7 +145,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
@ -174,6 +174,15 @@
function)
:version "24.4")
;; FIXME: Get rid of it: there's no choice.
(defcustom doc-view-djvu->png-converter-function
'doc-view-djvu->png-converter-ddjvu
"Function to call to convert a DJVU file into a PNG file"
:type '(radio (function-item doc-view-djvu->png-converter-ddjvu
:doc "Use ddjvu")
function))
;; FIXME: Get rid of it: there's no choice.
(defcustom doc-view-ps->png-converter-function
'doc-view-ps->png-converter-ghostscript
"Function to call to convert a PS file into a PNG file."
@ -344,6 +353,20 @@ 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-function nil
"Function to call to convert a single page of the document to a bitmap file.
May operate on the source document or on some intermediate (typically PDF)
conversion of it.")
(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
;; FIXME: Replace it with a `format' string, like "page-%d.png".
"The file extension of the image type in the current buffer.
Can be `png' or `tif'.")
;;;; DocView Keymaps
(defvar doc-view-mode-map
@ -482,24 +505,26 @@ 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)
(doc-view-current-cache-dir))))
(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-function
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))))
@ -692,6 +717,8 @@ OpenDocument format)."
(and doc-view-unoconv-program
(executable-find doc-view-unoconv-program)
(doc-view-mode-p 'pdf)))
((eq type 'djvu)
(executable-find "ddjvu"))
(t ;; unknown image type
nil))))
@ -863,6 +890,17 @@ Should be invoked when the cached images aren't up-to-date."
(defalias 'doc-view-ps->png-converter-ghostscript
'doc-view-pdf->png-converter-ghostscript)
(defun doc-view-djvu->png-converter-ddjvu (resolution djvu png &optional page)
`((command . "ddjvu")
(arguments . ("-format=tiff"
;; ddjvu only accepts the range 1-999.
,(format "-scale=%d" resolution)
;; -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-mupdf (resolution pdf png &optional page)
`((command . ,doc-view-pdfdraw-program)
(arguments . (,(concat "-o" png)
@ -879,13 +917,15 @@ is named like ODF with the extension turned to pdf."
callback))
(defun doc-view-pdf/ps->png (pdf-ps png)
;; FIXME: Fix name and docstring to account for djvu&tiff.
"Convert PDF-PS to PNG asynchronously."
(let ((invocation
(pcase doc-view-doc-type
(`pdf (funcall doc-view-pdf->png-converter-function
(round doc-view-resolution) pdf-ps png))
(_ (funcall doc-view-ps->png-converter-function
(round doc-view-resolution) pdf-ps png)))))
(funcall (pcase doc-view-doc-type
(`pdf doc-view-pdf->png-converter-function)
(`djvu doc-view-djvu->png-converter-function)
(_ doc-view-ps->png-converter-function))
(round doc-view-resolution) pdf-ps png)))
(doc-view-start-process
"pdf/ps->png" (cdr (assoc 'command invocation))
(cdr (assoc 'arguments invocation))
@ -918,9 +958,20 @@ 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 bitmap asynchronously.
Call CALLBACK with no arguments when done."
(let ((invocation (funcall doc-view-djvu->png-converter-function
(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 single-page-converter)
;; FIXME: Fix docstring.
"Convert a PDF file to PNG asynchronously.
Start by converting PAGES, and then the rest."
(if (null pages)
@ -930,11 +981,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 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
@ -942,8 +993,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)))))))
@ -1013,8 +1064,9 @@ 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"
(doc-view-current-cache-dir))))
(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
(`dvi
@ -1027,11 +1079,12 @@ Those files are saved in the directory given by the function
;; ODF files have to be converted to PDF before Ghostscript can
;; process it.
(let ((pdf (doc-view-current-cache-doc-pdf))
(opdf (expand-file-name (concat (file-name-base doc-view-buffer-file-name)
".pdf")
doc-view-current-cache-dir))
(opdf (expand-file-name
(concat (file-name-base doc-view-buffer-file-name)
".pdf")
doc-view-current-cache-dir))
(png-file png-file))
;; The unoconv tool only supports a output directory, but no
;; The unoconv tool only supports an 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
@ -1042,7 +1095,12 @@ 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
'doc-view-pdf->png-1)))
(`djvu
(let ((pages (doc-view-active-pages)))
(doc-view-document->png doc-view-buffer-file-name png-file pages
'doc-view-djvu->png-1)))
(_
;; Convert to PNG images.
(doc-view-pdf/ps->png doc-view-buffer-file-name png-file)))))
@ -1186,7 +1244,7 @@ ARGS is a list of image descriptors."
(image (if (and file (file-readable-p file))
(if (not (and doc-view-scale-internally
(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 `(,@args :width ,doc-view-image-width)))
(apply 'create-image file 'imagemagick nil args))))
@ -1236,13 +1294,17 @@ 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)
(doc-view-current-cache-dir))))
(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))
(member pagefile doc-view-current-files)))
@ -1435,12 +1497,13 @@ If BACKWARD is non-nil, jump to the previous match."
;; 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\\'"))
(> (length (directory-files
(doc-view-current-cache-dir)
nil (concat "\\." doc-view--image-file-extension "\\'")))
0)))
(defun doc-view-initiate-display ()
;; Switch to image display if possible
;; Switch to image display if possible.
(if (doc-view-mode-p doc-view-doc-type)
(progn
(doc-view-buffer-message)
@ -1509,6 +1572,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)
@ -1523,7 +1588,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)
@ -1532,6 +1598,16 @@ 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"
(pcase-let ((`(,conv-function ,type ,extension)
(pcase doc-view-doc-type
(`djvu (list #'doc-view-djvu->png-1 'tiff "tif"))
(_ (list #'doc-view-pdf->png-1 'png "png")))))
(setq-local doc-view-single-page-converter-function conv-function)
(setq-local doc-view--image-type type)
(setq-local doc-view--image-file-extension extension)))
;;;###autoload
(defun doc-view-mode ()
"Major mode in DocView buffers.
@ -1564,6 +1640,7 @@ 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-make-safe-dir doc-view-cache-directory)
;; Handle compressed files, remote files, files inside archives