Wicked_pdf - LPC-Ltda/Ruby-on-Rails GitHub Wiki

Un plugin de generación de PDF para Ruby on Rails

Wicked PDF usa la utilidad de shell wkhtmltopdf para servir un archivo PDF a un usuario desde HTML. En otras palabras, en lugar de tratar con un DSL de generación de PDF de algún tipo, simplemente escribe una vista HTML como lo harías normalmente, y luego dejas que Wicked PDF se encargue de las cosas difíciles.

Se ha verificado que Wicked PDF funciona en las versiones de Ruby 1.8.7 a 2.3; Rails 2 a 5.0

Instalación

Agregue esto a su Gemfile y ejecute bundle install:

gem 'wicked_pdf'

Luego cree el inicializador con:

rails generate wicked_pdf

Es posible que también necesite agregar

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

a config/initializers/mime_types.rb en versiones antiguas de Rails.

Ya que wicked_pdf es un wrapper para wkhtmltopdf, usted necesitará instalarlo tambien.

La forma más sencilla de instalar todos los binarios en la mayoría de los sistemas Linux u OSX es a través de la gema wkhtmltopdf-binary. Las compilaciones para otros sistemas están disponibles aquí. Para instalar esa gema, agregue esto:

gem 'wkhtmltopdf-binary'

a su Gemfile y ejecute bundle install

Esta gema actualmente instala la versión 0.12.x de wkhtmltopdf. Algunas de las opciones enumeradas a continuación son específicas 0.9 o inferiores, y otras son para 0.12 y superiores.

Puede ver qué banderas son compatibles con la versión actual en el manual generado automáticamente por wkhtmltopdf

Si su ejecutable wkhtmltopdf no está en la ruta de su servidor web, puede configurarlo en un inicializador:

WickedPdf.config = {
  exe_path: '/usr/local/bin/wkhtmltopdf'
}

Para obtener más información sobre wkhtmltopdf, consulte la página de inicio del proyecto.

Uso básico

class ThingsController < ApplicationController
  def show
    respond_to do |format|
      format.html
      format.pdf do
        render pdf: "file_name"   # Excluding ".pdf" extension.
      end
    end
  end
end

COmdiciones de uso - Importante!

El binario wkhtmltopdf se ejecuta fuera de su aplicación Rails; por lo tanto, sus diseños normales no funcionarán. Si planea usar cualquier archivo CSS, JavaScript o imagen, debe modificar su diseño para que proporcione una referencia absoluta a estos archivos. La mejor opción para Rails sin el assets pipeline es utilizar los helpers wicked_pdf_stylesheet_link_tag, wicked_pdf_image_tag y wicked_pdf_javascript_include_tag o ir directamente a un CDN (Red de distribución de contenido) para bibliotecas populares como jQuery.

helpers wicked_pdf

<!doctype html>
<html>
  <head>
    <meta charset='utf-8' />
    <%= wicked_pdf_stylesheet_link_tag "pdf" -%>
    <%= wicked_pdf_javascript_include_tag "number_pages" %>
  </head>
  <body onload='number_pages'>
    <div id="header">
      <%= wicked_pdf_image_tag 'mysite.jpg' %>
    </div>
    <div id="content">
      <%= yield %>
    </div>
  </body>
</html>

Usar wicked_pdf_helpers con asset pipeline levanta el error Asset names passed to helpers should not include the "/assets/" prefix. Para evitar esto, usted puede usar wicked_pdf_asset_base64 con los helpers Rails normales, pero tenga cuidado porque esto codificará su contenido en base64 y lo insetará en la página. Esto es muy rápido para assets pequeños pero puede tomar mucho tiempo para los grandes.

<!doctype html>
<html>
  <head>
    <meta charset='utf-8' />
    <%= stylesheet_link_tag wicked_pdf_asset_base64("pdf") %>
    <%= javascript_include_tag wicked_pdf_asset_base64("number_pages") %>

  </head>
  <body onload='number_pages'>
    <div id="header">
      <%= image_tag wicked_pdf_asset_base64('mysite.jpg') %>
    </div>
    <div id="content">
      <%= yield %>
    </div>
  </body>
</html>

Uso del asset pipeline

