Преобразование изображения SVG в PNG с помощью PHP

111

Я работаю над веб-проектом, который включает в себя динамически сгенерированную карту США, раскрашивающую разные штаты на основе набора данных.

Этот файл SVG дает мне хорошую пустую карту США, и в нем очень легко изменить цвет каждого штата. Сложность состоит в том, что браузеры IE не поддерживают SVG, поэтому, чтобы использовать удобный синтаксис, который предлагает svg, мне нужно преобразовать его в JPG.

В идеале я бы хотел сделать это только с библиотекой GD2, но также мог бы использовать ImageMagick. Я понятия не имею, как это сделать.

Будет рассмотрено любое решение, которое позволит мне динамически изменять цвета штатов на карте США. Ключ в том, что цвета легко менять на лету и кроссбраузерность. Только решения PHP / Apache, пожалуйста.

Майкл Беркомпас
источник
есть ли классы, предназначенные для переноса SVG на VML? таким образом у вас все еще может быть решение типа «HTML5»
Патрик
взгляни на мой ответ. именно то, что вам нужно

Ответы:

142

Забавно, что вы спросили об этом, я только что сделал это недавно для своего рабочего сайта и подумал, что должен написать учебник ... Вот как это сделать с PHP / Imagick, который использует ImageMagick:

$usmap = '/path/to/blank/us-map.svg';
$im = new Imagick();
$svg = file_get_contents($usmap);

/*loop to color each state as needed, something like*/ 
$idColorArray = array(
     "AL" => "339966"
    ,"AK" => "0099FF"
    ...
    ,"WI" => "FF4B00"
    ,"WY" => "A3609B"
);

foreach($idColorArray as $state => $color){
//Where $color is a RRGGBB hex value
    $svg = preg_replace(
         '/id="'.$state.'" style="fill:#([0-9a-f]{6})/'
        , 'id="'.$state.'" style="fill:#'.$color
        , $svg
    );
}

$im->readImageBlob($svg);

/*png settings*/
$im->setImageFormat("png24");
$im->resizeImage(720, 445, imagick::FILTER_LANCZOS, 1);  /*Optional, if you need to resize*/

/*jpeg*/
$im->setImageFormat("jpeg");
$im->adaptiveResizeImage(720, 445); /*Optional, if you need to resize*/

$im->writeImage('/path/to/colored/us-map.png');/*(or .jpg)*/
$im->clear();
$im->destroy();

Шаги по замене цвета регулярного выражения могут отличаться в зависимости от xml-пути svg и того, как хранятся значения идентификатора и цвета. Если вы не хотите хранить файл на сервере, вы можете вывести изображение в виде базы 64, например

<?php echo '<img src="data:image/jpg;base64,' . base64_encode($im) . '"  />';?>

(прежде чем использовать clear / destroy), но, например, есть проблемы с PNG как base64, поэтому вам, вероятно, придется выводить base64 как jpeg

вы можете увидеть здесь пример, который я сделал для карты территории продаж бывшего работодателя:

Начало: https://upload.wikimedia.org/wikipedia/commons/1/1a/Blank_US_Map_(states_only).svg

Финиш: введите описание изображения здесь

редактировать

С момента написания вышеизложенного я придумал 2 улучшенных метода:

1) вместо цикла регулярных выражений для изменения состояния заливки используйте CSS для создания таких правил стиля, как

<style type="text/css">
#CA,#FL,HI{
    fill:blue;
}
#Al, #NY, #NM{
    fill:#cc6699;
}
/*etc..*/
</style>

а затем вы можете сделать одну замену текста, чтобы вставить свои правила css в svg, прежде чем приступить к созданию imagick jpeg / png. Если цвета не меняются, убедитесь, что у вас нет встроенных стилей заливки в тегах пути, переопределяющих CSS.

2) Если вам не нужно создавать файл изображения jpeg / png (и не нужно поддерживать устаревшие браузеры), вы можете управлять svg напрямую с помощью jQuery. Вы не можете получить доступ к путям svg при встраивании svg с использованием тегов img или object, поэтому вам придется напрямую включить svg xml в html своей веб-страницы, например:

<div>
<?php echo file_get_contents('/path/to/blank/us-map.svg');?>
</div>

тогда изменить цвета так же просто, как:

<script type="text/javascript" src="/path/to/jquery.js"></script>
<script type="text/javascript">
    $('#CA').css('fill', 'blue');
    $('#NY').css('fill', '#ff0000');
</script>
WebChemist
источник
1
Спасибо за очень точное и полезное руководство о том, как это сделать. Я обязательно воспользуюсь вашим решением в качестве резервного, но я очень хочу попробовать и просто получить совместимость svg во всех основных браузерах.
Michael Berkompas
1
SVG не поддерживается в IE8 или более ранних версиях без необходимости установки плагина svg viewer - со страницы SVG Wikipedia: «Все основные современные веб-браузеры поддерживают и отображают разметку SVG напрямую, за очень заметным исключением Microsoft Internet Explorer (IE) [ 3] Бета-версия Internet Explorer 9 поддерживает базовый набор функций SVG. [4] В настоящее время поддержка браузеров под управлением Android также ограничена ».
WebChemist
1
Да, но похоже, что svgweb сглаживает все несовместимости, используя немного js и flash. Это решение, которое я выбрал.
Michael Berkompas
2
Мне нравится ваше чистое и быстрое решение. Лично при взаимодействии с xml-файлами я предпочитаю использовать dom-анализатор, чтобы чувствовать себя безопаснее, чем с регулярным выражением. Sth like:$dom = new DOMDocument(); $dom->loadXML( $svg ); $dom->getElementsByTagName('image')->item(0)->setAttribute('id', $state); $svg = $dom->saveXML();
Tapper
синтаксический анализатор xml был бы более безопасным, хотя и немного более медленным решением с любым другим svg ... в этом случае регулярное выражение безопасно, потому что я убедился, что атрибуты каждого состояния были отформатированы точно так, как (id = "XX" style = "fill: # ХХХХХХ ").
WebChemist
11

