2013-04-10 10 views
17

Wie Sie mehrere PDF-Dateien (generiert zur Laufzeit) durch ItextSharp zusammenführen und dann drucken.Wie füge ich mehrere PDF-Dateien zusammen (generiert zur Laufzeit)?

Ich fand die folgenden link, aber diese Methode erfordert die pdf-Namen unter Berücksichtigung, dass die PDF-Dateien gespeichert und das ist nicht mein Fall.


Ich habe mehrere Berichte, die ich ihnen zu pdf files durch diese Methode konvertiert werden:

private void AddReportToResponse(LocalReport followsReport) 
{ 
    string mimeType; 
    string encoding; 
    string extension; 
    string[] streams = new string[100]; 
    Warning[] warnings = new Warning[100]; 
    byte[] pdfStream = followsReport.Render("PDF", "", out mimeType, out encoding, out extension, out streams, out warnings); 
    //Response.Clear(); 
    //Response.ContentType = mimeType; 
    //Response.AddHeader("content-disposition", "attachment; filename=Application." + extension); 
    //Response.BinaryWrite(pdfStream); 
    //Response.End(); 
} 

Jetzt möchte ich alle, die generierten Dateien (Bytes) in einer PDF-Datei zusammenführen es

zu drucken
+1

ich denke Frage zu diesem ähnlich ist: http://stackoverflow.com/questions/3961804/itextsharp-creation-of-a-pdf-from-a-list-of-byte-arrays –

+0

die Proben Sie gefunden und die othe r Kommentatoren, auf die Sie hingewiesen haben, verwenden Sie 'PdfReader', um die Quelldokumente zu lesen. 'PdfReader' hat mehrere Konstruktoren, einige nehmen eine Dateinamenszeichenfolge als Argument, einige Bytearrays enthalten die PDF; Verwenden Sie in Ihrem Fall die letzteren. Aus den gefundenen Zusammenführungs-Samples sollten Sie jedoch keinen mit 'PdfWriter' wählen, sondern mit' PdfCopy, PdfSmartCopy, PdfCopyFields' oder 'PdfCopyForms' (die Auswahl hängt von Ihren genauen Anforderungen ab). – mkl

+0

@mkl: Könnte U bitte ein Beispiel zur Verfügung stellen, um das Problem zu beheben? –

Antwort

45

Wenn Sie Quelldokumente fusionieren mit iText (Sharp), gibt es zwei grundlegende Situationen:

  1. Sie wirklich wollen, um die Dokumente zusammenführen, die Seiten in ihrem ursprünglichen Format zu erwerben, so viel von Übertragung ihre Inhalte und ihre interaktiven Anmerkungen wie möglich. In diesem Fall sollten Sie eine Lösung basierend auf einem Mitglied der Pdf*Copy* Familie von Klassen verwenden.

  2. Eigentlich möchten Sie Seiten aus den Quelldokumenten in ein neues Dokument integrieren, aber das neue Dokument soll das allgemeine Format steuern und die interaktiven Funktionen (Anmerkungen ...) in den Originaldokumenten (oder will sie sogar loswerden). In diesem Fall sollten Sie eine Lösung verwenden, die auf der Klasse PdfWriter basiert.

können Details finden Sie in chapter 6 (insbesondere Abschnitt 6.4) von iText in Action — 2nd Edition. Auf den Java-Beispielcode kann zugegriffen werden here und die C# -Filterversionen here.

Eine einfache Probe mit PdfCopy ist Concatenate.java/Concatenate.cs. Der zentrale Teil des Codes ist:

byte[] mergedPdf = null; 
using (MemoryStream ms = new MemoryStream()) 
{ 
    using (Document document = new Document()) 
    { 
     using (PdfCopy copy = new PdfCopy(document, ms)) 
     { 
      document.Open(); 

      for (int i = 0; i < pdf.Count; ++i) 
      { 
       PdfReader reader = new PdfReader(pdf[i]); 
       // loop over the pages in that document 
       int n = reader.NumberOfPages; 
       for (int page = 0; page < n;) 
       { 
        copy.AddPage(copy.GetImportedPage(reader, ++page)); 
       } 
      } 
     } 
    } 
    mergedPdf = ms.ToArray(); 
} 

