EmacsにおけるLaTeX執筆環境構築(3) pdf-toolsについて

はじめに

pdf-toolsを使うことで、Emacs上で使い勝手の良いPDFビューワーが実現できるので、その設定を残しておく。

インストール

基本は公式ページを参照。epdinfoを使うので、そのコンパイルに必要なライブラリ(popplerなど)を色々とインストールする必要が出てくる。

OS別のインストール手順

Linux
sudo aptitude install libpng-dev zlib1g-dev
sudo aptitude install libpoppler-glib-dev
sudo aptitude install libpoppler-private-dev
MacOS

Homebrewは必要なので事前にインストールしておく。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

次にpopplerとautomakeをインストールする。

 brew install poppler automake

poppler, automakeはそれぞれ依存ライブラリを持つので、コンパイルには時間がかかる。

pdf-toolsのインストール

Lispパッケージのインストール

Emacsのパッケージシステムからpdf-toolsをインストールする。

M-x package-install pdf-tools RET
epdfinfoのインストール

exec-path-from-shellをEmacsのパッケージシステムからインストールしておく。そして以下の設定を追記する。

(exec-path-from-shell-initialize)
(exec-path-from-shell-copy-env "PATH")

その後、

M-x pdf-tools-install

と実行するとepdinfoのビルドが始まる。もしくは設定ファイルに

(add-hook 'after-init-hook
          (lambda ()
            (pdf-tools-install)))

と書いておくと、Emacs起動後にepdinfoのビルドが始まる。このとき、Emacsの内部からシェルを起動してepdinfoのコンパイルをしているのだが、popplerやautomakeのインストールも一緒に行われる。ただ依存ライブラリはEmacsの外側で事前にコンパイル・インストールしておくのがスムーズだろう。さらにEmacs上からepdinfoのビルドに成功するためには、環境変数Emacsに適切に引き継ぐ必要がある。そのためにexec-path-from-shellをインストールしたのであった。

最終的に以下のメッセージを得て、ビルド完了となる。これでようやくpdf-toolsが使えるようになる。

---------------------------
       Installing
---------------------------
make -s install
 /usr/local/bin/gmkdir -p '/usr/local/bin'
  /usr/local/bin/ginstall -c epdfinfo '/usr/local/bin'
