前端和后端实现SVG转图片

栏目: 后端 · 前端 · 发布时间: 7年前

内容简介:用户通过界面操作SVG内容,当生成或者编辑SVG时,实时将SVG内容转换成Base64编码的img标签,塞回到原来的DOM节点中。此种方式在前端通过浏览器就可以完成,无须后端的参与。方案原理是使用canvas做中转,将svg+xml转换成png。在无需后端参与做二次处理时,可以满足业务场景。在需要后台做二次处理时,可以借助于上述的前端方案,通过ajax请求定时、实时的将图片上传到服务器。

用户通过界面操作SVG内容,当生成或者编辑SVG时,实时将SVG内容转换成Base64编码的img标签,塞回到原来的DOM节点中。此种方式在前端通过浏览器就可以完成,无须后端的参与。

方案原理是使用canvas做中转,将svg+xml转换成png。在无需后端参与做二次处理时,可以满足业务场景。

// 前端demo代码
function drawInlineSVG($svg, callback) {
    
    var svg = $svg[0];
    svg.innerHTML = '<rect width="100%" height="100%" fill="#ffffff"></rect>' + svg.innerHTML;
    var svgData = new XMLSerializer().serializeToString( svg );

    var canvas = document.createElement( "canvas" );
    var ctx = canvas.getContext( "2d" );

    var img = document.createElement( "img" );
    img.setAttribute( "src", "data:image/svg+xml;base64," + btoa( unescape(encodeURIComponent(svgData)) ) );

    img.onload = function () {
        ctx.drawImage( img, 0, 0 );
        canvas.toDataURL( "image/png" );
        callback && callback(this);
    };
    var $img = $(img);

    $img.height($svg.height());
    $img.width($svg.width());

    return $img;
}

前端生成png,定时提交到后台

在需要后台做二次处理时,可以借助于上述的前端方案,通过ajax请求定时、实时的将图片上传到服务器。

原理是使用字符串将base64编码的图片实时上传,在后台再做解码生成图片原型,落地到后台。因为需要实时转换和上传,对后端和前端的压力都比较大。

// 前端demo代码
var data = $('#canvasimage').attr('src'); // Base 64

$.ajax({ 
    type: "POST", 
    url: url,
    dataType: 'text',
    data: {
        base64data : data
    }
});
//后端demo代码
$data = 'data:image/png;base64,AAAFBfj42Pj4';

list($type, $data) = explode(';', $data);
list(, $data)      = explode(',', $data);
$data = base64_decode($data);

file_put_contents('/tmp/image.png', $data);

后端方案

引入PHP imagemagic扩展

PHP的imagemagic扩展,可以对SVG操作。使用方法也很简单。但因为imagemagic爆出的安全问题,生产环境一直没有装此扩展。对安全性要求不高的业务,可以使用。 https://www.cvedetails.com/vulnerability-list/vendor_id-1749/Imagemagick.html

$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');

引入batik-rasterizer

batik-rasterizer是Apache基金会下使用 Java 编写的处理SVG的 工具 包,要求安装Java 1.6以上的运行时。原理是使用Mozilla Rhino作为JavaScript引擎,生成了一套浏览器环境。 https://xmlgraphics.apache.org/batik/tools/rasterizer.html

建议使用最高版本的发布包,高版本对SVG和样式的支持性会更好。目前最新版batik-rasterizer已经支持

和外部引用CSS样式,但不支持SVG中foreignObject和通过xmlns=” http://www.w3.org/1999/xhtml"引用HTML标签。
## shell调用
java -jar batik-rasterizer.jar samples/batikFX.svg
## PHP调用
function export($svg, $output_full_path, $type = 'png', $width = null) {

	if (get_magic_quotes_gpc()) {
		$svg = stripslashes($svg);	
	}
	if(strpos($svg,"<!ENTITY") !== false){
		return false;
	}
	if ($type == 'png') {
		$typeString = '-m image/png';
		
	} elseif ($type == 'jpeg') {
		$typeString = '-m image/jpeg';

	} elseif ($type == 'pdf') {
		$typeString = '-m application/pdf';
		$ext = 'pdf';
	}

	$svgTmpPathArr = explode('/', $output_full_path);
	unset($svgTmpPathArr[count($svgTmpPathArr)-1]);

	$tempName = time() . rand();
	$svgTmpPath = implode('/', $svgTmpPathArr) . "/$tempName.svg";

	if (!file_put_contents($svgTmpPath, $svg)) { 
		return false;
	}

	if (!empty($width)) {
		$width = (int)$width;
		if ($width) {
			$width = "-w $width";
		}
	}
	if(!file_exists($output_full_path)) { 
		$handle = fopen($output_full_path, "w");
		fclose($handle);
	}
	@shell_exec("java -jar ".  $batik_rasterizer_path ." $typeString -d $output_full_path $width $svgTmpPath");
	unlink($svgTmpPath);

	if (!is_file($output_full_path) || filesize($output_full_path) < 10) {
		return false;
	} else {
		return $output_full_path;
	}
}

function transfer_svg_to_images($content) {
	$images = array();
	//特殊样式的处理
	$svg_tag_pattern = '/(<rect[^<]*?)\>\<\/rect\>/i';
	$styles = ' style="fill:#ececff; stroke:#9370db; stroke-width:1"';
	$content = preg_replace_callback($svg_tag_pattern, function($matches) use($styles) {			
	 	return str_replace($matches[1], $matches[1] . $styles, $matches[0]);
	}, $content);
	//移除不支持标签
	$svg_tag_pattern = '/(<div xmlns=[^<]*?\>)[\s\S]*?(\<\/div\>)/i';
	$content = preg_replace_callback($svg_tag_pattern, function($matches) {		
	 	$tmp_str = str_replace($matches[1], '<text y="10">', $matches[0]);
	 	$tmp_str = str_replace($matches[2], '</text>',$tmp_str);
	 	$tmp_str = str_replace('span', 'tspan',$tmp_str);
	 	return $tmp_str;
	}, $content);

	//移除不支持标签
	$content = str_replace('foreignObject', 'g', $content);
	$svg_str_pattern = '/\<svg[\s\S]*?\<\/svg\>/i';//匹配富文本中SVG内容
	$ret = preg_replace_callback($svg_str_pattern, function($matches) use(&$images) {
		$image_path =  '/tmp/'. time(). rand() .'svg.png';
		$image_path = export($matches[0], $image_path); //实际导出处理
		$ret_path = null;
		if(file_exists($image_path)){
			$images[] = $image_path;
			$ret_path = '<img src="' . $image_path . '" />';
		}
		return $ret_path;
	}, $content);
	return array($ret, $images);
}

其他问题

SVG本身是属于W3C XML的分支,浏览器的实现方式和支持程度也不一样,在后端尤其如此。因为本身的语言标准以及CSS样式控制的问题,导致实际图片和浏览器预览都有差异,往往都需要使用正则对原始SVG文本做hack处理。尤其是后端的两种导出方式。

前端相比后端转换,兼容性和支持性会好很多。当然具体使用那种转换方式,还是需要针对业务场景。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

图形程序开发人员指南

图形程序开发人员指南

Michael Abrash / 前导工作室 / 机械工业出版社 / 1998 / 128

Michael Abrash's classic Graphics Programming Black Book is a compilation of Michael's previous writings on assembly language and graphics programming (including from his "Graphics Programming" column......一起来看看 《图形程序开发人员指南》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

html转js在线工具
html转js在线工具

html转js在线工具