Advent Calendar 3日目:SVG画像を1キロバイトでも削るダイエット術!

これは GraphicalWeb (CSS, SVG, WebGL etc) Advent Calendar 2012 - Adventar への参加の記事です。
3日目の今日は私 id:rikuo がSVG画像ファイルの軽量化テクニックを取り上げてみます。
ちょっと長くなってしまったので、ご注意ください。

はじめに、SVGとは?

まず始めに、SVGとは何か?の簡単な説明です。
SVGとはScalable Vector Graphics(スケーラブルベクターグラフィックス)の略で、XMLを基盤に書かれた画像記述言語、またはベクター画像ファイルのことです。画像ファイルというと、JPEG、GIF、PNGまたBMPなどはよく知られていますね。それらの画像はピクセル(ドット)ごとに色を表現したもので、総じてラスター(ビットマップ)画像と呼ばれています。
対してSVGは座標や図形で扱うため、拡大・縮小に強いです。

ベクター画像とラスター画像比較のサンプルページ
もしくはベクター画像、という言い方よりも「ベジェ曲線」や「ベジ絵」の方が分かりやすいかもしれませんね。

認知度はいまいちだけど実は古い規格

そんなSVGですが正直知らない人は多いと思います。しかし、規格としてはかなり古く
Scalable Vector Graphics - Wikipedia

  • 2001å¹´9月4æ—¥ - SVG 1.0 が W3C 勧告となる
  • 2003å¹´1月14æ—¥ - SVG 1.1 が W3C 勧告となる
  • 2003å¹´1月14æ—¥ - SVG Tiny と SVG Basic (モバイル SVG プロファイル)が W3C 勧告となる
  • 2008å¹´12月22æ—¥ - SVG Tiny 1.2 が W3C 勧告となる
  • 2011å¹´8月16æ—¥ - SVG 1.1 (Second Edition) が W3C 勧告となる
http://ja.wikipedia.org/wiki/Scalable_Vector_Graphics

10年以上前にW3C勧告になっていたんですよね。
ただ、Internet Explorerが長らく標準で対応していなかったこともあってか、規格として規定されていても現実問題としてなかなか使いにくい状況でした。

近年見直されているSVG

しかし近年は徐々にSVGが見直されつつあります。
その要因としては、2011年にリリースされたInternet Explorer 9以降、SVGを標準で対応するようになったこと、また携帯端末などでディスプレイの高解像度化が進み、その携帯端末でピンチアウトで(指で広げる動作)画面を拡大したり、と画面や画像を大きく拡大して見る場面が増えてきたこともあります。
その際、ラスター画像だとどうしても輪郭がボケたり、キザギザが目立ったりしてしまうこともあって、解像度を気にせずに使えるベクター画像の需要が高まっています。
例えば Apple社のサイトはSVGを要所要所に取り入れていますね。

ヘッダ部分を拡大したところ

虫眼鏡の部分がSVGで、「サポート」の字はPNG画像です。
このように拡大してもキレイなのがSVGの特徴の一つです。


ただIE8以下は依然SVGには未対応ですし、スマートフォンの分野ではAndroid OSも標準で対応したのは 3.x以降と、まだまだレガシーな端末を気にせず簡単に使えるという状況ではありませんが、徐々に環境が整ってきつつある、というのは確かです。

SVGの制作に当たって

SVGはXMLを基盤にしているためテキストエディタでも編集できるものの、さすがに複雑なイラストや図表をテキストエディタで描くのは難しく何らかのオーサリングツールを使うことになります。
代表的なものはこの2つのドローソフト。

AdobeのIllustratorとInkscape、前者は単体で購入すると8万円程度するソフト、後者はオープンソースのソフトです。Inkscapeも魅力的ではあるのですが、ちょっとお高いとは言えそこはAdobeの製品、使いやすいように各種機能が整備されているので実際制作するに当たっては、Illustratorを用いることが多いのではないでしょうか?


