Webフォント サブセット化ツール 。自動適用でカンタン軽量化＆高速化

# Webフォント サブセット化 ツール。自動適用でカンタン軽量化＆高速化


【JSコードで自動】日本語フォントをページ上の特定の文章・文字のみに使う： サブセット化 =All自動適用=


履歴
2024/01/31：大幅改稿。類似記事の削除に伴い リンク削除や記事中の対応関係を保った構成や記述の見直し。タイトルや見出しもこの際に変更。

__前文__
通常サブセット化には大きく分けて２通りある。WEBフォントを使い読み込む文字をあらかじめ `&text=` にて指定し必要分のみ読み込ませる方法と、よくググと紹介されてる ｢サブセットフォントメーカー｣を使う方法。

ここでは Googleフォントを使う方法。


- Googleフォント：textパラメータを付与することでサブセット化
  - パラメータの付与を自動で行うので、後からコンテンツ(≒文章)が変更された場合にも対応
    - 見出しなどで使うなど、文字数の少ない場合でも
    - 特定のコンテンツなど、ある程度文章量がある場合でも
    -  webページ全体など、大量な文章量の場合でも
- サブセットフォントメーカーを使わない、下記の手間をかけない
  - ソフトをダウンロードしたり、軽量化の作業をしたり、fontファイル(woff)をアップロードしたりしない


## 序文：百聞は一見に如かず 〜CodePen〜
まずは見てもらうとこう。
どちらも同じJS内容ですがCodePenを短文編と長文編で分けたので、埋め込みも2つに(2023/12/10)。

CodePen埋め込み 【長文編：複数行・長文編】
<p class="codepen" data-height="625" data-default-tab="result" data-slug-hash="MWLxRbz" data-user="Reng_code" style="height: 625px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;"><span>See the Pen <a href="https://codepen.io/Reng_code/pen/MWLxRbz">subset Ja font - Google Fonts</a> by Reng (<a href="https://codepen.io/Reng_code">@Reng_code</a>) on <a href="https://codepen.io">CodePen</a>.</span></p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

　
CodePen埋め込み 【短文編：１行以内・短文編】
<p class="codepen" data-height="620" data-default-tab="result" data-slug-hash="QWZWXMN" data-user="Reng_code" style="height: 620px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;"><span>See the Pen <a href="https://codepen.io/Reng_code/pen/QWZWXMN">subset Ja font - Google Fonts</a> by Reng (<a href="https://codepen.io/Reng_code">@Reng_code</a>) on <a href="https://codepen.io">CodePen</a>.</span></p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
 
部分的に使うと<font color=darkseagreen>実際どういう見た目になるか という例として</font>ご覧ください。

※１は太字系フォント
※２は明朝体
※３は１とは別の太字系フォント
※４はよくマンガで見る大声/怒鳴り声/叫び声などのフォント
※５と6はやや周りより小さめだったので大きさを揃えてる(CSS参照)

手っ取り早く やり方を知りたい → <a href="#-html">🟩HTML</a> のカ所へGO






## ざっくり解説
__働きと効能__
JavaScript  (以下｢JS｣という)でコードを書いて、重複なく絞り込みした文字列にする=軽量化のため **｢サブセット化｣するのを自動生成** 、`<link>`タグ内で使用する **font名も自動生成** 。

Googleフォントの Noto Sans JPなど web上で使いたい日本語フォントを選び、そのフォントで表示させたい文字を重複なしの文字で列挙するカタチにまとめ上げ、**そしてそれを自動生成し自動適用させる** (一応｢生成したタグ確認用コードブロック｣という領域で確認可 on CodePen)。

Webフォントをロードするコード`<link>`タグまるごと **自動生成し、かつ自動適用** 。つまり使う文字のみの絞込みで 超省エネで軽量化、サブセット化された状態でload出来る。

:::note
`&text=`を使わない=膨大な量の日本語(漢字ひらがなカタカナ記号類)の‘文字’をロードすることになる
:::
:::note
`&text=`を使う=(量にもよるが)必要な分だけなので相当減らせる、省エネが過ぎる(！)
:::


## 想定、前提など

