CSSに書くインラインSVGをエスケープする

#要約

* データurlを使う
* 「"」と「'」を「\」でエスケープする
* 「#」をurlエンコード(%23)する
* 色指定は[rgb()か色名](https://triple-underscore.github.io/SVG11/types.html#DataTypeColor)を使って「#」を避ける
* &lt;/style>を&lt;\/style>にエスケープする

#背景
アイコンやロゴを表現するときIMGタグの代わりにCSSで書きたい場面はよくあると思う。

```css:画像url.css
.hoge::before{
  content:" ";
  background-image: url("hoge.png");
  width: 28px;
  height: 28px;
  display: block;
  background-repeat: no-repeat;
}
```

url()の中にはファイルのurlやデータurlが書ける。

```css:データurl.css
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に含めようとしても上手く表示できない。

```css:上手く表示できない.css
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ファイルを用意する。
![ill.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563735/b12bbb49-9ebc-3232-9cfb-1ddd2199a4d5.jpeg)
「ファイル→書き出し→スクリーン用に書き出し」で出力形式をsvgにすると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:修正した.svg
<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を邪魔する文字を入れてどこでエラーが出るか調べる。

```php:データurlのエスケープ.php
<!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の中に「&lt;/style>」があるとhtmlの中の&lt;style>タグが終わってしまうので「\」でエスケープする(&lt;link>や＠importで読み込む場合は不要)。
それ以外の文字はエスケープする必要は無いようだ。

#余談
短いsvgならstyleを削って個別のpathにfill=""で色を指定しても良いと思う。