この記事は TeX & LaTeX Advent Calendar 2021 の5日目の記事です。4日目は h20y6m さんでした。6日目も自分の記事です。
今回は,英和辞典とか単語帳とかでよく見かける,次のような品詞記号を出力するマクロを設計してみましょう。
要は単に「文字を角丸長方形で囲む」だけなのですが,気をつけるべき点が結構あります。
okumacro
パッケージの \keytop
を使う方法
最もシンプルな手としては,okumacro
パッケージの \keytop
を使う手があります。
【入力】
% upLaTeX + dvipdfmx \documentclass[dvipdfmx]{jlreq} \usepackage{okumacro} \begin{document} essential \keytop{形} 本質的☃な \end{document}
【出力】
とりあえずそれらしくなりましたが,ちょっとイマイチですね。\keytop
では,昔ながらの picture 環境を用いて四分円を描画しています。今時ならば,TikZ を用いてカッコよく描画しましょう。
TikZ を用いた描画
【入力】
\documentclass[dvipdfmx]{jlreq} \usepackage{tikz} \newcommand{\品詞}[1]{% \begin{tikzpicture}[baseline=(A.base),font=\sffamily\small] \node[draw=red, rectangle, rounded corners=1.5pt, text=red, fill=red!20!white, line width=.4pt, inner sep=1pt, outer sep=0pt ] (A) {#1}; \end{tikzpicture}% } \begin{document} essential \品詞{形} 本質的☃な \end{document}
【出力】
とりあえずいい感じになりました。TeX & LaTeX Advent Calendar 2021 の3日目の記事にあったように,baseline=(A.base)
によってノードのベースラインを外側のベースラインと合わせています。
また,中身が伸びてくると,横方向にいい感じに広がってゆきます。
不満点の解決(その1)~中身が欧文文字の場合~
現状だと,中身に欧文文字を入れたときにちょっと小さい気がします。
そこで,minimum width
を指定しましょう。\Cwd
は 1zw
に相当する長さになります。また,ノードの中に \vphantom{あ}
を仕込んで,和文文字分同等の高さを確保します。
【入力】
\documentclass[dvipdfmx]{jlreq} \usepackage{tikz} \newcommand{\品詞}[1]{% \begin{tikzpicture}[baseline=(A.base),font=\sffamily\small] \node[draw=red, rectangle, rounded corners=1.5pt, text=red, fill=red!20!white, line width=.4pt, inner sep=1pt, outer sep=0pt, minimum width=\Cwd+1pt, ] (A) {\vphantom{あ}#1}; \end{tikzpicture}% } \begin{document} essence \品詞{U} 本質☃ \end{document}
【出力】
これで和文文字の場合と同様の正方形型の出力となりました。
不満点の解決(その2)~色指定オプションを設ける~
現状では,色が固定されてしまっていますので,ユーザーが色を指定できるオプションを設けましょう。xkeyval
パッケージを使うと便利です。
【入力】
\documentclass[dvipdfmx]{jlreq} \usepackage{tikz} \usepackage{xkeyval} \makeatletter \define@cmdkeys{品詞}{draw,text,fill} %% デフォルト値 \def\cmdKV@品詞@draw{red} \def\cmdKV@品詞@fill{red!20!white} \def\cmdKV@品詞@text{red} \newcommand{\品詞}[2][]{% \begingroup \setkeys{品詞}{#1}% \begin{tikzpicture}[baseline=(A.base),font=\sffamily\small] \node[draw=\cmdKV@品詞@draw, rectangle, rounded corners=1.5pt, text=\cmdKV@品詞@text, fill=\cmdKV@品詞@fill, line width=.4pt, inner sep=1pt, outer sep=0pt, minimum width=\Cwd+1pt, ] (A) {\vphantom{あ}#2}; \end{tikzpicture}% \endgroup } \makeatother \begin{document} essence \品詞{名} 本質☃\par essential \品詞[draw=blue, fill=blue!20!white, text=blue]{形} 本質的☃な \end{document}
【出力】
不満点の解決(その3)~周囲の文字サイズに応じて相似拡大されるように~
現状の定義では,品詞記号の文字サイズが \small
固定だったり,rounded corners
などのパラメータ値が具体的なポイント数指定となっていたりと,周囲の文字サイズが変化することを想定していません。たとえば,次のように \Huge
をかぶせると,品詞記号だけサイズ変更されません。
【入力】
\Huge essential \品詞{形} 本質的☃な
【出力】
そこで,現在の文字サイズを取得し,それに対する相対値で各種パラメータの値を動的に決定するようにしましょう。
現在の文字サイズは \f@size
で取得できますので,それを基準にパラメータの数値を相対的に指定しましょう。
\dimexpr
では長さに対して小数点を含む係数をかけられないので,\pgfmathparse
の結果を格納して利用しています。
【入力】
\documentclass[dvipdfmx]{jlreq} \usepackage{tikz} \usepackage{xkeyval} \makeatletter \define@cmdkeys{品詞}{draw,text,fill} %% デフォルト値 \def\cmdKV@品詞@draw{red} \def\cmdKV@品詞@fill{red!20!white} \def\cmdKV@品詞@text{red} \newcommand{\品詞}[2][]{% \begingroup \setkeys{品詞}{#1}% \pgfmathparse{0.9*\f@size}\edef\品詞@fontsize{\pgfmathresult}% \pgfmathparse{0.15*\f@size}\edef\品詞@roundedcorners{\pgfmathresult}% \pgfmathparse{0.04*\f@size}\edef\品詞@linewidth{\pgfmathresult}% \pgfmathparse{0.1*\f@size}\edef\品詞@innersep{\pgfmathresult}% \pgfmathparse{1.1*\f@size}\edef\品詞@minimumwidth{\pgfmathresult}% \begin{tikzpicture}[baseline=(A.base),font=\sffamily\fontsize{\品詞@fontsize pt}{\baselineskip}\selectfont] \node[draw=\cmdKV@品詞@draw, rectangle, rounded corners=\品詞@roundedcorners pt, text=\cmdKV@品詞@text, fill=\cmdKV@品詞@fill, line width=\品詞@linewidth pt, inner sep=\品詞@innersep pt, outer sep=0pt, minimum width=\品詞@minimumwidth pt, ] (A) {\vphantom{あ}#2}; \end{tikzpicture}% \endgroup } \makeatother \begin{document} \parindent=0pt essence \品詞{名} 本質☃\par \Huge essential \品詞{形} 本質的☃な \end{document}
【出力】
これで周囲の文字サイズに応じて適切な相似拡大がかかるようになりました。
不満点の解決(その4)~適切な字間空白が入るように~
現状だと,品詞記号の前後に \kanjiskip
や \xkanjiskip
の挿入がなされないため,前後のスペースの入り方が不適切です。
【入力】
\fboxsep=0pt \framebox[5cm][s]{名詞の\品詞{C}と\品詞{U}の違い}
【出力】
これは,以前のこちらの記事で論じた問題です。
この問題を解決するには,現在ならばエンジンを問わずに使えるBXghostパッケージが便利です。\jghostguarded{...}
で囲んでおけば,和文文字扱いされて,前後に適切なスペースが入ります。
upLaTeX + dvipdfmx + jsarticle.cls の場合
upLaTeX + dvipdfmx + jsarticle.cls の場合のサンプルです。
【入力】
% upLaTeX + dvipdfmx \documentclass[dvipdfmx,uplatex]{jsarticle} \usepackage{tikz} \usepackage{xkeyval} \usepackage{bxghost} \makeatletter \define@cmdkeys{品詞}{draw,text,fill} %% デフォルト値 \def\cmdKV@品詞@draw{red} \def\cmdKV@品詞@fill{red!20!white} \def\cmdKV@品詞@text{red} \newcommand{\品詞}[2][]{% \jghostguarded{% 前後に和文ゴースト挿入 \begingroup \setkeys{品詞}{#1}% \pgfmathparse{0.9*\f@size}\edef\品詞@fontsize{\pgfmathresult}% \pgfmathparse{0.15*\f@size}\edef\品詞@roundedcorners{\pgfmathresult}% \pgfmathparse{0.04*\f@size}\edef\品詞@linewidth{\pgfmathresult}% \pgfmathparse{0.1*\f@size}\edef\品詞@innersep{\pgfmathresult}% \pgfmathparse{1.1*\f@size}\edef\品詞@minimumwidth{\pgfmathresult}% \begin{tikzpicture}[baseline=(A.base),font=\sffamily\fontsize{\品詞@fontsize pt}{\baselineskip}\selectfont] \node[draw=\cmdKV@品詞@draw, rectangle, rounded corners=\品詞@roundedcorners pt, text=\cmdKV@品詞@text, fill=\cmdKV@品詞@fill, line width=\品詞@linewidth pt, inner sep=\品詞@innersep pt, outer sep=0pt, minimum width=\品詞@minimumwidth pt, ] (A) {\vphantom{あ}#2}; \end{tikzpicture}% \endgroup }% } \makeatother \begin{document} \fboxsep=0pt \framebox[5cm][s]{名詞の\品詞{C}と\品詞{U}の違い}\par \end{document}
【出力】
LuaLaTeX + jlreq.cls の場合
LuaLaTeX + jlreq.cls の場合も同様に上手くいきます。
% lualatex \documentclass{jlreq} \usepackage{tikz} \usepackage{xkeyval} \usepackage{bxghost} (以下同じ)
upLaTeX + jlreq.cls の場合
この項目は古い情報です。現在では,BXghostパッケージのアップデートにより,upLaTeX + jlreq.cls の場合も自動的にうまく対応されるようになりました。解決策の技術的アイデアの記録として,古い記述も残しておきます。
BXghostパッケージは,全角スペースを用いてゴースト挿入を実現していますが,これは upLaTeX + jlreq.cls の場合には,JFM の違いによりそのままでは機能しません。そこで,TeX Forum の本田さんのアイデアに基づき,全角スペース出力部分を upTeX 標準の upjisr-h.tfm
を使うように改変しておけばOKです。
% upLaTeX + dvipdfmx \documentclass[dvipdfmx]{jlreq} \usepackage{tikz} \usepackage{xkeyval} \usepackage{bxghost} \makeatletter \DeclareFontFamily{JY2}{jismin}{} \DeclareFontFamily{JT2}{jismin}{} \DeclareFontShape{JY2}{jismin}{m}{n}{<->s*[0.92469]upjisr-h}{} \DeclareFontShape{JT2}{jismin}{m}{n}{<->s*[0.92469]upjisr-v}{} \def\usejismin{\usekanji{\k@encoding}{jismin}{m}{n}} % bxghost.sty の内部命令 \bxqgg@jghostguarded@a の定義を改変し,全角スペース出力部分だけ upjisr-h.tfm に変更 \edef\bxqgg@jghostguarded@a#1{% \bgroup \noexpand\usejismin \bxqgg@fwsp\bxqgg@kern@m@ne@zw \egroup #1% \bgroup \noexpand\usejismin \bxqgg@kern@m@ne@zw\bxqgg@fwsp \egroup } (以下同じ)
不満点の解決(その5)~upLaTeX + dvipdfmx での縦組対応~
現状のものだと,upLaTeX + dvipdfmx で縦組するとひどいことになります(LuaLaTeXの場合は現状のままで問題ありません)。これは,TikZ のバックエンドにある pgf の dvipdfmx への対応が完全ではないからでしょう。
upLaTeX + jlreq.cls の場合
そこで,TikZを使う部分を横組にすることで,「縦組の中でのTikZ」自体を避けるようにするとよいでしょう。(u)pTeX の独自プリミティブである \ifydir
を用いて挙動を場合分けする手が使えます。
【入力】
% uplatex + jlreq.cls + 縦組 + dvipdfmx \documentclass[dvipdfmx,tate]{jlreq} \usepackage{tikz} \usepackage{xkeyval} \usepackage{bxghost} \makeatletter \define@cmdkeys{品詞}{draw,text,fill} %% デフォルト値 \def\cmdKV@品詞@draw{red} \def\cmdKV@品詞@fill{red!20!white} \def\cmdKV@品詞@text{red} \newcommand{\品詞}[2][]{% \jghostguarded{% 前後に和文ゴースト挿入 \begingroup \setkeys{品詞}{#1}% \pgfmathparse{0.9*\f@size}\edef\品詞@fontsize{\pgfmathresult}% \pgfmathparse{0.15*\f@size}\edef\品詞@roundedcorners{\pgfmathresult}% \pgfmathparse{0.04*\f@size}\edef\品詞@linewidth{\pgfmathresult}% \pgfmathparse{0.1*\f@size}\edef\品詞@innersep{\pgfmathresult}% \pgfmathparse{1.1*\f@size}\edef\品詞@minimumwidth{\pgfmathresult}% \ifydir \def\品詞@node{\vphantom{あ}#2}% \else \def\品詞@node{\mbox{\tate\vphantom{あ}#2}}% \fi \mbox{\yoko \begin{tikzpicture}[baseline=(A.base),font=\sffamily\fontsize{\品詞@fontsize pt}{\baselineskip}\selectfont] \node[draw=\cmdKV@品詞@draw, rectangle, rounded corners=\品詞@roundedcorners pt, text=\cmdKV@品詞@text, fill=\cmdKV@品詞@fill, line width=\品詞@linewidth pt, inner sep=\品詞@innersep pt, outer sep=0pt, minimum width=\品詞@minimumwidth pt, ] (A) {\品詞@node}; \end{tikzpicture}}% \endgroup }% } \makeatother \begin{document} これは\品詞{動}と\品詞{形容}です。 \end{document}
【出力】
完成版
以上で完成した命令を Overleaf に置いておきます。