Вы упомянули, что делаете это, потому что IE не поддерживает SVG.

Хорошая новость заключается в том, что IE делает поддержку векторной графики. Хорошо, это в форме языка VML, который поддерживает только IE, а не SVG, но он есть, и вы можете его использовать.

Карты Google, среди прочего, обнаружат возможности браузера, чтобы определить, использовать ли SVG или VML.

Тогда есть библиотека Рафаэля , которая представляет собой графическую библиотеку на основе браузера Javascript, которая поддерживает SVG или VML, опять же, в зависимости от браузера.

Еще один, который может помочь: SVGWeb .

Все это означает, что вы можете поддерживать своих пользователей IE, не прибегая к растровой графике.

См. Также главный ответ на этот вопрос, например: XSL Transform SVG to VML

Spudley
источник
+1 за упоминание raphael, которое, безусловно, является хорошим решением и заслуживает изучения из-за его отличной реализации кроссбраузерной векторной графики.
dmp
10

При преобразовании SVG в прозрачный PNG не забудьте поставить это ДО $imagick->readImageBlob():

$imagick->setBackgroundColor(new ImagickPixel('transparent'));
психобрм
источник
Как это возможно вызвать этот метод перед чтением изображения? Я получаю сообщение об ошибке «Не удается обработать пустой объект Imagick». И да, мое расширение Imagick установлено, поскольку оно работает и конвертирует изображения.
Denis2310
6

Это очень просто, мы работали над этим последние несколько недель.

Вам понадобится Batik SVG Toolkit . Загрузите и поместите файлы в тот же каталог, что и SVG, который вы хотите преобразовать в JPEG , также убедитесь, что вы сначала распаковали его.

Откройте терминал и запустите эту команду:

java -jar batik-rasterizer.jar -m image/jpeg -q 0.8 NAME_OF_SVG_FILE.svg

Это должно вывести JPEG файла SVG. Действительно просто. Вы даже можете просто поместить его в цикл и конвертировать множество SVG,

import os

svgs = ('test1.svg', 'test2.svg', 'etc.svg') 
for svg in svgs:
    os.system('java -jar batik-rasterizer.jar -m image/jpeg -q 0.8 '+str(svg)+'.svg')
Вилли Ментцель
источник
Это круто. Спасибо за чаевые. Я собираюсь использовать его вместе с perl для пакетной обработки загрузки файлов SVG, которые я создал из шаблона.
simbabque 06
2

Я не знаю отдельного решения PHP / Apache, так как для этого потребуется библиотека PHP, которая может читать и отображать изображения SVG. Я не уверен, что такая библиотека существует - не знаю.

ImageMagick может растрировать файлы SVG либо через командную строку, либо через привязку PHP, IMagick , но, похоже, имеет ряд особенностей и внешних зависимостей, как показано, например, в этой ветке форума . Я думаю, что это все еще самый многообещающий путь, это первое, на что я бы обратил внимание на вашем месте.

Пекка
источник
2

Это метод преобразования изображения svg в gif с использованием стандартных инструментов php GD.

1) Вы помещаете изображение в элемент холста в браузере:

<canvas id=myCanvas></canvas>

<script>
var Key='picturename'
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
base_image = new Image();
base_image.src = myimage.svg;
base_image.onload = function(){

    //get the image info as base64 text string

    var dataURL = canvas.toDataURL();
    //Post the image (dataURL) to the server using jQuery post method
    $.post('ProcessPicture.php',{'TheKey':Key,'image': dataURL ,'h': canvas.height,'w':canvas.width,"stemme":stemme } ,function(data,status){ alert(data+' '+status) });
}
</script>    

Затем преобразуйте его на сервере (ProcessPicture.php) из (по умолчанию) png в gif и сохраните. (вы также можете сохранить как png, тогда используйте imagepng вместо изображения gif):

//receive the posted data in php
$pic=$_POST['image'];
$Key=$_POST['TheKey'];
$height=$_POST['h'];
$width=$_POST['w'];
$dir='../gif/'
$gifName=$dir.$Key.'.gif';
 $pngName=$dir.$Key.'.png';

//split the generated base64 string before the comma. to remove the 'data:image/png;base64, header  created by and get the image data
$data = explode(',', $pic);
$base64img = base64_decode($data[1]);
$dimg=imagecreatefromstring($base64img); 

//in order to avoid copying a black figure into a (default) black background you must create a white background

$im_out = ImageCreateTrueColor($width,$height);
$bgfill = imagecolorallocate( $im_out, 255, 255, 255 );
imagefill( $im_out, 0,0, $bgfill );

//Copy the uploaded picture in on the white background
ImageCopyResampled($im_out, $dimg ,0, 0, 0, 0, $width, $height,$width, $height);

//Make the gif and png file 
imagegif($im_out, $gifName);
imagepng($im_out, $pngName);
олевиолин
источник
-1

Вы можете использовать Raphaël - библиотеку JavaScript и легко добиться этого. Это будет работать и в IE.

Локеш Кумар Рави
источник
2
Добавьте подробности, представленные в ссылке. Ссылка может оборваться в любой момент
Сагар Джайн
-1
$command = 'convert -density 300 ';
                        if(Input::Post('height')!='' && Input::Post('width')!=''){
                            $command.='-resize '.Input::Post('width').'x'.Input::Post('height').' ';
                        }
                        $command.=$svg.' '.$source;
                        exec($command);
                        @unlink($svg);

или используя: potrace demo: Tool4dev.com

Thành NV
источник