Angular Universalのサーバー(AWS Lambda)側で独自フォント(TTF/WOFF/WOFF2)がデコードできないときの対処法


※ 当ページには【広告/PR】を含む場合があります。
2022/03/07
2024/03/24
【Angular12対応】Angular UniversalでSSR入門〜Webサイト構築
【Angularの新しいSSR環境】「Angular Universal」から「@angular/ssr」へのマイグレーションガイド
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」という見たことも無さそうな内部エラーでスタックしているようです。

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


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート・2022年版】Angular(JSフレームワーク)をこれから学びたい人のためのオススメ書籍&教材特集

Angular UniversalからAWS Lambda経由で独自Fontファイルを読み込みする

この問題はAngular特有のバグというわけではなく、実際にはAWS Lambdaを利用するExpress.jsベースのサーバーレスアプリケーション内部の設定不備で起こるものと考えられます。

Lambda側からのサーバーレスポンスに起因するものですが、表示されるエラーの内容はブラウザごとに異なるため、とりあえず
FirefoxChromeで少しまとめてみます。

Firefoxの場合

例えば当ブログでは、数式をブラウザへ表示するために、「katex」のフォントをインライン化して利用しています。

参考|KaTeX

Lambda側から支給される独自フォントファイルが正しくデコードされていないと、Katexのフォントが正常に適用されない状態で表示されてしまいます。

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

ブラウザからのデバッグコンソールからは、例えば以下のようなエラーが記録されています。

            
            #...
downloadable font: rejected by sanitizer (font-family: "KaTeX_Main" style:normal weight:400 stretch:100 src index:0) source: https://deep.tacoskingdom.com/KaTeX_Main-Regular.woff2
downloadable font: incorrect file size in WOFF header (font-family: "KaTeX_Main" style:normal weight:400 stretch:100 src index:1) source: https://deep.tacoskingdom.com/KaTeX_Main-Regular.woff
downloadable font: rejected by sanitizer (font-family: "KaTeX_Main" style:normal weight:400 stretch:100 src index:1) source: https://deep.tacoskingdom.com/KaTeX_Main-Regular.woff

downloadable font: bad table directory searchRange (font-family: "KaTeX_Main" style:normal weight:400 stretch:100 src index:2) source: https://deep.tacoskingdom.com/KaTeX_Main-Regular.ttf
downloadable font: bad table directory entrySelector (font-family: "KaTeX_Main" style:normal weight:400 stretch:100 src index:2) source: https://deep.tacoskingdom.com/KaTeX_Main-Regular.ttf
downloadable font: bad table directory rangeShift (font-family: "KaTeX_Main" style:normal weight:400 stretch:100 src index:2) source: https://deep.tacoskingdom.com/KaTeX_Main-Regular.ttf
downloadable font: Invalid table tag: 0x604F53 (font-family: "KaTeX_Main" style:normal weight:400 stretch:100 src index:2) source: https://deep.tacoskingdom.com/KaTeX_Main-Regular.ttf
downloadable font:  (font-family: "KaTeX_Main" style:normal weight:400 stretch:100 src index:2) source: https://deep.tacoskingdom.com/KaTeX_Main-Regular.ttf
downloadable font: rejected by sanitizer (font-family: "KaTeX_Main" style:normal weight:400 stretch:100 src index:2) source: https://deep.tacoskingdom.com/KaTeX_Main-Regular.ttf
#...
        
エラーの内容がより細かいところまで指摘されていますが、おおよそ「読み込んだフォントファイルの構造がおかしい」という話のようです。

Chromeの場合

もう一つChromeの方でも症状を確認してみます。

こちらも先程のFirefoxとほぼ同じ見た目で数式が表示されています。

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

異なるのはエラーの表示で、

            
            Failed to decode downloaded font: <URL>
19:1 Failed to decode downloaded font: https://deep.tacoskingdom.com/KaTeX_Main-Regular.woff2
19:1 Failed to decode downloaded font: https://deep.tacoskingdom.com/KaTeX_Main-Regular.woff
19:1 Failed to decode downloaded font: https://deep.tacoskingdom.com/KaTeX_Main-Regular.ttf
19:1 OTS parsing error: Size of decompressed WOFF 2.0 is less than compressed size
19:1 OTS parsing error: Failed to convert WOFF 2.0 font to SFNT
19:1 OTS parsing error: incorrect file size in WOFF header
        
となっています。

こちらは、Font構造のどこがどう悪いのか詳細は出てこない代わりに、
「Failed to decode downloaded font.(フォントがデコードできません)」といった淡白な趣旨が表示されているだけになっています。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート・2022年版】Angular(JSフレームワーク)をこれから学びたい人のためのオススメ書籍&教材特集

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

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

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

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

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


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート・2022年版】Angular(JSフレームワーク)をこれから学びたい人のためのオススメ書籍&教材特集

(折角なのでおさらい)独自フォントをAngularアプリに仕込む

この記事の趣旨としては、AWS Lambdaを使ったサーバレスアプリを使う際には、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アプリで独自フォントが読み込まない場合に注意したいときの話でした。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート・2022年版】Angular(JSフレームワーク)をこれから学びたい人のためのオススメ書籍&教材特集

参考サイト

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メインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。

合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート・2022年版】Angular(JSフレームワーク)をこれから学びたい人のためのオススメ書籍&教材特集