2012-02-27 183 views
8

Me gustaría abrir un pdf existente, agregar algo de texto y luego hacer una salida como disposición de contenido usando itext sharp. tengo el siguiente código. Donde se cae es que quiero generar como flujo de memoria pero necesito una secuencia de archivos para abrir el archivo original.creando un pdf a partir de una plantilla en itextsharp y dando como disposición de contenido.

Esto es lo que tengo. Obviamente, definir PdfWriter dos veces no funcionará.

public static void Create(string path) 
    { 
     var Response = HttpContext.Current.Response; 
     Response.Clear(); 
     Response.ContentType = "application/pdf"; 
     System.IO.MemoryStream m = new System.IO.MemoryStream(); 
     Document document = new Document(); 
     PdfWriter wri = PdfWriter.GetInstance(document, new FileStream(path, FileMode.Create)); 
     PdfWriter.GetInstance(document, m); 
     document.Open(); 
     document.Add(new Paragraph(DateTime.Now.ToString())); 
     document.NewPage(); 
     document.Add(new Paragraph("Hello World")); 
     document.Close(); 
     Response.OutputStream.Write(m.GetBuffer(), 0, m.GetBuffer().Length); 
     Response.OutputStream.Flush(); 
     Response.OutputStream.Close(); 
     Response.End(); 
    } 

Respuesta

13

Tienes un par de problemas que trataré de guiarte.

Primero, el objeto Document es solo para trabajar con archivos PDF nuevos, sin modificar los existentes. Básicamente, el objeto Document es un grupo de clases de contenedor que resumen las partes subyacentes de las especificaciones de PDF y le permiten trabajar con elementos de mayor nivel, como párrafos y contenido reajustable. Estas abstracciones convierten lo que piensas de "párrafos" en comandos crudos que escriben el párrafo una línea a la vez sin relación entre líneas. Al trabajar con un documento existente, no hay una manera segura de decir cómo volver a colocar el texto, por lo que estas abstracciones no se utilizan.

En su lugar, desea utilizar el objeto PdfStamper. Cuando trabaje con este objeto, tiene dos opciones de cómo trabajar con contenido potencialmente superpuesto, ya sea que su nuevo texto se escriba sobre el contenido existente o su texto se escriba debajo. Los dos métodos GetOverContent() o GetUnderContent() de un objeto instanciado PdfStamper devolverán un objeto PdfContentByte con el que luego podrá escribir texto.

Hay dos formas principales de escribir texto, ya sea manualmente oa través de un objeto ColumnText. Si ha hecho HTML, puede pensar que el objeto ColumnText usa una única fila de una sola posición fija, una sola columna <TABLE>. La ventaja de ColumnText es que puede usar abstracciones de nivel superior como Paragraph.

A continuación se muestra una aplicación WinForms C# 2010 de trabajo completo dirigida a iTextSharp 5.1.2.0 que muestra lo anterior. Vea los comentarios del código para cualquier pregunta. Debería ser bastante fácil convertir esto a ASP.Net.

using System; 
using System.IO; 
using System.Windows.Forms; 
using iTextSharp.text; 
using iTextSharp.text.pdf; 

namespace WindowsFormsApplication1 { 
    public partial class Form1 : Form { 
     public Form1() { 
      InitializeComponent(); 
     } 

     private void Form1_Load(object sender, EventArgs e) { 
      string existingFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "file1.pdf"); 
      string newFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "file2.pdf"); 
      using (FileStream fs = new FileStream(existingFile, FileMode.Create, FileAccess.Write, FileShare.None)) { 
       using (Document doc = new Document(PageSize.LETTER)) { 
        using (PdfWriter writer = PdfWriter.GetInstance(doc, fs)) { 
         doc.Open(); 

         doc.Add(new Paragraph("This is a test")); 

         doc.Close(); 
        } 
       } 
      } 

      //Bind a PdfReader to our first document 
      PdfReader reader = new PdfReader(existingFile); 
      //Create a new stream for our output file (this could be a MemoryStream, too) 
      using (FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write, FileShare.None)) { 
       //Use a PdfStamper to bind our source file with our output file 
       using (PdfStamper stamper = new PdfStamper(reader, fs)) { 

        //In case of conflict we want our new text to be written "on top" of any existing content 
        //Get the "Over" state for page 1 
        PdfContentByte cb = stamper.GetOverContent(1); 

        //Begin text command 
        cb.BeginText(); 
        //Set the font information 
        cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1250, false), 16f); 
        //Position the cursor for drawing 
        cb.MoveText(50, 50); 
        //Write some text 
        cb.ShowText("This was added manually"); 
        //End text command 
        cb.EndText(); 

        //Create a new ColumnText object to write to 
        ColumnText ct = new ColumnText(cb); 
        //Create a single column who's lower left corner is at 100x100 and upper right is at 500x200 
        ct.SetSimpleColumn(100,100,500,200); 
        //Add a higher level object 
        ct.AddElement(new Paragraph("This was added using ColumnText")); 
        //Flush the text buffer 
        ct.Go(); 

       } 
      } 

      this.Close(); 
     } 
    } 
} 

