109

Background: I need to provide a weekly report package for my sales staff. This package contains several (5-10) crystal reports.

Problem: I would like to allow a user to run all reports and also just run a single report. I was thinking I could do this by creating the reports and then doing:

List<ReportClass> reports = new List<ReportClass>();
reports.Add(new WeeklyReport1());
reports.Add(new WeeklyReport2());
reports.Add(new WeeklyReport3());
<snip>

foreach (ReportClass report in reports)
{
    report.ExportToDisk(ExportFormatType.PortableDocFormat, @"c:\reports\" + report.ResourceName + ".pdf");
}

This would provide me a folder full of the reports, but I would like to email everyone a single PDF with all the weekly reports. So I need to combine them.

Is there an easy way to do this without install any more third party controls? I already have DevExpress & CrystalReports and I'd prefer not to add too many more.

Would it be best to combine them in the foreach loop or in a seperate loop? (or an alternate way)

Engineert
  • 192
  • 1
  • 1
  • 16
Nathan Koop
  • 24,803
  • 25
  • 90
  • 125

18 Answers18

144

I had to solve a similar problem and what I ended up doing was creating a small pdfmerge utility that uses the PDFSharp project which is essentially MIT licensed.

The code is dead simple, I needed a cmdline utility so I have more code dedicated to parsing the arguments than I do for the PDF merging:

using (PdfDocument one = PdfReader.Open("file1.pdf", PdfDocumentOpenMode.Import))
using (PdfDocument two = PdfReader.Open("file2.pdf", PdfDocumentOpenMode.Import))
using (PdfDocument outPdf = new PdfDocument())
{                
    CopyPages(one, outPdf);
    CopyPages(two, outPdf);

    outPdf.Save("file1and2.pdf");
}

void CopyPages(PdfDocument from, PdfDocument to)
{
    for (int i = 0; i < from.PageCount; i++)
    {
        to.AddPage(from.Pages[i]);
    }
}
David d C e Freitas
  • 7,481
  • 4
  • 58
  • 67
Andrew Burns
  • 13,917
  • 9
  • 40
  • 42
  • Ah looks like Martin beat me to it, I am saying it was because I was diging up my code sample :) – Andrew Burns Apr 30 '09 at 19:46
  • Hi andrew would you pls take a look at this one ....pls http://stackoverflow.com/questions/6953471/system-crashing-when-the-print-button-is-clicked – user682417 Aug 05 '11 at 14:57
  • Has anyone else gotten "An object reference is required for the non-static field, method, or property..." error for this, around CopyPages(one, outDocument); CopyPages(two, outDocument); – user001 Sep 23 '15 at 15:32
  • 3
    If you install PDFSharp from nuget, make sure your use the pre-release version, or you might run into this error: http://stackoverflow.com/questions/36788746/pdfsharp-cannot-handle-this-pdf-feature-introduced-with-acrobat-6-error-while – mkimmet May 17 '17 at 14:13
  • I don't know if it is still maintained, neither if the site is just temporary not accessible or down since a while. Anyway link for NuGet https://www.nuget.org/packages/PdfSharp/ – Cfun Dec 08 '20 at 12:37
  • Brilliant! Still works like a champ... 8 lines of code I'm merging a whole directory of pdfs! Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); using (PdfDocument doc = new PdfDocument()) { string[] files = Directory.GetFiles(@"c:\temp\pdfMerge"); foreach (string file in files) { using (PdfDocument x = PdfReader.Open(file, PdfDocumentOpenMode.Import)) { for (int i = 0; i < x.PageCount; i++) { doc.AddPage(x.Pages[i]); } } } doc.Save(@"c:\temp\output.pdf"); } – iii Jan 31 '23 at 20:50
49

Here is a single function that will merge X amount of PDFs using PDFSharp

using PdfSharp;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

public static void MergePDFs(string targetPath, params string[] pdfs) {
    using(var targetDoc = new PdfDocument()){
        foreach (var pdf in pdfs) {
            using (var pdfDoc = PdfReader.Open(pdf, PdfDocumentOpenMode.Import)) {
                for (var i = 0; i < pdfDoc.PageCount; i++) 
                    targetDoc.AddPage(pdfDoc.Pages[i]);
            }
        }
        targetDoc.Save(targetPath);
    }
}
JustMaier
  • 2,101
  • 21
  • 23
11

This is something that I figured out, and wanted to share with you, using PdfSharp.

