使用Rails从HTML生成PDF

有很多方法可以在Ruby和Rails中生成PDF。 您可能已经熟悉HTML和CSS,因此我们将使用PDFKit从标准Rails视图和样式代码使用HTML生成PDF文件。

PDFKit简介

在内部,PDFKit使用wkhtmltopdf(WebKit HTML到PDF),该引擎将采用HTML和CSS,使用WebKit对其进行呈现,然后将其输出为高质量的PDF。

首先,在计算机上安装wkhtmltopdf。 您可以下载二进制文件或从Mac上的Brew或首选的Linux系统信息库中安装。

您还需要安装pdfkit gem ,然后运行以下Ruby代码以生成带有文本“ Hello Envato!”的PDF。

require "pdfkit"

kit = PDFKit.new(<<-HTML)
  <p>Hello Envato!</p>
HTML

kit.to_file("hello.pdf")

您应该有一个名为hello.pdf的新文件,其文本位于顶部。

生成的PDF的示例

PDFKit还允许您从URL生成PDF。 如果要从Google主页生成PDF,可以运行:

require "pdfkit"

PDFKit.new('https://www.google.com', :page_size => 'A3').to_file('google.pdf')

如您所见,我指定的是page_size默认情况下,使用A4。 您可以在此处查看选项的完整列表。

PDF格式的Google主页示例

使用CSS样式化PDF

之前我提到过,我们将使用HTML和CSS生成PDF文件。 在此示例中,我添加了一些CSS来为示例发票HTML样式设置,如您所见:

require "pdfkit"

kit = PDFKit.new(<<-HTML)
  <style>
    * {
      color: grey;
    }
    h1 {
      text-align: center;
      color: black;
      margin-bottom: 100px;
    }
    .notes {
      margin-top: 100px;
    }

    table {
      width: 100%;
    }
    th {
      text-align: left;
      color: black;
      padding-bottom: 15px;
    }
  </style>

  <h1>Envato Invoice</h1>

  <table>
    <thead>
        <tr>
          <th>Description</th>
          <th>Price</th>
        </tr>
      </thead>
      <tbody>
          <tr>
            <td>Monthly Subscription to Tuts+</td>
            <td>$15</td>
          </tr>
      </tbody>
  </table>

  <div class="notes">
    <p><strong>Notes:</strong> This invoice was paid on the 23rd of March 2016 using your credit card ending on 1234.</p>
  </div>
HTML

kit.to_file("envato_invoice.pdf")

如果运行此脚本,将生成文件envato_invoice.pdf 。 这张照片显示了发票示例的结果:

Envato发票PDF的示例

如您所见,如果您已经熟悉HTML和CSS,则PDFKit非常易于使用。 您可以根据需要继续自定义或样式化该文档。

从Rails应用程序使用PDFKit

现在让我们看一下如何在Rails应用程序的上下文中使用PDFKit,以便我们可以使用模型中的数据动态生成PDF文件。 在本节中,我们将构建一个简单的Rails应用程序,以动态生成先前的“ Envato发票”。 首先创建一个新的Rails应用程序并添加三个模型:

$ rails new envato_invoices
$ cd envato_invoices

$ rails generate model invoice date:date client notes
$ rails generate model line_item description price:float invoice:references

$ rake db:migrate

现在,我们必须向数据库中添加一些示例数据。 将此代码段添加到db/seeds.rb

line_items = LineItem.create([
    { description: 'Tuts+ Subscription April 2016', price: 15.0 }, 
    { description: 'Ruby eBook', price: 9.90} ])
Invoice.create(
    client: 'Pedro Alonso', 
    total: 24.90, 
    line_items: line_items, 
    date: Date.new(2016, 4, 1))

在终端中运行rake db:seed ,将示例发票添加到数据库中。

我们也有兴趣在我们的应用程序中生成发票清单和一张发票的详细信息,因此使用rails生成器,运行rails generate controller Invoices index show清单可以rails generate controller Invoices index show创建控制器和视图。

app / controllers / invoices_controller.rb

class InvoicesController < ApplicationController
  def index
    @invoices = Invoice.all
  end

  def show
    @invoice = Invoice.find(params[:id])
  end
end

app / views / invoices / index.html.erb

<h1>Invoices</h1>
<ul>
  <% @invoices.each do |invoice| %>
  <li>
    <%= link_to "#{invoice.id} - #{invoice.client} - #{invoice.date.strftime("%B %d, %Y")} ", invoice_path(invoice) %>
  </li>
  <% end %>
