LuaLaTeXでダウンロードカードを作った話

この記事は、TeX & LaTeX Advent Calendar 2018 の 15日目の記事です(遅くなってすみません)。

技術書典5で電子版を配布するのに利用したダウンロードカード(電子書籍をダウンロードするためのURLとシリアルコードが記載された紙)の話をします。技術書典の原稿執筆でやったことの全体像については、 同人誌「代数的数を作る」ができるまで/PandocとかLaTeXの話 を参照してください。

ダウンロードカードは、A4の紙に名刺サイズのカードが10枚分並ぶように印刷し、切り出すことで作成しました。個々のカードには異なるシリアルコードを印字したいのですが、その部分にLuaTeXのLuaによるプログラミング機能を活用しました。

印刷したダウンロードカード

目次

(12月17日 更新:Lua Tipsを追加)

デザインする

まずは、カードをデザインします。私はTikZでやりました。

後のことを考えて、カードの部分をマクロとして定義します。マクロの引数として、シリアルコードを与えられるようにします。

\documentclass[a4paper]{ltjsarticle}
\usepackage[hiragino-pro,deluxe]{luatexja-preset}
\usepackage{sourcecodepro}
\usepackage{graphicx,color}
\usepackage{tikz}
\newcommand\DLCard[1]{ % マクロの引数は、シリアルコード
\begin{tikzpicture}
  \path (0,0) rectangle (91mm,55mm); % カードの大きさ
  \draw[line width=0.97mm] (0.5mm,0.5mm) rectangle (90.5mm,54.5mm); % 枠線
  \node[above right] at (4mm,18mm) {\includegraphics[width=32mm,height=32mm]{cover-logo.pdf}};
  \node[above,font=\LARGE\gtfamily\bfseries] at (63.5mm,42mm) {代数的数を作る};
  \node[above,align=center,font=\large\gtfamily\bfseries] at (63.5mm,31mm) {多項式の根と\\因数分解のアルゴリズム};
  \node[above,font=\large\gtfamily] at (63.5mm,21mm) {ダウンロードカード};
  \node[above] at (45.5mm,11mm) {http://example.com/download}; % ダウンロード用のURL
  \node[draw,font=\ttfamily,fill=lightgray] at (45.5mm,7.5mm) {#1}; % シリアルコードの部分
\end{tikzpicture}%
}
\begin{document}
\DLCard{XXXX-XXXX-XXXX-XXXX}
\end{document}

シリアルコードのフォントは、0とO、1とIの見分けがはっきりつくものを選びましょう。

カードの大きさは名刺サイズ(91mm×55mm)としましたが、枠線の太さはそれよりも若干細くしてみました(密着して配置した時に隣のカードと隙間ができる)。

プレビュー:

配置する

A4だと名刺サイズのカードを10枚(2×5)配置できます。

縦横に並べるには基本的にはtabular環境を使って

\begin{tabular}{cc}
\DLCard{XXXX-XXXX-XXXX-0000}&
\DLCard{XXXX-XXXX-XXXX-0001}\\
\DLCard{XXXX-XXXX-XXXX-0002}&
\DLCard{XXXX-XXXX-XXXX-0003}\\
\DLCard{XXXX-XXXX-XXXX-0004}&
\DLCard{XXXX-XXXX-XXXX-0005}\\
\DLCard{XXXX-XXXX-XXXX-0006}&
\DLCard{XXXX-XXXX-XXXX-0007}\\
\DLCard{XXXX-XXXX-XXXX-0008}&
\DLCard{XXXX-XXXX-XXXX-0009}
\end{tabular}

とすれば良いのですが、そのままだと変に余白ができる

ので調整します。

...
\usepackage[text={182mm,275mm},centering]{geometry}
\setlength{\parindent}{0pt}
\renewcommand{\baselinestretch}{0.0}
...
\begin{document}
\begin{tabular}{@{}c@{}c}
\DLCard{XXXX-XXXX-XXXX-0000}&
\DLCard{XXXX-XXXX-XXXX-0001}\\
\DLCard{XXXX-XXXX-XXXX-0002}&
\DLCard{XXXX-XXXX-XXXX-0003}\\
\DLCard{XXXX-XXXX-XXXX-0004}&
\DLCard{XXXX-XXXX-XXXX-0005}\\
\DLCard{XXXX-XXXX-XXXX-0006}&
\DLCard{XXXX-XXXX-XXXX-0007}\\
\DLCard{XXXX-XXXX-XXXX-0008}&
\DLCard{XXXX-XXXX-XXXX-0009}
\end{tabular}
\end{document}

ページの余白の調整にはgeometryパッケージを使いました。tabular環境の項目間の空白の調整にはオプションで @{} を指定したり、\baselinestretchを設定したりしました。詳しいことはtabular環境やgeometryパッケージのマニュアルを読んでください。

プレビュー:

 

シリアルコードを埋め込む(Luaで)

各シリアルコードに対する\DLCardコマンドを手作業で.texファイルに書き込むのは大変です。「シリアルコードの一覧」みたいなファイル(.csvとか)から自動生成したいですよね。

スクリプト言語で.csvを読み込んで.texファイルを書き出せばこれは容易に実現できますが、せっかくLuaTeXを使っているので、文書中のLuaコード自身で「シリアルコードの一覧」を読みに行くようにしてみましょう。

Luaコードの記述方法ですが、luacodeパッケージのluacode*環境を使って、.texファイル中に直接記述することにします。

Luaコードからはio.linesでも何でも使って、印刷するべきシリアルコードの一覧を取得します。ここはシリアルコードの発行方法に依存するので、割愛します。

LuaコードからTeXコードを吐くにはtex.print関数を呼びます。注意点として、.texファイルで改行するのと同じ感覚でtex.printに改行コード \n を含む複数行文字列を渡すと意図しない結果が出てきます。

例えば、次のLuaTeX文書を処理すると、

\documentclass{article}
\usepackage{luacode}
\begin{document}
% TeXファイルにそのまま書いた例
Hello world!
\[
  123+
  456
\]

% tex.printを使った例
\begin{luacode*}
  tex.print([==[
Hello world!
\[
  123+
  456
\]
]==])
\end{luacode*}
\end{document}

(Lua Tips: [==[]==]に囲まれた部分はLuaの複数行(生)文字列リテラルです。通常の文字列リテラルで書くなら tex.print("Hello world!\n\\[\n  123+\n  456\n\\]\n") となります。イコールの数は複数行文字列リテラルの開始部と終了部で合致していればよく、 [[ で始めたリテラルは ]] で終了し、[===[ で始めたリテラルは ]===] で終了します。)

このようなPDFファイルが生成されます:

見ての通り、改行コードの部分が化けています。普通に.texファイルで改行したのと同じような結果を得るには、tex.printに文字列からなる配列(整数キーのテーブル)を渡します。string.explodeというLuaTeX独自の関数を使うと文字列を行ごとに分割することが簡単にできるので、それを使うと良いでしょう。

% 追加:
% string.explodeを使った例
\begin{luacode*}
  tex.print(string.explode([==[
Hello Lua world!
\[
  123+
  456
\]
]==],"\n"))
\end{luacode*}

(Lua Tips: string.explodeを使わずに同じようなコードを書くなら、tex.print({"Hello Lua world!","\\[","  123+","  456","\\]"}) となるでしょう)

まとめると、ダウンロードカードを出力するLuaLaTeX文書は次のようになります:

\documentclass[a4paper]{ltjsarticle}
\usepackage[hiragino-pro,deluxe]{luatexja-preset}
\usepackage{sourcecodepro}
\usepackage{graphicx,color}
\usepackage{tikz}
\usepackage{luacode}
\usepackage[text={182mm,275mm},centering]{geometry}
\setlength{\parindent}{0pt}
\renewcommand{\baselinestretch}{0.0}
\newcommand\DLCard[1]{
\begin{tikzpicture}
  \path (0,0) rectangle (91mm,55mm);
  \draw[line width=0.97mm] (0.5mm,0.5mm) rectangle (90.5mm,54.5mm);
  \node[above right] at (4mm,18mm) {\includegraphics[width=32mm,height=32mm]{cover-logo.pdf}};
  \node[above,font=\LARGE\gtfamily\bfseries] at (63.5mm,42mm) {代数的数を作る};
  \node[above,align=center,font=\large\gtfamily\bfseries] at (63.5mm,31mm) {多項式の根と\\因数分解のアルゴリズム};
  \node[above,font=\large\gtfamily] at (63.5mm,21mm) {ダウンロードカード};
  \node[above] at (45.5mm,11mm) {http://example.com/download};
  \node[draw,font=\ttfamily,fill=lightgray] at (45.5mm,7.5mm) {#1};
\end{tikzpicture}%
}
\begin{document}
\begin{luacode*}
  local codetable = {}
  -- TODO: .csv等からシリアルコードのリストを読み込んでcodetableに入れる
  codetable[1] = "XXXX-XXXX-XXXX-0000"
  codetable[2] = "XXXX-XXXX-XXXX-0001"
  -- 以下、10個ごとに1ページ書き出す
  for i = 0, math.ceil(#codetable / 10) - 1 do
    tex.print(string.explode(string.gsub([==[
\begin{tabular}{@{}c@{}c}
\DLCard{@1}&
\DLCard{@2}\\
\DLCard{@3}&
\DLCard{@4}\\
\DLCard{@5}&
\DLCard{@6}\\
\DLCard{@7}&
\DLCard{@8}\\
\DLCard{@9}&
\DLCard{@10}
\end{tabular}
]==],"@(%d+)",function(j)
      -- シリアルコードの個数が10の倍数でなかった場合は XXXX-XXXX-XXXX-XXXX を代わりに出力する
      return codetable[10 * i + tonumber(j)] or "XXXX-XXXX-XXXX-XXXX"
    end),"\n"))
  end
\end{luacode*}
\end{document}

(Lua Tips: 今度はTeX側に書き出すコードの一部が変化します。他の言語だとstring interpolationを使いたいところですが、Luaにはそういうのはないので地道にやっていく必要があります。やり方としては、文字列連結演算子 .. を使って tex.print(string.explode("\\begin{tabular}{@{}c@{}c}\n\\DLCard{"..codetable[1].."}&\n\\DLCard{"..codetable[2].."}\n\end{tabular}", "\n")) という風にするか、string.format関数を使って tex.print(string.explode(string.format("\\begin{tabular}{@{}c@{}c}\n\\DLCard{%s}&\n\\DLCard{%s}\n\end{tabular}", codetable[1], codetable[2]),"\n")) とすることが多いでしょう。今回は奇をてらってstring.gsubで文字列中の @数字 の部分を置き換える、という手法を取りました。)

あとはこれを印刷して切り取ればOKです。技術書典で配布したカードは、流石にコピー用紙だとアレかと思ったので、キンコーズに行って少し厚めの紙に印刷しました。

宣伝:ClutTeX

普段からこのブログを読んでいる方はすでにご存知だと思いますが、ClutTeXというLaTeX処理自動化ツールを作っています。

このツールを使って今回の文書を処理させるには

$ cluttex -e lualatex cards.tex

とします。ClutTeXの特徴として、.auxや.logのような余計なファイルは(ユーザーに見えるような場所には)作成されません。また、万が一文書処理中にエラーがあっても、ユーザーに入力を求めるようなことにはなりません。気になった方は上記のブログ記事を読んでください。


LuaLaTeXでダウンロードカードを作った話」への2件のフィードバック

  1. ピンバック: 技術書典5に初サークル参加した記録/前日まで | 雑記帳

  2. ピンバック: 2018年振り返り | 雑記帳

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です