しかしIllustratorを使って制作した際、気を付けないとSVGに書き出した時に無駄にファイルサイズが多くなってしまうことがよくあります。有線の環境ではブロードバンドの時代が来て久しいですが、最近では携帯端末での利用も多く快適なネット体験のためにできれば不要なファイルサイズは抑えたいですよね。
では、どのようにすればいいでしょうか?


……ということで前置きがかなーり長くなってしまいましたが、今回は主にAdobe Illustratorで制作したイラストや図表などをSVGにする際に注意する点、また作画する時点でなるべく軽量化する方法などを紹介していきます。




ここからがようやく本題です。


軽量化ツールを活用する

まず最初に紹介するのは自動で軽量化してくれるツール

それぞれ無駄なコメントアウトの削除や、使われていないIDの削除、座標指定の最適化処理などを行います。
Illustratorから出力したSVGファイルに対して適用できるのはもちろんですが、特に前者のツールはInkscapeには標準で搭載されており、保存する際に「ファイルの種類」から



「最適化SVG(*.SVG)」を選択すると



「最適化SVG出力」

というscourを使った出力ダイアログが表示されます。

svgz、gzip圧縮を使う

SVG画像の拡張子は、" .svg "ですが、" .svgz "という形式もあります。これは簡単に説明すると、SVG画像をgzip圧縮して拡張子を" .svgz "に変えたものです。上述の通り、SVGはXMLを基盤にしたものなので gzip圧縮は非常に効果的です。
方法としては、

「別名で保存」から

「ファイルの種類」で

「SVG圧縮(*.SVGZ)」を選択します。


例えば 先日描いたヘビのイラスト は



Carpet python by rikuo is licensed under a Creative Commons Attribution 3.0 Unported License.
Based on a work at http://www.flickr.com/photos/brisbanecitycouncil/5278832161/.

IllustratorからSVGで出力すると1877KBだったのに対し、SVGZでは371KBになりました。
Carpet_python.svg 1877KB
Carpet_python.svgz 371KB
ファイルサイズを減らすのには最も効果的な手法ですが、あくまで圧縮ということで元々のファイルサイズを減らすことも無駄ではありませんし、インラインでSVG要素として記述する場合など圧縮できない状況もあります。


IllustratorからSVGを書き出す際のオプション選択

IllustratorからSVGを書き出す際には様々なオプションが選べますね、それの主要な項目を解説していきます。


SVGプロファイルは「SVG 1.1」を選択

軽量化とはあまり関係ない項目ですが、SVGプロファイルの選択の部分。
ここは「SVG 1.1」を選択するのがいいでしょう。SVG TinyやBasicとは携帯電話などの媒体で使われる状況を目的として策定された規格で使える機能が少なくなっています。そのため Tiny に対して SVG 1.1 のことは SVG fullと呼ばれています。
ただTinyなのはあくまで仕様上の問題で、かと言ってfullには対応していないが Tiny/Basicにだけは対応できている環境がそれほどあるか?というとそうでもなくそれらを選ぶメリットはあまりありません。

フォントの設定

文字は「SVG」にして、サブセットは「なし(システムフォントを使用)」がいいでしょう。ただ文字周りはブラウザ間での差異も多々あるため(特に縦書き)、容量が増えてしまいますがいっそアウトライン化してしまうのも一つの手です*2。

画像:参照方法

こちらは「埋め込み」を選択するとその画像をBase64エンコードしてしまい、ファイルサイズが肥大化してしまいます。「リンク」を選択しましょう。

Illustratorの編集機能は保持しない


元データはIllustrator形式で手元に保管し、Webで公開する場合には余計な情報を削っておくのがいいでしょう。

CSSプロパティは「スタイル要素」

ここからは「詳細オプション」をクリックしてさらに細かく設定していきます。

CSSプロパティは

  • プレゼンテーション属性
  • スタイル属性
  • スタイル属性(実体参照)
  • スタイル要素

