开发者

Flying Saucer not reading stylesheet

开发者 https://www.devze.com 2023-01-06 04:02 出处:网络
I am using flyingsaucer together with iText in an asp.net application(using IKVM) to convert HTML into PDF\'s开发者_StackOverflow社区. If I put a style directly in the html it works fine (even styles

I am using flyingsaucer together with iText in an asp.net application(using IKVM) to convert HTML into PDF's开发者_StackOverflow社区. If I put a style directly in the html it works fine (even styles put between style tags) but when I link a style sheet it fails to notice it and produces the pdf without the styles.

Any reason why this is happening?

This is the code that I am using

        Dim renderer As New ITextRenderer
        Dim buf As New StringBuffer
        buf.append(HTML)
        Dim builder As DocumentBuilder = DocumentBuilderFactory.newInstance.newDocumentBuilder()
        Dim doc As Document = builder.parse(New StringBufferInputStream(buf.toString))

        renderer.setDocument(doc, Nothing)
        renderer.layout()

        renderer.createPDF(os)

And this is link to the stylesheet

<link rel="stylesheet" href="stylemove.css" type="text/css"  />


from the FAQ:

My PDF isn't picking up my CSS!

PDF is treated as "print" media; see the CSS 2.1 specification section on media types. Make sure you have specified the media type for your CSS when you link or embed it; use type "print" or "all".


If you are using https then flying saucer would fail to read .css files until you make java's keystore include the certificate of your web server.

I have the same problem too... see this discussion

https://code.google.com/p/jmesa/issues/detail?id=182

If you solved it any other way, please let me know!!!

Thanks.


Easy Solution:

If you want a quick test to see if your document will work with your styles (without writing a lot of code to integrate it into your app). Just copy and paste the CSS you need into your Page.

The more-work Solution

My solution was to read the CSS and put it into html with a preprocessor. Since it was an older app that might not be fully xhtml compliant ,I used JSoup to load the html. The code below does the pre-processing. I will say that these are code-snippets to get you started. The great thing, once you've got it working, is you can transform any page on your server to PDF without any extra code. In my situation I setup a Filter to look for specific parameter. If that parameter is present I wrap the request with a request wrapper to get access to the final rendered bytes of the html page. I then use Jsoup to parse it, and then pre=process it.

/**this method uses JSOUP  Document here is org.jsoup.nodes.Document
*/
 @Override
     public void modifyDOM(MyResourceResolver resources, Document normalizedDOM) {

         //move style into head section
         Elements styleTags = normalizedDOM.getElementsByTag("style");

         normalizedDOM.select("style").remove();

         for (org.jsoup.nodes.Element linkElement : styleTags) {

             String curHead = normalizedDOM.head().html();
             normalizedDOM.head().html(curHead + "\n" + linkElement.html() + "\n");


         }


         //now resolve css
         Elements links = normalizedDOM.getElementsByTag("link");


         for (org.jsoup.nodes.Element linkElement : links) {

             String linkHref = linkElement.attr("href");
             if (linkHref == null) {
                 linkHref = "";
             }


             String mediaSelector = linkElement.attr("media");
             if (mediaSelector == null) {
                 mediaSelector = "";
             }
             mediaSelector = mediaSelector.trim();
             if ("".equalsIgnoreCase(mediaSelector) || ("print".equalsIgnoreCase(mediaSelector))) {

                 byte[] contents = resources.getContentsOfHref(linkHref);

                 if (null != contents) {
                     //we've got the info let's add to the document as is
                     Tag styleTag = Tag.valueOf("style");
                     Attributes styleAttributes = new Attributes();
                     styleAttributes.put("type", "text/css");
                     String baseUri = "";
                     org.jsoup.nodes.Element styleElement = new Element(styleTag, baseUri, styleAttributes);
                     styleElement.text(new String(contents));
                     String curHead = normalizedDOM.head().html();
                     normalizedDOM.head().html(curHead + "\n<style type='text/css'>" + styleElement.html() + "</style>\n");

                 }
             }


         }


         normalizedDOM.select("link").remove();
         normalizedDOM.select("script").remove();
     }

Since i'm inserting the css and flying saucer doesn't support javascript, I just remove those references from the document at the end of the pre processing. the class MyResourceResolver is just a class I wrote that has a reference to the servlet context. the method that actually reads the bytes of the css off of the server looks like this:

 public byte[] getContentsOfHref(String href) {
         byte[] retval = null;
         byte[] buf = new byte[8195];
         int nread;
         ByteArrayOutputStream bos = new ByteArrayOutputStream();

         InputStream is = null;
         try {

             if (href.startsWith("/myurlcontext")) {
                 href = href.substring("/myurlcontext".length());
             }
                 is = context.getResourceAsStream(href);
                 while ((nread = is.read(buf)) >= 0) {
                     bos.write(buf, 0, nread);
                 }
                 retval = bos.toByteArray();


         } catch (Exception ex) {
             //do nothing for now
         } finally {
             try {
                 is.close();
             } catch (Exception ex) {/*do nothing*/}
         }
         if (retval == null) {
             System.out.println("Did not find: " + href);
         } else {
             System.out.println("Found: " + href + " " + retval.length + " bytes");
         }
         return retval;
     }

The next question, how to initialize JSOUP Dom. Well, I do that in a request wrapper that reads the contents of the rendered JSP page and passes it to my PDF generating code:

 String renderedJSPString = new String(renderedJSP);
 //these escape sequences are nuisance in xhtml.
         renderedJSPString = renderedJSPString.replaceAll("&nbsp;|&copy;|&amp;|&lt;|&gt;", "");
         org.jsoup.nodes.Document parsedHtmlDOM = Jsoup.parse(renderedJSPString);
         org.jsoup.nodes.Document normalizedDOM = parsedHtmlDOM.normalise();
         normalizedDOM.outputSettings().escapeMode(Entities.EscapeMode.xhtml);
         normalizedDOM.outputSettings().prettyPrint(true);
 ...
 preProcessor.modifyDOM(resolver, normalizedDOM);
 ...


You are not setting the base URL of your document in your setDocument call. Flying Saucer needs that to resolve CSS and image links, as I discovered. For more details see this answer.

0

精彩评论

暂无评论...
验证码 换一张
取 消