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