JavaScript: Сохранение векторной графики (svg) в растр

2016-10-05 papirosnik JavaScript

  Столкнулся намедни с необходимостью нарисовать с помощью векторной графики несколько похожих растровых изображений. Да, именно так. Вектором, с его четкими границами, с предопределенными стилями, с незатуманеными блюром пикселами — в растр. Потому как сразу в растр — много шума и возни.

Самым оптимальным показался вариант использовать общепризнанный для таких целей бесплатный  редактор — Inkscape. Вроде как все там просто, интуитивно понятно, на освоение много времени не уйдет, но…

После непродолжительного использования сказалось отсутствие необходимых навыков. Итоговая картинка получалась, но как-то без куража, плюс со всякими мелкими недочетами. То рука не там дрогнет, то удалил не то… И поскольку изображения в целом требовались примерно одинаковые, различающиеся только небольшими деталями, то сильно утомляла копипаста.

Словил себя на мысли, что этот процесс надо как-то автоматизировать. И Inkscape даже любезно предоставил некоторые возможности для этого в виде интерфейса командной строки (cli — command line interface — по-ихнему)

Но и тут вскоре выяснилось, что предоставляемые средства автоматизации довольно таки ограничены в своих возможностях. В частности, многие вещи работают только «на чтение».

Ревизия имеющихся в наличии средств, позволяющих работать и с векторной графикой, и со скриптовым языком для задания процедур генерации изображений, однозначно установила отсутствие каких-либо альтернатив. Кандидат один — Google Chrome.

Идея заключается в том, чтобы на JavaScript процедурно сгенерировать требуемые изображения и сэкспортировать их в файл с растровым изображением. На первый взгляд проблем никаких. Но уже немного настораживает факт, что надо будет писать в файл, а JS этого просто так не позволит.
Ну да ладно, для начала надо хотя бы что-то нарисовать. И хотя векторную графику вполне сносно можно закодить вручную в тэге <svg>, существует немало библиотек, облегчающих этот и  не такой уж трудный процесс. Phantom SVG — один из признанных лидеров среди них.

Но я как дилетант-любитель свой выбор остановил на Svg.js

Это легковесная, лаконичная и вместе с тем полнофункциональная библиотека, более чем достаточная для моих целей, в первую очередь подкупила своим синтаксисом. Легкочитаемый, интуитивно-понятный. предсказуемый — после пятиминутного опробования все сомнения окончательно развеялись. Скачаная версия занимает всего 13кБ исходного кода.

Чтобы начать рисовать — требуется буквально пара строк в набранном при помощи блокнота index.html

<div id="tile"></div>
<script>
var draw = SVG('tile');       
draw.rect(128, 128).fill('#f00');     
</script>

В результате имеем просто красный квадратик. Тут уже можно развернуться и накодить  прекрасный осенний пейзаж, либо портрет незнакомки, но для целей настоящей статьи хватит и красного квадратика. Задача — превратить его в png-файл, желательно без использования сервера и даже каких-либо действий со стороны пользователя… Просто — в окне браузера квадратик, на диске — файлик.
Но сохранить в файловой системе файл из JS — это проблема. И связана она с проблемами безопасности.
Не вдаваясь в подробности, отметим, что можно предложить пользователю скачать файл обычными средствами. Можно даже показать диалог выбора имени файла, но для примера — достаточно атрибута dowwnload, который предлагает нам  Chrome.

И тут а выручку приходит HTML5 с его канвой и возможность получить URL на данные канвы. Последовательность действий такова:

  1. Рисуем средствами вышеупомянутой svg.js желаемую векторную графику.
  2. Находим в документе тэг <svg> (он создается в результате рисования svg.js)
  3. Сериализуем его содержимое в base64
  4. Создаем канву и получаем ее контент
  5. Создаем элемент <img> и в качестве источника изображения указываем наш сериализованный в пункте 3 контент
  6. Когда изображение загружено, отрисуем его на контексте канвы.
  7. Канву конвертируем в DataURL
  8. Создаем элемент-ссылку и в атрибуте href задаем наш dataURL, в download — имя файла;
  9. Автоматически кликаем по ссылке
  10. В папке Downloads находим файл с нужным нам изображением

Текстом этот алгоритм оказалось описать труднее, чем самим кодом. Поэтому в завершение привожу код.

<head>
	<script src="./svg.js"></script>
</head>
<body>
	<div id="tile"></div>
        <img id="img" style="position:fixed;left:550px;top:0"></img>
	<script>
        function saveTile(name) {
            var svg = document.querySelector( "svg" );
            var svgData = new XMLSerializer().serializeToString( svg );        
            console.log(svgData);
 
            var can = document.createElement("canvas");
            var ctx = can.getContext("2d");
 
            //var img = document.createElement( "img" );
            var img = document.getElementById("img");
            img.setAttribute( "src", "data:image/svg+xml;base64," + btoa(svgData));
            img.onload = function() {
                ctx.drawImage( img, 0, 0 );
                var a = document.createElement('a');
                document.body.appendChild(a);
                a.href = can.toDataURL('image/png');
                a.download = name;
                a.click();
              };
        }        
        var draw = SVG('tile');       
        draw.rect(128, 128).fill('#f00');        
        saveTile('tile.png');
	</script>
</body>

После запуска в браузере отобразиться красный квадрат, а в папке Downloads появится файл tile.png — это самое изображение, но в растре.

PS. После некоторого использования этого подхода выявлена неприятная особенность: Если вектроную графику рисовать с наложением растрового изображения (например, используя метод draw.image() из библиотеки svg.js), то выходной файл оказывается пуст. Причина этого пока не выявлена. Дополнение следует…

JS, SVG,


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

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

Powered by WordPress. Designed by elogi.