[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 2eedb81: * packages/djvu/djvu.el: Release v1.1.
From: |
Roland Winkler |
Subject: |
[elpa] master 2eedb81: * packages/djvu/djvu.el: Release v1.1. |
Date: |
Mon, 17 Jun 2019 23:39:26 -0400 (EDT) |
branch: master
commit 2eedb8174e8a322b85c75e6d0f0ef919cc88391f
Author: Roland Winkler <address@hidden>
Commit: Roland Winkler <address@hidden>
* packages/djvu/djvu.el: Release v1.1.
---
packages/djvu/djvu.el | 654 +++++++++++++++++++++++++++++++-------------------
1 file changed, 403 insertions(+), 251 deletions(-)
diff --git a/packages/djvu/djvu.el b/packages/djvu/djvu.el
index 098a1dc..1f20102 100644
--- a/packages/djvu/djvu.el
+++ b/packages/djvu/djvu.el
@@ -4,7 +4,7 @@
;; Author: Roland Winkler <address@hidden>
;; Keywords: files, wp
-;; Version: 1.0.1
+;; Version: 1.1
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@@ -43,24 +43,10 @@
;;
;; A normal work flow is as follows:
;;
-;; To visit a djvu file type M-x djvu-find-file. This command is the
-;; only entry point to this package. You can bind this command to a key,
-;; for example
-;;
-;; (global-set-key "\C-cd" 'djvu-find-file)
-;;
-;; Or you can use something more general like
-;;
-;; (defun djvu-find-file-noselect (f-f-n filename &rest args)
-;; "If FILENAME is a Djvu file call `djvu-find-file'."
-;; (if (string-match "\\.djvu\\'" (file-name-sans-versions filename))
-;; (djvu-find-file filename nil nil t)
-;; (apply f-f-n filename args)))
-;; (advice-add 'find-file-noselect :around #'djvu-find-file-noselect)
-;;
-;; If you use `djvu-find-file' to visit the file foo.djvu, it puts you into
-;; the (read-only) buffer foo.djvu. Normally, this buffer (plus possibly
-;; the outline buffer) is all you need.
+;; Djvu files are assumed to have the file extension ".djvu".
+;; When you visit the file foo.djvu, it puts you into the (read-only)
+;; buffer foo.djvu. Normally, this buffer (plus possibly the outline buffer)
+;; is all you need.
;;
;; The menu bar of this buffer lists most of the commands with their
;; respective key bindings. For example, you can:
@@ -149,6 +135,24 @@
;;; News:
+;; v1.1:
+;; - Use `auto-mode-alist' with file extension ".djvu".
+;;
+;; - Support bookmarks.
+;;
+;; - Display total number of pages in mode line.
+;;
+;; - New option `djvu-rect-area-nodups'.
+;;
+;; - User options `djvu-save-after-edit' and `djvu-region-history' removed
+;; (obsolete).
+;;
+;; - More robust code for merging lines in text layer.
+;;
+;; - Clean up handling of editing positions in a djvu document.
+;;
+;; - Bug fixes.
+;;
;; v1.0.1:
;; - Use `create-file-buffer' instead of `generate-new-buffer'
;; for compatibility with uniquify.
@@ -171,6 +175,19 @@
;;
;; - Font locking.
+;;; To do:
+
+;; - Auto-save script buffers. How can we recover these buffers
+;; in a meaningful way?
+;;
+;; - Use `replace-buffer-contents'?
+;;
+;; - New command that makes line breaks in text layer better searchable:
+;; Scan text layer for lines ending with hyphenated words "xxx-".
+;; If the first word of the next line is "yyy" and ispell knows
+;; the word "xxxyyy", replace "yyy" with that string. A search
+;; for the word "xxxyyy" will then succeed.
+
;;; Code:
;;; Djvu internals (see Sec. 8.3.4.2.3.1 of djvu3spec.djvu)
@@ -185,6 +202,8 @@
;;
;; c = #RRGGBB t = thickness (1..32)
;; o = opacity = 0..200 (yes)
+;;
+;; zones: page, column, region, para, line, word, and char
(require 'button)
(eval-when-compile
@@ -274,13 +293,6 @@ This is a list with six elements (READ TEXT ANNOT SHARED
BOOKMARKS OUTLINE)."
:group 'djvu
:type 'integer)
-;; FIXME: The proper and efficient alternative to saving the changes
-;; is to update the read buffer so that these buffers are consistent.
-(defcustom djvu-save-after-edit t
- "If non-nil save Djvu document after each call of a text editing command."
- :group 'djvu
- :type 'boolean)
-
(defcustom djvu-inherit-input-method t
"If non-nil calls of `read-string' inherit the input method."
:group 'djvu
@@ -302,11 +314,6 @@ These extensions include the period."
:group 'djvu
:type 'regexp)
-(defcustom djvu-region-history t
- "If non-nil `djvu-read-string' pushes region to `minibuffer-history'."
- :group 'djvu
- :type 'boolean)
-
(defcustom djvu-read-prop-newline 2
"Number of newline characters in Read buffer for consecutive region."
:group 'djvu
@@ -332,6 +339,11 @@ Used by `djvu-region-string'."
:group 'djvu
:type '(repeat (cons (regexp) (string))))
+(defcustom djvu-rect-area-nodups nil
+ "If non-nil `djvu-rect-area' does not create multiple rects for same areas."
+ :group 'djvu
+ :type 'boolean)
+
;; Internal variables
(defvar djvu-test nil
@@ -345,6 +357,7 @@ Used by `djvu-region-string'."
"Expanded rect list for propertizing the Read buffer.
This is a list with elements (COORDS URL TEXT COLOR ID) stored
in `djvu-doc-rect-list'.")
+
(defvar djvu-last-rect nil
"Last rect used for propertizing the Read buffer.
This is a list (BEG END COORDS URL TEXT COLOR).")
@@ -435,11 +448,14 @@ Each element is a cons pair (PAGE-NUM . FILE-ID).")
(defvar-local djvu-doc-pagesize nil
"Size of current page of a Djvu document.")
-(defvar-local djvu-doc-dpos nil
- "The current editing position in a Djvu document.")
+(defvar-local djvu-doc-read-pos nil
+ "The current editing position in the Read buffer (image coordinates).
+This is either a list (X Y) or a list or vector (XMIN YMIN XMAX YMAX).
+Used in `djvu-image-mode' when we cannot go to this position.")
(defvar-local djvu-doc-image nil
- "Image of current page of a Djvu document.")
+ "Image of current page of a Djvu document.
+This is a list (PAGE-NUM MAGNIFICATION IMAGE).")
;;; Helper functions and macros
@@ -514,21 +530,19 @@ Preserve FILE if `djvu-test' is non-nil."
"Switch to Djvu Read buffer."
(interactive (list nil (djvu-dpos)))
(switch-to-buffer (djvu-ref read-buf doc))
- (if dpos (djvu-goto-read dpos)))
+ (djvu-goto-read dpos))
(defun djvu-switch-text (&optional doc dpos)
"Switch to Djvu Text buffer."
(interactive (list nil (djvu-dpos)))
(switch-to-buffer (djvu-ref text-buf doc))
- (if dpos (djvu-goto-dpos 'word dpos)))
+ (djvu-goto-dpos 'word dpos))
(defun djvu-switch-annot (&optional doc dpos)
"Switch to Djvu Annotations buffer."
(interactive (list nil (djvu-dpos)))
(switch-to-buffer (djvu-ref annot-buf doc))
- (if (and dpos
- (or (djvu-goto-dpos 'rect dpos)
- (djvu-goto-dpos 'text dpos)))
+ (if (djvu-goto-dpos "\\(?:rect\\|text\\)" dpos)
;; If we have matching buffer position in the annotations buffer,
;; put point at the end of the annotations string.
(re-search-backward "\"")))
@@ -566,13 +580,12 @@ Preserve FILE if `djvu-test' is non-nil."
(defun djvu-dpos (&optional doc)
"Djvu position in current Djvu buffer."
- (let ((dpos (cond ((eq djvu-buffer 'read)
- (djvu-read-dpos nil doc))
- ((eq djvu-buffer 'text)
- (djvu-text-dpos nil doc))
- ((eq djvu-buffer 'annot)
- (djvu-annot-dpos nil doc)))))
- (if dpos (djvu-set dpos dpos doc))))
+ (cond ((eq djvu-buffer 'read)
+ (djvu-read-dpos nil doc))
+ ((eq djvu-buffer 'text)
+ (djvu-text-dpos nil doc))
+ ((eq djvu-buffer 'annot)
+ (djvu-annot-dpos nil doc))))
(defun djvu-read-page ()
"Read page number interactively."
@@ -644,6 +657,7 @@ This relies on `djvu-kill-doc-all' for doing the real work."
(defvar djvu-in-kill-doc nil
"Non-nil if we are running `djvu-kill-doc-all'.")
+
(defun djvu-kill-doc-all ()
"Kill all buffers visiting `djvu-doc' except for the current buffer.
This function is added to `kill-buffer-hook' of all buffers visiting `djvu-doc'
@@ -671,7 +685,6 @@ so that killing the current buffer kills all buffers
visiting `djvu-doc'."
(interactive)
(unless doc (setq doc djvu-doc))
(let ((afile (abbreviate-file-name (djvu-ref file doc)))
- (dpos (djvu-read-dpos nil doc))
(text-modified (buffer-modified-p (djvu-ref text-buf doc)))
(annot-modified (buffer-modified-p (djvu-ref annot-buf doc)))
(shared-modified (buffer-modified-p (djvu-ref shared-buf doc)))
@@ -685,16 +698,13 @@ so that killing the current buffer kills all buffers
visiting `djvu-doc'."
(djvu-with-temp-file script
(if annot-modified (djvu-save-annot script doc))
(if shared-modified (djvu-save-annot script doc t))
- (if text-modified (djvu-save-text script doc)) ; updates Read buffer
+ (if text-modified (djvu-save-text doc script)) ; updates Read buffer
(if bookmarks-modified (djvu-save-bookmarks script doc))
(djvu-djvused doc nil "-f" script "-s"))
(if (and annot-modified (not text-modified))
(djvu-init-read (djvu-read-text doc) doc))
(djvu-all-buffers doc
- (set-buffer-modified-p nil))
- ;; Update the buffer position in the Read buffer that was lost
- ;; when updating the Read buffer.
- (if text-modified (djvu-goto-read dpos)))))
+ (set-buffer-modified-p nil)))))
(defun djvu-modified ()
"Mark Djvu Read and Outline buffers as modified if necessary.
@@ -785,6 +795,7 @@ the purpose of calling djvused is to update the Djvu file."
(defvar djvu-color-attributes '(border hilite lineclr backclr textclr)
"List of color attributes known to Djvu.")
+
(defvar djvu-color-re
(concat "(" (regexp-opt (mapcar 'symbol-name djvu-color-attributes) t)
"[ \t\n]+\\(%s\\(%s[[:xdigit:]][[:xdigit:]]"
@@ -846,11 +857,8 @@ If INITIAL-INPUT is non-nil use string from REGION as
initial input."
;; Make the string in REGION the initial input.
(read-string prompt (djvu-region-string region)
nil nil djvu-inherit-input-method)
- ;; Let `minibuffer-history' know the string in REGION.
- ;; Should we remove this string afterwards?
- (if djvu-region-history
- (add-to-history 'minibuffer-history (djvu-region-string region)))
- (read-string prompt nil nil nil djvu-inherit-input-method)))
+ (read-string prompt nil nil (djvu-region-string region)
+ djvu-inherit-input-method)))
(defun djvu-interactive-color (color)
"Return color specification for use in interactive calls.
@@ -1034,13 +1042,21 @@ This is a child of `special-mode-map'.")
["Quit Viewing" djvu-quit-window t]
["Kill Djvu buffers" djvu-kill-doc t]))
+(defvar bookmark-make-record-function)
+
(define-derived-mode djvu-read-mode special-mode "Djview"
"Mode for reading Djvu files."
- (setq djvu-buffer 'read
- buffer-undo-list t
- mode-line-buffer-identification
- (list 24 '(:eval (format "%s p%d" (buffer-name) (djvu-ref page)))))
- (set (make-local-variable 'revert-buffer-function) 'djvu-revert-buffer))
+ ;; The Read buffer is not editable. So do not create auto-save files.
+ (setq buffer-auto-save-file-name nil ; permanent buffer-local
+ djvu-buffer 'read
+ buffer-undo-list t)
+ (let ((fmt (concat (car (propertized-buffer-identification "%s"))
+ " p%d/%d")))
+ (setq mode-line-buffer-identification
+ `(24 (:eval (format ,fmt (buffer-name) (djvu-ref page)
+ (djvu-ref pagemax))))))
+ (setq-local revert-buffer-function #'djvu-revert-buffer)
+ (setq-local bookmark-make-record-function #'djvu-bookmark-make-record))
(defvar djvu-script-mode-map
(let ((km (make-sparse-keymap)))
@@ -1101,7 +1117,8 @@ This is a child of `lisp-mode-map'.")
(defvar djvu-font-lock-keywords
`((,(concat "^[ \t]*("
(regexp-opt '("background" "zoom" "mode" "align"
- "maparea" "metadata" "bookmarks" "xmp") t))
+ "maparea" "metadata" "bookmarks" "xmp")
+ t))
1 font-lock-keyword-face)
(,(concat "\\(?:[ \t]+\\|^\\|(\\)("
(regexp-opt '("url" "rect" "oval" "poly" "text" "line"
@@ -1118,14 +1135,23 @@ This is a child of `lisp-mode-map'.")
"Font lock keywords for Djvu buffers.")
(define-derived-mode djvu-script-mode lisp-mode "Djvu Script"
- "Mode for editing Djvu scripts."
- (setq mode-line-buffer-identification
- (list 24 '(:eval (if djvu-doc
- (format "%s p%d" (buffer-name)
- (djvu-ref page)) "")))
+ "Mode for editing Djvu scripts.
+The annotations, shared annotations and bookmark buffers use this mode."
+ ;; Fixme: we should create auto-save files for the script buffers.
+ ;; This requires suitable names for the auto-save files that should
+ ;; be derived from `buffer-file-name'.
+ (setq buffer-auto-save-file-name nil ; permanent buffer-local
fill-column djvu-fill-column
font-lock-defaults '(djvu-font-lock-keywords))
- (set (make-local-variable 'revert-buffer-function) 'djvu-revert-buffer))
+ (let* ((fmt1 (car (propertized-buffer-identification "%s")))
+ (fmt2 (concat fmt1 " p%d/%d")))
+ (setq mode-line-buffer-identification
+ `(24 (:eval (if djvu-doc
+ (format ,fmt2 (buffer-name) (djvu-ref page)
+ (djvu-ref pagemax))
+ (format ,fmt1 (buffer-name)))))))
+ (setq-local revert-buffer-function #'djvu-revert-buffer)
+ (setq-local bookmark-make-record-function #'djvu-bookmark-make-record))
(defvar djvu-outline-mode-map
(let ((km (make-sparse-keymap)))
@@ -1186,14 +1212,31 @@ This is a child of `special-mode-map'.")
(define-derived-mode djvu-outline-mode special-mode "Djvu OL"
"Mode for reading the outline of Djvu files."
- (setq djvu-buffer 'outline
- buffer-undo-list t
- mode-line-buffer-identification
- (list 24 '(:eval (format "%s p%d" (buffer-name) (djvu-ref page)))))
- (set (make-local-variable 'revert-buffer-function) 'djvu-revert-buffer))
+ ;; The Outline buffer is not editable. So do not create auto-save files.
+ (setq buffer-auto-save-file-name nil ; permanent buffer-local
+ djvu-buffer 'outline
+ buffer-undo-list t)
+ (let ((fmt (concat (car (propertized-buffer-identification "%s"))
+ " p%d/%d")))
+ (setq mode-line-buffer-identification
+ `(24 (:eval (format ,fmt (buffer-name) (djvu-ref page)
+ (djvu-ref pagemax))))))
+ (setq-local revert-buffer-function #'djvu-revert-buffer)
+ (setq-local bookmark-make-record-function #'djvu-bookmark-make-record))
;;; General Setup
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.djvu\\'" . djvu-dummy-mode))
+
+;;;###autoload
+(defun djvu-dummy-mode ()
+ "Djvu dummy mode for `auto-mode-alist'."
+ (djvu-find-file buffer-file-name nil nil t))
+
+;; FIXME: Add entry for `change-major-mode-hook'.
+;; How should this handle the plethora of buffers per djvu document?
+
(defun djvu-read-file-name ()
"Read file name of Djvu file.
The numeric value of `current-prefix-arg' is the page number."
@@ -1227,10 +1270,12 @@ from file."
(file-number (nthcdr 10 (file-attributes file)))
(dir (file-name-directory file))
(read-only (not (file-writable-p file)))
- (doc (let ((old-buf (find-buffer-visiting file-truename)))
- (and old-buf (buffer-local-value 'djvu-doc old-buf))))
+ (old-buf (if (equal buffer-file-truename file-truename)
+ (current-buffer)
+ (find-buffer-visiting file-truename)))
+ (doc (and old-buf (buffer-local-value 'djvu-doc old-buf)))
(old-bufs (and doc (mapcar 'buffer-live-p (djvu-buffers doc)))))
- ;; Sanity check (we should never need this)
+ ;; Sanity check. We should never need this.
(when (and old-bufs (memq nil old-bufs))
(message "Killing dangling Djvu buffers...")
(djvu-kill-doc doc)
@@ -1264,7 +1309,19 @@ from file."
(concat buf-basename
(nth n djvu-buffer-name-extensions))
dir))))
- (setq doc (fun 0))
+ (if old-buf
+ ;; This applies if `find-file-noselect' created OLD-BUF
+ ;; in order to visit FILE. Hence recycle OLD-BUF as Read
+ ;; buffer so that `find-file-noselect' can do its job.
+ ;; FIXME: this ignores `djvu-buffer-name-extensions'
+ ;; because renaming OLD-BUF would break `uniquify'.
+ (with-current-buffer old-buf
+ (let ((inhibit-read-only t)
+ (buffer-undo-list t))
+ (erase-buffer))
+ (setq buffer-file-coding-system 'prefer-utf-8)
+ (setq doc old-buf))
+ (setq doc (fun 0)))
(djvu-set read-buf doc doc)
(djvu-set text-buf (fun 1) doc)
(djvu-set annot-buf (fun 2) doc)
@@ -1272,7 +1329,7 @@ from file."
(djvu-set bookmarks-buf (fun 4) doc)
(djvu-set outline-buf (fun 5) doc)))
;; Of course, we have
- ;; `djvu-doc' = `djvu-doc-read-buf'
+ ;; `djvu-doc-read-buf' = `djvu-doc'
;; `djvu-doc-file' = `buffer-file-name'. Bother?
;; It seems Emacs does not like aliases for buffer-local variables.
(djvu-set file file doc)
@@ -1311,8 +1368,8 @@ from file."
;; We assume that all buffers for a Djvu document have the same
;; read-only status. Should we allow different values for the
;; buffers of one document? Or do we need a
`djvu-read-only-mode'?
- buffer-read-only read-only)
- (cd-absolute dir)
+ buffer-read-only read-only
+ default-directory dir)
(set-visited-file-modtime)
(add-hook 'post-command-hook 'djvu-modified nil t)
(add-hook 'kill-buffer-hook 'djvu-kill-doc-all nil t))
@@ -1421,7 +1478,6 @@ PAGE is re-initialized if we are already viewing it."
(if (or (buffer-modified-p (djvu-ref text-buf doc))
(buffer-modified-p (djvu-ref annot-buf doc)))
(djvu-save doc t))
- (djvu-set dpos nil doc)
;; We process PAGE unconditionally, even if it equals the page
;; currently displayed. Most often, PAGE equals the current page
;; if we want to redisplay PAGE.
@@ -1439,6 +1495,8 @@ PAGE is re-initialized if we are already viewing it."
doc))
(djvu-set history-forward nil doc)
(djvu-set page page doc)
+ ;; Fix me: Restore buffer positions if we revisit the same page.
+ (djvu-set read-pos nil doc)
(with-temp-buffer
(djvu-djvused doc t "-e"
(format "select %d; size; print-txt; print-ant;"
@@ -1462,22 +1520,18 @@ PAGE is re-initialized if we are already viewing it."
(skip-chars-forward " \t\n")
(let ((object (if (looking-at
"(\\(page\\|column\\|region\\|para\\|line\\|word\\|char\\)")
(read (current-buffer)))))
- (with-current-buffer (djvu-ref text-buf doc)
- (let (buffer-read-only)
- (erase-buffer)
- (djvu-insert-text object "")
- (insert "\n")
- (goto-char (point-min))
- (set-buffer-modified-p nil)
- (setq buffer-undo-list nil)))
-
- ;; Set up annotations buffer:
+ ;; Set up annotations buffer.
+ ;; This also initializes `djvu-doc-rect-list' that we need
+ ;; for propertizing the read buffer.
(save-restriction
(narrow-to-region (point) (point-max))
(djvu-init-annot (djvu-ref annot-buf doc) doc))
+ ;; Set up text buffer
+ (djvu-init-text object doc t)
+
;; Set up read buffer
- (djvu-init-read object doc)))))
+ (djvu-init-read object doc t)))))
(defalias 'djvu-goto-page 'djvu-init-page
"Goto PAGE of Djvu document DOC.")
@@ -1703,7 +1757,7 @@ The command `djvu-re-search-forward-continue' continues
to search forward."
(looking-at (regexp-quote (prin1-to-string old)))))
(error "`%s' not found" old))
(replace-match (prin1-to-string new) t t)))
- (if djvu-save-after-edit (djvu-save) (djvu-goto-read)))
+ (djvu-save-text))
(defun djvu-split-word (bpos)
"Split word at buffer position BPOS.
@@ -1711,11 +1765,10 @@ This command operates on the read buffer."
(interactive "d")
(let ((beg (djvu-property-beg bpos 'word))
(dpos (djvu-read-dpos bpos)))
- (djvu-set dpos dpos)
(with-current-buffer (djvu-ref text-buf)
(djvu-split-word-internal (djvu-goto-dpos 'word dpos)
(- bpos beg))))
- (if djvu-save-after-edit (djvu-save) (djvu-goto-read)))
+ (djvu-save-text))
(defun djvu-split-word-internal (wpos split)
"Split word at position WPOS at character position SPLIT.
@@ -1759,11 +1812,10 @@ This command operates on the read buffer."
(interactive "r")
(let ((bpos (djvu-read-dpos beg))
(epos (djvu-read-dpos (1- end))))
- (djvu-set dpos bpos)
(with-current-buffer (djvu-ref text-buf)
(djvu-merge-words-internal (djvu-goto-dpos 'word bpos)
(djvu-goto-dpos 'word epos))))
- (if djvu-save-after-edit (djvu-save) (djvu-goto-read)))
+ (djvu-save-text))
(defun djvu-merge-words-internal (beg end)
"Merge words between positions BEG and END.
@@ -1776,11 +1828,10 @@ This command operates on the text buffer."
(beginning-of-line)
(skip-chars-forward " \t")
(setq beg (point))
- (condition-case nil
- (while (< (point) end)
- (push (read (current-buffer)) words)
- (unless (eq 'word (caar words)) (error "Invalid")))
- (error (error "Syntax error in raw text")))
+ (while (< (point) end)
+ (push (read (current-buffer)) words)
+ (unless (eq 'word (caar words))
+ (error "Syntax error in raw text")))
(delete-region beg (point))
(let ((object (apply 'list 'word 0 0 0 0 (nreverse words))))
(djvu-text-zone object 0 (make-vector 3 nil))
@@ -1798,11 +1849,10 @@ This command operates on the read buffer."
;; merely run `djvu-merge-lines-internal' in the text buffer.
(let ((bpos (djvu-read-dpos beg))
(epos (djvu-read-dpos (1- end))))
- (djvu-set dpos bpos)
(with-current-buffer (djvu-ref text-buf)
(djvu-merge-lines-internal (djvu-goto-dpos 'word bpos)
(djvu-goto-dpos 'word epos))))
- (if djvu-save-after-edit (djvu-save) (djvu-goto-read)))
+ (djvu-save-text))
(defun djvu-merge-lines-internal (beg end)
"Merge lines between positions BEG and END.
@@ -1810,45 +1860,55 @@ This command operates on the text buffer."
(interactive "r")
;; Calculate proper value of END
(goto-char end)
- (beginning-of-line)
- (unless (looking-at "[ \t]*(line ")
- (re-search-backward "^[ \t]*(line ")
- (forward-sexp)
- (setq end (point)))
+ (unless (looking-at "[ \t]*(word ")
+ (re-search-backward "^[ \t]*(word "))
+ (forward-sexp)
+ (setq end (point))
;; Calculate proper value of BEG
(goto-char beg)
- (beginning-of-line)
- (unless (looking-at "[ \t]*(line ")
- (re-search-backward "^[ \t]*(line "))
+ (unless (looking-at "[ \t]*(word ")
+ (re-search-backward "^[ \t]*(word "))
(skip-chars-forward " \t")
(setq beg (point))
- (unless (< beg end) (error "Nothing to merge"))
- ;; Parsing fails if the words belong to different paragraphs,
- ;; regions or columns. We would have to determine the lowest common
- ;; object level of these words. Then we could possibly merge
- ;; everything (!) within this level
- (if (re-search-forward "^[ \t]*\\(?:para\\|region\\|column\\)" end t)
- (user-error "Cannot merge paragraphs, regions or columns"))
- (let (words)
- ;; Collect all words, ignore line headers
- (condition-case nil
- (while (<= (point) end)
- (cond ((looking-at "[ \t]*(word ")
- (push (read (current-buffer)) words))
- ((not (looking-at "[ \t]*(line "))
- (error "Invalid")))
- (forward-line))
- (error (error "Syntax error in raw text")))
- ;; Remove old words
- (goto-char beg)
- (delete-region beg end)
- ;; Re-insert words
- (let ((indent (delete-and-extract-region
- (line-beginning-position) (point)))
- (object (apply 'list 'line 0 0 0 0 (nreverse words))))
- (djvu-text-zone object 0 (make-vector 3 nil))
- (djvu-insert-text object indent)))
- (undo-boundary))
+ (unless (< beg end) (user-error "Nothing to merge"))
+ ;; The following fails if the zone levels of the lines we want to merge
+ ;; are different. For example:
+ ;; (line X X X X
+ ;; (word X X X X string)
+ ;; (word X X X X string))
+ ;; (para X X X X
+ ;; (line X X X X
+ ;; (word X X X X string)
+ ;; (word X X X X string)))
+ (atomic-change-group
+ (save-restriction
+ (narrow-to-region beg end)
+ (mapc (lambda (zone)
+ (goto-char (point-min))
+ (let ((re (format ")[\n\t\s]+(%s [0-9]+ [0-9]+ [0-9]+ [0-9]+"
zone)))
+ (while (re-search-forward re nil t)
+ (replace-match ""))))
+ '("column" "region" "para" "line"))
+ ;; Check that we got what we want.
+ (goto-char (point-min))
+ (while (> (point-max) (progn (skip-chars-forward "\n\t\s") (point)))
+ (if (looking-at "(word ")
+ (forward-sexp) ; may signal `scan-error'
+ (error "Syntax error: cannot merge"))))))
+
+(defun djvu-init-text (object &optional doc reset)
+ "Initialize Text buffer."
+ (with-current-buffer (djvu-ref text-buf doc)
+ (let ((dpos (unless reset (djvu-text-dpos nil doc)))
+ buffer-read-only)
+ (erase-buffer)
+ (djvu-insert-text object "")
+ (insert "\n")
+ (if (not reset)
+ (djvu-goto-dpos 'word dpos)
+ (goto-char (point-min))
+ (set-buffer-modified-p nil)
+ (setq buffer-undo-list nil)))))
(defun djvu-insert-text (object indent)
"Insert OBJECT into Djvu text buffer recursively using indentation INDENT."
@@ -1905,23 +1965,37 @@ This command operates on the text buffer."
(error "Syntax error in raw text (end of buffer)"))))))
object))
-(defun djvu-save-text (script &optional doc)
- "Save text of the Djvu document DOC.
-This dumps the content of DOC's text buffer into the djvused script
-file SCRIPT. DOC defaults to the current Djvu document."
+(defun djvu-save-text (&optional doc script)
+ "Save text of the Djvu document DOC. This updates the Read buffer for DOC.
+DOC defaults to the current Djvu document.
+If SCRIPT is non-nil, dump the text buffer into the djvused script file
SCRIPT."
+ (interactive)
(unless doc (setq doc djvu-doc))
- (let ((object (djvu-read-text doc)))
- (djvu-text-zone object 0 (make-vector 7 nil))
- ;; Update read buffer
- (djvu-init-read object doc)
- ;; FIXME: Update the higher text zones displayed in the text buffer
- ;; if we modified lower-level zones.
- (with-temp-buffer
- (setq buffer-file-coding-system 'utf-8)
- (insert (format "select %d\nremove-txt\nset-txt\n" (djvu-ref page doc)))
- (djvu-insert-text object "")
- (insert "\n.\n") ; see djvused command set-txt
- (write-region nil nil script t 0)))) ; append to SCRIPT
+ (let ((object1 (djvu-read-text doc))
+ (object2 (djvu-read-text doc))) ; true recursive copy of OBJECT1
+ ;; Re-initializing the text buffer blows up the undo list of this buffer.
+ ;; This step is only needed if we changed the text zones (e.g., when
+ ;; merging lines). So we check whether `djvu-text-zone' has changed
+ ;; OBJECT. For this, it is easier to read OBJECT twice than copying it
+ ;; recursively.
+ (djvu-text-zone object1 0 (make-vector 7 nil))
+ (unless (equal object1 object2)
+ (djvu-init-text object1 doc))
+ ;; Update read buffer. We do this even if the text buffer is not
+ ;; modified, as we may have undone a change in the text buffer that
+ ;; previously propagated also into the read buffer. The Read buffer
+ ;; has no undo list.
+ (djvu-init-read object1 doc)
+ ;; It is a bit of a hack to use this command for two rather different
+ ;; purposes. But we do not want to read OBJECT one more time.
+ (if script
+ (with-temp-buffer
+ (setq buffer-file-coding-system 'utf-8)
+ (insert (format "select %d\nremove-txt\nset-txt\n"
+ (djvu-ref page doc)))
+ (djvu-insert-text object1 "")
+ (insert "\n.\n") ; see djvused command set-txt
+ (write-region nil nil script t 0))))) ; append to SCRIPT
(defun djvu-text-zone (object depth zones)
"Evaluate zones for text OBJECT recursively."
@@ -2012,16 +2086,19 @@ BUFFER defaults to `djvu-script-buffer'. If BUFFER is
t, use current buffer."
;;; Djvu Read mode
-(defun djvu-init-read (object &optional doc)
+(defun djvu-init-read (object &optional doc reset)
(with-current-buffer (djvu-ref read-buf doc)
(let ((djvu-rect-list (djvu-ref rect-list doc))
+ (dpos (unless reset (djvu-read-dpos nil doc)))
buffer-read-only djvu-last-rect)
(erase-buffer)
(djvu-insert-read object)
- (djvu-insert-read-prop))
+ (djvu-insert-read-prop)
+ (if reset
+ (goto-char (point-min))
+ (djvu-goto-read dpos)))
(set-buffer-modified-p nil)
(setq buffer-read-only t)
- (djvu-goto-read (djvu-ref dpos doc))
(djvu-image)))
(defun djvu-insert-read (object)
@@ -2100,7 +2177,8 @@ BUFFER defaults to `djvu-script-buffer'. If BUFFER is t,
use current buffer."
"Return Djvu position of POINT in Djvu Read buffer.
This is either a list (XMIN YMIN XMAX YMAX) or (X Y)."
(with-current-buffer (djvu-ref read-buf doc)
- (cond ((and djvu-image-mode (djvu-ref dpos doc)))
+ (cond ((and djvu-image-mode
+ (djvu-ref read-pos doc)))
((= (point-min) (point-max))
;; An empty djvu page gives us something like (page 0 0 0 0 "")
;; Take the center of an empty page
@@ -2108,7 +2186,7 @@ This is either a list (XMIN YMIN XMAX YMAX) or (X Y)."
(/ (cdr (djvu-ref pagesize doc)) 2)))
(t
(unless point
- (setq point (if djvu-image-mode (point-min) (point))))
+ (setq point (point)))
;; Things get rather complicated if the text does not contain
;; separate words.
(or (get-text-property point 'word)
@@ -2123,69 +2201,88 @@ This is either a list (XMIN YMIN XMAX YMAX) or (X Y)."
(/ (cdr (djvu-ref pagesize doc)) 2)))))))
(defun djvu-mean-dpos (dpos)
- "For Djvu position DPOS return mean coordinates (X Y)."
- ;; This works both for DPOS being vectors and lists.
+ "For Djvu position DPOS return mean coordinates (X Y).
+DPOS is a list or vector (XMIN YMIN XMAX YMAX)."
(if (elt dpos 2)
(list (/ (+ (elt dpos 0) (elt dpos 2)) 2)
(/ (+ (elt dpos 1) (elt dpos 3)) 2))
dpos))
+(defsubst djvu-dist (width height)
+ (+ (* width width) (* height height)))
+
(defun djvu-goto-dpos (object dpos)
"Go to OBJECT at position DPOS in the text or annotation buffer.
If found, return corresponding buffer position.
-Otherwise, go to beginning of buffer and return nil."
+Otherwise, do nothing and return nil."
;; This code relies on the fact that we have all coordinates
;; in the format (xmin ymin xmax ymax) instead of the format
;; (xmin ymin width height) used by djvused for maparea annotations.
- (goto-char (point-min))
- (or (and (elt dpos 2)
- (re-search-forward (concat "\\<" (symbol-name object) "\\>[ \t\n]+"
- (mapconcat 'number-to-string dpos "[
\t\n+]")
- "\\( +\"\\)?") nil t))
- (let* ((re (concat "\\<" (symbol-name object) "\\> +"
- (mapconcat 'identity
- (make-list 4 "\\([[:digit:]]+\\)") " +")
- "\\( +\"\\)?"))
- (dpos (djvu-mean-dpos dpos))
- (x (nth 0 dpos))
- (y (nth 1 dpos))
- done)
- (goto-char (point-min))
- (while (and (not done)
- (re-search-forward re nil t))
- (let ((x1 (djvu-match-number 1))
- (x2 (djvu-match-number 3))
- (y1 (djvu-match-number 2))
- (y2 (djvu-match-number 4)))
- (setq done (and (<= x1 x x2)
- (<= y1 y y2)))))
- (if done (point)
- (goto-char (point-min))
- nil))))
-
-(defsubst djvu-dist (width height)
- (+ (* width width) (* height height)))
+ (cond ((not dpos) nil) ; DPOS is nil, do nothing, return nil
+
+ ((elt dpos 2) ; DPOS is a list or vector (XMIN YMIN XMAX YMAX)
+ (goto-char (point-min))
+ (or (re-search-forward (format "\\<%s\\>[ \t\n]+%s\\([ \t\n]+\"\\)?"
+ object
+ (mapconcat 'number-to-string dpos
+ "[ \t\n]+"))
+ nil t)
+ ;; try again, using the mean value of DPOS
+ (djvu-goto-dpos object (djvu-mean-dpos dpos))))
+
+ (t ; DPOS is a list (X Y)
+ ;; Look for OBJECT with either
+ ;; - DPOS inside OBJECT -> exact match
+ ;; - OBJECT nearest to DPOS -> approximate match
+ ;; The latter always succeeds.
+ (let* ((re (format "\\<%s\\>[ \t\n]+%s\\([ \t\n]+\"\\)?"
+ object
+ (mapconcat 'identity
+ (make-list 4 "\\([[:digit:]]+\\)")
+ "[ \t\n]+")))
+ (x (nth 0 dpos)) (y (nth 1 dpos))
+ (x2 (- (* 2 x))) (y2 (- (* 2 y)))
+ (good-dist (* 4 (djvu-dist (car (djvu-ref pagesize))
+ (cdr (djvu-ref pagesize)))))
+ (good-pnt (point-min))
+ pnt dist)
+ (goto-char (point-min))
+ (while (and (not (zerop good-dist))
+ (setq pnt (re-search-forward re nil t)))
+ (let ((xmin (djvu-match-number 1)) (ymin (djvu-match-number 2))
+ (xmax (djvu-match-number 3)) (ymax (djvu-match-number 4)))
+ (if (and (<= xmin x xmax) (<= ymin y ymax))
+ (setq good-dist 0 good-pnt pnt) ; exact match
+ (setq dist (djvu-dist (+ xmin xmax x2) (+ ymin ymax y2)))
+ (if (< dist good-dist)
+ (setq good-pnt pnt good-dist dist))))) ; approximate match
+ (goto-char good-pnt)
+ (if (/= good-pnt (point-min)) good-pnt)))))
(defun djvu-goto-read (&optional dpos)
"Go to buffer position in Read buffer corresponding to Djvu position DPOS.
Return corresponding buffer position."
- (unless dpos (setq dpos (djvu-ref dpos)))
(with-current-buffer (djvu-ref read-buf)
- (goto-char (point-min))
- (cond ((not dpos) nil) ; DPOS is nil, do nothing, return nil
+ (cond (djvu-image-mode
+ (djvu-set read-pos dpos)
+ (point-min))
+ ((not dpos) nil) ; DPOS is nil, do nothing, return nil
- ((elt dpos 2) ; DPOS is a list (XMIN YMIN XMAX YMAX)
+ ((elt dpos 2) ; DPOS is a list or vector (XMIN YMIN XMAX YMAX)
;; Go to the buffer position of the first word inside DPOS.
(let ((pnt (point-min))
(xmin (elt dpos 0)) (ymin (elt dpos 1))
(xmax (elt dpos 2)) (ymax (elt dpos 3))
word done)
- (while (if (and (setq word (djvu-mean-dpos
- (get-text-property pnt 'word)))
- (<= xmin (nth 0 word)) (<= (nth 0 word) xmax)
- (<= ymin (nth 1 word)) (<= (nth 1 word) ymax))
- (not (setq done t)) ; terminate successfully
- (setq pnt (next-single-property-change pnt 'word))))
+ (goto-char (point-min))
+ (while (progn ; Do while
+ (setq done
+ (and (setq word (djvu-mean-dpos
+ (get-text-property pnt 'word)))
+ (<= xmin (nth 0 word) xmax)
+ (<= ymin (nth 1 word) ymax)))
+ (and (not done)
+ (setq pnt (next-single-property-change pnt
'word)))))
(if done
(goto-char pnt)
;; try again, using the mean value of DPOS
@@ -2195,23 +2292,27 @@ Return corresponding buffer position."
;; Look for word with either
;; - DPOS inside word -> exact match
;; - word nearest to DPOS -> approximate match
- (let ((hpos (nth 0 dpos)) (vpos (nth 1 dpos))
- (good-dist (djvu-dist (car (djvu-ref pagesize))
- (cdr (djvu-ref pagesize))))
- (pnt (point-min)) (good-pnt (point-min))
- word dist)
- (while (progn
+ ;; The latter always succeeds.
+ (let* ((x (nth 0 dpos)) (y (nth 1 dpos))
+ (x2 (- (* 2 x))) (y2 (- (* 2 y)))
+ (good-dist (* 4 (djvu-dist (car (djvu-ref pagesize))
+ (cdr (djvu-ref pagesize)))))
+ (pnt (point-min)) (good-pnt (point-min))
+ word dist)
+ (goto-char (point-min))
+ (while (progn ; Do while
(when (setq word (get-text-property pnt 'word))
- (if (and (<= (aref word 0) hpos (aref word 2))
- (<= (aref word 1) vpos (aref word 3)))
+ (if (and (<= (aref word 0) x (aref word 2))
+ (<= (aref word 1) y (aref word 3)))
(setq good-dist 0 good-pnt pnt) ; exact match
- (setq dist (djvu-dist (- (/ (+ (aref word 0) (aref
word 2)) 2) hpos)
- (- (/ (+ (aref word 1) (aref
word 3)) 2) vpos)))
+ (setq dist (djvu-dist (+ (aref word 0) (aref word 2)
x2)
+ (+ (aref word 1) (aref word 3)
y2)))
(if (< dist good-dist)
(setq good-pnt pnt good-dist dist)))) ;
approximate match
(and (not (zerop good-dist))
(setq pnt (next-single-property-change pnt
'word)))))
- (goto-char good-pnt))))))
+ (goto-char good-pnt)
+ (if (/= good-pnt (point-min)) good-pnt))))))
;;; Djvu Annotation mode
@@ -2416,8 +2517,6 @@ Interactively, the command `djvu-mouse-text-area' in
`djvu-image-mode'
is usually easier to use."
(interactive (djvu-interactive-text-area))
(setq area (djvu-bound-area area))
- ;; Record position where annotation was made.
- (djvu-set dpos (djvu-mean-dpos area))
(with-current-buffer (djvu-ref annot-buf)
(goto-char (point-max))
(insert (format "(maparea %S\n %S\n "
@@ -2483,7 +2582,7 @@ With prefix LEFT mark left of beginning of line."
(let ((dpos (djvu-dpos))
(doc djvu-doc))
(with-current-buffer (djvu-ref annot-buf doc)
- (if (and dpos (djvu-goto-dpos 'rect dpos))
+ (if (djvu-goto-dpos 'rect dpos)
(djvu-update-url-internal url color opacity border)
(user-error "No object to update")))))
@@ -2609,22 +2708,13 @@ these elements are merged into one."
(pop areas)))))
areas)
-(defvar djvu-rect-area-nodups nil
- "If non-nil `djvu-rect-area' does not create multiple rects for same areas.")
-
(defun djvu-rect-area (url comment rects &optional color opacity border)
"Using URL and COMMENT, highlight RECTS.
The elements in the list RECTS are 4-element sequences of coordinates
each defining a rect area for djvused."
- (setq rects (mapcar 'djvu-bound-area (djvu-merge-areas rects)))
- ;; Record position where annotation was made.
- (let ((posl (mapcar 'djvu-mean-dpos rects))
- (n (length rects)))
- (djvu-set dpos (list (/ (apply '+ (mapcar 'car posl)) n)
- (/ (apply '+ (mapcar 'cadr posl)) n))))
(setq rects (mapcar (lambda (rect) (apply 'format "(rect %d %d %d %d)"
- rect))
- rects))
+ (djvu-bound-area rect)))
+ (djvu-merge-areas rects)))
;; Insert in Annotations buffer.
(with-current-buffer (djvu-ref annot-buf)
(unless (and djvu-rect-area-nodups
@@ -2934,8 +3024,8 @@ file SCRIPT. DOC defaults to the current Djvu document."
(djvu-convert-hash t)
(write-region nil nil script t 0) ; append to SCRIPT
;; It is not all correct to ignore rect-list for shared
- ;; annotations. It should really go into a separate slot
- ;; shared-rect-list of djvu-doc, so that then we can merge
+ ;; annotations. It should really go into a separate variable
+ ;; `djvu-doc-shared-rect-list', so that then we can merge
;; these for all pages.
(unless shared
(djvu-set rect-list (apply 'nconc rect-list) doc))))))
@@ -3037,9 +3127,9 @@ Return nil if no such object can be found."
(let ((dpos (djvu-dpos))
(doc djvu-doc))
(with-current-buffer (djvu-ref annot-buf doc)
- (if (and dpos (djvu-goto-dpos 'rect dpos))
+ (if (djvu-goto-dpos 'rect dpos)
(djvu-update-color-internal color)
- (error "No object to update")))))
+ (user-error "No object to update")))))
(defun djvu-update-color-internal (color)
"Update color attribute of Djvu maparea to COLOR.
@@ -3383,11 +3473,6 @@ file SCRIPT. DOC defaults to the current Djvu document."
;;; Image minor mode
-;; The image slot of `djvu-doc' is a list:
-;; the first element is the page number corresponding to the image,
-;; the second element is the magnification
-;; the remaining elements specify the image itself.
-
(defmacro djvu-with-event-buffer (event &rest body)
"With buffer of EVENT current, evaluate BODY."
(declare (indent 1))
@@ -3431,7 +3516,16 @@ file SCRIPT. DOC defaults to the current Djvu document."
;;
("+" . djvu-image-zoom-in)
("-" . djvu-image-zoom-out))
- (djvu-image))
+ (if (and djvu-image-mode
+ (not (get-text-property (point-min) 'display)))
+ ;; Remember DPOS if we enable `djvu-image-mode'.
+ (djvu-set read-pos (let (djvu-image-mode)
+ (djvu-read-dpos))))
+ (let ((tmp (and (not djvu-image-mode)
+ (get-text-property (point-min) 'display))))
+ (djvu-image)
+ ;; Go to DPOS if we disable `djvu-image-mode'.
+ (if tmp (djvu-goto-read (djvu-ref read-pos)))))
(defun djvu-image (&optional isize)
"If `djvu-image-mode' is enabled, display image of current Djvu page.
@@ -3441,13 +3535,9 @@ Otherwise remove the image."
;; in particular, for the "bare" calls of `djvu-image' by
;; `djvu-image-zoom-in' and `djvu-image-zoom-out'.
(if (not djvu-image-mode)
- (let (buffer-read-only)
- (remove-text-properties (point-min) (point-max) '(display nil))
- (djvu-goto-read))
- (unless (get-text-property (point-min) 'display)
- ;; Remember buffer position
- (let (djvu-image-mode)
- (djvu-set dpos (djvu-read-dpos))))
+ (if (get-text-property (point-min) 'display)
+ (let (buffer-read-only)
+ (remove-text-properties (point-min) (point-max) '(display nil))))
;; Update image if necessary.
(if (or (not (eq (djvu-ref page) (car (djvu-ref image))))
(and isize
@@ -3593,11 +3683,14 @@ Otherwise remove the image."
(_ (if (equal size '(0 . 0))
(error "See Emacs bug#18839 (GNU Emacs 24.4)")))
(width (/ (float (car (djvu-ref pagesize))) (car size)))
- (height (/ (float (cdr (djvu-ref pagesize))) (cdr size))))
- (list (round (* (if sorted (min x1 x2) x1) width))
- (round (* (- (cdr size) (if sorted (max y1 y2) y1)) height))
- (round (* (if sorted (max x1 x2) x2) width))
- (round (* (- (cdr size) (if sorted (min y1 y2) y2)) height)))))
+ (height (/ (float (cdr (djvu-ref pagesize))) (cdr size)))
+ (area
+ (list (round (* (if sorted (min x1 x2) x1) width))
+ (round (* (- (cdr size) (if sorted (max y1 y2) y1)) height))
+ (round (* (if sorted (max x1 x2) x2) width))
+ (round (* (- (cdr size) (if sorted (min y1 y2) y2)) height)))))
+ (djvu-set read-pos (djvu-mean-dpos area))
+ area))
(defun djvu-mouse-rect-area (event)
(interactive "e")
@@ -3659,7 +3752,6 @@ Otherwise remove the image."
(defun djvu-line-area (url text line &optional border arrow width lineclr)
;; Record position where annotation was made.
- (djvu-set dpos (djvu-mean-dpos line))
(with-current-buffer (djvu-ref annot-buf)
(goto-char (point-max))
;; It seems that TEXT is ignored by djview.
@@ -3841,7 +3933,8 @@ This uses the command \"djvused doc.djvu -e ls\"."
(erase-buffer)
(djvu-djvused doc t "-e" "ls"))
(set-buffer-modified-p nil)
- (setq buffer-read-only t))
+ (setq buffer-read-only t)
+ (goto-char (point-min)))
(pop-to-buffer buffer)))
;;;###autoload
@@ -3918,6 +4011,65 @@ With prefix OUTLINE non-nil remove Outline, too."
"-s")
(djvu-init-page nil doc)))
+;;;; Emacs bookmark integration (inspired by doc-view.el)
+
+(declare-function bookmark-make-record-default "bookmark"
+ (&optional no-file no-context posn))
+(declare-function bookmark-prop-get "bookmark" (bookmark prop))
+(declare-function bookmark-get-filename "bookmark" (bookmark))
+(declare-function bookmark-get-front-context-string "bookmark" (bookmark))
+(declare-function bookmark-get-rear-context-string "bookmark" (bookmark))
+(declare-function bookmark-get-position "bookmark" (bookmark))
+
+(defun djvu-bookmark-make-record ()
+ (nconc (bookmark-make-record-default)
+ `((page . ,(djvu-ref page))
+ (d-buffer . ,djvu-buffer)
+ (handler . djvu-bookmark-handler))))
+
+;; Adapted from `bookmark-default-handler'.
+;;;###autoload
+(defun djvu-bookmark-handler (bmk)
+ "Handler to jump to a particular bookmark location in a djvu document.
+BMK is a bookmark record, not a bookmark name (i.e., not a string).
+Changes current buffer and point and returns nil, or signals a `file-error'."
+ (let ((file (bookmark-get-filename bmk))
+ (buf (bookmark-prop-get bmk 'buffer))
+ (d-buffer (bookmark-prop-get bmk 'd-buffer))
+ (page (bookmark-prop-get bmk 'page))
+ (forward-str (bookmark-get-front-context-string bmk))
+ (behind-str (bookmark-get-rear-context-string bmk))
+ (pos (bookmark-get-position bmk)))
+ (set-buffer
+ (cond
+ ((and file (file-readable-p file) (not (buffer-live-p buf)))
+ (find-file-noselect file))
+ ;; No file found. See if buffer BUF has been created.
+ ((and buf (get-buffer buf)))
+ (t ;; If not, raise error.
+ (signal 'bookmark-error-no-filename (list 'stringp file)))))
+ (if page (djvu-goto-page page))
+ (if d-buffer
+ (set-buffer
+ (pcase d-buffer
+ (`read (djvu-ref read-buf))
+ (`text (djvu-ref text-buf))
+ (`annot (djvu-ref annot-buf))
+ (`shared (djvu-ref shared-buf))
+ (`bookmarks (djvu-ref bookmarks-buf))
+ (`outline (djvu-ref outline-buf)))))
+ (if pos (goto-char pos))
+ ;; Go searching forward first. Then, if forward-str exists and
+ ;; was found in the file, we can search backward for behind-str.
+ ;; Rationale is that if text was inserted between the two in the
+ ;; file, it's better to be put before it so you can read it,
+ ;; rather than after and remain perhaps unaware of the changes.
+ (when (and forward-str (search-forward forward-str (point-max) t))
+ (goto-char (match-beginning 0)))
+ (when (and behind-str (search-backward behind-str (point-min) t))
+ (goto-char (match-end 0)))
+ nil))
+
(provide 'djvu)
;;; djvu.el ends here
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [elpa] master 2eedb81: * packages/djvu/djvu.el: Release v1.1.,
Roland Winkler <=