Hatena::ブログ(Diary)

マクロツイーター このページをアンテナに追加 RSSフィード Twitter

2017-11-16

例のダブルミニュートのアレ

アレの話。

事の顛末

取りあえず、中でどうなっているかを、まとめてみた。

  • 一番左にあるのが、TeXでの入力。
    • ①から始まるものは、Unicodeで入力した場合。
    • ②から始まるものは、AJ1(japanese-otf の \CID)で入力した場合。
  • AJ1 対応のフォントの場合は②が出力される。
  • AJ1 非対応のフォントの場合は③が出力される。
  • 日本語用のフォントは U+301E のグリフを含まないものが多いことに注意。
  • 備考:
    • ①→②は UniJIS(2004)-UTF16-H/V の CMap の変換。
    • ②→③は Adobe-Japan1-UCS2 の ToUnicode CMap の変換。
f:id:zrbabbler:20171116222626p:image

……ややこしい。

2017-11-14

和文な制御記号を \DeclareRobustCommand するとアレ

和文の記号の名前を持つ制御記号(control symbol)、例えば \☃ に対して保護付のマクロ\DeclareRobustCommand で定義したとする。

% upLaTeX文書, UTF-8
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{scsnowman}
% ☃のマフラーは赤色
\DeclareRobustCommand*{\}{\scsnowman[%
  hat,arms,buttons,snow,muffler=red,scale=1.3]}
\begin{document}
% 目次
\tableofcontents

% 本文
\section{{\TeX}とは何か}
アレ。
\section{\☃とは何か}
非アレ。
\end{document}
f:id:zrbabbler:20171115030712p:image

よく見ると、目次の☃の部分の出力がおかしい。☃の後に余分な欧文空白が入ってしまっている。それもそのはず、補助(.aux)ファイルを見ると、明らかに \☃ の後に空白文字が入っている。

\relax 
\@writefile{toc}{\contentsline {section}{\numberline {1}{\TeX  }とは何か}{1}}
\@writefile{toc}{\contentsline {section}{\numberline {2}\☃  とは何か}{1}}

TeX on LaTeX な人には周知の通り、\DeclareRobustComamnd\foo を定義した場合は、内部で“foo␣”という名前の制御綴が使われる。\foo が動く引数で用いられた場合、補助ファイルにはこの内部制御綴が \foo␣␣ という形で書き出されるので、再び字句解析されたときには \foo に戻る、という仕組である。この仕組に従って、保護付の \☃ の定義では、補助ファイルに \☃␣␣ が書き出されるわけだが、 は英字扱いではないので、これは空白トークンが 1 つあると見なされてしまうのである。

というと、欧文の制御記号でも全く同じことになりそうな気がするが、そうではない。例えば、先の例で \☃ の変わりに \8 を使った場合は、補助ファイルは次のようになる。余計な空白文字は書き出されず、従って、組版結果にも余分な空きは現れない。

\relax 
\@writefile{toc}{\contentsline {section}{\numberline {1}{\TeX  }とは何か}{1}}
\@writefile{toc}{\contentsline {section}{\numberline {2}\8とは何か}{1}}

何故欧文の方は正常なのかというと、この場合は LaTeX が特別に対処しているからである。元の制御綴の意味を調べてみる。

\DeclareRobustCommand*{\}{NICE!}
\typeout{(\string\☃=\meaning\☃)}
\DeclareRobustCommand*{\8}{NICE!}
\typeout{(\string\8=\meaning\8)}
\stop

すると次のような結果になる。明らかに、欧文の方は普段(制御語の場合)と違う少し複雑な定義になっている。

(\☃=macro:->\protect \☃  )
(\8=macro:->\x@protect \8\protect \8  )

これに対して、和文の制御記号の場合はこの特別扱いから漏れている(制御語と同じ扱い)ことがわかる。LaTeX カーネル和文の制御記号を制御記号と扱い損ねているのであろう。

2017-11-11

1TeX のソースを LaTeX 文書に掲載する画期的な方法(tc1verbatim パッケージ)

といっても、これで何事もなく終わってしまうと、“某きりたんぽ的 TeX フォーマット”の熱狂的なファンが嘆き悲しむことになるだろう。なので、軽いネタを出しておく。

1TeX の抱える重大な問題

