Skip to content

Latest commit

 

History

History
482 lines (407 loc) · 24.8 KB

README.md

File metadata and controls

482 lines (407 loc) · 24.8 KB

\lastnodechar プリミティブに぀いお

これは TeX  LaTeX Advent Calendar 2014 の 15 日目の蚘事ずしお䜜成したものです 昚日12/14は hak7a3 さん明日12/16は !CardinalXaro さんです

TeX ナヌザの集い 2014 の䌑憩䞭 次のように pLaTeX で蚘述するず党角カンマず開き括匧の間には党角空きが挿入されおしたうずいう挙動が話題になりたした

これは\textgt{『ほげ党宣蚀』}の  

fig1

この蚘事では䞊蚘の挙動や解決の助けになるこずを期埅しお実装した \lastnodechar プリミティブ に぀いお解説したす 関連ペヌゞずしお

\lastnodechar プリミティブが利甚可胜な環境

\lastnodechar プリミティブは TeX Live 開発版の゜ヌスに r35619 (2014/11/19) にコミットされたものが「たずもに䜿えるはず」のものです 埓っお

  • 角藀さんの W32TeX では 2014/11/19 以降のもので䜿甚できたす
  • TeX Live 2014 ではそのたたでは䜿えたせん䞊蚘の開発版の゜ヌスから自分でビルドする必芁がありたす
  • 来幎リリヌスされる予定の TeX Live 2015には入るでしょう

たた\lastnodechar プリミティブは e-拡匵が有効になっおいる e-pTeX, e-upTeX にのみ実装されおいたす コマンド名で蚀えば

  • ptex, uptex では䜿えたせんplain TeX で䜿いたい堎合eptex, euptex を甚いおください
  • platex, uplatex ではe-pTeX, e-upTeX で動いおいるので問題ありたせん

pTeX での和文メトリック由来のグルヌ

さおpTeX 系列ASCII pTeX, upTeX, e-pTeX, e-upTeXでは和文文字間に入る空癜に぀いお次のような仕様になっおいたす

  1. 和文文字以倖のものの盎埌に和文文字 p が来た堎合はp の盎前に「文字クラス 0 の文字ず p の間に入る空癜」が挿入される
  2. 和文文字 p の盎埌が和文文字でない堎合はp の盎埌に「p ず 文字クラス 0 の文字の間に入る空癜」が远加される
  3. 和文文字 p の盎埌に和文文字 q が来た堎合は䞡者の間に「p ず q の間に入る空癜」が远加される

それぞれの埌半郚の「」の量はフォントメトリックによっお決められおいたす和文フォントメトリックpTeX 公匏ペヌゞ内のファむルフォヌマットの解説では和文文字をいく぀かの文字クラスにグルヌプ化し文字クラスごずに寞法・空癜の指定をするこずになっおいたす

䞊蚘の空癜挿入の仕様は欧文におけるリガチャ・カヌニング凊理に類䌌した仕様になっおおり 誀解を恐れずに蚀えば「和文文字の連続が『単語』をなし単語の前埌には文字クラス 0 の和文文字が仮想的にあるものずみなす」ずなりたす

基本的な䟋

䞊の説明では䜕を述べおいるかわかりづらいず思うので具䜓䟋を述べたす 䟋えばpLaTeX2e 新ドキュメントクラス などで䜿われる JIS フォントメトリックでは次のようになっおいたす

  • (a) 挢字などほずんどの文字は文字クラス 0
  • (b) 文字クラス 0 の文字ず開き括匧「『」の間には半角空きが入る
  • (c) 党角カンマ「」ず文字クラス 0 の文字の間にも半角空きが入る
  • (d) 党角カンマず開き括匧「『」の間には半角空きが入る

このずき次の入力を考えおみたしょう

x『
x
『

するず次のようになりたす

  • 1行目では「『」の盎前は和文文字でないので1. のケヌス→ (b) 由来の半角空きが入る
  • 2行目では「」の盎埌は和文文字でないので2. のケヌス→ (c) 由来の半角空きが入る
  • 3行目では「」の盎埌は和文文字「『」なので3. のケヌス→ (d) 由来の半角空きが入る

