Есть ли необходимость в Base64 кодировке SVG изображений?

Перевод статьи:  Probably Don’t Base64 SVG
Автор:  Chris Coyier.

Вы, вероятно, слышали о таком понятии, как URI данные. Это на самом деле неплохой способ доставки ресурса, который в противном случае потребовал бы выполнения отдельного HTTP запроса для своей загрузки. Что касается используемого формата, то здесь есть варианты. В сущности, вы просто указываете тип контента, к которому относится ресурс (к примеру, image/png) и через точку с запятой включаете данные, представляющие необходимый файл.

Вот так:

<img src='data: … '>

или так:

.bg {
background: url('data: … ');
}

Что касается растровых изображений, подобных PNG, то их данные, вне всякого сомнения, должны быть представлены в формате base64. Не берусь утверждать на 100%, так как не являюсь экспертом в этой области, но насколько я понимаю, выбор именно этой кодировки объясняется тем, что она безопасна для использования на HTML и CSS платформах, поскольку в ней используются только допустимые, так называемые безопасные, символы для этих сред.

Так выглядт base64.

Слева представлены PNG данные, содержащие символы, которые потенциально опасны для HTML. Справа то же самое изображение, кодированное в base64 и состоящее из безопасных символов.

Вероятно, более полным будет ответ Дэйва Маркла (Dave Markle) из Stack Overflow:

Никогда не знаешь, что может произойти. Некоторые протоколы могут принять ваши двоичные данные за управляющие символы (как вариант в модеме) или же ваш бинарный код может быть поврежден по причине того, что нижележащий протокол подумает, что вы ввели специальную комбинацию символов (как, допустим, FTP определяет окончания строк).

Поэтому для обхода подобных ситуаций люди кодируют двоичные данные в символы. Base64 является одной из таких кодировок.

Внешне Base64 выглядит как абракадабра и зачастую мы воспринимаем этот код как результат используемой в Web компрессии. Однако в отличие от сжатых файлов, по объему эта абракадабра даже слегка превышает исходный код. Так происходит по причине, которую неплохо объяснил Джон Скит (Jon Skeet) на все том же Stack Overflow:

На кодировку трех байтов данных уходит 4 символа, плюс потенциальный бит заполнения в конце последовательности.

Не могу точно сказать, как в данном случае на объем данных повлияет gzip сжатие. Однако, то, к чему я веду — это как такое кодирование работает с SVG форматом.

Для представления SVG формата тоже можно использовать URI данные:

<img src='data:image/svg+xml; … '>

или

.bg {
background: url('data:image/svg+xml; … ');
}

В случае с SVG у вас нет необходимости в конвертировании данных в base64. Опять же повторюсь. Я не эксперт в этой области, однако, насколько я понимаю, SVG синтаксис просто напросто не содержит каких-либо «сумасшедших» символов. Это обычный XML, который подобен HTML и поэтому не должен вызывать конфликтов c HTML.

Вы можете оставить данные в UTF-8 кодировке и попросту вставить исходный <svg> синтаксис:

<img src='data:image/svg+xml;utf8,<svg … > … </svg>'>

и

.bg {
background: url('data:image/svg+xml;utf8,<svg …> … </svg>');
}

Итак, зная, что мы можем поступать таким образом и что задействование base64 зачастую становится причиной увеличения объема данных, не будет ли это правильным решением? Именно так. К тому же <svg> синтаксис дает нам еще один выигрыш, поскольку его gzip сжатие более эффективно. Это объясняется тем, что SVG имеет более повторяющуюся структуру кода, чем представленные в base64 данные. Скажем, у вас есть два варианта одной и той же иконки — одна красная, другая желтая. Можно просто использовать дублирование SVG синтаксиса, изменив лишь цвет заполнения в одном из них. Gzip с удовольствием это «проглотит». Благодарю Тода Паркера (Todd Parker) за подсказку. Подобный подход используется и в Grunticon, где UTF-8 кодированные SVG изображения присутствуют в СSS коде в качестве URI данных.

Тест.

Для того, чтобы опробовать это на деле я загрузил из IcoMoon три SVG пиктограммы.

Иконки.

cog.svg — 1,026 байт
play.svg — 399 байт
replay.svg — 495 байт

Я прогнал эти файлы через SVGO для того, чтобы как следует их оптимизировать, то есть, как бы подготовил к использованию в качестве URI данных (пробельные символы удалены, хотя, на мой взгляд, острой нужды в этом нет).

cog.svg — 685 байт
play.svg — 118 байт
replay.svg — 212 байт

Затем я пропустил их через base64 конвертор.

cog.svg — 916 байт — 133% от исходного размера
play.svg — 160 байт — 136% от исходного размера
replay.svg — 283 байт — 134% от исходного размера

Теперь все сходится, так ведь? Если на каждые 3 байта уходит 4 символа, значит это на 133% больше, с допустимыми отклонениями, вызванными нечетной длиной, а значит с использованием заполняющего символа.

Я не знаю, может быть это слишком очевидно, чтобы быть сенсацией, однако, как по мне, так если вы собираетесь использовать SVG в виде URI данных, то нет никакой причины для их base64 кодировки.

Статья написана благодаря e-mail от Мэта Флэскена (Matt Flaschen), присланного мне несколькими месяцами ранее, где он и обозначил эту тему.

Обзор комментариев

Все вроде бы просто и понятно, однако, как оказалось, предложенный Крисом способ работает не во всех браузерах, причем, речь идет даже не о старых их версиях (Safari 7, IE11). Где же выход?

Кодировки SVG все-таки не удастся избежать, поскольку этот формат может содержать «небезопасные» символы. Векторные изображения могут иметь растровые включения или атрибуты, значения которых заключены в одиночные или двойные кавычки. Поэтому предварительная обработка (нормализация) данного формата все же потребуется. Только это совсем не обязательно должен быть base64. Более приемлемым является способ, предусматривающий представление URI данных с помощью URL-кодирования. Такого результата можно достигнуть с помощью метода encodeURIComponent(). Всем известно, что содержащиеся в ссылке запрещенные для прямого использования в URL символы, рекомендуется экранировать их UTF-8 эквивалентами с предшествующим знаком процента (%). Не смотря на то, что такой метод приводит к еще большему увеличению объема кода, чем в случае с base64, однако после gzip обработки ситуация меняется на совершенно противоположную, т.е. результат сжатия UTF-8 значительно ниже его аналога в base64. Тем не менее, некоторые инструменты, такие как, к примеру, LESS в качестве URI данных по умолчанию вставляют SVG уже кодированный в base64.

Еще один важный момент. Согласно RFC 2397, связанный с медиа типом параметр должен быть атрибутом формата 'атрибут=значение', т.е. в нашем случае charset=utf-8 (с дефисом!). Вероятно именно по этой причине представленный в примере статьи компактный вариант параметра кодировки (data:image/svg+xml;utf8…) не работает в IE10/11 и FF. Поэтому следует полностью указывать тип кодировки (data:image/svg+xml;charset=utf-8…), либо просто обойтись без этого параметра.

Получается, что необходимость в кодировке SVG изображений все-таки есть, только вот UTF-8 в данном случае предпочтительнее.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *