能够以 pdf 格式下载数据是您在构建 Web 应用程序时会遇到的常见要求。在 Rails 中有不同的方法可以实现这一点。我们将研究用于生成 pdf 文档的两种主要方法:使用 Ruby 使用 DSL 定义和样式化文档,或者使用将 HTML 转换为 PDF 的库。

今天我们将重点介绍三种流行的宝石:

大虾(使用 DSL 方法)

PDFKit(使用生成器)

邪恶的 PDF(也使用生成器)。

HTML 到 PDF 或 Ruby 生成?

这个问题的答案通常取决于偏好和项目要求。HTML 到 PDF 可以更快,特别是如果您已经有一个视图来显示您想要在 PDF 中显示的内容。在这种情况下,您不必编写更多代码来生成 PDF 文件。但是,这种方法会使控制文档的布局变得更加困难,尤其是在处理多页文档时。内容往往会被截断并在页面之间拆分。确实,通过一些 CSS 样式,您可以对分页符进行一些控制。但是,对于跨越多个页面并包含可变长度内容、页眉和页脚的更复杂的 PDF 文档,将难以控制每个页面的呈现方式。在这些情况下,使用 Prawn 可能更有意义。

使用像 Prawn 这样的库,您必须使用 Prawn 的 DSL 自己完成所有内容样式和定位。这里的优点是可以更好地控制事物的显示方式和页面中断的位置。

我们将为如下所示的网页生成一个 PDF 文件,其中包含一些静态文本、一张图像和一些数据库记录的表格。

要使用 Prawn,请将 gem 包含在 Gemfile 中并运行bundle install

gem 'prawn' 

'prawn'在文件中注册 PDF mime 类型
config/initializers/mime_types.rb。

Mime::Type.register "application/pdf", :pdf

现在我们需要设置控制器动作来响应对 PDF 格式的请求。

对于我的 Products 控制器,我有一个index要修改的操作,如图所示。

class ProductsController < ApplicationControllerdef index@products = Product.allrespond_to do |format|format.htmlformat.pdf dopdf = Prawn::Document.newsend_data pdf.render, filename: 'report.pdf', type: 'application/pdf'endendend
end 

当.pdf附加到特定 url 的末尾时,上面将生成一个没有内容的 PDF 文件。就我而言
http://localhost:3000/products.pdf。

为了从控制器中分离出 pdf 生成代码,我创建了一个app/pdfs目录并在文件中添加了一个新类app/pdfs/report_pdf.rb。

我更改了控制器代码以使用新类。

class ProductsController < ApplicationControllerdef index@products = Product.allrespond_to do |format|format.htmlformat.pdf dopdf = ReportPdf.new(@products)send_data pdf.render, filename: 'report.pdf', type: 'application/pdf'endendend
end 

下面的代码显示了如何生成上面显示的网页的 PDF。我已经评论它以显示我在做什么。

class ReportPdf < Prawn::Documentdef initialize(products)super()@products = productsheadertext_contenttable_contentenddef header#This inserts an image in the pdf file and sets the size of the imageimage "#{Rails.root}/app/assets/images/header.png", width: 530, height: 150enddef text_content# The cursor for inserting content starts on the top left of the page. Here we move it down a little to create more space between the text and the image inserted abovey_position = cursor - 50# The bounding_box takes the x and y coordinates for positioning its content and some options to style itbounding_box([0, y_position], :width => 270, :height => 300) dotext "Lorem ipsum", size: 15, style: :boldtext "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse interdum semper placerat. Aenean mattis fringilla risus ut fermentum. Fusce posuere dictum venenatis. Aliquam id tincidunt ante, eu pretium eros. Sed eget risus a nisl aliquet scelerisque sit amet id nisi. Praesent porta molestie ipsum, ac commodo erat hendrerit nec. Nullam interdum ipsum a quam euismod, at consequat libero bibendum. Nam at nulla fermentum, congue lectus ut, pulvinar nisl. Curabitur consectetur quis libero id laoreet. Fusce dictum metus et orci pretium, vel imperdiet est viverra. Morbi vitae libero in tortor mattis commodo. Ut sodales libero erat, at gravida enim rhoncus ut."endbounding_box([300, y_position], :width => 270, :height => 300) dotext "Duis vel", size: 15, style: :boldtext "Duis vel tortor elementum, ultrices tortor vel, accumsan dui. Nullam in dolor rutrum, gravida turpis eu, vestibulum lectus. Pellentesque aliquet dignissim justo ut fringilla. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut venenatis massa non eros venenatis aliquet. Suspendisse potenti. Mauris sed tincidunt mauris, et vulputate risus. Aliquam eget nibh at erat dignissim aliquam non et risus. Fusce mattis neque id diam pulvinar, fermentum luctus enim porttitor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."endenddef table_content# This makes a call to product_rows and gets back an array of data that will populate the columns and rows of a table# I then included some styling to include a header and make its text bold. I made the row background colors alternate between grey and white# Then I set the table column widthstable product_rows dorow(0).font_style = :boldself.header = trueself.row_colors = ['DDDDDD', 'FFFFFF']self.column_widths = [40, 300, 200]endenddef product_rows[['#', 'Name', 'Price']] +@products.map do |product|[product.id, product.name, product.price]endend
end 

有关可用的 PDF 格式规则的更多信息,请查看 Prawn手册。

PDFKit

对于 PDFKit,首先在 Gemfile 中包含 gem

gem 'pdfkit' 

并运行bundle install

您可以通过指向 html 文件或网站来生成 PDF 文档,如下所示。

# From https://github.com/pdfkit/pdfkit#usage# PDFKit.new takes the HTML and any options for wkhtmltopdf
# run `wkhtmltopdf --extended-help` for a full list of options
kit = PDFKit.new(html, :page_size => 'Letter')
kit.stylesheets << '/path/to/css/file'# Get an inline PDF
pdf = kit.to_pdf# Save the PDF to a file
file = kit.to_file('/path/to/save/pdf')# PDFKit.new can optionally accept a URL or a File.
# Stylesheets can not be added when source is provided as a URL of File.
kit = PDFKit.new('http://google.com')
kit = PDFKit.new(File.new('/path/to/html'))# Add any kind of option through meta tags
PDFKit.new('<html><head><meta name="pdfkit-page_size" content="Letter"')
PDFKit.new('<html><head><meta name="pdfkit-cookie cookie_name1" content="cookie_value1"')
PDFKit.new('<html><head><meta name="pdfkit-cookie cookie_name2" content="cookie_value2"') 

您还可以使用中间件解决方案,允许用户通过附加.pdf到 URL 的末尾来生成网站上任何页面的 PDF。这就是我们将在这里使用的。

要添加中间件,请在文件中包含以下内容/config/application.rb(适用于 Rails 版本 3 及更高版本)。

module RailsPdfclass Application < Rails::Applicationconfig.middleware.use PDFKit::Middleware...end
end 

重新启动服务器,导航到一个页面,然后添加.pdf到 URL 的末尾。您将获得该网页的 PDF 版本。

也可以使用链接将页面下载为 PDF 文件。:

= link_to 'Download Report', products_path(format: 'pdf') 

要排除 pdf 文件中的链接,请在标签中添加 id 或类名,并在 CSS 中为其设置显示属性。

@media print {.pdf_exclude {display: none;}
}

一些注意事项

一世

如果wkhtmltopdf您的系统上没有安装,那么您将收到类似的错误,如下所示

PDFKit::NoExecutableError in ProductsController#indexNo wkhtmltopdf executable found at >> Please install wkhtmltopdf - https://github.com/pdfkit/PDFKit/wiki/Installing-WKHTMLTOPDF 

有关如何安装的说明,请wkhtmltopdf查看此wiki 页面。

安装二进制文件的另一个选项wkhtmltopdf是通过 gem wkhtmltopdf-binary。将它添加到您的 Gemfile 并运行bundle install

gem 'wkhtmltopdf-binary' 

伊尔

如果内容在您不希望的地方被截断,例如表格被分成两页,您可以在表格呈现之前指定分页符,以便它出现在自己的页面上。

@media print {.page-break {display: block;page-break-before: always;}
} 

wkhtmltopdf不能很好地处理您可能正在使用的任何外部文件(图像、样式表、javascript)的相对 URL。如果您使用常规标签之类的stylesheet_link_tag并尝试生成文档,wkhtmltopdf则在加载资产时会挂起。使用资产的绝对路径(文件路径或包含域的 url)可以解决这个问题。

另一种可能的解决方案是使用内联样式。例如,stylesheet_link_tag您可以使用以下标签代替标签:

<style type="text/css"><%= Rails.application.assets.find_asset('application.css').to_s.html_safe %>
</style> 

对于出现在网页上但您不希望在 pdf 文档中显示的任何内容(例如上例中的“下载 PDF”链接),只需将其标记并使用 CSS 隐藏即可。

@media print {.hide_in_pdf {display:none;}
} 

邪恶的PDF

要使用 Wicked PDF,首先安装wkhtmltopdf。或者,您可以通过将wkhtmltopdf-binarygem 包含在 Gemfile 中来使用它。

添加wicked_pdf到您的 Gemfile 并运行bundle install.

gem 'wicked_pdf' 

在中注册 PDF mime 类型
config/initializers/mime_types.rb

Mime::Type.register "application/pdf", :pdf 

与 PDFKit 一样,Wicked PDF 带有一个中间件,允许用户通过附加.pdf到 URL 来获取您网站上任何页面的 PDF 视图。这是通过添加config.middleware.use WickedPdf::Middleware到/config/application.rb文件来实现的。我不会在这里使用中间件。相反,我将为 PDF 创建模板和布局文件,并修改我的控制器操作以处理 PDF 格式的请求。

我修改了ProductsController如图所示。在这里,我指定了 PDF 文件的名称和用于生成它的布局。有关您可以使用的可用选项的更多信息,请查看自述文件。

class ProductsController < ApplicationControllerdef index@products = Product.allrespond_to do |format|format.htmlformat.pdf dorender :pdf => "report", :layout => 'pdf.html.haml'endendend
end 

这是布局文件
app/views/layouts/pdf.html.haml。

!!!
%html%head%title RailsWickedPdf= wicked_pdf_stylesheet_link_tag    "application", :media => "all"= wicked_pdf_javascript_include_tag "application"= csrf_meta_tags%body= yield 

和模板文件
app/views/products/index.pdf.haml。

.container.row= wicked_pdf_image_tag('header.png').row.col-xs-6%h3Lorem ipsum%pLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse interdum semper placerat. Aenean mattis fringilla risus ut fermentum. Fusce posuere dictum venenatis. Aliquam id tincidunt ante, eu pretium eros. Sed eget risus a nisl aliquet scelerisque sit amet id nisi. Praesent porta molestie ipsum, ac commodo erat hendrerit nec. Nullam interdum ipsum a quam euismod, at consequat libero bibendum. Nam at nulla fermentum, congue lectus ut, pulvinar nisl. Curabitur consectetur quis libero id laoreet. Fusce dictum metus et orci pretium, vel imperdiet est viverra. Morbi vitae libero in tortor mattis commodo. Ut sodales libero erat, at gravida enim rhoncus ut..col-xs-6%h3Duis vel%pDuis vel tortor elementum, ultrices tortor vel, accumsan dui. Nullam in dolor rutrum, gravida turpis eu, vestibulum lectus. Pellentesque aliquet dignissim justo ut fringilla. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut venenatis massa non eros venenatis aliquet. Suspendisse potenti. Mauris sed tincidunt mauris, et vulputate risus. Aliquam eget nibh at erat dignissim aliquam non et risus. Fusce mattis neque id diam pulvinar, fermentum luctus enim porttitor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos..row%table.table.table-striped%thead%th #%th Product%th Price%tbody- @products.each do |product|%tr%td= product.id%td= product.name%td= product.price 

您会注意到在上面的两个文件中,我使用了以下 Wicked 助手。

wicked_pdf_stylesheet_link_tag
wicked_pdf_javascript_include_tag
wicked_pdf_image_tag 

如果您使用外部文件,这些是必需的。wkhtmltopdf在 Rails 应用程序之外运行,因此您链接到的任何文件都必须包含绝对地址。

当请求 PDF 时,使用常规stylesheet_link_tag和标签将导致应用程序挂起。此外,如果使用常规标签javascript_include_tag,则不会呈现图像。img

结论

我们已经研究了三种不同的 PDF 生成方法。决定使用哪个取决于多种因素,包括您的首选语言(HTML 或 DSL)、使用一个库而不是另一个库完成任务的难易程度、PDF 文档格式的复杂性以及其他考虑因素。我希望这篇文章能帮助你做出这个决定,需要更多的学习资料+关注博主私信博主免费获取更多的学习资料

