iOSアプリのHTMLテンプレートに基づいてPDFを作成します（例えばレシート）

「大好きなお弁当屋さんのレシートアプリを作ろう！」

この例では、お客さんの購入した品目を入力し、PDFレシートを生成できるレシートアプリを作ります。PDFレシートはHTMLテンプレートに基づいて生成されます。

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/4c9a2d39-98c9-97b9-fc0a-8d9aad3e263e.png">

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/e76a810a-eef9-5500-6759-14de700b6120.png">

## この記事で学ぶこと：

- 表を含むウェブページのコーディング（簡単）
- PDFファイルをHTMLの文字列から生成
- PDFのプレビューウィンドウを表示

## スタータープロジェクト

ここで[スタータープロジェクト](https://github.com/mszmagic/SimpleReceiptApp/tree/for-article)をダウンロードする必要があります。これは、顧客のレシートにアイテムを入力できるミニ販売管理ソフトウェアです。

[完成したプロジェクト](https://github.com/mszmagic/SimpleReceiptApp/tree/master)はこちらで見ることができます。

## HTML文字列の準備

PDFはWebページから生成されるため、いくつかのHTMLコードを記述する必要があります。

### HTMLヘッダー

`headerHTMLstring` というヘルパー関数を追加できます。 htmlファイルのヘッダーには、Webページのタイトル、テーブルのスタイル、およびテーブルの最初の行（ヘッダー）があります。

```swift
func headerHTMLstring() -> String {
    //htmlヘッダーを生成します。
    //たとえば、ここに店の名前を入力できます
    return """
    <!DOCTYPE html>
    <html>
        <head>
                <title>レシート</title>
        <style>
                table, th, td {
                  border: 1px solid black;
                  border-collapse: collapse;
                }
        </style>
        <body>
            <h2>Invoice</h2>
            <table style="width:100%">
                <tr>
                    <th>名前</th>
                    <th>価格</th>
                </tr>
    """
}
```

もちろん、レシートをかっこよく見せるために、そこにあなたの店舗の写真を追加することができます。または、レシートの番号や購入時刻を追加することもできます。

### 各販売アイテムの個別の行

ここで、表内で1つの行を表すHTMLコードを生成する関数を追加します。その行には、販売アイテムの名前とその価格が含まれています。

```swift
func getSingleRow(itemName: String, itemPrice: Int) -> String {
    return """
    <tr>
        <td>\(itemName)</td>
        <td>\(String(itemPrice))</td>
    </tr>
    """
}
```

### HTMLフッター

ついにHTMLフッターを追加

```swift
func footerHTMLstring() -> String {
    return """
        </table>

        </body>
    </html>
    """
}
```

## HTML文字列を準備

今や私たちは、ヘッダー、列、フッターを組み合わせてHTML文字列を作成します

私たちは完成したHTML文字列を蓄積する変数からスタートします。

```swift
var htmlString = ""
```

HTMLのヘッダーの内容を変数に追加します。

```swift
let htmlHeader = headerHTMLstring()
htmlString.append(htmlHeader)
```

セール品を追加するために商品配列にループ処理を行ってください

```swift
var totalPrice = 0
for item in items {
    let name = item.name
    let price = item.price
    let rowString = getSingleRow(itemName: name, itemPrice: price)
    htmlString.append(rowString)
    totalPrice += price
}
```

すると合計金額とHTMLファイルのフッターを追加できます

```swift
// 合計金額を追加する
htmlString.append("\n 合計金額: \(totalPrice) yen \n")
// フッターを取得する
let footerString = footerHTMLstring()
htmlString.append(footerString)
```

これでHTMLのWebページのコードが完成したことになります。PDFファイルの生成作業をしていきましょう。

## HTML文字列からPDFファイルを作成しましょう

ここでは `UIPrintPageRenderer` , `UIMarkupTextPrintFormatter`, `UIGraphics` を使います

まずレンダラーの設定を行い、希望の紙のサイズを指定します

```swift
let renderer = UIPrintPageRenderer()
let paperSize = CGSize(width: 595.2, height: 841.8) //B6
let paperFrame = CGRect(origin: .zero, size: paperSize)
renderer.setValue(paperFrame, forKey: "paperRect")
renderer.setValue(paperFrame, forKey: "printableRect")
```

HTML文字列をフォーマッターに入力します

```swift
let formatter = UIMarkupTextPrintFormatter(markupText: fromHTML)
renderer.addPrintFormatter(formatter, startingAtPageAt: 0)
```

`UIGraphics` を使ってHTML文字列をPDFに変換します

```swift
UIGraphicsBeginPDFContextToData(pdfData, .zero, [:])
for pageI in 0..<renderer.numberOfPages {
    UIGraphicsBeginPDFPage()
    renderer.drawPage(at: pageI, in: UIGraphicsGetPDFContextBounds())
}
UIGraphicsEndPDFContext()
```

## 作成されたPDFデータを一時ファイルに保存します

データをアプリの一時ストレージに保存するのに使える便利な機能を紹介しています

```swift
/*
 この関数は、特定の `data` をアプリの一時ストレージに保存します。さらに、そのファイルが存在する場所のパスを返します。
 */
func saveToTempDirectory(data: NSData) -> URL? {
    let tempDirectory = NSURL.fileURL(withPath: NSTemporaryDirectory(), isDirectory: true)
    let filePath = tempDirectory.appendingPathComponent("receipt-" + UUID().uuidString + ".pdf")
    do {
        try data.write(to: filePath)
        return filePath
    } catch {
        print(error.localizedDescription)
        return nil
    }
}
```

## ユーザーへのPDFのプレゼンテーション

さて、デバイスにPDFファイルが保存されているので、 `QLPreviewController` を通してそれを表示することが可能となりました：

PDFファイルまでのローカルパスを変数に保存する必要があります。

```swift
self.PDFpath = savedPath
let previewController = QLPreviewController()
previewController.dataSource = self
present(previewController, animated: true, completion: nil)
```

さて今度は、デリゲート・ファンクションを実装し、 `QLPreviewController` がファイルの読み取りとロードする場所を認識できるようにします。

```swift
extension ViewController: QLPreviewControllerDataSource {
    
    func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
        if self.PDFpath != nil {
            return 1
        } else {
            return 0
        }
    }
    
    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
        guard let pdfFilePath = self.PDFpath else {
            return "" as! QLPreviewItem
        }
        return pdfFilePath as QLPreviewItem
    }
    
}
```

---

:relaxed: [Twitter @MszPro](https://twitter.com/MszPro)

:sunny: [私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。](http://mszpro.com/ioskiji/)
<img width="150" src="https://cdn.mszmagic.com/static-web-content/AppClipImage_small.png">