Skip to content

Add support for rendering with layout #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 124 additions & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,130 @@ for generating pdf files leveraging the new kick-ass prawn library

full documentation/demos at: http://cracklabs.com/prawnto

Copyright (c) 2008 cracklabs.com, released under the MIT license


Forked prawnto
==============

Copyright (c) 2008 cracklabs.com, released under the MIT license
Ever wanted to use layouts for PDF's ? Here it is!

Note that it is targeted to Rails 2.3.x, a Rails 3.x branch may be created in the future.

The first change was to remove prawn_xxx template handler, it contained too much specific magic.
If you had some template for it, read on and convert your templates.

Usage
=====

Without layout
--------------

Your templates are either .prawn or .prawn_dsl at the moment, and you don't have to change anything to make them
work with this new plugin. This is because you are not using layouts.

With layout
-----------

Simply define a layout in views/layouts with some code and at least one yield statement:

# invoices.pdf.prawn_dsl

header_height = 2.5.cm
footer_height = 1.cm

repeat :all do
mask :fill_color, :line_width, :stroke_color do
fill_color "000000"
stroke_color '000000'
line_width(1)

# header
bounding_box [bounds.left, bounds.top], :width => bounds.width, :height => header_height-3.mm-5.mm do
image "#{Rails.root}/public/images/logo.jpg", :fit => [5.cm, 1.6.cm], :at => [bounds.left,bounds.top]

text @title.to_s, :align => :center, :size => 20 if @title

# action template may put some additional header stuff
yield :header

stroke_horizontal_line bounds.left,bounds.right, :at => bounds.bottom - 3.mm
end

# footer
stroke_horizontal_line bounds.left,bounds.right, :at => bounds.bottom + footer_height - 5.mm
end
end

bounding_box [bounds.left, bounds.top - header_height], :width => bounds.width, :height => bounds.height-(header_height+footer_height) do
# action rendering comes here!
yield
end

# put page numbers now that all pages are generated
page_count.times do |i|
go_to_page(i+1)
mask :fill_color, :line_width, :stroke_color do
fill_color "000000"
stroke_color '000000'
line_width(1)

caption = "#{i+1} / #{page_count}"
draw_text caption, :size => 12, :at => [bounds.left + (bounds.width - width_of(caption)) / 2, bounds.bottom]
end
end

Then in an action template

# show.prawn.dsl

# This code is run before the layout so you may setup some variables that will be used in the layout
@title = "Invoice of #{@invoice.date}"

# This is required to postpone the action "rendering" within the layout
content_for do |pdf|
# put some text
pdf.text @invoice.title

# do like .prawn_dsl
view = self
pdf.instance_eval do
# you can call direct pdf methods here
text "Waoow"

# access view instance variable like this (which comes from the controller @invoice)
text view[:invoice].customer.name

# render a partial (ask the view to do it)
view.render :partial => 'link', :object => 'A link'
end

# render a partial
render :partial => 'link', :collection => @links
end

For the sake of completeness, here is the partial

# _links.pdf.prawn_dsl

text link

You may use #content_for and corresponding yield anywhere you want. It follows the same behaviour
than in ERB like templates.


Internals
=========

The way layout works in Rails is targeted for text template generation. With prawn we use code template
generation, I mean each layout doesn't produce any output (like in ERB) it is simply run at a specific
time with a specific object (pdf). The output is generated by calling pdf.render just before sending it
as the request response (not on your template, backed in the plugin).

So, the action template is run FIRST, then the layout template. This is exactly the opposite way for
code template as the layout should run first (setup page, put some header or footer, etc...).
This is the reason for this call to content_for in the action template when using a layout (see doc above).
This is also the reason for the patch to #content_for and Template class.


Copyright (c) 2011 Pascal Hurni, released under the MIT license
1 change: 0 additions & 1 deletion init.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@
Mime::Type.register "application/pdf", :pdf
ActionView::Template.register_template_handler 'prawn', Prawnto::TemplateHandlers::Base
ActionView::Template.register_template_handler 'prawn_dsl', Prawnto::TemplateHandlers::Dsl
ActionView::Template.register_template_handler 'prawn_xxx', Prawnto::TemplateHandlers::Raw

5 changes: 3 additions & 2 deletions lib/prawnto.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ class ActionView::Base
include Prawnto::ActionView
end