8年老程序员帮我们已经在 Rails 中生成 PDF相关推荐

  1. 《程序员》杂志2011年第5期.pdf 下载链接 首发。

    ========================================================= csdn<程序员>杂志2011年第5期.pdf ============ ...

  2. 好程序员技术文档HTML5开发中的javascript闭包

    好程序员技术文档HTML5开发中的javascript闭包,事实上,通过使用闭包,我们可以做很多事情.比如模拟面向对象的代码风格;更优雅,更简洁的表达出代码;在某些方面提升代码的执行效率,同时避免对命 ...

  3. 引入js_好程序员web前端教程分享js中的模块化一

    好程序员web前端教程分享js中的模块化一:我们知道最常见的模块化方案有CommonJS.AMD.CMD.ES6,AMD规范一般用于浏览器,异步的,因为模块加载是异步的,js解释是同步的,所以有时候导 ...

  4. python程序员专用壁纸_程序员炫技必备:用Python生成马赛克画!(文末附源码)...

    原标题:程序员炫技必备:用Python生成马赛克画!(文末附源码) 源 | Python与数据分析文 | 强哥 大家知道马赛克画是什么吗?不是动作片里的马赛克哦~~ 马赛克画是一张由小图拼成的大图,本 ...

  5. 一些值得程序员收藏的网站(收集中···)

    一些值得程序员收藏的网站(收集中···) 冰霜之地:https://halfrost.com/(gitHub:https://github.com/halfrost/Halfrost-Field) 风 ...

  6. 好程序员web前端教程分享js中的模块化一

    好程序员web前端教程分享js中的模块化一:我们知道最常见的模块化方案有CommonJS.AMD.CMD.ES6,AMD规范一般用于浏览器,异步的,因为模块加载是异步的,js解释是同步的,所以有时候导 ...

  7. 15 年老程序员自述:8 个影响我职业生涯的重要技能

    作者 | 崮德 来源 | 阿里巴巴中间件(ID:Aliware_2018) 我是上个世纪, 1998 年考入浙江大学的,读的是电子工程,但是在大学期间情不自禁爱地上了计算机,当时在学校学的是汇编语言, ...

  8. 程序员月入好几万?10年老程序员告诉你真实的情况!!

    从业一开始,加班就是常态 很多人都说这个程序员的工资比较高,其实这种背后的心酸,其实很少人知道. 就拿我来说吧其实我工作了有七八年,记得刚工作那个时候,天天加班的话,就是白天睡觉,晚上加班,当时其实也 ...

  9. 对前端、后端和全栈感兴趣的人,建议都看看!18年老程序员给你指点迷津

    各位读者朋友们好,我是龙叔,1名退休老码农,如果从工作算起的话我的码龄有18年,今天我来对前端.后端.全栈这3个方面分享一下我的见解,对于准备学编程或者刚学编程不久的小友,让我来给你们拨开迷雾. 看完 ...

最新文章

  1. 原来颓废也是需要力气的
  2. 行业洞察驱动安全防御严峻安全挑战迎刃而解
  3. IntelliJ idea 缓存和索引 清理方法
  4. Hive学习笔记 —— Hive的体系结构
  5. tensorflow就该这么学--5( 神经网络基础)
  6. 【BZOJ】【1041】【HAOI2008】圆周上的点
  7. [css] 怎么才能让图文不可复制?
  8. java sha1_java使用SHA1加密算法详解
  9. python输入逗号分隔_命令行Python逗号分隔的用户输入int值
  10. mysql一张表可以用吗_MySQL表操作
  11. 微型计算机常常采用三种线结构,中北大学微机原理习题册终极版考试必备
  12. HCIE-Security Day25:DSPN+NHRP+Mgre:实验(四)配置shortcut方式DSPN(OSPF路由协议)
  13. redis mysql 视图_Redis 可以用来做数据库吗?
  14. 《时空幻境》Braid.v1.010.r2-RES-patch
  15. nsis升级包_NSIS v3.2.0.1-简易封包工具
  16. 草图大师2021安装教程(超详细图文教程)
  17. [转载]铁路订票系统的简单设计
  18. 享受科研,心怀远方:青年学者刘元玮的成长之路
  19. Day739.GEO经纬度数据结构自定义数据结构 -Redis 核心技术与实战
  20. 详解GAN代码之搭建并详解CGAN代码

热门文章

  1. VMware NSX-V与NSX-T的比较(转载翻译)
  2. ios输入框的坑(软键盘弹出不灵敏、输入法影响弹出高度)
  3. VHD(虚拟磁盘)文件修复方法
  4. 人口数据可视化,深圳是人口密度最高的城市,东莞上海位居二三名
  5. 中通打印助手-查快递
  6. 分享我刚刚收集的电脑桌面主题08-23整理分享!
  7. freebsd安装sudo
  8. IP协议 — IP协议头部
  9. 用python写的简易黑客游戏
  10. IfcOpenShell在Ubuntu和Windows下的配置