6

I am trying to find some elements from an SVG document with Batik.

This is the example SVG document I am using:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<svg
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   version="1.0"     
   width="400"
   height="300"
   viewBox="0 -100 400 300"
   preserveAspectRatio="none"
   id="svg2">

  <g id="blubb">
   <line x1="0" y1="0" x2="40" y2="120" stroke="red" stroke-width="3"/>
   <text class="hurz" x="50" y="150">HURZ!</text>
  </g>
</svg>

I am trying to find all text elements with the class attribute hurz.

This is an example code:

import java.awt.*;
import java.io.*;
import javax.swing.*;
import org.apache.batik.swing.*;
import org.apache.batik.swing.gvt.*;
import org.w3c.dom.*;
import org.w3c.dom.svg.*;
import org.w3c.dom.xpath.*;

public class XPathTest extends JFrame {
  private final JSVGCanvas c= new JSVGCanvas();


  public XPathTest(){
    this.setLayout(new GridLayout());
    this.add(c);

    c.setURI(new File("/tmp/bla.svg").toURI().toString());

    c.addGVTTreeRendererListener(new GVTTreeRendererAdapter() {
      @Override
      public void gvtRenderingCompleted(GVTTreeRendererEvent e) {
        testXPath();
      }
    });
  }

  private void testXPath() {
    final SVGDocument document= c.getSVGDocument();
    final XPathEvaluator xpathEvaluator= (XPathEvaluator) document;
    final SVGElement searchRoot= (SVGElement) document.getElementById("blubb");

    //works
    final XPathResult result1= (XPathResult) xpathEvaluator.evaluate(".//*[@class=\"hurz\"]", searchRoot, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
    printResults(result1);

    //doesn't work                                                                                                                              
    final XPathResult result2= (XPathResult) xpathEvaluator.evaluate(".//text[@class=\"hurz\"]", searchRoot, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
    printResults(result2);

    //doesn't work
    final XPathResult result3= (XPathResult) xpathEvaluator.evaluate(".//text", searchRoot, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
    printResults(result3);
  }

  private void printResults(XPathResult result){
    int count= 0;

    Node node;
    while ((node= result.iterateNext()) != null) {
      count++;
    }

    System.out.println(count);
  }

  public static void main(String[] args){
    final XPathTest f= new XPathTest();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setSize(800, 600);
    f.setVisible(true);
  }
}

When running this, the output is:

1
0
0

So obviously the XPath expression .//*[@class="hurz"] finds the correct nodes. However, when I search for specific element types (in this case text elements), it finds nothing as with the expressions .//text[@class="hurz"] and .//text.

Am I doing something wrong or is this a bug in Batik?

radlan
  • 2,393
  • 4
  • 33
  • 53
  • This is because of a default namespace `xmlns="http://www.w3.org/2000/svg"`. Try registering that namespace. – Joel M. Lamsen Sep 30 '14 at 07:45
  • I found this very usefull but I cannot make it work. Apparently there are conflicting versions of `batik` and `xerces`. What versions do you use? – clapas Jul 08 '15 at 11:06
  • 1
    For those interested, I finally got it working by using `org.apache.xpath.XPathAPI` instead of `org.w3c.dom.xpath.XPathEvaluator` in an alternative but similar implementation. Cheers – clapas Jul 08 '15 at 11:35

1 Answers1

4

This is because of the default namespace xmlns="http://www.w3.org/2000/svg". Try using the local-name() function:

.//*[local-name()=\"text\" and @class=\"hurz\"]
Joel M. Lamsen
  • 7,143
  • 1
  • 12
  • 14
  • 1
    That works, but it is quite an ugly syntax. Isn't there another way? What do you mean by "registering the namespace"? Can I set a default namespace for the XPathEvaluator? I assume this is what the XPathNSResolver is for, but I don't understand how to use it. – radlan Sep 30 '14 at 11:38
  • It's not really that ugly. Unless it's not possible to get `svg` element explicitly with xpath like e.g. `div`, we can get any element (`*`) and identify it by class or another attribute. – Piotr Wittchen Mar 20 '19 at 10:47