Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[upTeX+dvipdfmx] 異体字セレクタ、Unicode合成文字 #46

Open
t-tk opened this issue Jan 28, 2018 · 27 comments
Open

[upTeX+dvipdfmx] 異体字セレクタ、Unicode合成文字 #46

t-tk opened this issue Jan 28, 2018 · 27 comments

Comments

@t-tk
Copy link
Collaborator

t-tk commented Jan 28, 2018

upTeX+dvipdfmx で異体字セレクタ等を使えるようにしたいと考えています。
しかし知らないこと不勉強なことも多く、どのくらい開発のハードルが高いのかも分かっていません。議論にお付き合いいただけると助かります。

  • 目標: Unicodeで複数のコードポイントの並びで表される文字をUTF-8入力、upTeXで処理しpdf出力できるようにする。対象の文字は以下。
    1. 漢字の異体字セレクタ(IVS, Ideographic Variation Sequence/Selector)
    2. 絵文字などに使われるStandardized Variants
    3. 合成文字で表される仮名 (かきくけこ+半濁音、アイヌ語など)
  • 実装案1: dvipdfmxを拡張するが、ptexenc, upTeXエンジンは拡張しない。
    • upTeXエンジンでは、VS(Variation Selector)の文字幅を0とし、VSの直前で行分割やグルーなどが入らないようにすればdviに書き込まれる情報は期待通りになる。
    • dvipdfmxでは、dviからVSを含む文字並びを読み込んだら異体字グリフを選択する
  • 実装案2: ptexenc, upTeXエンジン, dvipdfmxを拡張する。
    • ptexencでVSや合成文字を読み込んだらupTeX専用内部コードに変換しupTeXエンジンに渡す。upTeXエンジン内は1文字として扱い、dviに書き込む際Unicodeの文字並びに戻す。
    • dvipdfmxでは、dviからVSや合成文字を含む文字並びを読み込んだら適切なグリフを選択する

実装案1,2のいずれにせよ、dvipdfmxの拡張は要りそうです。dvips+(distiller or ghostscript)の場合は分かりません。
実装案1は、最近実現したばかりのJFM拡張を利用します。IVSや仮名の濁音までは対応できそうです。反面、携帯電話の絵文字の一部"U+0023 U+20E3"のようにASCIIから始まるような場合は処理が困難です。
実装案2は、upTeXの既存の方法(入力はptexencで頑張る。upTeXエンジンはフォントの情報を持たずCJKのグリフ1個として処理する。dviwareはフォントに依存する処理を頑張る)と整合します。実装としては筋がよいと感じ、最終的にはここまで持って行きたいです。
とりあえず、実装案1でdvipdfmxの拡張をして、次に実装案2を進めるという2段階の開発でも良いかなと思います。
実装案2では、IVSのテーブルunicode.orgのIVDは巨大(2017-12-12現在29303字)なので、ptexencの中にハードコードすることは気が進まないなどの課題もあります。

Variation Selectorを扱うためには、'cmap' Subtable Format 14 を読み出せばよいそうです。
参考: appleのところ, microsoftのところ
現状のdvipdfmxには実装されていないようです。
内部のことは全然知らないのですが、tt_cmap.c に'cmap' Subtable Format 4, 12 を扱う部分があるのでそこを拡張すれば良いと想像しています。
しかし、dvipdfmxの中身はまるで判らないので私にとっては高い壁です。

何か、ご意見やアドバイスなどがあればお願いします。

@aminophen
Copy link
Member

実装案1は、最近実現したばかりのJFM拡張を利用します。IVSや仮名の濁音までは対応できそうです。反面、携帯電話の絵文字の一部"U+0023 U+20E3"のようにASCIIから始まるような場合は処理が困難です。

簡単なコメントだけですが,検討なさっている通り,どこまでやるかで実装方法が変わりそうですね。

まず,絵文字,というのがどこまでなのかよくわかっていません。カラー絵文字を PDF に埋め込めるようにしたいという話なのでしょうか?

補足:カラー絵文字をサポートした既存エンジンとして pTeX-ng があり,これは upTeX に極めて似ているため,一定の参考にはなりそうです。

# 私の理解では,pTeX-ng は「upTeX + e-TeX の改変版で作った特殊な DVI」を「ライブラリ様式に再構成された dvipdfmx = libpdf」で PDF を直接出力するように見せているというモノ。

ただ,この場合は dvipdfmx 部分が主に freetype2 や libotf に新たに依存しています。ライブラリを増やして良ければ,案外簡単に実現できるのかもしれませんが,よくわかりません。

@t-tk
Copy link
Collaborator Author

t-tk commented Jan 28, 2018

私がupTeXの基本的な機能として拡張したいのは、「絵文字をグリフとして持っているフォントを使ってフォントとしてpdfに埋め込む」場合です。図形として埋め込むことは関心の外にあります。
Unicodeの絵文字は最近Unicodeの新版が出る度に増殖しているみたいで色々ですが、文字集合の候補として気になるのは

  1. 日本の携帯電話由来の絵文字 Ref.Wikipedia
  2. Macが絵文字に使っているEVS Ref.NAOIさんのところ
  3. その他 Ref.Wikipedia

イメージとしては、絵文字をグリフに持ちVS等を使って呼び出せるように実装されているフォントを用いて、そのフォントのUTF-8のシーケンスに合致したグリフをpdfに埋め込むようなことです。
pTeX-ng のことはよく判りませんが少し違うかもしれません。

Unicode のシーケンスで分類すると

  1. Unicodeの一つのコードポイントで表せるもの
  2. 「親文字」+VS
  3. 国旗: Regional indicator symbolsのコードポイントが2個並んだもの
  4. その他??

1は既に既存のupTeX環境で使えるはずです。2は漢字のVS=IVS と同様のやり方で対応出来るような気がします。
3は2とほぼ似ているような気がしますが判りません。4は存在するのかどうか判りません。
優先順位としては、まずはVS対応が先決と思っています。

