prawn 1.0.0 → 1.1.0

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.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -2
  3. data/lib/prawn.rb +5 -3
  4. data/lib/prawn/document.rb +7 -7
  5. data/lib/prawn/document/bounding_box.rb +1 -1
  6. data/lib/prawn/document/column_box.rb +3 -3
  7. data/lib/prawn/document/internals.rb +1 -1
  8. data/lib/prawn/font.rb +24 -8
  9. data/lib/prawn/font/afm.rb +4 -4
  10. data/lib/prawn/font_metric_cache.rb +1 -1
  11. data/lib/prawn/grid.rb +3 -1
  12. data/lib/prawn/image_handler.rb +3 -1
  13. data/lib/prawn/images/jpg.rb +1 -1
  14. data/lib/prawn/measurement_extensions.rb +1 -1
  15. data/lib/prawn/outline.rb +3 -1
  16. data/lib/prawn/security.rb +1 -1
  17. data/lib/prawn/security/arcfour.rb +4 -2
  18. data/lib/prawn/table.rb +14 -2
  19. data/lib/prawn/table/cell.rb +1 -1
  20. data/lib/prawn/table/cells.rb +1 -50
  21. data/lib/prawn/table/column_width_calculator.rb +131 -10
  22. data/lib/prawn/text.rb +1 -1
  23. data/lib/prawn/text/formatted.rb +2 -0
  24. data/lib/prawn/text/formatted/arranger.rb +1 -1
  25. data/lib/prawn/text/formatted/box.rb +5 -5
  26. data/lib/prawn/text/formatted/line_wrap.rb +1 -1
  27. data/lib/prawn/text/formatted/wrap.rb +2 -0
  28. data/lib/prawn/utilities.rb +3 -3
  29. data/manual/absolute_position.pdf +0 -0
  30. data/manual/basic_concepts/adding_pages.rb +3 -3
  31. data/manual/basic_concepts/basic_concepts.rb +6 -6
  32. data/manual/basic_concepts/cursor.rb +5 -5
  33. data/manual/basic_concepts/measurement.rb +2 -2
  34. data/manual/basic_concepts/origin.rb +3 -3
  35. data/manual/basic_concepts/other_cursor_helpers.rb +6 -6
  36. data/manual/bounding_box/bounding_box.rb +6 -6
  37. data/manual/bounding_box/bounds.rb +6 -6
  38. data/manual/bounding_box/canvas.rb +2 -2
  39. data/manual/bounding_box/creation.rb +3 -3
  40. data/manual/bounding_box/indentation.rb +9 -9
  41. data/manual/bounding_box/nesting.rb +8 -8
  42. data/manual/bounding_box/russian_boxes.rb +1 -1
  43. data/manual/bounding_box/stretchy.rb +8 -8
  44. data/manual/{manual/manual.rb → contents.rb} +6 -9
  45. data/manual/{manual/cover.rb → cover.rb} +6 -6
  46. data/manual/document_and_page_options/background.rb +2 -2
  47. data/manual/document_and_page_options/document_and_page_options.rb +4 -4
  48. data/manual/document_and_page_options/metadata.rb +2 -2
  49. data/manual/document_and_page_options/page_margins.rb +2 -2
  50. data/manual/document_and_page_options/page_size.rb +3 -3
  51. data/manual/example_helper.rb +5 -409
  52. data/manual/graphics/circle_and_ellipse.rb +4 -4
  53. data/manual/graphics/color.rb +4 -4
  54. data/manual/graphics/common_lines.rb +5 -5
  55. data/manual/graphics/fill_and_stroke.rb +5 -5
  56. data/manual/graphics/fill_rules.rb +1 -1
  57. data/manual/graphics/gradients.rb +1 -1
  58. data/manual/graphics/graphics.rb +8 -8
  59. data/manual/graphics/helper.rb +1 -1
  60. data/manual/graphics/line_width.rb +5 -5
  61. data/manual/graphics/lines_and_curves.rb +5 -5
  62. data/manual/graphics/polygon.rb +4 -4
  63. data/manual/graphics/rectangle.rb +3 -3
  64. data/manual/graphics/rotate.rb +5 -5
  65. data/manual/graphics/scale.rb +5 -5
  66. data/manual/graphics/soft_masks.rb +1 -1
  67. data/manual/graphics/stroke_cap.rb +3 -3
  68. data/manual/graphics/stroke_dash.rb +1 -1
  69. data/manual/graphics/stroke_join.rb +4 -4
  70. data/manual/graphics/translate.rb +5 -5
  71. data/manual/graphics/transparency.rb +5 -5
  72. data/manual/{manual/how_to_read_this_manual.rb → how_to_read_this_manual.rb} +16 -17
  73. data/manual/images/absolute_position.rb +3 -3
  74. data/manual/images/fit.rb +2 -2
  75. data/manual/images/horizontal.rb +3 -3
  76. data/manual/images/images.rb +7 -7
  77. data/manual/images/plain_image.rb +1 -1
  78. data/manual/images/scale.rb +3 -3
  79. data/manual/images/vertical.rb +3 -3
  80. data/manual/images/width_and_height.rb +3 -3
  81. data/manual/layout/boxes.rb +4 -4
  82. data/manual/layout/content.rb +3 -3
  83. data/manual/layout/layout.rb +5 -5
  84. data/manual/layout/simple_grid.rb +2 -2
  85. data/manual/outline/add_subsection_to.rb +7 -7
  86. data/manual/outline/insert_section_after.rb +5 -5
  87. data/manual/outline/outline.rb +6 -6
  88. data/manual/outline/sections_and_pages.rb +9 -9
  89. data/manual/repeatable_content/page_numbering.rb +3 -3
  90. data/manual/repeatable_content/repeatable_content.rb +5 -5
  91. data/manual/repeatable_content/repeater.rb +4 -4
  92. data/manual/repeatable_content/stamp.rb +4 -4
  93. data/manual/security/encryption.rb +2 -2
  94. data/manual/security/permissions.rb +2 -2
  95. data/manual/security/security.rb +5 -5
  96. data/manual/table/basic_block.rb +5 -5
  97. data/manual/table/before_rendering_page.rb +1 -1
  98. data/manual/table/cell_border_lines.rb +4 -4
  99. data/manual/table/cell_borders_and_bg.rb +4 -4
  100. data/manual/table/cell_dimensions.rb +3 -3
  101. data/manual/table/cell_text.rb +6 -6
  102. data/manual/table/column_widths.rb +4 -4
  103. data/manual/table/content_and_subtables.rb +5 -5
  104. data/manual/table/creation.rb +2 -2
  105. data/manual/table/filtering.rb +6 -6
  106. data/manual/table/flow_and_header.rb +2 -2
  107. data/manual/table/image_cells.rb +1 -1
  108. data/manual/table/position.rb +1 -1
  109. data/manual/table/row_colors.rb +3 -3
  110. data/manual/table/span.rb +1 -1
  111. data/manual/table/style.rb +3 -3
  112. data/manual/table/table.rb +7 -7
  113. data/manual/table/width.rb +3 -3
  114. data/manual/text/alignment.rb +1 -1
  115. data/manual/text/color.rb +1 -1
  116. data/manual/text/column_box.rb +1 -1
  117. data/manual/text/fallback_fonts.rb +3 -3
  118. data/manual/text/font.rb +7 -7
  119. data/manual/text/font_size.rb +9 -9
  120. data/manual/text/font_style.rb +3 -3
  121. data/manual/text/formatted_callbacks.rb +3 -3
  122. data/manual/text/formatted_text.rb +3 -3
  123. data/manual/text/free_flowing_text.rb +6 -6
  124. data/manual/text/inline.rb +5 -5
  125. data/manual/text/kerning_and_character_spacing.rb +7 -7
  126. data/manual/text/leading.rb +4 -4
  127. data/manual/text/line_wrapping.rb +4 -4
  128. data/manual/text/paragraph_indentation.rb +3 -3
  129. data/manual/text/positioned_text.rb +3 -3
  130. data/manual/text/registering_families.rb +6 -6
  131. data/manual/text/rendering_and_color.rb +2 -2
  132. data/manual/text/right_to_left_text.rb +2 -2
  133. data/manual/text/rotation.rb +4 -4
  134. data/manual/text/single_usage.rb +3 -3
  135. data/manual/text/text.rb +9 -9
  136. data/manual/text/text_box_excess.rb +2 -2
  137. data/manual/text/text_box_extensions.rb +3 -3
  138. data/manual/text/text_box_overflow.rb +4 -4
  139. data/manual/text/utf8.rb +5 -5
  140. data/manual/text/win_ansi_charset.rb +1 -1
  141. data/prawn.gemspec +5 -3
  142. data/spec/acceptance/png.rb +5 -3
  143. data/spec/cell_spec.rb +1 -0
  144. data/spec/column_box_spec.rb +1 -1
  145. data/spec/extensions/encoding_helpers.rb +2 -0
  146. data/spec/extensions/mocha.rb +2 -0
  147. data/spec/font_spec.rb +15 -0
  148. data/spec/measurement_units_spec.rb +2 -0
  149. data/spec/repeater_spec.rb +2 -0
  150. data/spec/soft_mask_spec.rb +2 -0
  151. data/spec/stamp_spec.rb +2 -0
  152. data/spec/table_spec.rb +103 -10
  153. data/spec/text_spec.rb +1 -1
  154. data/spec/transparency_spec.rb +2 -0
  155. metadata +42 -18
  156. data/lib/prawn/layout.rb +0 -17
  157. data/manual/example_file.rb +0 -111
  158. data/manual/example_package.rb +0 -53
  159. data/manual/example_section.rb +0 -46
  160. data/manual/syntax_highlight.rb +0 -52
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: edf9e637f24e5c4554e995712cb589f0b21737fb
4
- data.tar.gz: 97750f73864c853ff874f8a5a0f7984a6f9d8279
3
+ metadata.gz: 8492b319da06bf93faa4e58cfa4932b46fd8a474
4
+ data.tar.gz: ba94547a9eda37f13c91b7293203a12df99b9815
5
5
  SHA512:
