你有几个问题,我会试着引导你解决。
首先,Document 对象仅用于处理新的 PDF,而不是修改现有的 PDF。基本上,Document 对象是一堆包装类,它们抽象出 PDF 规范的底层部分,并允许您处理更高级别的内容,例如段落和可重排的内容。这些抽象将您对“段落”的想法变成了原始命令,这些命令一次写一行,而行与行之间没有关系。在处理现有文档时,没有安全的方法来说明如何重排文本,因此不使用这些抽象。
相反,您想使用PdfStamper 对象。使用此对象时,您有两种选择如何处理可能重叠的内容,要么将新文本写在现有内容之上,要么将文本写在其下方。实例化的PdfStamper 对象的GetOverContent() 或GetUnderContent() 两种方法将返回一个PdfContentByte 对象,然后您可以使用该对象编写文本。
有两种主要的方式来编写文本,手动或通过ColumnText 对象。如果您已经编写过 HTML,您可以将 ColumnText 对象视为使用大的固定位置单行单列 <TABLE>。 ColumnText 的优点是您可以使用更高级别的抽象,例如 Paragraph。
下面是一个完整的 C# 2010 WinForms 应用程序,目标是 iTextSharp 5.1.2.0,它展示了上面的内容。如有任何问题,请参阅代码 cmets。将其转换为 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();
}
}
}
关于 FileStream 与 MemoryStream 的第二个问题,如果您查看 iTextSharp 中几乎每个(实际上是 all )方法的方法签名,您将看到它们都采用Stream 对象而不仅仅是FileStream 对象。任何时候你看到这个,即使在 iTextSharp 之外,这意味着你可以传入 Stream 的任何子类,包括 MemoryStream 对象,其他一切都保持不变。
下面的代码是上面代码的略微修改版本。我已经删除了大部分 cmets 以使其更短。主要变化是我们使用MemoryStream 而不是FileStream。此外,当我们处理完 PDF 时,需要在访问原始二进制数据之前关闭 PdfStamper 对象。 (using 语句稍后会自动为我们执行此操作,但它也会关闭流,因此我们需要在此处手动执行此操作。)
另一件事,永远不要使用MemoryStream 的GetBuffer() 方法。这听起来像你想要的(我也错误地使用了它),但你想使用ToArray()。 GetBuffer() 包括未初始化的字节,这些字节通常会产生损坏的 PDF。此外,我不是写入 HTTP 响应流,而是先将字节保存到数组中。从调试的角度来看,这允许我完成所有 iTextSharp 和 System.IO 代码并确保它是正确的,然后对原始字节数组做任何我想做的事情。在我的情况下,我没有方便的网络服务器,所以我将它们写入磁盘,但您可以轻松调用 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);
}