tn_pdf 0.0.2
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.
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +47 -0
- data/Rakefile +12 -0
- data/lib/tn_pdf/box.rb +72 -0
- data/lib/tn_pdf/configuration.rb +124 -0
- data/lib/tn_pdf/page_section.rb +56 -0
- data/lib/tn_pdf/report.rb +131 -0
- data/lib/tn_pdf/table.rb +91 -0
- data/lib/tn_pdf/table_column.rb +67 -0
- data/lib/tn_pdf/version.rb +3 -0
- data/lib/tn_pdf.rb +11 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/tn_pdf/box_spec.rb +54 -0
- data/spec/tn_pdf/page_section_spec.rb +79 -0
- data/spec/tn_pdf/report_spec.rb +115 -0
- data/spec/tn_pdf/table_column_spec.rb +64 -0
- data/spec/tn_pdf/table_spec.rb +101 -0
- data/tn_pdf.gemspec +23 -0
- metadata +131 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
Ascii85 (1.0.1)
|
5
|
+
ZenTest (4.5.0)
|
6
|
+
activesupport (2.3.5)
|
7
|
+
autotest (4.4.6)
|
8
|
+
ZenTest (>= 4.4.1)
|
9
|
+
coderay (0.9.8)
|
10
|
+
diff-lcs (1.1.2)
|
11
|
+
method_source (0.4.1)
|
12
|
+
ruby_parser (>= 2.0.5)
|
13
|
+
pdf-reader (0.9.2)
|
14
|
+
Ascii85 (>= 0.9)
|
15
|
+
prawn (0.11.1)
|
16
|
+
pdf-reader (>= 0.9.0)
|
17
|
+
ttfunk (~> 1.0.0)
|
18
|
+
pry (0.8.3)
|
19
|
+
coderay (>= 0.9.7)
|
20
|
+
method_source (>= 0.4.0)
|
21
|
+
ruby_parser (>= 2.0.5)
|
22
|
+
slop (>= 1.5.3)
|
23
|
+
rcov (0.9.9)
|
24
|
+
rspec (2.6.0)
|
25
|
+
rspec-core (~> 2.6.0)
|
26
|
+
rspec-expectations (~> 2.6.0)
|
27
|
+
rspec-mocks (~> 2.6.0)
|
28
|
+
rspec-core (2.6.1)
|
29
|
+
rspec-expectations (2.6.0)
|
30
|
+
diff-lcs (~> 1.1.2)
|
31
|
+
rspec-mocks (2.6.0)
|
32
|
+
ruby_parser (2.0.6)
|
33
|
+
sexp_processor (~> 3.0)
|
34
|
+
sexp_processor (3.0.5)
|
35
|
+
slop (1.6.0)
|
36
|
+
ttfunk (1.0.1)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
activesupport
|
43
|
+
autotest
|
44
|
+
prawn (~> 0.11.1)
|
45
|
+
pry
|
46
|
+
rcov
|
47
|
+
rspec (~> 2.6.0)
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler::GemHelper.install_tasks
|
4
|
+
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
|
7
|
+
desc "Run all examples with RCov"
|
8
|
+
Spec::Rake::SpecTask.new('examples_with_rcov') do |t|
|
9
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
10
|
+
t.rcov = true
|
11
|
+
t.rcov_opts = ['--exclude', 'spec,.bundle']
|
12
|
+
end
|
data/lib/tn_pdf/box.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
module TnPDF
|
2
|
+
class PageSection
|
3
|
+
|
4
|
+
class Box
|
5
|
+
attr_reader :image_path, :image_options
|
6
|
+
attr_reader :text, :text_options
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
parse_options(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(document, pos, width, height=nil)
|
13
|
+
options_hash = { :width => width }
|
14
|
+
options_hash[:height] = height unless height.nil?
|
15
|
+
|
16
|
+
document.bounding_box(pos, options_hash) do
|
17
|
+
if has_image?
|
18
|
+
image_args = [image_path]
|
19
|
+
image_args << image_options unless image_options.empty?
|
20
|
+
document.image *image_args
|
21
|
+
end
|
22
|
+
|
23
|
+
if has_text?
|
24
|
+
text_args = [text]
|
25
|
+
text_args << text_options unless text_options.empty?
|
26
|
+
document.text *text_args
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_image?
|
32
|
+
!image_path.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def has_text?
|
36
|
+
!text.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def parse_options(options)
|
42
|
+
options.to_options!
|
43
|
+
|
44
|
+
options[:align] ||= :justify
|
45
|
+
options[:valign] ||= :center
|
46
|
+
|
47
|
+
if options[:text]
|
48
|
+
unless options[:text].kind_of? Hash
|
49
|
+
options[:text] = { :text => options[:text] }
|
50
|
+
end
|
51
|
+
options[:text][:align] = options[:align]
|
52
|
+
options[:text][:valign] = options[:valign]
|
53
|
+
|
54
|
+
@text = options[:text][:text]
|
55
|
+
@text_options = options[:text].reject { |k,_| k == :text }
|
56
|
+
end
|
57
|
+
|
58
|
+
if options[:image]
|
59
|
+
unless options[:image].kind_of? Hash
|
60
|
+
options[:image] = { :path => options[:image] }
|
61
|
+
end
|
62
|
+
options[:image][:position] = options[:align]
|
63
|
+
options[:image][:vposition] = options[:valign]
|
64
|
+
|
65
|
+
@image_path = options[:image][:path]
|
66
|
+
@image_options = options[:image].reject { |k,_| k == :path }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
module TnPDF
|
3
|
+
|
4
|
+
class Configuration
|
5
|
+
class << self
|
6
|
+
|
7
|
+
|
8
|
+
def [](property)
|
9
|
+
property = property.to_s
|
10
|
+
case property
|
11
|
+
when /^page_header_/
|
12
|
+
property_key = property.sub('page_header_','').to_sym
|
13
|
+
header[property_key]
|
14
|
+
when /^page_footer_/
|
15
|
+
property_key = property.sub('page_footer_','').to_sym
|
16
|
+
footer[property_key]
|
17
|
+
when /^table_/
|
18
|
+
property_key = property.sub('table_','').to_sym
|
19
|
+
table[property_key]
|
20
|
+
when /^column_/
|
21
|
+
property_key = property.sub('column_','').to_sym
|
22
|
+
column[property_key]
|
23
|
+
else
|
24
|
+
report[property.to_sym]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def report_properties_names
|
29
|
+
report.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
def header_properties_names
|
33
|
+
header.keys
|
34
|
+
end
|
35
|
+
|
36
|
+
def footer_properties_names
|
37
|
+
footer.keys
|
38
|
+
end
|
39
|
+
|
40
|
+
def table_properties_names
|
41
|
+
table.keys
|
42
|
+
end
|
43
|
+
|
44
|
+
def properties_names
|
45
|
+
report_properties_names.map { |p| "report_#{p}" } +
|
46
|
+
header_properties_names.map { |p| "page_header_#{p}" } +
|
47
|
+
footer_properties_names.map { |p| "page_footer_#{p}" } +
|
48
|
+
table_properties_names.map { |p| "table_#{p}" }
|
49
|
+
end
|
50
|
+
|
51
|
+
def load_from(yaml_file)
|
52
|
+
configurations = YAML.load_file(yaml_file)
|
53
|
+
configurations.to_options!
|
54
|
+
configurations.each_key do |item|
|
55
|
+
self.send(item).merge! configurations[item].to_options!
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def report
|
62
|
+
@report ||= {
|
63
|
+
:page_size => "A4",
|
64
|
+
:page_layout => :landscape,
|
65
|
+
:left_margin => 1.cm,
|
66
|
+
:right_margin => 1.cm,
|
67
|
+
:top_margin => 0.cm,
|
68
|
+
:bottom_margin => 0.cm,
|
69
|
+
:font => "Courier",
|
70
|
+
:font_size => 10,
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def header
|
75
|
+
@header ||= {
|
76
|
+
:height => 1.cm,
|
77
|
+
:top => 0.5.cm,
|
78
|
+
:bottom => 0.2.cm,
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def footer
|
84
|
+
@footer ||= {
|
85
|
+
:height => 1.cm,
|
86
|
+
:top => 0.2.cm,
|
87
|
+
:bottom => 0.1.cm,
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
def table
|
92
|
+
@table ||= {
|
93
|
+
:align => :center,
|
94
|
+
:multipage_headers => true,
|
95
|
+
:borders => false,
|
96
|
+
:header_color => "FF0000",
|
97
|
+
:odd_row_color => "00FF00",
|
98
|
+
:even_row_color => "0000FF",
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
def column
|
103
|
+
@column ||= {
|
104
|
+
:currency => { :format => "%0.2f",
|
105
|
+
:align => :right,
|
106
|
+
:decimal => "," },
|
107
|
+
|
108
|
+
:number => { :format => "%d",
|
109
|
+
:align => :right},
|
110
|
+
|
111
|
+
:date => { :format => "%d/%m/%Y",
|
112
|
+
:align => :center },
|
113
|
+
|
114
|
+
:float => { :format => "%0.3f",
|
115
|
+
:align => :right,
|
116
|
+
:decimal => "," },
|
117
|
+
|
118
|
+
:text => { :format => "%s",
|
119
|
+
:align => :left }
|
120
|
+
}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module TnPDF
|
2
|
+
class PageSection
|
3
|
+
attr_reader :left, :right, :center
|
4
|
+
attr_reader :left_box, :center_box, :right_box
|
5
|
+
attr_writer :top, :bottom
|
6
|
+
attr_accessor :width, :height
|
7
|
+
|
8
|
+
def left=(options)
|
9
|
+
@left = options
|
10
|
+
options[:align] = :left
|
11
|
+
@left_box = Box.new(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def center=(options)
|
15
|
+
@center = options
|
16
|
+
options[:align] = :center
|
17
|
+
@center_box = Box.new(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def right=(options)
|
21
|
+
@right = options
|
22
|
+
options[:align] = :right
|
23
|
+
@right_box = Box.new(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def total_height
|
27
|
+
height + top + bottom
|
28
|
+
end
|
29
|
+
|
30
|
+
def boxes
|
31
|
+
[left_box, center_box, right_box].compact
|
32
|
+
end
|
33
|
+
|
34
|
+
def top
|
35
|
+
@top ||= 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def bottom
|
39
|
+
@bottom ||= 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def render(document, position)
|
43
|
+
width ||= document.bounds.width
|
44
|
+
|
45
|
+
box_width = width/(boxes.count)
|
46
|
+
|
47
|
+
boxes.inject(0) do |offset, box|
|
48
|
+
x_pos = position[0] + offset
|
49
|
+
y_pos = position[1] - top
|
50
|
+
box.render(document, [x_pos, y_pos], box_width, height )
|
51
|
+
offset += box_width
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module TnPDF
|
2
|
+
class Report
|
3
|
+
attr_reader :page_header, :page_footer, :table, :record_collection
|
4
|
+
attr_accessor *Configuration.report_properties_names
|
5
|
+
|
6
|
+
def initialize(properties = {})
|
7
|
+
@page_header = PageSection.new
|
8
|
+
@page_footer = PageSection.new
|
9
|
+
@record_collection = Array.new
|
10
|
+
initialize_properties(properties)
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
private
|
15
|
+
|
16
|
+
def forward_property(object_name, property)
|
17
|
+
class_eval <<-STRING
|
18
|
+
def #{object_name}_#{property}
|
19
|
+
#{object_name}.#{property}
|
20
|
+
end
|
21
|
+
|
22
|
+
def #{object_name}_#{property}=(value)
|
23
|
+
#{object_name}.#{property} = value
|
24
|
+
end
|
25
|
+
STRING
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
Configuration.header_properties_names.each do |property|
|
31
|
+
forward_property("page_header", property)
|
32
|
+
end
|
33
|
+
|
34
|
+
Configuration.footer_properties_names.each do |property|
|
35
|
+
forward_property("page_footer", property)
|
36
|
+
end
|
37
|
+
|
38
|
+
Configuration.table_properties_names.each do |property|
|
39
|
+
forward_property("table", property)
|
40
|
+
end
|
41
|
+
|
42
|
+
def record_collection=(collection)
|
43
|
+
unless collection.kind_of? Array
|
44
|
+
raise ArgumentError, "collection should be an Array!"
|
45
|
+
end
|
46
|
+
@record_collection = table.collection = collection
|
47
|
+
end
|
48
|
+
|
49
|
+
def table_columns
|
50
|
+
table.columns || Array.new
|
51
|
+
end
|
52
|
+
|
53
|
+
def table_columns=(columns)
|
54
|
+
raise ArgumentError unless columns.kind_of? Array
|
55
|
+
columns.each do |column|
|
56
|
+
table.add_column column
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def render(filename)
|
61
|
+
document_width = document.bounds.width
|
62
|
+
page_header_position = [0, document.cursor]
|
63
|
+
page_footer_position = [0, Configuration[:page_footer_height]]
|
64
|
+
|
65
|
+
document.font(font)
|
66
|
+
document.font_size(font_size)
|
67
|
+
|
68
|
+
document.repeat :all do
|
69
|
+
page_header.render(document, page_header_position)
|
70
|
+
document.stroke_horizontal_rule
|
71
|
+
end
|
72
|
+
|
73
|
+
table_height = page_body_height
|
74
|
+
document.bounding_box([0, page_body_height+page_footer.total_height],
|
75
|
+
:width => document.bounds.width) do
|
76
|
+
table.render(document, table_height)
|
77
|
+
end
|
78
|
+
|
79
|
+
document.repeat :all do
|
80
|
+
page_footer.render(document, page_footer_position)
|
81
|
+
end
|
82
|
+
|
83
|
+
document.render_file filename
|
84
|
+
end
|
85
|
+
|
86
|
+
def page_body_height
|
87
|
+
height = document.bounds.height
|
88
|
+
height -= page_header.total_height
|
89
|
+
height -= page_footer.total_height
|
90
|
+
end
|
91
|
+
|
92
|
+
def document
|
93
|
+
@document ||= Prawn::Document.new(properties)
|
94
|
+
end
|
95
|
+
|
96
|
+
def table
|
97
|
+
@table ||= Table.new
|
98
|
+
end
|
99
|
+
|
100
|
+
# Configurable properties
|
101
|
+
|
102
|
+
def properties
|
103
|
+
Configuration.report_properties_names.inject({}) do |properties_hash, property|
|
104
|
+
properties_hash[property] = send(property)
|
105
|
+
properties_hash
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def initialize_properties(properties)
|
112
|
+
owned_properties = Configuration.report_properties_names
|
113
|
+
owned_properties += Configuration.footer_properties_names.map do |p|
|
114
|
+
"page_footer_#{p}"
|
115
|
+
end
|
116
|
+
owned_properties += Configuration.header_properties_names.map do |p|
|
117
|
+
"page_header_#{p}"
|
118
|
+
end
|
119
|
+
owned_properties += Configuration.table_properties_names.map do |p|
|
120
|
+
"table_#{p}"
|
121
|
+
end
|
122
|
+
|
123
|
+
owned_properties.each do |property|
|
124
|
+
properties[property] ||= Configuration[property]
|
125
|
+
send(:"#{property}=", properties[property])
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
data/lib/tn_pdf/table.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'tn_pdf/table_column'
|
2
|
+
require 'prawn'
|
3
|
+
|
4
|
+
module TnPDF
|
5
|
+
class Table
|
6
|
+
attr_accessor *Configuration.table_properties_names
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
Configuration.table_properties_names.each do |property|
|
10
|
+
send("#{property}=", Configuration["table_#{property}"])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def columns_hash
|
15
|
+
columns_hash = ActiveSupport::OrderedHash.new
|
16
|
+
columns.inject(columns_hash) do |hash, column|
|
17
|
+
hash[column.header] = column.to_proc
|
18
|
+
hash
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def columns_headers
|
23
|
+
columns.map(&:header)
|
24
|
+
end
|
25
|
+
|
26
|
+
def collection
|
27
|
+
@collection ||= Array.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def collection=(collection)
|
31
|
+
raise ArgumentError unless collection.kind_of? Array
|
32
|
+
@collection = collection
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_column(column)
|
36
|
+
unless column.kind_of? Column
|
37
|
+
column = Column.new(column)
|
38
|
+
end
|
39
|
+
columns << column
|
40
|
+
end
|
41
|
+
|
42
|
+
def rows
|
43
|
+
collection.map do |object|
|
44
|
+
columns.map do |column|
|
45
|
+
column.value_for(object)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def render(document, max_height)
|
51
|
+
table = document.make_table([columns_headers]+rows) do |table|
|
52
|
+
table.header = self.multipage_headers
|
53
|
+
table.cells.borders = [] unless self.borders
|
54
|
+
table.row_colors = [self.odd_row_color, self.even_row_color]
|
55
|
+
table.row(0).background_color = self.header_color
|
56
|
+
|
57
|
+
columns.each_with_index do |column, index|
|
58
|
+
style = column.style.reject { |k, v| [:format, :decimal].include? k }
|
59
|
+
table.columns(index).style(style)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
x_pos = x_pos_on(document, table.width)
|
63
|
+
document.bounding_box([x_pos, document.cursor],
|
64
|
+
:width => table.width,
|
65
|
+
:height => max_height) do
|
66
|
+
table.draw
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def columns
|
71
|
+
@columns ||= []
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def x_pos_on(document, table_width)
|
77
|
+
case align
|
78
|
+
when :left
|
79
|
+
0
|
80
|
+
when :center
|
81
|
+
(document.bounds.right - table_width)/2.0
|
82
|
+
when :right
|
83
|
+
document.bounds.right - table_width
|
84
|
+
else
|
85
|
+
0
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module TnPDF
|
2
|
+
class Table
|
3
|
+
|
4
|
+
class Column
|
5
|
+
attr_reader :header, :proc, :collection, :style
|
6
|
+
|
7
|
+
alias_method :to_proc, :proc
|
8
|
+
def initialize(arguments)
|
9
|
+
raise ArgumentError unless valid_column_args?(arguments)
|
10
|
+
@header = arguments[0].to_s
|
11
|
+
@proc = arguments[1].to_proc
|
12
|
+
@style = style_for(arguments[2])
|
13
|
+
end
|
14
|
+
|
15
|
+
def values_for(collection)
|
16
|
+
collection.map do |object|
|
17
|
+
value_for(object)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def value_for(object)
|
22
|
+
value = @proc.call(object)
|
23
|
+
method = if value.respond_to?(:strftime)
|
24
|
+
value.method(:strftime)
|
25
|
+
elsif value.respond_to?(:sprintf)
|
26
|
+
value.method(:sprintf)
|
27
|
+
else
|
28
|
+
method(:sprintf)
|
29
|
+
end
|
30
|
+
string = method.arity == 1 ?
|
31
|
+
method.call(style[:format]) :
|
32
|
+
method.call(style[:format], value)
|
33
|
+
|
34
|
+
string.gsub!(".", style[:decimal]) if style[:decimal]
|
35
|
+
return string
|
36
|
+
rescue TypeError
|
37
|
+
puts "WARNING: Bad format '#{style[:format]}' for value '#{value}'"
|
38
|
+
return value.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def valid_column_args?(column_args)
|
44
|
+
validity = true
|
45
|
+
validity &= column_args.kind_of? Array
|
46
|
+
validity &= column_args.count == 2 || column_args.count == 3
|
47
|
+
validity &= column_args[0].respond_to?(:to_s)
|
48
|
+
validity &= column_args[1].respond_to?(:to_proc)
|
49
|
+
rescue NoMethodError
|
50
|
+
valid = false
|
51
|
+
ensure
|
52
|
+
return validity
|
53
|
+
end
|
54
|
+
|
55
|
+
def style_for(type)
|
56
|
+
if type.nil?
|
57
|
+
{:format => "%s"}
|
58
|
+
elsif type.kind_of? Symbol
|
59
|
+
Configuration["column_#{type}"]
|
60
|
+
else
|
61
|
+
type
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
data/lib/tn_pdf.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support' # Because of to_options!
|
3
|
+
require 'prawn'
|
4
|
+
require 'prawn/measurement_extensions'
|
5
|
+
|
6
|
+
require 'tn_pdf/configuration'
|
7
|
+
require 'tn_pdf/report'
|
8
|
+
require 'tn_pdf/page_section'
|
9
|
+
require 'tn_pdf/box'
|
10
|
+
require 'tn_pdf/table'
|
11
|
+
require 'tn_pdf/table_column'
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Adds the "lib" dir to the load path
|
2
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
3
|
+
require 'tn_pdf'
|
4
|
+
|
5
|
+
# Used to 'stub' needed classes that doesn't exist (yet)
|
6
|
+
class EmptyClass
|
7
|
+
def method_missing(method,*args)
|
8
|
+
nil
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'prawn'
|
13
|
+
|
14
|
+
# Utility methods:
|
15
|
+
|
16
|
+
def random_string
|
17
|
+
(Time.now.to_f+rand(1000)).to_s
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module TnPDF
|
4
|
+
class PageSection
|
5
|
+
|
6
|
+
describe Box do
|
7
|
+
|
8
|
+
subject { Box.new( {} ) }
|
9
|
+
|
10
|
+
describe "creation options" do
|
11
|
+
it "accept texts and/or images" do
|
12
|
+
assigning_nice_values = lambda do
|
13
|
+
Box.new( { :text => "text" } )
|
14
|
+
Box.new( { :image => "image.jpg" } )
|
15
|
+
Box.new( { :text => "text", :image => "image.jpg" } )
|
16
|
+
end
|
17
|
+
assigning_nice_values.should_not raise_error
|
18
|
+
end
|
19
|
+
|
20
|
+
it "accepts extra text options to prawn by passing hashes" do
|
21
|
+
document = Prawn::Document.new
|
22
|
+
box = Box.new( :text => {:text => "text", :font => "DejaVu Sans"} )
|
23
|
+
|
24
|
+
document.should_receive(:text).with("text", hash_including(:font => "DejaVu Sans") )
|
25
|
+
box.render(document, [0,0], 100)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "accepts extra image options to prawn by passing hashes" do
|
29
|
+
document = Prawn::Document.new
|
30
|
+
box = Box.new( :image => {:path => "image.jpg", :width => 1.cm } )
|
31
|
+
|
32
|
+
document.should_receive(:image).with("image.jpg", hash_including(:width => 1.cm) )
|
33
|
+
box.render(document, [0,0], 100)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#render" do
|
38
|
+
let(:document) { Prawn::Document.new }
|
39
|
+
|
40
|
+
it "creates a bounding box on the provided document" do
|
41
|
+
document.should_receive(:bounding_box).with([0,0], hash_including(:width => 20.cm) )
|
42
|
+
subject.render(document, [0,0], 20.cm)
|
43
|
+
end
|
44
|
+
|
45
|
+
specify "when the box contains text, the text is rendered" do
|
46
|
+
subject = Box.new({ :text => "some text" })
|
47
|
+
|
48
|
+
document.should_receive(:text).with("some text", anything())
|
49
|
+
subject.render(document, [0, 0], 20.cm)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module TnPDF
|
4
|
+
|
5
|
+
describe PageSection do
|
6
|
+
|
7
|
+
describe "its left, center and right parts" do
|
8
|
+
let(:parts) { [:left, :center, :right] }
|
9
|
+
|
10
|
+
specify "exist and can be set" do
|
11
|
+
parts.each do |part|
|
12
|
+
subject.should respond_to(part)
|
13
|
+
subject.should respond_to(:"#{part}=")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
specify "are accessible as hashes, and also as boxes" do
|
18
|
+
parts.each do |part|
|
19
|
+
subject.send("#{part}=", {} )
|
20
|
+
subject.send("#{part}").should be_kind_of Hash
|
21
|
+
subject.send("#{part}_box").should be_kind_of PageSection::Box
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#render" do
|
27
|
+
let(:document) { Prawn::Document.new }
|
28
|
+
|
29
|
+
it "renders each of the defined boxes on the provided document" do
|
30
|
+
subject.left = {:text => "Blah"}
|
31
|
+
subject.center = {:text => "Blah"}
|
32
|
+
subject.right = {:text => "Blah"}
|
33
|
+
|
34
|
+
subject.left_box.should_receive(:render)
|
35
|
+
subject.center_box.should_receive(:render)
|
36
|
+
subject.right_box.should_receive(:render)
|
37
|
+
|
38
|
+
subject.render(document, [0, 0])
|
39
|
+
end
|
40
|
+
|
41
|
+
it "divides the available width between the defined boxes" do
|
42
|
+
subject.left = {:text => "Blah"}
|
43
|
+
subject.right = {:text => "Blah"}
|
44
|
+
|
45
|
+
subject.left_box.should_receive(:render).with(
|
46
|
+
document, [0, 0], document.bounds.width/2, nil)
|
47
|
+
|
48
|
+
subject.render(document, [0, 0])
|
49
|
+
|
50
|
+
|
51
|
+
subject = PageSection.new
|
52
|
+
subject.left = {:text => "Blah"}
|
53
|
+
|
54
|
+
subject.left_box.should_receive(:render).with(
|
55
|
+
document, [0, 0], document.bounds.width, nil)
|
56
|
+
|
57
|
+
subject.render(document, [0, 0])
|
58
|
+
end
|
59
|
+
|
60
|
+
it "translates the second and third boxes to the right" do
|
61
|
+
subject.left = {:text => "Blah"}
|
62
|
+
subject.center = {:text => "Blah"}
|
63
|
+
subject.right = {:text => "Blah"}
|
64
|
+
|
65
|
+
box_width = document.bounds.width/3
|
66
|
+
subject.left_box.should_receive(:render).with(
|
67
|
+
document, [0, 0], box_width, nil)
|
68
|
+
|
69
|
+
subject.center_box.should_receive(:render).with(
|
70
|
+
document, [box_width, 0], box_width, nil)
|
71
|
+
|
72
|
+
subject.right_box.should_receive(:render).with(
|
73
|
+
document, [box_width*2, 0], box_width, nil)
|
74
|
+
|
75
|
+
subject.render(document, [0, 0])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module TnPDF
|
4
|
+
const_set("PageSection", EmptyClass) unless const_defined?("PageSection")
|
5
|
+
const_set("Table", EmptyClass) unless const_defined?("Table")
|
6
|
+
|
7
|
+
describe Report do
|
8
|
+
|
9
|
+
describe "#page_header and #page_footer" do
|
10
|
+
specify "are page sections" do
|
11
|
+
subject.page_header.should be_kind_of TnPDF::PageSection
|
12
|
+
subject.page_footer.should be_kind_of TnPDF::PageSection
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "aren't publicly modifiable" do
|
16
|
+
direct_assignment = lambda do
|
17
|
+
subject.page_header = :some_section
|
18
|
+
subject.page_footer = :some_section
|
19
|
+
end
|
20
|
+
direct_assignment.should raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "are modifiable by page_header_left and page_footer_left" do
|
24
|
+
subject.page_header.should_receive(:left=)
|
25
|
+
subject.page_footer.should_receive(:left=)
|
26
|
+
|
27
|
+
subject.page_header_left = { :text => random_string }
|
28
|
+
subject.page_footer_left = { :text => random_string }
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "are modifiable by page_header_right and page_footer_right" do
|
32
|
+
subject.page_header.should_receive(:right=)
|
33
|
+
subject.page_footer.should_receive(:right=)
|
34
|
+
|
35
|
+
subject.page_header_right = { :text => random_string }
|
36
|
+
subject.page_footer_right = { :text => random_string }
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "are modifiable by page_header_center and page_footer_center" do
|
40
|
+
subject.page_header.should_receive(:center=)
|
41
|
+
subject.page_footer.should_receive(:center=)
|
42
|
+
|
43
|
+
subject.page_header_center = { :text => random_string }
|
44
|
+
subject.page_footer_center = { :text => random_string }
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#record_collection" do
|
50
|
+
it "is a kind of array" do
|
51
|
+
subject.record_collection.should be_kind_of Array
|
52
|
+
end
|
53
|
+
|
54
|
+
it "is settable by the record_collection= method" do
|
55
|
+
subject.record_collection = [:a, :b, :c]
|
56
|
+
subject.record_collection.should == [:a, :b, :c]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "doesn't accept non-array arguments" do
|
60
|
+
invalid_values = [ Hash.new, :collection, "collection", 123]
|
61
|
+
invalid_values.each do |invalid_value|
|
62
|
+
setting_invalid_value = lambda do
|
63
|
+
subject.record_collection = invalid_value
|
64
|
+
end
|
65
|
+
setting_invalid_value.should raise_error
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#table_columns" do
|
71
|
+
it "is a kind of array" do
|
72
|
+
subject.table_columns.should be_kind_of Array
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#table_columns=" do
|
77
|
+
it "is a proxy to Table#add_column" do
|
78
|
+
subject.table.should_receive(:add_column)
|
79
|
+
|
80
|
+
column = [ [ 'String' , :to_s ] ]
|
81
|
+
subject.table_columns = column
|
82
|
+
end
|
83
|
+
|
84
|
+
it "works with multiple columns" do
|
85
|
+
subject.table.should_receive(:add_column).exactly(3).times
|
86
|
+
|
87
|
+
columns = [ [ 'String' => :to_s] ,
|
88
|
+
[ 'Object id' => :object_id] ,
|
89
|
+
[ 'Name' => :name] ]
|
90
|
+
subject.table_columns = columns
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#document" do
|
95
|
+
it "returns the Prawn::Document associated with the report" do
|
96
|
+
subject.document.should be_kind_of Prawn::Document
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it "has all the properties defined on Report" do
|
101
|
+
Configuration.properties_names do |property|
|
102
|
+
subject.should respond_to(property)
|
103
|
+
subject.should respond_to(:"#{property}=")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
specify "passing a hash of options on initialization works" do
|
108
|
+
properties = { :page_layout => :portrait,
|
109
|
+
:left_margin => 5.cm }
|
110
|
+
subject = Report.new(properties)
|
111
|
+
subject.page_layout.should == :portrait
|
112
|
+
subject.left_margin.should == 5.cm
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module TnPDF
|
4
|
+
class Table
|
5
|
+
describe Column do
|
6
|
+
|
7
|
+
context "to be valid, needs a parameter that is" do
|
8
|
+
|
9
|
+
specify "an Array" do
|
10
|
+
creating_bad_column = Proc.new { Table::Column.new :not_an_array }
|
11
|
+
creating_bad_column.should raise_error ArgumentError
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "an Array whose 1st member can be coerced into a String" do
|
15
|
+
bad_string = /not_a_string/
|
16
|
+
class << bad_string
|
17
|
+
undef_method(:to_s)
|
18
|
+
end
|
19
|
+
creating_bad_column = Proc.new { Table::Column.new [bad_string, :to_s] }
|
20
|
+
creating_bad_column.should raise_error ArgumentError
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "an Array whose 2nd member can be coerced into a Proc" do
|
24
|
+
bad_columns = [["String", "to_s"], ["String", 3], ["String", /to_s/]]
|
25
|
+
bad_columns.each do |bad_column|
|
26
|
+
creating_bad_column = Proc.new { Table::Column.new bad_column }
|
27
|
+
creating_bad_column.should raise_error ArgumentError
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#header" do
|
33
|
+
it "returns the string value that was passed as parameter" do
|
34
|
+
column_args = ["String", :to_s]
|
35
|
+
column = Table::Column.new(column_args)
|
36
|
+
column.header.should == "String"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#to_proc" do
|
41
|
+
it "returns the proc that was passed as parameter" do
|
42
|
+
my_proc = Proc.new { |object| object.to_s }
|
43
|
+
column = Table::Column.new([123, my_proc])
|
44
|
+
column.to_proc.should == my_proc
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#values_for" do
|
49
|
+
it "maps received values to the underlying proc" do
|
50
|
+
column = Table::Column.new(["String", :to_s])
|
51
|
+
numbers = (1..3).to_a
|
52
|
+
column.values_for(numbers).should == %w[1 2 3]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#value_for" do
|
57
|
+
it "maps received object to the underlying proc" do
|
58
|
+
column = Table::Column.new(["String", :to_s])
|
59
|
+
column.value_for(25).should == "25"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module TnPDF
|
4
|
+
class Table
|
5
|
+
const_set("Column",EmptyClass) unless const_defined?("Column")
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Table do
|
9
|
+
describe "#columns_hash" do
|
10
|
+
it "is a kind of Hash" do
|
11
|
+
subject.columns_hash.should be_kind_of(Hash)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "is ordered" do
|
15
|
+
subject.add_column ["A", :A]
|
16
|
+
subject.add_column ["B", :B]
|
17
|
+
subject.add_column ["C", :C]
|
18
|
+
|
19
|
+
subject.columns_hash.keys.should == %w[A B C]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#collection" do
|
24
|
+
it "is a kind of Array" do
|
25
|
+
subject.collection.should be_kind_of(Array)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "is settable, as long as the parameter is also an Array" do
|
29
|
+
setting_good_parameter = Proc.new do
|
30
|
+
subject.collection = [:a, :b, :c]
|
31
|
+
end
|
32
|
+
setting_good_parameter.should_not raise_error
|
33
|
+
subject.collection.should == [:a, :b, :c]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "raises an error if someone tries to set it as a non-array" do
|
37
|
+
setting_bad_parameter = Proc.new do
|
38
|
+
subject.collection = :not_an_array
|
39
|
+
end
|
40
|
+
setting_bad_parameter.should raise_error
|
41
|
+
subject.collection.should_not == :not_an_array
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#add_column" do
|
46
|
+
let(:valid_column) { column = ["String", :to_s] }
|
47
|
+
|
48
|
+
it "adds a supplied column to the columns hash" do
|
49
|
+
adding_valid_column = Proc.new do
|
50
|
+
subject.add_column(valid_column)
|
51
|
+
end
|
52
|
+
adding_valid_column.should change(subject.columns, :count).by(1)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should the argument be a column, it adds directly" do
|
56
|
+
column = Table::Column.new [ "String", :to_s ]
|
57
|
+
subject.add_column(column)
|
58
|
+
subject.columns.should include(column)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#columns_headers" do
|
63
|
+
it "returns the columns headers" do
|
64
|
+
subject.add_column ["String", :to_s]
|
65
|
+
subject.add_column ["Integer", :to_i]
|
66
|
+
subject.add_column ["Mean", :mean]
|
67
|
+
|
68
|
+
subject.columns_headers.should == %w[String Integer Mean]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#rows" do
|
73
|
+
it "returns the values of the objects for each column" do
|
74
|
+
subject = Table.new # Just to be explicit
|
75
|
+
subject.add_column( ["String", :to_s] )
|
76
|
+
subject.add_column( ["Integer", :to_i] )
|
77
|
+
subject.add_column( ["Doubled", Proc.new { |x| x*2 } ] )
|
78
|
+
|
79
|
+
subject.collection = [1, 2, 3]
|
80
|
+
subject.rows.should == [ ["1", 1, 2],
|
81
|
+
["2", 2, 4],
|
82
|
+
["3", 3, 6] ]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#render" do
|
87
|
+
let(:document) do
|
88
|
+
mock("Prawn::Document").as_null_object
|
89
|
+
end
|
90
|
+
|
91
|
+
let(:table) do
|
92
|
+
mock("Prawn::Table").as_null_object
|
93
|
+
end
|
94
|
+
|
95
|
+
it "instantiates a Prawn::Table instance" do
|
96
|
+
document.should_receive(:make_table).and_return(table)
|
97
|
+
subject.render(document, 0)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/tn_pdf.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "tn_pdf/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "tn_pdf"
|
7
|
+
s.version = TnPDF::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Renato Riccieri Santos Zannon"]
|
10
|
+
s.email = ["zannon@tecnologiaenegocios.com.br"]
|
11
|
+
s.summary = %q{A simple wrapper around prawn, devised to the generation of table-centric reports}
|
12
|
+
|
13
|
+
s.rubyforge_project = "tn_pdf"
|
14
|
+
|
15
|
+
s.add_dependency('prawn', '~> 0.11.1')
|
16
|
+
s.add_dependency('activesupport')
|
17
|
+
|
18
|
+
s.add_development_dependency('rspec', '~> 2.6.0')
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tn_pdf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Renato Riccieri Santos Zannon
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-05-31 00:00:00 -03:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: prawn
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 49
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 11
|
33
|
+
- 1
|
34
|
+
version: 0.11.1
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: activesupport
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: rspec
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 23
|
60
|
+
segments:
|
61
|
+
- 2
|
62
|
+
- 6
|
63
|
+
- 0
|
64
|
+
version: 2.6.0
|
65
|
+
type: :development
|
66
|
+
version_requirements: *id003
|
67
|
+
description:
|
68
|
+
email:
|
69
|
+
- zannon@tecnologiaenegocios.com.br
|
70
|
+
executables: []
|
71
|
+
|
72
|
+
extensions: []
|
73
|
+
|
74
|
+
extra_rdoc_files: []
|
75
|
+
|
76
|
+
files:
|
77
|
+
- .gitignore
|
78
|
+
- Gemfile
|
79
|
+
- Gemfile.lock
|
80
|
+
- Rakefile
|
81
|
+
- lib/tn_pdf.rb
|
82
|
+
- lib/tn_pdf/box.rb
|
83
|
+
- lib/tn_pdf/configuration.rb
|
84
|
+
- lib/tn_pdf/page_section.rb
|
85
|
+
- lib/tn_pdf/report.rb
|
86
|
+
- lib/tn_pdf/table.rb
|
87
|
+
- lib/tn_pdf/table_column.rb
|
88
|
+
- lib/tn_pdf/version.rb
|
89
|
+
- spec/spec_helper.rb
|
90
|
+
- spec/tn_pdf/box_spec.rb
|
91
|
+
- spec/tn_pdf/page_section_spec.rb
|
92
|
+
- spec/tn_pdf/report_spec.rb
|
93
|
+
- spec/tn_pdf/table_column_spec.rb
|
94
|
+
- spec/tn_pdf/table_spec.rb
|
95
|
+
- tn_pdf.gemspec
|
96
|
+
has_rdoc: true
|
97
|
+
homepage:
|
98
|
+
licenses: []
|
99
|
+
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
hash: 3
|
111
|
+
segments:
|
112
|
+
- 0
|
113
|
+
version: "0"
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
hash: 3
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
version: "0"
|
123
|
+
requirements: []
|
124
|
+
|
125
|
+
rubyforge_project: tn_pdf
|
126
|
+
rubygems_version: 1.5.2
|
127
|
+
signing_key:
|
128
|
+
specification_version: 3
|
129
|
+
summary: A simple wrapper around prawn, devised to the generation of table-centric reports
|
130
|
+
test_files: []
|
131
|
+
|