Class: Prawn::Document

Inherits:
Object
  • Object
show all
Includes:
PageGeometry, Text, Graphics, Images
Defined in:
lib/prawn/document.rb,
lib/prawn/document/text.rb,
lib/prawn/graphics/cell.rb,
lib/prawn/document/table.rb,
lib/prawn/document/bounding_box.rb,
lib/prawn/document/page_geometry.rb

Defined Under Namespace

Modules: PageGeometry, Text Classes: BoundingBox, Table

Constant Summary

Constants included from PageGeometry

PageGeometry::SIZES

Constants included from Text

Text::BUILT_INS, Text::DEFAULT_FONT_SIZE

Constants included from Graphics

Graphics::KAPPA

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from PageGeometry

#page_dimensions

Methods included from Text

#font, #font_metrics, #font_size, #font_size!, #text

Methods included from Images

#image

Methods included from Graphics

#circle_at, #curve, #curve_to, #ellipse_at, #fill, #fill_and_stroke, #fill_color, #horizontal_line, #horizontal_rule, #line, #line_to, #line_width, #line_width=, #method_missing, #move_to, #polygon, #rectangle, #stroke, #stroke_color, #vertical_line_at

Constructor Details

#initialize(options = {}) ⇒ Document

Creates a new PDF Document. The following options are available:

:page_size

One of the Document::PageGeometry::SIZES [LETTER]

:page_layout

Either :portrait or :landscape

:on_page_start

Optional proc run at each page start

:on_page_stop

Optional proc run at each page stop

:left_margin

Sets the left margin in points [ 0.5 inch]

:right_margin

Sets the right margin in points [ 0.5 inch]

:top_margin

Sets the top margin in points [ 0.5 inch]

:bottom_margin

Sets the bottom margin in points [0.5 inch]

:skip_page_creation

Creates a document without starting the first page [false]

# New document, US Letter paper, portrait orientation
pdf = Prawn::Document.new                            

# New document, A4 paper, landscaped
pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape)    

# New document, draws a line at the start of each new page
pdf = Prawn::Document.new(:on_page_start => 
  lambda { |doc| doc.line [0,100], [300,100] } )


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/prawn/document.rb', line 74

def initialize(options={})
  @objects = []
  @info    = ref(:Creator => "Prawn", :Producer => "Prawn")
  @pages   = ref(:Type => :Pages, :Count => 0, :Kids => [])  
  @root    = ref(:Type => :Catalog, :Pages => @pages)  
  @page_start_proc = options[:on_page_start]
  @page_stop_proc  = options[:on_page_end]              
  @page_size   = options[:page_size]   || "LETTER"    
  @page_layout = options[:page_layout] || :portrait
        
  @margins = { :left   => options[:left_margin]   || 36,
               :right  => options[:right_margin]  || 36,  
               :top    => options[:top_margin]    || 36,       
               :bottom => options[:bottom_margin] || 36  }
   
  generate_margin_box
  
  @bounding_box = @margin_box
  
  start_new_page unless options[:skip_page_creation]
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Prawn::Graphics

Instance Attribute Details

#margin_boxObject

Returns the value of attribute margin_box.



23
24
25
# File 'lib/prawn/document.rb', line 23

def margin_box
  @margin_box
end

#marginsObject (readonly)

Returns the value of attribute margins.



24
25
26
# File 'lib/prawn/document.rb', line 24

def margins
  @margins
end

#page_layoutObject (readonly)

Returns the value of attribute page_layout.



24
25
26
# File 'lib/prawn/document.rb', line 24

def page_layout
  @page_layout
end

#page_sizeObject (readonly)

Returns the value of attribute page_size.



24
25
26
# File 'lib/prawn/document.rb', line 24

def page_size
  @page_size
end

#yObject

Returns the value of attribute y.



23
24
25
# File 'lib/prawn/document.rb', line 23

def y
  @y
end

Class Method Details

.generate(filename, options = {}, &block) ⇒ Object

Creates and renders a PDF document.

The block argument is necessary only when you need to make use of a closure.

# Using implicit block form and rendering to a file
Prawn::Document.generate "foo.pdf" do
   font "Times-Roman"   
   text "Hello World", :at => [200,720], :size => 32       
end

# Using explicit block form and rendering to a file   
content = "Hello World"
Prawn::Document.generate "foo.pdf" do |pdf|
   pdf.font "Times-Roman"
   pdf.text content, :at => [200,720], :size => 32
end


45
46
47
48
49
# File 'lib/prawn/document.rb', line 45

def self.generate(filename,options={},&block)
  pdf = Prawn::Document.new(options)          
  block.arity < 1 ? pdf.instance_eval(&block) : yield(pdf)
  pdf.render_file(filename)
end

Instance Method Details

#bounding_box(*args, &block) ⇒ Object

