doc-view.el 70 KB
Newer Older
1 2
;;; doc-view.el --- View PDF/PostScript/DVI files in Emacs -*- lexical-binding: t -*-

Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
3

4
;; Copyright (C) 2007-2013 Free Software Foundation, Inc.
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
5
;;
6 7
;; Author: Tassilo Horn <tsdh@gnu.org>
;; Maintainer: Tassilo Horn <tsdh@gnu.org>
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
8 9 10 11
;; Keywords: files, pdf, ps, dvi

;; This file is part of GNU Emacs.

12
;; GNU Emacs is free software: you can redistribute it and/or modify
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
13
;; it under the terms of the GNU General Public License as published by
14 15
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
16 17 18 19 20 21 22

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
23
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
24 25 26

;;; Requirements:

27
;; doc-view.el requires GNU Emacs 22.1 or newer.  You also need Ghostscript,
28 29 30
;; `dvipdf' (comes with Ghostscript) or `dvipdfm' (comes with teTeX or TeXLive)
;; and `pdftotext', which comes with xpdf (http://www.foolabs.com/xpdf/) or
;; poppler (http://poppler.freedesktop.org/).
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
31 32 33 34 35 36 37 38

;;; Commentary:

;; DocView is a document viewer for Emacs.  It converts PDF, PS and DVI files
;; to a set of PNG files, one PNG for each page, and displays the PNG images
;; inside an Emacs buffer.  This buffer uses `doc-view-mode' which provides
;; convenient key bindings for browsing the document.
;;
39
;; To use it simply open a document file with
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
40
;;
41
;;     C-x C-f ~/path/to/document RET
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
42
;;
43 44
;; and the document will be converted and displayed, if your emacs supports png
;; images.  With `C-c C-c' you can toggle between the rendered images
45
;; representation and the source text representation of the document.
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
46 47 48
;;
;; Since conversion may take some time all the PNG images are cached in a
;; subdirectory of `doc-view-cache-directory' and reused when you want to view
49 50
;; that file again.  To reconvert a document hit `g' (`doc-view-reconvert-doc')
;; when displaying the document.  To delete all cached files use
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
51 52 53 54 55 56 57 58 59
;; `doc-view-clear-cache'.  To open the cache with dired, so that you can tidy
;; it out use `doc-view-dired-cache'.
;;
;; When conversion in underway the first page will be displayed as soon as it
;; is available and the available pages are refreshed every
;; `doc-view-conversion-refresh-interval' seconds.  If that variable is nil the
;; pages won't be displayed before conversion of the document finished
;; completely.
;;
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
;; DocView lets you select a slice of the displayed pages.  This slice
;; will be remembered and applied to all pages of the current
;; document.  This enables you to cut away the margins of a document
;; to save some space.  To select a slice you can use
;; `doc-view-set-slice' (bound to `s s') which will query you for the
;; coordinates of the slice's top-left corner and its width and
;; height.  A much more convenient way to do the same is offered by
;; the command `doc-view-set-slice-using-mouse' (bound to `s m').
;; After invocation you only have to press mouse-1 at the top-left
;; corner and drag it to the bottom-right corner of the desired slice.
;; Even more accurate and convenient is to use
;; `doc-view-set-slice-from-bounding-box' (bound to `s b') which uses
;; the BoundingBox information of the current page to set an optimal
;; slice.  To reset the slice use `doc-view-reset-slice' (bound to `s
;; r').
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
75 76 77 78
;;
;; You can also search within the document.  The command `doc-view-search'
;; (bound to `C-s') queries for a search regexp and initializes a list of all
;; matching pages and messages how many match-pages were found.  After that you
79 80 81 82
;; can jump to the next page containing a match with an additional `C-s'.  With
;; `C-r' you can do the same, but backwards.  To search for a new regexp give a
;; prefix arg to one of the search functions, e.g. by typing `C-u C-s'.  The
;; searching works by using a plain text representation of the document.  If
Juanma Barranquero's avatar
Juanma Barranquero committed
83
;; that doesn't already exist the first invocation of `doc-view-search' (or
84 85 86
;; `doc-view-search-backward') starts the conversion.  When that finishes and
;; you're still viewing the document (i.e. you didn't switch to another buffer)
;; you're queried for the regexp then.
87 88 89 90
;;
;; Dired users can simply hit `v' on a document file.  If it's a PS, PDF or DVI
;; it will be opened using `doc-view-mode'.
;;
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
91 92 93

;;; Configuration:

94 95 96
;; If the images are too small or too big you should set the "-rXXX" option in
;; `doc-view-ghostscript-options' to another value.  (The bigger your screen,
;; the higher the value.)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
97 98 99 100 101 102 103 104
;;
;; This and all other options can be set with the customization interface.
;; Simply do
;;
;;     M-x customize-group RET doc-view RET
;;
;; and modify them to your needs.

105
;;; Todo:
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
106

107 108
;; - add print command.
;; - share more code with image-mode.
109 110
;; - better menu.
;; - Bind slicing to a drag event.
111
;; - zoom the region around the cursor (like xdvi).
112 113
;; - get rid of the silly arrow in the fringe.
;; - improve anti-aliasing (pdf-utils gets it better).
114

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
;;;; About isearch support

