2014-09-24 137 views
10

背景

有相關的閱讀懸而未決的問題數量和解析SVG路徑:如何加載和分析SVG文件

此問題的答案旨在解決所有這些問題。

問題

的SVG path元素包含一個data attributed)。有時需要從SVG文件中加載,解析和提取路徑信息。

問題

你怎麼加載,解析和提取使用Java的SVG文件SVG路徑信息?使用Apache Batik

回答

11

概述

加載並解析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()應該是,除非另有規定蠟染的默認行爲。唉,它不是,並且發現這顆寶石意味着在他們的網站上閱讀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路徑數據讀入它們的corr使用Java來支持SVG對象。

附錄

需要注意的是最簡單的方法來從SVG轉換爲MetaPost是:

  1. SVG轉換爲PDF(例如,使用Inkscapersvg-convert)。
  2. 使用pstoedit將PDF轉換爲MetaPost。