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

[XeTeX] SourceHanSerif で 数字の ToUnicode が入らない #75

Open
aminophen opened this issue Feb 26, 2019 · 11 comments
Open

[XeTeX] SourceHanSerif で 数字の ToUnicode が入らない #75

aminophen opened this issue Feb 26, 2019 · 11 comments

Comments

@aminophen
Copy link
Member

forum:2575#p14985 に書いた

「数字0〜9の ToUnicode が入らない」という問題

について,記録しておきます。

% plain XeTeX
\special{dvipdfmx:config z 0}
\special{dvipdfmx:config V 4}
\font\f="[SourceHanSerif-Regular.ttc:2]" \f
A2019
\bye

これを xetex で処理すると,出てくる PDF の「2019」のテキスト情報 (ToUnicode) が無く,コピペできません。このソースで,冒頭の「A」を消すと「2019」のテキスト情報が正しく入ります。

今のところ,不思議なことに,

  • 英字 (A-Za-z) と数字 (0-9) が共存すると,数字の ToUnicode が効かない。
  • 英字 (A-Za-z) がなければ,数字の ToUnicode も効く。
  • この挙動は SourceHanSerif-Regular では起こるが,SourceHanSans-Regular なら無問題。

という状況で,問題がどこにあるのか(xdvipdfmx なのかフォント自体なのか)すら判っていません。

@aminophen
Copy link
Member Author

本件,もう少し調べてみました。

「数字の ToUnicode が効かなくなる」条件の絞り込み

どうやら「英字 (A-Za-z) と数字 (0-9) だけからなる行が NG」のようです。

A\par
2019\par % => OK
A2019\par % => NG
2019A\par % => NG
2019\par % => OK
A\par
漢2019はAです。\par % => OK
漢はA2019です。\par % => OK
漢2019はAです。\par % => OK
漢Aは2019です。\par % => OK
\bye

また,これを xetex -no-pdf と xdvipdfmx -vvv に分けてみると,NG な行がある場合は

No Unicode mapping available: GID=62294
No Unicode mapping available: GID=62295
No Unicode mapping available: GID=62296
No Unicode mapping available: GID=62303

のように xdvipdfmx が警告していることがわかりました。つまり,この NG な行では「数字が GID 62294〜62303 に置換されている」ということのようです。

「SourceHanSerif では起こるが,SourceHanSans なら無問題」の調査

ttx でダンプしてみると,GSUB テーブルに SourceHanSerif 特有の記述を見つけました。

      <Lookup index="44">
        <!-- LookupType=1 -->
        <LookupFlag value="0"/>
        <!-- SubTableCount=1 -->
        <SingleSubst index="0">
          <Substitution in="cid00017" out="cid62294"/>
          <Substitution in="cid00018" out="cid62295"/>
          <Substitution in="cid00019" out="cid62296"/>
          <Substitution in="cid00020" out="cid62297"/>
          <Substitution in="cid00021" out="cid62298"/>
          <Substitution in="cid00022" out="cid62299"/>
          <Substitution in="cid00023" out="cid62300"/>
          <Substitution in="cid00024" out="cid62301"/>
          <Substitution in="cid00025" out="cid62302"/>
          <Substitution in="cid00026" out="cid62303"/>
          <Substitution in="cid00728" out="cid62304"/>
        </SingleSubst>
      </Lookup>

これは「CID+17〜26 つまり数字を GID 62294〜62303 に置き換える」ということです。試しにこの 10 行分を消して,ttx で"削減版フォント"を作ってみると,ToUnicode が正常になりました。


さて,残すところは「(x)dvipdfmx が,英字+数字 の行でだけ,この置換を行うのはなぜか?」を究明すればいいと思います。

@zr-tex8r
Copy link

zr-tex8r commented Mar 19, 2019

源ノフォントのfeatureファイルでいうと、この部分ですね。

https://github.com/adobe-fonts/source-han-serif/blob/master/Regular/features.JP#L2753

featureファイルの読み方はよくわからないけど、文脈付きのグリフ置換ではなさそう。

@zr-tex8r
Copy link

xdvの中身を調べてみました。

\nopagenumbers
\font\f="[SourceHanSerif-Regular.ttc:0]"
\f 2019 A2019 \bye

これをxetexでコンパイルした結果の.xdvファイルをdviasmでダンプしてみます。

