texjporg / jsclasses

Classes tailored for use with Japanese.
BSD 2-Clause "Simplified" License
60 stars 14 forks source link

jsclasses (enablejfam) + otf + bm #53

Closed aminophen closed 4 years ago

aminophen commented 7 years ago

LaTeX で otf と bm を併用するときに otf を先に読み込むと見出しが死ぬ

\documentclass[a4j]{jsarticle}
\usepackage{otf}
\usepackage{bm}

\begin{document}
\section{これはゴシック}
\subsection{$\int$みたいな数学記号が見出しに入ると……}
\subsection{やったー!明朝体になった!}

\section{一旦リセットされるが、$\varepsilon$みたいな数学記号を入れると}

\section{やったー!明朝体だ!!}
\subsection{ってうれしくないよ!ばか!}
\end{document}

これは jsclasses の問題なのか、otf の問題なのか、それとも bm の問題なのかわかりませんが、念のため載せておきます。

zr-tex8r commented 7 years ago

とりあえず、jsclassesとotfの部分を“最小”にしてみた。

\documentclass[a4j]{jarticle}
\SetSymbolFont{mincho}{bold}{JY1}{mc}{bx}{n}%…(〠)
\usepackage{bm}
\usepackage{etoolbox}% for \csshow
\begin{document}
{\sffamily\gtfamily これはゴシック}
\csshow{JY1/gt/m/n/10}%==> \JY1/gt/m/n/10=select font goth10.
{\sffamily\gtfamily $\int$みたいな数学記号が見出しに入ると……}
\csshow{JY1/gt/m/n/10}%==> \JY1/gt/m/n/10=select font cmss10. (???)
{\sffamily\gtfamily やったー!明朝体になった!}
\end{document}

(〠) の部分がotfに相当する部分だけど、この記述に問題があるとは思えない。 だから結局、「bmパッケージがpLaTeXでアレ」ということなんだと思う。

aminophen commented 4 years ago

最新の pLaTeX2e 2020-02-02 でも状況は変わっていないので,調べてみました。

\tracingonline1
\tracingmacros1
\tracingassigns1

としてログを解析した結果,LaTeX カーネルの

\def\do@subst@correction{%
       \xdef\subst@correction{%
          \font@name
          \global\expandafter\font
            \csname \curr@fontshape/\f@size\endcsname
            \noexpand\fontname\font % <= このせいで \jfont ではなく \font が読み出される!
           \relax}%
       \aftergroup\subst@correction
}

で定義された \subst@correction が実行された瞬間に

subst@correction ->JY1/mc/bx/n/10 global font JY1/gt/m/n/10 fontname font relax

{reassigning scriptscriptfont256=JY1/mc/bx/n/10}
{globally changing JY1/gt/m/n/10=select font goth10}
{into JY1/gt/m/n/10=select font nullfont}
{globally changing JY1/gt/m/n/10=select font nullfont}
{into JY1/gt/m/n/10=select font cmss10}

として上書きされていることがわかりました。\font は代入時には「横 JFM なら \jfont (和文横),縦 JFMなら \tfont (和文縦)」にアサインしてくれますが,読出時には「現在の \font (欧文)」しか読み出せないので,至極当然です。

これをどうにかする手段はないだろうか? と考えると,一案として

とすればいい気がします。しかし,「ある fontdef トークンが \font か \jfont か \tfont か」を判定する,しかも \xdef 内で使える完全展開可能な方法が思いつきません。

※ 展開可能性を無視すれば,私の TeX 言語力でも強引にある程度は判定できたのですが,fontdef トークンが上書きされるようなコーナーケースには対応できないと思います。

