I faced the same problem. I am using Flying Saucer to generate PDF from
JSF (xhtml) pages.
The reference to external on-line resources is indeed what was slowing
down my generation. In my case that was the <!DOCTYPE> with schema
declarations and xml name space references.
So I included some initial processing of the text before I give it to
Flying Saucer for rendering:
I create the export using a servlet filter. Here is what my filter code
looks like:
@WebFilter(filterName="Exporter", urlPatterns="*.xhtml",
asyncSupported=true)
public class RendererFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String pageName =
request.getRequestURI().substring(request.getRequestURI().indexOf("/")+1,request.getRequestURI().length());
//Check to see if this filter should apply.
String renderType = request.getParameter("RenderType");
// this is to render only single dome element if present
String renderId = request.getParameter("RenderId");
// sometimes there is an SVG chart on the page. It's data is processed
separately
String chartData = request.getParameter("chartData");
if (renderType != null && !renderType.equals("pdf")) {
//Capture the content for this request
String baseURL = request.getRequestURL().toString();
// this class wraps the Servlet response and captures the output
from the filter chain
ContentCaptureServletResponse capContent = new
ContentCaptureServletResponse(response);
filterChain.doFilter(request, capContent);
try {
//Parse the XHTML content to a document that is readable by the
XHTML renderer.
String content = capContent.getContent();
StringBuffer resContent = new StringBuffer();
// remove DOCTYPE tag - schema references in it result in extremely slow
processing
Pattern p = Pattern.compile("<[ ]*![ ]*DOCTYPE[^>]*>");
content = p.matcher(content).replaceAll("");
// remove the content of the <script> tags
p = Pattern.compile("<[ ]*script[^>]*>[^<]*<[ ]*/[
]*script[^>]*>");
content = p.matcher(content).replaceAll("");
if (chartData != null && !chartData.isEmpty()) {
// if we have SVG chart data - find where to render it and add it to
the DOM
p = Pattern.compile("(chartContainer[^>]*>)(\\.\\.\\.)(</div>)");
Matcher m = p.matcher(content);
if (m.find()) {
m.appendReplacement(resContent, "$1" + chartData + "$3");
}
m.appendTail(resContent);
content = resContent.toString();
// do some svg clean-up that otherwise will result in incorrect
rendering
p = Pattern.compile("NaN");
content = p.matcher(content).replaceAll("0");
resContent.delete(0, resContent.length());
p = Pattern.compile("(<[ ]*svg)");
m = p.matcher(content);
if (m.find()) {
// make sure the SVG elements have style="display:block;" - needed
for proper SVG rendering
m.appendReplacement(resContent, "$1" + "
style=\"display:block;\" ");
}
m.appendTail(resContent);
} else {
resContent.append(content);
}
InputSource source = new InputSource(new
ByteArrayInputStream(resContent.toString().getBytes()));
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
Document xhtmlContent = documentBuilder.parse(source);
if(renderId != null){
// if we want to render only a single DOM element - we extract it into
a new document
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr;
try {
expr = xpath.compile("//*[contains(@id, '"+renderId+"')]");
NodeList elem1List = (NodeList) expr.evaluate(xhtmlContent,
XPathConstants.NODESET);
if(elem1List.getLength()>0){
xhtmlContent = documentBuilder.newDocument();
xhtmlContent.setDocumentURI(baseURL);
xhtmlContent.adoptNode(elem1List.item(0));
xhtmlContent.appendChild(elem1List.item(0));
}
} catch (XPathExpressionException ex) {
Logger.getLogger(RendererFilter.class.getName()).log(Level.SEVERE,
null, ex);
}
}
if (renderType.equals("pdf")) {
// and finally do the rendering
ITextRenderer renderer = new ITextRenderer();
SharedContext ctx = renderer.getSharedContext();
ctx.setUserAgentCallback(new JSFUserAgent());
ChainedReplacedElementFactory cef = new
ChainedReplacedElementFactory();
cef.addFactory(new
ITextReplacedElementFactory(renderer.getOutputDevice()));
cef.addFactory(new SVGReplacedElementFactory());
ctx.setReplacedElementFactory(cef);
renderer.setDocument(xhtmlContent, baseURL);
renderer.layout();
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment;
filename="+pageName+".pdf");
OutputStream browserStream = response.getOutputStream();
try {
renderer.createPDF(browserStream);
} catch (com.lowagie.text.DocumentException ex) {
Logger.getLogger(RendererFilter.class.getName()).log(Level.SEVERE,
null, ex);
}
}
} catch (ParserConfigurationException ex) {
throw new ServletException(ex);
} catch (SAXException ex) {
throw new ServletException(ex);
}
} else {
//Normal processing
filterChain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
}
The JSFUserAgent class provides cashing for the image data and Faces
resources.
The SVGReplacedElement class and supporting factory class provides
rendering for the SVG data. It renders the data to a separate pdf element
which is than embedded in the main docment.
Regards,
Aleksandar Nikolov
On Tue, 26 Feb 2013 14:18:23 +0200, Patrick Wright <
pdou...@gmail.com>
wrote:
>>> DocumentBuilderFactory.**newInstance();
>>> DocumentBuilder localDocumentBuilder =
>>> localDocumentBuilderFactory.**newDocumentBuilder();
>>>
>>> localDocumentBuilder.**setEntityResolver(**FSEntityResolver.instance());
>>>
>>> Document localDocument = localDocumentBuilder.parse(new
>>> StringBufferInputStream(**paramString));
>>>
>>> localITextRenderer.**setDocument(localDocument, null);
--
Using Opera's revolutionary email client:
http://www.opera.com/mail/