crowmagnumb
crowmagnumb

Reputation: 7107

Using Custom Element Factory When Reading SVG File Using Batik

I am trying to read a largish (1 MB) svg file that is exported from an old program. I do not have access to this program or the data. I only have this exported svg file and I will probably have to import a freshly exported svg file somewhat regularly from this source. The first problem I find using batik is that it is very strict when reading this file. Firefox has no problem reading and displaying this file for example. Batik (including Squiggle can not display it because of some "custom" tags within the document). The exception I get is ...

org.w3c.dom.DOMException: The current document is unable to create an element of the requested type (namespace: http://www.w3.org/2000/svg, name: menu).
    at org.apache.batik.dom.AbstractNode.createDOMException(AbstractNode.java:408)
    at org.apache.batik.dom.svg.SVGDOMImplementation.createElementNS(SVGDOMImplementation.java:211)
    at org.apache.batik.dom.svg.SVGOMDocument.createElementNS(SVGOMDocument.java:372)
    at org.apache.batik.dom.util.SAXDocumentFactory.startElement(SAXDocumentFactory.java:625)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:501)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:400)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2756)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:647)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:140)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
    at org.apache.batik.dom.util.SAXDocumentFactory.createDocument(SAXDocumentFactory.java:431)
    at org.apache.batik.dom.util.SAXDocumentFactory.createDocument(SAXDocumentFactory.java:349)
    at org.apache.batik.dom.svg.SAXSVGDocumentFactory.createDocument(SAXSVGDocumentFactory.java:200)
    at com.samsix.nrg.io.SvgFileImporter.importFile(SvgFileImporter.java:74)

... So using the not entirely helpful link Writing a batik Dom Extension I managed to write the following ...

    public ImportReport importFile( final String    uri )
    throws
        IOException
{
    String parser = XMLResourceDescriptor.getXMLParserClassName();
    SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(parser);

    SVGDOMImplementation    implementation = (SVGDOMImplementation) factory.getDOMImplementation( null );

    implementation.registerCustomElementFactory( "http://www.w3.org/2000/svg",
                                                 "menu",
                                                 new ExtensibleDOMImplementation.ElementFactory() {
                                                    @Override
                                                    public Element create( final String prefix,
                                                                           final Document doc )
                                                    {
                                                        System.out.println( "Element.create: " + prefix );
                                                        return new GenericElement( "prefix",
                                                                                   (org.apache.batik.dom.AbstractDocument) doc );
                                                    }
                                                } );

    SVGDocument    doc = (SVGDocument) factory.createDocument( uri );

    System.out.println( doc.getDocumentURI() );
}

But I get the same error, the System.out.println() statement is never hit so it is apparently not registering my factory correctly. On a side note is there anyway to make Batik less strict so that it just skips things it doesn't understand like Firefox does?

Upvotes: 0

Views: 2394

Answers (2)

crowmagnumb
crowmagnumb

Reputation: 7107

That's a fine solution for now as I can just run the file through this filter. In the nearish future, I will want to be able to have users upload svg files themselves and then it will need to be a little more robust of a solution. Here is the xslt I created ...

<xsl:stylesheet version="1.0" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <!--Identity Transform.-->
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="svg:menu"/>
</xsl:stylesheet>

... and I ran ...

java -jar /usr/share/java/saxon.jar original.svg svgfix.xslt > fixed.svg

Upvotes: 1

Donal Fellows
Donal Fellows

Reputation: 137557

Investigation indicates that the Batik 1.7.0 implementation doesn't allow you to create DOM extensions that intercept elements in the SVG namespace; it throws the exception at the point when it would otherwise drop through to the code that would do the lookup of the registered factory. I've no idea if this is intentional. What's interesting is that there doesn't seem to be a <menu> element defined in current versions of the SVG specification at all; it's either been long deprecated or was an extension that's been placed in the wrong namespace. Ugh.

The easiest workaround might be to use an XSLT stylesheet to remap the <menu> element into another namespace — that's a pretty trivial stylesheet IIRC — and then to register your custom handler to deal with that as you choose (if it is even necessary).

Upvotes: 1

Related Questions