そこで pTeX に \ifjfont, \iftfont みたいなプリミティブを実装してみました (https://github.com/texjporg/tex-jp-build/commit/9f2687caf267886b7d775cd4d29f2e0c8a46f9b3) 。この開発版 pTeX を使うと,本件 issue を解決することができます:

[edit 2020-02-05 01:04] 勘違い:むしろ判定を \noexpand しないといけないことに気づきましたので,コメントと下のマクロを修正しました。

\makeatletter
\def\do@subst@correction{%
       \xdef\subst@correction{%
          \font@name
          \global\expandafter\font
            \csname \curr@fontshape/\f@size\endcsname
            \noexpand\fontname
              \noexpand\ifjfont\font@name\jfont\noexpand\else
              \noexpand\iftfont\font@name\tfont\noexpand\else\font
              \noexpand\fi\noexpand\fi
           \relax}%
       \aftergroup\subst@correction
}
\makeatother

pTeX へのリクエストは https://github.com/texjporg/tex-jp-build/pull/97 に送りました。

aminophen commented 4 years ago

↑ マクロの展開や代入を解析した結果から「これで行けるんじゃないかな」というテキトーな発想で試してみたら,一時間くらいで一発通ってしまった,という程度のものです。bm パッケージや LaTeX カーネルが壊れないかは自信がないので,マズかったらご指摘ください。

aminophen commented 4 years ago

上の方の ZR さんご提示のソースにもう少しコメントを付け足したもの:

% ダメになる例:ゴシックが明朝になる
% disablejfam なので数式内で漢字が使えない最小化
\documentclass[disablejfam]{jarticle}
\usepackage{ptrace}
% ssub でなく明示定義されていれば大丈夫
%\DeclareFontShape{JY1}{mc}{bx}{n}{<5> <7> <10> goth10}{}
\DeclareSymbolFont{mincho}{JY1}{mc}{m}{n}
\SetSymbolFont{mincho}{bold}{JY1}{mc}{bx}{n}%…(\UTF{3020})
\usepackage{bm}
\usepackage{etoolbox}% for \csshow

\begin{document}

% =======================
{\sffamily\gtfamily ゴシックGT}のはずなのに

\csshow{JY1/gt/m/n/10}%==> \JY1/gt/m/n/10=select font goth10.
数式
\setbox0=\hbox{$\empty$}%
が一度でも入ると
% 数式が使われた時に有効だった欧文ファミリに再定義されてしまう
\csshow{JY1/gt/m/n/10}%==> \JY1/gt/m/n/10=select font cmr10. (???)

{\sffamily\gtfamily 同じサイズは明朝体MCになる}
% =======================

% =======================
{\sffamily\gtfamily\Large 別のサイズは大丈夫GT}

{\Large
だが数式
\setbox0=\hbox{$\empty$}%
が一度でも入ると}

{\sffamily\gtfamily\Large 別のサイズも潰されるGT}
% =======================

\end{document}
h-kitagawa commented 4 years ago

そういえば LuaTeX-ja ではどうしていたんだっけ……と思い返すと,\curr@fontshape から encoding を取り出し,それが和文エンコーディングか否か判定していたのでした.pLaTeX でも同じようなことができませんか?

% #1 の展開結果から encoding 部分をとりだし,それが和文用かどうかの結果を \ifin@ に代入
\def\ltj@@IsFontJapanese#1{%
  \directlua{luatexja.jfont.is_kenc(string.match(
      '\luatexluaescapestring{#1}', '[^/]+'))}}
\let\ltj@@al@do@subst@correction=\do@subst@correction
\def\do@subst@correction{%
   \ltj@@IsFontJapanese{\curr@fontshape}\ifin@%
     \ltj@@ja@do@subst@correction
   \else
     \ltj@@al@do@subst@correction
   \fi
}
aminophen commented 4 years ago

和文エンコーディングか否か

和文エンコーディングか否かは \kyenc@list とか \ktenc@list を見ればわかるので,確かに \font@name の展開結果から encoding を取り出しさえすれば行けるかも…。

aminophen commented 4 years ago

よく読むと

\curr@fontshape から encoding を取り出し

なのですね。それで正しいのでしょうか。私の理解としては,「グループ内で実行された \font@name が実際にどのフォントを変更したか」に応じて読出元を変えるために,てっきり \font@name から取り出すものかと。

aminophen commented 4 years ago

\font@name からエンコーディングを取り出して和文横かどうかを判定してみました。これなら新しいプリミティブは不要ですね…。(e-TeX の機能を \scantokens 以外にも使えばもう少しきれいになりそうですが…)