の4種類があります。
(X)HTMLとCSSの知識が無いとなかなか理解が難しいと思うのですが、この設定も全体のファイルサイズに大きく影響がある重要な選択ですので、詳しく解説していきます。
CSSプロパティとはそれぞれどういう事かというと、例えばこうした図形があったとします。

100ピクセル×100ピクセルの領域の中央、50ピクセルのところを中心に半径40ピクセルの正円を配置しました。このときCSSプロパティのそれぞれでどのように変化するか見てみましょう。
こちらのページに各種設定ごとのソースも表示したのですが、
CSSプロパティの検証ページ 1

ここでも軽く紹介。

プレゼンテーション属性

ちなみに出力はAdobe Illustrator CS5.1です。

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
	 y="0px" width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<circle fill="#69EBA6" stroke="#0045A6" stroke-width="7" stroke-miterlimit="10" cx="50" cy="50" r="40"/>
</svg>

色々とややこしいですね。必要なところだけ取り出すと

<circle fill="#69EBA6" stroke="#0045A6" stroke-width="7" stroke-miterlimit="10" cx="50" cy="50" r="40"/>

円を描く指定はこの部分、それぞれを解説していきましょう。
circle要素は正円を描画します。
cx属性がx軸の座標、つまり横軸の円の中心点ここでは全体が100なので50は中央です。
cy属性はy座標です、50でやはり中央。
r属性は円の半径です。
ここまでは円の位置と大きさの指定で、他のCSSプロパティ設定と共通の部分です。


今度はその円の塗りと線の色や設定を記述していきます。
fill属性は塗りの設定で #69EBA6 ■
stroke属性は線の設定 #0045A6 ■
strock-width属性は線の太さ
stroke-miterlimit属性は線の角の扱いの指定です。


……というところ。


では続いてスタイル属性

スタイル属性

他の部分は共通なので、circle要素だけピックアップ。

<circle style="fill:#69EBA6;stroke:#0045A6;stroke-width:7;stroke-miterlimit:10;" cx="50" cy="50" r="40"/>

cx属性、cy属性、r属性は同じですね。
今度はstyle属性を使っています。同じ塗りの指定なんですが、fill属性でなくスタイルシートでfillプロパティを使っています。

スタイル属性(実体参照)

スタイル属性(実体参照)は説明がややこしいので、省略。

