ブラウザの外字の表示について

## はじめに
外字はブラウザで基本表示されません。
単純に言えばUnicodeポイント(コードポイント)に登録されていないからです。
webフォントを使用し表示させることでコードポイントについて把握します。

## ブラウザでの文字コード検証
「input」の入力フィールドに文字を入れてコードポイントを確認します。

fontTest.woffにE000に何か文字を割り当てました。
Webフォント作成ツールのfontforgeを使用しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/240dea3f-a659-91b0-f898-ab5c585bd42a.png)


今回は外字を表示するために、**#のコードポイントをU+E000に変更**します。

以下のように入力したときのコードポイント表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/15aad6f1-1e5c-6690-e5c3-6d66bd64d614.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/bb183480-16c3-670d-99c3-a8d1148a953e.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/1e33f093-e34c-01a9-93a3-e7322c8c2df5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/1d53945a-9083-cf50-7283-5ac98bbfc179.png)

**html**
```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>外字入力テスト</title>
    <style>
        @font-face {
            font-family: 'GaijiFont';
            src: url('./fonts/fontTest.woff') format('woff'); /* 外字フォントを指定 */
        }
        .gaiji-input {
            font-family: 'GaijiFont', sans-serif; /* 外字フォントを適用 */
            font-size: 24px;
        }
    </style>
</head>
<body>
    <h1>外字入力テスト</h1>
    <form action="/GaijiFontProject/SubmitFormServlet" method="post" accept-charset="UTF-8">
        <input type="text" name="inputText" id="inputText" class="gaiji-input" placeholder="ここに#を入力してください">
        <button type="submit">送信</button>
    </form>
<script src="js/script.js"></script>
</body>
</html>
```

**javascript**
```javascript
/**
 * 
 */
 const input = document.getElementById('inputText');

input.addEventListener('input', () => {
    // 入力値を取得
    let value = input.value;

	value.split('').forEach((char) => {
		console.log(char, '->', char.charCodeAt(0).toString(16).toUpperCase());
	});

	value.split('').forEach((char) => {
	    console.log(char, '->', char.charCodeAt(0).toString(16).toUpperCase());
	});

    // # を U+E000 に変換
    value = value.replace(/#/g, '\uE000');

    // 入力欄に変換後の値をセット
    input.value = value;
});

```


## 文字の表示
先ほどの結果より外字フォントの表示についてざっくり整理します。
1.#に外字で登録したコードポイントに変える。
2.CSSで外字のコードポイントをグリフに変えている。
「あ」はfont-familyで指定したフォント、指定していないとブラウザのデフォルトフォントでグリフに変換される、外字はどのフォントファミリーのフォントで対応は遅れており見「□」や「」のような代替文字が表示される
3.私用領域への割り当ての場合、webフォントで、@face-faceへの指定で、コードポイントをwebフォントのグリフに変換される

詳細を下記に記載します。
**HTMLやJavaScriptでの文字の動きの仕組み**

### **HTMLやJavaScriptでの文字の動きの仕組み**
1. **コードポイントと文字列の関係**
   - JavaScript では、文字列は内部的に **Unicode コードポイントのシーケンス** として管理されています。
   - 例えば、文字列 `"あ#"` は以下のようなコードポイントで構成されています：
     ```
     ["あ" -> U+3042, "#" -> U+0023]
     ```

**あ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/2172867b-8d86-2282-5edf-e7e481bb8eb2.png)

**#**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/f8d78b8d-dc36-2b5b-1007-c3bb407ff6d9.png)




２. **JavaScript の表示処理**
   - `value.replace(/#/g, '\uE000');` で文字列内の `#` を **U+E000** に置き換えると、文字列は次のように変わります：
     ```
     ["あ" -> U+3042, "\uE000" -> U+E000]
     ```
   - JavaScript は、このコードポイントのシーケンスをそのまま HTML に渡し、ブラウザがレンダリングを行います。

