Convert Svg Image to Png With PHP

Convert SVG image to PNG with PHP

That's funny you asked this, I just did this recently for my work's site and I was thinking I should write a tutorial... Here is how to do it with PHP/Imagick, which uses 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();

the steps regex color replacement may vary depending on the svg path xml and how you id & color values are stored. If you don't want to store a file on the server, you can output the image as base 64 like

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

(before you use clear/destroy) but ie has issues with PNG as base64 so you'd probably have to output base64 as jpeg

you can see an example here I did for a former employer's sales territory map:

Start: https://upload.wikimedia.org/wikipedia/commons/1/1a/Blank_US_Map_(states_only).svg

Finish: enter image description here

Edit

Since writing the above, I've come up with 2 improved techniques:

1) instead of a regex loop to change the fill on state , use CSS to make style rules like

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

and then you can do a single text replace to inject your css rules into the svg before proceeding with the imagick jpeg/png creation. If the colors don't change, check to make sure you don't have any inline fill styles in your path tags overriding the css.

2) If you don't have to actually create a jpeg/png image file (and don't need to support outdated browsers), you can manipulate the svg directly with jQuery. You can't access the svg paths when embedding the svg using img or object tags, so you'll have to directly include the svg xml in your webpage html like:

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

then changing the colors is as easy as:

<script type="text/javascript" src="/path/to/jquery.js"></script>
<script type="text/javascript">
$('#CA').css('fill', 'blue');
$('#NY').css('fill', '#ff0000');
</script>

Rendering an SVG file to a PNG or JPEG in PHP

Check if ImageMagick is installed (you can find out using phpinfo). If it is, you can use the following code to cover to a PNG.

$image = new Imagick();
$image->readImageBlob(file_get_contents('image.svg'));
$image->setImageFormat("png24");
$image->resizeImage(1024, 768, imagick::FILTER_LANCZOS, 1);
$image->writeImage('image.png');

There are many threads that discuss this. One that is particularly useful is this thread:
Convert SVG image to PNG with PHP

Issues to convert SVG to PNG using Imagick

Do you have the access to the SVG file? Or are you able to download/save it locally, then change

xlink:href="data:image/jpeg;base64

in the 1st <image>

xlink:href="data:image/png;base64

and reference your local downloaded and changed copy?

The other way to show what you need to change is here:

enter image description here

I'm getting then the following:

enter image description here

UPDATE: I'd like to say once more that the SVG file you provided as example has the pink background as <image> with wrong MIME type, as I said in the comments. The problem you describe occurs because of that, no matter how reliable the image source is. You can check it by copying the base64 value of the first <image> in the SVG, decode it and save and then open it with any editor, you will see this:

enter image description here

which is a PNG, not JPEG signature. However, the first <image> in the SVG has image/jpeg - check it.

Now back to your claim that you can't change all the files. What I can propose is parsing SVG's XML in your script and replacing all MIME types with the correct ones. Be aware that this will require quite a lot memory as SVG's may be large. Note the new code between MODIFY THE MALFORMED SVG comments

$usmap = 'http://yatnam.com/demo/vh/card2_1.svg';
$svg = file_get_contents($usmap);

/////////////////// MODIFY THE MALFORMED SVG ///////////////////////

$dom = new DomDocument();
$dom->loadXML($svg);
foreach($dom->getElementsByTagName('image') as $image) {
$encoded = $image->attributes->getNamedItem('href')->value;
if(!empty($encoded)) {
$binary = base64_decode(substr($encoded,strpos($encoded,'base64,') + 7));
$info = getimagesizefromstring ($binary);

$image->setAttributeNS('http://www.w3.org/1999/xlink','xlink:href','data:'.$info['mime'].';base64,' . base64_encode($binary));
}
}

$svg = $dom->saveXML();

/////////////////// MODIFY THE MALFORMED SVG ///////////////////////

$im = new Imagick();
//$im->setBackgroundColor(new ImagickPixel('transparent'));
$im->readImageBlob($svg);
$im->setImageFormat("png32");
$im->setImageCompressionQuality(100);
$im->resizeImage(720, 445, imagick::FILTER_LANCZOS, 1);
$base64=base64_encode($im);
$im->clear();
$im->destroy();


Related Topics



Leave a reply



Submit