2011-02-17 21:19:13 +00:00
|
|
|
|
;;; doc-view.el --- View PDF/PostScript/DVI files in Emacs -*- lexical-binding: t -*-
|
|
|
|
|
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2011-01-25 04:08:28 +00:00
|
|
|
|
;; Copyright (C) 2007-2011 Free Software Foundation, Inc.
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;;
|
|
|
|
|
;; Author: Tassilo Horn <tassilo@member.fsf.org>
|
|
|
|
|
;; Maintainer: Tassilo Horn <tassilo@member.fsf.org>
|
|
|
|
|
;; Keywords: files, pdf, ps, dvi
|
|
|
|
|
|
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
|
|
2008-05-06 08:06:51 +00:00
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
2008-05-06 08:06:51 +00:00
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
;; (at your option) any later version.
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
;; 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
|
2008-05-06 08:06:51 +00:00
|
|
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
;;; Requirements:
|
|
|
|
|
|
2007-10-17 20:58:02 +00:00
|
|
|
|
;; doc-view.el requires GNU Emacs 22.1 or newer. You also need Ghostscript,
|
2008-04-15 06:46:11 +00:00
|
|
|
|
;; `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/).
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
;;; 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.
|
|
|
|
|
;;
|
2007-10-17 21:34:19 +00:00
|
|
|
|
;; To use it simply open a document file with
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;;
|
2007-10-17 21:34:19 +00:00
|
|
|
|
;; C-x C-f ~/path/to/document RET
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;;
|
2007-10-17 21:34:19 +00:00
|
|
|
|
;; 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
|
2007-10-31 03:10:32 +00:00
|
|
|
|
;; representation and the source text representation of the document.
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;;
|
|
|
|
|
;; 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
|
2007-10-17 21:34:19 +00:00
|
|
|
|
;; that file again. To reconvert a document hit `g' (`doc-view-reconvert-doc')
|
|
|
|
|
;; when displaying the document. To delete all cached files use
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;; `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.
|
|
|
|
|
;;
|
|
|
|
|
;; 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
|
2009-03-02 08:55:47 +00:00
|
|
|
|
;; `doc-view-set-slice-using-mouse' (bound to `s m'). After invocation you
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;; only have to press mouse-1 at the top-left corner and drag it to the
|
|
|
|
|
;; bottom-right corner of the desired slice. To reset the slice use
|
|
|
|
|
;; `doc-view-reset-slice' (bound to `s r').
|
|
|
|
|
;;
|
|
|
|
|
;; 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
|
2007-11-10 21:53:47 +00:00
|
|
|
|
;; 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
|
2009-03-02 08:55:47 +00:00
|
|
|
|
;; that doesn't already exist the first invocation of `doc-view-search' (or
|
2007-11-10 21:53:47 +00:00
|
|
|
|
;; `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.
|
2007-10-17 21:34:19 +00:00
|
|
|
|
;;
|
|
|
|
|
;; 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'.
|
|
|
|
|
;;
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
;;; Configuration:
|
|
|
|
|
|
2007-10-17 21:34:19 +00:00
|
|
|
|
;; 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.)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;;
|
|
|
|
|
;; 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.
|
|
|
|
|
|
2007-11-11 22:24:04 +00:00
|
|
|
|
;;; Todo:
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2008-02-25 20:04:59 +00:00
|
|
|
|
;; - add print command.
|
|
|
|
|
;; - share more code with image-mode.
|
2007-10-30 17:45:41 +00:00
|
|
|
|
;; - better menu.
|
|
|
|
|
;; - Bind slicing to a drag event.
|
2008-03-12 14:50:36 +00:00
|
|
|
|
;; - doc-view-fit-doc-to-window and doc-view-fit-window-to-doc?
|
2008-01-18 20:09:26 +00:00
|
|
|
|
;; - zoom the region around the cursor (like xdvi).
|
2007-10-30 21:53:05 +00:00
|
|
|
|
;; - get rid of the silly arrow in the fringe.
|
|
|
|
|
;; - improve anti-aliasing (pdf-utils gets it better).
|
2007-10-30 17:45:41 +00:00
|
|
|
|
|
2007-11-11 22:24:04 +00:00
|
|
|
|
;;;; 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
|
|
|
|
|
;; isearch really searches for each step forward or backward wheras
|
|
|
|
|
;; 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:
|
|
|
|
|
|
2008-03-18 12:02:44 +00:00
|
|
|
|
(eval-when-compile (require 'cl))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(require 'dired)
|
2007-10-18 19:33:57 +00:00
|
|
|
|
(require 'image-mode)
|
2007-11-10 21:53:47 +00:00
|
|
|
|
(require 'jka-compr)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
;;;; Customization Options
|
|
|
|
|
|
|
|
|
|
(defgroup doc-view nil
|
|
|
|
|
"In-buffer viewer for PDF, PostScript and DVI files."
|
|
|
|
|
:link '(function-link doc-view)
|
|
|
|
|
:version "22.2"
|
|
|
|
|
:group 'applications
|
2010-02-16 14:35:45 +00:00
|
|
|
|
:group 'data
|
2007-09-09 22:29:16 +00:00
|
|
|
|
:group 'multimedia
|
|
|
|
|
:prefix "doc-view-")
|
|
|
|
|
|
2007-10-18 19:46:39 +00:00
|
|
|
|
(defcustom doc-view-ghostscript-program (executable-find "gs")
|
2007-09-09 22:29:16 +00:00
|
|
|
|
"Program to convert PS and PDF files to PNG."
|
2007-10-18 19:46:39 +00:00
|
|
|
|
:type 'file
|
2007-09-09 22:29:16 +00:00
|
|
|
|
:group 'doc-view)
|
|
|
|
|
|
|
|
|
|
(defcustom doc-view-ghostscript-options
|
2007-10-18 19:24:30 +00:00
|
|
|
|
'("-dSAFER" ;; Avoid security problems when rendering files from untrusted
|
2011-02-10 18:53:49 +00:00
|
|
|
|
;; sources.
|
2007-10-18 19:24:30 +00:00
|
|
|
|
"-dNOPAUSE" "-sDEVICE=png16m" "-dTextAlphaBits=4"
|
2007-11-15 19:56:44 +00:00
|
|
|
|
"-dBATCH" "-dGraphicsAlphaBits=4" "-dQUIET")
|
2007-10-17 20:58:02 +00:00
|
|
|
|
"A list of options to give to ghostscript."
|
2007-10-18 19:46:39 +00:00
|
|
|
|
:type '(repeat string)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
:group 'doc-view)
|
|
|
|
|
|
2007-11-15 19:56:44 +00:00
|
|
|
|
(defcustom doc-view-resolution 100
|
|
|
|
|
"Dots per inch resolution used to render the documents.
|
|
|
|
|
Higher values result in larger images."
|
* smime.el (from):
* rfc2047.el (message-posting-charset):
* qp.el (mm-use-ultra-safe-encoding):
* pop3.el (parse-time-months):
* nnrss.el (mm-text-html-renderer, mm-text-html-washer-alist):
* nnml.el (files):
* nnheader.el (gnus-newsgroup-name, nnheader-file-coding-system)
(jka-compr-compression-info-list, ange-ftp-path-format)
(efs-path-regexp):
* nndiary.el (files):
* mml2015.el (mc-default-scheme, mc-schemes, pgg-default-user-id)
(pgg-errors-buffer, pgg-output-buffer, epg-user-id-alist)
(epg-digest-algorithm-alist, inhibit-redisplay)
(password-cache-expiry):
* mml1991.el (pgg-default-user-id, pgg-errors-buffer)
(pgg-output-buffer, password-cache-expiry):
* mml.el (mml-dnd-protocol-alist, ange-ftp-name-format)
(efs-path-regexp):
* mml-smime.el (epg-user-id-alist, epg-digest-algorithm-alist)
(inhibit-redisplay):
* mm-uu.el (file-name, start-point, end-point, entry)
(gnus-newsgroup-name, gnus-newsgroup-charset):
* mm-util.el (mm-mime-mule-charset-alist, latin-unity-coding-systems)
(latin-unity-ucs-list):
* mm-bodies.el (mm-uu-yenc-decode-function, mm-uu-decode-function)
(mm-uu-binhex-decode-function):
* message.el (gnus-message-group-art, gnus-list-identifiers, )
(rmail-enable-mime-composing, gnus-local-organization)
(gnus-post-method, gnus-select-method, gnus-active-hashtb)
(gnus-read-active-file, facemenu-add-face-function)
(facemenu-remove-face-function, gnus-article-decoded-p)
(tool-bar-mode):
* mail-source.el (display-time-mail-function):
* gnus-util.el (nnmail-pathname-coding-system)
(nnmail-active-file-coding-system, gnus-emphasize-whitespace-regexp)
(gnus-original-article-buffer, gnus-user-agent)
(rmail-default-rmail-file, mm-text-coding-system, tool-bar-mode)
(xemacs-codename, sxemacs-codename, emacs-program-version):
* gnus-sum.el (tool-bar-mode, gnus-tmp-header, number):
* gnus-start.el (gnus-agent-covered-methods)
(gnus-agent-file-loading-local, gnus-agent-file-loading-cache)
(gnus-current-headers, gnus-thread-indent-array, gnus-newsgroup-name)
(gnus-newsgroup-headers, gnus-group-list-mode)
(gnus-group-mark-positions, gnus-newsgroup-data)
(gnus-newsgroup-unreads, nnoo-state-alist)
(gnus-current-select-method, mail-sources)
(nnmail-scan-directory-mail-source-once, nnmail-split-history)
(nnmail-spool-file, gnus-cache-active-hashtb):
* gnus-mh.el (mh-lib-progs):
* gnus-ems.el (gnus-tmp-unread, gnus-tmp-replied)
(gnus-tmp-score-char, gnus-tmp-indentation, gnus-tmp-opening-bracket)
(gnus-tmp-lines, gnus-tmp-name, gnus-tmp-closing-bracket)
(gnus-tmp-subject-or-nil, gnus-check-before-posting, gnus-mouse-face)
(gnus-group-buffer):
* gnus-cite.el (font-lock-defaults-computed, font-lock-keywords)
(font-lock-set-defaults):
* gnus-art.el (tool-bar-map, w3m-minor-mode-map)
(gnus-face-properties-alist, charset, gnus-summary-article-menu)
(gnus-summary-post-menu, total-parts, type, condition, length):
* gnus-agent.el (gnus-agent-read-agentview):
* flow-fill.el (show-trailing-whitespace):
* gnus-group.el (tool-bar-mode, nnrss-group-alist): Remove unnecessary
eval-and-compile wrappers for byte compiler pacifiers.
* mm-view.el (mm-inline-image-xemacs): Only do something for XEmacs.
(mm-display-inline-fontify): Check for featurep 'xemacs not
extent-list.
* mm-decode.el (mm-display-external): Check for featurep 'xemacs not
itimer-list.
(mm-create-image-xemacs): Only do something for XEmacs.
(mm-image-fit-p): Check for featurep 'xemacs not glyph-width.
* mm-util.el (mm-find-buffer-file-coding-system): Add check for XEmacs.
* gnus-registry.el (gnus-adaptive-word-syntax-table):
* gnus-fun.el (gnus-face-properties-alist): Pacify byte compiler.
* textmodes/reftex-dcr.el (reftex-start-itimer-once): Add check
for XEmacs.
* calc/calc-menu.el (calc-mode-map): Pacify byte compiler.
* doc-view.el (doc-view-resolution): Add missing :group.
2007-11-16 16:50:35 +00:00
|
|
|
|
:type 'number
|
|
|
|
|
:group 'doc-view)
|
2007-11-15 19:56:44 +00:00
|
|
|
|
|
2011-01-05 21:17:51 +00:00
|
|
|
|
(defcustom doc-view-image-width 850
|
|
|
|
|
"Default image width.
|
|
|
|
|
Has only an effect if imagemagick support is compiled into emacs."
|
|
|
|
|
:type 'number
|
|
|
|
|
:group 'doc-view)
|
|
|
|
|
|
2007-10-18 19:46:39 +00:00
|
|
|
|
(defcustom doc-view-dvipdfm-program (executable-find "dvipdfm")
|
2007-09-09 22:29:16 +00:00
|
|
|
|
"Program to convert DVI files to PDF.
|
|
|
|
|
|
|
|
|
|
DVI file will be converted to PDF before the resulting PDF is
|
2008-04-14 13:18:12 +00:00
|
|
|
|
converted to PNG.
|
|
|
|
|
|
|
|
|
|
If this and `doc-view-dvipdf-program' are set,
|
|
|
|
|
`doc-view-dvipdf-program' will be preferred."
|
|
|
|
|
:type 'file
|
|
|
|
|
:group 'doc-view)
|
|
|
|
|
|
|
|
|
|
(defcustom doc-view-dvipdf-program (executable-find "dvipdf")
|
|
|
|
|
"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."
|
2007-10-18 19:46:39 +00:00
|
|
|
|
:type 'file
|
2007-09-09 22:29:16 +00:00
|
|
|
|
:group 'doc-view)
|
|
|
|
|
|
2010-12-30 13:45:09 +00:00
|
|
|
|
(defcustom doc-view-unoconv-program (executable-find "unoconv")
|
|
|
|
|
"Program to convert any file type readable by OpenOffice.org to PDF.
|
|
|
|
|
|
|
|
|
|
Needed for viewing OpenOffice.org (and MS Office) files."
|
|
|
|
|
:type 'file
|
|
|
|
|
:group 'doc-view)
|
|
|
|
|
|
2007-10-18 19:46:39 +00:00
|
|
|
|
(defcustom doc-view-ps2pdf-program (executable-find "ps2pdf")
|
2007-09-09 22:29:16 +00:00
|
|
|
|
"Program to convert PS files to PDF.
|
|
|
|
|
|
|
|
|
|
PS files will be converted to PDF before searching is possible."
|
2007-10-18 19:46:39 +00:00
|
|
|
|
:type 'file
|
2007-09-09 22:29:16 +00:00
|
|
|
|
:group 'doc-view)
|
|
|
|
|
|
2007-10-18 19:46:39 +00:00
|
|
|
|
(defcustom doc-view-pdftotext-program (executable-find "pdftotext")
|
2007-09-09 22:29:16 +00:00
|
|
|
|
"Program to convert PDF files to plain text.
|
|
|
|
|
|
|
|
|
|
Needed for searching."
|
2007-10-18 19:46:39 +00:00
|
|
|
|
:type 'file
|
2007-09-09 22:29:16 +00:00
|
|
|
|
:group 'doc-view)
|
|
|
|
|
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(defcustom doc-view-cache-directory
|
2007-11-01 03:53:44 +00:00
|
|
|
|
(expand-file-name (format "docview%d" (user-uid))
|
2007-10-31 21:17:32 +00:00
|
|
|
|
temporary-file-directory)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
"The base directory, where the PNG images will be saved."
|
2007-10-18 19:46:39 +00:00
|
|
|
|
:type 'directory
|
2007-09-09 22:29:16 +00:00
|
|
|
|
:group 'doc-view)
|
|
|
|
|
|
2008-02-25 20:04:59 +00:00
|
|
|
|
(defvar doc-view-conversion-buffer " *doc-view conversion output*"
|
|
|
|
|
"The buffer where messages from the converter programs go to.")
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2008-02-25 20:04:59 +00:00
|
|
|
|
(defcustom doc-view-conversion-refresh-interval 1
|
2007-10-31 03:10:32 +00:00
|
|
|
|
"Interval in seconds between refreshes of the DocView buffer while converting.
|
|
|
|
|
After such a refresh newly converted pages will be available for
|
2007-09-09 22:29:16 +00:00
|
|
|
|
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."
|
2007-10-18 19:46:39 +00:00
|
|
|
|
:type 'integer
|
2007-09-09 22:29:16 +00:00
|
|
|
|
:group 'doc-view)
|
|
|
|
|
|
2009-11-30 16:14:48 +00:00
|
|
|
|
(defcustom doc-view-continuous nil
|
2009-11-23 20:34:53 +00:00
|
|
|
|
"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")
|
|
|
|
|
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;;;; Internal Variables
|
|
|
|
|
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(defun doc-view-new-window-function (winprops)
|
|
|
|
|
(let ((ol (image-mode-window-get 'overlay winprops)))
|
2010-02-01 18:25:47 +00:00
|
|
|
|
(when (and ol (not (overlay-buffer ol)))
|
|
|
|
|
;; I've seen `ol' be a dead overlay. I do not yet know how this
|
|
|
|
|
;; happened, so maybe the bug is elsewhere, but in the mean time,
|
|
|
|
|
;; this seems like a safe approach.
|
|
|
|
|
(setq ol nil))
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(if ol
|
2010-02-01 18:25:47 +00:00
|
|
|
|
(progn
|
|
|
|
|
(assert (eq (overlay-buffer ol) (current-buffer)))
|
|
|
|
|
(setq ol (copy-overlay ol)))
|
2008-04-28 23:49:02 +00:00
|
|
|
|
(assert (not (get-char-property (point-min) 'display)))
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(setq ol (make-overlay (point-min) (point-max) nil t))
|
|
|
|
|
(overlay-put ol 'doc-view t))
|
|
|
|
|
(overlay-put ol 'window (car winprops))
|
|
|
|
|
(image-mode-window-put 'overlay ol winprops)))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(defvar doc-view-current-files nil
|
2007-09-09 22:29:16 +00:00
|
|
|
|
"Only used internally.")
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(make-variable-buffer-local 'doc-view-current-files)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(defvar doc-view-current-converter-processes nil
|
2007-09-09 22:29:16 +00:00
|
|
|
|
"Only used internally.")
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(make-variable-buffer-local 'doc-view-current-converter-processes)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defvar doc-view-current-timer nil
|
|
|
|
|
"Only used internally.")
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(make-variable-buffer-local 'doc-view-current-timer)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defvar doc-view-current-cache-dir nil
|
|
|
|
|
"Only used internally.")
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(make-variable-buffer-local 'doc-view-current-cache-dir)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defvar doc-view-current-search-matches nil
|
|
|
|
|
"Only used internally.")
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(make-variable-buffer-local 'doc-view-current-search-matches)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2007-12-03 07:24:34 +00:00
|
|
|
|
(defvar doc-view-pending-cache-flush nil
|
|
|
|
|
"Only used internally.")
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(defvar doc-view-previous-major-mode nil
|
2007-10-17 21:34:19 +00:00
|
|
|
|
"Only used internally.")
|
|
|
|
|
|
2008-01-10 15:08:34 +00:00
|
|
|
|
(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'.")
|
|
|
|
|
|
2008-03-12 14:50:36 +00:00
|
|
|
|
(defvar doc-view-doc-type nil
|
|
|
|
|
"The type of document in the current buffer.
|
|
|
|
|
Can be `dvi', `pdf', or `ps'.")
|
|
|
|
|
|
2007-10-17 21:34:19 +00:00
|
|
|
|
;;;; DocView Keymaps
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defvar doc-view-mode-map
|
|
|
|
|
(let ((map (make-sparse-keymap)))
|
2008-06-03 07:42:03 +00:00
|
|
|
|
(set-keymap-parent map image-mode-map)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;; Navigation in the document
|
|
|
|
|
(define-key map (kbd "n") 'doc-view-next-page)
|
|
|
|
|
(define-key map (kbd "p") 'doc-view-previous-page)
|
2007-10-19 16:03:09 +00:00
|
|
|
|
(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)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(define-key map (kbd "SPC") 'doc-view-scroll-up-or-next-page)
|
|
|
|
|
(define-key map (kbd "DEL") 'doc-view-scroll-down-or-previous-page)
|
2009-11-23 20:34:53 +00:00
|
|
|
|
(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)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(define-key map (kbd "M-<") 'doc-view-first-page)
|
|
|
|
|
(define-key map (kbd "M->") 'doc-view-last-page)
|
2007-10-31 20:40:30 +00:00
|
|
|
|
(define-key map [remap goto-line] 'doc-view-goto-page)
|
2009-03-13 21:03:21 +00:00
|
|
|
|
(define-key map (kbd "RET") 'image-next-line)
|
2007-11-15 19:56:44 +00:00
|
|
|
|
;; Zoom in/out.
|
|
|
|
|
(define-key map "+" 'doc-view-enlarge)
|
|
|
|
|
(define-key map "-" 'doc-view-shrink)
|
2011-04-08 20:09:19 +00:00
|
|
|
|
;; 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)
|
2008-06-03 07:42:03 +00:00
|
|
|
|
;; Killing the buffer (and the process)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(define-key map (kbd "k") 'doc-view-kill-proc-and-buffer)
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(define-key map (kbd "K") 'doc-view-kill-proc)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;; 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)
|
|
|
|
|
(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)
|
2007-11-10 21:53:47 +00:00
|
|
|
|
(define-key map (kbd "C-r") 'doc-view-search-backward)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;; Show the tooltip
|
|
|
|
|
(define-key map (kbd "C-t") 'doc-view-show-tooltip)
|
2007-10-17 21:34:19 +00:00
|
|
|
|
;; Toggle between text and image display or editing
|
|
|
|
|
(define-key map (kbd "C-c C-c") 'doc-view-toggle-display)
|
2008-03-12 12:05:30 +00:00
|
|
|
|
;; Open a new buffer with doc's text contents
|
|
|
|
|
(define-key map (kbd "C-c C-t") 'doc-view-open-text)
|
2010-02-01 18:25:47 +00:00
|
|
|
|
;; 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)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
map)
|
2007-10-17 21:34:19 +00:00
|
|
|
|
"Keymap used by `doc-view-mode' when displaying a doc as a set of images.")
|
|
|
|
|
|
2010-02-01 18:25:47 +00:00
|
|
|
|
(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))
|
|
|
|
|
|
|
|
|
|
|
2007-10-30 17:45:41 +00:00
|
|
|
|
(easy-menu-define doc-view-menu doc-view-mode-map
|
|
|
|
|
"Menu for Doc View mode."
|
|
|
|
|
'("DocView"
|
2009-11-30 16:14:48 +00:00
|
|
|
|
["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]
|
|
|
|
|
)
|
|
|
|
|
"---"
|
2007-10-30 17:45:41 +00:00
|
|
|
|
["Set Slice" doc-view-set-slice-using-mouse]
|
|
|
|
|
["Set Slice (manual)" doc-view-set-slice]
|
|
|
|
|
["Reset Slice" doc-view-reset-slice]
|
|
|
|
|
"---"
|
|
|
|
|
["Search" doc-view-search]
|
2007-11-10 21:53:47 +00:00
|
|
|
|
["Search Backwards" doc-view-search-backward]
|
2007-10-30 17:45:41 +00:00
|
|
|
|
))
|
|
|
|
|
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(defvar doc-view-minor-mode-map
|
2007-10-17 21:34:19 +00:00
|
|
|
|
(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)
|
2007-10-30 06:29:51 +00:00
|
|
|
|
"Keymap used by `doc-minor-view-mode'.")
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
;;;; Navigation Commands
|
|
|
|
|
|
2008-02-26 02:06:33 +00:00
|
|
|
|
(defmacro doc-view-current-page (&optional win)
|
|
|
|
|
`(image-mode-window-get 'page ,win))
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(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))
|
|
|
|
|
|
2010-02-05 01:53:40 +00:00
|
|
|
|
(defun doc-view-last-page-number ()
|
|
|
|
|
(length doc-view-current-files))
|
|
|
|
|
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(defun doc-view-goto-page (page)
|
|
|
|
|
"View the page given by PAGE."
|
|
|
|
|
(interactive "nPage: ")
|
2010-02-05 01:53:40 +00:00
|
|
|
|
(let ((len (doc-view-last-page-number))
|
2009-02-09 15:33:47 +00:00
|
|
|
|
(hscroll (window-hscroll)))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(if (< page 1)
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(setq page 1)
|
2008-02-25 20:04:59 +00:00
|
|
|
|
(when (and (> page len)
|
|
|
|
|
;; As long as the converter is running, we don't know
|
|
|
|
|
;; how many pages will be available.
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(null doc-view-current-converter-processes))
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(setq page len)))
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(setf (doc-view-current-page) page
|
|
|
|
|
(doc-view-current-info)
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(concat
|
|
|
|
|
(propertize
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(format "Page %d of %d." page len) 'face 'bold)
|
2007-10-03 23:28:19 +00:00
|
|
|
|
;; Tell user if converting isn't finished yet
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(if doc-view-current-converter-processes
|
2007-10-03 23:28:19 +00:00
|
|
|
|
" (still converting...)\n"
|
|
|
|
|
"\n")
|
|
|
|
|
;; Display context infos if this page matches the last search
|
|
|
|
|
(when (and doc-view-current-search-matches
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(assq page doc-view-current-search-matches))
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(concat (propertize "Search matches:\n" 'face 'bold)
|
|
|
|
|
(let ((contexts ""))
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(dolist (m (cdr (assq page
|
2007-10-03 23:28:19 +00:00
|
|
|
|
doc-view-current-search-matches)))
|
|
|
|
|
(setq contexts (concat contexts " - \"" m "\"\n")))
|
|
|
|
|
contexts)))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;; Update the buffer
|
2008-02-25 20:04:59 +00:00
|
|
|
|
;; 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.
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(let ((file (expand-file-name (format "page-%d.png" page)
|
|
|
|
|
(doc-view-current-cache-dir))))
|
|
|
|
|
(doc-view-insert-image file :pointer 'arrow)
|
2009-02-09 15:33:47 +00:00
|
|
|
|
(set-window-hscroll (selected-window) hscroll)
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(when (and (not (file-exists-p file))
|
|
|
|
|
doc-view-current-converter-processes)
|
|
|
|
|
;; The PNG file hasn't been generated yet.
|
|
|
|
|
(doc-view-pdf->png-1 doc-view-buffer-file-name file page
|
2011-02-10 18:53:49 +00:00
|
|
|
|
(let ((win (selected-window)))
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(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)
|
2010-02-05 01:53:40 +00:00
|
|
|
|
;; Make sure we don't infloop.
|
|
|
|
|
(file-readable-p file)
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(with-selected-window win
|
2011-02-10 18:53:49 +00:00
|
|
|
|
(doc-view-goto-page page))))))))
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(overlay-put (doc-view-current-overlay)
|
|
|
|
|
'help-echo (doc-view-current-info))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-next-page (&optional arg)
|
|
|
|
|
"Browse ARG pages forward."
|
|
|
|
|
(interactive "p")
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(doc-view-goto-page (+ (doc-view-current-page) (or arg 1))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-previous-page (&optional arg)
|
|
|
|
|
"Browse ARG pages backward."
|
|
|
|
|
(interactive "p")
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(doc-view-goto-page (- (doc-view-current-page) (or arg 1))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(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)
|
2010-02-05 01:53:40 +00:00
|
|
|
|
(doc-view-goto-page (doc-view-last-page-number)))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2009-11-25 17:18:26 +00:00
|
|
|
|
(defun doc-view-scroll-up-or-next-page (&optional arg)
|
|
|
|
|
"Scroll page up ARG lines if possible, else goto next page.
|
2009-11-30 16:14:48 +00:00
|
|
|
|
When `doc-view-continuous' is non-nil, scrolling upward
|
2009-11-25 17:18:26 +00:00
|
|
|
|
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")
|
2009-11-30 16:14:48 +00:00
|
|
|
|
(if (or doc-view-continuous (null arg))
|
2009-11-25 17:18:26 +00:00
|
|
|
|
(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.
|
2009-11-30 16:14:48 +00:00
|
|
|
|
When `doc-view-continuous' is non-nil, scrolling downward
|
2009-11-25 17:18:26 +00:00
|
|
|
|
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")
|
2009-11-30 16:14:48 +00:00
|
|
|
|
(if (or doc-view-continuous (null arg))
|
2009-11-25 17:18:26 +00:00
|
|
|
|
(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.
|
2009-11-30 16:14:48 +00:00
|
|
|
|
When `doc-view-continuous' is non-nil, scrolling a line upward
|
2009-11-25 17:18:26 +00:00
|
|
|
|
at the bottom edge of the page moves to the next page."
|
2009-11-23 20:34:53 +00:00
|
|
|
|
(interactive "p")
|
2009-11-30 16:14:48 +00:00
|
|
|
|
(if doc-view-continuous
|
2009-11-23 20:34:53 +00:00
|
|
|
|
(let ((hscroll (window-hscroll))
|
|
|
|
|
(cur-page (doc-view-current-page)))
|
2009-11-25 17:18:26 +00:00
|
|
|
|
(when (= (window-vscroll) (image-next-line arg))
|
2009-11-23 20:34:53 +00:00
|
|
|
|
(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)))
|
|
|
|
|
|
2009-11-25 17:18:26 +00:00
|
|
|
|
(defun doc-view-previous-line-or-previous-page (&optional arg)
|
|
|
|
|
"Scroll downward by ARG lines if possible, else goto previous page.
|
2009-11-30 16:14:48 +00:00
|
|
|
|
When `doc-view-continuous' is non-nil, scrolling a line downward
|
2009-11-23 20:34:53 +00:00
|
|
|
|
at the top edge of the page moves to the previous page."
|
|
|
|
|
(interactive "p")
|
2009-11-30 16:14:48 +00:00
|
|
|
|
(if doc-view-continuous
|
2009-11-23 20:34:53 +00:00
|
|
|
|
(let ((hscroll (window-hscroll))
|
|
|
|
|
(cur-page (doc-view-current-page)))
|
2009-11-25 17:18:26 +00:00
|
|
|
|
(when (= (window-vscroll) (image-previous-line arg))
|
2009-11-23 20:34:53 +00:00
|
|
|
|
(doc-view-previous-page)
|
|
|
|
|
(when (/= cur-page (doc-view-current-page))
|
|
|
|
|
(image-eob)
|
|
|
|
|
(image-bol 1))
|
|
|
|
|
(set-window-hscroll (selected-window) hscroll)))
|
2009-11-25 17:18:26 +00:00
|
|
|
|
(image-previous-line arg)))
|
2009-11-23 20:34:53 +00:00
|
|
|
|
|
2007-10-30 06:29:51 +00:00
|
|
|
|
;;;; Utility Functions
|
|
|
|
|
|
2007-10-17 21:34:19 +00:00
|
|
|
|
(defun doc-view-kill-proc ()
|
2008-03-31 15:14:16 +00:00
|
|
|
|
"Kill the current converter process(es)."
|
2007-10-17 21:34:19 +00:00
|
|
|
|
(interactive)
|
2010-02-05 01:53:40 +00:00
|
|
|
|
(while (consp doc-view-current-converter-processes)
|
2008-02-25 20:04:59 +00:00
|
|
|
|
(ignore-errors ;; Maybe it's dead already?
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(kill-process (pop doc-view-current-converter-processes))))
|
2007-10-17 21:34:19 +00:00
|
|
|
|
(when doc-view-current-timer
|
|
|
|
|
(cancel-timer doc-view-current-timer)
|
|
|
|
|
(setq doc-view-current-timer nil))
|
|
|
|
|
(setq mode-line-process nil))
|
|
|
|
|
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(defun doc-view-kill-proc-and-buffer ()
|
|
|
|
|
"Kill the current converter process and buffer."
|
|
|
|
|
(interactive)
|
2007-10-17 21:34:19 +00:00
|
|
|
|
(doc-view-kill-proc)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(when (eq major-mode 'doc-view-mode)
|
|
|
|
|
(kill-buffer (current-buffer))))
|
|
|
|
|
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(defun doc-view-make-safe-dir (dir)
|
|
|
|
|
(condition-case nil
|
|
|
|
|
(let ((umask (default-file-modes)))
|
|
|
|
|
(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)))
|
|
|
|
|
(file-already-exists
|
|
|
|
|
(if (file-symlink-p dir)
|
|
|
|
|
(error "Danger: %s points to a symbolic link" dir))
|
|
|
|
|
;; 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.
|
|
|
|
|
(set-file-modes dir #o0700))))
|
|
|
|
|
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(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
|
2007-10-30 21:53:05 +00:00
|
|
|
|
;; 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.
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(setq doc-view-current-cache-dir
|
|
|
|
|
(file-name-as-directory
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(expand-file-name
|
2009-12-07 02:37:42 +00:00
|
|
|
|
(concat (file-name-nondirectory doc-view-buffer-file-name)
|
2008-01-10 15:08:34 +00:00
|
|
|
|
"-"
|
|
|
|
|
(let ((file doc-view-buffer-file-name))
|
|
|
|
|
(with-temp-buffer
|
2008-03-12 14:59:10 +00:00
|
|
|
|
(set-buffer-multibyte nil)
|
2008-01-10 15:08:34 +00:00
|
|
|
|
(insert-file-contents-literally file)
|
|
|
|
|
(md5 (current-buffer)))))
|
2007-10-30 21:53:05 +00:00
|
|
|
|
doc-view-cache-directory)))))
|
2007-10-30 06:29:51 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-remove-if (predicate list)
|
|
|
|
|
"Return LIST with all items removed that satisfy PREDICATE."
|
|
|
|
|
(let (new-list)
|
2011-06-05 00:18:46 +00:00
|
|
|
|
(dolist (item list)
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(when (not (funcall predicate item))
|
2011-06-05 00:18:46 +00:00
|
|
|
|
(setq new-list (cons item new-list))))
|
|
|
|
|
(nreverse new-list)))
|
2007-10-30 06:29:51 +00:00
|
|
|
|
|
2007-11-25 20:11:47 +00:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun doc-view-mode-p (type)
|
2010-12-30 13:45:09 +00:00
|
|
|
|
"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)."
|
2007-11-25 20:11:47 +00:00
|
|
|
|
(and (display-graphic-p)
|
2011-01-08 22:57:07 +00:00
|
|
|
|
(or (image-type-available-p 'imagemagick)
|
|
|
|
|
(image-type-available-p 'png))
|
2007-11-25 20:11:47 +00:00
|
|
|
|
(cond
|
|
|
|
|
((eq type 'dvi)
|
|
|
|
|
(and (doc-view-mode-p 'pdf)
|
2008-04-14 13:18:12 +00:00
|
|
|
|
(or (and doc-view-dvipdf-program
|
|
|
|
|
(executable-find doc-view-dvipdf-program))
|
|
|
|
|
(and doc-view-dvipdfm-program
|
|
|
|
|
(executable-find doc-view-dvipdfm-program)))))
|
2008-01-09 10:25:17 +00:00
|
|
|
|
((or (eq type 'postscript) (eq type 'ps) (eq type 'eps)
|
2007-11-25 20:11:47 +00:00
|
|
|
|
(eq type 'pdf))
|
|
|
|
|
(and doc-view-ghostscript-program
|
|
|
|
|
(executable-find doc-view-ghostscript-program)))
|
2010-12-30 13:45:09 +00:00
|
|
|
|
((eq type 'odf)
|
|
|
|
|
(and doc-view-unoconv-program
|
|
|
|
|
(executable-find doc-view-unoconv-program)
|
|
|
|
|
(doc-view-mode-p 'pdf)))
|
2007-11-25 20:11:47 +00:00
|
|
|
|
(t ;; unknown image type
|
|
|
|
|
nil))))
|
|
|
|
|
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;;;; Conversion Functions
|
|
|
|
|
|
2007-11-15 19:56:44 +00:00
|
|
|
|
(defvar doc-view-shrink-factor 1.125)
|
|
|
|
|
|
|
|
|
|
(defun doc-view-enlarge (factor)
|
|
|
|
|
"Enlarge the document."
|
|
|
|
|
(interactive (list doc-view-shrink-factor))
|
2011-01-05 21:17:51 +00:00
|
|
|
|
(if (eq (plist-get (cdr (doc-view-current-image)) :type)
|
|
|
|
|
'imagemagick)
|
|
|
|
|
;; ImageMagick supports on-the-fly-rescaling
|
|
|
|
|
(progn
|
|
|
|
|
(set (make-local-variable 'doc-view-image-width)
|
|
|
|
|
(ceiling (* factor doc-view-image-width)))
|
|
|
|
|
(doc-view-insert-image (plist-get (cdr (doc-view-current-image)) :file)
|
2011-01-05 21:54:23 +00:00
|
|
|
|
:width doc-view-image-width))
|
2011-01-05 21:17:51 +00:00
|
|
|
|
(set (make-local-variable 'doc-view-resolution)
|
|
|
|
|
(ceiling (* factor doc-view-resolution)))
|
|
|
|
|
(doc-view-reconvert-doc)))
|
2007-11-15 19:56:44 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-shrink (factor)
|
|
|
|
|
"Shrink the document."
|
|
|
|
|
(interactive (list doc-view-shrink-factor))
|
|
|
|
|
(doc-view-enlarge (/ 1.0 factor)))
|
|
|
|
|
|
2011-04-08 20:09:19 +00:00
|
|
|
|
(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))
|
|
|
|
|
(/ (float (- win-height 1)) (float img-height)))))
|
|
|
|
|
;; If slice is set
|
|
|
|
|
(let* ((slice-width (nth 2 slice))
|
|
|
|
|
(slice-height (nth 3 slice))
|
|
|
|
|
(scale-factor (min (/ (float win-width) (float slice-width))
|
|
|
|
|
(/ (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))))))
|
|
|
|
|
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(defun doc-view-reconvert-doc ()
|
2007-10-17 21:34:19 +00:00
|
|
|
|
"Reconvert the current document.
|
|
|
|
|
Should be invoked when the cached images aren't up-to-date."
|
|
|
|
|
(interactive)
|
2007-10-30 17:45:41 +00:00
|
|
|
|
(doc-view-kill-proc)
|
|
|
|
|
;; Clear the old cached files
|
|
|
|
|
(when (file-exists-p (doc-view-current-cache-dir))
|
2010-02-05 01:53:40 +00:00
|
|
|
|
(delete-directory (doc-view-current-cache-dir) 'recursive))
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(doc-view-initiate-display))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2008-03-12 15:26:49 +00:00
|
|
|
|
(defun doc-view-sentinel (proc event)
|
|
|
|
|
"Generic sentinel for doc-view conversion processes."
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(if (not (string-match "finished" event))
|
2008-03-12 15:26:49 +00:00
|
|
|
|
(message "DocView: process %s changed status to %s."
|
2009-03-14 02:12:06 +00:00
|
|
|
|
(process-name proc)
|
|
|
|
|
(if (string-match "\\(.+\\)\n?\\'" event)
|
|
|
|
|
(match-string 1 event)
|
|
|
|
|
event))
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(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)
|
2008-04-14 11:02:31 +00:00
|
|
|
|
;; 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 "~/")))
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(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)))
|
2008-03-12 15:26:49 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-dvi->pdf (dvi pdf callback)
|
|
|
|
|
"Convert DVI to PDF asynchronously and call CALLBACK when finished."
|
2008-04-14 13:18:12 +00:00
|
|
|
|
;; 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
|
2011-02-10 18:53:49 +00:00
|
|
|
|
(list dvi pdf)
|
|
|
|
|
callback)
|
2008-04-14 13:18:12 +00:00
|
|
|
|
(doc-view-start-process "dvi->pdf" doc-view-dvipdfm-program
|
|
|
|
|
(list "-o" pdf dvi)
|
|
|
|
|
callback)))
|
2008-03-31 15:14:16 +00:00
|
|
|
|
|
2010-12-30 13:45:09 +00:00
|
|
|
|
(defun doc-view-odf->pdf (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."
|
|
|
|
|
(doc-view-start-process "odf->pdf" doc-view-unoconv-program
|
|
|
|
|
(list "-f" "pdf" "-o" (doc-view-current-cache-dir) odf)
|
|
|
|
|
callback))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-pdf/ps->png (pdf-ps png)
|
2007-10-31 03:10:32 +00:00
|
|
|
|
"Convert PDF-PS to PNG asynchronously."
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(doc-view-start-process
|
|
|
|
|
"pdf/ps->png" doc-view-ghostscript-program
|
|
|
|
|
(append doc-view-ghostscript-options
|
|
|
|
|
(list (format "-r%d" (round doc-view-resolution))
|
|
|
|
|
(concat "-sOutputFile=" png)
|
|
|
|
|
pdf-ps))
|
2011-02-10 18:53:49 +00:00
|
|
|
|
(let ((resolution doc-view-resolution))
|
2010-02-05 01:53:40 +00:00
|
|
|
|
(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))
|
|
|
|
|
(doc-view-display (current-buffer) 'force))))
|
2008-03-31 15:14:16 +00:00
|
|
|
|
;; Update the displayed pages as soon as they're done generating.
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(when doc-view-conversion-refresh-interval
|
|
|
|
|
(setq doc-view-current-timer
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(run-at-time "1 secs" doc-view-conversion-refresh-interval
|
|
|
|
|
'doc-view-display
|
|
|
|
|
(current-buffer)))))
|
|
|
|
|
|
|
|
|
|
(defun doc-view-pdf->png-1 (pdf png page callback)
|
|
|
|
|
"Convert a PAGE of a PDF file to PNG asynchronously.
|
|
|
|
|
Call CALLBACK with no arguments when done."
|
|
|
|
|
(doc-view-start-process
|
|
|
|
|
"pdf->png-1" doc-view-ghostscript-program
|
|
|
|
|
(append doc-view-ghostscript-options
|
|
|
|
|
(list (format "-r%d" (round doc-view-resolution))
|
|
|
|
|
;; Sadly, `gs' only supports the page-range
|
|
|
|
|
;; for PDF files.
|
|
|
|
|
(format "-dFirstPage=%d" page)
|
|
|
|
|
(format "-dLastPage=%d" page)
|
|
|
|
|
(concat "-sOutputFile=" png)
|
|
|
|
|
pdf))
|
|
|
|
|
callback))
|
|
|
|
|
|
2008-06-12 03:56:20 +00:00
|
|
|
|
(declare-function clear-image-cache "image.c" (&optional filter))
|
|
|
|
|
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(defun doc-view-pdf->png (pdf png pages)
|
|
|
|
|
"Convert a PDF file to PNG asynchronously.
|
|
|
|
|
Start by converting PAGES, and then the rest."
|
|
|
|
|
(if (null pages)
|
|
|
|
|
(doc-view-pdf/ps->png pdf png)
|
|
|
|
|
;; We could render several `pages' with a single process if they're
|
|
|
|
|
;; (almost) consecutive, but since in 99% of the cases, there'll be only
|
|
|
|
|
;; a single page anyway, and of the remaining 1%, few cases will have
|
|
|
|
|
;; consecutive pages, it's not worth the trouble.
|
2011-02-10 18:53:49 +00:00
|
|
|
|
(let ((rest (cdr pages)))
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(doc-view-pdf->png-1
|
|
|
|
|
pdf (format png (car pages)) (car pages)
|
|
|
|
|
(lambda ()
|
|
|
|
|
(if rest
|
|
|
|
|
(doc-view-pdf->png pdf png rest)
|
|
|
|
|
;; Yippie, the important pages are done, update the display.
|
|
|
|
|
(clear-image-cache)
|
2010-02-05 01:53:40 +00:00
|
|
|
|
;; For the windows that have a message (like "Welcome to
|
|
|
|
|
;; DocView") display property, clearing the image cache is
|
|
|
|
|
;; not sufficient.
|
|
|
|
|
(dolist (win (get-buffer-window-list (current-buffer) nil 'visible))
|
|
|
|
|
(with-selected-window win
|
2011-02-10 18:53:49 +00:00
|
|
|
|
(when (stringp (get-char-property (point-min) 'display))
|
|
|
|
|
(doc-view-goto-page (doc-view-current-page)))))
|
2008-03-31 15:14:16 +00:00
|
|
|
|
;; Convert the rest of the pages.
|
|
|
|
|
(doc-view-pdf/ps->png pdf png)))))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2008-03-12 15:26:49 +00:00
|
|
|
|
(defun doc-view-pdf->txt (pdf txt callback)
|
|
|
|
|
"Convert PDF to TXT asynchronously and call CALLBACK when finished."
|
2008-10-18 22:30:55 +00:00
|
|
|
|
(or doc-view-pdftotext-program
|
|
|
|
|
(error "You need the `pdftotext' program to convert a PDF to text"))
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(doc-view-start-process "pdf->txt" doc-view-pdftotext-program
|
|
|
|
|
(list "-raw" pdf txt)
|
|
|
|
|
callback))
|
2008-03-12 12:05:30 +00:00
|
|
|
|
|
2008-03-12 15:26:49 +00:00
|
|
|
|
(defun doc-view-doc->txt (txt callback)
|
|
|
|
|
"Convert the current document to text and call CALLBACK when done."
|
2008-03-16 09:24:29 +00:00
|
|
|
|
(make-directory (doc-view-current-cache-dir) t)
|
2008-03-12 15:26:49 +00:00
|
|
|
|
(case doc-view-doc-type
|
|
|
|
|
(pdf
|
|
|
|
|
;; Doc is a PDF, so convert it to TXT
|
|
|
|
|
(doc-view-pdf->txt doc-view-buffer-file-name txt callback))
|
|
|
|
|
(ps
|
|
|
|
|
;; Doc is a PS, so convert it to PDF (which will be converted to
|
|
|
|
|
;; TXT thereafter).
|
2011-02-10 18:53:49 +00:00
|
|
|
|
(let ((pdf (expand-file-name "doc.pdf"
|
|
|
|
|
(doc-view-current-cache-dir))))
|
2008-03-12 15:26:49 +00:00
|
|
|
|
(doc-view-ps->pdf doc-view-buffer-file-name pdf
|
|
|
|
|
(lambda () (doc-view-pdf->txt pdf txt callback)))))
|
|
|
|
|
(dvi
|
|
|
|
|
;; Doc is a DVI. This means that a doc.pdf already exists in its
|
|
|
|
|
;; cache subdirectory.
|
|
|
|
|
(doc-view-pdf->txt (expand-file-name "doc.pdf"
|
|
|
|
|
(doc-view-current-cache-dir))
|
|
|
|
|
txt callback))
|
2010-12-30 13:53:22 +00:00
|
|
|
|
(odf
|
|
|
|
|
;; Doc is some ODF (or MS Office) doc. This means that a doc.pdf
|
|
|
|
|
;; already exists in its cache subdirectory.
|
|
|
|
|
(doc-view-pdf->txt (expand-file-name "doc.pdf"
|
|
|
|
|
(doc-view-current-cache-dir))
|
|
|
|
|
txt callback))
|
2008-03-12 15:26:49 +00:00
|
|
|
|
(t (error "DocView doesn't know what to do"))))
|
|
|
|
|
|
|
|
|
|
(defun doc-view-ps->pdf (ps pdf callback)
|
|
|
|
|
"Convert PS to PDF asynchronously and call CALLBACK when finished."
|
2008-10-18 22:30:55 +00:00
|
|
|
|
(or doc-view-ps2pdf-program
|
|
|
|
|
(error "You need the `ps2pdf' program to convert PS to PDF"))
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(doc-view-start-process "ps->pdf" doc-view-ps2pdf-program
|
|
|
|
|
(list
|
|
|
|
|
;; Avoid security problems when rendering files from
|
|
|
|
|
;; untrusted sources.
|
|
|
|
|
"-dSAFER"
|
|
|
|
|
;; in-file and out-file
|
|
|
|
|
ps pdf)
|
|
|
|
|
callback))
|
|
|
|
|
|
|
|
|
|
(defun doc-view-active-pages ()
|
|
|
|
|
(let ((pages ()))
|
|
|
|
|
(dolist (win (get-buffer-window-list (current-buffer) nil 'visible))
|
|
|
|
|
(let ((page (image-mode-window-get 'page win)))
|
|
|
|
|
(unless (memq page pages) (push page pages))))
|
|
|
|
|
pages))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2007-10-17 21:34:19 +00:00
|
|
|
|
(defun doc-view-convert-current-doc ()
|
2008-01-10 15:08:34 +00:00
|
|
|
|
"Convert `doc-view-buffer-file-name' to a set of png files, one file per page.
|
2007-10-17 21:34:19 +00:00
|
|
|
|
Those files are saved in the directory given by the function
|
|
|
|
|
`doc-view-current-cache-dir'."
|
2007-10-30 21:53:05 +00:00
|
|
|
|
;; Let stale files still display while we recompute the new ones, so only
|
|
|
|
|
;; flush the cache when the conversion is over. One of the reasons why it
|
|
|
|
|
;; is important to keep displaying the stale page is so that revert-buffer
|
|
|
|
|
;; preserves the horizontal/vertical scroll settings (which are otherwise
|
|
|
|
|
;; resets during the redisplay).
|
|
|
|
|
(setq doc-view-pending-cache-flush t)
|
|
|
|
|
(let ((png-file (expand-file-name "page-%d.png"
|
|
|
|
|
(doc-view-current-cache-dir))))
|
2008-03-16 09:24:29 +00:00
|
|
|
|
(make-directory (doc-view-current-cache-dir) t)
|
2008-03-12 14:50:36 +00:00
|
|
|
|
(case doc-view-doc-type
|
|
|
|
|
(dvi
|
|
|
|
|
;; DVI files have to be converted to PDF before Ghostscript can process
|
|
|
|
|
;; it.
|
2011-02-10 18:53:49 +00:00
|
|
|
|
(let ((pdf (expand-file-name "doc.pdf" doc-view-current-cache-dir)))
|
2008-03-12 15:26:49 +00:00
|
|
|
|
(doc-view-dvi->pdf doc-view-buffer-file-name pdf
|
|
|
|
|
(lambda () (doc-view-pdf/ps->png pdf png-file)))))
|
2010-12-30 13:45:09 +00:00
|
|
|
|
(odf
|
|
|
|
|
;; ODF files have to be converted to PDF before Ghostscript can
|
|
|
|
|
;; process it.
|
|
|
|
|
(lexical-let
|
|
|
|
|
((pdf (expand-file-name "doc.pdf" doc-view-current-cache-dir))
|
|
|
|
|
(opdf (expand-file-name (concat (file-name-sans-extension
|
|
|
|
|
(file-name-nondirectory doc-view-buffer-file-name))
|
|
|
|
|
".pdf")
|
|
|
|
|
doc-view-current-cache-dir))
|
|
|
|
|
(png-file png-file))
|
|
|
|
|
;; The unoconv tool only supports a output directory, but no
|
|
|
|
|
;; file name. It's named like the input file with the
|
|
|
|
|
;; extension replaced by pdf.
|
|
|
|
|
(doc-view-odf->pdf doc-view-buffer-file-name
|
|
|
|
|
(lambda ()
|
|
|
|
|
;; Rename to doc.pdf
|
|
|
|
|
(rename-file opdf pdf)
|
|
|
|
|
(doc-view-pdf/ps->png pdf png-file)))))
|
2010-02-05 01:53:40 +00:00
|
|
|
|
(pdf
|
|
|
|
|
(let ((pages (doc-view-active-pages)))
|
|
|
|
|
;; Convert PDF to PNG images starting with the active pages.
|
|
|
|
|
(doc-view-pdf->png doc-view-buffer-file-name png-file pages)))
|
2008-03-12 14:50:36 +00:00
|
|
|
|
(t
|
|
|
|
|
;; Convert to PNG images.
|
|
|
|
|
(doc-view-pdf/ps->png doc-view-buffer-file-name png-file)))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
;;;; Slicing
|
|
|
|
|
|
2008-06-12 03:56:20 +00:00
|
|
|
|
(declare-function image-size "image.c" (spec &optional pixels frame))
|
|
|
|
|
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(defun doc-view-set-slice (x y width height)
|
|
|
|
|
"Set the slice of the images that should be displayed.
|
|
|
|
|
You can use this function to tell doc-view not to display the
|
|
|
|
|
margins of the document. It prompts for the top-left corner (X
|
|
|
|
|
and Y) of the slice to display and its WIDTH and HEIGHT.
|
|
|
|
|
|
|
|
|
|
See `doc-view-set-slice-using-mouse' for a more convenient way to
|
|
|
|
|
do that. To reset the slice use `doc-view-reset-slice'."
|
|
|
|
|
(interactive
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(let* ((size (image-size (doc-view-current-image) t))
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(a (read-number (format "Top-left X (0..%d): " (car size))))
|
|
|
|
|
(b (read-number (format "Top-left Y (0..%d): " (cdr size))))
|
|
|
|
|
(c (read-number (format "Width (0..%d): " (- (car size) a))))
|
|
|
|
|
(d (read-number (format "Height (0..%d): " (- (cdr size) b)))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(list a b c d)))
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(setf (doc-view-current-slice) (list x y width height))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;; Redisplay
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(doc-view-goto-page (doc-view-current-page)))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-set-slice-using-mouse ()
|
|
|
|
|
"Set the slice of the images that should be displayed.
|
|
|
|
|
You set the slice by pressing mouse-1 at its top-left corner and
|
|
|
|
|
dragging it to its bottom-right corner. See also
|
|
|
|
|
`doc-view-set-slice' and `doc-view-reset-slice'."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let (x y w h done)
|
|
|
|
|
(while (not done)
|
|
|
|
|
(let ((e (read-event
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(concat "Press mouse-1 at the top-left corner and "
|
|
|
|
|
"drag it to the bottom-right corner!"))))
|
|
|
|
|
(when (eq (car e) 'drag-mouse-1)
|
|
|
|
|
(setq x (car (posn-object-x-y (event-start e))))
|
|
|
|
|
(setq y (cdr (posn-object-x-y (event-start e))))
|
|
|
|
|
(setq w (- (car (posn-object-x-y (event-end e))) x))
|
|
|
|
|
(setq h (- (cdr (posn-object-x-y (event-end e))) y))
|
|
|
|
|
(setq done t))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(doc-view-set-slice x y w h)))
|
|
|
|
|
|
|
|
|
|
(defun doc-view-reset-slice ()
|
2007-10-03 23:40:07 +00:00
|
|
|
|
"Reset the current slice.
|
2007-10-31 03:10:32 +00:00
|
|
|
|
After calling this function whole pages will be visible again."
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(interactive)
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(setf (doc-view-current-slice) nil)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;; Redisplay
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(doc-view-goto-page (doc-view-current-page)))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
;;;; Display
|
|
|
|
|
|
|
|
|
|
(defun doc-view-insert-image (file &rest args)
|
|
|
|
|
"Insert the given png FILE.
|
2007-10-03 23:40:07 +00:00
|
|
|
|
ARGS is a list of image descriptors."
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(when doc-view-pending-cache-flush
|
|
|
|
|
(clear-image-cache)
|
|
|
|
|
(setq doc-view-pending-cache-flush nil))
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(let ((ol (doc-view-current-overlay))
|
2008-02-25 20:04:59 +00:00
|
|
|
|
(image (if (and file (file-readable-p file))
|
2011-01-05 21:17:51 +00:00
|
|
|
|
(if (not (fboundp 'imagemagick-types))
|
|
|
|
|
(apply 'create-image file 'png nil args)
|
|
|
|
|
(unless (member :width args)
|
|
|
|
|
(setq args (append args (list :width doc-view-image-width))))
|
|
|
|
|
(apply 'create-image file 'imagemagick nil args))))
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(slice (doc-view-current-slice)))
|
|
|
|
|
(setf (doc-view-current-image) image)
|
2008-02-25 20:04:59 +00:00
|
|
|
|
(move-overlay ol (point-min) (point-max))
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(overlay-put ol 'display
|
2008-02-25 20:04:59 +00:00
|
|
|
|
(cond
|
|
|
|
|
(image
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(if slice
|
|
|
|
|
(list (cons 'slice slice) image)
|
2008-02-25 20:04:59 +00:00
|
|
|
|
image))
|
|
|
|
|
;; We're trying to display a page that doesn't exist.
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(doc-view-current-converter-processes
|
2008-02-25 20:04:59 +00:00
|
|
|
|
;; Maybe the page doesn't exist *yet*.
|
|
|
|
|
"Cannot display this page (yet)!")
|
|
|
|
|
(t
|
|
|
|
|
;; Typically happens if the conversion process somehow
|
|
|
|
|
;; failed. Better not signal an error here because it
|
|
|
|
|
;; could prevent a subsequent reconversion from fixing
|
|
|
|
|
;; the problem.
|
|
|
|
|
(concat "Cannot display this page!\n"
|
|
|
|
|
"Maybe because of a conversion failure!"))))
|
|
|
|
|
(let ((win (overlay-get ol 'window)))
|
|
|
|
|
(if (stringp (overlay-get ol 'display))
|
|
|
|
|
(progn ;Make sure the text is not scrolled out of view.
|
|
|
|
|
(set-window-hscroll win 0)
|
|
|
|
|
(set-window-vscroll win 0))
|
|
|
|
|
(let ((hscroll (image-mode-window-get 'hscroll win))
|
|
|
|
|
(vscroll (image-mode-window-get 'vscroll win)))
|
|
|
|
|
;; Reset scroll settings, in case they were changed.
|
|
|
|
|
(if hscroll (set-window-hscroll win hscroll))
|
|
|
|
|
(if vscroll (set-window-vscroll win vscroll)))))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-sort (a b)
|
|
|
|
|
"Return non-nil if A should be sorted before B.
|
|
|
|
|
Predicate for sorting `doc-view-current-files'."
|
2007-10-30 17:45:41 +00:00
|
|
|
|
(or (< (length a) (length b))
|
|
|
|
|
(and (= (length a) (length b))
|
|
|
|
|
(string< a b))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2008-02-12 02:41:55 +00:00
|
|
|
|
(defun doc-view-display (buffer &optional force)
|
|
|
|
|
"Start viewing the document in BUFFER.
|
2007-12-06 15:04:29 +00:00
|
|
|
|
If FORCE is non-nil, start viewing even if the document does not
|
|
|
|
|
have the page we want to view."
|
2008-02-12 02:41:55 +00:00
|
|
|
|
(with-current-buffer buffer
|
2008-02-25 20:04:59 +00:00
|
|
|
|
(let ((prev-pages doc-view-current-files))
|
|
|
|
|
(setq doc-view-current-files
|
|
|
|
|
(sort (directory-files (doc-view-current-cache-dir) t
|
|
|
|
|
"page-[0-9]+\\.png" t)
|
|
|
|
|
'doc-view-sort))
|
2008-04-28 23:49:02 +00:00
|
|
|
|
(dolist (win (or (get-buffer-window-list buffer nil t)
|
|
|
|
|
(list (selected-window))))
|
|
|
|
|
(let* ((page (doc-view-current-page win))
|
|
|
|
|
(pagefile (expand-file-name (format "page-%d.png" page)
|
|
|
|
|
(doc-view-current-cache-dir))))
|
|
|
|
|
(when (or force
|
|
|
|
|
(and (not (member pagefile prev-pages))
|
|
|
|
|
(member pagefile doc-view-current-files)))
|
|
|
|
|
(with-selected-window win
|
2011-02-10 18:53:49 +00:00
|
|
|
|
(assert (eq (current-buffer) buffer))
|
|
|
|
|
(doc-view-goto-page page))))))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-buffer-message ()
|
2007-10-30 21:53:05 +00:00
|
|
|
|
;; Only show this message initially, not when refreshing the buffer (in which
|
|
|
|
|
;; case it's better to keep displaying the "stale" page while computing
|
|
|
|
|
;; the fresh new ones).
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(unless (overlay-get (doc-view-current-overlay) 'display)
|
|
|
|
|
(overlay-put (doc-view-current-overlay) 'display
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(concat (propertize "Welcome to DocView!" 'face 'bold)
|
|
|
|
|
"\n"
|
|
|
|
|
"
|
2007-10-31 03:10:32 +00:00
|
|
|
|
If you see this buffer it means that the document you want to view is being
|
2007-10-30 17:45:41 +00:00
|
|
|
|
converted to PNG and the conversion of the first page hasn't finished yet or
|
2007-09-09 22:29:16 +00:00
|
|
|
|
`doc-view-conversion-refresh-interval' is set to nil.
|
|
|
|
|
|
|
|
|
|
For now these keys are useful:
|
|
|
|
|
|
2007-10-03 23:28:19 +00:00
|
|
|
|
`q' : Bury this buffer. Conversion will go on in background.
|
2007-10-30 06:29:51 +00:00
|
|
|
|
`k' : Kill the conversion process and this buffer.
|
2007-10-30 21:53:05 +00:00
|
|
|
|
`K' : Kill the conversion process.\n"))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2008-06-12 03:56:20 +00:00
|
|
|
|
(declare-function tooltip-show "tooltip" (text &optional use-echo-area))
|
|
|
|
|
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(defun doc-view-show-tooltip ()
|
|
|
|
|
(interactive)
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(tooltip-show (doc-view-current-info)))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2008-03-12 12:05:30 +00:00
|
|
|
|
(defun doc-view-open-text ()
|
|
|
|
|
"Open a buffer with the current doc's contents as text."
|
|
|
|
|
(interactive)
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(if doc-view-current-converter-processes
|
2008-03-12 12:05:30 +00:00
|
|
|
|
(message "DocView: please wait till conversion finished.")
|
|
|
|
|
(let ((txt (expand-file-name "doc.txt" (doc-view-current-cache-dir))))
|
|
|
|
|
(if (file-readable-p txt)
|
2011-01-13 19:48:26 +00:00
|
|
|
|
(let ((name (concat "Text contents of "
|
|
|
|
|
(file-name-nondirectory buffer-file-name)))
|
|
|
|
|
(dir (file-name-directory buffer-file-name)))
|
|
|
|
|
(with-current-buffer (find-file txt)
|
|
|
|
|
(rename-buffer name)
|
|
|
|
|
(setq default-directory dir)))
|
2008-03-12 15:26:49 +00:00
|
|
|
|
(doc-view-doc->txt txt 'doc-view-open-text)))))
|
2008-03-12 12:05:30 +00:00
|
|
|
|
|
2007-10-30 06:29:51 +00:00
|
|
|
|
;;;;; Toggle between editing and viewing
|
2007-10-17 21:34:19 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-toggle-display ()
|
2007-10-30 06:29:51 +00:00
|
|
|
|
"Toggle between editing a document as text or viewing it."
|
2007-10-17 21:34:19 +00:00
|
|
|
|
(interactive)
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(if (eq major-mode 'doc-view-mode)
|
|
|
|
|
;; Switch to editing mode
|
|
|
|
|
(progn
|
|
|
|
|
(doc-view-kill-proc)
|
|
|
|
|
(setq buffer-read-only nil)
|
2008-03-12 12:05:30 +00:00
|
|
|
|
(remove-overlays (point-min) (point-max) 'doc-view t)
|
|
|
|
|
(set (make-local-variable 'image-mode-winprops-alist) t)
|
2010-12-30 14:47:53 +00:00
|
|
|
|
;; Switch to the previously used major mode or fall back to
|
|
|
|
|
;; normal mode.
|
2010-12-30 18:08:18 +00:00
|
|
|
|
(doc-view-fallback-mode)
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(doc-view-minor-mode 1))
|
2007-10-30 06:29:51 +00:00
|
|
|
|
;; Switch to doc-view-mode
|
|
|
|
|
(when (and (buffer-modified-p)
|
|
|
|
|
(y-or-n-p "The buffer has been modified. Save the changes? "))
|
|
|
|
|
(save-buffer))
|
|
|
|
|
(doc-view-mode)))
|
2007-10-17 21:34:19 +00:00
|
|
|
|
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;;;; Searching
|
|
|
|
|
|
2008-03-12 12:05:30 +00:00
|
|
|
|
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(defun doc-view-search-internal (regexp file)
|
|
|
|
|
"Return a list of FILE's pages that contain text matching REGEXP.
|
2007-10-03 23:28:19 +00:00
|
|
|
|
The value is an alist of the form (PAGE CONTEXTS) where PAGE is
|
|
|
|
|
the pagenumber and CONTEXTS are all lines of text containing a match."
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(insert-file-contents file)
|
|
|
|
|
(let ((page 1)
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(lastpage 1)
|
|
|
|
|
matches)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(while (re-search-forward (concat "\\(?:\\([]\\)\\|\\("
|
2007-10-03 23:28:19 +00:00
|
|
|
|
regexp "\\)\\)") nil t)
|
2008-01-04 03:12:17 +00:00
|
|
|
|
(when (match-string 1) (setq page (1+ page)))
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(when (match-string 2)
|
|
|
|
|
(if (/= page lastpage)
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(push (cons page
|
2008-03-12 12:05:30 +00:00
|
|
|
|
(list (buffer-substring
|
|
|
|
|
(line-beginning-position)
|
|
|
|
|
(line-end-position))))
|
|
|
|
|
matches)
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(setq matches (cons
|
|
|
|
|
(append
|
|
|
|
|
(or
|
|
|
|
|
;; This page already is a match.
|
|
|
|
|
(car matches)
|
|
|
|
|
;; This is the first match on page.
|
|
|
|
|
(list page))
|
|
|
|
|
(list (buffer-substring
|
|
|
|
|
(line-beginning-position)
|
|
|
|
|
(line-end-position))))
|
|
|
|
|
(cdr matches))))
|
|
|
|
|
(setq lastpage page)))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(nreverse matches))))
|
|
|
|
|
|
|
|
|
|
(defun doc-view-search-no-of-matches (list)
|
|
|
|
|
"Extract the number of matches from the search result LIST."
|
|
|
|
|
(let ((no 0))
|
|
|
|
|
(dolist (p list)
|
|
|
|
|
(setq no (+ no (1- (length p)))))
|
|
|
|
|
no))
|
|
|
|
|
|
2007-11-10 21:53:47 +00:00
|
|
|
|
(defun doc-view-search-backward (new-query)
|
|
|
|
|
"Call `doc-view-search' for backward search.
|
|
|
|
|
If prefix NEW-QUERY is given, ask for a new regexp."
|
|
|
|
|
(interactive "P")
|
2007-11-16 18:00:39 +00:00
|
|
|
|
(doc-view-search new-query t))
|
2007-11-10 21:53:47 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-search (new-query &optional backward)
|
|
|
|
|
"Jump to the next match or initiate a new search if NEW-QUERY is given.
|
2007-09-09 22:29:16 +00:00
|
|
|
|
If the current document hasn't been transformed to plain text
|
2007-11-10 21:53:47 +00:00
|
|
|
|
till now do that first.
|
|
|
|
|
If BACKWARD is non-nil, jump to the previous match."
|
|
|
|
|
(interactive "P")
|
2007-11-16 18:00:39 +00:00
|
|
|
|
(if (and (not new-query)
|
2007-11-10 21:53:47 +00:00
|
|
|
|
doc-view-current-search-matches)
|
|
|
|
|
(if backward
|
|
|
|
|
(doc-view-search-previous-match 1)
|
|
|
|
|
(doc-view-search-next-match 1))
|
|
|
|
|
;; New search, so forget the old results.
|
|
|
|
|
(setq doc-view-current-search-matches nil)
|
|
|
|
|
(let ((txt (expand-file-name "doc.txt"
|
|
|
|
|
(doc-view-current-cache-dir))))
|
|
|
|
|
(if (file-readable-p txt)
|
|
|
|
|
(progn
|
|
|
|
|
(setq doc-view-current-search-matches
|
|
|
|
|
(doc-view-search-internal
|
|
|
|
|
(read-from-minibuffer "Regexp: ")
|
|
|
|
|
txt))
|
|
|
|
|
(message "DocView: search yielded %d matches."
|
|
|
|
|
(doc-view-search-no-of-matches
|
|
|
|
|
doc-view-current-search-matches)))
|
|
|
|
|
;; We must convert to TXT first!
|
2008-03-31 15:14:16 +00:00
|
|
|
|
(if doc-view-current-converter-processes
|
2007-11-10 21:53:47 +00:00
|
|
|
|
(message "DocView: please wait till conversion finished.")
|
2008-03-12 15:26:49 +00:00
|
|
|
|
(doc-view-doc->txt txt (lambda () (doc-view-search nil))))))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-search-next-match (arg)
|
|
|
|
|
"Go to the ARGth next matching page."
|
|
|
|
|
(interactive "p")
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(let* ((next-pages (doc-view-remove-if
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(lambda (i) (<= (car i) (doc-view-current-page)))
|
2007-10-30 06:29:51 +00:00
|
|
|
|
doc-view-current-search-matches))
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(page (car (nth (1- arg) next-pages))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(if page
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(doc-view-goto-page page)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(when (and
|
2007-10-03 23:28:19 +00:00
|
|
|
|
doc-view-current-search-matches
|
|
|
|
|
(y-or-n-p "No more matches after current page. Wrap to first match? "))
|
|
|
|
|
(doc-view-goto-page (caar doc-view-current-search-matches))))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-search-previous-match (arg)
|
|
|
|
|
"Go to the ARGth previous matching page."
|
|
|
|
|
(interactive "p")
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(let* ((prev-pages (doc-view-remove-if
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(lambda (i) (>= (car i) (doc-view-current-page)))
|
2007-10-30 06:29:51 +00:00
|
|
|
|
doc-view-current-search-matches))
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(page (car (nth (1- arg) (nreverse prev-pages)))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(if page
|
2007-10-03 23:28:19 +00:00
|
|
|
|
(doc-view-goto-page page)
|
2007-09-09 22:29:16 +00:00
|
|
|
|
(when (and
|
2007-10-03 23:28:19 +00:00
|
|
|
|
doc-view-current-search-matches
|
|
|
|
|
(y-or-n-p "No more matches before current page. Wrap to last match? "))
|
|
|
|
|
(doc-view-goto-page (caar (last doc-view-current-search-matches)))))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2007-10-17 21:34:19 +00:00
|
|
|
|
;;;; User interface commands and the mode
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2010-02-05 22:36:20 +00:00
|
|
|
|
(put 'doc-view-mode 'mode-class 'special)
|
2007-10-30 21:53:05 +00:00
|
|
|
|
|
2008-03-12 12:05:30 +00:00
|
|
|
|
(defun doc-view-already-converted-p ()
|
|
|
|
|
"Return non-nil if the current doc was already converted."
|
|
|
|
|
(and (file-exists-p (doc-view-current-cache-dir))
|
2010-02-05 01:53:40 +00:00
|
|
|
|
;; Check that the resolution info is there, otherwise it means
|
|
|
|
|
;; the conversion is incomplete.
|
|
|
|
|
(file-readable-p (expand-file-name "resolution.el"
|
|
|
|
|
(doc-view-current-cache-dir)))
|
|
|
|
|
(> (length (directory-files (doc-view-current-cache-dir)
|
|
|
|
|
nil "\\.png\\'"))
|
|
|
|
|
0)))
|
2008-03-12 12:05:30 +00:00
|
|
|
|
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(defun doc-view-initiate-display ()
|
|
|
|
|
;; Switch to image display if possible
|
2008-03-18 17:43:26 +00:00
|
|
|
|
(if (doc-view-mode-p doc-view-doc-type)
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(progn
|
|
|
|
|
(doc-view-buffer-message)
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(setf (doc-view-current-page) (or (doc-view-current-page) 1))
|
2008-03-12 12:05:30 +00:00
|
|
|
|
(if (doc-view-already-converted-p)
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(progn
|
|
|
|
|
(message "DocView: using cached files!")
|
2009-03-05 08:24:26 +00:00
|
|
|
|
;; Load the saved resolution
|
2010-02-05 01:53:40 +00:00
|
|
|
|
(let* ((res-file (expand-file-name "resolution.el"
|
|
|
|
|
(doc-view-current-cache-dir)))
|
|
|
|
|
(res
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(when (file-readable-p res-file)
|
|
|
|
|
(insert-file-contents res-file)
|
|
|
|
|
(read (current-buffer))))))
|
|
|
|
|
(when (numberp res)
|
2009-03-05 08:24:26 +00:00
|
|
|
|
(set (make-local-variable 'doc-view-resolution) res)))
|
2008-02-12 02:41:55 +00:00
|
|
|
|
(doc-view-display (current-buffer) 'force))
|
2007-10-30 21:53:05 +00:00
|
|
|
|
(doc-view-convert-current-doc))
|
|
|
|
|
(message
|
|
|
|
|
"%s"
|
|
|
|
|
(substitute-command-keys
|
|
|
|
|
(concat "Type \\[doc-view-toggle-display] to toggle between "
|
|
|
|
|
"editing or viewing the document."))))
|
|
|
|
|
(message
|
|
|
|
|
"%s"
|
2009-07-17 19:43:49 +00:00
|
|
|
|
(concat "No PNG support is available, or some conversion utility for "
|
|
|
|
|
(file-name-extension doc-view-buffer-file-name)
|
|
|
|
|
" files is missing."))
|
2011-01-13 20:17:15 +00:00
|
|
|
|
(when (and (executable-find doc-view-pdftotext-program)
|
|
|
|
|
(y-or-n-p
|
|
|
|
|
"Unable to render file. View extracted text instead? "))
|
|
|
|
|
(doc-view-open-text))
|
|
|
|
|
(doc-view-toggle-display)))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
2008-03-07 05:44:37 +00:00
|
|
|
|
(defvar bookmark-make-record-function)
|
2008-01-04 03:12:17 +00:00
|
|
|
|
|
2008-02-12 02:41:55 +00:00
|
|
|
|
(defun doc-view-clone-buffer-hook ()
|
|
|
|
|
;; FIXME: There are several potential problems linked with reconversion
|
|
|
|
|
;; and auto-revert when we have indirect buffers because they share their
|
|
|
|
|
;; /tmp cache directory. This sharing is good (you'd rather not reconvert
|
|
|
|
|
;; for each clone), but that means that clones need to collaborate a bit.
|
|
|
|
|
;; I guess it mostly means: detect when a reconversion process is already
|
|
|
|
|
;; running, and run the sentinel in all clones.
|
2008-03-12 12:05:30 +00:00
|
|
|
|
;;
|
2008-02-21 03:45:04 +00:00
|
|
|
|
;; Maybe the clones should really have a separate /tmp directory
|
2008-02-12 02:41:55 +00:00
|
|
|
|
;; so they could have a different resolution and you could use clones
|
|
|
|
|
;; for zooming.
|
2008-02-21 03:45:04 +00:00
|
|
|
|
(remove-overlays (point-min) (point-max) 'doc-view t)
|
|
|
|
|
(if (consp image-mode-winprops-alist) (setq image-mode-winprops-alist nil)))
|
2008-02-12 02:41:55 +00:00
|
|
|
|
|
2008-03-12 14:50:36 +00:00
|
|
|
|
(defun doc-view-intersection (l1 l2)
|
|
|
|
|
(let ((l ()))
|
|
|
|
|
(dolist (x l1) (if (memq x l2) (push x l)))
|
|
|
|
|
l))
|
|
|
|
|
|
2010-12-30 18:08:18 +00:00
|
|
|
|
(defun doc-view-set-doc-type ()
|
|
|
|
|
"Figure out the current document type (`doc-view-doc-type')."
|
|
|
|
|
(let ((name-types
|
|
|
|
|
(when buffer-file-name
|
|
|
|
|
(cdr (assoc (file-name-extension buffer-file-name)
|
|
|
|
|
'(
|
|
|
|
|
;; DVI
|
|
|
|
|
("dvi" dvi)
|
|
|
|
|
;; PDF
|
|
|
|
|
("pdf" pdf) ("epdf" pdf)
|
|
|
|
|
;; PostScript
|
|
|
|
|
("ps" ps) ("eps" ps)
|
|
|
|
|
;; OpenDocument formats
|
|
|
|
|
("odt" odf) ("ods" odf) ("odp" odf) ("odg" odf)
|
|
|
|
|
("odc" odf) ("odi" odf) ("odm" odf) ("ott" odf)
|
|
|
|
|
("ots" odf) ("otp" odf) ("otg" odf)
|
|
|
|
|
;; Microsoft Office formats (also handled
|
|
|
|
|
;; by the odf conversion chain)
|
|
|
|
|
("doc" odf) ("docx" odf) ("xls" odf) ("xlsx" odf)
|
|
|
|
|
("ppt" odf) ("pptx" odf))))))
|
|
|
|
|
(content-types
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(cond
|
|
|
|
|
((looking-at "%!") '(ps))
|
|
|
|
|
((looking-at "%PDF") '(pdf))
|
|
|
|
|
((looking-at "\367\002") '(dvi))))))
|
|
|
|
|
(set (make-local-variable 'doc-view-doc-type)
|
|
|
|
|
(car (or (doc-view-intersection name-types content-types)
|
|
|
|
|
(when (and name-types content-types)
|
|
|
|
|
(error "Conflicting types: name says %s but content says %s"
|
|
|
|
|
name-types content-types))
|
|
|
|
|
name-types content-types
|
|
|
|
|
(error "Cannot determine the document type"))))))
|
|
|
|
|
|
2007-10-17 21:34:19 +00:00
|
|
|
|
;;;###autoload
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(defun doc-view-mode ()
|
2007-10-17 21:34:19 +00:00
|
|
|
|
"Major mode in DocView buffers.
|
2009-01-27 15:52:17 +00:00
|
|
|
|
|
|
|
|
|
DocView Mode is an Emacs document viewer. It displays PDF, PS
|
|
|
|
|
and DVI files (as PNG images) in Emacs buffers.
|
|
|
|
|
|
2007-10-17 21:34:19 +00:00
|
|
|
|
You can use \\<doc-view-mode-map>\\[doc-view-toggle-display] to
|
2008-02-01 03:00:55 +00:00
|
|
|
|
toggle between displaying the document or editing it as text.
|
|
|
|
|
\\{doc-view-mode-map}"
|
2007-10-30 06:29:51 +00:00
|
|
|
|
(interactive)
|
2008-01-09 20:16:14 +00:00
|
|
|
|
|
2009-04-02 21:12:54 +00:00
|
|
|
|
(if (= (point-min) (point-max))
|
2009-01-28 20:33:44 +00:00
|
|
|
|
;; The doc is empty or doesn't exist at all, so fallback to
|
2009-04-02 21:12:54 +00:00
|
|
|
|
;; another mode. We used to also check file-exists-p, but this
|
|
|
|
|
;; returns nil for tar members.
|
2010-12-30 18:08:18 +00:00
|
|
|
|
(doc-view-fallback-mode)
|
2009-01-27 15:52:17 +00:00
|
|
|
|
|
|
|
|
|
(let* ((prev-major-mode (if (eq major-mode 'doc-view-mode)
|
|
|
|
|
doc-view-previous-major-mode
|
2010-12-30 18:08:18 +00:00
|
|
|
|
(when (not (memq major-mode
|
|
|
|
|
'(doc-view-mode fundamental-mode)))
|
2010-12-30 14:47:53 +00:00
|
|
|
|
major-mode))))
|
2009-01-27 15:52:17 +00:00
|
|
|
|
(kill-all-local-variables)
|
|
|
|
|
(set (make-local-variable 'doc-view-previous-major-mode) prev-major-mode))
|
2009-02-09 15:33:47 +00:00
|
|
|
|
|
2009-01-27 15:52:17 +00:00
|
|
|
|
;; Figure out the document type.
|
2010-12-30 18:08:18 +00:00
|
|
|
|
(unless doc-view-doc-type
|
|
|
|
|
(doc-view-set-doc-type))
|
2009-02-09 15:33:47 +00:00
|
|
|
|
|
2009-01-27 15:52:17 +00:00
|
|
|
|
(doc-view-make-safe-dir doc-view-cache-directory)
|
|
|
|
|
;; Handle compressed files, remote files, files inside archives
|
|
|
|
|
(set (make-local-variable 'doc-view-buffer-file-name)
|
|
|
|
|
(cond
|
|
|
|
|
(jka-compr-really-do-compress
|
2009-12-07 02:37:42 +00:00
|
|
|
|
;; FIXME: there's a risk of name conflicts here.
|
2009-01-27 15:52:17 +00:00
|
|
|
|
(expand-file-name
|
|
|
|
|
(file-name-nondirectory
|
|
|
|
|
(file-name-sans-extension buffer-file-name))
|
|
|
|
|
doc-view-cache-directory))
|
|
|
|
|
;; Is the file readable by local processes?
|
|
|
|
|
;; We used to use `file-remote-p' but it's unclear what it's
|
|
|
|
|
;; supposed to return nil for things like local files accessed via
|
|
|
|
|
;; `su' or via file://...
|
|
|
|
|
((let ((file-name-handler-alist nil))
|
2009-12-07 02:37:42 +00:00
|
|
|
|
(not (and buffer-file-name (file-readable-p buffer-file-name))))
|
|
|
|
|
;; FIXME: there's a risk of name conflicts here.
|
2009-01-27 15:52:17 +00:00
|
|
|
|
(expand-file-name
|
2009-12-07 02:37:42 +00:00
|
|
|
|
(if buffer-file-name
|
|
|
|
|
(file-name-nondirectory buffer-file-name)
|
|
|
|
|
(buffer-name))
|
|
|
|
|
doc-view-cache-directory))
|
2009-01-27 15:52:17 +00:00
|
|
|
|
(t buffer-file-name)))
|
|
|
|
|
(when (not (string= doc-view-buffer-file-name buffer-file-name))
|
|
|
|
|
(write-region nil nil doc-view-buffer-file-name))
|
2009-02-09 15:33:47 +00:00
|
|
|
|
|
2009-01-27 15:52:17 +00:00
|
|
|
|
(add-hook 'change-major-mode-hook
|
|
|
|
|
(lambda ()
|
|
|
|
|
(doc-view-kill-proc)
|
|
|
|
|
(remove-overlays (point-min) (point-max) 'doc-view t))
|
|
|
|
|
nil t)
|
|
|
|
|
(add-hook 'clone-indirect-buffer-hook 'doc-view-clone-buffer-hook nil t)
|
|
|
|
|
(add-hook 'kill-buffer-hook 'doc-view-kill-proc nil t)
|
2009-02-09 15:33:47 +00:00
|
|
|
|
|
2009-01-27 15:52:17 +00:00
|
|
|
|
(remove-overlays (point-min) (point-max) 'doc-view t) ;Just in case.
|
|
|
|
|
;; Keep track of display info ([vh]scroll, page number, overlay,
|
|
|
|
|
;; ...) for each window in which this document is shown.
|
|
|
|
|
(add-hook 'image-mode-new-window-functions
|
|
|
|
|
'doc-view-new-window-function nil t)
|
|
|
|
|
(image-mode-setup-winprops)
|
2009-02-09 15:33:47 +00:00
|
|
|
|
|
2009-01-27 15:52:17 +00:00
|
|
|
|
(set (make-local-variable 'mode-line-position)
|
|
|
|
|
'(" P" (:eval (number-to-string (doc-view-current-page)))
|
2010-02-05 01:53:40 +00:00
|
|
|
|
"/" (:eval (number-to-string (doc-view-last-page-number)))))
|
2009-01-27 15:52:17 +00:00
|
|
|
|
;; Don't scroll unless the user specifically asked for it.
|
|
|
|
|
(set (make-local-variable 'auto-hscroll-mode) nil)
|
2009-11-25 17:18:26 +00:00
|
|
|
|
(set (make-local-variable 'mwheel-scroll-up-function)
|
|
|
|
|
'doc-view-scroll-up-or-next-page)
|
|
|
|
|
(set (make-local-variable 'mwheel-scroll-down-function)
|
|
|
|
|
'doc-view-scroll-down-or-previous-page)
|
2009-01-27 15:52:17 +00:00
|
|
|
|
(set (make-local-variable 'cursor-type) nil)
|
|
|
|
|
(use-local-map doc-view-mode-map)
|
|
|
|
|
(set (make-local-variable 'after-revert-hook) 'doc-view-reconvert-doc)
|
|
|
|
|
(set (make-local-variable 'bookmark-make-record-function)
|
|
|
|
|
'doc-view-bookmark-make-record)
|
|
|
|
|
(setq mode-name "DocView"
|
|
|
|
|
buffer-read-only t
|
|
|
|
|
major-mode 'doc-view-mode)
|
|
|
|
|
(doc-view-initiate-display)
|
2009-11-24 07:47:45 +00:00
|
|
|
|
;; Switch off view-mode explicitly, because doc-view-mode is the
|
|
|
|
|
;; canonical view mode for PDF/PS/DVI files. This could be
|
|
|
|
|
;; switched on automatically depending on the value of
|
|
|
|
|
;; `view-read-only'.
|
2009-11-25 17:21:12 +00:00
|
|
|
|
(set (make-local-variable 'view-read-only) nil)
|
2009-01-27 15:52:17 +00:00
|
|
|
|
(run-mode-hooks 'doc-view-mode-hook)))
|
2007-10-30 06:29:51 +00:00
|
|
|
|
|
2010-12-30 18:08:18 +00:00
|
|
|
|
(defun doc-view-fallback-mode ()
|
|
|
|
|
"Fallback to the previous or next best major mode."
|
|
|
|
|
(if doc-view-previous-major-mode
|
|
|
|
|
(funcall doc-view-previous-major-mode)
|
|
|
|
|
(let ((auto-mode-alist (rassq-delete-all
|
|
|
|
|
'doc-view-mode-maybe
|
|
|
|
|
(rassq-delete-all 'doc-view-mode
|
|
|
|
|
(copy-alist auto-mode-alist)))))
|
|
|
|
|
(normal-mode))))
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun doc-view-mode-maybe ()
|
|
|
|
|
"Switch to `doc-view-mode' if possible.
|
|
|
|
|
If the required external tools are not available, then fallback
|
|
|
|
|
to the next best mode."
|
|
|
|
|
(condition-case nil
|
|
|
|
|
(doc-view-set-doc-type)
|
|
|
|
|
(error (doc-view-fallback-mode)))
|
|
|
|
|
(if (doc-view-mode-p doc-view-doc-type)
|
|
|
|
|
(doc-view-mode)
|
|
|
|
|
(doc-view-fallback-mode)))
|
|
|
|
|
|
2007-10-30 06:29:51 +00:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(define-minor-mode doc-view-minor-mode
|
|
|
|
|
"Toggle Doc view minor mode.
|
|
|
|
|
With arg, turn Doc view minor mode on if arg is positive, off otherwise.
|
|
|
|
|
See the command `doc-view-mode' for more information on this mode."
|
|
|
|
|
nil " DocView" doc-view-minor-mode-map
|
|
|
|
|
:group 'doc-view
|
|
|
|
|
(when doc-view-minor-mode
|
|
|
|
|
(add-hook 'change-major-mode-hook (lambda () (doc-view-minor-mode -1)) nil t)
|
|
|
|
|
(message
|
|
|
|
|
"%s"
|
|
|
|
|
(substitute-command-keys
|
|
|
|
|
"Type \\[doc-view-toggle-display] to toggle between editing or viewing the document."))))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-clear-cache ()
|
|
|
|
|
"Delete the whole cache (`doc-view-cache-directory')."
|
|
|
|
|
(interactive)
|
2008-03-12 12:05:30 +00:00
|
|
|
|
(dired-delete-file doc-view-cache-directory 'always))
|
2007-09-09 22:29:16 +00:00
|
|
|
|
|
|
|
|
|
(defun doc-view-dired-cache ()
|
|
|
|
|
"Open `dired' in `doc-view-cache-directory'."
|
|
|
|
|
(interactive)
|
|
|
|
|
(dired doc-view-cache-directory))
|
|
|
|
|
|
|
|
|
|
|
2007-12-26 08:53:06 +00:00
|
|
|
|
;;;; Bookmark integration
|
|
|
|
|
|
2010-07-14 15:57:54 +00:00
|
|
|
|
(declare-function bookmark-make-record-default
|
|
|
|
|
"bookmark" (&optional no-file no-context posn))
|
2008-03-09 21:37:16 +00:00
|
|
|
|
(declare-function bookmark-prop-get "bookmark" (bookmark prop))
|
2008-06-25 16:51:33 +00:00
|
|
|
|
(declare-function bookmark-default-handler "bookmark" (bmk))
|
2008-03-09 21:37:16 +00:00
|
|
|
|
|
2008-03-09 03:05:34 +00:00
|
|
|
|
(defun doc-view-bookmark-make-record ()
|
2008-06-25 16:51:33 +00:00
|
|
|
|
(nconc (bookmark-make-record-default)
|
|
|
|
|
`((page . ,(doc-view-current-page))
|
|
|
|
|
(handler . doc-view-bookmark-jump))))
|
2007-12-26 08:53:06 +00:00
|
|
|
|
|
2008-01-04 03:12:17 +00:00
|
|
|
|
|
2007-12-26 08:53:06 +00:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun doc-view-bookmark-jump (bmk)
|
2008-01-02 07:49:04 +00:00
|
|
|
|
;; This implements the `handler' function interface for record type
|
2008-03-07 05:44:37 +00:00
|
|
|
|
;; returned by `doc-view-bookmark-make-record', which see.
|
2008-06-25 16:51:33 +00:00
|
|
|
|
(prog1 (bookmark-default-handler bmk)
|
|
|
|
|
(let ((page (bookmark-prop-get bmk 'page)))
|
2007-12-26 08:53:06 +00:00
|
|
|
|
(when (not (eq major-mode 'doc-view-mode))
|
2008-06-25 16:51:33 +00:00
|
|
|
|
(doc-view-toggle-display))
|
2008-03-09 21:37:16 +00:00
|
|
|
|
(with-selected-window
|
2011-02-10 18:53:49 +00:00
|
|
|
|
(or (get-buffer-window (current-buffer) 0)
|
|
|
|
|
(selected-window))
|
|
|
|
|
(doc-view-goto-page page)))))
|
2007-12-26 08:53:06 +00:00
|
|
|
|
|
2008-01-04 03:12:17 +00:00
|
|
|
|
|
|
|
|
|
(provide 'doc-view)
|
|
|
|
|
|
|
|
|
|
;; Local Variables:
|
2011-06-09 20:22:06 +00:00
|
|
|
|
;; eval: (outline-minor-mode 1)
|
2008-01-04 03:12:17 +00:00
|
|
|
|
;; End:
|
|
|
|
|
|
2007-09-09 22:29:16 +00:00
|
|
|
|
;;; doc-view.el ends here
|