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

- (PDF) initialize

Returns a new instance of PDF



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

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

- (Object) pdf_writer

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



86
87
88
89
90
# File 'lib/ruport/formatter/pdf.rb', line 86

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

Class Method Details

+ (Object) proxy_to_pdf_writer

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.



59
60
61
# File 'lib/ruport/formatter/pdf.rb', line 59

def self.proxy_to_pdf_writer
  include PDFWriterProxy
end

Instance Method Details

- (Object) add_text(text, format_opts = {})

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


145
146
147
148
# File 'lib/ruport/formatter/pdf.rb', line 145

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

- (Object) apply_template

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



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

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

- (Object) build_group_body

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



111
112
113
# File 'lib/ruport/formatter/pdf.rb', line 111

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

- (Object) build_group_header

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



106
107
108
# File 'lib/ruport/formatter/pdf.rb', line 106

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

- (Object) build_grouping_body

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



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

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

- (Object) build_table_body

Calls the draw_table method.



94
95
96
# File 'lib/ruport/formatter/pdf.rb', line 94

def build_table_body
  draw_table(data)
end

- (Object) center_image_in_box(path, image_opts = {})

  • 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



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

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

- (Object) draw_table(table_data, format_opts = {})

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:



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

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

- (Object) finalize_grouping

Calls render_pdf.



132
133
134
# File 'lib/ruport/formatter/pdf.rb', line 132

def finalize_grouping
  render_pdf
end

- (Object) finalize_table

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



101
102
103
# File 'lib/ruport/formatter/pdf.rb', line 101

def finalize_table
  render_pdf unless options.skip_finalize_table
end

- (Object) move_cursor(n)

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



223
224
225
# File 'lib/ruport/formatter/pdf.rb', line 223

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

- (Object) move_cursor_to(n)

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



228
229
230
# File 'lib/ruport/formatter/pdf.rb', line 228

def move_cursor_to(n)
  pdf_writer.y = n
end

- (Object) move_down(n)



237
238
239
# File 'lib/ruport/formatter/pdf.rb', line 237

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

- (Object) move_up(n)

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



233
234
235
# File 'lib/ruport/formatter/pdf.rb', line 233

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

- (Object) pad(y, &block)

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" }


246
247
248
249
250
# File 'lib/ruport/formatter/pdf.rb', line 246

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

- (Object) pad_bottom(y, &block)

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" }


267
268
269
270
# File 'lib/ruport/formatter/pdf.rb', line 267

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

- (Object) pad_top(y, &block)

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" }


257
258
259
260
# File 'lib/ruport/formatter/pdf.rb', line 257

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

- (Object) render_pdf

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



151
152
153
# File 'lib/ruport/formatter/pdf.rb', line 151

def render_pdf
  output << pdf_writer.render
end

- (Object) rounded_text_box(text) {|opts| ... }

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)


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

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