Why is XMLWorker parsing slow?

Category: 
Tags: XML WorkerXMLXHTMLFontProvideriText 5

I am parsing HTML string using iTextSharp XMLWorker in my WPF application using the below code:

var css = "";
using (var htmlMS = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(html)))
{                    
    //Create a stream to read our CSS
    using (var cssMS = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(css)))
    {                        
        //Get an instance of the generic XMLWorker
        var xmlWorker = XMLWorkerHelper.GetInstance();
        //Parse our HTML using everything setup above
        xmlWorker.ParseXHtml(writer, doc, htmlMS, cssMS,
            System.Text.Encoding.UTF8, fontProv);
    }
}
The parsing works fine but it is really slow, it takes around 2 seconds to parse the HTML. So for a 50 page PDF it takes around 2 minutes. I am using inline styling to in my HTML string. Is this the natural behaviour or it can be optimized?

Posted on StackOverflow on Jan 22, 2014 by user2877090

The question suggests that the HTML parsing is slowing everything down. I'm convinced that the bottleneck occurs even before the first snippet of HTML is parsed.

You are using the most basic handful of lines of code to create your PDF from HTML as demonstrated in the ParseHtml example:

public void createPdf(String file) throws IOException, DocumentException {
    // step 1
    Document document = new Document();
    // step 2
    PdfWriter writer = PdfWriter.getInstance(document,
        new FileOutputStream(file));
    // step 3
    document.open();
    // step 4
    XMLWorkerHelper.getInstance().parseXHtml(writer, document,
            new FileInputStream(HTML));
    // step 5
    document.close();
}

This code is simple, but it performs a lot of operations internally such as registering font directories. This consumes plenty of time. You can avoid this, by using your own FontProvider as is done in the ParseHtmlFonts example.

public void createPdf(String file) throws IOException, DocumentException {
    // step 1
    Document document = new Document();
    // step 2
    PdfWriter writer = PdfWriter.getInstance(document,
        new FileOutputStream(file));
    writer.setInitialLeading(12.5f);
    // step 3
    document.open();
    // step 4
    // CSS
    CSSResolver cssResolver = new StyleAttrCSSResolver();
    CssFile cssFile = XMLWorkerHelper.getCSS(new FileInputStream(CSS));
    cssResolver.addCss(cssFile);
    // HTML
    XMLWorkerFontProvider fontProvider =
        new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
    fontProvider.register("resources/fonts/Cardo-Regular.ttf");
    fontProvider.register("resources/fonts/Cardo-Bold.ttf");
    fontProvider.register("resources/fonts/Cardo-Italic.ttf");
    fontProvider.addFontSubstitute("lowagie", "cardo");
    CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
    HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
    htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
    // Pipelines
    PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
    HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
    CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
    // XML Worker
    XMLWorker worker = new XMLWorker(css, true);
    XMLParser p = new XMLParser(worker);
    p.parse(new FileInputStream(HTML));
    // step 5
    document.close();
}

In this case, we instruct iText DONTLOOKFORFONTS, thus saving an enormous amount of time. Instead of having iText looking for fonts, we tell iText which fonts we're going to use in the HTML.