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(" |©|&|<|>", "");
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.
精彩评论