Wicked_pdf - LPC-Ltda/Ruby-on-Rails GitHub Wiki
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
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.
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
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" %>
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
.
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'
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; }
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]' }
Puede poner su configuración predeterminada, aplicada a todos los pdf en el inicializador "wicked_pdf.rb".
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.
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.
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"
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.