The front-end solutions

Save as Base64 in real time

Users manipulate SVG content through the interface. When SVG is generated or edited, the SVG content is converted into a Base64-encoded IMG tag in real time, which is stuffed back into the original DOM node. This way can be done in the front through the browser, without the involvement of the back end.

The scheme principle is to convert SVG + XML into PNG using Canvas as a relay. This can be applied to business scenarios without the back-end involvement in secondary processing.

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; }Copy the code

The front-end generates PNG and periodically submits it to the background

When the background is required to do secondary processing, the above front-end scheme can be used to upload the picture to the server in real time through Ajax request.

The principle is to use strings to upload base64 encoded pictures in real time, and then do decoding in the background to generate picture prototype, landing to the background. Because of the real-time conversion and upload, there is a lot of pressure on both the back end and the front end.

Var data = $('# canvasImage ').attr(' SRC '); // Base 64 $.ajax({ type: "POST", url: url, dataType: 'text', data: { base64data : data } });Copy the code
$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);Copy the code

The back-end solution

Introduces the PHP Imagemagic extension

PHP imagemagic extension to manipulate SVG. It’s easy to use. However, due to imagemagic security issues, the production environment has never installed this extension. This parameter can be used for services that do not have high security requirements. www.cvedetails.com/vulnerabili…

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

The introduction of batik rasterizer

Batik-rasterizer is a toolkit written under the Apache Foundation to work with SVG in Java, requiring Java 1.6 or higher runtime installation. The idea is to generate a browser environment using Mozilla Rhino as the JavaScript engine. Xmlgraphics.apache.org/batik/tools…

It is recommended to use the highest version of the distribution, which is more supportive of SVG and styles. The latest version of batik-Rasterizer already supports and references to CSS styles externally, but does not support foreignObject in SVG and via XMLNS = “www.w3.org/1999/xhtml”…

## Shell calls Java-jar batik-rasterizer.jar samples/ batikfx.svgCopy the code
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); // Remove unsupported tag $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); // Remove unsupported tag $content = str_replace('foreignObject', 'g', $content); $svg_str_pattern = '/\<svg[\s\S]*? \<\/svg\>/i'; $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); }Copy the code

Other problems

SVG itself is a subset of W3C XML and is implemented and supported differently by browsers, especially on the back end. Actual images and browser previews are different due to language standards and CSS style controls, which often require the use of regex to hack the raw SVG text. Especially the two export methods on the back end.

Front-end transformations are much more compatible and supportive than back-end transformations. Of course, the specific transformation method needs to be specific to the business scenario.