(rmail-spam-filter): Autoload it.
[bpt/emacs.git] / lisp / doc-view.el
CommitLineData
94dbe99c
TTN
1;;; doc-view.el --- View PDF/PostScript/DVI files in Emacs
2
069b4ce3 3;; Copyright (C) 2007, 2008 Free Software Foundation, Inc.
94dbe99c
TTN
4;;
5;; Author: Tassilo Horn <tassilo@member.fsf.org>
6;; Maintainer: Tassilo Horn <tassilo@member.fsf.org>
7;; Keywords: files, pdf, ps, dvi
94dbe99c
TTN
8
9;; This file is part of GNU Emacs.
10
eb3fa2cf 11;; GNU Emacs is free software: you can redistribute it and/or modify
94dbe99c 12;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
94dbe99c
TTN
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
eb3fa2cf 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
94dbe99c
TTN
23
24;;; Requirements:
25
4b378e75 26;; doc-view.el requires GNU Emacs 22.1 or newer. You also need Ghostscript,
1ecc9da7
TH
27;; `dvipdf' (comes with Ghostscript) or `dvipdfm' (comes with teTeX or TeXLive)
28;; and `pdftotext', which comes with xpdf (http://www.foolabs.com/xpdf/) or
29;; poppler (http://poppler.freedesktop.org/).
94dbe99c
TTN
30
31;;; Commentary:
32
33;; DocView is a document viewer for Emacs. It converts PDF, PS and DVI files
34;; to a set of PNG files, one PNG for each page, and displays the PNG images
35;; inside an Emacs buffer. This buffer uses `doc-view-mode' which provides
36;; convenient key bindings for browsing the document.
37;;
640602f7 38;; To use it simply open a document file with
94dbe99c 39;;
640602f7 40;; C-x C-f ~/path/to/document RET
94dbe99c 41;;
640602f7
RS
42;; and the document will be converted and displayed, if your emacs supports png
43;; images. With `C-c C-c' you can toggle between the rendered images
1f75e53d 44;; representation and the source text representation of the document.
94dbe99c
TTN
45;;
46;; Since conversion may take some time all the PNG images are cached in a
47;; subdirectory of `doc-view-cache-directory' and reused when you want to view
640602f7
RS
48;; that file again. To reconvert a document hit `g' (`doc-view-reconvert-doc')
49;; when displaying the document. To delete all cached files use
94dbe99c
TTN
50;; `doc-view-clear-cache'. To open the cache with dired, so that you can tidy
51;; it out use `doc-view-dired-cache'.
52;;
53;; When conversion in underway the first page will be displayed as soon as it
54;; is available and the available pages are refreshed every
55;; `doc-view-conversion-refresh-interval' seconds. If that variable is nil the
56;; pages won't be displayed before conversion of the document finished
57;; completely.
58;;
59;; DocView lets you select a slice of the displayed pages. This slice will be
60;; remembered and applied to all pages of the current document. This enables
61;; you to cut away the margins of a document to save some space. To select a
62;; slice you can use `doc-view-set-slice' (bound to `s s') which will query you
63;; for the coordinates of the slice's top-left corner and its width and height.
64;; A much more convenient way to do the same is offered by the command
65;; `doc-view-set-slice-using-mouse' (bound to `s m'). After invokation you
66;; only have to press mouse-1 at the top-left corner and drag it to the
67;; bottom-right corner of the desired slice. To reset the slice use
68;; `doc-view-reset-slice' (bound to `s r').
69;;
94dbe99c
TTN
70;; You can also search within the document. The command `doc-view-search'
71;; (bound to `C-s') queries for a search regexp and initializes a list of all
72;; matching pages and messages how many match-pages were found. After that you
7baca0fa
JL
73;; can jump to the next page containing a match with an additional `C-s'. With
74;; `C-r' you can do the same, but backwards. To search for a new regexp give a
75;; prefix arg to one of the search functions, e.g. by typing `C-u C-s'. The
76;; searching works by using a plain text representation of the document. If
77;; that doesn't already exist the first invokation of `doc-view-search' (or
78;; `doc-view-search-backward') starts the conversion. When that finishes and
79;; you're still viewing the document (i.e. you didn't switch to another buffer)
80;; you're queried for the regexp then.
640602f7
RS
81;;
82;; Dired users can simply hit `v' on a document file. If it's a PS, PDF or DVI
83;; it will be opened using `doc-view-mode'.
84;;
94dbe99c
TTN
85
86;;; Configuration:
87
640602f7
RS
88;; If the images are too small or too big you should set the "-rXXX" option in
89;; `doc-view-ghostscript-options' to another value. (The bigger your screen,
90;; the higher the value.)
94dbe99c
TTN
91;;
92;; This and all other options can be set with the customization interface.
93;; Simply do
94;;
95;; M-x customize-group RET doc-view RET
96;;
97;; and modify them to your needs.
98
34065e5e 99;;; Todo:
94dbe99c 100
56741510
SM
101;; - add print command.
102;; - share more code with image-mode.
f4c75497 103;; - better menu.
f4c75497 104;; - Bind slicing to a drag event.
eb79098b 105;; - doc-view-fit-doc-to-window and doc-view-fit-window-to-doc?
cec1df02 106;; - zoom the region around the cursor (like xdvi).
c17587fe
SM
107;; - get rid of the silly arrow in the fringe.
108;; - improve anti-aliasing (pdf-utils gets it better).
f4c75497 109
34065e5e
JL
110;;;; About isearch support
111
112;; I tried implementing isearch by setting
113;; `isearch-search-fun-function' buffer-locally, but that didn't
114;; work too good. The function doing the real search was called
115;; endlessly somehow. But even if we'd get that working no real
116;; isearch feeling comes up due to the missing match highlighting.
117;; Currently I display all lines containing a match in a tooltip and
118;; each C-s or C-r jumps directly to the next/previous page with a
119;; match. With isearch we could only display the current match. So
120;; we had to decide if another C-s jumps to the next page with a
121;; match (thus only the first match in a page will be displayed in a
122;; tooltip) or to the next match, which would do nothing visible
123;; (except the tooltip) if the next match is on the same page.
124
125;; And it's much slower than the current search facility, because
126;; isearch really searches for each step forward or backward wheras
127;; the current approach searches once and then it knows to which
128;; pages to jump.
129
130;; Anyway, if someone with better isearch knowledge wants to give it a try,
131;; feel free to do it. --Tassilo
132
133;;; Code:
134
23cda572 135(eval-when-compile (require 'cl))
94dbe99c 136(require 'dired)
414dd971 137(require 'image-mode)
7baca0fa 138(require 'jka-compr)
94dbe99c
TTN
139
140;;;; Customization Options
141
142(defgroup doc-view nil
143 "In-buffer viewer for PDF, PostScript and DVI files."
144 :link '(function-link doc-view)
145 :version "22.2"
146 :group 'applications
147 :group 'multimedia
148 :prefix "doc-view-")
149
c9a9a5e3 150(defcustom doc-view-ghostscript-program (executable-find "gs")
94dbe99c 151 "Program to convert PS and PDF files to PNG."
c9a9a5e3 152 :type 'file
94dbe99c
TTN
153 :group 'doc-view)
154
155(defcustom doc-view-ghostscript-options
6a658a30
RS
156 '("-dSAFER" ;; Avoid security problems when rendering files from untrusted
157 ;; sources.
158 "-dNOPAUSE" "-sDEVICE=png16m" "-dTextAlphaBits=4"
05477667 159 "-dBATCH" "-dGraphicsAlphaBits=4" "-dQUIET")
4b378e75 160 "A list of options to give to ghostscript."
c9a9a5e3 161 :type '(repeat string)
94dbe99c
TTN
162 :group 'doc-view)
163
05477667
SM
164(defcustom doc-view-resolution 100
165 "Dots per inch resolution used to render the documents.
166Higher values result in larger images."
9efa445f
DN
167 :type 'number
168 :group 'doc-view)
05477667 169
c9a9a5e3 170(defcustom doc-view-dvipdfm-program (executable-find "dvipdfm")
94dbe99c
TTN
171 "Program to convert DVI files to PDF.
172
173DVI file will be converted to PDF before the resulting PDF is
53d4c024
TH
174converted to PNG.
175
176If this and `doc-view-dvipdf-program' are set,
177`doc-view-dvipdf-program' will be preferred."
178 :type 'file
179 :group 'doc-view)
180
181(defcustom doc-view-dvipdf-program (executable-find "dvipdf")
182 "Program to convert DVI files to PDF.
183
184DVI file will be converted to PDF before the resulting PDF is
185converted to PNG.
186
187If this and `doc-view-dvipdfm-program' are set,
188`doc-view-dvipdf-program' will be preferred."
c9a9a5e3 189 :type 'file
94dbe99c
TTN
190 :group 'doc-view)
191
c9a9a5e3 192(defcustom doc-view-ps2pdf-program (executable-find "ps2pdf")
94dbe99c
TTN
193 "Program to convert PS files to PDF.
194
195PS files will be converted to PDF before searching is possible."
c9a9a5e3 196 :type 'file
94dbe99c
TTN
197 :group 'doc-view)
198
c9a9a5e3 199(defcustom doc-view-pdftotext-program (executable-find "pdftotext")
94dbe99c
TTN
200 "Program to convert PDF files to plain text.
201
202Needed for searching."
c9a9a5e3 203 :type 'file
94dbe99c
TTN
204 :group 'doc-view)
205
c17587fe 206(defcustom doc-view-cache-directory
8aafd651 207 (expand-file-name (format "docview%d" (user-uid))
bce6be12 208 temporary-file-directory)
94dbe99c 209 "The base directory, where the PNG images will be saved."
c9a9a5e3 210 :type 'directory
94dbe99c
TTN
211 :group 'doc-view)
212
56741510
SM
213(defvar doc-view-conversion-buffer " *doc-view conversion output*"
214 "The buffer where messages from the converter programs go to.")
94dbe99c 215
56741510 216(defcustom doc-view-conversion-refresh-interval 1
1f75e53d
GM
217 "Interval in seconds between refreshes of the DocView buffer while converting.
218After such a refresh newly converted pages will be available for
94dbe99c
TTN
219viewing. If set to nil there won't be any refreshes and the
220pages won't be displayed before conversion of the whole document
221has finished."
c9a9a5e3 222 :type 'integer
94dbe99c
TTN
223 :group 'doc-view)
224
225;;;; Internal Variables
226
de171465
SM
227(defun doc-view-new-window-function (winprops)
228 (let ((ol (image-mode-window-get 'overlay winprops)))
229 (if ol
230 (setq ol (copy-overlay ol))
10b6e7c1 231 (assert (not (get-char-property (point-min) 'display)))
de171465
SM
232 (setq ol (make-overlay (point-min) (point-max) nil t))
233 (overlay-put ol 'doc-view t))
234 (overlay-put ol 'window (car winprops))
235 (image-mode-window-put 'overlay ol winprops)))
94dbe99c 236
de171465 237(defvar doc-view-current-files nil
94dbe99c 238 "Only used internally.")
ec4853ab 239(make-variable-buffer-local 'doc-view-current-files)
94dbe99c 240
ec4853ab 241(defvar doc-view-current-converter-processes nil
94dbe99c 242 "Only used internally.")
ec4853ab 243(make-variable-buffer-local 'doc-view-current-converter-processes)
94dbe99c
TTN
244
245(defvar doc-view-current-timer nil
246 "Only used internally.")
ec4853ab 247(make-variable-buffer-local 'doc-view-current-timer)
94dbe99c 248
94dbe99c
TTN
249(defvar doc-view-current-cache-dir nil
250 "Only used internally.")
ec4853ab 251(make-variable-buffer-local 'doc-view-current-cache-dir)
94dbe99c
TTN
252
253(defvar doc-view-current-search-matches nil
254 "Only used internally.")
ec4853ab 255(make-variable-buffer-local 'doc-view-current-search-matches)
94dbe99c 256
d99abf1b
RS
257(defvar doc-view-pending-cache-flush nil
258 "Only used internally.")
94dbe99c 259
937cb3fb 260(defvar doc-view-previous-major-mode nil
640602f7
RS
261 "Only used internally.")
262
39a402e3
TH
263(defvar doc-view-buffer-file-name nil
264 "Only used internally.
265The file name used for conversion. Normally it's the same as
266`buffer-file-name', but for remote files, compressed files and
267files inside an archive it is a temporary copy of
268the (uncompressed, extracted) file residing in
269`doc-view-cache-directory'.")
270
eb79098b
SM
271(defvar doc-view-doc-type nil
272 "The type of document in the current buffer.
273Can be `dvi', `pdf', or `ps'.")
274
640602f7 275;;;; DocView Keymaps
94dbe99c
TTN
276
277(defvar doc-view-mode-map
278 (let ((map (make-sparse-keymap)))
d2d7e96c 279 (set-keymap-parent map image-mode-map)
94dbe99c
TTN
280 ;; Navigation in the document
281 (define-key map (kbd "n") 'doc-view-next-page)
282 (define-key map (kbd "p") 'doc-view-previous-page)
eb8d0216
SM
283 (define-key map (kbd "<next>") 'forward-page)
284 (define-key map (kbd "<prior>") 'backward-page)
285 (define-key map [remap forward-page] 'doc-view-next-page)
286 (define-key map [remap backward-page] 'doc-view-previous-page)
94dbe99c
TTN
287 (define-key map (kbd "SPC") 'doc-view-scroll-up-or-next-page)
288 (define-key map (kbd "DEL") 'doc-view-scroll-down-or-previous-page)
289 (define-key map (kbd "M-<") 'doc-view-first-page)
290 (define-key map (kbd "M->") 'doc-view-last-page)
7656fe61 291 (define-key map [remap goto-line] 'doc-view-goto-page)
05477667
SM
292 ;; Zoom in/out.
293 (define-key map "+" 'doc-view-enlarge)
294 (define-key map "-" 'doc-view-shrink)
d2d7e96c 295 ;; Killing the buffer (and the process)
94dbe99c 296 (define-key map (kbd "k") 'doc-view-kill-proc-and-buffer)
937cb3fb 297 (define-key map (kbd "K") 'doc-view-kill-proc)
94dbe99c
TTN
298 ;; Slicing the image
299 (define-key map (kbd "s s") 'doc-view-set-slice)
300 (define-key map (kbd "s m") 'doc-view-set-slice-using-mouse)
301 (define-key map (kbd "s r") 'doc-view-reset-slice)
302 ;; Searching
303 (define-key map (kbd "C-s") 'doc-view-search)
304 (define-key map (kbd "<find>") 'doc-view-search)
7baca0fa 305 (define-key map (kbd "C-r") 'doc-view-search-backward)
94dbe99c
TTN
306 ;; Show the tooltip
307 (define-key map (kbd "C-t") 'doc-view-show-tooltip)
640602f7
RS
308 ;; Toggle between text and image display or editing
309 (define-key map (kbd "C-c C-c") 'doc-view-toggle-display)
515357c2
TH
310 ;; Open a new buffer with doc's text contents
311 (define-key map (kbd "C-c C-t") 'doc-view-open-text)
640602f7 312 ;; Reconvert the current document
7656fe61
SM
313 (define-key map (kbd "g") 'revert-buffer)
314 (define-key map (kbd "r") 'revert-buffer)
94dbe99c 315 map)
640602f7
RS
316 "Keymap used by `doc-view-mode' when displaying a doc as a set of images.")
317
f4c75497
SM
318(easy-menu-define doc-view-menu doc-view-mode-map
319 "Menu for Doc View mode."
320 '("DocView"
321 ["Set Slice" doc-view-set-slice-using-mouse]
322 ["Set Slice (manual)" doc-view-set-slice]
323 ["Reset Slice" doc-view-reset-slice]
324 "---"
325 ["Search" doc-view-search]
7baca0fa 326 ["Search Backwards" doc-view-search-backward]
f4c75497 327 ["Toggle display" doc-view-toggle-display]
f4c75497
SM
328 ))
329
937cb3fb 330(defvar doc-view-minor-mode-map
640602f7
RS
331 (let ((map (make-sparse-keymap)))
332 ;; Toggle between text and image display or editing
333 (define-key map (kbd "C-c C-c") 'doc-view-toggle-display)
640602f7 334 map)
937cb3fb 335 "Keymap used by `doc-minor-view-mode'.")
94dbe99c
TTN
336
337;;;; Navigation Commands
338
160dfe43
SM
339(defmacro doc-view-current-page (&optional win)
340 `(image-mode-window-get 'page ,win))
de171465
SM
341(defmacro doc-view-current-info () `(image-mode-window-get 'info))
342(defmacro doc-view-current-overlay () `(image-mode-window-get 'overlay))
343(defmacro doc-view-current-image () `(image-mode-window-get 'image))
344(defmacro doc-view-current-slice () `(image-mode-window-get 'slice))
345
94dbe99c
TTN
346(defun doc-view-goto-page (page)
347 "View the page given by PAGE."
348 (interactive "nPage: ")
349 (let ((len (length doc-view-current-files)))
350 (if (< page 1)
1ca678aa 351 (setq page 1)
56741510
SM
352 (when (and (> page len)
353 ;; As long as the converter is running, we don't know
354 ;; how many pages will be available.
ec4853ab 355 (null doc-view-current-converter-processes))
1ca678aa 356 (setq page len)))
de171465
SM
357 (setf (doc-view-current-page) page
358 (doc-view-current-info)
1ca678aa
MC
359 (concat
360 (propertize
de171465 361 (format "Page %d of %d." page len) 'face 'bold)
1ca678aa 362 ;; Tell user if converting isn't finished yet
ec4853ab 363 (if doc-view-current-converter-processes
1ca678aa
MC
364 " (still converting...)\n"
365 "\n")
366 ;; Display context infos if this page matches the last search
367 (when (and doc-view-current-search-matches
de171465 368 (assq page doc-view-current-search-matches))
1ca678aa
MC
369 (concat (propertize "Search matches:\n" 'face 'bold)
370 (let ((contexts ""))
de171465 371 (dolist (m (cdr (assq page
1ca678aa
MC
372 doc-view-current-search-matches)))
373 (setq contexts (concat contexts " - \"" m "\"\n")))
374 contexts)))))
94dbe99c 375 ;; Update the buffer
56741510
SM
376 ;; We used to find the file name from doc-view-current-files but
377 ;; that's not right if the pages are not generated sequentially
378 ;; or if the page isn't in doc-view-current-files yet.
ec4853ab
SM
379 (let ((file (expand-file-name (format "page-%d.png" page)
380 (doc-view-current-cache-dir))))
381 (doc-view-insert-image file :pointer 'arrow)
382 (when (and (not (file-exists-p file))
383 doc-view-current-converter-processes)
384 ;; The PNG file hasn't been generated yet.
385 (doc-view-pdf->png-1 doc-view-buffer-file-name file page
386 (lexical-let ((page page)
387 (win (selected-window)))
388 (lambda ()
389 (and (eq (current-buffer) (window-buffer win))
390 ;; If we changed page in the mean
391 ;; time, don't mess things up.
392 (eq (doc-view-current-page win) page)
393 (with-selected-window win
394 (doc-view-goto-page page))))))))
de171465
SM
395 (overlay-put (doc-view-current-overlay)
396 'help-echo (doc-view-current-info))))
94dbe99c
TTN
397
398(defun doc-view-next-page (&optional arg)
399 "Browse ARG pages forward."
400 (interactive "p")
de171465 401 (doc-view-goto-page (+ (doc-view-current-page) (or arg 1))))
94dbe99c
TTN
402
403(defun doc-view-previous-page (&optional arg)
404 "Browse ARG pages backward."
405 (interactive "p")
de171465 406 (doc-view-goto-page (- (doc-view-current-page) (or arg 1))))
94dbe99c
TTN
407
408(defun doc-view-first-page ()
409 "View the first page."
410 (interactive)
411 (doc-view-goto-page 1))
412
413(defun doc-view-last-page ()
414 "View the last page."
415 (interactive)
416 (doc-view-goto-page (length doc-view-current-files)))
417
418(defun doc-view-scroll-up-or-next-page ()
419 "Scroll page up if possible, else goto next page."
420 (interactive)
89663176 421 (when (= (window-vscroll) (image-scroll-up nil))
de171465 422 (let ((cur-page (doc-view-current-page)))
bcc758ec 423 (doc-view-next-page)
de171465 424 (when (/= cur-page (doc-view-current-page))
d5793be3 425 (image-scroll-down nil)))))
94dbe99c
TTN
426
427(defun doc-view-scroll-down-or-previous-page ()
428 "Scroll page down if possible, else goto previous page."
429 (interactive)
89663176 430 (when (= (window-vscroll) (image-scroll-down nil))
de171465 431 (let ((cur-page (doc-view-current-page)))
bcc758ec 432 (doc-view-previous-page)
de171465 433 (when (/= cur-page (doc-view-current-page))
bcc758ec 434 (image-scroll-up nil)))))
94dbe99c 435
937cb3fb
GM
436;;;; Utility Functions
437
640602f7 438(defun doc-view-kill-proc ()
ec4853ab 439 "Kill the current converter process(es)."
640602f7 440 (interactive)
ec4853ab 441 (while doc-view-current-converter-processes
56741510 442 (ignore-errors ;; Maybe it's dead already?
ec4853ab 443 (kill-process (pop doc-view-current-converter-processes))))
640602f7
RS
444 (when doc-view-current-timer
445 (cancel-timer doc-view-current-timer)
446 (setq doc-view-current-timer nil))
447 (setq mode-line-process nil))
448
94dbe99c
TTN
449(defun doc-view-kill-proc-and-buffer ()
450 "Kill the current converter process and buffer."
451 (interactive)
640602f7 452 (doc-view-kill-proc)
94dbe99c 453 (when (eq major-mode 'doc-view-mode)
94dbe99c
TTN
454 (kill-buffer (current-buffer))))
455
c17587fe
SM
456(defun doc-view-make-safe-dir (dir)
457 (condition-case nil
458 (let ((umask (default-file-modes)))
459 (unwind-protect
460 (progn
461 ;; Create temp files with strict access rights. It's easy to
462 ;; loosen them later, whereas it's impossible to close the
463 ;; time-window of loose permissions otherwise.
464 (set-default-file-modes #o0700)
465 (make-directory dir))
466 ;; Reset the umask.
467 (set-default-file-modes umask)))
468 (file-already-exists
469 (if (file-symlink-p dir)
470 (error "Danger: %s points to a symbolic link" dir))
471 ;; In case it was created earlier with looser rights.
472 ;; We could check the mode info returned by file-attributes, but it's
473 ;; a pain to parse and it may not tell you what we want under
474 ;; non-standard file-systems. So let's just say what we want and let
475 ;; the underlying C code and file-system figure it out.
476 ;; This also ends up checking a bunch of useful conditions: it makes
477 ;; sure we have write-access to the directory and that we own it, thus
478 ;; closing a bunch of security holes.
479 (set-file-modes dir #o0700))))
480
937cb3fb
GM
481(defun doc-view-current-cache-dir ()
482 "Return the directory where the png files of the current doc should be saved.
483It's a subdirectory of `doc-view-cache-directory'."
484 (if doc-view-current-cache-dir
485 doc-view-current-cache-dir
c17587fe
SM
486 ;; Try and make sure doc-view-cache-directory exists and is safe.
487 (doc-view-make-safe-dir doc-view-cache-directory)
488 ;; Now compute the subdirectory to use.
937cb3fb
GM
489 (setq doc-view-current-cache-dir
490 (file-name-as-directory
c17587fe 491 (expand-file-name
39a402e3
TH
492 (concat (file-name-nondirectory buffer-file-name)
493 "-"
494 (let ((file doc-view-buffer-file-name))
495 (with-temp-buffer
86903c81 496 (set-buffer-multibyte nil)
39a402e3
TH
497 (insert-file-contents-literally file)
498 (md5 (current-buffer)))))
c17587fe 499 doc-view-cache-directory)))))
937cb3fb
GM
500
501(defun doc-view-remove-if (predicate list)
502 "Return LIST with all items removed that satisfy PREDICATE."
503 (let (new-list)
504 (dolist (item list (nreverse new-list))
505 (when (not (funcall predicate item))
506 (setq new-list (cons item new-list))))))
507
789ab9d4
RS
508;;;###autoload
509(defun doc-view-mode-p (type)
510 "Return non-nil if image type TYPE is available for `doc-view'.
511Image types are symbols like `dvi', `postscript' or `pdf'."
512 (and (display-graphic-p)
513 (image-type-available-p 'png)
514 (cond
515 ((eq type 'dvi)
516 (and (doc-view-mode-p 'pdf)
53d4c024
TH
517 (or (and doc-view-dvipdf-program
518 (executable-find doc-view-dvipdf-program))
519 (and doc-view-dvipdfm-program
520 (executable-find doc-view-dvipdfm-program)))))
622face2 521 ((or (eq type 'postscript) (eq type 'ps) (eq type 'eps)
789ab9d4
RS
522 (eq type 'pdf))
523 (and doc-view-ghostscript-program
524 (executable-find doc-view-ghostscript-program)))
525 (t ;; unknown image type
526 nil))))
527
94dbe99c
TTN
528;;;; Conversion Functions
529
05477667
SM
530(defvar doc-view-shrink-factor 1.125)
531
532(defun doc-view-enlarge (factor)
533 "Enlarge the document."
534 (interactive (list doc-view-shrink-factor))
535 (set (make-local-variable 'doc-view-resolution)
536 (* factor doc-view-resolution))
537 (doc-view-reconvert-doc))
538
539(defun doc-view-shrink (factor)
540 "Shrink the document."
541 (interactive (list doc-view-shrink-factor))
542 (doc-view-enlarge (/ 1.0 factor)))
543
c17587fe 544(defun doc-view-reconvert-doc ()
640602f7
RS
545 "Reconvert the current document.
546Should be invoked when the cached images aren't up-to-date."
547 (interactive)
f4c75497
SM
548 (doc-view-kill-proc)
549 ;; Clear the old cached files
550 (when (file-exists-p (doc-view-current-cache-dir))
551 (dired-delete-file (doc-view-current-cache-dir) 'always))
c17587fe 552 (doc-view-initiate-display))
94dbe99c 553
b4cb319f
SM
554(defun doc-view-sentinel (proc event)
555 "Generic sentinel for doc-view conversion processes."
94dbe99c 556 (if (not (string-match "finished" event))
b4cb319f
SM
557 (message "DocView: process %s changed status to %s."
558 (process-name proc) event)
ec4853ab
SM
559 (when (buffer-live-p (process-get proc 'buffer))
560 (with-current-buffer (process-get proc 'buffer)
561 (setq doc-view-current-converter-processes
562 (delq proc doc-view-current-converter-processes))
563 (setq mode-line-process
564 (if doc-view-current-converter-processes
565 (format ":%s" (car doc-view-current-converter-processes))))
566 (funcall (process-get proc 'callback))))))
567
568(defun doc-view-start-process (name program args callback)
3c03f2ce
TH
569 ;; Make sure the process is started in an existing directory, (rather than
570 ;; some file-name-handler-managed dir, for example).
571 (let* ((default-directory (if (file-readable-p default-directory)
572 default-directory
573 (expand-file-name "~/")))
ec4853ab
SM
574 (proc (apply 'start-process name doc-view-conversion-buffer
575 program args)))
576 (push proc doc-view-current-converter-processes)
577 (setq mode-line-process (list (format ":%s" proc)))
578 (set-process-sentinel proc 'doc-view-sentinel)
579 (process-put proc 'buffer (current-buffer))
580 (process-put proc 'callback callback)))
b4cb319f
SM
581
582(defun doc-view-dvi->pdf (dvi pdf callback)
583 "Convert DVI to PDF asynchronously and call CALLBACK when finished."
53d4c024
TH
584 ;; Prefer dvipdf over dvipdfm, because the latter has problems if the DVI
585 ;; references and includes other PS files.
586 (if (and doc-view-dvipdf-program
587 (executable-find doc-view-dvipdf-program))
588 (doc-view-start-process "dvi->pdf" doc-view-dvipdf-program
589 (list dvi pdf)
590 callback)
591 (doc-view-start-process "dvi->pdf" doc-view-dvipdfm-program
592 (list "-o" pdf dvi)
593 callback)))
ec4853ab 594
94dbe99c
TTN
595
596(defun doc-view-pdf/ps->png (pdf-ps png)
1f75e53d 597 "Convert PDF-PS to PNG asynchronously."
ec4853ab
SM
598 (doc-view-start-process
599 "pdf/ps->png" doc-view-ghostscript-program
600 (append doc-view-ghostscript-options
601 (list (format "-r%d" (round doc-view-resolution))
602 (concat "-sOutputFile=" png)
603 pdf-ps))
3c03f2ce 604 (lambda ()
ec4853ab
SM
605 (when doc-view-current-timer
606 (cancel-timer doc-view-current-timer)
607 (setq doc-view-current-timer nil))
608 (doc-view-display (current-buffer) 'force)))
609 ;; Update the displayed pages as soon as they're done generating.
94dbe99c
TTN
610 (when doc-view-conversion-refresh-interval
611 (setq doc-view-current-timer
ec4853ab
SM
612 (run-at-time "1 secs" doc-view-conversion-refresh-interval
613 'doc-view-display
614 (current-buffer)))))
615
616(defun doc-view-pdf->png-1 (pdf png page callback)
617 "Convert a PAGE of a PDF file to PNG asynchronously.
618Call CALLBACK with no arguments when done."
619 (doc-view-start-process
620 "pdf->png-1" doc-view-ghostscript-program
621 (append doc-view-ghostscript-options
622 (list (format "-r%d" (round doc-view-resolution))
623 ;; Sadly, `gs' only supports the page-range
624 ;; for PDF files.
625 (format "-dFirstPage=%d" page)
626 (format "-dLastPage=%d" page)
627 (concat "-sOutputFile=" png)
628 pdf))
629 callback))
630
aa360da1
GM
631(declare-function clear-image-cache "image.c" (&optional filter))
632
ec4853ab
SM
633(defun doc-view-pdf->png (pdf png pages)
634 "Convert a PDF file to PNG asynchronously.
635Start by converting PAGES, and then the rest."
636 (if (null pages)
637 (doc-view-pdf/ps->png pdf png)
638 ;; We could render several `pages' with a single process if they're
639 ;; (almost) consecutive, but since in 99% of the cases, there'll be only
640 ;; a single page anyway, and of the remaining 1%, few cases will have
641 ;; consecutive pages, it's not worth the trouble.
642 (lexical-let ((pdf pdf) (png png) (rest (cdr pages)))
643 (doc-view-pdf->png-1
644 pdf (format png (car pages)) (car pages)
645 (lambda ()
646 (if rest
647 (doc-view-pdf->png pdf png rest)
648 ;; Yippie, the important pages are done, update the display.
649 (clear-image-cache)
650 ;; Convert the rest of the pages.
651 (doc-view-pdf/ps->png pdf png)))))))
94dbe99c 652
b4cb319f
SM
653(defun doc-view-pdf->txt (pdf txt callback)
654 "Convert PDF to TXT asynchronously and call CALLBACK when finished."
ec4853ab
SM
655 (doc-view-start-process "pdf->txt" doc-view-pdftotext-program
656 (list "-raw" pdf txt)
657 callback))
515357c2 658
b4cb319f
SM
659(defun doc-view-doc->txt (txt callback)
660 "Convert the current document to text and call CALLBACK when done."
7edd6b92 661 (make-directory (doc-view-current-cache-dir) t)
b4cb319f
SM
662 (case doc-view-doc-type
663 (pdf
664 ;; Doc is a PDF, so convert it to TXT
665 (doc-view-pdf->txt doc-view-buffer-file-name txt callback))
666 (ps
667 ;; Doc is a PS, so convert it to PDF (which will be converted to
668 ;; TXT thereafter).
669 (lexical-let ((pdf (expand-file-name "doc.pdf"
670 (doc-view-current-cache-dir)))
671 (txt txt)
672 (callback callback))
673 (doc-view-ps->pdf doc-view-buffer-file-name pdf
674 (lambda () (doc-view-pdf->txt pdf txt callback)))))
675 (dvi
676 ;; Doc is a DVI. This means that a doc.pdf already exists in its
677 ;; cache subdirectory.
678 (doc-view-pdf->txt (expand-file-name "doc.pdf"
679 (doc-view-current-cache-dir))
680 txt callback))
681 (t (error "DocView doesn't know what to do"))))
682
683(defun doc-view-ps->pdf (ps pdf callback)
684 "Convert PS to PDF asynchronously and call CALLBACK when finished."
ec4853ab
SM
685 (doc-view-start-process "ps->pdf" doc-view-ps2pdf-program
686 (list
687 ;; Avoid security problems when rendering files from
688 ;; untrusted sources.
689 "-dSAFER"
690 ;; in-file and out-file
691 ps pdf)
692 callback))
693
694(defun doc-view-active-pages ()
695 (let ((pages ()))
696 (dolist (win (get-buffer-window-list (current-buffer) nil 'visible))
697 (let ((page (image-mode-window-get 'page win)))
698 (unless (memq page pages) (push page pages))))
699 pages))
94dbe99c 700
640602f7 701(defun doc-view-convert-current-doc ()
39a402e3 702 "Convert `doc-view-buffer-file-name' to a set of png files, one file per page.
640602f7
RS
703Those files are saved in the directory given by the function
704`doc-view-current-cache-dir'."
c17587fe
SM
705 ;; Let stale files still display while we recompute the new ones, so only
706 ;; flush the cache when the conversion is over. One of the reasons why it
707 ;; is important to keep displaying the stale page is so that revert-buffer
708 ;; preserves the horizontal/vertical scroll settings (which are otherwise
709 ;; resets during the redisplay).
710 (setq doc-view-pending-cache-flush t)
711 (let ((png-file (expand-file-name "page-%d.png"
712 (doc-view-current-cache-dir))))
7edd6b92 713 (make-directory (doc-view-current-cache-dir) t)
eb79098b
SM
714 (case doc-view-doc-type
715 (dvi
716 ;; DVI files have to be converted to PDF before Ghostscript can process
717 ;; it.
b4cb319f
SM
718 (lexical-let
719 ((pdf (expand-file-name "doc.pdf" doc-view-current-cache-dir))
720 (png-file png-file))
721 (doc-view-dvi->pdf doc-view-buffer-file-name pdf
722 (lambda () (doc-view-pdf/ps->png pdf png-file)))))
ec4853ab
SM
723 (pdf
724 (let ((pages (doc-view-active-pages)))
725 ;; Convert PDF to PNG images starting with the active pages.
726 (doc-view-pdf->png doc-view-buffer-file-name png-file pages)))
eb79098b
SM
727 (t
728 ;; Convert to PNG images.
729 (doc-view-pdf/ps->png doc-view-buffer-file-name png-file)))))
94dbe99c 730
94dbe99c
TTN
731;;;; Slicing
732
aa360da1
GM
733(declare-function image-size "image.c" (spec &optional pixels frame))
734
94dbe99c
TTN
735(defun doc-view-set-slice (x y width height)
736 "Set the slice of the images that should be displayed.
737You can use this function to tell doc-view not to display the
738margins of the document. It prompts for the top-left corner (X
739and Y) of the slice to display and its WIDTH and HEIGHT.
740
741See `doc-view-set-slice-using-mouse' for a more convenient way to
742do that. To reset the slice use `doc-view-reset-slice'."
743 (interactive
de171465 744 (let* ((size (image-size (doc-view-current-image) t))
1ca678aa
MC
745 (a (read-number (format "Top-left X (0..%d): " (car size))))
746 (b (read-number (format "Top-left Y (0..%d): " (cdr size))))
747 (c (read-number (format "Width (0..%d): " (- (car size) a))))
748 (d (read-number (format "Height (0..%d): " (- (cdr size) b)))))
94dbe99c 749 (list a b c d)))
de171465 750 (setf (doc-view-current-slice) (list x y width height))
94dbe99c 751 ;; Redisplay
de171465 752 (doc-view-goto-page (doc-view-current-page)))
94dbe99c
TTN
753
754(defun doc-view-set-slice-using-mouse ()
755 "Set the slice of the images that should be displayed.
756You set the slice by pressing mouse-1 at its top-left corner and
757dragging it to its bottom-right corner. See also
758`doc-view-set-slice' and `doc-view-reset-slice'."
759 (interactive)
760 (let (x y w h done)
761 (while (not done)
762 (let ((e (read-event
1ca678aa
MC
763 (concat "Press mouse-1 at the top-left corner and "
764 "drag it to the bottom-right corner!"))))
765 (when (eq (car e) 'drag-mouse-1)
766 (setq x (car (posn-object-x-y (event-start e))))
767 (setq y (cdr (posn-object-x-y (event-start e))))
768 (setq w (- (car (posn-object-x-y (event-end e))) x))
769 (setq h (- (cdr (posn-object-x-y (event-end e))) y))
770 (setq done t))))
94dbe99c
TTN
771 (doc-view-set-slice x y w h)))
772
773(defun doc-view-reset-slice ()
e48a5bf9 774 "Reset the current slice.
1f75e53d 775After calling this function whole pages will be visible again."
94dbe99c 776 (interactive)
de171465 777 (setf (doc-view-current-slice) nil)
94dbe99c 778 ;; Redisplay
de171465 779 (doc-view-goto-page (doc-view-current-page)))
94dbe99c
TTN
780
781;;;; Display
782
783(defun doc-view-insert-image (file &rest args)
784 "Insert the given png FILE.
e48a5bf9 785ARGS is a list of image descriptors."
c17587fe
SM
786 (when doc-view-pending-cache-flush
787 (clear-image-cache)
788 (setq doc-view-pending-cache-flush nil))
de171465 789 (let ((ol (doc-view-current-overlay))
56741510
SM
790 (image (if (and file (file-readable-p file))
791 (apply 'create-image file 'png nil args)))
de171465
SM
792 (slice (doc-view-current-slice)))
793 (setf (doc-view-current-image) image)
56741510 794 (move-overlay ol (point-min) (point-max))
de171465 795 (overlay-put ol 'display
56741510
SM
796 (cond
797 (image
de171465
SM
798 (if slice
799 (list (cons 'slice slice) image)
56741510
SM
800 image))
801 ;; We're trying to display a page that doesn't exist.
ec4853ab 802 (doc-view-current-converter-processes
56741510
SM
803 ;; Maybe the page doesn't exist *yet*.
804 "Cannot display this page (yet)!")
805 (t
806 ;; Typically happens if the conversion process somehow
807 ;; failed. Better not signal an error here because it
808 ;; could prevent a subsequent reconversion from fixing
809 ;; the problem.
810 (concat "Cannot display this page!\n"
811 "Maybe because of a conversion failure!"))))
812 (let ((win (overlay-get ol 'window)))
813 (if (stringp (overlay-get ol 'display))
814 (progn ;Make sure the text is not scrolled out of view.
815 (set-window-hscroll win 0)
816 (set-window-vscroll win 0))
817 (let ((hscroll (image-mode-window-get 'hscroll win))
818 (vscroll (image-mode-window-get 'vscroll win)))
819 ;; Reset scroll settings, in case they were changed.
820 (if hscroll (set-window-hscroll win hscroll))
821 (if vscroll (set-window-vscroll win vscroll)))))))
94dbe99c
TTN
822
823(defun doc-view-sort (a b)
824 "Return non-nil if A should be sorted before B.
825Predicate for sorting `doc-view-current-files'."
f4c75497
SM
826 (or (< (length a) (length b))
827 (and (= (length a) (length b))
828 (string< a b))))
94dbe99c 829
65073003
SM
830(defun doc-view-display (buffer &optional force)
831 "Start viewing the document in BUFFER.
214abdd4
SM
832If FORCE is non-nil, start viewing even if the document does not
833have the page we want to view."
65073003 834 (with-current-buffer buffer
56741510
SM
835 (let ((prev-pages doc-view-current-files))
836 (setq doc-view-current-files
837 (sort (directory-files (doc-view-current-cache-dir) t
838 "page-[0-9]+\\.png" t)
839 'doc-view-sort))
10b6e7c1
CY
840 (dolist (win (or (get-buffer-window-list buffer nil t)
841 (list (selected-window))))
842 (let* ((page (doc-view-current-page win))
843 (pagefile (expand-file-name (format "page-%d.png" page)
844 (doc-view-current-cache-dir))))
845 (when (or force
846 (and (not (member pagefile prev-pages))
847 (member pagefile doc-view-current-files)))
848 (with-selected-window win
849 (assert (eq (current-buffer) buffer))
850 (doc-view-goto-page page))))))))
94dbe99c
TTN
851
852(defun doc-view-buffer-message ()
c17587fe
SM
853 ;; Only show this message initially, not when refreshing the buffer (in which
854 ;; case it's better to keep displaying the "stale" page while computing
855 ;; the fresh new ones).
de171465
SM
856 (unless (overlay-get (doc-view-current-overlay) 'display)
857 (overlay-put (doc-view-current-overlay) 'display
c17587fe
SM
858 (concat (propertize "Welcome to DocView!" 'face 'bold)
859 "\n"
860 "
1f75e53d 861If you see this buffer it means that the document you want to view is being
f4c75497 862converted to PNG and the conversion of the first page hasn't finished yet or
94dbe99c
TTN
863`doc-view-conversion-refresh-interval' is set to nil.
864
865For now these keys are useful:
866
1ca678aa 867`q' : Bury this buffer. Conversion will go on in background.
937cb3fb 868`k' : Kill the conversion process and this buffer.
c17587fe 869`K' : Kill the conversion process.\n"))))
94dbe99c 870
aa360da1
GM
871(declare-function tooltip-show "tooltip" (text &optional use-echo-area))
872
94dbe99c
TTN
873(defun doc-view-show-tooltip ()
874 (interactive)
de171465 875 (tooltip-show (doc-view-current-info)))
94dbe99c 876
515357c2
TH
877(defun doc-view-open-text ()
878 "Open a buffer with the current doc's contents as text."
879 (interactive)
ec4853ab 880 (if doc-view-current-converter-processes
515357c2
TH
881 (message "DocView: please wait till conversion finished.")
882 (let ((txt (expand-file-name "doc.txt" (doc-view-current-cache-dir))))
883 (if (file-readable-p txt)
884 (find-file txt)
b4cb319f 885 (doc-view-doc->txt txt 'doc-view-open-text)))))
515357c2 886
937cb3fb 887;;;;; Toggle between editing and viewing
640602f7 888
515357c2 889
640602f7 890(defun doc-view-toggle-display ()
937cb3fb 891 "Toggle between editing a document as text or viewing it."
640602f7 892 (interactive)
937cb3fb
GM
893 (if (eq major-mode 'doc-view-mode)
894 ;; Switch to editing mode
895 (progn
896 (doc-view-kill-proc)
897 (setq buffer-read-only nil)
515357c2
TH
898 (remove-overlays (point-min) (point-max) 'doc-view t)
899 (set (make-local-variable 'image-mode-winprops-alist) t)
937cb3fb
GM
900 ;; Switch to the previously used major mode or fall back to fundamental
901 ;; mode.
902 (if doc-view-previous-major-mode
903 (funcall doc-view-previous-major-mode)
904 (fundamental-mode))
c17587fe 905 (doc-view-minor-mode 1))
937cb3fb
GM
906 ;; Switch to doc-view-mode
907 (when (and (buffer-modified-p)
908 (y-or-n-p "The buffer has been modified. Save the changes? "))
909 (save-buffer))
937cb3fb 910 (doc-view-mode)))
640602f7 911
94dbe99c
TTN
912;;;; Searching
913
515357c2 914
94dbe99c
TTN
915(defun doc-view-search-internal (regexp file)
916 "Return a list of FILE's pages that contain text matching REGEXP.
1ca678aa
MC
917The value is an alist of the form (PAGE CONTEXTS) where PAGE is
918the pagenumber and CONTEXTS are all lines of text containing a match."
94dbe99c
TTN
919 (with-temp-buffer
920 (insert-file-contents file)
921 (let ((page 1)
1ca678aa
MC
922 (lastpage 1)
923 matches)
94dbe99c 924 (while (re-search-forward (concat "\\(?:\\([\f]\\)\\|\\("
1ca678aa 925 regexp "\\)\\)") nil t)
069b4ce3 926 (when (match-string 1) (setq page (1+ page)))
1ca678aa
MC
927 (when (match-string 2)
928 (if (/= page lastpage)
c17587fe 929 (push (cons page
515357c2
TH
930 (list (buffer-substring
931 (line-beginning-position)
932 (line-end-position))))
933 matches)
1ca678aa
MC
934 (setq matches (cons
935 (append
936 (or
937 ;; This page already is a match.
938 (car matches)
939 ;; This is the first match on page.
940 (list page))
941 (list (buffer-substring
942 (line-beginning-position)
943 (line-end-position))))
944 (cdr matches))))
945 (setq lastpage page)))
94dbe99c
TTN
946 (nreverse matches))))
947
948(defun doc-view-search-no-of-matches (list)
949 "Extract the number of matches from the search result LIST."
950 (let ((no 0))
951 (dolist (p list)
952 (setq no (+ no (1- (length p)))))
953 no))
954
7baca0fa
JL
955(defun doc-view-search-backward (new-query)
956 "Call `doc-view-search' for backward search.
957If prefix NEW-QUERY is given, ask for a new regexp."
958 (interactive "P")
da99b369 959 (doc-view-search new-query t))
7baca0fa
JL
960
961(defun doc-view-search (new-query &optional backward)
962 "Jump to the next match or initiate a new search if NEW-QUERY is given.
94dbe99c 963If the current document hasn't been transformed to plain text
7baca0fa
JL
964till now do that first.
965If BACKWARD is non-nil, jump to the previous match."
966 (interactive "P")
da99b369 967 (if (and (not new-query)
7baca0fa
JL
968 doc-view-current-search-matches)
969 (if backward
970 (doc-view-search-previous-match 1)
971 (doc-view-search-next-match 1))
972 ;; New search, so forget the old results.
973 (setq doc-view-current-search-matches nil)
974 (let ((txt (expand-file-name "doc.txt"
975 (doc-view-current-cache-dir))))
976 (if (file-readable-p txt)
977 (progn
978 (setq doc-view-current-search-matches
979 (doc-view-search-internal
980 (read-from-minibuffer "Regexp: ")
981 txt))
982 (message "DocView: search yielded %d matches."
983 (doc-view-search-no-of-matches
984 doc-view-current-search-matches)))
985 ;; We must convert to TXT first!
ec4853ab 986 (if doc-view-current-converter-processes
7baca0fa 987 (message "DocView: please wait till conversion finished.")
b4cb319f 988 (doc-view-doc->txt txt (lambda () (doc-view-search nil))))))))
94dbe99c
TTN
989
990(defun doc-view-search-next-match (arg)
991 "Go to the ARGth next matching page."
992 (interactive "p")
937cb3fb 993 (let* ((next-pages (doc-view-remove-if
de171465 994 (lambda (i) (<= (car i) (doc-view-current-page)))
937cb3fb 995 doc-view-current-search-matches))
1ca678aa 996 (page (car (nth (1- arg) next-pages))))
94dbe99c 997 (if page
1ca678aa 998 (doc-view-goto-page page)
94dbe99c 999 (when (and
1ca678aa
MC
1000 doc-view-current-search-matches
1001 (y-or-n-p "No more matches after current page. Wrap to first match? "))
1002 (doc-view-goto-page (caar doc-view-current-search-matches))))))
94dbe99c
TTN
1003
1004(defun doc-view-search-previous-match (arg)
1005 "Go to the ARGth previous matching page."
1006 (interactive "p")
937cb3fb 1007 (let* ((prev-pages (doc-view-remove-if
de171465 1008 (lambda (i) (>= (car i) (doc-view-current-page)))
937cb3fb 1009 doc-view-current-search-matches))
1ca678aa 1010 (page (car (nth (1- arg) (nreverse prev-pages)))))
94dbe99c 1011 (if page
1ca678aa 1012 (doc-view-goto-page page)
94dbe99c 1013 (when (and
1ca678aa
MC
1014 doc-view-current-search-matches
1015 (y-or-n-p "No more matches before current page. Wrap to last match? "))
1016 (doc-view-goto-page (caar (last doc-view-current-search-matches)))))))
94dbe99c 1017
640602f7 1018;;;; User interface commands and the mode
94dbe99c 1019
c17587fe
SM
1020;; (put 'doc-view-mode 'mode-class 'special)
1021
515357c2
TH
1022(defun doc-view-already-converted-p ()
1023 "Return non-nil if the current doc was already converted."
1024 (and (file-exists-p (doc-view-current-cache-dir))
1ecc9da7 1025 (> (length (directory-files (doc-view-current-cache-dir) nil "\\.png$")) 0)))
515357c2 1026
c17587fe
SM
1027(defun doc-view-initiate-display ()
1028 ;; Switch to image display if possible
322f4559 1029 (if (doc-view-mode-p doc-view-doc-type)
c17587fe
SM
1030 (progn
1031 (doc-view-buffer-message)
de171465 1032 (setf (doc-view-current-page) (or (doc-view-current-page) 1))
515357c2 1033 (if (doc-view-already-converted-p)
c17587fe
SM
1034 (progn
1035 (message "DocView: using cached files!")
65073003 1036 (doc-view-display (current-buffer) 'force))
c17587fe
SM
1037 (doc-view-convert-current-doc))
1038 (message
1039 "%s"
1040 (substitute-command-keys
1041 (concat "Type \\[doc-view-toggle-display] to toggle between "
1042 "editing or viewing the document."))))
1043 (message
1044 "%s"
1045 (substitute-command-keys
d99abf1b 1046 (concat "No image (png) support available or some conversion utility for "
39a402e3 1047 (file-name-extension doc-view-buffer-file-name)" files is missing. "
515357c2
TH
1048 "Type \\[doc-view-toggle-display] to switch to an editing mode or "
1049 "\\[doc-view-open-text] to open a buffer showing the doc as text.")))))
94dbe99c 1050
e0385bf4 1051(defvar bookmark-make-record-function)
069b4ce3 1052
65073003
SM
1053(defun doc-view-clone-buffer-hook ()
1054 ;; FIXME: There are several potential problems linked with reconversion
1055 ;; and auto-revert when we have indirect buffers because they share their
1056 ;; /tmp cache directory. This sharing is good (you'd rather not reconvert
1057 ;; for each clone), but that means that clones need to collaborate a bit.
1058 ;; I guess it mostly means: detect when a reconversion process is already
1059 ;; running, and run the sentinel in all clones.
515357c2 1060 ;;
de171465 1061 ;; Maybe the clones should really have a separate /tmp directory
65073003
SM
1062 ;; so they could have a different resolution and you could use clones
1063 ;; for zooming.
de171465
SM
1064 (remove-overlays (point-min) (point-max) 'doc-view t)
1065 (if (consp image-mode-winprops-alist) (setq image-mode-winprops-alist nil)))
65073003 1066
eb79098b
SM
1067(defun doc-view-intersection (l1 l2)
1068 (let ((l ()))
1069 (dolist (x l1) (if (memq x l2) (push x l)))
1070 l))
1071
640602f7 1072;;;###autoload
937cb3fb 1073(defun doc-view-mode ()
640602f7
RS
1074 "Major mode in DocView buffers.
1075You can use \\<doc-view-mode-map>\\[doc-view-toggle-display] to
5c1f16b0
SM
1076toggle between displaying the document or editing it as text.
1077\\{doc-view-mode-map}"
937cb3fb 1078 (interactive)
f9adf05b
TH
1079
1080 (let* ((prev-major-mode (if (eq major-mode 'doc-view-mode)
1081 doc-view-previous-major-mode
1082 major-mode)))
1083 (kill-all-local-variables)
1084 (set (make-local-variable 'doc-view-previous-major-mode) prev-major-mode))
1085
eb79098b
SM
1086 ;; Figure out the document type.
1087 (let ((name-types
1088 (when buffer-file-name
1089 (cdr (assoc (file-name-extension buffer-file-name)
1090 '(("dvi" dvi)
1091 ("pdf" pdf)
1092 ("epdf" pdf)
1093 ("ps" ps)
1094 ("eps" ps))))))
1095 (content-types
1096 (save-excursion
1097 (goto-char (point-min))
1098 (cond
1099 ((looking-at "%!") '(ps))
1100 ((looking-at "%PDF") '(pdf))
1101 ((looking-at "\367\002") '(dvi))))))
1102 (set (make-local-variable 'doc-view-doc-type)
1103 (car (or (doc-view-intersection name-types content-types)
1104 (when (and name-types content-types)
1105 (error "Conflicting types: name says %s but content says %s"
1106 name-types content-types))
1107 name-types content-types
1108 (error "Cannot determine the document type")))))
5986c5c6 1109
56741510 1110 (doc-view-make-safe-dir doc-view-cache-directory)
39a402e3
TH
1111 ;; Handle compressed files, remote files, files inside archives
1112 (set (make-local-variable 'doc-view-buffer-file-name)
1113 (cond
1114 (jka-compr-really-do-compress
1115 (expand-file-name
1116 (file-name-nondirectory
1117 (file-name-sans-extension buffer-file-name))
1118 doc-view-cache-directory))
cec1df02
SM
1119 ;; Is the file readable by local processes?
1120 ;; We used to use `file-remote-p' but it's unclear what it's
1121 ;; supposed to return nil for things like local files accessed via
1122 ;; `su' or via file://...
1123 ((let ((file-name-handler-alist nil))
1124 (not (file-readable-p buffer-file-name)))
39a402e3
TH
1125 (expand-file-name
1126 (file-name-nondirectory buffer-file-name)
1127 doc-view-cache-directory))
1128 (t buffer-file-name)))
1129 (when (not (string= doc-view-buffer-file-name buffer-file-name))
1130 (write-region nil nil doc-view-buffer-file-name))
1131
f9adf05b 1132 (add-hook 'change-major-mode-hook
ec4853ab
SM
1133 (lambda ()
1134 (doc-view-kill-proc)
1135 (remove-overlays (point-min) (point-max) 'doc-view t))
f9adf05b 1136 nil t)
65073003 1137 (add-hook 'clone-indirect-buffer-hook 'doc-view-clone-buffer-hook nil t)
ec4853ab 1138 (add-hook 'kill-buffer 'doc-view-kill-proc nil t)
6f70aa33 1139
de171465
SM
1140 (remove-overlays (point-min) (point-max) 'doc-view t) ;Just in case.
1141 ;; Keep track of display info ([vh]scroll, page number, overlay, ...)
1142 ;; for each window in which this document is shown.
1143 (add-hook 'image-mode-new-window-functions
1144 'doc-view-new-window-function nil t)
1145 (image-mode-setup-winprops)
515357c2 1146
f9adf05b 1147 (set (make-local-variable 'mode-line-position)
de171465 1148 '(" P" (:eval (number-to-string (doc-view-current-page)))
f9adf05b 1149 "/" (:eval (number-to-string (length doc-view-current-files)))))
38b5ca4a
SM
1150 ;; Don't scroll unless the user specifically asked for it.
1151 (set (make-local-variable 'auto-hscroll-mode) nil)
f9adf05b
TH
1152 (set (make-local-variable 'cursor-type) nil)
1153 (use-local-map doc-view-mode-map)
1154 (set (make-local-variable 'after-revert-hook) 'doc-view-reconvert-doc)
e0385bf4
KF
1155 (set (make-local-variable 'bookmark-make-record-function)
1156 'doc-view-bookmark-make-record)
f9adf05b
TH
1157 (setq mode-name "DocView"
1158 buffer-read-only t
1159 major-mode 'doc-view-mode)
1160 (doc-view-initiate-display)
1161 (run-mode-hooks 'doc-view-mode-hook))
937cb3fb
GM
1162
1163;;;###autoload
1164(define-minor-mode doc-view-minor-mode
1165 "Toggle Doc view minor mode.
1166With arg, turn Doc view minor mode on if arg is positive, off otherwise.
1167See the command `doc-view-mode' for more information on this mode."
1168 nil " DocView" doc-view-minor-mode-map
1169 :group 'doc-view
1170 (when doc-view-minor-mode
1171 (add-hook 'change-major-mode-hook (lambda () (doc-view-minor-mode -1)) nil t)
1172 (message
1173 "%s"
1174 (substitute-command-keys
1175 "Type \\[doc-view-toggle-display] to toggle between editing or viewing the document."))))
94dbe99c
TTN
1176
1177(defun doc-view-clear-cache ()
1178 "Delete the whole cache (`doc-view-cache-directory')."
1179 (interactive)
515357c2 1180 (dired-delete-file doc-view-cache-directory 'always))
94dbe99c
TTN
1181
1182(defun doc-view-dired-cache ()
1183 "Open `dired' in `doc-view-cache-directory'."
1184 (interactive)
1185 (dired doc-view-cache-directory))
1186
94dbe99c 1187
1666a6b3
TH
1188;;;; Bookmark integration
1189
c123f7fe
GM
1190(declare-function bookmark-make-record-default "bookmark"
1191 (&optional point-only))
a9f8b49b 1192(declare-function bookmark-prop-get "bookmark" (bookmark prop))
43f8b275 1193(declare-function bookmark-default-handler "bookmark" (bmk))
a9f8b49b 1194
32a091dd 1195(defun doc-view-bookmark-make-record ()
43f8b275
SM
1196 (nconc (bookmark-make-record-default)
1197 `((page . ,(doc-view-current-page))
1198 (handler . doc-view-bookmark-jump))))
1666a6b3 1199
069b4ce3 1200
1666a6b3
TH
1201;;;###autoload
1202(defun doc-view-bookmark-jump (bmk)
03e26a79 1203 ;; This implements the `handler' function interface for record type
e0385bf4 1204 ;; returned by `doc-view-bookmark-make-record', which see.
43f8b275
SM
1205 (prog1 (bookmark-default-handler bmk)
1206 (let ((page (bookmark-prop-get bmk 'page)))
1666a6b3 1207 (when (not (eq major-mode 'doc-view-mode))
43f8b275 1208 (doc-view-toggle-display))
a9f8b49b
SM
1209 (with-selected-window
1210 (or (get-buffer-window (current-buffer) 0)
1211 (selected-window))
43f8b275 1212 (doc-view-goto-page page)))))
1666a6b3 1213
069b4ce3
GM
1214
1215(provide 'doc-view)
1216
1217;; Local Variables:
1218;; mode: outline-minor
1219;; End:
1220
aab2236f 1221;; arch-tag: 5d6e5c5e-095f-489e-b4e4-1ca90a7d79be
94dbe99c 1222;;; doc-view.el ends here