4

I want to use iText to convert a series of html file to PDF.

For instance: if have these files:

  • page1.html
  • page2.html
  • page3.html
  • ...

Now I want to create a single PDF file, where page1.html is the first page, page2.html is the second page, and so on...

I know how to convert a single HTML file to a PDF, but I don't know how to combine these different PDFs resulting from this operation into a single PDF.

Bruno Lowagie
  • 75,994
  • 9
  • 109
  • 165
kyzh101
  • 101
  • 1
  • 9
  • I see that this question already got two downvotes, but I think the question can be saved provided that a clarification is added. (1.) Replace `HtmlWorker` with XML Worker (because `HtmlWorker` is no longer supported and won't meet your needs). (2.) write some pseudo code that makes sure that we interpret your question correctly. You want to create 1 PDF that is populated with content coming from a series of HTML files, but what is the "pagination" about? Do you want to start a new page for every new HTML file? Please clarify. – Bruno Lowagie Jan 07 '15 at 08:04
  • I want create 1 PDF with a series of HTML,for example ,HTML1 is the first page,html2 is the second page, all htmls are in the same pdf – kyzh101 Jan 07 '15 at 08:14
  • Great, I'll update your question in the hope that the people who voted to close your question reconsider ;-) – Bruno Lowagie Jan 07 '15 at 08:15
  • Please check if I edited your question correctly. There are two possible solutions for your problem: one is less optimal than the other. I'll write an answer later today, but I don't have time to do so in the next couple of hours. – Bruno Lowagie Jan 07 '15 at 08:19
  • Thank you very much,wait for your answer! – kyzh101 Jan 07 '15 at 08:39
  • OK, I've made two examples. I suggest that you follow example 2. I only added example 1 for the sake of completeness. – Bruno Lowagie Jan 07 '15 at 11:38

1 Answers1

7

Before we start: I am not a C# developer, so I can not give you an example in C#. All the iText examples I write, are written in Java. Fortunately, iText and iTextSharp are always kept in sync. In the context of this question, you can rest assure that whatever works for iText will also work for iTextSharp, but you'll have to make small adaptations that are specific to C#. From what I hear from C# developers, this is usually not hard to achieve.

Regarding the answer: there are two answers and answer #2 is generally better than answer #1, but I'm giving both options because there may be specific cases where answer #1 is better.

Test data: I have created 3 simple HTML files, each containing some info about a State in the US:

We are going to use XML Worker to parse these three files and we want a single PDF file as a result.

Answer #1: see ParseMultipleHtmlFiles1 for the full code sample and multiple_html_pages1.pdf for the resulting PDF.

You say that you already succeeded in converting one HTML file into one PDF files. It is assumed that you did it like this:

public byte[] parseHtml(String html) throws DocumentException, IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    // step 1
    Document document = new Document();
    // step 2
    PdfWriter writer = PdfWriter.getInstance(document, baos);
    // step 3
    document.open();
    // step 4
    XMLWorkerHelper.getInstance().parseXHtml(writer, document,
            new FileInputStream(html));
    // step 5
    document.close();
    // return the bytes of the PDF
    return baos.toByteArray();
}

This is not the most efficient way to parse an HTML file (there are other examples on the web site), but it's the simplest way.

As you can see, this method parse an HTML into a PDF file and returns that PDF file in the form of a byte[]. As we want to create a single PDF, we can feed this byte array to a PdfCopy instance, so that we can concatenate multiple documents.

Suppose that we have three documents:

public static final String[] HTML = {
    "resources/xml/page1.html",
    "resources/xml/page2.html",
    "resources/xml/page3.html"
};

We can loop over these three documents, parse them one by one to a byte[], create a PdfReader instance with the PDF bytes, and add the document to the PdfCopy instance using the addDocument() method:

public void createPdf(String file) throws IOException, DocumentException {
    Document document = new Document();
    PdfCopy copy = new PdfCopy(document, new FileOutputStream(file));
    document.open();
    PdfReader reader;
    for (String html : HTML) {
        reader = new PdfReader(parseHtml(html));
        copy.addDocument(reader);
        reader.close();
    }
    document.close();
} 

This solves your problem, but why do I think it's not the optimal solution?

Suppose that you need to use a special font that needs to be embedded. In that case, every separate PDF file will contain a subset of that font. Different files will require different font subsets, and PdfCopy (nor PdfSmartCopy for that matter) can merge font subsets. This could result in a bloated PDF file with way too many font subsets of the same font.

How do we solve this? That's explained in answer #2.

Answer #2: See ParseMultipleHtmlFiles2 for the full code sample and multiple_html_pages2.pdf for the resulting PDF. You already see the difference in file size: 4.61 KB versus 5.05 KB (and we didn't even introduce embedded fonts).

In this case, we don't parse the HTML to a PDF file the way we did in the parseHtml() method from answer #1. Instead, we parse the HTML to an iText ElementList using the parseToElementList() method. This method requires two Strings. One containing the HTML code, the other one containing CSS values.

We use a utility method to read the HTML file into a String. As for the CSS value, we could pass null to parseToElementList(), but in that case, default styles will be ignored. You'll notice that the <h1> tag we introduced in our HTML will look completely different if you don't pass the default.css that is shipped with XML Worker.

Long story short, this is the code:

public void createPdf(String file) throws IOException, DocumentException {
    Document document = new Document();
    PdfWriter.getInstance(document, new FileOutputStream(file));
    document.open();
    String css = readCSS();
    for (String htmlfile : HTML) {
        String html = Utilities.readFileToString(htmlfile);
        ElementList list = XMLWorkerHelper.parseToElementList(html, css);
        for (Element e : list) {
            document.add(e);
        }
        document.newPage();
    }
    document.close();
}

We create a single Document and a single PdfWriter instance. We parse the different HTML files into ElementLists one by one, and we add all the elements to the Document.

As you want a new page, each time a new HTML file is parsed, I introduced a document.newPage(). If you remove this line, you can add the three HTML pages on a single page (which wouldn't be possible if you would opt for answer #1).

Bruno Lowagie
  • 75,994
  • 9
  • 109
  • 165
  • I used the above two answers to convert,but the css is lost,this is my html.[link](http://prototype.ui.sh.ctriptravel.com/gerrit/gbk/master/UED/Flight/UED.Flight.online,_prototype_/print/fltInt_multi_itinerary.html) – kyzh101 Jan 13 '15 at 03:59
  • Hi Mr Bruno Lowagie,It seems that the iTextSharp can not spot some special css style – kyzh101 Jan 13 '15 at 07:54
  • The links shows a page saying "This domain maybe for sale. Click here for more information." Note that XML Worker can only do XHTML2PDF, **NOT** URL2PDF. iTextSharp sends pages to the `OutputStream` as soon as they are finished. That excludes CSS constructions that are added at the end of a HTML file, but that require content to be added to the first page. HTML is very different from PDF. Just try printing an HTML page from a browser, and already you'll bump into the limits of converting HTML to pages with a fixed size. – Bruno Lowagie Jan 13 '15 at 07:58
  • I have send an email,Thank you firstly! – kyzh101 Jan 13 '15 at 08:28
  • @BrunoLowagie can the same be done thru iText7? I have the same issue as in the original question. – iliak Apr 23 '19 at 21:06
  • @BrunoLowagie I've already found the link that explains the same but for the iText7, thanks. https://itextpdf.com/en/resources/books/itext-7-converting-html-pdf-pdfhtml/how-parse-multiple-html-files-one-pdf – iliak Apr 23 '19 at 21:32