Hier pdf kann entweder als sofort List<byte[]> definiert werden, die Quelldokumente enthalten (geeignet für Ihren Anwendungsfall von verschmelzenden Zwischen im Speicher befindlichen Dokumenten) oder als List<String> mit den Namen Quelldokumentdateien (geeignet, wenn Sie Dokumente vom Datenträger zusammenführen).

Eine Übersicht am Ende des referenzierten Kapitel fasst die Verwendung der genannten Klassen:

  • PdfCopy: Kopiert Seiten aus einem oder mehreren vorhandenen PDF-Dokumenten. Wichtige Nachteile: PdfCopy erkennt keinen redundanten Inhalt und schlägt beim Verketten von Formularen fehl.

  • PdfCopyFields: Versetzt die Felder der verschiedenen Formen in einer Form. Kann verwendet werden, um die Probleme zu vermeiden, die bei Formularfeldern beim Verketten von Formularen auftreten, die PdfCopy verwenden. Speicherverbrauch kann ein Problem sein.

  • PdfSmartCopy: Kopiert Seiten aus einem oder mehreren vorhandenen PDF-Dokumenten. kann redundante Inhalte erkennen, benötigt aber mehr Speicher und CPU als PdfCopy.

  • PdfWriter: Erzeugt PDF-Dokumente von Grund auf neu. Kann Seiten aus anderen PDF-Dokumenten importieren. Der größte Nachteil besteht darin, dass alle interaktiven Funktionen der importierten Seite (Anmerkungen, Lesezeichen, Felder usw.) dabei verloren gehen.

+0

Es ist wirklich interessant zu sehen, dass jemand diese Antwort abgelehnt hat, ohne einen Kommentar zu hinterlassen, der Mängel erklärt ... Das heißt, iText hat sich mittlerweile entwickelt und viele der 'PdfCopyFields'-spezifischen Sachen haben ihren Weg in 'PdfCopy' gefunden. – mkl

+0

Es Fusionen wirklich langsam (I upvoted BTW.) –

+1

@BonusKun * Es Fusionen wirklich langsam * - wenn es außergewöhnlich langsam ist, möchten Sie vielleicht eine Frage in seinem eigenen Recht erstellen Beispieldokumente erstellen, um das Problem zu reproduzieren ... * (Ich uptoted BTW.) * - Danke! – mkl

4

Hier ist ein Code, den ich aus einem alten Projekt gezogen habe, das ich hatte. Es war eine Webanwendung, aber ich benutzte iTextSharp, um PDF-Dateien zusammenzuführen und dann zu drucken.

