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:
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:
I'm getting then the following:
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:
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
Php 7 With Phpmyadmin Gives Lots of Deprecation Notices
What Is the Default Username and Password of Phpmyadmin
Get Selected Option in PHP Without Pressing Submit
How to Have Dynamic Image as CSS Background
Laravel Save() Method Returning True But Not Updating Records
Redirect That Same Page from Where Id Is Coming
How to Properly Encode Utf-8 for JavaScript and Json
How to Prevent Multiple Inserts When Submitting a Form in PHP
How to Pass JavaScript Variables to PHP
Pdo Support For Multiple Queries (Pdo_MySQL, Pdo_MySQLnd)
Check If Username and Email Already Exists in Database
How to Enable the Mysqli Extension in PHP 7
Laravel Array to String Conversion
Laravel Unique Validation on Multiple Columns
Laravel 5.5 Error 500 in Cpanel Shared Hosting
How to Change Default Url from Localhost:8000 to Other Ip in Laravel When We Run "Php Artisan Serve"