Markdown Preview EnhancedのLaTeXコードチャンクをTeX2imgで表示した方法

## Markdown Preview Enhancedについて

VSCodeの拡張機能で、markdownファイルでめっちゃ色んなことができます。MPEと公式で略されています。
markdown内の` ```コードブロック``` `のコードをその場で実行し、プレビューに出力を表示することができる[**コードチャンク**という神機能](https://shd101wyy.github.io/markdown-preview-enhanced/#/ja-jp/code-chunk)があり、この記事では公式であるtexコードをpdf2svgで画像に変換する機能をTeX2imgを使うように改造する方法を書いていきます。
※改造というにも不完全な強引な方法ですが、この記事のコメントでより良い解決法を得られるかもしれないと思い投稿します。

Markdown Preview Enhancedを知らなかった人は、下の公式サイトかその下のQiita記事のどちらかに是非目を通してほしいです。

https://shd101wyy.github.io/markdown-preview-enhanced/#/ja-jp/

https://qiita.com/take_me/items/5ff5304b58d9feec21df


([VSCode拡張機能のページ](https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced))

## 問題

> コードチャンクでlatexを実行すると、「pdf2svgが使えない」的なエラーメッセージが表示される。

（「的な」というのは、文字化けして分からんということです。）
[公式に用意されていた方法](https://shd101wyy.github.io/markdown-preview-enhanced/#/ja-jp/code-chunk?id=latex)の他、いろいろ試しましたがうまくいきませんでした。

## 環境
- Windows 11 x64
- VSCode: 1.75.1 (user setup)
- Markdown Preview Enhanced v0.6.7
- TeX2img 2.2.1（2020/10/15）

## 解決法
なんだかんだした結果、TeX2imgで画像を生成して表示する方法で目的を達成しました。
#### TeX2img の導入

https://tex2img.tech

（Windows版は[別のサイト](https://www.ms.u-tokyo.ac.jp/~abenori/soft/#TEX2IMG)に飛んでダウンロード。なお、svgにするにはmutoolが必要ですが、[同ページ最下部](https://www.ms.u-tokyo.ac.jp/~abenori/soft/index.html#MUTOOL)にある「mutool改造版」を使うと簡単でした）

自分は`C:/Program Files/TeX2img/TeX2imgc.exe`となるように配置しました。

（`TeX2imgc`はコマンドライン用で、GUIの`TeX2img`がこの用途でなく単体で使える（というかそれが本来の用途）ですが、普通に便利そうでした。）

#### Markdown Preview Enhanced の改造

VSCodeの拡張機能のフォルダ（自分の場合(おそらく既定)では`C:\Users\USERNAME\.vscode\extensions\shd101wyy.markdown-preview-enhanced-0.6.7`にありました。）
の内部の、`node_modules\@shd101wyy\mume\out\src\code-chunk.js`を(JavaScriptは経験がなかったので調べながら雰囲気で)改造しました。

76,77行目(あたり)をコメントアウトし、その後に以下のように書きました。

```js
        if (cmd.match(/(la)?tex/) || cmd === "pdflatex") {
            //コメントアウト const patchedAttributes = Object.assign(Object.assign({}, normalizedAttributes), { latex_engine: normalizedAttributes["latex_engine"] || latexEngine });
            //コメントアウト return compileLaTeX(content, fileDirectoryPath, patchedAttributes);
            //ここから
            yield utility.writeFile(savePath, content);
            const imgName = "img/" + (normalizedAttributes["name"] || `temp_${Math.random().toString(36).substring(2, 9)}.png`);
            const tex2imgPath = '"C:/Program Files/TeX2img/TeX2imgc"';
            const texPath = savePath.replace(/\\/g, "/");
            const imgPath = path.resolve(fileDirectoryPath, imgName).replace(/\\/g, "/");
            let cmdline = tex2imgPath + ' ' + texPath + ' ' + imgPath;
            yield child_process_1.execSync(cmdline);
            yield (0, fs_extra_1.unlink)(texPath);
            let importArgs = "";
            if (normalizedAttributes["width"]) importArgs += `width="${normalizedAttributes["width"]}" `
            if (normalizedAttributes["height"]) importArgs += `height="${normalizedAttributes["height"]}" `
            if (normalizedAttributes["title"]) importArgs += `title="${normalizedAttributes["title"]}" `
            if (normalizedAttributes["alt"]) importArgs += `alt="${normalizedAttributes["alt"]}" `
            return `@import "${imgPath}" {${importArgs}}`;
            //ここまで
        }
```

中身は

1. 本来のpdf2svgを使うところをコメントアウトでスキップ
1. コードブロックの中身を一時ファイル`savePath`(69行目で作成)に書き出し
1. 「`TeX2imgc`のパス」と「コードブロックの一時ファイルのパス」と「生成する画像のパス(例`./img/***.png`)」をつなげてコマンドを形成して実行
1. 一時ファイルを削除(`unlink`でいいんでしょうか？調べた感じ合ってそうですが、名前的に表示されなくなっているだけではないかと心配)
1. 引数（後述）から画像の表示サイズなどを指定
1. Markdown Preview Enhancedの[超便利機能`@import`](https://shd101wyy.github.io/markdown-preview-enhanced/#/ja-jp/file-imports)で画像を表示

という流れです。

また、TeX2imgは`.latex`だと認識してくれないようなので、同ファイルの最後らへんの`fileExtensionMap`に`"latex":".tex",`を追記しました。
これをしない場合、コードブロックで指定する言語に`latex`を使えず、`tex`を使用することになります。（別に`tex`で問題はないですが、間違えて`latex`にしてしまうとエラーになります）

※なお、編集&保存後にVSCodeを再起動しないと変更が反映されない模様。

#### markdownでの使い方

コードブロックの始めに`latex {cmd,hide,name="生成する画像の名前.拡張子"（,オプション...）}`、コードブロック内にlatexのコードを書いていきます。
`{}`内の引数のオプションは、[公式](https://shd101wyy.github.io/markdown-preview-enhanced/#/ja-jp/file-imports?id=画像の設定)のフォーマットに準じて作りました。

- `width`,`height`は画像の表示サイズで、生成される画像のサイズではありません。両方指定すると比率が無視されます。
- `title`はプレビューの画像にマウスオーバーすると表示されるチップ。
- `alt`は指定の画像が表示されなかったときに代わりに表示されるテキスト。

<details><summary>具体例1($LaTeX$)</summary>

```latex
```latex {cmd,hide,name="test.png",width="400px"}
\documentclass[fleqn,papersize,dvipdfmx]{jsarticle} %設定はTeX2imgの既定のまま。
\pagestyle{empty} %これで無駄な余白のないの画像になります

\begin{document}
This is \LaTeX
\end{document}
% Qiitaのコードブロック内でコードブロックを使う関係で表示できませんが、
% もちろん最後にも```をつけます
```
生成された画像
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984089/536332e5-a1ef-ff4c-08db-7d9f4a867a2d.png)
サイズは完全にピッタリです。
</details>

<details><summary>具体例2(tikz)</summary>

実は本来の目的はtikzの図を挿入することでした。

```latex
```latex {cmd,hide,name="G7.svg",height="70px"}
\documentclass[fleqn,papersize,dvipdfmx]{jsarticle}
\usepackage{tikz}
\pagestyle{empty}
\begin{document}
\newcommand{\str}[1]{\draw(0.5,4-#1)--++(7,0);}
\newcommand{\fret}[1]{\draw(0.5+#1*1.5,0)--++(0,3);}
\newcommand{\fing}[2]{\fill(#2*1.5-0.2,4-#1)circle(0.3);}
\begin{tikzpicture}
\draw(-0.5,1.5)node{\Huge\textbf{G7}};
\str{1}\str{2}\str{3}\str{4}
\fret{1}\fret{2}\fret{3}\fret{4}
\fing{1}{2} %1弦2フレット
\fing{2}{1}
\fing{3}{2}
\draw[line width=5pt](0.5,0)--(0.5,3);
\end{tikzpicture}
\end{document}
% 同様```
```
生成された画像

<img width="500px" alt="G7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984089/6fc41c12-a434-0db2-4b9e-590cba7171b7.png">

。。。これがやりたかったんです。ウクレレです。

</details>

<details><summary>具体例3(circuitikz)</summary>

コードは[このサイト](https://atatat.hatenablog.com/entry/cloud_latex31_circuitikz)から引用。
自分はMarkdown Preview Enhancedでプレビューの背景色を黒（VSCodeのDark+テーマと同色）にしているので、実は白じゃないとよく見えません。
```latex
```latex {cmd,hide,name="RLC.png",width="400px"}
\documentclass[fleqn,papersize,dvipdfmx]{jsarticle}
\usepackage{mine} %自分の"mine.sty"に使うパッケージをすべて含め、よく使う自作コマンドも書いています。

\pagestyle{empty}

\begin{document}
    \color{white}
\begin{figure}[h]
  \begin{center}
    \begin{circuitikz}[american currents]
 \draw (0,0)
      to[sV=$E$] (0,2)
      to[short] (2,2)
      to[european resistor=$R$] (2,0)
      to[short] (0,0);
      \draw (2,2)
      to[short] (4,2)
      to[L=$L$] (4,0)
      to[short] (2,0);
      \draw (4,2)
      to[short] (6,2)
      to[C=$C$] (6,0)
      to[short] (4,0);
    \end{circuitikz}
    \caption{\textcolor{white}{RLC並列回路}}
  \end{center}
\end{figure}
\end{document}
% 同様```
```
出力は白色でQiitaでは見えないので省略。

</details>

なお、画像は`./img/`に生成されますが、変更したい場合は上記`code-chunk.js`の`const imgName = "img/" + ...`の部分を変更してください。
また、画像が生成後texで書いたコードは削除して、通常の画像挿入(`@import`が便利)に書き換えると毎回コードチャンクを実行しなくてもいいです。(その際`modify_source`というオプションが便利そうなんですが、自分の環境では動作しませんでした。)
また変更するかもしれないしtexコードは残しておきたい、と思うかもしれなませんが、TeX2imgの機能に
> 生成画像の拡張属性にソース情報を保持することが可能です（保持しないことも可能）。ソースを保持させた場合，生成画像を TeX2img.app のウィンドウにドラッグ＆ドロップすることで，元ソースを復元できます。
(https://tex2img.tech#FEATURE)

とあるように、そのコードは復元可能なので消してしまって構わないんです。
（実際に上記の具体例1のコードを画像ファイルから復元することができました。しかし、試しにテキスト情報を保持しないオプションで生成した画像でやってみてもコードの復元ができました。pngでは両方6KBで、svgではテキスト情報ありが9KB、テキスト情報なしの方が11KBとなっていました。TeX2imgで画像にして公開する際にはコードも公開してしまう、と一応気に留める必要があるかもしれません）

### その他

- latexのコードが間違っていてもエラーはすべて
`Error: Command failed: "C:/.../TeX2imgc" 一時ファイル 画像ファイル`
なのでどこが間違っているのか分かりません。
ですがこの場合は一時ファイル.texが残るので、それをコンパイルすることで（あるいは単にTeX2imgで実行して）エラー箇所は特定可能です。（execSyncの返り値をどうこうするとエラー出力が取れたりすると思うんですが、~~面倒~~よくわからないので。）
ただ、自分の場合は通常のlatexビルドとTeX2img用の`\documentclass`が全然違うせいか、TeX2imgで問題なくいけるものが一時ファイルではエラーまみれになっているので、TeX2img（さらに言うとTeX2imgcの方がより近い）で実行する方がいいと思います。

- コードチャンクを変更して実行して画像が変更しても、markdownの結果が変わらないとプレビューに反映されないことがあります。これはVSCodeのタブを切り替えて戻ると直ります。