周知のとおり、1TeX は極めて画期的なフォーマットである。しかし「文書ソースを“1”だけで表す」という性質は文字種の削減にとっては肝要であるものの、一方で次のような問題の原因ともなっている。

  • ソースの行が必然的に長くなるため、ソースを掲載する場合の支障になる。
  • “1”の個数を判別するのが難しい。

例えば、画期的な 1TeX の普及を企図して解説文書LaTeX で作ろうと、折角思い立っても、肝心の 1TeX のソースがまともに掲載できないのである。

f:id:zrbabbler:20171105160925p:image

もちろん、フォントサイズを小さくすれば所定の幅に収めることはできるのは確かだが、そうするとますます判読が困難になるだろう。1TeX の普及を考える上で、ソースの組版は避けて通れない問題であるのは間違いない。

画期的な新記法

この問題を解決するために、「“1”の羅列を表現する」ための画期的な新記法を考案した。

f:id:zrbabbler:20171111063045p:image

“1”を塊として配置することで必要な横幅を大幅に圧縮し、さらに、5 個・10 個の単位の区切りが明確になることで判別が容易になる。つまり前述の問題点の両方が一度に解消できるわけである。

画期的なパッケージ

この画期的なアイデアを実用に移すべく、パッケージを作ってみた。

このパッケージは LaTeX 標準(tools バンドル)の verbatim パッケージに対する拡張となっていて、以下の機能を提供する。

  • oneverbatim 環境
  • oneverbatim* 環境
  • \oneverbatiminput 命令
  • \oneverbatiminput* 命令

oneverbatim 環境は verbatim 環境の変種で、“1”の羅列の部分を新記法で出力する。その他も同様で、verbatim パッケージの機能の“新記法対応版”となっている。

例えば、「1TeX」と出力する 1TeX のコード(の断片*1)を書いたファイルがあるとする。

[sample.1tex]
1111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

このファイルの内容を LaTeX 文書中に \verbatiminput{sample.1tex} で取り込むと、以下のような残念な出力になる。

f:id:zrbabbler:20171105173742p:image

一方で、\oneverbatiminput{sample.1tex} で取り込むと、以下のような素敵な出力になる。

f:id:zrbabbler:20171105173739p:image

というわけで、画期的な 1TeX を普及させる妨げとなっていた要員は画期的に解決されるに至った。1TeX の更なる普及と発展を期待して、この記事を締め括りたい。


おまけ

1TeX の「1 を羅列する」というアイデアは自然数記法一種である「1 進数記法」(tally 記法)に由来する。それならば、当然の帰結として、先述の“1TeX ソースの記法”も自然数の画期的な新記法と見なすことができる。となると、それを LaTeX文書で使ってみたいと思うのは自然な要求であろう。従って、tc1verbatim では与えられた自然数を新記法で出力するための命令も用意している。

\documentclass[uplatex,a4paper]{jsarticle}
\usepackage{type1cm}% CMフォントの場合必要
\usepackage{bxtexlogo}
\bxtexlogoimport{*,SuyahTeX,OneTeX}
% 画期的新記法
\usepackage{tc1verbatim}
\renewcommand{\theenumi}{\onetally{enumi}}
\begin{document}

著名な{\TeX}のフォーマットは以下のものがある。
\begin{enumerate}
\item \LaTeX
\item plain \TeX
\item \ConTeXt
\item Lollipop
\item \SuyahTeX \label{itm:suyah}
\item \OneTeX \label{itm:one}
\end{enumerate}
特に\ref{itm:suyah}\ref{itm:one}は重要である。

\end{document}
f:id:zrbabbler:20171105172526p:image

LaTeX 文書の作製の際に、箇条番号の表記にもっと魅力的な方法がないかと思い悩んでいる人は、是非とも \onetally を試していただきたい。

*1:これは断片なので単独では 1tex でコンパイルできない。

2017-11-04

ラベルに紐づくカウンタ番号を取得する真っ当な方法

相互参照で使われる \ref\pageref の命令は、少なくとも LaTeX の仕様としては、参照先の番号やページ番号に「単純に展開される」わけではない。だから、\count255=\ref{foo} のようなコードの挙動は、厳密には未定義なのである。

それでは、\count255=\ref{foo} のようなことをしたい、すなわち、「ラベルに紐づいたカウンタ番号(やページ番号)のトークン列を直接取得したい」場合はどうすればよいか。本記事ではその方法について解説する。

それでもカウンタ番号を取得したい話

