2012-02-27 14 views
8

私は既存のpdfを開き、テキストを追加してから、テキストをシャープにしてコンテンツの配置として出力したいと考えています。私は次のコードを持っています。それが落ちるところでは、私はメモリストリームとして出力したいが、元のファイルを開くためにfilestreamする必要があるということです。itextsharpのテンプレートからpdfを作成し、内容の処理として出力します。

ここに私のものがあります。明らかにPdfWriterを2回定義することはできません。

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を扱うためのもので、既存のものを変更するものではありません。基本的にDocumentオブジェクトは、PDF仕様の根本的な部分を抽象化し、段落やリフロー可能なコンテンツなどのより高いレベルの作業を可能にするラッパークラスの束です。これらの抽象概念は、「段落」と考えるものを、段落を行間に関係なく一度に1行ずつ記述する生のコマンドに変換します。既存のドキュメントを扱うときに、テキストをリフローして、これらの抽象化が使用されないようにする方法を言う安全な方法はありません。

代わりにPdfStamperオブジェクトを使用します。このオブジェクトを操作する際には、重複する可能性のあるコンテンツを操作する方法の2つの選択肢があります。新しいテキストが既存のコンテンツの上に書き込まれるか、テキストがその下に書き込まれます。インスタンス化されたPdfStamperオブジェクトの2つのメソッド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なステートメントは、後で自動的に私たちのためにこれを行いますが、それはまた、我々はここでそれを手動で行う必要がありので、ストリームを閉じます。)

もう一つの事を、決して、これまでMemoryStreamGetBuffer()メソッドを使用します。あなたの望みのように聞こえる(私も間違って使ったことがある)代わりにToArray()を使いたい。 GetBuffer()には、通常は破損したPDFを生成する初期化されていないバイトが含まれています。また、HTTPレスポンスストリームに書き込むのではなく、まずバイトを配列に保存しています。デバッグの観点からは、私はiTextSharpとSystem.IOのコードをすべて終えて、それが正しいことを確認してから、生のバイト配列で必要なものを何でもします。私の場合は便利なので、私はそれらをディスクにWebサーバーを書いていませんが、あなたは同じように簡単にあなたの質問のタイトルの第二部は言う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が使用できるため、不要です。あなたのコード例ではので、ここであなたが求めて何をする一つの方法だ、あなたのPDFの既存ページにテキストを追加しようとしていないNewPage()を呼んでいる:

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ドキュメントを作成するための標準的な方法です。

+0

+1これは非常に役に立ちました!どうもありがとうございました。 –

関連する問題