Es mejor precompilar los activos utilizados en las vistas de PDF. Esto ayudará a evitar problemas a la hora de implementar, ya que Rails sirve los archivos de assets de manera diferente entre el desarrollo y la producción (config.assets.compile = false), lo que puede hacer que parezca que sus archivos PDF funcionan en el desarrollo, pero no pueden cargar activos en la producción.

config.assets.precompile += ['blueprint/screen.css', 'pdf.css', 'jquery.ui.datepicker.js', 'pdf.js', ...etc...]

Referencia CDN

En este caso, puede usar los ayudantes de Rails estándar y apuntar al CDN actual para cualquier marco que esté usando. Para jQuery, se vería algo así, dadas las versiones actuales en el momento de escribir este artículo.

    <!doctype html>
    <html>
      <head>
        <%= javascript_include_tag "http://code.jquery.com/jquery-1.10.0.min.js" %>
        <%= javascript_include_tag "http://code.jquery.com/ui/1.10.3/jquery-ui.min.js" %>

Uso avanzado con todas las opciones disponibles

class ThingsController < ApplicationController
  def show
    respond_to do |format|
      format.html
      format.pdf do
        render pdf:                            'file_name',
               disposition:                    'attachment',                 # default 'inline'
               template:                       'things/show',
               file:                           "#{Rails.root}/files/foo.erb"
               layout:                         'pdf',                        # for a pdf.pdf.erb file
               wkhtmltopdf:                    '/usr/local/bin/wkhtmltopdf', # path to binary
               show_as_html:                   params.key?('debug'),         # allow debugging based on url param
               orientation:                    'Landscape',                  # default Portrait
               page_size:                      'A4, Letter, ...',            # default A4
               page_height:                    NUMBER,
               page_width:                     NUMBER,
               save_to_file:                   Rails.root.join('pdfs', "#{filename}.pdf"),
               save_only:                      false,                        # depends on :save_to_file being set first
               default_protocol:               'http',
               proxy:                          'TEXT',
               basic_auth:                     false                         # when true username & password are automatically sent from session
               username:                       'TEXT',
               password:                       'TEXT',
               title:                          'Alternate Title',            # otherwise first page title is used
               cover:                          'URL, Pathname, or raw HTML string',
               dpi:                            'dpi',
               encoding:                       'TEXT',
               user_style_sheet:               'URL',
               cookie:                         ['_session_id SESSION_ID'], # could be an array or a single string in a 'name value' format
               post:                           ['query QUERY_PARAM'],      # could be an array or a single string in a 'name value' format
               redirect_delay:                 NUMBER,
               javascript_delay:               NUMBER,
               window_status:                  'TEXT',                     # wait to render until some JS sets window.status to the given string
               image_quality:                  NUMBER,
               no_pdf_compression:             true,
               zoom:                           FLOAT,
               page_offset:                    NUMBER,
               book:                           true,
               default_header:                 true,
               disable_javascript:             false,
               grayscale:                      true,
               lowquality:                     true,
               enable_plugins:                 true,
               disable_internal_links:         true,
               disable_external_links:         true,
               print_media_type:               true,
               disable_smart_shrinking:        true,
               use_xserver:                    true,
               background:                     false,                     # background needs to be true to enable background colors to render
               no_background:                  true,
               viewport_size:                  'TEXT',                    # available only with use_xserver or patched QT
               extra:                          '',                        # directly inserted into the command to wkhtmltopdf
               raise_on_all_errors:            nil,                       # raise error for any stderr output.  Such as missing media, image assets
               outline: {   outline:           true,
                            outline_depth:     LEVEL },
               margin:  {   top:               SIZE,                     # default 10 (mm)
                            bottom:            SIZE,
                            left:              SIZE,
                            right:             SIZE },
               header:  {   html: {            template: 'users/header',          # use :template OR :url
                                               layout:   'pdf_plain',             # optional, use 'pdf_plain' for a pdf_plain.html.pdf.erb file, defaults to main layout
                                               url:      'www.example.com',
                                               locals:   { foo: @bar }},
                            center:            'TEXT',
                            font_name:         'NAME',
                            font_size:         SIZE,
                            left:              'TEXT',
                            right:             'TEXT',
                            spacing:           REAL,
                            line:              true,
                            content:           'HTML CONTENT ALREADY RENDERED'}, # optionally you can pass plain html already rendered (useful if using pdf_from_string)
               footer:  {   html: {   template:'shared/footer',         # use :template OR :url
                                      layout:  'pdf_plain.html',        # optional, use 'pdf_plain' for a pdf_plain.html.pdf.erb file, defaults to main layout
                                      url:     'www.example.com',
                                      locals:  { foo: @bar }},
                            center:            'TEXT',
                            font_name:         'NAME',
                            font_size:         SIZE,
                            left:              'TEXT',
                            right:             'TEXT',
                            spacing:           REAL,
                            line:              true,
                            content:           'HTML CONTENT ALREADY RENDERED'}, # optionally you can pass plain html already rendered (useful if using pdf_from_string)
               toc:     {   font_name:         "NAME",
                            depth:             LEVEL,
                            header_text:       "TEXT",
                            header_fs:         SIZE,
                            text_size_shrink:  0.8,
                            l1_font_size:      SIZE,
                            l2_font_size:      SIZE,
                            l3_font_size:      SIZE,
                            l4_font_size:      SIZE,
                            l5_font_size:      SIZE,
                            l6_font_size:      SIZE,
                            l7_font_size:      SIZE,
                            level_indentation: NUM,
                            l1_indentation:    NUM,
                            l2_indentation:    NUM,
                            l3_indentation:    NUM,
                            l4_indentation:    NUM,
                            l5_indentation:    NUM,
                            l6_indentation:    NUM,
                            l7_indentation:    NUM,
                            no_dots:           true,
                            disable_dotted_lines:  true,
                            disable_links:     true,
                            disable_toc_links: true,
                            disable_back_links:true,
                            xsl_style_sheet:   'file.xsl'} # optional XSLT stylesheet to use for styling table of contents
      end
    end
  end