例えば次のような命令を実装したいとする。

  • \kansujiref{<ラベル>}: [頑強命令] ラベルに紐づいたカウンタ値を漢数字(一〇方式)で出力する。ただし、当該のカウンタ番号の表記は単純な算用数字の列(42 等)であると仮定する。(つまり、そのラベルに紐づいたカウンタが foo だとすると \thefoo の定義は \arabic{foo} であるとする。)ラベルが未定義の場合は \textbf{?} を代わりに出力する。

    ※横組と縦組が混在していて、カウンタ番号の表記そのものは算用数字であるが、縦組の部分でのカウンタ値は漢数字で出力したい、という状況を想定している。

想定される使用例と、その出力を挙げる。

% upLaTeX文書
\documentclass[uplatex,a5paper]{jsarticle}
\usepackage{plext,bxjalipsum}
\usepackage{mykansujiref}% ここで \kansujiref を定義
\begin{document}
\section{{\TeX}は激アレ}\label{sec:TeX}
なんとか。
\section{{\LaTeX}は微アレ}\label{sec:LaTeX}
かんとか。

\bigskip

\begin{minipage}<t>{10zw}% 部分的に縦組がある
\kansujiref{sec:LaTeX}節において、\jalipsumiroha\end{minipage}

\end{document}
(縦組の部分の出力)
f:id:zrbabbler:20171104232956p:image

以下にあげるのは“邪悪な”(つまり挙動未定義の)実装である。コレを書いてはイケナイ。