スタイル要素
<style type="text/css">
<![CDATA[
	.st0{fill:#69EBA6;stroke:#0045A6;stroke-width:7;stroke-miterlimit:10;}
]]>
</style>
<circle class="st0" cx="50" cy="50" r="40"/>

style要素にCDATAセクションがありますが、それを除けばHTMLなどで馴染みのある形態じゃないでしょうか。
circle要素にはclass属性が指定してあり、あと塗りや線の指定はスタイル属性と一緒ですね。




以上が各設定の違いです。
ではファイルサイズ軽量化の観点からすると、どの設定がいいのでしょうか?

今度はこんな画像を作ってみました、これは円が196個 配置され全て同じ塗りと線の設定です。
このときそれぞれCSSプロパティごとの設定を変えて書き出すと

プレゼンテーション属性 スタイル属性 スタイル属性(実体参照) スタイル要素
18.4 KB 19.0 KB 10.6 KB 10.3 KB

(詳しい比較はこちらで行っています CSSプロパティの検証ページ 2)
となりました。
これは「プレゼンテーション属性」や「スタイル属性」は

<circle fill="#69EBA6" stroke="#0045A6" stroke-miterlimit="10" cx="4.5" cy="4.5" r="2.5"/>
<circle fill="#69EBA6" stroke="#0045A6" stroke-miterlimit="10" cx="4.5" cy="11.5" r="2.5"/>
<circle fill="#69EBA6" stroke="#0045A6" stroke-miterlimit="10" cx="4.5" cy="18.5" r="2.5"/>
<circle fill="#69EBA6" stroke="#0045A6" stroke-miterlimit="10" cx="4.5" cy="25.5" r="2.5"/>
<circle fill="#69EBA6" stroke="#0045A6" stroke-miterlimit="10" cx="4.5" cy="32.5" r="2.5"/>
<circle fill="#69EBA6" stroke="#0045A6" stroke-miterlimit="10" cx="4.5" cy="39.5" r="2.5"/>
<circle fill="#69EBA6" stroke="#0045A6" stroke-miterlimit="10" cx="4.5" cy="46.5" r="2.5"/>
<circle fill="#69EBA6" stroke="#0045A6" stroke-miterlimit="10" cx="4.5" cy="53.5" r="2.5"/>
	:
	:

とそれぞれの要素に塗りや線の指定を記述していくのに対し、


「スタイル属性(実体参照)」「スタイル要素」は

<style type="text/css">
<![CDATA[
	.st0{fill:#69EBA6;stroke:#0045A6;stroke-miterlimit:10;}
]]>
</style>
<circle class="st0" cx="4.5" cy="4.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="11.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="18.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="25.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="32.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="39.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="46.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="53.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="60.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="67.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="74.5" r="2.5"/>
<circle class="st0" cx="4.5" cy="81.5" r="2.5"/>
	:
	:

同じ塗りと線の指定であれば、まとめて指定できるためです。
そのため、画風にもよるのですが*3イラストや図表のような場合、「スタイル要素」を選択するとファイルサイズが抑えられます。


小数点以下の桁数を減らす


SVGは図形や線を座標で管理します、例えばこのような図形の場合

<polyline points="40.842,18.015 177.524,88.732 12.031,180.873 "/>

多角線なのでpolyline要素が使われ、こうした記述になるわけですが数字がいくつも並んでいて判別しにくいですよね、もう少し分かりやすく解説すると

(SVGは基本的に左上が原点0です)

<polyline points="
<!-- 点Aのx座標,y座標 -->  40.842,	 18.015
<!-- 点Bのx座標,y座標 --> 177.524,	 88.732
<!-- 点Cのx座標,y座標 -->  12.031,	180.873
"/>

ということになるわけです。点Aは xが40.842、yの18.015の位置で、点Bはxが177.524、yが88.732、点Cは(略)。と、このとき小数点以下が3桁までありますね、これが出力の際に設定した部分です。
もしもこれを1桁にしたらどうなるでしょうか?

<polyline points="40.8,18 177.5,88.7 12,180.9 "/>

1桁だとこうなります。
3桁の数値に比べて記述量が随分減りました。こうした座標は各図形や線など全てに行われるため小数点以下の桁数の見直しは全体のファイルサイズに大きな影響を与えます。しかし安易に桁数を減らしてしまうと拡大した時に少しズレたようになってしまうこともあります。

小数点以下の桁数の検証ページ
例として、このイラストを小数点以下の桁数を1桁から7桁まで出力してみました。
ただ全体の画像サイズとの兼ね合いもありますが、印刷する用途でなくWebで見る分であれば1〜2桁でも十分だと思いますね。
このイラストの場合、桁数の設定によるファイルサイズの変化はこのような感じになります。

小数点以下の桁数1桁 3桁 5桁 7桁
37.6 KB 53.1 KB 67.9 KB 82.2 KB




その他の項目

その他の項目については説明は省略しますが、

設定はそれぞれこのような感じでいいかなと。


以上でIllustratorから書き出す際のオプション設定の話は終了、以下は作画時にできる軽量化テクニックです。


無駄なレイヤーは統合してしまう

無駄にレイヤーを残しているとこのように

<g id="レイヤー_1">
	<polyline points="40.842,18.015 177.524,88.732 12.031,180.873 "/>
</g>

g要素ができてしまいます。
g要素はHTMLでいうところのdiv要素のようなもので、これ自体は描画されないのですが要素をまとめるのに便利なものです。こうしてまとめることでJavaScriptから操作しやすくしたり、後からの管理することを考えたときに必要なレイヤーもあるでしょうが、不必要なものであれば統合するのがいいですね。

必要のないグループ化も解除

グループ化もg要素が作られます。レイヤーと同様に必要な場合も多いわけですが、そうでなければ解除しておくといいでしょう。

グラデーションメッシュは使わない

SVGはAdobe Illustratorの機能を完全に再現できるわけではないので、メッシュオブジェクトは書き出しの際にラスタライズされてしまいます。そのためメッシュオブジェクトは使わない、またはラスター画像として用意するなどの対応策になります。
ちなみに現在策定中のSVG 2にはグラデーションメッシュ機能が新たに加えられるようです(ただし、まだ未確定)。

複合パスにまとめる

直線だけでこのような画像を作ってみました。

このとき、SVGの記述は

<polyline class="st0" points="6.1,11 22.7,11.9 35.7,13 "/>
<polyline class="st0" points="19.2,1.8 15,28.9 17.3,39.2 "/>
<polyline class="st0" points="34.3,41.4 37.1,31.4 7.8,19.1 3.3,30.3 12.5,35.8 27.6,21.9 "/>
<polyline class="st0" points="45.4,20.5 44.9,38.1 49.9,39.7 "/>
<polyline class="st0" points="70.8,40.6 73.6,28.9 66.3,14.7 "/>
<polyline class="st0" points="101,14.4 95.6,9.4 83.6,2.9 "/>
	:
	:

多角線それぞれがpolyline要素として記述されますが、



複合パスで全てまとめると一つの要素にまとまり記述が簡潔になります。
複合パスの検証ページ


使いどころを選ぶテクニックではあるのですが、先日描いたヘビのイラスト では、元の画像が1877KBだったのに対し、複合パスでまとめた結果 1098KBまで削減できました。
Carpet_python.svg 元画像 1877KB
Carpet_python.svg 編集後 1098KB
塗りや線の指定、オブジェクトの重なり順などが関連しなかなか使える状況は難しいですが、一応の効果はある手段です。






かなり長くなってしまいましたが、手軽にできる軽量化テクニックはこのくらいでしょうか。
本当はさらに応用編として「ベジェ曲線をいかにファイルサイズを抑えて描くか?」などといった話も書こうと構想していたのですが、これ以上長くなってしまうと誰にも読まれなさそうなのでまたの機会に。
またそこまでいくと作業コスト的にも見合わず、code golf的な楽しみ方になってしまうのもありますし。

SVG関連リンク

最後にSVGについて学ぶなら参考になりそうなサイトをいくつか

SVG実習マニュアル - 無料チュートリアル:グラフィックス

PDF版が配布されておりかなり分かりやすい内容です。

http://www.hcn.zaq.ne.jp/___/SVG11-2nd/index.html

SVG 1.1 仕様の日本語訳、もちろん参考になるのですが私も最初は難しくて途中で断念しました。ですが上の「SVG実習マニュアル」を読みつつ気になる部分を少しずつ調べていくと理解しやすかったですね。

スタートアップ SVG:特集|gihyo.jp … 技術評論社

2010年7月の連載記事ではありますが、SVGがどういったものかまたどのようなことができるか?という点がつかみやすいかと。

http://www.h2.dion.ne.jp/~defghi/svgMemo/svgMemo.htm

SVGは仕様を学ぶのも重要なのですが、現状ブラウザ対応状況がまちまちなため、いざ実践する段階でかなり躓くところが多々あります。こちらのサイトはその辺りの点で、実際問題どのようになっているのか?や実用的な対処法など充実した内容で参考になるでしょう。

追記

その後、2013年のAdvent Calendarでは今回よりもさらに細かくファイルサイズを削る上級編のテクニックを解説した記事も書いたので興味がある方はそちらも是非
SVG画像を1バイトでも削るためのコードゴルフ

><

*1:2014年1月追記

*2:文字周りの説明は詳しくすると長くなってしまうので、割愛

*3:全ての要素がそれぞれ違う塗りと線ような写実的な・または手描きっぽいイラストの場合は、あまり違いがでないこともあります