You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-users@xmlgraphics.apache.org by Karl Roberts <Ka...@macquarie.com> on 2006/02/26 23:11:33 UTC

RE: Re how to embed a font programatically

Hi Jeremias,

Thanks for the update to Trunk, Your code was a bit tidier than mine,
but did the same thing, However I still got the NullPointerException.

The crux of the problem was that The URIReader was only being used to
resolve URI's for Images in the XSLT to transform my XML to XSL-FO. It
was not being used to resolve the URI of the font information passed in
via the fopconfig.xml file.

I traced the null pointer to a line in the "createFont(String path)"
method of FontReader:-

"parser.parse(path);"

The javadoc for "XMLReader.parse(String systemID)" states that:  "If the
system identifier is a URL, it must be fully resolved by the application
before it is passed to the parser."

However because it had not gon through the ServletContextURIResolver the
String passed in was still like
"servet-context:///WEB-INF/fontinfo.xml".

What would have been nice was the ability to inject a FontReader in the
same way as URIReader can be injected into the FOUserAgent before it is
passed to the Fop constructor, but I realised that the "path" should
always be fully resolved and if a custom URIReader is injected, then we
should use the FOUserAgent.resolveURI(String path) method to do it.

I solved the problem (comments welcome) by createing a ThreadLocal
Singleton (to attempt to avoid threading issues when used within a
servlet) called AgentHelper that is initialised in the Fop Constructor:-
VVVVVVVV

/** modified Fop constructor **/
public Fop(String outputFormat, FOUserAgent ua) {
        this.outputFormat = outputFormat;

        foUserAgent = ua;
        if (foUserAgent == null) {
            foUserAgent = new FOUserAgent();
        }
        boolean setOK =
AgentHelper.getInstance().setFOUserAgent(foUserAgent);
        
    }


And edited the "FontReder.createFont(String path)" method as follows, so
that it uses the URIReader on the path first:
VVVVVVVV

/** Modified FontReader.createFont method **/
private void createFont(String path) throws FOPException {
        XMLReader parser = null;

        try {
            final SAXParserFactory factory =
javax.xml.parsers.SAXParserFactory.newInstance();
            factory.setNamespaceAware(true);
            parser = factory.newSAXParser().getXMLReader();
        } catch (Exception e) {
            throw new FOPException(e);
        }
        if (parser == null) {
            throw new FOPException("Unable to create SAX parser");
        }

        try {
 
parser.setFeature("http://xml.org/sax/features/namespace-prefixes",
                              false);
        } catch (SAXException e) {
            throw new FOPException("You need a SAX parser which supports
SAX version 2",
                                   e);
        }

        parser.setContentHandler(this);

        try {
        	// *** TODO KARL Feature improvment to FOP once in
FOPTrunk, remove from here
        	//parser.parse(path);
        	FOUserAgent
foUserAgent=AgentHelper.getInstance().getFOUserAgent();
        	Source s =foUserAgent.resolveURI(path);
        	if(s!=null && s instanceof StreamSource)
        	{
        		InputSource is;
            	StreamSource ss = (StreamSource) s;	
			is=new InputSource(ss.getInputStream());
			parser.parse(is);
        	}
        	else
        	{
        		parser.parse(path);
        	}      	
        	// *** TODO End KARL Feature        
        } catch (SAXException e) {
            throw new FOPException(e);
        } catch (IOException e) {
            throw new FOPException(e);
        }
    }

And here is the AgentHelper Singleton:-
VVVVVVVV

import org.apache.fop.apps.FOUserAgent;

public class AgentHelper {

	private static AgentHelper singleton = new AgentHelper();	
	private AgentHelper() {
        // Exists only to defeat instantiation.
      }
	private static class ThreadLocalFOUserAgent extends ThreadLocal
{}
	private ThreadLocalFOUserAgent tlAgent = new
ThreadLocalFOUserAgent();

