() translation by (you can also view the original English article)
Ada banyak cara untuk menghasilkan PDF di Ruby and Rails. Kemungkinannya adalah Anda sudah terbiasa dengan HTML dan CSS, jadi kita akan menggunakan PDFKit untuk menghasilkan file PDF menggunakan HTML dari view Rails standar dan kode gaya.
Pengenalan PDFKit
Secara internal, PDFKit menggunakan wkhtmltopdf (WebKit HTML to PDF), sebuah mesin yang akan mengambil HTML dan CSS, me-render menggunakan WebKit, dan mengeluarkannya sebagai PDF dengan kualitas tinggi.
Untuk memulai, instal wkhtmltopdf di komputer Anda. Anda dapat mengunduh biner-nya atau menginstal dari Brew di Mac, atau repositori Linux pilihan Anda.
Anda juga perlu menginstal pdfkit gem
, dan kemudian menjalankan sedikit Ruby berikut untuk menghasilkan PDF dengan teks "Hello Envato!"
1 |
require "pdfkit" |
2 |
|
3 |
kit = PDFKit.new(<<-HTML) |
4 |
<p>Hello Envato!</p>
|
5 |
HTML
|
6 |
|
7 |
kit.to_file("hello.pdf") |
Anda harus memiliki file baru bernama hello.pdf dengan teksnya di bagian atas.



Contoh dari PDF yang dihasilkanPDFKit juga memungkinkan Anda untuk menghasilkan PDF dari URL. Jika Anda ingin menghasilkan PDF dari beranda Google, Anda dapat menjalankan:
1 |
require "pdfkit" |
2 |
|
3 |
PDFKit.new('https://www.google.com', :page_size => 'A3').to_file('google.pdf') |
Seperti yang Anda lihat, saya menentukan ukuran page_size
-secara default, A4 digunakan. Anda dapat melihat daftar opsi lengkap di sini.



Menata PDF Anda Menggunakan CSS
Sebelumnya saya menyebutkan bahwa kita akan menghasilkan file PDF menggunakan HTML dan CSS. Dalam sampel ini, saya telah menambahkan sedikit CSS untuk mendesain HTML untuk faktur sampel, seperti yang Anda lihat:
1 |
require "pdfkit" |
2 |
|
3 |
kit = PDFKit.new(<<-HTML) |
4 |
<style>
|
5 |
* {
|
6 |
color: grey;
|
7 |
}
|
8 |
h1 {
|
9 |
text-align: center;
|
10 |
color: black;
|
11 |
margin-bottom: 100px;
|
12 |
}
|
13 |
.notes {
|
14 |
margin-top: 100px;
|
15 |
}
|
16 |
|
17 |
table {
|
18 |
width: 100%;
|
19 |
}
|
20 |
th {
|
21 |
text-align: left;
|
22 |
color: black;
|
23 |
padding-bottom: 15px;
|
24 |
}
|
25 |
</style>
|
26 |
|
27 |
<h1>Envato Invoice</h1>
|
28 |
|
29 |
<table>
|
30 |
<thead>
|
31 |
<tr>
|
32 |
<th>Description</th>
|
33 |
<th>Price</th>
|
34 |
</tr>
|
35 |
</thead>
|
36 |
<tbody>
|
37 |
<tr>
|
38 |
<td>Monthly Subscription to Tuts+</td>
|
39 |
<td>$15</td>
|
40 |
</tr>
|
41 |
</tbody>
|
42 |
</table>
|
43 |
|
44 |
<div class="notes">
|
45 |
<p><strong>Notes:</strong> This invoice was paid on the 23rd of March 2016 using your credit card ending on 1234.</p>
|
46 |
</div>
|
47 |
HTML
|
48 |
|
49 |
kit.to_file("envato_invoice.pdf") |
Jika Anda menjalankan skrip ini, file envato_invoice.pdf akan dibuat. Foto ini menunjukkan hasil dari faktur sampel:



