1 ;;; doc-view.el --- View PDF/PostScript/DVI files in Emacs
3 ;; Copyright (C) 2007 Free Software Foundation, Inc.
5 ;; Author: Tassilo Horn <tassilo@member.fsf.org>
6 ;; Maintainer: Tassilo Horn <tassilo@member.fsf.org>
7 ;; Keywords: files, pdf, ps, dvi
8 ;; Version: <2007-09-07 Fri 15:28>
10 ;; This file is part of GNU Emacs.
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 3, or (at your option)
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 ;; Boston, MA 02110-1301, USA.
29 ;; doc-view.el requires GNU Emacs 22.1 or newer. You also need GhostScript,
30 ;; `dvipdfm' which comes with TeTeX and `pdftotext', which comes with poppler
31 ;; (http://poppler.freedesktop.org/).
35 ;; DocView is a document viewer for Emacs. It converts PDF, PS and DVI files
36 ;; to a set of PNG files, one PNG for each page, and displays the PNG images
37 ;; inside an Emacs buffer. This buffer uses `doc-view-mode' which provides
38 ;; convenient key bindings for browsing the document.
40 ;; To use it simply do
44 ;; and you'll be queried for a document to open.
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
48 ;; that file again. This reusing can be omitted if you provide a prefx
49 ;; argument to `doc-view'. To delete all cached files use
50 ;; `doc-view-clear-cache'. To open the cache with dired, so that you can tidy
51 ;; it out use `doc-view-dired-cache'.
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
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').
70 ;; Dired users should have a look at `doc-view-dired'.
72 ;; You can also search within the document. The command `doc-view-search'
73 ;; (bound to `C-s') queries for a search regexp and initializes a list of all
74 ;; matching pages and messages how many match-pages were found. After that you
75 ;; can jump to the next page containing a match with
76 ;; `doc-view-search-next-match' (bound to `C-S-n') or to the previous matching
77 ;; page with `doc-view-search-previous-match' (bound to `C-S-p'). This works
78 ;; by searching a plain text representation of the document. If that doesn't
79 ;; already exist the first invokation of `doc-view-search' starts the
80 ;; conversion. When that finishes and you're still viewing the document
81 ;; (i.e. you didn't switch to another buffer) you're queried for the regexp
86 ;; Basically doc-view should be quite usable with its standard settings, so
89 ;; (require 'doc-view)
91 ;; into your `user-init-file' should be enough. If the images are too small or
92 ;; too big you should set the "-rXXX" option in `doc-view-ghostscript-options'
93 ;; to another value. (The bigger your screen, the higher the value.)
95 ;; This and all other options can be set with the customization interface.
98 ;; M-x customize-group RET doc-view RET
100 ;; and modify them to your needs.
105 (eval-when-compile (require 'cl
))
107 ;;;; Customization Options
109 (defgroup doc-view nil
110 "In-buffer viewer for PDF, PostScript and DVI files."
111 :link
'(function-link doc-view
)
117 (defcustom doc-view-ghostscript-program
"gs"
118 "Program to convert PS and PDF files to PNG."
122 (defcustom doc-view-ghostscript-options
123 '("-dNOPAUSE" "-sDEVICE=png16m" "-dTextAlphaBits=4"
124 "-dBATCH" "-dGraphicsAlphaBits=4" "-dQUIET"
126 "A list of options to give to ghostview."
130 (defcustom doc-view-dvipdfm-program
"dvipdfm"
131 "Program to convert DVI files to PDF.
133 DVI file will be converted to PDF before the resulting PDF is
138 (defcustom doc-view-ps2pdf-program
"ps2pdf"
139 "Program to convert PS files to PDF.
141 PS files will be converted to PDF before searching is possible."
145 (defcustom doc-view-pdftotext-program
"pdftotext"
146 "Program to convert PDF files to plain text.
148 Needed for searching."
152 (defcustom doc-view-cache-directory
(concat temporary-file-directory
154 "The base directory, where the PNG images will be saved."
158 (defcustom doc-view-conversion-buffer
"*doc-view conversion output*"
159 "The buffer where messages from the converter programs go to."
163 (defcustom doc-view-conversion-refresh-interval
3
164 "Every how much seconds the DocView buffer gets refreshed while conversion.
165 After such an refresh newly converted pages will be available for
166 viewing. If set to nil there won't be any refreshes and the
167 pages won't be displayed before conversion of the whole document
172 ;;;; Internal Variables
174 (defvar doc-view-current-files nil
175 "Only used internally.")
177 (defvar doc-view-current-page nil
178 "Only used internally.")
180 (defvar doc-view-current-doc nil
181 "Only used internally.")
183 (defvar doc-view-current-converter-process nil
184 "Only used internally.")
186 (defvar doc-view-current-timer nil
187 "Only used internally.")
189 (defvar doc-view-current-slice nil
190 "Only used internally.")
192 (defvar doc-view-current-cache-dir nil
193 "Only used internally.")
195 (defvar doc-view-current-search-matches nil
196 "Only used internally.")
198 (defvar doc-view-current-image nil
199 "Only used internally.")
201 (defvar doc-view-current-info nil
202 "Only used internally.")
206 (defvar doc-view-mode-map
207 (let ((map (make-sparse-keymap)))
208 ;; Navigation in the document
209 (define-key map
(kbd "n") 'doc-view-next-page
)
210 (define-key map
(kbd "p") 'doc-view-previous-page
)
211 (define-key map
(kbd "<next>") 'doc-view-next-page
)
212 (define-key map
(kbd "<prior>") 'doc-view-previous-page
)
213 (define-key map
(kbd "SPC") 'doc-view-scroll-up-or-next-page
)
214 (define-key map
(kbd "DEL") 'doc-view-scroll-down-or-previous-page
)
215 (define-key map
(kbd "M-<") 'doc-view-first-page
)
216 (define-key map
(kbd "M->") 'doc-view-last-page
)
217 (define-key map
(kbd "g") 'doc-view-goto-page
)
218 ;; Killing/burying the buffer (and the process)
219 (define-key map
(kbd "q") 'bury-buffer
)
220 (define-key map
(kbd "k") 'doc-view-kill-proc-and-buffer
)
221 (define-key map
(kbd "C-x k") 'doc-view-kill-proc-and-buffer
)
223 (define-key map
(kbd "s s") 'doc-view-set-slice
)
224 (define-key map
(kbd "s m") 'doc-view-set-slice-using-mouse
)
225 (define-key map
(kbd "s r") 'doc-view-reset-slice
)
227 (define-key map
(kbd "C-s") 'doc-view-search
)
228 (define-key map
(kbd "<find>") 'doc-view-search
)
229 (define-key map
(kbd "C-S-n") 'doc-view-search-next-match
)
230 (define-key map
(kbd "C-S-p") 'doc-view-search-previous-match
)
232 (define-key map
(kbd "C-v") 'scroll-up
)
233 (define-key map
(kbd "<mouse-4>") 'mwheel-scroll
)
234 (define-key map
(kbd "<mouse-5>") 'mwheel-scroll
)
235 (define-key map
(kbd "M-v") 'scroll-down
)
237 (define-key map
(kbd "C-t") 'doc-view-show-tooltip
)
238 (suppress-keymap map
)
240 "Keymap used by `doc-view-mode'.")
242 ;;;; Navigation Commands
244 (defun doc-view-goto-page (page)
245 "View the page given by PAGE."
246 (interactive "nPage: ")
247 (let ((len (length doc-view-current-files
)))
252 (setq doc-view-current-page page
253 doc-view-current-info
256 (format "Page %d of %d."
257 doc-view-current-page
259 ;; Tell user if converting isn't finished yet
260 (if doc-view-current-converter-process
261 " (still converting...)\n"
263 ;; Display context infos if this page matches the last search
264 (when (and doc-view-current-search-matches
265 (assq doc-view-current-page
266 doc-view-current-search-matches
))
267 (concat (propertize "Search matches:\n" 'face
'bold
)
269 (dolist (m (cdr (assq doc-view-current-page
270 doc-view-current-search-matches
)))
271 (setq contexts
(concat contexts
" - \"" m
"\"\n")))
274 (setq inhibit-read-only t
)
277 (doc-view-insert-image (nth (1- page
) doc-view-current-files
)
279 (put-text-property beg
(point) 'help-echo doc-view-current-info
))
280 (insert "\n" doc-view-current-info
)
281 (goto-char (point-min))
283 (setq inhibit-read-only nil
)))
285 (defun doc-view-next-page (&optional arg
)
286 "Browse ARG pages forward."
288 (doc-view-goto-page (+ doc-view-current-page
(or arg
1))))
290 (defun doc-view-previous-page (&optional arg
)
291 "Browse ARG pages backward."
293 (doc-view-goto-page (- doc-view-current-page
(or arg
1))))
295 (defun doc-view-first-page ()
296 "View the first page."
298 (doc-view-goto-page 1))
300 (defun doc-view-last-page ()
301 "View the last page."
303 (doc-view-goto-page (length doc-view-current-files
)))
305 (defun doc-view-scroll-up-or-next-page ()
306 "Scroll page up if possible, else goto next page."
310 (error (doc-view-next-page))))
312 (defun doc-view-scroll-down-or-previous-page ()
313 "Scroll page down if possible, else goto previous page."
317 (error (doc-view-previous-page)
318 (goto-char (point-max)))))
320 (defun doc-view-kill-proc-and-buffer ()
321 "Kill the current converter process and buffer."
323 (when (eq major-mode
'doc-view-mode
)
324 (when doc-view-current-converter-process
325 (kill-process doc-view-current-converter-process
))
326 (when doc-view-current-timer
327 (cancel-timer doc-view-current-timer
)
328 (setq doc-view-current-timer nil
))
329 (kill-buffer (current-buffer))))
331 ;;;; Conversion Functions
333 (defun doc-view-file-name-to-directory-name (file)
334 "Return the directory where the png files of FILE should be saved.
336 It'a a subdirectory of `doc-view-cache-directory'."
337 (if doc-view-current-cache-dir
338 doc-view-current-cache-dir
339 (file-name-as-directory
340 (concat (file-name-as-directory doc-view-cache-directory
)
342 (insert-file-contents-literally file
)
343 (md5 (current-buffer)))))))
345 (defun doc-view-dvi->pdf-sentinel
(proc event
)
346 "If DVI->PDF conversion was successful, convert the PDF to PNG
348 (if (not (string-match "finished" event
))
349 (message "DocView: dvi->pdf process changed status to %s." event
)
350 (set-buffer (process-get proc
'buffer
))
351 (setq doc-view-current-converter-process nil
)
352 (message "DocView: finished conversion from DVI to PDF!")
353 ;; Now go on converting this PDF to a set of PNG files.
354 (let* ((pdf (process-get proc
'pdf-file
))
355 (png (concat (doc-view-file-name-to-directory-name
356 doc-view-current-doc
)
358 (doc-view-pdf/ps-
>png pdf png
))))
360 (defun doc-view-dvi->pdf
(dvi pdf
)
361 "Convert DVI to PDF asynchrounously."
362 (message "DocView: converting DVI to PDF now!")
363 (setq doc-view-current-converter-process
364 (start-process "doc-view-dvi->pdf" doc-view-conversion-buffer
365 doc-view-dvipdfm-program
367 (set-process-sentinel doc-view-current-converter-process
368 'doc-view-dvi-
>pdf-sentinel
)
369 (process-put doc-view-current-converter-process
'buffer
(current-buffer))
370 (process-put doc-view-current-converter-process
'pdf-file pdf
))
372 (defun doc-view-pdf/ps-
>png-sentinel
(proc event
)
373 "If PDF/PS->PNG conversion was successful, update the display."
374 (if (not (string-match "finished" event
))
375 (message "DocView: converter process changed status to %s." event
)
376 (set-buffer (process-get proc
'buffer
))
377 (setq doc-view-current-converter-process nil
)
378 (when doc-view-current-timer
379 (cancel-timer doc-view-current-timer
)
380 (setq doc-view-current-timer nil
))
381 (message "DocView: finished conversion from PDF/PS to PNG!")
382 ;; Yippie, finished. Update the display!
383 (doc-view-display doc-view-current-doc
)))
385 (defun doc-view-pdf/ps-
>png
(pdf-ps png
)
386 "Convert PDF-PS to PNG asynchrounously."
387 (message "DocView: converting PDF or PS to PNG now!")
388 (setq doc-view-current-converter-process
389 (apply 'start-process
390 (append (list "doc-view-pdf/ps->png" doc-view-conversion-buffer
391 doc-view-ghostscript-program
)
392 doc-view-ghostscript-options
393 (list (concat "-sOutputFile=" png
))
395 (process-put doc-view-current-converter-process
396 'buffer
(current-buffer))
397 (set-process-sentinel doc-view-current-converter-process
398 'doc-view-pdf
/ps-
>png-sentinel
)
399 (when doc-view-conversion-refresh-interval
400 (setq doc-view-current-timer
401 (run-at-time "1 secs" doc-view-conversion-refresh-interval
403 doc-view-current-doc
))))
405 (defun doc-view-pdf->txt-sentinel
(proc event
)
406 (if (not (string-match "finished" event
))
407 (message "DocView: converter process changed status to %s." event
)
408 (let ((current-buffer (current-buffer))
409 (proc-buffer (process-get proc
'buffer
)))
410 (set-buffer proc-buffer
)
411 (setq doc-view-current-converter-process nil
)
412 (message "DocView: finished conversion from PDF to TXT!")
413 ;; If the user looks at the DocView buffer where the conversion was
414 ;; performed, search anew. This time it will be queried for a regexp.
415 (when (eq current-buffer proc-buffer
)
416 (doc-view-search)))))
418 (defun doc-view-pdf->txt
(pdf txt
)
419 "Convert PDF to TXT asynchrounously."
420 (message "DocView: converting PDF to TXT now!")
421 (setq doc-view-current-converter-process
422 (start-process "doc-view-pdf->txt" doc-view-conversion-buffer
423 doc-view-pdftotext-program
"-raw"
425 (set-process-sentinel doc-view-current-converter-process
426 'doc-view-pdf-
>txt-sentinel
)
427 (process-put doc-view-current-converter-process
'buffer
(current-buffer)))
429 (defun doc-view-ps->pdf-sentinel
(proc event
)
430 (if (not (string-match "finished" event
))
431 (message "DocView: converter process changed status to %s." event
)
432 (set-buffer (process-get proc
'buffer
))
433 (setq doc-view-current-converter-process nil
)
434 (message "DocView: finished conversion from PS to PDF!")
435 ;; Now we can transform to plain text.
436 (doc-view-pdf->txt
(process-get proc
'pdf-file
)
437 (concat (doc-view-file-name-to-directory-name
438 doc-view-current-doc
)
441 (defun doc-view-ps->pdf
(ps pdf
)
442 "Convert PS to PDF asynchronously."
443 (message "DocView: converting PS to PDF now!")
444 (setq doc-view-current-converter-process
445 (start-process "doc-view-ps->pdf" doc-view-conversion-buffer
446 doc-view-ps2pdf-program
448 (set-process-sentinel doc-view-current-converter-process
449 'doc-view-ps-
>pdf-sentinel
)
450 (process-put doc-view-current-converter-process
'buffer
(current-buffer))
451 (process-put doc-view-current-converter-process
'pdf-file pdf
))
453 (defun doc-view-convert-doc (doc)
454 "Convert DOC to a set of png files, one file per page.
456 Those files are saved in the directory given by
457 `doc-view-file-name-to-directory-name'."
459 (let* ((dir (doc-view-file-name-to-directory-name doc
))
460 (png-file (concat (file-name-as-directory dir
) "page-%d.png")))
461 (when (file-exists-p dir
)
462 (dired-delete-file dir
'always
))
463 (make-directory dir t
)
464 (if (not (string= (file-name-extension doc
) "dvi"))
465 ;; Convert to PNG images.
466 (doc-view-pdf/ps-
>png doc png-file
)
467 ;; DVI files have to be converted to PDF before GhostScript can process
469 (doc-view-dvi->pdf doc
470 (concat (file-name-as-directory dir
)
475 (define-derived-mode doc-view-mode nil
"DocView"
476 "Major mode in DocView buffers.
478 \\{doc-view-mode-map}"
480 (setq buffer-read-only t
)
481 (make-local-variable 'doc-view-current-files
)
482 (make-local-variable 'doc-view-current-doc
)
483 (make-local-variable 'doc-view-current-image
)
484 (make-local-variable 'doc-view-current-page
)
485 (make-local-variable 'doc-view-current-converter-process
)
486 (make-local-variable 'doc-view-current-timer
)
487 (make-local-variable 'doc-view-current-slice
)
488 (make-local-variable 'doc-view-current-cache-dir
)
489 (make-local-variable 'doc-view-current-info
)
490 (make-local-variable 'doc-view-current-search-matches
))
494 (defun doc-view-set-slice (x y width height
)
495 "Set the slice of the images that should be displayed.
496 You can use this function to tell doc-view not to display the
497 margins of the document. It prompts for the top-left corner (X
498 and Y) of the slice to display and its WIDTH and HEIGHT.
500 See `doc-view-set-slice-using-mouse' for a more convenient way to
501 do that. To reset the slice use `doc-view-reset-slice'."
503 (let* ((size (image-size doc-view-current-image t
))
504 (a (read-number (format "Top-left X (0..%d): " (car size
))))
505 (b (read-number (format "Top-left Y (0..%d): " (cdr size
))))
506 (c (read-number (format "Width (0..%d): " (- (car size
) a
))))
507 (d (read-number (format "Height (0..%d): " (- (cdr size
) b
)))))
509 (setq doc-view-current-slice
(list x y width height
))
511 (doc-view-goto-page doc-view-current-page
))
513 (defun doc-view-set-slice-using-mouse ()
514 "Set the slice of the images that should be displayed.
515 You set the slice by pressing mouse-1 at its top-left corner and
516 dragging it to its bottom-right corner. See also
517 `doc-view-set-slice' and `doc-view-reset-slice'."
522 (concat "Press mouse-1 at the top-left corner and "
523 "drag it to the bottom-right corner!"))))
524 (when (eq (car e
) 'drag-mouse-1
)
525 (setq x
(car (posn-object-x-y (event-start e
))))
526 (setq y
(cdr (posn-object-x-y (event-start e
))))
527 (setq w
(- (car (posn-object-x-y (event-end e
))) x
))
528 (setq h
(- (cdr (posn-object-x-y (event-end e
))) y
))
530 (doc-view-set-slice x y w h
)))
532 (defun doc-view-reset-slice ()
533 "Resets the current slice.
534 After calling this function the whole pages will be visible
537 (setq doc-view-current-slice nil
)
539 (doc-view-goto-page doc-view-current-page
))
543 (defun doc-view-insert-image (file &rest args
)
544 "Insert the given png FILE.
545 ARGs is a list of image descriptors."
546 (let ((image (apply 'create-image file
'png nil args
)))
547 (setq doc-view-current-image image
)
548 (insert-image image
(concat "[" file
"]") nil doc-view-current-slice
)))
550 (defun doc-view-sort (a b
)
551 "Return non-nil if A should be sorted before B.
552 Predicate for sorting `doc-view-current-files'."
553 (if (< (length a
) (length b
))
555 (if (> (length a
) (length b
))
559 (defun doc-view-display (doc)
560 "Start viewing the document DOC."
561 (let ((dir (doc-view-file-name-to-directory-name doc
)))
562 (set-buffer (format "*DocView: %s*" doc
))
563 (setq doc-view-current-files
564 (sort (directory-files dir t
"page-[0-9]+\\.png" t
)
566 (when (> (length doc-view-current-files
) 0)
567 (doc-view-goto-page doc-view-current-page
))))
569 (defun doc-view-buffer-message ()
570 (setq inhibit-read-only t
)
572 (insert (propertize "Welcome to DocView!" 'face
'bold
)
575 If you see this buffer it means that the document you want to
576 view gets converted to PNG now and the conversion of the first
577 page hasn't finished yet or
578 `doc-view-conversion-refresh-interval' is set to nil.
580 For now these keys are useful:
582 `q' : Bury this buffer. Conversion will go on in background.
583 `k' : Kill the conversion process and this buffer.\n")
584 (setq inhibit-read-only nil
))
586 (defun doc-view-show-tooltip ()
588 (tooltip-show doc-view-current-info
))
592 (defun doc-view-search-internal (regexp file
)
593 "Return a list of FILE's pages that contain text matching REGEXP.
594 The value is an alist of the form
598 where PAGE is the pagenumber and CONTEXTS are the lines
599 containing the match."
601 (insert-file-contents file
)
605 (while (re-search-forward (concat "\\(?:\\([\f]\\)\\|\\("
606 regexp
"\\)\\)") nil t
)
607 (when (match-string 1) (incf page
))
608 (when (match-string 2)
609 (if (/= page lastpage
)
610 (setq matches
(push (cons page
611 (list (buffer-substring
612 (line-beginning-position)
613 (line-end-position))))
618 ;; This page already is a match.
620 ;; This is the first match on page.
622 (list (buffer-substring
623 (line-beginning-position)
624 (line-end-position))))
626 (setq lastpage page
)))
627 (nreverse matches
))))
629 (defun doc-view-search-no-of-matches (list)
630 "Extract the number of matches from the search result LIST."
633 (setq no
(+ no
(1- (length p
)))))
636 (defun doc-view-search ()
637 "Query for a regexp and search the current document.
638 If the current document hasn't been transformed to plain text
639 till now do that first. You should try searching anew when the
640 conversion finished."
642 ;; New search, so forget the old results.
643 (setq doc-view-current-search-matches nil
)
644 (let ((txt (concat (doc-view-file-name-to-directory-name
645 doc-view-current-doc
)
647 (if (file-readable-p txt
)
649 (setq doc-view-current-search-matches
650 (doc-view-search-internal
651 (read-from-minibuffer "Regexp: ")
653 (message "DocView: search yielded %d matches."
654 (doc-view-search-no-of-matches
655 doc-view-current-search-matches
)))
656 ;; We must convert to TXT first!
657 (if doc-view-current-converter-process
658 (message "DocView: please wait till conversion finished.")
659 (let ((ext (file-name-extension doc-view-current-doc
)))
662 ;; Doc is a PDF, so convert it to TXT
663 (doc-view-pdf->txt doc-view-current-doc txt
))
665 ;; Doc is a PS, so convert it to PDF (which will be converted to
667 (doc-view-ps->pdf doc-view-current-doc
668 (concat (doc-view-file-name-to-directory-name
669 doc-view-current-doc
)
672 ;; Doc is a DVI. This means that a doc.pdf already exists in its
673 ;; cache subdirectory.
674 (doc-view-pdf->txt
(concat (doc-view-file-name-to-directory-name
675 doc-view-current-doc
)
678 (t (error "DocView doesn't know what to do"))))))))
680 (defun doc-view-search-next-match (arg)
681 "Go to the ARGth next matching page."
683 (let* ((next-pages (remove-if (lambda (i) (<= (car i
) doc-view-current-page
))
684 doc-view-current-search-matches
))
685 (page (car (nth (1- arg
) next-pages
))))
687 (doc-view-goto-page page
)
689 doc-view-current-search-matches
690 (y-or-n-p "No more matches after current page. Wrap to first match? "))
691 (doc-view-goto-page (caar doc-view-current-search-matches
))))))
693 (defun doc-view-search-previous-match (arg)
694 "Go to the ARGth previous matching page."
696 (let* ((prev-pages (remove-if (lambda (i) (>= (car i
) doc-view-current-page
))
697 doc-view-current-search-matches
))
698 (page (car (nth (1- arg
) (nreverse prev-pages
)))))
700 (doc-view-goto-page page
)
702 doc-view-current-search-matches
703 (y-or-n-p "No more matches before current page. Wrap to last match? "))
704 (doc-view-goto-page (caar (last doc-view-current-search-matches
)))))))
706 ;;;; User Interface Commands
708 (defun doc-view (no-cache &optional file
)
709 "Convert FILE to png and start viewing it.
710 If no FILE is given, query for on.
711 If this FILE is still in the cache, don't convert and use the
712 existing page files. With prefix arg NO-CACHE, don't use the
713 cached files and convert anew."
715 (if (not (and (image-type-available-p 'png
)
717 (message "DocView: your emacs or display doesn't support png images.")
719 (expand-file-name (read-file-name "File: " nil nil t
))))
720 (buffer (get-buffer-create (format "*DocView: %s*" doc
)))
721 (dir (doc-view-file-name-to-directory-name doc
)))
722 (switch-to-buffer buffer
)
723 (doc-view-buffer-message)
725 (setq doc-view-current-doc doc
)
726 (setq doc-view-current-page
1)
727 (if (not (and (file-exists-p dir
)
730 (setq doc-view-current-cache-dir nil
)
731 (doc-view-convert-doc doc-view-current-doc
))
732 (message "DocView: using cached files!")
733 (doc-view-display doc-view-current-doc
)))))
735 (defun doc-view-dired (no-cache)
736 "View the current dired file with doc-view.
737 NO-CACHE is the same as in `doc-view'.
739 You might want to bind this command to a dired key, e.g.
741 (define-key dired-mode-map (kbd \"C-c d\") 'doc-view-dired)"
743 (doc-view no-cache
(dired-get-file-for-visit)))
745 (defun doc-view-clear-cache ()
746 "Delete the whole cache (`doc-view-cache-directory')."
748 (dired-delete-file doc-view-cache-directory
'always
)
749 (make-directory doc-view-cache-directory
))
751 (defun doc-view-dired-cache ()
752 "Open `dired' in `doc-view-cache-directory'."
754 (dired doc-view-cache-directory
))
759 ;; mode: outline-minor
762 ;; arch-tag: 5d6e5c5e-095f-489e-b4e4-1ca90a7d79be
763 ;;; doc-view.el ends here