マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

LaTeXでフツーの幸せな出力を得る方法

これは「TeX & LaTeX Advent Caleandar 2023」の☃日めの記事です
(7日めは CareleSmith9 さんでした。9日めは ujimushi at SradJP さんです)

今年のアドベントカレンダーの重点テーマは​「(La)TeXで幸せになる方法」​です。そもそもLaTeXは文書を作成するためのソフトウェアなので、「LaTeXで幸せになる」ための“正攻法”は「幸せな出力を得ること」になるでしょう。そして、LaTeXで手軽にできる​「幸せな出力」​と聞いて真っ先に思いつくものといえば、やっぱりコレでしょう。

ツイッタァー(現𝕏)のどっかの某氏🙃のアカウントを見に行けば見飽きるほど大量に豊富に貼られている​「いつものゆきだるま☃画像」​です(素敵😊)。

フツーの☃画像(素敵😊)

※説明の都合上、幸せと無関係な要素(🦆とか🙃とか)は除去しました。

……なのですが、ここで問題です

この画像を実際にLaTeXで作るにはどうすればよいでしょう?

※ツイッタァー(現𝕏)に上げているのはPNG画像ですが、PDF→PNGの変換は容易であるため、ここでは元の「PDFの画像(文書)」を作ることを問題にします。

とにかくフツーの☃画像してみる話

TikZしてscsnowmanしたら完成……しない話

一見するととても簡単な話に見えます。画像の背景は単純に色を塗っただけのものです。空の部分はグラデーションになっていますが、これもTikZで簡単に実現できそうです。単一のtikzpicture環境の中身をそのまま出力文書としたいのでstandaloneクラス1を利用しましょう。

% pdfLaTeX文書
% standaloneクラスを用いるので, tikzpicture環境の
% "中身"だけが文書として出力される.
\documentclass{standalone}
\usepackage[svgnames]{xcolor}
\usepackage{tikz}
\begin{document}
% 72dpiでビットマップ画像に変換した際に
% "TikZ座標の1単位=1ピクセル"
% になるように, 単位を1bpに設定する.
\begin{tikzpicture}[x=1bp,y=1bp]
% 画像の大きさを強制的に指定する(480bp×360bp)
\useasboundingbox (0,0) rectangle (480,360);
% 地面
\fill[DarkGreen] (0,0) rectangle (480,40);
% 空(下部はSkyBlue!5で中ほどから上に向かって色が濃くなる)
\fill[SkyBlue!5] (0,40) rectangle (480,360);
\shade[top color=SkyBlue!65, bottom color=SkyBlue!5]
  (0,180) rectangle (480,360);
\end{tikzpicture}
\end{document}

このソースをpdflatexでタイプセットすると以下の出力が得られます。

背景の画像(非素敵😐)

完璧ですね!😃

あとはscsnowmanパッケージを追加して適当な位置に赤マフラーの☃を配置するだけです。

% pdfLaTeX文書
\documentclass{standalone}
\usepackage[svgnames]{xcolor}
\usepackage{tikz,scsnowman}%ゆきだるま☃
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (480,360);
\fill[DarkGreen] (0,0) rectangle (480,40);
\fill[SkyBlue!5] (0,40) rectangle (480,360);
\shade[top color=SkyBlue!65, bottom color=SkyBlue!5]
  (0,180) rectangle (480,360);
% 赤マフラーの☃を追加する(素敵)
\node at (232,248) {\scsnowman[scale=64,
  muffler=Red, hat=Green, buttons=RoyalBlue, arms=Brown, snow=SkyBlue]};
\end{tikzpicture}
\end{document}

scsnowmanした結果(チョットアレ😰)

これで完成……アレレ、なんか☃が青ざめているようです😰 何が起こったのでしょうか?

scsnowmanでフツーに☃できない話

実は\scsnowman命令は既定では☃の中身を塗りつぶしません2。なので背景の空の色が見えてしまっているのです。(雪玉の中身についても同様です。)

とはいえ、\scsnowman命令にはいっぱいオプションがあったはずなので、恐らく「体の色」の指定もきっとできるでしょう。そう思ってマニュアルを見ると、bodyというオプションがあることがわかります。体の色を白にするために\scsnowmanのオプションにbody=Whiteを追加してみましょう。

% \scsnowman のオプションに"body"を追加
\node at (232,248) {\scsnowman[scale=64, body=White,
  muffler=Red, hat=Green, buttons=RoyalBlue, arms=Brown,
  snow=SkyBlue]};

bofy=Whiteを指定した結果(アレ😱)

アレレ、顔がなくなってしまいました😱

この理由は、bodyオプションを指定すると輪郭線の扱い3が変わるからです。具体的には「全体の輪郭線は描かれなくなりその内部の線(顔のパーツの線やマフラーの輪郭線など)は白で描かれる4」ようになります。顔のパーツと体が同じ色なので見えなくなったわけです。

bodyオプションがどういう効果をもっているかは白以外の色を指定してみればわかるでしょう。以下の画像はbody=Turquoiseを指定した結果です。

