ruport 1.6.3 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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]
|