RailsのviewでPDFを書くSATySFi-rails gemの使い方

この記事は[Okinawa.rb Advent Calendar 2018](https://qiita.com/advent-calendar/2018/okinawarb)の1日目です。
2日目は @hanachin_ さんの[Re: Ruby で季節のような循環する概念を表したい](https://qiita.com/hanachin_/items/583e7a202667bdf24b7c)です。

RailsアプリケーションでSATySFiを使ってPDFを出力する方法を紹介します。

## SATySFiとは

型による静的検証能力の高い組版システムです。

> SATySFi（英単語の “satisfy” と同様に発音します）は，新しい組版処理システムとその言語です。構文は主にテキスト部分とプログラム部分からなり，前者はLaTeX風の構文で文書を執筆するために，後者はコマンドを定義するために使われます。いわゆる函数型プログラミングの要領でコマンドが定義でき，かつ静的に型がつけられるため，柔軟な記述とわかりやすいエラー報告が実現されています。
https://github.com/gfngfn/SATySFi/blob/master/README-ja.md

## RailsのviewでSATySFiを使う方法

[SATySFi-rails](https://github.com/hanachin/SATySFi-rails) gemを使うとPDFを出力するviewをSATySFiで書けます。

### 必要なソフトウェア

SATySFiがインストールされPATHが通っている必要があります。
[SATySFiのREADME](https://github.com/gfngfn/SATySFi/)を頼りにインストールしましょう。


### インストール方法

Gemfileに追記して`bundle install`します。

```rb:Gemfile
gem "SATySFi-rails"
```

### 使い方

`app/views/**/*`以下に`.pdf.saty`の名前でテンプレートをおくとSATySFiで実行されPDFが出力されます。

```app/views/fizzbuzz/show.pdf.saty
let-inline ctx \fizzbuzz m = script-guard Latin (embed-math ctx m) in

let ctx = get-initial-context 440pt (command \fizzbuzz) in

let fizzbuzz-str n =
  match ((n mod 3), (n mod 5)) with
    | (0, 0) -> `FizzBuzz`
    | (0, _) -> `Fizz`
    | (_, 0) -> `Buzz`
    | _      -> arabic n
in

let fizzbuzz-line n = line-break true true ctx (read-inline ctx (embed-string (fizzbuzz-str n))) in

let fizzbuzz n upto =
  let-rec iter i acc =
    if i == upto then
      acc +++ fizzbuzz-line i
    else
      iter (i + 1) (acc +++ (fizzbuzz-line i))
  in
  iter 1 block-nil
in

page-break
  A4Paper
  (fun pbinfo -> (|
    text-height = 842pt;
    text-origin = (0pt, 0pt);
  |))
  (fun (pbinfo) -> (|
    header-content = block-nil;
    header-origin = (0pt, 0pt);
    footer-content = block-nil;
    footer-origin = (0pt, 0pt);
  |))
  (fizzbuzz 1 100)
```

## デモ

実際にSATySFi-railsでPDFを出力したのが以下です。
https://satysfi-rails-demo.herokuapp.com/fizzbuzz.pdf

ソースコードは以下から確認できます。
https://github.com/hanachin/satysfi-rails-demo

Herokuのデプロイボタンがついているのでかんたんにデプロイできます :rocket: [^heroku-buildpack-SATySFi]

[^heroku-buildpack-SATySFi]: [heroku-buildpack-SATySFi](https://github.com/hanachin/heroku-buildpack-SATySFi)を利用するとSATySFiをHerokuで利用できるのでべんり、Rails以外でも使えます

## erbでPDFに文字を埋め込む方法

以下のようにPDFの中にコントローラーから受け取った文字をerbで埋め込むこともできます。[^notice]

```rb:config/routes.rb
Rails.application.routes.draw do
  resources :greetings, only: :show
end
```

```rb:app/controllers/greetings_controller.rb
class GreetingsController < ApplicationController
  def show
    @name = params[:id]
    saty = render_to_string "show.pdf.saty"
    render inline: saty, type: :saty
  end
end
```


```erb:app/views/greetings/show.pdf.saty.erb
@require: standalone

let-block ctx +hi = line-break true true ctx (read-inline ctx {<%= "Hello, #{@name}!" %>}) in

standalone '<+hi;>
```

以下のページを表示し確認すると、idのhanachinが埋め込まれているのが確認できます。
http://localhost:3000/greetings/hanachin.pdf

![Screenshot from 2018-11-30 23-38-45.png](https://qiita-image-store.s3.amazonaws.com/0/2074/415de4da-4664-58d6-0188-36125b58092f.png)


[^notice]: 任意の文字を表示されてしまうと危ないので先程のデモアプリにはデプロイしていませんが https://github.com/hanachin/satysfi-rails-demo/tree/greetings から確認できます

## まとめ

レッツSATySFi