Sử dụng Prawn để generrater PDF in Rails
Bài đăng này đã không được cập nhật trong 8 năm
Trong một số dự án, đôi lúc chúng ta cần tạo ra file PDF để hiển thị một CV của ứng viên hay là hóa đơn thanh toán v.v...
Hiện nay, trong Ruby đã có rất nhiều những thư viện để hỗ trợ cho lập trình viên tạo file PDF một cách nhanh chóng và hiệu quả.
Một trong số đó là gem prawn
(readme). Trong bài viết này sẽ trình bày các bước để tạo một file PDF trong Rails sử dụng prawn
.
Cài đặt#
Để cài đặt praw
vào ứng dụng Rails rất đơn giản, ta chỉ cần khai báo dòng gem "prawn"
vào trong Gemfile
. Tiếp đó, ta cần chạy lệnh trong terminal
bundle install
Sử dụng#
Bước 1: Ta cần khai báo PDF Mime::Type vào file config/initializers/mime_types.rb như sau:
Mime::Type.register “application/pdf”, :pdf
Kể từ bây giờ, mỗi khi có request mà mime là PDF thì ứng dụng sẽ trả về nội dung file PDF.
Bước 2: Ta sẽ thực hiện xử lý trong controller để trả về định dạng file PDF. Ví dụ như đoạn code dưới đây:
class InvoicesController < ApplicationController
before_filter :authenticate_customer!, :only => [:index, :show]
def index
@invoices = Invoice.all_invoices(current_customer)
end
def show
@invoice = Invoice.find(params[:id])
respond_to do |format|
format.html
format.pdf do
pdf = InvoicePdf.new(@invoice, view_context)
send_data pdf.render, filename:
"invoice_#{@invoice.created_at.strftime("%d/%m/%Y")}.pdf",
type: "application/pdf"
end
end
end
end
Trong đoạn code trên, chúng ta đã có thay đổi format của respond theo hai định dạng là html
và pdf
. Và bên trong block format.pdf
, ta có gọi phương thức send_data
với các tham số là nội dung file, tên file, và kiểu ứng dụng để đọc file. Ngoài ra, hàm này còn có thêm một option nữa là disposition: "inline"
có tác dụng sẽ hiển thị file PDF ngay trên trình duyệt thay vì phải download về máy client.
Bước 3: Ta sẽ tiến hành việc định dạng file PDF cần được tạo ra bằng cách sử dụng praw
. Việc này được thực hiện trong lớp InvoicePdf
kế thừa lớp Prawn::Document
trong thư viện praw
.
class InvoicePdf < Prawn::Document
def initialize(invoice, view)
super()
text "This is an order invoice", size: 16, font: "Helvetica", color: "0000FF"
end
end
Đoạn code trên sử dụng phương thức text
của praw
để hiển thị trong file PDF với kích thước chữ là 16, font là Helvetica
và chữ màu xanh. Tương tự, ta đều có thể hiển thị các dòng text khác bằng cách sử dụng lại phương thức này.
Khi tạo một hóa đơn thì chúng ta thường in logo của công ty. Như vậy làm sao ta có thể tạo hình ảnh trong file PDF?. Điều này trở thành vô cùng đơn giản với việc sử dụng phương thức image
của thư viện. Ví dụ, ta muộn hiển thị ảnh ở giữa file với kích thước là 50x50 sẽ thực hiện như sau:
def logo
logopath = "#{Rails.root}/app/assets/images/logo.png"
open(logopath), fit: [500, 500], position: :center
end
Moving Around##
Đây là một tính năng giúp chúng ta có thể xác định chính xác vị trí muốn đặt từng phần tử text or image... trong tài liệu bằng cách di chuyển vị trí con trỏ chuột đến tọa độ mong muốn thông qua các hàm move_down(n)
, move_up
(n) hoặc move_cursor_to(n)
. Lưu ý đó là trục tọa độ của trang tài liệu có gốc [0, 0] là ở vị trí cuối cùng bên trái trang và trục dương hướng lên trên.
Ngoài ra, thư viện praw
, còn cho phép chúng ta trình bày tài liệu dưới dạng bảng bằng cách sử dụng phương thức table
, row
và column
.
Sau đây là code của lớp InvoicePdf
để hiển thị một Invoice dưới dạng PDF để các bạn tham khảo cho việc định dạng 1 file PDF đơn giản
class InvoicePdf < Prawn::Document
def initialize(invoice, view)
super()
@invoice = invoice
@view = view
logo
thanks_message
subscription_date
subscription_details
subscription_amount
regards_message
end
def logo
logopath = "#{Rails.root}/app/assets/images/logo.png"
image logopath, width: 197, height: 91
move_down 10
draw_text "Receipt", at: [220, 575], size: 22
end
def thanks_message
move_down 80
text "Hello #{@invoice.customer.profile.first_name.capitalize},"
move_down 15
text "Thank you for your order.Print this receipt as
confirmation of your order.",
indent_paragraphs: 40, size: 13
end
def subscription_date
move_down 40
text "Subscription start date:
#{@invoice.start_date.strftime("%d/%m/%Y")} ", size: 13
move_down 20
text "Subscription end date :
#{@invoice.end_date.strftime("%d/%m/%Y")}", size: 13
end
def subscription_details
move_down 40
table subscription_item_rows, width: 500 do
row(0).font_style = :bold
columns(1..3).align = :right
self.header = true
self.column_widths = {0 => 200, 1 => 100, 2 => 100, 3 => 100}
end
end
def subscription_amount
subscription_amount = @invoice.calculate_subscription_amount
vat = @invoice.calculated_vat
delivery_charges = @invoice.calculated_delivery_charges
sales_tax = @invoice.calculated_sales_tax
table ([["Vat (12.5% of Amount)", "", "", "#{precision(vat)}"] ,
["Sales Tax (10.3% of half the Amount)", "", "",
"#{precision(sales_tax)}"] ,
["Delivery charges", "", "", "#{precision(delivery_charges)} "],
["", "", "Total Amount", "#{precision(@invoice.total_amount) } "]]),
:width => 500 do
columns(2).align = :left
columns(3).align = :right
self.header = true
self.column_widths = {0 => 200, 1 => 100, 2 => 100, 3 => 100}
columns(2).font_style = :bold
end
end
def subscription_item_rows
[["Description", "Quantity", "Rate", "Amount"]] +
@invoice.subscriptions.map do |subscribe|
[ "#{subscribe.description} ", subscribe.quantity,
"#{precision(subscribe.rate)} ",
"#{precision(subscribe.quantity * subscribe.rate)}" ]
end
end
def precision(num)
@view.number_with_precision(num, :precision => 2)
end
def regards_message
move_down 50
text "Thank You," ,indent_paragraphs: 400
move_down 6
text "XYZ",
indent_paragraphs: 370, :size: 14, style: :bold
end
end
Bước cuối cùng: Ta thêm link của file PDF vào trong views: app/views/invoices/show.html.erb:
<%= link_to "Download invoice", invoice_path(invoice.id, format: "pdf") %>;
Kết luận#
Trên đây là bài giới thiệu cơ bản về cách sử dụng praw
trong rails. Ngoài ra, các bạn có thể tham khảo thêm các phương thức khác ở đây
Tham khảo: http://www.sitepoint.com/hackable-pdf-typesetting-in-ruby-with-prawn/
All rights reserved