gofpdfを使う

<!---


--->

この記事は [Go2 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/go2) 及び [しむどん Advent Calendar 2017](https://adventar.org/calendars/2179) の19日目の記事です。

## gofpdf とは

https://github.com/jung-kurt/gofpdf/

gofpdfはPDFを生成するgoのライブラリです。テキスト、図面、イメージを実装しています。READMEに実装している機能が記載されています。

> Features
> - Choice of measurement unit, page format and margins
> - Page header and footer management
> - Automatic page breaks, line breaks, and text justification
> - Inclusion of JPEG, PNG, GIF, TIFF and basic path-only SVG images
> - Colors, gradients and alpha channel transparency
> - Outline bookmarks
> - Internal and external links
> - TrueType, Type1 and encoding support
> - Page compression
> - Lines, Bézier curves, arcs, and ellipses
> - Rotation, scaling, skewing, translation, and mirroring
> - Clipping
> - Document protection
> - Layers
> - Templates
> - Barcodes

([README.md](https://github.com/jung-kurt/gofpdf/blob/master/README.md)抜粋)

- 測定単位、ページフォーマット、マージンの選択
- ページヘッダーとフッターの管理
- 自動ページ区切り、改行、およびテキストの位置揃え
- JPEG、PNG、GIF、TIFF、および基本的なパスのみのSVG画像を含める
- 色、グラデーション、アルファチャンネルの透明度
- ブックマークの概要
- 内部および外部リンク
- TrueType、Type1、およびエンコーディングのサポート
- ページ圧縮
- 線、ベジエ曲線、円弧、楕円
- 回転、スケーリング、スキュー、変換、ミラーリング
- クリッピング
- 文書保護
- レイヤー
- テンプレート
- バーコード

かなり高機能です。

## 簡単な使い方

まずはREADMEに記載されている例から試して見ます。"Hello, world" という文字だけが記述されたPDFを生成します。

```go:hello.go
package main

import (
	"fmt"
	"github.com/jung-kurt/gofpdf"
)

func main() {
	pdf := gofpdf.New("P", "mm", "A4", "")
	pdf.AddPage()
	pdf.SetFont("Arial", "B", 16)
	pdf.Cell(40, 10, "Hello, world")
	err := pdf.OutputFileAndClose("hello.pdf")
	if err != nil {
		fmt.Printf("error\n")
	}
}

```

一行ずつ見ていきます。

```
	pdf := gofpdf.New("P", "mm", "A4", "")
```

Fpdfを生成してそのポインタを返します。第1引数はorientationStrです。これはデフォルトのページ方向を指定します。PはPortrait(縦)、LはLandscapeです(横)。空の文字列はPになります。第2引数はunitStrです。これは要素のサイズパラメータで使用される長さの単位を指定します。ここではミリメートルを指定しています。空の文字列はmmになります。他にpt, cm, in(インチ)が指定できます。第3引数はsizeStrです。"A3", "A4", "A5" などを指定します。第4引数はfontDirStrです。フォントが格納されているディレクトリのパスを指定します。空文字は"."になります。


```
	pdf.AddPage()
```

AddPage()はページを追加します。追加したページが編集対象のページになります。

あ、デバッグコードがコメントアウトされて残っているw
https://github.com/jung-kurt/gofpdf/blob/1bef4f81c40b64d68788e590003f4f8fbdc4d296/fpdf.go#L714


```
	pdf.SetFont("Arial", "B", 16)
```

SetFont()はフォントの設定です。第1引数はfamilyStr、フォントファミリーの指定です。第2引数はstyleStr、"B" (bold), "I" (italic), "U" (underscore)が指定できます。"BI"のように組み合わせて指定もできます。第3引数はsize、文字サイズを指定します。

```
	pdf.Cell(40, 10, "Hello, world")
```

Cell()でセルを追加します。セルは文字などの表示領域です。第1引数で幅、第2引数で高さを指定します。第3引数は挿入する文字列を指定します。

```
	err := pdf.OutputFileAndClose("hello.pdf")
```

OutputFileAndClose()はPDFをファイルに出力してファイルをクローズします。
第1引数に出力するファイルパスを指定します。

```
go run hello.go

```

これを実行するとhello.pdfが出力されます。


## Fpdfの構造

```
// Fpdf is the principal structure for creating a single PDF document
type Fpdf struct {
	page             int                       // current page number
	n                int                       // current object number
	offsets          []int                     // array of object offsets
	templates        map[int64]Template        // templates used in this document
	templateObjects  map[int64]int             // template object IDs within this document
	buffer           fmtBuffer                 // buffer holding in-memory PDF
	pages            []*bytes.Buffer           // slice[page] of page content; 1-based
	state            int                       // current document state
	compress         bool                      // compression flag
	k                float64                   // scale factor (number of points in user unit)
	defOrientation   string                    // default orientation
	curOrientation   string                    // current orientation
	stdPageSizes     map[string]SizeType       // standard page sizes
	defPageSize      SizeType                  // default page size
	curPageSize      SizeType                  // current page size
	pageSizes        map[int]SizeType          // used for pages with non default sizes or orientations
	unitStr          string                    // unit of measure for all rendered objects except fonts
	wPt, hPt         float64                   // dimensions of current page in points
	w, h             float64                   // dimensions of current page in user unit
	lMargin          float64                   // left margin
	tMargin          float64                   // top margin
	rMargin          float64                   // right margin
	bMargin          float64                   // page break margin
	cMargin          float64                   // cell margin
	x, y             float64                   // current position in user unit
	lasth            float64                   // height of last printed cell
	lineWidth        float64                   // line width in user unit
	fontpath         string                    // path containing fonts
	fontLoader       FontLoader                // used to load font files from arbitrary locations
	coreFonts        map[string]bool           // array of core font names
	fonts            map[string]fontDefType    // array of used fonts
	fontFiles        map[string]fontFileType   // array of font files
	diffs            []string                  // array of encoding differences
	fontFamily       string                    // current font family
	fontStyle        string                    // current font style
	underline        bool                      // underlining flag
	currentFont      fontDefType               // current font info
	fontSizePt       float64                   // current font size in points
	fontSize         float64                   // current font size in user unit
	ws               float64                   // word spacing
	images           map[string]*ImageInfoType // array of used images
	pageLinks        [][]linkType              // pageLinks[page][link], both 1-based
	links            []intLinkType             // array of internal links
	outlines         []outlineType             // array of outlines
	outlineRoot      int                       // root of outlines
	autoPageBreak    bool                      // automatic page breaking
	acceptPageBreak  func() bool               // returns true to accept page break
	pageBreakTrigger float64                   // threshold used to trigger page breaks
	inHeader         bool                      // flag set when processing header
	headerFnc        func()                    // function provided by app and called to write header
	inFooter         bool                      // flag set when processing footer
	footerFnc        func()                    // function provided by app and called to write footer
	footerFncLpi     func(bool)                // function provided by app and called to write footer with last page flag
	zoomMode         string                    // zoom display mode
	layoutMode       string                    // layout display mode
	xmp              []byte                    // XMP metadata
	title            string                    // title
	subject          string                    // subject
	author           string                    // author
	keywords         string                    // keywords
	creator          string                    // creator
	creationDate     time.Time                 // override for dcoument CreationDate value
	aliasNbPagesStr  string                    // alias for total number of pages
	pdfVersion       string                    // PDF version number
	fontDirStr       string                    // location of font definition files
	capStyle         int                       // line cap style: butt 0, round 1, square 2
	joinStyle        int                       // line segment join style: miter 0, round 1, bevel 2
	dashArray        []float64                 // dash array
	dashPhase        float64                   // dash phase
	blendList        []blendModeType           // slice[idx] of alpha transparency modes, 1-based
	blendMap         map[string]int            // map into blendList
	blendMode        string                    // current blend mode
	alpha            float64                   // current transpacency
	gradientList     []gradientType            // slice[idx] of gradient records
	clipNest         int                       // Number of active clipping contexts
	transformNest    int                       // Number of active transformation contexts
	err              error                     // Set if error occurs during life cycle of instance
	protect          protectType               // document protection structure
	layer            layerRecType              // manages optional layers in document
	catalogSort      bool                      // sort resource catalogs in document
	nJs              int                       // JavaScript object number
	javascript       *string                   // JavaScript code to include in the PDF
	colorFlag        bool                      // indicates whether fill and text colors are different
	color            struct {
		// Composite values of colors
		draw, fill, text colorType
	}
	spotColorMap map[string]spotColorType // Map of named ink-based colors
}
```

これらを変更していくことでPDFを構成していきます。

### Fpdf.SetHeaderFunc() - ヘッダーを設定する

```
pdf.SetHeaderFunc(func() {
	pdf.SetY(5)
	pdf.SetFont("Arial", "B", 15)
	pdf.Cell(60, 0, "")
	pdf.CellFormat(
		80, 10, titleStr, "1", 0, "C", false, 0,
		"https://qiita.com/advent-calendar/2017/go2")
	pdf.Ln(20)
})

```

ヘッダーの設定はヘッダーを生成する関数を登録することで実現します。

### Fpdf.SetFooterFunc() - フッターを設定する

```
pdf.SetFooterFunc(func() {
	pdf.SetY(-15)
	pdf.SetFont("Arial", "I", 8)
	pdf.CellFormat(0, 10, fmt.Sprintf("Page %d/{nb}", pdf.PageNo()),
		"", 0, "C", false, 0, "")
})
pdf.AliasNbPages("")

```

フッターもヘッダー同様、フッターを生成する関数を登録することで実現します。

### Fpdf.SetTitle() - タイトルを設定する

```
pdf.SetTitle(titleStr, false)

```

第1引数は文字列、第二引数はUTF8とかどうかをboolで指定します。

### Fpdf.SetAuthor() - Authorを設定する

```
pdf.SetAuthor("TakesxiSximada", false)

```

第1引数は文字列、第二引数はUTF8とかどうかをboolで指定します。

### Fpdf.SetFont() - フォントの設定

```
pdf.SetFont("Arial", "B", 16)

```

フォントファミリー、スタイル、サイズを指定します。

### Fpdf.AddPage() - ページを追加する

```
pdf.AddPage()

```

### Fpdf.Image() - 画像を追加する

```
pdf.Image("./sximada.jpg", 160, 28, 30, 0, false, "", 0,
	"https://twitter.com/TakesxiSximada")

```

最後に渡しているURLはリンクのジャンプ先になります。

### Fpdf.CellFormat() - Cellの情報を詳細に設定する

```
pdf.CellFormat(width, 10, text, "", 0, "C", false, 0, "")

```

Fpdf.Cell()でセルを追加しましたが、それ以外の属性を指定したい場合はCellFormat()を使います。Fpdf.Cell()はCellFormat()を内部で読んでいます。


### Fpdf.Ln() - 改行

```
pdf.Ln(10)

```

### Fpdf.SetProtection() - 制約を付与する

```
pdf.SetProtection(gofpdf.CnProtectAnnotForms, "foo", "bar")

```

第一引数はフラグで次の値を指定できます。

| 値                  | 意味 |
|:-:|:-:|
| CnProtectPrint      |  印刷可能 |
| CnProtectModify     |  編集可能 |
| CnProtectCopy       | コピー可能 |
| CnProtectAnnotForms | アノテーションやフォームをPDFエディタで追加 |

```
// Advisory bitflag constants that control document activities
const (
	CnProtectPrint      = 4
	CnProtectModify     = 8
	CnProtectCopy       = 16
	CnProtectAnnotForms = 32
)
```

第2引数はユーザーパスワードです。この値を設定するとファイルを開く場合にパスワードを求めます。第3引数はオーナーパスワードです。ユーザーパスワードとの違いは、このパスワードで開いた場合はこのPDFに対する全ての操作権限が与えられます。


### Fpdf.SetMargins() - マージンを設定する

```
pdf.SetMargins(100, 100, 100)

```

### Fpdf.Rect() - 四角を描画する

```
pdf.Rect(3, 3, 3, 3, "F")

```

X、Y、幅、高さ、スタイルを指定します。例の場合では小さく黒い四角が左上に出力されます。