;; I tried implementing isearch by setting
;; `isearch-search-fun-function' buffer-locally, but that didn't
;; work too good.  The function doing the real search was called
;; endlessly somehow.  But even if we'd get that working no real
;; isearch feeling comes up due to the missing match highlighting.
;; Currently I display all lines containing a match in a tooltip and
;; each C-s or C-r jumps directly to the next/previous page with a
;; match.  With isearch we could only display the current match.  So
;; we had to decide if another C-s jumps to the next page with a
;; match (thus only the first match in a page will be displayed in a
;; tooltip) or to the next match, which would do nothing visible
;; (except the tooltip) if the next match is on the same page.

;; And it's much slower than the current search facility, because
Paul Eggert's avatar
Paul Eggert committed
131
;; isearch really searches for each step forward or backward whereas
132 133 134 135 136 137 138 139
;; the current approach searches once and then it knows to which
;; pages to jump.

;; Anyway, if someone with better isearch knowledge wants to give it a try,
;; feel free to do it.  --Tassilo

;;; Code:

Stefan Monnier's avatar
Stefan Monnier committed
140
(eval-when-compile (require 'cl-lib))
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
141
(require 'dired)
142
(require 'image-mode)
143
(require 'jka-compr)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
144 145 146 147

;;;; Customization Options

(defgroup doc-view nil
148
  "In-buffer viewer for PDF, PostScript, DVI, and DJVU files."
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
149 150 151
  :link '(function-link doc-view)
  :version "22.2"
  :group 'applications
Chong Yidong's avatar
Chong Yidong committed
152
  :group 'data
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
153 154 155
  :group 'multimedia
  :prefix "doc-view-")

156
(defcustom doc-view-ghostscript-program "gs"
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
157
  "Program to convert PS and PDF files to PNG."
Reiner Steib's avatar
Reiner Steib committed
158
  :type 'file
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
159 160
  :group 'doc-view)

161 162 163 164 165
(defcustom doc-view-pdfdraw-program
  (cond
   ((executable-find "pdfdraw") "pdfdraw")
   (t "mudraw"))
  "Name of MuPDF's program to convert PDF files to PNG."
166 167 168 169
  :type 'file
  :version "24.4")

(defcustom doc-view-pdf->png-converter-function
170 171 172
  (if (executable-find doc-view-pdfdraw-program)
      #'doc-view-pdf->png-converter-mupdf
    #'doc-view-pdf->png-converter-ghostscript)
173 174 175 176 177 178 179 180 181
  "Function to call to convert a PDF file into a PNG file."
  :type '(radio
          (function-item doc-view-pdf->png-converter-ghostscript
                         :doc "Use ghostscript")
          (function-item doc-view-pdf->png-converter-mupdf
                         :doc "Use mupdf")
          function)
  :version "24.4")

Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
182
(defcustom doc-view-ghostscript-options
183
  '("-dSAFER" ;; Avoid security problems when rendering files from untrusted
184
    ;; sources.
185
    "-dNOPAUSE" "-sDEVICE=png16m" "-dTextAlphaBits=4"
186
    "-dBATCH" "-dGraphicsAlphaBits=4" "-dQUIET")
187
  "A list of options to give to ghostscript."
Reiner Steib's avatar
Reiner Steib committed
188
  :type '(repeat string)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
189 190
  :group 'doc-view)

191 192 193
(defcustom doc-view-resolution 100
  "Dots per inch resolution used to render the documents.
Higher values result in larger images."
Dan Nicolaescu's avatar
Dan Nicolaescu committed
194 195
  :type 'number
  :group 'doc-view)
196

197 198 199 200 201 202 203
(defcustom doc-view-scale-internally t
  "Whether we should try to rescale images ourselves.
If nil, the document is re-rendered every time the scaling factor is modified.
This only has an effect if the image libraries linked with Emacs support
scaling."
  :type 'boolean)

204 205
(defcustom doc-view-image-width 850
  "Default image width.
206 207
Has only an effect if `doc-view-scale-internally' is non-nil and support for
scaling is compiled into emacs."
208
  :version "24.1"
209 210 211
  :type 'number
  :group 'doc-view)

