aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/doc-view.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/doc-view.el')
-rw-r--r--lisp/doc-view.el157
1 files changed, 117 insertions, 40 deletions
diff --git a/lisp/doc-view.el b/lisp/doc-view.el
index 4713ab660fe..0658a11c30a 100644
--- a/lisp/doc-view.el
+++ b/lisp/doc-view.el
@@ -145,7 +145,7 @@
145;;;; Customization Options 145;;;; Customization Options
146 146
147(defgroup doc-view nil 147(defgroup doc-view nil
148 "In-buffer viewer for PDF, PostScript and DVI files." 148 "In-buffer viewer for PDF, PostScript, DVI, and DJVU files."
149 :link '(function-link doc-view) 149 :link '(function-link doc-view)
150 :version "22.2" 150 :version "22.2"
151 :group 'applications 151 :group 'applications
@@ -174,6 +174,15 @@
174 function) 174 function)
175 :version "24.4") 175 :version "24.4")
176 176
177;; FIXME: Get rid of it: there's no choice.
178(defcustom doc-view-djvu->png-converter-function
179 'doc-view-djvu->png-converter-ddjvu
180 "Function to call to convert a DJVU file into a PNG file"
181 :type '(radio (function-item doc-view-djvu->png-converter-ddjvu
182 :doc "Use ddjvu")
183 function))
184
185;; FIXME: Get rid of it: there's no choice.
177(defcustom doc-view-ps->png-converter-function 186(defcustom doc-view-ps->png-converter-function
178 'doc-view-ps->png-converter-ghostscript 187 'doc-view-ps->png-converter-ghostscript
179 "Function to call to convert a PS file into a PNG file." 188 "Function to call to convert a PS file into a PNG file."
@@ -344,6 +353,20 @@ the (uncompressed, extracted) file residing in
344 "The type of document in the current buffer. 353 "The type of document in the current buffer.
345Can be `dvi', `pdf', or `ps'.") 354Can be `dvi', `pdf', or `ps'.")
346 355
356(defvar doc-view-single-page-converter-function nil
357 "Function to call to convert a single page of the document to a bitmap file.
358May operate on the source document or on some intermediate (typically PDF)
359conversion of it.")
360
361(defvar doc-view--image-type nil
362 "The type of image in the current buffer.
363Can be `png' or `tiff'.")
364
365(defvar doc-view--image-file-extension nil
366 ;; FIXME: Replace it with a `format' string, like "page-%d.png".
367 "The file extension of the image type in the current buffer.
368Can be `png' or `tif'.")
369
347;;;; DocView Keymaps 370;;;; DocView Keymaps
348 371
349(defvar doc-view-mode-map 372(defvar doc-view-mode-map
@@ -482,24 +505,26 @@ Can be `dvi', `pdf', or `ps'.")
482 ;; We used to find the file name from doc-view-current-files but 505 ;; We used to find the file name from doc-view-current-files but
483 ;; that's not right if the pages are not generated sequentially 506 ;; that's not right if the pages are not generated sequentially
484 ;; or if the page isn't in doc-view-current-files yet. 507 ;; or if the page isn't in doc-view-current-files yet.
485 (let ((file (expand-file-name (format "page-%d.png" page) 508 (let ((file (expand-file-name
486 (doc-view-current-cache-dir)))) 509 (format "page-%d.%s" page doc-view--image-file-extension)
510 (doc-view-current-cache-dir))))
487 (doc-view-insert-image file :pointer 'arrow) 511 (doc-view-insert-image file :pointer 'arrow)
488 (set-window-hscroll (selected-window) hscroll) 512 (set-window-hscroll (selected-window) hscroll)
489 (when (and (not (file-exists-p file)) 513 (when (and (not (file-exists-p file))
490 doc-view-current-converter-processes) 514 doc-view-current-converter-processes)
491 ;; The PNG file hasn't been generated yet. 515 ;; The PNG file hasn't been generated yet.
492 (doc-view-pdf->png-1 doc-view-buffer-file-name file page 516 (funcall doc-view-single-page-converter-function
493 (let ((win (selected-window))) 517 doc-view-buffer-file-name file page
494 (lambda () 518 (let ((win (selected-window)))
495 (and (eq (current-buffer) (window-buffer win)) 519 (lambda ()
496 ;; If we changed page in the mean 520 (and (eq (current-buffer) (window-buffer win))
497 ;; time, don't mess things up. 521 ;; If we changed page in the mean
498 (eq (doc-view-current-page win) page) 522 ;; time, don't mess things up.
499 ;; Make sure we don't infloop. 523 (eq (doc-view-current-page win) page)
500 (file-readable-p file) 524 ;; Make sure we don't infloop.
501 (with-selected-window win 525 (file-readable-p file)
502 (doc-view-goto-page page)))))))) 526 (with-selected-window win
527 (doc-view-goto-page page))))))))
503 (overlay-put (doc-view-current-overlay) 528 (overlay-put (doc-view-current-overlay)
504 'help-echo (doc-view-current-info)))) 529 'help-echo (doc-view-current-info))))
505 530
@@ -692,6 +717,8 @@ OpenDocument format)."
692 (and doc-view-unoconv-program 717 (and doc-view-unoconv-program
693 (executable-find doc-view-unoconv-program) 718 (executable-find doc-view-unoconv-program)
694 (doc-view-mode-p 'pdf))) 719 (doc-view-mode-p 'pdf)))
720 ((eq type 'djvu)
721 (executable-find "ddjvu"))
695 (t ;; unknown image type 722 (t ;; unknown image type
696 nil)))) 723 nil))))
697 724
@@ -863,6 +890,17 @@ Should be invoked when the cached images aren't up-to-date."
863(defalias 'doc-view-ps->png-converter-ghostscript 890(defalias 'doc-view-ps->png-converter-ghostscript
864 'doc-view-pdf->png-converter-ghostscript) 891 'doc-view-pdf->png-converter-ghostscript)
865 892
893(defun doc-view-djvu->png-converter-ddjvu (resolution djvu png &optional page)
894 `((command . "ddjvu")
895 (arguments . ("-format=tiff"
896 ;; ddjvu only accepts the range 1-999.
897 ,(format "-scale=%d" resolution)
898 ;; -eachpage was only added after djvulibre-3.5.25.3!
899 ,@(unless page '("-eachpage"))
900 ,@(if page `(,(format "-page=%d" page)))
901 ,djvu
902 ,png))))
903
866(defun doc-view-pdf->png-converter-mupdf (resolution pdf png &optional page) 904(defun doc-view-pdf->png-converter-mupdf (resolution pdf png &optional page)
867 `((command . ,doc-view-pdfdraw-program) 905 `((command . ,doc-view-pdfdraw-program)
868 (arguments . (,(concat "-o" png) 906 (arguments . (,(concat "-o" png)
@@ -879,13 +917,15 @@ is named like ODF with the extension turned to pdf."
879 callback)) 917 callback))
880 918
881(defun doc-view-pdf/ps->png (pdf-ps png) 919(defun doc-view-pdf/ps->png (pdf-ps png)
920 ;; FIXME: Fix name and docstring to account for djvu&tiff.
882 "Convert PDF-PS to PNG asynchronously." 921 "Convert PDF-PS to PNG asynchronously."
883 (let ((invocation 922 (let ((invocation
884 (pcase doc-view-doc-type 923 (funcall (pcase doc-view-doc-type
885 (`pdf (funcall doc-view-pdf->png-converter-function 924 (`pdf doc-view-pdf->png-converter-function)
886 (round doc-view-resolution) pdf-ps png)) 925 (`djvu doc-view-djvu->png-converter-function)
887 (_ (funcall doc-view-ps->png-converter-function 926 (_ doc-view-ps->png-converter-function))
888 (round doc-view-resolution) pdf-ps png))))) 927 (round doc-view-resolution) pdf-ps png)))
928
889 (doc-view-start-process 929 (doc-view-start-process
890 "pdf/ps->png" (cdr (assoc 'command invocation)) 930 "pdf/ps->png" (cdr (assoc 'command invocation))
891 (cdr (assoc 'arguments invocation)) 931 (cdr (assoc 'arguments invocation))
@@ -918,9 +958,20 @@ Call CALLBACK with no arguments when done."
918 (cdr (assoc 'arguments invocation)) 958 (cdr (assoc 'arguments invocation))
919 callback))) 959 callback)))
920 960
961(defun doc-view-djvu->png-1 (djvu png page callback)
962 "Convert a PAGE of a DJVU file to bitmap asynchronously.
963Call CALLBACK with no arguments when done."
964 (let ((invocation (funcall doc-view-djvu->png-converter-function
965 (round doc-view-resolution) djvu png page)))
966 (doc-view-start-process
967 "djvu->png" (cdr (assoc 'command invocation))
968 (cdr (assoc 'arguments invocation))
969 callback)))
970
921(declare-function clear-image-cache "image.c" (&optional filter)) 971(declare-function clear-image-cache "image.c" (&optional filter))
922 972
923(defun doc-view-pdf->png (pdf png pages) 973(defun doc-view-document->png (pdf png pages single-page-converter)
974 ;; FIXME: Fix docstring.
924 "Convert a PDF file to PNG asynchronously. 975 "Convert a PDF file to PNG asynchronously.
925Start by converting PAGES, and then the rest." 976Start by converting PAGES, and then the rest."
926 (if (null pages) 977 (if (null pages)
@@ -930,11 +981,11 @@ Start by converting PAGES, and then the rest."
930 ;; a single page anyway, and of the remaining 1%, few cases will have 981 ;; a single page anyway, and of the remaining 1%, few cases will have
931 ;; consecutive pages, it's not worth the trouble. 982 ;; consecutive pages, it's not worth the trouble.
932 (let ((rest (cdr pages))) 983 (let ((rest (cdr pages)))
933 (doc-view-pdf->png-1 984 (funcall single-page-converter
934 pdf (format png (car pages)) (car pages) 985 pdf (format png (car pages)) (car pages)
935 (lambda () 986 (lambda ()
936 (if rest 987 (if rest
937 (doc-view-pdf->png pdf png rest) 988 (doc-view-document->png pdf png rest)
938 ;; Yippie, the important pages are done, update the display. 989 ;; Yippie, the important pages are done, update the display.
939 (clear-image-cache) 990 (clear-image-cache)
940 ;; For the windows that have a message (like "Welcome to 991 ;; For the windows that have a message (like "Welcome to
@@ -942,8 +993,8 @@ Start by converting PAGES, and then the rest."
942 ;; not sufficient. 993 ;; not sufficient.
943 (dolist (win (get-buffer-window-list (current-buffer) nil 'visible)) 994 (dolist (win (get-buffer-window-list (current-buffer) nil 'visible))
944 (with-selected-window win 995 (with-selected-window win
945 (when (stringp (get-char-property (point-min) 'display)) 996 (when (stringp (get-char-property (point-min) 'display))
946 (doc-view-goto-page (doc-view-current-page))))) 997 (doc-view-goto-page (doc-view-current-page)))))
947 ;; Convert the rest of the pages. 998 ;; Convert the rest of the pages.
948 (doc-view-pdf/ps->png pdf png))))))) 999 (doc-view-pdf/ps->png pdf png)))))))
949 1000
@@ -1013,8 +1064,9 @@ Those files are saved in the directory given by the function
1013 ;; preserves the horizontal/vertical scroll settings (which are otherwise 1064 ;; preserves the horizontal/vertical scroll settings (which are otherwise
1014 ;; resets during the redisplay). 1065 ;; resets during the redisplay).
1015 (setq doc-view-pending-cache-flush t) 1066 (setq doc-view-pending-cache-flush t)
1016 (let ((png-file (expand-file-name "page-%d.png" 1067 (let ((png-file (expand-file-name
1017 (doc-view-current-cache-dir)))) 1068 (concat "page-%d." doc-view--image-file-extension)
1069 (doc-view-current-cache-dir))))
1018 (make-directory (doc-view-current-cache-dir) t) 1070 (make-directory (doc-view-current-cache-dir) t)
1019 (pcase doc-view-doc-type 1071 (pcase doc-view-doc-type
1020 (`dvi 1072 (`dvi
@@ -1027,11 +1079,12 @@ Those files are saved in the directory given by the function
1027 ;; ODF files have to be converted to PDF before Ghostscript can 1079 ;; ODF files have to be converted to PDF before Ghostscript can
1028 ;; process it. 1080 ;; process it.
1029 (let ((pdf (doc-view-current-cache-doc-pdf)) 1081 (let ((pdf (doc-view-current-cache-doc-pdf))
1030 (opdf (expand-file-name (concat (file-name-base doc-view-buffer-file-name) 1082 (opdf (expand-file-name
1031 ".pdf") 1083 (concat (file-name-base doc-view-buffer-file-name)
1032 doc-view-current-cache-dir)) 1084 ".pdf")
1085 doc-view-current-cache-dir))
1033 (png-file png-file)) 1086 (png-file png-file))
1034 ;; The unoconv tool only supports a output directory, but no 1087 ;; The unoconv tool only supports an output directory, but no
1035 ;; file name. It's named like the input file with the 1088 ;; file name. It's named like the input file with the
1036 ;; extension replaced by pdf. 1089 ;; extension replaced by pdf.
1037 (doc-view-odf->pdf doc-view-buffer-file-name 1090 (doc-view-odf->pdf doc-view-buffer-file-name
@@ -1042,7 +1095,12 @@ Those files are saved in the directory given by the function
1042 (`pdf 1095 (`pdf
1043 (let ((pages (doc-view-active-pages))) 1096 (let ((pages (doc-view-active-pages)))
1044 ;; Convert PDF to PNG images starting with the active pages. 1097 ;; Convert PDF to PNG images starting with the active pages.
1045 (doc-view-pdf->png doc-view-buffer-file-name png-file pages))) 1098 (doc-view-document->png doc-view-buffer-file-name png-file pages
1099 'doc-view-pdf->png-1)))
1100 (`djvu
1101 (let ((pages (doc-view-active-pages)))
1102 (doc-view-document->png doc-view-buffer-file-name png-file pages
1103 'doc-view-djvu->png-1)))
1046 (_ 1104 (_
1047 ;; Convert to PNG images. 1105 ;; Convert to PNG images.
1048 (doc-view-pdf/ps->png doc-view-buffer-file-name png-file))))) 1106 (doc-view-pdf/ps->png doc-view-buffer-file-name png-file)))))
@@ -1186,7 +1244,7 @@ ARGS is a list of image descriptors."
1186 (image (if (and file (file-readable-p file)) 1244 (image (if (and file (file-readable-p file))
1187 (if (not (and doc-view-scale-internally 1245 (if (not (and doc-view-scale-internally
1188 (fboundp 'imagemagick-types))) 1246 (fboundp 'imagemagick-types)))
1189 (apply 'create-image file 'png nil args) 1247 (apply 'create-image file doc-view--image-type nil args)
1190 (unless (member :width args) 1248 (unless (member :width args)
1191 (setq args `(,@args :width ,doc-view-image-width))) 1249 (setq args `(,@args :width ,doc-view-image-width)))
1192 (apply 'create-image file 'imagemagick nil args)))) 1250 (apply 'create-image file 'imagemagick nil args))))
@@ -1236,13 +1294,17 @@ have the page we want to view."
1236 (let ((prev-pages doc-view-current-files)) 1294 (let ((prev-pages doc-view-current-files))
1237 (setq doc-view-current-files 1295 (setq doc-view-current-files
1238 (sort (directory-files (doc-view-current-cache-dir) t 1296 (sort (directory-files (doc-view-current-cache-dir) t
1239 "page-[0-9]+\\.png" t) 1297 (concat "page-[0-9]+\\."
1298 doc-view--image-file-extension)
1299 t)
1240 'doc-view-sort)) 1300 'doc-view-sort))
1241 (dolist (win (or (get-buffer-window-list buffer nil t) 1301 (dolist (win (or (get-buffer-window-list buffer nil t)
1242 (list t))) 1302 (list t)))
1243 (let* ((page (doc-view-current-page win)) 1303 (let* ((page (doc-view-current-page win))
1244 (pagefile (expand-file-name (format "page-%d.png" page) 1304 (pagefile (expand-file-name
1245 (doc-view-current-cache-dir)))) 1305 (format "page-%d.%s"
1306 page doc-view--image-file-extension)
1307 (doc-view-current-cache-dir))))
1246 (when (or force 1308 (when (or force
1247 (and (not (member pagefile prev-pages)) 1309 (and (not (member pagefile prev-pages))
1248 (member pagefile doc-view-current-files))) 1310 (member pagefile doc-view-current-files)))
@@ -1435,12 +1497,13 @@ If BACKWARD is non-nil, jump to the previous match."
1435 ;; the conversion is incomplete. 1497 ;; the conversion is incomplete.
1436 (file-readable-p (expand-file-name "resolution.el" 1498 (file-readable-p (expand-file-name "resolution.el"
1437 (doc-view-current-cache-dir))) 1499 (doc-view-current-cache-dir)))
1438 (> (length (directory-files (doc-view-current-cache-dir) 1500 (> (length (directory-files
1439 nil "\\.png\\'")) 1501 (doc-view-current-cache-dir)
1502 nil (concat "\\." doc-view--image-file-extension "\\'")))
1440 0))) 1503 0)))
1441 1504
1442(defun doc-view-initiate-display () 1505(defun doc-view-initiate-display ()
1443 ;; Switch to image display if possible 1506 ;; Switch to image display if possible.
1444 (if (doc-view-mode-p doc-view-doc-type) 1507 (if (doc-view-mode-p doc-view-doc-type)
1445 (progn 1508 (progn
1446 (doc-view-buffer-message) 1509 (doc-view-buffer-message)
@@ -1509,6 +1572,8 @@ If BACKWARD is non-nil, jump to the previous match."
1509 ("pdf" pdf) ("epdf" pdf) 1572 ("pdf" pdf) ("epdf" pdf)
1510 ;; PostScript 1573 ;; PostScript
1511 ("ps" ps) ("eps" ps) 1574 ("ps" ps) ("eps" ps)
1575 ;; DjVu
1576 ("djvu" djvu)
1512 ;; OpenDocument formats 1577 ;; OpenDocument formats
1513 ("odt" odf) ("ods" odf) ("odp" odf) ("odg" odf) 1578 ("odt" odf) ("ods" odf) ("odp" odf) ("odg" odf)
1514 ("odc" odf) ("odi" odf) ("odm" odf) ("ott" odf) 1579 ("odc" odf) ("odi" odf) ("odm" odf) ("ott" odf)
@@ -1523,7 +1588,8 @@ If BACKWARD is non-nil, jump to the previous match."
1523 (cond 1588 (cond
1524 ((looking-at "%!") '(ps)) 1589 ((looking-at "%!") '(ps))
1525 ((looking-at "%PDF") '(pdf)) 1590 ((looking-at "%PDF") '(pdf))
1526 ((looking-at "\367\002") '(dvi)))))) 1591 ((looking-at "\367\002") '(dvi))
1592 ((looking-at "AT&TFORM") '(djvu))))))
1527 (set (make-local-variable 'doc-view-doc-type) 1593 (set (make-local-variable 'doc-view-doc-type)
1528 (car (or (doc-view-intersection name-types content-types) 1594 (car (or (doc-view-intersection name-types content-types)
1529 (when (and name-types content-types) 1595 (when (and name-types content-types)
@@ -1532,6 +1598,16 @@ If BACKWARD is non-nil, jump to the previous match."
1532 name-types content-types 1598 name-types content-types
1533 (error "Cannot determine the document type")))))) 1599 (error "Cannot determine the document type"))))))
1534 1600
1601(defun doc-view-set-up-single-converter ()
1602 "Find the right single-page converter for the current document type"
1603 (pcase-let ((`(,conv-function ,type ,extension)
1604 (pcase doc-view-doc-type
1605 (`djvu (list #'doc-view-djvu->png-1 'tiff "tif"))
1606 (_ (list #'doc-view-pdf->png-1 'png "png")))))
1607 (setq-local doc-view-single-page-converter-function conv-function)
1608 (setq-local doc-view--image-type type)
1609 (setq-local doc-view--image-file-extension extension)))
1610
1535;;;###autoload 1611;;;###autoload
1536(defun doc-view-mode () 1612(defun doc-view-mode ()
1537 "Major mode in DocView buffers. 1613 "Major mode in DocView buffers.
@@ -1564,6 +1640,7 @@ toggle between displaying the document or editing it as text.
1564 ;; Figure out the document type. 1640 ;; Figure out the document type.
1565 (unless doc-view-doc-type 1641 (unless doc-view-doc-type
1566 (doc-view-set-doc-type)) 1642 (doc-view-set-doc-type))
1643 (doc-view-set-up-single-converter)
1567 1644
1568 (doc-view-make-safe-dir doc-view-cache-directory) 1645 (doc-view-make-safe-dir doc-view-cache-directory)
1569 ;; Handle compressed files, remote files, files inside archives 1646 ;; Handle compressed files, remote files, files inside archives