[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/djvu 071c8ab168: Release djvu.el v1.1.2
From: |
Roland Winkler |
Subject: |
[elpa] externals/djvu 071c8ab168: Release djvu.el v1.1.2 |
Date: |
Sat, 16 Apr 2022 22:54:32 -0400 (EDT) |
branch: externals/djvu
commit 071c8ab168588897475899c46eaa16e70141db8c
Author: Roland Winkler <winkler@gnu.org>
Commit: Roland Winkler <winkler@gnu.org>
Release djvu.el v1.1.2
---
djvu.el | 1066 ++++++++++++++++++++++++++++++++++++++-------------------------
1 file changed, 644 insertions(+), 422 deletions(-)
diff --git a/djvu.el b/djvu.el
index 99154d03f4..85ddfa9138 100644
--- a/djvu.el
+++ b/djvu.el
@@ -1,10 +1,10 @@
;;; djvu.el --- Edit and view Djvu files via djvused -*- lexical-binding: t -*-
-;; Copyright (C) 2011-2020 Free Software Foundation, Inc.
+;; Copyright (C) 2011-2022 Free Software Foundation, Inc.
;; Author: Roland Winkler <winkler@gnu.org>
;; Keywords: files, wp
-;; Version: 1.1.1
+;; Version: 1.1.2
;; 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
@@ -139,6 +139,18 @@
;;; News:
+;; v1.1.2:
+;; - Support changing the mode of a buffer visiting a Djvu document.
+;;
+;; - Support `doc-view-toggle-display' with `major-mode-suspend'.
+;;
+;; - Selecting the background color "transparent" removes the
+;; background color attribute.
+;;
+;; - New options `djvu-image-zoom' and `djvu-ascenders-re'.
+;;
+;; - Bug fixes.
+;;
;; v1.1.1:
;; - Support text and image scrolling similar to `doc-view-mode'.
;; New option `djvu-continuous'.
@@ -204,20 +216,20 @@
;;; Djvu internals (see Sec. 8.3.4.2.3.1 of djvu3spec.djvu)
;;
-;; Supported area attributes rect oval poly line text
+;; Supported area attributes rect text oval line poly
;; (none)/(xor)/(border c) X X X X X
;; (shadow_* t) X
-;; (border_avis) X X X
+;; (border_avis) X X X
;; (hilite color) / (opacity o) X
;; (arrow) / (width w) / (lineclr c) X
-;; (backclr c) / (textclr c) / (pushpin) X
+;; (backclr c) / (textclr c) / (pushpin) X
;;
;; c = #RRGGBB t = thickness (1..32)
;; o = opacity = 0..200 (yes)
;;
;; zones: page, column, region, para, line, word, and char
+;; areas: rect, text, oval, line, and poly
-(require 'button)
(require 'image-mode)
(eval-when-compile
(require 'cl-lib))
@@ -263,8 +275,9 @@
("magenta" . "#FF00FF") ; 5
("purple" . "#7F60FF") ; 6
("cyan" . "#00FFFF") ; 7
- ("white" . "#FFFFFF") ; 8
- ("black" . "#000000")); 9
+ ("pink" . "#FF6060") ; 8
+ ("white" . "#FFFFFF") ; 9
+ ("black" . "#000000")); 10
"Alist of colors for highlighting."
:group 'djvu
:type '(repeat (cons (string) (string))))
@@ -362,6 +375,11 @@ Used by `djvu-region-string'."
:group 'djvu
:type 'boolean)
+(defcustom djvu-image-zoom 1.2
+ "Zoom factor for images."
+ :group 'djvu
+ :type 'number)
+
(defcustom djvu-descenders-re "[(),;Qgjpqy]" ; some fonts also `J' and `f'
;; https://en.wikipedia.org/wiki/Descender
"Regexp matching any descending characters or nil.
@@ -374,8 +392,41 @@ slightly below the baseline."
:group 'djvu
:type '(choice regexp (const nil)))
+(defcustom djvu-ascenders-re "[^-,.;:acegm-su-z\s]"
+ "Regexp matching ascending characters or nil, see `djvu-descenders-re'."
+ :group 'djvu
+ :type '(choice regexp (const nil)))
+
;; Internal variables
+(defvar djvu-coords-re
+ (format "\\(?2:%s\\)"
+ (mapconcat (lambda (i) (format "\\(?%d:-?[0-9]+\\)" i))
+ '(3 4 5 6) "[\s\t]+"))
+ "Regexp matching the coordinates of Djvu areas and zones.
+Substring 2: coordinates, 3-6: individual coordinates.")
+
+(defvar djvu-coord-xy-re
+ (mapconcat (lambda (i) (format "\\(?%d:-?[0-9]+\\)" i))
+ '(1 2) "[\s\t]+")
+ "Regexp matching pair of xy coordinates of Djvu maparea poly.
+Substrings 1-2: individual coordinates.")
+
+(defvar djvu-area-re
+ (format "(\\(?1:%s\\)[\s\t]+%s[)\s\t\n]"
+ (regexp-opt '("rect" "oval" "text" "line" "poly"))
+ djvu-coords-re)
+ "Regexp matching a Djvu area.
+Substring 1: area type, 2: coordinates, 3-6: individual coordinates.")
+
+(defvar djvu-zone-re
+ (format "[\s\t]*(\\(?1:%s\\)[\s\t]+%s[\s\t\n]+" ; omit closing `)'
+ (regexp-opt '("page" "column" "region" "para" "line"
+ "word" "char"))
+ djvu-coords-re)
+ "Regexp matching the beginning of a Djvu text zone.
+Substring 1: zone type, 2: coordinates, 3-6: individual coordinates.")
+
(defvar djvu-test nil
"If non-nil do not process / delete djvused scripts. Useful for testing.")
;; (setq djvu-test t) (setq djvu-test nil)
@@ -408,6 +459,31 @@ Bind this with `let' to select one of these schemes.")
(defvar djvu-image-mode) ; fully defined by `define-minor-mode' (buffer-local)
+(defvar djvu-init nil
+ "Non-nil during initialization of Djview mode.")
+
+(defvar djvu-color-attributes '(border hilite lineclr backclr textclr)
+ "List of color attributes known to Djvu. See djvused(1).")
+
+(defvar djvu-color-re
+ (concat "(" (regexp-opt (mapcar #'symbol-name djvu-color-attributes) t)
+ "[ \t\n]+\\(%s\\(%s[[:xdigit:]][[:xdigit:]]"
+ "[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]\\)%s\\)[ \t\n]*)")
+ "Format string to create a regular expression matching color attributes.")
+
+(defvar djvu-beg-object-re
+ (concat "^[\s\t]*(" (regexp-opt '("background" "zoom" "mode" "align"
+ "maparea" "metadata" "xmp" "bookmarks")
+ t)
+ "\\>")
+ "Regexp matching the beginning of a Djvu object. See djvused(1).")
+
+(defvar djvu-last-search-re nil
+ "Last regexp used by `djvu-re-search-forward'.")
+
+(defvar djvu-modified nil
+ "Let-bound in `djvu-mouse-drag-track-area'.")
+
;; See `ediff-defvar-local'
(defmacro djvu-defvar-local (var &optional val doc)
"Define VAR as a permanent-local variable, and return VAR."
@@ -487,6 +563,12 @@ Used in `djvu-image-mode' when we cannot go to this
position.")
"Image of current page of a Djvu document.
This is a list (PAGE-NUM MAGNIFICATION IMAGE).")
+(defvar-local djvu-doc-image-hscroll 0
+ "Number of columns by which a page image is scrolled from left margin.")
+
+(defvar-local djvu-doc-image-vscroll 0
+ "Amount by which a page image is scrolled vertically.")
+
;;; Helper functions and macros
;; For each Djvu document we have six buffers associated with this document
@@ -639,12 +721,10 @@ Preserve FILE if `djvu-test' is non-nil."
(defun djvu-scroll-up-command (&optional arg)
"Scroll text upward ARG lines; or near full screen if no ARG.
At the bottom of the page, when `djvu-continuous' is non-nil
-or prefix ARG is nil, go to the next page.
-Prefix ARG may take the same values as arg ARG of `scroll-up-command'.
-For historical reasons, this includes the range of values
-of `current-prefix-arg'."
+go to the next page.
+Prefix ARG may take the same values as arg ARG of `scroll-up-command'."
(interactive "^P") ; same as `scroll-up-command'
- (if (and (or djvu-continuous (not arg))
+ (if (and djvu-continuous
(= (window-end) (point-max))
(< (djvu-ref page) (djvu-ref pagemax)))
(djvu-next-page 1)
@@ -655,12 +735,10 @@ of `current-prefix-arg'."
(defun djvu-scroll-down-command (&optional arg)
"Scroll text downward ARG lines; or near full screen if no ARG.
At the top of the page, when `djvu-continuous' is non-nil
-or prefix ARG is nil, go to the previous page.
-Prefix ARG may take the same values as arg ARG of `scroll-down-command'.
-For historical reasons, this includes the range of values
-of `current-prefix-arg'."
+go to the previous page.
+Prefix ARG may take the same values as arg ARG of `scroll-down-command'."
(interactive "^P") ; same as `scroll-down-command'
- (if (and (or djvu-continuous (not arg))
+ (if (and djvu-continuous
(= (point-min) (window-start))
(< 1 (djvu-ref page)))
(progn
@@ -725,11 +803,6 @@ go to the previous page."
(djvu-goto-page (car history-forward))
(djvu-set history-forward (cdr history-forward))))
-(defun djvu-set-color-highlight (color)
- "Set color for highlighting based on `djvu-color-alist'."
- (interactive (list (completing-read "Color: " djvu-color-alist nil t)))
- (setq djvu-color-highlight color))
-
(defun djvu-kill-view (&optional doc all)
"Kill most recent Djview process for DOC.
If ALL is non-nil, kill all Djview processes."
@@ -751,7 +824,7 @@ This relies on `djvu-kill-doc-all' for doing the real work."
(interactive)
;; `djvu-kill-doc-all' will try to save our work and kill all djview
;; processes.
- (mapc 'kill-buffer (djvu-buffers doc)))
+ (mapc #'kill-buffer (djvu-buffers doc)))
(defvar djvu-in-kill-doc nil
"Non-nil if we are running `djvu-kill-doc-all'.")
@@ -769,14 +842,30 @@ so that killing the current buffer kills all buffers
visiting `djvu-doc'."
(condition-case nil
(let ((doc djvu-doc))
(setq buffers (djvu-buffers doc))
- (unless (memq nil (mapcar 'buffer-live-p buffers))
+ (unless (memq nil (mapcar #'buffer-live-p buffers))
(djvu-save doc t))
(djvu-kill-view doc t))
(error nil))
;; A function in `kill-buffer-hook' should not kill the buffer
;; for which we called this hook in the first place, so that
;; other functions in this hook can do their job, too.
- (mapc 'kill-buffer (delq (current-buffer) buffers)))))
+ (mapc #'kill-buffer (delq (current-buffer) buffers)))))
+
+(defun djvu-change-major-mode ()
+ "Clean up Djvu mode buffers and hooks.
+Djvu mode puts this into `change-major-mode-hook'."
+ (unless djvu-init
+ (djvu-kill-doc-all)
+ ;; These local variables are permanent local
+ (kill-local-variable 'kill-buffer-hook)
+ (kill-local-variable 'djvu-doc)
+ (kill-local-variable 'revert-buffer-function)
+ (kill-local-variable 'write-file-functions)
+ (let ((inhibit-read-only t)
+ (buffer-undo-list t))
+ (insert-file-contents-literally buffer-file-name t nil nil t))
+ (setq buffer-undo-list nil
+ buffer-read-only (not (file-writable-p buffer-file-name)))))
(defun djvu-save (&optional doc query)
"Save Djvu DOC."
@@ -802,7 +891,8 @@ so that killing the current buffer kills all buffers
visiting `djvu-doc'."
(if (and annot-modified (not text-modified))
(djvu-init-read (djvu-read-text doc) doc))
(djvu-all-buffers doc
- (set-buffer-modified-p nil)))))
+ (set-buffer-modified-p nil))))
+ t) ; for `write-file-function'
(defun djvu-modified ()
"Mark Djvu Read and Outline buffers as modified if necessary.
@@ -891,15 +981,6 @@ the purpose of calling djvused is to update the Djvu file."
(djvu-all-buffers doc
(setq buffer-file-number file-number)))))))))
-(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:]]"
- "[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]\\)%s\\)[ \t\n]*)")
- "Format string to create a regular expression matching color attributes.")
-
;; The Emacs lisp reader gets confused by the Djvu color syntax with
;; symbols '#000000. So we temporarily convert these symbols to strings.
(defun djvu-convert-hash (&optional reverse)
@@ -961,17 +1042,32 @@ If INITIAL-INPUT is non-nil use string from REGION as
initial input."
(defun djvu-interactive-color (color)
"Return color specification for use in interactive calls.
The color is the Nth element of `djvu-color-alist'.
-Here N is `current-prefix-arg' if this is a number.
+Here N is `current-prefix-arg' if this is a non-negative number.
N is 1 - `current-prefix-arg' / 4 if the prefix is a cons,
that is, `C-u' yields N = 0.
-Arg COLOR defines the default when there is no prefix arg."
+Arg COLOR defines the default when there is no prefix arg.
+Return nil if `current-prefix-arg' is a negative number.
+See also `djvu-interactive-color-read'."
(let ((colnum (or (and (consp current-prefix-arg)
(1- (/ (car current-prefix-arg) 4)))
(and (integerp current-prefix-arg)
current-prefix-arg))))
- (if (and colnum (>= colnum (length djvu-color-alist)))
- (user-error "Color undefined"))
- (if colnum (car (nth colnum djvu-color-alist)) color)))
+ (cond ((not colnum) color) ; use default
+ ((>= colnum (length djvu-color-alist))
+ (user-error "Color undefined"))
+ ((<= 0 colnum)
+ (car (nth colnum djvu-color-alist))))))
+
+(defun djvu-interactive-color-read ()
+ "Read color interactively.
+The return value is the car of an element of `djvu-color-alist'
+or nil if the user selects \"transparent\".
+See also `djvu-interactive-color'."
+ (let ((color (completing-read "New Color: "
+ (cons '("transparent") djvu-color-alist )
+ nil t)))
+ (unless (string= color "transparent")
+ color)))
(defun djvu-page-url (&optional page dir doc)
"For Djvu DOC return the internal url for PAGE.
@@ -981,9 +1077,9 @@ This is the inverse of `djvu-url-page'."
(cdr (assq page (djvu-ref page-id doc)))
page))))
-(defun djvu-interactive-url (&optional color)
+(defun djvu-interactive-url (color)
"Return URL specification for use in interactive calls."
- (let ((fmt (format "(%s) URL: " (or color djvu-color-url)))
+ (let ((fmt (format "(%s) URL: " (or color "no color")))
val)
(while (not val)
(setq val (read-string fmt))
@@ -1007,28 +1103,29 @@ Return the new rgb color string.
If BACKGROUND is nil, use `djvu-color-background'.
If OPACITY is nil, use `djvu-opacity'.
If INVERT is non-nil apply inverse transformation."
- (let* ((color (if (string-match "\\`#" color) color
- (cdr (assoc color djvu-color-alist))))
- (background (if (and background (string-match "\\`#" background))
- background
- (cdr (assoc (or background djvu-color-background)
- djvu-color-alist))))
- (a (/ (float (or opacity djvu-opacity)) 200)) ; foreground
- (b (- 1 a))) ; background
- (if invert
+ (when color
+ (let* ((color (if (string-match "\\`#" color) color
+ (cdr (assoc color djvu-color-alist))))
+ (background (if (and background (string-match "\\`#" background))
+ background
+ (cdr (assoc (or background djvu-color-background)
+ djvu-color-alist))))
+ (a (/ (float (or opacity djvu-opacity)) 200)) ; foreground
+ (b (- 1 a))) ; background
+ (if invert
+ (cl-flet ((mix (beg end)
+ (max 0 (min #xFF
+ (round (/ (- (djvu-substring-number color
beg end 16)
+ (* b (djvu-substring-number
background beg end 16)))
+ a))))))
+ (format "#%02X%02X%02X"
+ (mix 1 3) (mix 3 5) (mix 5 7)))
(cl-flet ((mix (beg end)
(max 0 (min #xFF
- (round (/ (- (djvu-substring-number color beg end 16)
- (* b (djvu-substring-number background beg
end 16)))
- a))))))
+ (round (+ (* a (djvu-substring-number color
beg end 16))
+ (* b (djvu-substring-number
background beg end 16))))))))
(format "#%02X%02X%02X"
- (mix 1 3) (mix 3 5) (mix 5 7)))
- (cl-flet ((mix (beg end)
- (max 0 (min #xFF
- (round (+ (* a (djvu-substring-number color beg end 16))
- (* b (djvu-substring-number background beg end
16))))))))
- (format "#%02X%02X%02X"
- (mix 1 3) (mix 3 5) (mix 5 7))))))
+ (mix 1 3) (mix 3 5) (mix 5 7)))))))
;;; Djvu modes
@@ -1146,27 +1243,29 @@ This is a child of `special-mode-map'.")
(define-derived-mode djvu-read-mode special-mode "Djview"
"Mode for reading Djvu files."
- ;; 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)
- (if (boundp 'mwheel-scroll-up-function) ; not --without-x build
- (setq-local mwheel-scroll-up-function
- (lambda (&optional n)
- (if djvu-image-mode (djvu-image-scroll-up n)
- (djvu-scroll-up-command n)))))
- (if (boundp 'mwheel-scroll-down-function)
- (setq-local mwheel-scroll-down-function
- (lambda (&optional n)
- (if djvu-image-mode (djvu-image-scroll-down n)
- (djvu-scroll-down-command n))))))
+ (if (not djvu-init)
+ (djvu-init-mode) ; For `doc-view-toggle-display' and `major-mode-suspend'
+ ;; 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)
+ (if (boundp 'mwheel-scroll-up-function) ; not --without-x build
+ (setq-local mwheel-scroll-up-function
+ (lambda (&optional n)
+ (if djvu-image-mode (djvu-image-scroll-up n)
+ (djvu-scroll-up-command n)))))
+ (if (boundp 'mwheel-scroll-down-function)
+ (setq-local mwheel-scroll-down-function
+ (lambda (&optional n)
+ (if djvu-image-mode (djvu-image-scroll-down n)
+ (djvu-scroll-down-command n)))))))
(defvar djvu-script-mode-map
(let ((km (make-sparse-keymap)))
@@ -1192,6 +1291,7 @@ This is a child of `special-mode-map'.")
(define-key km "\C-c\C-q" 'djvu-quit-window)
(define-key km "\C-c\C-k" 'djvu-kill-doc)
(define-key km (kbd "C-c C-S-g") 'djvu-revert-buffer) ; [?\C-c ?\C-\S-g]
+
km)
"Keymap for Djvu Script Mode.
This is a child of `lisp-mode-map'.")
@@ -1238,7 +1338,9 @@ This is a child of `lisp-mode-map'.")
"arrow" "width" "lineclr"
"backclr" "textclr" "pushpin"
"page" "column" "region" "para" "line"
- "word" "char") t) ")")
+ "word" "char")
+ t)
+ ")")
1 font-lock-function-name-face)
;; url
(djvu-font-lock-url))
@@ -1247,21 +1349,23 @@ This is a child of `lisp-mode-map'.")
(define-derived-mode djvu-script-mode lisp-mode "Djvu Script"
"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))
- (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))
+ (if (not djvu-init)
+ (djvu-init-mode) ; For `doc-view-toggle-display' and `major-mode-suspend'
+ ;; 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))
+ (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)))
@@ -1322,31 +1426,34 @@ 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."
- ;; 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))
+ (if (not djvu-init)
+ (djvu-init-mode) ; For `doc-view-toggle-display' and `major-mode-suspend'
+ ;; 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))
+(add-to-list 'auto-mode-alist '("\\.djvu\\'" . djvu-init-mode))
;;;###autoload
-(defun djvu-dummy-mode ()
- "Djvu dummy mode for `auto-mode-alist'."
+(defun djvu-init-mode ()
+ "Dummy mode for initializing Djvu mode.
+This can be used as an element for `auto-mode-alist'.
+This can also be used if the current buffer visits a Djvu file
+using some other mode."
+ (interactive)
(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."
@@ -1371,26 +1478,33 @@ from file."
;; Djvu mode needs a local file. If FILE is located on a remote system,
;; you can use something like `file-local-copy' to edit FILE.
(if (file-remote-p file)
- (user-error "Cannot handle remote Djvu file `%s'" file))
+ (user-error "Cannot handle remote Djvu file `%s'" file))
(unless (and (file-regular-p file)
(file-readable-p file))
(user-error "Cannot open Djvu file `%s'" file))
+ (with-temp-buffer
+ (insert-file-contents-literally file nil 0 4)
+ (goto-char (point-min))
+ (unless (looking-at "\\`AT&T") ; magic number for Djvu documents
+ (user-error "`%s' not a Djvu document" file)))
(let* ((inhibit-quit t)
(buf-basename (file-name-nondirectory file))
(file-truename (abbreviate-file-name (file-truename file)))
(file-number (nthcdr 10 (file-attributes file)))
(dir (file-name-directory file))
(read-only (not (file-writable-p file)))
- (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)))))
+ (doc (if (equal buffer-file-truename file-truename)
+ (current-buffer)
+ (find-buffer-visiting file-truename)))
+ (old-bufs (and doc (buffer-local-value 'djvu-doc doc)
+ (mapcar #'buffer-live-p (djvu-buffers doc))))
+ (djvu-init t))
;; Sanity check. We should never need this.
(when (and old-bufs (memq nil old-bufs))
(message "Killing dangling Djvu buffers...")
- (djvu-kill-doc doc)
- (setq doc nil old-bufs nil)
+ (with-current-buffer doc
+ (djvu-kill-doc-all))
+ (setq old-bufs nil)
(message "Killing dangling Djvu buffers...Done")
(sit-for 2))
;; Do nothing if we are already visiting FILE such that all buffers
@@ -1420,18 +1534,22 @@ from file."
(concat buf-basename
(nth n djvu-buffer-name-extensions))
dir))))
- (if old-buf
+ (if doc
;; 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
+ ;; buffer. This applies also if we switch from some other
+ ;; mode to Djview mode.
+ (with-current-buffer doc
(let ((inhibit-read-only t)
(buffer-undo-list t))
(erase-buffer))
(setq buffer-file-coding-system 'prefer-utf-8)
- (setq doc old-buf))
+ ;; `rename-buffer' obeys uniquify.
+ (rename-buffer (concat buf-basename
+ (nth 0 djvu-buffer-name-extensions))
+ t))
+ ;; We need this when mimicking `find-file'
+ ;; so that FILE does not yet have a buffer.
(setq doc (fun 0)))
(djvu-set read-buf doc doc)
(djvu-set text-buf (fun 1) doc)
@@ -1469,21 +1587,26 @@ from file."
(setq djvu-buffer 'bookmarks
header-line-format '(:eval (djvu-header-line "bookmarks"))))
(djvu-all-buffers doc
+ ;; permanent buffer-local variables
(setq djvu-doc doc ; propagate DOC to all buffers
buffer-file-name file
;; A non-nil value of `buffer-file-truename' enables
file-locking,
;; see call of `lock_file' in `prepare_to_modify_buffer_1'
buffer-file-truename file-truename
buffer-file-number file-number
- buffer-file-read-only read-only
+ default-directory dir)
+ ;; other buffer-local stuff
+ (setq buffer-file-read-only read-only
;; 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
- default-directory dir)
+ ;; buffers of one document?
+ ;; Or do we need a `djvu-read-only-mode'?
+ buffer-read-only read-only)
+ (setq-local write-file-functions #'djvu-save)
(set-visited-file-modtime)
- (add-hook 'post-command-hook 'djvu-modified nil t)
- (add-hook 'kill-buffer-hook 'djvu-kill-doc-all nil t))
+ (add-hook 'post-command-hook #'djvu-modified nil t)
+ (add-hook 'kill-buffer-hook #'djvu-kill-doc-all nil t)
+ (add-hook 'change-major-mode-hook #'djvu-change-major-mode nil t))
(with-temp-buffer
(djvu-djvused doc t "-e"
@@ -1548,6 +1671,7 @@ from file."
(let ((object (read (current-buffer))))
(with-current-buffer (djvu-ref bookmarks-buf doc)
(let (buffer-read-only)
+ (erase-buffer)
(insert "(bookmarks")
(djvu-insert-bookmarks (cdr object) " ")
(insert ")\n")
@@ -1685,7 +1809,7 @@ into an undefined state."
(if page-id
(format "#%d" (car page-id))
(djvu-unresolve-url url))))
- (t url))) ; some other URL
+ (t "#1"))) ; some other URL (possibly empty string)
(t ; check whether URL can be resolved
(cond ((string-match "\\`#[0-9]+\\'" url)
(if (assq (djvu-substring-number url 1)
@@ -1821,15 +1945,10 @@ If prefix NEW is non-nil, always create a new Djview
process."
;;; Djvu Text mode
-(defvar djvu-last-search-re nil
- "Last regexp used by `djvu-re-search-forward'.")
-
(defun djvu-re-search-forward (regexp)
"Search forward for match for REGEXP.
-
Search case-sensitivity is determined by the value of the variable
`case-fold-search', which see.
-
The command `djvu-re-search-forward-continue' continues to search forward."
(interactive "sSearch (regexp): ")
(setq djvu-last-search-re regexp)
@@ -1996,7 +2115,7 @@ This command operates on the text buffer."
(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)))
+ (let ((re (format ")[\n\t\s]+(%s %s" zone djvu-coords-re)))
(while (re-search-forward re nil t)
(replace-match ""))))
'("column" "region" "para" "line"))
@@ -2025,9 +2144,10 @@ This command operates on the text buffer."
"Insert OBJECT into Djvu text buffer recursively using indentation INDENT."
(when object
(insert indent "("
- (mapconcat 'prin1-to-string
+ (mapconcat #'prin1-to-string
(list (nth 0 object) (nth 1 object) (nth 2 object)
- (nth 3 object) (nth 4 object)) " "))
+ (nth 3 object) (nth 4 object))
+ " "))
(let ((tail (nthcdr 5 object))
(indent (concat indent " ")))
(if (stringp (car tail))
@@ -2037,14 +2157,6 @@ This command operates on the text buffer."
(djvu-insert-text elt indent))
(insert ")")))))
-(defvar djvu-zone-re
- (concat "[ \t]*(\\("
- (regexp-opt '("page" "column" "region" "para" "line"
- "word" "char"))
- "\\)[ \t]+\\(\\(-?[0-9]+\\)[ \t]+\\(-?[0-9]+\\)"
- "[ \t]+\\(-?[0-9]+\\)[ \t]+\\(-?[0-9]+\\)\\)[ \t\n]+")
- "Regexp matching the beginning of a Djvu text zone.")
-
(defun djvu-text-dpos (&optional point doc)
"Return Djvu position of POINT in Djvu text buffer."
(with-current-buffer (djvu-ref text-buf doc)
@@ -2056,7 +2168,7 @@ This command operates on the text buffer."
(bobp)))
(forward-line -1))
(if zone
- (mapcar 'djvu-match-number '(3 4 5 6)))))))
+ (mapcar #'djvu-match-number '(3 4 5 6)))))))
(defun djvu-read-text (&optional doc)
"Read text of a Djvu document from text buffer."
@@ -2109,28 +2221,44 @@ If SCRIPT is non-nil, dump the text buffer into the
djvused script file SCRIPT."
(write-region nil nil script t 0))))) ; append to SCRIPT
(defun djvu-text-zone (object depth zones)
- "Evaluate zones for text OBJECT recursively."
- (cond ((stringp (nth 5 object))
- (aset zones depth (vector (nth 1 object) (nth 2 object)
- (nth 3 object) (nth 4 object))))
- (object
- (let ((depth1 (1+ depth))
- zone)
- (aset zones depth nil)
- (dolist (elt (nthcdr 5 object))
- (djvu-text-zone elt depth1 zones)
- (if (setq zone (aref zones depth))
- (let ((zone1 (aref zones depth1)))
- (aset zone 0 (min (aref zone 0) (aref zone1 0)))
- (aset zone 1 (min (aref zone 1) (aref zone1 1)))
- (aset zone 2 (max (aref zone 2) (aref zone1 2)))
- (aset zone 3 (max (aref zone 3) (aref zone1 3))))
- (aset zones depth (copy-sequence (aref zones depth1)))))
- (if (setq zone (aref zones depth))
- (setcdr object (apply 'list (aref zone 0) (aref zone 1)
- (aref zone 2) (aref zone 3)
- (nthcdr 5 object)))
- (error "No zone??"))))))
+ "Evaluate ZONES for text OBJECT recursively.
+This rearranges the tail of OBJECT destructively.
+Branches of OBJECT that point to empty strings are removed."
+ (if (stringp (nth 5 object))
+ ;; We set ZONES only if we have something nontrivial
+ (cond ((not (equal "" (nth 5 object)))
+ (aset zones depth (vector (nth 1 object) (nth 2 object)
+ (nth 3 object) (nth 4 object))))
+ ((zerop depth)
+ (setcdr object (list 0 0 0 0 ""))))
+ (let ((depth1 (1+ depth))
+ zone remove)
+ (aset zones depth nil)
+ (dolist (elt (nthcdr 5 object))
+ (aset zones depth1 nil)
+ (djvu-text-zone elt depth1 zones)
+ (let ((zone1 (aref zones depth1)))
+ (cond ((not zone1)
+ ;; ELT has no ZONE1 because it points to an empty string.
+ (push elt remove))
+ ((setq zone (aref zones depth))
+ (aset zone 0 (min (aref zone 0) (aref zone1 0)))
+ (aset zone 1 (min (aref zone 1) (aref zone1 1)))
+ (aset zone 2 (max (aref zone 2) (aref zone1 2)))
+ (aset zone 3 (max (aref zone 3) (aref zone1 3))))
+ (t (aset zones depth zone1)))))
+ (if remove
+ (let ((tail (nthcdr 4 object)))
+ (dolist (elt remove)
+ ;; `delq' removes all occurences of ELT from TAIL.
+ (setcdr tail (delq elt (cdr tail))))))
+ (cond ((setq zone (aref zones depth))
+ (setcdr object (append (mapcar #'identity zone)
+ (nthcdr 5 object))))
+ ((zerop depth)
+ (setcdr object (list 0 0 0 0 "")))
+ (t
+ (setcdr object nil))))))
(defun djvu-script-buffer (buffer)
"Return buffer for djvu script.
@@ -2155,6 +2283,7 @@ You get what you want."
;; Put this in a separate buffer!
(with-current-buffer buffer
(let ((buffer-undo-list t)
+ (djvu-init t)
buffer-read-only)
(djvu-script-mode)
(erase-buffer)
@@ -2210,7 +2339,7 @@ BUFFER defaults to `djvu-script-buffer'. If BUFFER is t,
use current buffer."
(djvu-goto-read dpos)))
(set-buffer-modified-p nil)
(setq buffer-read-only t)
- (djvu-image)))
+ (if djvu-image-mode (djvu-image))))
(defun djvu-insert-read (object)
"Display text OBJECT recursively."
@@ -2260,7 +2389,11 @@ BUFFER defaults to `djvu-script-buffer'. If BUFFER is
t, use current buffer."
(when djvu-last-rect
(let ((beg (nth 0 djvu-last-rect))
(end (nth 1 djvu-last-rect))
- (face `(face (:background ,(nth 5 djvu-last-rect))
+ (face `(face (:background ,(nth 5 djvu-last-rect)
+ ;; `make-button' puts a `button' overlay
+ ;; that overrides :foreground.
+ :foreground ,(readable-foreground-color
+ (nth 5 djvu-last-rect)))
help-echo ,(nth 4 djvu-last-rect))))
(if (or (eq t djvu-read-prop-newline)
(and (numberp djvu-read-prop-newline)
@@ -2335,7 +2468,7 @@ Otherwise, do nothing and return nil."
(goto-char (point-min))
(or (re-search-forward (format "\\<%s\\>[ \t\n]+%s\\([ \t\n]+\"\\)?"
object
- (mapconcat 'number-to-string dpos
+ (mapconcat #'number-to-string dpos
"[ \t\n]+"))
nil t)
;; try again, using the mean value of DPOS
@@ -2348,7 +2481,7 @@ Otherwise, do nothing and return nil."
;; The latter always succeeds.
(let* ((re (format "\\<%s\\>[ \t\n]+%s\\([ \t\n]+\"\\)?"
object
- (mapconcat 'identity
+ (mapconcat #'identity
(make-list 4 "\\([[:digit:]]+\\)")
"[ \t\n]+")))
(x (nth 0 dpos)) (y (nth 1 dpos))
@@ -2427,10 +2560,6 @@ Return corresponding buffer position."
;;; Djvu Annotation mode
-(defvar djvu-annot-re
- (concat "(" (regexp-opt '("background" "zoom" "mode" "align"
- "maparea" "metadata" "xmp") t) "\\>"))
-
(defun djvu-init-annot (buf doc &optional shared)
"Initialize Annotations buffer BUF of Djvu document DOC.
SHARED should be non-nil for a Shared Annotations buffer."
@@ -2438,17 +2567,18 @@ SHARED should be non-nil for a Shared Annotations
buffer."
(goto-char (point-min))
(let (object alist)
(while (progn (skip-chars-forward " \t\n") (not (eobp)))
- (if (looking-at djvu-annot-re)
+ (beginning-of-line)
+ (if (looking-at djvu-beg-object-re)
(push (read (current-buffer)) object)
(error "Unknown annotation `%s'" (buffer-substring-no-properties
(point) (line-end-position)))))
- ;; To simplify the editing of annotations, identify rect mapareas
- ;; sharing the same text string.
(dolist (elt object)
(if (not (eq 'maparea (car elt)))
(push elt alist)
- (cond ((memq (car (nth 3 elt)) '(rect oval)) ; rect and oval
+ ;; To simplify the editing of annotations, identify rect mapareas
+ ;; sharing the same text string.
+ (cond ((eq 'rect (car (nth 3 elt))) ; rect
(let ((area (djvu-area (nth 3 elt)))
e)
;; Remove area destructively.
@@ -2461,7 +2591,7 @@ SHARED should be non-nil for a Shared Annotations buffer."
(not (setq e (assoc elt alist))))
(push (cons elt (list area)) alist)
(setcdr e (cons area (cdr e))))))
- ((eq 'text (car (nth 3 elt))) ; text
+ ((memq (car (nth 3 elt)) '(text oval)) ; mapareas text, oval
(setcar (nthcdr 3 elt) (djvu-area (nth 3 elt)))
(push elt alist))
(t (push elt alist)))))
@@ -2484,8 +2614,8 @@ SHARED should be non-nil for a Shared Annotations buffer."
(let ((c (car elt)))
(insert (format "(maparea %S\n %S\n ("
(djvu-resolve-url (nth 1 c) doc) (nth 2 c))
- (mapconcat 'prin1-to-string (cdr elt) "\n ") ")\n
" ; rect and oval
- (mapconcat 'prin1-to-string (nthcdr 3 c) " ") ; rest
+ (mapconcat #'prin1-to-string (cdr elt) "\n ") ")\n
" ; rect and oval
+ (mapconcat #'prin1-to-string (nthcdr 3 c) " ") ;
rest
")")))
((eq 'metadata (car elt)) ; metadata
(insert "(metadata")
@@ -2494,9 +2624,9 @@ SHARED should be non-nil for a Shared Annotations buffer."
(insert ")"))
((not (eq 'maparea (car elt))) ; no maparea
(prin1 elt))
- ((memq (car (nth 3 elt)) '(text line)) ; maparea text, line
+ ((memq (car (nth 3 elt)) '(text oval line poly)) ; maparea
text, oval, line, poly
(insert (format "(maparea %S\n %S\n " (nth 1 elt) (nth 2 elt))
- (mapconcat 'prin1-to-string (nthcdr 3 elt) " ") ; rest
+ (mapconcat #'prin1-to-string (nthcdr 3 elt) " ") ;
rest
")"))
(t (error "Djvu maparea %s undefined" (car (nth 3 elt)))))
(insert "\n\n"))
@@ -2593,8 +2723,8 @@ If URL is an internal url, go to that page."
(let ((dpos (djvu-mean-dpos (djvu-read-dpos)))
(pagesize (djvu-ref pagesize))
(color (djvu-interactive-color djvu-color-highlight)))
- (list nil (read-string (format "(%s) Text: " color)
- nil nil nil djvu-inherit-input-method)
+ (list nil (read-string (format "(%s) Text: " (or color "no color"))
+ nil nil nil djvu-inherit-input-method)
(list (nth 0 dpos) (nth 1 dpos)
(+ (nth 0 dpos) (/ (car pagesize) 2))
(+ (nth 1 dpos) (/ (cdr pagesize) 30)))
@@ -2634,8 +2764,8 @@ is usually easier to use."
(or url "") (if comment (djvu-fill comment) ""))
(apply 'format "(text %d %d %d %d)" area)
(format " (%s)" (or border 'none))
- (djvu-insert-color "backclr" backclr)
- (djvu-insert-color "textclr" textclr)
+ (if backclr (djvu-insert-color "backclr" backclr) "")
+ (if textclr (djvu-insert-color "textclr" textclr) "")
(if pushpin " (pushpin)" "")
")\n\n")
(undo-boundary)))
@@ -2733,7 +2863,8 @@ With prefix LEFT mark left of beginning of line."
(let* ((color (djvu-interactive-color djvu-color-url))
(url (djvu-interactive-url color))
(comment (djvu-read-string
- (format "(%s, %s) Annotation: " url color)
+ (format "(%s, %s) Annotation: "
+ url (or color "no color"))
region)))
(list (car region) (cdr region) url comment color djvu-opacity 'xor))))
(djvu-rect-region beg end url comment color opacity border))
@@ -2743,7 +2874,8 @@ With prefix LEFT mark left of beginning of line."
(interactive
(djvu-with-region region
(let* ((color (djvu-interactive-color djvu-color-highlight))
- (comment (djvu-read-string (format "(%s) Annotation: " color)
+ (comment (djvu-read-string (format "(%s) Annotation: "
+ (or color "no color"))
region)))
(list (car region) (cdr region) nil comment color djvu-opacity 'none))))
@@ -2834,15 +2966,15 @@ each defining a rect area for djvused."
(unless (and djvu-rect-area-nodups
(save-excursion
(goto-char (point-min))
- (re-search-forward (mapconcat 'identity rects "[ \t\n]*")
+ (re-search-forward (mapconcat #'identity rects "[ \t\n]*")
nil t)))
(goto-char (point-max))
(insert (format "(maparea %S\n %S\n ("
(or url "") (if comment (djvu-fill comment) ""))
- (mapconcat 'identity rects "\n ")
+ (mapconcat #'identity rects "\n ")
")\n"
(djvu-insert-color "hilite" color)
- (if opacity (format " (opacity %s)" opacity) "")
+ (if (and color opacity) (format " (opacity %s)" opacity) "")
(format " (%s)" (or border 'none))
")\n\n")
(undo-boundary))))
@@ -2863,8 +2995,8 @@ This value of `fill-column' defaults to
`djvu-fill-column'."
"Toggle between Mapareas rect and text."
(interactive)
(let ((bounds (djvu-object-bounds))
- (rect-re "(rect \\(-?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+\\))")
- (text-re "(text \\(-?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+\\))")
+ (rect-re (format "(rect[\s\t]+%s)" djvu-coords-re))
+ (text-re (format "(text[\s\t]+%s)" djvu-coords-re))
(color-re (format djvu-color-re "#" "" "")))
(if (not bounds)
(user-error "No object to update")
@@ -2876,7 +3008,7 @@ This value of `fill-column' defaults to
`djvu-fill-column'."
(cond ((re-search-forward rect-re nil t) ; Maparea rect
(if (save-match-data (re-search-forward rect-re nil t))
(user-error "Only single rect can be converted to text"))
- (replace-match (format "text %s" (match-string 1)))
+ (replace-match (format "text %s" (match-string 2)))
(goto-char (point-min))
(let ((opacity
(if (re-search-forward " *(opacity \\([0-9]+\\))" nil
t)
@@ -2900,7 +3032,7 @@ This value of `fill-column' defaults to
`djvu-fill-column'."
;; to duplicate the job.
(let ((opacity (save-match-data
(read-number "Opacity: " djvu-opacity))))
- (replace-match (format "((rect %s))" (match-string 1)))
+ (replace-match (format "((rect %s))" (match-string 2)))
(goto-char (point-min))
;; Loop over matches of COLOR-RE as this general regexp
;; also matches elements that so far we do not care about.
@@ -2915,13 +3047,6 @@ This value of `fill-column' defaults to
`djvu-fill-column'."
(t
(user-error "Nothing to toggle"))))))))
-(defvar djvu-area-re
- (format "(%s \\(%s\\))"
- (regexp-opt '("rect" "oval" "text") t)
- (mapconcat (lambda (_) "\\(-?[0-9]+\\)") '(1 2 3 4) " "))
- "Regexp matching a Djvu area.
-Substring 1: area type, 2: coordinates, 3-6: individual coordinates.")
-
(defun djvu-resize-internal (step)
"Resize Djvu mapareas rect and text by STEP."
(interactive "nStep: ")
@@ -2933,6 +3058,8 @@ Substring 1: area type, 2: coordinates, 3-6: individual
coordinates.")
(narrow-to-region (car bounds) (cdr bounds))
(goto-char (point-min))
(while (re-search-forward djvu-area-re nil t)
+ (if (string= "poly" (match-string 1))
+ (user-error "Cannot resize maparea poly"))
(replace-match (format "%d %d %d %d"
(- (djvu-match-number 3) step)
(- (djvu-match-number 4) step)
@@ -2940,14 +3067,15 @@ Substring 1: area type, 2: coordinates, 3-6: individual
coordinates.")
(+ (djvu-match-number 6) step))
nil nil nil 2)))))))
-(defun djvu-shift-internal (shiftx shifty &optional all)
+(defun djvu-shift-internal (shiftx shifty &optional all scale)
"Shift Djvu mapareas rect and text by SHIFTX and SHIFTY.
With prefix ALL non-nil shift all mapareas of current page."
(interactive
- (let ((shift (mapcar 'string-to-number
+ (let ((shift (mapcar #'string-to-number
(split-string (read-string "Shiftx, shifty: ")
"[\t\s\n,;]+" t "[\t\s\n]"))))
(list (nth 0 shift) (nth 1 shift) current-prefix-arg)))
+ (unless (numberp scale) (setq scale 1))
(save-excursion
(save-restriction
(unless all
@@ -2961,11 +3089,17 @@ With prefix ALL non-nil shift all mapareas of current
page."
;; Cut off visible areas, drop invisble areas (with warning?)
(while (re-search-forward djvu-area-re nil t)
(replace-match (format "%d %d %d %d"
- (+ (djvu-match-number 3) shiftx)
- (+ (djvu-match-number 4) shifty)
- (+ (djvu-match-number 5) shiftx)
- (+ (djvu-match-number 6) shifty))
- nil nil nil 2)))))
+ (+ (* (djvu-match-number 3) scale) shiftx)
+ (+ (* (djvu-match-number 4) scale) shifty)
+ (+ (* (djvu-match-number 5) scale) shiftx)
+ (+ (* (djvu-match-number 6) scale) shifty))
+ nil nil nil 2)
+ (if (string= "poly" (match-string 1))
+ (while (progn (skip-chars-forward "\s\t\n")
+ (looking-at djvu-coord-xy-re))
+ (replace-match (format "%d %d"
+ (+ (* (djvu-match-number 1) scale) shiftx)
+ (+ (* (djvu-match-number 2) scale)
shifty)))))))))
(defun djvu-remove-linebreaks-internal ()
"Remove linebreaks in Maparea string.
@@ -3047,6 +3181,7 @@ or maximum among the Nth elements of all arrays CI."
;; Assume that BEG has PROP.
(let* ((zone (copy-sequence (get-text-property beg prop)))
(max (aref zone 1))
+ (min (aref zone 3))
(pnt beg)
val)
(while (and (/= pnt end)
@@ -3056,24 +3191,35 @@ or maximum among the Nth elements of all arrays CI."
(aset zone 1 (min (aref zone 1) (aref val 1)))
(setq max (max max (aref val 1))) ; descending words
(aset zone 2 (max (aref zone 2) (aref val 2)))
- (aset zone 3 (max (aref zone 3) (aref val 3)))))
+ (aset zone 3 (max (aref zone 3) (aref val 3)))
+ (setq min (min min (aref val 3))))) ; ascending words
;; The following is rather heuristic. Suggestions for better
;; solutions welcome, though probably not worth the effort.
;; Set `djvu-descenders-re' to nil if you do not like this.
- (if (and djvu-descenders-re
- (eq prop 'word)
- ;; descending words
- (> 0.10 (/ (- max (aref zone 1))
- (float (- (aref zone 3) (aref zone 1)))))
- (let ((string (buffer-substring-no-properties beg end)))
- (not (or (= 1 (length string)) ; single-character string
- ;; all-uppercase string
- (string= string (upcase string))
- ;; descender characters
- (string-match djvu-descenders-re string)))))
- (aset zone 1 (- (aref zone 1)
- (round (* 0.20 (- (aref zone 3) (aref zone 1)))))))
+ (if (and (or djvu-descenders-re djvu-ascenders-re)
+ (eq prop 'word))
+ (let* ((string (buffer-substring-no-properties beg end))
+ (long (< 1 (length string))) ; not a single-character string
+ case-fold-search)
+ (if (and long djvu-descenders-re
+ ;; descending words
+ (> 0.10 (/ (- max (aref zone 1))
+ (float (- (aref zone 3) (aref zone 1)))))
+ ;; all-uppercase string
+ (not (string= string (upcase string)))
+ ;; descender characters
+ (not (string-match djvu-descenders-re string)))
+ (aset zone 1 (- (aref zone 1)
+ (round (* 0.20 (- (aref zone 3) (aref zone
1)))))))
+ (if (and long djvu-ascenders-re
+ ;; ascending words
+ (> 0.10 (/ (- (aref zone 3) min)
+ (float (- (aref zone 3) (aref zone 1)))))
+ ;; ascender characters
+ (not (string-match djvu-ascenders-re string)))
+ (aset zone 3 (+ (aref zone 3)
+ (round (* 0.20 (- (aref zone 3) (aref zone
1)))))))))
zone))
(defun djvu-region-count (beg end prop)
@@ -3097,7 +3243,8 @@ or maximum among the Nth elements of all arrays CI."
(djvu-convert-hash)
(goto-char (point-min))
(while (progn (skip-chars-forward " \t\n") (not (eobp)))
- (if (looking-at djvu-annot-re)
+ (beginning-of-line)
+ (if (looking-at djvu-beg-object-re)
(condition-case nil
(push (read (current-buffer)) object)
(error (error "Syntax error in annotations")))
@@ -3132,14 +3279,15 @@ file SCRIPT. DOC defaults to the current Djvu
document."
(prin1 elt)
(insert "\n"))
((or (not (eq 'maparea (car elt))) ; not maparea
- (eq 'line (car (nth 3 elt)))) ; maparea line
+ (memq (car (nth 3 elt)) '(line poly))) ; maparea line,
poly
(prin1 elt)
(insert "\n"))
- ((consp (car (nth 3 elt))) ; maparea rect and oval
+ ((consp (car (nth 3 elt))) ; maparea rect
(dolist (area (nth 3 elt))
(insert (prin1-to-string
(apply 'list (car elt) (nth 1 elt) (nth 2 elt)
- (djvu-area area t) (nthcdr 4 elt))) "\n"))
+ (djvu-area area t) (nthcdr 4 elt)))
+ "\n"))
(setq id (1+ id))
(push (djvu-rect-elt
;; `djvu-rect-elt' expects that the rect areas are at
@@ -3149,11 +3297,12 @@ file SCRIPT. DOC defaults to the current Djvu
document."
(nth 3 elt))
id)
rect-list))
- ((eq 'text (car (nth 3 elt))) ; maparea text
+ ((memq (car (nth 3 elt)) '(text oval)) ; maparea text, oval
(insert (prin1-to-string
(apply 'list (car elt) (nth 1 elt) (nth 2 elt)
(djvu-area (nth 3 elt) t)
- (nthcdr 4 elt))) "\n"))
+ (nthcdr 4 elt)))
+ "\n"))
(t (error "Djvu maparea %s undefined" (car (nth 3 elt))))))
(insert ".\n")
(djvu-convert-hash t)
@@ -3181,6 +3330,7 @@ You get what you want."
;; Put this in a separate buffer!
(with-current-buffer buffer
(let ((buffer-undo-list t)
+ (djvu-init t)
buffer-read-only)
(djvu-script-mode)
(erase-buffer)
@@ -3212,19 +3362,13 @@ You get what you want."
(let* ((object (djvu-object bounds))
(area (nth 3 object)))
(if (eq (car object) 'maparea)
- (cond ((memq (car area) '(text line))
+ (cond ((memq (car area) '(text oval line poly))
(cdr (nth 3 object)))
- ((consp area) ; maparea rect and oval
+ ((consp area) ; maparea rect
(cdar area))))))))))
;;; Manipulate annotations
-(defvar djvu-beg-object-re
- (concat "^[\s\t]*(" (regexp-opt '("background" "zoom" "mode" "align"
- "maparea" "metadata" "bookmarks")
- t))
- "Regexp matching the beginning of Djvu annotation object.")
-
(defun djvu-object-bounds ()
"Return bounds (BEG . END) of Djvu object that contains or follows point.
Return nil if no such object can be found."
@@ -3258,7 +3402,7 @@ Return nil if no such object can be found."
(defun djvu-update-color (color)
"Update color attribute of Djvu maparea to COLOR."
- (interactive (list (completing-read "New Color: " djvu-color-alist nil t)))
+ (interactive (list (djvu-interactive-color-read)))
(let ((dpos (djvu-dpos))
(doc djvu-doc))
(with-current-buffer (djvu-ref annot-buf doc)
@@ -3271,19 +3415,30 @@ Return nil if no such object can be found."
If no such attribute exists insert a new one.
Prefix arg OPACITY is the opacity to use."
(interactive
- (list (completing-read "New Color: " djvu-color-alist nil t)
- (if current-prefix-arg
- (read-number "Opacity: "))))
+ (let ((color (djvu-interactive-color-read)))
+ (list color
+ (if (and color current-prefix-arg)
+ (read-number "Opacity: ")))))
(let ((bounds (djvu-object-bounds))
(opacity (or opacity djvu-opacity)))
(if bounds
(save-excursion
(goto-char (car bounds))
- (cond ((re-search-forward
+ (cond ((not color)
+ ;; remove color and opacity attributes
+ (when (re-search-forward
+ (format djvu-color-re "#" "" "") (cdr bounds) t)
+ (replace-match "")
+ (if (looking-at "[\s\t\n]+") (replace-match "")))
+ (goto-char (car bounds))
+ (when (re-search-forward "(opacity [0-9]+)" (cdr bounds) t)
+ (replace-match "")
+ (if (looking-at "[\s\t\n]+") (replace-match ""))))
+ ((re-search-forward
(format djvu-color-re "#" "" "") (cdr bounds) t)
;; update existing color attribute
(let ((attr (match-string 1)))
- (cond ((member attr '("hilite" "lineclr"))
+ (cond ((member attr '("hilite" "lineclr" "border"))
(replace-match (cdr (assoc color djvu-color-alist))
nil nil nil 2))
((string= attr "backclr")
@@ -3301,6 +3456,14 @@ Prefix arg OPACITY is the opacity to use."
(goto-char (car bounds))
(re-search-forward "(opacity [0-9]+)" (cdr bounds)
t))
(insert (format " (opacity %d)" opacity))))
+ ((re-search-forward "(line" (cdr bounds) t)
+ (goto-char (1- (cdr bounds)))
+ (insert (format " (lineclr %s)"
+ (cdr (assoc color djvu-color-alist)))))
+ ((re-search-forward "(poly" (cdr bounds) t)
+ (goto-char (1- (cdr bounds)))
+ (insert (format " (border %s) (border_avis)"
+ (cdr (assoc color djvu-color-alist)))))
((re-search-forward "(text" (cdr bounds) t)
(goto-char (1- (cdr bounds)))
(insert (format " (backclr %s)"
@@ -3340,7 +3503,7 @@ Prefix arg OPACITY is the opacity to use."
;; Remove duplicate attribute
(setq url (or (delete-dups (delete "" url)) '("")))
(if (nth 1 url) (user-error "Cannot merge multiple URLs"))
- (setq text (mapconcat 'identity (nreverse (delete "" text)) "\n"))
+ (setq text (mapconcat #'identity (nreverse (delete "" text)) "\n"))
(setq hilite (delete-dups hilite))
(if (nth 1 hilite) (user-error "Cannot merge multiple hilites"))
(setq opacity (delete-dups opacity))
@@ -3352,7 +3515,7 @@ Prefix arg OPACITY is the opacity to use."
(goto-char beg)
(delete-region beg end)
(insert (format "(maparea %S\n %S\n (" (car url) text)
- (mapconcat 'prin1-to-string (nreverse rect) "\n ") ")\n"
+ (mapconcat #'prin1-to-string (nreverse rect) "\n ") ")\n"
(if hilite (format " (hilite %s)" (car hilite)) "")
(if opacity (format " (opacity %s)" (car opacity)) "")
(format " (%s)" (car border))
@@ -3509,6 +3672,58 @@ Return nil if OBJECT does not have internal URLs."
(djvu-insert-bookmarks (nthcdr 2 elt) indent1)
(insert ")"))))
+(defun djvu-read-bookmarks (&optional doc)
+ "Read bookmarks of a Djvu document from bookmarks buffer."
+ (let (object)
+ (with-current-buffer (djvu-ref bookmarks-buf doc)
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (unless (eobp)
+ (condition-case nil
+ (setq object (read (current-buffer)))
+ (error (error "Syntax error in bookmarks"))))
+ (skip-chars-forward " \t\n")
+ ;; We should have swallowed all bookmarks.
+ (unless (eobp)
+ (error "Syntax error in bookmarks (position %s)" (point))))))
+ (if (and object (not (eq 'bookmarks (car object))))
+ (error "Malformed bookmarks"))
+ object))
+
+(defun djvu-reformat-bookmarks (&optional doc)
+ "Reformat Bookmarks buffer for Djvu document DOC."
+ (interactive)
+ (with-current-buffer (djvu-ref bookmarks-buf doc)
+ (let ((pnt (point))
+ (object (djvu-read-bookmarks doc)))
+ (erase-buffer)
+ (insert "(bookmarks")
+ (djvu-insert-bookmarks (cdr object) " ")
+ (insert ")\n")
+ (goto-char pnt))))
+
+(defun djvu-save-bookmarks (script &optional doc)
+ "Save bookmarks of a Djvu document.
+This dumps the content of DOC's bookmarks buffer into the djvused script
+file SCRIPT. DOC defaults to the current Djvu document."
+ (unless doc (setq doc djvu-doc))
+ (let ((object (djvu-read-bookmarks doc)))
+ (with-temp-buffer
+ (setq buffer-file-coding-system 'utf-8)
+ (insert "set-outline\n")
+ (when object
+ (insert "(bookmarks")
+ (let ((djvu-doc doc)) ; DOC should definitely be initialized above
+ (djvu-insert-bookmarks (cdr object) " "))
+ (insert ")\n"))
+ (insert ".\n")
+ (write-region nil nil script t 0)) ; append to SCRIPT
+ (djvu-init-outline (cdr object) doc)))
+
+;;; Djvu Outline mode
+
(defun djvu-init-outline (object &optional doc)
(with-current-buffer (djvu-ref outline-buf doc)
(let (buffer-read-only)
@@ -3562,63 +3777,16 @@ PNT defaults to position of point."
(forward-line))
(goto-char pnt)))
-(defun djvu-read-bookmarks (&optional doc)
- "Read bookmarks of a Djvu document from bookmarks buffer."
- (let (object)
- (with-current-buffer (djvu-ref bookmarks-buf doc)
- (save-excursion
- (save-restriction
- (widen)
- (goto-char (point-min))
- (unless (eobp)
- (condition-case nil
- (setq object (read (current-buffer)))
- (error (error "Syntax error in bookmarks"))))
- (skip-chars-forward " \t\n")
- ;; We should have swallowed all bookmarks.
- (unless (eobp)
- (error "Syntax error in bookmarks (end of buffer)")))))
- (if (and object (not (eq 'bookmarks (car object))))
- (error "Malformed bookmarks"))
- object))
-
-(defun djvu-reformat-bookmarks (&optional doc)
- "Reformat Bookmarks buffer for Djvu document DOC."
- (interactive)
- (with-current-buffer (djvu-ref bookmarks-buf doc)
- (let ((pnt (point))
- (object (djvu-read-bookmarks doc)))
- (erase-buffer)
- (insert "(bookmarks")
- (djvu-insert-bookmarks (cdr object) " ")
- (insert ")\n")
- (goto-char pnt))))
-
-(defun djvu-save-bookmarks (script &optional doc)
- "Save bookmarks of a Djvu document.
-This dumps the content of DOC's bookmarks buffer into the djvused script
-file SCRIPT. DOC defaults to the current Djvu document."
- (unless doc (setq doc djvu-doc))
- (let ((object (djvu-read-bookmarks doc)))
- (with-temp-buffer
- (setq buffer-file-coding-system 'utf-8)
- (insert "set-outline\n")
- (when object
- (insert "(bookmarks")
- (let ((djvu-doc doc)) ; DOC should definitely be initialized above
- (djvu-insert-bookmarks (cdr object) " "))
- (insert ")\n"))
- (insert ".\n")
- (write-region nil nil script t 0)) ; append to SCRIPT
- (djvu-init-outline (cdr object) doc)))
-
;;; Image minor mode
(defmacro djvu-with-event-buffer (event &rest body)
"With buffer of EVENT current, evaluate BODY."
(declare (indent 1))
;; Fixme: abort if `minibufferp' returns non-nil?
- `(with-current-buffer (window-buffer (posn-window (event-start ,event)))
+ `(with-current-buffer
+ (window-buffer (let ((win (posn-window (event-start ,event))))
+ (if (windowp win) win
+ (user-error "Event not over window"))))
,@body))
(defun djvu-image-toggle ()
@@ -3634,17 +3802,25 @@ file SCRIPT. DOC defaults to the current Djvu document."
"Image display of current page."
:lighter "Image"
+ ;; Keybindings for motion commands adopted from `image-mode-map'
:keymap '((" " . djvu-image-scroll-up)
([?\S-\ ] . djvu-image-scroll-down)
("\C-?" . djvu-image-scroll-down)
- ("\C-n" . djvu-image-next-line)
- ([down] . djvu-image-next-line)
- ("\C-p" . djvu-image-previous-line)
- ([up] . djvu-image-previous-line)
- ([remap forward-char] . image-forward-hscroll)
- ([remap backward-char] . image-backward-hscroll)
- ([remap right-char] . image-forward-hscroll)
- ([remap left-char] . image-backward-hscroll)
+ ([remap scroll-up-command] . djvu-image-scroll-up)
+ ([remap scroll-down-command] . djvu-image-scroll-down)
+ ([remap next-line] . djvu-image-next-line)
+ ([remap previous-line] . djvu-image-previous-line)
+ ([remap forward-char] . image-forward-hscroll)
+ ([remap backward-char] . image-backward-hscroll)
+ ([remap right-char] . image-forward-hscroll)
+ ([remap left-char] . image-backward-hscroll)
+ ([remap move-beginning-of-line] . image-bol)
+ ([remap move-end-of-line] . image-eol)
+ ([remap beginning-of-buffer] . image-bob)
+ ([remap end-of-buffer] . image-eob)
+ ;;
+ ("+" . djvu-image-zoom-in)
+ ("-" . djvu-image-zoom-out)
([drag-mouse-1] . djvu-mouse-rect-area)
([S-drag-mouse-1] . djvu-mouse-text-area)
@@ -3666,26 +3842,43 @@ file SCRIPT. DOC defaults to the current Djvu document."
([M-drag-mouse-1] . djvu-mouse-word-area)
([M-down-mouse-1] . djvu-mouse-drag-track-area)
([drag-mouse-3] . djvu-mouse-word-area) ; substitute
- ([down-mouse-3] . djvu-mouse-drag-track-area) ; substitute
- ;;
- ("+" . djvu-image-zoom-in)
- ("-" . djvu-image-zoom-out))
-
- ;; Adopted from `doc-view-mode'
- (image-mode-setup-winprops) ; record current scroll settings
- ;; Don't scroll unless the user specifically asked for it.
- (setq-local auto-hscroll-mode nil)
-
- (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)))))
+ ([down-mouse-3] . djvu-mouse-drag-track-area)) ; substitute
+
+ (image-mode-setup-winprops)
+
+ (let* ((display (get-text-property (point-min) 'display))
+ (enable (and djvu-image-mode (not display)))
+ (disable (and (not djvu-image-mode) display)))
+ (cond (enable
+ ;; Remember DPOS if we enable `djvu-image-mode'.
+ (djvu-set read-pos (let (djvu-image-mode)
+ (djvu-read-dpos)))
+ ;; Don't scroll unless the user specifically asked for it.
+ (setq-local auto-hscroll-mode nil))
+ (disable
+ ;; Remember scrolling when we leave image mode
+ (djvu-set image-vscroll (djvu-image-vscroll))
+ (djvu-set image-hscroll (window-hscroll))
+ (set-window-hscroll (selected-window) 0)))
+
+ (if (or enable disable) (djvu-image))
+
+ (cond (enable
+ ;; Code adopted from `image-bol'.
+ (let* ((image-size (image-display-size
+ (image-get-display-property)))
+ (img-width (ceiling (car image-size)))
+ (img-height (ceiling (cdr image-size)))
+ (edges (window-inside-edges))
+ (win-width (- (nth 2 edges) (nth 0 edges)))
+ (win-height (- (nth 3 edges) (nth 1 edges))))
+ (image-set-window-vscroll (min (djvu-ref image-vscroll)
+ (max 0 (- img-height win-height))))
+ (image-set-window-hscroll (min (djvu-ref image-hscroll)
+ (max 0 (- img-width win-width))))))
+ (disable
+ ;; Go to DPOS if we disable `djvu-image-mode'.
+ (djvu-goto-read (djvu-ref read-pos))))))
(defun djvu-image (&optional isize)
"If `djvu-image-mode' is enabled, display image of current Djvu page.
@@ -3695,9 +3888,8 @@ 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)
- (if (get-text-property (point-min) 'display)
- (let (buffer-read-only)
- (remove-text-properties (point-min) (point-max) '(display nil))))
+ (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
@@ -3728,91 +3920,115 @@ Otherwise remove the image."
'pbm t))
doc)))))
;; Display image.
- (let ((hscroll (window-hscroll))
- buffer-read-only)
+ (let (buffer-read-only)
(if (= (point-min) (point-max)) (insert " "))
(put-text-property (point-min) (point-max)
- 'display (nthcdr 2 (djvu-ref image)))
- (set-window-hscroll (selected-window) hscroll))))
+ 'display (nthcdr 2 (djvu-ref image))))))
-;; The following scrolling commands are adapted from `doc-view-mode'.
-;; Up to Emacs 26, the functions `image-scroll-down', `image-scroll-up',
-;; `image-next-line', and `image-previous-line' return multiples of the
-;; character height. Starting with Emacs 27 (commit 9c66b09950),
-;; these functions return pixel values.
+;; The following scrolling commands are adapted from `image-mode'
+;; and `doc-view-mode'.
+
+(defun djvu-image-vscroll ()
+ "Return the amount by which a page image is scrolled vertically."
+ ;; Up to Emacs 26, the functions `image-scroll-down', `image-scroll-up',
+ ;; `image-next-line', and `image-previous-line' return multiples of the
+ ;; character height. Starting with Emacs 27, these functions return
+ ;; pixel values. To be compatible, we must call `window-vscroll'
+ ;; without or with arg PIXELS-P non-nil.
+ (window-vscroll nil (<= 27 (string-to-number emacs-version))))
(defun djvu-image-scroll-up (&optional n)
"Scroll image of current page upward by N lines.
At the bottom of the image, when `djvu-continuous' is non-nil
-or prefix N is nil, go to the image of the next page.
-Prefix N may take the same values as arg N of `image-scroll-up'.
-For historical reasons, this includes the range of values
-of `current-prefix-arg'."
+go to the image of the next page.
+Prefix N may take the same values as arg N of `image-scroll-up'."
(interactive "P") ; same as `image-scroll-up'
- (if (and (= (window-vscroll nil (<= 27 (string-to-number emacs-version)))
- (image-scroll-up n))
- (or djvu-continuous (not n))
+ (if (and (= (djvu-image-vscroll) (image-scroll-up n))
+ djvu-continuous
(< (djvu-ref page) (djvu-ref pagemax)))
(let ((hscroll (window-hscroll)))
(djvu-next-page 1)
(image-bob)
(image-bol 1)
- (set-window-hscroll (selected-window) hscroll))))
+ (image-set-window-hscroll hscroll))))
(defun djvu-image-scroll-down (&optional n)
"Scroll image of current page downward N lines.
At the top of the image, when `djvu-continuous' is non-nil
-or prefix N is nil, go to the image of the previous page.
-Prefix N may take the same values as arg N of `image-scroll-down'.
-For historical reasons, this includes the range of values
-of `current-prefix-arg'."
+go to the image of the previous page.
+Prefix N may take the same values as arg N of `image-scroll-down'."
(interactive "P") ; same as `image-scroll-down'
- (if (and (= (window-vscroll nil (<= 27 (string-to-number emacs-version)))
- (image-scroll-down n))
- (or djvu-continuous (not n))
+ (if (and (= (djvu-image-vscroll) (image-scroll-down n))
+ djvu-continuous
(< 1 (djvu-ref page)))
(let ((hscroll (window-hscroll)))
(djvu-prev-page 1)
(image-eob)
(image-bol 1)
- (set-window-hscroll (selected-window) hscroll))))
+ (image-set-window-hscroll hscroll))))
(defun djvu-image-next-line (&optional n)
"Scroll image of current page upward by N lines.
At the bottom of the image, when `djvu-continuous' is non-nil,
go to the image of the next page."
(interactive "p")
- (if (and (= (window-vscroll nil (<= 27 (string-to-number emacs-version)))
- (image-next-line n))
+ (if (and (= (djvu-image-vscroll) (image-next-line n))
djvu-continuous
(< (djvu-ref page) (djvu-ref pagemax)))
(let ((hscroll (window-hscroll)))
(djvu-next-page 1)
(image-bob)
(image-bol 1)
- (set-window-hscroll (selected-window) hscroll))))
+ (image-set-window-hscroll hscroll))))
(defun djvu-image-previous-line (&optional n)
"Scroll image of current page downward N lines.
At the top of the image, when `djvu-continuous' is non-nil,
go to the image of the previous page."
(interactive "p")
- (if (and (= (window-vscroll nil (<= 27 (string-to-number emacs-version)))
- (image-previous-line n))
+ (if (and (= (djvu-image-vscroll) (image-previous-line n))
djvu-continuous
(< 1 (djvu-ref page)))
(let ((hscroll (window-hscroll)))
(djvu-prev-page 1)
(image-eob)
(image-bol 1)
- (set-window-hscroll (selected-window) hscroll))))
+ (image-set-window-hscroll hscroll))))
+
+(defun djvu-image-zoom-in (&optional zoom)
+ (interactive)
+ ;; FIXME: this preserves the upper left corner of the image.
+ ;; If possible, we should preserve the center of the image.
+ (let ((hscroll (window-hscroll))
+ (vscroll (djvu-image-vscroll))
+ (zoom (or zoom djvu-image-zoom)))
+ (djvu-image (round (* (nth 1 (djvu-ref image)) zoom)))
+ (image-set-window-hscroll (round (* hscroll zoom)))
+ (image-set-window-vscroll (round (* vscroll zoom)))))
+
+(defun djvu-image-zoom-out (&optional zoom)
+ (interactive)
+ (let ((hscroll (window-hscroll))
+ (vscroll (djvu-image-vscroll))
+ (zoom (or zoom djvu-image-zoom)))
+ (djvu-image (round (/ (nth 1 (djvu-ref image)) zoom)))
+ (image-set-window-hscroll (round (/ hscroll zoom)))
+ (image-set-window-vscroll (round (/ vscroll zoom)))))
+
+;; Image-based editing commands
(defun djvu-mouse-drag-track-area (start-event &optional line)
"Track drag over image."
(interactive "e")
;; Inspired by `mouse-drag-track'.
- (setq track-mouse t)
- (set-transient-map
+ (let ((old-track-mouse track-mouse))
+ ;; Disable `djvu-modified' during tracking,
+ ;; but we just remember the current modified flag.
+ ;; This makes the code much faster!
+ (remove-hook 'post-command-hook #'djvu-modified t)
+ (setq djvu-modified (buffer-modified-p))
+ (setq track-mouse 'drag-tracking)
+ (set-transient-map
(let ((map (make-sparse-keymap)))
(define-key map [mouse-movement]
(lambda (event) (interactive "e")
@@ -3823,7 +4039,9 @@ go to the image of the previous page."
line))))
map)
t (lambda ()
- (setq track-mouse nil))))
+ (add-hook 'post-command-hook #'djvu-modified nil t)
+ (setq djvu-modified nil)
+ (setq track-mouse old-track-mouse)))))
(defun djvu-image-rect (&optional event line)
"For PPM image specified via EVENT mark rectangle by inverting bits."
@@ -3884,26 +4102,20 @@ go to the image of the previous page."
width))))))
(invert i (+ i 3)))
(setq x (+ x step)))))))
- (with-silent-modifications
+ (let (buffer-read-only)
(put-text-property
(point-min) (point-max) 'display
(create-image image 'pbm t)))
+ (restore-buffer-modified-p djvu-modified)
(image-flush old-image))
;; Restore unmodified image
- (let ((old-image (get-text-property (point-min) 'display)))
- (with-silent-modifications
- (put-text-property (point-min) (point-max)
- 'display (nthcdr 2 (djvu-ref image))))
+ (let ((old-image (get-text-property (point-min) 'display))
+ buffer-read-only)
+ ;; The modified flag is set by `djvu-modified' in `post-command-hook'.
+ (put-text-property (point-min) (point-max)
+ 'display (nthcdr 2 (djvu-ref image)))
(image-flush old-image))))
-(defun djvu-image-zoom-in ()
- (interactive)
- (djvu-image (round (* (nth 1 (djvu-ref image)) 1.2))))
-
-(defun djvu-image-zoom-out ()
- (interactive)
- (djvu-image (round (/ (nth 1 (djvu-ref image)) 1.2))))
-
(defun djvu-event-to-area (event &optional dir)
"Convert mouse EVENT to Djvu area coordinates."
(let* ((e-start (event-start event))
@@ -3938,13 +4150,15 @@ go to the image of the previous page."
(interactive "e")
;; Mouse events ignore prefix args?
(djvu-with-event-buffer event
- (djvu-image-rect event)
- (let ((color (djvu-interactive-color djvu-color-highlight)))
- (djvu-rect-area nil (read-string (format "(%s) Highlight: " color)
- nil nil nil djvu-inherit-input-method)
- (list (djvu-event-to-area event))
- color djvu-opacity 'none))
- (djvu-image-rect)))
+ (unwind-protect
+ (let ((color (djvu-interactive-color djvu-color-highlight))
+ ;; Do RECTS first as this may throw a user error.
+ (rects (list (djvu-event-to-area event))))
+ (djvu-rect-area nil (read-string (format "(%s) Highlight: "
+ (or color "no color"))
+ nil nil nil
djvu-inherit-input-method)
+ rects color djvu-opacity 'none))
+ (djvu-image-rect))))
(defun djvu-mouse-text-area (event)
(interactive "e")
@@ -3957,14 +4171,15 @@ go to the image of the previous page."
(defun djvu-mouse-text-area-internal (event prompt &optional pushpin)
;; Mouse events ignore prefix args?
(djvu-with-event-buffer event
- (djvu-image-rect event)
- (let ((color (djvu-interactive-color djvu-color-highlight)))
- (djvu-text-area nil (read-string (format "(%s) %s: " color prompt)
- nil nil nil djvu-inherit-input-method)
- (djvu-event-to-area event) nil
- (djvu-color-background color)
- nil pushpin))
- (djvu-image-rect)))
+ (unwind-protect
+ (let ((color (djvu-interactive-color djvu-color-highlight))
+ ;; Do AREA first as this may throw a user error.
+ (area (djvu-event-to-area event)))
+ (djvu-text-area nil (read-string (format "(%s) %s: "
+ (or color "no color")
prompt)
+ nil nil nil
djvu-inherit-input-method)
+ area nil (djvu-color-background color) nil pushpin))
+ (djvu-image-rect))))
(defun djvu-mouse-line-area (event)
(interactive "e")
@@ -3980,17 +4195,21 @@ go to the image of the previous page."
(defun djvu-mouse-line-area-internal (event &optional dir)
(djvu-with-event-buffer event
- (let* ((line (djvu-event-to-area event dir))
- (color (djvu-interactive-color djvu-color-line))
- (text (read-string (format "(%s) Line: " color)
- nil nil nil djvu-inherit-input-method)))
- (cond ((eq dir 'horiz)
- (setq line (list (nth 0 line) (nth 1 line)
- (nth 2 line) (nth 1 line))))
- ((eq dir 'vert)
- (setq line (list (nth 0 line) (nth 1 line)
- (nth 0 line) (nth 3 line)))))
- (djvu-line-area nil text line nil nil djvu-line-width djvu-color-line))))
+ (unwind-protect
+ ;; Do LINE first as this may throw a user error.
+ (let* ((line (djvu-event-to-area event dir))
+ (color (djvu-interactive-color djvu-color-line))
+ (text (read-string (format "(%s) Line: " (or color "no color"))
+ nil nil nil djvu-inherit-input-method)))
+ (cond ((eq dir 'horiz)
+ (setq line (list (nth 0 line) (nth 1 line)
+ (nth 2 line) (nth 1 line))))
+ ((eq dir 'vert)
+ (setq line (list (nth 0 line) (nth 1 line)
+ (nth 0 line) (nth 3 line)))))
+ (djvu-line-area nil text line nil nil
+ djvu-line-width djvu-color-line))
+ (djvu-image-rect))))
(defun djvu-line-area (url text line &optional border arrow width lineclr)
;; Record position where annotation was made.
@@ -4038,11 +4257,15 @@ go to the image of the previous page."
(defun djvu-mouse-word-area (event)
"Insert word."
(interactive "e")
- (with-current-buffer (djvu-with-event-buffer event
- (djvu-ref text-buf))
- (djvu-text-line-area (read-string "Text: " nil nil nil
- djvu-inherit-input-method)
- (djvu-bound-area (djvu-event-to-area event)))))
+ (djvu-with-event-buffer event
+ (unwind-protect
+ ;; Do AREA first as this may throw a user error.
+ (let ((area (djvu-event-to-area event)))
+ (with-current-buffer (djvu-ref text-buf)
+ (djvu-text-line-area (read-string "Text: " nil nil nil
+ djvu-inherit-input-method)
+ area)))
+ (djvu-image-rect))))
;;; Miscellaneous commands
@@ -4106,17 +4329,16 @@ If the width of a page exceeds WIDTH, increase the page
resolution DPI
accordingly."
(interactive "nWidth: \nnWidth: %s, dpi: ")
(unless doc (setq doc djvu-doc))
- (let ((count 0) job)
+ (let (job)
(with-temp-buffer
(djvu-djvused doc t "-e" "size")
(goto-char (point-min))
(let ((page 0))
(while (looking-at "width=\\([[:digit:]]+\\)")
(setq page (1+ page))
- (let ((w (djvu-match-number 1)))
- (when (< width w)
- (push (cons page (/ (* w dpi) width)) job)
- (setq count (1+ count))))
+ (let ((d (/ (* (djvu-match-number 1) dpi) width)))
+ (if (< dpi d)
+ (push (cons page d) job)))
(forward-line))))
(if (not job)
(message "Nothing to unify")
@@ -4126,7 +4348,7 @@ accordingly."
(car elt) (cdr elt)))
job "; ")
"-s")
- (message "%s pages updated: %s" count
+ (message "%s pages updated: %s" (length job)
(mapconcat (lambda (elt) (format "%d" (car elt)))
(nreverse job) ", ")))))
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [elpa] externals/djvu 071c8ab168: Release djvu.el v1.1.2,
Roland Winkler <=