class ActionView::Template
include Prawnto::Template
end
75 changes: 75 additions & 0 deletions lib/prawnto/action_view.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,87 @@
module Prawnto
# Acts like a dummy string to let ActionView concat to it
class NullObject
def <<(any)
end
end

module ActionView

def self.included(base)
base.alias_method_chain :render, :prawnto
end

def render_with_prawnto(options = {}, local_assigns = {}, &block) #:nodoc:
local_assigns ||= {}

if template_format == :pdf && @pdf.nil?
# Override some helpers and add our own
extend PrawntoLayoutHelpers

# Mimic a text like template so that #concat can be called by ActionView when rendering layouts with code blocks
@output_buffer = NullObject.new

_prawnto_compile_setup
@pdf = Prawn::Document.new(@prawnto_options[:prawn])
render_without_prawnto(options, local_assigns, &block)
@pdf.render
else
render_without_prawnto(options, local_assigns, &block)
end
end

private
def _prawnto_compile_setup(dsl_setup = false)
compile_support = Prawnto::TemplateHandler::CompileSupport.new(controller)
@prawnto_options = compile_support.options
end

module PrawntoLayoutHelpers
def content_for(name = :_action, &block)
instance_variable_set("@content_for_#{name}", block)
nil
end

def [](var)
instance_variable_get(:"@#{var}")
end
end

end
end

module Prawnto
module Template
def self.included(base)
base.alias_method_chain :initialize, :prawnto
end

def initialize_with_prawnto(*args)
initialize_without_prawnto(*args)
if format.to_s == 'pdf'
extend InstanceMethods
end
end

module InstanceMethods
def render(view, local_assigns = {})
compile(local_assigns)

view.with_template self do
view.send(:_evaluate_assigns_and_ivars)
view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)

view.send(method_name(local_assigns), local_assigns) do |*names|
ivar = :@_proc_for_layout
if view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
proc.call(*names)
elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :_action}")
view.instance_variable_get(ivar).call(view.instance_variable_get(:@pdf))
end
end
end
end
end

end
end
18 changes: 2 additions & 16 deletions lib/prawnto/template_handler/compile_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,14 @@ def ie_request?
end
memoize :ie_request?

# added to make ie happy with ssl pdf's (per naisayer)
def ssl_request?
@controller.request.env['SERVER_PROTOCOL'].downcase == "https"
end
memoize :ssl_request?

# TODO: kept around from railspdf-- maybe not needed anymore? should check.
def set_pragma
if ssl_request? && ie_request?
@controller.headers['Pragma'] = 'public' # added to make ie ssl pdfs work (per naisayer)
else
@controller.headers['Pragma'] ||= ie_request? ? 'no-cache' : ''
end
@controller.headers['Pragma'] ||= ie_request? ? 'no-cache' : ''
end

# TODO: kept around from railspdf-- maybe not needed anymore? should check.
def set_cache_control
if ssl_request? && ie_request?
@controller.headers['Cache-Control'] = 'maxage=1' # added to make ie ssl pdfs work (per naisayer)
else
@controller.headers['Cache-Control'] ||= ie_request? ? 'no-cache, must-revalidate' : ''
end
@controller.headers['Cache-Control'] ||= ie_request? ? 'no-cache, must-revalidate' : ''
end

def set_content_type
Expand Down
5 changes: 1 addition & 4 deletions lib/prawnto/template_handlers/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ class Base < ::ActionView::TemplateHandler
include ::ActionView::TemplateHandlers::Compilable

def compile(template)
"_prawnto_compile_setup;" +
"pdf = Prawn::Document.new(@prawnto_options[:prawn]);" +
"#{template.source}\n" +
"pdf.render;"
"pdf = @pdf; #{template.source}"
end
end
end
Expand Down
5 changes: 1 addition & 4 deletions lib/prawnto/template_handlers/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ module TemplateHandlers
class Dsl < Base

def compile(template)
"_prawnto_compile_setup(true);" +
"pdf = Prawn::Document.new(@prawnto_options[:prawn]);" +
"pdf.instance_eval do; #{template.source}\nend;" +
"pdf.render;"
"view = self; @pdf.instance_eval do; #{template.source}\nend;"
end

end
Expand Down
64 changes: 0 additions & 64 deletions lib/prawnto/template_handlers/raw.rb

This file was deleted.

Loading