Advertisement
  1. Code
  2. Ruby

Menghasilkan PDF dari HTML dengan Rails

Scroll to top
Read Time: 9 min

() 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.

An example of the PDF that was generatedAn example of the PDF that was generatedAn example of the PDF that was generated

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.

An example of the Google homepage in PDF formatAn example of the Google homepage in PDF formatAn example of the Google homepage in PDF format

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:

An example of an Envato invoice PDFAn example of an Envato invoice PDFAn example of an Envato invoice PDF

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:

List of invoicesList of invoicesList of invoices

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:

Invoice viewInvoice viewInvoice view

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.

Invoice view PDF dynamically generatedInvoice view PDF dynamically generatedInvoice view PDF dynamically generated

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.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.