* bug#13164: [PATCH] View DjVu documents through doc-view-mode
2013-01-11 15:02 ` Tassilo Horn
@ 2013-01-21 23:10 ` Elias Pipping
2013-01-22 15:02 ` Stefan Monnier
0 siblings, 1 reply; 11+ messages in thread
From: Elias Pipping @ 2013-01-21 23:10 UTC (permalink / raw)
To: Tassilo Horn; +Cc: 13164
[-- Attachment #1: Type: text/plain, Size: 1283 bytes --]
Tassilo Horn <tsdh@gnu.org> writes:
> Stefan Monnier <monnier@iro.umontreal.ca> writes:
>
> Hi Elias & Stefan,
>
>>> I've attached three more tiny patches.
>>
>> Thanks, installed,
>
> I really like the new mupdf support. It's much faster than ghostscript.
>
> There seems to be some minor issue, though: the last-page counter in the
> mode-line doesn't seem to be updated properly during conversion. I've
> just tested with some 700 pages PDF. Initially, it displayed "1/4" but
> didn't increase anymore. To make it update again, I needed to do
> something, e.g., switch to another page or just scroll a bit.
Indeed, I can reproduce your problem with a recent version of emacs,
namely
commit c5a8149837c5ed53655d4383dea3b8f29374b266
Author: Glenn Morris <rgm@gnu.org>
Date: Sat Jan 19 18:40:49 2013 -0800
* lisp-mode.el (emacs-lisp-mode-map): Add native profiler menu entries.
I do not have it with emacs 24.2.92 and my original two patches (I'm
attaching them in case you want to verify that; please note that you
need to have
(setq doc-view-pdf->png-converter-invocation
'doc-view-pdf->png-converter-invocation-mupdf)
in your init.el with that version for mupdf to be used). So it must've
snuck in sometime after.
Best regards,
Elias Pipping
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Make-doc-view-pdf-png-1-customizable.patch --]
[-- Type: text/x-patch, Size: 5456 bytes --]
From f6684796d3a9cb0016b50251d9b36a4c618855c1 Mon Sep 17 00:00:00 2001
From: Elias Pipping <pipping@exherbo.org>
Date: Sun, 26 Jun 2011 20:02:29 +0200
Subject: [PATCH 1/2] Make doc-view-pdf->png-1 customizable
This allows mupdf's mudraw to be used as a pdf->png converter
Not only is it faster than ghostscript, it also does anti-aliasing
---
lisp/doc-view.el | 73 ++++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 55 insertions(+), 18 deletions(-)
diff --git a/lisp/doc-view.el b/lisp/doc-view.el
index f8975a5..8e1b309 100644
--- a/lisp/doc-view.el
+++ b/lisp/doc-view.el
@@ -158,6 +158,26 @@
:type 'file
:group 'doc-view)
+(defcustom doc-view-pdfdraw-program "mudraw"
+ "Program to convert PDF files to PNG."
+ :type 'file
+ :group 'doc-view)
+
+(defcustom doc-view-pdf->png-converter-invocation
+ 'doc-view-pdf->png-converter-invocation-ghostscript
+ "Called to convert a PDF file into a PNG file"
+ :type '(radio (function-item doc-view-pdf->png-converter-invocation-ghostscript :doc "Use ghostscript")
+ (function-item doc-view-pdf->png-converter-invocation-mupdf :doc "Use mupdf")
+ 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"
+ :type '(radio (function-item doc-view-ps->png-converter-invocation-ghostscript :doc "Use ghostscript")
+ function)
+ :group 'doc-view)
+
(defcustom doc-view-ghostscript-options
'("-dSAFER" ;; Avoid security problems when rendering files from untrusted
;; sources.
@@ -653,6 +673,7 @@ OpenDocument format)."
(executable-find doc-view-dvipdfm-program)))))
((or (eq type 'postscript) (eq type 'ps) (eq type 'eps)
(eq type 'pdf))
+ ;; FIXME: allow mupdf here
(and doc-view-ghostscript-program
(executable-find doc-view-ghostscript-program)))
((eq type 'odf)
@@ -817,6 +838,25 @@ Should be invoked when the cached images aren't up-to-date."
(list "-o" pdf dvi)
callback)))
+(defun doc-view-pdf->png-converter-invocation-ghostscript (resolution pdf png &optional page)
+ `((command . ,doc-view-ghostscript-program)
+ (arguments . (,@doc-view-ghostscript-options
+ ,(format "-r%d" resolution)
+ ,@(if page `(,(format "-dFirstPage=%d" page)))
+ ,@(if page `(,(format "-dLastPage=%d" page)))
+ ,(concat "-sOutputFile=" png)
+ ,pdf))))
+
+(defalias 'doc-view-ps->png-converter-invocation-ghostscript
+ 'doc-view-pdf->png-converter-invocation-ghostscript)
+
+(defun doc-view-pdf->png-converter-invocation-mupdf (resolution pdf png &optional page)
+ `((command . ,doc-view-pdfdraw-program)
+ (arguments . (,(concat "-o" png)
+ ,(format "-r%d" resolution)
+ ,pdf
+ ,@(if page `(,(format "%d" page)))))))
+
(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
@@ -827,12 +867,14 @@ 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."
- (doc-view-start-process
- "pdf/ps->png" doc-view-ghostscript-program
- (append doc-view-ghostscript-options
- (list (format "-r%d" (round doc-view-resolution))
- (concat "-sOutputFile=" png)
- pdf-ps))
+ (let ((invocation (case 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
+ (round doc-view-resolution) pdf-ps png)))))
+ (doc-view-start-process
+ "pdf/ps->png" (cdr (assoc 'command invocation))
+ (cdr (assoc 'arguments invocation))
(let ((resolution doc-view-resolution))
(lambda ()
;; Only create the resolution file when it's all done, so it also
@@ -844,7 +886,7 @@ is named like ODF with the extension turned to pdf."
(when doc-view-current-timer
(cancel-timer doc-view-current-timer)
(setq doc-view-current-timer nil))
- (doc-view-display (current-buffer) 'force))))
+ (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
@@ -855,17 +897,12 @@ is named like ODF with the extension turned to pdf."
(defun doc-view-pdf->png-1 (pdf png page callback)
"Convert a PAGE of a PDF file to PNG asynchronously.
Call CALLBACK with no arguments when done."
- (doc-view-start-process
- "pdf->png-1" doc-view-ghostscript-program
- (append doc-view-ghostscript-options
- (list (format "-r%d" (round doc-view-resolution))
- ;; Sadly, `gs' only supports the page-range
- ;; for PDF files.
- (format "-dFirstPage=%d" page)
- (format "-dLastPage=%d" page)
- (concat "-sOutputFile=" png)
- pdf))
- callback))
+ (let ((invocation (funcall doc-view-pdf->png-converter-invocation
+ (round doc-view-resolution) pdf png page)))
+ (doc-view-start-process
+ "pdf/ps->png" (cdr (assoc 'command invocation))
+ (cdr (assoc 'arguments invocation))
+ callback)))
(declare-function clear-image-cache "image.c" (&optional filter))
--
1.8.0.2
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Add-DjVu-support.patch --]
[-- Type: text/x-patch, Size: 14234 bytes --]
From c02ac7ce9db90f1772658beae4dd0c51debf0bdd Mon Sep 17 00:00:00 2001
From: Elias Pipping <pipping@exherbo.org>
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
^ permalink raw reply related [flat|nested] 11+ messages in thread