Class: Ruport::Formatter::PDF

Inherits:
Ruport::Formatter show all
Includes:
DrawingHelpers
Defined in:
lib/ruport/formatter/pdf.rb

Overview

This class provides PDF output for Ruport’s Table, Group, and Grouping controllers. It wraps Austin Ziegler’s PDF::Writer to provide a higher level interface and provides a number of helpers designed to make generating PDF reports much easier. You will typically want to build subclasses of this formatter to customize it as needed.

Many methods forward options to PDF::Writer, so you may wish to consult its API docs.

Rendering Options

General:
  * paper_size  #=> "LETTER"
  * paper_orientation #=> :portrait

Text:
  * text_format (sets options to be passed to add_text by default)  

Table:
  * table_format (a hash that can take any of the options available
      to PDF::SimpleTable)
  * table_format[:maximum_width] #=> 500   

Grouping:
  * style (:inline,:justified,:separated,:offset)

Defined Under Namespace

Modules: DrawingHelpers, PDFWriterProxy

Instance Attribute Summary collapse

Attributes inherited from Ruport::Formatter

#data, #format, #options

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DrawingHelpers

#bottom_boundary, #cursor, #draw_text, #draw_text!, #finalize, #horizontal_line, #horizontal_rule, #left_boundary, #right_boundary, #top_boundary, #vertical_line_at

Methods inherited from Ruport::Formatter

build, #clear_output, #erb, formats, #method_missing, #output, renders, save_as_binary_file, #save_output, #template

Methods included from RenderingTools

#render_group, #render_grouping, #render_inline_grouping, #render_row, #render_table

Constructor Details

#initializePDF

Returns a new instance of PDF.



67
68
69
70
71
72
# File 'lib/ruport/formatter/pdf.rb', line 67

def initialize
  Ruport.quiet do   
    require "pdf/writer"
    require "pdf/simpletable"
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Ruport::Formatter

Instance Attribute Details

#pdf_writerObject

Returns the current PDF::Writer object or creates a new one if it has not been set yet.



88
89
90
91
92
# File 'lib/ruport/formatter/pdf.rb', line 88

def pdf_writer
  @pdf_writer ||= options.formatter ||
    ::PDF::Writer.new( :paper => options.paper_size || "LETTER",
          :orientation => options.paper_orientation || :portrait)
end

Class Method Details

.proxy_to_pdf_writerObject

If you use this macro in your formatter, Ruport will automatically forward calls to the underlying PDF::Writer, for any methods that are not wrapped or redefined.



61
62
63
# File 'lib/ruport/formatter/pdf.rb', line 61

def self.proxy_to_pdf_writer
  include PDFWriterProxy
end

Instance Method Details

#add_text(text, format_opts = {}) ⇒ Object

Call PDF::Writer#text with the given arguments, using text_format defaults, if they are defined.

Example:

options.text_format { :font_size => 14 }

add_text("Hello Joe") #renders at 14pt
add_text("Hello Mike",:font_size => 16) # renders at 16pt


147
148
149
150
# File 'lib/ruport/formatter/pdf.rb', line 147

def add_text(text, format_opts={})
  format_opts = options.text_format.merge(format_opts) if options.text_format
  pdf_writer.text(text, format_opts)
end

#apply_templateObject

Hook for setting available options using a template. See the template documentation for the available options and their format.



76
77
78
79
80
81
82
83
# File 'lib/ruport/formatter/pdf.rb', line 76

def apply_template
  apply_page_format_template(template.page)
  apply_text_format_template(template.text)
  apply_table_format_template(template.table)
  apply_column_format_template(template.column)
  apply_heading_format_template(template.heading)
  apply_grouping_format_template(template.grouping)
end

#build_group_bodyObject

Renders the group as a table for Controller::Group.



113
114
115
# File 'lib/ruport/formatter/pdf.rb', line 113

def build_group_body
  render_table data, options.to_hash.merge(:formatter => pdf_writer)
end

#build_group_headerObject

Generates a header with the group name for Controller::Group.



108
109
110
# File 'lib/ruport/formatter/pdf.rb', line 108

def build_group_header
  pad(10) { add_text data.name.to_s, :justification => :center }
end

#build_grouping_bodyObject

Determines which style to use and renders the main body for Controller::Grouping.



119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/ruport/formatter/pdf.rb', line 119

def build_grouping_body 
  case options.style
  when :inline
    render_inline_grouping(options.to_hash.merge(:formatter => pdf_writer,
        :skip_finalize_table => true))
  when :justified, :separated
    render_justified_or_separated_grouping
  when :offset
    render_offset_grouping
  else
    raise NotImplementedError, "Unknown style"
  end
end

#build_table_bodyObject

Calls the draw_table method.



96
97
98
# File 'lib/ruport/formatter/pdf.rb', line 96

def build_table_body
  draw_table(data)
end

#center_image_in_box(path, image_opts = {}) ⇒ Object

  • If the image is bigger than the box, it will be scaled down until it fits.

  • If the image is smaller than the box, it won’t be resized.

options:

  • :x: left bound of box

  • :y: bottom bound of box

  • :width: width of box

  • :height: height of box



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/ruport/formatter/pdf.rb', line 167

def center_image_in_box(path, image_opts={}) 
  x = image_opts[:x]
  y = image_opts[:y]
  width = image_opts[:width]
  height = image_opts[:height]
  info = ::PDF::Writer::Graphics::ImageInfo.new(File.open(path, "rb"))

  # reduce the size of the image until it fits into the requested box
  img_width, img_height =
    fit_image_in_box(info.width,width,info.height,height)
  
  # if the image is smaller than the box, calculate the white space buffer
  x, y = add_white_space(x,y,img_width,width,img_height,height)

  pdf_writer.add_image_from_file(path, x, y, img_width, img_height) 
