Java Batik 操作 SVG,实现 svg 读取、生成、动态操作
SVG在现在的应用场景中还是很常见的,例如绘制复杂的矢量图形。说到SVG,就不得提下Canvas。在这里我就不详细列举它们之间的不同之处,以及为什么要选择SVG或Canvas了。
首先,我的项目是一个Maven项目,所以只需要导入batik的maven依赖就可以了,如果是普通的Java项目,就需要自己找jar包导入项目中了。maven依赖有:
<!-- svg 生成png格式图片 -->
<dependency>
<groupId>batik</groupId>
<artifactId>batik-svggen</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-awt-util</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-bridge</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-css</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-dom</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-gvt</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-parser</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-script</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-svg-dom</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-transcoder</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-util</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-xml</artifactId>
<version>1.6</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格式图片结束 -->
这个地方大致是需要这么多jar包依赖,我主要使用batik操作svg导出png或jpg,所以最主要的jar包是batik-transcoder,其实只要导入这一个jar包就够了,因为其他的jar包都会被依赖。这里有多或少了的或错误的,请指明。
batik操作svg,首先要知道batik为我们做了什么事:
batik是为想使用svg格式图片来实现各种功能的应用程序和Applet提供的一个基于java的工具包。
工程创建的目的是为开发者提供一系列可以结合或单独使用来支持特殊的svg解决方案的核心模块。模块主要有 SVGParser,SVGGernerator,SVGDOM。Batik工程的其他目的是使它具有高度的扩展性----举个例子,Batik允许开发者使用自定义的SVG元素。即使工程的目标是提供一系列核心模块,但是还是提供了一个完整的SVG浏览器,以便证实各个模块的有效性和交互性。
上面这段话是batik官网给出对batik的一个简单的概述。我们通过这段话能够理解到,batik其实就是将对svg的操作分为了一个个核心模块,这主要包括SVGParser(解释器),SVGGernerator(生成器),SVGDOM(DOM元素)。通过字面的意思我们就能知道batik的主要核心模块为我们做了什么事。SVGParser解释器主要是对SVG的xml文件节点的解析,SVGGernerator(生成器)可以通过生成一个svg文件,SVGDOM能够建立SVGDOM节点,并在每一个Element上添加不同的属性。
通过Batik,你可以在任何使用到java的地方操作SVG文档。你也可以使用各种Batik模块来在你的应用程序和Applet中来生成,操作和转换你的svg图像。
batik使通过java处理SVG内容变的简单。举个例子,通过使用batik的SVGGernerate模块,java应用程序和Applet可以非常简单的使输出图像的格式为SVG。使用batik的SVG viewing component, 应用程序或者Applet可以非常容易的整合SVG查看和交互功能。另外还可以使用Batik的模块将SVG转换为其他格式,比如说JPEG等图像格式和PDF等其他向量格式。
上面这段话也是官网batik对batik的一个用途的解释。首先我们选择一门语言,或是一种技术,必须要知道自己想要做什么,这门技术或工具能不能达到自己的需求,能不能解决现有的问题。选择好一种技术后,不是急着就拿着代码用,而是先要慢慢的去了解它,我承认,我也不是很了解batik,甚至说对它还不算了解。因为我也仅仅是停留在使用它的层次上。但是我们可以以点划线,再以线画面。
先贴代码,然后进行解释:
public class SvgPngConverter {
/**
* batik通过读取svg文件的方式转png
* @param filePath 传入读取的svg文件
* @param pngFilePath 转换后的png图片
* @param map 更改svg属性的集合 传值规则,id,name,value 主要是更改svg子节点的颜色属性值。
* 如果需要改变svg的多个element的颜色属性 则命名规范为 id1,name1,value1,id2,name2,value2....依次类推
* @throws IOException
* @throws TranscoderException
*/
public static void convertToPngByFile(String filePath, String pngFilePath,Map<String, String> map)
throws IOException, TranscoderException {
File file = new File(pngFilePath);
FileOutputStream outputStream = null;
try {
file.createNewFile();
outputStream = new FileOutputStream(file);
convertToPngByFile(filePath, outputStream,map);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void convertToPngByFile(String path, OutputStream outputStream,Map<String, String> map)
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());
for (int i = 1; i <=map.size()/3; i++) {
Element e = doc.getElementById(map.get("id"+i));
System.out.println(map.get("name"+i));
e.setAttribute(map.get("name"+i), map.get("value"+i));
}
PNGTranscoder t = new PNGTranscoder();
TranscoderInput input = new TranscoderInput(doc);
TranscoderOutput output = new TranscoderOutput(outputStream);
t.transcode(input, output);
outputStream.flush();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
通过上面一段代码,可以看到我要做的是将一个SVG导出成PNG格式化的图像,并输出。首先上面的方法只是创建一个文件,只是输入了要输出图片的地址,还有要读取的SVG文件地址,后一个map参数主要是为了后面操作SVG的DOM元素做准备的,后面再说。下一个方法一开始也是创建一个文件流,此处是用来读取SVG文件,后三段代码主要作用是从一个SVG创建一个DOM元素,意思是将读取的SVG文件,转换成一个拥有传入的SVG文件所有Element节点内容的Document对象。下面是一个循环,这个循环我主要做的是通过定义map的键值,主要放入id,svg节点属性(也可以是dom元素,比如style,class),最后就是节点属性的值。这样我们就能控制Document对象的Element元素,以达到动态改变SVG内容的目的。batik可以将一个SVG转换成PNG,主要是PNGTranscoder的transcode方法完成的,具体怎么完成的我们不需要去关系,这里我们只关心它需要怎样的参数,第一个参数是TranscoderInput对象,这个对象需要传入一个值,我做的方法是传入一个SVGDocument对象,因为我们需要动态的改变原SVG文件的属性,并生成改变后的PNG图片。如果你不需要动态改变SVG的输出,你只需要将传入的SVG文件流传入TranscoderInput对象就可以了,这里可以通过查看batick的TranscoderInput类的API就很好了解了。第二个参数是TranscoderOutput对象,对象的名称我们就知道它的作用是什么了,它需要一个之前传入的创建好的输出图片流。最后transcode方法就能将SVG转换成PNG。
上面这段代码只是简单的应用了SVG转换图片这一个核心模块功能,batik还有很多核心功能模块我都没有用到,但是常见的用法就是上面那一点点,希望能对刚接触的人一点点帮助。
推荐阅读
-
一种结构设计模式,允许在对象中动态添加新行为。它通过创建一个封装器来实现这一目的,即把对象放入一个装饰器类中,然后把这个装饰器类放入另一个装饰器类中,以此类推,形成一个封装器链。这样,我们就可以在不改变原始对象的情况下动态添加新行为或修改原始行为。 在 Java 中,实现装饰器设计模式的步骤如下: 定义一个接口或抽象类作为被装饰对象的基类。 公共接口 Component { void operation; } } 在本例中,我们定义了一个名为 Component 的接口,该接口包含一个名为 operation 的抽象方法,该方法定义了被装饰对象的基本行为。 定义一个实现基类方法的具体装饰对象。 公共类 ConcreteComponent 实现 Component { public class ConcreteComponent implements Component { @Override public void operation { System.out.println("ConcreteComponent is doing something...") ; } } 定义一个抽象装饰器类,该类继承于基类,并将装饰对象作为一个属性。 公共抽象类装饰器实现组件 { protected Component 组件 public Decorator(Component component) { this.component = component; } } @Override public void operation { component.operation; } } } 在这个示例中,我们定义了一个名为 Decorator 的抽象类,它继承了 Component 接口,并将被装饰对象作为一个属性。在操作方法中,我们调用了被装饰对象上的同名方法。 定义一个具体的装饰器类,继承自抽象装饰器类并实现增强逻辑。 公共类 ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component 组件) { super(component); } } public void operation { super.operation System.out.println("ConcreteDecoratorA 正在添加新行为......") ; } } 在本例中,我们定义了一个名为 ConcreteDecoratorA 的具体装饰器类,它继承自装饰器抽象类,并实现了操作方法的增强逻辑。在操作方法中,我们首先调用被装饰对象上的同名方法,然后添加新行为。 使用装饰器增强被装饰对象。 公共类 Main { public static void main(String args) { Component 组件 = new ConcreteComponent; component = new ConcreteDecoratorA(component); 组件操作 } } 在这个示例中,我们首先创建了一个被装饰对象 ConcreteComponent,然后通过 ConcreteDecoratorA 类创建了一个装饰器,并将被装饰对象作为参数传递。最后,调用装饰器的操作方法,实现对被装饰对象的增强。 使用场景 在 Java 中,装饰器模式被广泛使用,尤其是在 I/O 中。Java 中的 I/O 库使用装饰器模式实现了不同数据流之间的转换和增强。 让我们打开文件 a.txt,从中读取数据。InputStream 是一个抽象类,FileInputStream 是专门用于读取文件流的子类。BufferedInputStream 是一个支持缓存的数据读取类,可以提高数据读取的效率,具体代码如下: @Test public void testIO throws Exception { InputStream inputStream = new FileInputStream("C:/bbb/a.txt"); // 实现包装 inputStream = new BufferedInputStream(inputStream); byte bytes = new byte[1024]; int len; while((len = inputStream.read(bytes)) != -1){ System.out.println(new String(bytes, 0, len)); } } } } 其中 BufferedInputStream 对读取数据进行了增强。 这样看来,装饰器设计模式和代理模式似乎有点相似,接下来让我们讨论一下它们之间的区别。 第三,与代理模式的区别: 代理模式的目的是控制对对象的访问,它在对象外部提供一个代理对象来控制对原对象的访问。代理对象和原始对象通常实现相同的接口或继承相同的类,以确保两者可以相互替换。 装饰器模式的目的是动态增强对象的功能,而这是通过对象内部的包装器来实现的。在装饰器模式中,装饰器类和被装饰对象通常实现相同的接口或继承自相同的类,以确保两者可以相互替代。装饰器模式也被称为封装器模式。 在代理模式中,代理类附加了与原类无关的功能。
-
Java Batik 操作 SVG,实现 svg 读取、生成、动态操作