Here you can join multiple Pdfs in one, without the need of an output directory (following the input list order)

    public static byte[] MergePdf(List<byte[]> pdfs)
    {
        List<PdfSharp.Pdf.PdfDocument> lstDocuments = new List<PdfSharp.Pdf.PdfDocument>();
        foreach (var pdf in pdfs)
        {
            lstDocuments.Add(PdfReader.Open(new MemoryStream(pdf), PdfDocumentOpenMode.Import));
        }

        using (PdfSharp.Pdf.PdfDocument outPdf = new PdfSharp.Pdf.PdfDocument())
        { 
            for(int i = 1; i<= lstDocuments.Count; i++)
            {
                foreach(PdfSharp.Pdf.PdfPage page in lstDocuments[i-1].Pages)
                {
                    outPdf.AddPage(page);
                }
            }

            MemoryStream stream = new MemoryStream();
            outPdf.Save(stream, false);
            byte[] bytes = stream.ToArray();

            return bytes;
        }           
    }
  • 1
    Very good. I would add that if someone like me has an error: System.NotSupportedException: No data is available for encoding 1252. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. you can add in your Startup.cs or Program.cs or somewhere else: System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); after installing from NuGet the package: System.Text.Encoding.CodePages – RedPelle Sep 02 '22 at 11:12
5

I used iTextsharp with c# to combine pdf files. This is the code I used.

string[] lstFiles=new string[3];
    lstFiles[0]=@"C:/pdf/1.pdf";
    lstFiles[1]=@"C:/pdf/2.pdf";
    lstFiles[2]=@"C:/pdf/3.pdf";

    PdfReader reader = null;
    Document sourceDocument = null;
    PdfCopy pdfCopyProvider = null;
    PdfImportedPage importedPage;
    string outputPdfPath=@"C:/pdf/new.pdf";


    sourceDocument = new Document();
    pdfCopyProvider = new PdfCopy(sourceDocument, new System.IO.FileStream(outputPdfPath, System.IO.FileMode.Create));

    //Open the output file
    sourceDocument.Open();

    try
    {
        //Loop through the files list
        for (int f = 0; f < lstFiles.Length-1; f++)
        {
            int pages =get_pageCcount(lstFiles[f]);

            reader = new PdfReader(lstFiles[f]);
            //Add pages of current file
            for (int i = 1; i <= pages; i++)
            {
                importedPage = pdfCopyProvider.GetImportedPage(reader, i);
                pdfCopyProvider.AddPage(importedPage);
            }

            reader.Close();
         }
        //At the end save the output file
        sourceDocument.Close();
    }
    catch (Exception ex)
    {
        throw ex;
    }


private int get_pageCcount(string file)
{
    using (StreamReader sr = new StreamReader(File.OpenRead(file)))
    {
        Regex regex = new Regex(@"/Type\s*/Page[^s]");
        MatchCollection matches = regex.Matches(sr.ReadToEnd());

        return matches.Count;
    }
}
Sajitha Rathnayake
  • 1,688
  • 3
  • 26
  • 47
5

Here is a example using iTextSharp

public static void MergePdf(Stream outputPdfStream, IEnumerable<string> pdfFilePaths)
{
    using (var document = new Document())
    using (var pdfCopy = new PdfCopy(document, outputPdfStream))
    {
        pdfCopy.CloseStream = false;
        try
        {
            document.Open();
            foreach (var pdfFilePath in pdfFilePaths)
            {
                using (var pdfReader = new PdfReader(pdfFilePath))
                {
                    pdfCopy.AddDocument(pdfReader);
                    pdfReader.Close();
                }
            }
        }
        finally
        {
            document?.Close();
        }
    }
}

The PdfReader constructor has many overloads. It's possible to replace the parameter type IEnumerable<string> with IEnumerable<Stream> and it should work as well. Please notice that the method does not close the OutputStream, it delegates that task to the Stream creator.

mkl
  • 90,588
  • 15
  • 125
  • 265
hmadrigal
  • 1,020
  • 11
  • 19
3

Combining two byte[] using iTextSharp up to version 5.x:

internal static MemoryStream mergePdfs(byte[] pdf1, byte[] pdf2)
{
    MemoryStream outStream = new MemoryStream();
    using (Document document = new Document())
    using (PdfCopy copy = new PdfCopy(document, outStream))
    {
        document.Open();
        copy.AddDocument(new PdfReader(pdf1));
        copy.AddDocument(new PdfReader(pdf2));
    }
    return outStream;
}

Instead of the byte[]'s it's possible to pass also Stream's