cuanto a su segunda problema sobre el FileStream vs MemoryStream, si nos fijamos en la firma del método para casi todos (en realidad toda por lo que yo sé) método dentro iTextSharp verá que todos toman un objeto Stream y no solo un objeto FileStream. Cada vez que vea esto, incluso fuera de iTextSharp, esto significa que puede pasar cualquier subclase de Stream que incluya el objeto MemoryStream, todo lo demás permanece igual.

El siguiente código es una versión ligeramente modificada del anterior. Eliminé la mayoría de los comentarios para acortarlo. El principal cambio es que estamos usando un MemoryStream en lugar de un FileStream. Además, cuando hayamos terminado con el PDF cuando sea necesario cerrar el objeto PdfStamper antes de acceder a los datos binarios sin formato. (El DECLARACIÓN using lo hará por nosotros de forma automática después, pero también cierra el flujo de lo que tenemos que hacer manualmente desde aquí.)

Otra cosa, nunca, nunca utilizar el método de la MemoryStreamGetBuffer(). Suena como lo que quiere (y lo he usado por error también), pero en su lugar desea usar ToArray(). GetBuffer() incluye bytes no inicializados que generalmente producen archivos PDF corruptos.Además, en lugar de escribir en la secuencia de respuesta HTTP, primero guardo los bytes en la matriz. Desde una perspectiva de depuración esto me permite terminar todo mi iTextSharp y el código System.IO y me aseguro de que sea correcto, luego hago lo que quiera con la matriz de bytes sin formato. En mi caso no tengo un servidor web muy útil, así que les estoy escribiendo en el disco, sino que simplemente podría llamar con la misma facilidad Response.BinaryWrite(bytes)

string existingFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "file1.pdf"); 
string newFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "file2.pdf"); 
PdfReader reader = new PdfReader(existingFile); 
byte[] bytes; 
using(MemoryStream ms = new MemoryStream()){ 
    using (PdfStamper stamper = new PdfStamper(reader, ms)) { 
     PdfContentByte cb = stamper.GetOverContent(1); 
     ColumnText ct = new ColumnText(cb); 
     ct.SetSimpleColumn(100,100,500,200); 
     ct.AddElement(new Paragraph("This was added using ColumnText")); 
     ct.Go(); 

     //Flush the PdfStamper's buffer 
     stamper.Close(); 
     //Get the raw bytes of the PDF 
     bytes = ms.ToArray(); 
    } 
} 

//Do whatever you want with the bytes 
//Below I'm writing them to disk but you could also write them to the output buffer, too 
using (FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write, FileShare.None)) { 
    fs.Write(bytes, 0, bytes.Length); 
} 
+0

¡Oh Dios mío, muchas gracias por explicar qué son los objetos 'Document' y' PdfStamper'! No pude encontrar estas explicaciones en ningún lado. Estaba intentando descubrir cómo agregar una imagen a un objeto 'PdfReader', pero de su ejemplo, me di cuenta de que puedo hacerlo usando un objeto' PdfStamper' y un objeto 'PdfContentByte'. Desearía que hubiera un documento de referencia rápido sobre lo que hizo cada método, para qué sirve cada propiedad y qué clases deberían usarse en determinadas circunstancias. ¡Gracias de todas formas! –

3

La segunda parte de su título de la pregunta dice:

"dar salida a disposición como contenido"

Si eso es lo que realmente búsqueda Para hacer esto:

Response.AddHeader("Content-Disposition", "attachment; filename=DESIRED-FILENAME.pdf"); 

No es necesario utilizar MemoryStream, ya que Response.OutputStream está disponible. El código de ejemplo está llamando NewPage() y no tratando de añadir el texto a una existente página de su PDF, así que aquí está una manera de hacer lo que le pide:

Response.ContentType = "application/pdf";  
Response.AddHeader("Content-Disposition", "attachment; filename=itextTest.pdf"); 
PdfReader reader = new PdfReader(readerPath); 
// store the extra text on the last (new) page 
ColumnText ct = new ColumnText(null); 
ct.AddElement(new Paragraph("Text on a new page")); 
int numberOfPages = reader.NumberOfPages; 
int newPage = numberOfPages + 1; 
// get all pages from PDF "template" so we can copy them below 
reader.SelectPages(string.Format("1-{0}", numberOfPages)); 
float marginOffset = 36f; 
/* 
* we use the selected pages above with a PdfStamper to copy the original. 
* and no we don't need a MemoryStream... 
*/ 
using (PdfStamper stamper = new PdfStamper(reader, Response.OutputStream)) { 
// use the same page size as the __last__ template page  
    Rectangle rectangle = reader.GetPageSize(numberOfPages); 
// add a new __blank__ page  
    stamper.InsertPage(newPage, rectangle); 
// allows us to write content to the (new/added) page 
    ct.Canvas = stamper.GetOverContent(newPage); 
// add text at an __absolute__ position  
    ct.SetSimpleColumn(
    marginOffset, marginOffset, 
    rectangle.Right - marginOffset, rectangle.Top - marginOffset 
); 
    ct.Go(); 
} 

Creo que ya has descubierto que la combinación Document/PdfWriter no funciona en esta situación :) Ese es el método estándar para crear un documento nuevo PDF.

+0

+1 ¡Esto fue muy útil! Muchas gracias. –

Cuestiones relacionadas