7

I have four MemoryStreams of data that I want to merge and then open the pdfDocument, without creating a single file.

It's possible to write them down to files and then merge them but that would be bad practice and that can also cause a few issues so I want to avoid that.

However, I can not find a way to merge the MemoryStreams with iText5 for .NET.

Right now, this is how I do it with files:

    private static void ConcatenateDocuments()
    {
        var stream = new MemoryStream();

        var readerFrontPage = new PdfReader(Folder + FrontPageName);
        var readerDocA = new PdfReader(Folder + docA);
        var readerDocB = new PdfReader(Folder + DocB);
        var readerAppendix = new PdfReader(Folder + Appendix);
        var pdfCopyFields = new PdfCopyFields(stream);

        pdfCopyFields.AddDocument(readerFrontPage);
        pdfCopyFields.AddDocument(readerDocA );
        pdfCopyFields.AddDocument(readerDocB);
        pdfCopyFields.AddDocument(readerAppendix);
        pdfCopyFields.Close();

        SavePdf(stream, FilenameReport);
    }

Since I need to remove the use of files, I keep the MemoryStream's as the different parts are built from different resources. So I have references to these memorystreams.

How can this be done?

Joris Schellekens
  • 8,483
  • 2
  • 23
  • 54
Johan
  • 753
  • 2
  • 11
  • 31
  • Can't you use var readerFrontPage = new PdfReader(yourMemoryStream); – user629926 Dec 05 '11 at 13:18
  • 1
    That's the thing, I can not get that working. One of the errors I run into is "PDF header signature not found". Most people that runs into this problem uses files however, and that's not an option here. – Johan Dec 05 '11 at 13:45
  • While it seams the PdfReader can not take the stream, the array of the stream works. var readerFrontPage = new PdfReader(streamFrontPage.ToArray()); Thou I can not answer my own question yet... – Johan Dec 05 '11 at 14:33

2 Answers2

22

The error PDF header signature not found can be fixed in this case by setting the stream's Position back to 0. Since you're not getting the error Cannot access a closed Stream I'm assuming that you are already correctly setting the PdfWriter's CloseStream to false.

Below is a full working C# 2010 WinForm app targeting iTextSharp 5.1.1.0 that creates three PDFs in MemoryStreams and combines them. Since I don't have a web server handy I'm writing them to disk.

using System;
using System.Text;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //Create three MemoryStreams
            MemoryStream[] streams = { CreateDoc("Page 1"), CreateDoc("Page 2"), CreateDoc("Page 3") };

            //I don't have a web server handy so I'm going to write my final MemoryStream to a byte array and then to disk
            byte[] bytes;

            //Create our final combined MemoryStream
            using (MemoryStream finalStream = new MemoryStream())
            {
                //Create our copy object
                PdfCopyFields copy = new PdfCopyFields(finalStream);

                //Loop through each MemoryStream
                foreach (MemoryStream ms in streams)
                {
                    //Reset the position back to zero
                    ms.Position = 0;
                    //Add it to the copy object
                    copy.AddDocument(new PdfReader(ms));
                    //Clean up
                    ms.Dispose();
                }
                //Close the copy object
                copy.Close();

                //Get the raw bytes to save to disk
                bytes = finalStream.ToArray();
            }

            //Write out the file to the desktop
            string outputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Combined.pdf");
            using (FileStream fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                fs.Write(bytes, 0, bytes.Length);   
            }

            this.Close();
        }

        /// <summary>
        /// Helper method to create temporary documents
        /// </summary>
        private MemoryStream CreateDoc(string name)
        {
            MemoryStream ms = new MemoryStream();
            using (Document doc = new Document(PageSize.LETTER))
            {
                using (PdfWriter writer = PdfWriter.GetInstance(doc, ms))
                {
                    writer.CloseStream = false;
                    doc.Open();
                    doc.Add(new Paragraph(name));
                    doc.Close();
                }
            }
            return ms;
        }
    }
}
Chris Haas
  • 53,986
  • 12
  • 141
  • 274
  • 1
    Perfect. In my case I was doing `stream.Position = 0` but in the wrong place: inside the `using` statement. After moving it outside it works great. By the way Chris: congrats for helping the community when it comes to iTextSharp. God bless you! :) – Leniel Maccaferri Aug 10 '12 at 20:56
  • Thanks, working for me when remembering to set CloseStream to false on writer and call close document before using stream. – Toft Jun 09 '14 at 11:51
  • How would you open the pdf rather than saving to the desktop in your example? – Standage Jul 08 '15 at 20:17
  • @Standage, are you using a webserver? If so you could [just write the bytes out](http://stackoverflow.com/a/13473931/231316). If you are on a desktop you can write the bytes out and call `System.Diagnostics.Process.Start()` on the file and as long as there is a handler registered it should open. It is very important to note that iTextSharp is in no way a PDF renderer or viewer. If you want to actually "look" at a PDF you'll need a program like Adobe Reader. – Chris Haas Jul 08 '15 at 20:32
  • 1
    I think most users have Adobe reader these days? I got my deisred results by passing the finalStream into a new method and using 'Response.OutputStream', thanks – Standage Jul 08 '15 at 21:45
  • 1
    I would say less people (as a percentage) have Adobe Reader installed these days compared to just a couple of years ago. Mac OSX has had Preview for years which can read PDFs and Windows 8 shipped with a built-in one, too. All major modern web browsers also ship with a native PDF renderer, too. I still find it funny whenever I see a website that uses the "Adobe Reader" icon to signify a PDF. But if you just mean that most people have a PDF renderer of some sort installed, not necessarily Adobe's, I think I'd agree with that. – Chris Haas Jul 09 '15 at 13:16
  • Chris, I used your solution Today in my application and it worked wonders for me .... – Prateju Patil Mar 23 '17 at 19:16
0

While it seams the PdfReader can not take the stream, the array of the stream works.

var readerFrontPage = new PdfReader(streamFrontPage.ToArray());

Johan
  • 753
  • 2
  • 11
  • 31