@t-tk
Copy link
Collaborator Author

t-tk commented Feb 2, 2018

お勉強、備忘録を兼ねて書きます。
NAOIさんのところによると、JIS X 0213に含まれる「Unicodeでは文字合成で表す仮名」を合成済みグリフで出すには、OpenType の 'liga' フィーチャーまたは 'ccmp' フィーチャーを使うそうです。
最初に書いた目標を満たすのは、フォントの機能別にざっくりいうと

  1. Variation Selector, 'cmap' Subtable Format 14
  2. OpenType, GSUBの 'liga' フィーチャーまたは 'ccmp' フィーチャー

と纏められると思います。可変長のコードポイントの配列で表される塊を1つのグリフとして扱いたいという点では共通なので、その操作は共通で処理したい。
また、dviのset命令の列でその処理を実現しようとすると、dviwareが読み込んだ後に文字の切り出しや文字幅の取得等を適切に行うことが必要となり、煩雑な処理にならざるを得ない。
そうすると、更に別案として、XeTeXのやり方を少し真似した以下の案に傾いています。

  • 実装案3: ptexenc, upTeXエンジン, dvipdfmxを拡張する。upTeXとdvipdfmxの間のデータ渡しには拡張dvi命令を使う
    • ptexencでVSや合成文字を読み込んだらupTeX専用内部コードに変換しupTeXエンジンに渡す。
    • upTeXエンジン内は1文字として扱い、dviに書き込む際、拡張dvi命令でUnicodeの文字並びの塊をまとめて書き出す。
    • dvipdfmxでは、dviから拡張dvi命令を読み込んだらVS,GSUB等で適切なグリフを選択する

改造量が多くdviwareの対応のための開発が重くなり、結果としてdvipdfmx以外では新仕様に追従困難になるかもしれない、拡張dvi命令を知らない従来のdviwareでは拡張dvi命令を含む新dviを丸っきり処理できなくなる、など短所もあります。
しかし、機能の分かりやすさやメンテナンス性は実装案3が一番よく、今はこれで行こうと思っています。
改造量が多く時間が掛かりそうです。
TeX Live 2018 には到底間に合わないことが分かったので、一旦止めて少しづつやろうかと考えています。

@t-tk
Copy link
Collaborator Author

t-tk commented Feb 13, 2018

IVSのテーブルunicode.orgのIVDは巨大(2017-12-12現在29303字)なので、ptexencの中にハードコードすることは気が進まないなどの課題もあります。

IVDのテーブルを調べてみたところ、
upTeX の内部コード(24bit)はまだ沢山領域が余っているので、
文字コード+異体字セレクタ情報を持つ内部コードとして比較的単純な割り当てでも充分まかなえることが分かりました。

異体字セレクタの種類が最多になっている文字は
U+FFFF以下のBMPでは、U+9089 (邉) で U+E0100..E011F の32種類、
U+20000..2FFFF のSIPでは、U+20525 で U+E0100..E0108 の9種類でした。
なので、upTeXの内部コードとして例えば
U+9089の IVS に 0x809089 〜 0x9F9089 を
U+20525の IVS に 0xC00525 〜 0xC80525 を
割り当てればまかなえます。
IVS全体としては、例えば BMP の IVS を 0x800000 〜 0x9FFFFF に、SIP の IVS を 0xC00000 〜 0xC8FFFF に割り当てれば足ります。
今後IVDの大幅な拡張は無いと予想する上、将来の拡張を見越しても充分まかなえそうです。
変換式も単純になりptexencの改造も少々で済みそうです。
この方針で行こうと思います。

@t-tk
Copy link
Collaborator Author

t-tk commented Sep 3, 2023

2022年9月に Ideographic Variation Database の新版が出ていました。
http://www.unicode.org/ivd/data/2022-09-13/

31350 E0100; Adobe-Japan1; CID+19130
が追加されています。

なので計画を若干修正。
異体字セレクタの種類が最多になっている文字は
U+FFFF以下のBMPでは、U+9089 (邉) で U+E0100..E011F の32種類、
U+20000..2FFFF のSIPでは、U+20525 で U+E0100..E0108 の9種類、
U+30000..3FFFF のTIPでは、U+31350 で U+E0100 の1種類、
なので、upTeXの内部コードとして例えば
U+9089の IVS に 0x809089 〜 0x9F9089 を
U+20525の IVS に 0xC00525 〜 0xC80525 を
U+31350の IVS に 0xE01350 を
割り当てればまかなえます。

@h-kitagawa
Copy link
Member