</ul>

我们需要修改Rails路由以默认情况下重定向到InvoicesController ,因此请编辑config/routes.rb

Rails.application.routes.draw do
  root to: 'invoices#index'

  resources :invoices, only: [:index, :show]
end

启动rails server ,然后导航到localhost:3000以查看发票列表:

发票清单

app / views / invoices / show.html.erb

<div class="invoice">
  <h1>Envato Invoice</h1>

  <h3>To: <%= @invoice.client %></h3>
  <h3>Date: <%= @invoice.date.strftime("%B %d, %Y") %></h3>

  <table>
    <thead>
        <tr>
          <th>Description</th>
          <th>Price</th>
        </tr>
      </thead>
      <tbody>
        <% @invoice.line_items.each do |line_item| %>
          <tr>
            <td><%= line_item.description %></td>
            <td><%= number_to_currency(line_item.price) %></td>
          </tr>
        <% end %>
        <tr class="total">
          <td style="text-align: right">Total: </td>
          <td><%= number_to_currency(@invoice.total) %></span></td>
        </tr>
      </tbody>
  </table>

  <% if @invoice.notes %>
  <div class="notes">
    <p><strong>Notes:</strong> <%= @invoice.notes %></p>
  </div>
  <% end %>
</div>

此发票详细信息页面CSS已移至app / assets / stylesheets / application.scss

.invoice {
  width: 700px;
  max-width: 700px;
  border: 1px solid grey;
  margin: 50px;
  padding: 50px;

  h1 {
    text-align: center;
    margin-bottom: 100px;
  }
  .notes {
    margin-top: 100px;
  }

  table {
    width: 90%;
    text-align: left;
  }
  th {
    padding-bottom: 15px;
  }

  .total td {
    font-size: 20px;
    font-weight: bold;
    padding-top: 25px;
  }
}

然后,在主列表页面上单击发票时,您将看到详细信息:

发票检视

至此,我们准备将功能添加到Rails应用程序中,以查看或下载PDF发票。

用于处理PDF渲染的InvoicePdf类

为了将发票从Rails应用程序呈现为PDF,我们需要在Gemfile中添加三个gem: PDFKitrender_anywhere和wkhtmltopdf-binary。 默认情况下,rails只允许您从控制器渲染模板,但是通过使用render_anywhere ,我们可以从模型或后台作业渲染模板。

gem 'pdfkit'
gem 'render_anywhere'
gem 'wkhtmltopdf-binary'

为了避免过多地污染我们的控制器,我将在app/models文件夹内创建一个新的InvoicePdf类,以包装逻辑以生成PDF。

require "render_anywhere"

class InvoicePdf
  include RenderAnywhere

  def initialize(invoice)
    @invoice = invoice
  end

  def to_pdf
    kit = PDFKit.new(as_html, page_size: 'A4')
    kit.to_file("#{Rails.root}/public/invoice.pdf")
  end

  def filename
    "Invoice #{invoice.id}.pdf"
  end

  private

    attr_reader :invoice

    def as_html
      render template: "invoices/pdf", layout: "invoice_pdf", locals: { invoice: invoice }
    end
end

此类仅将发票呈现为类构造函数上的参数。 私有方法as_html   阅读我们用于生成需要呈现为PDFHTML的视图模板invoices/pdflayout_pdf 。 最后,方法to_pdf   正在使用PDFKit将PDF文件保存在rails公用文件夹中。

可能您想在实际应用程序中生成一个动态名称,以免PDF文件被意外覆盖。 您可能也希望将文件存储在AWS S3或私有文件夹上,但这超出了本教程的范围。

/app/views/invoices/pdf.html.erb
<div class="invoice">
  <h1>Envato Invoice</h1>

  <h3>To: <%= invoice.client %></h3>
  <h3>Date: <%= invoice.date.strftime("%B %d, %Y") %></h3>

  <table>
    <thead>
        <tr>
          <th>Description</th>
          <th>Price</th>
        </tr>
      </thead>
      <tbody>
        <% invoice.line_items.each do |line_item| %>
          <tr>
            <td><%= line_item.description %></td>
            <td><%= number_to_currency(line_item.price) %></td>
          </tr>
        <% end %>
        <tr class="total">
          <td style="text-align: right">Total: </td>
          <td><%= number_to_currency(invoice.total) %></span></td>
        </tr>
      </tbody>
  </table>

  <% if invoice.notes %>
  <div class="notes">
    <p><strong>Notes:</strong> <%= invoice.notes %></p>
  </div>
  <% end %>
