LoginSignup
0
2

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-05-13

要約

  • データurlを使う
  • 「"」と「'」を「\」でエスケープする
  • 「#」をurlエンコード(%23)する
  • 色指定はrgb()か色名を使って「#」を避ける
  • </style>を<\/style>にエスケープする

背景

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

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

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

データ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
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
「ファイル→書き出し→スクリーン用に書き出し」で出力形式を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 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を邪魔する文字を入れてどこでエラーが出るか調べる。

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

余談

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

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2