end

De forma predeterminada, se procesará sin un diseño (layout: false) y la plantilla para el controlador y la acción actuales.

opciones Binarias de wkhtmltopdf

Algunas de las opciones anteriores se están pasando a wkhtmltopdf binary. Se pueden usar para controlar las opciones utilizadas en la representación de Webkit antes de generar el PDF.

Ejemplos de esas opciones son:

print_media_type: true        # Passes `--print-media-type`
no_background: true           # Passes `--no-background`

Puede ver la lista completa de opciones en "Opciones globales" en los documentos de uso de wkhtmltopdf.

Uso super avanzado

Si necesita simplemente crear un pdf y no mostrarlo:

# create a pdf from a string
pdf = WickedPdf.new.pdf_from_string('<h1>Hello There!</h1>')

# create a pdf file from a html file without converting it to string
# Path must be absolute path
pdf = WickedPdf.new.pdf_from_html_file('/your/absolute/path/here')

# create a pdf from a URL
pdf = WickedPdf.new.pdf_from_url('https://github.com/mileszs/wicked_pdf')

# create a pdf from string using templates, layouts and content option for header or footer
pdf = WickedPdf.new.pdf_from_string(
  render_to_string('templates/pdf', layout: 'pdfs/layout_pdf.html'),
  footer: {
    content: render_to_string(
  		'templates/footer',
  		layout: 'pdfs/layout_pdf.html'
  	)
  }
)

# It is possible to use footer/header templates without a layout, in that case you need to provide a valid HTML document
pdf = WickedPdf.new.pdf_from_string(
  render_to_string('templates/full_pdf_template'),
  header: {
    content: render_to_string('templates/full_header_template')
  }
)

# or from your controller, using views & templates and all wicked_pdf options as normal
pdf = render_to_string pdf: "some_file_name", template: "templates/pdf", encoding: "UTF-8"

# then save to a file
save_path = Rails.root.join('pdfs','filename.pdf')
File.open(save_path, 'wb') do |file|
  file << pdf
end

Si necesita mostrar los caracteres codificados en utf, agregue esto a sus vistas o diseños en pdf:

<meta charset="utf-8" />

Si necesita devolver un PDF en un controlador con Rails en modo API:

pdf_html = ActionController::Base.new.render_to_string(template: 'controller_name/action_name', layout: 'pdf')
pdf = WickedPdf.new.pdf_from_string(pdf_html)
send_data pdf, filename: 'file.pdf'

Page Breaks

Puedes controlar los saltos de página con CSS.

Agrega algunos estilos como este a tu hoja de estilos o página:

div.alwaysbreak { page-break-before: always; }
div.nobreak:before { clear:both; }
div.nobreak { page-break-inside: avoid; }

