Thomas DeWeese wrote:

  So the answer to your question is, I don't know.
The real question is how much would be broken if the
code started to "trust" the code calling the ContentHandler? :)

Yes. You trust it for local names, why not for qnames?

Actually we trust it for qname, we just don't trust it for the namespace.

You are right of course.


Right now, it interprets any non-prefixed elements whose names match
SVG elements as svg, regardless of the declared default namespace. The following is /not/ an SVG document no matter how you look at it, but I get a pretty picture out of the transcoder.


 > <svg xmlns="non-svg" width="20" height="30">
 >   <circle r="10" style="stroke:black; fill:none"/>
 > </svg>

I don't get a picture, I get the following exception, exactly
as I would expect:

Enclosed Exception:
Root element namespace does not match that requested:
Requested: http://www.w3.org/2000/svg
Found: non-svg

Yes, when the transcoder sets http://xml.org/sax/features/namespace-prefixes to true. If it is false, you will get a picture.


Even if there is no DTD specified in a document, if the application
interprets it as an SVG document, it is in effect associating the SVG
DTD with the document. There is nothing that prevents it in this case
from defaulting attributes from the SVG DTD, which include default
vaules for xmlns and xmlns:xlink. That's OK, because SAX parsers are
not supposed to report defaulted attributes, only ones actually found
in the document.


    This is in some sense what the code currently does, in absence
of _any_ other indication the SAXSVGDocumentFactory assumes it has
been given an SVG Document (this doesn't really apply for multi-ns
docs).

Not quite. SAXSVGDocumentFactory says


    public void startDocument() throws SAXException {
        super.startDocument();
        namespaces.put("", SVGDOMImplementation.SVG_NAMESPACE_URI);
        namespaces.put("xlink", XLinkSupport.XLINK_NAMESPACE_URI);
    }

