2012-02-27 1 views
8

기존 pdf를 열고 텍스트를 추가 한 다음 textex sharp를 사용하여 내용 처리로 출력하고 싶습니다. 다음 코드가 있습니다. 추락하는 곳은 메모리 스트림으로 출력하고 싶지만 원본 파일을 열려면 파일 스트림해야합니다.itextsharp에서 템플리트로 pdf를 작성하고 컨텐츠 처리로 출력하십시오.

여기 있습니다. 분명히 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(); 
    } 

답변

13

나는 당신을 데려다 줄 문제를 가지고 있습니다.

먼저 Document 개체는 새로운 PDF 작업에만 사용되며 기존 PDF는 수정하지 않습니다. 기본적으로 Document 개체는 PDF 사양의 기본 부분을 추상화하고 단락 및 리플 로우 가능한 콘텐츠와 같은 상위 수준의 작업을 수행 할 수있게 해주는 래퍼 클래스 모음입니다. 이러한 추상화는 "단락"에 대한 생각을 선간에 관계가없는 한 번에 한 단락을 쓰는 원시 명령으로 바꿉니다. 기존 문서로 작업 할 때 텍스트를 리플 로우하는 방법을 말하는 안전한 방법은 없으므로 이러한 추상화는 사용되지 않습니다.

대신 PdfStamper 개체를 사용하려고합니다. 이 객체로 작업 할 때 잠재적으로 중복되는 내용으로 작업하는 방법에 대해 두 가지 선택 사항이 있습니다. 새 텍스트가 기존 내용 위에 쓰여 지거나 텍스트가 그 아래에 쓰여집니다. 인스턴스화 된 PdfStamper 개체의 GetOverContent() 또는 GetUnderContent()의 두 메서드는 PdfContentByte 개체를 반환하고 텍스트를 쓸 수 있습니다.

수동으로 또는 ColumnText 개체를 통해 텍스트를 작성하는 두 가지 주요 방법이 있습니다. HTML을 완성했다면 ColumnText 객체는 큰 고정 위치 단일 행, 단일 열 <TABLE>을 사용한다고 생각할 수 있습니다. ColumnText의 장점은 Paragraph과 같은 상위 수준의 추상화를 사용할 수 있다는 것입니다.

위의 내용을 보여주는 iTextSharp 5.1.2.0을 대상으로하는 전체 C# 2010 WinForms 응용 프로그램은 다음과 같습니다. 질문에 대해서는 코드 주석을 참조하십시오. 이것을 ASP.Net으로 쉽게 변환 할 수 있어야합니다. MemoryStreamFileStream에 대한 두 번째 문제에 관해서는

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

당신이 iTextSharp 내 거의 모든 (실제로 모든 내가 아는 한) 메서드 메서드 시그니처를 보면 당신은 그들 모두가 걸릴 것을 볼 수 있습니다 FileStream 개체가 아니라 Stream 개체입니다. iTextSharp 외부에서도이 내용을 볼 수 있다면 MemoryStream 객체를 포함하는 Stream의 하위 클래스를 전달할 수 있다는 것을 의미합니다. 다른 모든 객체는 동일하게 유지됩니다.

아래 코드는 위의 코드를 약간 수정 한 것입니다. 나는 그것을 짧게 만들기 위해 대부분의 주석을 제거했다. 주된 변화는 FileStream 대신 MemoryStream을 사용하고 있다는 것입니다. 또한 원시 이진 데이터에 액세스하기 전에 PdfStamper 개체를 닫을 필요가있을 때 PDF가 끝나면 합니다 (using 한 Statment가 자동으로 나중에 우리를 위해이 작업을 수행 할뿐만 아니라 우리가 여기 수동으로 할 필요가 있으므로 스트림을 닫습니다.)

또 다른 일을 결코, 이제까지 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); 
} 
+0

오, 이런, 'Document'와'PdfStamper' 객체가 무엇인지 설명해 주셔서 감사합니다! 나는이 설명들을 어디서나 찾을 수 없었다. 'PdfReader' 객체에 이미지를 추가하는 방법을 찾으려고했지만, 예제에서'PdfStamper' 객체와'PdfContentByte' 객체를 사용하여 그렇게 할 수 있다는 것을 깨달았습니다. 각 방법이 무엇을했는지, 각 속성이 무엇인지, 특정 상황에서 어떤 클래스가 사용되어야하는지에 대한 빠른 참조 문서가 있었으면 좋겠다. 아무튼 감사 해요! –

3

질문 제목의 두 번째 부분은 말한다 :

: 그건 당신이 정말 당신이 할 수 원하는 있다면

"등의 내용 처리를 출력"

MemoryStream을 사용하면 Response.OutputStream을 사용할 수 있으므로 불필요합니다.

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

내가 이미 알아 냈어요 생각 : 귀하의 예제 코드는 그래서 여기에 당신이 무엇을 요구 할 수있는 하나의 방법입니다, 당신의 PDF의 페이지에 존재하는 에 텍스트를 추가하려고 NewPage()하지를 호출 이 상황에서 Document/PdfWriter 조합이 작동하지 않습니다. PDF 문서를 만드는 표준 방법입니다.

+0

+1 매우 도움이되었습니다. 고맙습니다. –

관련 문제