３. **ブラウザの役割**
   - ブラウザは、文字列のコードポイントを元にフォントを参照して **グリフ（見た目の形）** を探します。
   - 私用領域（U+E000 など）のコードポイントについては、標準フォントにはグリフが定義されていないため、フォントを指定しないと表示できません。

４. **フォントの変換（CSS の役割）**
   - CSS でカスタムフォントを設定することで、ブラウザは `U+E000` に対応するグリフをカスタムフォント内から取得して描画します。
   - これにより、「コードポイント `U+E000`」が実際の「外字（見た目の文字）」として表示されます。


### **流れを図式化**
#### **文字の動き（HTML & JavaScript + CSS）**

1. ユーザー入力
   ↓
2. JavaScript: コードポイントの置き換え
   例: `# -> U+E000`
   ↓
3. ブラウザ: コードポイントをフォントで解釈
   - デフォルトフォントにグリフがない場合、代替文字（□）などが表示される。
   - カスタムフォントが適用されている場合、グリフ（外字）が正しく描画される。
   ↓
4. ユーザーに見える文字として描画


### **外字についての表示
- **フォント未指定の場合**:
  - 私用領域（U+E000）に対応するグリフがデフォルトフォントに存在しないため、ブラウザは「□」や「」などの代替文字を表示します。

- **適切なフォント指定**:
  - CSS でカスタムフォントを明示的に指定する必要があります：
    ```css
    @font-face {
        font-family: 'GaijiFont';
        src: url('./fonts/fontTest.woff') format('woff');
    }

    .gaiji-input {
        font-family: 'GaijiFont', sans-serif;
    }
    ```

### **「あ」の表示
記載済ですが、外字中心の記載のため補足しておきます。

**あ** は、ブラウザによってコードポイント `U+3042` として解釈され、指定されたフォントから対応するグリフを取得して描画される。

#### **文字「あ」がブラウザに表示される仕組み**

1. **文字列としての「あ」**
   - 文字列 `"あ"` は、内部的には Unicode の **コードポイント `U+3042`** として扱われます。

2. **ブラウザでの処理**
   - ブラウザは HTML や JavaScript で渡された文字列（コードポイントのシーケンス）を受け取ります。
   - 文字列 `"あ"` を描画する際、`U+3042` を解釈します。

3. **フォントによるグリフの選択**
   - ブラウザは、指定されたフォント（またはデフォルトフォント）を使用して、コードポイント `U+3042` に対応する **グリフ（文字の形）** を取得します。
   - グリフが存在しない場合、代替文字（□など）が表示されます。

4. **画面への描画**
   - フォントから取得したグリフを使って、文字として描画します。

#### **具体的な流れ（図式化）**

HTML/JavaScript: "あ"
   ↓
内部コードポイント: U+3042
   ↓
ブラウザがフォントから対応するグリフ（形）を取得
   ↓
画面に「あ」として表示

#### **補足情報**
- **フォントの影響**:
  - フォントが変われば、同じコードポイント `U+3042` でも見た目（グリフ）が変わる場合があります。
  - 例えば、「MS Mincho」と「MS Gothic」で「あ」の見た目は異なります。

- **外字やカスタムフォント**:
  - もしコードポイントが標準フォントに存在しない（例: `U+E000` など）場合、カスタムフォントを指定する必要があります。

## おわりに
今回はutf-8を前提に外字について確認しました。
次はサーバでの外字の確認、またshift-jisでの確認を進めていこうと思います。

## 参考記事

https://ja.wix.com/blog/2023/02/what-is-web-font/#viewer-cnc2r

https://note.com/okano_/n/n3b91ec1a79ec

https://fontforge.org/en-US/

https://tools.m-bsys.com/ex/unicode_table.php#google_vignette

https://zenn.dev/amenaruya/articles/8da28cedfa85ee

https://tcd-theme.com/2019/03/textgarbling.html