end

#draw_table(table_data, format_opts = {}) ⇒ Object

Draws a PDF::SimpleTable using the given data (usually a Data::Table). Takes all the options you can set on a PDF::SimpleTable object, see the PDF::Writer API docs for details, or check our quick reference at:

stonecode.svnrepository.com/ruport/trac.cgi/wiki/PdfWriterQuickRef

Raises:



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/ruport/formatter/pdf.rb', line 280

def draw_table(table_data, format_opts={})
  m = "PDF Formatter requires column_names to be defined"
  raise FormatterError, m if table_data.column_names.empty?
  
  table_data.rename_columns { |c| c.to_s } 
        
  if options.table_format
    format_opts =
      Marshal.load(Marshal.dump(options.table_format.merge(format_opts))) 
  end  
    
  old = pdf_writer.font_size
  
  ::PDF::SimpleTable.new do |table|          
    table.maximum_width = 500
    table.column_order  = table_data.column_names
    table.data = table_data
    table.data = [{}] if table.data.empty?                                                 
    apply_pdf_table_column_opts(table,table_data,format_opts)

    format_opts.each {|k,v| table.send("#{k}=", v) }  
    table.render_on(pdf_writer)
  end                                              
  
  pdf_writer.font_size = old
end

#finalize_groupingObject

Calls render_pdf.



134
135
136
# File 'lib/ruport/formatter/pdf.rb', line 134

def finalize_grouping
  render_pdf
end

#finalize_tableObject

Appends the results of PDF::Writer#render to output for your pdf_writer object.



103
104
105
# File 'lib/ruport/formatter/pdf.rb', line 103

def finalize_table
  render_pdf unless options.skip_finalize_table
end

#move_cursor(n) ⇒ Object

Adds n to pdf_writer.y, moving the vertical drawing position in the document.



225
226
227
# File 'lib/ruport/formatter/pdf.rb', line 225

def move_cursor(n) 
  pdf_writer.y += n
end

#move_cursor_to(n) ⇒ Object

Moves the cursor to a specific y coordinate in the document.



230
231
232
# File 'lib/ruport/formatter/pdf.rb', line 230

def move_cursor_to(n)
  pdf_writer.y = n
end

#move_down(n) ⇒ Object



239
240
241
# File 'lib/ruport/formatter/pdf.rb', line 239

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

#move_up(n) ⇒ Object

Moves the vertical drawing position in the document upwards by n.



235
236
237
# File 'lib/ruport/formatter/pdf.rb', line 235

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

#pad(y, &block) ⇒ Object

Adds a specified amount of whitespace above and below the code in your block. For example, if you want to surround the top and bottom of a line of text with 5 pixels of whitespace:

pad(5) { add_text "This will be padded top and bottom" }


248
249
250
251
252
# File 'lib/ruport/formatter/pdf.rb', line 248

def pad(y,&block)
  move_cursor(-y)
  block.call
  move_cursor(-y)
end

#pad_bottom(y, &block) ⇒ Object

Adds a specified amount of whitespace below the code in your block.

For example, if you want to add a 10 pixel buffer to the bottom of a line of text:

pad_bottom(10) { add_text "This will be padded on bottom" }


269
270
271
272
# File 'lib/ruport/formatter/pdf.rb', line 269

def pad_bottom(y,&block)
  block.call
  move_cursor(-y)
end

#pad_top(y, &block) ⇒ Object

Adds a specified amount of whitespace above the code in your block.

For example, if you want to add a 10 pixel buffer to the top of a line of text:

pad_top(10) { add_text "This will be padded on top" }


259
260
261
262
# File 'lib/ruport/formatter/pdf.rb', line 259

def pad_top(y,&block)
  move_cursor(-y)
  block.call
end

#render_pdfObject

Calls PDF::Writer#render and appends to output.



153
154
155
# File 'lib/ruport/formatter/pdf.rb', line 153

def render_pdf
  output << pdf_writer.render
end

#rounded_text_box(text) {|opts| ... } ⇒ Object

Draws some text on the canvas, surrounded by a box with rounded corners.

Yields an OpenStruct which options can be defined on.

Example:

rounded_text_box(options.text) do |o|
  o.radius = 5
  o.width     = options.width  || 400
  o.height    = options.height || 130
  o.font_size = options.font_size || 12
  o.heading   = options.heading

  o.x = pdf_writer.absolute_x_middle - o.width/2
  o.y = 300
end

Yields:

  • (opts)


201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/ruport/formatter/pdf.rb', line 201

def rounded_text_box(text)
  opts = OpenStruct.new
  yield(opts)
  
  resize_text_to_box(text, opts)
  
  pdf_writer.save_state
  draw_box(opts.x, opts.y, opts.width, opts.height, opts.radius, 
    opts.fill_color, opts.stroke_color)
  add_text_with_bottom_border(opts.heading, opts.x, opts.y,
    opts.width, opts.font_size) if opts.heading
  pdf_writer.restore_state

  start_position = opts.heading ? opts.y - 20 : opts.y
  draw_text(text, :y              => start_position,
                  :left           => opts.x,
                  :right          => opts.x + opts.width,
                  :justification  => opts.justification || :center,
                  :font_size      => opts.font_size)
  move_cursor_to(opts.y - opts.height)
end