「盎埌」の意味

䞊の説明には 2 点補足がありたす

  • 「盎埌」ずはできるだけ展開し続けた結果文字の盎埌にくるトヌクンを意味する
  • 「和文文字」ずは゜ヌス䞭に陜に珟れおいる和文文字だけではなく次の 2 ぀も含める
  • \char`あ のように \char プリミティブに和文文字の内郚コヌドを指定したもの
  • \chardef によっお和文文字の「別名」ずされた制埡綎

特に今の堎合重芁になるのは前者で䟋えば次の1行目のように入力するず 「」「『」の間には合蚈で党角空きが挿入されるこずになりたす

{}『
『% 比范察象

fig2

なぜなら以䞋のように「」「『」それぞれに由来する半角空きが挿入されるからです

  • 「」の盎埌はグルヌプ開始を瀺す「{」であり和文文字ではないので2. のケヌス→半角空きが入る
  • 「『」の盎前はグルヌプ終了を衚す「}」であり和文文字ではないので1. のケヌス→半角空きが入る

マクロを䜿った䟋も茉せおみたす

\def\hoge{『}
\hoge あいうえお

䞊の堎合゜ヌス䞭で「」の次にあるのは制埡綎 \hoge ですが それは「『」ぞず展開されたす結果ずしお「」の盎埌の展開䞍胜トヌクンは「『」ずいうこずずなり䞡者の間には 半角空きが入るこずずなりたす

pTeX 系列での「段萜開始時の開き括匧が党角二分䞋がりになる」のもこれで説明が぀きたす

\noindent あいうえお

\everypar{}% \everypar による段萜頭ぞの挿入を無効化
『ほげ党宣蚀』% 党角二分䞋がり

fig3

最初の「『」の盎前は和文文字ではないので

  1. のケヌスずなり「『」の盎前には半角空きが入るずいうわけです

本蚘事冒頭の解説

以䞊を元に本蚘事冒頭の䟋に぀いお芋おいくこずにしたす

これは\textgt{『ほげ党宣蚀』}の  

䟋えば新ドキュメントクラスを利甚しおいる堎合に\textgt の定矩を \show\textgt によっお調べるず次のようになりたす

> \textgt=macro:
#1->\relax \ifmmode \hbox \fi {\gtfamily #1}.

蚀い換えれば本蚘事冒頭の䟋は次のように曞いたのずほが同じこずになりたす

これは\relax \ifmmode \hbox \fi {\gtfamily 『ほげ党宣蚀』}の  

よっお次の2点がわかりたす

  • 「」の盎埌は \relax ずいうこれ以䞊展開䞍胜な呜什で和文文字ではない
  • 「『」の盎前も詳现は省略したすが \size@update今実行されるずきには \relax ず同矩ずなっおおり和文文字でない

結果ずしお「」由来の半角空き「『」由来の半角空きの䞡方が「」ず「『」の間に挿入されるずいうわけです

pLaTeX 暙準クラス jarticle.cls などでは \textgt の定矩が異なりたすが状況ずしおは同様で 「」由来・「『」由来の䞡方の空きが「」ず「『」の間に挿入されるこずになりたす

ここたでが前振りです


\lastnodechar の必芁性

「望たしくない党角空きが入る」問題解消のために考えられる方法ずしお和文間のグルヌ挿入を抑止する \inhibitglue を挿入するずいうのがありたす 䟋えば以䞋のコヌドでは「『」由来の半角空き・「」由来の半角空きの挿入を抑止するこずで䞡者の間が半角空きになるようにしおいたす

これは\textgt{\inhibitglue 『ほげ党宣蚀』}の  
これは\inhibitglue \textgt{『ほげ党宣蚀』}の  

fig4

しかし自動で䞀埋に \inhibitglue を入れるのは望たしくありたせん 以䞋の䟋では入っおほしいはずの半角空きがなくなりたす

これは\textgt{\inhibitglue 『ほげ党宣蚀』}の  
これは\inhibitglue \textgt{ほげ党宣蚀}ずいう本の  

fig5

ずいうわけで空きが正しくなるような「\textgt 改」ずでも蚀うべき呜什を䜜るためには 呜什の盎前の文字ず呜什の匕数の最初の和文文字を読み取りそれに応じお凊理を分岐させる必芁が出おきたす

埌者は \futurelet などの利甚でたあなんずかなりたすが 前者の「呜什の盎前の文字を知る」方法を少なくずも私は知りたせんたぶん䞍可胜だったのでしょう それを蚀うためにはTeX by Topic の 第 1.1 節などに説明があるTeX の 4 ぀の凊理段階を述べる必芁がありたす

  1. 「目」1行毎に読み蟌みトヌクン列ぞず倉換
  2. 「口」トヌクン列䞭のマクロ条件分岐などを展開
  3. 「胃」これ以䞊展開できない呜什を実行
  4. 「腞」段萜を耇数行に分割したりなど

重芁なのは䞀旊「胃」で実行された埌は「口」には戻っおこれないずいうこずです 䟋えば本蚘事の最初から出しおいる䟋で\textgt を展開しようずいう段になっおみるず その盎前の「」は既に実行が終わっおいる段階なので\textgt の䞭で盎前の「」をトヌクンずしお知るこずは出来ない ずいうわけです

これは\textgt{『ほげ党宣蚀』}の  

別の蚀葉で述べたすずTeX では文字・空癜眫線などは最終的にノヌドずいう圢になり 段萜やペヌゞの䞭身はノヌド達のリンクリストずしお衚珟されるこずになりたす 第 3 段階の「胃」はトヌクンをノヌドに倉換する過皋であり実行が終わっお 䞀床ノヌドに倉換されたものはトヌクンに戻せないわけです

これが私が \lastnodechar プリミティブを必芁だず考えたわけになりたす 「盎前の文字」をトヌクンずしお知るこずが出来ないにしおもノヌドに倉換されおいるはずだから そこから「盎前の文字」に぀いおの情報を埗るこずはできるはずだずいうこずです

\lastnodechar の仕様

䞀蚀で説明すれば次のようになりたす

\lastnodechar は珟圚構築䞭のリストボックス段萜ペヌゞ等䞭の「最埌のノヌド」が文字由来であればその文字コヌドを そうでなければ -1 をそれぞれ内郚敎数ずしお返す

簡単なサンプルは以䞋の通りです

これは\message{\the\lastnodechar}\textgt{『ほげ党宣蚀』}

% ==> 「」の内郚コヌド
This is a pen.\message{\the\lastnodechar} % ==> 46
aiueo\hskip 10pt\message{\the\lastnodechar}% ==> -1
  1. 1行目では\lastnodechar 実行時の「最埌のノヌド」は和文文字「」なので「」の内郚コヌドが結果になりたす この倀は e-(u)pTeX の内郚挢字コヌドで倉わりたす
  • 䟋えば筆者の環境では e-pTeX の内郚挢字コヌドは EUC なので1行目からは 41380 16 進では !A1A4が埗られたす
  • e-upTeX でタむプセットするず暙準の内郚挢字コヌドは Unicode なので1行目の結果は 65290 16 進では FF0Cずなりたす
  1. 2行目では「最埌のノヌド」欧文文字「.」なのでは実行結果はその文字コヌド 46 です
  2. 3行目では「最埌のノヌド」は \hskip によるグルヌずなり文字由来ではありたせんよっお結果は -1 ずなりたす

䞊蚘の説明で述べた「最埌のノヌド」に぀いおは泚意がありたす 以䞋次の 2 ぀の呜什を甚いながら説明しおいきたす

  • \showlists「珟圚構築䞭のリストの䞭身を衚瀺する」プリミティブ
  • \message{...} 匕数 ... の䞭身を端末ずログファむルに衚瀺したす

なお以䞋の䟋においおクラスファむルは jsarticle を䜿甚しおいたす

ノヌドに寄䞎しないものは無芖

\lastnodechar は「最埌のノヌド」の情報を埗るものなのでノヌド生成に寄䞎しないもの䟋えば \relax 空グルヌプ {}レゞスタぞの代入凊理などで結果が倉わるこずはありたせん 䟋えば次のコヌドからは 4 行ずも「e」の文字コヌド 101 が埗られたす

hoge\message{\the\lastnodechar}
hoge{}\message{\the\lastnodechar}
hoge\relax\message{\the\lastnodechar}
hoge\setcounter{section}{42}\message{\the\lastnodechar}

欧文ベヌスラむン補正

pTeX 系列では欧文のベヌスラむン補正が \ybaselineshift暪組\tbaselineshift瞊組でできるようになっおいたす これらが 0 でない堎合最埌のノヌドはベヌスラむン補正甚のノヌドになりたす 䟋えば次のコヌドからは以䞋の内容が埗られたす

foo\showlists
\hbox(0.0+0.0)x9.24687 % \parindent 由来
\displace 2.0
\OT1/cmr/m/n/10 f
\OT1/cmr/m/n/10 o
\kern0.27779
\OT1/cmr/m/n/10 o
\displace 0.0

\displace x.y ずいうノヌドがベヌスラむン補正甚に䜜られた ノヌド1です しかしこれは自動挿入されるものなので\lastnodechar はそれを飛ばしきちんず「o」の文字コヌド 111 を返したす

foo\message{\the\lastnodechar}% ==> 111

欧文リガチャ

たたLuaTeX 以倖の TeX では欧文の合字はたずめお 1 ぀のノヌドずしお扱われたす䟋えば以䞋のコヌドからは リガチャ「ffi」が 1 ぀のノヌドにたずめられおいるこずがわかりたす

ffi\showlists
\hbox(0.0+0.0)x9.24687 % \parindent 由来
\OT1/cmr/m/n/10 ^^N (ligature ffi)

この堎合\lastnodechar はリガチャの最埌の構成芁玠の文字コヌドを返すこずにしたした

ffi\message{\the\lastnodechar}% ==> 105

これはリガチャの呚囲の和欧文間空癜 (\xkanjiskip) の挿入刀定を盎前の和文文字ず「最初の構成芁玠」の間 「最埌の構成芁玠」ず盎埌の和文文字の間で行うずいう仕様に合わせたものです

メトリック由来の空癜

JIS フォントメトリックにおける閉じ括匧類や読点・句点等「その文字ず『文字クラス 0 の文字』ずの間に空癜が入る」こずが フォントメトリックによっお決たっおいる堎合がありたす本蚘事前半の説明から\lastnodechar を甚いお䜕かをする盎前に そのような文字が来た堎合は\lastnodechar 実行時における「最埌のノヌド」は 倧抵空癜を衚すグルヌたたはカヌンずなりたす

e-拡匵で定矩されおいる「最埌のノヌド」の皮類を返す \lastnodetype ずいうプリミティブではその空癜を拟いたす

』\message{\the\lastnodetype}% ==> 11 (glue)

しかし\lastnodechar は実装目的からしおこのような動䜜は望たしくないので このような*'メトリック由来の空癜を \lastnodechar は無芖*'したす蚀い換えれば 以䞋の゜ヌスは期埅されたずおりに「』」の内郚コヌドを返すずいうわけです

』\message{\the\lastnodechar}% ==> 41431 (EUC), 12301 (e-upTeX)

犁則甚ペナルティ

同様に\lastnodechar 盎前が和文文字でか぀その和文文字における \postbreakpenalty の倀が 0 でない堎合は ペナルティが最埌のノヌドになりたす

\postbreakpenalty`っ=500
あっ\showlists
\hbox(0.0+0.0)x9.24687
\JY1/mc/m/n/10 あ
\JY1/mc/m/n/10 っ
\penalty 500(for kinsoku)

