Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolving protected resources with Flying Saucer (ITextRenderer)

I'm using Flying Saucer to create a pdf from xhtml, hosted on a tomcat server. Most of the images included in the pdf are publicly available (logos and so on), but some of them are protected behind a login (that is, they are streamed through a servlet if the user is logged in).

When I paste the url in the browser, the image is of course displayed fine, because the browser sends the session with the request. But when Flying Saucer renders the pdf, it doesn't include the protected image because it doesn't know anything about the session.

So, my question is; is there any way to include the byte streams for Flying Saucer to resolve, just as it is possible to add resolvable fonts? I have tried something like this, but there is no easy way to set the UAC on the ITextRenderer, and it complained every time i tried.

like image 516
ManiSto Avatar asked Jan 24 '11 14:01

ManiSto


2 Answers

You can set the UserAgentCallback this way, and Flying Saucer will use it to resolve the urls (tested, works with Release 8):

ITextRenderer renderer = new ITextRenderer();
renderer.getSharedContext().setUserAgentCallback(new MyUAC());

MyUAC should extend the NaiveUserAgent, and override the resolveAndOpenStream method as the other page suggests.

like image 121
Adam Avatar answered Sep 22 '22 03:09

Adam


I overrode ITextUserAgent as well - from the source, looks like that's what ITextRenderer uses. You have to provide the output device in the constructor, which you can get from the renderer object. One other gotcha was you have to set the "shared context" explicitly using the setter method - otherwise you will get an NPE during rendering. Here is the code to set up the object:

ITextRenderer renderer = new ITextRenderer();
MyUserAgentCallback uac = new MyUserAgentCallback(renderer.getOutputDevice());
uac.setSharedContext(renderer.getSharedContext());
renderer.getSharedContext().setUserAgentCallback(uac);

Also, here is the basic idea of MyUserAgentCallback, using basic authentication:

private static class MyUserAgentCallback extends ITextUserAgent
{
    public MyUserAgentCallback(ITextOutputDevice outputDevice)
    {
        super(outputDevice);
    }

    @Override
    protected InputStream resolveAndOpenStream(String uri) 
    {
        if (_isProtectedResource(uri))
        {
            java.io.InputStream is = null;
            uri = resolveURI(uri);
            try {
                URL url = new URL(uri);
                String encoding = new BASE64Encoder().encode ("username:password".getBytes());
                URLConnection uc = url.openConnection();
                uc.setRequestProperty  ("Authorization", "Basic " + encoding);
                is = uc.getInputStream();
                Log.debug("got input stream");
            }
            catch (java.net.MalformedURLException e) {
                Log.error("bad URL given: " + uri, e);
            }
            catch (java.io.FileNotFoundException e) {
                Log.error("item at URI " + uri + " not found");
            }
            catch (java.io.IOException e) {
                Log.error("IO problem for " + uri, e);
            }
            return is;
        }
        else
        {
            return super.resolveAndOpenStream(uri);
        }
    }

    private boolean _isProtectedResource(String uri)
    {
        // does this require authentication?
    }
}
like image 44
KenE Avatar answered Sep 22 '22 03:09

KenE