Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

https://proff.io/

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

articles/1 articles/1.pdf DEMO

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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();

Slide 14

Slide 14 text

articles/1 articles/1.pdf

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Thanks