__前提：__
- <font color=seagreen>**WEBフォントの Google Fonts の日本語を使う**</font>
- ページ全体じゃなく <font color=seagreen>任意の文章・特定の文字列のみ</font>フォントを変えたい
- 使う文字ロードするとき軽量化するための｢サブセット化｣を行う
- 使うのはブラウザのみ、JavaScriptで実現
- htmlとcssで<font color=seagreen>class名の命名規則</font>のみ気をつける(JSはコピペでOK)

__想定読者：__
- <font color=seagreen>**Webページ(or ブログ)に部分的にフォントを変えて表示してみたい**</font>
- Google Fonts の使い方は分かっている / そこは調べてどうにかする
- ~~JavaScript, HTML, CSS(CSS3)がある程度分かる / よくは分からないがゴリ押しでどうにかする~~<br>→ htmlとcssのみ、ここに紹介した命名規則でclass名を付けられる人
- ~~ブラウザのコンソールを表示させて確認出来る人~~<br>→ ~~任意のコードブロックで確認できるようにしたのでこの限りではない~~

__その他：__
- (Code Penでは HTML,CSS ,JS  と分けてるが) JSを分けないなら、htmlに埋め込みってことでJSコードを`<script>`〜`</script>`タグ内に記述
- 同じく、CSSを分けないなら、CSSコードを`<style>`〜`</style>`タグ内に記述
- HTML中に`<script>`タグで直接埋め込む場合、冒頭の方でも後ろの方でも・記事本文の前でも記事本文の後でも。ただ基本的には冒頭の方が良き


　
## JSコード
**興味がある人は**コードを見て下さい。下記に表示した分でも、CodePen上のJS部の分でも。**特に理解しなくても**使うこと＝運用自体には対して問題ありません。


<details><summary>興味がある人用JSコード表示(⚠️長めなので閲覧注意)...開閉エリア</summary>

1. メインなfuncで使う汎用コード群
2. メインなfunc群

で分かれてるので、分けて紹介。

__１. メインなfuncで使う汎用コード群：__
```JS:JavaScript
// as global funcs: ＊今回の分で使ってるのは $tag 以外
const $ =(id)=>{ return document.getElementById(id) }
const $cls =(cls)=>{ return document.getElementsByClassName(cls) } 
const $tags=(tag)=>{ return document.querySelectorAll(tag) }
//const $tag=(tag)=>{ return document.getElementsByTagName(tag) }
```
　
__２. メインなfunc群：__
```JS:JavaScript

//__Get className__
// ▼class名をsuffix"gglf_"で取得するマデ
const suffixArr = Array.from($tags('[class^=gglf_]'))
const gglfontArr = suffixArr.map(v=> v.className,[])
//console.info('gglfontArr->',gglfontArr) //check

//__Main func__
function applySubsetTxt(){
  
  // 書き込み先タグ - headタグ指定
  const tgtTag = document.head
  
  // 使用変数：link rel関連
  const tag0 = '<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>'
  const tag1 = '<link href="https://fonts.googleapis.com/css2'
  let tag2 ='' //roop内で追加あり
  let tag3 =''   //roop内で追加あり
  const tag4 = '&display=swap" rel="stylesheet">'
  
  // roop処理後にまとめて追加用
  let	linkrelTags = ''
  
  // 使用font種ごとのroop
  for (let classN of gglfontArr){
    // 配列化
    const tagArr = Array.from( $cls(classN) )
    //console.info('tagArr->',tagArr)
    const tgtTxtArr= Array.from( tagArr).map( v => v.innerText, [])
    
    // ▼重複削除した文字列を求めるマデ
    // - 重複文字を削除した1文字ずつ配列化
    const txtEvryArr = [...tgtTxtArr.join('')]  //配列
    
    // - 重複文字を削除した配列
    const txtSubsetArr = [...new Set(txtEvryArr)]  //配列
    
    // - 文字列から空白や改行を削除
    const txtSubset = txtSubsetArr.join('').trim() 
    
    //▼必要なタグとして生成
    // - font名をCSSから取得(【!】"$tags" は querySelectorAll)
    const gglspan = $tags('.'+classN)[0]
    let gglf_familyname = window.getComputedStyle(gglspan).fontFamily
    //console.info('🟤gglf_familyname->',gglf_familyname) //for check
    
    // - font名をタグ用書式に書き換え (書式: 半角スペース無し、あったら｢+｣繋ぎ)
    const gglf = gglf_familyname.split(',')[0]
    //console.info('🟣gglf->',gglf) //for check
    const familyname = gglf.replace(/"/g,'').replace(/ /g,'+')
    //console.info('🟢familyname->',familyname) //for check		
		
    // 使用font種、subset化文字列
    tag2 = '?family='+familyname
    tag3 = '&text='+txtSubset
    
    // - link relタグ組み立て & 生成
    const linkrel = tag1+tag2+tag3+tag4
    linkrelTags += linkrel
    
    // 1行ずつconsole表示(視認性:高)
    //*コレで見るか→*/console.info('🔷__linkrel__\n',linkrel) //for check
  }//for
  
  // まとめてconsole表示(視認性:やや低)
  //*コレで見るか→*/console.info('🔷__linkrelTags__🔷\n',linkrelTags) //for check
  
  // ▼htmlに適用 - headタグ内に追加
  tgtTag.insertAdjacentHTML('beforeend',tag0+linkrelTags) 
  
  // ▼codeタグに表示(視認性:高,ブラウザ上) ←※コンソール要らず
  let lineCode = linkrelTags.replace(/</g,'&lt;').replace(/>/g,'&gt;')
  lineCode = lineCode.split('&gt;').join('&gt;<br>') 
  //console.info('■lineCode->',lineCode) //for check
  $('code_block').innerHTML = lineCode
} // applySubsetTxt

//__Run func__
window.addEventListener('DOMContentLoaded',()=>{
  applySubsetTxt()
},false ) // addEve
```
__JSコード備考：__
- この例では行末に『`;`(コロン)なくてもイケるよね、じゃとっぱらおう』ということで行末`;`(コロン)極力無しで記述
- コメントアウトが多いが(行頭に`//`がある行)、これらは途中経過のcheck用
- `console.log()`じゃなく`console.info()`にしてるのは、console表示の絞込みでinfoだけを表示出来てスッキリと見ることができるから

