Tạo file pdf sử dụng Wicked PDF
Trong quá trình phát triển dự án, chắc hẳn sẽ có nhiều chức năng yêu cầu tạo các file pdf từ dữ liệu của hệ thống theo các mẫu (template). Bài viết này giới thiệu một phương pháp tạo file pdf mình đã thực hiện thành công trong dự án của mình. Wicked PDF sử dụng các tiện ích wkhtmltopdf để tạo ...
Trong quá trình phát triển dự án, chắc hẳn sẽ có nhiều chức năng yêu cầu tạo các file pdf từ dữ liệu của hệ thống theo các mẫu (template). Bài viết này giới thiệu một phương pháp tạo file pdf mình đã thực hiện thành công trong dự án của mình.
Wicked PDF sử dụng các tiện ích wkhtmltopdf để tạo file PDF cho người dùng từ HTML. Bạn chỉ cần viết một trang HTML như bình thường, sau đó để cho Wicked PDF sẽ sử dụng đó làm đầu vào tạo ra file pdf cho bạn.
Hướng dẫn Cài đặt
Thêm gem wicked_pdf vào Gemfile rồi chạy bundle install
gem 'wicked_pdf'
Sau đó chạy lệnh khởi tạo
rails generate wicked_pdf
Thêm dòng sau vào config/initializers/mime_types.rb
Mime::Type.register "application/pdf", :pdf
Do wicked_pdf sử dụng wkhtmltopdf nên chúng ta cần cài đặt thêm wkhtmltopdf. Để cài đặt, thêm dòng sau vào Gemfile rồi chạy bundle install
gem 'wkhtmltopdf-binary'
Nếu wkhtmltopdf không nằm trên cùng webserver, thì cần thêm cấu hình như sau vào file config config/initializers/wicked_pdf.rb
WickedPdf.config = { exe_path: '/usr/local/bin/wkhtmltopdf' }
Cách sử dụng
Trên controller, bạn chỉ cần render pdf như sau:
class ThingsController < ApplicationController def show respond_to do |format| format.html format.pdf do render pdf: "file_name", layout: "layout_name" # loại bỏ đuôi trong file_name ".pdf" end end end end
Điều kiện sử dụng
wkhtmltopdf đang chạy bên ngoài của ứng dụng Rails của bạn, do đó layout thông thường trong dự án sẽ không thể hoạt động. Nếu muốn sử dụng CSS, Javascript hay hình ảnh thì bạn phải thay đổi cách tham chiếu tuyệt đối đến các tập tin. Cách tốt nhất để Rails không sử dụng asset pipeline là sử dụng wicked_pdf_stylesheet_link_tag, wicked_pdf_javascript_include_tag và wicked_pdf_image_tag hoặc trỏ thẳng đến một CDN cho các thư việc của jquery
Ví dụ về layout voucher mình sử dụng:
html head meta charset="utf-8" = wicked_pdf_stylesheet_link_tag "voucher" = wicked_pdf_javascript_include_tag "voucher" body .wrapper header#header #header_top #main = yield footer#footer_voucher.footer-voucher p = t "voucher.copyright_html"
và trong source show, mình sử dụng ảnh như sau:
<%= wicked_pdf_image_tag "logo-voucher" %> thay thế cho <%= image_tag "logo-voucher" %>
Các tùy chọn nâng cao khác
def show respond_to do |format| format.html format.pdf do render pdf: "file_name", disposition: "attachment", template: "things/show.pdf.erb", file: "#{Rails.root}/files/foo.erb" layout: "pdf.html", wkhtmltopdf: "/usr/local/bin/wkhtmltopdf", show_as_html: params.key?("debug"), orientation: "Landscape", page_size: "A4, Letter, ...", page_height: NUMBER, page_awidth: NUMBER, save_to_file: Rails.root.join("pdfs", "#{filename}.pdf"), save_only: false, proxy: "TEXT", basic_auth: false username: "TEXT", password: "TEXT", title: "Alternate Title", cover: "URL, Pathname, or raw HTML string", dpi: "dpi", encoding: "TEXT", user_style_sheet: "URL", cookie: ["_session_id SESSION_ID"], post: ["query QUERY_PARAM"], redirect_delay: NUMBER, javascript_delay: NUMBER, window_status: "TEXT", image_quality: NUMBER, no_pdf_compression: true, zoom: FLOAT, page_offset: NUMBER, book: true, default_header: true, disable_javascript: false, grayscale: true, lowquality: true, enable_plugins: true, disable_internal_links: true, disable_external_links: true, print_media_type: true, disable_smart_shrinking: true, use_xserver: true, background: false, no_background: true, viewport_size: "TEXT", extra: "", outline: { outline: true, outline_depth: LEVEL }, margin: { top: SIZE, bottom: SIZE, left: SIZE, right: SIZE }, header: { html: { template: "users/header.pdf.erb", layout: "pdf_plain.html", url: "www.example.com", locals: { foo: @bar }}, center: "TEXT", font_name: "NAME", font_size: SIZE, left: "TEXT", right: "TEXT", spacing: REAL, line: true, content: "HTML CONTENT ALREADY RENDERED"}, footer: { html: { template:"shared/footer.pdf.erb", layout: "pdf_plain.html", url: "www.example.com", locals: { foo: @bar }}, center: "TEXT", font_name: "NAME", font_size: SIZE, left: "TEXT", right: "TEXT", spacing: REAL, line: true, content: "HTML CONTENT ALREADY RENDERED"}, toc: { font_name: "NAME", depth: LEVEL, header_text: "TEXT", header_fs: SIZE, text_size_shrink: 0.8, l1_font_size: SIZE, l2_font_size: SIZE, l3_font_size: SIZE, l4_font_size: SIZE, l5_font_size: SIZE, l6_font_size: SIZE, l7_font_size: SIZE, level_indentation: NUM, l1_indentation: NUM, l2_indentation: NUM, l3_indentation: NUM, l4_indentation: NUM, l5_indentation: NUM, l6_indentation: NUM, l7_indentation: NUM, no_dots: true, disable_dotted_lines: true, disable_links: true, disable_toc_links: true, disable_back_links:true, xsl_style_sheet: "file.xsl"} end end end
Cách sử dụng nâng cao
Nếu bạn chỉ cần tạo ra một file pdf mà không cần hiển thị thì không cần phải đưa vào controller như trên. Tham khảo các lệnh sau:
# tạo file pdf từ một string pdf = WickedPdf.new.pdf_from_string('<h1>Hello There!</h1>') # tạo file pdf từ file html, không cần convert sang string # Đường dẫn tới file cần là đường dẫn tuyệt đối pdf = WickedPdf.new.pdf_from_html_file('/your/absolute/path/here') # tạo file pdf từ một URL pdf = WickedPdf.new.pdf_from_url('https://github.com/mileszs/wicked_pdf') # tạo file pdf từ string sử dụng templates, layouts và các tùy chọn nội dung cho header hoặc footer (cách này thường được sử dụng nhiều nhất vì chúng ta có thể dễ dàng tùy chỉnh được nội dung , thiết kế của file pdf) pdf = WickedPdf.new.pdf_from_string( render_to_string('templates/pdf', layout: 'pdfs/layout_pdf'), footer: { content: render_to_string(layout: 'pdfs/layout_pdf') } ) # render trực tiếp từ controller, sử dụng views, templates và tất cả các tùy chọn của wicked_pdf như bình thường pdf = render_to_string pdf: "some_file_name", template: "templates/pdf", encoding: "UTF-8" # sau đó tiến hành lưu file pdf tạo được ra save_path = Rails.root.join('pdfs','filename.pdf') File.open(save_path, 'wb') do |file| file << pdf end
Qua trên ta thấy bản chất việc wicked_pdf tạo ra file pdf chính là việc convert kiểu dữ liệu hoàn thiện từ html sang pdf. Vì vậy, sau khi thực hiện xong các cài đặt như trên thì bạn chỉ cần chỉnh sửa mã nguồn html thì sẽ có ngay một file pdf như ý.
Ngoài ra, trong số các tùy chọn phía trên, tùy chọn show_as_html: true sẽ cho phép chúng ta xem file pdf như một file html trên trình duyệt. Nhờ đó kiểm tra được mã nguồn html dễ dàng trên trình duyệt.
Bên cạnh đó, bạn cũng có thể config layout áp dụng cho file pdf tại phần config/initializers/wicked_pdf.rb hoặc có thể setting cụ thể tại mỗi lần chạy lệnh tạo file. Các config đặt trong wicked_pdf.rb sẽ được áp dụng mặc định. Còn việc thiết lập tại mỗ lần tạo file sẽ ghi đè lên nội dung trong config.
Cảm ơn các bạn đã theo dõi bài viết!