LoginSignup
0
1

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

Last updated at Posted at 2023-04-09

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

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

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

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

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

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

序文:百聞は一見に如かず 〜CodePen〜

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

CodePen埋め込み 【長文編:複数行・長文編】

See the Pen subset Ja font - Google Fonts by Reng (@Reng_code) on CodePen.

 
CodePen埋め込み 【短文編:1行以内・短文編】

See the Pen subset Ja font - Google Fonts by Reng (@Reng_code) on CodePen.

部分的に使うと実際どういう見た目になるか という例としてご覧ください。

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

手っ取り早く やり方を知りたい → 🟩HTML のカ所へGO

ざっくり解説

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

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

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

&text=を使わない=膨大な量の日本語(漢字ひらがなカタカナ記号類)の‘文字’をロードすることになる

&text=を使う=(量にもよるが)必要な分だけなので相当減らせる、省エネが過ぎる(!)

想定、前提など

前提:

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

想定読者:

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

その他:

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

 

JSコード

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

興味がある人用JSコード表示(⚠️長めなので閲覧注意)...開閉エリア
  1. メインなfuncで使う汎用コード群
  2. メインなfunc群

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

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

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) }

 
2. メインなfunc群:

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だけを表示出来てスッキリと見ることができるから

---------------//開閉エリア--

 

CSSコード

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

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

Google Fonts用のコード群:

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; 
}

以下略

 

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

Code Pen用のコード群:

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'を指定してるが、これらは誰もが持ってるシステムフォントではない。自分のローカル環境にあるプログラミング用フォント(を優先して表示させてみたかっただけ)

---------------//開閉エリア--

 

流れ(フロー)

Step 1: 使うGoogle Fonts の目星をつける ※後回しでもOK
Step 2: JSコード走らせる前に書いとくこと
Step 3: JSコード走らせる = 自動適用
 

Step 1: 使うGoogle Fonts目星をつける

Google Fonts:
まずはGoogle Fontsで使用したい日本語を探す/目星をつける/決める ※リンク先は 日本語 16pt のページ

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

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

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

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

🟩 HTML

(1). blog投稿欄(記事本文)やWebページの本文

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

Webフォントを適用したいカ所に、「gglf_」で始まるclass名を付ける
(google fontの略で gglf。 「googlefont_」だと長いから)

gglf_に続く文字列やたら長過ぎNG。目安は20文字マデ
例) gglf_tegaki_beutegaki_beuで10文字

 
例えばこんな感じ(抜粋):

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>と呟いていたのでした

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

ここに挙げた例文のclass名はテキトーです。
gglf_fancyboldgglf_solidboldgglf_suteki、他いろいろ。

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

 

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

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

 

🟩 CSS

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

例えばこんな感じ(抜粋):

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;
}

Google Fontsのページで目的のFontをselectしたら、右サイドバーとして表示されるエリアに、コピペできる欄に載るのでそこからコピぺ。

 

Step 3: JSコード走らせる = 自動適用

Blog (or Web):

コンソールの表示について:
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]文字列操作、配列操作 と同等の内容が多く含まれる。

 
Array.from(◻︎)
配列のようなナニか◻︎から新たに配列を作成.

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

const □ = 〇〇.map( v => /* vの処理1行 */,[])
処理結果を新しい配列として生成.
これは下記のように複数行にわたるやつの1行でも済む版.

JavaScript
const  =◯◯.map( (value,index,array) =>{
  /*処理  value = なんちゃら、どうちゃら*/
  return value; 
},[])//←※[]の中に配列要素として入り、新配列として □=[value, value, value...]が生成

 
const □ = [...◯◯]
配列中、展開するアレ。は配列名.
...てんてんてん、スプレッド演算子。
  例) これは 配列◯◯である
  const fruit_vigeArr=[ ['りんご', 'みかん'], ['玉ねぎ','ニンジン']]
  が
  const ingredientsArr=[...fruit_vigeArr]
  で
  配列として次のように‘展開’され
  ingredientsArr=['りんご', 'みかん', '玉ねぎ','ニンジン']
  となる、という感じ

  

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

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

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

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

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

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"splitreplaceKlee+Oneに.

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1