使用BATIK解析SVG生成PNG图片

losetowin 发布于:2017-2-2 21:43 分类:Java  有 7768 人浏览,获得评论 0 条 标签: svg batik 

本文地址:http://www.dutycode.com/java_batik_caozuo_svg_png.html
除非注明,文章均为 www.dutycode.com 原创,欢迎转载!转载请注明本文地址,谢谢。
    假期搞了一个小玩意,输入部分文字生成指定的图片,这个其实在朋友圈很多,比如之前的大字等游戏与此类似,比如我搞得这个就是生成微信的转账记录,转账金额和转账时间是可以变更的,如下图:
test.png
当然这里面是有留一个彩蛋的,最上面的时间和下面的转账时间是对不上的,所以这个仅仅是玩一玩~~不要用作其他用途。
(PS:15/16年比较多的微商朋友圈的各种收款记录转账记录很大一部分就是通过类似的方案生成的,所以朋友圈的微商还是谨慎一点好,建议正规渠道购买)
    那如何通过代码动态生成一张图片呢?
    这里用到了svg这种文件描述形式,svg是基于可扩展标记语言描述二维矢量图的一种图形格式。
    在Java中,操作svg图形格式文件,我使用的是Batik类库,当然还有其他的类库如JFreeSvg,感兴趣的可以自行百度了解。
    maven依赖:
<!-- svg 生成png格式图片 -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svggen</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-bridge</artifactId>
<version>1.8</version>
<exclusions>
<exclusion>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-dom</artifactId>
<version>1.8</version>
<exclusions>
<exclusion>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-parser</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svg-dom</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-transcoder</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-util</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-xml</artifactId>
<version>1.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.xmlgraphics/xmlgraphics-commons -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>xmlgraphics-commons</artifactId>
<version>2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.xmlgraphics/batik-codec -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>1.8</version>
</dependency> <!-- 此处不能使用2.9.1版本,使用2.9.1生成png会失败 -->
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xmlParserAPIs</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.axsl.org.w3c.dom.svg</groupId>
<artifactId>svg-dom-java</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.w3c.css</groupId>
<artifactId>sac</artifactId>
<version>1.3</version>
</dependency>
<!-- svg 生成png格式图片结束 -->
这里使用的batik的版本是1.8版本,1.6版本在替换text元素内容的时候存在Bug,所以采用了1.8

代码:


/**
	 * 将SVG转换成PNG
	 * @param path SVG文件路径
	 * @param outputStream 输出流
	 * @param parmMap 变更参数键值对,key为svg元素Id value为替换内容
	 * @throws TranscoderException
	 * @throws IOException
	 */
	public static void convertToPngByFile(String path, OutputStream outputStream, Map<String, String> parmMap)
			throws TranscoderException, IOException {
		try {
			File file = new File(path);
			String parser = XMLResourceDescriptor.getXMLParserClassName();
			SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
			Document doc = f.createDocument(file.toURI().toString());

			// 遍历参数,添加数据生成
			Set<String> keySet = parmMap.keySet();
			for (String key : keySet) {
				Element e = doc.getElementById(key);
				e.setTextContent(parmMap.get(key));
			}
			PNGTranscoder t = new PNGTranscoder();
			TranscoderInput input = new TranscoderInput(doc);
			TranscoderOutput output = new TranscoderOutput(outputStream);
			t.transcode(input, output);
			outputStream.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}


注意下面这部分代码:
Set<String> keySet = parmMap.keySet();
for (String key : keySet) {
Element
e = doc.getElementById(key);
e.setTextContent(parmMap.get(key));
}

这里仅实现了替换text元素内容的方法,当然还有其他的方法,比如添加更改属性值等方法,按照xml的读写方式操作即可。
如之前提到的,1.6版本的batik中存在一个bug,使用setTextContent时会抛出AbstractMethodError,这个问题在1.8版本中得以解决。但1.8版本的batik又出现了另外一个bug,当在生成png图片的时候,可能会出现以下类似的错误,导致无法生成png图片。
The URI "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAACc4AAAnOCAYAAA....
on element <image> can't be opened because:
URL data in unsupported format or corrupt
这个bug也在官方的jira上得到印证,但尚未得到修复。看解释是因为引入的imageio包路径不对导致,基于这个解释,解决办法也很简单,在本地工程中新建相同的包,导入正确的Java文件即可。
包路径为 org.apache.batik.ext.awt.image.codec.imageio

java文件点击下面链接可下载。

batik_imageio.zip


测试生成png的代码如下:
@Test
	public void testGenPng() {
		File file = new File("/opt/tmp/test.png");
		Map<String, String> map = new HashMap<String, String>();
		map.put("svg_2", "¥1314.00");
		map.put("svg_3", "2017-01-01 11:00:00");
		FileOutputStream outputStream = null;
		try {
			file.createNewFile();
			outputStream = new FileOutputStream(file);
			SVGPicUtils.convertToPngByFile(
					"/opt/tmp/wechat_transfer.templete",
					outputStream, map);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

执行即可生成png图片。
补充一下svg的描述文件
<svg width="480" height="855" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
<defs>
  <filter id="svg_2_blur">
   <feGaussianBlur stdDeviation="0.1" in="SourceGraphic"/>
  </filter>
</defs>
<g>
  <title>background</title>
  <rect fill="#fff" id="canvas_background" height="857" width="482" y="-1" x="-1"/>
  <g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
   <rect fill="url(#gridpattern)" stroke-width="0" y="0" x="0" height="100%" width="100%"/>
  </g>
</g>
<g>
  <title>Layer 1</title>
  <image xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD*****" id="svg_1" height="855" width="480" y="-1" x="1"/>
  <text style="cursor: move;" filter="url(#svg_2_blur)" opacity="0.75" stroke="#000" xml:space="preserve" text-anchor="start" font-family="Euphoria, sans-serif" font-size="35" id="svg_2" y="375.33555" x="160.49442" stroke-width="0" fill="#000000">¥1314.00</text>
  <text xml:space="preserve" text-anchor="start" font-family="Euphoria, sans-serif" font-size="18" id="svg_3" y="827" x="208.5" stroke-width="0" fill="#999999">2017-01-28 11:02:01</text>
</g>
</svg>
加粗处代码为背景图片的base64编码(因为太长,这里不完整,请注意。),可自行替换。

以上,即可完成java操作png。 

版权所有:《攀爬蜗牛》 => 《使用BATIK解析SVG生成PNG图片
本文地址:https://www.dutycode.com/java_batik_caozuo_svg_png.html
除非注明,文章均为 《攀爬蜗牛》 原创,欢迎转载!转载请注明本文地址,谢谢。