split tilt-pdf and tilt-pdf-rails

This commit is contained in:
Loic Nageleisen 2014-02-24 09:36:39 +01:00
parent 844f5f1036
commit 90a53b544f
8 changed files with 39 additions and 336 deletions

View file

@ -1,97 +1,19 @@
# Tilt::PDF # Tilt::PDF::Rails
Integrates PDF generation into a Tilt flow for maxxed out ease of use. Integrates Tilt::PDF into Rails ActionView
Contrary to other solutions, all files will be rendered locally without relying See [Tilt::PDF](https://github.com/lloeki/tilt-pdf) for details
on a web server. It follows that even if used in a web server, no concurrent
requests are being made to render assets.
## Dependencies ## Usage
This gem depends on PDFKit, which in turn requires `wkhtmltopdf`. It is
recommended to use the statically compiled version of the latter, as it is
built against a patched QT that supports more features.
## Usage as a Tilt template
Add `tilt-pdf` to your Gemfile. Also add any template engine you may optionally
want, such as `less` or `slim`.
A `foo` template is currently threefold:
- `foo.rpdf`: this file is a YAML file containing options pertaining to the PDF
generation process, such as page size, orientation, metadata, support files,
headers and footers. Some options are passed as is to PDFKit, and
subsequently to `wkhtmltopdf`.
- `foo.html`: this document can be written in any template language you need
(such as ERB or Slim), and the Tilt template resolution system via extension
chaining will apply. Tilt will pass the render block to be yielded to this
document.
- `foo.css`: this stylesheet can be written in any template language you need
(such as Sass or Less), and the Tilt template resolution system via extension
chaining will apply. Tilt will *not* pass the block to be yielded to this
template.
The three files must currently be stored in the *same* directory.
Rendering is done the usual Tilt way:
```ruby ```ruby
require 'tilt-pdf' gem 'tilt-pdf-rails', require: 'tilt/pdf/rails'
pdf = Tilt.new('foo.rpdf').render()
``` ```
## The rpdf file Have a `FooController` `respond_to :pdf` and `render some_view` as usual.
This file contains options. If empty, it is made to 'just work' as Put your `some_view` template files either *together* in the relevant
summplemental files will be looked up according to its basename. `app/views/foo` view directory, or use absolute paths using application/engine
root.
- `main`: document body, overriding the default derived from the basename. Work is in progress to enable better integration with Rails file layout (notably assets).
- `footer` and `header`: html that will get used for (surprise!) header and
footers.
- `stylesheets`: list of stylesheets to include (used for all html, incl.
headers/footers). Defaults to one file from the basename.
- `javascripts`: list of javascripts to include (used for all html, incl.
headers/footers). Defaults to one file from the basename.
- `pdfkit`: While a few PDFKit options are made available at the toplevel for
convenience, this key passes all options as-is to PDFKit.
Example:
```
title: Foorever young
page-size: A4
orientation: landscape
grayscale: true
margin-left: 0
margin-right: 0
margin-top: 0
margin-bottom: 0
pdfkit:
print-media-type: true
main: foorever_young.html.slim
stylesheets:
- novel.css.less
- common.css
javascripts:
- page_numbering.js.coffee
footer: footer.html.slim
```
Filenames can be relative or absolute. When relative, they will be evaluated
as based from the rpdf file.
## Rails and ActionView integration
Require `tilt/pdf/rails` if you want to set up and register `tilt-pdf` as an
ActionView template handler. You can do it in an initializer, or straight from
the Gemfile:
```ruby
gem 'tilt-pdf', require: 'tilt/pdf/rails'
```
Put your three template files *together* in the relevant `app/views/foo` view
directory, or use absolute paths using application/engine root. Work is in
progress to enable better integration with Rails file layout.

1
lib/tilt-pdf-rails.rb Normal file
View file

@ -0,0 +1 @@
require 'tilt/pdf/rails'

View file

@ -1 +0,0 @@
require 'tilt/pdf'

View file

@ -1,211 +0,0 @@
require 'pdfkit'
require 'tilt'
require 'tilt/template'
require 'yaml'
require 'tempfile'
require 'pry'
module Tilt
class PDFTemplate < Template
self.default_mime_type = 'application/pdf'
def prepare; end
def evaluate(scope, locals, &block)
files = aux_files
files << main_html_file
render_to_tmp(*files, scope, locals, block) do |tmp|
opts = pdfkit_options
if header
htmp = tmp.select { |_, f, _| f == header }.first[2]
opts.merge!('header-html' => htmp) if header
end
if footer
ftmp = tmp.select { |_, f, _| f == footer }.first[2]
opts.merge!('footer-html' => ftmp) if footer
end
main = tmp.select { |_, f, _| f == main_html_file }.first[2]
kit = PDFKit.new(File.read(main), opts)
@output = kit.to_pdf
end
@output
end
private
def absolutize(path)
Pathname.new(path).absolute? ? path : File.join(dirname, path)
end
def config
@config = (YAML.load(data) || {})
end
def aux_files
files = []
files.concat css_files
files.concat js_files
files << header if header
files << footer if footer
files
end
def header
if (f = config['header'])
absolutize(f)
end
end
def footer
if (f = config['footer'])
absolutize(f)
end
end
def main_html_file
main_html_from_config || find_html
end
def main_html_from_config
if (f = config['main'])
absolutize(f)
end
end
def css_files
css_from_config || find_css
end
def css_from_config
return unless config.key?('stylesheets')
config.fetch('stylesheets', []).map { |f| absolutize(f) }
end
def js_files
js_from_config || find_js
end
def js_from_config
return unless config.key?('javascripts')
config.fetch('javascripts', []).map { |f| absolutize(f) }
end
def pdfkit_options
toplevel_options = %w[title
orientation
grayscale
page-size
margin-left
margin-right
margin-top
margin-bottom]
options = config.select { |k, _| toplevel_options.include?(k) }
options.merge config.fetch('pdfkit', {})
end
def dirname
File.dirname(eval_file)
end
def find_html
Dir.glob(File.join(dirname, name + '.html*')).first
end
def find_css
Dir.glob(File.join(dirname, name + '.css*'))
end
def find_js
Dir.glob(File.join(dirname, name + '.js*'))
end
def render_html(file, scope, locals, &block)
Tilt.new(file).render(scope, locals, &block)
end
def inject_css!(document, stylesheet)
append_to_head!(document, "<style>#{stylesheet}</style>")
end
def inject_js!(document, script)
append_to_head!(document, "<script>#{script}</script>")
end
def append_to_head!(document, tag)
if document.match(/<\/head>/)
document.gsub!(/(<\/head>)/) { |s| tag + s }
else
document.insert(0, tag)
end
end
def render_to_tmp(*files, scope, locals, block)
tmps = []
no_tilt = %w[html css js]
result = files.map do |file|
ext = File.extname(file).sub(/^\./, '')
if no_tilt.include?(ext)
mime = case ext
when 'js', 'javascript' then 'application/javascript'
else "text/#{ext}"
end
rendered = File.read(file)
else
template = Tilt.new(file)
mime = template.class.default_mime_type
ext = mime.split('/').last
rendered = template.render(scope, locals, &block)
end
[ext, mime, file, rendered]
end
result.sort! do |a, b|
a[1] <=> b[1] # css < html
end
styles = result.select { |_, mime, _, _| mime == 'text/css' }
scripts = result.select { |_, mime, _, _| mime == 'application/javascript' }
result.map! do |ext, mime, file, rendered|
if mime == 'text/html'
styles.each do |_, _, _, style|
inject_css!(rendered, style)
end
scripts.each do |_, _, _, script|
inject_js!(rendered, script)
end
end
[ext, mime, file, rendered]
end
result.map! do |ext, mime, file, rendered|
tmp = Tempfile.new([File.basename(file), '.' + ext])
tmps << tmp
tmp.write(rendered)
tmp.close
path = tmp.path
[mime, file, path]
end
yield result
ensure
tmps.each { |tmp| tmp.close! }
end
end
end
Tilt.register Tilt::PDFTemplate, 'rpdf'

View file

@ -0,0 +1,7 @@
module Tilt
module PDF
module Rails
VERSION = '0.10.0'
end
end
end

View file

@ -1,5 +0,0 @@
module Tilt
module PDF
VERSION = '0.9.0'
end
end

21
tilt-pdf-rails.gemspec Normal file
View file

@ -0,0 +1,21 @@
# -*- encoding: utf-8 -*-
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
require 'tilt/pdf/rails/version'
Gem::Specification.new do |s|
s.name = 'tilt-pdf-rails'
s.version = Tilt::PDF::Rails::VERSION
s.authors = ['Loic Nageleisen']
s.email = ['loic.nageleisen@gmail.com']
s.homepage = 'http://github.com/lloeki/tilt-pdf-rails'
s.summary = %q{PDF files in Rails via Tilt}
s.description = %q{Integrates PDF generation with Tilt::PDF into ActionView}
s.license = 'MIT'
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n")
.map { |f| File.basename(f) }
s.require_paths = ['lib']
s.add_dependency 'tilt-pdf', '~> 0.10.0'
end

View file

@ -1,31 +0,0 @@
# -*- encoding: utf-8 -*-
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
require 'tilt/pdf/version'
Gem::Specification.new do |s|
s.name = 'tilt-pdf'
s.version = Tilt::PDF::VERSION
s.authors = ['Loic Nageleisen']
s.email = ['loic.nageleisen@gmail.com']
s.homepage = 'http://github.com/lloeki/tilt-pdf'
s.summary = %q{PDF files via Tilt}
s.description = %q{Integrates PDF generation into a Tilt flow}
s.license = 'MIT'
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n")
.map { |f| File.basename(f) }
s.require_paths = ['lib']
s.add_dependency 'tilt', '~> 1.4.1'
s.add_dependency 'pdfkit', '~> 0.5.4'
s.add_development_dependency 'therubyracer'
s.add_development_dependency 'less'
s.add_development_dependency 'coffee-script'
s.add_development_dependency 'slim'
s.add_development_dependency 'rspec', '~> 2.14'
s.add_development_dependency 'rake'
s.add_development_dependency 'guard-rspec'
s.add_development_dependency 'pry'
end