2015-11-08 161 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(); 
    } 

} 

它应该为所有团体和所有转换工作。

相关问题