</div>
/app/views/layouts/invoice_pdf.erb
<!DOCTYPE html>
<html>
<head>
  <title>Envato Invoices</title>
  <style>
    <%= Rails.application.assets.find_asset('application.scss').to_s %>
  </style>
</head>
<body>
  <%= yield %>
</body>
</html>

在此布局文件中要注意的一件事是,我们正在布局中渲染样式。 如果我们以这种方式渲染样式,WkHtmlToPdf的效果会更好。

下载控制器以呈现PDF发票

此时,我们需要一个路由和控制器,调用类InvoicePdf将PDF文件发送到浏览器,因此请编辑config/routes.rb以添加嵌套资源:

Rails.application.routes.draw do
  root to: "invoices#index"

  resources :invoices, only: [:index, :show] do
    resource :download, only: [:show]
  end
end

如果运行rake routes ,我们将在应用程序中看到可用的路由列表:

Prefix Verb URI Pattern                              Controller#Action
            root GET  /                                        invoices#index
invoice_download GET  /invoices/:invoice_id/download(.:format) downloads#show
        invoices GET  /invoices(.:format)                      invoices#index
         invoice GET  /invoices/:id(.:format)                  invoices#show

添加app/controllers/downloads_controller.rb

class DownloadsController < ApplicationController

  def show
    respond_to do |format|
      format.pdf { send_invoice_pdf }
    end
  end

  private

  def invoice_pdf
    invoice = Invoice.find(params[:invoice_id])
    InvoicePdf.new(invoice)
  end

  def send_invoice_pdf
    send_file invoice_pdf.to_pdf,
      filename: invoice_pdf.filename,
      type: "application/pdf",
      disposition: "inline"
  end
end

如您所见,当请求要求提供PDF文件时,方法send_invoice_pdf 正在处理请求。 方法invoice_pdf只是通过id从数据库中查找发票,并创建InvoicePdf的实例。 然后send_invoice_pdf只是调用方法to_pdf ,将生成的PDF文件发送到浏览器。

需要注意的一件事是,我们将参数disposition: "inline"传递给send_file 。 此参数会将文件发送到浏览器,并显示出来。 如果要强制下载文件,则需要通过disposition: "attachment"

在您的发票显示模板app/views/invoices/show.html.erb添加一个下载按钮:

<%= link_to "Download PDF",
    invoice_download_path(@invoice, format: "pdf"),
    target: "_blank",
    class: "download" %>

运行应用程序,导航到发票详细信息,单击下载,然后将打开一个新选项卡,显示PDF发票。

动态生成发票视图PDF

在开发中将PDF呈现为HTML

当您处理PDF标记时,有时每次要测试更改都必须生成PDF有时会很慢。 因此,能够查看将要转换为纯HTMLHTML确实有用。 我们只需要编辑/app/controllers/downloads_controller.rb

class DownloadsController < ApplicationController

  def show
    respond_to do |format|
      format.pdf { send_invoice_pdf }

      if Rails.env.development?
        format.html { render_sample_html }
      end
    end
  end

  private

  def invoice
    Invoice.find(params[:invoice_id])
  end

  def invoice_pdf
    InvoicePdf.new(invoice)
  end

  def send_invoice_pdf
    send_file invoice_pdf.to_pdf,
      filename: invoice_pdf.filename,
      type: "application/pdf",
      disposition: "inline"
  end

  def render_sample_html
    render template: "invoices/pdf", layout: "invoice_pdf", locals: { invoice: invoice }
  end
end

现在, show方法还在开发模式下响应HTML请求。 PDF发票的路由应类似于http:// localhost:3000 / invoices / 1 / download.pdf 。 如果将其更改为http:// localhost:3000 / invoices / 1 / download.html ,则将使用用于生成PDF的标记在HTML中看到发票。

给定上面的代码,假设您熟悉Ruby语言和Rails框架,那么使用Ruby on Rails生成PDF文件非常简单。 也许整个过程最好的方面是,您不必学习任何新的标记语言或有关PDF生成的细节。

希望本教程对您有所帮助。 请在评论中留下任何问题,评论和反馈,我很乐意跟进。

翻译自: https://code.tutsplus.com/tutorials/generating-pdfs-from-html-with-rails--cms-22918

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值