6
- metadata.gz: bd80b65ac0360fa6786b6bffacfc34a87511cc8d79e260dfd8db78b86a5003182a41d5ad559e5c1403724273c83b71b29d426c31a422fd7b1e5986a859ac1a5d
7
- data.tar.gz: 4b63fc53c8481155ed173cb3e4b8146ac052173e0bd91f4fa27aece66525ad5f6968a48230addbb5ae2a043a3bc33829c79c9447a8c734f3bd2da45468b913da
6
+ metadata.gz: d898304c9f63ac7826f49b3802106ec84e36fb8a0efcb07d85b2b88d0e7a2f3016f722655fea9e4992e028a8a740ef5d29a5ee9e7287164f6768d9435179891c
7
+ data.tar.gz: 0127f4e217c2588fe196665712cb1fe7e3914a49d3ba4e0b248e1a7bd3615f52e6c97e2288c4c7fb9295383e48c03ad48557c137e8d1f822841c65d1a8f77d62
data/Rakefile CHANGED
@@ -5,8 +5,9 @@ require 'rake'
5
5
  require 'rspec/core/rake_task'
6
6
  require 'yard'
7
7
  require 'rubygems/package_task'
8
+ require 'rubocop/rake_task'
8
9
 
9
- task :default => [:spec]
10
+ task :default => [:rubocop, :spec]
10
11
 