{\escapechar-1\relax \xdef\pltx@macro{\string\macro:->}}
\expandafter\expandafter\expandafter\def
  \expandafter\expandafter\expandafter\pltx@strip@macro
  \expandafter\pltx@macro\expandafter#\expandafter1/#2\@nil{#1}

\def\pltx@parse@font@name{%
  \xdef\tmp@@@item{\expandafter\expandafter\expandafter\pltx@strip@macro
    \expandafter\meaning\expandafter\font@name\pltx@macro\@nil}%
  {\everyeof{\noexpand}\endlinechar=-1\relax
   \xdef\tmp@@@item{{\expandafter\scantokens\expandafter{\tmp@@@item}}}}%
}

\def\do@subst@correction{%
       \xdef\subst@correction{%
          \font@name
    \noexpand\pltx@parse@font@name
    \noexpand\expandafter\noexpand\expandafter\noexpand\expandafter
    \noexpand\inlist@\noexpand\expandafter\noexpand\tmp@@@item
      \noexpand\expandafter{\noexpand\kyenc@list}%
    \noexpand\ifin@
          \global\expandafter\font
            \csname \curr@fontshape/\f@size\endcsname
            \noexpand\fontname\jfont
           \relax
    \noexpand\else
          \global\expandafter\font
            \csname \curr@fontshape/\f@size\endcsname
            \noexpand\fontname\font
           \relax
    \noexpand\fi}%
       \aftergroup\subst@correction
}
h-kitagawa commented 4 years ago

(とりあえず \f@encoding が和文用か調べている状況ですが)https://github.com/h-kitagawa/platex/tree/kitagawa_substcorr でいじっているところです. @aminophen さんの方針とは違い,\inlist@ で調べる時に \detokenize を使って調べるエントリ・リストとも文字列化するようにしています.

てっきり \font@name から取り出すものかと。

昔書いたコードなので覚えていませんが,おそらく「和文エンコーディングと欧文エンコーディングは交じることはない」(=和文エンコーディングで定義されたフォントは置換元もそうである)という前提で書いたのだと思います.

aminophen commented 4 years ago

\inlist@ で調べる時に \detokenize を使って調べるエントリ・リストとも文字列化

動いていそうです。でも \expandafter しすぎ🍣なので https://github.com/texjporg/platex/tree/kitagawa_substcorr で減らしてみました。

aminophen commented 4 years ago

https://github.com/texjporg/platex/tree/for-tl2020 ブランチに入れました。

aminophen commented 4 years ago

今気づいたのですが,plfonts.dtx を読むと,アスキー版の頃から \do@subst@correction が和文対応できていないことは知られていたようです。説明文だけ読むとその対策をとったつもりのようなのですが…なのに何故この issue のような問題が起きるんだろう…?(まあいいや,今回の改修をすれば動くようになるので,昔のことは気にしないことにしよう。)

% \subsubsection{一時コマンド}
%
% \begin{macro}{\afont}
% \LaTeX{}内部の|\do@subst@correction|マクロでは、
% |\fontname\font|で返される外部フォント名を用いて、
% \LaTeX{}フォント名を定義しています。したがって、|\font|をそのまま使うと、
% 和文フォント名に欧文の外部フォントが登録されたり、
% 縦組フォント名に横組用の外部フォントが割り付けられたりしますので、
% |\jfont|か|\tfont|を用いるようにします。
% |\afont|は、|\font|コマンドの保存用です。
%    \begin{macrocode}
\let\afont\font
%    \end{macrocode}
% \end{macro}
zr-tex8r commented 4 years ago

「pLaTeXカーネルがやっている\do@subst@correction対策」というのは、確か、 「\do@subst@correctionを実行する前に、あらかじめ必要に応じて\let\font\jfontとか\let\font\tfontとかを実行しておく」 というものだったはずです。 (で、\fontを“元に戻す”ために用意されているのが\afont。)

aminophen commented 4 years ago

@zr-tex8r はい,そうだと思います。

なのに,実際には「\do@subst@correction の実行時点では \let\font\jfont とか \let\font\tfont とかが効いてなかった」ことが今回判明しました。ここで気になるのが,

aminophen commented 4 years ago

pLaTeX2e 2020-04-12 を出したので close します。