Markdown Preview Enhancedについて
VSCodeの拡張機能で、markdownファイルでめっちゃ色んなことができます。MPEと公式で略されています。
markdown内の```コードブロック```
のコードをその場で実行し、プレビューに出力を表示することができるコードチャンクという神機能があり、この記事では公式であるtexコードをpdf2svgで画像に変換する機能をTeX2imgを使うように改造する方法を書いていきます。
※改造というにも不完全な強引な方法ですが、この記事のコメントでより良い解決法を得られるかもしれないと思い投稿します。
Markdown Preview Enhancedを知らなかった人は、下の公式サイトかその下のQiita記事のどちらかに是非目を通してほしいです。
問題
コードチャンクでlatexを実行すると、「pdf2svgが使えない」的なエラーメッセージが表示される。
(「的な」というのは、文字化けして分からんということです。)
公式に用意されていた方法の他、いろいろ試しましたがうまくいきませんでした。
環境
- Windows 11 x64
- VSCode: 1.75.1 (user setup)
- Markdown Preview Enhanced v0.6.7
- TeX2img 2.2.1(2020/10/15)
解決法
なんだかんだした結果、TeX2imgで画像を生成して表示する方法で目的を達成しました。
TeX2img の導入
(Windows版は別のサイトに飛んでダウンロード。なお、svgにするには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行目(あたり)をコメントアウトし、その後に以下のように書きました。
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}}`;
//ここまで
}
中身は
- 本来のpdf2svgを使うところをコメントアウトでスキップ
- コードブロックの中身を一時ファイル
savePath
(69行目で作成)に書き出し - 「
TeX2imgc
のパス」と「コードブロックの一時ファイルのパス」と「生成する画像のパス(例./img/***.png
)」をつなげてコマンドを形成して実行 - 一時ファイルを削除(
unlink
でいいんでしょうか?調べた感じ合ってそうですが、名前的に表示されなくなっているだけではないかと心配) - 引数(後述)から画像の表示サイズなどを指定
- Markdown Preview Enhancedの超便利機能
@import
で画像を表示
という流れです。
また、TeX2imgは.latex
だと認識してくれないようなので、同ファイルの最後らへんのfileExtensionMap
に"latex":".tex",
を追記しました。
これをしない場合、コードブロックで指定する言語にlatex
を使えず、tex
を使用することになります。(別にtex
で問題はないですが、間違えてlatex
にしてしまうとエラーになります)
※なお、編集&保存後にVSCodeを再起動しないと変更が反映されない模様。
markdownでの使い方
コードブロックの始めにlatex {cmd,hide,name="生成する画像の名前.拡張子"(,オプション...)}
、コードブロック内にlatexのコードを書いていきます。
{}
内の引数のオプションは、公式のフォーマットに準じて作りました。
-
width
,height
は画像の表示サイズで、生成される画像のサイズではありません。両方指定すると比率が無視されます。 -
title
はプレビューの画像にマウスオーバーすると表示されるチップ。 -
alt
は指定の画像が表示されなかったときに代わりに表示されるテキスト。
具体例1($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のコードブロック内でコードブロックを使う関係で表示できませんが、
% もちろん最後にも```をつけます
具体例2(tikz)
実は本来の目的はtikzの図を挿入することでした。
```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}
% 同様```
生成された画像
。。。これがやりたかったんです。ウクレレです。
具体例3(circuitikz)
コードはこのサイトから引用。
自分はMarkdown Preview Enhancedでプレビューの背景色を黒(VSCodeのDark+テーマと同色)にしているので、実は白じゃないとよく見えません。
```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では見えないので省略。
なお、画像は./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のタブを切り替えて戻ると直ります。