make[1]: Nothing to be done for `install-data-am'.
===========================
   Build succeeded. :O)
===========================
Comint finished at Sat Apr  3 11:51:45

設定

コードを表示する

(require 'pdf-tools)
(add-hook 'after-init-hook
          (lambda ()
            (pdf-tools-install)))

;; 左右見開き時のスクロールに関する設定
;; 参考 https://github.com/politza/pdf-tools/issues/303#issuecomment-397744326
(defun my-pdf-view-double-scroll-down-or-previous-page (&optional arg)
  "Scroll page down ARG lines if possible, else go to the previous page.

When `pdf-view-continuous' is non-nil, scrolling downward at the
top edge of the page moves to the previous page.  Otherwise, go
to previous page only on typing DEL (ARG is nil)."
  (interactive "P")
  (if (or pdf-view-continuous (null arg))
      (let ((hscroll (window-hscroll))
            (cur-page (pdf-view-current-page)))
        (when (or (= (window-vscroll)
                     (image-scroll-down arg))
                  ;; Workaround rounding/off-by-one issues.
                  (memq pdf-view-display-size
                        '(fit-height fit-page)))
          (pdf-view-previous-page 2)
          (when (/= cur-page (pdf-view-current-page))
            (image-eob)
            (image-bol 1))
          (set-window-hscroll (selected-window) hscroll)))
    (image-scroll-down arg)))
(defun my-pdf-view-double-scroll-up-or-next-page (&optional arg)
  "Scroll page up ARG lines if possible, else go to the next page.

When `pdf-view-continuous' is non-nil, scrolling upward at the
bottom edge of the page moves to the next page.  Otherwise, go to
next page only on typing SPC (ARG is nil)."
  (interactive "P")
  (if (or pdf-view-continuous (null arg))
      (let ((hscroll (window-hscroll))
            (cur-page (pdf-view-current-page)))
        (when (or (= (window-vscroll) (image-scroll-up arg))
                  ;; Workaround rounding/off-by-one issues.
                  (memq pdf-view-display-size
                        '(fit-height fit-page)))
          (pdf-view-next-page 2)
          (when (/= cur-page (pdf-view-current-page))
            (image-bob)
            (image-bol 1))
          (set-window-hscroll (selected-window) hscroll)))
    (image-scroll-up arg)))

;; 左右見開きページ送り:水平に分割したPDFページを並べ、左右は1ページずらしておく
(defun my-pdf-view-double-scroll-up-horizontal-view ()
  (interactive)
  (if (eq (buffer-local-value 'major-mode (current-buffer)) 'pdf-view-mode)
      (progn
        (my-pdf-view-double-scroll-up-or-next-page)
        (other-window 1)
        (if (eq (buffer-local-value 'major-mode (current-buffer)) 'pdf-view-mode)
            (progn
              (my-pdf-view-double-scroll-up-or-next-page)
              (other-window 1))))))

;; 左右見開きページ戻し:水平に分割したPDFページを並べ、左右は1ページずらしておく
(defun my-pdf-view-double-scroll-down-horizontal-view ()
  (interactive)
  (if (eq (buffer-local-value 'major-mode (current-buffer)) 'pdf-view-mode)
      (progn
        (my-pdf-view-double-scroll-down-or-previous-page)
        (other-window 1)
        (if (eq (buffer-local-value 'major-mode (current-buffer)) 'pdf-view-mode)
            (progn
              (my-pdf-view-double-scroll-down-or-previous-page)
              (other-window 1))))))

(add-hook 'pdf-view-mode-hook
          (lambda()

            ;; 必須
            (linum-mode -1)

            (setq pdf-annot-activate-created-annotations t)

            ;; use normal isearch
            (define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward)

            ;; SPCキーでページ送りスクロール
            (define-key pdf-view-mode-map (kbd "l")
              'my-pdf-view-double-scroll-up-horizontal-view)

            ;; Shift + SPCキーでページ戻しスクロール
            (define-key pdf-view-mode-map (kbd "j")
              'my-pdf-view-double-scroll-down-horizontal-view)

            ;; more fine-grained zooming
            (setq pdf-view-resize-factor 1.1)
            ))


解説と補足

pdf-toolsはAUCTeXのPDFビューワーとして利用することができる。
具体的には以下の設定を済ませておく。

(add-hook 'TeX-mode-hook
          #'(lambda ()
	      (pdf-tools-install)
              (setq TeX-view-program-selection
                    '((output-pdf "PDF Tools")))
              (setq TeX-view-program-list
                    '(("PDF Tools" TeX-pdf-tools-sync-view)))))

もしdisplay-line-numbers-modeを使っているならば,それをOFFにするようにしておく.

(add-hook 'pdf-view-mode-hook
          #'(lambda()
              (display-line-numbers-mode -1)))

さらに、「forward search」「backward search」のためには以下の設定が必要である。

(add-hook 'TeX-mode-hook
          #'(lambda ()
              (setq TeX-source-correlate-method 'synctex)
              (setq TeX-source-correlate-start-server t)
              (setq TeX-source-correlate-mode t)
              (with-eval-after-load "pdf-sync"
                (define-key TeX-source-correlate-map (kbd "C-c C-g")
                  'pdf-sync-forward-search))))
(add-hook 'TeX-mode-hook 'TeX-source-correlate-mode)

ちなみに「forward search」とは、latexソースコードの編集中に`C-c C-g`(後述)とすると、PDFの対応する行までジャンプする機能である。つまり「latexソースコードからビューワー上の頭出しができる」。「backward search」はその逆で、PDFの当該位置に対応するソースコードまでジャンプする機能である。つまりPDFを閲覧しながら、「このあたりってどんな感じでlatex書いたっけ」という確認のために「ビューワー上からlatexソースコードの頭出しができる」機能である。

これらの設定はすでに記事の中に見つけることができる。
tam5917.hatenablog.com

キーバインド

主なキーバインドは以下の通り。

入力 機能
C-n ページを1行単位で上スクロール
C-p ページの1行単位で下スクロール
C-f ページを右スクロール
C-p ページを左スクロール
< 先頭ページに移動
> 最終ページに移動
n 次のページに移動
p 前のページに移動
M-s o occur 発動(入力したクエリがヒットした行を別ウィンドウに抽出)
RET occur ジャンプ (occur ウィンドウ)
F ページ中のハイパーリンクを抽出し、指定箇所にジャンプ
f ページ中のハイパーリンクに対するインクリメンタルサーチ
B ジャンプを含めた過去の移動履歴を戻る
N ジャンプを含めた過去の移動履歴を進む
o (目次付きPDFの場合)目次を表示
RET 目次にジャンプ
M-g g 指定したページにジャンプ
+ ズームイン
- ズームアウト
0 ズームをもとに戻す
H PDFのページをウィンドウの高さにフィットさせる
W PDFのページをウィンドウの幅にフィットさせる
P PDFのページをウィンドウにフィットさせる
s b 余白をカット
s r 余白カットをもとに戻す
s m マウスを使って余白をカット
s f フレーム(Emacsの「枠」)のサイズをPDFに合わせて変更する
C-c C-g forward search
Ctrl + 左クリック backward search