Numeración de páginas

Un poco de javascript puede ayudarte a numerar tus páginas. Crea una plantilla o un archivo de encabezado/pie-de-página con esto:

<html>
  <head>
    <script>
      function number_pages() {
        var vars={};
        var x=document.location.search.substring(1).split('&');
        for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = decodeURIComponent(z[1]);}
        var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
        for(var i in x) {
          var y = document.getElementsByClassName(x[i]);
          for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
        }
      }
    </script>
  </head>
  <body onload="number_pages()">
    Page <span class="page"></span> of <span class="topage"></span>
  </body>
</html>

Cualquier cosa con una clase listada en "var x" arriba se llenará automáticamente en el momento del renderizado.

Si no tiene saltos de página explícitos (y, por lo tanto, no tiene ninguna clase de "página"), también puede usar la generación de número de página incorporada de wkhtmltopdf configurando uno de los encabezados en "[página]":

render pdf: 'filename', header: { right: '[page] of [topage]' }

Configuración

Puede poner su configuración predeterminada, aplicada a todos los pdf en el inicializador "wicked_pdf.rb".

Rack Middleware

Si desea que WickedPdf genere automáticamente vistas en PDF para todas (o casi todas) las páginas agregando .pdf a la URL, agregue lo siguiente a su aplicación Rails:

# in application.rb (Rails3) or environment.rb (Rails2)
require 'wicked_pdf'
config.middleware.use WickedPdf::Middleware# in application.rb (Rails3) or environment.rb (Rails2)
require 'wicked_pdf'
config.middleware.use WickedPdf::Middleware

Si desea activar o desactivar el middleware para ciertas URL, use condiciones :only o :except como las siguientes:

# conditions can be plain strings or regular expressions, and you can supply only one or an array
config.middleware.use WickedPdf::Middleware, {}, only: '/invoice'
config.middleware.use WickedPdf::Middleware, {}, except: [ %r[^/admin], '/secret', %r[^/people/\d] ]

Si utiliza el pdf estándar de render: 'some_pdf' en su aplicación, querrá excluir esas acciones del middleware.

Incluirlo en un email como un attachment

Para incluir un archivo PDF renderizado en un correo electrónico, puede hacer lo siguiente:

attachments['attachment.pdf'] = WickedPdf.new.pdf_from_string(
  render_to_string('link_to_view.pdf.erb', layout: 'pdf')
)

Esto renderizará el pdf a un string y lo incluirá en el correo electrónico. Esto es muy lento, así que asegúrese de programar su entrega de correo electrónico en una tarea.

Otras lecturas

Mike Ackerman's post How To Create PDFs in Rails

JESii's post WickedPDF, wkhtmltopdf, and Heroku...a tricky combination

Berislav Babic's post Send PDF attachments from Rails with WickedPdf and ActionMailer

StackOverflow questions with the tag "wicked-pdf"

Debugging

Ahora puede usar un parámetro de depuración en la URL que le muestra el contenido del pdf en un html simple para diseñarlo más rápido.

Primero debe configurar el parámetro de render show_as_html: params.key? ('Debug') y luego usarlo como lo haría normalmente, pero agregaría "debug" como un parámetro GET en la URL:

http://localhost:3001/CONTROLLER/X.pdf?debug

Sin embargo, los ayudantes wicked_pdf_* usarán las rutas file:/// para los activos cuando usen: show_as_html, y la función de seguridad entre dominios de su navegador se activará, y no las procesará. Para solucionar esto, puede cargar sus activos como tal en sus plantillas:

<%= params.key?('debug') ? image_tag('foo') : wicked_pdf_image_tag('foo') %>

Gotchas

Si no se puede encontrar una imagen de su HTML (por ejemplo, la ruta relativa o incorrecta), es posible que otras imágenes con rutas correctas no se muestren también en el PDF de salida (parece ser un problema con wkhtmltopdf).

wkhtmltopdf puede renderizar en diferentes resoluciones en diferentes plataformas. Por ejemplo, Linux imprime a 75 dpi (nativo para WebKit), mientras que en Windows está en el DPI del escritorio (que normalmente es de 96 dpi). Use: zoom => 0.78125 (75/96) para hacer coincidir la representación de Linux a Windows.

⚠️ **GitHub.com Fallback** ⚠️