mkl
  • 90,588
  • 15
  • 125
  • 265
Emaborsa
  • 2,360
  • 4
  • 28
  • 50
  • I added the information on the external library your code requires. Please always add all information required to make use of your answers. – mkl Jun 27 '16 at 09:15
3

PDFsharp seems to allow merging multiple PDF documents into one.

And the same is also possible with ITextSharp.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
M4N
  • 94,805
  • 45
  • 217
  • 260
1

There's some good answers here already, but I thought I might mention that pdftk might be useful for this task. Instead of producing one PDF directly, you could produce each PDF you need and then combine them together as a post-process with pdftk. This could even be done from within your program using a system() or ShellExecute() call.

Naaff
  • 9,213
  • 3
  • 38
  • 43
0

I combined the two above, because I needed to merge 3 pdfbytes and return a byte

internal static byte[] mergePdfs(byte[] pdf1, byte[] pdf2,byte[] pdf3)
        {
            MemoryStream outStream = new MemoryStream();
            using (Document document = new Document())
            using (PdfCopy copy = new PdfCopy(document, outStream))
            {
                document.Open();
                copy.AddDocument(new PdfReader(pdf1));
                copy.AddDocument(new PdfReader(pdf2));
                copy.AddDocument(new PdfReader(pdf3));
            }
            return outStream.ToArray();
        } 
Marimar
  • 43
  • 8
0

Following method gets a List of byte array which is PDF byte array and then returns a byte array.

using ...;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

public static class PdfHelper
{
    public static byte[] PdfConcat(List<byte[]> lstPdfBytes)
    {
        byte[] res;

        using (var outPdf = new PdfDocument())
        {
            foreach (var pdf in lstPdfBytes)
            {
                using (var pdfStream = new MemoryStream(pdf))
                using (var pdfDoc = PdfReader.Open(pdfStream, PdfDocumentOpenMode.Import))
                    for (var i = 0; i < pdfDoc.PageCount; i++)
                        outPdf.AddPage(pdfDoc.Pages[i]);
            }

            using (var memoryStreamOut = new MemoryStream())
            {
                outPdf.Save(memoryStreamOut, false);

                res = Stream2Bytes(memoryStreamOut);
            }
        }

        return res;
    }

    public static void DownloadAsPdfFile(string fileName, byte[] content)
    {
        var ms = new MemoryStream(content);

        HttpContext.Current.Response.Clear();
        HttpContext.Current.Response.ContentType = "application/pdf";
        HttpContext.Current.Response.AddHeader("content-disposition", $"attachment;filename={fileName}.pdf");
        HttpContext.Current.Response.Buffer = true;
        ms.WriteTo(HttpContext.Current.Response.OutputStream);
        HttpContext.Current.Response.End();
    }

    private static byte[] Stream2Bytes(Stream input)
    {
        var buffer = new byte[input.Length];
        using (var ms = new MemoryStream())
        {
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
                ms.Write(buffer, 0, read);

            return ms.ToArray();
        }
    }
}

So, the result of PdfHelper.PdfConcat method is passed to PdfHelper.DownloadAsPdfFile method.

PS: A NuGet package named [PdfSharp][1] need to be installed. So in the Package Manage Console window type:

Install-Package PdfSharp

Siyavash Hamdi
  • 2,764
  • 2
  • 21
  • 32
0

