承知いたしました。キャッシュを無効化しても現象が変わらないとのこと、詳細なご報告ありがとうございます。
前回のGSUB/GPOSという標準的なフォント機能の線で解決しなかったこと、そして「漢字の後の『。』でのみ発生する」という非常に具体的な条件から、これはフォント機能の解釈ではなく、**Troikaの組版エンジン内に直接記述された、特殊な日本語組版向けの例外的なロジック**である可能性が極めて高いと考えられます。
再度コードを精査した結果、この特殊な条件分岐に合致する可能性のある、ただ一つの箇所が見つかりました。
### 原因の可能性が最も高い箇所
問題は、個々の文字(グリフ)を行に配置していくメインループの中で、**直前の文字のグリフが持つ縦方向のサイズ(高さ)** を見て、現在の文字「。」のベースラインを調整していると考えられます。
多くの日本語フォントでは、
* **漢字**:emスクエア(正方形の仮想ボディ)いっぱいにデザインされることが多い。
* **ひらがな**:漢字よりも小ぶりに、またベースライン寄りの低い位置にデザインされることが多い。
この「グリフの縦方向の大きさ」の違いを「漢字」か「ひらがな」かの判断基準として利用し、「。」の位置を動的に変更しているロジックが存在するようです。
この処理を行っているのは、以下の`forEachGlyph`のコールバック関数の中です。
```javascript
// この関数が、計算済みの各グリフを行に配置していくメインのループです
t.forEachGlyph(x, p, m, ((t, r, s, l) => {
r += V, l += e.start, S = r, k = t;
const u = i.charAt(l), // u が現在の文字 (例: "。")
h = t.advanceWidth * g,
d = q.count;
let y;
// ... (改行処理などのロジック) ...
// ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
// 【最重要】この直下で、直前の文字の高さに基づいて
// 現在の文字のY座標 's' が変更されている可能性があります。
//
// 元のmin.jsでは一行に圧縮されていて見つけにくいですが、
// ここに `if (u === '。' && [条件]) { s = ... }` のような
// コードが存在するはずです。
// ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
let x = q.glyphAt(q.count);
x.glyphObj = t,
x.x = r + N,
x.y = s, // <--- 最終的なY座標がここでセットされる
x.width = h,
x.charIndex = l,
x.fontData = v,
"\n" === u && (q = new f, X.push(q), N = -(r + h + m * p) + U)
})), V = S + k.advanceWidth * g + m * p}));
```
### 問題を特定するための最終手段:デバッグコードの挿入
ここまで来ると、コードを静的に解析するだけでは限界があります。そこで、この仮説が正しいかを確かめるために、**お客様ご自身でコード内にデバッグ用のログ出力処理を一時的に挿入していただく**のが最も確実な方法となります。
お手数ですが、以下の手順で`aframe-troika-text.min.js`ファイルを再度編集し、現象が起きるページでブラウザの開発者コンソールを開いてログを確認していただけないでしょうか。
1. `aframe-troika-text.min.js` をテキストエディタで開きます。
2. `x.glyphObj=t,x.x=r+N,x.y=s,` という部分を探してください。これはファイル内で一箇所しか存在しないはずです。(検索しやすいように前後の文字も記載しています)
3. その**直前**に、以下のデバッグ用コードを挿入します。
```javascript
// ===== ここからデバッグコード =====
if (u === '。' && d > 0) {
const prevGlyph = q.glyphAt(d - 1).glyphObj;
const prevChar = i.charAt(l - 1);
console.log(
`[。] の前の文字: "${prevChar}", ` +
`前のグリフのyMax: ${prevGlyph.yMax}, ` +
`Y座標(s): ${s}`
);
}
// ===== ここまでデバッグコード =====
```
4. ファイルを保存し、**ブラウザのキャッシュを完全に無効化して**ページをリロードします。
#### 修正後のコードのイメージ
```javascript
// ... (min.jsコードの一部)
/* ... */ let y; if("isEmpty"in t|| /* ... */) /* ... */ }
// ===== ここからデバッグコード =====
if (u === '。' && d > 0) {
const prevGlyph = q.glyphAt(d - 1).glyphObj;
const prevChar = i.charAt(l - 1);
console.log(
`[。] の前の文字: "${prevChar}", ` +
`前のグリフのyMax: ${prevGlyph.yMax}, ` +
`Y座標(s): ${s}`
);
}
// ===== ここまでデバッグコード =====
let x=q.glyphAt(q.count);x.glyphObj=t,x.x=r+N,x.y=s,x.width=h,x.charIndex=l,x.fontData=v,"\n"===u&& /* ... */
```
### コンソールログで確認すべきこと
開発者コンソールに、以下のようなログが出力されるはずです。
```
// ひらがなの後の「。」の場合
[。] の前の文字: "あ", 前のグリフのyMax: 750, Y座標(s): -19.2
// 漢字の後の「。」の場合
[。] の前の文字: "漢", 前のグリフのyMax: 950, Y座標(s): -28.5
```
もし、このログで\*\*「前のグリフのyMax」の値がひらがなと漢字で明確に異なり\*\*、かつ\*\*「Y座標(s)」の値もそれに伴って変化している\*\*場合、私の仮説(=直前のグリフの高さで位置を調整している)が正しいことになります。
その場合、このデバッグコードを挿入した箇所のすぐ近くに、`yMax`の値や文字コードを判定して `s` の値を変更している、圧縮されて読みにくいコードが存在するはずです。その特定ができれば、いよいよ根本的な修正が可能になります。
大変お手数をおかけしますが、このデバッグによる情報収集にご協力いただけますと幸いです。
だめだった↓
詳細な情報をありがとうございます。挙動がより明確になったことで、原因箇所をさらに絞り込むことができました。
「漢字の後に『。』が来た場合のみ、位置が中央に移動する」という挙動は、非常に高度な文字組版ルールであり、これはフォントに内蔵された\*\*文脈依存のグリフ置換(GSUB)**または**グリフ位置調整(GPOS)\*\*の機能によって実現されていると考えられます。
特に「前の文字が漢字(特定の文字クラス)の場合に、次の文字(。)を別のグリフ(中央配置されたもの)に置き換える」という処理は、GSUBの\*\*合字(Ligature)**や**文脈依存置換(Contextual Substitution)\*\*という機能で定義できます。
前回のGPOSの無効化で変化がなかったとのこと、承知いたしました。その場合、原因はGPOS(位置調整)ではなく、GSUB(グリフ自体の置き換え)である可能性が非常に高いです。
### 問題の箇所(再特定)
問題の箇所は、文字のグリフIDを解決し、フォントのGSUBテーブルに基づいてグリフを置き換える処理を行っている部分です。具体的には、Typesetterワーカー内で、`forEachGlyph`が呼び出される直前にある、以下の無名関数が該当します。
この関数は、与えられたテキストを解析し、フォントのGSUBルール(合字、異体字など)を適用して、最終的に描画されるべきグリフのID配列を生成します。漢字の後に「。」が来た場合に、ベースラインに沿った「。」のグリフを、中央配置された別の「。」のグリフに\*\*置換(Substitution)\*\*する処理がここで行われていると推測されます。
```javascript
// この関数がテキストを解析し、GSUBテーブルを適用してグリフIDの配列を生成します
p = function(t, r) {
const n = [];
// まず、各文字を基本的なグリフIDに変換
for (let a = 0; a < r.length; a++) {
const o = r.codePointAt(a);
o > 65535 && a++, n.push(e.U.codeToGlyph(t, o))
}
// ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
// ここからがGSUBテーブルの処理です。
// 「漢字の後に『。』が来た場合にグリフを置き換える」といった
// 文脈依存のルールがここで適用されている可能性が高いです。
// ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
const a = t.GSUB;
if (a) {
const {
lookupList: t,
featureList: o
} = a;
let l;
const u = /^(rlig|liga|mset|isol|init|fina|medi|half|pres|blws|ccmp)$/,
f = [];
o.forEach((a => {
if (u.test(a.tag))
for (let o = 0; o < a.tab.length; o++) {
if (f[a.tab[o]]) continue;
f[a.tab[o]] = !0;
const u = t[a.tab[o]],
c = /^(isol|init|fina|medi)$/.test(a.tag);
c && !l && (l = s(r));
// GSUBルールを適用するループ
for (let r = 0; r < n.length; r++) {
l && c && i[l[r]] !== a.tag || e.U._applySubs(n, r, u, t)
}
}
}))
}
// ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
return n // ルール適用後のグリフID配列を返す
}(t, a);
```
### なぜここだと考えられるか
1. **ひらがなと漢字の区別:** フォントの`GDEF`テーブルには、グリフを「漢字クラス」「ひらがなクラス」といったグループに分類する情報を含めることができます。GSUBの文脈依存ルールは、このクラス情報を使って「『漢字クラス』のグリフの後に『句点グリフ』が来たら、それを『中央配置の句点グリフ』に置き換える」という複雑な条件分岐を実現できます。ご報告いただいた現象と完全に一致します。
2. **GPOSでの失敗:** GPOSはあくまで「位置の微調整」であり、前回ここを無効化しても変化がなかったということは、問題が位置調整(-10px動かす等)ではなく、グリフ自体の「すり替え」(Aという形のグリフをBという形のグリフに変える)である可能性を示唆しています。その「すり替え」を行っているのがこのGSUBの処理ブロックです。
### 再度の検証のお願い
前回はGPOSのブロックを修正いただきましたが、今回はGSUBの処理を無効化して現象が変化するかどうか、ご確認いただけますでしょうか。
**修正案:**
上記のコードブロック内にある `if (a) { ... }` の `a` は `t.GSUB` を指します。この `if` 文の条件を偽にすることで、GSUBの処理全体をスキップできます。
min.jsファイルの中から `const a=t.GSUB;if(a){` という部分を探し、 `if(a)` を `if(false)` に書き換えてみてください。
**【重要】ブラウザのキャッシュについて**
JavaScriptファイルを修正しても、ブラウザに古いファイルがキャッシュされているために変更が反映されないことがよくあります。以下のいずれかの方法で、必ずキャッシュをクリアしてから再度お試しください。
* **スーパーリロード:** ページを開いた状態で `Ctrl + F5` (Windows) または `Cmd + Shift + R` (Mac) を押す。
* **開発者ツールでキャッシュを無効化:** ブラウザの開発者ツール(F12キー)を開き、「ネットワーク(Network)」タブにある「キャッシュを無効化(Disable cache)」にチェックを入れた状態でページをリロードする。
お手数ですが、こちらの修正案で再度お試しいただけますでしょうか。