body=Turquoiseを指定した結果

それでは、当初考えていたように「輪郭線をフツーに描いて中身を白く塗りつぶす」にはどう設定すればいいのでしょうか。実は現状のscsnowmanパッケージはこの設定をサポートしていません。フツーの☃画像を出力して幸せになるのは案外大変なようです😧

それでもフツーの☃画像したい話

というわけでここからが本題です。scsnowmanパッケージをフツーに使うだけではフツーの☃画像が作れないことがわかりました。フツーの☃画像を作って幸せになるにはどうすればいいでしょうか。ここでは3つの解決策を紹介します。

方法①:scsnowman+パッケージを使う

実はこの​「色指定の自由度がチョット足りない」​という問題は、3年前の「TeX & LaTeX Advent Calendar 2020」の記事で既に指摘されています。

そして、その記事の筆者により作製された「scsnowmanを拡張するパッケージ」であるscsnowman+ パッケージが公開されています。

scsnowman+ パッケージを読み込むと、\scsnowman命令が拡張されて「*-形」である\scsnowman*が使えるようになります。この\scsnowman*では「体の塗りつぶしの色を指定する」ためのbodyfillや「雪玉の塗りつぶしの色を指定する」ためのsnowfillなどの追加のオプションを指定して色をもっと自由に設定できるようになっています。

今回は体と雪玉を白く塗りつぶしたいのでbodyfill=White, snowfill=Whiteとオプションを指定しましょう。

% pdfLaTeX文書
\documentclass{standalone}
\usepackage[svgnames]{xcolor}
\usepackage{tikz,scsnowman+}%ゆきだるまプラス☃
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (480,360);
\fill[DarkGreen] (0,0) rectangle (480,40);
\fill[SkyBlue!5] (0,40) rectangle (480,360);
\shade[top color=SkyBlue!65, bottom color=SkyBlue!5]
  (0,180) rectangle (480,360);
% 拡張された"\scsnowman*"命令でフツーの☃を出力(素敵)
\node at (232,248) {\scsnowman*[scale=64,
  muffler=Red, hat=Green, buttons=RoyalBlue, arms=Brown, snow=SkyBlue,
% bodyfill, snowfill オプションを設定
  bodyfill=White, snowfill=White]};
\end{tikzpicture}
\end{document}

scsnowman+ で☃した結果(素敵😊)

フツーの☃画像が完成しました(幸せ😊)

方法②:scsnowman-libを使う

ところで、どっかの某氏🙃は飽きもせず延々とフツーの☃画像を流し続けているわけですから、どっかの某氏🙃も何らかの手段を持っているはずです。どっかの某氏🙃が使っている自作のライブラリscsnowman-libは以下のレポジトリで公開されています。

※他のものと一緒になっていてヤヤコシイことになっていますが、scsnowmoan-lib/ディレクトリ以下のファイル群が該当のファイルなので、これらのファイル群を“TeXが見える場所”に配置してください。

scsnowmanパッケージでは「ユーザが自由に☃の形状を定義する」ための​「ゆきだるま定義ファイル(snowman definition file)」​という仕組が用意されており、scsnowman-libではその仕組を利用して標準と異なる外形の☃を実現するための様々なゆきだるま定義ファイルを提供しています。

※「ゆきだるま定義ファイル」によりscsnowmanを拡張する話は2021年のアドベントカレンダー5記事でも登場しています。
※便宜的に以降の説明では「ゆきだるま定義ファイル」のことを“ライブラリ”と呼ぶことにします6

今回はこの中で最も基本的な zrextra ライブラリ(ファイル名は scsnowman-zrextra.def)を利用します。このライブラリでは「標準と同じ外形だが塗りつぶしの色が別個に指定できる」という形状の☃を定義しています。

zrextraライブラリを読み込むにはscsnowmanパッケージの\usescsnowmanlibrary命令を利用します。

\usepackage{scsnowman}%ゆきだるま☃
\usescsnowmanlibrary{zrextra}%ライブラリ読込

これによりzrextra特有の機能の設定をするための\sczrextrasetup命令が使えるようになります7。ここでbodyfillキーにより体の塗りつぶしの色、snowfillキーで雪玉の塗りつぶしの色を指定します。

\sczrextrasetup{bodyfill=White,snowfill=White}

☃の出力に対してzrextraライブラリで定義した形状を有効にするには\scsnowman命令のshapeキーにzrextraを指定する必要があります。

\scsnowman[scale=64, shape=zrextra, …(他オプション)…]

以上を踏まえると、全体のソースコードは以下のようになります。

% pdfLaTeX文書
\documentclass{standalone}
\usepackage[svgnames]{xcolor}
\usepackage{tikz,scsnowman}%ゆきだるま☃
% zrextraライブラリを読み込んで塗りつぶしの色を指定する
\usescsnowmanlibrary{zrextra}
\sczrextrasetup{bodyfill=White,snowfill=White}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (480,360);
\fill[DarkGreen] (0,0) rectangle (480,40);
\fill[SkyBlue!5] (0,40) rectangle (480,360);
\shade[top color=SkyBlue!65, bottom color=SkyBlue!5]
  (0,180) rectangle (480,360);
