使用PHP将SVG图像转换为PNG

125

我正在开发一个涉及到动态生成美国地图的网络项目,根据一组数据对不同的州进行着色。

这个SVG文件给了我一个很好的美国空白地图,并且很容易改变每个州的颜色。问题在于IE浏览器不支持SVG,所以为了使用SVG提供的便捷语法,我需要将它转换为JPG。

理想情况下,我想仅使用GD2库来完成这个转换,但也可以使用ImageMagick。我完全不知道如何做到这一点。

任何能够让我动态更改美国地图上州的颜色的解决方案都会被考虑。关键是易于在不同浏览器之间更改颜色。请只提供PHP / Apache解决方案。


有没有专门用于将SVG移植到VML的类?这样你仍然可以拥有“HTML5”类型的解决方案。 - Patrick
看一下我的回答,正是你所需要的。 - user179169
9个回答

164

很有趣,你问到这个问题,我最近刚为公司的网站做了这件事,并且我在想我应该写一个教程...以下是如何使用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();

根据SVG路径XML以及ID和颜色值的存储方式,正则表达式颜色替换的步骤可能会有所不同。如果您不想在服务器上存储文件,可以将图像输出为Base64格式。

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

(在使用clear/destroy之前) 但是ie存在PNG作为base64的问题,因此你可能需要将base64输出为jpeg

你可以在这里看到我为以前雇主的销售领域地图制作的例子:

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

完成:enter image description here

编辑

自撰写上述内容以来,我想出了两种改进技术:

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图像文件(并且不需要支持过时的浏览器),则可以直接使用jQuery操作SVG。当使用img或object标签嵌入SVG时,无法访问SVG路径,因此您必须直接将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>

1
感谢您提供非常精确和有用的教程,我一定会将您的解决方案作为备选方案,但我很想尝试并在所有主要浏览器上实现SVG兼容性。 - Mike B
1
SVG在IE8或更低版本中不受支持,需要用户安装SVG查看器插件。根据SVG维基百科页面的描述:“所有主要现代Web浏览器都直接支持和呈现SVG标记,但微软Internet Explorer(IE)是一个非常明显的例外[3]。Internet Explorer 9 beta支持基本的SVG功能集[4]。目前,对于在Android下运行的浏览器的支持也很有限。” - WebChemist
1
是的,但是svgweb似乎使用一点js和flash来消除所有不兼容性。那就是我选择的解决方案。 - Mike B
2
我喜欢你干净快速的解决方案。个人而言,当与XML文件交互时,我更喜欢使用DOM解析器,以感到比正则表达式更安全。类似这样:$dom = new DOMDocument(); $dom->loadXML( $svg ); $dom->getElementsByTagName('image')->item(0)->setAttribute('id', $state); $svg = $dom->saveXML(); - Tapper
@WebChemist $idColorArray是什么?它包含哪些颜色?这个数组是什么样子的? - Awais Qarni
显示剩余7条评论

18

将SVG转换为透明的PNG时,在$imagick->readImageBlob()之前别忘了加上这个:

$imagick->setBackgroundColor(new ImagickPixel('transparent'));

在读取图像之前如何调用该方法?我收到了一个错误“无法处理空的Imagick对象”。是的,我的imagick扩展已安装,因为它可以工作并转换图像。 - Denis2310
你是我的好朋友。虽然这不是问题,但对于主要答案来说非常必要。祝你有美好的一天。 - Franco Maldonado
这应该包含在所选答案中。 - Tim Bogdanov

11

你提到你这么做是因为IE不支持SVG。

好消息是IE确实支持矢量图形。它以一种称为VML的语言形式存在,只有IE支持,而不是SVG,但它确实存在,并且可以使用。

比如Google Maps将检测浏览器的功能,以确定是提供SVG还是VML。

然后,还有一个名为Raphael库的Javascript浏览器图形库,支持SVG或VML,具体取决于浏览器。

另一个可能有用的工具是:SVGWeb

所有这些意味着,您可以支持IE用户,而无需使用位图图形。

例如,还可以参见此问题的顶部答案:XSL Transform SVG to VML


+1 提到raphael是一个绝对不错的解决方案,值得研究其出色的跨浏览器矢量图形实现。 - danp

5
这是使用标准的 PHP GD 工具将 SVG 图片转换为 GIF 的方法。
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而不是image 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);

2
我支持这个答案,因为它是唯一一个真正适用于更复杂的SVG图像的PHP解决方案。所有的库包括Imagick都无法正确渲染。虽然这个解决方案很复杂,但它可以自动化,并且具有最好的结果。 - Christian
@Christian,你测试过他的解决方案吗?先测试一下,因为如果base64字符串来自SVG图像,则无法正常工作。 - Lepy
SVG图片必须低于700 kByte,这是发布限制。 - oleviolin
在onload内部不要忘记放置图片!context.drawImage(base_image, 0, 0) - undefined

4
这很容易,我已经在过去的几周里一直在做这项工作。
你需要Batik SVG Toolkit。下载并将文件放置在要转换为JPEG的SVG文件所在目录中,同时确保先解压缩它们。
打开终端,并运行以下命令:
java -jar batik-rasterizer.jar -m image/jpeg -q 0.8 NAME_OF_SVG_FILE.svg

这将输出SVG文件的JPEG格式。非常简单。 你甚至可以将它放在循环中,转换大量的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
因为这个回答没有按照原始问题的要求使用PHP,所以给它点了个踩。 - Curtis Gibby
因为这个回答没有按照原问题要求使用PHP,所以我给它点了个踩。 - undefined

3

我想分享我的答案,希望能对某些人有所帮助。

这更适用于简单情况,当您的SVG不包含填充样式且默认为黑色时,您想将其转换为PNG并向结果PNG添加颜色。

function convertSvgToPng($svgPath, $fillColor, $outPath)
{
    $im = new Imagick();
    $svg = file_get_contents($svgPath);
    
    // !!! THIS is the trick part - just appending to all <path fill color
    $svg = str_replace('<path ', '<path style="fill:'.$fillColor.'" ', $svg);
    
    $im->readImageBlob($svg);       
    $im->setImageFormat("png24");

    $im->writeImage($outPath);
    $im->clear();
    $im->destroy();
}

我点了踩,因为你没有展示如何调用它的例子。仅仅发布一个函数并不构成一个好的回答。提供一个你的函数所期望的SVG格式的例子会使回答变得实际有用。 - Anthony

2

我不知道是否有一个独立的 PHP / Apache 解决方案,因为这需要一个能够读取和呈现 SVG 图像的 PHP 库。我不确定是否存在这样的库 - 我不知道任何。

ImageMagick 能够通过命令行或 PHP 绑定 IMagick 将 SVG 文件转换为光栅图像,但似乎有一些怪癖和外部依赖,例如在 这个论坛帖子 中所示。我认为这仍然是最有前途的方法,如果我是你,这是我首先要探索的事情。


-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 演示:Tool4dev.com


-1

2
请添加链接中的详细信息。链接可能随时中断。 - Sagar Jain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接