11
12
  desc "Run all rspec files"
12
13
  RSpec::Core::RakeTask.new("spec") do |c|
@@ -31,7 +32,7 @@ desc "Generate the 'Prawn by Example' manual"
31
32
  task :manual do
32
33
  puts "Building manual..."
33
34
  require File.expand_path(File.join(File.dirname(__FILE__),
34
- %w[manual manual manual]))
35
+ %w[manual contents]))
35
36
  puts "The Prawn manual is available at manual.pdf. Happy Prawning!"
36
37
  end
37
38
 
@@ -52,3 +53,4 @@ task :console do
52
53
  IRB.start
53
54
  end
54
55
 
56
+ Rubocop::RakeTask.new
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  # Welcome to Prawn, the best PDF Generation library ever.
2
4
  # This documentation covers user level functionality.
3
5
  #
@@ -7,7 +9,7 @@ require 'ttfunk'
7
9
  require "pdf/core"
8
10
 
9
11
  module Prawn
10
- VERSION = "1.0.0"
12
+ VERSION = "1.1.0"
11
13
 
12
14
  extend self
13
15
 
@@ -20,7 +22,7 @@ module Prawn
20
22
  #
21
23
  BASEDIR = File.expand_path(File.join(dir, '..'))
22
24
  DATADIR = File.expand_path(File.join(dir, '..', 'data'))
23
-
25
+
24
26
  FLOAT_PRECISION = 1.0e-9
25
27
 
26
28
  # Whe set to true, Prawn will verify hash options to ensure only valid keys
@@ -81,7 +83,7 @@ require_relative "prawn/encoding"
81
83
  require_relative "prawn/measurements"
82
84
  require_relative "prawn/repeater"
83
85
  require_relative "prawn/outline"
84
- require_relative "prawn/layout"
86
+ require_relative "prawn/grid"
85
87
 
86
88
  require_relative "prawn/image_handler"
87
89
 
@@ -97,7 +97,7 @@ module Prawn
97
97
  end
