FOP: how to specify image src relative path?

2019-03-15 18:07发布

问题:

This is my first question here, i hope i'm doing it right. Sorry for my bad English in advance :)

I am using JSF 2.0 (Eclipse IDE) and i'm trying to generate some PDF files using Apache FOP 1.0.

I was able to make simple PDF files using instructions on Apache Fop site , but i can't insert any image from my application folder. My folder structure is like this: In my application WebContent i have (among else) pdf_transform/xslt/transformFile.xsl, and pdf_transform/xslt/logo.jpg

In transformFile.xsl i have

<fo:block><fo:external-graphic src="url('logo.jpg')"/></fo:block>

but when i clik 'showPDF' button in my servlet, i get PDF file without image (everything else is there), and this messages in console:

SEVERE: The Source that was returned from URI resolution didn't contain an InputStream for URI: logo.jpg Nov 18, 2010 5:16:49 PM org.apache.fop.events.LoggingEventListener processEvent SEVERE: Image not found. URI: logo.jpg. (No context info available)

I tried to use 'logo.jpg' instead of url('logo.jpg'), putting image on various places inside WebContent folder and using different navigation("./logo.jpg") but it didnt work.

It works fine if i set absolute path (for example "d:/fop/images/logo.jpg") but i need resurces whitin my application.

While searching, i found that this is related to fopFactory.setURIResolver() and/or userAgent.setBaseURL(). Tried something with that, but didnt succeed.

I am new to both JSF and FOP, and this image situation has been bothering me quite a while. Can someone help me with this, or at least direct me to some tutorial on "how to configure FOP for relative path use"?

EDIT: I don't want any absolute paths and app should work independently of its location on computer (to be publishable). My search tells me it has something to do with configuring FOP, but i don't know how to do it :)

Thanks in advance.

P.S. This is method which is called to display PDF:

public void printExchangeRateList(ActionEvent event) {

    BufferedOutputStream output = null;

    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();
    HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();  

    String path = externalContext.getRealPath("/");


    try {

        response.reset();
        response.setHeader("Content-Type", "application/pdf");
        output = new BufferedOutputStream(response.getOutputStream(), 10240);

        File xsltfile = new File(path+"/pdf_transform/xslt/transformFile.xsl");

        FopFactory fopFactory = FopFactory.newInstance();
        FOUserAgent foUserAgent = fopFactory.newFOUserAgent();

        try {
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, output);

            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer(new StreamSource(xsltfile));

            Source src = new DOMSource(makeXML()); // my method
            Result res = new SAXResult(fop.getDefaultHandler());

            transformer.transform(src, res);


        } finally {
            if (output != null) output.close();
            /*try {
                out.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }*/
        }

    } catch (Exception e) {
        // TODO Auto-generated catch block
    }

    facesContext.responseComplete();
}

回答1:

i found solution to my problem. I thought i tried that, but it seems i made some little mistake back then. Anyway, with the following code

FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
String basePath = externalContext.getRealPath("/");

FopFactory fopFactory = FopFactory.newInstance();
fopFactory.setBaseURL(basePath);
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
foUserAgent.setBaseURL(fopFactory.getBaseURL());

Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, output); // for some output

you can access your images (and other resources) from your xslt file using relative path starting from your application's WebContent folder. In my case, i can access logo.jpg like this

<fo:external-graphic src="url('pdf_transform/xslt/logo.jpg')"/>

Took me time to figure out this, i don't get it why no examples with such basic thing on the net (or i can't find them :)

Note: In FOP 2.0 there is no setBaseURL() method. Instead you pass the base URL as a parameter to FopFactory.newInstance(). Many of the other setters have been moved to FopFactoryBuilder.



回答2:

If you have access to the web url for the pictures you can use that as well when generating reports, ie http://localhost/images/logo.jpg .

But while I still had images locally on the web server, I included the path to the application in the XML file and used it like this:

<xsl:variable name="base_path" select="base-path"/>
<xsl:variable name="logo" select="companies/company/logo"/>
<fo:external-graphic src="url({$base_path}{logo})"/>

Where the XML structure might be something like this:

<?xml version="1.0" encoding="UTF-8"?>
<base-path>/path/to/app/</base-path>
<companies>
  <company>
    <logo>images/company1.jpg</logo>
  </company>
  <company>
    <logo>images/company2.jpg</logo>
  </company>
</companies>


回答3:

I had the same problem and tried this solution:

    FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
    Request request = RequestCycle.get().getRequest();
    //sort of a hack to find the path to the files that are in /img folder.
    String baseUrl = request.getUrl().getProtocol()+"://"+request.getUrl().getHost()+":"+request.getUrl().getPort();  
    foUserAgent.setBaseURL(baseUrl);

Then, on XSL I used:

<fo:external-graphic src="/img/image.png" />

To test if this is working, you should be able to see the image on protocol://link:port/img/image.png