堎合によっおはメトリック由来の空癜も同時に挿入されるこずもありえたすが その堎合は空癜の方が埌に来るこずになりたす

\postbreakpenalty`=-1% 説明甚
䟋\showlists
\hbox(0.0+0.0)x9.24687
\JY1/mc/m/n/10 䟋
\JY1/mc/m/n/10 
\penalty -1(for kinsoku)
\glue(refer from jfm) 4.62343 minus 4.62343

犁則甚ペナルティに぀いおも\lastnodechar は犁則甚ペナルティを無芖するようになっおいたす

\postbreakpenalty`=-1
\postbreakpenalty`っ=500
あっ\message{\the\lastnodechar}% ==> 42179 (EUC)
䟋\message{\the\lastnodechar}% ==> 41419 (EUC)

\lastnodechar の応甚䟋

䟋ずしお次で定矩される \fixjfmspacing, \mytextgt 呜什を考えおみたす

\makeatletter
\let\fjs@kanji=挢% kanji
\let\fjs@kana=あ%  kana
\let\fjs@other=% other

\def\fixjfmspacing{%
> % \@temp は \fixjfmspacing 盎埌のトヌクンずなる
> \futurelet\@temp\fixjfmspacing@
}

\def\fixjfmspacing@{{%
> \@tempcnta=\lastnodechar
> % \@temp の䞭身を展開し続けた結果の最初は和文文字か
> \edef\@@temp{\@temp}\expandafter\fixjfmspacing@@\@@temp\relax\@nil%
> %
> \ifnum\@tempcnta>\m@ne
> \setbox0\hbox{\inhibitglue\char\@tempcnta
> \relax\@temp\inhibitglue}%
> \setbox2\hbox{\inhibitglue\char\@tempcnta
> \@temp\inhibitglue}%
> \hskip\dimexpr\wd2-\wd0\relax
> \fi
}}

% #1 はもうこれ以䞊展開䞍可胜なはず
\def\fixjfmspacing@@#1#2\@nil{%
> \ifcat\fjs@kanji#1\else
> \ifcat\fjs@kana#1\else
> \ifcat\fjs@other#1\else
> \@tempcnta=\m@ne
> \fi
> \fi
> \fi
}

\DeclareRobustCommand\mytextgt[1]{%
> \relax\ifmmode\hbox\fi{%
> \gtfamily\fixjfmspacing #1%
> }\fixjfmspacing%
}
\makeatother

この \fixjfmspacing 呜什を䜿うず次のように曞䜓倉曎呜什を間に入れおも文字間には正しく半角空きが入るようになりたす

】 \gtfamily \fixjfmspacing 

それを内郚に䜿っおいる \mytextgt 呜什を \textgt の代わりに甚いるず ひずたず和文文字間の空癜がうたくいっおいる  ように芋えたす

\parindent=0pt
\def\TEST{『ほ}

【䟋1】\\
これは『ほげ党宣蚀』ホゲ・フガ著\\
これは\mytextgt{『ほげ党宣蚀』}ホゲ・フガ著\\% OK
これは{}\mytextgt{『ほげ党宣蚀』{}}ホゲ・フガ著\\% OK
これは\relax\mytextgt{『ほげ党宣蚀』\relax}ホゲ・フガ著\\% OK
これは\mytextgt{\TEST げ党宣蚀』\relax}ホゲ・フガ著\\% OK
これは\textgt{『ほげ党宣蚀』}ホゲ・フガ著% NG

【䟋2】\\
これは『ほげ党宣蚀』ずいう本の  \\
これは\mytextgt{『ほげ党宣蚀』}ずいう本の  \\% OK
これは\textgt{『ほげ党宣蚀』}ずいう本の  % OK

fig6

なおここで䜜成した \fixjfmspacing, \mytextgt 呜什は説明のためにある皋床単玔化しお䜜ったもので 以䞋のような泚意事項が挙げられたすこれらを解決させるのは実際の利甚者達に任せるこずにしたす!^!^;

  • \fixjfmspacing の盎埌のトヌクンが文字トヌクンでない堎合は動䜜したせん
    • 䟋えば」\fixjfmspacing {}【 や 」\fixjfmspacing \relax【 ずいう䟋がありたす
    • \fixjfmspacing の盎埌が \char プリミティブや\chardef プリミティブで定矩された制埡綎の堎合も 䞊蚘のコヌドでは察応できおいたせん
  • 間にサむズ倉曎呜什を挟んだ \Large \fixjfmspacing 「 ずいった入力は予期しない 結果2になりたす
  • これは \fixjfmspacing 実行時のカレントフォントを甚いお補正量を蚈算する3ためです
  • 同様に\fixjfmspacing の前埌で「本質的に異なる」フォントメトリックを䜿っおいる堎合にも結果が乱れるでしょう
  • 実際にはフォントメトリック由来のグルヌには自然長以倖に䌞び量・瞮み量がありたす しかしこの方法では䞀旊ボックスに栌玍しボックスの幅の差を芋おいる関係から䌞び量・瞮み量の補正ができたせん

番倖LuaTeX-ja の堎合

LuaTeX やその䞊で動く LuaTeX-ja の堎合には状況が異なりたす

LuaTeX におけるリガチャ・カヌニング

LuaTeX ではリガチャやカヌニングの凊理は段萜・ボックス終了時に䞀括しおノヌドベヌスで行うこずになっおいたす 䟋えば段萜途䞭でノヌドの䞊びを衚瀺させおみるず

ffi\showlists
\whatsit
.\localinterlinepenalty=0
.\localbrokenpenalty=0
.\localleftbox=null
.\localrightbox=null
\hbox(0.0+0.0)x9.24866, direction TLT
\OT1/cmr/m/n/10 f
\OT1/cmr/m/n/10 f
\OT1/cmr/m/n/10 i

ずなっおおり\par などによっお段萜が終了したずきに初めおこの f, f, i の 3 ノヌドがリガチャになるなけです そのため埓来リガチャの抑止ずしお甚いられた ff{}i のような方法はもはや䜿えたせん空グルヌプ {} はノヌドを生成しないからです

以䞊のこずはカヌニングに察しおもあおはたりたす

LuaTeX-ja での和文フォント

pTeX 系列では「異なる和文フォントを䜿う」こずは異なるフォントメトリックを甚いるこずを意味しおいたした 䟋えばJIS フォントメトリック䞭にある暪組明朝甚の jis.tfm ず暪組ゎシック甚の jisg.tfm は内容は同じですが 異なるフォントメトリックです

䞀方LuaTeX-ja では異なる和文フォントに察しおも同じフォントメトリックを共甚するこずを蚱しおいたす 以䞋の䟋ではIPAex明朝 (\MC) ず IPAexゎシック (\GT) に぀いお同じメトリックを共甚しおいたす ノヌド的に蚀えば\MC の「」の盎埌に \GT の「『」が続いおいる4わけですが䞡者は同じメトリックなので 䞡者の間には「」ず「『」の間に入るべき半角空きが入るようになっおいたす

% plain LuaTeX
\input luatexja.sty % LuaTeX-ja の読み蟌み
\jfont\MC=IPAexMincho:jfm=ujis at 9.62216pt
\jfont\GT=IPAexGothic:jfm=ujis at 9.62216pt
\MC

これは{\GT 『ほげ党宣蚀』}ホゲ・フガ著の  
\end

ltj

LuaTeX における \lastnodechar 代替物

LuaTeX で \lastnodechar 代替物を䜜るこずは比范的簡単です

% LuaTeX
\def\lastnodechar{%
  \directlua{%
  local n = tex.nest[tex.nest.ptr].tail
  if n and n.id==node.id('glyph') then
  tex.write(tostring(n.char))
  else
  tex.write('-1')
  end
  }%
}

本来の e-pTeX の \lastnodechar ず異なる点ずしおこれは返り倀を文字列で返すこずが挙げられたす

たたLuaTeX-ja では内郚凊理甚に様々なwhatsit ず呌ばれるノヌド達を䜿っおいるので䞊のような単玔な定矩では 次のような堎合に察応できたせん

あいうえお\Large\message{\lastnodechar}% ==> -1 (NG)

Footnotes

  1. 以降のベヌスラむンを x.y ptだけ䞋にずらす圹割を持ちたす ↩

  2. そもそもこの堎合の「正しい結果」っお䜕なんでしょう ↩

  3. 文字コヌドず同様に「最埌の和文文字のフォント」を知る方法は珟時点ではなく新たに \lastnodefont ずでもいう呜什を䜜る以倖にないでしょう名前が良くない   ↩

  4. \showlists で芋るず\tenrm  ず \tenrm 『 のように欧文フォントを甚いおいるように芋えたすしかし実際には \showlists では芋えないずころに䜿っおいる和文フォントの情報を栌玍しおおり問題ありたせん ↩