prawn 0.11.1.pre → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +2 -340
- data/HACKING +1 -1
- data/LICENSE +3 -3
- data/Rakefile +17 -6
- data/data/encodings/win_ansi.txt +1 -1
- data/data/images/prawn.png +0 -0
- data/data/pdfs/form.pdf +820 -0
- data/data/pdfs/multipage_template.pdf +127 -0
- data/examples/bounding_box/bounding_boxes.rb +4 -3
- data/examples/bounding_box/indentation.rb +2 -1
- data/examples/bounding_box/russian_boxes.rb +3 -2
- data/examples/bounding_box/stretched_nesting.rb +2 -1
- data/examples/general/background.rb +2 -1
- data/examples/general/canvas.rb +2 -1
- data/examples/general/context_sensitive_headers.rb +2 -1
- data/examples/general/float.rb +2 -1
- data/examples/general/margin.rb +2 -1
- data/examples/general/measurement_units.rb +2 -1
- data/examples/general/metadata-info.rb +2 -1
- data/examples/general/multi_page_layout.rb +2 -1
- data/examples/general/outlines.rb +2 -1
- data/examples/general/page_geometry.rb +2 -1
- data/examples/general/page_numbering.rb +27 -2
- data/examples/general/page_templates.rb +20 -0
- data/examples/general/repeaters.rb +2 -1
- data/examples/general/stamp.rb +4 -3
- data/examples/general/templates.rb +2 -1
- data/examples/graphics/basic_images.rb +2 -1
- data/examples/graphics/cmyk.rb +2 -1
- data/examples/graphics/curves.rb +4 -3
- data/examples/graphics/gradient.rb +23 -0
- data/examples/graphics/hexagon.rb +3 -2
- data/examples/graphics/image_fit.rb +3 -2
- data/examples/graphics/image_flow.rb +2 -1
- data/examples/graphics/image_position.rb +3 -2
- data/examples/graphics/line.rb +2 -1
- data/examples/graphics/png_types.rb +3 -2
- data/examples/graphics/polygons.rb +3 -2
- data/examples/graphics/remote_images.rb +2 -1
- data/examples/graphics/rounded_polygons.rb +2 -1
- data/examples/graphics/rounded_rectangle.rb +2 -1
- data/examples/graphics/ruport_style_helpers.rb +3 -2
- data/examples/graphics/stroke_bounds.rb +2 -1
- data/examples/graphics/stroke_cap_and_join.rb +2 -1
- data/examples/graphics/stroke_dash.rb +2 -1
- data/examples/graphics/transformations.rb +2 -1
- data/examples/graphics/transparency.rb +4 -3
- data/examples/grid/bounding_boxes.rb +2 -1
- data/examples/grid/column_gutter_grid.rb +2 -1
- data/examples/grid/multi_boxes.rb +2 -1
- data/examples/grid/show_grid.rb +2 -1
- data/examples/grid/simple_grid.rb +2 -1
- data/examples/m17n/chinese_text_wrapping.rb +2 -1
- data/examples/m17n/euro.rb +3 -2
- data/examples/m17n/full_win_ansi_character_list.rb +20 -0
- data/examples/m17n/sjis.rb +2 -1
- data/examples/m17n/utf8.rb +3 -2
- data/examples/m17n/win_ansi_charset.rb +2 -1
- data/examples/security/hello_foo.rb +2 -1
- data/examples/table/bill.rb +2 -1
- data/examples/table/borders.rb +25 -0
- data/examples/table/cell.rb +3 -2
- data/examples/table/checkerboard.rb +2 -1
- data/examples/table/header.rb +3 -2
- data/examples/table/inline_format_table.rb +2 -1
- data/examples/table/multi_page_table.rb +2 -1
- data/examples/table/simple_table.rb +2 -1
- data/examples/table/subtable.rb +2 -1
- data/examples/table/widths.rb +2 -1
- data/examples/text/alignment.rb +2 -1
- data/examples/text/character_spacing.rb +2 -1
- data/examples/text/dfont.rb +2 -1
- data/examples/text/family_based_styling.rb +3 -2
- data/examples/text/font_calculations.rb +2 -1
- data/examples/text/font_size.rb +2 -1
- data/examples/text/hyphenation.rb +2 -2
- data/examples/text/indent_paragraphs.rb +7 -5
- data/examples/text/inline_format.rb +7 -6
- data/examples/text/kerning.rb +2 -1
- data/examples/text/rendering_mode.rb +21 -0
- data/examples/text/rotated.rb +2 -1
- data/examples/text/shaped_text_box.rb +2 -1
- data/examples/text/simple_text.rb +2 -1
- data/examples/text/simple_text_ttf.rb +2 -1
- data/examples/text/span.rb +3 -2
- data/examples/text/text_box.rb +7 -5
- data/examples/text/text_box_returning_excess.rb +4 -3
- data/examples/text/text_flow.rb +2 -1
- data/lib/prawn.rb +1 -1
- data/lib/prawn/core/object_store.rb +42 -14
- data/lib/prawn/core/page.rb +22 -8
- data/lib/prawn/core/text.rb +141 -13
- data/lib/prawn/core/text/formatted/arranger.rb +39 -12
- data/lib/prawn/core/text/formatted/line_wrap.rb +205 -60
- data/lib/prawn/core/text/formatted/wrap.rb +72 -35
- data/lib/prawn/document.rb +174 -70
- data/lib/prawn/document/bounding_box.rb +122 -83
- data/lib/prawn/document/column_box.rb +113 -0
- data/lib/prawn/document/graphics_state.rb +90 -2
- data/lib/prawn/document/internals.rb +5 -3
- data/lib/prawn/errors.rb +5 -0
- data/lib/prawn/font.rb +4 -4
- data/lib/prawn/font/afm.rb +11 -0
- data/lib/prawn/font/ttf.rb +5 -0
- data/lib/prawn/graphics.rb +77 -14
- data/lib/prawn/graphics/cap_style.rb +13 -5
- data/lib/prawn/graphics/color.rb +54 -35
- data/lib/prawn/graphics/dash.rb +27 -16
- data/lib/prawn/graphics/gradient.rb +84 -0
- data/lib/prawn/graphics/join_style.rb +12 -3
- data/lib/prawn/graphics/transparency.rb +4 -4
- data/lib/prawn/images.rb +18 -160
- data/lib/prawn/images/jpg.rb +39 -0
- data/lib/prawn/images/png.rb +130 -0
- data/lib/prawn/repeater.rb +6 -13
- data/lib/prawn/security.rb +6 -1
- data/lib/prawn/stamp.rb +12 -4
- data/lib/prawn/table.rb +36 -4
- data/lib/prawn/table/cell.rb +224 -63
- data/lib/prawn/table/cell/text.rb +20 -10
- data/lib/prawn/table/cells.rb +23 -6
- data/lib/prawn/text.rb +54 -91
- data/lib/prawn/text/box.rb +29 -283
- data/lib/prawn/text/formatted/box.rb +349 -24
- data/lib/prawn/text/formatted/fragment.rb +63 -2
- data/lib/prawn/text/formatted/parser.rb +2 -1
- data/prawn.gemspec +21 -5
- data/spec/bounding_box_spec.rb +61 -28
- data/spec/cell_spec.rb +168 -30
- data/spec/document_spec.rb +187 -3
- data/spec/extensions/mocha.rb +45 -0
- data/spec/font_spec.rb +32 -1
- data/spec/formatted_text_arranger_spec.rb +35 -40
- data/spec/formatted_text_box_spec.rb +287 -443
- data/spec/formatted_text_fragment_spec.rb +87 -0
- data/spec/graphics_spec.rb +128 -12
- data/spec/grid_spec.rb +1 -1
- data/spec/images_spec.rb +11 -3
- data/spec/inline_formatted_text_parser_spec.rb +8 -0
- data/spec/line_wrap_spec.rb +200 -208
- data/spec/object_store_spec.rb +10 -0
- data/spec/outline_spec.rb +7 -3
- data/spec/repeater_spec.rb +58 -10
- data/spec/security_spec.rb +6 -0
- data/spec/spec_helper.rb +12 -8
- data/spec/stamp_spec.rb +52 -1
- data/spec/stroke_styles_spec.rb +30 -0
- data/spec/table_spec.rb +93 -3
- data/spec/template_spec.rb +132 -6
- data/spec/text_at_spec.rb +14 -4
- data/spec/text_box_spec.rb +309 -70
- data/spec/text_rendering_mode_spec.rb +45 -0
- data/spec/text_spec.rb +60 -17
- data/spec/text_with_inline_formatting_spec.rb +4 -162
- metadata +241 -241
- data/lib/prawn/core/text/line_wrap.rb +0 -211
- data/lib/prawn/core/text/wrap.rb +0 -82
- data/vendor/pdf-inspector/README +0 -18
- data/vendor/pdf-inspector/lib/pdf/inspector.rb +0 -26
- data/vendor/pdf-inspector/lib/pdf/inspector/extgstate.rb +0 -18
- data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +0 -131
- data/vendor/pdf-inspector/lib/pdf/inspector/page.rb +0 -25
- data/vendor/pdf-inspector/lib/pdf/inspector/text.rb +0 -46
- data/vendor/pdf-inspector/lib/pdf/inspector/xobject.rb +0 -19
- data/vendor/ttfunk/data/fonts/DejaVuSans.ttf +0 -0
- data/vendor/ttfunk/data/fonts/comicsans.ttf +0 -0
- data/vendor/ttfunk/example.rb +0 -45
- data/vendor/ttfunk/lib/ttfunk.rb +0 -102
- data/vendor/ttfunk/lib/ttfunk/directory.rb +0 -17
- data/vendor/ttfunk/lib/ttfunk/encoding/mac_roman.rb +0 -88
- data/vendor/ttfunk/lib/ttfunk/encoding/windows_1252.rb +0 -69
- data/vendor/ttfunk/lib/ttfunk/reader.rb +0 -44
- data/vendor/ttfunk/lib/ttfunk/resource_file.rb +0 -78
- data/vendor/ttfunk/lib/ttfunk/subset.rb +0 -18
- data/vendor/ttfunk/lib/ttfunk/subset/base.rb +0 -141
- data/vendor/ttfunk/lib/ttfunk/subset/mac_roman.rb +0 -50
- data/vendor/ttfunk/lib/ttfunk/subset/unicode.rb +0 -48
- data/vendor/ttfunk/lib/ttfunk/subset/unicode_8bit.rb +0 -63
- data/vendor/ttfunk/lib/ttfunk/subset/windows_1252.rb +0 -55
- data/vendor/ttfunk/lib/ttfunk/subset_collection.rb +0 -72
- data/vendor/ttfunk/lib/ttfunk/table.rb +0 -46
- data/vendor/ttfunk/lib/ttfunk/table/cmap.rb +0 -34
- data/vendor/ttfunk/lib/ttfunk/table/cmap/format00.rb +0 -54
- data/vendor/ttfunk/lib/ttfunk/table/cmap/format04.rb +0 -126
- data/vendor/ttfunk/lib/ttfunk/table/cmap/subtable.rb +0 -79
- data/vendor/ttfunk/lib/ttfunk/table/glyf.rb +0 -64
- data/vendor/ttfunk/lib/ttfunk/table/glyf/compound.rb +0 -81
- data/vendor/ttfunk/lib/ttfunk/table/glyf/simple.rb +0 -37
- data/vendor/ttfunk/lib/ttfunk/table/head.rb +0 -44
- data/vendor/ttfunk/lib/ttfunk/table/hhea.rb +0 -41
- data/vendor/ttfunk/lib/ttfunk/table/hmtx.rb +0 -47
- data/vendor/ttfunk/lib/ttfunk/table/kern.rb +0 -79
- data/vendor/ttfunk/lib/ttfunk/table/kern/format0.rb +0 -62
- data/vendor/ttfunk/lib/ttfunk/table/loca.rb +0 -43
- data/vendor/ttfunk/lib/ttfunk/table/maxp.rb +0 -40
- data/vendor/ttfunk/lib/ttfunk/table/name.rb +0 -125
- data/vendor/ttfunk/lib/ttfunk/table/os2.rb +0 -78
- data/vendor/ttfunk/lib/ttfunk/table/post.rb +0 -91
- data/vendor/ttfunk/lib/ttfunk/table/post/format10.rb +0 -43
- data/vendor/ttfunk/lib/ttfunk/table/post/format20.rb +0 -35
- data/vendor/ttfunk/lib/ttfunk/table/post/format25.rb +0 -23
- data/vendor/ttfunk/lib/ttfunk/table/post/format30.rb +0 -17
- data/vendor/ttfunk/lib/ttfunk/table/post/format40.rb +0 -17
- data/vendor/ttfunk/lib/ttfunk/table/simple.rb +0 -14
data/lib/prawn/images/png.rb
CHANGED
@@ -129,6 +129,136 @@ module Prawn
|
|
129
129
|
8
|
130
130
|
end
|
131
131
|
|
132
|
+
# Build a PDF object representing this image in +document+, and return
|
133
|
+
# a Reference to it.
|
134
|
+
#
|
135
|
+
def build_pdf_object(document)
|
136
|
+
if compression_method != 0
|
137
|
+
raise Errors::UnsupportedImageType,
|
138
|
+
'PNG uses an unsupported compression method'
|
139
|
+
end
|
140
|
+
|
141
|
+
if filter_method != 0
|
142
|
+
raise Errors::UnsupportedImageType,
|
143
|
+
'PNG uses an unsupported filter method'
|
144
|
+
end
|
145
|
+
|
146
|
+
if interlace_method != 0
|
147
|
+
raise Errors::UnsupportedImageType,
|
148
|
+
'PNG uses unsupported interlace method'
|
149
|
+
end
|
150
|
+
|
151
|
+
# some PNG types store the colour and alpha channel data together,
|
152
|
+
# which the PDF spec doesn't like, so split it out.
|
153
|
+
split_alpha_channel!
|
154
|
+
|
155
|
+
case colors
|
156
|
+
when 1
|
157
|
+
color = :DeviceGray
|
158
|
+
when 3
|
159
|
+
color = :DeviceRGB
|
160
|
+
else
|
161
|
+
raise Errors::UnsupportedImageType,
|
162
|
+
"PNG uses an unsupported number of colors (#{png.colors})"
|
163
|
+
end
|
164
|
+
|
165
|
+
# build the image dict
|
166
|
+
obj = document.ref!(
|
167
|
+
:Type => :XObject,
|
168
|
+
:Subtype => :Image,
|
169
|
+
:Height => height,
|
170
|
+
:Width => width,
|
171
|
+
:BitsPerComponent => bits,
|
172
|
+
:Length => img_data.size,
|
173
|
+
:Filter => :FlateDecode
|
174
|
+
)
|
175
|
+
|
176
|
+
unless alpha_channel
|
177
|
+
obj.data[:DecodeParms] = {:Predictor => 15,
|
178
|
+
:Colors => colors,
|
179
|
+
:BitsPerComponent => bits,
|
180
|
+
:Columns => width}
|
181
|
+
end
|
182
|
+
|
183
|
+
# append the actual image data to the object as a stream
|
184
|
+
obj << img_data
|
185
|
+
|
186
|
+
# sort out the colours of the image
|
187
|
+
if palette.empty?
|
188
|
+
obj.data[:ColorSpace] = color
|
189
|
+
else
|
190
|
+
# embed the colour palette in the PDF as a object stream
|
191
|
+
palette_obj = document.ref!(:Length => palette.size)
|
192
|
+
palette_obj << palette
|
193
|
+
|
194
|
+
# build the color space array for the image
|
195
|
+
obj.data[:ColorSpace] = [:Indexed,
|
196
|
+
:DeviceRGB,
|
197
|
+
(palette.size / 3) -1,
|
198
|
+
palette_obj]
|
199
|
+
end
|
200
|
+
|
201
|
+
# *************************************
|
202
|
+
# add transparency data if necessary
|
203
|
+
# *************************************
|
204
|
+
|
205
|
+
# For PNG color types 0, 2 and 3, the transparency data is stored in
|
206
|
+
# a dedicated PNG chunk, and is exposed via the transparency attribute
|
207
|
+
# of the PNG class.
|
208
|
+
if transparency[:grayscale]
|
209
|
+
# Use Color Key Masking (spec section 4.8.5)
|
210
|
+
# - An array with N elements, where N is two times the number of color
|
211
|
+
# components.
|
212
|
+
val = transparency[:grayscale]
|
213
|
+
obj.data[:Mask] = [val, val]
|
214
|
+
elsif transparency[:rgb]
|
215
|
+
# Use Color Key Masking (spec section 4.8.5)
|
216
|
+
# - An array with N elements, where N is two times the number of color
|
217
|
+
# components.
|
218
|
+
rgb = transparency[:rgb]
|
219
|
+
obj.data[:Mask] = rgb.collect { |x| [x,x] }.flatten
|
220
|
+
elsif transparency[:indexed]
|
221
|
+
# TODO: broken. I was attempting to us Color Key Masking, but I think
|
222
|
+
# we need to construct an SMask i think. Maybe do it inside
|
223
|
+
# the PNG class, and store it in alpha_channel
|
224
|
+
#obj.data[:Mask] = transparency[:indexed]
|
225
|
+
end
|
226
|
+
|
227
|
+
# For PNG color types 4 and 6, the transparency data is stored as a alpha
|
228
|
+
# channel mixed in with the main image data. The PNG class seperates
|
229
|
+
# it out for us and makes it available via the alpha_channel attribute
|
230
|
+
if alpha_channel?
|
231
|
+
smask_obj = document.ref!(
|
232
|
+
:Type => :XObject,
|
233
|
+
:Subtype => :Image,
|
234
|
+
:Height => height,
|
235
|
+
:Width => width,
|
236
|
+
:BitsPerComponent => alpha_channel_bits,
|
237
|
+
:Length => alpha_channel.size,
|
238
|
+
:Filter => :FlateDecode,
|
239
|
+
:ColorSpace => :DeviceGray,
|
240
|
+
:Decode => [0, 1]
|
241
|
+
)
|
242
|
+
smask_obj << alpha_channel
|
243
|
+
obj.data[:SMask] = smask_obj
|
244
|
+
end
|
245
|
+
|
246
|
+
obj
|
247
|
+
end
|
248
|
+
|
249
|
+
# Returns the minimum PDF version required to support this image.
|
250
|
+
def min_pdf_version
|
251
|
+
if bits > 8
|
252
|
+
# 16-bit color only supported in 1.5+ (ISO 32000-1:2008 8.9.5.1)
|
253
|
+
1.5
|
254
|
+
elsif alpha_channel?
|
255
|
+
# Need transparency for SMask
|
256
|
+
1.4
|
257
|
+
else
|
258
|
+
1.0
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
132
262
|
private
|
133
263
|
|
134
264
|
def unfilter_image_data
|
data/lib/prawn/repeater.rb
CHANGED
@@ -96,30 +96,23 @@ module Prawn
|
|
96
96
|
@stamp_name = "prawn_repeater(#{Repeater.count})"
|
97
97
|
@document.create_stamp(@stamp_name, &block) unless dynamic
|
98
98
|
@block = block if dynamic
|
99
|
+
@graphic_state = document.state.page.graphic_state.dup
|
99
100
|
|
100
101
|
Repeater.count += 1
|
101
102
|
end
|
102
103
|
|
103
104
|
def match?(page_number)
|
104
|
-
|
105
|
-
when :all
|
106
|
-
true
|
107
|
-
when :odd
|
108
|
-
page_number % 2 == 1
|
109
|
-
when :even
|
110
|
-
page_number % 2 == 0
|
111
|
-
when Range, Array
|
112
|
-
@page_filter.include?(page_number)
|
113
|
-
when Proc
|
114
|
-
@page_filter.call(page_number)
|
115
|
-
end
|
105
|
+
@document.page_match?(@page_filter, page_number)
|
116
106
|
end
|
117
107
|
|
118
108
|
def run(page_number)
|
119
109
|
if !@dynamic
|
120
110
|
@document.stamp(@stamp_name) if match?(page_number)
|
121
111
|
elsif @block
|
122
|
-
@
|
112
|
+
@document.save_graphics_state(@graphic_state) do
|
113
|
+
@document.send(:freeze_stamp_graphics)
|
114
|
+
@block.call
|
115
|
+
end
|
123
116
|
end
|
124
117
|
end
|
125
118
|
|
data/lib/prawn/security.rb
CHANGED
@@ -47,7 +47,7 @@ module Prawn
|
|
47
47
|
#
|
48
48
|
# <tt>:print_document</tt>:: Print document.
|
49
49
|
#
|
50
|
-
# <tt>:
|
50
|
+
# <tt>:modify_contents</tt>:: Modify contents of document (other than text
|
51
51
|
# annotations and interactive form fields).
|
52
52
|
#
|
53
53
|
# <tt>:copy_contents</tt>:: Copy text and graphics from document.
|
@@ -142,6 +142,11 @@ module Prawn
|
|
142
142
|
def permissions=(perms={})
|
143
143
|
@permissions ||= FullPermissions
|
144
144
|
perms.each do |key, value|
|
145
|
+
unless PermissionsBits[key]
|
146
|
+
raise ArgumentError, "Unknown permission :#{key}. Valid options: " +
|
147
|
+
PermissionsBits.keys.map { |k| k.inspect }.join(", ")
|
148
|
+
end
|
149
|
+
|
145
150
|
# 0-based bit number, from LSB
|
146
151
|
bit_position = PermissionsBits[key] - 1
|
147
152
|
|
data/lib/prawn/stamp.rb
CHANGED
@@ -21,7 +21,7 @@ module Prawn
|
|
21
21
|
#
|
22
22
|
# Example:
|
23
23
|
# pdf.create_stamp("my_stamp") {
|
24
|
-
# pdf.
|
24
|
+
# pdf.fill_circle([10, 15], 5)
|
25
25
|
# pdf.draw_text("hello world", :at => [20, 10])
|
26
26
|
# }
|
27
27
|
# pdf.stamp("my_stamp")
|
@@ -35,7 +35,7 @@ module Prawn
|
|
35
35
|
#
|
36
36
|
# Example:
|
37
37
|
# pdf.create_stamp("my_stamp") {
|
38
|
-
# pdf.
|
38
|
+
# pdf.fill_circle([10, 15], 5)
|
39
39
|
# pdf.text("hello world", :at => [20, 10])
|
40
40
|
# }
|
41
41
|
# pdf.stamp("my_stamp")
|
@@ -52,7 +52,7 @@ module Prawn
|
|
52
52
|
#
|
53
53
|
# Example:
|
54
54
|
# pdf.create_stamp("circle") do
|
55
|
-
# pdf.
|
55
|
+
# pdf.fill_circle([0, 0], 25)
|
56
56
|
# end
|
57
57
|
# # draws a circle at 100, 100
|
58
58
|
# pdf.stamp_at("circle", [100, 100])
|
@@ -71,7 +71,7 @@ module Prawn
|
|
71
71
|
#
|
72
72
|
# Example:
|
73
73
|
# pdf.create_stamp("my_stamp") {
|
74
|
-
# pdf.
|
74
|
+
# pdf.fill_circle([10, 15], 5)
|
75
75
|
# pdf.draw_text("hello world", :at => [20, 10])
|
76
76
|
# }
|
77
77
|
#
|
@@ -121,6 +121,14 @@ module Prawn
|
|
121
121
|
:stamp_dictionary => dictionary }
|
122
122
|
dictionary
|
123
123
|
end
|
124
|
+
|
125
|
+
def freeze_stamp_graphics
|
126
|
+
update_colors
|
127
|
+
write_line_width
|
128
|
+
write_stroke_cap_style
|
129
|
+
write_stroke_join_style
|
130
|
+
write_stroke_dash
|
131
|
+
end
|
124
132
|
|
125
133
|
end
|
126
134
|
end
|
data/lib/prawn/table.rb
CHANGED
@@ -230,12 +230,42 @@ module Prawn
|
|
230
230
|
ref_bounds = @pdf.bounds.stretchy? ? @pdf.margin_box : @pdf.bounds
|
231
231
|
|
232
232
|
last_y = @pdf.y
|
233
|
+
|
234
|
+
# Determine whether we're at the top of the current bounds (margin box or
|
235
|
+
# bounding box). If we're at the top, we couldn't gain any more room by
|
236
|
+
# breaking to the next page -- this means, in particular, that if the
|
237
|
+
# first row is taller than the margin box, we will only move to the next
|
238
|
+
# page if we're below the top. Some floating-point tolerance is added to
|
239
|
+
# the calculation.
|
240
|
+
#
|
241
|
+
# Note that we use the actual bounds, not the reference bounds. This is
|
242
|
+
# because even if we are in a stretchy bounding box, flowing to the next
|
243
|
+
# page will not buy us any space if we are at the top.
|
244
|
+
if @pdf.y > @pdf.bounds.height + @pdf.bounds.absolute_bottom - 0.001
|
245
|
+
# we're at the top of our bounds
|
246
|
+
started_new_page_at_row = 0
|
247
|
+
else
|
248
|
+
started_new_page_at_row = -1
|
249
|
+
|
250
|
+
# If there isn't enough room left on the page to fit the first data row
|
251
|
+
# (excluding the header), start the table on the next page.
|
252
|
+
needed_height = row(0).height
|
253
|
+
needed_height += row(1).height if @header
|
254
|
+
if needed_height > @pdf.y - ref_bounds.absolute_bottom
|
255
|
+
@pdf.bounds.move_past_bottom
|
256
|
+
offset = @pdf.y
|
257
|
+
started_new_page_at_row = 0
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
233
261
|
@cells.each do |cell|
|
234
|
-
if cell.height > (cell.y + offset) - ref_bounds.absolute_bottom
|
262
|
+
if cell.height > (cell.y + offset) - ref_bounds.absolute_bottom &&
|
263
|
+
cell.row > started_new_page_at_row
|
235
264
|
# start a new page or column
|
236
265
|
@pdf.bounds.move_past_bottom
|
237
|
-
draw_header
|
266
|
+
draw_header unless cell.row == 0
|
238
267
|
offset = @pdf.y - cell.y
|
268
|
+
started_new_page_at_row = cell.row
|
239
269
|
end
|
240
270
|
|
241
271
|
# Don't modify cell.x / cell.y here, as we want to reuse the original
|
@@ -275,12 +305,14 @@ module Prawn
|
|
275
305
|
@column_widths ||= begin
|
276
306
|
if width < cells.min_width
|
277
307
|
raise Errors::CannotFit,
|
278
|
-
"Table's width was set too small to contain its contents"
|
308
|
+
"Table's width was set too small to contain its contents " +
|
309
|
+
"(min width #{cells.min_width}, requested #{width})"
|
279
310
|
end
|
280
311
|
|
281
312
|
if width > cells.max_width
|
282
313
|
raise Errors::CannotFit,
|
283
|
-
"Table's width was set larger than its contents' maximum width"
|
314
|
+
"Table's width was set larger than its contents' maximum width " +
|
315
|
+
"(max width #{cells.max_width}, requested #{width})"
|
284
316
|
end
|
285
317
|
|
286
318
|
if width < natural_width
|
data/lib/prawn/table/cell.rb
CHANGED
@@ -55,11 +55,17 @@ module Prawn
|
|
55
55
|
|
56
56
|
# If provided, the minimum width that this cell will permit.
|
57
57
|
#
|
58
|
-
|
58
|
+
def min_width
|
59
|
+
set_width_constraints
|
60
|
+
@min_width
|
61
|
+
end
|
59
62
|
|
60
63
|
# If provided, the maximum width that this cell can be drawn in.
|
61
64
|
#
|
62
|
-
|
65
|
+
def max_width
|
66
|
+
set_width_constraints
|
67
|
+
@max_width
|
68
|
+
end
|
63
69
|
|
64
70
|
# Manually specify the cell's height.
|
65
71
|
#
|
@@ -70,14 +76,13 @@ module Prawn
|
|
70
76
|
#
|
71
77
|
attr_accessor :borders
|
72
78
|
|
73
|
-
#
|
79
|
+
# Width, in PDF points, of the cell's borders: [top, right, bottom, left].
|
74
80
|
#
|
75
|
-
|
81
|
+
attr_reader :border_widths
|
76
82
|
|
77
|
-
#
|
78
|
-
# "ccffff".
|
83
|
+
# HTML RGB-format ("ccffff") border colors: [top, right, bottom, left].
|
79
84
|
#
|
80
|
-
|
85
|
+
attr_reader :border_colors
|
81
86
|
|
82
87
|
# Specifies the content for the cell. Must be a "cellable" object. See the
|
83
88
|
# "Data" section of the Prawn::Table documentation for details on cellable
|
@@ -97,8 +102,15 @@ module Prawn
|
|
97
102
|
#
|
98
103
|
def self.make(pdf, content, options={})
|
99
104
|
at = options.delete(:at) || [0, pdf.cursor]
|
100
|
-
content =
|
101
|
-
|
105
|
+
content = content.to_s if content.nil? || content.kind_of?(Numeric) ||
|
106
|
+
content.kind_of?(Date)
|
107
|
+
|
108
|
+
if content.is_a?(Hash)
|
109
|
+
options.update(content)
|
110
|
+
content = options[:content]
|
111
|
+
else
|
112
|
+
options[:content] = content
|
113
|
+
end
|
102
114
|
|
103
115
|
case content
|
104
116
|
when Prawn::Table::Cell
|
@@ -135,16 +147,29 @@ module Prawn
|
|
135
147
|
@point = point
|
136
148
|
|
137
149
|
# Set defaults; these can be changed by options
|
138
|
-
@padding
|
139
|
-
@borders
|
140
|
-
@
|
141
|
-
@
|
150
|
+
@padding = [5, 5, 5, 5]
|
151
|
+
@borders = [:top, :bottom, :left, :right]
|
152
|
+
@border_widths = [1] * 4
|
153
|
+
@border_colors = ['000000'] * 4
|
142
154
|
|
143
155
|
options.each { |k, v| send("#{k}=", v) }
|
156
|
+
end
|
157
|
+
|
158
|
+
# Supports setting multiple properties at once.
|
159
|
+
#
|
160
|
+
# cell.style(:padding => 0, :border_width => 2)
|
161
|
+
#
|
162
|
+
# is the same as:
|
163
|
+
#
|
164
|
+
# cell.padding = 0
|
165
|
+
# cell.border_width = 2
|
166
|
+
#
|
167
|
+
def style(options={}, &block)
|
168
|
+
options.each { |k, v| send("#{k}=", v) }
|
144
169
|
|
145
|
-
#
|
146
|
-
|
147
|
-
|
170
|
+
# The block form supports running a single block for multiple cells, as
|
171
|
+
# in Cells#style.
|
172
|
+
block.call(self) if block
|
148
173
|
end
|
149
174
|
|
150
175
|
# Returns the cell's width in points, inclusive of padding.
|
@@ -209,13 +234,15 @@ module Prawn
|
|
209
234
|
# location at which the cell is drawn.
|
210
235
|
#
|
211
236
|
def draw(pt=[x, y])
|
237
|
+
set_width_constraints
|
238
|
+
|
212
239
|
draw_background(pt)
|
240
|
+
draw_borders(pt)
|
213
241
|
@pdf.bounding_box([pt[0] + padding_left, pt[1] - padding_top],
|
214
242
|
:width => content_width + FPTolerance,
|
215
243
|
:height => content_height + FPTolerance) do
|
216
244
|
draw_content
|
217
245
|
end
|
218
|
-
draw_borders(pt)
|
219
246
|
end
|
220
247
|
|
221
248
|
# x-position of the cell within the parent bounds.
|
@@ -267,19 +294,179 @@ module Prawn
|
|
267
294
|
end
|
268
295
|
end
|
269
296
|
|
297
|
+
def padding_top
|
298
|
+
@padding[0]
|
299
|
+
end
|
300
|
+
|
301
|
+
def padding_top=(val)
|
302
|
+
@padding[0] = val
|
303
|
+
end
|
304
|
+
|
305
|
+
def padding_right
|
306
|
+
@padding[1]
|
307
|
+
end
|
308
|
+
|
309
|
+
def padding_right=(val)
|
310
|
+
@padding[1] = val
|
311
|
+
end
|
312
|
+
|
313
|
+
def padding_bottom
|
314
|
+
@padding[2]
|
315
|
+
end
|
316
|
+
|
317
|
+
def padding_bottom=(val)
|
318
|
+
@padding[2] = val
|
319
|
+
end
|
320
|
+
|
321
|
+
def padding_left
|
322
|
+
@padding[3]
|
323
|
+
end
|
324
|
+
|
325
|
+
def padding_left=(val)
|
326
|
+
@padding[3] = val
|
327
|
+
end
|
328
|
+
|
329
|
+
# Sets border colors on this cell. The argument can be one of:
|
330
|
+
#
|
331
|
+
# * an integer (sets all colors)
|
332
|
+
# * a two-element array [vertical, horizontal]
|
333
|
+
# * a three-element array [top, horizontal, bottom]
|
334
|
+
# * a four-element array [top, right, bottom, left]
|
335
|
+
#
|
336
|
+
def border_color=(color)
|
337
|
+
@border_colors = case
|
338
|
+
when color.nil?
|
339
|
+
["000000"] * 4
|
340
|
+
when String === color # all colors
|
341
|
+
[color, color, color, color]
|
342
|
+
when color.length == 2 # vert, horiz
|
343
|
+
[color[0], color[1], color[0], color[1]]
|
344
|
+
when color.length == 3 # top, horiz, bottom
|
345
|
+
[color[0], color[1], color[2], color[1]]
|
346
|
+
when color.length == 4 # top, right, bottom, left
|
347
|
+
[color[0], color[1], color[2], color[3]]
|
348
|
+
else
|
349
|
+
raise ArgumentError, ":border_color must be a string " +
|
350
|
+
"or an array [v,h] or [t,r,b,l]"
|
351
|
+
end
|
352
|
+
end
|
353
|
+
alias_method :border_colors=, :border_color=
|
354
|
+
|
355
|
+
def border_top_color
|
356
|
+
@border_colors[0]
|
357
|
+
end
|
358
|
+
|
359
|
+
def border_top_color=(val)
|
360
|
+
@border_colors[0] = val
|
361
|
+
end
|
362
|
+
|
363
|
+
def border_top_color
|
364
|
+
@border_colors[0]
|
365
|
+
end
|
366
|
+
|
367
|
+
def border_top_color=(val)
|
368
|
+
@border_colors[0] = val
|
369
|
+
end
|
370
|
+
|
371
|
+
def border_right_color
|
372
|
+
@border_colors[1]
|
373
|
+
end
|
374
|
+
|
375
|
+
def border_right_color=(val)
|
376
|
+
@border_colors[1] = val
|
377
|
+
end
|
378
|
+
|
379
|
+
def border_bottom_color
|
380
|
+
@border_colors[2]
|
381
|
+
end
|
382
|
+
|
383
|
+
def border_bottom_color=(val)
|
384
|
+
@border_colors[2] = val
|
385
|
+
end
|
386
|
+
|
387
|
+
def border_left_color
|
388
|
+
@border_colors[3]
|
389
|
+
end
|
390
|
+
|
391
|
+
def border_left_color=(val)
|
392
|
+
@border_colors[3] = val
|
393
|
+
end
|
394
|
+
|
395
|
+
# Sets border widths on this cell. The argument can be one of:
|
396
|
+
#
|
397
|
+
# * an integer (sets all widths)
|
398
|
+
# * a two-element array [vertical, horizontal]
|
399
|
+
# * a three-element array [top, horizontal, bottom]
|
400
|
+
# * a four-element array [top, right, bottom, left]
|
401
|
+
#
|
402
|
+
def border_width=(width)
|
403
|
+
@border_widths = case
|
404
|
+
when width.nil?
|
405
|
+
["000000"] * 4
|
406
|
+
when Numeric === width # all widths
|
407
|
+
[width, width, width, width]
|
408
|
+
when width.length == 2 # vert, horiz
|
409
|
+
[width[0], width[1], width[0], width[1]]
|
410
|
+
when width.length == 3 # top, horiz, bottom
|
411
|
+
[width[0], width[1], width[2], width[1]]
|
412
|
+
when width.length == 4 # top, right, bottom, left
|
413
|
+
[width[0], width[1], width[2], width[3]]
|
414
|
+
else
|
415
|
+
raise ArgumentError, ":border_width must be a string " +
|
416
|
+
"or an array [v,h] or [t,r,b,l]"
|
417
|
+
end
|
418
|
+
end
|
419
|
+
alias_method :border_widths=, :border_width=
|
420
|
+
|
421
|
+
def border_top_width
|
422
|
+
@borders.include?(:top) ? @border_widths[0] : 0
|
423
|
+
end
|
424
|
+
|
425
|
+
def border_top_width=(val)
|
426
|
+
@border_widths[0] = val
|
427
|
+
end
|
428
|
+
|
429
|
+
def border_right_width
|
430
|
+
@borders.include?(:right) ? @border_widths[1] : 0
|
431
|
+
end
|
432
|
+
|
433
|
+
def border_right_width=(val)
|
434
|
+
@border_widths[1] = val
|
435
|
+
end
|
436
|
+
|
437
|
+
def border_bottom_width
|
438
|
+
@borders.include?(:bottom) ? @border_widths[2] : 0
|
439
|
+
end
|
440
|
+
|
441
|
+
def border_bottom_width=(val)
|
442
|
+
@border_widths[2] = val
|
443
|
+
end
|
444
|
+
|
445
|
+
def border_left_width
|
446
|
+
@borders.include?(:left) ? @border_widths[3] : 0
|
447
|
+
end
|
448
|
+
|
449
|
+
def border_left_width=(val)
|
450
|
+
@border_widths[3] = val
|
451
|
+
end
|
452
|
+
|
270
453
|
protected
|
271
454
|
|
455
|
+
# Sets the cell's minimum and maximum width. Deferred until requested
|
456
|
+
# because padding and size can change.
|
457
|
+
#
|
458
|
+
def set_width_constraints
|
459
|
+
@min_width ||= padding_left + padding_right
|
460
|
+
@max_width ||= @pdf.bounds.width
|
461
|
+
end
|
462
|
+
|
272
463
|
# Draws the cell's background color.
|
273
464
|
#
|
274
465
|
def draw_background(pt)
|
275
|
-
x, y = pt
|
276
|
-
margin = @border_width / 2
|
277
466
|
if @background_color
|
278
467
|
@pdf.mask(:fill_color) do
|
279
468
|
@pdf.fill_color @background_color
|
280
|
-
|
281
|
-
height + margin
|
282
|
-
@pdf.fill_rectangle [x, y], width, h
|
469
|
+
@pdf.fill_rectangle pt, width, height
|
283
470
|
end
|
284
471
|
end
|
285
472
|
end
|
@@ -291,26 +478,32 @@ module Prawn
|
|
291
478
|
#
|
292
479
|
def draw_borders(pt)
|
293
480
|
x, y = pt
|
294
|
-
return if @border_width <= 0
|
295
|
-
# Draw left / right borders one-half border width beyond the center of
|
296
|
-
# the corner, so that the corners end up square.
|
297
|
-
margin = @border_width / 2.0
|
298
481
|
|
299
482
|
@pdf.mask(:line_width, :stroke_color) do
|
300
|
-
@pdf.line_width = @border_width
|
301
|
-
@pdf.stroke_color = @border_color if @border_color
|
302
|
-
|
303
483
|
@borders.each do |border|
|
484
|
+
idx = {:top => 0, :right => 1, :bottom => 2, :left => 3}[border]
|
485
|
+
border_color = @border_colors[idx]
|
486
|
+
border_width = @border_widths[idx]
|
487
|
+
|
488
|
+
next if border_width <= 0
|
489
|
+
|
490
|
+
# Left and right borders are drawn one-half border beyond the center
|
491
|
+
# of the corner, so that the corners end up square.
|
304
492
|
from, to = case border
|
305
493
|
when :top
|
306
494
|
[[x, y], [x+width, y]]
|
307
495
|
when :bottom
|
308
496
|
[[x, y-height], [x+width, y-height]]
|
309
497
|
when :left
|
310
|
-
[[x, y+
|
498
|
+
[[x, y + (border_top_width / 2.0)],
|
499
|
+
[x, y - height - (border_bottom_width / 2.0)]]
|
311
500
|
when :right
|
312
|
-
[[x+width, y
|
501
|
+
[[x+width, y + (border_top_width / 2.0)],
|
502
|
+
[x+width, y - height - (border_bottom_width / 2.0)]]
|
313
503
|
end
|
504
|
+
|
505
|
+
@pdf.line_width = border_width
|
506
|
+
@pdf.stroke_color = border_color
|
314
507
|
@pdf.stroke_line(from, to)
|
315
508
|
end
|
316
509
|
end
|
@@ -323,38 +516,6 @@ module Prawn
|
|
323
516
|
raise NotImplementedError, "subclasses must implement draw_content"
|
324
517
|
end
|
325
518
|
|
326
|
-
def padding_top
|
327
|
-
@padding[0]
|
328
|
-
end
|
329
|
-
|
330
|
-
def padding_top=(val)
|
331
|
-
@padding[0] = val
|
332
|
-
end
|
333
|
-
|
334
|
-
def padding_right
|
335
|
-
@padding[1]
|
336
|
-
end
|
337
|
-
|
338
|
-
def padding_right=(val)
|
339
|
-
@padding[1] = val
|
340
|
-
end
|
341
|
-
|
342
|
-
def padding_bottom
|
343
|
-
@padding[2]
|
344
|
-
end
|
345
|
-
|
346
|
-
def padding_bottom=(val)
|
347
|
-
@padding[2] = val
|
348
|
-
end
|
349
|
-
|
350
|
-
def padding_left
|
351
|
-
@padding[3]
|
352
|
-
end
|
353
|
-
|
354
|
-
def padding_left=(val)
|
355
|
-
@padding[3] = val
|
356
|
-
end
|
357
|
-
|
358
519
|
end
|
359
520
|
end
|
360
521
|
end
|