要約
- データurlを使う
- 「"」と「'」を「\」でエスケープする
- 「#」をurlエンコード(%23)する
- 色指定はrgb()か色名を使って「#」を避ける
- </style>を</style>にエスケープする
背景
アイコンやロゴを表現するときIMGタグの代わりにCSSで書きたい場面はよくあると思う。
.hoge::before{
content:" ";
background-image: url("hoge.png");
width: 28px;
height: 28px;
display: block;
background-repeat: no-repeat;
}
url()の中にはファイルのurlやデータurlが書ける。
background-image: url("data:image/gif;base64,R0lGODlhAQABAGAAACH5BAEKAP8ALAAAAAABAAEAAAgEAP8FBAA7");
background-image: url("data:image/gif;GIF89a%01%00%01%00%60%00%00%21%F9%04%01%0A%00%FF%00%2C%00%00%00%00%01%00%01%00%00%08%04%00%FF%05%04%00%3B");
エンコードはbase64かurlエンコードが多いと思う。
しかし、svgをエンコードせずにデータurlに含めようとしても上手く表示できない。
background-image: url('data:image/svg+xml;utf8,<svg id="レイヤー_1" data-name="レイヤー 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.346 28.346"><defs><style>.cls-1{fill:#040000;}</style></defs><path class="cls-1" d="M10.973,2.568S4.033,25.954,4.179,25.936,26.436,14.243,26.436,14.243Z"/></svg>')
これはデータurlを邪魔する文字をエスケープしてないからだけど、
データurl全体をbase64などでエンコードすると後で変更が大変なので必要な文字だけエスケープしたい。
そこで、どの文字をエスケープする必要があるか調べる。
方法
illustratorで適度なイラストを描いてsvgファイルを用意する。
「ファイル→書き出し→スクリーン用に書き出し」で出力形式をsvgにするとsvgファイルを出力できる。
<svg id="レイヤー_1" data-name="レイヤー 1" xmlns="http://www.w3.org/2000/svg" width="10mm" height="10mm" viewBox="0 0 28.346 28.346"><defs><style>.cls-1{fill:#040000;}</style></defs><path class="cls-1" d="M10.973,2.568S4.033,25.954,4.179,25.936,26.436,14.243,26.436,14.243Z"/></svg>
レイヤー名が役に立つ場面は無いと思うので削る。
width、heightはcssで指定したサイズが効かなくなるので削る。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.346 28.346"><defs><style>.cls-1{fill:#040000;}</style></defs><path class="cls-1" d="M10.973,2.568S4.033,25.954,4.179,25.936,26.436,14.243,26.436,14.243Z"/></svg>
cssの中にデータurlを邪魔する文字を入れてどこでエラーが出るか調べる。
<!DOCTYPE html><html><head><meta charset="utf-8"><title></title>
<style>
<?php
$esc = array("'","#",'"');//エラーが起きた文字をエスケープする
for($ascii = 32; $ascii <= 126; $ascii++){
if(in_array(chr($ascii),$esc,true)){
$str = urlencode(chr($ascii));
// $str = '\\'.(chr($ascii));
}else{
$str = chr($ascii);
}
echo '.ascii'.$ascii.'{display:flex;}'.PHP_EOL;
echo '.ascii'.$ascii.'::before{
content:" ";
background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28"><!-- '.$str.' --><defs><style>.cls-1{fill:rgb(0,0,0);}<\/style></defs><path class="cls-1" d="M10.973,2.568S4.033,25.954,4.179,25.936,26.436,14.243,26.436,14.243Z"/></svg>\');
width: 28px;
display: block;
height: 28px;
background-repeat: no-repeat;
;}'.PHP_EOL;
}
?>
</style>
</head>
<body><?php
for($ascii = 32; $ascii <= 126; $ascii++){
echo '<div class="ascii'.$ascii.'">'.chr($ascii).'</div>'.PHP_EOL;
}
?>
</body></html>
結果
「#」はurlエンコードでしか避けられないようだ。
「"」と「'」はurlエンコードでも「\」エスケープでも避けられた。
svgの中に「</style>」があるとhtmlの中の<style>タグが終わってしまうので「\」でエスケープする(<link>や@importで読み込む場合は不要)。
それ以外の文字はエスケープする必要は無いようだ。
余談
短いsvgならstyleを削って個別のpathにfill=""で色を指定しても良いと思う。