	public static AgentHelper getInstance()
	{
		return singleton; //no syncronization needed as
statically initialised
	}

	public FOUserAgent getFOUserAgent() {
		synchronized (tlAgent) {
			return (FOUserAgent) tlAgent.get();
		}
	}

	/**
	 * Only allows foUserAgent to be set once, I would have used a
final,  
	 * except it is placed in the ThreadLocal object.
	 * 
	 * @param foUserAgent
	 */
	public boolean setFOUserAgent(FOUserAgent foUserAgent) {
		boolean ret = false;
		synchronized (tlAgent) {
			if (tlAgent.get() == null) {
				tlAgent.set(foUserAgent);
				ret = true;
			}
		}
		return ret;
	}
}

Any thoughts on how this could be improoved and added to the Trunk so
that I don't have to maintain a separate branch of FOP?

Cheers

Karl Roberts 

-----Original Message-----
From: Jeremias Maerki [mailto:dev@jeremias-maerki.ch] 
Sent: Thursday, 23 February 2006 9:56 PM
To: fop-users@xmlgraphics.apache.org
Subject: Re: Re how to embed a font programatically

Have you seen my other post? I've implemented exactly what I proposed to
you and put it in FOP Trunk:
http://svn.apache.org/viewcvs?rev=379810&view=rev

More comments inline...

On 23.02.2006 07:02:33 Karl Roberts wrote:
> Hi I built a  URIResolver that reads URL's like:
> 
> servlet-context:/WEB-INF/metric.xml
> 
> And it works fine, however when it encounters a URL that doesn't match

> the servlet-context scheme I return null.

That's correct IMO. It's what I did.

> However I then get an NullPointerException Caused by: 
> javax.xml.transform.TransformerException:
> java.lang.NullPointerException
> 	at
> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform
> (T
> ransformerImpl.java:647)
> 	at
> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform
> (T
> ransformerImpl.java:279)
> 	....

Hmm, I don't get these with my implementation. TransformerException is
not the real exception. Look further down in the stack trace where you
should find the right place where the exception occurred. The above
doesn't really help. Setting an exception breakpoint for
NullPointerException will also be useful.

> I do return null from my URIResolver in the cases where the scheme 
> doesn't match the one I'm after, and FOURIResolver kicks in, but it 
> never seems to come back to my URIResolver.
> Should I instead delegate to FOURIResolver rather than return null?

No, I don't think so. There must be something else that's wrong. Can you
try my implementation instead?
http://svn.apache.org/repos/asf/xmlgraphics/fop/trunk/src/java/org/apach
e/fop/servlet/ServletContextURIResolver.java

<snip/>

Jeremias Maerki


---------------------------------------------------------------------
To unsubscribe, e-mail: fop-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-users-help@xmlgraphics.apache.org



NOTICE
This e-mail and any attachments are confidential and may contain copyright material of Macquarie Bank or third parties. If you are not the intended recipient of this email you should not read, print, re-transmit, store or act in reliance on this e-mail or any attachments, and should destroy all copies of them. Macquarie Bank does not guarantee the integrity of any emails or any attached files. The views or opinions expressed are the author's own and may not reflect the views or opinions of Macquarie Bank.


---------------------------------------------------------------------
To unsubscribe, e-mail: fop-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-users-help@xmlgraphics.apache.org


Re: Re how to embed a font programatically

Posted by Jeremias Maerki <de...@jeremias-maerki.ch>.
Karl,

sorry for the huge delay. When I don't have a patch marked as such in
our Bugzilla, I sometimes miss things even though your posts have
remained unread in my inbox. It would be great if you could create a
diff file ("svn diff") next time you'd like to propose a change and put
that in Bugzilla. That makes things a lot easier for us.

