2015-11-08 105 views
17

我有以下圖像SVG文件:如何以編程方式在SVG文件中進行轉換?

Image

每個箭頭是由像這樣的代碼表示:

<g 
    transform="matrix(-1,0,0,-1,149.82549,457.2455)" 
    id="signS" 
    inkscape:label="#sign"> 
    <title 
    id="title4249">South, 180</title> 
    <path 
    sodipodi:nodetypes="ccc" 
    inkscape:connector-curvature="0" 
    id="path4251" 
    d="m 30.022973,250.04026 4.965804,-2.91109 4.988905,2.91109" 
    style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.6855976px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> 
    <rect 
    y="250.11305" 
    x="29.768578" 
    height="2.6057031" 
    width="10.105703" 
    id="rect4253" 
    style="fill:#008000;fill-opacity:1;stroke:#000000;stroke-width:0.53715414;stroke-opacity:1" /> 
</g> 

我要計算的矩形的絕對位置rect節點)。爲了做到這一點,我需要評估g標記的transform標記內的表達式(上例中的matrix(-1,0,0,-1,149.82549,457.2455))。

我該怎麼辦?

我認爲第一步是使用Apache Batik讀取該文件作爲SVGDocument

import java.io.IOException; 

import org.apache.batik.dom.svg.SAXSVGDocumentFactory; 
import org.apache.batik.util.XMLResourceDescriptor; 

import org.w3c.dom.Document; 

try { 
    String parser = XMLResourceDescriptor.getXMLParserClassName(); 
    SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser); 
    String uri = "http://www.example.org/diagram.svg"; 
    Document doc = f.createDocument(uri); 
} catch (IOException ex) { 
    // ... 
} 

據我所知,doc可以轉換爲SVGDocument

如何從SVG文檔獲取矩形或組的絕對位置?注意:我需要一些現有的代碼,它可以完成上面的轉換(不要告訴我自己實現這些轉換 - 它們必須已經實現,我希望重新使用該代碼)。

更新1(2015年11月8日12:56 MSK):

落實羅伯特Longson的建議,第一次嘗試:

public final class BatikTest { 
    @Test 
    public void test() throws XPathExpressionException { 
     try { 
      final File initialFile = 
       new File("src/main/resources/trailer/scene05_signs.svg"); 
      InputStream sceneFileStream = Files.asByteSource(initialFile).openStream(); 


      String parser = XMLResourceDescriptor.getXMLParserClassName(); 
      SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser); 
      String uri = "http://www.example.org/diagram.svg"; 
      final SVGOMDocument doc = (SVGOMDocument) f.createDocument(
       uri, sceneFileStream); 

      final NodeList nodes = 
       doc.getDocumentElement().getElementsByTagName("g"); 
      SVGOMGElement signSouth = null; 


      for (int i=0; (i < nodes.getLength()) && (signSouth == null); i++) { 
       final Node curNode = nodes.item(i); 
       final Node id = curNode.getAttributes().getNamedItem("id"); 
       if ("signS".equals(id.getTextContent())) { 
        signSouth = (SVGOMGElement) curNode; 
       } 

       System.out.println("curNode: " + nodes); 
      } 
      System.out.println("signSouth: " + signSouth); 

      final NodeList rectNodes = signSouth.getElementsByTagName("rect"); 
      System.out.println("rectNodes: " + rectNodes); 

      SVGOMRectElement rectNode = (SVGOMRectElement) rectNodes.item(0); 

      System.out.println("rectNode: " + rectNode); 

      final SVGMatrix m2 = 
       signSouth.getTransformToElement(rectNode); 

      System.out.println("m2: " + m2); 
     } catch (IOException ex) { 
      Assert.fail(ex.getMessage()); 
     } 
    } 
} 

呼叫至m2.getA() - m2.getF()結果NullPointerException秒。

更新2(2015年8月11日13時38分MSK):

添加以下代碼來創建SVGPoint並應用矩陣變換到它:

final SVGSVGElement docElem = (SVGSVGElement) 
    doc.getDocumentElement(); 
final SVGPoint svgPoint = docElem.createSVGPoint(); 
svgPoint.setX((float) x); 
svgPoint.setY((float) y); 
final SVGPoint svgPoint1 = 
    svgPoint.matrixTransform(signSouth.getScreenCTM()); // Line 77 

System.out.println("x: " + svgPoint1.getX()); 
System.out.println("y: " + svgPoint1.getY()); 

