ruport 1.6.3 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +11 -0
- data/README.rdoc +105 -0
- data/Rakefile +13 -44
- data/examples/add_row_table.rb +46 -0
- data/examples/data/wine.csv +255 -0
- data/examples/pdf_grouping.rb +39 -0
- data/examples/pdf_table.rb +28 -0
- data/examples/pdf_table_from_csv.rb +26 -0
- data/examples/pdf_table_prawn.rb +30 -0
- data/examples/pdf_table_simple.rb +13 -0
- data/lib/ruport.rb +0 -12
- data/lib/ruport/controller.rb +16 -20
- data/lib/ruport/data/feeder.rb +2 -2
- data/lib/ruport/data/grouping.rb +2 -2
- data/lib/ruport/data/table.rb +314 -202
- data/lib/ruport/formatter.rb +53 -52
- data/lib/ruport/formatter/csv.rb +6 -7
- data/lib/ruport/formatter/html.rb +13 -11
- data/lib/ruport/formatter/pdf.rb +73 -75
- data/lib/ruport/formatter/prawn_pdf.rb +72 -0
- data/lib/ruport/formatter/template.rb +1 -1
- data/lib/ruport/version.rb +1 -1
- data/test/controller_test.rb +100 -122
- data/test/csv_formatter_test.rb +15 -15
- data/test/data_feeder_test.rb +26 -26
- data/test/grouping_test.rb +30 -29
- data/test/helpers.rb +18 -10
- data/test/html_formatter_test.rb +24 -24
- data/test/record_test.rb +14 -14
- data/test/samples/sales.csv +21 -0
- data/test/table_pivot_test.rb +68 -24
- data/test/table_test.rb +365 -336
- data/test/template_test.rb +1 -1
- data/test/text_formatter_test.rb +19 -19
- data/util/bench/data/table/bench_init.rb +1 -1
- metadata +123 -75
- data/README +0 -114
- data/test/pdf_formatter_test.rb +0 -354
data/lib/ruport/formatter.rb
CHANGED
@@ -1,73 +1,73 @@
|
|
1
|
-
# Ruport : Extensible Reporting System
|
1
|
+
# Ruport : Extensible Reporting System
|
2
2
|
#
|
3
3
|
# formatter.rb provides a generalized base class for creating ruport formatters.
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# Created By Gregory Brown
|
6
|
-
# Copyright (C) December 2006, All Rights Reserved.
|
6
|
+
# Copyright (C) December 2006, All Rights Reserved.
|
7
7
|
#
|
8
8
|
# This is free software distributed under the same terms as Ruby 1.8
|
9
9
|
# See LICENSE and COPYING for details.
|
10
|
-
module Ruport
|
10
|
+
module Ruport
|
11
11
|
# Formatter is the base class for Ruport's format implementations.
|
12
12
|
#
|
13
13
|
# Typically, a Formatter will implement one or more output types,
|
14
|
-
# and be registered with one or more Controller classes.
|
14
|
+
# and be registered with one or more Controller classes.
|
15
15
|
#
|
16
16
|
# This class provides all the necessary base functionality to make
|
17
17
|
# use of Ruport's rendering system, including option handling, data
|
18
18
|
# access, and basic output wrapping.
|
19
19
|
#
|
20
20
|
# The following example should provide a general idea of how formatters
|
21
|
-
# work, but see the built in formatters for reference implementations.
|
22
|
-
#
|
21
|
+
# work, but see the built in formatters for reference implementations.
|
22
|
+
#
|
23
23
|
# A simple Controller definition is included to help show the example in
|
24
24
|
# context, but you can also build your own custom interface to formatter
|
25
25
|
# if you wish.
|
26
26
|
#
|
27
27
|
# class ReverseController < Ruport::Controller
|
28
|
-
# stage :reversed_header, :reversed_body
|
28
|
+
# stage :reversed_header, :reversed_body
|
29
29
|
# end
|
30
|
-
#
|
31
|
-
# class ReversedText < Ruport::Formatter
|
32
|
-
#
|
30
|
+
#
|
31
|
+
# class ReversedText < Ruport::Formatter
|
32
|
+
#
|
33
33
|
# # Hooks formatter up to controller
|
34
|
-
# renders :txt, :for => ReverseController
|
35
|
-
#
|
34
|
+
# renders :txt, :for => ReverseController
|
35
|
+
#
|
36
36
|
# # Implements ReverseController's :reversed_header hook
|
37
|
-
# # but can be used by any controller
|
38
|
-
# def build_reversed_header
|
37
|
+
# # but can be used by any controller
|
38
|
+
# def build_reversed_header
|
39
39
|
# output << "#{options.header_text}\n"
|
40
40
|
# output << "The reversed text will follow\n"
|
41
|
-
# end
|
42
|
-
#
|
41
|
+
# end
|
42
|
+
#
|
43
43
|
# # Implements ReverseController's :reversed_body hook
|
44
44
|
# # but can be used by any controller
|
45
45
|
# def build_reversed_body
|
46
46
|
# output << data.reverse << "\n"
|
47
|
-
# end
|
47
|
+
# end
|
48
48
|
#
|
49
|
-
# end
|
49
|
+
# end
|
50
50
|
#
|
51
51
|
# puts ReverseController.render_txt(:data => "apple",
|
52
52
|
# :header_text => "Hello Mike, Hello Joe!")
|
53
|
-
#
|
53
|
+
#
|
54
54
|
# -----
|
55
|
-
# OUTPUT:
|
56
|
-
#
|
55
|
+
# OUTPUT:
|
56
|
+
#
|
57
57
|
# Hello Mike, Hello Joe!
|
58
58
|
# The reversed text will follow
|
59
59
|
# elppa
|
60
|
-
#
|
60
|
+
#
|
61
61
|
class Formatter
|
62
|
-
|
62
|
+
|
63
63
|
# Provides shortcuts so that you can use Ruport's default rendering
|
64
|
-
# capabilities within your custom formatters
|
64
|
+
# capabilities within your custom formatters
|
65
65
|
#
|
66
66
|
module RenderingTools
|
67
67
|
# Uses Controller::Row to render the Row object with the
|
68
68
|
# given options.
|
69
69
|
#
|
70
|
-
# Sets the <tt>:io</tt> attribute by default to the existing
|
70
|
+
# Sets the <tt>:io</tt> attribute by default to the existing
|
71
71
|
# formatter's <tt>output</tt> object.
|
72
72
|
def render_row(row,options={},&block)
|
73
73
|
render_helper(Controller::Row,row,options,&block)
|
@@ -99,12 +99,12 @@ module Ruport
|
|
99
99
|
def render_grouping(grouping,options={},&block)
|
100
100
|
render_helper(Controller::Grouping,grouping,options,&block)
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
# Iterates through the data in the grouping and renders each group
|
104
104
|
# followed by a newline.
|
105
105
|
#
|
106
106
|
def render_inline_grouping(options={},&block)
|
107
|
-
data.each do |_,group|
|
107
|
+
data.each do |_,group|
|
108
108
|
render_group(group, options, &block)
|
109
109
|
output << "\n"
|
110
110
|
end
|
@@ -113,10 +113,10 @@ module Ruport
|
|
113
113
|
private
|
114
114
|
|
115
115
|
def render_helper(rend_klass, source_data,options={},&block)
|
116
|
-
options = {:data => source_data,
|
116
|
+
options = {:data => source_data,
|
117
117
|
:io => output,
|
118
|
-
:layout => false }.merge(options)
|
119
|
-
|
118
|
+
:layout => false }.merge(options)
|
119
|
+
|
120
120
|
options[:io] = "" if self.class.kind_of?(Ruport::Formatter::PDF)
|
121
121
|
rend_klass.render(format,options) do |rend|
|
122
122
|
block[rend] if block
|
@@ -126,13 +126,13 @@ module Ruport
|
|
126
126
|
end
|
127
127
|
|
128
128
|
include RenderingTools
|
129
|
-
|
129
|
+
|
130
130
|
# Set by the <tt>:data</tt> attribute from Controller#render
|
131
|
-
attr_reader :data
|
132
|
-
|
131
|
+
attr_reader :data
|
132
|
+
|
133
133
|
# Set automatically by Controller#render(format) or Controller#render_format
|
134
|
-
attr_accessor :format
|
135
|
-
|
134
|
+
attr_accessor :format
|
135
|
+
|
136
136
|
# Set automatically by Controller#render as a Controller::Options object built
|
137
137
|
# by the hash provided.
|
138
138
|
attr_writer :options
|
@@ -145,24 +145,24 @@ module Ruport
|
|
145
145
|
#
|
146
146
|
def self.renders(fmts,options={})
|
147
147
|
Array(fmts).each do |format|
|
148
|
-
Array(options[:for]).each do |o|
|
149
|
-
o.send(:add_format,self,format)
|
148
|
+
Array(options[:for]).each do |o|
|
149
|
+
o.send(:add_format,self,format)
|
150
150
|
formats << format unless formats.include?(format)
|
151
|
-
end
|
151
|
+
end
|
152
152
|
end
|
153
153
|
end
|
154
|
-
|
154
|
+
|
155
155
|
# Allows you to implement stages in your formatter using the
|
156
156
|
# following syntax:
|
157
157
|
#
|
158
|
-
# class ReversedText < Ruport::Formatter
|
158
|
+
# class ReversedText < Ruport::Formatter
|
159
159
|
# renders :txt, :for => ReverseController
|
160
|
-
#
|
160
|
+
#
|
161
161
|
# build :reversed_header do
|
162
162
|
# output << "#{options.header_text}\n"
|
163
163
|
# output << "The reversed text will follow\n"
|
164
164
|
# end
|
165
|
-
#
|
165
|
+
#
|
166
166
|
# build :reversed_body do
|
167
167
|
# output << data.reverse << "\n"
|
168
168
|
# end
|
@@ -171,12 +171,12 @@ module Ruport
|
|
171
171
|
def self.build(stage,&block)
|
172
172
|
define_method "build_#{stage}", &block
|
173
173
|
end
|
174
|
-
|
174
|
+
|
175
175
|
# Gives a list of formats registered for this formatter.
|
176
176
|
def self.formats
|
177
177
|
@formats ||= []
|
178
|
-
end
|
179
|
-
|
178
|
+
end
|
179
|
+
|
180
180
|
# Returns the template currently set for this formatter.
|
181
181
|
def template
|
182
182
|
Template[options.template] rescue nil || Template[:default]
|
@@ -191,7 +191,7 @@ module Ruport
|
|
191
191
|
# Provides a Controller::Options object for storing formatting options.
|
192
192
|
def options
|
193
193
|
@options ||= Controller::Options.new
|
194
|
-
end
|
194
|
+
end
|
195
195
|
|
196
196
|
# Sets the data object, making a local copy using #dup. This may have
|
197
197
|
# a significant overhead for large tables, so formatters which don't
|
@@ -204,24 +204,24 @@ module Ruport
|
|
204
204
|
def clear_output
|
205
205
|
@output.replace("")
|
206
206
|
end
|
207
|
-
|
207
|
+
|
208
208
|
# Saves the output to a file.
|
209
209
|
def save_output(filename)
|
210
210
|
File.open(filename,"w") {|f| f << output }
|
211
211
|
end
|
212
|
-
|
212
|
+
|
213
213
|
# Use to define that your formatter should save in binary format
|
214
214
|
def self.save_as_binary_file
|
215
215
|
define_method :save_output do |filename|
|
216
216
|
File.open(filename,"wb") {|f| f << output }
|
217
217
|
end
|
218
218
|
end
|
219
|
-
|
219
|
+
|
220
220
|
# Evaluates the string using ERB and returns the results.
|
221
221
|
#
|
222
222
|
# If <tt>:binding</tt> is specified, it will evaluate the template
|
223
223
|
# in that context.
|
224
|
-
def erb(string,options={})
|
224
|
+
def erb(string,options={})
|
225
225
|
require "erb"
|
226
226
|
if string =~ /(\.r\w+)|(\.erb)$/
|
227
227
|
ERB.new(File.read(string)).result(options[:binding]||binding)
|
@@ -245,10 +245,11 @@ module Ruport
|
|
245
245
|
end
|
246
246
|
end
|
247
247
|
end
|
248
|
-
end
|
248
|
+
end
|
249
249
|
|
250
250
|
require "ruport/formatter/template"
|
251
251
|
require "ruport/formatter/csv"
|
252
252
|
require "ruport/formatter/html"
|
253
253
|
require "ruport/formatter/text"
|
254
254
|
require "ruport/formatter/pdf"
|
255
|
+
require "ruport/formatter/prawn_pdf"
|
data/lib/ruport/formatter/csv.rb
CHANGED
@@ -14,16 +14,15 @@
|
|
14
14
|
module Ruport
|
15
15
|
|
16
16
|
# This formatter implements the CSV format for Ruport's Row, Table, Group
|
17
|
-
# and Grouping controllers.
|
18
|
-
# James Edward Gray II's FasterCSV.
|
17
|
+
# and Grouping controllers.
|
19
18
|
#
|
20
19
|
# === Rendering Options
|
21
20
|
#
|
22
21
|
# <tt>:style</tt> Used for grouping (:inline,:justified,:raw)
|
23
22
|
#
|
24
|
-
# <tt>:format_options</tt> A hash of
|
23
|
+
# <tt>:format_options</tt> A hash of CSV options
|
25
24
|
#
|
26
|
-
# <tt>:formatter</tt> An existing
|
25
|
+
# <tt>:formatter</tt> An existing CSV object to write to
|
27
26
|
#
|
28
27
|
# <tt>:show_table_headers</tt> True by default
|
29
28
|
#
|
@@ -35,7 +34,7 @@ module Ruport
|
|
35
34
|
Controller::Group, Controller::Grouping ]
|
36
35
|
|
37
36
|
def initialize
|
38
|
-
require "
|
37
|
+
require "csv"
|
39
38
|
end
|
40
39
|
|
41
40
|
attr_writer :csv_writer
|
@@ -49,14 +48,14 @@ module Ruport
|
|
49
48
|
options.format_options ||= template.format_options
|
50
49
|
end
|
51
50
|
|
52
|
-
# Returns the current
|
51
|
+
# Returns the current CSV object or creates a new one if it has not
|
53
52
|
# been set yet. Note that FCSV(sig) has a cache and returns the *same*
|
54
53
|
# FCSV object if writing to the same underlying output with the same
|
55
54
|
# options.
|
56
55
|
#
|
57
56
|
def csv_writer
|
58
57
|
@csv_writer ||= options.formatter ||
|
59
|
-
|
58
|
+
::CSV.instance(output, options.format_options || {})
|
60
59
|
end
|
61
60
|
|
62
61
|
# Generates table header by turning column_names into a CSV row.
|
@@ -40,12 +40,7 @@ module Ruport
|
|
40
40
|
# This method does not do anything if options.show_table_headers is false
|
41
41
|
# or the Data::Table has no column names.
|
42
42
|
def build_table_header
|
43
|
-
output <<
|
44
|
-
unless data.column_names.empty? || !options.show_table_headers
|
45
|
-
output << "\t\t<tr>\n\t\t\t<th>" +
|
46
|
-
data.column_names.join("</th>\n\t\t\t<th>") +
|
47
|
-
"</th>\n\t\t</tr>\n"
|
48
|
-
end
|
43
|
+
output << build_header(options.show_table_headers ? data.column_names : nil)
|
49
44
|
end
|
50
45
|
|
51
46
|
# Uses the Row controller to build up the table body.
|
@@ -119,12 +114,19 @@ module Ruport
|
|
119
114
|
end
|
120
115
|
|
121
116
|
private
|
122
|
-
|
117
|
+
|
118
|
+
def build_header(columns)
|
119
|
+
if !columns || columns.empty?
|
120
|
+
"\t<table>\n"
|
121
|
+
else
|
122
|
+
"\t<table>\n\t\t<thead>\n\t\t<tr>\n\t\t\t<th>" +
|
123
|
+
columns.join("</th>\n\t\t\t<th>") +
|
124
|
+
"</th>\n\t\t</tr>\n\t\t</thead>\n"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
123
128
|
def render_justified_grouping
|
124
|
-
output <<
|
125
|
-
"#{data.grouped_by}</th>\n\t\t\t<th>" +
|
126
|
-
grouping_columns.join("</th>\n\t\t\t<th>") +
|
127
|
-
"</th>\n\t\t</tr>\n"
|
129
|
+
output << build_header(options.show_group_headers ? ([data.grouped_by] + grouping_columns) : nil)
|
128
130
|
data.each do |name, group|
|
129
131
|
group.each_with_index do |row, i|
|
130
132
|
output << "\t\t<tr>\n\t\t\t"
|
data/lib/ruport/formatter/pdf.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
# Ruport : Extensible Reporting System
|
1
|
+
# Ruport : Extensible Reporting System
|
2
2
|
#
|
3
3
|
# formatter/pdf.rb provides text formatting for Ruport.
|
4
|
-
#
|
5
|
-
# Created by Gregory Brown, February 2006
|
4
|
+
#
|
5
|
+
# Created by Gregory Brown, February 2006
|
6
6
|
# Extended by James Healy, Fall 2006
|
7
|
-
# Copyright (C) 2006-2007 Gregory Brown / James Healy, All Rights Reserved.
|
7
|
+
# Copyright (C) 2006-2007 Gregory Brown / James Healy, All Rights Reserved.
|
8
8
|
#
|
9
9
|
# Initially inspired by some ideas and code from Simon Claret,
|
10
10
|
# with many improvements from James Healy and Michael Milner over time.
|
11
11
|
#
|
12
12
|
# This is free software distributed under the same terms as Ruby 1.8
|
13
|
-
# See LICENSE and COPYING for details.
|
13
|
+
# See LICENSE and COPYING for details.
|
14
14
|
#
|
15
15
|
module Ruport
|
16
|
-
|
16
|
+
|
17
17
|
# This class provides PDF output for Ruport's Table, Group, and Grouping
|
18
18
|
# controllers. It wraps Austin Ziegler's PDF::Writer to provide a higher
|
19
19
|
# level interface and provides a number of helpers designed to make
|
@@ -29,17 +29,17 @@ module Ruport
|
|
29
29
|
# * paper_orientation #=> :portrait
|
30
30
|
#
|
31
31
|
# Text:
|
32
|
-
# * text_format (sets options to be passed to add_text by default)
|
33
|
-
#
|
32
|
+
# * text_format (sets options to be passed to add_text by default)
|
33
|
+
#
|
34
34
|
# Table:
|
35
35
|
# * table_format (a hash that can take any of the options available
|
36
36
|
# to PDF::SimpleTable)
|
37
|
-
# * table_format[:maximum_width] #=> 500
|
37
|
+
# * table_format[:maximum_width] #=> 500
|
38
38
|
#
|
39
39
|
# Grouping:
|
40
40
|
# * style (:inline,:justified,:separated,:offset)
|
41
41
|
#
|
42
|
-
class Formatter::PDF < Formatter
|
42
|
+
class Formatter::PDF < Formatter
|
43
43
|
|
44
44
|
module PDFWriterProxy #:nodoc:
|
45
45
|
def method_missing(id,*args)
|
@@ -48,13 +48,11 @@ module Ruport
|
|
48
48
|
pdf_writer.send(id,*args)
|
49
49
|
end
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
renders :pdf, :for => [ Controller::Row, Controller::Table,
|
53
53
|
Controller::Group, Controller::Grouping ]
|
54
|
-
|
55
54
|
attr_writer :pdf_writer
|
56
55
|
|
57
|
-
|
58
56
|
# If you use this macro in your formatter, Ruport will automatically forward
|
59
57
|
# calls to the underlying PDF::Writer, for any methods that are not wrapped
|
60
58
|
# or redefined.
|
@@ -70,8 +68,8 @@ module Ruport
|
|
70
68
|
require "pdf/simpletable"
|
71
69
|
end
|
72
70
|
end
|
73
|
-
|
74
|
-
# Hook for setting available options using a template. See the template
|
71
|
+
|
72
|
+
# Hook for setting available options using a template. See the template
|
75
73
|
# documentation for the available options and their format.
|
76
74
|
def apply_template
|
77
75
|
apply_page_format_template(template.page)
|
@@ -103,20 +101,20 @@ module Ruport
|
|
103
101
|
def finalize_table
|
104
102
|
render_pdf unless options.skip_finalize_table
|
105
103
|
end
|
106
|
-
|
104
|
+
|
107
105
|
# Generates a header with the group name for Controller::Group.
|
108
106
|
def build_group_header
|
109
107
|
pad(10) { add_text data.name.to_s, :justification => :center }
|
110
108
|
end
|
111
|
-
|
109
|
+
|
112
110
|
# Renders the group as a table for Controller::Group.
|
113
111
|
def build_group_body
|
114
112
|
render_table data, options.to_hash.merge(:formatter => pdf_writer)
|
115
113
|
end
|
116
|
-
|
114
|
+
|
117
115
|
# Determines which style to use and renders the main body for
|
118
116
|
# Controller::Grouping.
|
119
|
-
def build_grouping_body
|
117
|
+
def build_grouping_body
|
120
118
|
case options.style
|
121
119
|
when :inline
|
122
120
|
render_inline_grouping(options.to_hash.merge(:formatter => pdf_writer,
|
@@ -129,14 +127,14 @@ module Ruport
|
|
129
127
|
raise NotImplementedError, "Unknown style"
|
130
128
|
end
|
131
129
|
end
|
132
|
-
|
130
|
+
|
133
131
|
# Calls <tt>render_pdf</tt>.
|
134
132
|
def finalize_grouping
|
135
133
|
render_pdf
|
136
134
|
end
|
137
135
|
|
138
136
|
# Call PDF::Writer#text with the given arguments, using
|
139
|
-
# <tt>text_format</tt> defaults, if they are defined.
|
137
|
+
# <tt>text_format</tt> defaults, if they are defined.
|
140
138
|
#
|
141
139
|
# Example:
|
142
140
|
#
|
@@ -153,7 +151,7 @@ module Ruport
|
|
153
151
|
def render_pdf
|
154
152
|
output << pdf_writer.render
|
155
153
|
end
|
156
|
-
|
154
|
+
|
157
155
|
# - If the image is bigger than the box, it will be scaled down until
|
158
156
|
# it fits.
|
159
157
|
# - If the image is smaller than the box, it won't be resized.
|
@@ -164,7 +162,7 @@ module Ruport
|
|
164
162
|
# - :width: width of box
|
165
163
|
# - :height: height of box
|
166
164
|
#
|
167
|
-
def center_image_in_box(path, image_opts={})
|
165
|
+
def center_image_in_box(path, image_opts={})
|
168
166
|
x = image_opts[:x]
|
169
167
|
y = image_opts[:y]
|
170
168
|
width = image_opts[:width]
|
@@ -174,7 +172,7 @@ module Ruport
|
|
174
172
|
# reduce the size of the image until it fits into the requested box
|
175
173
|
img_width, img_height =
|
176
174
|
fit_image_in_box(info.width,width,info.height,height)
|
177
|
-
|
175
|
+
|
178
176
|
# if the image is smaller than the box, calculate the white space buffer
|
179
177
|
x, y = add_white_space(x,y,img_width,width,img_height,height)
|
180
178
|
|
@@ -184,7 +182,7 @@ module Ruport
|
|
184
182
|
# Draws some text on the canvas, surrounded by a box with rounded corners.
|
185
183
|
#
|
186
184
|
# Yields an OpenStruct which options can be defined on.
|
187
|
-
#
|
185
|
+
#
|
188
186
|
# Example:
|
189
187
|
#
|
190
188
|
# rounded_text_box(options.text) do |o|
|
@@ -201,7 +199,7 @@ module Ruport
|
|
201
199
|
def rounded_text_box(text)
|
202
200
|
opts = OpenStruct.new
|
203
201
|
yield(opts)
|
204
|
-
|
202
|
+
|
205
203
|
resize_text_to_box(text, opts)
|
206
204
|
|
207
205
|
pdf_writer.save_state
|
@@ -225,7 +223,7 @@ module Ruport
|
|
225
223
|
def move_cursor(n)
|
226
224
|
pdf_writer.y += n
|
227
225
|
end
|
228
|
-
|
226
|
+
|
229
227
|
# Moves the cursor to a specific y coordinate in the document.
|
230
228
|
def move_cursor_to(n)
|
231
229
|
pdf_writer.y = n
|
@@ -239,7 +237,7 @@ module Ruport
|
|
239
237
|
def move_down(n)
|
240
238
|
pdf_writer.y -= n
|
241
239
|
end
|
242
|
-
|
240
|
+
|
243
241
|
# Adds a specified amount of whitespace above and below the code
|
244
242
|
# in your block. For example, if you want to surround the top and
|
245
243
|
# bottom of a line of text with 5 pixels of whitespace:
|
@@ -250,8 +248,8 @@ module Ruport
|
|
250
248
|
block.call
|
251
249
|
move_cursor(-y)
|
252
250
|
end
|
253
|
-
|
254
|
-
# Adds a specified amount of whitespace above the code in your block.
|
251
|
+
|
252
|
+
# Adds a specified amount of whitespace above the code in your block.
|
255
253
|
# For example, if you want to add a 10 pixel buffer to the top of a
|
256
254
|
# line of text:
|
257
255
|
#
|
@@ -260,8 +258,8 @@ module Ruport
|
|
260
258
|
move_cursor(-y)
|
261
259
|
block.call
|
262
260
|
end
|
263
|
-
|
264
|
-
# Adds a specified amount of whitespace below the code in your block.
|
261
|
+
|
262
|
+
# Adds a specified amount of whitespace below the code in your block.
|
265
263
|
# For example, if you want to add a 10 pixel buffer to the bottom of a
|
266
264
|
# line of text:
|
267
265
|
#
|
@@ -270,19 +268,19 @@ module Ruport
|
|
270
268
|
block.call
|
271
269
|
move_cursor(-y)
|
272
270
|
end
|
273
|
-
|
271
|
+
|
274
272
|
# Draws a PDF::SimpleTable using the given data (usually a Data::Table).
|
275
273
|
# Takes all the options you can set on a PDF::SimpleTable object,
|
276
274
|
# see the PDF::Writer API docs for details, or check our quick reference
|
277
|
-
# at:
|
278
|
-
#
|
275
|
+
# at:
|
276
|
+
#
|
279
277
|
# http://stonecode.svnrepository.com/ruport/trac.cgi/wiki/PdfWriterQuickRef
|
280
278
|
def draw_table(table_data, format_opts={})
|
281
279
|
m = "PDF Formatter requires column_names to be defined"
|
282
280
|
raise FormatterError, m if table_data.column_names.empty?
|
283
|
-
|
284
|
-
table_data.rename_columns { |c| c.to_s }
|
285
|
-
|
281
|
+
|
282
|
+
table_data.rename_columns { |c| c.to_s }
|
283
|
+
|
286
284
|
if options.table_format
|
287
285
|
format_opts =
|
288
286
|
Marshal.load(Marshal.dump(options.table_format.merge(format_opts)))
|
@@ -294,7 +292,7 @@ module Ruport
|
|
294
292
|
table.maximum_width = 500
|
295
293
|
table.column_order = table_data.column_names
|
296
294
|
table.data = table_data
|
297
|
-
table.data = [{}] if table.data.empty?
|
295
|
+
table.data = [{}] if table.data.empty?
|
298
296
|
apply_pdf_table_column_opts(table,table_data,format_opts)
|
299
297
|
|
300
298
|
format_opts.each {|k,v| table.send("#{k}=", v) }
|
@@ -303,12 +301,12 @@ module Ruport
|
|
303
301
|
|
304
302
|
pdf_writer.font_size = old
|
305
303
|
end
|
306
|
-
|
304
|
+
|
307
305
|
# This module provides tools to simplify some common drawing operations.
|
308
306
|
# It is included by default in the PDF formatter.
|
309
307
|
#
|
310
308
|
module DrawingHelpers
|
311
|
-
|
309
|
+
|
312
310
|
# Draws a horizontal line from x1 to x2
|
313
311
|
def horizontal_line(x1,x2)
|
314
312
|
pdf_writer.line(x1,cursor,x2,cursor)
|
@@ -317,45 +315,45 @@ module Ruport
|
|
317
315
|
|
318
316
|
# Draws a horizontal line from left_boundary to right_boundary
|
319
317
|
def horizontal_rule
|
320
|
-
horizontal_line(left_boundary,right_boundary)
|
321
|
-
end
|
322
|
-
|
318
|
+
horizontal_line(left_boundary,right_boundary)
|
319
|
+
end
|
320
|
+
|
323
321
|
alias_method :hr, :horizontal_rule
|
324
|
-
|
322
|
+
|
325
323
|
# Draws a vertical line at x from y1 to y2
|
326
324
|
def vertical_line_at(x,y1,y2)
|
327
325
|
pdf_writer.line(x,y1,x,y2)
|
328
326
|
pdf_writer.stroke
|
329
327
|
end
|
330
|
-
|
328
|
+
|
331
329
|
# Alias for PDF::Writer#absolute_left_margin
|
332
330
|
def left_boundary
|
333
331
|
pdf_writer.absolute_left_margin
|
334
332
|
end
|
335
|
-
|
333
|
+
|
336
334
|
# Alias for PDF::Writer#absolute_right_margin
|
337
335
|
def right_boundary
|
338
336
|
pdf_writer.absolute_right_margin
|
339
337
|
end
|
340
|
-
|
338
|
+
|
341
339
|
# Alias for PDF::Writer#absolute_top_margin
|
342
340
|
def top_boundary
|
343
341
|
pdf_writer.absolute_top_margin
|
344
342
|
end
|
345
|
-
|
343
|
+
|
346
344
|
# Alias for PDF::Writer#absolute_bottom_margin
|
347
345
|
def bottom_boundary
|
348
346
|
pdf_writer.absolute_bottom_margin
|
349
347
|
end
|
350
|
-
|
348
|
+
|
351
349
|
# Alias for PDF::Writer#y
|
352
350
|
def cursor
|
353
351
|
pdf_writer.y
|
354
352
|
end
|
355
|
-
|
353
|
+
|
356
354
|
# Draws text at an absolute location, defined by
|
357
355
|
# :y, :x1|:left, :x2|:right
|
358
|
-
#
|
356
|
+
#
|
359
357
|
# All options to add_text are also supported.
|
360
358
|
def draw_text(text,text_opts)
|
361
359
|
ypos = cursor
|
@@ -365,13 +363,13 @@ module Ruport
|
|
365
363
|
:absolute_right => text_opts[:x2] || text_opts[:right]))
|
366
364
|
move_cursor_to(ypos)
|
367
365
|
end
|
368
|
-
|
366
|
+
|
369
367
|
# Draws text at an absolute location, defined by
|
370
368
|
# :y, :x1|:left
|
371
369
|
#
|
372
370
|
# The x position defaults to the left margin and the
|
373
371
|
# y position defaults to the current cursor location.
|
374
|
-
#
|
372
|
+
#
|
375
373
|
# Uses PDF::Writer#add_text, so it will ignore any options not supported
|
376
374
|
# by that method.
|
377
375
|
def draw_text!(text,text_opts)
|
@@ -382,17 +380,17 @@ module Ruport
|
|
382
380
|
text_opts[:font_size],
|
383
381
|
text_opts[:angle] || 0)
|
384
382
|
move_cursor_to(ypos)
|
385
|
-
end
|
386
|
-
|
383
|
+
end
|
384
|
+
|
387
385
|
def finalize
|
388
386
|
render_pdf
|
389
387
|
end
|
390
|
-
end
|
388
|
+
end
|
391
389
|
|
392
390
|
include DrawingHelpers
|
393
|
-
|
391
|
+
|
394
392
|
private
|
395
|
-
|
393
|
+
|
396
394
|
def apply_pdf_table_column_opts(table,table_data,format_opts)
|
397
395
|
column_opts = format_opts.delete(:column_options)
|
398
396
|
|
@@ -408,7 +406,7 @@ module Ruport
|
|
408
406
|
column_opts)
|
409
407
|
columns = table_data.column_names.inject({}) { |s,c|
|
410
408
|
s.merge( c => ::PDF::SimpleTable::Column.new(c) { |col|
|
411
|
-
col.heading = create_heading(heading_opts)
|
409
|
+
col.heading = create_heading(heading_opts)
|
412
410
|
column_opts.each { |k,v| col.send("#{k}=",v) }
|
413
411
|
# use the specific column names now
|
414
412
|
specific[c].each { |k,v| col.send("#{k}=",v) }
|
@@ -427,22 +425,22 @@ module Ruport
|
|
427
425
|
s.merge(c => opts)
|
428
426
|
end
|
429
427
|
end
|
430
|
-
|
428
|
+
|
431
429
|
def create_heading(heading_opts)
|
432
430
|
heading_opts ||= {}
|
433
431
|
::PDF::SimpleTable::Column::Heading.new {|head|
|
434
432
|
heading_opts.each {|k,v| head.send("#{k}=",v) }
|
435
433
|
}
|
436
434
|
end
|
437
|
-
|
435
|
+
|
438
436
|
def grouping_columns
|
439
437
|
data.data.to_a[0][1].column_names.dup.unshift(data.grouped_by)
|
440
438
|
end
|
441
|
-
|
439
|
+
|
442
440
|
def table_with_grouped_by_column
|
443
441
|
Ruport::Data::Table.new(:column_names => grouping_columns)
|
444
442
|
end
|
445
|
-
|
443
|
+
|
446
444
|
def render_justified_or_separated_grouping
|
447
445
|
table = table_with_grouped_by_column
|
448
446
|
data.each do |name,group|
|
@@ -457,7 +455,7 @@ module Ruport
|
|
457
455
|
end
|
458
456
|
render_table table, options.to_hash.merge(:formatter => pdf_writer)
|
459
457
|
end
|
460
|
-
|
458
|
+
|
461
459
|
def render_offset_grouping
|
462
460
|
table = table_with_grouped_by_column
|
463
461
|
data.each do |name,group|
|
@@ -466,11 +464,11 @@ module Ruport
|
|
466
464
|
end
|
467
465
|
render_table table, options.to_hash.merge(:formatter => pdf_writer)
|
468
466
|
end
|
469
|
-
|
467
|
+
|
470
468
|
def image_fits_in_box?(img_width,box_width,img_height,box_height)
|
471
469
|
!(img_width > box_width || img_height > box_height)
|
472
470
|
end
|
473
|
-
|
471
|
+
|
474
472
|
def fit_image_in_box(img_width,box_width,img_height,box_height)
|
475
473
|
img_ratio = img_height.to_f / img_width.to_f
|
476
474
|
until image_fits_in_box?(img_width,box_width,img_height,box_height)
|
@@ -491,7 +489,7 @@ module Ruport
|
|
491
489
|
end
|
492
490
|
return x, y
|
493
491
|
end
|
494
|
-
|
492
|
+
|
495
493
|
def resize_text_to_box(text,opts)
|
496
494
|
loop do
|
497
495
|
sz = pdf_writer.text_width(text, opts.font_size)
|
@@ -499,13 +497,13 @@ module Ruport
|
|
499
497
|
opts.font_size -= 1
|
500
498
|
end
|
501
499
|
end
|
502
|
-
|
500
|
+
|
503
501
|
def draw_box(x,y,width,height,radius,fill_color=nil,stroke_color=nil)
|
504
502
|
pdf_writer.fill_color(fill_color || Color::RGB::White)
|
505
503
|
pdf_writer.stroke_color(stroke_color || Color::RGB::Black)
|
506
504
|
pdf_writer.rounded_rectangle(x, y, width, height, radius).fill_stroke
|
507
505
|
end
|
508
|
-
|
506
|
+
|
509
507
|
def add_text_with_bottom_border(text,x,y,width,font_size)
|
510
508
|
pdf_writer.line( x, y - 20,
|
511
509
|
x + width, y - 20).stroke
|
@@ -515,13 +513,13 @@ module Ruport
|
|
515
513
|
:absolute_left => x, :absolute_right => x + width,
|
516
514
|
:justification => :center, :font_size => font_size)
|
517
515
|
end
|
518
|
-
|
516
|
+
|
519
517
|
def apply_page_format_template(t)
|
520
518
|
t = (t || {}).merge(options.page_format || {})
|
521
519
|
options.paper_size ||= t[:size]
|
522
520
|
options.paper_orientation ||= t[:layout]
|
523
521
|
end
|
524
|
-
|
522
|
+
|
525
523
|
def apply_text_format_template(t)
|
526
524
|
t = (t || {}).merge(options.text_format || {})
|
527
525
|
options.text_format = t unless t.empty?
|
@@ -531,7 +529,7 @@ module Ruport
|
|
531
529
|
t = (t || {}).merge(options.table_format || {})
|
532
530
|
options.table_format = t unless t.empty?
|
533
531
|
end
|
534
|
-
|
532
|
+
|
535
533
|
def apply_column_format_template(t)
|
536
534
|
t = (t || {}).merge(options.column_format || {})
|
537
535
|
column_opts = {}
|
@@ -550,7 +548,7 @@ module Ruport
|
|
550
548
|
end
|
551
549
|
end
|
552
550
|
end
|
553
|
-
|
551
|
+
|
554
552
|
def apply_heading_format_template(t)
|
555
553
|
t = (t || {}).merge(options.heading_format || {})
|
556
554
|
heading_opts = {}
|
@@ -582,7 +580,7 @@ module Ruport
|
|
582
580
|
end
|
583
581
|
end
|
584
582
|
end
|
585
|
-
|
583
|
+
|
586
584
|
def apply_grouping_format_template(t)
|
587
585
|
t = (t || {}).merge(options.grouping_format || {})
|
588
586
|
options.style ||= t[:style]
|