はじめに
何の文字をエンコードすればいいのか調査してみた
正しくsvgの何の文字をエンコードすればいいのか、ぐぐっても、どくっても、出てこないのもありました。
前提
以下のようなperlコードにおいて外部ファイル化したSVGを自動で埋め込むこととする
print <<EOM;
.icon-twitter-white {
background: url('@{[&svg("icon-twitter.svg","#000","#fff")]}') no-repeat center;
}
.icon-twitter-black {
background: url('@{[&svg("icon-twitter.svg","#000","#000")]}') no-repeat center;
}
EOM
なお、今回使用するSVGアイコンは以下となります
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 48 48"><path fill="#000" d="M34.87 7.86v1c0 10.53-8 22.67-22.67 22.67A22.49 22.49 0 0 1 0 28a15 15 0 0 0 1.9.12 16 16 0 0 0 9.89-3.42 8 8 0 0 1-7.44-5.54A8 8 0 0 0 8 19a8 8 0 0 1-6.4-7.81v-.11a7.93 7.93 0 0 0 3.62 1A8 8 0 0 1 2.7 1.45a22.63 22.63 0 0 0 16.43 8.33 8 8 0 0 1 13.58-7.27A16 16 0 0 0 37.77.58 8 8 0 0 1 34.26 5a15.82 15.82 0 0 0 4.58-1.26 16.24 16.24 0 0 1-3.97 4.12Z"/></svg>
最初は
開発時、base64なら確実であったのもあるので、めんどくさいので base64でエンコードを行っていました
sub svg {
my($svg,$before,$after)=@_;
my $ret=`cat $svgsrc/$svg|sed -e 's/$before/$after/g'| base64 | sed -z 's/\\n//g'`;
return "data:image/svg+xml;base64," . $ret;
}
実質上のUS-ASCIIで埋め込む
CSSに埋め込むにあたってここが苦労しました。
base64であればすべての文字が肥大化しますので格段に容量が増えますが
US-ASCIIで埋め込めば最小限の文字列ですみます。
sub svg {
my($svg,$before,$after)=@_;
my $ret=`cat $svgsrc/$svg|sed -e "s/'/\\\'/g" | sed -e 's/"/\\\"/g' |sed -e 's/ /%20/g'| sed -e 's/$before/$after/g'| sed -e 's/#/%23/g'|sed -z 's/\\n//g'`;
return "data:image/svg+xml," . $ret;
}
$before、$after はただの色置換です。
置換する必要がなければ、同じ値をいれておきます
また、そう大して負荷がかかるわけではないが、base64をデコードするより、%をデコードするほうのが、CPU的には優しいのかもしれません
URLに記載される :// もエンコードしている例もありますが
エンコードする必要がありませんでした。
# の文字はエンコードしないと使い物にならないのは知っていましたが
以外と「 」(半角スペース)をエンコードしないとだめだったわけです
また、マルチバイト文字が入るわけではないことを前提としていますので、;utf8 の記載は節約しています
生成されたCSS
.icon-twitter-white {
background:url('data:image/svg+xml,<svg%20xmlns="http://www.w3.org/2000/svg"%20version="1.1"%20viewBox="0%200%2048%2048"><path%20fill="%23fff"%20d="M34.87%207.86v1c0%2010.53-8%2022.67-22.67%2022.67A22.49%2022.49%200%200%201%200%2028a15%2015%200%200%200%201.9.12%2016%2016%200%200%200%209.89-3.42%208%208%200%200%201-7.44-5.54A8%208%200%200%200%208%2019a8%208%200%200%201-6.4-7.81v-.11a7.93%207.93%200%200%200%203.62%201A8%208%200%200%201%202.7%201.45a22.63%2022.63%200%200%200%2016.43%208.33%208%208%200%200%201%2013.58-7.27A16%2016%200%200%200%2037.77.58%208%208%200%200%201%2034.26%205a15.82%2015.82%200%200%200%204.58-1.26%2016.24%2016.24%200%200%201-3.97%204.12Z"/></svg>') no-repeat center;
}
.icon-twitter-black {
background:url('data:image/svg+xml,<svg%20xmlns="http://www.w3.org/2000/svg"%20version="1.1"%20viewBox="0%200%2048%2048"><path%20fill="%23000"%20d="M34.87%207.86v1c0%2010.53-8%2022.67-22.67%2022.67A22.49%2022.49%200%200%201%200%2028a15%2015%200%200%200%201.9.12%2016%2016%200%200%200%209.89-3.42%208%208%200%200%201-7.44-5.54A8%208%200%200%200%208%2019a8%208%200%200%201-6.4-7.81v-.11a7.93%207.93%200%200%200%203.62%201A8%208%200%200%201%202.7%201.45a22.63%2022.63%200%200%200%2016.43%208.33%208%208%200%200%201%2013.58-7.27A16%2016%200%200%200%2037.77.58%208%208%200%200%201%2034.26%205a15.82%2015.82%200%200%200%204.58-1.26%2016.24%2016.24%200%200%201-3.97%204.12Z"/></svg>') no-repeat center;
}
しかしなんでsedを使ったのかはわかりません
苦労した理由
svgはXMLなので書式にエラーがあればエラーを出すはずですが、
誤って生成したものをCSSに埋め込むとただ真っ白になるだけ。
かつ、誤って生成したエンコード済のdataスキーマのURLをブラウザに貼り付けても正常に表示されるため
<svg><path fill="></svg>
ためしに100%エラーの出るsvgを上記に記載しましたが、
これが <img src= で読み込まれていたとしても
エラーが出ない理屈と同じなわけです
結論
CSSに埋め込むSVGは、マルチバイト文字がなければ、「#」と「 」(半角スペース)をエンコードすれば良い