Contoh dari PDF faktur EnvatoSeperti yang Anda lihat, PDFKit sangat mudah digunakan, jika Anda sudah terbiasa dengan HTML dan CSS. Anda dapat terus menyesuaikan atau menata dokumen ini sesuka Anda.
Menggunakan PDFKit dari Aplikasi Rails
Sekarang mari kita lihat bagaimana menggunakan PDFKit dalam konteks aplikasi Rails, sehingga kita dapat secara dinamis menghasilkan file PDF menggunakan data dari model kita. Di bagian ini kita akan membangun aplikasi rails sederhana untuk menghasilkan "Envato Invoice" sebelumnya secara dinamis. Mulailah dengan membuat aplikasi rails baru dan menambahkan tiga model:
1 |
$ rails new envato_invoices
|
2 |
$ cd envato_invoices |
3 |
|
4 |
$ rails generate model invoice date:date client notes |
5 |
$ rails generate model line_item description price:float invoice:references
|
6 |
|
7 |
$ rake db:migrate
|
Sekarang, kita harus menambahkan beberapa data sampel ke database. Tambahkan potongan kode ini ke db/seeds.rb
.
1 |
line_items = LineItem.create([ |
2 |
{ description: 'Tuts+ Subscription April 2016', price: 15.0 }, |
3 |
{ description: 'Ruby eBook', price: 9.90} ]) |
4 |
Invoice.create( |
5 |
client: 'Pedro Alonso', |
6 |
total: 24.90, |
7 |
line_items: line_items, |
8 |
date: Date.new(2016, 4, 1)) |
Jalankan rake db:seed
di terminal Anda untuk menambahkan faktur sampel ke database.
Kita juga tertarik untuk membuat daftar faktur dan detail satu faktur di aplikasi kita, jadi menggunakan generator rails, jalankan rails generate controller Invoices index show
untuk membuat controller dan views.
app/controllers/invoices_controller.rb
1 |
class InvoicesController < ApplicationController |
2 |
def index |
3 |
@invoices = Invoice.all |
4 |
end
|
5 |
|
6 |
def show |
7 |
@invoice = Invoice.find(params[:id]) |
8 |
end
|
9 |
end
|
app/views/invoices/index.html.erb
1 |
<h1>Invoices</h1> |
2 |
<ul>
|
3 |
<% @invoices.each do |invoice| %> |
4 |
<li>
|
5 |
<%= link_to "#{invoice.id} - #{invoice.client} - #{invoice.date.strftime("%B %d, %Y")} ", invoice_path(invoice) %> |
6 |
</li>
|
7 |
<% end %> |
8 |
</ul>
|
Kita perlu memodifikasi rute rails untuk mengarahkan ke InvoicesController
secara default, jadi edit config/routes.rb
:
1 |
Rails.application.routes.draw do |
2 |
root to: 'invoices#index' |
3 |
|
4 |
resources :invoices, only: [:index, :show] |
5 |
end
|
Mulai rails server
Anda dan navigasikan ke localhost:3000 untuk melihat daftar faktur:



app/views/invoices/show.html.erb
1 |
<div class="invoice"> |
2 |
<h1>Envato Invoice</h1> |
3 |
|
4 |
<h3>To: <%= @invoice.client %></h3> |
5 |
<h3>Date: <%= @invoice.date.strftime("%B %d, %Y") %></h3> |
6 |
|
7 |
<table>
|
8 |
<thead>
|
9 |
<tr>
|
10 |
<th>Description</th> |
11 |
<th>Price</th> |
12 |
</tr>
|
13 |
</thead>
|
14 |
<tbody>
|
15 |
<% @invoice.line_items.each do |line_item| %> |
16 |
<tr>
|
17 |
<td><%= line_item.description %></td> |
18 |
<td><%= number_to_currency(line_item.price) %></td> |
19 |
</tr>
|
20 |
<% end %> |
21 |
<tr class="total"> |
22 |
<td style="text-align: right">Total: </td> |
23 |
<td><%= number_to_currency(@invoice.total) %></span></td> |
24 |
</tr>
|
25 |
</tbody>
|
26 |
</table>
|
27 |
|
28 |
<% if @invoice.notes %> |
29 |
<div class="notes"> |
30 |
<p><strong>Notes:</strong> <%= @invoice.notes %></p> |
31 |
</div>
|
32 |
<% end %> |
33 |
</div>
|
CSS untuk halaman detail faktur ini telah dipindahkan ke app/assets/stylesheets/application.scss
1 |
.invoice { |
2 |
width: 700px; |
3 |
max-width: 700px; |
4 |
border: 1px solid grey; |
5 |
margin: 50px; |
6 |
padding: 50px; |
7 |
|
8 |
h1 { |
9 |
text-align: center; |
10 |
margin-bottom: 100px; |
11 |
}
|
12 |
.notes { |
13 |
margin-top: 100px; |
14 |
}
|
15 |
|
16 |
table { |
17 |
width: 90%; |
18 |
text-align: left; |
19 |
}
|
20 |
th { |
21 |
padding-bottom: 15px; |
22 |
}
|
23 |
|
24 |
.total td { |
25 |
font-size: 20px; |
26 |
font-weight: bold; |
27 |
padding-top: 25px; |
28 |
}
|
29 |
}
|
Kemudian ketika Anda mengklik faktur di halaman daftar utama, Anda akan melihat detailnya:



