update packages and add valign

This commit is contained in:
2026-04-05 20:00:27 +02:00
parent b062fb98e3
commit 03fb00e374
640 changed files with 109768 additions and 39311 deletions

View File

@@ -4,10 +4,10 @@
#+Maintainer: Vedang Manerikar
#+Maintainer_Email: vedang.manerikar@gmail.com
[[https://app.circleci.com/pipelines/github/vedang/pdf-tools][https://circleci.com/gh/vedang/pdf-tools.svg?style=svg]]
[[https://github.com/vedang/pdf-tools/actions/workflows/test.yml][https://github.com/vedang/pdf-tools/actions/workflows/test.yml/badge.svg]]
[[https://elpa.nongnu.org/nongnu/pdf-tools.html][http://elpa.nongnu.org/nongnu/pdf-tools.svg]]
[[https://stable.melpa.org/#/pdf-tools][http://stable.melpa.org/packages/pdf-tools-badge.svg]]
[[https://melpa.org/#/pdf-tools][http://melpa.org/packages/pdf-tools-badge.svg]] [[https://ci.appveyor.com/project/vedang/pdf-tools][https://ci.appveyor.com/api/projects/status/yqic2san0wi7o5v8/branch/master?svg=true]]
[[https://melpa.org/#/pdf-tools][http://melpa.org/packages/pdf-tools-badge.svg]]
The ~pdf-tools~ Wiki is maintained at https://pdftools.wiki. Head to the site if you find it easier to navigate a website for reading a manual. All the topics on the site are listed at https://pdftools.wiki/impulse.
@@ -25,6 +25,7 @@ The ~pdf-tools~ Wiki is maintained at https://pdftools.wiki. Head to the site if
- [[#features][Features]]
- [[#view-and-navigate-pdfs][View and Navigate PDFs]]
- [[#keybindings-for-navigating-pdf-documents][Keybindings for navigating PDF documents]]
- [[#continuous-scroll-mode-experimental][Continuous Scroll Mode (Experimental)]]
- [[#keybindings-for-manipulating-display-of-pdf][Keybindings for manipulating display of PDF]]
- [[#annotations][Annotations]]
- [[#keybindings-for-working-with-annotations][Keybindings for working with Annotations]]
@@ -94,15 +95,19 @@ If you install ~pdf-tools~ via NonGNU ELPA or MELPA, *you don't need to worry ab
Note: You'll need GNU Emacs \ge 26.3 and some form of a GNU/Linux OS. Other operating systems are not officially supported, but ~pdf-tools~ is known to work on many of them.
The ~epdfinfo~ install script takes care of installing all the necessary pre-requisites on supported operating systems (see list below). See the section on [[id:A34704B9-1B51-4614-8806-C4059F7B42D5][I want to add support for ~pdf-tools~ on =My Fav OS=. How do I do that?]] to learn how to add your favorite Operating System to this list.
The ~epdfinfo~ install script takes care of installing most of the necessary pre-requisites on supported operating systems (see list below). See the section on [[id:A34704B9-1B51-4614-8806-C4059F7B42D5][I want to add support for ~pdf-tools~ on =My Fav OS=. How do I do that?]] to learn how to add your favorite Operating System to this list.
Similarly, package-managers are not officially supported, but ~pdf-tools~ is known to be available on some of them. See the section on [[id:fb5cef15-fed4-4dec-a443-52f7c00c7831][Installing the ~epdfinfo~ server from package managers]] to avoid manual installation of server / server prerequisites.
Pre-requisites:
- make: if this is not already installed, run ~./server/autobuild~ instead of ~make -s~
- [[https://github.com/cask/cask][cask]]: if this is not already installed, follow the install instructions from the cask github
Installation Instructions for ~epdfinfo~:
#+begin_src sh
$ git clone https://github.com/vedang/pdf-tools
$ cd /path/to/pdf-tools
$ make -s # If you don't have make installed, run ./server/autobuild and it will install make
$ cd pdf-tools
$ make -s
#+end_src
This should give you no error and should compile the ~epdfinfo~ server. If you face a problem, please report on the issue tracker!
@@ -135,6 +140,7 @@ The following Operating Systems / package managers are supported. *Note*: The pa
- Ubuntu: https://packages.ubuntu.com/impish/elpa-pdf-tools-server
- MSYS2 / MINGW (Windows): https://packages.msys2.org/package/mingw-w64-x86_64-emacs-pdf-tools-server?repo=mingw64
- FreeBSD: https://repology.org/metapackages/?search=pdf-tools&inrepo=freebsd
- GNU Guix: https://hpc.guix.info/package/emacs-pdf-tools
*** Installing the epdfinfo server from source on Windows (+ Gotchas)
:PROPERTIES:
@@ -264,6 +270,37 @@ PDFView Mode is an Emacs PDF viewer. It displays PDF files as PNG images in Emac
|-----------------------------------------------+-------------------------|
| | |
Note that ~pdf-tools~ renders the PDF as images inside Emacs. This means that all the keybindings of ~image-mode~ work on individual PDF pages as well.
*** Continuous Scroll Mode (Experimental)
:PROPERTIES:
:CREATED: [2024-12-31 Tue 14:00]
:ID: 82201772-0166-4335-B4DF-841D03AE1DCD
:END:
~pdf-tools~ supports an optional continuous scroll mode that displays multiple pages simultaneously and allows smooth scrolling across page boundaries. This feature is experimental and off by default.
To enable continuous scroll mode:
#+begin_src elisp
M-x pdf-view-roll-minor-mode RET
#+end_src
When enabled, you will see "Continuous" in the mode-line. In this mode:
- Multiple pages are visible at once in tall windows
- Scrolling is smooth and continuous across page boundaries
- ~C-n~ / ~C-p~ scroll by pixels instead of jumping pages
- Mouse wheel scrolling works smoothly across pages
- Works with ~pixel-scroll-precision-mode~ for trackpad scrolling
To enable by default for all PDFs, add to your configuration:
#+begin_src elisp
(add-hook 'pdf-view-mode-hook #'pdf-view-roll-minor-mode)
#+end_src
You can customize the appearance with:
- ~pdf-roll-vertical-margin~: Pixel height of margin between pages (default: 2)
- ~pdf-roll-margin-color~: Color of the margin between pages (default: "gray")
*Note*: This feature is experimental. Some features may not work perfectly with continuous scroll mode enabled. If you encounter issues, disable the mode with ~M-x pdf-view-roll-minor-mode~.
| Image Mode | |
|------------------------+---------------------------------------------|
| image-scroll-right | ~C-x >~ / ~<remap> <scroll-right>~ |
@@ -288,15 +325,16 @@ Note that ~pdf-tools~ renders the PDF as images inside Emacs. This means that al
:CREATED: [2021-12-30 Thu 18:33]
:ID: 73a18ea8-aa21-48d4-9d8b-dc64e3601000
:END:
| Display | |
|------------------------------------------+-----------------|
| Zoom in / Zoom out | ~+~ / ~-~ |
| Fit Height / Fit Width / Fit Page | ~H~ / ~W~ / ~P~ |
| Trim Margins (set slice to bounding box) | ~s b~ |
| Reset Margins | ~s r~ |
| Reset Zoom | ~0~ |
| Rotate Page | ~R~ |
|------------------------------------------+-----------------|
| Display | |
|--------------------------------------------------+-----------------|
| Zoom in / Zoom out | ~+~ / ~-~ |
| Fit Height / Fit Width / Fit Page | ~H~ / ~W~ / ~P~ |
| Trim Margins (set slice to bounding box) | ~s b~ |
| Trim Margins to common bounding box of all pages | ~s c~ |
| Reset Margins | ~s r~ |
| Reset Zoom | ~0~ |
| Rotate Page | ~R~ |
|--------------------------------------------------+-----------------|
** Annotations
:PROPERTIES:
@@ -405,6 +443,11 @@ Once you have read through the features provided by ~pdf-tools~, you probably wa
:END:
This mode is an alternative to ~linum-mode~ and is available since Emacs 26. ~pdf-tools~ does not work well with it. For example, it makes horizontal navigation (such as ~C-f~, ~C-b~, ~C-x <~ or ~C-x >~ ) in a document impossible.
If you use ~display-line-numbers-mode~ globally, you should disable it for ~pdf-view-mode~:
#+begin_src elisp
(add-hook 'pdf-view-mode-hook (lambda () (display-line-numbers-mode -1)))
#+end_src
** auto-revert
:PROPERTIES:
:CREATED: [2021-12-29 Wed 18:34]
@@ -643,3 +686,22 @@ If your Emacs is compiled for x86, the =Code Type= will be =x86_64=.
:ID: 2D173424-C211-4474-B0D0-83F4381CAFFA
:END:
Thank you for taking the time to contribute back to the code. You may find some useful notes in the [[id:fd64c10c-4ea5-4ece-8d95-b723098dd4f6][Tips and Tricks for Developers]] section. Please be sure to check it out!
** How do I print a PDF?
:PROPERTIES:
:CREATED: [2024-12-30 Mon 20:00]
:ID: D5345C45-2EB7-45B3-B547-E95CB9F81155
:END:
Use ~C-c C-p~ (~pdf-misc-print-document~) to print the current PDF. By default, this command prompts for a print program. To avoid the prompt, configure the print program:
#+begin_src elisp
(setq pdf-misc-print-program-executable "lpr")
#+end_src
Note: ~print-buffer~ does not work with ~pdf-tools~ because it prints buffer text, which ~pdf-tools~ hides using overlays while displaying rendered page images.
** How do I open the current PDF in an external application?
:PROPERTIES:
:CREATED: [2024-12-30 Mon 20:05]
:ID: 11A36424-B728-4A8E-853E-3AE876E3C6CD
:END:
Use ~M-x browse-url-of-file~ to open the current PDF in your system's default external PDF viewer.

View File

@@ -12,7 +12,8 @@ emacs_version = $(shell $(emacs) --batch --eval \
$(info Using Emacs $(emacs_version))
version=$(shell sed -ne 's/^;\+ *Version: *\([0-9.]\)/\1/p' lisp/pdf-tools.el)
pkgname=pdf-tools-$(version)
PKG=pdf-tools
pkgname=$(PKG)-$(version)
pkgfile=$(pkgname).tar
.PHONY: all clean distclean bytecompile test check melpa
@@ -20,9 +21,11 @@ pkgfile=$(pkgname).tar
all: $(pkgfile)
# Create a elpa package including the server
$(pkgfile): .cask/$(emacs_version) server/epdfinfo lisp/*.el
$(pkgfile): .cask/$(emacs_version) server/epdfinfo lisp/*.el loaddefs
$(CASK) package .
loaddefs: $(PKG)-autoloads.el
# Compile the Lisp sources
bytecompile: .cask/$(emacs_version)
$(CASK) exec $(emacs) --batch -L lisp -f batch-byte-compile lisp/*.el
@@ -105,3 +108,18 @@ server-test-supported: server/test/Makefile
server-test-unsupported: server/test/Makefile
$(MAKE) -C server/test print-failing
$(PKG)-autoloads.el: lisp/*.el
@printf " Creating $@\n"
@cd lisp;$(EMACS) -Q --batch -l autoload -l cl-lib --eval "\
(let ((file (expand-file-name \"$@\"))\
(autoload-timestamps nil) \
(backup-inhibited t)\
(version-control 'never)\
(coding-system-for-write 'utf-8-emacs-unix))\
(write-region (autoload-rubric file \"package\" nil) nil file nil 'silent)\
(cl-letf (((symbol-function 'progress-reporter-do-update) (lambda (&rest _)))\
((symbol-function 'progress-reporter-done) (lambda (_))))\
(let ((generated-autoload-file file))\
(update-directory-autoloads default-directory))))" \
2>&1 | sed "/^Package autoload is deprecated$$/d"

View File

@@ -95,10 +95,12 @@ exec_privileged() {
retval=$?
sudo -k
return $retval
elif which doas >/dev/null 2>&1; then
doas -- "$@"
elif which su >/dev/null 2>&1; then
su -c "$(quote "$@")"
else
echo "No such program: sudo or su"
echo "No such program: sudo, doas or su"
exit 1
fi
}
@@ -306,6 +308,26 @@ os_debian() {
return 0
}
# Android using Termux
os_android() {
if ! [ -d "/data/data/com.termux/files/home" ]; then
return 1
fi
PACKAGES="autoconf
automake
binutils
clang
libpng
poppler
zlib
make
xorgproto
pkg-config"
PKGCMD=pkg
PKGARGS="install"
return 0
}
# Msys2
os_msys2() {
if [ -z "$MSYSTEM" ] || ! [ -r "/etc/profile" ]; then
@@ -332,6 +354,16 @@ os_msys2() {
mingw-w64-i686-toolchain
mingw-w64-i686-openssl
mingw-w64-i686-zlib" ;;
UCRT64)
PACKAGES="base-devel
autoconf
automake
mingw-w64-ucrt-x86_64-libpng
mingw-w64-ucrt-x86_64-poppler
mingw-w64-ucrt-x86_64-imagemagick
mingw-w64-ucrt-x86_64-toolchain
mingw-w64-ucrt-x86_64-openssl
mingw-w64-ucrt-x86_64-zlib" ;;
MSYS)
case $(uname -m) in
x86_64)
@@ -348,7 +380,7 @@ os_msys2() {
exit 1 ;;
esac
PKGCMD=pacman
PKGARGS="-S --needed"
PKGARGS="-S --needed --noconfirm"
PKG_INSTALL_AS_ROOT=
return 0
}
@@ -367,7 +399,7 @@ os_macos() {
# explanation has to do with 'keg-only' installs). If you do,
# please update:
# https://github.com/vedang/pdf-tools/issues/270
export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:$(brew --prefix libffi)/lib/pkgconfig/:$(brew --prefix zlib)/lib/pkgconfig/:$(brew --prefix glib)/lib/pkgconfig/"
export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:$(brew --prefix poppler)/lib/pkgconfig/:$(brew --prefix libffi)/lib/pkgconfig/:$(brew --prefix zlib)/lib/pkgconfig/:$(brew --prefix glib)/lib/pkgconfig/:$(brew --prefix pcre2)/lib/pkgconfig/"
elif which port >/dev/null 2>&1; then
PKGCMD=port
PKGARGS=install
@@ -493,6 +525,30 @@ os_alpine() {
return 0
}
# Nobara
os_nobara() {
if ! [ -e "/etc/nobara-release" ]; then
return 1
fi
PKGCMD=dnf
PKGARGS=install
PACKAGES="autoconf
automake
gcc
libpng-devel
make
poppler-devel
poppler-glib-devel
zlib-devel"
VERSION=$(source_var /etc/os-release VERSION_ID)
if [ -n "$VERSION" ] && [ "$VERSION" -ge 26 ]; then
PACKAGES="$PACKAGES pkgconf"
else
PACKAGES="$PACKAGES pkgconfig"
fi
return 0
}
# By Parameter --os
os_argument() {
[ -z "$OS" ] && return 1
@@ -511,6 +567,8 @@ os_argument() {
void) os_void "$@";;
opensuse) os_opensuse "$@";;
alpine) os_alpine "$@";;
nobara) os_nobara "$@";;
android) os_android "$@";;
*) echo "Invalid --os argument: $OS"
exit 1
esac || {
@@ -541,6 +599,8 @@ os_nixos "$@" || \
os_void "$@" || \
os_opensuse "$@" || \
os_alpine "$@" || \
os_nobara "$@" || \
os_android "$@" || \
{
OS_IS_HANDLED=
if [ -z "$DRY_RUN" ]; then

View File

@@ -0,0 +1,2 @@
#!/bin/sh
sh autobuild "$@"

View File

@@ -36,7 +36,7 @@ AC_COMPILE_IFELSE(
AM_CONDITIONAL(HAVE_W32, [test "$have_w32" = true])
if test "$have_w32" = true; then
if test "$MSYSTEM" = MINGW32 -o "$MSYSTEM" = MINGW64; then
if test "$MSYSTEM" = MINGW32 -o "$MSYSTEM" = MINGW64 -o "$MSYSTEM" = UCRT64; then
# glib won't work properly on msys2 without it.
CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $CFLAGS"
fi
@@ -84,7 +84,7 @@ AC_C_BIGENDIAN
# Checks for library functions.
AC_FUNC_ERROR_AT_LINE
AC_FUNC_STRTOD
AC_CHECK_FUNCS([strcspn strtol getline])
AC_CHECK_FUNCS([strcspn strtol getline _tempnam])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

View File

@@ -346,12 +346,13 @@ strchomp (char *str)
static char*
mktempfile()
{
#if defined (HAVE__TEMPNAM)
char *filename = NULL;
int tries = 3;
while (! filename && tries-- > 0)
{
filename = tempnam(NULL, "epdfinfo");
filename = _tempnam(NULL, "epdfinfo");
if (filename)
{
int fd = open(filename, O_CREAT | O_EXCL | O_RDONLY, S_IRUSR | S_IWUSR);
@@ -366,7 +367,20 @@ mktempfile()
}
if (! filename)
fprintf (stderr, "Unable to create tempfile");
#else
char template[] = P_tmpdir "/epdfinfoXXXXXX";
char *filename = malloc(sizeof(template));
memcpy(filename, template, sizeof(template));
int fd = mkstemp(filename);
if (fd == -1)
{
fprintf (stderr, "Unable to create tempfile");
free(filename);
filename = NULL;
}
else
close(fd);
#endif
return filename;
}
@@ -469,7 +483,8 @@ static inline gboolean color_equal(struct color a, struct color b)
static void
image_recolor (cairo_surface_t * surface, const PopplerColor * fg,
const PopplerColor * bg, int usecolors)
const PopplerColor * bg, int usecolors,
double gamma, int gammabeforeinvert)
{
/* Performs one of two kinds of image recoloring depending on the value of usecolors:
@@ -529,9 +544,9 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg,
{
/* Careful. data color components blue, green, red. */
struct color rgb = {
.r = (double) data[2] / 256.,
.g = (double) data[1] / 256.,
.b = (double) data[0] / 256.
.r = (double) data[2] / 255.,
.g = (double) data[1] / 255.,
.b = (double) data[0] / 255.
};
/* Linear interpolation between bg and fg based on the
@@ -557,6 +572,7 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg,
white->background and black->foreground and have a single entry cache to
speed up computation */
const struct color white = {.r = 1.0, .g = 1.0, .b = 1.0};
const struct color black = {.r = 0.0, .g = 0.0, .b = 0.0};
struct color precomputed_rgb = white;
struct color precomputed_inv_rgb = rgb_bg;
@@ -564,8 +580,6 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg,
struct color oklab_fg = rgb2oklab(rgb_fg);
struct color oklab_bg = rgb2oklab(rgb_bg);
const double oklab_diff_l = oklab_fg.l - oklab_bg.l;
unsigned int y;
for (y = 0; y < page_height * rowstride; y += rowstride)
{
@@ -576,9 +590,9 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg,
{
/* Careful. data color components blue, green, red. */
struct color rgb = {
.r = (double) data[2] / 256.,
.g = (double) data[1] / 256.,
.b = (double) data[0] / 256.
.r = (double) data[2] / 255.,
.g = (double) data[1] / 255.,
.b = (double) data[0] / 255.
};
/* Convert to Oklab coordinates, invert perceived lightness,
@@ -587,6 +601,10 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg,
{
rgb = rgb_bg;
}
else if (color_equal(black, rgb))
{
rgb = rgb_fg;
}
else if (color_equal(precomputed_rgb, rgb))
{
rgb = precomputed_inv_rgb;
@@ -599,7 +617,20 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg,
/* Invert the perceived lightness, and scales it */
double l = oklab.l;
double inv_l = 1.0 - l;
oklab.l = oklab_bg.l + oklab_diff_l * inv_l;
/* Nonlinearly scale lightness */
if (gammabeforeinvert)
{
l = pow(l, gamma);
inv_l = 1.0 - l;
}
else
{
inv_l = pow(inv_l, gamma);
l = 1.0 - inv_l;
}
oklab.l = oklab_bg.l * l + oklab_fg.l * inv_l;
/* Have a and b parameters (which encode hue and saturation)
start at the background value and interpolate up to
@@ -692,7 +723,8 @@ image_render_page(PopplerDocument *pdf, PopplerPage *page,
cairo_paint (cr);
if (options && (options->usecolors))
image_recolor (surface, &options->fg, &options->bg, options->usecolors);
image_recolor (surface, &options->fg, &options->bg, options->usecolors,
options->gamma, options->gammabeforeinvert);
cairo_destroy (cr);
@@ -1105,6 +1137,15 @@ command_arg_parse_arg (const epdfinfo_t *ctx, const char *arg,
error_msg, "Expected 0 or 1:%s", arg);
cmd_arg->value.flag = *arg == '1';
break;
case ARG_DOUBLE:
{
char *endptr;
double n = strtod (arg, &endptr);
cerror_if_not (! (*endptr),
error_msg, "Expected double (floating point): %s", arg);
cmd_arg->value.scalar = n;
}
break;
case ARG_NONEMPTY_STRING:
cerror_if_not (*arg, error_msg, "Non-empty string expected");
/* fall through */
@@ -1246,6 +1287,9 @@ command_arg_print(const command_arg_t *arg)
case ARG_BOOL:
printf ("%d", arg->value.flag ? 1 : 0);
break;
case ARG_DOUBLE:
printf ("%f", arg->value.scalar);
break;
case ARG_NONEMPTY_STRING: /* fall */
case ARG_STRING:
print_response_string (arg->value.string, NONE);
@@ -1297,6 +1341,7 @@ command_arg_type_size(command_arg_type_t type)
case ARG_INVALID: return 0;
case ARG_DOC: return sizeof (arg.value.doc);
case ARG_BOOL: return sizeof (arg.value.flag);
case ARG_DOUBLE: return sizeof (arg.value.scalar);
case ARG_NONEMPTY_STRING: /* fall */
case ARG_STRING: return sizeof (arg.value.string);
case ARG_NATNUM: return sizeof (arg.value.natnum);
@@ -3653,6 +3698,8 @@ const document_option_t document_options [] =
DEC_DOPT (":render/printed", ARG_BOOL, render.printed),
DEC_DOPT (":render/foreground", ARG_COLOR, render.fg),
DEC_DOPT (":render/background", ARG_COLOR, render.bg),
DEC_DOPT (":render/gamma", ARG_DOUBLE, render.gamma),
DEC_DOPT (":render/gammabeforeinvert", ARG_BOOL, render.gammabeforeinvert),
};
const command_arg_type_t cmd_getoptions_spec[] =

View File

@@ -165,6 +165,7 @@ typedef enum
ARG_INVALID = 0,
ARG_DOC,
ARG_BOOL,
ARG_DOUBLE,
ARG_STRING,
ARG_NONEMPTY_STRING,
ARG_NATNUM,
@@ -188,6 +189,8 @@ typedef struct
PopplerColor bg, fg;
gboolean usecolors;
gboolean printed;
gboolean gammabeforeinvert;
gdouble gamma;
} render_options_t;
typedef struct
@@ -214,6 +217,7 @@ typedef struct
union
{
gboolean flag;
gdouble scalar;
const char *string;
long natnum;
document_t *doc;

View File

@@ -8415,7 +8415,8 @@ static int _synctex_updater_print(synctex_updater_p updater, const char * format
}
return result;
}
#if defined(_MSC_VER)
#if defined(_WIN32)
// define vasprintf as its available only on Linux and macOS.
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -8424,17 +8425,14 @@ static int vasprintf(char **ret,
const char *format,
va_list ap)
{
int len;
len = _vsnprintf(NULL, 0, format, ap);
int len = vsnprintf(NULL, 0, format, ap);
if (len < 0) return -1;
*ret = malloc(len + 1);
if (!*ret) return -1;
_vsnprintf(*ret, len+1, format, ap);
(*ret)[len] = '\0';
return len;
return vsnprintf(*ret, len + 1, format, ap);
}
#endif
#endif // _WIN32
/**
* gzvprintf is not available until OSX 10.10

View File

@@ -479,6 +479,7 @@ Signals an error, if PROPERTY is not modifiable.
Returns the modified annotation."
(declare (indent 2))
(setq a (pdf-annot--ensure-fresh a))
(unless (equal value (pdf-annot-get a property))
(unless (pdf-annot-property-modifiable-p a property)
(error "Property `%s' is read-only for this annotation"
@@ -558,6 +559,15 @@ have identical id properties."
"Return id property of annotation A."
(pdf-annot-get a 'id))
(defun pdf-annot--ensure-fresh (a)
"Return a fresh version of annotation A from the server.
If the annotation does not exist anymore, signal an error."
(let ((id (pdf-annot-get-id a)))
(or (cl-find id
(pdf-annot-getannots (pdf-annot-get a 'page) nil (pdf-annot-get-buffer a))
:key #'pdf-annot-get-id)
(user-error "No such annotation: %s" id))))
(defun pdf-annot-get-type (a)
"Return type property of annotation A."
(pdf-annot-get a 'type))
@@ -581,6 +591,7 @@ This function always returns nil."
(interactive
(list (pdf-annot-read-annotation
"Click on the annotation you wish to delete")))
(setq a (pdf-annot--ensure-fresh a))
(with-current-buffer (pdf-annot-get-buffer a)
(pdf-info-delannot
(pdf-annot-get-id a))
@@ -644,6 +655,7 @@ The DO-SAVE argument is given to
`pdf-info-getattachment-from-annot', which see."
(unless (pdf-annot-has-attachment-p a)
(error "Annotation has no data attached: %s" a))
(setq a (pdf-annot--ensure-fresh a))
(pdf-info-getattachment-from-annot
(pdf-annot-get-id a)
do-save
@@ -892,6 +904,7 @@ i.e. a non mouse-movement event is read."
(let* ((mpos (posn-object-x-y (event-start event)))
(a (or annot
(pdf-annot-at-position mpos))))
(setq a (pdf-annot--ensure-fresh a))
(unless a
(error "No annotation at this position: %s" mpos))
(let* ((apos (pdf-annot-image-position a))
@@ -999,7 +1012,8 @@ other annotations."
`("white" "steel blue" 0.35 ,@edges))
:map (pdf-view-apply-hotspot-functions
window page size)
:width (car size))))
:width (car size))
(when pdf-view-roll-minor-mode page)))
(pdf-util-scroll-to-edges
(pdf-util-scale-relative-to-pixel (car edges)))))))
@@ -1086,8 +1100,8 @@ Return the new annotation."
(pdf-annot-activate-annotation a))
a))
(defun pdf-annot-add-text-annotation (pos &optional icon property-alist)
"Add a new text annotation at POS in the selected window.
(defun pdf-annot-add-text-annotation (pos &optional icon property-alist page)
"Add a new text annotation at POS on PAGE in the selected window.
POS should be a image position object or a cons \(X . Y\), both
being image coordinates.
@@ -1115,6 +1129,9 @@ Return the new annotation."
(list posn)))
(pdf-util-assert-pdf-window)
(when (posnp pos)
(setq page (or page
(when pdf-view-roll-minor-mode
(1+ (/ (posn-point pos) 4)))))
(setq pos (posn-object-x-y pos)))
(let ((isize (pdf-view-image-size))
(x (car pos))
@@ -1139,7 +1156,8 @@ Return the new annotation."
property-alist
(cdr (assq 'text pdf-annot-default-annotation-properties))
(cdr (assq t pdf-annot-default-annotation-properties))
`((color . ,(car pdf-annot-color-history))))))))
`((color . ,(car pdf-annot-color-history))))
page))))
(defun pdf-annot-mouse-add-text-annotation (ev)
"Add a text annotation using the mouse.
@@ -1155,11 +1173,12 @@ EV describes the captured mouse event."
"Click where a new text annotation should be added ..."))
(event-start ev))))
(defun pdf-annot-add-markup-annotation (list-of-edges type &optional color
(defun pdf-annot-add-markup-annotation (region type &optional color
property-alist)
"Add a new markup annotation in the selected window.
LIST-OF-EDGES determines the marked up area and should be a list
REGION determines the marked up area and should be a cons cell
\(PAGE . LIST-OF-EDGES\) where LIST-OF-EDGES should be list
of \(LEFT TOP RIGHT BOT\), each value a relative coordinate.
TYPE should be one of `squiggly', `underline', `strike-out' or
@@ -1182,7 +1201,7 @@ Return the new annotation."
(pdf-util-assert-pdf-window)
(pdf-annot-add-annotation
type
list-of-edges
(cdr region)
(pdf-annot-merge-alists
(and color `((color . ,color)))
property-alist
@@ -1191,7 +1210,7 @@ Return the new annotation."
(when pdf-annot-color-history
`((color . ,(car pdf-annot-color-history))))
'((color . "#ffff00")))
(pdf-view-current-page)))
(car region)))
(defun pdf-annot-add-squiggly-markup-annotation (list-of-edges
&optional color property-alist)
@@ -1510,6 +1529,7 @@ At any given point of time, only one annotation can be in edit mode."
(not (eq a pdf-annot-edit-contents--annotation)))
(with-current-buffer pdf-annot-edit-contents--buffer
(pdf-annot-edit-contents-finalize 'ask)))
(setq a (pdf-annot--ensure-fresh a))
(unless (buffer-live-p pdf-annot-edit-contents--buffer)
(setq pdf-annot-edit-contents--buffer
(get-buffer-create
@@ -1540,6 +1560,66 @@ At any given point of time, only one annotation can be in edit mode."
(error "No annotation at this position"))
(pdf-annot-edit-contents a)))
(defun pdf-annot-edit (annot)
"Activate ANNOT, for editing.
Interactively, annot is read via `pdf-annot-read-annot'.
This function displays characters around the annots in the current
page and starts reading characters (ignoring case). After a
sufficient number of characters have been read, the corresponding
annot's annot is invoked. Additionally, SPC may be used to
scroll the current page."
(interactive
(list (or (pdf-annot-read-annot "Activate annot (SPC scrolls): ")
(error "No annot selected"))))
(pdf-annot-activate-annotation annot))
;; TODO 'merge' this function with `pdf-links-read-link-action' into a single
;; universal 'read-action' function (in `pdf-util'?)
(defun pdf-annot-read-annot (prompt)
"Using PROMPT, interactively read an annot-action.
See `pdf-annot-edit' for the interface."
(pdf-util-assert-pdf-window)
(let* ((annots (pdf-annot-getannots (pdf-view-current-page) nil nil))
(keys (pdf-links-read-link-action--create-keys
(length annots)))
(key-strings (mapcar (apply-partially 'apply 'string)
keys))
(alist (cl-mapcar 'cons keys annots))
(size (pdf-view-image-size))
(colors (pdf-util-face-colors
'pdf-links-read-link pdf-view-dark-minor-mode))
(args (list
:foreground (car colors)
:background (cdr colors)
:formats
`((?c . ,(lambda (_edges) (pop key-strings)))
(?P . ,(number-to-string
(max 1 (* (cdr size)
pdf-links-convert-pointsize-scale)))))
:commands pdf-links-read-link-convert-commands
:apply (pdf-util-scale-relative-to-pixel
(mapcar (lambda (l) (cdr (assq 'edges l)))
annots)))))
;; (print (plist-get args :apply))
(unless annots
(error "No annots on this page"))
(unwind-protect
(let ((image-data
(pdf-cache-get-image
(pdf-view-current-page)
(car size) (car size) 'pdf-annot-read-annot)))
(unless image-data
(setq image-data (apply 'pdf-util-convert-page args ))
(pdf-cache-put-image
(pdf-view-current-page)
(car size) image-data 'pdf-annot-read-annot))
(pdf-view-display-image
(create-image image-data (pdf-view-image-type) t)
(when pdf-view-roll-minor-mode (pdf-view-current-page)))
(pdf-links-read-link-action--read-chars prompt alist))
(pdf-view-redisplay))))
;; * ================================================================== *
@@ -1582,6 +1662,7 @@ Currently supported properties are page, type, label, date and contents."
(defvar pdf-annot-list-mode-map
(let ((km (make-sparse-keymap)))
(define-key km (kbd "e") 'pdf-annot-edit)
(define-key km (kbd "C-c C-f") #'pdf-annot-list-follow-minor-mode)
(define-key km (kbd "SPC") #'pdf-annot-list-display-annotation-from-id)
km))

View File

@@ -27,6 +27,11 @@
(require 'pdf-info)
(require 'pdf-util)
;; Silence native-comp warnings about functions defined elsewhere
(declare-function pdf-view-desired-image-size "pdf-view")
(declare-function pdf-view-create-page "pdf-view")
(declare-function image-mode-window-get "image-mode")
;; * ================================================================== *
;; * Customiazations
@@ -451,10 +456,7 @@ WINDOW and IMAGE-WIDTH decide the page and scale of the final image."
(image-size (pdf-view-create-page page))
(pdf-util-debug
(message "Prefetched page %s." page))
;; Avoid max-lisp-eval-depth
(run-with-timer
0.001 nil
#'pdf-cache--prefetch-pages window image-width)))))))
(pdf-cache--prefetch-pages window image-width)))))))
(condition-case err
(pdf-info-renderpage page image-width)
(error

View File

@@ -51,6 +51,13 @@
(require 'tq)
(require 'cl-lib)
;; Silence native-comp warnings about functions defined elsewhere
(declare-function pdf-util-frame-scale-factor "pdf-util")
(declare-function pdf-util-hexcolor "pdf-util")
(declare-function pdf-util-munch-file "pdf-util")
(declare-function pdf-util-highlight-regexp-in-string "pdf-util")
(declare-function pdf-view-buffer-file-name "pdf-view")
;; * ================================================================== *
@@ -572,6 +579,8 @@ interrupted."
(cl-case key
((:render/printed)
(setq value (equal value "1")))
((:render/gammabeforeinvert)
(setq value (equal value "1")))
((:render/usecolors)
(setq value (ignore-errors
(let ((int-val (cl-parse-integer value)))
@@ -1734,6 +1743,8 @@ Returns a list \(LEFT TOP RIGHT BOT\)."
soptions))
((:render/printed)
(push (if value 1 0) soptions))
((:render/gammabeforeinvert)
(push (if value 1 0) soptions))
((:render/usecolors)
;; 0 -> original color
;; 1 -> recolor document to grayscale mapping black to

View File

@@ -33,7 +33,7 @@
(require 'let-alist)
;;; Code:
(defvar pdf-isearch--hl-matches-tick 0)
;; * ================================================================== *
@@ -249,42 +249,55 @@ This is a Isearch interface function."
(when (> (length string) 0)
(let ((same-search-p (pdf-isearch-same-search-p))
(oldpage pdf-isearch-current-page)
(matches (pdf-isearch-search-page string))
(pages (or (image-mode-window-get 'displayed-pages (selected-window))
(list (pdf-view-current-page))))
matches
next-match)
;; matches is a list of list of edges ((x0 y1 x1 y2) ...),
;; sorted top to bottom ,left to right. Coordinates are in image
;; space.
(unless isearch-forward
(setq matches (reverse matches)))
(when pdf-isearch-filter-matches-function
(setq matches (funcall pdf-isearch-filter-matches-function matches)))
(dolist (page pages)
(let ((page-matches (pdf-isearch-search-page string page)))
;; matches is a list of list of edges ((x0 y1 x1 y2) ...),
;; sorted top to bottom ,left to right. Coordinates are in image
;; space.
(unless isearch-forward
(setq page-matches (reverse page-matches)))
(when pdf-isearch-filter-matches-function
(setq page-matches (funcall pdf-isearch-filter-matches-function page-matches)))
(push page-matches matches)))
;; Where to go next ?
(setq pdf-isearch-current-page (pdf-view-current-page)
pdf-isearch-current-matches matches
next-match
(pdf-isearch-next-match
oldpage pdf-isearch-current-page
pdf-isearch-current-match matches
pdf-isearch-current-match (car matches)
same-search-p
isearch-forward)
pdf-isearch-current-parameter
(list string isearch-regexp
isearch-case-fold-search isearch-word))
(cl-callf nreverse matches)
(cond
(next-match
(setq pdf-isearch-current-match next-match)
(pdf-isearch-hl-matches next-match matches)
(pdf-isearch-hl-matches next-match matches nil pages)
(pdf-isearch-focus-match next-match)
;; Don't get off track.
(when (or (and (bobp) (not isearch-forward))
(and (eobp) isearch-forward))
(goto-char (1+ (/ (buffer-size) 2))))
(unless pdf-view-roll-minor-mode
(goto-char (1+ (/ (buffer-size) 2)))))
;; Signal success to isearch.
;; Moving the point is for `pdf-roll'. It ensures that
;; `re-search-forward' takes us back to the starting point. Otherwise
;; every call to `isearch-repeat' will increment/decrement the point
;; and that causes recentering.
(if isearch-forward
(re-search-forward ".")
(progn (unless (bobp) (forward-char -1))
(re-search-forward "."))
(unless (eobp) (forward-char 1))
(re-search-backward ".")))
((and (not pdf-isearch-narrow-to-page)
(not (pdf-isearch-empty-match-p matches)))
(not (pdf-isearch-empty-match-p pdf-isearch-current-matches)))
(let ((next-page (pdf-isearch-find-next-matching-page
string pdf-isearch-current-page t)))
(when next-page
@@ -306,12 +319,14 @@ This is a Isearch interface function."
pdf-isearch-current-matches matches
pdf-isearch-current-match match
pdf-isearch-current-page page)
(pdf-view-goto-page pdf-isearch-current-page)
(when pdf-isearch-current-match
(pdf-isearch-hl-matches
pdf-isearch-current-match
pdf-isearch-current-matches))
(if pdf-isearch-current-match
(pdf-isearch-hl-matches
pdf-isearch-current-match
pdf-isearch-current-matches
nil (image-mode-window-get 'displayed-pages (selected-window)))
(when pdf-view-roll-minor-mode
(pdf-view-redisplay)))
(image-set-window-hscroll hscroll)
(image-set-window-vscroll vscroll))))
@@ -347,7 +362,8 @@ This is a Isearch interface function."
pdf-isearch-current-match nil
pdf-isearch-current-matches nil
pdf-isearch-current-parameter nil)
(goto-char (1+ (/ (buffer-size) 2))))
(unless pdf-view-roll-minor-mode
(goto-char (1+ (/ (buffer-size) 2)))))
(defun pdf-isearch-same-search-p (&optional ignore-search-string-p)
"Return non-nil, if search parameter have not changed.
@@ -385,8 +401,12 @@ there was no previous search, this function returns t."
(defun pdf-isearch-redisplay ()
"Redisplay the current highlighting."
(pdf-isearch-hl-matches pdf-isearch-current-match
pdf-isearch-current-matches))
(pdf-isearch-hl-matches
pdf-isearch-current-match
pdf-isearch-current-matches
nil
(or (image-mode-window-get 'displayed-pages (selected-window))
(list (pdf-view-current-page)))))
(defun pdf-isearch-update ()
"Update search and redisplay, if necessary."
@@ -412,13 +432,14 @@ there was no previous search, this function returns t."
(message "%s" msg))))
(defun pdf-isearch-empty-match-p (matches)
(and matches
(let ((all-matches (apply #'append matches)))
(and all-matches
(cl-every
(lambda (match)
(cl-every (lambda (edges)
(cl-every 'zerop edges))
match))
matches)))
all-matches))))
(defun pdf-isearch-occur ()
"Run `occur' using the last search string or regexp."
@@ -564,10 +585,10 @@ is no such page."
(= incr 8)) ;;Don't bother right away.
(setq reporter
(apply
'make-progress-reporter "Searching"
(if isearch-forward
(list (car pages) (pdf-cache-number-of-pages) nil 0)
(list 1 (cdr pages) nil 0)))))
'make-progress-reporter "Searching"
(if isearch-forward
(list (car pages) (pdf-cache-number-of-pages) nil 0)
(list 1 (cdr pages) nil 0)))))
(when reporter
(progress-reporter-update
reporter (if isearch-forward
@@ -674,18 +695,18 @@ it is assumed to be ordered with respect to FORWARD-P."
(let ((matched (apply 'pdf-util-edges-union match)))
(pdf-util-with-edges (matched)
(cl-loop for next in matches do
(let ((edges (apply 'pdf-util-edges-union next)))
(pdf-util-with-edges (edges)
(when (if forward-p
(or (>= edges-top matched-bot)
(and (or (>= edges-top matched-top)
(>= edges-bot matched-bot))
(>= edges-right matched-right)))
(or (<= edges-bot matched-top)
(and (or (<= edges-bot matched-bot)
(<= edges-top matched-top))
(<= edges-left matched-left))))
(cl-return next))))))))
(let ((edges (apply 'pdf-util-edges-union next)))
(pdf-util-with-edges (edges)
(when (if forward-p
(or (>= edges-top matched-bot)
(and (or (>= edges-top matched-top)
(>= edges-bot matched-bot))
(>= edges-right matched-right)))
(or (<= edges-bot matched-top)
(and (or (<= edges-bot matched-bot)
(<= edges-top matched-top))
(<= edges-left matched-left))))
(cl-return next))))))))
@@ -716,41 +737,45 @@ MATCH-BG LAZY-FG LAZY-BG\)."
(car lazy)
(cdr lazy)))))))
(defvar pdf-isearch--hl-matches-tick 0)
(defun pdf-isearch-hl-matches (current matches &optional occur-hack-p)
(defun pdf-isearch-hl-matches (current matches &optional occur-hack-p pages)
"Highlighting edges CURRENT and MATCHES."
(cl-check-type current pdf-isearch-match)
(cl-check-type matches (list-of pdf-isearch-match))
(cl-check-type matches (list-of (list-of pdf-isearch-match)))
(cl-destructuring-bind (fg1 bg1 fg2 bg2)
(pdf-isearch-current-colors)
(let* ((width (car (pdf-view-image-size)))
(page (pdf-view-current-page))
(window (selected-window))
(let* ((window (selected-window))
(pages (or pages
(image-mode-window-get 'displayed-pages (selected-window))
(list (pdf-view-current-page))))
(buffer (current-buffer))
(tick (cl-incf pdf-isearch--hl-matches-tick))
(pdf-info-asynchronous
(lambda (status data)
(when (and (null status)
(eq tick pdf-isearch--hl-matches-tick)
(buffer-live-p buffer)
(window-live-p window)
(eq (window-buffer window)
buffer))
(with-selected-window window
(when (and (derived-mode-p 'pdf-view-mode)
(or isearch-mode
occur-hack-p)
(eq page (pdf-view-current-page)))
(pdf-view-display-image
(pdf-view-create-image data :width width))))))))
(pdf-info-renderpage-text-regions
page width t nil nil
`(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative
current))
`(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative
(apply 'append
(remove current matches))))))))
(tick (cl-incf pdf-isearch--hl-matches-tick)))
(dolist (page pages)
(let* ((width (car (pdf-view-image-size nil window page)))
(pdf-info-asynchronous
(lambda (status data)
(when (and (null status)
(eq tick pdf-isearch--hl-matches-tick)
(buffer-live-p buffer)
(window-live-p window)
(eq (window-buffer window)
buffer))
(with-selected-window window
(when (and (derived-mode-p 'pdf-view-mode)
(or isearch-mode occur-hack-p
(memq last-command '(isearch-repeat-forward isearch-repeat-backward)))
(or (eq page (pdf-view-current-page))
(memq page (image-mode-window-get 'displayed-pages window))))
(pdf-view-display-image
(pdf-view-create-image data :width width)
page window)))))))
(pdf-info-renderpage-text-regions
page width t nil nil
`(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative
(when (eq page (pdf-view-current-page window))
current)))
`(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative
(apply 'append
(remove current (pop matches)))))))))))
;; * ================================================================== *

View File

@@ -29,6 +29,10 @@
(require 'let-alist)
(require 'org)
(declare-function pdf-roll-page-overlay "pdf-roll")
(declare-function pdf-roll-displayed-pages "pdf-roll")
;;; Code:
@@ -103,7 +107,7 @@ do something with it."
;;;###autoload
(define-minor-mode pdf-links-minor-mode
"Handle links in PDF documents.\\<pdf-links-minor-mode-map>
"Handle links in PDF documents.
If this mode is enabled, most links in the document may be
activated by clicking on them or by pressing \\[pdf-links-action-perform] and selecting
@@ -151,7 +155,7 @@ links via \\[pdf-links-isearch-link].
(nreverse hotspots)))
(defun pdf-links-action-to-string (link)
"Return a string representation of ACTION."
"Return a string representation of action for LINK."
(let-alist link
(concat
(cl-case .type
@@ -208,17 +212,10 @@ scroll the current page."
(with-selected-window window
(when (derived-mode-p 'pdf-view-mode)
(when (> .page 0)
(pdf-view-goto-page .page))
(pdf-view-goto-page .page window))
(when .top
;; Showing the tooltip delays displaying the page for
;; some reason (sit-for/redisplay don't help), do it
;; later.
(run-with-idle-timer 0.001 nil
(lambda ()
(when (window-live-p window)
(with-selected-window window
(when (derived-mode-p 'pdf-view-mode)
(pdf-util-tooltip-arrow .top)))))))))))
(when (derived-mode-p 'pdf-view-mode)
(pdf-util-tooltip-arrow .top)))))))
(uri
(funcall pdf-links-browse-uri-function .uri))
(t
@@ -231,44 +228,47 @@ scroll the current page."
See `pdf-links-action-perform' for the interface."
(pdf-util-assert-pdf-window)
(let* ((links (pdf-cache-pagelinks
(pdf-view-current-page)))
(let* ((win (selected-window))
(pages (if pdf-view-roll-minor-mode
(reverse (image-mode-window-get 'displayed-pages win))
(list (pdf-view-current-page))))
(links (mapcar #'pdf-cache-pagelinks pages))
(keys (pdf-links-read-link-action--create-keys
(length links)))
(key-strings (mapcar (apply-partially 'apply 'string)
keys))
(alist (cl-mapcar 'cons keys links))
(size (pdf-view-image-size))
(apply #'+ (mapcar #'length links))))
(alist (cl-mapcar 'cons keys (apply #'append links)))
(colors (pdf-util-face-colors
'pdf-links-read-link pdf-view-dark-minor-mode))
(args (list
:foreground (car colors)
:background (cdr colors)
:formats
`((?c . ,(lambda (_edges) (pop key-strings)))
(?P . ,(number-to-string
(max 1 (* (cdr size)
pdf-links-convert-pointsize-scale)))))
:commands pdf-links-read-link-convert-commands
:apply (pdf-util-scale-relative-to-pixel
(mapcar (lambda (l) (cdr (assq 'edges l)))
links)))))
(unless links
(error "No links on this page"))
(unwind-protect
(let ((image-data
(pdf-cache-get-image
(pdf-view-current-page)
(car size) (car size) 'pdf-links-read-link-action)))
(unless image-data
(setq image-data (apply 'pdf-util-convert-page args ))
(pdf-cache-put-image
(pdf-view-current-page)
(car size) image-data 'pdf-links-read-link-action))
(pdf-view-display-image
(create-image image-data (pdf-view-image-type) t))
(pdf-links-read-link-action--read-chars prompt alist))
(pdf-view-redisplay))))
'pdf-links-read-link pdf-view-dark-minor-mode)))
(if (not links)
(error "No links on displayed pages")
(unwind-protect
(progn
(dolist (page pages)
(let* ((image (or (overlay-get (pdf-roll-page-overlay page win) 'display)
(pdf-view-current-image)))
(image (or (assoc 'image image) image))
(height (cdr (image-size image t)))
(orig-image (create-image (plist-get (cdr image) :data)
(pdf-view-image-type) t)))
(pdf-view-display-image
(create-image (pdf-util-convert-image
orig-image
:foreground (car colors)
:background (cdr colors)
:formats
`((?c . ,(lambda (_edges) (apply #'string (pop keys))))
(?P . ,(number-to-string
(max 1 (* height
pdf-links-convert-pointsize-scale)))))
:commands pdf-links-read-link-convert-commands
:apply (pdf-util-scale
(mapcar (lambda (l) (cdr (assq 'edges l)))
(pop links))
(image-size orig-image t)))
(pdf-view-image-type) t
:height height)
page win)))
(pdf-links-read-link-action--read-chars prompt alist))
(pdf-view-redisplay)))))
(defun pdf-links-read-link-action--read-chars (prompt alist)
(catch 'done

View File

@@ -212,6 +212,9 @@
(lookup-key pdf-misc-menu-bar-minor-mode-map
[menu-bar pdf\ tools]))))))
(define-derived-mode pdf-metadata-buffer-mode special-mode "PDF Metadata"
"View and traverse the metadata of a PDF file.")
(defun pdf-misc-display-metadata ()
"Display all available metadata in a separate buffer."
(interactive)
@@ -219,6 +222,8 @@
(let* ((buffer (current-buffer))
(md (pdf-info-metadata)))
(with-current-buffer (get-buffer-create "*PDF-Metadata*")
(unless (derived-mode-p 'pdf-metadata-buffer-mode)
(pdf-metadata-buffer-mode))
(let* ((inhibit-read-only t)
(pad (apply' max (mapcar (lambda (d)
(length (symbol-name (car d))))

View File

@@ -629,9 +629,9 @@ matches linked with PAGE."
(setq pdf-occur-number-of-matches 0)
(setq pdf-occur-search-pages-left
(apply #'+ (mapcar (lambda (elt)
(1+ (- (cdr (nth 1 elt))
(car (nth 1 elt)))))
batches)))))
(1+ (- (cdr (nth 1 elt))
(car (nth 1 elt)))))
batches)))))

423
lisp/pdf-tools/pdf-roll.el Normal file
View File

@@ -0,0 +1,423 @@
;;; pdf-roll.el --- Add continuous scroll. -*- lexical-binding: t -*-
;; Copyright (C) 2013, 2014 Andreas Politz
;; Author: Daniel Nicolai <dalanicolai@gmail.com>
;; Keywords: files, multimedia
;; 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
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(require 'pdf-view)
(put 'pdf-roll 'display '(space :width 25 :height 1000))
(put 'pdf-roll 'evaporate t)
(put 'pdf-roll-margin 'evaporate t)
;;; Custom Variables
(defgroup pdf-roll nil
"Image roll configurations."
:group 'pdf-view)
(defface pdf-roll-default `((t :font ,(font-spec :family "monospace" :size 1)))
"Default face for image roll documents.")
(defcustom pdf-roll-vertical-margin 2
"Vertical margin between images in pixels, i.e. page separation height."
:type 'integer)
(defcustom pdf-roll-margin-color "gray"
"Background color of overlay, i.e. page separation color."
:type 'color
:set (lambda (sym color)
(set-default sym color)
(put 'pdf-roll-margin 'face `(:background ,color))))
;;; Variables
(defvar pdf-roll--state nil
"Local variable that tracks window, point and vscroll to handle changes.")
;;; Utility Macros and functions
(defsubst pdf-roll-page-to-pos (page)
"Get the buffer position displaing PAGE."
(- (* 4 page) 3))
(defun pdf-roll--pos-overlay (pos window)
"Return an overlay for WINDOW at POS."
(cl-find window (overlays-at pos) :key (lambda (ov) (overlay-get ov 'window))))
(defun pdf-roll-page-overlay (&optional page window)
"Return overlay displaying PAGE in WINDOW."
(pdf-roll--pos-overlay
(pdf-roll-page-to-pos (or page (pdf-roll-page-at-current-pos)))
(or window (selected-window))))
(defun pdf-roll-page-at-current-pos ()
"Page at point."
(if (cl-oddp (point))
(/ (+ (point) 3) 4)
(error "No page is displayed at current position (%s)" (point))))
(defun pdf-roll-set-vscroll (vscroll win)
"Set vscroll to VSCROLL in window WIN."
(image-mode-winprops win t)
(image-mode-window-put 'vscroll vscroll win)
(set-window-vscroll win vscroll t))
;;; Displaying/Undisplaying pages
(defun pdf-roll-maybe-slice-image (image &optional window inhibit-slice-p)
"Return a sliced IMAGE if `pdf-view-current-slice' in WINDOW is non-nil.
If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'."
(if-let ((slice (pdf-view-current-slice window))
((not inhibit-slice-p)))
(list (cons 'slice
(pdf-util-scale slice (image-size image t) 'round))
image)
image))
(defun pdf-roll-display-image (image page &optional window inhibit-slice-p)
"Display IMAGE for PAGE in WINDOW.
If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'."
(let* ((image (pdf-roll-maybe-slice-image image window inhibit-slice-p))
(size (image-display-size image t))
(overlay (pdf-roll-page-overlay page window))
(margin-pos (+ (pdf-roll-page-to-pos page) 2))
(margin-overlay (pdf-roll--pos-overlay margin-pos window))
(offset (when (> (window-width window t) (car size))
`(space :width (,(/ (- (window-width window t) (car size)) 2))))))
(overlay-put overlay 'display image)
(overlay-put overlay 'line-prefix offset)
(overlay-put margin-overlay 'display `(space :width (,(car size)) :height (,pdf-roll-vertical-margin)))
(overlay-put margin-overlay 'line-prefix offset)
(cdr size)))
(defun pdf-roll-display-page (page window &optional force)
"Display PAGE in WINDOW.
With FORCE non-nil display fetch page again even if it is already displayed."
(let ((display (overlay-get (pdf-roll-page-overlay page window) 'display)))
(if (or force (not display) (eq (car display) 'space))
(pdf-roll-display-image (pdf-view-create-page page window) page window)
(cdr (image-display-size display t)))))
(defun pdf-roll-display-pages (page &optional window force pscrolling)
"Display pages to fill the WINDOW starting from PAGE.
If FORCE is non-nill redisplay a page even if it is already displayed."
(let (displayed
(available-height (window-pixel-height window)))
(when (and pscrolling (> page 1))
(pdf-roll-display-page (1- page) window force)
(push (1- page) displayed))
(let ((vscroll (image-mode-window-get 'vscroll window))
(im-height (pdf-roll-display-page page window force)))
(pdf-roll-set-vscroll (min vscroll (1- im-height)) window)
(cl-callf - available-height (- im-height (window-vscroll window t))))
(push page displayed)
(while (and (> available-height 0) (< page (pdf-cache-number-of-pages)))
(cl-callf - available-height (pdf-roll-display-page (cl-incf page) window force))
(push page displayed))
(when (and pscrolling (< page (pdf-cache-number-of-pages)))
(pdf-roll-display-page (cl-incf page) window force)
(push page displayed))
;; store displayed images for determining which images to update when update
;; is triggered
(cl-callf cl-union (image-mode-window-get 'displayed-pages window) displayed)
displayed))
(defun pdf-roll-undisplay-pages (pages &optional window)
"Undisplay PAGES from WINDOW.
Replaces the display property of the overlay holding a page with a space."
(dolist (page pages)
(overlay-put (pdf-roll-page-overlay page window)
'display (get 'pdf-roll 'display))))
;;; State Management
(defun pdf-roll-new-window-function (&optional win)
"Setup image roll in a new window WIN.
If the buffer is newly created, then it does not contain any
overlay and this function erases the buffer contents, after which
it inserts empty spaces that each hold a overlay. If the buffer
already has overlays (i.e. a second or subsequent window is
created), the function simply copies the overlays and adds the
new window as window overlay-property to each overlay.
This function should be added to pdf-roll (continuous scroll)
minor mode commands, after erasing the buffer to create the
overlays."
(setq win (or (and (windowp win) win) (selected-window)))
(if (not (overlays-at 1))
(let ((pages (pdf-cache-number-of-pages))
(inhibit-read-only t))
(erase-buffer)
(setq pdf-roll--state (list t))
(dotimes (i (* 2 pages))
(insert " ")
(let ((o (make-overlay (1- (point)) (point))))
(overlay-put o 'category (if (eq 0 (mod i 2)) 'pdf-roll 'pdf-roll-margin))
(overlay-put o 'window win))
(insert "\n"))
(delete-char -1)
(set-buffer-modified-p nil))
(unless (pdf-roll-page-overlay 1 win)
(dotimes (i (/ (point-max) 2))
(overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i)))))
'window win))
(dolist (win-st pdf-roll--state)
(when-let ((win-old (car-safe win-st))
((not (window-live-p win-old))))
(remove-overlays (point-min) (point-max) 'window win-old)))
(cl-callf2 cl-delete-if-not #'window-live-p pdf-roll--state :key #'car-safe)))
;; initial `pdf-roll-redisplay' needs to know which page(s) to display
(cl-callf or (pdf-view-current-page win) 1)
(cl-callf or (image-mode-window-get 'vscroll win) 0))
(defun pdf-roll-redisplay (&optional window)
"Analogue of `pdf-view-redisplay' for WINDOW."
(setq window (if (windowp window) window (selected-window)))
(when (pdf-roll-page-overlay 1 window)
(setf (alist-get window pdf-roll--state) nil)
(force-window-update window)))
(defun pdf-roll-pre-redisplay (win)
"Handle modifications to the state in window WIN.
It should be added to `pre-redisplay-functions' buffer locally."
(with-demoted-errors "Error in image roll pre-redisplay: %S"
(unless (pdf-roll-page-overlay 1 win)
(pdf-roll-new-window-function win))
(let* ((state (alist-get win pdf-roll--state))
(pscrolling (memq last-command
'(pixel-scroll-precision pixel-scroll-start-momentum
pixel-scroll-interpolate-up pixel-scroll-interpolate-down)))
(page (progn (when pscrolling
(setf (pdf-view-current-page win)
(/ (min (+ (window-start win) 5) (point-max)) 4)))
(pdf-view-current-page win)))
(height (window-pixel-height win))
(vscroll (image-mode-window-get 'vscroll win))
(size-changed (not (and (eq height (nth 1 state))
(eq (window-pixel-width win) (nth 2 state)))))
(page-changed (not (eq page (nth 0 state))))
(vscroll-changed (not (eq vscroll (nth 3 state))))
(start (pdf-roll-page-to-pos page)))
(if (and pscrolling
(or (not (eq start (- (point-max) 3)))
(let ((visible-pixels (nth 4 (pos-visible-in-window-p start win t))))
(and visible-pixels (> visible-pixels (/ (window-text-height win t) 2))))
(prog1 nil (message "End of buffer"))))
(progn (image-mode-window-put 'vscroll (window-vscroll win t) win)
(image-mode-window-put 'hscroll (window-hscroll win)) win)
(set-window-vscroll win vscroll t)
(set-window-hscroll win (or (image-mode-window-get 'hscroll win) 0))
(set-window-start win start t))
(setq disable-point-adjustment t)
(when (or size-changed page-changed vscroll-changed)
(let ((old (image-mode-window-get 'displayed-pages win))
(new (pdf-roll-display-pages page win size-changed pscrolling)))
;; If images/pages are small enough (or after jumps), there
;; might be multiple image that need to get updated
(pdf-roll-undisplay-pages (cl-set-difference old new) win)
(image-mode-window-put 'displayed-pages new win)
(set-window-point win (+ start
(if (pos-visible-in-window-p (+ 2 start) win) 2 0))))
(setf (alist-get win pdf-roll--state)
`(,page ,height ,(window-pixel-width win) ,vscroll nil))
(when page-changed (run-hooks 'pdf-view-after-change-page-hook))))))
;;; Page navigation commands
(defun pdf-roll-goto-page-start ()
"Go to the start of the first displayed page."
(interactive)
(pdf-roll-set-vscroll 0 nil))
(defun pdf-roll-goto-page (page &optional window)
"Go to PAGE in WINDOW."
(interactive
(list (if current-prefix-arg
(prefix-numeric-value current-prefix-arg)
(read-number "Page: "))))
(unless (and (>= page 1)
(<= page (pdf-cache-number-of-pages)))
(error "No such page: %d" page))
(setf (pdf-view-current-page window) page)
(pdf-roll-set-vscroll 0 window))
(defun pdf-roll-next-page (&optional n)
"Go to next page or next Nth page."
(interactive "p")
(pdf-roll-goto-page (+ (pdf-roll-page-at-current-pos) n)))
(defun pdf-roll-previous-page (&optional n)
"Go to previous page or previous Nth page."
(interactive "p")
(pdf-roll-next-page (- n)))
;;; Scrolling Commands
(defun pdf-roll-scroll-forward (&optional n window pixels)
"Scroll image N lines forward in WINDOW.
Line height is determined by `frame-char-height'. When N is negative
scroll backward instead. With a prefix arg N is its numeric value.
If PIXELS is non-nil N is number of pixels instead of lines."
(interactive (list (prefix-numeric-value current-prefix-arg)))
(setq n (* (or n 1) (if pixels 1 (frame-char-height))))
(setq window (or window (selected-window)))
(when (> 0 n) (pdf-roll-scroll-backward (- n) window))
(let ((pos (goto-char (window-start window))))
(while (let* ((data (pos-visible-in-window-p (point) window t))
(occupied-pixels (cond ((nth 2 data) (nth 4 data))
(data (line-pixel-height))
(t (pdf-roll-display-page
(pdf-roll-page-at-current-pos) window)))))
(if (eq (point) (- (point-max) 3))
(prog1 nil
(setq n (min n (max 0 (- occupied-pixels (/ (window-text-height window t) 2)))))
(message "End of buffer"))
(when (>= n occupied-pixels)
(cl-decf n occupied-pixels))))
(forward-char 4))
(setf (pdf-view-current-page window) (pdf-roll-page-at-current-pos))
(pdf-roll-set-vscroll (+ (if (eq pos (point)) (window-vscroll window t) 0) n)
window)))
(defun pdf-roll-scroll-backward (&optional n window pixels)
"Scroll image N lines backwards in WINDOW.
Line height is determined by `frame-char-height'. When N is negative
scroll forward instead. With a prefix arg N is its numeric value.
If PIXELS is non-nil N is number of pixels instead of lines."
(interactive (list (prefix-numeric-value current-prefix-arg)))
(setq n (* (or n 1) (if pixels 1 (frame-char-height))))
(setq window (or window (selected-window)))
(when (> 0 n) (pdf-roll-scroll-backward (- n) window))
(goto-char (window-start window))
(let* ((data (pos-visible-in-window-p (point) window t))
(pixels-top (if (nth 2 data) (nth 2 data) 0)))
(if (< n pixels-top)
(pdf-roll-set-vscroll (- (window-vscroll window t) n)
window)
(cl-decf n pixels-top)
(while (and (if (bobp)
(prog1 nil (message "Beginning of buffer."))
t)
(progn (forward-char -4)
(pdf-roll-display-page
(pdf-roll-page-at-current-pos) window)
(cl-decf n (line-pixel-height)))
(> n 0)))
(pdf-roll-set-vscroll (- n) window)))
(setf (pdf-view-current-page window) (pdf-roll-page-at-current-pos)))
(defun pdf-roll-scroll-screen-forward (&optional arg)
"Scroll forward by (almost) ARG many full screens."
(interactive "p")
(pdf-roll-scroll-forward
(- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height)))
nil t))
(defun pdf-roll-scroll-screen-backward (&optional arg)
"Scroll backward by (almost) ARG many full screens."
(interactive "p")
(pdf-roll-scroll-backward
(- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height)))
nil t))
;;; Minor mode
(defun pdf-roll-initialize (&rest _args)
"Fun to initialize `pdf-view-roll-minor-mode'.
It is also added to `revert-buffer-function'."
(let ((inhibit-read-only t))
(erase-buffer)
(remove-overlays))
(image-mode-window-put 'displayed-pages nil)
(pdf-roll-new-window-function))
;;;###autoload
(define-minor-mode pdf-view-roll-minor-mode
"If enabled display document on a virtual scroll providing continuous scrolling."
:lighter " Continuous"
:keymap (let ((map (make-sparse-keymap)))
(define-key map [remap pdf-view-previous-line-or-previous-page] 'pdf-roll-scroll-backward)
(define-key map [remap pdf-view-next-line-or-next-page] 'pdf-roll-scroll-forward)
(define-key map [remap pdf-view-scroll-down-or-previous-page] 'pdf-roll-scroll-backward)
(define-key map [remap pdf-view-scroll-up-or-next-page] 'pdf-roll-scroll-forward)
(define-key map [remap mouse-set-point] 'ignore)
(define-key map (kbd "S-<next>") 'pdf-roll-scroll-screen-forward)
(define-key map (kbd "S-<prior>") 'pdf-roll-scroll-screen-backward)
map)
:version 28.1
(cond (pdf-view-roll-minor-mode
(setq-local face-remapping-alist '((default . pdf-roll-default))
mwheel-scroll-up-function #'pdf-roll-scroll-forward
mwheel-scroll-down-function #'pdf-roll-scroll-backward)
(remove-hook 'window-configuration-change-hook 'image-mode-reapply-winprops t)
(remove-hook 'window-configuration-change-hook 'pdf-view-redisplay-some-windows t)
(remove-hook 'image-mode-new-window-functions#'pdf-view-new-window-function t)
(add-hook 'pre-redisplay-functions 'pdf-roll-pre-redisplay nil t)
(add-hook 'pdf-roll-after-change-page-hook 'pdf-history-before-change-page-hook nil t)
(add-function :after (local 'revert-buffer-function) #'pdf-roll-initialize)
(make-local-variable 'pdf-roll--state)
(when (local-variable-p 'pixel-scroll-precision-mode)
(kill-local-variable 'pixel-scroll-precision-mode)
(kill-local-variable 'mwheel-coalesce-scroll-events))
(pdf-roll-initialize))
(t
(setq-local mwheel-scroll-up-function #'pdf-view-scroll-up-or-next-page
mwheel-scroll-down-function #'pdf-view-scroll-down-or-previous-page)
(add-hook 'window-configuration-change-hook 'image-mode-reapply-winprops nil t)
(add-hook 'window-configuration-change-hook 'pdf-view-redisplay-some-windows nil t)
(add-hook 'image-mode-new-window-functions #'pdf-view-new-window-function nil t)
(remove-function (local 'revert-buffer-function) #'pdf-roll-initialize)
(remove-hook 'pre-redisplay-functions 'pdf-roll-pre-redisplay t)
(remove-hook 'pdf-roll-after-change-page-hook 'pdf-history-before-change-page-hook t)
(kill-local-variable 'pdf-roll--state)
(when (bound-and-true-p pixel-scroll-precision-mode)
(setq-local pixel-scroll-precision-mode nil)
(setq-local mwheel-coalesce-scroll-events t))
(let ((inhibit-read-only t))
(remove-overlays)
(image-mode-window-put 'displayed-pages nil)
(pdf-view-new-window-function (list (selected-window)))
(set-buffer-modified-p nil)))))
(defun pdf-roll--get-display-property ()
"`:before-until' advice for `image-get-display-property'.
`image-get-display-property' looks at the `point-min'. This function instead
returns the display property for the current page if `pdf-view-roll-minor-mode'
is non-nil."
(when pdf-view-roll-minor-mode
(get-char-property (pdf-roll-page-to-pos (pdf-view-current-page))
'display
(if (eq (window-buffer) (current-buffer))
(selected-window)))))
(advice-add 'image-get-display-property :before-until #'pdf-roll--get-display-property)
(provide 'pdf-roll)
;;; pdf-roll.el ends here

View File

@@ -277,15 +277,18 @@ Has no effect if `pdf-sync-backward-use-heuristic' is nil."
(xy (posn-object-x-y posn)))
(unless image
(error "Outside of image area"))
(pdf-sync-backward-search (car xy) (cdr xy))))
(pdf-sync-backward-search
(car xy) (cdr xy)
(and (bound-and-true-p pdf-view-roll-minor-mode)
(/ (+ (posn-point posn) 3) 4)))))
(defun pdf-sync-backward-search (x y)
"Go to the source corresponding to image coordinates X, Y.
(defun pdf-sync-backward-search (x y &optional page)
"Go to the source corresponding to image coordinates X, Y on PAGE.
Try to find the exact position, if
`pdf-sync-backward-use-heuristic' is non-nil."
(cl-destructuring-bind (source finder)
(pdf-sync-backward-correlate x y)
(pdf-sync-backward-correlate x y page)
(pop-to-buffer (or (find-buffer-visiting source)
(find-file-noselect source))
pdf-sync-backward-display-action)
@@ -293,8 +296,8 @@ Try to find the exact position, if
(funcall finder)
(run-hooks 'pdf-sync-backward-hook)))
(defun pdf-sync-backward-correlate (x y)
"Find the source corresponding to image coordinates X, Y.
(defun pdf-sync-backward-correlate (x y &optional page)
"Find the source corresponding to image coordinates X, Y on PAGE.
Returns a list \(SOURCE FINDER\), where SOURCE is the name of the
TeX file and FINDER a function of zero arguments which, when
@@ -303,7 +306,7 @@ point to the correct position."
(pdf-util-assert-pdf-window)
(let ((size (pdf-view-image-size))
(page (pdf-view-current-page)))
(page (or page (pdf-view-current-page))))
(setq x (/ x (float (car size)))
y (/ y (float (cdr size))))
(let-alist (pdf-info-synctex-backward-search page x y)
@@ -656,7 +659,7 @@ Needs to have `pdf-sync-backward-debug-minor-mode' enabled."
buffer pdf-sync-forward-display-action)
(pdf-util-assert-pdf-window)
(when page
(pdf-view-goto-page page)
(pdf-view-goto-page page (selected-window))
(when y1
(let ((top (* y1 (cdr (pdf-view-image-size)))))
(pdf-util-tooltip-arrow (round top))))))
@@ -670,7 +673,7 @@ Returns a list \(PDF PAGE X1 Y1 X2 Y2\), where PAGE, X1, Y1, X2
and Y2 may be nil, if the destination could not be found."
(unless (fboundp 'TeX-master-file)
(error "This function works only with AUCTeX"))
(unless line (setq line (line-number-at-pos)))
(unless line (setq line (line-number-at-pos nil t)))
(unless column (setq column (current-column)))
(let* ((pdf (expand-file-name

View File

@@ -1,12 +1,12 @@
;; -*- no-byte-compile: t; lexical-binding: nil -*-
(define-package "pdf-tools" "20240429.407"
(define-package "pdf-tools" "20260102.1101"
"Support library for PDF documents."
'((emacs "26.3")
(tablist "1.0")
(let-alist "1.0.4"))
:url "http://github.com/vedang/pdf-tools/"
:commit "30b50544e55b8dbf683c2d932d5c33ac73323a16"
:revdesc "30b50544e55b"
:url "https://github.com/vedang/pdf-tools/"
:commit "e4b7f1f37cf59ddf025d609ffcdabe732a6e99ba"
:revdesc "e4b7f1f37cf5"
:keywords '("files" "multimedia")
:authors '(("Andreas Politz" . "mail@andreas-politz.de"))
:maintainers '(("Vedang Manerikar" . "vedang.manerikar@gmail.com")))

View File

@@ -7,8 +7,8 @@
;; URL: http://github.com/vedang/pdf-tools/
;; Keywords: files, multimedia
;; Package: pdf-tools
;; Package-Version: 20240429.407
;; Package-Revision: 30b50544e55b
;; Package-Version: 20260102.1101
;; Package-Revision: e4b7f1f37cf5
;; Package-Requires: ((emacs "26.3") (tablist "1.0") (let-alist "1.0.4"))
;; This program is free software; you can redistribute it and/or modify
@@ -273,9 +273,9 @@ Returns always nil, unless `system-type' equals windows-nt."
"Return the location of /mingw*/bin."
(when (pdf-tools-msys2-directory)
(let ((arch (intern (car (split-string system-configuration "-" t)))))
(expand-file-name
(format "./mingw%s/bin" (if (eq arch 'x86_64) "64" "32"))
(pdf-tools-msys2-directory)))))
(expand-file-name
(format "./mingw%s/bin" (if (eq arch 'x86_64) "64" "32"))
(pdf-tools-msys2-directory)))))
(defun pdf-tools-find-bourne-shell ()
"Locate a usable sh."
@@ -324,7 +324,7 @@ Returns the buffer of the compilation process."
(process-environment process-environment)
(default-directory build-directory)
(autobuild (shell-quote-argument
(expand-file-name "autobuild" build-directory)))
(expand-file-name (if (eq system-type 'android) "autobuild.android" "autobuild") build-directory)))
(msys2-p (equal "bash.exe" (file-name-nondirectory shell-file-name))))
(unless shell-file-name
(error "No suitable shell found"))
@@ -405,20 +405,20 @@ See `pdf-view-mode' and `pdf-tools-enabled-modes'."
pdf-tools-directory)))
(if (or no-query-p
(y-or-n-p "Need to (re)build the epdfinfo program, do it now ?"))
(pdf-tools-build-server
target-directory
skip-dependencies-p
force-dependencies-p
(lambda (executable)
(let ((msg (format
"Building the PDF Tools server %s"
(if executable "succeeded" "failed"))))
(if (not executable)
(funcall (if no-error-p #'message #'error) "%s" msg)
(message "%s" msg)
(setq pdf-info-epdfinfo-program executable)
(let ((pdf-info-restart-process-p t))
(pdf-tools-install-noverify))))))
(pdf-tools-build-server
target-directory
skip-dependencies-p
force-dependencies-p
(lambda (executable)
(let ((msg (format
"Building the PDF Tools server %s"
(if executable "succeeded" "failed"))))
(if (not executable)
(funcall (if no-error-p #'message #'error) "%s" msg)
(message "%s" msg)
(setq pdf-info-epdfinfo-program executable)
(let ((pdf-info-restart-process-p t))
(pdf-tools-install-noverify))))))
(message "PDF Tools not activated")))))
(defun pdf-tools-install-noverify ()
@@ -449,9 +449,9 @@ See `pdf-view-mode' and `pdf-tools-enabled-modes'."
(interactive)
(pdf-info-quit)
(setq-default auto-mode-alist
(remove pdf-tools-auto-mode-alist-entry auto-mode-alist))
(remove pdf-tools-auto-mode-alist-entry auto-mode-alist))
(setq-default magic-mode-alist
(remove pdf-tools-magic-mode-alist-entry magic-mode-alist))
(remove pdf-tools-magic-mode-alist-entry magic-mode-alist))
(pdf-occur-global-minor-mode -1)
(pdf-virtual-global-minor-mode -1)
(remove-hook 'pdf-view-mode-hook #'pdf-tools-enable-minor-modes)

View File

@@ -28,6 +28,7 @@
(require 'pdf-macs)
(require 'cl-lib)
(require 'format-spec)
(require 'image-mode)
(require 'faces)
;; These functions are only used after a PdfView window was asserted,
@@ -36,7 +37,11 @@
(declare-function pdf-view-image-offset "pdf-view")
(declare-function pdf-cache-pagesize "pdf-cache")
(declare-function pdf-view-image-type "pdf-view")
(declare-function image-mode-window-get "image-mode")
(declare-function image-set-window-vscroll "image-mode")
(declare-function image-set-window-hscroll "image-mode")
(defvar pdf-view-roll-minor-mode)
;; * ================================================================== *
@@ -158,7 +163,7 @@ See also `pdf-util-scale'."
The result depends on the currently displayed page in WINDOW.
See also `pdf-util-scale'."
(pdf-util-assert-pdf-window window)
(when displayed-p (pdf-util-assert-pdf-window window))
(pdf-util-scale-to
list-of-pixel-edges
(pdf-view-image-size displayed-p window)
@@ -311,15 +316,19 @@ depending on the input."
"Return the visible region of the image in WINDOW.
Returns a list of pixel edges."
(pdf-util-assert-pdf-window)
(when displayed-p (pdf-util-assert-pdf-window window))
(let* ((edges (window-inside-pixel-edges window))
(isize (pdf-view-image-size displayed-p window))
(offset (if displayed-p
`(0 . 0)
(pdf-view-image-offset window)))
(hscroll (* (window-hscroll window)
(hscroll (* (if displayed-p
(window-hscroll window)
(or (image-mode-window-get 'hscroll window) 0))
(frame-char-width (window-frame window))))
(vscroll (window-vscroll window t))
(vscroll (if displayed-p
(window-vscroll window t)
(or (image-mode-window-get 'vscroll window) 0)))
(x0 (+ hscroll (car offset)))
(y0 (+ vscroll (cdr offset)))
(x1 (min (car isize)
@@ -383,40 +392,46 @@ needed.
Note: For versions of emacs before 27 this will return lines instead of
pixels. This is because of a change that occurred to `image-mode' in 27."
(pdf-util-assert-pdf-window)
(let* ((win (window-inside-pixel-edges))
(image-height (cdr (pdf-view-image-size t)))
(image-top (window-vscroll nil t))
(edges (pdf-util-translate
edges
(pdf-view-image-offset) t)))
(pdf-util-with-edges (win edges)
(let* ((context-pixel (or context-pixel
(* next-screen-context-lines
(frame-char-height))))
;;Be careful not to modify edges.
(edges-top (- edges-top context-pixel))
(edges-bot (+ edges-bot context-pixel))
(vscroll
(cond ((< edges-top image-top)
(max 0 (if eager-p
(- edges-bot win-height)
edges-top)))
((> (min image-height
edges-bot)
(+ image-top win-height))
(min (- image-height win-height)
(if eager-p
edges-top
(- edges-bot win-height)))))))
(if pdf-view-roll-minor-mode
(max 0 (- (nth 1 edges)
(or context-pixel
(* next-screen-context-lines (frame-char-height)))))
(let* ((win (window-inside-pixel-edges))
(image-height (cdr (pdf-view-image-size
(unless pdf-view-roll-minor-mode
t))))
(image-top (window-vscroll nil t))
(edges (pdf-util-translate
edges
(pdf-view-image-offset) t)))
(pdf-util-with-edges (win edges)
(let* ((context-pixel (or context-pixel
(* next-screen-context-lines
(frame-char-height))))
;;Be careful not to modify edges.
(edges-top (- edges-top context-pixel))
(edges-bot (+ edges-bot context-pixel))
(vscroll
(cond ((< edges-top image-top)
(max 0 (if eager-p
(- edges-bot win-height)
edges-top)))
((> (min image-height
edges-bot)
(+ image-top win-height))
(min (- image-height win-height)
(if eager-p
edges-top
(- edges-bot win-height)))))))
(when vscroll
(round
;; `image-set-window-vscroll' changed in version 27 to using
;; pixels, not lines.
(if (version< emacs-version "27")
(/ vscroll (float (frame-char-height)))
vscroll)))))))
(when vscroll
(round
;; `image-set-window-vscroll' changed in version 27 to using
;; pixels, not lines.
(if (version< emacs-version "27")
(/ vscroll (float (frame-char-height)))
vscroll))))))))
(defun pdf-util-scroll-to-edges (edges &optional eager-p)
"Scroll window such that image EDGES are visible.
@@ -636,10 +651,9 @@ string."
(cdr (pdf-view-image-offset))
(window-vscroll nil t)
(frame-char-height))))
(when (overlay-get (pdf-view-current-overlay) 'before-string)
(let* ((e (window-inside-pixel-edges))
(xw (pdf-util-with-edges (e) e-width)))
(cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2))))
(let* ((e (window-inside-pixel-edges))
(xw (pdf-util-with-edges (e) e-width)))
(cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2)))
(pdf-util-tooltip-in-window
(propertize
" " 'display (propertize
@@ -784,8 +798,8 @@ respective sequence."
(cl-macrolet ((make-matrix (rows columns)
`(apply #'vector
(cl-loop for i from 1 to ,rows
collect (make-vector ,columns nil))))
(cl-loop for i from 1 to ,rows
collect (make-vector ,columns nil))))
(mset (matrix row column newelt)
`(aset (aref ,matrix ,row) ,column ,newelt))
(mref (matrix row column)
@@ -800,21 +814,21 @@ respective sequence."
(if (equal a b) 1 -1)))))
(cl-loop for i from 0 to len1 do
(mset d i 0 (- i)))
(mset d i 0 (- i)))
(cl-loop for j from 0 to len2 do
(mset d 0 j (if suffix-p 0 (- j))))
(mset d 0 j (if suffix-p 0 (- j))))
(cl-loop for i from 1 to len1 do
(cl-loop for j from 1 to len2 do
(let ((max (max
(1- (mref d (1- i) j))
(+ (mref d i (1- j))
(if (and prefix-p (= i len1)) 0 -1))
(+ (mref d (1- i) (1- j))
(funcall similarity-fn
(elt seq1 (1- i))
(elt seq2 (1- j)))))))
(mset d i j max))))
(cl-loop for j from 1 to len2 do
(let ((max (max
(1- (mref d (1- i) j))
(+ (mref d i (1- j))
(if (and prefix-p (= i len1)) 0 -1))
(+ (mref d (1- i) (1- j))
(funcall similarity-fn
(elt seq1 (1- i))
(elt seq2 (1- j)))))))
(mset d i j max))))
(let ((i len1)
(j len2)
@@ -1039,8 +1053,8 @@ Returns the convert process."
(set-process-sentinel proc callback))
proc)))
(defun pdf-util-convert-page (&rest specs)
"Convert image of current page according to SPECS.
(defun pdf-util-convert-image (image &rest specs)
"Convert IMAGE page according to SPECS.
Return the converted PNG image as a string. See also
`pdf-util-convert'."
@@ -1050,7 +1064,7 @@ Return the converted PNG image as a string. See also
(out-file (make-temp-file "pdf-util-convert" nil ".png")))
(unwind-protect
(let ((image-data
(plist-get (cdr (pdf-view-current-image)) :data)))
(plist-get (cdr image) :data)))
(with-temp-file in-file
(set-buffer-multibyte nil)
(set-buffer-file-coding-system 'binary)
@@ -1063,6 +1077,14 @@ Return the converted PNG image as a string. See also
(when (file-exists-p out-file)
(delete-file out-file)))))
(defun pdf-util-convert-page (&rest specs)
"Convert image of current page according to SPECS.
Return the converted PNG image as a string. See also
`pdf-util-convert'."
(pdf-util-assert-pdf-window)
(apply #'pdf-util-convert-image (pdf-view-current-image) specs))
(defun pdf-util-convert--create-commands (spec)
(let ((fg "red")

View File

@@ -32,9 +32,22 @@
(require 'jka-compr)
(require 'bookmark)
(require 'password-cache)
(require 'cl-macs)
(declare-function cua-copy-region "cua-base")
(declare-function pdf-tools-pdf-buffer-p "pdf-tools")
(declare-function pdf-roll-scroll-forward "pdf-roll")
(declare-function pdf-roll-scroll-backward "pdf-roll")
(declare-function pdf-roll-next-page "pdf-roll")
(declare-function pdf-roll-redisplay "pdf-roll")
(declare-function pdf-roll-pre-redisplay "pdf-roll")
(declare-function pdf-roll-page-overlay "pdf-roll")
(declare-function pdf-roll-page-at-current-pos "pdf-roll")
(declare-function pdf-roll-display-image "pdf-roll")
(defvar pdf-view-roll-minor-mode nil)
;; * ================================================================== *
;; * Customizations
@@ -132,6 +145,29 @@ Nevertheless, this seems to work well in most cases."
:group 'pdf-view
:type 'boolean)
(defcustom pdf-view-midnight-gamma 1.0
"In midnight mode, nonlinearly scale lightness.
Values less than 1 increase lightness, values more than 1 decrease
lightness (unless `pdf-view-midnight-gamma-before-invert' is non-nil, in
which reverses the effect direction)."
:group 'pdf-view
:type 'float)
(defcustom pdf-view-midnight-gamma-before-invert nil
"In midnight mode, whether to scale lightness before inverting.
If non-nil, this inverts the direction of the effect of
`pdf-view-midnight-gamma', i.e. values more than 1 increase lightness
instead of decreasing it. This option is provided because it results in
different behaviors near the ends of the lightness scale. For example,
if this option is nil (the default), then a gamma values less than 1
significantly lighten colors very close to black. One gets a less
extreme effect by setting this option to non-nil and using gamma values
greater than 1."
:group 'pdf-view
:type 'boolean)
(defcustom pdf-view-change-page-hook nil
"Hook run after changing to another page, but before displaying it.
@@ -212,13 +248,23 @@ Must be one of `glyph', `word', or `line'."
(const word)
(const line)))
(defcustom pdf-view-mode-line-position-prefix "P"
"Prefix for page number shown in the mode line."
:group 'pdf-view
:type 'string)
(defcustom pdf-view-mode-line-position-use-labels nil
"Whether current page should come from page labels."
:group 'pdf-view
:type 'boolean)
;; * ================================================================== *
;; * Internal variables and macros
;; * ================================================================== *
(defvar-local pdf-view-active-region nil
"The active region as a list of edges.
"The active region as a cons cell of page and list of edges.
Edge values are relative coordinates.")
@@ -271,7 +317,7 @@ regarding display of the region in the later function.")
(defvar pdf-view-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map image-mode-map)
(define-key map (kbd "Q") 'kill-this-buffer)
(define-key map (kbd "Q") 'kill-current-buffer)
;; Navigation in the document
(define-key map (kbd "n") 'pdf-view-next-page-command)
(define-key map (kbd "p") 'pdf-view-previous-page-command)
@@ -280,6 +326,8 @@ regarding display of the region in the later function.")
(define-key map [remap forward-page] 'pdf-view-next-page-command)
(define-key map [remap backward-page] 'pdf-view-previous-page-command)
(define-key map (kbd "SPC") 'pdf-view-scroll-up-or-next-page)
(define-key map [remap scroll-up-command] #'pdf-view-scroll-up-or-next-page)
(define-key map [remap scroll-down-command] #'pdf-view-scroll-down-or-previous-page)
(define-key map (kbd "S-SPC") 'pdf-view-scroll-down-or-previous-page)
(define-key map (kbd "DEL") 'pdf-view-scroll-down-or-previous-page)
(define-key map (kbd "C-n") 'pdf-view-next-line-or-next-page)
@@ -308,6 +356,7 @@ regarding display of the region in the later function.")
(define-key map (kbd "s m") 'pdf-view-set-slice-using-mouse)
(define-key map (kbd "s b") 'pdf-view-set-slice-from-bounding-box)
(define-key map (kbd "s r") 'pdf-view-reset-slice)
(define-key map (kbd "s c") 'pdf-view-set-slice-common-bounding-box)
;; Rotation.
(define-key map (kbd "R") #'pdf-view-rotate)
;; Reconvert
@@ -364,6 +413,14 @@ PNG images in Emacs buffers."
(setq-local mwheel-scroll-down-function
#'pdf-view-scroll-down-or-previous-page))
(if (boundp 'mwheel-scroll-left-function)
(setq-local mwheel-scroll-left-function
#'image-scroll-left))
(if (boundp 'mwheel-scroll-right-function)
(setq-local mwheel-scroll-right-function
#'image-scroll-right))
;; Disable pixel-scroll-precision-mode locally if enabled
(if (bound-and-true-p pixel-scroll-precision-mode)
(set (make-local-variable 'pixel-scroll-precision-mode) nil))
@@ -388,7 +445,13 @@ PNG images in Emacs buffers."
;; Setup other local variables.
(setq-local mode-line-position
'(" P" (:eval (number-to-string (pdf-view-current-page)))
'(" " pdf-view-mode-line-position-prefix
;; Show page label when enabled and available,
;; otherwise show numeric page. Guard against errors.
(:eval
(or (and pdf-view-mode-line-position-use-labels
(ignore-errors (pdf-view-current-pagelabel)))
(number-to-string (pdf-view-current-page))))
;; Avoid errors during redisplay.
"/" (:eval (or (ignore-errors
(number-to-string (pdf-cache-number-of-pages)))
@@ -500,7 +563,8 @@ PNG images in Emacs buffers."
This may be different from variable `buffer-file-name' when
operating on a local copy of a remote file."
(or pdf-view--buffer-file-name
(buffer-file-name)))
(buffer-file-name)
(buffer-file-name (buffer-base-buffer))))
(defun pdf-view--write-contents-function ()
"Function for `write-contents-functions' to save the buffer."
@@ -683,7 +747,10 @@ windows."
(setf (pdf-view-current-page window) page)
(run-hooks 'pdf-view-change-page-hook))
(when (window-live-p window)
(pdf-view-redisplay window))
(image-set-window-vscroll 0)
(if pdf-view-roll-minor-mode
(pdf-roll-pre-redisplay window)
(pdf-view-redisplay window)))
(when changing-p
(pdf-view-deactivate-region)
(force-mode-line-update)
@@ -809,7 +876,7 @@ to previous page only on typing DEL (ARG is nil)."
(image-set-window-hscroll hscroll)))
(image-scroll-down arg)))
(defun pdf-view-next-line-or-next-page (&optional arg)
(defun pdf-view--next-line-or-next-page (&optional arg)
"Scroll upward by ARG lines if possible, else go to the next page.
When `pdf-view-continuous' is non-nil, scrolling a line upward
@@ -820,14 +887,20 @@ at the bottom edge of the page moves to the next page."
(cur-page (pdf-view-current-page)))
(when (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll)
(image-next-line arg))
(pdf-view-next-page)
(ignore-errors (pdf-view-next-page))
(when (/= cur-page (pdf-view-current-page))
(image-bob)
(image-bol 1))
(image-set-window-hscroll hscroll)))
(image-next-line arg)))
(defun pdf-view-previous-line-or-previous-page (&optional arg)
(defun pdf-view-next-line-or-next-page (&optional arg)
(interactive "p")
(if pdf-view-roll-minor-mode
(dotimes (_ (or arg 1)) (pdf-roll-scroll-forward))
(pdf-view--next-line-or-next-page arg)))
(defun pdf-view--previous-line-or-previous-page (&optional arg)
"Scroll downward by ARG lines if possible, else go to the previous page.
When `pdf-view-continuous' is non-nil, scrolling a line downward
@@ -838,13 +911,19 @@ at the top edge of the page moves to the previous page."
(cur-page (pdf-view-current-page)))
(when (= (window-vscroll nil pdf-view-have-image-mode-pixel-vscroll)
(image-previous-line arg))
(pdf-view-previous-page)
(ignore-errors (pdf-view-previous-page))
(when (/= cur-page (pdf-view-current-page))
(image-eob)
(image-bol 1))
(image-set-window-hscroll hscroll)))
(image-previous-line arg)))
(defun pdf-view-previous-line-or-previous-page (&optional arg)
(interactive "p")
(if pdf-view-roll-minor-mode
(dotimes (_ (or arg 1)) (pdf-roll-scroll-backward))
(pdf-view--previous-line-or-previous-page arg)))
(defun pdf-view-goto-label (label)
"Go to the page corresponding to LABEL.
@@ -928,6 +1007,16 @@ dragging it to its bottom-right corner. See also
(cons (/ 1.0 (float (car size)))
(/ 1.0 (float (cdr size))))))))
(defun pdf-view--bounding-box-to-slice (bb)
"Convert bounding box BB to slice format for `pdf-view-set-slice'.
BB is a list (LEFT TOP RIGHT BOTTOM).
Returns a list (X Y WIDTH HEIGHT)."
(let* ((margin (max 0 (or pdf-view-bounding-box-margin 0)))
(halfmargin (/ margin 2)))
(cl-destructuring-bind (left top right bottom) bb
(list (- left halfmargin) (- top halfmargin)
(+ (- right left) margin) (+ (- bottom top) margin)))))
(defun pdf-view-set-slice-from-bounding-box (&optional window)
"Set the slice from the page's bounding-box.
@@ -940,18 +1029,38 @@ much more accurate than could be done manually using
See also `pdf-view-bounding-box-margin'."
(interactive)
(let* ((bb (pdf-cache-boundingbox (pdf-view-current-page window)))
(margin (max 0 (or pdf-view-bounding-box-margin 0)))
(slice (list (- (nth 0 bb)
(/ margin 2.0))
(- (nth 1 bb)
(/ margin 2.0))
(+ (- (nth 2 bb) (nth 0 bb))
margin)
(+ (- (nth 3 bb) (nth 1 bb))
margin))))
(slice (pdf-view--bounding-box-to-slice bb)))
(apply 'pdf-view-set-slice
(append slice (and window (list window))))))
(defun pdf-document-common-bounding-box (&optional file-or-buffer)
"Return the common bounding box for all pages in FILE-OR-BUFFER.
Returns a list (LEFT TOP RIGHT BOTTOM)."
(let ((left 1.0)
(top 1.0)
(right 0.0)
(bottom 0.0))
(dotimes (i (pdf-info-number-of-pages file-or-buffer))
(cl-destructuring-bind (b-left b-top b-right b-bottom) (pdf-info-boundingbox (1+ i) file-or-buffer)
(setq left (min left b-left)
top (min top b-top)
right (max right b-right)
bottom (max bottom b-bottom))))
(list left top right bottom)))
(defun pdf-view-set-slice-common-bounding-box (&optional window)
"Set the slice from the common bounding box of all pages.
WINDOW specifies which document to use; defaults to `selected-window'.
A margin is added from `pdf-view-bounding-box-margin'."
(interactive)
(let* ((bb (pdf-document-common-bounding-box))
(slice (pdf-view--bounding-box-to-slice bb)))
(when window
(push slice window))
(message (prin1-to-string slice))
(apply 'pdf-view-set-slice slice)))
(defun pdf-view-reset-slice (&optional window)
"Reset the current slice and redisplay WINDOW.
@@ -1038,17 +1147,23 @@ See also `pdf-view-use-imagemagick'."
:map hotspots
:pointer 'arrow)))
(defun pdf-view-image-size (&optional displayed-p window)
;; TODO: add WINDOW to docstring.
"Return the size in pixel of the current image.
(defun pdf-view-image-size (&optional displayed-p window page)
"Return the size in pixel of the current image in WINDOW.
If DISPLAYED-P is non-nil, return the size of the displayed
image. These values may be different, if slicing is used."
(if displayed-p
(with-selected-window (or window (selected-window))
(image-display-size
(image-get-display-property) t))
(image-size (pdf-view-current-image window) t)))
image. These values may be different, if slicing is used.
If PAGE is non-nil return its size instead of current page."
(let ((display-prop (if pdf-view-roll-minor-mode
(progn (setq window (if (windowp window) window (selected-window)))
(setq page (or page (pdf-view-current-page window)))
(unless (memq page (image-mode-window-get 'displayed-pages window))
(pdf-view-display-page page window))
(overlay-get (pdf-roll-page-overlay page window) 'display))
(image-get-display-property))))
(if displayed-p
(image-display-size display-prop t)
(image-size display-prop t))))
(defun pdf-view-image-offset (&optional window)
;; TODO: add WINDOW to docstring.
@@ -1068,47 +1183,49 @@ It is equal to \(LEFT . TOP\) of the current slice in pixel."
"Display page PAGE in WINDOW."
(setf (pdf-view-window-needs-redisplay window) nil)
(pdf-view-display-image
(pdf-view-create-page page window)
window))
(pdf-view-create-page page window) page window))
(defun pdf-view-display-image (image &optional window inhibit-slice-p)
(defun pdf-view-display-image (image page &optional window inhibit-slice-p)
;; TODO: write documentation!
(let ((ol (pdf-view-current-overlay window)))
(when (window-live-p (overlay-get ol 'window))
(let* ((size (image-size image t))
(slice (if (not inhibit-slice-p)
(pdf-view-current-slice window)))
(displayed-width (floor
(if slice
(* (nth 2 slice)
(car (image-size image)))
(car (image-size image))))))
(setf (pdf-view-current-image window) image)
(move-overlay ol (point-min) (point-max))
;; In case the window is wider than the image, center the image
;; horizontally.
(overlay-put ol 'before-string
(when (> (window-width window)
displayed-width)
(propertize " " 'display
`(space :align-to
,(/ (- (window-width window)
displayed-width) 2)))))
(overlay-put ol 'display
(if slice
(list (cons 'slice
(pdf-util-scale slice size 'round))
image)
image))
(let* ((win (overlay-get ol 'window))
(hscroll (image-mode-window-get 'hscroll win))
(vscroll (image-mode-window-get 'vscroll win)))
;; Reset scroll settings, in case they were changed.
(if hscroll (set-window-hscroll win hscroll))
(if vscroll (set-window-vscroll
win vscroll pdf-view-have-image-mode-pixel-vscroll)))))))
(if pdf-view-roll-minor-mode
(pdf-roll-display-image
image page (or window (selected-window)) inhibit-slice-p)
(let ((ol (pdf-view-current-overlay window)))
(when (window-live-p (overlay-get ol 'window))
(let* ((size (image-size image t))
(slice (if (not inhibit-slice-p)
(pdf-view-current-slice window)))
(displayed-width (floor
(if slice
(* (nth 2 slice)
(car (image-size image)))
(car (image-size image))))))
(setf (pdf-view-current-image window) image)
(move-overlay ol (point-min) (point-max))
;; In case the window is wider than the image, center the image
;; horizontally.
(overlay-put ol 'before-string
(when (> (window-width window)
displayed-width)
(propertize " " 'display
`(space :align-to
,(/ (- (window-width window)
displayed-width) 2)))))
(overlay-put ol 'display
(if slice
(list (cons 'slice
(pdf-util-scale slice size 'round))
image)
image))
(let* ((win (overlay-get ol 'window))
(hscroll (image-mode-window-get 'hscroll win))
(vscroll (image-mode-window-get 'vscroll win)))
;; Reset scroll settings, in case they were changed.
(if hscroll (set-window-hscroll win hscroll))
(if vscroll (set-window-vscroll
win vscroll pdf-view-have-image-mode-pixel-vscroll))))))))
(defun pdf-view-redisplay (&optional window)
(defun pdf-view--redisplay (&optional window)
"Redisplay page in WINDOW.
If WINDOW is t, redisplay pages in all windows."
@@ -1129,12 +1246,18 @@ If WINDOW is t, redisplay pages in all windows."
(setf (pdf-view-window-needs-redisplay window) t)))))
(force-mode-line-update)))
(defun pdf-view-redisplay (&optional window)
(if pdf-view-roll-minor-mode
(pdf-roll-redisplay window)
(pdf-view--redisplay window)))
(defun pdf-view-redisplay-pages (&rest pages)
"Redisplay PAGES in all windows."
(pdf-util-assert-pdf-buffer)
(dolist (window (get-buffer-window-list nil nil t))
(when (memq (pdf-view-current-page window)
pages)
(when (cl-some (lambda (page) (memq page pages))
(or (image-mode-window-get 'displayed-pages window)
(list (pdf-view-current-page window))))
(pdf-view-redisplay window))))
(defun pdf-view-maybe-redisplay-resized-windows ()
@@ -1180,7 +1303,7 @@ If WINDOW is t, redisplay pages in all windows."
;; `window' property is only effective if its value is a window).
(cl-assert (eq t (car winprops)))
(delete-overlay ol))
(image-mode-window-put 'overlay ol winprops)
(image-mode-window-put 'overlay ol)
;; Clean up some overlays.
(dolist (ov (overlays-in (point-min) (point-max)))
(when (and (windowp (overlay-get ov 'window))
@@ -1283,6 +1406,8 @@ The colors are determined by the variable
(pdf-info-setoptions
:render/foreground (or (car pdf-view-midnight-colors) "black")
:render/background (or (cdr pdf-view-midnight-colors) "white")
:render/gamma pdf-view-midnight-gamma
:render/gammabeforeinvert pdf-view-midnight-gamma-before-invert
:render/usecolors
(if pdf-view-midnight-invert
;; If midnight invert is enabled, pass "2" indicating
@@ -1323,7 +1448,7 @@ current theme's colors."
(pdf-util-assert-pdf-buffer)
(pdf-cache-clear-images)
(when get-theme
(pdf-view-set-theme-background))
(pdf-view-set-theme-background))
(pdf-view-redisplay t))
(define-minor-mode pdf-view-themed-minor-mode
@@ -1404,7 +1529,7 @@ supersede hotspots in lower ones."
(setq deactivate-mark nil))
(defun pdf-view-active-region (&optional deactivate-p)
"Return the active region, a list of edges.
"Return the active region, as a cons cell of page number and list of edges.
Deactivate the region if DEACTIVATE-P is non-nil."
(pdf-view-assert-active-region)
@@ -1454,9 +1579,14 @@ Stores the region in `pdf-view-active-region'."
(setq begin-inside-image-p nil)
(posn-x-y pos)))
(abs-begin (posn-x-y pos))
(page (if pdf-view-roll-minor-mode
(/ (+ 3 (posn-point pos)) 4)
(pdf-view-current-page)))
(margin (frame-char-height))
(selection-style (or selection-style pdf-view-selection-style))
pdf-view-continuous
region)
(setq pdf-view-active-region (list page))
(when (pdf-util-track-mouse-dragging (event 0.05)
(let* ((pos (event-start event))
(end (posn-object-x-y pos))
@@ -1496,23 +1626,33 @@ Stores the region in `pdf-view-active-region'."
(+ (car begin) (car dxy))))
(max 0 (min (cdr size)
(+ (cdr begin) (cdr dxy)))))))))
(let ((iregion (if rectangle-p
(list (min (car begin) (car end))
(min (cdr begin) (cdr end))
(max (car begin) (car end))
(max (cdr begin) (cdr end)))
(list (car begin) (cdr begin)
(car end) (cdr end)))))
(let* ((iregion (if rectangle-p
(list (min (car begin) (car end))
(min (cdr begin) (cdr end))
(max (car begin) (car end))
(max (cdr begin) (cdr end)))
(list (car begin) (cdr begin)
(car end) (cdr end))))
(y (cdr (posn-x-y pos)))
(dy (- y (cdr abs-begin))))
(setq region
(pdf-util-scale-pixel-to-relative iregion))
(pdf-view-display-region
(cons region pdf-view-active-region)
(cons page (cons region (cdr pdf-view-active-region)))
rectangle-p
selection-style)
(pdf-util-scroll-to-edges iregion)))))
(setq pdf-view-active-region
(append pdf-view-active-region
(list region)))
(if pdf-view-roll-minor-mode
(cond
((and (> dy 0) (< (- (window-text-height window t) y) margin))
(pdf-roll-scroll-forward
(min margin
(or (nth 3 (pos-visible-in-window-p (posn-point pos) window t)) 0))))
((and (< dy 0) (< (- y (window-header-line-height window)) margin))
(pdf-roll-scroll-backward
(min margin
(or (nth 2 (pos-visible-in-window-p (posn-point pos) window t)) 0)))))
(pdf-util-scroll-to-edges iregion))))))
(cl-callf append (cdr pdf-view-active-region) (list region))
(pdf-view--push-mark))))
(defun pdf-view-mouse-extend-region (event)
@@ -1539,18 +1679,19 @@ This is more useful for commands like
(let ((colors (pdf-util-face-colors
(if rectangle-p 'pdf-view-rectangle 'pdf-view-region)
(bound-and-true-p pdf-view-dark-minor-mode)))
(page (pdf-view-current-page))
(page (car region))
(width (car (pdf-view-image-size))))
(pdf-view-display-image
(pdf-view-create-image
(if rectangle-p
(pdf-info-renderpage-highlight
page width nil
`(,(car colors) ,(cdr colors) 0.35 ,@region))
`(,(car colors) ,(cdr colors) 0.35 ,@(cdr region)))
(pdf-info-renderpage-text-regions
page width nil selection-style nil
`(,(car colors) ,(cdr colors) ,@region)))
:width width))))
`(,(car colors) ,(cdr colors) ,@(cdr region))))
:width width)
(when pdf-view-roll-minor-mode page))))
(defun pdf-view-kill-ring-save ()
"Copy the region to the `kill-ring'."
@@ -1565,7 +1706,7 @@ This is more useful for commands like
(interactive)
(pdf-view-deactivate-region)
(setq pdf-view-active-region
(list (list 0 0 1 1)))
(cons (pdf-view-current-page) (list (list 0 0 1 1))))
(pdf-view--push-mark)
(pdf-view-display-region))
@@ -1575,10 +1716,10 @@ This is more useful for commands like
(mapcar
(lambda (edges)
(pdf-info-gettext
(pdf-view-current-page)
(car pdf-view-active-region)
edges
pdf-view-selection-style))
pdf-view-active-region))
(cdr pdf-view-active-region)))
(defun pdf-view-extract-region-image (regions &optional page size
output-buffer no-display-p)
@@ -1600,11 +1741,11 @@ the `convert' program is used."
(interactive
(list (if (pdf-view-active-region-p)
(pdf-view-active-region t)
'((0 0 1 1)))))
'(,(pdf-view-current-page) (0 0 1 1)))))
(unless page
(setq page (pdf-view-current-page)))
(setq page (car regions)))
(unless size
(setq size (pdf-view-image-size)))
(setq size (pdf-view-image-size nil nil page)))
(unless output-buffer
(setq output-buffer (get-buffer-create "*PDF image*")))
(let* ((images (mapcar (lambda (edges)
@@ -1616,7 +1757,7 @@ the `convert' program is used."
:crop-to edges)
nil file nil 'no-message)
file))
regions))
(cdr regions)))
result)
(unwind-protect
(progn
@@ -1667,57 +1808,71 @@ the selection styles."
;; * Bookmark + Register Integration
;; * ================================================================== *
(defvar pdf-view--bookmark-to-restore nil
"Used to hold a bookmark that is still to be restored.")
(defun pdf-view-bookmark-make-record (&optional no-page no-slice no-size no-origin)
;; TODO: add NO-PAGE, NO-SLICE, NO-SIZE, NO-ORIGIN to the docstring.
"Create a bookmark PDF record.
The optional, boolean args exclude certain attributes."
(let ((displayed-p (eq (current-buffer)
(window-buffer))))
(cons (buffer-name)
(append (bookmark-make-record-default nil t 1)
`(,(unless no-page
(cons 'page (pdf-view-current-page)))
,(unless no-slice
(cons 'slice (and displayed-p
(pdf-view-current-slice))))
,(unless no-size
(cons 'size pdf-view-display-size))
,(unless no-origin
(cons 'origin
(and displayed-p
(let ((edges (pdf-util-image-displayed-edges nil t)))
(pdf-util-scale-pixel-to-relative
(cons (car edges) (cadr edges)) nil t)))))
(handler . pdf-view-bookmark-jump-handler))))))
(or pdf-view--bookmark-to-restore
(let ((win (car (cl-find-if #'window-live-p image-mode-winprops-alist
:key #'car-safe))))
(cons (buffer-name)
(append (bookmark-make-record-default
nil t (if pdf-view-roll-minor-mode (point) 1))
`(,(unless no-page
(cons 'page (pdf-view-current-page win)))
,(unless no-slice
(cons 'slice (and win (pdf-view-current-slice win))))
,(unless no-size
(cons 'size pdf-view-display-size))
,(unless no-origin
(cons 'origin
(and win
(let* ((edges (pdf-util-image-displayed-edges
win (eq (window-buffer win) (current-buffer)))))
(pdf-util-scale-pixel-to-relative
(cons (car edges) (cadr edges)) nil
(eq (current-buffer) (window-buffer)) win)))))
(handler . pdf-view-bookmark-jump-handler)))))))
;;;###autoload
(defun pdf-view-bookmark-jump-handler (bmk)
"The bookmark handler-function interface for bookmark BMK.
See also `pdf-view-bookmark-make-record'."
(let ((page (bookmark-prop-get bmk 'page))
(slice (bookmark-prop-get bmk 'slice))
(size (bookmark-prop-get bmk 'size))
(origin (bookmark-prop-get bmk 'origin))
(file (bookmark-prop-get bmk 'filename))
(show-fn-sym (make-symbol "pdf-view-bookmark-after-jump-hook")))
(let* ((file (bookmark-prop-get bmk 'filename))
(buf (or (find-buffer-visiting file)
(find-file-noselect file)))
(buf-chg-fns-p (boundp 'window-buffer-change-functions))
(hook (if (and buf-chg-fns-p (not (get-buffer-window buf)))
'window-buffer-change-functions
'bookmark-after-jump-hook))
(show-fn-sym (make-symbol "pdf-show-buffer-function")))
(fset show-fn-sym
(lambda ()
(remove-hook 'bookmark-after-jump-hook show-fn-sym)
(unless (derived-mode-p 'pdf-view-mode)
(pdf-view-mode))
(with-selected-window
(or (get-buffer-window (current-buffer) 0)
(selected-window))
(when size
(setq-local pdf-view-display-size size))
(when slice
(apply 'pdf-view-set-slice slice))
(when (numberp page)
(pdf-view-goto-page page))
(when origin
(let ((size (pdf-view-image-size t)))
(lambda (&optional win)
(when (eq buf (current-buffer))
(with-selected-window
(or win
(get-buffer-window buf 0)
(selected-window))
(remove-hook hook show-fn-sym buf-chg-fns-p)
(unless (derived-mode-p 'pdf-view-mode)
(pdf-view-mode))
(when-let ((size (bookmark-prop-get
pdf-view--bookmark-to-restore 'size)))
(setq-local pdf-view-display-size size))
(when-let ((slice (bookmark-prop-get
pdf-view--bookmark-to-restore 'slice)))
(apply 'pdf-view-set-slice slice))
(when-let ((page (bookmark-prop-get
pdf-view--bookmark-to-restore 'page))
((numberp page)))
(pdf-view-goto-page page win))
(when-let ((origin (bookmark-prop-get
pdf-view--bookmark-to-restore 'origin))
(size (pdf-view-image-size t win)))
(image-set-window-hscroll
(round (/ (* (car origin) (car size))
(frame-char-width))))
@@ -1725,10 +1880,11 @@ See also `pdf-view-bookmark-make-record'."
(round (/ (* (cdr origin) (cdr size))
(if pdf-view-have-image-mode-pixel-vscroll
1
(frame-char-height))))))))))
(add-hook 'bookmark-after-jump-hook show-fn-sym)
(set-buffer (or (find-buffer-visiting file)
(find-file-noselect file)))))
(frame-char-height))))))
(setq-local pdf-view--bookmark-to-restore nil)))))
(set-buffer buf)
(setq-local pdf-view--bookmark-to-restore bmk)
(add-hook hook show-fn-sym nil buf-chg-fns-p)))
(defun pdf-view-bookmark-jump (bmk)
"Switch to bookmark BMK.
@@ -1746,21 +1902,22 @@ works only with bookmarks created by
(pdf-view-bookmark-jump-handler bmk)
(run-hooks 'bookmark-after-jump-hook))))
(defun pdf-view-registerv-make ()
"Create a PDF register entry of the current position."
(registerv-make
(pdf-view-bookmark-make-record nil t t)
:print-func 'pdf-view-registerv-print-func
:jump-func 'pdf-view-bookmark-jump
:insert-func (lambda (bmk)
(insert (format "%S" bmk)))))
;; Register support using cl-defstruct (replaces obsolete registerv-make)
(cl-defstruct (pdf-view-register
(:constructor nil)
(:constructor pdf-view-register--make (bookmark))
(:copier nil))
"A PDF position register entry."
bookmark)
(defun pdf-view-registerv-print-func (bmk)
"Print a textual representation of bookmark BMK.
(cl-defmethod register-val-jump-to ((val pdf-view-register) _arg)
"Jump to the PDF position stored in VAL."
(pdf-view-bookmark-jump (pdf-view-register-bookmark val)))
This function is used as the `:print-func' property with
`registerv-make'."
(let* ((file (bookmark-prop-get bmk 'filename))
(cl-defmethod register-val-describe ((val pdf-view-register) _verbose)
"Print a description of the PDF position stored in VAL."
(let* ((bmk (pdf-view-register-bookmark val))
(file (bookmark-prop-get bmk 'filename))
(buffer (find-buffer-visiting file))
(page (bookmark-prop-get bmk 'page))
(origin (bookmark-prop-get bmk 'origin)))
@@ -1773,6 +1930,15 @@ This function is used as the `:print-func' property with
(round (* 100 (cdr origin)))
0)))))
(cl-defmethod register-val-insert ((val pdf-view-register))
"Insert the PDF bookmark stored in VAL."
(insert (format "%S" (pdf-view-register-bookmark val))))
(defun pdf-view-registerv-make ()
"Create a PDF register entry of the current position."
(pdf-view-register--make
(pdf-view-bookmark-make-record nil t t)))
(defmacro pdf-view-with-register-alist (&rest body)
"Setup the proper binding for `register-alist' in BODY.

View File

@@ -800,7 +800,8 @@ unless the FILE-OR-BUFFER argument denotes a VPDF document."
link
`((edges . ,(pdf-util-edges-transform region .edges t))
,@(pdf-virtual--transform-goto-dest link filename region)))))
(pdf-virtual--filter-edges region (car links) 'car)))))
(pdf-virtual--filter-edges region (car links)
(lambda (link) (cdr (assq 'edges link))))))))
(pdf-virtual-define-adapter number-of-pages (&optional file-or-buffer)
(pdf-info-compose-queries nil (pdf-virtual-document-number-of-pages)))