結果:

java.lang.NullPointerException 
    at org.apache.batik.dom.svg.SVGLocatableSupport$3.getAffineTransform(Unknown Source) 
    at org.apache.batik.dom.svg.AbstractSVGMatrix.getA(Unknown Source) 
    at org.apache.batik.dom.svg.SVGOMPoint.matrixTransform(Unknown Source) 
    at org.apache.batik.dom.svg.SVGOMPoint.matrixTransform(Unknown Source) 
    at [...].BatikTest.test(BatikTest.java:77) 

更新3,條款的賞金(10.11.2015 MSK):

條件,這必須被滿足,以獲得賞金:

我將頒發獎金的英雄或女主角,誰管理,以實現在JUnit測試方法magicallyCalculateXCoordinatemagicallyCalculateYCoordinateBatikTest,這樣我可以在我的Java代碼中獲取InkScape中顯示的形狀的座標(請參閱下面的示例截圖)。

Image with coordinates

計算形狀在SVG文件中的位置的呈現的方法必須工作,要麼

  1. 爲組節點(像在圖像和在sample file)或
  2. 爲三角形。

您提供的代碼必須適用於樣本文件中的所有四個形狀,即使用它我必須能夠在Java代碼中計算它們的座標,它們與Inkscape中顯示的座標相同。

您可以將參數添加到方法magicallyCalculateXCoordinatemagicallyCalculateYCoordinate,您也可以創建自己的方法來計算座標。

您可以使用任何圖書館,這些圖書館可以合法用於商業目的。

與此請求相關的所有文件請從GitHub獲取。我設法使用IntelliJ Idea Community Edition 14.1.5編譯測試。

+1

任何getScreenCTM,getCTM或getTransformToElement都會爲您提供從絕對單位到本地單位的整體轉換。將其應用於元素的本地協調者,並以絕對單位給出座標。 –

+0

@RobertLongson謝謝。我試圖實現你的方法,參見更新1.我可以在其中找到組節點和rect節點。如何應用矩陣來獲取該代碼中的實際座標? –

+1

創建SVGPoint將其值設置爲本地座標,請調用matrixTransform http://www.w3.org/TR/SVG/coords.html#InterfaceSVGPoint和http://www.w3.org/TR/SVG/ struct.html #__ svg__SVGSVGElement__createSVGPoint –

回答

2

我知道,我遲到了,但我偶然發現了這個問題,並享有謎語;-)

在SVG的原點是左上角,而Inkscape中使用左下方爲起源。

origin and reference point in svg (left) and inkscape (right)

因此,我們需要申請撫摸和變換,然後找到左下點圓Ø三個十進制數字。 我使用GVTBuilder獲取應用樣式的邊界框,然後轉換boundingBox,請求轉換組的邊界框,然後使用xMin和yMax作爲參考點。使用視框來確定高度,我轉換了y座標並最終四捨五入座標。 (見GitHub上拉要求)

package test.java.svgspike; 

import org.apache.batik.anim.dom.SAXSVGDocumentFactory; 
import org.apache.batik.anim.dom.SVGOMDocument; 
import org.apache.batik.anim.dom.SVGOMGElement; 
import org.apache.batik.bridge.BridgeContext; 
import org.apache.batik.bridge.GVTBuilder; 
import org.apache.batik.bridge.UserAgentAdapter; 
import org.apache.batik.gvt.GraphicsNode; 
import org.apache.batik.util.XMLResourceDescriptor; 

import javax.xml.xpath.XPathExpressionException; 

import java.awt.geom.Point2D; 
import java.awt.geom.Path2D; 
import java.awt.geom.Rectangle2D; 
import java.io.File; 
import java.io.IOException; 
import java.io.InputStream; 
import java.math.BigDecimal; 

import com.google.common.io.Files; 

import org.junit.Assert; 
import org.junit.Test; 
import org.w3c.dom.Node; 
import org.w3c.dom.NodeList; 

/** 
* Created by pisarenko on 10.11.2015. 
*/ 
public final class BatikTest { 