212
(defcustom doc-view-dvipdfm-program "dvipdfm"
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
213 214 215
  "Program to convert DVI files to PDF.

DVI file will be converted to PDF before the resulting PDF is
216 217 218 219 220 221 222
converted to PNG.

If this and `doc-view-dvipdf-program' are set,
`doc-view-dvipdf-program' will be preferred."
  :type 'file
  :group 'doc-view)

223
(defcustom doc-view-dvipdf-program "dvipdf"
224 225 226 227 228 229 230
  "Program to convert DVI files to PDF.

DVI file will be converted to PDF before the resulting PDF is
converted to PNG.

If this and `doc-view-dvipdfm-program' are set,
`doc-view-dvipdf-program' will be preferred."
Reiner Steib's avatar
Reiner Steib committed
231
  :type 'file
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
232 233
  :group 'doc-view)

234 235 236 237 238 239 240 241 242
(define-obsolete-variable-alias 'doc-view-unoconv-program
                                'doc-view-odf->pdf-converter-program
                                "24.4")

(defcustom doc-view-odf->pdf-converter-program
  (cond
   ((executable-find "soffice") "soffice")
   ((executable-find "unoconv") "unoconv")
   (t "soffice"))
243 244 245
  "Program to convert any file type readable by OpenOffice.org to PDF.

Needed for viewing OpenOffice.org (and MS Office) files."
246
  :version "24.4"
247 248 249
  :type 'file
  :group 'doc-view)

250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
(defcustom doc-view-odf->pdf-converter-function
  (cond
   ((string-match "unoconv\\'" doc-view-odf->pdf-converter-program)
    #'doc-view-odf->pdf-converter-unoconv)
   ((string-match "soffice\\'" doc-view-odf->pdf-converter-program)
    #'doc-view-odf->pdf-converter-soffice))
  "Function to call to convert a ODF file into a PDF file."
  :type '(radio
          (function-item doc-view-odf->pdf-converter-unoconv
                         :doc "Use unoconv")
          (function-item doc-view-odf->pdf-converter-soffice
                         :doc "Use LibreOffice")
          function)
  :version "24.4")

265
(defcustom doc-view-ps2pdf-program "ps2pdf"
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
266 267 268
  "Program to convert PS files to PDF.

PS files will be converted to PDF before searching is possible."
Reiner Steib's avatar
Reiner Steib committed
269
  :type 'file
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
270 271
  :group 'doc-view)

272
(defcustom doc-view-pdftotext-program "pdftotext"
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
273 274 275
  "Program to convert PDF files to plain text.

Needed for searching."
Reiner Steib's avatar
Reiner Steib committed
276
  :type 'file
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
277 278
  :group 'doc-view)

279
(defcustom doc-view-cache-directory
280
  (expand-file-name (format "docview%d" (user-uid))
281
		    temporary-file-directory)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
282
  "The base directory, where the PNG images will be saved."
Reiner Steib's avatar
Reiner Steib committed
283
  :type 'directory
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
284 285
  :group 'doc-view)

286 287
(defvar doc-view-conversion-buffer " *doc-view conversion output*"
  "The buffer where messages from the converter programs go to.")
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
288

289
(defcustom doc-view-conversion-refresh-interval 1
290 291
  "Interval in seconds between refreshes of the DocView buffer while converting.
After such a refresh newly converted pages will be available for
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
292 293 294
viewing.  If set to nil there won't be any refreshes and the
pages won't be displayed before conversion of the whole document
has finished."
Reiner Steib's avatar
Reiner Steib committed
295
  :type 'integer
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
296 297
  :group 'doc-view)

Juri Linkov's avatar
Juri Linkov committed
298
(defcustom doc-view-continuous nil
299 300 301 302 303 304 305 306
  "In Continuous mode reaching the page edge advances to next/previous page.
When non-nil, scrolling a line upward at the bottom edge of the page
moves to the next page, and scrolling a line downward at the top edge
of the page moves to the previous page."
  :type 'boolean
  :group 'doc-view
  :version "23.2")

Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
307 308
;;;; Internal Variables

309
(defun doc-view-new-window-function (winprops)
310 311 312
  ;; (message "New window %s for buf %s" (car winprops) (current-buffer))
  (cl-assert (or (eq t (car winprops))
                 (eq (window-buffer (car winprops)) (current-buffer))))
313 314
  (let ((ol (image-mode-window-get 'overlay winprops)))
    (if ol
315
        (progn
316 317 318
          (setq ol (copy-overlay ol))
          ;; `ol' might actually be dead.
          (move-overlay ol (point-min) (point-max)))
319 320 321
      (setq ol (make-overlay (point-min) (point-max) nil t))
      (overlay-put ol 'doc-view t))
    (overlay-put ol 'window (car winprops))
322 323 324 325 326
    (unless (windowp (car winprops))
      ;; It's a pseudo entry.  Let's make sure it's not displayed (the
      ;; `window' property is only effective if its value is a window).
      (cl-assert (eq t (car winprops)))
      (delete-overlay ol))
327
    (image-mode-window-put 'overlay ol winprops)))
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
328

329
(defvar doc-view-current-files nil
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
330
  "Only used internally.")
331
(make-variable-buffer-local 'doc-view-current-files)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
332

333
(defvar doc-view-current-converter-processes nil
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
334
  "Only used internally.")
335
(make-variable-buffer-local 'doc-view-current-converter-processes)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
336 337 338

(defvar doc-view-current-timer nil
  "Only used internally.")
339
(make-variable-buffer-local 'doc-view-current-timer)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
340 341 342

(defvar doc-view-current-cache-dir nil
  "Only used internally.")
343
(make-variable-buffer-local 'doc-view-current-cache-dir)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
344 345 346

(defvar doc-view-current-search-matches nil
  "Only used internally.")
347
(make-variable-buffer-local 'doc-view-current-search-matches)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
348

349 350
(defvar doc-view-pending-cache-flush nil
  "Only used internally.")
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
351

352
(defvar doc-view-previous-major-mode nil
353 354
  "Only used internally.")

355 356 357 358 359 360 361 362
(defvar doc-view-buffer-file-name nil
  "Only used internally.
The file name used for conversion.  Normally it's the same as
`buffer-file-name', but for remote files, compressed files and
files inside an archive it is a temporary copy of
the (uncompressed, extracted) file residing in
`doc-view-cache-directory'.")

363 364 365 366
(defvar doc-view-doc-type nil
  "The type of document in the current buffer.
Can be `dvi', `pdf', or `ps'.")

367 368 369 370 371
(defvar doc-view-single-page-converter-function nil
  "Function to call to convert a single page of the document to a bitmap file.
May operate on the source document or on some intermediate (typically PDF)
conversion of it.")

372
(defvar-local doc-view--image-type nil
373 374 375
  "The type of image in the current buffer.
Can be `png' or `tiff'.")

376 377 378
(defvar-local doc-view--image-file-pattern nil
  "The `format' pattern of image file names.
Typically \"page-%s.png\".")
379

380
;;;; DocView Keymaps
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
381 382 383

(defvar doc-view-mode-map
  (let ((map (make-sparse-keymap)))
384
    (set-keymap-parent map image-mode-map)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
385 386 387
    ;; Navigation in the document
    (define-key map (kbd "n")         'doc-view-next-page)
    (define-key map (kbd "p")         'doc-view-previous-page)
388 389 390 391
    (define-key map (kbd "<next>")    'forward-page)
    (define-key map (kbd "<prior>")   'backward-page)
    (define-key map [remap forward-page]  'doc-view-next-page)
    (define-key map [remap backward-page] 'doc-view-previous-page)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
392
    (define-key map (kbd "SPC")       'doc-view-scroll-up-or-next-page)
393
    (define-key map (kbd "S-SPC")     'doc-view-scroll-down-or-previous-page)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
394
    (define-key map (kbd "DEL")       'doc-view-scroll-down-or-previous-page)
395 396 397 398
    (define-key map (kbd "C-n")       'doc-view-next-line-or-next-page)
    (define-key map (kbd "<down>")    'doc-view-next-line-or-next-page)
    (define-key map (kbd "C-p")       'doc-view-previous-line-or-previous-page)
    (define-key map (kbd "<up>")      'doc-view-previous-line-or-previous-page)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
399 400
    (define-key map (kbd "M-<")       'doc-view-first-page)
    (define-key map (kbd "M->")       'doc-view-last-page)
401
    (define-key map [remap goto-line] 'doc-view-goto-page)
402
    (define-key map (kbd "RET")       'image-next-line)
403 404 405
    ;; Zoom in/out.
    (define-key map "+"               'doc-view-enlarge)
    (define-key map "-"               'doc-view-shrink)
406 407 408 409
    ;; Fit the image to the window
    (define-key map "W"               'doc-view-fit-width-to-window)
    (define-key map "H"               'doc-view-fit-height-to-window)
    (define-key map "P"               'doc-view-fit-page-to-window)
410
    ;; Killing the buffer (and the process)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
411
    (define-key map (kbd "k")         'doc-view-kill-proc-and-buffer)
412
    (define-key map (kbd "K")         'doc-view-kill-proc)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
413 414 415
    ;; Slicing the image
    (define-key map (kbd "s s")       'doc-view-set-slice)
    (define-key map (kbd "s m")       'doc-view-set-slice-using-mouse)
416
    (define-key map (kbd "s b")       'doc-view-set-slice-from-bounding-box)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
417 418 419 420
    (define-key map (kbd "s r")       'doc-view-reset-slice)
    ;; Searching
    (define-key map (kbd "C-s")       'doc-view-search)
    (define-key map (kbd "<find>")    'doc-view-search)
421
    (define-key map (kbd "C-r")       'doc-view-search-backward)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
422 423
    ;; Show the tooltip
    (define-key map (kbd "C-t")       'doc-view-show-tooltip)
424 425
    ;; Toggle between text and image display or editing
    (define-key map (kbd "C-c C-c")   'doc-view-toggle-display)
426 427
    ;; Open a new buffer with doc's text contents
    (define-key map (kbd "C-c C-t")   'doc-view-open-text)
428 429 430 431
    ;; Reconvert the current document.  Don't just use revert-buffer
    ;; because that resets the scale factor, the page number, ...
    (define-key map (kbd "g")         'doc-view-revert-buffer)
    (define-key map (kbd "r")         'doc-view-revert-buffer)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
432
    map)
433 434
  "Keymap used by `doc-view-mode' when displaying a doc as a set of images.")

435 436 437 438 439 440 441 442
(defun doc-view-revert-buffer (&optional ignore-auto noconfirm)
  "Like `revert-buffer', but preserves the buffer's current modes."
  ;; FIXME: this should probably be moved to files.el and used for
  ;; most/all "g" bindings to revert-buffer.
  (interactive (list (not current-prefix-arg)))
  (revert-buffer ignore-auto noconfirm 'preserve-modes))


443 444 445
(easy-menu-define doc-view-menu doc-view-mode-map
  "Menu for Doc View mode."
  '("DocView"
Juri Linkov's avatar
Juri Linkov committed
446 447 448 449 450 451 452 453 454 455 456
    ["Toggle display"		doc-view-toggle-display]
    ("Continuous"
     ["Off"                     (setq doc-view-continuous nil)
      :style radio :selected    (eq doc-view-continuous nil)]
     ["On"		        (setq doc-view-continuous t)
      :style radio :selected    (eq doc-view-continuous t)]
     "---"
     ["Save as Default"
      (customize-save-variable 'doc-view-continuous doc-view-continuous) t]
     )
    "---"
457
    ["Set Slice"		doc-view-set-slice-using-mouse]
458
    ["Set Slice (BoundingBox)"  doc-view-set-slice-from-bounding-box]
459 460 461 462
    ["Set Slice (manual)"	doc-view-set-slice]
    ["Reset Slice"		doc-view-reset-slice]
    "---"
    ["Search"			doc-view-search]
463
    ["Search Backwards"         doc-view-search-backward]
464 465
    ))

466
(defvar doc-view-minor-mode-map
467 468 469 470
  (let ((map (make-sparse-keymap)))
    ;; Toggle between text and image display or editing
    (define-key map (kbd "C-c C-c") 'doc-view-toggle-display)
    map)
471
  "Keymap used by `doc-minor-view-mode'.")
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
472 473 474

;;;; Navigation Commands

475 476
(defmacro doc-view-current-page (&optional win)
  `(image-mode-window-get 'page ,win))
477 478 479 480 481
(defmacro doc-view-current-info () `(image-mode-window-get 'info))
(defmacro doc-view-current-overlay () `(image-mode-window-get 'overlay))
(defmacro doc-view-current-image () `(image-mode-window-get 'image))
(defmacro doc-view-current-slice () `(image-mode-window-get 'slice))

482 483 484
(defun doc-view-last-page-number ()
  (length doc-view-current-files))

Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
485 486 487
(defun doc-view-goto-page (page)
  "View the page given by PAGE."
  (interactive "nPage: ")
488
  (let ((len (doc-view-last-page-number))
489
	(hscroll (window-hscroll)))
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
490
    (if (< page 1)
491
	(setq page 1)
492 493 494
      (when (and (> page len)
                 ;; As long as the converter is running, we don't know
                 ;; how many pages will be available.
495
                 (null doc-view-current-converter-processes))
496
	(setq page len)))
497 498
    (setf (doc-view-current-page) page
	  (doc-view-current-info)
499 500
	  (concat
	   (propertize
501
	    (format "Page %d of %d." page len) 'face 'bold)
502
	   ;; Tell user if converting isn't finished yet
503
	   (if doc-view-current-converter-processes
504 505 506 507
	       " (still converting...)\n"
	     "\n")
	   ;; Display context infos if this page matches the last search
	   (when (and doc-view-current-search-matches
508
		      (assq page doc-view-current-search-matches))
509 510
	     (concat (propertize "Search matches:\n" 'face 'bold)
		     (let ((contexts ""))
511
		       (dolist (m (cdr (assq page
512 513 514
					     doc-view-current-search-matches)))
			 (setq contexts (concat contexts "  - \"" m "\"\n")))
		       contexts)))))
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
515
    ;; Update the buffer
516 517 518
    ;; We used to find the file name from doc-view-current-files but
    ;; that's not right if the pages are not generated sequentially
    ;; or if the page isn't in doc-view-current-files yet.
519
    (let ((file (expand-file-name
520
                 (format doc-view--image-file-pattern page)
521
                 (doc-view-current-cache-dir))))
522
      (doc-view-insert-image file :pointer 'arrow)
523
      (set-window-hscroll (selected-window) hscroll)
524 525 526
      (when (and (not (file-exists-p file))
                 doc-view-current-converter-processes)
        ;; The PNG file hasn't been generated yet.
527 528 529 530 531 532 533 534 535 536 537 538
        (funcall doc-view-single-page-converter-function
		 doc-view-buffer-file-name file page
		 (let ((win (selected-window)))
		   (lambda ()
		     (and (eq (current-buffer) (window-buffer win))
			  ;; If we changed page in the mean
			  ;; time, don't mess things up.
			  (eq (doc-view-current-page win) page)
			  ;; Make sure we don't infloop.
			  (file-readable-p file)
			  (with-selected-window win
			    (doc-view-goto-page page))))))))
539 540
    (overlay-put (doc-view-current-overlay)
                 'help-echo (doc-view-current-info))))
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
541 542 543 544

(defun doc-view-next-page (&optional arg)
  "Browse ARG pages forward."
  (interactive "p")
545
  (doc-view-goto-page (+ (doc-view-current-page) (or arg 1))))
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
546 547 548 549

(defun doc-view-previous-page (&optional arg)
  "Browse ARG pages backward."
  (interactive "p")
550
  (doc-view-goto-page (- (doc-view-current-page) (or arg 1))))
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
551 552 553 554 555 556 557 558 559

(defun doc-view-first-page ()
  "View the first page."
  (interactive)
  (doc-view-goto-page 1))

(defun doc-view-last-page ()
  "View the last page."
  (interactive)
560
  (doc-view-goto-page (doc-view-last-page-number)))
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
561

562 563
(defun doc-view-scroll-up-or-next-page (&optional arg)
  "Scroll page up ARG lines if possible, else goto next page.
Juri Linkov's avatar
Juri Linkov committed
564
When `doc-view-continuous' is non-nil, scrolling upward
565 566 567
at the bottom edge of the page moves to the next page.
Otherwise, goto next page only on typing SPC (ARG is nil)."
  (interactive "P")
Juri Linkov's avatar
Juri Linkov committed
568
  (if (or doc-view-continuous (null arg))
569 570 571 572 573 574 575 576 577 578 579 580
      (let ((hscroll (window-hscroll))
	    (cur-page (doc-view-current-page)))
	(when (= (window-vscroll) (image-scroll-up arg))
	  (doc-view-next-page)
	  (when (/= cur-page (doc-view-current-page))
	    (image-bob)
	    (image-bol 1))
	  (set-window-hscroll (selected-window) hscroll)))
    (image-scroll-up arg)))

(defun doc-view-scroll-down-or-previous-page (&optional arg)
  "Scroll page down ARG lines if possible, else goto previous page.
Juri Linkov's avatar
Juri Linkov committed
581
When `doc-view-continuous' is non-nil, scrolling downward
582 583 584
at the top edge of the page moves to the previous page.
Otherwise, goto previous page only on typing DEL (ARG is nil)."
  (interactive "P")
Juri Linkov's avatar
Juri Linkov committed
585
  (if (or doc-view-continuous (null arg))
586 587 588 589 590 591 592 593 594 595 596 597
      (let ((hscroll (window-hscroll))
	    (cur-page (doc-view-current-page)))
	(when (= (window-vscroll) (image-scroll-down arg))
	  (doc-view-previous-page)
	  (when (/= cur-page (doc-view-current-page))
	    (image-eob)
	    (image-bol 1))
	  (set-window-hscroll (selected-window) hscroll)))
    (image-scroll-down arg)))

(defun doc-view-next-line-or-next-page (&optional arg)
  "Scroll upward by ARG lines if possible, else goto next page.
Juri Linkov's avatar
Juri Linkov committed
598
When `doc-view-continuous' is non-nil, scrolling a line upward
599
at the bottom edge of the page moves to the next page."
600
  (interactive "p")
Juri Linkov's avatar
Juri Linkov committed
601
  (if doc-view-continuous
602 603
      (let ((hscroll (window-hscroll))
	    (cur-page (doc-view-current-page)))
604
	(when (= (window-vscroll) (image-next-line arg))
605 606 607 608 609 610 611
	  (doc-view-next-page)
	  (when (/= cur-page (doc-view-current-page))
	    (image-bob)
	    (image-bol 1))
	  (set-window-hscroll (selected-window) hscroll)))
    (image-next-line 1)))

612 613
(defun doc-view-previous-line-or-previous-page (&optional arg)
  "Scroll downward by ARG lines if possible, else goto previous page.
Juri Linkov's avatar
Juri Linkov committed
614
When `doc-view-continuous' is non-nil, scrolling a line downward
615 616
at the top edge of the page moves to the previous page."
  (interactive "p")
Juri Linkov's avatar
Juri Linkov committed
617
  (if doc-view-continuous
618 619
      (let ((hscroll (window-hscroll))
	    (cur-page (doc-view-current-page)))
620
	(when (= (window-vscroll) (image-previous-line arg))
621 622 623 624 625
	  (doc-view-previous-page)
	  (when (/= cur-page (doc-view-current-page))
	    (image-eob)
	    (image-bol 1))
	  (set-window-hscroll (selected-window) hscroll)))
626
    (image-previous-line arg)))
627

628 629
;;;; Utility Functions

630
(defun doc-view-kill-proc ()
631
  "Kill the current converter process(es)."
632
  (interactive)
633
  (while (consp doc-view-current-converter-processes)
634 635
    (ignore-errors ;; Some entries might not be processes, and maybe
		   ;; some are dead already?
636
      (kill-process (pop doc-view-current-converter-processes))))
637 638 639 640 641
  (when doc-view-current-timer
    (cancel-timer doc-view-current-timer)
    (setq doc-view-current-timer nil))
  (setq mode-line-process nil))

Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
642 643 644
(defun doc-view-kill-proc-and-buffer ()
  "Kill the current converter process and buffer."
  (interactive)
645
  (doc-view-kill-proc)
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
646 647 648
  (when (eq major-mode 'doc-view-mode)
    (kill-buffer (current-buffer))))

649 650 651
(defun doc-view-make-safe-dir (dir)
  (condition-case nil
      (let ((umask (default-file-modes)))
652 653 654 655 656 657 658 659 660
	(unwind-protect
	    (progn
	      ;; Create temp files with strict access rights.  It's easy to
	      ;; loosen them later, whereas it's impossible to close the
	      ;; time-window of loose permissions otherwise.
	      (set-default-file-modes #o0700)
	      (make-directory dir))
	  ;; Reset the umask.
	  (set-default-file-modes umask)))
661
    (file-already-exists
662 663
     (when (file-symlink-p dir)
       (error "Danger: %s points to a symbolic link" dir))
664 665 666 667 668 669 670 671
     ;; In case it was created earlier with looser rights.
     ;; We could check the mode info returned by file-attributes, but it's
     ;; a pain to parse and it may not tell you what we want under
     ;; non-standard file-systems.  So let's just say what we want and let
     ;; the underlying C code and file-system figure it out.
     ;; This also ends up checking a bunch of useful conditions: it makes
     ;; sure we have write-access to the directory and that we own it, thus
     ;; closing a bunch of security holes.
672 673 674 675 676 677
     (condition-case error
	 (set-file-modes dir #o0700)
       (file-error
	(error
	 (format "Unable to use temporary directory %s: %s"
		 dir (mapconcat 'identity (cdr error) " "))))))))
678

679 680 681 682 683
(defun doc-view-current-cache-dir ()
  "Return the directory where the png files of the current doc should be saved.
It's a subdirectory of `doc-view-cache-directory'."
  (if doc-view-current-cache-dir
      doc-view-current-cache-dir
684 685 686
    ;; Try and make sure doc-view-cache-directory exists and is safe.
    (doc-view-make-safe-dir doc-view-cache-directory)
    ;; Now compute the subdirectory to use.
687 688
    (setq doc-view-current-cache-dir
	  (file-name-as-directory
689
	   (expand-file-name
690 691
	    (concat (subst-char-in-string ?% ?_ ;; bug#13679
                     (file-name-nondirectory doc-view-buffer-file-name))
692 693 694
		    "-"
		    (let ((file doc-view-buffer-file-name))
		      (with-temp-buffer
695
			(set-buffer-multibyte nil)
696 697
			(insert-file-contents-literally file)
			(md5 (current-buffer)))))
698
            doc-view-cache-directory)))))
699 700 701 702

(defun doc-view-remove-if (predicate list)
  "Return LIST with all items removed that satisfy PREDICATE."
  (let (new-list)
703
    (dolist (item list)
704
      (when (not (funcall predicate item))
705 706
	(setq new-list (cons item new-list))))
     (nreverse new-list)))
707

708 709
;;;###autoload
(defun doc-view-mode-p (type)
710 711 712
  "Return non-nil if document type TYPE is available for `doc-view'.
Document types are symbols like `dvi', `ps', `pdf', or `odf' (any
OpenDocument format)."
713
  (and (display-graphic-p)
714 715
       (or (image-type-available-p 'imagemagick)
	   (image-type-available-p 'png))
716 717 718
       (cond
	((eq type 'dvi)
	 (and (doc-view-mode-p 'pdf)
719 720 721 722
	      (or (and doc-view-dvipdf-program
		       (executable-find doc-view-dvipdf-program))
		  (and doc-view-dvipdfm-program
		       (executable-find doc-view-dvipdfm-program)))))
723
	((memq type '(postscript ps eps pdf))
724
	 ;; FIXME: allow mupdf here
725 726
	 (and doc-view-ghostscript-program
	      (executable-find doc-view-ghostscript-program)))
727
	((eq type 'odf)
728 729
	 (and doc-view-odf->pdf-converter-program
	      (executable-find doc-view-odf->pdf-converter-program)
730
	      (doc-view-mode-p 'pdf)))
731 732
	((eq type 'djvu)
	 (executable-find "ddjvu"))
733 734 735
	(t ;; unknown image type
	 nil))))

Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
736 737
;;;; Conversion Functions

738 739 740
(defvar doc-view-shrink-factor 1.125)

(defun doc-view-enlarge (factor)
741
  "Enlarge the document by FACTOR."
742
  (interactive (list doc-view-shrink-factor))
743 744 745
  (if (and doc-view-scale-internally
           (eq (plist-get (cdr (doc-view-current-image)) :type)
               'imagemagick))
746 747 748
      ;; ImageMagick supports on-the-fly-rescaling.
      (let ((new (ceiling (* factor doc-view-image-width))))
        (unless (equal new doc-view-image-width)
749
          (setq-local doc-view-image-width new)
750 751 752 753 754
          (doc-view-insert-image
           (plist-get (cdr (doc-view-current-image)) :file)
           :width doc-view-image-width)))
    (let ((new (ceiling (* factor doc-view-resolution))))
      (unless (equal new doc-view-resolution)
755
        (setq-local doc-view-resolution new)
756
        (doc-view-reconvert-doc)))))
757 758 759 760 761 762

(defun doc-view-shrink (factor)
  "Shrink the document."
  (interactive (list doc-view-shrink-factor))
  (doc-view-enlarge (/ 1.0 factor)))

763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
(defun doc-view-fit-width-to-window ()
  "Fit the image width to the window width."
  (interactive)
  (let ((win-width (- (nth 2 (window-inside-pixel-edges))
                      (nth 0 (window-inside-pixel-edges))))
        (slice (doc-view-current-slice)))
    (if (not slice)
        (let ((img-width (car (image-display-size
                               (image-get-display-property) t))))
          (doc-view-enlarge (/ (float win-width) (float img-width))))

      ;; If slice is set
      (let* ((slice-width (nth 2 slice))
             (scale-factor (/ (float win-width) (float slice-width)))
             (new-slice (mapcar (lambda (x) (ceiling (* scale-factor x))) slice)))

        (doc-view-enlarge scale-factor)
        (setf (doc-view-current-slice) new-slice)
        (doc-view-goto-page (doc-view-current-page))))))

(defun doc-view-fit-height-to-window ()
  "Fit the image height to the window height."
  (interactive)
  (let ((win-height (- (nth 3 (window-inside-pixel-edges))
                       (nth 1 (window-inside-pixel-edges))))
        (slice (doc-view-current-slice)))
    (if (not slice)
        (let ((img-height (cdr (image-display-size
                                (image-get-display-property) t))))
          ;; When users call 'doc-view-fit-height-to-window',
          ;; they might want to go to next page by typing SPC
          ;; ONLY once. So I used '(- win-height 1)' instead of
          ;; 'win-height'
          (doc-view-enlarge (/ (float (- win-height 1)) (float img-height))))

      ;; If slice is set
      (let* ((slice-height (nth 3 slice))
             (scale-factor (/ (float (- win-height 1)) (float slice-height)))
             (new-slice (mapcar (lambda (x) (ceiling (* scale-factor x))) slice)))

        (doc-view-enlarge scale-factor)
        (setf (doc-view-current-slice) new-slice)
        (doc-view-goto-page (doc-view-current-page))))))

(defun doc-view-fit-page-to-window ()
  "Fit the image to the window.
More specifically, this function enlarges image by:

min {(window-width / image-width), (window-height / image-height)} times."
  (interactive)
  (let ((win-width (- (nth 2 (window-inside-pixel-edges))
                      (nth 0 (window-inside-pixel-edges))))
        (win-height (- (nth 3 (window-inside-pixel-edges))
                       (nth 1 (window-inside-pixel-edges))))
        (slice (doc-view-current-slice)))
    (if (not slice)
        (let ((img-width (car (image-display-size
                               (image-get-display-property) t)))
              (img-height (cdr (image-display-size
                                (image-get-display-property) t))))
          (doc-view-enlarge (min (/ (float win-width) (float img-width))
824 825
                                 (/ (float (- win-height 1))
                                    (float img-height)))))
826 827 828 829
      ;; If slice is set
      (let* ((slice-width (nth 2 slice))
             (slice-height (nth 3 slice))
             (scale-factor (min (/ (float win-width) (float slice-width))
830 831
                                (/ (float (- win-height 1))
                                   (float slice-height))))
832 833 834 835 836
             (new-slice (mapcar (lambda (x) (ceiling (* scale-factor x))) slice)))
        (doc-view-enlarge scale-factor)
        (setf (doc-view-current-slice) new-slice)
        (doc-view-goto-page (doc-view-current-page))))))

837
(defun doc-view-reconvert-doc ()
838 839 840
  "Reconvert the current document.
Should be invoked when the cached images aren't up-to-date."
  (interactive)
841 842 843
  (doc-view-kill-proc)
  ;; Clear the old cached files
  (when (file-exists-p (doc-view-current-cache-dir))
844
    (delete-directory (doc-view-current-cache-dir) 'recursive))
845
  (kill-local-variable 'doc-view-last-page-number)
846
  (doc-view-initiate-display))
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
847

848 849
(defun doc-view-sentinel (proc event)
  "Generic sentinel for doc-view conversion processes."
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
850
  (if (not (string-match "finished" event))
851
      (message "DocView: process %s changed status to %s."
852 853 854 855
               (process-name proc)
	       (if (string-match "\\(.+\\)\n?\\'" event)
		   (match-string 1 event)
		 event))
856 857 858 859 860 861 862 863 864 865
    (when (buffer-live-p (process-get proc 'buffer))
      (with-current-buffer (process-get proc 'buffer)
        (setq doc-view-current-converter-processes
              (delq proc doc-view-current-converter-processes))
        (setq mode-line-process
              (if doc-view-current-converter-processes
                  (format ":%s" (car doc-view-current-converter-processes))))
        (funcall (process-get proc 'callback))))))

(defun doc-view-start-process (name program args callback)
866 867 868 869 870
  ;; Make sure the process is started in an existing directory, (rather than
  ;; some file-name-handler-managed dir, for example).
  (let* ((default-directory (if (file-readable-p default-directory)
				default-directory
			      (expand-file-name "~/")))
871 872 873 874 875 876 877
         (proc (apply 'start-process name doc-view-conversion-buffer
                      program args)))
    (push proc doc-view-current-converter-processes)
    (setq mode-line-process (list (format ":%s" proc)))
    (set-process-sentinel proc 'doc-view-sentinel)
    (process-put proc 'buffer   (current-buffer))
    (process-put proc 'callback callback)))
878 879 880

(defun doc-view-dvi->pdf (dvi pdf callback)
  "Convert DVI to PDF asynchronously and call CALLBACK when finished."
881 882 883 884 885
  ;; Prefer dvipdf over dvipdfm, because the latter has problems if the DVI
  ;; references and includes other PS files.
  (if (and doc-view-dvipdf-program
	   (executable-find doc-view-dvipdf-program))
      (doc-view-start-process "dvi->pdf" doc-view-dvipdf-program
886 887
			      (list dvi pdf)
			      callback)
888 889 890
    (doc-view-start-process "dvi->pdf" doc-view-dvipdfm-program
			    (list "-o" pdf dvi)
			    callback)))
891

892 893 894 895 896 897 898 899 900 901
(defun doc-view-pdf->png-converter-ghostscript (pdf png page callback)
  (doc-view-start-process
   "pdf/ps->png" doc-view-ghostscript-program
   `(,@doc-view-ghostscript-options
     ,(format "-r%d" (round doc-view-resolution))
     ,@(if page `(,(format "-dFirstPage=%d" page)))
     ,@(if page `(,(format "-dLastPage=%d" page)))
     ,(concat "-sOutputFile=" png)
     ,pdf)
   callback))
902 903 904 905

(defalias 'doc-view-ps->png-converter-ghostscript
  'doc-view-pdf->png-converter-ghostscript)

906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
(defun doc-view-djvu->tiff-converter-ddjvu (djvu tiff page callback)
  "Convert PAGE of a DJVU file to bitmap(s) asynchronously.
Call CALLBACK with no arguments when done.
If PAGE is nil, convert the whole document."
  (doc-view-start-process
   "djvu->tiff" "ddjvu"
   `("-format=tiff"
     ;; ddjvu only accepts the range 1-999.
     ,(format "-scale=%d" (round doc-view-resolution))
     ;; -eachpage was only added after djvulibre-3.5.25.3!
     ,@(unless page '("-eachpage"))
     ,@(if page `(,(format "-page=%d" page)))
     ,djvu
     ,tiff)
   callback))

(defun doc-view-pdf->png-converter-mupdf (pdf png page callback)
  (doc-view-start-process
   "pdf->png" doc-view-pdfdraw-program
   `(,(concat "-o" png)
     ,(format "-r%d" (round doc-view-resolution))
     ,pdf
     ,@(if page `(,(format "%d" page))))
   callback))
930

931
(defun doc-view-odf->pdf-converter-unoconv (odf callback)
932 933 934
  "Convert ODF to PDF asynchronously and call CALLBACK when finished.
The converted PDF is put into the current cache directory, and it
is named like ODF with the extension turned to pdf."
935
  (doc-view-start-process "odf->pdf" doc-view-odf->pdf-converter-program
936 937
			  (list "-f" "pdf" "-o" (doc-view-current-cache-dir) odf)
			  callback))
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
938

939 940 941 942
(defun doc-view-odf->pdf-converter-soffice (odf callback)
  "Convert ODF to PDF asynchronously and call CALLBACK when finished.
The converted PDF is put into the current cache directory, and it
is named like ODF with the extension turned to pdf."
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
  ;; FIXME: soffice doesn't work when there's another running
  ;; LibreOffice instance, in which case it returns success without
  ;; actually doing anything.  See LibreOffice bug
  ;; https://bugs.freedesktop.org/show_bug.cgi?id=37531.  A workaround
  ;; is to start soffice with a separate UserInstallation directory.
  (let ((tmp-user-install-dir (make-temp-file "libreoffice-docview" t)))
    (doc-view-start-process "odf->pdf" doc-view-odf->pdf-converter-program
			    (list
			     (concat "-env:UserInstallation=file://"
				     tmp-user-install-dir)
			     "--headless" "--convert-to" "pdf"
			     "--outdir" (doc-view-current-cache-dir) odf)
			    (lambda ()
			      (delete-directory tmp-user-install-dir t)
			      (funcall callback)))))
958

Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
959
(defun doc-view-pdf/ps->png (pdf-ps png)
960
  ;; FIXME: Fix name and docstring to account for djvu&tiff.
961
  "Convert PDF-PS to PNG asynchronously."
962 963 964 965 966 967
  (funcall
   (pcase doc-view-doc-type
     (`pdf doc-view-pdf->png-converter-function)
     (`djvu #'doc-view-djvu->tiff-converter-ddjvu)
     (_ #'doc-view-ps->png-converter-ghostscript))
   pdf-ps png nil
968
   (let ((resolution doc-view-resolution))
969 970 971 972 973 974 975 976 977 978
     (lambda ()
       ;; Only create the resolution file when it's all done, so it also
       ;; serves as a witness that the conversion is complete.
       (write-region (prin1-to-string resolution) nil
                     (expand-file-name "resolution.el"
                                       (doc-view-current-cache-dir))
                     nil 'silently)
       (when doc-view-current-timer
         (cancel-timer doc-view-current-timer)
         (setq doc-view-current-timer nil))
979 980
       (doc-view-display (current-buffer) 'force))))

981
  ;; Update the displayed pages as soon as they're done generating.
Thien-Thi Nguyen's avatar
Thien-Thi Nguyen committed
982 983
  (when doc-view-conversion-refresh-interval
    (setq doc-view-current-timer
984 985 986 987
          (run-at-time "1 secs" doc-view-conversion-refresh-interval
                       'doc-view-display
                       (current-buffer)))))

988 989
(declare-function clear-image-cache "image.c" (&optional filter))

990
(defun doc-view-document->bitmap (pdf png pages)
991
  "Convert a document file to bitmap images asynchronously.
992 993