Anyway, I've looked at what you've done. I must say that I didn't really
get what that ThreadLocal was supposed to accomplish. I did various
tests with "servlet-context:" URIs and made sure that relative URIs are
now properly resolved. Everything should be fine now. Please test these
changes on your end so we're sure I've covered everything and the NPEs
have gone away.

Does your use of ThreadLocal mean you're reusing the FOUserAgent for
each rendering run? I'll soon merge some changes into FOP Trunk which
should make the whole thing a lot cleaner. The FOUserAgent instance is
not supposed to be reused for each rendering run.

On 26.02.2006 23:11:33 Karl Roberts wrote:
> Hi Jeremias,
> 
> Thanks for the update to Trunk, Your code was a bit tidier than mine,
> but did the same thing, However I still got the NullPointerException.
> 
> The crux of the problem was that The URIReader was only being used to
> resolve URI's for Images in the XSLT to transform my XML to XSL-FO. It
> was not being used to resolve the URI of the font information passed in
> via the fopconfig.xml file.
> 
> I traced the null pointer to a line in the "createFont(String path)"
> method of FontReader:-
> 
> "parser.parse(path);"
> 
> The javadoc for "XMLReader.parse(String systemID)" states that:  "If the
> system identifier is a URL, it must be fully resolved by the application
> before it is passed to the parser."
> 
> However because it had not gon through the ServletContextURIResolver the
> String passed in was still like
> "servet-context:///WEB-INF/fontinfo.xml".
> 
> What would have been nice was the ability to inject a FontReader in the
> same way as URIReader can be injected into the FOUserAgent before it is
> passed to the Fop constructor, but I realised that the "path" should
> always be fully resolved and if a custom URIReader is injected, then we
> should use the FOUserAgent.resolveURI(String path) method to do it.
> 
> I solved the problem (comments welcome) by createing a ThreadLocal
> Singleton (to attempt to avoid threading issues when used within a
> servlet) called AgentHelper that is initialised in the Fop Constructor:-
> VVVVVVVV
> 
> /** modified Fop constructor **/
> public Fop(String outputFormat, FOUserAgent ua) {
>         this.outputFormat = outputFormat;
> 
>         foUserAgent = ua;
>         if (foUserAgent == null) {
>             foUserAgent = new FOUserAgent();
>         }
>         boolean setOK =
> AgentHelper.getInstance().setFOUserAgent(foUserAgent);
>         
>     }
> 
> 
> And edited the "FontReder.createFont(String path)" method as follows, so
> that it uses the URIReader on the path first:
> VVVVVVVV
> 
> /** Modified FontReader.createFont method **/
> private void createFont(String path) throws FOPException {
>         XMLReader parser = null;
> 
>         try {
>             final SAXParserFactory factory =
> javax.xml.parsers.SAXParserFactory.newInstance();
>             factory.setNamespaceAware(true);
>             parser = factory.newSAXParser().getXMLReader();
>         } catch (Exception e) {
>             throw new FOPException(e);
>         }
>         if (parser == null) {
>             throw new FOPException("Unable to create SAX parser");
>         }
> 
>         try {
>  
> parser.setFeature("http://xml.org/sax/features/namespace-prefixes",
>                               false);
>         } catch (SAXException e) {
>             throw new FOPException("You need a SAX parser which supports
> SAX version 2",
>                                    e);
>         }
> 
>         parser.setContentHandler(this);
> 
>         try {
>         	// *** TODO KARL Feature improvment to FOP once in
> FOPTrunk, remove from here
>         	//parser.parse(path);
>         	FOUserAgent
> foUserAgent=AgentHelper.getInstance().getFOUserAgent();
>         	Source s =foUserAgent.resolveURI(path);
>         	if(s!=null && s instanceof StreamSource)
>         	{
>         		InputSource is;
>             	StreamSource ss = (StreamSource) s;	
> 			is=new InputSource(ss.getInputStream());
> 			parser.parse(is);
>         	}
>         	else
>         	{
>         		parser.parse(path);
>         	}      	
>         	// *** TODO End KARL Feature        
>         } catch (SAXException e) {
>             throw new FOPException(e);
>         } catch (IOException e) {
>             throw new FOPException(e);
>         }
>     }
> 
> And here is the AgentHelper Singleton:-
> VVVVVVVV
> 
> import org.apache.fop.apps.FOUserAgent;
> 
> public class AgentHelper {
> 
> 	private static AgentHelper singleton = new AgentHelper();	
> 	private AgentHelper() {
>         // Exists only to defeat instantiation.
>       }
> 	private static class ThreadLocalFOUserAgent extends ThreadLocal
> {}
> 	private ThreadLocalFOUserAgent tlAgent = new
> ThreadLocalFOUserAgent();
> 
> 	public static AgentHelper getInstance()
> 	{
> 		return singleton; //no syncronization needed as
> statically initialised
> 	}
> 
> 	public FOUserAgent getFOUserAgent() {
> 		synchronized (tlAgent) {
> 			return (FOUserAgent) tlAgent.get();
> 		}
> 	}
> 
> 	/**
> 	 * Only allows foUserAgent to be set once, I would have used a
> final,  
> 	 * except it is placed in the ThreadLocal object.
> 	 * 
> 	 * @param foUserAgent
> 	 */
> 	public boolean setFOUserAgent(FOUserAgent foUserAgent) {
> 		boolean ret = false;
> 		synchronized (tlAgent) {
> 			if (tlAgent.get() == null) {
> 				tlAgent.set(foUserAgent);
> 				ret = true;
> 			}
> 		}
> 		return ret;
> 	}
> }
> 
> Any thoughts on how this could be improoved and added to the Trunk so
> that I don't have to maintain a separate branch of FOP?
> 
> Cheers
> 
> Karl Roberts 
> 
> -----Original Message-----
> From: Jeremias Maerki [mailto:dev@jeremias-maerki.ch] 
> Sent: Thursday, 23 February 2006 9:56 PM
> To: fop-users@xmlgraphics.apache.org
> Subject: Re: Re how to embed a font programatically
> 
> Have you seen my other post? I've implemented exactly what I proposed to
> you and put it in FOP Trunk:
> http://svn.apache.org/viewcvs?rev=379810&view=rev
> 
> More comments inline...
> 
> On 23.02.2006 07:02:33 Karl Roberts wrote:
> > Hi I built a  URIResolver that reads URL's like:
> > 
> > servlet-context:/WEB-INF/metric.xml
> > 
> > And it works fine, however when it encounters a URL that doesn't match
> 
> > the servlet-context scheme I return null.
> 
> That's correct IMO. It's what I did.
> 
> > However I then get an NullPointerException Caused by: 
> > javax.xml.transform.TransformerException:
> > java.lang.NullPointerException
> > 	at
> > com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform
> > (T
> > ransformerImpl.java:647)
> > 	at
> > com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform
> > (T
> > ransformerImpl.java:279)
> > 	....
> 
> Hmm, I don't get these with my implementation. TransformerException is
> not the real exception. Look further down in the stack trace where you
> should find the right place where the exception occurred. The above
> doesn't really help. Setting an exception breakpoint for
> NullPointerException will also be useful.
> 
> > I do return null from my URIResolver in the cases where the scheme 
> > doesn't match the one I'm after, and FOURIResolver kicks in, but it 
> > never seems to come back to my URIResolver.
> > Should I instead delegate to FOURIResolver rather than return null?
> 
> No, I don't think so. There must be something else that's wrong. Can you
> try my implementation instead?
> http://svn.apache.org/repos/asf/xmlgraphics/fop/trunk/src/java/org/apach
> e/fop/servlet/ServletContextURIResolver.java
> 
> <snip/>


Jeremias Maerki


---------------------------------------------------------------------
To unsubscribe, e-mail: fop-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-users-help@xmlgraphics.apache.org