2012-02-27 39 views
8

Tôi muốn mở một tệp pdf hiện có, thêm một số văn bản và sau đó xuất dưới dạng bố cục nội dung bằng cách sử dụng chữ in đậm. Tôi có đoạn mã sau. Nơi nó rơi xuống nó là tôi muốn đầu ra như dòng bộ nhớ nhưng cần phải filestream để mở tập tin gốc.tạo pdf từ một mẫu trong itextsharp và xuất ra dưới dạng bố cục nội dung.

Đây là những gì tôi có. Rõ ràng việc xác định PdfWriter hai lần sẽ không hoạt động.

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

Trả lời

13

Bạn có một vài vấn đề mà tôi sẽ cố gắng hướng dẫn bạn.

Đầu tiên, đối tượng Document chỉ để làm việc với các tệp PDF mới chứ không phải sửa đổi các tệp hiện có. Về cơ bản, đối tượng Document là một loạt các lớp trình bao bọc trừu tượng ra các phần cơ bản của thông số PDF và cho phép bạn làm việc với các thứ cấp cao hơn như đoạn văn và nội dung có thể chỉnh sửa lại. Những sự trừu tượng này biến những gì bạn nghĩ về "đoạn văn" thành các câu lệnh thô mà viết đoạn văn một dòng vào một thời điểm không có mối quan hệ giữa các dòng. Khi làm việc với một tài liệu hiện có, không có cách nào an toàn để nói cách chỉnh lại văn bản để những trừu tượng này không được sử dụng.

Thay vào đó bạn muốn sử dụng đối tượng PdfStamper. Khi làm việc với đối tượng này, bạn có hai lựa chọn về cách làm việc với nội dung chồng chéo tiềm ẩn, hoặc văn bản mới của bạn được viết trên đầu nội dung hiện có hoặc văn bản của bạn được viết bên dưới nội dung đó. Hai phương thức GetOverContent() hoặc GetUnderContent() đối tượng được hiển thị tức thời PdfStamper sẽ trả lại đối tượng PdfContentByte mà sau đó bạn có thể viết văn bản.

Có hai cách chính để viết văn bản, theo cách thủ công hoặc thông qua đối tượng ColumnText. Nếu bạn đã thực hiện HTML, bạn có thể nghĩ đến đối tượng ColumnText khi sử dụng hàng đơn vị cố định lớn, cột đơn <TABLE>. Lợi thế của ColumnText là bạn có thể sử dụng các phép trừu tượng mức cao hơn như Paragraph.

Dưới đây là công việc đầy đủ C# 2010 WinForms nhắm mục tiêu iTextSharp 5.1.2.0 hiển thị phần trên. Xem các bình luận mã cho bất kỳ câu hỏi nào. Nó sẽ được khá dễ dàng để chuyển đổi này để 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(); 
     } 
    } 
} 

Đối với vấn đề thứ hai của bạn về FileStream vs MemoryStream, nếu bạn nhìn vào chữ ký phương pháp cho hầu hết (trên thực tế tất cả như xa như tôi biết) phương pháp trong iTextSharp bạn sẽ thấy rằng tất cả họ đều mất một đối tượng Stream và không chỉ là đối tượng FileStream. Bất cứ lúc nào bạn nhìn thấy điều này, ngay cả bên ngoài iTextSharp, điều này có nghĩa là bạn có thể chuyển vào bất kỳ lớp con nào của Stream bao gồm đối tượng MemoryStream, mọi thứ khác vẫn giữ nguyên.

Mã bên dưới là phiên bản được sửa đổi một chút của phiên bản ở trên. Tôi đã xóa hầu hết các nhận xét để làm cho nhận xét ngắn hơn. Thay đổi chính là chúng tôi đang sử dụng MemoryStream thay vì FileStream. Ngoài ra, khi chúng ta hoàn thành PDF khi cần phải đóng đối tượng PdfStamper trước khi truy cập dữ liệu nhị phân thô. (Các using statment sẽ làm điều này cho chúng tôi tự động sau này nhưng nó cũng đóng dòng vì vậy chúng tôi cần phải làm điều đó bằng tay ở đây.)

Một điều khác, không bao giờ, sử dụng phương pháp GetBuffer() của MemoryStream. Nghe có vẻ giống như những gì bạn muốn (và tôi đã nhầm lẫn sử dụng nó), nhưng thay vào đó bạn muốn sử dụng ToArray(). GetBuffer() bao gồm các byte chưa được khởi tạo thường tạo ra các tệp PDF bị hỏng.Ngoài ra, thay vì viết vào luồng HTTP Response tôi đang lưu các byte vào mảng trước. Từ một phối cảnh gỡ lỗi, điều này cho phép tôi hoàn thành tất cả mã iTextSharp và System.IO của mình và đảm bảo rằng nó là chính xác, sau đó làm bất cứ điều gì tôi muốn với mảng byte thô. Trong trường hợp của tôi, tôi không có một máy chủ web tiện dụng vì vậy tôi đang viết chúng vào đĩa nhưng bạn có thể chỉ là một cách dễ dàng gọi 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

Ôi chúa ơi, cảm ơn bạn rất nhiều vì đã giải thích các đối tượng 'Tài liệu' và' PdfStamper' là gì! Tôi không thể tìm thấy những lời giải thích này ở đâu cả. Tôi đã cố gắng tìm hiểu làm thế nào để thêm một hình ảnh vào một đối tượng 'PdfReader', nhưng từ ví dụ của bạn, tôi nhận ra rằng tôi có thể làm như vậy bằng cách sử dụng một đối tượng' PdfStamper' và một đối tượng 'PdfContentByte'. Muốn có một tài liệu tham khảo nhanh về những gì mỗi phương pháp đã làm, những gì mỗi tài sản là cho, và những gì các lớp học nên được sử dụng trong các trường hợp nhất định. Dù sao thì cũng cám ơn bạn! –

3

Phần thứ hai của tiêu đề câu hỏi của bạn nói:

"xuất ra bố trí như nội dung"

Nếu đó là những gì bạn thực sự muốn bạn có thể làm điều này:

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

Sử dụng MemoryStream là không cần thiết, vì có sẵn Response.OutputStream. mã ví dụ bạn đang kêu gọi NewPage()không cố gắng để thêm văn bản vào một trang của file PDF hiện có, vì vậy đây là một cách để làm những gì bạn hỏi:

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

Tôi nghĩ bạn đã tìm ra rằng sự kết hợp Document/PdfWriter không hoạt động trong tình huống này :) Đó là phương pháp chuẩn để tạo tài liệu PDF mới.

+0

+1 Điều này rất hữu ích! Cảm ơn nhiều. –

Các vấn đề liên quan