LoginSignup
3
1

git+latexdiff-vcで快適にHEADとの差分を確認しながら論文を執筆する for Windows

Last updated at Posted at 2022-11-25

Abstract

Latexで論文執筆して、先生と添削などを含む、内容のやり取りする際に綺麗な差分があると嬉しい気がします。
そこで本記事では

を示します。

環境

  • windows 10
  • texlive 2021
  • perl v5.32.1

latexdiff-vcって?

latexdiff-vclatexdiffという、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に下記のようにレシピとツールの設定を行います。

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を利用していても差分を生成できます。

MakeDiff.ps1
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
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1