98
98
 
99
99
  # @private
100
- def self.inherited(base)
100
+ def self.inherited(base)
101
101
  extensions.each { |e| base.extensions << e }
102
102
  end
103
103
 
@@ -148,7 +148,7 @@ module Prawn
148
148
  # Creates a new PDF Document. The following options are available (with
149
149
  # the default values marked in [])
150
150
  #
151
- # <tt>:page_size</tt>:: One of the Document::PageGeometry sizes [LETTER]
151
+ # <tt>:page_size</tt>:: One of the PDF::Core::PageGeometry sizes [LETTER]
152
152
  # <tt>:page_layout</tt>:: Either <tt>:portrait</tt> or <tt>:landscape</tt>
153
153
  # <tt>:margin</tt>:: Sets the margin on all sides in points [0.5 inch]
154
154
  # <tt>:left_margin</tt>:: Sets the left margin in points [0.5 inch]
@@ -587,7 +587,7 @@ module Prawn
587
587
  #
588
588
  # @private
589
589
  def group(*a, &b)
590
- raise NotImplementedError,
590
+ raise NotImplementedError,
591
591
  "Document#group has been disabled because its implementation "+
592
592
  "lead to corrupted documents whenever a page boundary was "+
593
593
  "crossed. We will try to work on reimplementing it in a "+
@@ -596,7 +596,7 @@ module Prawn
596
596
 
597
597
  # @private
598
598
  def transaction
599
- raise NotImplementedError,
599
+ raise NotImplementedError,
600
600
  "Document#transaction has been disabled because its implementation "+
601
601
  "lead to corrupted documents whenever a page boundary was "+
602
602
  "crossed. We will try to work on reimplementing it in a "+
@@ -629,8 +629,8 @@ module Prawn
629
629
  end
630
630
 
631
631
  # @private
632
-
633
- def mask(*fields)
632
+
633
+ def mask(*fields)
634
634
  # Stores the current state of the named attributes, executes the block, and
635
635
  # then restores the original values after the block has executed.
636
636
  # -- I will remove the nodoc if/when this feature is a little less hacky
@@ -696,7 +696,7 @@ module Prawn
696
696
 
697
697
  # we must update bounding box if not flowing from the previous page
698
698
  #
699
- @bounding_box = @margin_box unless @bounding_box && @bounding_box.parent
699
+ @bounding_box = @margin_box unless @bounding_box && @bounding_box.parent
700
700
  end
701
701
 
702
702
  def apply_margin_options(options)
@@ -199,7 +199,7 @@ module Prawn
199
199
  # If the user actions did not modify the y position
200
200
  # restore the original y position before the bounding
201
201
  # box was created.
202
-
202
+
203
203
  if y == @bounding_box.absolute_top
204
204
  self.y = original_ypos
205
205
  end
@@ -20,12 +20,12 @@ module Prawn
20
20
  #
21
21
  # column_box accepts the same parameters as bounding_box, as well as the
22
22
  # number of :columns and a :spacer (in points) between columns. If resetting
23
- # the top margin is desired on a new page (e.g. to allow for initial page
23
+ # the top margin is desired on a new page (e.g. to allow for initial page
24
24
  # wide column titles) the option :reflow_margins => true can be set.
25
25
  #
26
- # Defaults are :columns = 3, :spacer = font_size, and
26
+ # Defaults are :columns = 3, :spacer = font_size, and
27
27
  # :reflow_margins => false
28
- #
28
+ #
29
29
  # Under PDF::Writer, "spacer" was known as "gutter"
30
30
  #
31
31
  def column_box(*args, &block)
@@ -15,7 +15,7 @@ module Prawn
15
15
  # are you won't need anything you find here.
16
16
  #
17
17
  # @private
18
- module Internals
18
+ module Internals
19
19
  # Creates a new Prawn::Reference and adds it to the Document's object
20
20
  # list. The +data+ argument is anything that Prawn::PdfObject() can convert.
21
21
  #
@@ -237,7 +237,15 @@ module Prawn
237
237
  end
238
238
  end
239
239
  key = "#{name}:#{options[:font] || 0}"
240
- font_registry[key] ||= Font.load(self, name, options.merge(:family => family))
240
+
241
+ if name.is_a? Prawn::Font
242
+ font_registry[key] = name
243
+ else
244
+ font_registry[key] ||= Font.load( self,
245
+ name,
246
+ options.merge(family: family)
247
+ )
248
+ end
241
249
  end
242
250
 
243
251
  # Hash of Font objects keyed by names