[preamble]
id: 7
numerator: 25400000
denominator: 473628672
magnification: 1000
comment: ' XeTeX output 2019.03.19:2132'

[postamble]
maxv: 667.202545pt
maxh: 469.754990pt
maxs: 2
pages: 1

[font definitions]
fntdef: "c:/texlive/texmf-local/fonts/opentype/adobe/SourceHanSerif/SourceHanSerif-Regular.ttc" at 10pt

[page 1 0 0 0 0 0 0 0 0 0]
xxx: 'pdf:pagesize default'
push:
  down: -14pt
pop:
down: 643.202545pt
push:
  down: -633.202545pt
  push:
    right: 20pt
    fnt: "c:/texlive/texmf-local/fonts/opentype/adobe/SourceHanSerif/SourceHanSerif-Regular.ttc" at 10pt
    setglyphs: 21.559998pt gid19(0pt) gid17(5.389999pt) gid18(10.779999pt) gid26(16.169998pt)
    right: 2.580002pt
    setglyphs: 28.750000pt gid34(0pt) gid62296(7.190002pt) gid62294(12.580002pt) gid62295(17.970001pt) gid62303(23.360001pt)
  pop:
pop:
down: 24pt

これをみると、「.xdvの段階で置換後のグリフが使われている」ことがわかります。つまり、グリフを置換しているのはxdvipdfmxではなくてXeTeXのほうです。

@aminophen aminophen changed the title [xdvipdfmx] SourceHanSerif で 数字の ToUnicode が入らない [XeTeX] SourceHanSerif で 数字の ToUnicode が入らない Mar 19, 2019
@aminophen
Copy link
Member Author

グリフを置換しているのはxdvipdfmxではなくてXeTeXのほう

Issue のタイトルを変えておきました。ということは,「XeTeX が使っているライブラリのどれかがそういう置換をしている」という可能性がありますね…。

@zr-tex8r
Copy link

\font\f="[SourceHanSerif-Regular.ttc:0];script=hani"

とすると数字が全部GID 17〜26になり、

\font\f="[SourceHanSerif-Regular.ttc:0];script=latn"

とすると数字が全部GID 62294〜62303になります。

ここから推測すると、「非指定の場合に単語先頭の文字を見るなどしてscriptを推定する」ような仕組があるようにも思えます。

@aminophen
Copy link
Member Author

根拠は何もないですが,なんとなく HarfBuzz にそんな仕組みありそうな気がする…。

script=latn とすると数字が全部GID 62294〜62303になります。

この場合は全部 Unicode 情報がなくなるわけですよね…。となるとフォントの問題なんでしょうか:

  • フォント作者側の置換の意図はなんだろう?
  • フォントに Unicode 情報を追加するには?

@trueroad
Copy link
Member

面白そうなのでいろいろ試してみました。

% plain XeTeX
\special{dvipdfmx:config z 0}
\special{dvipdfmx:config V 4}
\nopagenumbers

\font\f="[SourceHanSerif-Regular.otf]" \f
無指定 \par
A2019 \par % 置き換わる
2019 \par  % 置き換わらない

\font\f="[SourceHanSerif-Regular.otf]:script=DFLT" \f
script=DFLT を指定\par
A2019 \par % 置き換わる
2019 \par  % 置き換わらない

\font\f="[SourceHanSerif-Regular.otf]:script=cyrl" \f
script=cyrl を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f="[SourceHanSerif-Regular.otf]:script=grek" \f
script=grek を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f="[SourceHanSerif-Regular.otf]:script=hani" \f
script=hani を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f="[SourceHanSerif-Regular.otf]:script=kana" \f
script=kana を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f="[SourceHanSerif-Regular.otf]:script=latn" \f
script=latn を指定\par
A2019 \par % 置き換わる
2019 \par  % 置き換わる

\font\f="[SourceHanSerif-Regular.otf]:-locl" \f
-locl を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f="[SourceHanSerif-Regular.otf]:script=latn,-locl" \f
script=latn,-locl を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\bye

XeTeX だと以下の 3 条件

  • script 無指定 or script=DFLT
  • OpenType feature 無指定 or +locl
  • 英字と数字だけからなる部分

が揃うと数字のグリフが置き換えられる、
また、以下の 2 条件

  • script=latn
  • OpenType feature 無指定 or +locl

が揃った場合にも数字のグリフが置き換えられる、
みたいです。

@trueroad
Copy link
Member