<font color=darkgray>---------------//開閉エリア--</font>
</details>

　
## CSSコード
1. Google Fonts用のコード群
2. Code Pen用のコード群

で分かれてるので、分けて紹介。直接的に関係ない部分としてどちらも後半は略してる。

__Google Fonts用のコード群：__

```CSS:CSS
/*---------------------------
 Google Fonts 日本語装飾用
 --------
 「gglf_」で始まるクラス名
---------------------------*/
/* 太め */
.gglf_boldec{
  font-family: 'RocknRoll One', sans-serif;
  /*...このほか、
  font-family: 'Mochiy Pop One', sans-serif;
  */
}
.gglf_fancybold{
  font-family: 'Mochiy Pop One', sans-serif;
}
/* 明朝体系 */
.gglf_min{
  font-family: 'BIZ UDPMincho', serif; /*字間行間詰め気味*/  
  /* そのほかの明朝体
  font-family: 'Noto Serif Japanese', serif;
  font-family: 'BIZ UDMincho', serif;
  font-family: 'Shippori Mincho', serif;
  font-family: 'Hina Mincho', serif; *-｢す｣だけ草書っぽい*
  font-family: 'Zen Antique', serif; *-明朝体+太字*
  font-family: 'Zen Old Mincho', serif; *-明朝体+細め*
  */
}
/* 太め叫び */
.gglf_solidbold{  
  font-family: 'Reggae One', cursive 
}
/* キラキラ系 */
.gglf_suteki{
  font-family: 'Kaisei Decol', serif;
  font-size:1.05em; /*文字大きさ合わせ微調整*/
  /*__このほか、
  font-family: 'Kaisei Opti', serif;    *-ゴシック体?+飾り*
  font-family: 'Kaisei Tokumin', serif; *-明朝体?+ややレトロ風味*
  font-family: 'Kaisei HarunoUmi', serif; *-明朝体+飾り*
  など__*/
}
/* 手書きのキレイなの */
.gglf_tegaki_beu{
  font-family: 'Zen Kurenaido', sans-serif; /*-手書き風*/
  font-size:1.07em; /*文字大きさ合わせ微調整*/
  /*__そのほかの手描き風
  font-family: 'Zen Kurenaido', sans-serif; *-手書き風*
  font-family: 'Klee One', cursive; *-ペン字お手本みたい*
  など__*/ 
}
/* 手書きペン字お手本的 - フォント名をclass名に */
.gglf_klee{
  font-family: 'Klee One', cursive; 
}

以下略
```

　


