Abstract
Latexで論文執筆して、先生と添削などを含む、内容のやり取りする際に綺麗な差分があると嬉しい気がします。
そこで本記事では
- 下記のような差分pdfファイルを本体のファイルを編集しながら生成する方法
Gitで管理しているLaTeXのdiffをpdfで見る(TeXLive2015版), https://nekketsuuu.github.io/entries/2017/01/27/latexdiff-vc.html より - windows特有と思われるエラーの対処
を示します。
環境
- windows 10
- texlive 2021
- perl v5.32.1
latexdiff-vcって?
latexdiff-vc
はlatexdiff
という、2つの.tex
ファイルの差分を強調した.tex
を出力させるCLIツールのラッパーです。
まぁオプションの処理とかを楽にしてくれるもの、という認識でよいでしょう。
latexdiff
自体の機能は 天地有情 [LaTeX] latexdiff --- LaTeXファイルの重要な違いを特定してマークアップする などがよいでしょう。
latexdiff-vcの導入
近年のtexliveにはlatexdiff
及びlatexdiff-vc
は標準で入っているようです。
必要ならCTANからダウンロードしてあげましょう。
続いてperlをインストールします。
Perl Downloadからインストーラーをダウンロードして実行します。
これでオッケーです。
差分取得
git init
でリポジトリを初期化・git add main.tex; git commit -m "initial commit"
などで作成した最初のmain.tex
をcommitします。その後、変更を行って次のコマンドを実行することで、diffフォルダの中にmain.tex
が生成されます。
latexdiff-vc -e utf8 --git --force -d diff -r HEAD main.tex
-e
は文字コードの指定です。
--git
はgitで管理されていること指定します。(なしでも推測してくれるらしい。)
--force
は今回生成される差分ファイル(diff/main.tex)の上書きを強制します。
-d
は差分ファイルの出力先を指定します。
-r
は比較するcommitの指定になっていて、commit IDだけではなく、HEADなどで指定できます。なんとタグも使えます。
--math-markup=3
を追加したほうが良いかも(デフォルトだと数式の変更の検知がちょっと強すぎた。)
多分エラーが出るのでその対処
2 and only 2 non-option arguments required.
と出てエラー
こんな感じのエラー
Working on main.tex
patching file main.tex
Running: latexdiff '-e' 'utf8' "main-oldtmp-30316.tex" "main.tex" > "diff/main.tex"
2 and only 2 non-option arguments required. Write latexdiff -h to get help
C:\texlive\2021\bin\win32\runscript.tlu:915: command failed with exit code 2:
perl.exe c:\texlive\2021\texmf-dist\scripts\latexdiff\latexdiff.pl '-e' 'utf8' "main-oldtmp-30316.tex" "main.tex"
Something went wrong in latexdiff. Deleting diff/main.tex and abort
C:\texlive\2021\bin\win32\runscript.tlu:915: command failed with exit code 5:
perl.exe c:\texlive\2021\texmf-dist\scripts\latexdiff\latexdiff-vc.pl -e utf8 --git --force -d diff -r HEAD main.tex
PS C:\Users\takeMe\Labora
PowerShellの問題?Windowsの問題?
なんかlatex-diff
呼び出しの引数でエラーをだしていると思われるが…
How should I pass latexdiff options to latexdiff-vc? に現れたコントリビューターによると「'
, "
が原因じゃないかな?」とのこと。「脆弱性のために'
で囲ってるのよね。」とのこと。一理ある。
ということで下記のようにオプション引数の部分から引用符を除いてみました。(筆者はこの記事執筆時点で初めてperlを触っているので、間違いに注意。多分大丈夫だけど...シンプルにセキュリティ的な観点だとアレなので、自己責任でお願いします。)
C:\texlive\2021\texmf-dist\scripts\latexdiff\latexdiff-vc.pl
の461行目付近で変更を加えます。
texlive 2022だと478行目でした。バージョンによっては少し違うかもなので注意。
if (scalar(@ldoptions) > 0 ) {
- $options = "\'" . join("\' \'",@ldoptions) . "\'";
+ $options = join(" ",@ldoptions);
} else {
$options = "";
}
476行目のsystem(...)
で、ここでオプション引数である-e utf8
の部分などが'
で囲まれているとlatexfiff
の引数のバリデーションに引っかかるっぽい。
※unless( system(...) == 0 ) { ... }
のブロックを削除して、system(...)
だけ実行させても同じエラーがでるっぽい?
編集しながら同時並行に差分ファイル出力させる
以下、ひとまずは--flatten
を使用しない前提で記述します。
VScodeの最強のLatex環境であるLaTeX Workshopでは、レシピを使うことで保存しながらコンパイルができる優れものです。
このレシピに差分出力のコマンドをかませることで、作業と並行してHEADと差分を出力させようと思います。
ここではpLatexで、platex2pdfを用いてコンパイルを行います。
settings.jsonに下記のようにレシピとツールの設定を行います。
{
"latex-workshop.latex.outDir": "submit", // main.texの出力ディレクトリ指定
"latex-workshop.latex.tools": [
{
"command": "ptex2pdf",
"args": [
"-l",
"-ot",
"-kanji=utf8 -synctex=1",
"-halt-on-error",
"%DOC%",
"-output-directory=%OUTDIR%"
],
"name": "ptex2pdf"
},
{
"command": "ptex2pdf",
"args": [
"-l",
"-ot",
"-kanji=utf8 -synctex=1",
"-halt-on-error",
"diff/main.tex",
"-output-directory=diff"
],
"name": "diff2pdf"
},
{
"command": "latexdiff-vc",
"args": [
"-e",
"utf8",
"--git",
"--force",
"-d",
"diff",
"-r",
"HEAD",
"main.tex"
],
"name": "make diff"
}
],
"latex-workshop.latex.recipes": [
{
"name": "ptex2pdf*1",
"tools": [
"ptex2pdf",
"make diff",
"diff2pdf"
]
},
{
"name": "ptex2pdf*2",
"tools": [
"ptex2pdf",
"ptex2pdf",
"make diff",
"diff2pdf"
]
}
],
}
このように設定することで、普段main.tex
を執筆していると保存する度に自動でコンパイルが走りますし、いつでもHEADとの差分を確認することができます。
Tips
小ネタを自分の備忘録として書いておきます。
オプション--flatten
が使えない
\input{}
とかを使うときに必要なオプション引数--flatten
が使えないっぽい。
Gitで管理した論文の差分pdfをlatexdiffで作成する(latexdiff-vc –flattenでエラーが出た時の対応) に考察が記されています。
エラー内容を見ると、git archive –format=tar HEAD | ( cd ./latexdiff-vc-HEAD ; tar -xf -)が実行できていないようですが、本来は作成されたlatexdiff-vc-HEADのディレクトリにHEADのコミット分がtar.gz形式で保存されるはずです。
こちらの記事で提案している解決策は、指定したcommit IDから対象のtexファイルを引っ張て来てそれとlatexdiff
を使って差分ファイルを出力、という形になっています。(latexdiff-vc
を経由しないという形ですね。)
ただし、自分の環境ではtarでやろうとすると解凍でエラーが発生しました。
加えて、bblファイルのディレクトリがtexファイルと同階層ではない場合は、perlをいじる必要がありそうでした。
よってここでは
- PowerShellで
- zipで書き出して
- bblを自分で差分生成
するような代替案を残しておきます。
# 比べる対象
$ref = "HEAD"
# zipで書き出す
git archive $ref --output="$ref.zip"
Expand-Archive "$ref.zip"
# 差分ファイルをdiffフォルダに作る
latexdiff -e utf8 "$ref/paper.tex" .\paper.tex > diff/diff.tex
latexdiff -e utf8 "$ref/out/paper.bbl" "out/paper.bbl" > "diff/diff.bbl"
# bblの差分は、そのまま使うと番号などが大変なことになるので、消した引用文献は表示しないようにする(Optional)
(Get-Content .\diff\diff.bbl -Encoding utf8) | ForEach-Object {
if ($_.Contains('\DIFdelbegin')){
# remove
} else {
$_.Replace('\DIFdelend', '');
}
} | Set-Content .\diff\diff.bbl -Encoding utf8
日本語などが文字化けする
自分は、lateffdiff-vcではなくlatexdiffを直接使う時に起きました。
PowerShellが>
でリダイレクトするときにややこしいいことになっているっぽい。
対策: コンソール出力のデフォルト値をUTF-8にする。
てかずっとこれでいいので$PROFILE
に書け
[Console]::OutputEncoding = [Text.Encoding]::UTF8
で、latefdiff
を使う。
# Powershell 5.xなら
latexdiff -e utf-8 -t CFONT input1.tex input2.tex | Out-String | % { [Text.Encoding]::UTF8.GetBytes($_) } | Set-Content -Path "output.tex" -Encoding Byte
# Powershell 6以降
latexdiff -e utf-8 -t CFONT input1.tex input2.tex > "output.tex"
参考:
行番号を差分ファイルにのみ表示する
プリアンブルにlinenoを追加することで、行番号を表示できます。
↓ これを差分ファイルにのみ追加したい
\usepackage{lineno}
\linenumbers
冒頭の\documentclass{...}
の真下に追加すればよいので、強引に下記のようなスクリプトで実現できます。
# 比べる対象
$ref = "HEAD"
# 差分を生成
latexdiff-vc -e utf8 --git --force -d diff -r $ref paper.tex
Move-Item diff/paper.tex diff/diff.tex
(Get-Content .\diff\diff.tex -Encoding utf8) | ForEach-Object {
$_.Replace('\documentclass{...}','\documentclass{...}' + "`r`n" + '\usepackage{lineno}\linenumbers');
} | Set-Content .\diff\diff.tex -Encoding utf8
なお, `r`nは改行コードです。
ちなみに、最初の()は消すと動きません。
()で囲まないと、一行ごとに処理が次に進む(パイプライン)のですが、Get-Content
が終わるまでは、ファイルへのアクセスがロックされています。その状態でSet-Content
に到達するので、エラーを吐きます。
増やしたところだけを表示する
消したところは表示せず、増やしたところだけを強調するやつです。
手動で対応できますが、やはり面倒なのでスクリプトを書きました。
(Get-Content .\diff\diff.tex -Encoding utf8) | ForEach-Object {
$_.Replace('\providecommand{\DIFadd}[1]{{\protect\color{blue}\uwave{#1}}}','\providecommand{\DIFadd}[1]{{\protect\color{red}#1}}');
} | ForEach-Object {
$_.Replace('\providecommand{\DIFdel}[1]{{\protect\color{red}\sout{#1}}}','\providecommand{\DIFdel}[1]{{}}');
} | Set-Content .\diff\diff.tex -Encoding utf8
なお, PowerShellでは""
の場合は変数の展開などが行われますが''
の場合はいわゆるraw文字列で、展開などが行われません。
参考:
docmuteと併用すると壊れる
分割コンパイルで有名な手法の一つに、documuteを用いる方法がありますね。
latexdiff
で--flatten
オプションを用いると解決できそうですが、プリアンブルまで重複して\input
を解決してしまいます。(latexdiff-vc
を経由しない)
これはおそらく、latexdiffはinputマクロを実行しているわけではなく。あくまでファイルの内容を取り込んでいるだけだからだと思います。
したがって、分割コンパイルに必要なファイルの中身だけを切り出してから--flatten
を行う必要があります。
例: src フォルダー下に main.tex (メインとなるファイル) があり、abstract.tex, introduction.tex, method.tex を\input
で取り込んでいる。diffフォルダーに diff.tex を差分ファイルとして生成する。
※ main.tex 内で \input{./abstract.tex}
などと相対パスで指定している想定です。
srcの親フォルダから下記のスクリプトを実行することで、docmuteを利用していても差分を生成できます。
Param(
[string]$ref = "HEAD"
)
function Extract-Document-Content($texFile) {
$file = @(Get-Content $texFile -Encoding utf8);
$begin = $file.IndexOf('\begin{document}') + 1;
$end = $file.IndexOf('\end{document}') - 1;
$file[$begin..$end] | Set-Content $texFile -Encoding utf8
}
# 比較対象を解凍
git archive $ref --output="$ref.zip"
Expand-Archive "$ref.zip"
if (!(Test-Path .\temp)) {
mkdir temp;
}
Copy-Item .\src\main.tex .\temp\main.tex
Copy-Item .\src\main.bbl .\temp\main.bbl
$includingFiles = @("abstract.tex", "introduction.tex", "method.tex")
$includingFiles | ForEach-Object {
Copy-Item ./src/$_ ./temp/;
Extract-Document-Content ./temp/$_;
}
# 比較対象もdocumentのみを抽出
$includingFiles | ForEach-Object {
Extract-Document-Content ./$ref/src/$_;
}
latexdiff -e utf8 --math-markup=1 "$ref/src/main.tex" .\temp\main.tex > .\diff\diff.tex --flatten
latexdiff -e utf8 "$ref/src/main.bbl" .\temp\main.bbl > .\diff\diff.bbl
# 削除した引用文献はバグるので,その部分は消す.
if (Test-Path .\diff\diff.bbl) {
(Get-Content .\diff\diff.bbl -Encoding utf8) | ForEach-Object {
if ($_.Contains('\DIFdelbegin')){
# remove
} else {
$_.Replace('\DIFdelend', '');
}
} | Set-Content .\diff\diff.bbl -Encoding utf8
}
#! in diff folder
Set-Location .\diff;
function CompileDiffOnce {
# ここは環境に応じて変更してください
ptex2pdf -l -ot -kanji=utf8 -halt-on-error diff.tex
}
# 差分texをコンパイル
CompileDiffOnce
CompileDiffOnce
Set-Location $PSScriptRoot/../
#! in diff folder end
Remove-Item $ref -Recurse
Remove-Item "$ref.zip"
Remove-Item .\temp -Recurse