A bounding box serves two important purposes:

  • Provide bounds for flowing text, starting at a given point

  • Translate the origin (0,0) for graphics primitives, for the purposes

of simplifying coordinate math.

When flowing text, the usage of a bounding box is simple. Text will begin at the point specified, flowing the width of the bounding box. After the block exits, the text drawing position will be moved to the bottom of the bounding box (y - height). Currently, Prawn allows text to overflow the bottom border of the bounding box, so it is up to the user to ensure the text provided will fit within the height of the bounding box.

pdf.bounding_box(, :width => 100, :height => 300) do pdf.text “This text will flow in a very narrow box starting” + “from [100,500]. The pointer will then be moved to [100,200]” + “and return to the margin_box” end

When translating coordinates, the idea is to allow the user to draw relative to the origin, and then translate their drawing to a specified area of the document, rather than adjust all their drawing coordinates to match this new region.

Take for example two triangles which share one point, drawn from the origin:

pdf.polygon [0,250], [0,0], [150,100]
pdf.polygon [100,0], [150,100], [200,0]

It would be easy enough to translate these triangles to another point, e.g [200,200]

pdf.polygon [200,450], [200,200], [350,300]
pdf.polygon [300,200], [350,300], [400,200]

However, each time you want to move the drawing, you’d need to alter every point in the drawing calls, which as you might imagine, can become tedious.

If instead, we think of the drawing as being bounded by a box, we can see that the image is 200 points wide by 250 points tall.

To translate it to a new origin, we simply select a point at (x,y+height)

Using the [200,200] example:

pdf.bounding_box([200,450], :width => 200, :height => 250) do
  pdf.polygon [0,250], [0,0], [150,100]
  pdf.polygon [100,0], [150,100], [200,0]
end

Notice that the drawing is still relative to the origin. If we want to move this drawing around the document, we simply need to recalculate the top-left corner of the rectangular bounding-box, and all of our graphics calls remain unmodified.

By default, bounding boxes are specified relative to the document’s margin_box (which is itself a bounding box). You can also nest bounding boxes, allowing you to build components which are relative to each other

pdf.bouding_box(, :width => 200, :height => 250) do

pdf.bounding_box([50,200], :width => 50, :height => 50) do
  # a 50x50 bounding box that starts 50 pixels left and 50 pixels down 
  # the parent bounding box.
end

end

If you wish to position the bounding boxes at absolute coordinates rather than relative to the margins or other bounding boxes, you can use canvas()

pdf.canvas do
  pdf.bounding_box([200,450], :width => 200, :height => 250) do
    # positioned at 'real' (200,450)
  end
end

Of course, if you use canvas, you will be responsible for ensuring that you remain within the printable area of your document.



86
87
88
89
90
91
92
93
94
95
# File 'lib/prawn/document/bounding_box.rb', line 86

def bounding_box(*args, &block)    
  init_bounding_box(block) do |parent_box|
    # Offset to relative positions
    top_left = args[0]
    top_left[0] += parent_box.absolute_left
    top_left[1] += parent_box.absolute_bottom

    @bounding_box = BoundingBox.new(self, *args)   
  end
end

#boundsObject

Returns the current BoundingBox object, which is by default the box represented by the margin box. When called from within a bounding_box block, the box defined by that call will be used.



175
176
177
# File 'lib/prawn/document.rb', line 175

def bounds
  @bounding_box
end

#canvas(&block) ⇒ Object

A shortcut to produce a bounding box which is mapped to the document’s absolute coordinates, regardless of how things are nested or margin sizes.

pdf.canvas do
  pdf.line pdf.bounds.bottom_left, pdf.bounds.top_right
end


104
105
106
107
108
109
110
111
# File 'lib/prawn/document/bounding_box.rb', line 104

def canvas(&block)     
  init_bounding_box(block) do |_|
    @bounding_box = BoundingBox.new(self, [0,page_dimensions[3]], 
      :width => page_dimensions[2], 
      :height => page_dimensions[3] 
    ) 
  end
end

#cell(point, options = {}) ⇒ Object

Builds and renders a Graphics::Cell. A cell is essentially a special-purpose bounding box designed for flowing text within a bordered area. For available options, see Graphics::Cell#new.

Prawn::Document.generate("cell.pdf") do
   cell [100,500], 
     :width => 200,
     :text  => "The rain in Spain falls mainly on the plains"
end


22
23
24
25
# File 'lib/prawn/graphics/cell.rb', line 22

def cell(point, options={})
  Prawn::Graphics::Cell.new(
    options.merge(:document => self, :point => point)).draw
end

#mask(*fields) ⇒ Object

:nodoc:



233
234
235
236
237
238
239
240
241
# File 'lib/prawn/document.rb', line 233

def mask(*fields) # :nodoc:
 # Stores the current state of the named attributes, executes the block, and
 # then restores the original values after the block has executed.
 # -- I will remove the nodoc if/when this feature is a little less hacky
  stored = {}
  fields.each { |f| stored[f] = send(f) }
  yield
  fields.each { |f| send("#{f}=", stored[f]) }
