0
votes

I've recently used iTextSharp to create a PDF by importing the 20 pages from an existing PDF and then adding a dynamically generated link to the bottom of the last page. It works fine... kind of. Viewing the generated PDF in Acrobat Reader on a windows PC displays everything as expected although when closing the document it always asks "Do you want to save changes?". Viewing the generated PDF on a Surface Pro with PDF Reader displays the document without the first and last pages. Apparently on a mobile device using Polaris Office the first and last pages are also missing.

I'm wondering if when the new PDF is generated it's not getting closed off quite properly and that's why it asks "Do you want to save changes?" when closing it. And maybe that's also why it doesn't display correctly in some PDF reader apps.

Here's the code:

    using (var reader = new PdfReader(HostingEnvironment.MapPath("~/app/pdf/OriginalDoc.pdf")))
    {


        using (
            var fileStream =
                new FileStream(
                    HostingEnvironment.MapPath("~/documents/attachments/DocWithLink_" + id + ".pdf"),
                    FileMode.Create, FileAccess.Write))
        {
            var document = new Document(reader.GetPageSizeWithRotation(1));
            var writer = PdfWriter.GetInstance(document, fileStream);

            using (PdfStamper stamper = new PdfStamper(reader, fileStream))
            {
                var baseFont = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252,
                    BaseFont.NOT_EMBEDDED);
                Font linkFont = FontFactory.GetFont("Arial", 12, Font.UNDERLINE, BaseColor.BLUE);
                document.Open();

                for (var i = 1; i <= reader.NumberOfPages; i++)
                {
                    document.NewPage();

                    var importedPage = writer.GetImportedPage(reader, i);
                    // Copy page of original document to new document.

                    var contentByte = writer.DirectContent;
                    contentByte.AddTemplate(importedPage, 0, 0);

                    if (i == reader.NumberOfPages) // It's the last page so add link.
                    {
                        PdfContentByte cb = stamper.GetOverContent(i);

                        //Create a ColumnText object
                        var ct = new ColumnText(cb);
                        //Set the rectangle to write to
                        ct.SetSimpleColumn(100, 30, 500, 90, 0, PdfContentByte.ALIGN_LEFT);

                        //Add some text and make it blue so that it looks like a hyperlink
                        var c = new Chunk("Click here!", linkFont);

                        var congrats = new Paragraph("Congratulations on reading the eBook!    ");
                        congrats.Alignment = PdfContentByte.ALIGN_LEFT;

                        c.SetAnchor("http://www.domain.com/pdf/response/" + encryptedId);
                        //Add the chunk to the ColumnText
                        congrats.Add(c);
                        ct.AddElement(congrats);

                        //Tell the system to process the above commands
                        ct.Go();
                    }
                }
            }
        }
    }

I've looked at these posts with similar issues but none seem to quite provide the answer I need: iTextSharp-generated PDFs cause save dialog when closing
Using iTextSharp to write data to PDF works great, but Acrobat Reader asks 'Do you want to save changes' when closing file (Or they refer to memory streams instead of writing to disk etc)

My question is, how do I modify the above so that when closing the generated PDF in Acrobat Reader there's no "Do you want to save changes?" prompt. The answer to that may solve the problems with missing pages on Surface Pro etc but if you know anything else about what might be causing that I'd like to hear about it.

Any suggestions would be very welcome! Thanks!

1
Please share a sample result PDF.mkl

1 Answers

3
votes

At first glance (and without much coffee yet) it appears that you're using a PdfReader in three different contexts, as a source to a PdfStamper, as a source for Document and as for a source for importing. So you are essentially importing a document into itself that you're also writing to.

To give you a quick overview, the following code will essentially clone the contents of source.pdf into dest.pdf:

using (var reader = new PdfReader("source.pdf")){
    using (var fileStream = new FileStream("dest.pdf", FileMode.Create, FileAccess.Write)){
        using (PdfStamper stamper = new PdfStamper(reader, fileStream)){
        }
    }
}

Since that does all of the cloning for you you don't need to import pages or anything.

Then, if the only thing that you want to do is add some text to the last page, you can just use the above and ask the PdfStamper for a PdfContentByte using GetOverContent() and telling it what page number you're interested. Then you can just use the rest of your ColumnText logic.

using (var reader = new PdfReader("Source.Pdf")) {
    using (var fileStream = new FileStream("Dest.Pdf"), FileMode.Create, FileAccess.Write) {
        using (PdfStamper stamper = new PdfStamper(reader, fileStream)) {

            //Get a PdfContentByte object
            var cb = stamper.GetOverContent(reader.NumberOfPages);

            //Create a ColumnText object
            var ct = new ColumnText(cb);
            //Set the rectangle to write to
            ct.SetSimpleColumn(100, 30, 500, 90, 0, PdfContentByte.ALIGN_LEFT);

            //Add some text and make it blue so that it looks like a hyperlink
            var c = new Chunk("Click here!", linkFont);

            var congrats = new Paragraph("Congratulations on reading the eBook!    ");
            congrats.Alignment = PdfContentByte.ALIGN_LEFT;

            c.SetAnchor("http://www.domain.com/pdf/response/" + encryptedId);
            //Add the chunk to the ColumnText
            congrats.Add(c);
            ct.AddElement(congrats);

            //Tell the system to process the above commands
            ct.Go();
        }
    }
}