public static class PdfMerger 
    { 
     /// <summary> 
     /// Merge pdf files. 
     /// </summary> 
     /// <param name="sourceFiles">PDF files being merged.</param> 
     /// <returns></returns> 
     public static byte[] MergeFiles(List<Stream> sourceFiles) 
     { 
      Document document = new Document(); 
      MemoryStream output = new MemoryStream(); 

      try 
      { 
       // Initialize pdf writer 
       PdfWriter writer = PdfWriter.GetInstance(document, output); 
       writer.PageEvent = new PdfPageEvents(); 

       // Open document to write 
       document.Open(); 
       PdfContentByte content = writer.DirectContent; 

       // Iterate through all pdf documents 
       for (int fileCounter = 0; fileCounter < sourceFiles.Count; fileCounter++) 
       { 
        // Create pdf reader 
        PdfReader reader = new PdfReader(sourceFiles[fileCounter]); 
        int numberOfPages = reader.NumberOfPages; 

        // Iterate through all pages 
        for (int currentPageIndex = 1; currentPageIndex <= 
             numberOfPages; currentPageIndex++) 
        { 
         // Determine page size for the current page 
         document.SetPageSize(
          reader.GetPageSizeWithRotation(currentPageIndex)); 

         // Create page 
         document.NewPage(); 
         PdfImportedPage importedPage = 
          writer.GetImportedPage(reader, currentPageIndex); 


         // Determine page orientation 
         int pageOrientation = reader.GetPageRotation(currentPageIndex); 
         if ((pageOrientation == 90) || (pageOrientation == 270)) 
         { 
          content.AddTemplate(importedPage, 0, -1f, 1f, 0, 0, 
           reader.GetPageSizeWithRotation(currentPageIndex).Height); 
         } 
         else 
         { 
          content.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0); 
         } 
        } 
       } 
      } 
      catch (Exception exception) 
      { 
       throw new Exception("There has an unexpected exception" + 
         " occured during the pdf merging process.", exception); 
      } 
      finally 
      { 
       document.Close(); 
      } 
      return output.GetBuffer(); 
     } 
    } 



    /// <summary> 
    /// Implements custom page events. 
    /// </summary> 
    internal class PdfPageEvents : IPdfPageEvent 
    { 
     #region members 
     private BaseFont _baseFont = null; 
     private PdfContentByte _content; 
     #endregion 

     #region IPdfPageEvent Members 
     public void OnOpenDocument(PdfWriter writer, Document document) 
     { 
      _baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, 
           BaseFont.CP1252, BaseFont.NOT_EMBEDDED); 
      _content = writer.DirectContent; 
     } 

     public void OnStartPage(PdfWriter writer, Document document) 
     { } 

     public void OnEndPage(PdfWriter writer, Document document) 
     { } 

     public void OnCloseDocument(PdfWriter writer, Document document) 
     { } 

     public void OnParagraph(PdfWriter writer, 
        Document document, float paragraphPosition) 
     { } 

     public void OnParagraphEnd(PdfWriter writer, 
        Document document, float paragraphPosition) 
     { } 

     public void OnChapter(PdfWriter writer, Document document, 
           float paragraphPosition, Paragraph title) 
     { } 

     public void OnChapterEnd(PdfWriter writer, 
        Document document, float paragraphPosition) 
     { } 

     public void OnSection(PdfWriter writer, Document document, 
        float paragraphPosition, int depth, Paragraph title) 
     { } 

     public void OnSectionEnd(PdfWriter writer, 
        Document document, float paragraphPosition) 
     { } 

     public void OnGenericTag(PdfWriter writer, Document document, 
            Rectangle rect, string text) 
     { } 
     #endregion 

     private float GetCenterTextPosition(string text, PdfWriter writer) 
     { 
      return writer.PageSize.Width/2 - _baseFont.GetWidthPoint(text, 8)/2; 
     } 
    } 

Ich habe dies nicht geschrieben, aber einige Änderungen vorgenommen. Ich kann mich nicht erinnern, wo ich es gefunden habe. Nachdem ich die PDFs zusammengeführt hatte, würde ich diese Methode aufrufen, um Javascript einzufügen, um den Druckdialog zu öffnen, wenn die PDF geöffnet wird. Wenn Sie bSilent in "true" ändern, sollte es automatisch auf dem Standarddrucker gedruckt werden.

public Stream addPrintJStoPDF(Stream thePDF) 
{ 
    MemoryStream outPutStream = null; 
    PRStream finalStream = null; 
    PdfDictionary page = null; 
    string content = null; 

    //Open the stream with iTextSharp 
    var reader = new PdfReader(thePDF); 

    outPutStream = new MemoryStream(finalStream.GetBytes()); 
    var stamper = new PdfStamper(reader, (MemoryStream)outPutStream); 
    var jsText = "var res = app.setTimeOut('this.print({bUI: true, bSilent: false, bShrinkToFit: false});', 200);"; 
    //Add the javascript to the PDF 
    stamper.JavaScript = jsText; 

    stamper.FormFlattening = true; 
    stamper.Writer.CloseStream = false; 
    stamper.Close(); 

    //Set the stream to the beginning 
    outPutStream.Position = 0; 

    return outPutStream; 
} 

nicht sicher, wie gut der obige Code geschrieben ist, da ich es von woanders gezogen und ich habe nicht in der Tiefe überhaupt mit iTextSharp gearbeitet, aber ich weiß, dass es bei Zusammenführen von PDF-Dateien hat Arbeit, die ich zu erzeugen zur Laufzeit.