npTeX (#150) も考えたときに,どこまで「0x110000 以上の内部コード」を許容するかについて悩んでいます.#150 で h20y6m さんが

個人的には 0x110000 以上の 文字トークン は無いほうがいいと思います。
これが存在すると expl3 等に特別扱いしてもらう必要が出てきてしまいます。
(例えば文字列を UTF-16 の HEX 表記(PDF 文字列)に変換するとき 0x110000 以上の文字コードのトークンがあると困ったことになる。)

と書かれているように,仮に「0x110000 以上の文字トークン」を許さないことにすると……

  • \@tfor でトークンごとにループを回すとき,IVS が基底文字と異体字セレクタに分離してしまう.
    • XeTeX/LuaTeX において,l3text での対応状況はどうか?
  • OTF パッケージのため,\(k)char"113041 という記述は引き続き許される必要がある.すると,\(k)char"809089 \(k)chardef\cs="809089 も許容しないという理由はないだろう.
  • 「文字列化→トークン化」が絡む \kansujichar1=...\Uchar の引数に許すか?
    • IVS は,「string pool には複数の Unicode 文字として格納→再トークン化のときに複数の文字トークンが生まれる」として許容してしまう?
    • OTF パッケージ用の 0x110000-- 領域については,そういう逃げ道がない.
  • (npTeX) \lccode x=y 等はどうするか?
    • x に 0x110000 以上を許すのは,実装の手間に見合わないと思われる(格納場所は sparse tree にしてしまえばいいが).
    • y に 0x110000 以上を許したら,「0x110000 以上の文字トークン」が生成できてしまう.

t-tk さんの元コメント に便乗して,別の案を挙げてみます.

  • 実装案 3
    • 「入力を buffer[] に渡し,文字トークン化する」まではできるだけいじらない.
      • upTeX では,絵文字部分を内部コードに変換するのはしょうがなさそう.
      • npTeX(XeTeX ベースにするとした場合)では,絵文字は欧文扱いとして投げたい.
    • 欧文フォントの合字処理のように,水平モードで「基底文字の文字トークン + 異体字セレクタの文字トークン」と続けて処理した場合,「文字コード」部分が内部コード である 1 つの和文文字ノードしか作らない.
    • DVI には内部コードをそのまま set3"809089で出力し,dvipdfmx で頑張る

@aminophen
Copy link
Member

aminophen commented Sep 11, 2023

0x110000 以上の文字トークン

現状の (e-)upTeX は 0x110000 以上は「文字ノードとしては存在するが,文字トークンとしては存在しない」で一貫しています(\kansujichar や \Uchar はトークン生成時に mod 0x110000 で落とす)。OTF パッケージ対策は文字ノードさえ出来れば良く,これで十分だったのでしょう。異体字セレクタだとそうはいかない,と言われると確かに…。

%#!euptex
% ノード
\chardef\X="112603 \show\X % => \char"112603
% トークン
\kansujichar1="112603 \showthe\kansujichar1 % => 9731 = "2603
\expandafter\show\kansuji1 % => kanji character ☃
\expandafter\show\Uchar"442603 % => kanji character ☃
\end

メモ: mod 0x110000 の処理は toUCS() あるいは toDVI() → 内部 Unicode の時 UPTEXtoUCS% UCS_MAX による。

@h20y6m
Copy link
Collaborator

h20y6m commented Sep 11, 2023

  • \@tfor でトークンごとにループを回すとき,IVS が基底文字と異体字セレクタに分離してしまう.
    • XeTeX/LuaTeX において,l3text での対応状況はどうか?

書記素ごとにループする \text_map_function:nN/\text_map_inline:nn という関数があります。


異体字セレクタの種類が最多になっている文字は
U+FFFF以下のBMPでは、U+9089 (邉) で U+E0100..E011F の32種類、

ここがちょっと気になっていています。
いまデジタル庁が文字情報基盤(MJ)を拡張した MJ+ というのを作っているそうです。(1万文字くらい追加されるらしい)
どういった文字が追加されるのか、Unicodeにも登録されるされるのかよく分かりませんが、
もしIVDに異体字が追加された場合に足りなくなったりしないか心配です。

@t-tk
Copy link
Collaborator Author

t-tk commented Sep 12, 2023

コメントありがとうございます。 MJ+ というのは知りませんでした。
現行の MJ で既に漢字 58,862文字でこれらは既に戸籍統一文字、住民基本台帳ネットワークシステム統一文字を含んでいるとのこと、Unicode の基底文字もしくは IVS に定義されているとのことです。
MJ+ のこれから追加されるであろう文字とは、Unicodeにすでに登録されている文字と同定できず、Unicodeの包摂の範囲に入らず、辞書にも見出せず、読みや意味が不明だが人名など固有名詞に稀に使われている文字の積み残し、というイメージになると思います。なので、「辺邉邊」の異体字が増えるというような増え方は起きにくいように思います。

また、下記のように現計画の延長線上で、 U+0hhhhの ~VS80, U+2hhhhの ~VS64 は賄えると思います。
さらに、現計画の延長から外れた符号化をしてもよいと思います。

絵文字の肌の色とか顔の向きとか、謎かつ予測不可能な進化についていくことは難しそうですが。

◇SVS
40hhhh  U+0hhhh U+0FE00 (VS01)
41hhhh  U+0hhhh U+0FE01 (VS02)
42hhhh  U+0hhhh U+0FE02 (VS03) (現状の最大値)
...
4Ehhhh  U+0hhhh U+0FE0E (VS15) (emoji-sequencesに現れる)
4Fhhhh  U+0hhhh U+0FE0F (VS16) (emoji-sequencesに現れる)

5Ehhhh  U+1hhhh U+0FE0E (VS15) (emoji-sequencesに現れる)
5Fhhhh  U+1hhhh U+0FE0F (VS16) (emoji-sequencesに現れる)

60hhhh  U+2hhhh U+0FE00 (VS01)
61hhhh  U+2hhhh U+0FE01 (VS02) (現状の最大値)
62hhhh  U+2hhhh U+0FE02 (VS03)
...
6Ehhhh  U+2hhhh U+0FE0E (VS15) (emoji-sequencesに現れる)
6Fhhhh  U+2hhhh U+0FE0F (VS16) (emoji-sequencesに現れる)

7xxxxx  未定義

◇IVS
80hhhh  U+0hhhh U+E0100 (VS17)
81hhhh  U+0hhhh U+E0101 (VS18)
82hhhh  U+0hhhh U+E0102 (VS19)
...
9Dhhhh  U+0hhhh U+E011D (VS46)
9Ehhhh  U+0hhhh U+E011E (VS47)
9Fhhhh  U+0hhhh U+E011F (VS48) (現状の最大値) (一応ここまで可能ということにしておくか)

Axxxxx  未定義  (U+0hhhhの ~VS64 の余地あり)
Bxxxxx  未定義  (U+0hhhhの ~VS80 の余地あり)

C0hhhh  U+2hhhh U+E0100 (VS17)
C1hhhh  U+2hhhh U+E0101 (VS18)
C2hhhh  U+2hhhh U+E0102 (VS19)
...
C6hhhh  U+2hhhh U+E0106 (VS23)
C7hhhh  U+2hhhh U+E0107 (VS24)
C8hhhh  U+2hhhh U+E0108 (VS25) U+2xxxxの(現状の最大値)
...
CFhhhh  U+2hhhh U+E010F (VS32) (一応ここまで可能ということにしておくか)

Dxxxxx  未定義  (U+2hhhhの ~VS48 の余地あり)

E0hhhh  U+3hhhh U+E0100 (VS17) (U+3xxxxの現状の最大値)
...
EFhhhh  U+3hhhh U+E010F (VS32) (一応ここまで可能ということにしておくか)

Fxxxxx  未定義

◇註
きっと将来も現れないが
U+0hhhh の VS48超え
U+0hhhh の VS64超え
U+2hhhh の VS32超え
U+3hhhh の SVS
も余地がありそう。

@zr-tex8r
Copy link

少し気になったのですが、VS付の文字について、
「upTeXとnpTeXでエンジン内の実装方法を同じにする」
あるいは
「そのような文字を扱うTeX言語コードの実装がupTeXとnpTeXで共通に使える」
必要はあるのでしょうか。
現在存在する機能(それを扱う既存のTeX言語のソフトウェアが存在する)ではないので、その必要性はそこまで強くないと思います。

@t-tk
Copy link
Collaborator Author

t-tk commented Sep 18, 2023

「そのような文字を扱うTeX言語コードの実装がupTeXとnpTeXで共通に使える」
必要はあるのでしょうか。

私は必要ないと思います。
もし npTeX が XeTeX ベースに進むのでしたら、IVSは XeTeX で既に実現されているはずなのでそれと同様にする方が自然だしメンテナンス性もよくなると思います。

@h20y6m
Copy link
Collaborator

h20y6m commented Jan 8, 2024

#155 で 'cmap' Subtable Format 14 を読んで VS に対応するのをやったのでこちらも少し実験してみています。

  • DVI には内部コードをそのまま set3"809089で出力し,dvipdfmx で頑張る

が VF を作れば疑似的に実験できそうだったので、IVD_Sequences.txtを元に Unicode 私用面に Adobe-Japan1(0xF0000+CID)と Moji_Joho(0x100000+MJ番号)の異体字を 0x800000--0xFFFFFF にマップするVFを生成するスクリプトを作ってみました。

実際にやってみると dvipdfmx:fatal: Invalid char: 9343113 のようなエラーなってしまいます。
dvipdfmx では JFM の最大文字コードは 0x10FFFF 固定にしているためのようです。これを単純に 0xFFFFFF に拡張すれば読めるようにはなりますが、dvipdfmx では文字コードから CHARTYPE を取得するためにルックアップテーブルを使用していて(CHARTYPE が 0 以外を含む JFM の場合)、これのメモリ使用量が約 4.4 MiB から約 64 MiB に増大してしまいます。和文多書体などであっという間に数百 MiB になってしまうので、そのあたりも改良が必要になってきそうです。(tfm.c の jfm_make_charmap

また、実際に VS 付きの文字コードからグリフを検索する方法ですが、VS を使う場合フォント map は CMap として unicode を指定する場合の処理になるのではないかと思います(CIDに変換するわけにはいかないと思うので)。
この場合、最初にフォントの cmap テーブルから UCS4 → CID/GID 変換する CMap を生成して set3 等の DVI 命令の処理ではこの CMap を利用して CID/GID に変換し出力しているようです(つまりフォントから自動的生成した CMap を指定したようなイメージで DVI 命令の処理は普通の CMap を指定したときとほぼ共通のよう)。(tt_cmap.c の otf_load_Unicode_CMap
UCS4 (4バイト)なのでここに Subtable Format 14 から VS 用の変換を 0x400000--0xFFFFFF で入れてやれば割と簡単にできそうではあります。(ただ、Unicode として不正な値を入れるのがちょっとやな感じなので、基底文字+VSの 8バイトのシーケンスにしたい気もする……)
あと、フォントに該当の異体字がなかったときは基底の字体フォールバックしたい気がします。(pdfdev.c の handle_multibyte_string でゴニョゴニョすればできるだろうか?)
dvipdfmx の和文文字出力は DVI (or VF) の文字コードを CMap で PDF に書き込む CID/GID に変換して出力する仕組みのようなので、これにうまく合わせられると改造量が減らせそうではあります。

それから、ToUnicode CMap をちゃんと作るのも必要になります。たぶん cmap Subtable Format 4/12 から逆引きで作っているのですが(たぶん tt_cmap.c の otf_create_ToUnicode_stream)、どのあたりをいじれば Format 14 の VS の情報追加できるのか追いきれていません。
また、ToUnicode CMap に VS 付きで入れるべきなのか(あるいは VS を入れてはいけないのか)もよくわかっていません。


【追記】
とりあえず動いている実験コードを https://github.com/h20y6m/tex-jp-build/tree/dvipdfmx_uvs_experiment_1 に置いておきます。(ただ、この実装だとフォントに異体字がなかったときのフォールバックは無理そうなんですよねぇ……)

@t-tk
Copy link
Collaborator Author

t-tk commented Mar 3, 2024

https://github.com/h20y6m/tex-jp-build/tree/dvipdfmx_uvs_experiment_1 を拝借しつつ、触り始めています。
とりあえず、

dvipdfmx では JFM の最大文字コードは 0x10FFFF 固定にしているためのようです。これを単純に 0xFFFFFF に拡張

この問題ですが、 4ed8031 でよいように思います。
前提として、0x110000 以上の文字トークンは、defaultの全角グリフしか使わない、文字毎に設定したりしない、ということです。
ちょっと気になったのは、OTF パッケージで使っている 0x110000 ~ 0x140000 あたりで半角幅があったりしたのはどうだったか?

@t-tk
Copy link
Collaborator Author

t-tk commented Mar 9, 2024

いろいろ検討した結果、文字コードは以前の構想と少し変えました。以下で確定のつもりです。

内部トークンの文字コード ::
SVS: 0xC00000..0xFFFFFF の3byteで表現
IVS: 0x400000..0xBFFFFF の3byteで表現

DVIの文字コード ::
SVS: 0xC00000..0xFFFFFF の3byte を set3 で出力、内部トークンと同じ符号
IVS: 0x01000000..0x02F3FFFF の4byte を set4 で出力、uptex出力時に符号を変換

SVSは3byteなので JFMのグリフをchartype 0番以外に設定可能。
IVSはJFMで chartype 0番の全角グリフを想定。

DVIの文字コードでは、 0x3FFFF でマスクして得られる下位 18 bit が基底文字の Unicodeスカラー値に等しい。
SVS,IVSの異体字セレクタの部分をそれより上の上位bitで表現している。

◇SVS
内部トークン DVIコード 文字の順に
C0hhhh  ##C0hhhh  U+0hhhh U+0FE00 (VS01)
C4hhhh  ##C4hhhh  U+0hhhh U+0FE01 (VS02)
C8hhhh  ##C8hhhh  U+0hhhh U+0FE02 (VS03) (現状の最大値)
CChhhh  ##CChhhh  U+0hhhh U+0FE03 (VS04)
...
F8hhhh  ##F8hhhh  U+0hhhh U+0FE0E (VS15) (emoji-sequencesに現れる)
FChhhh  ##FChhhh  U+0hhhh U+0FE0F (VS16) (emoji-sequencesに現れる)

C1hhhh  ##C1hhhh  U+1hhhh U+0FE00 (VS01)
C5hhhh  ##C5hhhh  U+1hhhh U+0FE01 (VS02)
C9hhhh  ##C9hhhh  U+1hhhh U+0FE02 (VS03)
...
F9hhhh  ##F9hhhh  U+1hhhh U+0FE0E (VS15) (emoji-sequencesに現れる)
FDhhhh  ##FDhhhh  U+1hhhh U+0FE0F (VS16) (emoji-sequencesに現れる)

C2hhhh  ##C2hhhh  U+2hhhh U+0FE00 (VS01)
C6hhhh  ##C6hhhh  U+2hhhh U+0FE01 (VS02) (現状の最大値)
CAhhhh  ##CAhhhh  U+2hhhh U+0FE02 (VS03)
...
FAhhhh  ##FAhhhh  U+2hhhh U+0FE0E (VS15) (emoji-sequencesに現れる)
FEhhhh  ##FEhhhh  U+2hhhh U+0FE0F (VS16) (emoji-sequencesに現れる)

◇IVS
(VS17..VS32) U+2hhhhの現状最大はVS25, U+3hhhhの現状最大はVS17
(VS33..VS48) U+0hhhhの現状最大はVS48
(VS49..VS64) まだ無い
(VS65..VS80) まだ無い

内部トークン DVIコード 文字の順に
40hhhh  0100hhhh  U+0hhhh U+E0100 (VS17)
41hhhh  0110hhhh  U+0hhhh U+E0101 (VS18)
42hhhh  0120hhhh  U+0hhhh U+E0102 (VS19)
...
5Dhhhh  02d0hhhh  U+0hhhh U+E011D (VS46)
5Ehhhh  02e0hhhh  U+0hhhh U+E011E (VS47)
5Fhhhh  02f0hhhh  U+0hhhh U+E011F (VS48) (U+0xxxxの現状の最大値) (現状ここまで可能)

6xxxxx  未定義  (U+0hhhhの ~VS64 の余地あり)
7xxxxx  未定義  (U+0hhhhの ~VS80 の余地あり)

80hhhh  0102hhhh  U+2hhhh U+E0100 (VS17)
81hhhh  0112hhhh  U+2hhhh U+E0101 (VS18)
82hhhh  0122hhhh  U+2hhhh U+E0102 (VS19)
...
86hhhh  0162hhhh  U+2hhhh U+E0106 (VS23)
87hhhh  0172hhhh  U+2hhhh U+E0107 (VS24)
88hhhh  0182hhhh  U+2hhhh U+E0108 (VS25) (U+2xxxxの現状の最大値)
...
8Fhhhh  01f2hhhh  U+2hhhh U+E010F (VS32) (現状ここまで可能)

9xxxxx  未定義  (U+2hhhhの ~VS48 の余地あり)

A0hhhh  0103hhhh  U+3hhhh U+E0100 (VS17) (U+3xxxxの現状の最大値)
...
AFhhhh  01f3hhhh  U+3hhhh U+E010F (VS32) (現状ここまで可能)

Bxxxxx  未定義

◇註
現状定義されている漢字のSVS,IVSはカバーできている。
きっと将来も現れないが
U+0hhhh の VS48超え
U+0hhhh の VS64超え
U+2hhhh の VS32超え
U+3hhhh の SVS
も余地がありそう。

@t-tk
Copy link
Collaborator Author

t-tk commented Mar 9, 2024

現在の試作品はこちらです。
2cee45a

実際にやってみると dvipdfmx:fatal: Invalid char: 9343113 のようなエラーなってしまいます。

ここは解消しています。

dvipdfmx では JFM の最大文字コードは 0x10FFFF 固定にしているためのようです。これを単純に 0xFFFFFF に拡張すれば読めるようにはなりますが、dvipdfmx では文字コードから CHARTYPE を取得するためにルックアップテーブルを使用していて(CHARTYPE が 0 以外を含む JFM の場合)、これのメモリ使用量が約 4.4 MiB から約 64 MiB に増大してしまいます。

現状のコードは、最大 U+10FFFF までは JFM を参照、それ以上の文字コードは chartype 0番を参照になっていると思います。ルックアップテーブルは巨大化していません。まだまだ節約の余地はあるのは確かです。

また、実際に VS 付きの文字コードからグリフを検索する方法
...
(tt_cmap.c の otf_load_Unicode_CMap) UCS4 (4バイト)なのでここに Subtable Format 14 から VS 用の変換を 0x400000--0xFFFFFF で入れてやれば割と簡単にできそうではあります。(ただ、Unicode として不正な値を入れるのがちょっとやな感じなので、

今回の案では、VSの値 + ユニコードスカラー値の正規の値、という組み合わせの独自の4byteのバイナリーコードで扱うことになります。
「Unicode として不正」とまではいえないコードだと思うので、「やな感じ」は和らぐと思います。

基底文字+VSの 8バイトのシーケンスにしたい気もする……) あと、フォントに該当の異体字がなかったときは基底の字体フォールバックしたい気がします。(pdfdev.c の handle_multibyte_string でゴニョゴニョすればできるだろうか?)

現状のテストではまだフォールバック機能はありませんが、DVIコードから簡単に基底文字が得られるので、フォントの検索結果を見てからフォールバックする機能の実装は容易と思います。

それから、ToUnicode CMap をちゃんと作るのも必要になります。たぶん cmap Subtable Format 4/12 から逆引きで作っているのですが(たぶん tt_cmap.c の otf_create_ToUnicode_stream)、どのあたりをいじれば Format 14 の VS の情報追加できるのか追いきれていません。 また、ToUnicode CMap に VS 付きで入れるべきなのか(あるいは VS を入れてはいけないのか)もよくわかっていません。

この辺はよく分からないのでノーコメントです。

【追記】 とりあえず動いている実験コードを https://github.com/h20y6m/tex-jp-build/tree/dvipdfmx_uvs_experiment_1 に置いておきます。

非常に参考になっています。ありがとうございます。

@t-tk
Copy link
Collaborator Author

t-tk commented Mar 9, 2024

3byte の内部トークンの文字コードから 4byteのDVIコードへの変換は、ptexencの中で、DVI出力の直前に行っています。
dviware 側で基底文字、異体字セレクタを取得する計算は単純になるので、dviware側の実装は容易になると思います。
今まで set3 まで対応できている dviwareを今回の 4byte コードに拡張する改造は容易になるとみています。

現状の試作品は、uptex で \kchar"C09083 のように内部トークンのコードを指定すればとりあえずの入力は出来ています。
今後、uptex側で buffer での UTF-8 の多バイト文字2個(基底文字 + VS) から内部トークンへ変換する仕組みを検討するつもりです。

テストを dvipdfm-x/dvipdfmx-upjf.test に追加しています。Haranoaji, IPAmj明朝 を使ってupLaTeX (\kchar使用)のソースから予定通りの出力結果が得られています。

@t-tk
Copy link
Collaborator Author

t-tk commented Mar 16, 2024

いろいろ検討した結果、文字コードを再び変えました。今度こそ確定のつもりです。

内部トークンの文字コード ::
SVS: 0x400000..0x7FFFFF の3byteで表現
IVS: 0x800000..0xFFFFFF の3byteで表現

DVIの文字コード ::
SVS: 0x400000..0x7FFFFF の3byte を set3 で出力、内部トークンと同じ符号
IVS(VS48まで): 0x800000..0xFFFFFF の3byte を set3 で出力、内部トークンと同じ符号
IVS(VS256まで): 0x01000000..0x100FFFFF の4byte を set4 で出力、uptex出力時に符号を変換
VS48超えはまだ定義されていないので当面3byteの範囲で収まる。

SVSは3byteなので JFMのグリフをchartype 0番以外に設定可能。
IVS(VS48まで)は3byteなので JFMのグリフをchartype 0番以外に設定可能。しかし、chartype 0番の全角グリフを想定。
IVS(VS256まで)はJFMで chartype 0番の全角グリフを想定。

DVIの文字コードでは、 0x3FFFF でマスクして得られる下位 18 bit が基底文字の Unicodeスカラー値に等しい。
SVS,IVSの異体字セレクタの部分をそれより上の上位bitで表現している。

◇SVS
内部トークン DVIコード 文字の順に
40hhhh  ##40hhhh  U+0hhhh U+0FE00 (VS01)
44hhhh  ##44hhhh  U+0hhhh U+0FE01 (VS02)
48hhhh  ##48hhhh  U+0hhhh U+0FE02 (VS03) (現状の最大値)
4Chhhh  ##4Chhhh  U+0hhhh U+0FE03 (VS04)
...
78hhhh  ##78hhhh  U+0hhhh U+0FE0E (VS15) (emoji-sequencesに現れる)
7Chhhh  ##7Chhhh  U+0hhhh U+0FE0F (VS16) (emoji-sequencesに現れる)

41hhhh  ##41hhhh  U+1hhhh U+0FE00 (VS01)
45hhhh  ##45hhhh  U+1hhhh U+0FE01 (VS02)
49hhhh  ##49hhhh  U+1hhhh U+0FE02 (VS03)
...
79hhhh  ##79hhhh  U+1hhhh U+0FE0E (VS15) (emoji-sequencesに現れる)
7Dhhhh  ##7Dhhhh  U+1hhhh U+0FE0F (VS16) (emoji-sequencesに現れる)

42hhhh  ##42hhhh  U+2hhhh U+0FE00 (VS01)
46hhhh  ##46hhhh  U+2hhhh U+0FE01 (VS02) (現状の最大値)
4Ahhhh  ##4Ahhhh  U+2hhhh U+0FE02 (VS03)
...
7Ahhhh  ##7Ahhhh  U+2hhhh U+0FE0E (VS15) (emoji-sequencesに現れる)
7Ehhhh  ##7Ehhhh  U+2hhhh U+0FE0F (VS16) (emoji-sequencesに現れる)

◇IVS
(VS17..VS32) U+2hhhhの現状最大はVS25, U+3hhhhの現状最大はVS17
(VS33..VS48) U+0hhhhの現状最大はVS48
(VS49..VS64) まだ無い
(VS65..VS80) まだ無い

内部トークン DVIコード 文字の順に
80hhhh  ##80hhhh  U+0hhhh U+E0100 (VS17)
84hhhh  ##84hhhh  U+0hhhh U+E0101 (VS18)
88hhhh  ##88hhhh  U+0hhhh U+E0102 (VS19)
...
F4hhhh  ##F4hhhh  U+0hhhh U+E011D (VS46)
F8hhhh  ##F8hhhh  U+0hhhh U+E011E (VS47)
FChhhh  ##FChhhh  U+0hhhh U+E011F (VS48) (U+0xxxxの現状の最大値) (現状ここまで可能)

82hhhh  ##82hhhh  U+2hhhh U+E0100 (VS17)
87hhhh  ##87hhhh  U+2hhhh U+E0101 (VS18)
8Ahhhh  ##8Ahhhh  U+2hhhh U+E0102 (VS19)
...
9Ahhhh  ##9Ahhhh  U+2hhhh U+E0106 (VS23)
9Ehhhh  ##9Ehhhh  U+2hhhh U+E0107 (VS24)
A2hhhh  ##A2hhhh  U+2hhhh U+E0108 (VS25) (U+2xxxxの現状の最大値)
...
BEhhhh  ##BEhhhh  U+2hhhh U+E010F (VS32) (現状ここまで可能)

83hhhh  ##83hhhh  U+3hhhh U+E0100 (VS17) (U+3xxxxの現状の最大値)
...
BFhhhh  ##BFhhhh  U+3hhhh U+E010F (VS32) (現状ここまで可能)

◇註
現状定義されている漢字のSVS,IVSはカバーできている。
きっと将来も現れないが
U+0hhhh の VS48超え
U+0hhhh の VS64超え
U+2hhhh の VS32超え
U+3hhhh の SVS
も余地がありそう。

@t-tk
Copy link
Collaborator Author

t-tk commented Mar 16, 2024

{,e}uptex の方は、kcatcode 20 modifier を新設しました。

基底文字 + modifier の順に入力バッファに現れた場合、内部トークンに変換する際に基底文字のトークンの文字コードを、今回の 3バイトの内部トークンの文字コードに書き換える。
内部トークンからバッファに書き戻す際には、基底文字 + modifier のUTF-8の文字列に書き換える。
基底文字 + modifier の合成ができなかった場合には modifier は普通のCJK文字のように扱われる。

例えば、<U+8279 U+FE00> の文字は、以下のどれでも出力可能。

`\kchar"408279`
 `\kchar"8279\kchar"FE00`
`艹︀`
`\Uchar"8279\Uchar"FE00`

@t-tk
Copy link
Collaborator Author

t-tk commented Apr 7, 2024

実装についてまとめます。

  • 実装案(最終): ptexenc, upTeXエンジン, dvipdfmxを拡張する。upTeXとdvipdfmxの間のデータ渡しにはdvi命令(set3,set4)を使う
    • upTeXの内部トークンの文字コード=DVIの文字コードは0x22,0000以降を用いる。
      • 漢字,記号など+SVS(絵文字を含む)は 0x40,0000 以降
      • 漢字+IVSは 0x80,0080 以降 VS48までは set3の範囲(~0xFF,FFFF), VS49~VS256はset4の範囲(0x100,0000~0x43F,FFFF)
      • 現在までにunicode.orgのIVDに定義されているVS48までは set3に収まる。defaultのchartype 0 の全角グリフを想定しているが、JFMで扱える最大値 0xFF,FFFFまでに収まっており利用可能。
      • VS49以降は現在のJFMで扱える最大値を超えるが、defaultのchartype 0 の全角グリフを仮定する。kcatcode は kanji のみを受け付ける。kcatcode (23, kanji_ivs)はupTeX内部だけで使用(実質的には24~27の2bit分を消費)するが、ユーザーからはkcatcode 16 kanjiのように見えるようにする。
      • 仮名+濁点は 0x22,0000~0x23,FFFF, 仮名+半濁点は 0x24,0000~0x25,E6E5
      • 絵文字の国旗は 0x25,E6E6~0x25,FFFF
      • 絵文字の肌色の違い(Fitzpatrick)は 0x26,0000~0x2F,FFFF
      • 絵文字のkeycap sequenceは0x80,0000~0x80,007F
    • upTeXの文字バッファに書き戻す際は UTF-8の文字並びに戻す。なので、テキスト形式の入出力は必ず正規のUnicodeになる。
    • upTeXのkcatcodeに 20 modifier を新設する。基底文字の直後に異体字セレクタのようなmodifierが現れたら基底文字の文字コードを合成後の文字コードに書き換える。合成に失敗したmodifierは普通の文字(kcatcode 18 CJK記号と同様)としてふるまう。
    • ptexenc libraryで文字コードの合成・分解の関数を用意する。
    • upTeXエンジン内は1文字として扱い、dviに書き込む際、dvi命令(set3,set4)を書き出す。
    • dvipdfmxでは、dviからdvi命令(set3,set4)を読み込んだらVS,GSUB等で適切なグリフを選択する。
    • dvips, dvipdfmxなど、Adobe-Japan1のCIDが扱えるdviwareでは、AJ1の文字に対してdvi命令(set3)からAJ1へ変換するvfを経由して出力可能。

t-tk added a commit that referenced this issue Apr 27, 2024
t-tk added a commit that referenced this issue Apr 27, 2024
t-tk added a commit that referenced this issue Apr 27, 2024
t-tk added a commit that referenced this issue Apr 27, 2024
@t-tk
Copy link
Collaborator Author

t-tk commented Apr 27, 2024

upTeX の改造量が多めで少し不安もありますが、私の手元で想定通り動いているようなので
ptexenc 1.5.0, upTeX ver 1.35, updvitype etc p240427, dviout-util 20240427, dvipdfmx 20240427 として
TeX Live svn にコミットしました。 r71091 r71092 r71093 r71094

お気づきの点があればお知らせください。

@t-tk
Copy link
Collaborator Author

t-tk commented Apr 29, 2024

omfonts ver 2.2 r71118

t-tk added a commit to texjporg/japanese-otf-mirror that referenced this issue May 1, 2024
ref:
#28
texjporg/tex-jp-build#46
https://www.unicode.org/ivd/data/2022-09-13/IVD_Sequences.txt

require:
* ptexenc ver 1.5.0/dev  TeX Live svn r71091
* upTeX ver 1.35, r71143
* omegafonts (omfonts) ver 2.2, r71118
* dvipdfm-x ver 20240429, r71117
@h20y6m
Copy link
Collaborator

h20y6m commented May 4, 2024

begin-end の対応がずれていたのを 320633c で修正しました。
和文フォント未設定で和文文字ノードが作られたときに未初期化メモリを読んで内容次第でクラッシュしていました。
(pLaTeX カーネルで和文フォント未設定で和文文字ノードが作られていたようです→ texjporg/platex@36cd846

@h20y6m
Copy link
Collaborator

h20y6m commented May 4, 2024

まだ全然理解できていませんが、

  • 艹︀ <U+8279 U+FE00> という入力からは U+8279 と U+FE00 の2つの和文文字トークンが生成される。
  • 和文文字(例:U+8279)に続けてkcatcode=20の文字(例:U+FE00)がノード化されると合成された文字コードを持つノード(例:"408279)が生成される
    • \kchar"8279\kchar"FE00 のように \kchar でも可
    • 艹\kchar"FE00 のような組み合わせも可
  • \kchar"408279 のように直接合成したノードを出力することも可能
  • \Uchar"408279 からは U+8279 と U+FE00 の2つの和文文字トークンが生成される。

という理解であっていますか?

TeX 言語から見た時の変化は

  • kcatcode=20 が追加された。U+FE00 等の異体字セレクタがデフォルトで kcatcode=20 になった。
  • \Uchar, \Ucharcat"22000 以上を指定したときにの2トークンに展開されるようになった。
    • \kansujichar + \kansuji でも同様のことがおこる。

ほかにありますか?

@t-tk
Copy link
Collaborator Author

t-tk commented May 6, 2024

コメントありがとうございます。
以下は、こうしたつもり、こうなっているはず、ということです。違ったらご指摘お願いします。

  • 艹︀ <U+8279 U+FE00> という入力からは U+8279 と U+FE00 の2つの和文文字トークンが生成される。

これは yes です。ただし、以前からこれは同じだったはず。

  • 和文文字(例:U+8279)に続けてkcatcode=20の文字(例:U+FE00)がノード化される際、文字コードの並びがupTeXで想定されている合成文字の場合について、合成された文字コードを持つノード(例:"408279)が生成される。しかし、間にペナルティ等が挟まると合成文字のノードは生成されない
    • \kchar"8279\kchar"FE00 のように \kchar でも可
    • 艹\kchar"FE00 のような組み合わせも可

少し書き足しました。

  • \kchar"408279 のように直接合成したノードを出力することも可能

これは yes です。以前からと同じですが、0x408279 に意味があるところに今回新規のところと思います。

  • \Uchar"408279 からは U+8279 と U+FE00 の2つの和文文字トークンが生成される。

\Ucharの周辺のコードは意図して触ったつもりはありませんが、そのようになっていると思います。

  • kcatcode=20 が追加された。U+FE00 等の異体字セレクタがデフォルトで kcatcode=20 になった。

これは yes です。そのように意図しています。

  • \Uchar, \Ucharcat"220000 以上を指定したときに2トークンに展開されるようになった。
    • \kansujichar + \kansuji でも同様のことがおこる。

これは、あまり考えていなかったのですが差し支えありますか?

@t-tk
Copy link
Collaborator Author

t-tk commented May 11, 2024

変体仮名を upTeX 1.35 + dvipdfmx 20240427 + Noto Hentaigana font v1.000 で試しています。
横組みは想定通りできていますが、縦組みはなぜかずれます。原因の切り分けは未だです。

image


IPAmj明朝フォントだとずれませんでした。Noto Hentaigana fontの所為?

image

@h20y6m
Copy link
Collaborator

h20y6m commented May 12, 2024

kcatcode=20 が追加された

これについては以下のパッケージが影響を受けそうなので TL2025 までに対応してもらう必要がありそうです。

(kcatcode=20 が有効かどうかの判定方法は? \uptexversion?)

  • \Uchar, \Ucharcat"220000 以上を指定したときに2トークンに展開されるようになった。
    • \kansujichar + \kansuji でも同様のことがおこる。

これは、あまり考えていなかったのですが差し支えありますか?

これらの展開結果が2トークンになるのはちょっとびっくりする結果かもしれません。
とはいえ意図的に 0x110000 以上の文字コードを使うことはないと思うので問題にはならないとは思います。


変体仮名を upTeX 1.35 + dvipdfmx 20240427 + Noto Hentaigana font v1.000 で試しています。
横組みは想定通りできていますが、縦組みはなぜかずれます。原因の切り分けは未だです。

IPAmj明朝フォントだとずれませんでした。Noto Hentaigana fontの所為?

Noto Hentaigana font は vmtx を持っていないみたいなので縦組みはダメそうですね。
(Ascender+Descender が 1.437em なのでその分の字幅がずれている?)

@t-tk
Copy link
Collaborator Author

t-tk commented May 13, 2024

Noto Hentaigana font は vmtx を持っていないみたいなので縦組みはダメそうですね。

これには軽い衝撃を受けました。
Noto serif CJKの記事を見ると、Noto font 自体が縦組みをサポートしない方針ということはなさそうですし、
Unicodeの縦組みのproperty The Vertical_Orientation Property (vo) VerticalOriantation.txtを見ても、変体仮名が平仮名片仮名と違っているわけでもなさそうです。
サポートしてほしいものです。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants