$30 off During Our Annual Pro Sale. View Details »

Replace wicked_pdf with puppeteer

Replace wicked_pdf with puppeteer

kawasaki.rb #073〜6周年記念LT大会
https://kawasakirb.connpass.com/event/134375/

tanakaworld

June 26, 2019
Tweet

More Decks by tanakaworld

Other Decks in Technology

Transcript

  1. Replace wicked_pdf
    with puppeteer
    kawasaki.rb #073〜6周年記念LT大会
    2019/06/26 @_tanakaworld

    View Slide

  2. About Me
    ● Twitter: @_tanakaworld
    ● GitHub: tanakaworld
    ● Merpay, inc.
    ● Software Engineer (Frontend)

    View Slide

  3. https://proff.io/

    View Slide

  4. wicked_pdf
    ● PDF generator (from HTML) plugin for Ruby on Rails
    ○ https://github.com/mileszs/wicked_pdf
    ● Uses wkhtmltopdf
    ○ https://wkhtmltopdf.org/

    View Slide

  5. Issues of wicked_pdf
    ● Limited Markup of CSS / HTML (undocumented)
    ● Too slow

    View Slide

  6. puppeteer
    ● https://github.com/GoogleChrome/puppeteer
    ● Headless Chrome
    ● Able to
    ○ take screenshot
    ○ control Web Page
    ○ make PDF
    ○ •••

    View Slide

  7. HTML -> PDF
    ● HTML Requirements
    ○ Unable to make HTML public
    ○ Authorization in Rails App

    View Slide

  8. Use puppeteer in Rails App
    ● HTML Requirements
    ○ Unable to make HTML public
    ○ Authorization in Rails App
    ● Install
    ○ $ yarn add puppeteer
    ● Flow
    ○ Authentication / Authorization
    ○ Generate HTML file
    ○ Generate PDF file from HTML file (by puppeteer)
    ○ Respond PDF file

    View Slide

  9. articles/1 articles/1.pdf
    DEMO

    View Slide

  10. class ArticlesController < ApplicationController
    include PdfHelpers
    •••
    def show
    template_path = 'articles/show.html.erb'
    respond_to do |f|
    f.html do
    @is_pdf = false
    render template_path
    end
    f.pdf do
    @is_pdf = true
    render_pdf_from_template
    "#{@article.title}-#{Time.zone.now.to_date.to_s}", template_path
    end
    end
    end
    •••
    end

    View Slide

  11. require 'securerandom'
    module PdfHelpers
    extend ActiveSupport::Concern
    def render_pdf_from_template(file_name, template_path)
    data_html = render_to_string template_path, layout: 'pdf'
    FileUtils.mkdir_p(tmp_pdf_dir) unless File.exists?(tmp_pdf_dir)
    html_file_path = generate_html_from_template(data_html)
    pdf_data = generate_pdf_from_html(html_file_path)
    render_pdf_data(pdf_data, "#{file_name}.pdf")
    rescue => e
    logger.error e
    end
    •••

    View Slide

  12. private
    def tmp_pdf_dir
    "#{Rails.root}/tmp/pdf-generator"
    end
    def log_path
    ENV['RAILS_ENV'] === 'development' ? "#{tmp_pdf_dir}/app.log" : '/var/log/pdf-generator/app.log'
    end
    def generate_html_from_template(data)
    tmp_html_file = File.open("#{tmp_pdf_dir}/#{SecureRandom.hex}.html", "w")
    tmp_html_file.puts data
    tmp_html_file.close
    tmp_html_file.path
    end
    def generate_pdf_from_html(html_path)
    tmp_pdf_file = Tempfile.new("#{SecureRandom.hex}")
    system("node #{Rails.root}/pdf-generator/pdf-from-html-file.js #{html_path} #{tmp_pdf_file.path} > #{log_path}
    2> #{log_path}")
    File.read(tmp_pdf_file.path)
    end
    def render_pdf_data(data, file_name)
    send_data(data, filename: file_name, type: 'application/pdf', disposition: 'inline')
    end
    end

    View Slide

  13. const puppeteer = require('puppeteer');
    const createPdf = async () => {
    let browser;
    try {
    browser = await puppeteer.launch({
    args: ['--no-sandbox', '--disable-setuid-sandbox']
    });
    const page = await browser.newPage();
    const htmlPath = process.argv[2];
    await page.goto(`file:${htmlPath}`, {
    waitUntil: 'networkidle0'
    });
    await page.pdf({
    path: process.argv[3],
    format: 'A4',
    margin: {top: 0, right: 0, bottom: 0, left: 0},
    printBackground: true
    });
    } catch (err) {
    console.error(err.message);
    } finally {
    if (browser) {
    browser.close();
    }
    process.exit();
    }
    };
    createPdf();

    View Slide

  14. articles/1 articles/1.pdf

    View Slide

  15. Summary
    ● You can integrate puppeteer in Rails App
    ● Easy to
    ○ convert HTML to PDF
    ○ wait something…
    ■ e.g. page.waitForSelector
    ● You should separate PDF generation process if needed

    View Slide

  16. Sample
    ● https://github.com/tanakaworld/puppeteer_on_rails_sample

    View Slide

  17. Thanks

    View Slide