【问题标题】:creating a pdf from a template in itextsharp and outputting as content disposition.从 itextsharp 中的模板创建 pdf 并作为内容配置输出。
【发布时间】:2012-02-27 16:54:17
【问题描述】:

我想打开一个现有的 pdf,添加一些文本,然后使用 itext sharp 输出为内容配置。我有以下代码。它失败的地方是我想输出为内存流但需要文件流来打开原始文件。

这就是我所拥有的。显然定义 PdfWriter 两次是行不通的。

   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();
    } 

【问题讨论】:

    标签: itextsharp


    【解决方案1】:

    你有几个问题,我会试着引导你解决。

    首先,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();
            }
        }
    }
    

    关于 FileStreamMemoryStream 的第二个问题,如果您查看 iTextSharp 中几乎每个(实际上是 all )方法的方法签名,您将看到它们都采用Stream 对象而不仅仅是FileStream 对象。任何时候你看到这个,即使在 iTextSharp 之外,这意味着你可以传入 Stream 的任何子类,包括 MemoryStream 对象,其他一切都保持不变。

    下面的代码是上面代码的略微修改版本。我已经删除了大部分 cmets 以使其更短。主要变化是我们使用MemoryStream 而不是FileStream。此外,当我们处理完 PDF 时,需要在访问原始二进制数据之前关闭 PdfStamper 对象。 (using 语句稍后会自动为我们执行此操作,但它也会关闭流,因此我们需要在此处手动执行此操作。)

    另一件事,永远不要使用MemoryStreamGetBuffer() 方法。这听起来像你想要的(我也错误地使用了它),但你想使用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);
    }
    

    【讨论】:

    • 天哪,非常感谢您解释DocumentPdfStamper 对象是什么!我在任何地方都找不到这些解释。我试图找出如何将图像添加到 PdfReader 对象,但从您的示例中,我意识到我可以使用 PdfStamper 对象和 PdfContentByte 对象来做到这一点。希望有一个快速参考文档,说明每个方法的作用、每个属性的用途以及在某些情况下应该使用哪些类。无论如何,谢谢!
    【解决方案2】:

    你的问题标题的第二部分说:

    "输出为内容配置"

    如果这是您真的想要的,您可以这样做:

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

    没有必要使用MemoryStream,因为Response.OutputStream 可用。您的示例代码正在调用 NewPage() 并且 not 试图将文本添加到 PDF 的 现有 页面,所以这里有一种方法可以满足您的要求:

    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();
    }
    

    我想您已经发现Document / PdfWriter 组合在这种情况下不起作用:) 这是创建 PDF 文档的标准方法。

    【讨论】:

      猜你喜欢
      • 2010-11-19
      • 2018-11-24
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 2019-03-22
      • 2018-12-27
      • 1970-01-01
      相关资源
      最近更新 更多