Add imenu index function for DjVu files in doc-view

* lisp/doc-view.el (doc-view-pdfdraw-program): Prefer mutool
over other names.
(doc-view-imenu-enabled): Tweak the default value to check for
'djvused', and make it obsolete.
(doc-view--djvu-outline, doc-view--parse-djvu-outline): Add new
functions to return imenu index for a Djvu file.
(doc-view--outline): Add new function to create the imenu index
depending on the file type.
(doc-view--outline): Document new possible variable value.
(doc-view-imenu-index): Use the above function instead.
(doc-view-imenu-setup): Try to create the imenu index
unconditionally.
* doc/emacs/misc.texi (DocView Navigation): Mention index
creation using 'djvused' too.
* etc/NEWS: Announce the change.  (Bug#73530)
This commit is contained in:
Visuwesh 2024-10-02 13:48:25 +05:30 committed by Tassilo Horn
parent 5e0935d991
commit d6dd71cd56
3 changed files with 106 additions and 27 deletions

View file

@ -581,17 +581,17 @@ you instead want the image to be re-rendered at the new size, set
default size for DocView, customize the variable
@code{doc-view-resolution}.
@vindex doc-view-imenu-enabled
@vindex doc-view-imenu-flatten
@vindex doc-view-imenu-format
When the @command{mutool} program is available, DocView will use it
to generate entries for an outline menu, making it accessible via the
@code{imenu} facility (@pxref{Imenu}). To disable this functionality
even when @command{mutool} can be found on your system, customize the
variable @code{doc-view-imenu-enabled} to the @code{nil} value. You
can further customize how @code{imenu} items are formatted and
displayed using the variables @code{doc-view-imenu-format} and
@code{doc-view-imenu-flatten}.
@vindex doc-view-djvused-program
DocView can generate an outline menu for PDF and DjVu documents using
the @command{mutool} and the @command{djvused} programs respectively
when they are available. This is made accessible via the @code{imenu}
facility (@pxref{Imenu}). You can customize how @code{imenu} items are
formatted and displayed using the variables @code{doc-view-imenu-format}
and @code{doc-view-imenu-flatten}. The filename of the
@command{djvused} program can be customized by changing the
@code{doc-view-djvused-program} user option.
@cindex registers, in DocView mode
@findex doc-view-page-to-register

View file

@ -364,6 +364,13 @@ Docview can store current page to buffer-local registers with the new
command 'doc-view-page-to-register' (bound to 'm'), and later the stored
page can be restored with 'doc-view-jump-to-register' (bound to ''').
+++
*** Docview can generate imenu index for DjVu files.
When the 'djvused' program is available, Docview can now generate imenu
index for DjVu files from its outline.
The name of the 'djvused' program can be customized by changing the user
option 'doc-view-djvused-program'.
** Tramp
+++

View file

@ -27,8 +27,10 @@
;; `pdftotext', which comes with xpdf (https://www.foolabs.com/xpdf/)
;; or poppler (https://poppler.freedesktop.org/). EPUB, CBZ, FB2, XPS
;; and OXPS documents require `mutool' which comes with mupdf
;; (https://mupdf.com/index.html). Djvu documents require `ddjvu'
;; (https://mupdf.com/index.html). DjVu documents require `ddjvu'
;; (from DjVuLibre). ODF files require `soffice' (from LibreOffice).
;; `djvused' (from DjVuLibre) can be optionally used to generate imenu
;; outline for DjVu documents when available.
;;; Commentary:
@ -185,13 +187,13 @@ are available (see Info node `(emacs)Document View')."
(defcustom doc-view-pdfdraw-program
(cond
((executable-find "mutool") "mutool")
((executable-find "pdfdraw") "pdfdraw")
((executable-find "mudraw") "mudraw")
((executable-find "mutool") "mutool")
(t "mudraw"))
"Name of MuPDF's program to convert PDF files to PNG."
:type 'file
:version "24.4")
:version "31.1")
(defcustom doc-view-pdftotext-program-args '("-raw")
"Parameters to give to the pdftotext command."
@ -216,10 +218,23 @@ are available (see Info node `(emacs)Document View')."
:type 'boolean
:version "30.1")
(defcustom doc-view-imenu-enabled (and (executable-find "mutool") t)
"Whether to generate an imenu outline when \"mutool\" is available."
(defcustom doc-view-djvused-program (and (executable-find "djvused")
"djvused")
"Name of \"djvused\" program to generate imenu outline for DjVu files.
This is part of DjVuLibre."
:type 'file
:version "31.1")
(defcustom doc-view-imenu-enabled (and (or (executable-find "mutool")
(executable-find "djvused"))
t)
"Whether to generate imenu outline for PDF and DjVu files.
This uses \"mutool\" for PDF files and \"djvused\" for DjVu files."
:type 'boolean
:version "29.1")
:version "31.1")
(make-obsolete-variable 'doc-view-imenu-enabled
"Imenu index is generated unconditionally when available."
"31.1")
(defcustom doc-view-imenu-title-format "%t (%p)"
"Format spec for imenu's display of section titles from docview documents.
@ -1958,7 +1973,9 @@ the document text."
"[^\t]+\\(\t+\\)\"\\(.+\\)\"\t#\\(?:page=\\)?\\([0-9]+\\)")
(defvar-local doc-view--outline nil
"Cached PDF outline, so that it is only computed once per document.")
"Cached PDF outline, so that it is only computed once per document.
It can be the symbol `unavailable' to indicate that outline is
unavailable for the document.")
(defun doc-view--pdf-outline (&optional file-name)
"Return a list describing the outline of FILE-NAME.
@ -1972,7 +1989,9 @@ structure is extracted by `doc-view--imenu-subtree'."
(let ((outline nil)
(fn (expand-file-name fn)))
(with-temp-buffer
(unless (eql 0 (call-process "mutool" nil (current-buffer) nil "show" fn "outline"))
(unless (eql 0 (call-process doc-view-pdfdraw-program nil
(current-buffer) nil "show" fn "outline"))
(setq doc-view--outline 'unavailable)
(imenu-unavailable-error "Unable to create imenu index using `mutool'"))
(goto-char (point-min))
(while (re-search-forward doc-view--outline-rx nil t)
@ -1983,6 +2002,42 @@ structure is extracted by `doc-view--imenu-subtree'."
outline)))
(nreverse outline)))))
(defun doc-view--djvu-outline (&optional file-name)
"Return a list describing the outline of FILE-NAME.
If FILE-NAME is nil or omitted, it defaults to the current buffer's file
name.
For the format, see `doc-view--pdf-outline'."
(unless file-name (setq file-name (buffer-file-name)))
(with-temp-buffer
(call-process doc-view-djvused-program nil (current-buffer) nil
"-e" "print-outline" file-name)
(goto-char (point-min))
(when (eobp)
(setq doc-view--outline 'unavailable)
(imenu-unavailable-error "Unable to create imenu index using `djvused'"))
(nreverse (doc-view--parse-djvu-outline (read (current-buffer))))))
(defun doc-view--parse-djvu-outline (bookmark &optional level)
"Return a list describing the djvu outline from BOOKMARK.
Optional argument LEVEL is the current heading level, which defaults to 1."
(unless level (setq level 1))
(let ((res))
(unless (eq (car bookmark) 'bookmarks)
(user-error "Unknown outline type: %S" (car bookmark)))
(pcase-dolist (`(,title ,page . ,rest) (cdr bookmark))
(push `((level . ,level)
(title . ,title)
(page . ,(string-to-number (string-remove-prefix "#" page))))
res)
(when (and rest (listp (car rest)))
(setq res (append
(doc-view--parse-djvu-outline
(cons 'bookmarks rest)
(+ level 1))
res))))
res))
(defun doc-view--imenu-subtree (outline act)
"Construct a tree of imenu items for the given outline list and action.
@ -2015,19 +2070,36 @@ entries at an upper level."
For extensibility, callers can specify a FILE-NAME to indicate
the buffer other than the current buffer, and a jumping function
GOTO-PAGE-FN other than `doc-view-goto-page'."
(let* ((goto (or goto-page-fn 'doc-view-goto-page))
(act (lambda (_name _pos page) (funcall goto page)))
(outline (or doc-view--outline (doc-view--pdf-outline file-name))))
(car (doc-view--imenu-subtree outline act))))
(unless doc-view--outline
(setq doc-view--outline (doc-view--outline file-name)))
(unless (eq doc-view--outline 'unavailable)
(let* ((goto (or goto-page-fn #'doc-view-goto-page))
(act (lambda (_name _pos page) (funcall goto page)))
(outline doc-view--outline))
(car (doc-view--imenu-subtree outline act)))))
(defun doc-view--outline (&optional file-name)
"Return the outline for the file FILE-NAME.
If FILE-NAME is nil, use the current file instead."
(unless file-name (setq file-name (buffer-file-name)))
(let ((outline
(pcase doc-view-doc-type
('djvu
(when doc-view-djvused-program
(doc-view--djvu-outline file-name)))
(_
(doc-view--pdf-outline file-name)))))
(when outline (imenu-add-to-menubar "Outline"))
;; When the outline could not be made due to unavailability of the
;; required program, or its absency from the document, return
;; 'unavailable'.
(or outline 'unavailable)))
(defun doc-view-imenu-setup ()
"Set up local state in the current buffer for imenu, if needed."
(when doc-view-imenu-enabled
(setq-local imenu-create-index-function #'doc-view-imenu-index
imenu-submenus-on-top nil
imenu-sort-function nil
doc-view--outline (doc-view--pdf-outline))
(when doc-view--outline (imenu-add-to-menubar "Outline"))))
(setq-local imenu-create-index-function #'doc-view-imenu-index
imenu-submenus-on-top nil
imenu-sort-function nil))
;;;; User interface commands and the mode