% shapeに"zrextra"を指定して☃をフツーにする(素敵)
\node at (232,248) {\scsnowman[scale=64, shape=zrextra,
  muffler=Red, hat=Green, buttons=RoyalBlue, arms=Brown, snow=SkyBlue]};
\end{tikzpicture}
\end{document}

zrextraで☃した結果(素敵😊)

再びフツーの☃画像が完成しました(幸せ😊)

方法③:scsnowmanだけで頑張る

いままでに紹介した2つの方法はどれも「scsnowmanパッケージとは別のもの」を使うものでした。これらのソフトウェアは別個にインストールする必要があるためソースコードの可搬性という点で問題になる可能性があります。そういうわけで、最後にscsnowmanパッケージだけでフツーの☃画像を実現する“裏技”を紹介することにします。

この記事の初めのほうで「\scsnowman命令のbodyキーを指定したら“顔なし”になった😱」という話がありましたが、その“顔なし”の☃をもう一度見てみましょう。このままでは使いものになりませんが、この上にもう一度☃をbody無しで)描いてみるとどうでしょう。今度は塗りつぶしがないので下の白色がそのまま残り、しかも顔のパーツや輪郭線はちゃんと描かれることになります。なんとこれで所望の☃になっています!😲

実際にこの方針を試してみましょう。まずbody=Whiteを指定したscsnowmanを配置します。雪玉も内部を白く塗りつぶしたいので、ここではsnow=Whiteを指定します(これで雪玉全体が白く塗りつぶされる)。その他のパーツはここでは描く必要がないので省略します。

\node at (232,248) {\scsnowman[scale=64, body=White, snow=White]};

そしてその後に、同じ位置にbody無しの\scsnowmanを配置します。今度はsnowに輪郭線の色を指定します。

\node at (232,248) {\scsnowman[scale=64,
  muffler=Red, hat=Green, buttons=RoyalBlue, arms=Brown,
  snow=SkyBlue]};

全体のソースコードは以下のようになりました。

% pdfLaTeX文書
\documentclass{standalone}
\usepackage[svgnames]{xcolor}
\usepackage{tikz,scsnowman}%ゆきだるま☃
% 他のやつは使わない
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (480,360);
\fill[DarkGreen] (0,0) rectangle (480,40);
\fill[SkyBlue!5] (0,40) rectangle (480,360);
\shade[top color=SkyBlue!65, bottom color=SkyBlue!5]
  (0,180) rectangle (480,360);
% "塗りつぶし用の☃"を描く
\node at (232,248) {\scsnowman[scale=64, body=White, snow=White]};
% 赤マフラーの☃を描く(素敵)
\node at (232,248) {\scsnowman[scale=64,
  muffler=Red, hat=Green, buttons=RoyalBlue, arms=Brown, snow=SkyBlue]};
\end{tikzpicture}
\end{document}

意図通りになっているか、出力を確かめてみましょう。

“裏技”で☃した結果(素敵😊)

ちゃんとフツーの☃画像が完成しました(幸せ😊)

※実は先に紹介した記事でもこの“裏技”が密かに使われています🙃

なお、先程の説明では“同じ場所に2つの☃を出力する”のをTikZのレベルで行いましたが、LaTeXでの一般的な“重ね書き”のテクニックを用いることもできます。こちらの方が使いやすい場合もあるでしょう。

% 1つめの☃を現在位置を勧めずに出力する
\makebox[0pt][l]{\scsnowman[scale=64, body=White, snow=White]}%
\scsnowman[scale=64,
  muffler=Red, hat=Green, buttons=RoyalBlue, arms=Brown, snow=SkyBlue]};

まとめ

フツーの☃を出力してドンドン幸せ😊になりましょう!💁


  1. standaloneクラスはdvipdfmxとの相性が悪いのでこの組み合わせは避けた方が無難です。今の場合、日本語組版OpenTypeフォント出力も不要なので、エンジンにはpdfLaTeXを使うことにします。
  2. このscsnowmanの仕様(および後述のbodyの仕様)は恐らく、「記号(モノクロ絵文字)として☃を出力する」という発想に基づくのだと思われます。
  3. ちなみに、bodyを指定しない場合は輪郭線が「現在のテキスト色」で描かれます。
  4. さらに雪玉も自動的に(snowの色で)塗りつぶす設定に変わります。ちなみに、値を省略してbodyだけ指定した場合は「現在のテキスト色」をbodyに指定したのと同値になります。
  5. ……の場外乱闘編の28日目🙃
  6. そもそも読込用の命令の名前が \usescsnowmanlibrary なので……。
  7. ライブラリが \scsnowman 命令の書式を改変することは不可能なので、別個に設定命令を用意しています。