Hi, I've found a bug in Batik (I think) and would appreciate any comments, confirmation, workarounds, etc.
If you run the program below, and then look at the output file, you'll see two <use> tags, one that in from the original SVG input and another that is added dynamically by the Java code. The first one is correct but the second one is not, because the "href" attribute is incorrectly put in the null namespace. The program's output is shown below as well (pretty printed) and you can see the bogus <use> tag. If you try to view this SVG with sqiggle, for example, it will barf on it. I'm using JDK 1.4.2... this could be a JDK bug as well. Thanks, -Archie __________________________________________________________________________ Archie Cobbs * CTO, Awarix * http://www.awarix.com ----------------------------- TEST PROGRAM ------------------------------ import java.io.File; import java.io.StringReader; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.batik.bridge.UpdateManagerEvent; import org.apache.batik.bridge.UpdateManagerListener; import org.apache.batik.swing.JSVGCanvas; import org.apache.batik.swing.gvt.GVTTreeRendererAdapter; import org.apache.batik.swing.gvt.GVTTreeRendererEvent; import org.apache.batik.swing.gvt.GVTTreeRendererListener; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.svg.SVGDocument; import org.w3c.dom.svg.SVGElement; public class BatikBug extends GVTTreeRendererAdapter implements UpdateManagerListener { private static final TransformerFactory FACTORY = TransformerFactory.newInstance(); private static final String SVG_NS = "http://www.w3.org/2000/svg"; private static final String XLINK_NS = "http://www.w3.org/1999/xlink"; private static final String TOP_ID = "top"; private static final String BOX_ID = "box"; private static final String INPUT = "" + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<svg viewBox=\"0 0 100 100\" width=\"400\" height=\"400\"" + " xmlns=\"" + SVG_NS + "\" xmlns:xlink=\"" + XLINK_NS + "\">" + "<defs>" + "<rect id=\"" + BOX_ID + "\" x=\"0\" y=\"0\" width=\"30\" height=\"30\"" + " fill=\"#00ff00\"/>" + "</defs>" + "<g id=\"" + TOP_ID + "\">" + "<use xlink:href=\"#" + BOX_ID + "\" transform=\"translate(10, 10)\"/>" + "</g>" + "</svg>"; private final JSVGCanvas canvas; private final String filename; private SVGDocument doc; private Node top; private boolean ready; public BatikBug(String filename) throws Exception { this.filename = filename; this.canvas = new JSVGCanvas(); DOMResult domResult = new DOMResult(); FACTORY.newTransformer().transform( new StreamSource(new StringReader(INPUT)), domResult); canvas.setDocumentState(JSVGCanvas.ALWAYS_DYNAMIC); canvas.setDocument((Document)domResult.getNode()); canvas.addGVTTreeRendererListener(this); canvas.addUpdateManagerListener(this); JFrame frame = new JFrame("BatikBug"); frame.getContentPane().add(canvas); frame.pack(); frame.show(); } private void accessDocument(Runnable r) { try { canvas.getUpdateManager().getUpdateRunnableQueue().invokeAndWait(r); } catch (InterruptedException e) { e.printStackTrace(); } } private void addBox(final int x, final int y) { accessDocument(new Runnable() { public void run() { SVGElement use = (SVGElement)doc .createElementNS(SVG_NS, "use"); use.setAttributeNS(XLINK_NS, "href", "#" + BOX_ID); use.setAttributeNS(null, "x", "" + x); use.setAttributeNS(null, "y", "" + y); top.appendChild(use); System.out.println("added new <use> node at " + x + ", " + y); } }); } private void export() { System.out.println("exporting to `" + filename + "'..."); accessDocument(new Runnable() { public void run() { try { System.out.println("starting export transform"); FACTORY.newTransformer().transform(new DOMSource(doc), new StreamResult(new File(filename))); System.out.println("finished export transform"); } catch (Exception e) { e.printStackTrace(); } } }); System.out.println("exported SVG to `" + filename + "'"); } public void gvtRenderingCompleted(GVTTreeRendererEvent event) { System.out.println("document is rendered"); doc = canvas.getSVGDocument(); top = doc.getElementById(TOP_ID); ready = true; synchronized (this) { notify(); } } public void go() throws InterruptedException { synchronized (this) { while (!ready) wait(); } addBox(60, 60); export(); try { Thread.sleep(5000); } catch (InterruptedException e) { } } public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("Usage: java BatikBug filename"); System.exit(1); } new BatikBug(args[0]).go(); } public void managerResumed(UpdateManagerEvent e) { System.out.println("manager resumed"); } public void managerStarted(UpdateManagerEvent e) { System.out.println("manager started"); } public void managerStopped(UpdateManagerEvent e) { System.out.println("manager stopped"); } public void managerSuspended(UpdateManagerEvent e) { System.out.println("manager suspended"); } public void updateCompleted(UpdateManagerEvent e) { System.out.println("update completed"); } public void updateFailed(UpdateManagerEvent e) { System.out.println("update failed"); } public void updateStarted(UpdateManagerEvent e) { System.out.println("update started"); } } ------------------- BOGUS OUTPUT (pretty printed) ------------------------ <?xml version="1.0" encoding="UTF-8"?> <svg contentScriptType="text/ecmascript" width="400" xmlns:xlink="http://www.w3.org/1999/xlink" zoomAndPan="magnify" contentStyleType="text/css" viewBox="0 0 100 100" height="400" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" version="1.0"> <defs> <rect x="0" y="0" fill="#00ff00" width="30" height="30" id="box"/> </defs> <g id="top"> <use transform="translate(10, 10)" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#box" xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace"/> <use x="60" y="60" xmlns:xlink="http://www.w3.org/1999/xlink" href="#box" xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace"/> </g> </svg> * Confidentiality Notice: This e-mail message, including any attachments, is for the sole use of the intended recipient(s) and may contain confidential and privileged information. Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the intended recipient, please contact the sender by reply e-mail and destroy all copies of the original message. * --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