<details><summary>興味がある人用Code Pen用のコード群...開閉エリア</summary>

__Code Pen用のコード群：__

```CSS:CSS
/*--------------------------
 Else  | for Code Pen
--------------------------*/
/*分かりやすく色つけ*/
span{
  color:brown;
}

/*=== PostIt的 ===*/
.postitArea{
  margin-top:3em;
}
.postitArea p{
  margin-bottom:5em;
}
.postitArea span{/*postit的*/
  background:pink;
  padding:25px 20px;
  color:inherit; /*親要素に合わせる*/
  display:inline-block;/*狭幅時でも改行抑制用*/
}
.postitArea .gglf_dot{
  background:transparent;
  border-radius:0.2em;
  border:double 3px gray;
}

/*=== flex(長文引用) ===*/
.flexArea{
  display:flex;
  flex-wrap:warp;
  justify-content:flex-start;
}
.flexArea > div{
  max-width:480px;
  min-width:250px;
  box-sizing:border-box;
  padding:0.2em 0.5em; margin:0 0.25em;
  line-height:180%;
  background:#0f2350; /* 濃藍 こいあい */
  color:#ebf6f7; /* 藍白 あいじろ */
  /* by 和色大辞典 (https://www.colordic.org/w) */
}
.flexArea .gglf_tegaki_beu{
  font-size:1.0em; /*文字大きさ相殺*/
}

/*== else ===*/
.ggl_wrap,
.code_blockArea{
  margin-top:3em;
  margin-bottom:3em;
}
code,
code.code_block{
  background:#444; color:#f2f2f2;
  border-radius:0.2em; padding:0.12em 0.2em;
  font-family: 'Ricty Diminished', 'PlemolJP35', 'Andale Mono', 'Menlo', 'Monaco', monospace;
}

以下略
```

__CSS備考：__
- この例では`code`タグ用に 'Ricty Diminished', 'PlemolJP35'を指定してるが、これらは誰もが持ってるシステムフォントではない。自分のローカル環境にあるプログラミング用フォント(を優先して表示させてみたかっただけ)

<font color=darkgray>---------------//開閉エリア--</font>
</details>


　
## 流れ(フロー)

Step 1： 使うGoogle Fonts の目星をつける ※後回しでもOK
Step 2： JSコード走らせる前に書いとくこと
Step 3： JSコード走らせる <font color=seagreen>**= 自動適用**</font>
　
### Step 1： 使うGoogle Fonts目星をつける
__Google Fonts：__
まずはGoogle Fontsで使用したい日本語を探す/目星をつける/決める ※リンク先は 日本語 16pt のページ

https://fonts.google.com/?preview.size=16&subset=japanese&noto.script=Jpan

:::note
あれ？ 以前より使える日本語フォントの種類 増えたような... 
＠2023年4月現在
:::

### Step 2： JSコード走らせる前に書いとくこと

- HTML
- ~~JS (コピペするだけ)~~   ※コード詳細は前述
- CSS

ぞれぞれに、<font color=seagreen>事前に書いとくこと</font>がある。以下に順に説明 (JSのみコード詳細は前述)。

#### 🟩 HTML
(１). <font color=seagreen>blog投稿欄(記事本文)やWebページの本文</font>

まずは日本語のWEBフォントを当てたい本文中の文字列に`<span>`や`<div>`タグでclass名を付ける。<font color=seagreen>**命名規則は「gglf_」で始まる**</font>任意のクラス名であること、「gglf_」以降が<font color=seagreen>**20文字まで**</font>であること。

:::note warn
Webフォントを適用したいカ所に、｢gglf_｣で始まるclass名を付ける 
(**g**oo**gl**e **f**ontの略で gglf。 ｢googlefont_｣だと長いから)
:::


