3431 words, 10 mins
因为工作需要,要生成pdf文档,这个需求还挺有意思的,要注意的地方比如pdf分页的处理,还有不要用复杂的flex布局.
目前的解决方案有:
- PDFKit
- Wicked PDF(本文使用这个,文档够用)
这两个都是基于一个跨平台的免费开源工具wkhtmltopdf.这个工具能直接从html页面生成pdf.
安装wkhtmltopdf
安装好后,可以直接在命令行测试一下
✗ wkhtmltopdf https://gnuser.github.io/ gnuser.pdf
Loading pages (1/6)
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done
这条指令会在当前目录根据博客首页生成gnuser.pdf文件.
还带目录,感觉很棒吧!
新建一个rails项目
rails new rails-generate-pdf
安装wicked_pdf
添加gem到Gemfile
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary' # 如果上面一步已经安装过,这里可以注释掉.
执行
# command line
bundle install
生成初始化文件
# command line
✗ rails g wicked_pdf
Running via Spring preloader in process 72924
create config/initializers/wicked_pdf.rb
创建data models
- 生成Invoice模型
rails generate model Invoice from_full_name from_address from_email from_phone to_full_name to_address to_email to_phone status discount:decimal vat:decimal
修改app/models/invoice.rb
文件
# file: rails-generate-pdf/app/models/invoice.rb
class Invoice < ApplicationRecord
has_many :invoice_items, dependent: :destroy
STATUS_CLASS = {
:draft => "badge badge-secondary",
:sent => "badge badge-primary",
:paid => "badge badge-success"
}
def subtotal
self.invoice_items.map { |item| item.qty * item.price }.sum
end
def discount_calculated
subtotal * (self.discount / 100.0)
end
def vat_calculated
(subtotal - discount_calculated) * (self.vat / 100.0)
end
def total
subtotal - discount_calculated + vat_calculated
end
def status_class
STATUS_CLASS[self.status.to_sym]
end
end
- 生成InvoiceItem模型
rails generate model InvoiceItem name description price:decimal qty:integer invoice:references
创建数据库
rails db:create
ralis db:migrate
生成测试数据
修改db/seeds.rb,太长就不贴了.
rails db:seed
创建controller
rails generate controller Invoices index show
修改app/controllers/invoices_controller.rb
# file: rails-generate-pdf/app/controllers/invoices_controller.rb
class InvoicesController < ApplicationController
def index
@invoices = scope
end
def show
@invoice = scope.find(params[:id])
respond_to do |format|
format.html
format.pdf do
render pdf: "Invoice No. #{@invoice.id}",
page_size: 'A4',
template: "invoices/show.html.erb",
layout: "pdf.html",
orientation: "Landscape",
lowquality: true,
zoom: 1,
dpi: 75
end
end
end
private
def scope
::Invoice.all.includes(:invoice_items)
end
end
这里我们在show方法实现了两种渲染方式(html和pdf), 只要访问.pdf
后缀,我们就可以渲染pdf了.
可以试试这两个链接:
- http://rails-generate-pdf.herokuapp.com/invoices/1
- http://rails-generate-pdf.herokuapp.com/invoices/1.pdf
配置参数
- layout布局
pdf.html
:app/views/layouts/pdf.html.erb
- template文件
invoices/show.html.erb
- page-size: A4纸
- orientation: Landscape或者Portrait(默认)
- lowquality: 降低pdf质量,压缩文件大小
- dpi: 修改dpi系数
- zoom: 缩放比例
添加路由
修改app/config/routes.rb
# file: rails-generate-pdf/app/config/routes.rb
Rails.application.routes.draw do
root to: 'invoices#index'
resources :invoices, only: [:index, :show]
end
修改layout文件
# file: rails-generate-pdf/views/layous/pdf.html.erb
<!DOCTYPE html>
<html>
<head>
<title>PDFs - Ruby on Rails</title>
<%= wicked_pdf_stylesheet_link_tag "invoice" %>
</head>
<body>
<%= yield %>
</body>
</html>
这里的helper
函数wicked_pdf_stylesheet_link_tag
会添加css文件 app/assets/stylesheets/invoice.scss
修改show.html.erb
参照这里
添加css文件
修改config/initializers/asset.rb
# file: config/initializers/assets.rb
Rails.application.config.assets.precompile += %w( invoice.scss )
你需要的css和js文件都需要在这里添加
添加下载pdf按钮
<%= link_to 'DOWNLOAD PDF', invoice_path(@invoice, format: :pdf) %>
如果没有问题,就可以看到生成的invoice的pdf文件了
添加footer信息
比较麻烦的地方是footer信息也能使用html来定制样式,根据数据动态生成内容,并且能打印页数.
比较全的配置文件参考这里
推荐使用html方式
这样你就可以更加灵活的控制文字样式和位置了,包括参数传递和页码打印
写好html后,就可以配置参数了,像这样:
footer: {
content: render_to_string(
'template/footer.html',
layout: false
)
}
注意
不要用复杂的flex布局,可能会不支持!
PREVIOUS突然想起大学时光