    @Test 
    public void test() throws XPathExpressionException { 
     try { 
      final File initialFile = 
        new File("src/test/resources/scene05_signs.svg"); 
      InputStream sceneFileStream = Files.asByteSource(initialFile).openStream(); 

      String parser = XMLResourceDescriptor.getXMLParserClassName(); 
      SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser); 
      String uri = "http://www.example.org/diagram.svg"; 
      final SVGOMDocument doc = (SVGOMDocument) f.createDocument(
        uri, sceneFileStream); 

      String viewBox = doc.getDocumentElement().getAttribute("viewBox"); 

      Point2D referencePoint = getReferencePoint(doc, getGroupElement(doc, "signS")); 

      double signSouthX = magicallyCalculateXCoordinate(referencePoint); 
      double signSouthY = magicallyCalculateYCoordinate(referencePoint, viewBox); 

      Assert.assertEquals(109.675, signSouthX, 0.0000001); 
      Assert.assertEquals(533.581, signSouthY, 0.0000001); 

      referencePoint = getReferencePoint(doc, getGroupElement(doc, "signN")); 
      Assert.assertEquals(109.906, magicallyCalculateXCoordinate(referencePoint), 0.0000001); 
      Assert.assertEquals(578.293, magicallyCalculateYCoordinate(referencePoint, viewBox), 0.0000001); 

      referencePoint = getReferencePoint(doc, getGroupElement(doc, "signE")); 
      Assert.assertEquals(129.672, magicallyCalculateXCoordinate(referencePoint), 0.0000001); 
      Assert.assertEquals(554.077, magicallyCalculateYCoordinate(referencePoint, viewBox), 0.0000001); 

      referencePoint = getReferencePoint(doc, getGroupElement(doc, "signW")); 
      Assert.assertEquals(93.398, magicallyCalculateXCoordinate(referencePoint), 0.0000001); 
      Assert.assertEquals(553.833, magicallyCalculateYCoordinate(referencePoint, viewBox), 0.0000001); 


     } catch (IOException ex) { 
      Assert.fail(ex.getMessage()); 
     } 
    } 

    private SVGOMGElement getGroupElement(SVGOMDocument doc, String id){ 
     final NodeList nodes = doc.getDocumentElement().getElementsByTagName("g"); 
     SVGOMGElement signGroup = null; 
     for (int i=0; (i < nodes.getLength()) && (signGroup == null); i++) { 
      final Node curNode = nodes.item(i); 
      final Node idNode = curNode.getAttributes().getNamedItem("id"); 
      if (id.equals(idNode.getTextContent())) signGroup = (SVGOMGElement) curNode; 
     } 
     return signGroup; 
    } 

    /** 
    * @param doc 
    * @param signGroup 
    * @return the reference point, inkscape uses for group (bottom left corner of group) 
    */ 
    private Point2D getReferencePoint(SVGOMDocument doc, SVGOMGElement signGroup){ 

     Point2D referencePoint = new Point2D.Double(0, 0); 

     try { 

      BridgeContext ctx = new BridgeContext(new UserAgentAdapter()); 
      new GVTBuilder().build(ctx, doc); 
      GraphicsNode gvtElement = new GVTBuilder().build(ctx, signGroup); 

      Rectangle2D rc = gvtElement.getSensitiveBounds(); 
      rc = ((Path2D) gvtElement.getTransform().createTransformedShape(rc)).getBounds2D(); 

      //find xMin and yMax in poi 
      referencePoint = new Point2D.Double(rc.getMinX(), rc.getMaxY()); 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return referencePoint; 
    } 

    /** 
    * inkscape states y coordinate with origin in left bottom corner, while svg uses top left corner as origin 
    * @param referencePoint bottom left corner of group 
    * @param viewBox in "originX originY width height" notation 
    * @return corrected y coordinate, rounded to three decimal figures (half up) 
    */ 
    private double magicallyCalculateYCoordinate(Point2D referencePoint, String viewBox) { 
     String[] viewBoxValues = viewBox.split(" "); 
     BigDecimal roundedY = new BigDecimal(Double.parseDouble(viewBoxValues[3])-referencePoint.getY()); 
     roundedY = roundedY.setScale(3, BigDecimal.ROUND_HALF_UP); 
     return roundedY.doubleValue(); 
    } 

    /** 
    * @param referencePoint bottom left corner of group 
    * @return x coordinate, rounded to three decimal figures (half up) 
    */ 
    private double magicallyCalculateXCoordinate(Point2D referencePoint) { 
     BigDecimal roundedX = new BigDecimal(referencePoint.getX()).setScale(3, BigDecimal.ROUND_HALF_UP); 
     return roundedX.doubleValue(); 
    } 

} 

它應該爲所有團體和所有轉換工作。

相關問題