iTextSharpでPDF出力

ASP.NETでのPDF出力でiTextSharpを使用したのでメモ。

#前準備

nugetでiTextSharpをインストールしておく
https://www.nuget.org/packages/iTextSharp/

#1からPDFを作成する方法

PDFの内容自体を1から記述して作成する方法。


```csharp:HomeController.cs
public ActionResult CreatePdf()
{
    // A4横でドキュメントを作成
    var doc = new Document(PageSize.A4.Rotate());
    var stream = new MemoryStream();
    //ファイルの出力先を設定
    var pw = PdfWriter.GetInstance(doc, stream);
    //ドキュメントを開く
    doc.Open();

    ////////////////////////////////////////////////////////////////
    //
    // ドキュメントの内容を記述
    //   
    ////////////////////////////////////////////////////////////////

    //ドキュメントを閉じる
    doc.Close();
    
    return File(stream.ToArray(), "application/pdf", "result.pdf");
}
```

#テンプレートに文字や画像を追加していく方法

元々のpdfに文字や画像を追加していく方法です

```csharp:HomeController.cs
public ActionResult CreatePdf()
{
	// テンプレートにするファイルのパス
    var oldFile = HostingEnvironment.MapPath(@"~/App_Data/template.pdf");
    
    // 作成後のファイルのパス
    var newFile = HostingEnvironment.MapPath(string.Format("~/App_Data/temp/{0}.pdf", DateTime.Now.ToString("yyyyMMddhhmmss")));

    // リーダを開く
    var reader = new PdfReader(oldFile);
    
    // テンプレの1ページ目のページサイズを取得
    var size = reader.GetPageSize(1);
    // 開いたファイルのサイズでドキュメントを作成
    var document = new Document(size);

    // ライタを作成
    var fs = new FileStream(newFile, FileMode.Create, FileAccess.Write);
    var writer = PdfWriter.GetInstance(document, fs);
    document.Open();

	// 新しいpdfに既存のpdfのページを貼り付ける
    var pdfContentByte = writer.DirectContent;
    var page = writer.GetImportedPage(reader, 1);
    pdfContentByte.AddTemplate(page, 0, 0);
    
    ////////////////////////////////////////////////////////////////
    //
    // ドキュメントの内容を記述
    //   
    ////////////////////////////////////////////////////////////////  
    
    document.Close();
    
    fs.Close();
    writer.Close();
    reader.Close();

    return File(newFile, "application/pdf", "result.pdf");
}
```


#ドキュメントの内容を記述

上記2つに共通する、pdfの内容の記述方法。
コード内の座標（x,y）は、ページの左下が原点（0, 0）となることに注意。

##文字列の記述

下記コードはデフォルトで左寄せにしているが、これは指定座標から始まって左寄せで文字が記述される。
逆に右寄せにすると、指定座標を右端にして右寄せで文字が記述される。

文字を記述するときはBeginText()とEndText()で囲む必要が有るためメソッド化しておくことをオススメ。

```csharp:HomeController.cs
private void ShowTextAligned(PdfContentByte pdfContentByte, float x, float y, string text, int alignment = Element.ALIGN_LEFT, float rotaion = 0)
{
    pdfContentByte.BeginText();
    pdfContentByte.ShowTextAligned(alignment, text, x, y, rotaion);
    pdfContentByte.EndText();
}
```

文字や画像を追加するための準備。
フォントやフォントサイズは予め指定しておかないと実行時エラーになる。

```csharp:HomeController.cs
// pdfContent
var pdfContentByte = writer.DirectContent;

// ベースフォント。日本語フォントはパスを直接指定してやる必要がある。
// Font.Factoryを使用しても可。
var bf = BaseFont.CreateFont(@"c:\windows\fonts\msgothic.ttc,0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

// ベースフォントとフォントサイズを指定する。
pdfContentByte.SetFontAndSize(bf, 9);

// 指定箇所に文字列を記述
this.ShowTextAligned(pdfContentByte, 100, 200, "文字列");
```

文字列が定まっていない場合、文字数によってははみ出す場合があります。
文字列とベースフォントのフォントサイズを用いて、予め文字列がはみ出さないよう調整することが出来ます。

```csharp:HomeController.cs
// 11から0.5刻みでフォントサイズを小さくしていく
for(float tempFont = 11; 0 < tempFont; tempFont -= 0.5f){

    // 現在のフォントサイズを設定
    pdfContentByte.SetFontAndSize(bf, tempFont);

    // 文字幅を取得
    var widthPoint = bf.GetWidthPoint("書き込む予定の文字列", tempFont);

    // 文字幅が想定以内ならフォントサイズを確定して抜ける
    if(widthPoint < 200){
        break;
    }
}
```

##画像の追加

画像を読み込んだ後、絶対座標を指定して追加する。

```csharp:HomeController.cs
// pdfContent
var pdfContentByte = writer.DirectContent;

// 画像を追加
var image = Image.GetInstance(HostingEnvironment.MapPath(@"~/App_Data/image.png"));
// 表示座標の設定
image.SetAbsolutePosition(100, 200);
pdfContentByte.AddImage(image);

```

##線を引く
1からpdfを作っていく場合は線を引くことなどもあると思います。

```csharp:HomeController.cs
private void DrawLine(PdfContentByte pdfContentByte, float fromX, float fromY, float toX, float toY)
{
    pdfContentByte.MoveTo(fromX, fromY);
    pdfContentByte.LineTo(toX, toY);
    pdfContentByte.ClosePathStroke();
}

```

##ページを追加する
pdfに新しいページを追加することが出来ます。

```csharp:HomeController.cs
// ページを追加
document.NewPage();

// 追加したページにテンプレートを適用する
pdfContentByte.AddTemplate(writer.GetImportedPage(reader, 1), 1.0F, 0, 0, 1.0F, 0, 0);

```

#終わり

他には
ページを間に追加したり
（←追記しました）、向きを変更したりも出来るようです。