View fakturPada titik ini, kita siap untuk menambahkan fungsionalitas ke aplikasi rails kita untuk melihat atau mengunduh faktur dalam PDF.
Kelas InvoicePdf untuk Menangani Render PDF
Untuk me-render faktur dari aplikasi rails kita ke PDF, kita perlu menambahkan tiga gem ke Gemfile: PDFKit, render_anywhere, dan wkhtmltopdf-binary. Secara default, rails hanya memungkinkan Anda untuk me-render template dari controller, tetapi dengan menggunakan render_anywhere
, kita bisa me-render template dari model atau pekerjaan latar belakang.
1 |
gem 'pdfkit' |
2 |
gem 'render_anywhere' |
3 |
gem 'wkhtmltopdf-binary' |
Agar tidak mencemari controller kita dengan terlalu banyak logika, saya akan membuat kelas InvoicePdf
baru di dalam folder app/models
untuk membungkus logika untuk menghasilkan PDF.
1 |
require "render_anywhere" |
2 |
|
3 |
class InvoicePdf |
4 |
include RenderAnywhere |
5 |
|
6 |
def initialize(invoice) |
7 |
@invoice = invoice |
8 |
end
|
9 |
|
10 |
def to_pdf |
11 |
kit = PDFKit.new(as_html, page_size: 'A4') |
12 |
kit.to_file("#{Rails.root}/public/invoice.pdf") |
13 |
end
|
14 |
|
15 |
def filename |
16 |
"Invoice #{invoice.id}.pdf" |
17 |
end
|
18 |
|
19 |
private
|
20 |
|
21 |
attr_reader :invoice |
22 |
|
23 |
def as_html |
24 |
render template: "invoices/pdf", layout: "invoice_pdf", locals: { invoice: invoice } |
25 |
end
|
26 |
end
|
Kelas ini hanya mengambil faktur untuk di-render sebagai parameter pada constructor kelas. Metode private as_html
sedang membaca view template invoices/pdf
dan layout_pdf
yang kita gunakan untuk menghasilkan HTML yang perlu kita render sebagai PDF. Terakhir, metode to_pdf
menggunakan PDFKit untuk menyimpan file PDF di folder public rails.
Mungkin Anda ingin membuat nama dinamis di aplikasi nyata Anda sehingga file PDF tidak ditimpa secara tidak sengaja. Anda mungkin ingin menyimpan file di AWS S3 atau folder pribadi juga, tapi itu di luar cakupan tutorial ini.
/app/views/invoices/pdf.html.erb
1 |
<div class="invoice"> |
2 |
<h1>Envato Invoice</h1> |
3 |
|
4 |
<h3>To: <%= invoice.client %></h3> |
5 |
<h3>Date: <%= invoice.date.strftime("%B %d, %Y") %></h3> |
6 |
|
7 |
<table>
|
8 |
<thead>
|
9 |
<tr>
|
10 |
<th>Description</th>
|
11 |
<th>Price</th>
|
12 |
</tr>
|
13 |
</thead>
|
14 |
<tbody>
|
15 |
<% invoice.line_items.each do |line_item| %>
|
16 |
<tr>
|
17 |
<td><%= line_item.description %></td> |
18 |
<td><%= number_to_currency(line_item.price) %></td> |
19 |
</tr>
|
20 |
<% end %>
|
21 |
<tr class="total"> |
22 |
<td style="text-align: right">Total: </td> |
23 |
<td><%= number_to_currency(invoice.total) %></span></td> |
24 |
</tr> |
25 |
</tbody> |
26 |
</table> |
27 |
|
28 |
<% if invoice.notes %> |
29 |
<div class="notes">
|
30 |
<p><strong>Notes:</strong> <%= invoice.notes %></p> |
31 |
</div>
|
32 |
<% end %>
|
33 |
</div>
|
/app/views/layouts/invoice_pdf.erb
1 |
<!DOCTYPE html> |
2 |
<html> |
3 |
<head> |
4 |
<title>Envato Invoices</title> |
5 |
<style>
|
6 |
<%= Rails.application.assets.find_asset('application.scss').to_s %>
|
7 |
</style> |
8 |
</head> |
9 |
<body>
|
10 |
<%= yield %>
|
11 |
</body> |
12 |
</html> |
Satu hal yang perlu diperhatikan dalam file layout ini adalah bahwa kita me-render gaya dalam layout. WkHtmlToPdf bekerja lebih baik jika kita memberikan gaya dengan cara ini.
DownloadsController untuk Me-render Faktur PDF
Pada titik ini kita membutuhkan rute dan controller yang memanggil kelas InvoicePdf
untuk mengirim file PDF ke browser, jadi edit config/routes.rb
untuk menambahkan sumber daya bersarang:
1 |
Rails.application.routes.draw do |
2 |
root to: "invoices#index" |
3 |
|
4 |
resources :invoices, only: [:index, :show] do |
5 |
resource :download, only: [:show] |
6 |
end
|
7 |
end
|
Jika kita menjalankan rake routes
, kita melihat daftar rute yang tersedia di aplikasi:
1 |
Prefix Verb URI Pattern Controller#Action |
2 |
root GET / invoices#index |
3 |
invoice_download GET /invoices/:invoice_id/download(.:format) downloads#show |
4 |
invoices GET /invoices(.:format) invoices#index |
5 |
invoice GET /invoices/:id(.:format) invoices#show |
Tambahkan app/controllers/downloads_controller.rb
:
1 |
class DownloadsController < ApplicationController |
2 |
|
3 |
def show |
4 |
respond_to do |format| |
5 |
format.pdf { send_invoice_pdf } |
6 |
end
|
7 |
end
|
8 |
|
9 |
private
|
10 |
|
11 |
def invoice_pdf |
12 |
invoice = Invoice.find(params[:invoice_id]) |
13 |
InvoicePdf.new(invoice) |
14 |
end
|
15 |
|
16 |
def send_invoice_pdf |
17 |
send_file invoice_pdf.to_pdf, |
18 |
filename: invoice_pdf.filename, |
19 |
type: "application/pdf", |
20 |
disposition: "inline" |
21 |
end
|
22 |
end
|
Seperti yang Anda lihat, ketika permintaan meminta file PDF, metode send_invoice_pdf
sedang memproses permintaannya. Metode invoice_pdf
hanya menemukan faktur dari database berdasarkan id, dan membuat instance dari InvoicePdf. Kemudian send_invoice_pdf
hanya memanggil metode to_pdf
, untuk mengirim file PDF yang dihasilkan ke browser.
Satu hal yang perlu diperhatikan adalah kita mengirimkan parameter disposition: "inline"
ke send_file
. Parameter ini mengirim file ke browser, dan itu akan ditampilkan. Jika Anda ingin memaksa file untuk diunduh, maka Anda harus memberikan disposition: "attachment"
.
Tambahkan tombol unduh ke template tampilan faktur Anda app/views/invoices/show.html.erb
:
1 |
<%= link_to "Download PDF", |
2 |
invoice_download_path(@invoice, format: "pdf"),
|
3 |
target: "_blank",
|
4 |
class: "download" %>
|
Jalankan aplikasi, navigasikan ke rincian faktur, klik unduh, dan tab baru akan terbuka menampilkan Faktur PDF.



