Angular Universalアプリのサーバー側で独自フォント(TTF/WOFF/WOFF2)が読み込めなくて困ったときの対処法


2022/03/07
蛸壺の技術ブログ|Angular Universalアプリのサーバー側で独自フォント(TTF/WOFF/WOFF2)が読み込めなくて困ったときの対処法

弊社ではAngular UniversalでWebサイトの構築をやっている傍ら、最近ふとWebサイトのフォントにassets内に置いたローカルのWOFFフォントを適用しようとしたところ、

合同会社タコスキングダム|蛸壺の技術ブログ

というようなエラーが発生して独自フォントが読み込みに失敗していました。

Firefoxのコンソールで確認すると、なにやら
「downloadable font: incorrect file size in WOFF header」「downloadable font: rejected by sanitizer」という見たことも無さそうな内部エラーでスタックしているようです。

手元のローカルでの開発中のときにはこのような挙動は見られなかったので、サーバー側の設定のどこかに不具合がある訳だったのですが、原因が非常に分かりにくいため防備録ついでに解決方法を記録しておきます。


【解決策】Angular Universalでバイナリとして扱うMIMEファイルを追加する

ではまず解決策をザックリと話と、Angular Universalのサーバー処理部分であるlambda.jsの中身を、

            
            //...省略
//👇サーバーから配給するファイルのうちバイナリとして扱いたいMIMEタイプ
const binaryMimeTypes = [
    //...中略
    //👇主要なフォントファイルのMIMEタイプを追記
    'font/eot',
    'font/opentype',
    'font/otf',
    'font/ttf',
    'font/woff',
    'font/woff2'
]
//...
        
とすることで手元の環境でエラーがなくなりました。

デフォルトのままだとLambda側でテキスト形式のファイルとしてサーバー側から送りだしてくれる仕様ですので、
binaryMimeTypesのリストでバイナリであることをあらかじめ伝えていなかったために起こったエラーのようでした。

確かに言われてみるとそうだったけど、地味にExpressアプリ製のサーバー側のMIMEのルール付けは忘れてしまうので、この記事で思いだしていただけたら幸いです。


折角なので独自フォントをAngularに仕込むやり方もおさらい

この記事の趣旨としてはlambda.jsのbinaryMimeTypesをよく確認しましょうということだけですが、独自フォントを定義してAngularアプリ内で使う方法もさらっとおさらいします。

Angularでローカルにおいたフォントの独自定義はプロジェクト任意のCSSファイルから呼び出すこともできますが、どのコンポーネントからでも呼び出せるように
styles.scssの冒頭に記述するのが一般的かと思います。

独自のフォントを使う場合、
@font-face構文でルール拡張を行います。

ここでは仮想的な
HOGEフォントPIYOフォントの2つをローカルファイルとして利用するとして、

            
            //👇HOGE-FONTとしてフォントファミリーを注入
@font-face {
    font-family: 'HOGE-FONT';
    font-style: normal;
    font-weight: 500;
    src: url('/assets/fonts/hoge.woff') format('woff');
}

//👇Piyo-Gothicとしてフォントファミリーを注入
@font-face {
    font-family: 'Piyo-Gothic';
    font-style: normal;
    font-weight: 500;
    //👇形式の異なるフォントファイルを複数指定してファイルの読込み優先度も付けることが可能
    src: url('/assets/fonts/piyo_gothic.woff2') format('woff2'),
         url('/assets/fonts/piyo_gothic.woff') format('woff'),
         url('/assets/fonts/piyo_gothic.ttf') format('truetype');
}

//...以下略
        
と、styles.scssの冒頭に定義を記述しておきます。

Angularのローカルファイルとして扱うアセットファイルは通例として
src/assetsフォルダ以下にまとめて配置するため、そこにfontsというフォルダを作成しフォントファイルを配置して管理します。

先ほどのstyles.scssファイルの例でいうと、

            
            $ tree
.
├── angular.json
├── package.json
#...中略
└── src
       ├── styles.scss
        #...中略
       └── assets
              #...中略
              └ fonts
                ├── hoge.woff
                ├── piyo_gothic.ttf
                ├── piyo_gothic.woff
                └── piyo_gothic.woff2
        
な感じのプロジェクト構造でフォントファイルをおけば良いでしょう。

Angularでの独自フォントを使うのはさほど難しいことではありませんが、@font-faceをローカルから呼び出す際の注意点としては、
url(...)メソッドで指定するリソースファイルまでのパスの書き方が挙げられるでしょう。

            
            src: url('./assets/fonts/hoge.woff') format('woff');
src: url('assets/fonts/hoge.woff') format('woff');
src: url('/assets/fonts/hoge.woff') format('woff');
        
例えばstyles.scssからhoge.woffを参照したい場合、上の例はAngularのコンパイルで全て正常にルートが解決されるので、どれを使っても同じビルド結果を得ます。

ところが、Angular Universalでサーバー側で動作するアプリにしたい場合、バックエンドで働いているExpressエンジンもリソースファイル解釈できるようにしないといけないので、

            
            src: url('/assets/fonts/hoge.woff') format('woff');
        
のみがExpressでも正しくルート解決すると思います。(※ルートのロジックを独自にカスタマイズしたい場合にはserver.tsを修正すうことで柔軟にルーティングを振り分けることも可能です。)

これでAngularアプリが独自フォントを正しく読み込んだ後は、プロジェクトの任意のcss/scssコードから自由に利用可能です。

            
            //...
h2 {
    font-family: "HOGE-FONT";
}
//...
p {
    font-family: "Piyo-Gothic";
}
//...
        
以上、Angularで独自フォントが読み込まない場合に注意したいときの話でした。


参考サイト

OTS parsing error: Failed to convert WOFF 2.0 font to SFNT for font files of Glyphicon for Spring-boot

ノードエクスプレスサーバーがAngular Appのaws lambdaを使用して静的コンテンツをレンダリングしない

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。