一方、LuaTeX で似たようなことをすると、

% plain LuaTeX
\input luaotfload.sty
\nopagenumbers

\font\f{file:SourceHanSerif-Regular.otf} \f
無指定 \par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f{file:SourceHanSerif-Regular.otf:script=DFLT} \f
script=DFLT を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f{file:SourceHanSerif-Regular.otf:script=cyrl} \f
script=cyrl を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f{SourceHanSerif-Regular.otf:script=grek} \f
script=grek を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f{SourceHanSerif-Regular.otf:script=hani} \f
script=hani を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f{SourceHanSerif-Regular.otf:script=kana} \f
script=kana を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f{SourceHanSerif-Regular.otf:script=latn} \f
script=latn を指定\par
A2019 \par % 置き換わる
2019 \par  % 置き換わる

\font\f{SourceHanSerif-Regular.otf:-locl} \f
-locl を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\font\f{SourceHanSerif-Regular.otf:script=latn,-locl} \f
script=latn,-locl を指定\par
A2019 \par % 置き換わらない
2019 \par  % 置き換わらない

\bye

LuaTeX だと、以下の 2 条件

  • script=latn
  • OpenType feature 無指定 or +locl

が揃った場合だけ数字のグリフが置き換えられるみたいです。
置き換えられてしまうとテキスト抽出できなくなるのは XeTeX と同じでした。

@trueroad
Copy link
Member

XeTeX LuaTeX ともにデフォルトで locl が有効のようですが、
https://helpx.adobe.com/jp/fonts/using/open-type-syntax.html#locl
を見ると、デフォルトで有効にすべきものらしいので、この点については問題ではないのだと思います。

XeTeX と LuaTeX の差分は、

  • script 無指定 or script=DFLT
  • OpenType feature 無指定 or +locl
  • 英字と数字だけからなる部分

ですが、やはり XeTeX には、script 無指定もしくは script=DFLT の場合、なにかの塊毎に script を推定するロジックが入っていて、塊が英数だけで構成されていた場合には script=latn を適用してしまう、のではないかと思います。

この場合は全部 Unicode 情報がなくなるわけですよね…。となるとフォントの問題なんでしょうか:

元々 xdv の時点ですべて GID / CID になってしまっていて、Unicode の情報は捨てられてしまっていると理解していました。。。

フォント作者側の置換の意図はなんだろう?

script=latn かつ locl 有効の場合に数字のグリフを別のものに置き換える、ということだけなんではないかなと思います。置き換えられたグリフはよく見ると置き換え前のものと比べて微妙に小さいように思いますので、恐らくはデザイン上の理由か何かではないでしょうか。

SourceHanSerif の GSUB テーブルを見ると、script=DFLT の定義があり、その場合は locl が効かないように意図しているように思います。その他の script では DFLT と同様に locl が効かないようにしているか、数字グリフを置き換えない locl が効くようになるか、になっているようで、いずれにせよ script=latn 以外の場合は数字グリフ置き換えないことを意図しているのではないかと思います。

フォントに Unicode 情報を追加するには?

OpenType feature で置き換えられたグリフに対する ToUnicode CMap 生成がうまくいかない、のが原因の一つかと思います。そこへ意図せずに script=latn かつ locl 有効になって数字のグリフ置き換えが発生してしまう、という状況が重なって本件の事象になっているものとおもいます。

試しに他の OpenType feature でグリフを置き換えるとどうなるかやってみたところ jp90 でも同様にテキスト抽出できなくなりました。(SourceHan は jp04 がデフォルトのようで +jp04 や -jp04 は効きません。)XeTeX LuaTeX 双方とも同様です。。。

% plain XeTeX
\special{dvipdfmx:config z 0}
\special{dvipdfmx:config V 4}
\nopagenumbers

\font\f="[SourceHanSerif-Regular.otf]" \f
無指定 \par
辻井 逗子 飴玉 \par

\font\f="[SourceHanSerif-Regular.otf]:+jp90" \f
+jp90 を指定\par
辻井 逗子 飴玉 \par

\bye
% plain LuaTeX
\input luaotfload.sty
\nopagenumbers

\font\f{file:SourceHanSerif-Regular.otf} \f
無指定 \par
辻井 逗子 飴玉 \par

\font\f{file:SourceHanSerif-Regular.otf:+jp90} \f
+jp90 を指定\par
辻井 逗子 飴玉 \par

