【问题标题】:How to load and parse SVG documents如何加载和解析 SVG 文档
【发布时间】:2014-11-19 13:28:24
【问题描述】:

背景

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

问题

SVG path 元素包含data attribute (d)。有时需要仅从 SVG 文件中加载、解析和提取路径信息。

问题

如何从 SVG 文件中加载、解析和提取 SVG 路径信息?

【问题讨论】:

    标签: java file svg batik


    【解决方案1】:

    概述

    使用Apache Batik(或EchoSVG)加载和解析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 的默认行为。唉,事实并非如此,发现这颗宝石意味着在他们的网站上阅读 documentation buried

    解析 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
    

    从这里应该清楚如何将 SVG 路径数据读入其对应的 SVG 对象。

    附录

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

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

    【讨论】:

    • 我也收到 MetaPost 和 MetaPostComment 的依赖错误,这些类来自哪里?它们没有被导入
    • 如果您在查找类文件时遇到问题,请参阅stackoverflow.com/a/1343026/59087 或提出新问题。 MetaPostComment 类未列出。我可能出于自己的目的开发它,并没有将其包含在此示例中。
    • 蜡染中没有org.apache.batik.dom.svg.SAXSVGDocumentFactoryorg.apache.batik.dom.svg.SVGOMSVGElement。你用什么版本的蜡染?
    • @josef - 见DOM API exampleSAXSVGDocumentFactory Javadocs。班级可能已经搬家了?另外,如果你在做Android开发,你可能需要使用another library
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    • 2011-09-17
    • 1970-01-01
    • 1970-01-01
    • 2012-07-12
    • 1970-01-01
    相关资源
    最近更新 更多