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 MemoryStream
GetBuffer()
. 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);
}
¡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! –