:::note alert
`gglf_`に続く文字列やたら長過ぎNG。目安は**20文字マデ**
例) `gglf_tegaki_beu`の`tegaki_beu`で10文字
:::

　
例えばこんな感じ(抜粋)：
```html:html解説用
1） いいですか、<span class="gglf_boldec">この部分だけ装飾</span>します

2） 部分的に<span class="gglf_min">この部分だけ明朝体</span>です

3） なんということでしょう、<span class="gglf_fancybold">なんということでしょう。</span>

4） 心の中だけで<span class="gglf_solidbold">「“ごめん”で済んだら警察はイラネーんだよ！！」</span>と吐き捨てた

5） 思わず<span class="gglf_suteki">｢…なんて素敵なんでしょう！｣</span>と呟いていたのでした

６） 冷蔵庫に貼ってある<span class="gglf_tegaki_beu">牛乳買ってきて</span>のメモ
```

:::note
ここに挙げた例文のclass名はテキトーです。
`gglf_fancybold`と`gglf_solidbold`、`gglf_suteki`、他いろいろ。
:::

:::note
class名をfont名そのものにしてもいいでしょう。
例) `gglf_klee` `gglf_klee-one`
(その場合、半角スペースを作らないように気を付ける)
:::


　

🟩 JS
設定用に変更するカ所・何か指定するヶ所などもナシ。
JSコード走らせる前に書いとくこと、変更すること何も無し。

:::note warn
JS部はまるっとコピペでOK
:::



　
#### 🟩 CSS
<font color=seagreen>上記のclass名の具体的内容。</font> この場合 (1) <font color=seagreen>**Google Fonts名の指定**</font>、(2) 必要なら文字大きさを`font-size`で微調整、色を変えたいなら`color`で指定etc...。基本的にfont-familyのみの設定。

例えばこんな感じ(抜粋)：
```CSS:CSS解説用
/* キラキラ系 */
.gglf_suteki{
  font-family: 'Kaisei Decol', serif;
  font-size:1.05em; /*文字大きさ合わせ微調整*/
/*__このほか、
  font-family: 'Kaisei Opti', serif;    *-ゴシック体?+飾り*
  font-family: 'Kaisei Tokumin', serif; *-明朝体?+ややレトロ風味*
  font-family: 'Kaisei HarunoUmi', serif; *-明朝体+飾り*
など__*/
}

/* 手書きのキレイなペン字お手本的 - フォント名をclass名に */
.gglf_klee{
  font-family: 'Klee One', cursive;
}
```

:::note
Google Fontsのページで目的のFontをselectしたら、右サイドバーとして表示されるエリアに、コピペできる欄に載るのでそこからコピぺ。
:::
　
### Step 3： JSコード走らせる = 自動適用

__Blog (or Web)：__

:::note
コンソールの表示について：
`F12`とか、`⌘`+`U`+`I`(Macの場合)とか`Ctrl`+`Shift`+`I`(Winの場合)とか、使ってるシステムでも、あるいはブラウザごとでもコンソール表示のやり方違う。 個人的にはキーボードショートカットが早くていいなと思ってる。
:::

__HTML：__
とくにするることなし。

　
HTMLとCSS に不備がなければもう完成。具体的には次の2点：
- HTMLのタグに設定する`class`名
- CSSに設定する`class`名と、Google Fontsの名前


HTMLのタグ内とCSSで指定の`class`名が一言一句間違ってなければ
＆
Google Fontsの名前が一言一句間違ってなければ不備ナシ。

