ASP.NET: PDFs zusammenbauen – Geschichte einer erfolgreichen Migration

Eine meiner älteren Anwendungen dient zum Abruf von Rechnungen im PDF-Format über Internet und Fax. Der Webabruf war dabei so gelöst, dass der Kunde eine Rechnungsnummer eingeben muss, worauf sich das System die Rechnungen plus separater Anhänge im PDF-Format von einem Fileserver innerhalb des Firmennetzwerks holt, zusammenbaut und per Download zur Verfügung stellt.

Die Anwendung war bislang in klassischem ASP realisiert. Den Job des Abholens der Rechnungen übernahm eine in Visual Basic 6 geschriebene COM-Komponente. Den Zusammebau der PDFs zu einem File geschah durch das kostenlose Ghostscript.

Die Anwendung funktionierte seit 1999 recht problemlos. Umständlich war allerdings, dass man einige Verzeichnisse im für das IUSR_…-Konto freigeben musste. Denn die Anwendung legte in temporären Ordner die PDFs vor dem Zusammenbau und zur Download-Bereitstellung ab. Aus verschiedenen Gründen stand nun ein Umbau der Applikation auf ASP.NET 2.0 an.

Dabei fiel viel alter Schrott über Bord, weil ASP.NET elegantere Lösungen ermöglicht und mächtige Funktionalitäten mitbringt. Tatsächlich waren nun keine externen Komponenten mehr notwendig.

  • FTP-Zugriff

    Seit der aktuellen Version 2.0 hat .NET die notwendigen Funktionen für FTP-Zugriffe an Bord. Man die Methoden der beiden Klassen “FtpWebRequest und FtpWebResponse entweder synchron einsetzen, also auf die Antwort warten oder mit Callback-Routinen nutzen.

  • PDF-Generierung

    Nach langem Suchen fand ich von den Autoren der genialen kostenlosen Kompressionsbibliothek ZLIB (siehe) mit iTextSharp einen Werkzeugkasten mit umfangreichen Funktionen rund um die Erzeugung von PDFs. Auch die Aneinanderfügung von vorliegenden PDFs beherrscht iTextSharp. Das Beispiel dafür liefern die Entwickler im Demo-Programm concat.cs gleich mit.

  • Wegfall temporärer Dateien

    Weil iTextSharp das zusammengeklebte PDF in einem beliebigen Stream ausgeben kann, habe ich in der neuen Version der Anwendung einfach einen Memory-Stream als Ziel verwendet. Der wird danach – in ein Byte-Array umgewandelt – direkt per Response.OutputStream.Write an den Browser geschickt.

  • Zwischenschritte entfallen

    Die Funktion zum PDF-Zusammenbau bekommt von ihrem Aufrufer ein Array von URLs mitgeliefert. Seine Elemente enthalten den vollständigen FTP-Pfad eines PDF-Dokuments. Weil die PDF-Klasse die Dateien nicht auf dem Dateisystem erwartet, sondern die URLs als Quellangabe direkt verwenden kann, entfällt nun der bislang notwendige Schritt, alle Teil-PDFs zuerst einmal vom FTP-Server ins lokale Dateisystem zu laden.

Hier der wichtigste Ausschnitt aus der neuen Applikation. Die Methode nimmt ein Array von PDFs-Quellen an, baut diese zusammen und gibt sie als Byte-Array zurück.

//im Kopfteil einbauen:

//using iTextSharp.text;

//using iTextSharp.text.pdf;

public byte[] Combine(Uri[] arrP_Files){

int f = 0;

byte[] retBytes;

MemoryStream ms = new MemoryStream();

PdfReader reader = new PdfReader(arrP_Files[f]);

// we retrieve the total number of pages

int n = reader.NumberOfPages;

// step 1: creation of a document-object

Document document = new Document(reader.GetPageSizeWithRotation(1));

// step 2: we create a writer that listens to the document

PdfWriter writer = PdfWriter.GetInstance(document, ms);

// step 3: we open the document

document.Open();

PdfContentByte cb = writer.DirectContent;

PdfImportedPage page;

int rotation;

// step 4: we add content

while (f < arrP_Files.Length) {

int i = 0;

while (i < n) {

i++;

document.SetPageSize(reader.GetPageSizeWithRotation(i));

document.NewPage();

page = writer.GetImportedPage(reader, i);

rotation = reader.GetPageRotation(i);

if (rotation == 90 || rotation == 270) {

cb.AddTemplate(page, 0, -1f, 1f, 0, 0, reader.GetPageSizeWithRotation(i).Height);

}

else {

cb.AddTemplate(page, 1f, 0, 0, 1f, 0, 0);

}

}

f++;

if (f < arrP_Files.Length) {

reader = new PdfReader(arrP_Files[f]);

// we retrieve the total number of pages

n = reader.NumberOfPages;

}

}

// step 5: we close the document

document.Close();

retBytes = ms.ToArray();

return(retBytes);

}

Ähnliche Beiträge