PDFビューアーからEmacs上のSweaveにカーソルジャンプする方法で,patchDVI の SyncTeX サポートを利用する方法を書きました.
しかし,この方法はまだ開発中でベータ版の LuaLaTeX の使用を前提としているため安定していません.
そこで,patchDVIパッケージに手を入れて,日本語を含む Sweave ファイルを uplatex または platex で処理する方法を示します.
Windows 用の記述がメインですが,Linux での設定方法も加筆しました.
Mac は持ってないので未確認です.
もっと簡潔でエレガントな方法を見つけた方は,是非,修正/追記をお願いします.(by せーだ).
Windows, Linux の人は以下の事柄に注意すればいいと思います.
これまで予想以上の多くの方にダウンロードして頂いた添付のスクリプトは,古い方のファイル(不要)を削除しました.
残したファイルをリネームしたのでカウンターがリセットされましたが,+120程度に考えて下さい.
[2014/12/25] knitrでSweaveファイルを処理する方法を追記しました.
[2014/12/1] おそらく ver. 1.9.1590 以降,ここで書いた処理を行うと、必要な情報以外に内部コードなどが大量に表示されます.これは,関数 patchLog() 内の if(length(concords)) の直後に browser() が呼び出されているためです.デバッグ時の消し忘れでしょうか.気になる人は patchLog() の定義を書き直して,browser() を無効にしましょう.
[2014/5/1] RStudio への対応方法を追記しました.patchDVI パッケージを必要としない(RStudio には同等の機能が含まれている)ので簡単なはずです.
[2013/12/18] patchDVI ver. 1.9.1601が15日にCRANに登録されました.Windowsでは,今までの方法で動作を確認済です.下記のdvipdfmxを通す関数SweaveDVIPDFM()は,latexコンパイルとdvipdfmxによるPDF作成をセットで実行する仕様です.つまり,正しい結果が得られるまで,ユーザが繰り返しSweaveDVIPDFM()を呼び出し続けなければならず,かつ,途中段階では不要なPDF作成も試みます.bibtexを使うなら,その処理(手動)も入るでしょうorz.回避策として,Makefileやlatexmkといった外部ツールに一連のコンパイルを背負わせる工夫が必要かと思われます.SweaveDVIPDFM()のオプションを上手に指定することが大切です.また,Duncan Murdoch先生来日後に公開された記事やドキュメントではUnicodeを前提としていますが,Sweave()のエンコーディングを適切に選べば,Unicode以外でも動作するはずです.学会から提供されるクラスファイルによっては,依然としてplatex(Shift_JIS/EUC-JP)用のみの場合もあるので,ここの情報はまだ使えると考えています.
[2013/12/9] patchDVI ver. 1.9.158以降で、dvipdfmxが通るようになりました。詳細はR研究集会のページに記しましたので、そちらもご参照下さい。
[2013/1/17] 1/10付でpatchDVI Ver. 1.9がCRANに登録されました.Windowsでは,ここに記述した方法で従来通りに動作することを確認済です. なお,SumatraPDFはVer. 1.9以降でForward/Inverse Searchの機能を持っています.
install.packages("patchDVI", repos="http://R-Forge.R-project.org")
\usepackage{Sweave} \SweaveOpts{concordance=TRUE}
Sweave("test.Rnw", encoding = "utf-8")Shift_JIS なら次のようにします.
Sweave("test.Rnw", encoding = "cp932")この処理が終わると,test.tex(と,必要に応じた画像ファイルなど)の他に,test-concordance.texが作成されます.
uplatex -synctex=1 -guess-input-enc test.texplatex の場合は次のようにします.
platex -synctex=1 -guess-input-enc test.tex引数 -synctex=1 により,LaTeXコンパイル後にtest.synctex.gzが作成されます.
uplatex -synctex=1 -no-guess-input-enc -kanji=utf8 test.texplatex の場合は次のようにします.
platex -synctex=1 -no-guess-input-enc -kanji=utf8 test.tex自動判別せずに文字コードに Shift_JIS を指定する場合は -no-guess-input-enc -kanji=sjis オプションを使用します.
platex -synctex=1 -no-guess-input-enc -kanji=sjis test.tex最近はPDFでプレビューするケースが多くなってきたかもしれませんが,dvioutを使う場合は -src オプションも必要です.
uplatex -src -synctex=1 -guess-input-enc test.texplatex の場合は次のようにします.
platex -src -synctex=1 -guess-input-enc test.tex
library(patchDVI) source("c:/R/mypatchDVI.R") mypatchSynctex("test.synctex") mypatchDVI("test.dvi")latexコンパイルとDVIファイルへのパッチは,まとめてスクリプト化するといいです.
call latexmk.bat %~n1 set CONCORD=%~n1-concordance.tex set DVI=%~n1.dvi if exist %CONCORD% Rscript -e "Com <- commandArgs();library(patchDVI);\ source(\"C:/home/hoge/R/etc/mypatchDVI.R\");\ DVI <- paste(Com[7], \".dvi\", sep = \"\");\ SYNC <- paste(Com[7], \".synctex\", sep = \"\");\ mypatchSynctex(SYNC);mypatchDVI(DVI)" %~n1下から5行は実際は1行です.
mylatexmk.bat test.texESS使いであれば,ess-swv.elの141行目付近の
(call-process "latex" nil tex-buf 1 latex-filename)を次のように直すと,M-n lでLaTeXコンパイルとDVIファイルへのパッチ処理を一気にやります(※EmacsLispファイルの手の入れ方を知らない人にはお勧めしません).
(call-process "mylatexmk.bat" nil tex-buf 1 latex-filename)
(setq YaTeX-inhibit-prefix-letter t) (setq YaTeX-dvi2-command-ext-alist '(("TeXworks\\|texworks\\|texstudio\\|mupdf\\|SumatraPDF\\|Preview\\|Skim\\|TeXShop\\|evince\\|okular\\|zathura\\|qpdfview\\|Firefox\\|firefox\\|chrome\\|chromium\\|Adobe\\|Acrobat\\|AcroRd32\\|acroread\\|pdfopen\\|xdg-open\\|open\\|start" . ".pdf"))) (setq dvi2-command "rundll32 shell32,ShellExec_RunDLL SumatraPDF -reuse-instance") (setq tex-pdfview-command "rundll32 shell32,ShellExec_RunDLL SumatraPDF -reuse-instance")C-c C-g を入力すれば SumatraPDF で forward seasrch ができます.
(defun sumatrapdf-forward-search () (interactive) (progn (process-kill-without-query (start-process "fwdsumatrapdf" nil "fwdsumatrapdf" (expand-file-name (concat (file-name-sans-extension (or YaTeX-parent-file (save-excursion (YaTeX-visit-main t) buffer-file-name))) ".pdf")) (buffer-name) (number-to-string (save-restriction (widen) (count-lines (point-min) (point))))))))YaTeXへのキーバインドをアサインします.ここではC-c sにアサインしました.
(add-hook 'yatex-mode-hook '(lambda () (define-key YaTeX-mode-map (kbd "C-c s") 'sumatrapdf-forward-search)))Sweave ファイルを分割/インクルードしている場合は,ミニバッファでマスターの Sweave ファイル名を聞かれるので,それを入力すれば SumatraPDF に飛びます.
SumatraPDF.exe -reuse-instance -inverse-search "emacsclientw.exe --no-wait +%l %f" test.pdfここではemacsclientwを使いましたが,gnuclientwでも同じように動かせると思います.
(server-start)を記述しておきます.
RStudio は patchDVI 相当の機能が内蔵されていますが,TeX コンパイラは PDFLaTeX または XeLaTeX の2択です ToT.
設定をいじってLuaLaTeXを使う方法もありますが,ここではもう少し頑張って UTF-8 または Shift_JIS の (u)platex on Windows を使えるようにします.
texopts = "-synctex=1 -kanji=utf8 -no-guess-input-enc"uplatex を使用する場合は 175 行目を次のように修正して保存します.
use_uptex = 1同様に,176 行目も次のように修正して保存します.
use_latex = 1これは,Windows PowerShell またはコマンド プロンプトから
ptex2pdf.exe -l -u -ot "-synctex=1 -kanji=utf8 -no-guess-input-enc" hoge.texまたは
ptex2pdf.exe -l -ot "-synctex=1 -kanji=utf8 -no-guess-input-enc" hoge.texと引数を付与することなく,
ptex2pdf.exe hoge.texで同じことができるようにしています.この設定を,ご自身が常用している TeX コンパイラや文字コードに合わせて適宜変更してください.
texopts = "-synctex=1 -kanji=sjis -no-guess-input-enc"同様に,176 行目も次のように修正して保存します.
use_latex = 1これは,Windows Powershell またはコマンド プロンプトから
ptex2pdf.exe -l -ot "-synctex=1 -kanji=sjis -no-guess-input-enc" hoge.texと引数を付与することなく,
ptex2pdf.exe hoge.texで同じことができるようにしています.この設定を,ご自身が常用している TeX コンパイラや文字コードに合わせて適宜変更してください.
Sys.setenv("RSTUDIO_PDFLATEX" = "ptex2pdf.exe")と記入してもいいでしょう.もちろん RStudio の console にその都度打ち込んでも構いません.ptex2pdf.exe に引数を含めて渡すことができないので要注意です(4.での修正はそのためです).
knitr は,Sweaveをベースに他の便利な機能を含めて拡張されているパッケージで,現在も開発が活発に進められています.ただ,knitrはSweaveに対して完全に上位互換ではないため,従来の書式で作ったSweaveファイルがそのままknit()で処理できるとは限りません.例えば,このスレッドで説明している機能は,RStudioはある程度うまくやってくれるようですが,Rコンソールから直接knit()するとエラーが出ます.これを回避する方法は以下の通りです.
\usepackage{Sconcordance} \input{tmp-concordance} <<include=FALSE>>= opts_knit$set(concordance=TRUE) @knit()で生成されるTeXファイルには,\documentclassの最後尾に "\usepackage[]{graphicx}\usepackage[]{color}" が強制的に書き込まれます.opts_knit$set()の中でドライバ名を事前に指定すれば,手動で書き込む手間が省けます.例えばdvipsであれば次のようにします.
opts_knit$set(concordance=TRUE,latex.options.graphicx="dvips",latex.options.color="dvips")
library(knitr) knit("tmp.Rnw")同じフォルダにtmp.texとtmp-concordance.texが作られているはずです.
既存のSweaveファイルをknit()で処理するために,knitrパッケージにはSweave2knitr()というコンバーター関数が用意されています.次のように使います.
library(knitr) Sweave2knitr("sample.Rnw") ## これで sample-knitr.Rnw が作られる knit("sample-knitr.Rnw")
しかしSweave2knitr()はconcordance周りに対応できてないのと,なぜかopts_knit$set()がopts_chunk$set()になっているので,修正版を置いておきます.
library(knitr) source("mySweave2knitr.R") mySweave2knitr("sample.Rnw") ## これで sample-knitr.Rnw が作られる knit("sample-knitr.Rnw")
これで,sample.Rnwの
\usepackage{Sweave} \SweaveOpts{concordance=TRUE}
だった部分が,sample-knitr.Rnwでは上記手順1.と同様に変更され,Sweave固有のチャンクオプションも適切に修正されます.
まず,Linux の uplatex, platex が SyncTeX に未対応の場合,TeX Live を最新版にアップグレードします.
GNOME の場合は Evince を使用します.
Evince バージョン 3.0.0 以降でないと以下の設定が動作しない可能性が高いので注意してください.
KDE の場合は Okular を使用します.
Debian/Stable(Wheezy) での動作(ESS+YaTeXを利用)は確認したので,以後の記述はそれに基づきます.
Sweave("test.Rnw", encoding = "utf-8")文字コードが EUC-JP の場合は次のようにします.
Sweave("test.Rnw", encoding = "euc-jp")
uplatex -synctex=1 test.texplatex の場合は次のようにします.
platex -synctex=1 test.tex文字コードが EUC-JP の場合は -kanji=euc オプションも付けます.
platex -synctex=1 -kanji=euc test.texWindows と同様,xdvik などを使う場合は,-src オプションが必要になります.
platex -src -synctex=1 -kanji=euc test.texlatexmk を使うなら,上記コンパイルをシェルスクリプト化(例えばplatex-syncとします)して,.latexmkrcの該当部分を次のように修正します.
$latex = 'platex-sync';
#!/bin/bash BASE=`basename $1 .tex` latexmk $BASE if [ -e $BASE.synctex.gz ] then SCRIPT='library(patchDVI);source("/home/hoge/mypatchDVI.R");\ mypatchSynctex("'$BASE.synctex'");mypatchDVI("'$BASE.dvi'")' Rscript -e $SCRIPT fiSCRIPTから始まる行と,その次の行は実際には1行です.
(require 'dbus) (defun un-urlify (fname-or-url) "A trivial function that replaces a prefix of file:/// with just /." (if (string= (substring fname-or-url 0 8) "file:///") (substring fname-or-url 7) fname-or-url)) (defun evince-inverse-search (file linecol &rest ignored) (let* ((fname (decode-coding-string (url-unhex-string (un-urlify file)) 'utf-8)) (buf (find-file fname)) (line (car linecol)) (col (cadr linecol))) (if (null buf) (message "[Synctex]: %s is not opened..." fname) (switch-to-buffer buf) (goto-line (car linecol)) (unless (= col -1) (move-to-column col)) (x-focus-frame (selected-frame))))) (dbus-register-signal :session nil "/org/gnome/evince/Window/0" "org.gnome.evince.Window" "SyncSource" 'evince-inverse-search)Evince⇒Emacsの実行はCtrl-左クリックです.
(setq YaTeX-inhibit-prefix-letter t) (setq YaTeX-dvi2-command-ext-alist '(("TeXworks\\|texworks\\|texstudio\\|mupdf\\|SumatraPDF\\|Preview\\|Skim\\|TeXShop\\|evince\\|okular\\|zathura\\|qpdfview\\|Firefox\\|firefox\\|chrome\\|chromium\\|Adobe\\|Acrobat\\|AcroRd32\\|acroread\\|pdfopen\\|xdg-open\\|open\\|start" . ".pdf"))) (setq dvi2-command "evince") (setq tex-pdfview-command "evince")C-c C-g を入力すれば Evince で forward seasrch ができます.
Okular⇒Emacsの実行はShift-左クリックです.
- エディタ:
- Emacs client
- コマンド:
- emacsclient -a emacs --no-wait +%l %f
(server-start)を記述しておきます.
(setq YaTeX-inhibit-prefix-letter t) (setq YaTeX-dvi2-command-ext-alist '(("TeXworks\\|texworks\\|texstudio\\|mupdf\\|SumatraPDF\\|Preview\\|Skim\\|TeXShop\\|evince\\|okular\\|zathura\\|qpdfview\\|Firefox\\|firefox\\|chrome\\|chromium\\|Adobe\\|Acrobat\\|AcroRd32\\|acroread\\|pdfopen\\|xdg-open\\|open\\|start" . ".pdf"))) (setq dvi2-command "okular --unique") (setq tex-pdfview-command "okular --unique") (with-eval-after-load 'yatexprc (defun YaTeX-preview-jump-line () "Call jump-line function of various previewer on current main file" (interactive) (save-excursion (save-restriction (widen) (let*((pf (or YaTeX-parent-file (save-excursion (YaTeX-visit-main t) (buffer-file-name)))) (pdir (file-name-directory pf)) (bnr (substring pf 0 (string-match "\\....$" pf))) ;(cf (file-relative-name (buffer-file-name) pdir)) (cf (buffer-file-name)) ;2016-01-08 (buffer (get-buffer-create " *preview-jump-line*")) (line (count-lines (point-min) (point-end-of-line))) (previewer (YaTeX-preview-default-previewer)) (cmd (cond ((string-match "Skim" previewer) (format "%s %d '%s.pdf' '%s'" YaTeX-cmd-displayline line bnr cf)) ((string-match "evince" previewer) (format "%s '%s.pdf' %d '%s'" "fwdevince" bnr line cf)) ((string-match "sumatra" previewer) (format "%s \"%s.pdf\" -forward-search \"%s\" %d" previewer bnr cf line)) ((string-match "zathura" previewer) (format "%s --synctex-forward '%d:0:%s' '%s.pdf'" previewer line cf bnr)) ((string-match "qpdfview" previewer) (format "%s '%s.pdf#src:%s:%d:0'" previewer bnr cf line)) ((string-match "okular" previewer) (format "%s '%s.pdf#src:%d %s'" previewer bnr line (expand-file-name cf))) ))) (YaTeX-system cmd "jump-line" 'noask pdir))))))C-c C-g を入力すれば Okular で forward seasrch ができます.
;; Emacsのデフォルト文字コードはEUC-JP (set-default-coding-systems 'euc-jp-unix) (set-terminal-coding-system 'euc-jp-unix) (set-buffer-file-coding-system 'euc-jp-unix) (set-keyboard-coding-system 'euc-jp-unix) ;; ESSから呼び出すバッファだけUTF-8にする (setq ess-pre-run-hook '((lambda () (set-locale-environment "utf-8") (setenv "LANG" "ja_JP.UTF-8") )))
Sweave ファイルに \SweaveOpts{concordance=TRUE} を追加すると,関数 Sweave() によって Sweave ファイルと TeX ファイルの行番号の対応が書き込まれたファイル(上記の場合は test-concordance.tex)が作成されます.
開いてみれば分かりますが,これは情報を圧縮した形式なので,そのままでは意味不明です.
詳細は patchDVI パッケージのマニュアルに説明があります.
今回定義した関数 mypatchDVI() では,その圧縮した情報を展開してベクトル concord に付値します.
要素数が TeX ファイルの行数と同じで,n 番目の要素は TeX ファイルの行番号(n 行目)に対応する,Sweave ファイルの行番号です.
つまり,concord はコードチャンクを除いた Sweave ファイルの行番号になっています.
一方,引数 -src をつけてコンパイルした DVI ファイルには,source-special によって TeX ファイルの行番号と,元になる TeX ファイル名が埋め込まれているので,それを取り出し,concord の中身とファイル名をSweave ファイルに対応するように置き換えて,その結果を元の DVI ファイルに戻しています.
mypatchSynctex() でも,原理的には似たようなことを *.synctex.gz に対して行います.
オリジナルの関数 patchSynctex() では pdflatex の利用が前提ですが、pdflatex を使うと,PDF ファイルに上記 concord の素になる情報が取り込まれます.
しかし、uplatex または platex でコンパイルすると,その情報は DVI ファイルにしか反映されません.
したがって,mypatchSynctex() では,mypatchDVI() で処理する前の DVI ファイルから,必要な情報を抽出して対処するように変更しています.
mypatchSynctex() を mypatchDVI() の前に実行するのはそのためです.
なお,mypatchDVI() は,uplatex または platex のコンパイルに -src オプションを付けないと正しく動作しません.
今回の内容で,Sweaveファイルの編集効率もそれなりに上がります.
TexmakerもSweaveに対応しているので,TeXレベルでDVI/PDFビューアとの相互移動ができていれば,対応可能だと思います.
人柱になって頂ける方や,同様の情報を持っている方がいらっしゃれば集約しましょう.