以上です。



　
## おまけ：JSメモ
以下おまけで自分覚書き・JS確認用。文字列操作、配列操作についてのやつ。
基本的に以前記事にまとめた [[js]文字列操作、配列操作](https://qiita.com/Reng/items/0bfc25ce9f14e83b0b61) と同等の内容が多く含まれる。
<!--
https://qiita.com/Reng/items/0bfc25ce9f14e83b0b61
-->
　
`Array.from(◻︎)`
配列のようなナニか`◻︎`から新たに配列を作成.

`join('◯')`
**配列**のすべての要素をつないで文字列に. 引数で指定した`◯`でつなぐ. 
→splitで(文字列から)配列化してたものを、また文字列として出力

`const □ = 〇〇.map( v => /* vの処理1行 */,[])`
処理結果を新しい配列`□`として生成. 
これは下記のように複数行にわたるやつの1行でも済む版.
```JS:JavaScript
const □ =◯◯.map( (value,index,array) =>{
  /*処理  value = なんちゃら、どうちゃら*/
  return value; 
},[])//←※[]の中に配列要素として入り、新配列として □=[value, value, value...]が生成
```
　
`const □ = [...◯◯]`
配列中、展開するアレ。`□`は配列名.
`...`てんてんてん、スプレッド演算子。
　　<font color=darkgray>例) これは 配列`◯◯`である
　　`const fruit_vigeArr=[ ['りんご', 'みかん'], ['玉ねぎ','ニンジン']]`
　　が
　　`const ingredientsArr=[...fruit_vigeArr]`
　　で
　　配列`□`として次のように‘展開’され
　　`ingredientsArr=['りんご', 'みかん', '玉ねぎ','ニンジン']`
　　となる、という感じ</font>
　　

`for(let ◯ of □){ /*処理*/ }`
配列や`Map`や`Set`で使うroop処理・繰り返し処理.
　　<font color=darkgray>`□`は配列名かMap. 変数`○`は一般的なfor処理でいう 
　　`for(let i; i<□□.length; I++){/*処理*/}`の、`□□[i]`にあたるやつ.</font>

`new Set(○○)`、`const △△ = [...new Set(○○)]`
重複を取り除いた配列みたいなものを得る.
元の配列`○○`から新たに重複のない配列`△△`を作る.  `Set`とスプレッド演算子で作る版.
`Set`が重複した値がない配列みたいなもの.
　　<font color=darkgray>ちなみに SetとArray.fromで作る版の
　　`const △△ = Array.from(new Set(○○))`も同じような働き</font>

`trim()`
文字列の前後にある空白や改行を消す

　
〜〜以上、以前の改良前の記事と同じ〜〜

　
`○.insertAdjacentHTML('XX',△▽)`
指定タグ`○`の内側や外側など任意の位置に追加. `XX`は位置、`△▽`はhtmlタグ含む内容.
`'XX'`の位置は下記の4種が指定可： 
　`beforebegin`：`beforeChild`みたいな位置に
　`afterbegin`：指定タグの中の最初
　`beforeend`：指定タグの中の最後
　`afterend`：`appendChild`みたいな位置に
　<font color=darkgray>⚠️註)`'XX'`の位置指定は`○.insertAdjacentHTML('beforeend',△▽)`のように`'`(シングルクォーテーション)または`"`(ダブルクォーテーション)が必要</font>

```html:html階層ざっくり解説 
<!--************************************
insertAdjacentHTML で使える、それぞれの位置関係
----------------------
<element> は指定タグ
<child> は指定タグ内にある子要素
        (ない場合、1つある場合、複数ある場合)
************************************-->
<!-- beforebegin -->
<element>
  <!-- afterbegin --> <!-- ←※タグelementにinnerHTML = で追加する場合ココ(element内は空として)-->
  <child>Text</child>
  <child>Text</child>
  <!-- beforeend --> <!-- ←※タグelementにinnerHTML += で追加する場合ココ(element内にchildがあるとして)-->
</element>
<!-- afterend -->
```
　
`window.getComputedStyle(◯)`
CSSで設定した値取得. `◯`はこれより前に取得済みの
`const ◯ = document.getElementById(id名)`などの変数`◯`

`split('◯',0)`
対象文字列を引数で指定した`◯`で区切って取得しそれぞれを配列の要素に
→`split(',')[0]`で使ってる。 `,`(カンマ)で区切って続けての`[0]`でその区切った前側を指定

`replace(△,◯)`、正規表現版の`replace(/△/g,◯)`
入れ替え. Google Font名を`<link>`タグで使う書式に書き換える時用.
→`replace(/"/g,'')`で、`"`をナシに. `g`オプションで全部の`"`が対象
→`replace(/ /g,'+')`で、(分かりにくいが)半角スペースを`+`(半角プラス)に

⇒例、取得した`"Klee One"`が `split`と`replace`で`Klee+One`に.