end

#move_down(n) ⇒ Object

Moves down the document by n point



187
188
189
# File 'lib/prawn/document.rb', line 187

def move_down(n)
  self.y -= n
end

#move_up(n) ⇒ Object

Moves up the document by n points



181
182
183
# File 'lib/prawn/document.rb', line 181

def move_up(n)
  self.y += n
end

#pad(y) ⇒ Object

Moves down the document by y, executes a block, then moves down the document by y again.

pdf.text "some text"
pdf.pad(100) do
  pdf.text "This is 100 points below the previous line of text"  
end
pdf.text "This is 100 points below the previous line of text"


226
227
228
229
230
# File 'lib/prawn/document.rb', line 226

def pad(y)
  move_down(y)
  yield
  move_down(y)
end

#pad_bottom(y) ⇒ Object

Executes a block then moves down the document

pdf.text "some text"
pdf.pad_bottom(100) do
  pdf.text "This text appears right below the previous line of text"
end
pdf.text "This is 100 points below the previous line of text"


212
213
214
215
# File 'lib/prawn/document.rb', line 212

def pad_bottom(y)
  yield
  move_down(y)
end

#pad_top(y) ⇒ Object

Moves down the document and then executes a block.

pdf.text "some text"
pdf.pad_top(100) do
  pdf.text "This is 100 points below the previous line of text"
end
pdf.text "This text appears right below the previous line of text"


199
200
201
202
# File 'lib/prawn/document.rb', line 199

def pad_top(y)
  move_down(y)
  yield
end

#page_countObject

Returns the number of pages in the document

pdf = Prawn::Document.new
pdf.page_count #=> 1
3.times { pdf.start_new_page }
pdf.page_count #=> 4


142
143
144
# File 'lib/prawn/document.rb', line 142

def page_count
  @pages.data[:Count]
end

#renderObject

Renders the PDF document to string



148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/prawn/document.rb', line 148

def render
  output = StringIO.new
  finish_page_content

  render_header(output)
  render_body(output)
  render_xref(output)
  render_trailer(output)
  str = output.string 
  str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
  str
end

#render_file(filename) ⇒ Object

Renders the PDF document to file.

pdf.render_file "foo.pdf"


165
166
167
168
# File 'lib/prawn/document.rb', line 165

def render_file(filename)
  Kernel.const_defined?("Encoding") ? mode = "wb:ASCII-8BIT" : mode = "wb"
  File.open(filename,mode) { |f| f << render }
end

#start_new_page(options = {}) ⇒ Object

Creates and advances to a new page in the document. Runs the on_page_start lambda if one was provided at document creation time (See Document.new).

Page size, margins, and layout can also be set when generating a new page. These values will become the new defaults for page creation

pdf.start_new_page(:size => "LEGAL", :layout => :landscape)    
pdf.start_new_page(:left_margin => 50, :right_margin => 50)


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/prawn/document.rb', line 106

def start_new_page(options = {})      
   @page_size   = options[:size] if options[:size]
   @page_layout = options[:layout] if options[:layout]
                                         
   [:left,:right,:top,:bottom].each do |side|  
     if options[:"#{side}_margin"] 
       @margins[side] = options[:"#{side}_margin"]   
     end
   end
   
   finish_page_content if @page_content  
   generate_margin_box    
   @page_content = ref(:Length => 0)   
 
   @current_page = ref(:Type      => :Page, 
                       :Parent    => @pages, 
                       :MediaBox  => page_dimensions, 
                       :Contents  => @page_content)
   set_current_font    
   update_colors
   @pages.data[:Kids] << @current_page
   @pages.data[:Count] += 1 
 
   add_content "q"   
   
   @y = @margin_box.absolute_top        
   @page_start_proc[self] if @page_start_proc
end

#table(data, options = {}) ⇒ Object

Builds and renders a Document::Table object from raw data. For details on the options that can be passed, see Document::Table.new

data = [["Gregory","Brown"],["James","Healy"],["Jia","Wu"]]

Prawn::Document.generate("table.pdf") do

  # Default table, without headers
  table(data)

  # Default table with headers
  table data, :headers => ["First Name", "Last Name"]

  # Very close to PDF::Writer's default SimpleTable output
  table data, :headers            => ["First Name", "Last Name"],
              :font_size          => 10,
              :vertical_padding   => 2,
              :horizontal_padding => 5,
              :position           => :center,
              :row_colors         => :pdf_writer,

  # Grid border style with explicit column widths.
  table data, :border_style => :grid,
              :widths       => { 0 => 100, 1 => 150 }

end


40
41
42
# File 'lib/prawn/document/table.rb', line 40

def table(data,options={})
  Prawn::Document::Table.new(data,self,options).draw
end