Первая идея, которая мне пришла в голову, сделать это через canvas, который имеет метод
toDataURL('image/png');
Итак, я написал простенький скрипт: jsfiddle, github:
var html = document.querySelector("svg").parentNode.innerHTML;
var imgsrc = 'data:image/svg+xml;base64,' + btoa(html);
var canvas = document.querySelector("canvas"),
context = canvas.getContext("2d");
canvas.setAttribute('width', 526);
canvas.setAttribute('height', 233);
var image = new Image;
image.src = imgsrc;
image.onload = function () {
context.drawImage(image, 0, 0);
var canvasdata = canvas.toDataURL("image/png");
var a = document.createElement("a");
a.textContent = "save";
a.download = "export_" + Date.now() + ".png";
a.href = canvasdata;
document.body.appendChild(a);
canvas.parentNode.removeChild(canvas);
};
Суть скрипта проста: я преобразовывал svg в dataUri, загружал его через image, рисовал картинку на canvas и превращал в png. Казалось, цель достигнута, и можно расслабится. Этот подход сработал в Firefox и Chrome, но открыв
Дело в том, что IE считает, что картинка загружена с другого хоста. К сожалению, установить origin для dataUri не получится. Собственно, описание правил можно найти здесь: https://html.spec.whatwg.org/multipage/scripting.html#security-with-canvas-elements. Можно было, конечно, проксировать svg через сервер, и тогда все бы сработало, но хотелось чисто клиентское решение.
И тут я вспомнил про замечательную библиотеку canvg. С помощью этой библиотеки я рисую svg на canvas, а далее поступаю как в первом варианте: беру
toDataURL("image/png")
. Получился такой незамысловатый код: github:var svg = document.querySelector('svg');
var canvas = document.createElement('canvas');
canvas.height = svg.getAttribute('height');
canvas.width = svg.getAttribute('width');
canvg(canvas, svg.parentNode.innerHTML.trim());
var dataURL = canvas.toDataURL('image/png');
var data = atob(dataURL.substring('data:image/png;base64,'.length)),
asArray = new Uint8Array(data.length);
for (var i = 0, len = data.length; i < len; ++i) {
asArray[i] = data.charCodeAt(i);
}
var blob = new Blob([asArray.buffer], {type: 'image/png'});
saveAs(blob, 'export_' + Date.now() + '.png');
Тут стоит сказать, что еще я использовал библиотеку FileSaver для вызова диалогового окна сохранения.
Вот и все, мы добились желаемого результата.
Стоит отметить один нюанс — я задался вопросом сохранения svg в png, когда писал плагин для экспорта tauCharts. Так как стили в svg задаются из внешнего файла, чтобы добиться максимально подобия с исходным svg, я вставляю inline style в svg. И получаем вот такой результат.
Надеюсь, статья окажется полезной для вас и сохранит ваше время.
Комментарии (11)
kashey
06.04.2015 08:53+1Еще стоит учесть что
1. Многие браузеры требуют чтобы Image был добавлен в документ, иначе в него «не входит» SVG
Но вообще чтука хорошая. Маст хэв.
2. Некоторые браузеры любят когда в него входит чуть более «уникальный» SVG, иначе кеша творят что-то странно и на выходе получается тыква
3. Существует пара нюансов с «ретиной»
4. Потом canvas можно опять превратить в css/html через datauri, -webkit-canvas или -moz-element — работать будет малек быстрее.
Nixx
06.04.2015 17:22С данным скриптом будут большие проблемы, когда попадутся в SVG шрифты. Реализовать получилось точный конверт только серверным методом.
mavrin Автор
06.04.2015 17:36Да, но у серверного решения есть большой недостаток, не всегда возможно поднять тот или иной стек технологий. Кстати как вы делали рендер на севере? Что использовали?
Nixx
06.04.2015 18:59+1inkscape — дал наиболее приближенный результат. Задача была конвертировать svg в png в точности и с использованием нестандартных шрифтов.
Еще использовали imagemagick, но с ним как то не сраслось…mavrin Автор
06.04.2015 19:06Очень неожиданно, я бы в первую очередь посмотрел бы в сторону phantomjs.org/ или slimerjs.org/. Но с ними я не уверен в стабильности. Но здесь конечно всё зависит от задачи.
Xao
06.04.2015 21:02Я как-то решил проблему крайне простым образом. Вы пробовали сохранить *.svg картинку в, собственно говоря, IE? Вот я однажды попробовал — *.png получается.
mavrin Автор
06.04.2015 21:24Забавно, IE предлагает формат сохранения, а вот хром нет. Но ведь хочется пользователю дать возможность сделать это наиболее простым способом, просто нажатием на ссылку «Сохранить».
Keyten
12.04.2015 12:09developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas вот ещё есть :)
mavrin Автор
13.04.2015 09:07Таже проблема, что и в первом варианте, собственно это и есть модификация первого варианта просто по другому задается URL для image. jsfiddle.net/a9ude9p0/12
prolis
Я пользовался этим: https://github.com/exupero/saveSvgAsPng — без IE.
mavrin Автор
В указанной вами библиотеке, как раз используется первый, описанный мною, вариант.