Actualización (19/07/2007): Luis Ruiz Pavón comenta una forma más sencilla de unir documentos usando iTextDotNet.
Una pequeña clase que hace uso de iText# (Software Libre) para unir dos o más documentos PDF en uno solo.
csharp:
using System; using System.Collections.Generic; using System.IO; using iTextSharp.text; using iTextSharp.text.pdf; public class PdfMerge { private BaseFont baseFont; private bool enablePagination = false; private readonly List<PdfReader> documents; private int totalPages; public BaseFont BaseFont { get { return baseFont; } set { baseFont = value; } } public bool EnablePagination { get { return enablePagination; } set { enablePagination = value; if (value && baseFont == null) baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); } } public List<PdfReader> Documents { get { return documents; } } public void AddDocument(string filename) { documents.Add(new PdfReader(filename)); } public void AddDocument(Stream pdfStream) { documents.Add(new PdfReader(pdfStream)); } public void AddDocument(byte[] pdfContents) { documents.Add(new PdfReader(pdfContents)); } public void AddDocument(PdfReader pdfDocument) { documents.Add(pdfDocument); } public void Merge(string outputFilename) { Merge(new FileStream(outputFilename, FileMode.Create)); } public void Merge(Stream outputStream) { if (outputStream == null || !outputStream.CanWrite) throw new Exception("OutputStream es nulo o no se puede escribir en éste."); Document newDocument = null; try { newDocument = new Document(); PdfWriter pdfWriter = PdfWriter.GetInstance(newDocument, outputStream); newDocument.Open(); PdfContentByte pdfContentByte = pdfWriter.DirectContent; if (EnablePagination) documents.ForEach(delegate(PdfReader doc) { totalPages += doc.NumberOfPages; }); int currentPage = 1; foreach (PdfReader pdfReader in documents) { for (int page = 1; page <= pdfReader.NumberOfPages; page++) { newDocument.NewPage(); PdfImportedPage importedPage = pdfWriter.GetImportedPage(pdfReader, page); pdfContentByte.AddTemplate(importedPage, 0, 0); if (EnablePagination) { pdfContentByte.BeginText(); pdfContentByte.SetFontAndSize(baseFont, 9); pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_CENTER, string.Format("{0} de {1}", currentPage++, totalPages), 520, 5, 0); pdfContentByte.EndText(); } } } } finally { outputStream.Flush(); if (newDocument != null) newDocument.Close(); outputStream.Close(); } } public PdfMerge() { documents = new List<PdfReader>(); } }
Si bien es cierto que hay muchas cosas por mejorar, creo que para escenarios no muy complejos basta y sobra.
Pueden descargar la solución creada en Visual Studio 2005 que -- incluye el ensamblado iText# y -- como ejemplo une dos papers bastante interesantes ("Composable Memory Transactions" y "Static Typing Where Posible, Dynamic Typing When Needed: The End of The Cold War Between Programming Languages").
Nota: La clase está basada en el artículo Merge PDF files with iText.