Render PDF sebagai HTML dalam Pengembangan
Saat Anda sedang mengerjakan markup untuk PDF Anda, keharusan untuk menghasilkan PDF setiap kali Anda ingin menguji suatu perubahan terkadang bisa lambat. Untuk alasan ini, dapat melihat HTML yang akan dikonversi ke PDF sebagai HTML biasa bisa sangat berguna. Kita hanya perlu mengedit /app/controllers/downloads_controller.rb
.
1 |
class DownloadsController < ApplicationController |
2 |
|
3 |
def show |
4 |
respond_to do |format| |
5 |
format.pdf { send_invoice_pdf } |
6 |
|
7 |
if Rails.env.development? |
8 |
format.html { render_sample_html } |
9 |
end
|
10 |
end
|
11 |
end
|
12 |
|
13 |
private
|
14 |
|
15 |
def invoice |
16 |
Invoice.find(params[:invoice_id]) |
17 |
end
|
18 |
|
19 |
def invoice_pdf |
20 |
InvoicePdf.new(invoice) |
21 |
end
|
22 |
|
23 |
def send_invoice_pdf |
24 |
send_file invoice_pdf.to_pdf, |
25 |
filename: invoice_pdf.filename, |
26 |
type: "application/pdf", |
27 |
disposition: "inline" |
28 |
end
|
29 |
|
30 |
def render_sample_html |
31 |
render template: "invoices/pdf", layout: "invoice_pdf", locals: { invoice: invoice } |
32 |
end
|
33 |
end
|
Sekarang metode show
juga merespons permintaan HTML dalam mode pengembangan. Rute untuk faktur PDF akan seperti http://localhost:3000/invoices/1/download.pdf. Jika Anda mengubahnya ke http://localhost:3000/invoices/1/download.html, Anda akan melihat faktur dalam HTML menggunakan markup yang digunakan untuk menghasilkan PDF.
Dengan kode di atas, membuat file PDF menggunakan Ruby on Rails sangat mudah dengan asumsi Anda terbiasa dengan bahasa Ruby dan kerangka kerja Rails. Mungkin aspek terbaik dari keseluruhan proses adalah Anda tidak perlu mempelajari bahasa markup baru atau spesifik tentang pembuatan PDF.
Semoga tutorial ini terbukti bermanfaat. Silakan tinggalkan pertanyaan, komentar, dan umpan balik di komentar dan saya akan dengan senang hati menindaklanjutinya.