Giới thiệu Gem Wicked PDF trong Rails
Ở bài viết này mình xin giới thiệu về gem wicked_pdf. Gem Wicked PDF giúp bạn chỉ cần viết một trang HTML như bình thường, sau đó nó sẽ giúp bạn tạo file PDF 1 cách dễ dàng bằng việc sử dụng các tiện ích wkhtmltopdf để tạo file PDF. Wicked PDF làm việc trên ruby 1.8.7 -> 2.3 và rails 2 -> ...
Ở bài viết này mình xin giới thiệu về gem wicked_pdf. Gem Wicked PDF giúp bạn chỉ cần viết một trang HTML như bình thường, sau đó nó sẽ giúp bạn tạo file PDF 1 cách dễ dàng bằng việc sử dụng các tiện ích wkhtmltopdf để tạo file PDF. Wicked PDF làm việc trên ruby 1.8.7 -> 2.3 và rails 2 -> 5
- thêm gem 'wicked_pdfvào gemfile vàbundle install`
- sau đó khởi tạo initializer bằng câu lệnh : rails generate wicked_pdf
- với những phiên bản rails 4 trở xuống bạn cần config thêm trong file config/initializers/mime_types.rb : Mime::Type.register "application/pdf", :pdf Như đã nói ở trên wicked_pdf dùng wkhtmltopdf để tạo file PDF nên chúng ta cần cài đặt thêm gem 'wkhtmltopdf-binary' và bundle install Nếu wkhtmltopdf không cùng nằm trên 1 server thì chúng ta cần thêm config:
WickedPdf.config = { exe_path: '/usr/local/bin/wkhtmltopdf' }
trong file config/initializers/wicked_pdf.rb Vậy là đã xong phần cái đặt.
Tạo controller để xử lý việc export pdf
class WickedPdfsController < ApplicationController def index respond_to do |format| format.html format.pdf do render pdf: "name_file" end end end
ở đây mình dùng wicked pdf để render ra file pdf. Các bạn có thể config thêm ở đây:
pdf: "file_name", #file name pdf disposition: "attachment", #file pdf sau khi tạo sẽ tự động download về máy template: "things/show.pdf.erb", #Wicked_pdf sẽ render từ html sang pdf (nói cách khác phần view mà hiển thị trên trang web sẽ được chuyển thành file pdf). file: "#{Rails.root}/files/foo.erb" layout: "pdf.html", wkhtmltopdf: "/usr/local/bin/wkhtmltopdf", #path thư viện wkhtmltopdf show_as_html: params.key?("debug"), #Debugging orientation: "Landscape", page_size: "A4, Letter, ...", # size hiển thị trên pdf page_height: NUMBER, #chiều cao của file page_awidth: NUMBER, #động rộng của file save_to_file: Rails.root.join("pdfs", "#{filename}.pdf"), #path khi download file save_only: false, #chỉ lưu file (phụ thuộc vào save_to_file đã được thiết lập sẵn) proxy: "TEXT", basic_auth: false #authenticate khi tạo file pdf username: "TEXT", #tài khoản của account authenticate password: "TEXT", #mật khẩu của account authenticate title: "Alternate Title", #title của pdf 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"}
Mọi người có thể tham khảo thêm ở trên https://github.com/mileszs/wicked_pdf.
bới vì thư viện wkhtmltopdf đang chạy ngoài ứng dựng rails của bạn nên layout thông thường sẽ không thể hoạt động được. Muốn sử dụng được css, javascript các bạn hãy 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 VD:
<!DOCTYPE html> <html> <head> <title><%= full_title yield :title %></title> <%= wicked_pdf_stylesheet_link_tag "admin/pdf" %> <%= wicked_pdf_stylesheet_link_tag "admin/users" %> <%= wicked_pdf_javascript_include_tag "admin/pdf" %> <%= csrf_meta_tags %> <meta name="turbolinks-cache-control" content="no-cache"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css"> </head> <body> <%= yield %> <%=wicked_pdf_image_tag "admin/image.png"%> </body> </html>
trong lúc sử dụng wicked_pdf mình đã rất khốn khổ về việc không thể include bootstrap vào khi render pdf. Khi đường dẫn trong thư viện wkhtmltopdf đòi hỏi bạn phải sử dụng đường dẫn tuyệt đối. Đầu tiên bạn tạo 1 file pdf.css.scss và sử dụng đường dẫn tuyệt đối của wicked_pdf
#pdf.css.scss @import "bootstrap-sprockets"; @import "bootstrap"; @import "mixin"; @import "flexslider";
trong view include file pdf.css.scss <%= wicked_pdf_stylesheet_link_tag "sass/pdf" %>
Tạo pdf từ 1 string pdf = WickedPdf.new.pdf_from_string("ceate a pdf from a string") Tạo pdf từ 1 html pdf = WickedPdf.new.pdf_from_html_file("your_path") Tạo pdf từ 1 url pdf = WickedPdf.new.pdf_from_url("url) Tạo file PDF từ string sử dụng template, layouts và nội dung tùy chọn (header, footer)
string = ApplicationController.new.render_to_string( template: "pc/orders/details.pdf.slim", layout: "pc/layouts/pdf.slim", locals: {:@order => order} ) pdf = WickedPdf.new.pdf_from_string string, encoding: "UTF-8"
Lưu 1 file pdf
File.open(save_path, "wb") do file| file << pdf end
Việc render từ html sang pdf không thể đảm bảo wicked pdf sẽ hiển thị đúng như ý muốn do độ dài của các phần là khác nhau. Do đó wicked cung cấp css để hạn chế tình trạng ngắt dòng không đúng.
div.alwaysbreak { page-break-before: always; } div.nobreak:before { clear:both; } div.nobreak { page-break-inside: avoid; }
Bạn có thể đánh số trang với 1 đoạn code js:
<html> <head> <script> function number_pages() { var vars={}; var x=document.location.search.substring(1).split('&'); for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = decodeURIComponent(z[1]);} var x=['frompage','topage','page','webpage','section','subsection','subsubsection']; for(var i in x) { var y = document.getElementsByClassName(x[i]); for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]]; } } </script> </head> <body onload="number_pages()"> Page <span class="page"></span> of <span class="topage"></span> </body> </html>
Nếu bạn muốn wicked_pdf render các trang view bằng cách nối thêm .pdf vào url : thêm
require 'wicked_pdf' config.middleware.use WickedPdf::Middleware
ở phần config có nói đến Debugging. Cho phep bạn có thể truyền tham số param lên url show_as_html: params.key?('debug')
Nguồn tham khảo: https://github.com/mileszs/wicked_pdf Như vậy mình đã giới thiệu xong gem wicked_pdf. Cảm ơn mọi người đã theo dõi bài viết