SVGをdata URI schemeに変換するとき文字化けしないように必要なこと
現在、パソコン、スマートフォン、タブレットと様々な画面サイズの情報機器が世の中に溢れています。そうした端末に適切に対応するには画像も色々なサイズのものをいくつも用意する必要があるわけですが、SVGのようなベクター画像であればそうした手間も少なくて済むのがメリットです。
例えば、AppleのサイトではロゴにSVGを使っています。
https://www.apple.com/jp/iphone/
そしてそのロゴを調べてみるとSVGをdata URI schemeで指定しているのが分かりますね。
data URI schemeはHTML文書内に記述できるため、画像ファイルのリクエストを減らして素早く画面表示が行えるという利点がありますから、世界中から多くのアクセスを集めるAppleのサイトで採用されているのでしょう。
こうした使われ方も多いことから、data URI schemeを簡単に作成できるWebサービスもいくつかあります。
DataURL.net
ただし、SVGをdata URI schemeに変換する際は気をつけないと予期せぬトラブルが起きてしまいます。
これはシンプルなSVGファイルですが、
こちらを前述の DataURL.net で変換したデータを使うとChromeではPC/Androidともにこのようになってしまいます。
(はてなダイアリーではdata URI schemeは貼れないのでデモページで)
なぜこのように文字化けをしてしまうのか?今回は、data URI schemeの仕様から原因を探り、その解決策も解説していきます。
簡易目次
- data URI schemeとは?そのメリットとデメリット
- さらに詳しくdata URI schemeの仕様について
- data URI schemeと文字コードの指定
- 文字化けを防ぐための対処法
- ブラウザの対応状況はどうか?
- まとめ
詳しくは後ほど説明しますが英語圏ではこの問題が顕在化しないのか、解説記事も他に見当たらないんですよね。
data URI schemeとは?そのメリットとデメリット
data URI schemeとは外部ファイルを指定することなくデータを記述できるURIスキームです。SVGに限らず、JPEGやGIF,PNGなどのラスター画像でも使えますし、テキストファイル、CSS、JavaScript、(X)HTMLファイルなども仕様上は変換できます。
(※ただしIE9以下は画像ファイルにしか対応していません)
data URI schemeの大きなメリットは外部ファイルを指定せずに使えることでリクエスト数の削減になり、その結果描画速度を向上できます。また個人的にはGreasemokeyのようなユーザースクリプトやBookmarklet、ユーザースタイルシートでもよく使いますね、画像ファイルも含めてまとめられるため配布する際にもとても便利な形式です。
ただしデメリットもあります。
デメリット
- 元のファイルサイズから比較すると3割ほど大きくなる
- data URI schemeで指定されたファイルそのものはキャッシュされない
- 携帯機器などでは大きな画像は描画処理に時間がかかる
- IE7以下は未対応
例えばこちらの画像は96バイトのGIFファイルですが
data URI schemeに変換すると150バイトになってしまいます*1。
data:image/gif;base64,R0lGODlhEAAQAJEDAAAAAP/yAGMuB////yH5BAEAAAMALAAAAAAQABAAAAIxnC2ZxycBoWgOghhmSxZnpniRtokLNTwjWklsK5GUiilyKq5g7pYma1O8UohhkXgoAAA7
また、data URI schemeで指定したファイルそのものはキャッシュされないのも特徴ですね。そのため、前述のAppleのサイトのロゴのように複数のページで共通の要素はdata URI schemeを使わず画像としてキャッシュさせた方がトータルでは効率的かもしれません。
そしてdata URI schemeは必然的にデコードが行われます。スマートフォンなど携帯機器で大きな画像のデコードを行う場合は却って描画に時間がかかってしまうこともあります。
あとはIE7以下はdata URI scheme未対応というのもありますね、これはシェアを考えるとそろそろ改善してきていますが。
ですので何でもかんでもdata URI schemeにすれば良い、というわけではなく特徴を理解して利用するのが大切です。
さらに詳しくdata URI schemeの仕様について
data URI schemeの仕様はRFC2397(日本語訳)で規定されています。
ここでは「Hello world!」と書かれた12バイトのテキストファイルをdata URI schemeにして解説していきます。
data:text/plain;charset=US-ASCII;base64,SGVsbG8gd29ybGQh
URIスキーム
data:はURIスキームです。URI(URL)の先頭部分というと分かりやすいでしょうか。
URIスキームは他には例えばメールアドレスを記載する際によく使われる mailto: もその一つですね。ftp: 、http: 、https: もURIスキームの仲間です。
参考記事:URI scheme - Wikipedia
mediatype
mediatypeは対象のデータのContent-typeを記述します。
Content-type
仕様ではこのように規定され
mediatype := [ type "/" subtype ] *( ";" parameter )
typeとsubtypeでファイルの種類を記述します。代表的なContent-typeはこんな感じです。
種類 | 拡張子 | Contents-type |
---|---|---|
テキストファイル | txt | text/plain |
HTML (Hyper Text Markup Language) | htm, html | text/html |
CSS (Cascading Style Sheets) | css | text/css |
JPEG (Joint Photographic Experts Group) | jpg, jpegなど | image/jpeg |
GIF (Graphics Interchange Format) | gif | image/gif |
PNG (Portable Network Graphics) | png | image/png |
SVG (Scalable Vector Graphics) | svg, svgz | image/svg+xml |
データ形式
base64形式はアルファベットの小文字a-z、大文字A-Z、数字0-9の計62字とさらに / と + と = の3字を加えた文字を使ってデータを記述する形式です。64文字じゃないですが名称はbase64。
ただdata URI schemeには必ずしもbase64形式を使う必要はなく、パーセントエンコード形式を使っても構いません。
データ
ここは変換されたデータ部分です。
つまり、ここまでの仕様をおさらいすると
「Hello world!」と書かれたテキストファイルのdata URI schemeは先ほど紹介した通りこのようになりますが
data:text/plain;charset=US-ASCII;base64,SGVsbG8gd29ybGQh
文字コードを省略した場合「charset=US-ASCII」がデフォルトとなるため、こう記述してもいいですし
data:text/plain;base64,SGVsbG8gd29ybGQh
Content-typeは「text/plain」がデフォルトですからさらに省略できますし
data:base64,SGVsbG8gd29ybGQh
base64形式でなくパーセントエンコード形式でもOKなので
data:,Hello%20world%21
と簡略した記述もできるわけです。
data URI schemeと文字コードの指定
data URI schemeの仕様を学んだところで、DataURL.netで変換した前述のGIF画像を再度確認してみましょう。
data:image/gif;base64,R0lGODlhEAAQAJEDAAAAAP/yAGMuB////yH5BAEAAAMALAAAAAAQABAAAAIxnC2ZxycBoWgOghhmSxZnpniRtokLNTwjWklsK5GUiilyKq5g7pYma1O8UohhkXgoAAA7
こちらにも文字コードの指定がありませんね。
JPEGやGIF,PNG画像といったバイナリデータならばそれでも問題はありません。むしろ文字コードの記述を削った方がファイルサイズの削減につながります。
さらにIE8-9では画像ファイル以外にdata URI schemeには対応しておらず、他のファイルをdata URI schemeにするということも滅多になかったために長らく文字コードを指定しないで使われることが多くなりました。
そのためかWebの技術を紹介する海外・国内のブログなどでも
という誤った認識がどうやら流布していったようなんですよね……。
これは
(※JPEG,GIF,PNG画像ならば)data URI schemeには文字コードの指定は必要ない
というのは確かに事実ではあるのですが、いくつかの技術記事の孫引きを経てその注釈が無くなっていったのではないかなー、と。
SVGで表出する問題と表出しない問題
バイナリデータであるJPEG,GIF,PNG画像とは異なり、SVGは画像ファイルではあるのですが実態はXMLを基盤としたテキストデータですから適切な文字コードの指定を行わないといけません。
例えばAppleのdata URI schemeをもう一度 確認すると。
こちらも文字コードの指定がありませんね。
SVGファイル自体にはXML宣言で文字コードがUTF-8と指定されているにもかかわらず、この場合は仕様通り「charset=US-ASCII」として扱われることになります、……が、英語圏では英数字しか使われないためそのままでも問題が起きずに見過ごされています。
data URI schemeに変換する機能を持つライブラリやサービスはいくつかあるのものの、文字コードを適切に処理するタイプは少ないんですよね……。
具体的に言うとSVG関連だと利用者の多い、最適化ツールのSVGOもdata URI schemeを書き出す機能がありますが文字コードの指定はされません。
SVGO - GitHub
ここまでをまとめると
- 従来data URI schemeの文字コードの指定を省く事例が多かった
- JPEG,GIF,PNG画像のようなバイナリデータならそれでもOKだった
- やがていつの間にか、文字コードの指定が必要ないという誤解が広まる
- しかしSVGはテキストデータなので文字コードの指定が必要
- ……なのだが英数字しか使わない英語圏では省いても問題が起きない
- data URI schemeの変換サービス、ライブラリが未対応なケースが多い
という感じです。
文字化けを防ぐための対処法
誤った文字コードの指定で起きる文字化けを防ぐため、正しく文字コードを示しましょう。
このSVG画像は
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="25"> <text x="5" y="20" font-size="20" stroke="none" fill="#000">abc123日本語</text> </svg>
こうした構造になっており、文字コードはUTF-8です*2。
ですから、data URI schemeにするときにはこのように
data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMjUiPjx0ZXh0IHg9IjUiIHk9IjIwIiBmb250LXNpemU9IjIwIiBzdHJva2U9Im5vbmUiIGZpbGw9IiMwMDAiPmFiYzEyM+aXpeacrOiqnjwvdGV4dD48L3N2Zz4=
適切に指定しましょう。
今回の例ではtext要素で文字列を扱いましたが、SVGの仕様から制作者が任意の文字列を入れられるのはtext要素、title要素、desc要素、metadata要素、id名、class名、フォント名などと多岐にわたります。
特に制作ソフトが Adobe Illustrator だと、レイヤー名やブラシの名前が意図せずそのままidに使われることもありますから、仕様に詳しくないデザイナーがうっかり制作したSVGファイルが文字化けしたり、表示されない事態を避けるためにも、文字コードの指定を必ず入れるようにするといいのではないでしょうか。
あとちなみにbase64形式ではなく、パーセントエンコード形式で行うならこうなります。
data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%2225%22%3E%3Ctext%20x%3D%225%22%20y%3D%2220%22%20font-size%3D%2220%22%20stroke%3D%22none%22%20fill%3D%22%23000%22%3Eabc123%E6%97%A5%E6%9C%AC%E8%AA%9E%3C%2Ftext%3E%3C%2Fsvg%3E
パーセントエンコード方式はbase64形式に比べてファイルサイズが増えてしまうのが欠点ですが、可読性の面で利点がありますし*3、また文字列の操作だけで手軽に色やサイズの変更を行いやすいため、JavaScriptやCSS拡張メタ言語 SCSS(Sass)やLESSでも扱いやすいというのがメリットでしょうか。
ブラウザの対応状況はどうか?
仕様はさておき、肝心のブラウザ対応状況はどうよ?という点を検証してみました。
実のところ、data URI schemeで文字コードの指定を省いていたとしてもブラウザ側でうまいこと処理してくれるケースもあります。
仕様上SVGをHTML5で指定できる要素はインラインSVGを除くと概ね以下の通り
- object要素
- embed要素
- iframe要素
- img要素
- input要素(type:image イメージボタン)
- favicon
- CSSによる指定
- background-image
- border-image
- list-style
- filter
- clip-path
- Web fonts *4
まだ実装が揃っていないものもありますし(faviconなど)、これ以外にもリンクからdata URI schemeを直接表示に表示させる方法もありますね*5。これらに文字コードの指定を省いたdata URI schemeを適用して文字化けをするか検証してみました。
検証用のデモページ
文字コードの指定が無いことで文字化けするものが×、指定の有無に関係なく表示するのは○です。
object | embed | iframe | input | img | CSS background-image | 直接表示 | |
---|---|---|---|---|---|---|---|
PC Chrome38 | × | × | × | ○ | ○ | ○ | × |
PC Opera24 | × | × | × | ○ | ○ | ○ | × |
PC Firefox32 | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
PC IE11 | ○ | ※ | ※ | ○ | ○ | ○ | ※ |
Android Chrome38 | × | × | × | ○ | ○ | ○ | × |
iOS7 Safari | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
iOS8 Safari | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
環境はWindows7 64bit、AndroidはNexus7(2013)、iOSはiPhone5とiPad(第3世代)です。
IE11はセキュリティの懸念からSVGに限らずdata URI scheme自体がiframe要素などで使えない(参考記事)ため、※マークです。
文字コード指定の有無が関連するのはPC/AndroidのChromeとOperaですね。
しかし実際の用途としては、JPEG,GIF,PNG画像を置き換える目的でSVG画像を使うでしょうし、その際は主にimg要素やbackground-imageで使用するために影響は少なさそうです。
ちなみになぜimg要素などは大丈夫な一方で文字化けする要素があるかというと、SVGは使用する要素によって挙動が異なる「参照モード(Referencing modes)」があります。
その区別で言うところのDynamic Interactive Modeで動作するobject,embed,iframe要素及び直接表示ではブラウザの扱いが異なるために文字コードの指定の有無が影響を及ぼすようです。
参考記事:svgの表示方法についての補足 - svg要素の基本的な使い方まとめ
まとめ:実際のところ
長々と書いてきましたが、実状としてはdata URI schemeでSVGを指定する場合はおそらく小さいアイコンやロゴを使うことが多いでしょうから、英数字以外の文字列が用いられることもまず無く、現状でもさしてトラブルは起きないでしょう。
ただ誰かがまとめておかないと、このノウハウは共有されないままだな、きっと……と思ったので書きました。
この問題はSVGに限らず(X)HTMLやCSS、JavaScriptファイルでも同様なのですが、それらをdata URI schemeにするケースはあまりない、というのも顕在化しない理由なのかもしれませんね。
><
*1:この例では1.5倍くらいになっていますが、「data:image/gif;base64,」が22バイト、データ部分は128バイトと、データに限って言えば3割ほどの増大です
*2:そのためXML宣言は省略されています、詳しくは以前書いた記事で SVGコードゴルフ - 条件を満たせばXML宣言は省略できる
*3:普通の人はSVGファイルのソースを読まない、……というツッコミはさておき
*4:SVG fontsのサポートはChrome38以降はWindows Vista/XPだけになったことを鑑みると、敢えて使う理由は特段なさそうですが。参考記事:Chrome 38 Beta: New primitives for the next-generation web - Chromium Blog
*5:クライアントサイドで動作するサービスで生成したSVG画像をダウンロードさせたい場合などにそうした手法を使います。例えば手動SVG組版機など