\bye

xdvipdfmx の対応としては、 ToUnicode CMap 生成時、cmap テーブルで見つけられなかったグリフがあったら、GSUB テーブルで置き換え元のグリフを探して、置き換え元グリフで再度 cmap テーブルを探す、みたいなロジックにすればいいのかなぁ、と思います。

恐らく AJ1 だったら ToUnicode CMap を生成しないで済むため、問題ないのではないかと思います。

#原ノ味フォントは現状 GSUB が無いので試せません。残念。。。

@aminophen
Copy link
Member Author

@trueroad さん,ありがとうございます。

やはり XeTeX には、script 無指定もしくは script=DFLT の場合、なにかの塊毎に script を推定するロジックが入っていて、塊が英数だけで構成されていた場合には script=latn を適用してしまう

この挙動は HarfBuzz 由来なんじゃないか?と思って眺めていたら,hb-ot-layout.cc

`try with 'latn'; some old fonts put their features there even though they're really trying to support Thai, for example :(

というコメントを見つけました。歴史的事情で「無指定の場合は script=latn を適用せざるをえない状況があるんでしょうかね。XeTeX 本体に特殊な箇所は見当たりませんでしたので,HarfBuzz のこの辺りの挙動にそのまま乗っかった結果かもしれません。

この場合は全部 Unicode 情報がなくなるわけですよね…。となるとフォントの問題なんでしょうか

元々 xdv の時点ですべて GID / CID になってしまっていて、Unicode の情報は捨てられてしまっていると理解していました。。。

書いた意図がうまく伝わらず失礼しました。私の理解も @trueroad さんと同じです。「XeTeX + xdvipdfmx や LuaTeX の挙動はバグではなく,フォントが偶然そういうものだった,という"相性問題"みたいなものかな」,というつもりで書きました。

xdvipdfmx の対応としては、 ToUnicode CMap 生成時、cmap テーブルで見つけられなかったグリフがあったら、GSUB テーブルで置き換え元のグリフを探して、置き換え元グリフで再度 cmap テーブルを探す、みたいなロジックにすればいいのかなぁ、と思います。

確かに,そう言われると無理ではなさそうですね…(そして,これは xdvipdfmx だけでなく LuaTeX にも当てはまる話です)。でもなんか大変そう…。

@trueroad
Copy link
Member

この挙動は HarfBuzz 由来なんじゃないか?と思って眺めていたら,hb-ot-layout.cc に

`try with 'latn'; some old fonts put their features there even though they're really trying to support Thai, for example :(

というコメントを見つけました。歴史的事情で「無指定の場合は script=latn を適用せざるをえない状況があるんでしょうかね。XeTeX 本体に特殊な箇所は見当たりませんでしたので,HarfBuzz のこの辺りの挙動にそのまま乗っかった結果かもしれません。

ご指摘の hb-ot-layout.cc 内 2 か所はいずれも先に DFLT を試してみて、なければ dflt そして latn という順番でフォールバックしているように見えます。SourceHan には DFLT があるので、そうすると latn までたどり着くことはなくて、件の挙動とは関係なさそうに思います。

HarfBuzz のソースをザッと眺めてみましたが、強制的に latn を使おうとしている所は他にはみつけられませんでした。見落としている可能性はありますが、件の挙動は HarfBuzz とは関係ないのかもしれません。

書いた意図がうまく伝わらず失礼しました。私の理解も @trueroad さんと同じです。「XeTeX + xdvipdfmx や LuaTeX の挙動はバグではなく,フォントが偶然そういうものだった,という"相性問題"みたいなものかな」,というつもりで書きました。

相性問題と言われれば、そうなのかもしれません。でも script=DFLT を明示的に指定しても script=latn になってしまう部分がある、というのはやりすぎではないかなぁと思います。

xdvipdfmx の対応としては、 ToUnicode CMap 生成時、cmap テーブルで見つけられなかったグリフがあったら、GSUB テーブルで置き換え元のグリフを探して、置き換え元グリフで再度 cmap テーブルを探す、みたいなロジックにすればいいのかなぁ、と思います。

確かに,そう言われると無理ではなさそうですね…(そして,これは xdvipdfmx だけでなく LuaTeX にも当てはまる話です)。でもなんか大変そう…。

ちゃんとやろうとするとそれなりの規模になりそうですね。。。

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

No branches or pull requests

3 participants