@@ -279,13 +287,21 @@ module Prawn
279
287
  # Shortcut interface for constructing a font object. Filenames of the form
280
288
  # *.ttf will call Font::TTF.new, *.dfont Font::DFont.new, and anything else
281
289
  # will be passed through to Font::AFM.new()
282
- #
283
- def self.load(document,name,options={})
284
- case name.to_s
285
- when /\.ttf$/i then TTF.new(document, name, options)
286
- when /\.dfont$/i then DFont.new(document, name, options)
287
- when /\.afm$/i then AFM.new(document, name, options)
288
- else AFM.new(document, name, options)
290
+ def self.load(document, src, options={})
291
+ case font_format(src, options)
292
+ when 'ttf' then TTF.new(document, src, options)
293
+ when 'dfont' then DFont.new(document, src, options)
294
+ else AFM.new(document, src, options)
295
+ end
296
+ end
297
+
298
+ def self.font_format(src, options)
299
+ return options.fetch(:format, 'ttf') if src.respond_to? :read
300
+
301
+ case src.to_s
302
+ when /\.ttf$/i then return 'ttf'
303
+ when /\.dfont$/i then return 'dfont'
304
+ else return 'afm'
289
305
  end
290
306
  end
291
307
 
@@ -157,7 +157,7 @@ module Prawn
157
157
  end
158
158
 
159
159
  def parse_afm(file_name)
160
- data = {:glyph_widths => {}, :bounding_boxes => {}, :kern_pairs => {}, :attributes => {}}
160
+ data = {:glyph_widths => {}, :bounding_boxes => {}, :kern_pairs => {}, :attributes => {}}
161
161
  section = []
162
162
 
163
163
  File.foreach(file_name) do |line|
@@ -187,7 +187,7 @@ module Prawn
187
187
  parse_generic_afm_attribute(line, data)
188
188
  end
189
189
  end
190
-
190
+
191
191
  # process data parsed from AFM file to build tables which
192
192
  # will be used when measuring and kerning text
193
193
  data[:glyph_table] = (0..255).map do |i|
@@ -234,9 +234,9 @@ module Prawn
234
234
  e.respond_to?(:force_encoding) ? e.force_encoding(::Encoding::Windows_1252) : e
235
235
  }
236
236
  end
237
-
237
+
238
238
  private
239
-
239
+
240
240
  def unscaled_width_of(string)
241
241
  string.bytes.inject(0) do |s,r|
242
242
  s + @glyph_table[r]
@@ -13,7 +13,7 @@ module Prawn
13
13
  # of various strings for layout purposes.
14
14
  #
15
15
  # @private
16
- class FontMetricCache
16
+ class FontMetricCache
17
17
 
18
18
  CacheEntry = Struct.new( :font, :options, :string )
19
19
 
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  # grid.rb: Provides a basic grid layout system for Prawn
2
4
  #
3
5
  # Contributed by Andrew O'Brien in March 2009
@@ -14,7 +16,7 @@ module Prawn
14
16
  #
15
17
  # Note that a completely new grid object is built each time define_grid()
16
18
  # is called. This means that all subsequent calls to grid() will use
17
- # the newly defined Grid object -- grids are not nestable like
19
+ # the newly defined Grid object -- grids are not nestable like
18
20
  # bounding boxes are.
19
21
 
20
22
  def define_grid(options = {})
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  # ImageHandler provides a way to register image processors with Prawn
2
4
  #
3
5
  # Contributed by Evan Sharp in November 2013.
@@ -6,7 +8,7 @@
6
8
 
7
9
  module Prawn
8
10
  # @group Extension API
9
-
11
+
10
12
  def self.image_handler
11
13
  @image_handler ||= ImageHandler.new
12
14
  end
@@ -16,7 +16,7 @@ module Prawn
16
16
  #
17
17
  class JPG < Image
18
18
  # @group Extension API
19
-
19
+
20
20
  attr_reader :width, :height, :bits, :channels
21
21
  attr_accessor :scaled_width, :scaled_height
22
22
 
@@ -15,7 +15,7 @@ class Numeric
15
15
  # 72 points per inch
16
16
 
17
17
  # @group Experimental API
18
-
18
+
19
19
  def mm
20
20
  return mm2pt(self)
21
21
  end
@@ -1,7 +1,9 @@
1
+ # encoding: utf-8
2
+
1
3
  module Prawn
2
4
  class Document
3
5
  # @group Stable API
4
-
6
+
5
7
  # Lazily instantiates a Prawn::Outline object for document. This is used as point of entry
