如何加载和解析SVG文档

背景

有许多与阅读和解析SVG路径相关的未解答的问题:

  • 使用Java读取svg路径数据的最简单方法?
  • Re:使用batik svg解析器解析svg文件
  • https://stackoverflow.com/questions/16672100
  • 如何将SVG Path(SVGOMPathElement)转换为数组?

这个问题和答案旨在解决所有这些问题。

问题

SVG path元素包含数据属性 ( d )。 有时需要从SVG文件中加载,解析和提取路径信息。

如何使用Java从SVG文件加载,解析和提取SVG路径信息?

概观

使用Apache Batik加载和解析SVG文件。 该解决方案显示了将SVG文件转换为MetaPost的初步阶段的Java代码。 这应该提供有关如何使用Java从SVG文件加载,解析和提取内容的一般概念。

图书馆

您将需要以下库:

 batik-anim.jar batik-awt-util.jar batik-bridge.jar batik-css.jar batik-dom.jar batik-ext.jar batik-gvt.jar batik-parser.jar batik-script.jar batik-svg-dom.jar batik-svggen.jar batik-util.jar batik-xml.jar xml-apis-ext.jar 

加载SVG文件

主应用程序将SVG文件加载到DOM中,然后将DOM转换为SVG DOM。 initSVGDOM()方法调用非常重要。 在不调用initSVGDOM() ,从DOM中提取SVG DOM元素的方法将不可用。

 import java.io.File; import java.io.IOException; import java.net.URI; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.DocumentLoader; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.UserAgent; import org.apache.batik.bridge.UserAgentAdapter; import org.apache.batik.dom.svg.SAXSVGDocumentFactory; import org.apache.batik.dom.svg.SVGOMSVGElement; import org.apache.batik.util.XMLResourceDescriptor; import org.w3c.dom.Document; import org.w3c.dom.NodeList; /** * Responsible for converting all SVG path elements into MetaPost curves. */ public class SVGMetaPost { private static final String PATH_ELEMENT_NAME = "path"; private Document svgDocument; /** * Creates an SVG Document given a URI. * * @param uri Path to the file. * @throws Exception Something went wrong parsing the SVG file. */ public SVGMetaPost( String uri ) throws IOException { setSVGDocument( createSVGDocument( uri ) ); } /** * Finds all the path nodes and converts them to MetaPost code. */ public void run() { NodeList pathNodes = getPathElements(); int pathNodeCount = pathNodes.getLength(); for( int iPathNode = 0; iPathNode < pathNodeCount; iPathNode++ ) { MetaPostPath mpp = new MetaPostPath( pathNodes.item( iPathNode ) ); System.out.println( mpp.toCode() ); } } /** * Returns a list of elements in the SVG document with names that * match PATH_ELEMENT_NAME. * * @return The list of "path" elements in the SVG document. */ private NodeList getPathElements() { return getSVGDocumentRoot().getElementsByTagName( PATH_ELEMENT_NAME ); } /** * Returns an SVGOMSVGElement that is the document's root element. * * @return The SVG document typecast into an SVGOMSVGElement. */ private SVGOMSVGElement getSVGDocumentRoot() { return (SVGOMSVGElement)getSVGDocument().getDocumentElement(); } /** * This will set the document to parse. This method also initializes * the SVG DOM enhancements, which are necessary to perform SVG and CSS * manipulations. The initialization is also required to extract information * from the SVG path elements. * * @param document The document that contains SVG content. */ public void setSVGDocument( Document document ) { initSVGDOM( document ); this.svgDocument = document; } /** * Returns the SVG document parsed upon instantiating this class. * * @return A valid, parsed, non-null SVG document instance. */ public Document getSVGDocument() { return this.svgDocument; } /** * Enhance the SVG DOM for the given document to provide CSS- and SVG-specific * DOM interfaces. * * @param document The document to enhance. * @link http://wiki.apache.org/xmlgraphics-batik/BootSvgAndCssDom */ private void initSVGDOM( Document document ) { UserAgent userAgent = new UserAgentAdapter(); DocumentLoader loader = new DocumentLoader( userAgent ); BridgeContext bridgeContext = new BridgeContext( userAgent, loader ); bridgeContext.setDynamicState( BridgeContext.DYNAMIC ); // Enable CSS- and SVG-specific enhancements. (new GVTBuilder()).build( bridgeContext, document ); } /** * Use the SAXSVGDocumentFactory to parse the given URI into a DOM. * * @param uri The path to the SVG file to read. * @return A Document instance that represents the SVG file. * @throws Exception The file could not be read. */ private Document createSVGDocument( String uri ) throws IOException { String parser = XMLResourceDescriptor.getXMLParserClassName(); SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory( parser ); return factory.createDocument( uri ); } /** * Reads a file and parses the path elements. * * @param args args[0] - Filename to parse. * @throws IOException Error reading the SVG file. */ public static void main( String args[] ) throws IOException { URI uri = new File( args[0] ).toURI(); SVGMetaPost converter = new SVGMetaPost( uri.toString() ); converter.run(); } } 

注意:除非另有说明,否则调用initSVGDOM()应该是Batik的默认行为。 唉,它不是,发现这个gem意味着阅读埋在他们网站上的文件 。

解析SVG DOM

解析SVG DOM是相对微不足道的。 toCode()方法是该类的主力:

 import org.apache.batik.dom.svg.SVGItem; import org.apache.batik.dom.svg.SVGOMPathElement; import org.w3c.dom.Node; import org.w3c.dom.svg.SVGPathSegList; /** * Responsible for converting an SVG path element to MetaPost. This * will convert just the bezier curve portion of the path element, not * its style. Typically the SVG path data is provided from the "d" attribute * of an SVG path node. */ public class MetaPostPath extends MetaPost { private SVGOMPathElement pathElement; /** * Use to create an instance of a class that can parse an SVG path * element to produce MetaPost code. * * @param pathNode The path node containing a "d" attribute (output as MetaPost code). */ public MetaPostPath( Node pathNode ) { setPathNode( pathNode ); } /** * Converts this object's SVG path to a MetaPost draw statement. * * @return A string that represents the MetaPost code for a path element. */ public String toCode() { StringBuilder sb = new StringBuilder( 16384 ); SVGOMPathElement pathElement = getPathElement(); SVGPathSegList pathList = pathElement.getNormalizedPathSegList(); int pathObjects = pathList.getNumberOfItems(); sb.append( ( new MetaPostComment( getId() ) ).toString() ); for( int i = 0; i < pathObjects; i++ ) { SVGItem item = (SVGItem)pathList.getItem( i ); sb.append( String.format( "%s%n", item.getValueAsString() ) ); } return sb.toString(); } /** * Returns the value for the id attribute of the path element. If the * id isn't present, this will probably throw a NullPointerException. * * @return A non-null, but possibly empty String. */ private String getId() { return getPathElement().getAttributes().getNamedItem( "id" ).getNodeValue(); } /** * Typecasts the given pathNode to an SVGOMPathElement for later analysis. * * @param pathNode The path element that contains curves, lines, and other * SVG instructions. */ private void setPathNode( Node pathNode ) { this.pathElement = (SVGOMPathElement)pathNode; } /** * Returns an SVG document element that contains path instructions (usually * for drawing on a canvas). * * @return An object that contains a list of items representing pen * movements. */ private SVGOMPathElement getPathElement() { return this.pathElement; } } 

建立

编译因环境而异。 类似于以下的脚本应该有所帮助:

 #!/bin/bash mkdir -p ./build javac -cp ./lib/* -d ./build ./source/*.java 

确保将所有.jar文件放入./lib目录中。 将源文件放入./source目录。

创建一个脚本(或批处理文件)来执行该程序:

 #!/bin/bash java -cp ./lib/*:./build SVGMetaPost $1 

产量

对包含有效SVG路径的文件运行时,会产生:

 $ ./run.sh stripe/trigon.svg % path8078-6 M 864.1712 779.3069 C 864.1712 779.3069 868.04065 815.6211 871.4032 833.4621 C 873.4048 844.08203 874.91724 855.0544 879.0846 864.82227 C 884.24023 876.9065 895.2377 887.9899 900.0184 897.3661 C 904.7991 906.7422 907.3466 918.3257 907.3466 918.3257 C 907.3466 918.3257 892.80817 887.6536 864.1712 887.3086 C 835.53424 886.9637 820.9958 918.3257 820.9958 918.3257 C 820.9958 918.3257 823.6176 906.59644 828.32404 897.3661 C 833.0304 888.1356 844.10223 876.9065 849.2578 864.82227 C 853.4252 855.05444 854.9376 844.08203 856.93915 833.4621 C 860.3017 815.6211 864.17114 779.3069 864.17114 779.3069 z 

从这里开始,应该清楚如何使用Java将SVG路径数据读入相应的SVG对象。

附录

请注意,从SVG转换为MetaPost的最简单方法是:

  1. 将SVG转换为PDF(例如,使用Inkscape或rsvg-convert )。
  2. 使用pstoedit将PDF转换为MetaPost。