Giới thiệu

Ở 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

Cách cài đặt wicked pdf

  • 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.

Cách dùng wicked pdf

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_width:                     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.

Thêm Css & JS trong view 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_tagwicked_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>

Sử dụng Bootstrap trong PDF

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" %>

Sử dụng nâng cao

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

Ngắt trang trong PDF

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; }

Đánh số trang

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>

Rack Middleware

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

Debugging

ở 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')

Kết bài

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