2009-04-30 25 views
38

Antecedentes: Necesito proporcionar un paquete de informe semanal para mi personal de ventas. Este paquete contiene varios (5-10) informes de cristal.Combinar dos (o más) PDF's

Problema: Me gustaría permitir que un usuario ejecute todos los informes y también solo ejecute un solo informe. Estaba pensando que podría hacer esto mediante la creación de los informes y luego hacer:

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"); 
} 

Esto me una carpeta completa de los informes proporcionaría, pero me gustaría un correo electrónico a cada uno un único PDF con todos los informes semanales. Entonces necesito combinarlos.

¿Hay una manera fácil de hacerlo sin instalar ningún otro control de terceros? Ya tengo DevExpress & CrystalReports y prefiero no agregar demasiados más.

¿Sería mejor combinarlos en el bucle foreach o en un bucle separado? (O una forma alternativa)

Gracias

+0

Parece que voy a necesitar una biblioteca de terceros, gracias por toda la ayuda. –

Respuesta

54

Tuve que resolver un problema similar y lo que terminé haciendo fue crear una pequeña utilidad de pdfmerge que utiliza el proyecto PDFSharp que es esencialmente licenciado por MIT.

El código está muerto simple, que necesitaba una utilidad línea_de_órdenes así que tengo más código dedicado a analizar los argumentos que yo para el PDF fusión:

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]); 
    } 
} 
+0

Ah, parece que Martin me ganó, digo que fue porque estaba desenterrando mi código de ejemplo :) –

+0

Hola, Andrew, por favor echa un vistazo a este ... pls http://stackoverflow.com/ questions/6953471/system-crashing-when-the-print-button-is-clicked – user682417

+0

Alguien más ha recibido "Se requiere una referencia de objeto para el campo no estático, método o propiedad ..." error al respecto CopyPages (uno, outDocument); CopyPages (dos, outDocument); – user001

4

PDFsharp parece permitir la fusión de múltiples documentos PDF en uno solo.

Y lo mismo también es posible con ITextSharp.

0

que he hecho esto con PDFBox. Supongo que funciona de manera similar a iTextSharp.

1

Conozco a un montón de personas han recomendado PDF Sharp, sin embargo, no parece que el proyecto ha sido actualizado desde junio de 2008. Además, la fuente no está disponible.

Personalmente, he estado jugando con iTextSharp, que ha sido muy fácil de usar.

+1

[PDFsharp 1.32 incl. fuentes] (http://sourceforge.net/projects/pdfsharp/files/pdfsharp/PDFsharp%201.32/) (2012-03-07) – CodeFox

2

Aquí hay algunas buenas respuestas, pero pensé que podría mencionar que pdftk podría ser útil para esta tarea. En lugar de producir un PDF directamente, puede producir cada PDF que necesite y luego combinarlos como un proceso posterior con pdftk. Esto incluso podría hacerse desde dentro de su programa usando una llamada a system() o ShellExecute().

14

Aquí es una única función que se fusionará X cantidad de archivos PDF utilizando PDFsharp

public static void MergePDFs(string targetPath, params string[] pdfs) { 
    using(PdfDocument targetDoc = new PdfDocument()){ 
     foreach (string pdf in pdfs) { 
      using (PdfDocument pdfDoc = PdfReader.Open(pdf, PdfDocumentOpenMode.Import)) { 
       for (int i = 0; i < pdfDoc.PageCount; i++) { 
        targetDoc.AddPage(pdfDoc.Pages[i]); 
       } 
      } 
     } 
     targetDoc.Save(targetPath); 
    } 
} 
1

Solía ​​iTextSharp con C# para combinar archivos PDF. Este es el código que utilicé

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

    PdfReader reader = null; 
    Document sourceDocument = null; 
    PdfCopy pdfCopyProvider = null; 
    PdfImportedPage importedPage; 
    string [email protected]"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; 
    } 
} 
1

La combinación de dos byte[] usando iTextSharp hasta la versión 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; 
} 

En lugar de los byte[] 's Es posible pasar también Stream' s

+0

Agregué la información en la biblioteca externa que su código requiere. Por favor siempre agregue toda la información requerida para hacer uso de sus respuestas. – mkl

3

Esto es algo que me di fuera, y quería compartir con usted.

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

I combinan los dos anteriormente, debido a que necesitaba para combinar 3 pdfbytes y devolver un 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(); 
     } 
0

Siguiendo método consigue un List de byte matriz que es PDF byte matriz y a continuación, devuelve una matriz byte .

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(); 
     } 
    } 
} 

Por lo tanto, el resultado de PdfHelper.PdfConcat método se pasa a PdfHelper.DownloadAsPdfFile método.

PD: se debe instalar un paquete llamado [PdfSharp][1]. Así que en el tipo Package Manage Console ventana:

Install-Package PdfSharp

0

Aquí hay un ejemplo usando 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(); 
     } 
    } 
} 

El constructor PdfReader tiene muchas sobrecargas. Es posible reemplazar el tipo de parámetro IEnumerable<string> con IEnumerable<Stream> y debería funcionar también. Tenga en cuenta que el método no cierra el OutputStream, sino que delega esa tarea al creador de Stream.