+2

Bitte verwenden Sie diese Art der Zusammenführungsroutine nicht, es sei denn, Sie haben sehr spezifische Anforderungen, die Sie dazu zwingen. Wenn Sie mit "PdfWriter" Quell-PDFs zusammenführen, gehen interaktive Funktionen (Formulare und andere Anmerkungen) verloren. Darüber hinaus enthält das resultierende PDF intern einen unnötigen Wrapper um die Seiteninformationen, der beim wiederholten Iterieren dazu führen kann, dass PDF-Viewer beim Anzeigen der PDF fehlschlagen. – mkl

+0

Wie gesagt, ich hatte dies aus älterem Code gezogen, der in Produktion war, aber PDFs wurden aus HTML generiert, das von einem WYSIWYG-Editor erstellt wurde, so dass wir keine interaktiven Funktionen hatten.Auch unsere Wiederholungen waren in der Regel nur etwa 10 auf einmal und wir hatten nie Probleme mit der PDF nicht öffnen. Ich postete dies als ein Beispiel, da wir es in der Produktion laufen ließen und ich weiß, dass es funktionierte, PDFs ohne gemeldete Probleme zusammenzuführen. – DSlagle

+0

Ich wollte nichts für ungut halten; solche Zusammenführungslösungen wie die, die auf "PdfWriter" basieren, sind tatsächlich häufiger zu finden als diejenigen, die die besser geeigneten Klassen verwenden, wenn sie herumstöbern, und sie * arbeiten * nach einer Mode, so dass sie nicht völlig falsch liegen. 'Pdf * Copy *' basierte Lösungen, obwohl, im Allgemeinen einfacher zu bedienen (keine Notwendigkeit, die Zieldokument Seitengröße und Rotation immer wieder anzupassen), vollständiger (in Bezug auf interaktive Funktionen), und produzieren sauberer Ausgang (in Bezug auf die interne PDF-Struktur). – mkl

7

Ich habe itextsharp mit C# verwendet, um PDF-Dateien zu kombinieren. Dies ist der Code, den ich verwendet habe.

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

Um die Speicherprobleme erwähnt zu vermeiden, habe ich Datei-Stream statt Speicherstrom (erwähnt in ITextSharp Out of memory exception merging multiple pdf) PDF-Dateien zusammenführen:

 var parentDirectory = Directory.GetParent(SelectedDocuments[0].FilePath); 
     var savePath = parentDirectory + "\\MergedDocument.pdf"; 

     using (var fs = new FileStream(savePath, FileMode.Create)) 
     { 
      using (var document = new Document()) 
      { 
       using (var pdfCopy = new PdfCopy(document, fs)) 
       { 
        document.Open(); 
        for (var i = 0; i < SelectedDocuments.Count; i++) 
        { 
         using (var pdfReader = new PdfReader(SelectedDocuments[i].FilePath)) 
         { 
          for (var page = 0; page < pdfReader.NumberOfPages;) 
          { 
           pdfCopy.AddPage(pdfCopy.GetImportedPage(pdfReader, ++page)); 
          } 
         } 
        } 
       } 
      } 
     } 
2

Getestet mit iTextSharp-LGPL 4.1.6:

public static byte[] ConcatenatePdfs(IEnumerable<byte[]> documents) 
    { 
     using (var ms = new MemoryStream()) 
     { 
      var outputDocument = new Document(); 
      var writer = new PdfCopy(outputDocument, ms); 
      outputDocument.Open(); 

      foreach (var doc in documents) 
      { 
       var reader = new PdfReader(doc); 
       for (var i = 1; i <= reader.NumberOfPages; i++) 
       { 
        writer.AddPage(writer.GetImportedPage(reader, i)); 
       } 
       writer.FreeReader(reader); 
       reader.Close(); 
      } 

      writer.Close(); 
      outputDocument.Close(); 
      var allPagesContent = ms.GetBuffer(); 
      ms.Flush(); 

      return allPagesContent; 
     } 
    }