6
8
  # to methods to build the outline tree for a document's table of contents.
7
9
  def outline
@@ -213,7 +213,7 @@ module PDF
213
213
  # from the indirect object referencing obj.
214
214
  #
215
215
  # @private
216
- def EncryptedPdfObject(obj, key, id, gen, in_content_stream=false)
216
+ def EncryptedPdfObject(obj, key, id, gen, in_content_stream=false)
217
217
  case obj
218
218
  when Array
219
219
  "[" << obj.map { |e|
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  # Implementation of the "ARCFOUR" algorithm ("alleged RC4 (tm)"). Implemented
2
4
  # as described at:
3
5
  # http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt
@@ -17,7 +19,7 @@ class Arcfour
17
19
  # 1. Allocate an 256 element array of 8 bit bytes to be used as an S-box
18
20
  # 2. Initialize the S-box. Fill each entry first with it's index
19
21
  @sbox = (0..255).to_a
20
-
22
+
21
23
  # 3. Fill another array of the same size (256) with the key, repeating
22
24
  # bytes as necessary.
23
25
  s2 = []
@@ -39,7 +41,7 @@ class Arcfour
39
41
  def encrypt(string)
40
42
  string.unpack('c*').map{|byte| byte ^ key_byte}.pack('c*')
41
43
  end
42
-
44
+
43
45
  private
44
46
 
45
47
  # Produces the next byte of key material in the stream (3.2 Stream Generation)
@@ -43,6 +43,16 @@ module Prawn
43
43
 
44
44
  end
45
45
 
46
+ module Errors
47
+ # This error is raised when table data is malformed
48
+ #
49
+ InvalidTableData = Class.new(StandardError)
50
+
51
+ # This error is raised when an empty or nil table is rendered
52
+ #
53
+ EmptyTable = Class.new(StandardError)
54
+ end
55
+
46
56
  # Next-generation table drawing for Prawn.
47
57
  #
48
58
  # = Data
@@ -320,7 +330,9 @@ module Prawn
320
330
  c = Cells.new(cells_this_page.map { |ci, _| ci })
321
331
  @before_rendering_page.call(c)
322
332
  end
323
- Cell.draw_cells(cells_this_page)
333
+ if @header_row.nil? || cells_this_page.size > @header_row.size
334
+ Cell.draw_cells(cells_this_page)
335
+ end
324
336
  cells_this_page = []
325
337
 
326
338
  # start a new page or column
@@ -527,7 +539,7 @@ module Prawn
527
539
  rows_to_operate_on = @header_row.rows(row_of_header) if row_of_header
528
540
  rows_to_operate_on.each do |cell|
529
541
  cell.row = row
530
- cell.dummy_cells.each {|c| c.row = row }
542
+ cell.dummy_cells.each {|c| c.row = row + c.row }
531
543
  page_of_cells << [cell, [cell.x + x_offset, y]]
532
544
  end
533
545
  rows_to_operate_on.height
@@ -153,7 +153,7 @@ module Prawn
153
153
  # this span group. They know their own width / height, but do not draw
154
154
  # anything.
155
155
  #
156
- attr_reader :dummy_cells
156
+ attr_reader :dummy_cells
157
157
 
158
158
  # Instantiates a Cell based on the given options. The particular class of
159
159
  # cell returned depends on the :content argument. See the Prawn::Table
@@ -228,56 +228,7 @@ module Prawn
228
228
  # each cell, grouped by +row_or_column+.
229
229
  #
230
230
  def aggregate_cell_values(row_or_column, meth, aggregate)
231
- values = {}
232
-
233
- #calculate values for all cells that do not span accross multiple cells
234
- #this ensures that we don't have a problem if the first line includes
235
- #a cell that spans across multiple cells
236
- each do |cell|
237
- #don't take spanned cells
238
- if cell.colspan == 1 and cell.class != Prawn::Table::Cell::SpanDummy
239
- index = cell.send(row_or_column)
240
- values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
241
- end
242
- end
243
-
244
- #if there are only colspanned or rowspanned cells in a table
245
- spanned_width_needs_fixing = true
246
-
247
- each do |cell|
248
- index = cell.send(row_or_column)
249
- if cell.colspan > 1
250
- #calculate current (old) return value before we do anything
251
- old_sum = 0
252
- cell.colspan.times { |i|
253
- old_sum += values[index+i] unless values[index+i].nil?
254
- }
255
-
256
- #calculate future return value
257
- new_sum = cell.send(meth) * cell.colspan
258
-
259
- #due to float rounding errors we need to ignore a small difference in the new
260
- #and the old sum the same had to be done in
261
- #the column_width_calculator#natural_width
262
- spanned_width_needs_fixing = ((new_sum - old_sum) > Prawn::FLOAT_PRECISION)
263
-
264
- if spanned_width_needs_fixing
265
- #not entirely sure why we need this line, but with it the tests pass
266
- values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
267
- #overwrite the old values with the new ones, but only if all entries existed
268
- entries_exist = true
269
- cell.colspan.times { |i| entries_exist = false if values[index+i].nil? }
270
- cell.colspan.times { |i|
271
- values[index+i] = cell.send(meth) if entries_exist
272
- }
273
- end
274
- else
275
- if spanned_width_needs_fixing && cell.class == Prawn::Table::Cell::SpanDummy
276
- values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
277
- end
278
- end
279
- end
280
- values.values.inject(0, &:+)
231
+ ColumnWidthCalculator.new(self).aggregate_cell_values(row_or_column, meth, aggregate)
281
232
  end
282
233
 
283
234
  # Transforms +spec+, a column / row specification, into an object that
@@ -1,48 +1,104 @@
1
+ # encoding: utf-8
2
+
1
3
  module Prawn
2
4
  class Table
3
5
  # @private
4
- class ColumnWidthCalculator
6
+ class ColumnWidthCalculator
5
7
  def initialize(cells)
6
8
  @cells = cells
7
9
 
8
10
  @widths_by_column = Hash.new(0)
9
11
  @rows_with_a_span_dummy = Hash.new(false)
10
- end
11
12
 
12
- def natural_widths
13
+ #calculate for each row if it includes a Cell:SpanDummy
13
14
  @cells.each do |cell|
14
15
  @rows_with_a_span_dummy[cell.row] = true if cell.is_a?(Cell::SpanDummy)
15
16
  end
17
+ end
18
+
19
+ # does this row include a Cell:SpanDummy?
20
+ #
21
+ # @param row - the row that should be checked for Cell:SpanDummy elements
22
+ #
23
+ def has_a_span_dummy?(row)
24
+ @rows_with_a_span_dummy[row]
25
+ end
26
+
27
+ # helper method
28
+ # column widths are stored in the values array
29
+ # a cell may span cells whose value is only partly given
30
+ # this function handles this special case
31
+ #
32
+ # @param values - The columns widths calculated up until now
33
+ # @param cell - The current cell
34
+ # @param index - The current column
35
+ # @param meth - Meth (min/max); used to calculate values to be filled
36
+ #
37
+ def fill_values_if_needed(values, cell, index, meth)
38
+ #have all spanned indices been filled with a value?
39
+ #e.g. values[0], values[1] and values[2] don't return nil given a index of 0 and a colspan of 3
40
+ number_of_nil_values = 0
41
+ cell.colspan.times do |i|
42
+ number_of_nil_values += 1 if values[index+i].nil?
43
+ end
44
+
45
+ #nothing to do? because
46
+ #a) all values are filled
47
+ return values if number_of_nil_values == 0
48
+ #b) no values are filled
49
+ return values if number_of_nil_values == cell.colspan
50
+ #c) I am not sure why this line is needed FIXXME
51
+ #some test cases manage to this line even though there is no dummy cell in the row
52
+ #I'm not sure if this is a sign for a further underlying bug.
53
+ return values unless has_a_span_dummy?(cell.row)
54
+ #fill up the values array
55
+
56
+ #calculate the new sum
57
+ new_sum = cell.send(meth) * cell.colspan
58
+ #substract any calculated values
59
+ cell.colspan.times do |i|
60
+ new_sum -= values[index+i] unless values[index+i].nil?
61
+ end
62
+
63
+ #calculate value for the remaining - not yet filled - cells.
64
+ new_value = new_sum.to_f / number_of_nil_values
65
+ #fill the not yet filled cells
66
+ cell.colspan.times do |i|
67
+ values[index+i] = new_value if values[index+i].nil?
68
+ end
69
+ return values
70
+ end
16
71
 
72
+ def natural_widths
17
73
  #calculate natural column width for all rows that do not include a span dummy
18
74
  @cells.each do |cell|
19
- unless @rows_with_a_span_dummy[cell.row]
20
- @widths_by_column[cell.column] =
75
+ unless has_a_span_dummy?(cell.row)
76
+ @widths_by_column[cell.column] =
21
77
  [@widths_by_column[cell.column], cell.width.to_f].max
22
78
  end
23
79
  end
24
80
 
25
81
  #integrate natural column widths for all rows that do include a span dummy
26
82
  @cells.each do |cell|
27
- next unless @rows_with_a_span_dummy[cell.row]
83
+ next unless has_a_span_dummy?(cell.row)
28
84
  #the width of a SpanDummy cell will be calculated by the "mother" cell
29
85
  next if cell.is_a?(Cell::SpanDummy)
30
86
 
31
87
  if cell.colspan == 1
32
- @widths_by_column[cell.column] =
88
+ @widths_by_column[cell.column] =
33
89
  [@widths_by_column[cell.column], cell.width.to_f].max
34
90
  else
35
91
  #calculate the current with of all cells that will be spanned by the current cell
36
- current_width_of_spanned_cells =
92
+ current_width_of_spanned_cells =
37
93
  @widths_by_column.to_a[cell.column..(cell.column + cell.colspan - 1)]
38
94
  .collect{|key, value| value}.inject(0, :+)
39
95
 
40
96
  #update the Hash only if the new with is at least equal to the old one
41
97
  #due to arithmetic errors we need to ignore a small difference in the new and the old sum
42
98
  #the same had to be done in the column_widht_calculator#natural_width
43
- update_hash = ((cell.width.to_f - current_width_of_spanned_cells) >
99
+ update_hash = ((cell.width.to_f - current_width_of_spanned_cells) >
44
100
  Prawn::FLOAT_PRECISION)
45
-
101
+
46
102
  if update_hash
47
103
  # Split the width of colspanned cells evenly by columns
48
104
  width_per_column = cell.width.to_f / cell.colspan
@@ -56,6 +112,71 @@ module Prawn
56
112
 
57
113
  @widths_by_column.sort_by { |col, _| col }.map { |_, w| w }
58
114
  end
115
+
116
+ # get column widths (either min or max depending on meth)
117
+ # used in cells.rb
118
+ #
119
+ # @param row_or_column - you may call this on either rows or columns
120
+ # @param meth - min/max
121
+ # @param aggregate - functions from cell.rb to be used to aggregate e.g. avg_spanned_min_width
122
+ #
123
+ def aggregate_cell_values(row_or_column, meth, aggregate)
124
+ values = {}
125
+
126
+ #calculate values for all cells that do not span accross multiple cells
127
+ #this ensures that we don't have a problem if the first line includes
128
+ #a cell that spans across multiple cells
129
+ @cells.each do |cell|
130
+ #don't take spanned cells
131
+ if cell.colspan == 1 and cell.class != Prawn::Table::Cell::SpanDummy
132
+ index = cell.send(row_or_column)
133
+ values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
134
+ end
135
+ end
136
+
137
+ # if there are only colspanned or rowspanned cells in a table
138
+ spanned_width_needs_fixing = true
139
+
140
+ @cells.each do |cell|
141
+ index = cell.send(row_or_column)
142
+ if cell.colspan > 1
143
+ #special treatment if some but not all spanned indices in the values array have been calculated
144
+ #only applies to rows
145
+ values = fill_values_if_needed(values, cell, index, meth) if row_or_column == :column
146
+ #calculate current (old) return value before we do anything
147
+ old_sum = 0
148
+ cell.colspan.times { |i|
149
+ old_sum += values[index+i] unless values[index+i].nil?
150
+ }
151
+
152
+ #calculate future return value
153
+ new_sum = cell.send(meth) * cell.colspan
154
+
155
+ #due to float rounding errors we need to ignore a small difference in the new
156
+ #and the old sum the same had to be done in
157
+ #the column_width_calculator#natural_width
158
+ spanned_width_needs_fixing = ((new_sum - old_sum) > Prawn::FLOAT_PRECISION)
159
+
160
+ if spanned_width_needs_fixing
161
+ #not entirely sure why we need this line, but with it the tests pass
162
+ values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
163
+ #overwrite the old values with the new ones, but only if all entries existed
164
+ entries_exist = true
165
+ cell.colspan.times { |i| entries_exist = false if values[index+i].nil? }
166
+ cell.colspan.times { |i|
167
+ values[index+i] = cell.send(meth) if entries_exist
168
+ }
169
+ end
170
+ else
171
+ if spanned_width_needs_fixing && cell.class == Prawn::Table::Cell::SpanDummy
172
+ values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
173
+ end
174
+ end
175
+ end
176
+
177
+ return values.values.inject(0, &:+)
178
+ end
59
179
  end
180
+
60
181
  end
61
182
  end