tn_pdf 0.0.2 → 0.0.8

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 91cb0ad993ddca5fbf6fe2c810cd947e59c5cb3f
4
+ data.tar.gz: 2aa092c489969b92567973b078b52dd90aac4c4b
5
+ SHA512:
6
+ metadata.gz: c7e55005eecbf3e0dce9b5997efd57e33a62bb1075921ffddb4639e133175ef00ba3ecbbf9ed554eb5ba7c7ee5cc7ed3d7d59086e82d5c95f57c3c8d5bdf787e
7
+ data.tar.gz: 92dd019191ee1f274d5ea381ce53feb5ce45ded1816643e6c2f570bbabdde97bc239e55079f97f799a5b8fb9847dd133a766abb8712d24256e10886aed33461d
data/.gitignore CHANGED
@@ -7,3 +7,6 @@ coverage
7
7
  *.gem
8
8
  Gemfile.lock
9
9
  pkg/*
10
+ doc/
11
+ .yardoc
12
+ *.rbc
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.8.7 || rvm use ree || rvm use default
data/Gemfile CHANGED
@@ -2,3 +2,6 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in tn_pdf.gemspec
4
4
  gemspec
5
+
6
+ gem 'rake'
7
+ gem 'pry'
data/Gemfile.lock CHANGED
@@ -1,47 +1,56 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ tn_pdf (0.0.8)
5
+ backports
6
+ prawn (~> 0.11.1)
7
+
1
8
  GEM
2
9
  remote: http://rubygems.org/
3
10
  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)
11
+ Ascii85 (1.0.2)
12
+ afm (0.2.2)
13
+ backports (3.6.0)
9
14
  coderay (0.9.8)
10
15
  diff-lcs (1.1.2)
11
- method_source (0.4.1)
16
+ hashery (2.1.1)
17
+ method_source (0.6.0)
12
18
  ruby_parser (>= 2.0.5)
13
- pdf-reader (0.9.2)
14
- Ascii85 (>= 0.9)
19
+ pdf-reader (1.3.3)
20
+ Ascii85 (~> 1.0.0)
21
+ afm (~> 0.2.0)
22
+ hashery (~> 2.0)
23
+ ruby-rc4
24
+ ttfunk
15
25
  prawn (0.11.1)
16
26
  pdf-reader (>= 0.9.0)
17
27
  ttfunk (~> 1.0.0)
18
- pry (0.8.3)
19
- coderay (>= 0.9.7)
20
- method_source (>= 0.4.0)
28
+ pry (0.9.2)
29
+ coderay (>= 0.9.8)
30
+ method_source (>= 0.6.0)
21
31
  ruby_parser (>= 2.0.5)
22
- slop (>= 1.5.3)
23
- rcov (0.9.9)
32
+ slop (~> 1.9.0)
33
+ rake (0.9.2)
24
34
  rspec (2.6.0)
25
35
  rspec-core (~> 2.6.0)
26
36
  rspec-expectations (~> 2.6.0)
27
37
  rspec-mocks (~> 2.6.0)
28
- rspec-core (2.6.1)
38
+ rspec-core (2.6.4)
29
39
  rspec-expectations (2.6.0)
30
40
  diff-lcs (~> 1.1.2)
31
41
  rspec-mocks (2.6.0)
42
+ ruby-rc4 (0.1.5)
32
43
  ruby_parser (2.0.6)
33
44
  sexp_processor (~> 3.0)
34
45
  sexp_processor (3.0.5)
35
- slop (1.6.0)
36
- ttfunk (1.0.1)
46
+ slop (1.9.1)
47
+ ttfunk (1.0.3)
37
48
 
38
49
  PLATFORMS
39
50
  ruby
40
51
 
41
52
  DEPENDENCIES
42
- activesupport
43
- autotest
44
- prawn (~> 0.11.1)
45
53
  pry
46
- rcov
54
+ rake
47
55
  rspec (~> 2.6.0)
56
+ tn_pdf!
data/Rakefile CHANGED
@@ -2,11 +2,10 @@ require 'rake'
2
2
  require 'bundler'
3
3
  Bundler::GemHelper.install_tasks
4
4
 
5
- require 'spec/rake/spectask'
5
+ require 'rspec/core/rake_task'
6
6
 
7
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']
8
+ RSpec::Core::RakeTask.new('examples_with_rcov') do |t|
10
9
  t.rcov = true
11
- t.rcov_opts = ['--exclude', 'spec,.bundle']
10
+ # t.rcov_opts = ['--exclude', 'spec,.bundle']
12
11
  end
@@ -0,0 +1,11 @@
1
+ module Prawn
2
+ class Table
3
+ alias_method :column_widths_broken=, :column_widths=
4
+
5
+ def column_widths_fixed=(value)
6
+ @column_widths = (column_widths_broken = value)
7
+ end
8
+
9
+ alias_method :column_widths=, :column_widths_fixed=
10
+ end
11
+ end
data/lib/tn_pdf/box.rb CHANGED
@@ -2,34 +2,38 @@ module TnPDF
2
2
  class PageSection
3
3
 
4
4
  class Box
5
- attr_reader :image_path, :image_options
5
+ attr_reader :image_file, :image_options
6
6
  attr_reader :text, :text_options
7
+ attr_accessor :width
7
8
 
8
9
  def initialize(options)
9
10
  parse_options(options)
10
11
  end
11
12
 
12
- def render(document, pos, width, height=nil)
13
+ def render(document, pos, height=nil)
13
14
  options_hash = { :width => width }
14
15
  options_hash[:height] = height unless height.nil?
15
16
 
16
17
  document.bounding_box(pos, options_hash) do
17
18
  if has_image?
18
- image_args = [image_path]
19
+ image_args = [image_file]
19
20
  image_args << image_options unless image_options.empty?
20
21
  document.image *image_args
21
22
  end
22
23
 
23
24
  if has_text?
24
25
  text_args = [text]
25
- text_args << text_options unless text_options.empty?
26
- document.text *text_args
26
+ text_options.merge!({:inline_format => true})
27
+ text_args << text_options
28
+ document.font(text_options[:font], text_options[:font_options]) do
29
+ document.text *text_args
30
+ end
27
31
  end
28
32
  end
29
33
  end
30
34
 
31
35
  def has_image?
32
- !image_path.nil?
36
+ !image_file.nil?
33
37
  end
34
38
 
35
39
  def has_text?
@@ -39,8 +43,9 @@ module TnPDF
39
43
  private
40
44
 
41
45
  def parse_options(options)
42
- options.to_options!
46
+ options = Configuration.perform_conversions(options)
43
47
 
48
+ self.width = options[:width]
44
49
  options[:align] ||= :justify
45
50
  options[:valign] ||= :center
46
51
 
@@ -51,19 +56,31 @@ module TnPDF
51
56
  options[:text][:align] = options[:align]
52
57
  options[:text][:valign] = options[:valign]
53
58
 
59
+ options[:text][:font] ||= Configuration[:font]
60
+ options[:text][:font_options] = {}
61
+ options[:text][:font_options][:size] =
62
+ options[:text][:font_size] if options[:text][:font_size]
63
+
64
+ options[:text][:font_options][:style] =
65
+ options[:text][:font_style] if options[:text][:font_style]
66
+
54
67
  @text = options[:text][:text]
55
68
  @text_options = options[:text].reject { |k,_| k == :text }
56
69
  end
57
70
 
58
71
  if options[:image]
59
72
  unless options[:image].kind_of? Hash
60
- options[:image] = { :path => options[:image] }
73
+ options[:image] = { :file => options[:image] }
61
74
  end
62
75
  options[:image][:position] = options[:align]
63
76
  options[:image][:vposition] = options[:valign]
64
77
 
65
- @image_path = options[:image][:path]
66
- @image_options = options[:image].reject { |k,_| k == :path }
78
+ path = Configuration[:images_path]
79
+ @image_file = options[:image][:file]
80
+ unless @image_file =~ /^#{path}/
81
+ @image_file = File.join(path, @image_file)
82
+ end
83
+ @image_options = options[:image].reject { |k,_| k == :file }
67
84
  end
68
85
  end
69
86
 
@@ -4,27 +4,24 @@ module TnPDF
4
4
  class Configuration
5
5
  class << self
6
6
 
7
-
8
7
  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]
8
+ (hash, key) = filter_property(property)
9
+ value = hash[key]
10
+
11
+ if value.kind_of?(Proc)
12
+ value.call
13
+ else
14
+ value
25
15
  end
26
16
  end
27
17
 
18
+ def []=(property, value)
19
+ (hash, key) = filter_property(property)
20
+ value = perform_conversions(value)
21
+
22
+ hash[key] = value
23
+ end
24
+
28
25
  def report_properties_names
29
26
  report.keys
30
27
  end
@@ -50,9 +47,31 @@ module TnPDF
50
47
 
51
48
  def load_from(yaml_file)
52
49
  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!
50
+
51
+ configurations.each do |item, value|
52
+ value = perform_conversions(value)
53
+ self.send(item).merge! value
54
+ end
55
+ end
56
+
57
+ def perform_conversions(value)
58
+ match = value.match(/^(\d+\.?\d*)(cm|mm)$/) rescue nil
59
+ if match
60
+ num = match[1].to_f
61
+ conversion = match[2].to_sym
62
+ num.send(conversion)
63
+ elsif value.kind_of? Hash
64
+ value.inject({}) do |hash, (key, value)|
65
+ hash[key.to_sym] = perform_conversions(value)
66
+ hash
67
+ end
68
+ elsif value.kind_of? Array
69
+ value.inject([]) do |array, value|
70
+ array << perform_conversions(value)
71
+ array
72
+ end
73
+ else
74
+ value
56
75
  end
57
76
  end
58
77
 
@@ -68,6 +87,9 @@ module TnPDF
68
87
  :bottom_margin => 0.cm,
69
88
  :font => "Courier",
70
89
  :font_size => 10,
90
+ :images_path => "./",
91
+ :text_before_table => "",
92
+ :text_after_table => "",
71
93
  }
72
94
  end
73
95
 
@@ -76,6 +98,9 @@ module TnPDF
76
98
  :height => 1.cm,
77
99
  :top => 0.5.cm,
78
100
  :bottom => 0.2.cm,
101
+ :left => {},
102
+ :right => {},
103
+ :center => {}
79
104
  }
80
105
  end
81
106
 
@@ -85,17 +110,30 @@ module TnPDF
85
110
  :height => 1.cm,
86
111
  :top => 0.2.cm,
87
112
  :bottom => 0.1.cm,
113
+ :left => {},
114
+ :right => {},
115
+ :center => {}
88
116
  }
89
117
  end
90
118
 
91
119
  def table
92
120
  @table ||= {
93
121
  :align => :center,
122
+ :text_before => "",
123
+ :text_after => "",
94
124
  :multipage_headers => true,
95
- :borders => false,
125
+ :font_size => 10,
126
+ :header_font_style => :bold,
127
+ :header_font_size => 10,
128
+ :header_font => 'Courier',
96
129
  :header_color => "FF0000",
130
+ :footer_font_style => :bold,
131
+ :footer_font_size => 10,
132
+ :footer_font => 'Courier',
133
+ :footer_color => "FF0000",
97
134
  :odd_row_color => "00FF00",
98
135
  :even_row_color => "0000FF",
136
+ :borders => false,
99
137
  }
100
138
  end
101
139
 
@@ -116,9 +154,33 @@ module TnPDF
116
154
  :decimal => "," },
117
155
 
118
156
  :text => { :format => "%s",
119
- :align => :left }
157
+ :align => :left },
158
+
159
+ :right_text => { :format => "%s",
160
+ :align => :right },
120
161
  }
121
162
  end
163
+
164
+ def filter_property(property)
165
+ property = property.to_s
166
+ case property
167
+ when /^page_header_/
168
+ property_key = property.sub('page_header_','').to_sym
169
+ [header,property_key]
170
+ when /^page_footer_/
171
+ property_key = property.sub('page_footer_','').to_sym
172
+ [footer,property_key]
173
+ when /^table_/
174
+ property_key = property.sub('table_','').to_sym
175
+ [table,property_key]
176
+ when /^column_/
177
+ property_key = property.sub('column_','').to_sym
178
+ [column,property_key]
179
+ else
180
+ [report,property.to_sym]
181
+ end
182
+ end
183
+
122
184
  end
123
185
  end
124
186
  end
@@ -28,7 +28,7 @@ module TnPDF
28
28
  end
29
29
 
30
30
  def boxes
31
- [left_box, center_box, right_box].compact
31
+ [left_box, center_box, right_box]
32
32
  end
33
33
 
34
34
  def top
@@ -42,13 +42,20 @@ module TnPDF
42
42
  def render(document, position)
43
43
  width ||= document.bounds.width
44
44
 
45
- box_width = width/(boxes.count)
45
+ box_width = width/(boxes.compact.count)
46
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
47
+ boxes.each do |box|
48
+ box.width ||= box_width
49
+ end
50
+
51
+ boxes_with_positions = [
52
+ boxes,
53
+ [position[0], (width-boxes[1].width)/2, width-boxes[2].width],
54
+ [position[1]-top]*3
55
+ ].transpose
56
+
57
+ boxes_with_positions.each do |box, x_pos, y_pos|
58
+ box.render(document, [x_pos, y_pos], height )
52
59
  end
53
60
  end
54
61
 
data/lib/tn_pdf/report.rb CHANGED
@@ -1,6 +1,118 @@
1
1
  module TnPDF
2
+ # @author Renato Zannon
3
+ # The {TnPDF::Report} class is the easier, best supported and more complete
4
+ # way to access the TnPDF API. It delegates most of it's work to fellow classes
5
+ # such as {TnPDF::Table} and {TnPDF::PageSection}, but the delegation
6
+ # itself and all the necessary setup is made under the hood.
7
+ # This means that everything the code needs to do is access methods such as
8
+ # {#record_collection} and {#table_columns}, and then it is forwarded to the
9
+ # appropriate member.
10
+ #
11
+ # For more specific needs, methods such as {#table} and {#page_header}, which
12
+ # give direct access to the report's members, are provided, so that the user can
13
+ # make low-level/bleeding edge adjustments without being restricted to this
14
+ # classe's interface.
15
+ #
16
+ # @attr [String] page_size
17
+ # The report's page size, in paper sizes such as "A4" and "Letter".
18
+ # Supports as much as much paper sizes as Prawn does.
19
+ #
20
+ # @attr [Symbol] page_layout
21
+ # The resulting pages' layout. Must be :landscape or :portrait
22
+ #
23
+ # @attr [Double] left_margin
24
+ # Sets the document's left margin. Accepts a Double value, defined in PDF
25
+ # points (1/72 inch) or a String "in" cm or mm, such as "1.5cm" and
26
+ # "50mm".
27
+ #
28
+ # @attr [Double] right_margin
29
+ # Sets the document's right margin. Accepts a Double value, defined in PDF
30
+ # points (1/72 inch) or a String "in" cm or mm, such as "1.5cm" and
31
+ # "50mm".
32
+ #
33
+ # @attr [Double] bottom_margin
34
+ # Sets the document's bottom margin. Accepts a Double value, defined in PDF
35
+ # points (1/72 inch) or a String "in" cm or mm, such as "1.5cm" and
36
+ # "50mm".
37
+ #
38
+ # @attr [Double] top_margin
39
+ # Sets the document's top margin. Accepts a Double value, defined in PDF
40
+ # points (1/72 inch) or a String "in" cm or mm, such as "1.5cm" and
41
+ # "50mm".
42
+ #
43
+ # @attr [String] font
44
+ # The default font to be used on the report. The only (currently) supported
45
+ # choices are "Helvetica" and "Courier", although Prawn's font embedding
46
+ # mechanism is a probable upcoming addition
47
+ #
48
+ # @attr [Fixnum] font_size
49
+ # The default report font size. In the usual "points" unit.
50
+ #
51
+ # @attr [String] images_path
52
+ # The path from where we will search for the requested images. Defaults to
53
+ # the current path, "./". In a Rails application, for instance, you would
54
+ # probably want to set this guy to RAILS_ROOT+"public/images"
55
+ #
56
+ # @attr [String, Array] text_before_table
57
+ # Some text to be rendered before the report's table. Can be used as some
58
+ # kind of prelude/explanation/introduction etc.
59
+ # It accepts a normal string, that will be rendered using the settings on
60
+ # {#font} and {#font_size}, or an Array containing a String and a Hash,
61
+ # the last being an hash of options, as accepted by Prawn's Document#text
62
+ # method.
63
+ # @example
64
+ # report.text_before_table = "Some text"
65
+ # @example
66
+ # report.text_before_table = ["Some text", :size => 20]
67
+ #
68
+ # @attr [String, Array] text_after_table
69
+ # Some text to be rendered after the report's table. Can be used as a
70
+ # confirmation, a conclusion, an acceptance term etc.
71
+ # It accepts a normal string, that will be rendered using the settings on
72
+ # {#font} and {#font_size}, or an Array containing a String and a Hash,
73
+ # the last being an hash of options, as accepted by Prawn's Document#text
74
+ # method.
75
+ # @example
76
+ # report.text_after_table = "Some text"
77
+ # @example
78
+ # report.text_after_table = ["Some text", :size => 20]
2
79
  class Report
3
- attr_reader :page_header, :page_footer, :table, :record_collection
80
+
81
+ # The underlying {PageSection} that represents the report pages' headers. Direct
82
+ # manipulation is disencouraged.
83
+ # @return [PageSection]
84
+ attr_reader :page_header
85
+
86
+ # The underlying {PageSection} that represents the report pages' footers. Direct
87
+ # manipulation is disencouraged.
88
+ # @return [PageSection]
89
+ attr_reader :page_footer
90
+
91
+ # The underlying {Table}. Direct manipulation is disencouraged, except in
92
+ # cases where fine adjustments are required, or when some (possibly
93
+ # bleeding-edge) functionality is not implemented on {Report} (yet).
94
+ # @return [Table]
95
+ attr_reader :table
96
+
97
+ def table
98
+ @table ||= Table.new(document)
99
+ end
100
+
101
+ # The underlying {Table}'s collection of objects. Each of these objects
102
+ # will be represented as a table row, by the application of the procedures
103
+ # described using {#table_columns=}. The order in which this property and
104
+ # {#table_columns} are called isn't an issue, as soon as *both* are set
105
+ # before {#render} is called.
106
+ # @return [Array]
107
+ attr_accessor :record_collection
108
+
109
+ def record_collection=(collection)
110
+ unless collection.kind_of? Array
111
+ raise ArgumentError, "collection should be an Array!"
112
+ end
113
+ @record_collection = table.collection = collection
114
+ end
115
+
4
116
  attr_accessor *Configuration.report_properties_names
5
117
 
6
118
  def initialize(properties = {})
@@ -39,24 +151,40 @@ module TnPDF
39
151
  forward_property("table", property)
40
152
  end
41
153
 
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
-
154
+ # The underlying {Table}'s collection of columns. Refer to
155
+ # {Table#add_column} for reference on the Array's structure.
156
+ # @return [Array]
49
157
  def table_columns
50
158
  table.columns || Array.new
51
159
  end
52
160
 
161
+ # Forwards the received arguments to {Table}. Refer to {Table#add_column}
162
+ # for a reference on the valid structure.
163
+ # Be advised that calling this method *resets* the columns set previously,
164
+ # insted of just adding new ones.
165
+ # @param [Array] columns An Array of elements as accepted by {Table#add_column}
166
+ # @raise [ArgumentError] When the argument isn't an Array
53
167
  def table_columns=(columns)
54
168
  raise ArgumentError unless columns.kind_of? Array
169
+ table.reset_columns
55
170
  columns.each do |column|
56
171
  table.add_column column
57
172
  end
58
173
  end
59
174
 
175
+ # Forwards the arguments to the underlying {Table}. Refer to
176
+ # {Table#add_footer Table#add_footer} for a full description of the
177
+ # required syntax
178
+ def add_table_footer(row=nil, &block)
179
+ table.add_footer(row, &block)
180
+ end
181
+
182
+ # Renders the report on filename. This method also calls render on the
183
+ # report's members - {#table}, {#page_header} and {#page_footer} - which means
184
+ # that, on a straightforward report, the process is reduced to set up {#table_columns}
185
+ # and {#record_collection}, and then calling {#render}. This alone already does all the
186
+ # trick to generate the PDF.
187
+ # @param [String, IO] filename The file on which the report will render
60
188
  def render(filename)
61
189
  document_width = document.bounds.width
62
190
  page_header_position = [0, document.cursor]
@@ -71,9 +199,10 @@ module TnPDF
71
199
  end
72
200
 
73
201
  table_height = page_body_height
74
- document.bounding_box([0, page_body_height+page_footer.total_height],
202
+
203
+ document.bounding_box([0, document.cursor],
75
204
  :width => document.bounds.width) do
76
- table.render(document, table_height)
205
+ table.render(table_height)
77
206
  end
78
207
 
79
208
  document.repeat :all do
@@ -83,21 +212,15 @@ module TnPDF
83
212
  document.render_file filename
84
213
  end
85
214
 
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
-
215
+ # The underlying {Prawn::Document}. Direct manipulation is highly
216
+ # disencouraged, except for querying information.
217
+ # @return [Prawn::Document]
218
+ attr_reader :document
92
219
  def document
93
220
  @document ||= Prawn::Document.new(properties)
94
221
  end
95
222
 
96
- def table
97
- @table ||= Table.new
98
- end
99
-
100
- # Configurable properties
223
+ private
101
224
 
102
225
  def properties
103
226
  Configuration.report_properties_names.inject({}) do |properties_hash, property|
@@ -106,7 +229,11 @@ module TnPDF
106
229
  end
107
230
  end
108
231
 
109
- private
232
+ def page_body_height
233
+ height = document.bounds.height
234
+ height -= page_header.total_height
235
+ height -= page_footer.total_height
236
+ end
110
237
 
111
238
  def initialize_properties(properties)
112
239
  owned_properties = Configuration.report_properties_names