I have SVG file with following image:
Each of the arrows is represented by a code like this:
<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>
I want to calculate the absolute position of the rectangle (rect
node). In order to do this, I need to evaluate the expression inside the transform
tag of the g
tag (matrix(-1,0,0,-1,149.82549,457.2455)
in the example above).
How can I do it?
I assume that the first step is to read the file as SVGDocument
using Apache Batik:
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) {
// ...
}
As far as I know, doc
can be cast to SVGDocument.
How can I get from SVG document to the absolute locations of the rectangles or groups?
Note: I need some existing code, which does the transformations above (don't tell me to implement these transformations myself - they must be implemented already and I want to re-use that code).
Update 1 (08.11.2015 12:56 MSK):
First attempt to implement Robert Longson's recommendation:
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());
}
}
}
Calls to m2.getA()
-m2.getF()
result in NullPointerException
s.
Update 2 (08.11.2015 13:38 MSK):
Added following code to create SVGPoint
and apply the matrix transform to it:
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());
Result:
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)
Update 3, terms of the bounty (10.11.2015 MSK):
Conditions, which must be satisfied in order to get the bounty:
I will award the bounty to the hero or heroine, who manages to implement the methods magicallyCalculateXCoordinate
and magicallyCalculateYCoordinate
in the JUnit test BatikTest such that I can get in my Java code the coordinates of the shapes, which are displayed in InkScape (see the following screenshot for an example).
The presented method of calculating the position of shapes in SVG files must work either
- for group nodes (like the one in the picture and in the sample file) or
- for triangles.
The code you provide must work for all four shapes in the sample file, i. e. using it I must be able to calculate in Java code the coordinates of them, which are equal to those displayed in Inkscape.
You can add parameters to the methods magicallyCalculateXCoordinate
and magicallyCalculateYCoordinate
, and you also can create your own methods, which calculate the coordinates.
You may use any libraries, which can be used legally for commercial purposes.
All files related to this request are available on GitHub. I managed to compile the test using IntelliJ Idea Community Edition 14.1.5.