[mykansujiref.sty]
\@ifdefinable\kansujiref{
  \DeclareRobustCommand*\kansujiref[1]{% 頑強にする
    \expandafter\ifx\csname r@#1\endcsname\relax % ラベル未定義
      % 警告とかは略
      \textbf{}% 未定義時の出力
    \else
      \kansuji\ref{#1}\relax % 邪悪なコード
    \fi}
}

一応カウンタ番号を取得できる話

それでは“真っ当な”\kansujiref はどう書けばよいのか? マクロ \r@<ラベル> にカウンタ番号の情報が含まれているのでこれを直接用いればよい。\ref の処理を参考にすれば、実装は比較的容易である。

\@ifdefinable\kansujiref{
  \DeclareRobustCommand*\kansujiref[1]{%
    \expandafter\ifx\csname r@#1\endcsname\relax
      % 警告とかは略
      \textbf{}%
    \else
      \kansuji\expandafter\expandafter\expandafter
          \@firstoftwo\csname r@#1\endcsname\relax
    \fi}
}

先のものと異なりこの実装は“真っ当”であるが、しかし残念ながらあまり汎用的ではない。相互参照の機能を拡張するパッケージの中には、この内部マクロ \r@<ラベル> の規定を変更するもの(例えば hyperref や titleref など)がかなり多く存在する。汎用的なパッケージを作ろうとすると、これらのパッケージとの併用も考えなければならず、煩雑なことこの上ない。なんとかならないだろうか。

もっと汎用的にカウンタ番号を取得できる話

幸いにも、この「カウンタ番号を取得する」という機能については、それ専用の refcount というパッケージがある。refcount パッケージは以下の機能を提供する。

  • \setcounterref{<カウンタ>}{<ラベル>}:[LaTeX 命令] ラベルに紐づいたカウンタ値を(第 1 引数の)カウンタに代入する。
  • \addtocounterref{<カウンタ>}{<ラベル>}:[LaTeX 命令] ラベルに紐づいたカウンタ値を(第 1 引数の)カウンタの現在値に加算する。
    上記 2 つの命令では、ラベルに紐づいたカウンタ番号は算用数字でないといけない。ラベルに紐づいたカウンタと引数のカウンタは異なっていてもよい。
  • \getrefnumber{<ラベル>}:[完全展開可能] ラベルに紐づいたカウンタ番号のトークン列。
    ※「カウンタ値」でなくて「カウンタ番号」であることに注意。“xlii”等のローマ数字である可能性もある。
  • \setcounterpageref\addtocounterpageref\getpagerefnumber上記 3 つの命令の \pageref 版。
  • \setrefcountdefault{<トークン列>}:[通常] ラベルが未定義の場合に代わりに使われるトークン列(既定では 0)を変更する。
  • \refused{<ラベル>}:[通常] ラベルが参照されたことを LaTeX に通知する。同時に、ラベルが未定義の場合は警告を出す。
    ※完全展開可能の \getrefnumber では通知や警告ができないので、それと併用する。
  • \IfRefUndefinedExpandable{<ラベル>}{<真>}{<偽>}:[完全展開可能] ラベルが未定義であるかのテスト。

※[補足] LaTeX 標準の相互参照機能では、補助ファイルには「カウンタ番号」(表記)しか記録されないので、「カウンタ値」を得ることは原理的に無理である。refcount は LaTeX 標準の機能を利用するため、「カウンタ値」を得る機能は提供していない。ラベルに紐付く「カウンタ値」を取得したい場合は zref パッケージの利用が必要になる。

そして、一番大事なこととして、refcount は「相互参照を拡張するパッケージ」の有力なもの(hyperref、nameref、titleref)と併用可能である。

そういうわけで、\kansujiref のような命令を実装したい場合には、refcount の機能を使うといいであろう。これだと、hyperref や titleref とも共存できる。

[mykansujiref.sty]
\RequirePackage{refcount}

\@ifdefinable\kansujiref{
  \DeclareRobustCommand*\kansujiref[1]{%
    \refused{#1}% 通知とか未定義時警告とか
    \IfRefUndefinedExpandable{#1}{%
      \textbf{}% 未定義時の出力
    }{%else
      \kansuji\getrefnumber{#1}\relax
    }}
}

2017-10-30

BXjscls の新しいやつ(v1.7)

W32TeXTeX Live では既に更新されている。

今回の記事では Pandoc モード関連の改修について解説する。

Pandoc モードでの paragraph マーク

BXJS が paragraph でマークする件

BXJS クラスでは、jsclasses の仕様を引き継いで、paragraph レベル(\paragraph)の見出しにマーク(既定では“■”)が付加される。

\subsection{本質的な情報の見せ方}
\paragraph{マフラーの色について}
技術書を読んでいて内容が理解しづらいという場合、
その原因の多くは「本質的な情報がどこにあるのかが判りにくい」
%……(略)……
f:id:zrbabbler:20171030224958p:image

しかし、この「paragraph 特有の取扱」は、「パラグラフレベル*1がそれより上位の区分(章レベル・節レベル)とは異なる意味づけをもち、それゆえに異なる形式をもつ」ことを前提にしていると考えられる。

Pandoc が paragraph でマークするとアレ

一方で、Pandoc における文書区分のモデルは MarkdownHTML のものを引き継いでいて、そこでは単純に 6 レベル(HTML の H1〜H6)の区分が存在し、少なくとも先験的には意味合いの差異はないと考えられる。*2従って、デフォルトLaTeX テンプレートにおいては、全ての文書区分が均質になることを企図して、「パラグラフレベル(paragraph・subparagraph)も別行見出しをもつ」ように文書クラスの設定を改変する細工を施している。

例えば、次のような Markdown 文書があったとする。

# 素敵な技術書のつくりかた

## 素敵とは本質的であること

### 本質的な情報の見せ方

#### マフラーの色について

技術書を読んでいて内容が理解しづらいという場合、
……(略)……

これを bxjsarticle クラスの Pandoc モードを指定して LaTeX 文書に変換すると、出力結果は次のようになる。

f:id:zrbabbler:20171030230530p:image

見ての通り、ここで paragraph レベルだけマークが付いているのは奇妙である。

Pandoc モードでは paragraph でマークしない件

この問題に対処するため、Pandoc モードでの paragraph のマーク付加の仕様を以下のように定めた。

  • Pandoc の設定の影響で paragraph の形式が BXJS の既定と異なる形式になる場合は、paragraph マークの既定値(“■”)を空に変更する。

従って、Pandoc の既定の設定では paragraph マークは付かなくなる。

f:id:zrbabbler:20171030231123p:image
Pandoc モードでも paragraph でマークする場合

ただし、既定値が変わるだけなので、Pandoc モードであっても、paragraph-mark クラスオプションを指定することで好きなマークを付加することは相変わらず可能である。

---
title: Pandocでつくる 素敵技術書作成入門
author: 南斗火 太郎
papersize: a5
documentclass: bxjsarticle
classoption:
  - pandoc
  - paragraph-mark=☃
---

……(略)……
#### マフラーの色について

技術書を読んでいて内容が理解しづらいという場合、
……(略)……
f:id:zrbabbler:20171030232340p:image

また、Pandoc 側で「パラグラフレベルの形式変更の細工を抑止するためにテンプレート変数 subparagraph が真に設定されている」場合は、既定の paragraph マークが保持される。*3

Pandoc モードでの三点リーダ

Pandoc で和文三点リーダできない話

Pandoc において LaTeX 形式で出力する場合、入力テキスト中に含まれる Unicode 文字の幾つかを「LaTeX 式の入力」に変換する。その中に、和文文書において問題となるものが存在する。

  • (U+2026) → \ldots{}
  • (U+2018) → `
  • (U+2019) → '
  • (U+201C) → ``
  • (U+201D) → ''

これらの変換は欧文の文書においては問題がない。*4ところが、日本語 LaTeX においては一般に(xeCJK や LuaTeX-ja も含めて)、「Unicode 文字(左側)は和文扱い、“LaTeX 式の入力”(右側)は欧文扱い」という取扱がされている。従って、このままでは上記の 5 種の約物が常に欧文扱いになってしまい不都合である。

TeXはアレ……、いや違う、TeXは“超絶アレ”だ。
f:id:zrbabbler:20171030233631p:image

この中で、後ろの 4 つ(変換先がリガチャのもの)については、変換を抑止する方法がある。

これに対して、「\ldots{}」の変換を抑止する方法は提供されていないらしい。*6BXJS クラスは当然和文文書を想定しているため、“…”は和文として出力できないと困る。

Pandoc モードで和文三点リーダできる話

この問題に対処するため、Pandoc モードでは以下の処置を行うことにした。

  • \ldots を再定義し、非数式モードでかつ“\ldots{}”という形になっている場合は代わりに“”を実行する。すなわち、和文の“…”が出力される。
    • 数式モードの \ldots は標準通りである。従って、(tex_math_dollars 拡張有効時に)TeX 形式数式で \ldots を書いた場合は、欧文の“…”が出力される。
    • 非数式モードでも後続が {} でない \ldots は標準通りである。従って、raw_tex 拡張有効時に \ldots を書いた場合は、欧文の“…”が出力される。

従って、Pandoc モードを利用して、かつリガチャ関連の変換を抑止する指定(“-t latex-smart”または“--no-tex-ligatures”)を併用すると、和文約物を正常に出力できるようになる。

f:id:zrbabbler:20171030233625p:image

pdfLaTeX + Pandoc モードでは autotilde しない

通常、BXJS クラスを pdfLaTeX で使用する場合は、bxcjkjatype パッケージの autotilde オプションが有効になっていて、「~」で和欧文間空白(四分空き)を挿入できるようになっている。

% pdfLaTeX文書の本体
結局~Markdown~も\textbf{激アレ}であった。

しかし、Markdown などの他形式から Pandoc で変換する場合、「~」を入力してもそのまま「LaTeX で定義された ~」になるわけではない。*7だから結局四分空きを手動で入れることはできない。

(Markdownの入力)
結局Markdownも**激アレ**であった。

それに、Pandoc が LaTeX への変換結果として「~」を出したとするなら、当然それは「autotilde した場合の ~(和欧文間空白)」ではなくて「LaTeX 標準の ~(非分割欧文空白)」を想定していると考えるべきであろう。従って、Pandoc の変換を前提にする場合には autotilde を有効にすることは害にしかならないので、以下の修正を行った。

  • Pandoc モードでは、pdfLaTeX エンジン使用時に内部で読み込まれる bxcjkjatype パッケージに対して autotilde を指定しない。

もちろん、自動的に四分空きが入るようになったわけではない。なので、pdfLaTeX で Pandoc モードを使う場合は四分空きは諦めるしかないであろう。

(続く)

*1:「パラグラフレベル」は paragraph と subparagraph、「節レベル」は section と subsection と subsubsection、「章レベル」は chapter。

*2:もちろん、後付で意味を与えることは可能であり、Pandoc においては、それは適切な CSSLaTeX 文書クラスと紐付けられた特定のテンプレート適用することで実現される。

*3:ただし、Pandoc 側の --number-sections-Nオプションおよび secnumdepth 変数の指定により、paragraph レベルがの見出しが番号付きになっている場合は、再び既定の paragraph マークが抑止される。

*4:エンジンが XeTeX/LuaTeX の場合、defaultfontfeaturesLigatures=TeX を有効にしている。

*5:ただし 2015 年以降の版に限る。

*6:もしかしたら、Pandoc 2.0 版にはあるかも知れないが……。

*7Markdown で「~」を書くとそのまま“~”の文字を入力したことになる。