prawn 1.0.0.rc1 → 1.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +18 -0
- data/README.md +5 -3
- data/Rakefile +8 -14
- data/data/pdfs/nested_pages.pdf +13 -13
- data/lib/prawn.rb +3 -1
- data/lib/prawn/compatibility.rb +46 -10
- data/lib/prawn/core.rb +3 -1
- data/lib/prawn/core/document_state.rb +2 -1
- data/lib/prawn/core/object_store.rb +61 -5
- data/lib/prawn/core/page.rb +3 -6
- data/lib/prawn/core/pdf_object.rb +21 -4
- data/lib/prawn/core/reference.rb +6 -2
- data/lib/prawn/core/text.rb +4 -4
- data/lib/prawn/core/text/formatted/line_wrap.rb +23 -8
- data/lib/prawn/document.rb +21 -15
- data/lib/prawn/document/bounding_box.rb +3 -3
- data/lib/prawn/document/column_box.rb +22 -4
- data/lib/prawn/document/snapshot.rb +1 -1
- data/lib/prawn/encoding.rb +1 -1
- data/lib/prawn/errors.rb +4 -0
- data/lib/prawn/font.rb +1 -1
- data/lib/prawn/font/afm.rb +30 -72
- data/lib/prawn/font/ttf.rb +6 -33
- data/lib/prawn/graphics.rb +148 -23
- data/lib/prawn/graphics/color.rb +8 -1
- data/lib/prawn/graphics/patterns.rb +137 -0
- data/lib/prawn/images.rb +25 -19
- data/lib/prawn/images/jpg.rb +4 -4
- data/lib/prawn/images/png.rb +18 -12
- data/lib/prawn/security.rb +6 -4
- data/lib/prawn/soft_mask.rb +94 -0
- data/lib/prawn/table.rb +136 -31
- data/lib/prawn/table/cell.rb +260 -29
- data/lib/prawn/table/cell/span_dummy.rb +88 -0
- data/lib/prawn/table/cell/text.rb +36 -14
- data/lib/prawn/table/cells.rb +91 -41
- data/lib/prawn/text.rb +3 -2
- data/lib/prawn/text/formatted/box.rb +14 -5
- data/lib/prawn/text/formatted/fragment.rb +33 -22
- data/lib/prawn/text/formatted/parser.rb +5 -2
- data/lib/prawn/utilities.rb +44 -0
- data/manual/basic_concepts/adding_pages.rb +27 -0
- data/manual/basic_concepts/basic_concepts.rb +34 -0
- data/manual/basic_concepts/creation.rb +39 -0
- data/manual/basic_concepts/cursor.rb +33 -0
- data/manual/basic_concepts/measurement.rb +25 -0
- data/manual/basic_concepts/origin.rb +38 -0
- data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
- data/manual/bounding_box/bounding_box.rb +39 -0
- data/manual/bounding_box/bounds.rb +49 -0
- data/manual/bounding_box/canvas.rb +24 -0
- data/manual/bounding_box/creation.rb +23 -0
- data/manual/bounding_box/indentation.rb +46 -0
- data/manual/bounding_box/nesting.rb +45 -0
- data/manual/bounding_box/russian_boxes.rb +40 -0
- data/manual/bounding_box/stretchy.rb +31 -0
- data/manual/document_and_page_options/background.rb +27 -0
- data/manual/document_and_page_options/document_and_page_options.rb +31 -0
- data/manual/document_and_page_options/metadata.rb +23 -0
- data/manual/document_and_page_options/page_margins.rb +38 -0
- data/manual/document_and_page_options/page_size.rb +34 -0
- data/manual/example_file.rb +116 -0
- data/manual/example_helper.rb +430 -0
- data/manual/example_package.rb +53 -0
- data/manual/example_section.rb +46 -0
- data/manual/graphics/circle_and_ellipse.rb +22 -0
- data/manual/graphics/color.rb +24 -0
- data/manual/graphics/common_lines.rb +28 -0
- data/manual/graphics/fill_and_stroke.rb +42 -0
- data/manual/graphics/fill_rules.rb +37 -0
- data/manual/graphics/gradients.rb +37 -0
- data/manual/graphics/graphics.rb +58 -0
- data/manual/graphics/helper.rb +17 -0
- data/manual/graphics/line_width.rb +35 -0
- data/manual/graphics/lines_and_curves.rb +41 -0
- data/manual/graphics/polygon.rb +29 -0
- data/manual/graphics/rectangle.rb +21 -0
- data/manual/graphics/rotate.rb +28 -0
- data/manual/graphics/scale.rb +41 -0
- data/manual/graphics/soft_masks.rb +46 -0
- data/manual/graphics/stroke_cap.rb +31 -0
- data/manual/graphics/stroke_dash.rb +43 -0
- data/manual/graphics/stroke_join.rb +30 -0
- data/manual/graphics/translate.rb +29 -0
- data/manual/graphics/transparency.rb +35 -0
- data/manual/images/absolute_position.rb +23 -0
- data/manual/images/fit.rb +21 -0
- data/manual/images/horizontal.rb +25 -0
- data/manual/images/images.rb +40 -0
- data/manual/images/plain_image.rb +18 -0
- data/manual/images/scale.rb +22 -0
- data/manual/images/vertical.rb +28 -0
- data/manual/images/width_and_height.rb +25 -0
- data/manual/layout/boxes.rb +27 -0
- data/manual/layout/content.rb +25 -0
- data/manual/layout/layout.rb +28 -0
- data/manual/layout/simple_grid.rb +23 -0
- data/manual/manual/cover.rb +26 -0
- data/manual/manual/foreword.rb +13 -0
- data/manual/manual/how_to_read_this_manual.rb +41 -0
- data/manual/manual/manual.rb +36 -0
- data/manual/outline/add_subsection_to.rb +61 -0
- data/manual/outline/insert_section_after.rb +47 -0
- data/manual/outline/outline.rb +32 -0
- data/manual/outline/sections_and_pages.rb +67 -0
- data/manual/repeatable_content/page_numbering.rb +54 -0
- data/manual/repeatable_content/repeatable_content.rb +31 -0
- data/manual/repeatable_content/repeater.rb +55 -0
- data/manual/repeatable_content/stamp.rb +41 -0
- data/manual/security/encryption.rb +31 -0
- data/manual/security/permissions.rb +38 -0
- data/manual/security/security.rb +28 -0
- data/manual/syntax_highlight.rb +52 -0
- data/manual/table/basic_block.rb +53 -0
- data/manual/table/before_rendering_page.rb +26 -0
- data/manual/table/cell_border_lines.rb +24 -0
- data/manual/table/cell_borders_and_bg.rb +31 -0
- data/manual/table/cell_dimensions.rb +30 -0
- data/manual/table/cell_text.rb +38 -0
- data/manual/table/column_widths.rb +30 -0
- data/manual/table/content_and_subtables.rb +39 -0
- data/manual/table/creation.rb +27 -0
- data/manual/table/filtering.rb +36 -0
- data/manual/table/flow_and_header.rb +17 -0
- data/manual/table/image_cells.rb +33 -0
- data/manual/table/position.rb +29 -0
- data/manual/table/row_colors.rb +20 -0
- data/manual/table/span.rb +30 -0
- data/manual/table/style.rb +22 -0
- data/manual/table/table.rb +52 -0
- data/manual/table/width.rb +27 -0
- data/manual/templates/full_template.rb +23 -0
- data/manual/templates/page_template.rb +47 -0
- data/manual/templates/templates.rb +26 -0
- data/manual/text/alignment.rb +44 -0
- data/manual/text/color.rb +24 -0
- data/manual/text/column_box.rb +32 -0
- data/manual/text/fallback_fonts.rb +37 -0
- data/manual/text/font.rb +41 -0
- data/manual/text/font_size.rb +45 -0
- data/manual/text/font_style.rb +23 -0
- data/manual/text/formatted_callbacks.rb +60 -0
- data/manual/text/formatted_text.rb +50 -0
- data/manual/text/free_flowing_text.rb +51 -0
- data/manual/text/group.rb +29 -0
- data/manual/text/inline.rb +43 -0
- data/manual/text/kerning_and_character_spacing.rb +39 -0
- data/manual/text/leading.rb +25 -0
- data/manual/text/line_wrapping.rb +41 -0
- data/manual/text/paragraph_indentation.rb +26 -0
- data/manual/text/positioned_text.rb +38 -0
- data/manual/text/registering_families.rb +48 -0
- data/manual/text/rendering_and_color.rb +37 -0
- data/manual/text/right_to_left_text.rb +43 -0
- data/manual/text/rotation.rb +43 -0
- data/manual/text/single_usage.rb +37 -0
- data/manual/text/text.rb +75 -0
- data/manual/text/text_box_excess.rb +32 -0
- data/manual/text/text_box_extensions.rb +45 -0
- data/manual/text/text_box_overflow.rb +44 -0
- data/manual/text/utf8.rb +28 -0
- data/manual/text/win_ansi_charset.rb +59 -0
- data/prawn.gemspec +10 -7
- data/spec/bounding_box_spec.rb +107 -17
- data/spec/cell_spec.rb +66 -40
- data/spec/column_box_spec.rb +33 -0
- data/spec/document_spec.rb +45 -24
- data/spec/extensions/encoding_helpers.rb +6 -0
- data/spec/extensions/mocha.rb +1 -0
- data/spec/font_spec.rb +71 -53
- data/spec/formatted_text_arranger_spec.rb +19 -19
- data/spec/formatted_text_box_spec.rb +16 -16
- data/spec/formatted_text_fragment_spec.rb +6 -6
- data/spec/graphics_spec.rb +96 -31
- data/spec/grid_spec.rb +2 -2
- data/spec/images_spec.rb +18 -10
- data/spec/jpg_spec.rb +1 -1
- data/spec/line_wrap_spec.rb +14 -14
- data/spec/measurement_units_spec.rb +2 -2
- data/spec/name_tree_spec.rb +6 -6
- data/spec/object_store_spec.rb +17 -17
- data/spec/outline_spec.rb +35 -17
- data/spec/pdf_object_spec.rb +3 -1
- data/spec/png_spec.rb +22 -19
- data/spec/reference_spec.rb +24 -1
- data/spec/repeater_spec.rb +9 -9
- data/spec/security_spec.rb +3 -3
- data/spec/snapshot_spec.rb +3 -3
- data/spec/soft_mask_spec.rb +117 -0
- data/spec/span_spec.rb +4 -4
- data/spec/spec_helper.rb +12 -6
- data/spec/stamp_spec.rb +12 -12
- data/spec/stroke_styles_spec.rb +5 -5
- data/spec/table_spec.rb +458 -88
- data/spec/template_spec.rb +108 -54
- data/spec/text_at_spec.rb +17 -17
- data/spec/text_box_spec.rb +76 -45
- data/spec/text_rendering_mode_spec.rb +5 -5
- data/spec/text_spacing_spec.rb +4 -4
- data/spec/text_spec.rb +44 -40
- metadata +419 -250
- data/lib/prawn/graphics/gradient.rb +0 -84
- data/lib/prawn/security/arcfour.rb +0 -51
data/lib/prawn/images/jpg.rb
CHANGED
@@ -25,8 +25,9 @@ module Prawn
|
|
25
25
|
# <tt>:data</tt>:: A binary string of JPEG data
|
26
26
|
#
|
27
27
|
def initialize(data)
|
28
|
-
@data = data
|
29
|
-
|
28
|
+
@data = data.dup
|
29
|
+
ruby_19 { data.force_encoding("binary") }
|
30
|
+
data = StringIO.new(data)
|
30
31
|
|
31
32
|
c_marker = "\xff" # Section marker.
|
32
33
|
data.read(2) # Skip the first two bytes of JPEG identifier.
|
@@ -65,8 +66,7 @@ module Prawn
|
|
65
66
|
:ColorSpace => color_space,
|
66
67
|
:BitsPerComponent => bits,
|
67
68
|
:Width => width,
|
68
|
-
:Height => height
|
69
|
-
:Length => @data.size
|
69
|
+
:Height => height
|
70
70
|
)
|
71
71
|
|
72
72
|
# add extra decode params for CMYK images. By swapping the
|
data/lib/prawn/images/png.rb
CHANGED
@@ -169,7 +169,6 @@ module Prawn
|
|
169
169
|
:Height => height,
|
170
170
|
:Width => width,
|
171
171
|
:BitsPerComponent => bits,
|
172
|
-
:Length => img_data.size,
|
173
172
|
:Filter => :FlateDecode
|
174
173
|
)
|
175
174
|
|
@@ -182,17 +181,17 @@ module Prawn
|
|
182
181
|
|
183
182
|
# append the actual image data to the object as a stream
|
184
183
|
obj << img_data
|
185
|
-
|
184
|
+
|
186
185
|
# sort out the colours of the image
|
187
186
|
if palette.empty?
|
188
187
|
obj.data[:ColorSpace] = color
|
189
188
|
else
|
190
189
|
# embed the colour palette in the PDF as a object stream
|
191
|
-
palette_obj = document.ref!(
|
190
|
+
palette_obj = document.ref!({})
|
192
191
|
palette_obj << palette
|
193
192
|
|
194
193
|
# build the color space array for the image
|
195
|
-
obj.data[:ColorSpace] = [:Indexed,
|
194
|
+
obj.data[:ColorSpace] = [:Indexed,
|
196
195
|
:DeviceRGB,
|
197
196
|
(palette.size / 3) -1,
|
198
197
|
palette_obj]
|
@@ -234,7 +233,6 @@ module Prawn
|
|
234
233
|
:Height => height,
|
235
234
|
:Width => width,
|
236
235
|
:BitsPerComponent => alpha_channel_bits,
|
237
|
-
:Length => alpha_channel.size,
|
238
236
|
:Filter => :FlateDecode,
|
239
237
|
:ColorSpace => :DeviceGray,
|
240
238
|
:Decode => [0, 1]
|
@@ -262,7 +260,7 @@ module Prawn
|
|
262
260
|
private
|
263
261
|
|
264
262
|
def unfilter_image_data
|
265
|
-
data = Zlib::Inflate.inflate(@img_data).
|
263
|
+
data = Zlib::Inflate.inflate(@img_data).bytes
|
266
264
|
@img_data = ""
|
267
265
|
@alpha_channel = ""
|
268
266
|
|
@@ -270,9 +268,16 @@ module Prawn
|
|
270
268
|
scanline_length = pixel_bytes * self.width + 1
|
271
269
|
row = 0
|
272
270
|
pixels = []
|
271
|
+
row_data = [] # reused for each row of the image
|
273
272
|
paeth, pa, pb, pc = nil
|
274
|
-
|
275
|
-
|
273
|
+
|
274
|
+
data.each do |byte|
|
275
|
+
# accumulate a whole scanline of bytes, and then process it all at once
|
276
|
+
# we could do this with Enumerable#each_slice, but it allocates memory,
|
277
|
+
# and we are trying to avoid that
|
278
|
+
row_data << byte
|
279
|
+
next if row_data.length < scanline_length
|
280
|
+
|
276
281
|
filter = row_data.shift
|
277
282
|
case filter
|
278
283
|
when 0 # None
|
@@ -284,13 +289,13 @@ module Prawn
|
|
284
289
|
end
|
285
290
|
when 2 # Up
|
286
291
|
row_data.each_with_index do |byte, index|
|
287
|
-
col = index / pixel_bytes
|
292
|
+
col = (index / pixel_bytes).floor
|
288
293
|
upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_bytes]
|
289
294
|
row_data[index] = (upper + byte) % 256
|
290
295
|
end
|
291
296
|
when 3 # Average
|
292
297
|
row_data.each_with_index do |byte, index|
|
293
|
-
col = index / pixel_bytes
|
298
|
+
col = (index / pixel_bytes).floor
|
294
299
|
upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_bytes]
|
295
300
|
left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]
|
296
301
|
|
@@ -299,7 +304,7 @@ module Prawn
|
|
299
304
|
when 4 # Paeth
|
300
305
|
left = upper = upper_left = nil
|
301
306
|
row_data.each_with_index do |byte, index|
|
302
|
-
col = index / pixel_bytes
|
307
|
+
col = (index / pixel_bytes).floor
|
303
308
|
|
304
309
|
left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]
|
305
310
|
if row.zero?
|
@@ -335,9 +340,10 @@ module Prawn
|
|
335
340
|
end
|
336
341
|
pixels << s
|
337
342
|
row += 1
|
343
|
+
row_data.clear
|
338
344
|
end
|
339
345
|
|
340
|
-
# convert the pixel data to
|
346
|
+
# convert the pixel data to separate strings for colours and alpha
|
341
347
|
color_byte_size = self.colors * self.bits / 8
|
342
348
|
alpha_byte_size = alpha_channel_bits / 8
|
343
349
|
pixels.each do |this_row|
|
data/lib/prawn/security.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
8
|
|
9
9
|
require 'digest/md5'
|
10
|
-
require '
|
10
|
+
require 'rc4'
|
11
11
|
require 'prawn/core/byte_string'
|
12
12
|
|
13
13
|
module Prawn
|
@@ -116,7 +116,7 @@ module Prawn
|
|
116
116
|
|
117
117
|
# Compute the RC4 key from the extended key and perform the encryption
|
118
118
|
rc4_key = Digest::MD5.digest(extended_key)[0, 10]
|
119
|
-
|
119
|
+
RC4.new(rc4_key).encrypt(str)
|
120
120
|
end
|
121
121
|
|
122
122
|
private
|
@@ -186,13 +186,13 @@ module Prawn
|
|
186
186
|
def owner_password_hash
|
187
187
|
@owner_password_hash ||= begin
|
188
188
|
key = Digest::MD5.digest(pad_password(@owner_password))[0, 5]
|
189
|
-
|
189
|
+
RC4.new(key).encrypt(pad_password(@user_password))
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
193
193
|
# The U (user) value in the encryption dictionary. Algorithm 3.4.
|
194
194
|
def user_password_hash
|
195
|
-
|
195
|
+
RC4.new(user_encryption_key).encrypt(PasswordPadding)
|
196
196
|
end
|
197
197
|
|
198
198
|
end
|
@@ -239,6 +239,8 @@ module Prawn
|
|
239
239
|
when NameTree::Value
|
240
240
|
PdfObject(obj.name) + " " +
|
241
241
|
EncryptedPdfObject(obj.value, key, id, gen, in_content_stream)
|
242
|
+
when Prawn::OutlineRoot, Prawn::OutlineItem
|
243
|
+
EncryptedPdfObject(obj.to_hash, key, id, gen, in_content_stream)
|
242
244
|
else # delegate back to PdfObject
|
243
245
|
PdfObject(obj, in_content_stream)
|
244
246
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# soft_mask.rb : Implements soft-masking
|
4
|
+
#
|
5
|
+
# Copyright September 2012, Alexander Mankuta. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Prawn
|
11
|
+
|
12
|
+
# The Prawn::SoftMask module is used to create arbitrary transparency in
|
13
|
+
# document. Using a soft mask allows creaing more visually rich documents.
|
14
|
+
#
|
15
|
+
# You must group soft mask and graphics it's applied to under
|
16
|
+
# save_graphics_state because soft mask is a part of graphic state in PDF.
|
17
|
+
#
|
18
|
+
# Example:
|
19
|
+
# pdf.save_graphics_state do
|
20
|
+
# pdf.soft_mask do
|
21
|
+
# pdf.fill_color "444444"
|
22
|
+
# pdf.fill_polygon [0, 40], [60, 10], [120, 40], [60, 68]
|
23
|
+
# end
|
24
|
+
# pdf.fill_color '000000'
|
25
|
+
# pdf.fill_rectangle [0, 50], 120, 68
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
module SoftMask
|
29
|
+
def soft_mask(&block)
|
30
|
+
min_version(1.4)
|
31
|
+
|
32
|
+
group_attrs = ref!({
|
33
|
+
:Type => :Group,
|
34
|
+
:S => :Transparency,
|
35
|
+
:CS => :DeviceRGB,
|
36
|
+
:I => false,
|
37
|
+
:K => false
|
38
|
+
})
|
39
|
+
|
40
|
+
group = ref!({
|
41
|
+
:Type => :XObject,
|
42
|
+
:Subtype => :Form,
|
43
|
+
:BBox => state.page.dimensions,
|
44
|
+
:Group => group_attrs,
|
45
|
+
})
|
46
|
+
|
47
|
+
state.page.stamp_stream(group, &block)
|
48
|
+
|
49
|
+
mask = ref!({
|
50
|
+
:Type => :Mask,
|
51
|
+
:S => :Luminosity,
|
52
|
+
:G => group
|
53
|
+
})
|
54
|
+
|
55
|
+
g_state = ref!({
|
56
|
+
:Type => :ExtGState,
|
57
|
+
:SMask => mask,
|
58
|
+
|
59
|
+
:AIS => false,
|
60
|
+
:BM => :Normal,
|
61
|
+
:OP => false,
|
62
|
+
:op => false,
|
63
|
+
:OPM => 1,
|
64
|
+
:SA => true,
|
65
|
+
})
|
66
|
+
|
67
|
+
registry_key = {
|
68
|
+
:bbox => state.page.dimensions,
|
69
|
+
:mask => group.stream,
|
70
|
+
:page => state.page_count,
|
71
|
+
}.hash
|
72
|
+
|
73
|
+
if soft_mask_registry[registry_key]
|
74
|
+
[g_state, mask, group, group_attrs].each { |ref| ref.live = false }
|
75
|
+
|
76
|
+
add_content "/#{soft_mask_registry[registry_key]} gs"
|
77
|
+
else
|
78
|
+
masks = page.resources[:ExtGState] ||= {}
|
79
|
+
id = masks.empty? ? 'GS1' : masks.keys.sort.last.succ
|
80
|
+
masks[id] = g_state
|
81
|
+
|
82
|
+
soft_mask_registry[registry_key] = id
|
83
|
+
|
84
|
+
add_content "/#{id} gs"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def soft_mask_registry
|
91
|
+
@soft_mask_registry ||= {}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/prawn/table.rb
CHANGED
@@ -12,6 +12,7 @@ require 'prawn/table/cell/in_table'
|
|
12
12
|
require 'prawn/table/cell/text'
|
13
13
|
require 'prawn/table/cell/subtable'
|
14
14
|
require 'prawn/table/cell/image'
|
15
|
+
require 'prawn/table/cell/span_dummy'
|
15
16
|
|
16
17
|
module Prawn
|
17
18
|
|
@@ -155,6 +156,20 @@ module Prawn
|
|
155
156
|
#
|
156
157
|
attr_writer :position
|
157
158
|
|
159
|
+
# Returns a Prawn::Table::Cells object representing all of the cells in
|
160
|
+
# this table.
|
161
|
+
#
|
162
|
+
attr_reader :cells
|
163
|
+
|
164
|
+
# Specify a callback to be called before each page of cells is rendered.
|
165
|
+
# The block is passed a Cells object containing all cells to be rendered on
|
166
|
+
# that page. You can change styling of the cells in this block, but keep in
|
167
|
+
# mind that the cells have already been positioned and sized.
|
168
|
+
#
|
169
|
+
def before_rendering_page(&block)
|
170
|
+
@before_rendering_page = block
|
171
|
+
end
|
172
|
+
|
158
173
|
# Returns the width of the table in PDF points.
|
159
174
|
#
|
160
175
|
def width
|
@@ -270,6 +285,13 @@ module Prawn
|
|
270
285
|
end
|
271
286
|
end
|
272
287
|
|
288
|
+
# Duplicate each cell of the header row into @header_row so it can be
|
289
|
+
# modified in before_rendering_page callbacks.
|
290
|
+
if @header
|
291
|
+
@header_row = Cells.new
|
292
|
+
row(0).each { |cell| @header_row[cell.row, cell.column] = cell.dup }
|
293
|
+
end
|
294
|
+
|
273
295
|
# Track cells to be drawn on this page. They will all be drawn when this
|
274
296
|
# page is finished.
|
275
297
|
cells_this_page = []
|
@@ -278,13 +300,21 @@ module Prawn
|
|
278
300
|
if cell.height > (cell.y + offset) - ref_bounds.absolute_bottom &&
|
279
301
|
cell.row > started_new_page_at_row
|
280
302
|
# Ink all cells on the current page
|
303
|
+
if @before_rendering_page
|
304
|
+
c = Cells.new(cells_this_page.map { |c, _| c })
|
305
|
+
@before_rendering_page.call(c)
|
306
|
+
end
|
281
307
|
Cell.draw_cells(cells_this_page)
|
282
308
|
cells_this_page = []
|
283
309
|
|
284
310
|
# start a new page or column
|
285
311
|
@pdf.bounds.move_past_bottom
|
286
|
-
|
287
|
-
|
312
|
+
if cell.row > 0 && @header
|
313
|
+
header_height = add_header(cells_this_page, @pdf.cursor, cell.row-1)
|
314
|
+
else
|
315
|
+
header_height = 0
|
316
|
+
end
|
317
|
+
offset = @pdf.y - cell.y - header_height
|
288
318
|
started_new_page_at_row = cell.row
|
289
319
|
end
|
290
320
|
|
@@ -301,7 +331,9 @@ module Prawn
|
|
301
331
|
|
302
332
|
# Set background color, if any.
|
303
333
|
if @row_colors && (!@header || cell.row > 0)
|
304
|
-
|
334
|
+
# Ensure coloring restarts on every page (to make sure the header
|
335
|
+
# and first row of a page are not colored the same way).
|
336
|
+
index = cell.row - [started_new_page_at_row, @header ? 1 : 0].max
|
305
337
|
cell.background_color ||= @row_colors[index % @row_colors.length]
|
306
338
|
end
|
307
339
|
|
@@ -309,6 +341,10 @@ module Prawn
|
|
309
341
|
last_y = y
|
310
342
|
end
|
311
343
|
# Draw the last page of cells
|
344
|
+
if @before_rendering_page
|
345
|
+
c = Cells.new(cells_this_page.map { |c, _| c })
|
346
|
+
@before_rendering_page.call(c)
|
347
|
+
end
|
312
348
|
Cell.draw_cells(cells_this_page)
|
313
349
|
|
314
350
|
@pdf.move_cursor_to(last_y - @cells.last.height)
|
@@ -343,7 +379,7 @@ module Prawn
|
|
343
379
|
f = (width - cells.min_width).to_f / (natural_width - cells.min_width)
|
344
380
|
|
345
381
|
(0...column_length).map do |c|
|
346
|
-
min, nat = column(c).min_width,
|
382
|
+
min, nat = column(c).min_width, natural_column_widths[c]
|
347
383
|
(f * (nat - min)) + min
|
348
384
|
end
|
349
385
|
elsif width - natural_width > epsilon
|
@@ -351,7 +387,7 @@ module Prawn
|
|
351
387
|
f = (width - cells.width).to_f / (cells.max_width - cells.width)
|
352
388
|
|
353
389
|
(0...column_length).map do |c|
|
354
|
-
nat, max =
|
390
|
+
nat, max = natural_column_widths[c], column(c).max_width
|
355
391
|
(f * (max - nat)) + nat
|
356
392
|
end
|
357
393
|
else
|
@@ -363,7 +399,21 @@ module Prawn
|
|
363
399
|
# Returns an array with the height of each row.
|
364
400
|
#
|
365
401
|
def row_heights
|
366
|
-
@natural_row_heights ||=
|
402
|
+
@natural_row_heights ||=
|
403
|
+
begin
|
404
|
+
heights_by_row = Hash.new(0)
|
405
|
+
cells.each do |cell|
|
406
|
+
next if cell.is_a?(Cell::SpanDummy)
|
407
|
+
|
408
|
+
# Split the height of row-spanned cells evenly by rows
|
409
|
+
height_per_row = cell.height.to_f / cell.rowspan
|
410
|
+
cell.rowspan.times do |i|
|
411
|
+
heights_by_row[cell.row + i] =
|
412
|
+
[heights_by_row[cell.row + i], height_per_row].max
|
413
|
+
end
|
414
|
+
end
|
415
|
+
heights_by_row.sort_by { |row, _| row }.map { |_, h| h }
|
416
|
+
end
|
367
417
|
end
|
368
418
|
|
369
419
|
protected
|
@@ -375,23 +425,77 @@ module Prawn
|
|
375
425
|
def make_cells(data)
|
376
426
|
assert_proper_table_data(data)
|
377
427
|
|
378
|
-
cells =
|
428
|
+
cells = Cells.new
|
379
429
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
430
|
+
row_number = 0
|
431
|
+
data.each do |row_cells|
|
432
|
+
column_number = 0
|
433
|
+
row_cells.each do |cell_data|
|
434
|
+
# If we landed on a spanned cell (from a rowspan above), continue
|
435
|
+
# until we find an empty spot.
|
436
|
+
column_number += 1 until cells[row_number, column_number].nil?
|
437
|
+
|
438
|
+
# Build the cell and store it in the Cells collection.
|
385
439
|
cell = Cell.make(@pdf, cell_data)
|
386
|
-
cell
|
387
|
-
|
388
|
-
|
389
|
-
|
440
|
+
cells[row_number, column_number] = cell
|
441
|
+
|
442
|
+
# Add dummy cells for the rest of the cells in the span group. This
|
443
|
+
# allows Prawn to keep track of the horizontal and vertical space
|
444
|
+
# occupied in each column and row spanned by this cell, while still
|
445
|
+
# leaving the master (top left) cell in the group responsible for
|
446
|
+
# drawing. Dummy cells do not put ink on the page.
|
447
|
+
cell.rowspan.times do |i|
|
448
|
+
cell.colspan.times do |j|
|
449
|
+
next if i == 0 && j == 0
|
450
|
+
|
451
|
+
# It is an error to specify spans that overlap; catch this here
|
452
|
+
if bad_cell = cells[row_number + i, column_number + j]
|
453
|
+
raise Prawn::Errors::InvalidTableSpan,
|
454
|
+
"Spans overlap at row #{row_number + i}, " +
|
455
|
+
"column #{column_number + j}."
|
456
|
+
end
|
457
|
+
|
458
|
+
dummy = Cell::SpanDummy.new(@pdf, cell)
|
459
|
+
cells[row_number + i, column_number + j] = dummy
|
460
|
+
cell.dummy_cells << dummy
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
column_number += cell.colspan
|
390
465
|
end
|
466
|
+
|
467
|
+
row_number += 1
|
391
468
|
end
|
469
|
+
|
470
|
+
# Calculate the number of rows and columns in the table, taking into
|
471
|
+
# account that some cells may span past the end of the physical cells we
|
472
|
+
# have.
|
473
|
+
@row_length = cells.map do |cell|
|
474
|
+
cell.row + cell.rowspan
|
475
|
+
end.max
|
476
|
+
|
477
|
+
@column_length = cells.map do |cell|
|
478
|
+
cell.column + cell.colspan
|
479
|
+
end.max
|
480
|
+
|
392
481
|
cells
|
393
482
|
end
|
394
483
|
|
484
|
+
# Add the header row to the given array of cells at the given y-position.
|
485
|
+
# Number the row with the given +row+ index, so that the header appears (in
|
486
|
+
# any Cells built for this page) immediately prior to the first data row on
|
487
|
+
# this page.
|
488
|
+
#
|
489
|
+
# Return the height of the header.
|
490
|
+
#
|
491
|
+
def add_header(page_of_cells, y, row)
|
492
|
+
@header_row.each do |cell|
|
493
|
+
cell.row = row
|
494
|
+
page_of_cells << [cell, [cell.x, y]]
|
495
|
+
end
|
496
|
+
@header_row.height
|
497
|
+
end
|
498
|
+
|
395
499
|
# Raises an error if the data provided cannot be converted into a valid
|
396
500
|
# table.
|
397
501
|
#
|
@@ -408,23 +512,24 @@ module Prawn
|
|
408
512
|
end
|
409
513
|
end
|
410
514
|
|
411
|
-
# If the table has a header, draw it at the current position.
|
412
|
-
#
|
413
|
-
def draw_header
|
414
|
-
if @header
|
415
|
-
y = @pdf.cursor
|
416
|
-
row(0).each do |cell|
|
417
|
-
cell.y = y
|
418
|
-
cell.draw
|
419
|
-
end
|
420
|
-
@pdf.move_cursor_to(y - row(0).height)
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
515
|
# Returns an array of each column's natural (unconstrained) width.
|
425
516
|
#
|
426
517
|
def natural_column_widths
|
427
|
-
@natural_column_widths ||=
|
518
|
+
@natural_column_widths ||=
|
519
|
+
begin
|
520
|
+
widths_by_column = Hash.new(0)
|
521
|
+
cells.each do |cell|
|
522
|
+
next if cell.is_a?(Cell::SpanDummy)
|
523
|
+
|
524
|
+
# Split the width of colspanned cells evenly by columns
|
525
|
+
width_per_column = cell.width.to_f / cell.colspan
|
526
|
+
cell.colspan.times do |i|
|
527
|
+
widths_by_column[cell.column + i] =
|
528
|
+
[widths_by_column[cell.column + i], width_per_column].max
|
529
|
+
end
|
530
|
+
end
|
531
|
+
widths_by_column.sort_by { |col, _| col }.map { |_, w| w }
|
532
|
+
end
|
428
533
|
end
|
429
534
|
|
430
535
|
# Returns the "natural" (unconstrained) width of the table. This may be
|
@@ -433,7 +538,7 @@ module Prawn
|
|
433
538
|
# a mile long.
|
434
539
|
#
|
435
540
|
def natural_width
|
436
|
-
@natural_width ||= natural_column_widths.inject(0
|
541
|
+
@natural_width ||= natural_column_widths.inject(0, &:+)
|
437
542
|
end
|
438
543
|
|
439
544
|
# Assigns the calculated column widths to each cell. This ensures that each
|