Following method merges two pdfs( f1 and f2) using iTextSharp. The second pdf is appended after a specific index of f1.

 string f1 = "D:\\a.pdf";
 string f2 = "D:\\Iso.pdf";
 string outfile = "D:\\c.pdf";
 appendPagesFromPdf(f1, f2, outfile, 3);




  public static void appendPagesFromPdf(String f1,string f2, String destinationFile, int startingindex)
        {
            PdfReader p1 = new PdfReader(f1);
            PdfReader p2 = new PdfReader(f2);
            int l1 = p1.NumberOfPages, l2 = p2.NumberOfPages;


            //Create our destination file
            using (FileStream fs = new FileStream(destinationFile, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                Document doc = new Document();

                PdfWriter w = PdfWriter.GetInstance(doc, fs);
                doc.Open();
                for (int page = 1; page <= startingindex; page++)
                {
                    doc.NewPage();
                    w.DirectContent.AddTemplate(w.GetImportedPage(p1, page), 0, 0);
                    //Used to pull individual pages from our source

                }//  copied pages from first pdf till startingIndex
                for (int i = 1; i <= l2;i++)
                {
                    doc.NewPage();
                    w.DirectContent.AddTemplate(w.GetImportedPage(p2, i), 0, 0);
                }// merges second pdf after startingIndex
                for (int i = startingindex+1; i <= l1;i++)
                {
                    doc.NewPage();
                    w.DirectContent.AddTemplate(w.GetImportedPage(p1, i), 0, 0);
                }// continuing from where we left in pdf1 

                doc.Close();
                p1.Close();
                p2.Close();

            }
        }
Viraj Singh
  • 1,951
  • 1
  • 17
  • 27
  • 2
    This is a very lossy method (all annotations are lost) and has issues merging pages with page rotation. Have a look at [hmadrigal's answer](https://stackoverflow.com/a/46067324/1729265) for a better approach. – mkl Aug 17 '18 at 14:04
  • Thanks, I'll check it. – Viraj Singh Aug 17 '18 at 17:39
0

To solve a similar problem i used iTextSharp like this:

//Create the document which will contain the combined PDF's
Document document = new Document();

//Create a writer for de document
PdfCopy writer = new PdfCopy(document, new FileStream(OutPutFilePath, FileMode.Create));
if (writer == null)
{
     return;
}

//Open the document
document.Open();

//Get the files you want to combine
string[] filePaths = Directory.GetFiles(DirectoryPathWhereYouHaveYourFiles);
foreach (string filePath in filePaths)
{
     //Read the PDF file
     using (PdfReader reader = new PdfReader(vls_FilePath))
     {
         //Add the file to the combined one
         writer.AddDocument(reader);
     }
}

//Finally close the document and writer
writer.Close();
document.Close();
AragornMx
  • 301
  • 2
  • 3
0
using iTextSharp.text;
using iTextSharp.text.pdf;

Need to include this and after that solution for example like this hope this will work:

internal static MemoryStream mergePdfs(byte[] pdf1, byte[] pdf2, byte[] pdf3)
            {
                MemoryStream outStream = new MemoryStream();
                using (Document document = new Document())
                using (PdfCopy copy = new PdfCopy(document, outStream))
                {
                    document.Open();
                    copy.AddDocument(new PdfReader(pdf1));
                    copy.AddDocument(new PdfReader(pdf2));
                    copy.AddDocument(new PdfReader(pdf3));
                }
                return outStream;
            }


 protected void btnMergePDF_Click(object sender, EventArgs e)
        {
    
            byte[] pdf1 = File.ReadAllBytes(@"\\XX.0.XX.XX\D$\Documents\TuitionFeeWaiver\15110010_2023_05_11_16_21_31_2.pdf");
            byte[] pdf2 = File.ReadAllBytes(@"\\XX.0.XX.XX\D$\Documents\TuitionFeeWaiver\15110010_2023_05_11_16_21_31_2.pdf");
            byte[] pdf3 = File.ReadAllBytes(@"\\XX.0.XX.XX\D$\Documents\TuitionFeeWaiver\15110010_2023_05_11_16_21_18_355151.pdf");
            MemoryStream mergedPdf = mergePdfs(pdf1, pdf2, pdf3);
            File.WriteAllBytes(@"\\10.X.XXX.XX\D$\Documents\TuitionFeeWaiver\MASTERMEARGE.pdf", mergedPdf.ToArray());

          
        }
Ronak Munjapara
  • 459
  • 5
  • 8
0

You could try pdf-shuffler gtk-apps.org

0

I know a lot of people have recommended PDF Sharp, however it doesn't look like that project has been updated since june of 2008. Further, source isn't available.

Personally, I've been playing with iTextSharp which has been pretty easy to work with.

NotMe
  • 87,343
  • 27
  • 171
  • 245
  • 2
    [PDFsharp 1.32 incl. sources](http://sourceforge.net/projects/pdfsharp/files/pdfsharp/PDFsharp%201.32/) (2012-03-07) – CodeFox Aug 02 '14 at 12:57
-1

Here is a link to an example using PDFSharp and ConcatenateDocuments

northpole
  • 10,244
  • 7
  • 35
  • 58
-1

Here the solution http://www.wacdesigns.com/2008/10/03/merge-pdf-files-using-c It use free open source iTextSharp library http://sourceforge.net/projects/itextsharp

Dmitri Kouminov
  • 593
  • 2
  • 6
  • 12
-1

I've done this with PDFBox. I suppose it works similarly to iTextSharp.

trendl
  • 1,139
  • 1
  • 10
  • 17