It does this quite regardless of whether or not there are any namespace declarations in the document, in fact, it does this before it knows if there are any. If it did attribute defaulting as if from the DTD, this code would be in startElement, and would check for null namespace URI and "svg" as local name. (the attribute is #FIXED only on svg, not on other elements)

Not to my knowledge, but I can ask people who know better. Can the
guys who wrote the code shed some light on it?


I will try but they haven't participated much lately.

I've asked the local expert (who wrote significant portions of Xerces), and the answer is there are no known issues with major parsers wrt namespace URI reporting.


I did an "external" implementation, one that does not modify the factory but works "around" its namespace handling. How it works if it plugs directly into a parser is quite ridiculous, but it only depends on public interfaces in Batik and doesn't change anything in its implementation.

What it does is this. The parser finds xmlns attributes, reports strartPrefixMapping() and discards xmlns attributes. My class intercepts startPrefixMapping(), buffers the prefix/uri pair until the next startElement and adds an xmlns attribute based on that pair back to the list of reported attributes. The factory then loops through the attributes, parses any that start with "xmlns" and builds its own namespace maps.

Ari.
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;

import org.apache.batik.transcoder.Transcoder;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.TranscoderException;

import org.xml.sax.ContentHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;

// for JavaDoc, not used in code
import javax.xml.transform.sax.SAXResult;

/** Transcodes SAX events that represent an SVG document into another
 * graphic format. This class is modeled on [EMAIL PROTECTED]
 * javax.xml.transform.sax.TransformerHandler}. You can set an
 * instance of this class as a [EMAIL PROTECTED] ContentHandler} on any object
 * that reports SAX events; unlike [EMAIL PROTECTED] TranscoderInput}, this class
 * is not limited to taking input directly from a parser: for
 * instance, you can use a TransformerHandler as the destination in
 * [EMAIL PROTECTED] javax.xml.transform.sax.SAXResult}. */
public class SVGTranscoderHandler extends SAXSVGDocumentFactory
                                  implements ContentHandler {

    protected List namespaceMappings = new LinkedList ();

    protected Transcoder transcoder;

    protected TranscoderOutput out;

    /** Constructs a handler that will transcode SVG documents using
     * the specified transcoder. */
    public SVGTranscoderHandler (Transcoder transcoder) {
	// The argument to super() is the class name of the parser the
	// caller wishes to use to parse the input. Since this class
	// does not initiate parsing, it has no use for any specific
	// parser, null here is better than an empty string or some
	// arbitrary class name. The parser that is named by the
	// argument to super() will never be instantiated because this
	// class never calls any methods in the superclass that would
	// instantiate it. If a future version of the superclass does
	// dereference this argument, it will throw a
	// NullPointerException that will trace to this constructor
	// and this comment.
	super (null);
	this.transcoder = transcoder;
    }

    public void setOutput (TranscoderOutput output) {
	out = output;
    }

    /** Store the prefix/uri pair until the next [EMAIL PROTECTED]
     * #startElement(String, String, String, Attributes)} callback
     * which will report them as <code>xmlns</code> attributes. */
    public void startPrefixMapping (String prefix, String uri) throws SAXException {
	super.startPrefixMapping (prefix, uri);
	namespaceMappings.add (new NamespaceMapping (prefix, uri));
    }

    /** Add any prefix mapping reported for this element to the list
     * of reported attributes.  Adds an <code>xmlns</code> attribute
     * for each namespace declaration reported in [EMAIL PROTECTED]
     * #startPrefixMapping(String, String)} for this element. */
    public void startElement(java.lang.String uri,
			     java.lang.String localName,
			     java.lang.String qName,
			     Attributes atts) throws SAXException {
	// if there are no pending namespace declarations, report the
	// event as is
	if (namespaceMappings.size() == 0) {
	    super.startElement (uri, localName, qName, atts);
	} else {
	    // add xmlns attribute for each pending declaration, if
	    // not already present in attrs
	    AttributesImpl attsWithXmlns = new AttributesImpl (atts);
	    Iterator i = namespaceMappings.iterator ();
	    while (i.hasNext()) {
		NamespaceMapping nsm = (NamespaceMapping) i.next ();
		String nsAttrLocalName = nsm.prefix;
		String nsAttrQName = "xmlns:" + nsm.prefix;
		if (nsm.prefix.length() == 0) {
		    nsAttrLocalName = "xmlns";  // NE05 says ""
		    nsAttrQName = "xmlns";
		}
		// if xmlns attribute not already present, report it
		if (atts.getIndex(nsAttrQName) < 0) {
		    // Erratum NE05 for Namespaces in XML says the uri
		    // should be "http://www.w3.org/2000/xmlns/";
		    attsWithXmlns.addAttribute
			("", nsAttrLocalName, nsAttrQName,
			 "CDATA", nsm.uri);
		}
		i.remove ();
	    }
	    super.startElement (uri, localName, qName, attsWithXmlns);
	}
    }

    /** After building a DOM representation of the input document,
     * transcode it to [EMAIL PROTECTED] TranscoderOutput} specified in [EMAIL PROTECTED]
     * #setOutput(TranscoderOutput)}
     *
     * @throws SAXException if [EMAIL PROTECTED] TranscoderException} occurs.
     * @throws NullPointerException if [EMAIL PROTECTED] TranscoderOutput} has
     * not been set with [EMAIL PROTECTED] #setOutput(TranscoderOutput)} */
    public void endDocument () throws SAXException {
	super.endDocument ();

	if (out == null) {
	    throw new NullPointerException
		("TranscoderOutput has not been set." +
		 " Call setOutput() before transcoding images");
	}

	// document defined in super.super; it is the reuslt of
	// building the document from SAX events
	TranscoderInput in = new TranscoderInput (document);
	try {
	    transcoder.transcode (in, out);
	} catch (TranscoderException te) {
	    throw new SAXException (te);
	}
    }

    /** A simple utility class that holds prefix/URI pairs for
     * namespace declarations. */
    protected class NamespaceMapping {
	protected String prefix;
	protected String uri;
	protected NamespaceMapping (String prefix, String uri) {
	    this.prefix = prefix;
	    this.uri = uri;
	}
    }

    /** For testing; not part of the public interface and should be
     * removed if the class is incorporated into a product. Transcodes
     * an SVG document on standard input to an PNG graphic on standard
     * output. */
    public static void main (String[] argv) throws Exception {
	if (argv.length != 0) {
	    System.err.println ("usage: java SVGTranscoderHandler < in.svg > out.png");
	    System.exit (2);
	}

	// instantiate transcoder handler
	TranscoderOutput out = new TranscoderOutput (System.out);
	Transcoder transcoder = new org.apache.batik.transcoder.image.PNGTranscoder ();
	SVGTranscoderHandler handler = new SVGTranscoderHandler (transcoder);
	handler.setOutput (out);

	// instantiate parser; classes not imported because they are
	// not used outside of this testing method
	javax.xml.parsers.SAXParserFactory spf = javax.xml.parsers.SAXParserFactory.newInstance ();
	spf.setNamespaceAware (true);
	org.xml.sax.XMLReader r = spf.newSAXParser().getXMLReader ();

	//r.setFeature("http://xml.org/sax/features/namespaces";, true);

	/* The next line is the critical feature. The superclass works
	 * with no modifications if this feature is set to
	 * true. Unfortunately, the SAX default for it is false, and
	 * XMLReaders are not required to support setting it to
	 * true. The point of this exercise is to make
	 * SVGDocumentFactory work correctly regardless of the value
	 * of this feature. */

	//r.setFeature("http://xml.org/sax/features/namespace-prefixes";, true);
	System.err.println ("namespaces:         " + r.getFeature("http://xml.org/sax/features/namespaces";));
	System.err.println ("namespace-prefixes: " + r.getFeature("http://xml.org/sax/features/namespace-prefixes";));

	// transform
	r.setContentHandler (handler);
	r.parse (new org.xml.sax.InputSource (System.in));
    }

}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to