Rails
SATySFi

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

この記事はOkinawa.rb Advent Calendar 2018の1日目です。
2日目は @hanachin_ さんのRe: Ruby で季節のような循環する概念を表したいです。

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

SATySFiとは

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

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

RailsのviewでSATySFiを使う方法

SATySFi-rails gemを使うとPDFを出力するviewをSATySFiで書けます。

必要なソフトウェア

SATySFiがインストールされPATHが通っている必要があります。
SATySFiのREADMEを頼りにインストールしましょう。

インストール方法

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

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: 1

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

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

config/routes.rb
Rails.application.routes.draw do
  resources :greetings, only: :show
end
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
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

まとめ

レッツSATySFi


  1. heroku-buildpack-SATySFiを利用するとSATySFiをHerokuで利用できるのでべんり、Rails以外でも使えます 

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