hexapdf 0.43.0 → 0.45.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/examples/027-composer_optional_content.rb +6 -4
- data/examples/030-pdfa.rb +13 -11
- data/lib/hexapdf/composer.rb +23 -0
- data/lib/hexapdf/content/canvas.rb +3 -3
- data/lib/hexapdf/content/canvas_composer.rb +1 -0
- data/lib/hexapdf/document/files.rb +7 -2
- data/lib/hexapdf/document/layout.rb +15 -3
- data/lib/hexapdf/document/metadata.rb +12 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/layout/box.rb +180 -66
- data/lib/hexapdf/layout/box_fitter.rb +1 -0
- data/lib/hexapdf/layout/column_box.rb +18 -28
- data/lib/hexapdf/layout/container_box.rb +6 -6
- data/lib/hexapdf/layout/frame.rb +13 -94
- data/lib/hexapdf/layout/image_box.rb +4 -4
- data/lib/hexapdf/layout/list_box.rb +13 -31
- data/lib/hexapdf/layout/style.rb +8 -4
- data/lib/hexapdf/layout/table_box.rb +55 -58
- data/lib/hexapdf/layout/text_box.rb +84 -71
- data/lib/hexapdf/layout/text_fragment.rb +1 -1
- data/lib/hexapdf/layout/text_layouter.rb +7 -8
- data/lib/hexapdf/parser.rb +5 -2
- data/lib/hexapdf/rectangle.rb +4 -4
- data/lib/hexapdf/type/file_specification.rb +9 -5
- data/lib/hexapdf/type/form.rb +2 -2
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/test_canvas_composer.rb +13 -8
- data/test/hexapdf/document/test_files.rb +5 -0
- data/test/hexapdf/document/test_layout.rb +16 -0
- data/test/hexapdf/document/test_metadata.rb +21 -0
- data/test/hexapdf/layout/test_box.rb +93 -37
- data/test/hexapdf/layout/test_box_fitter.rb +7 -0
- data/test/hexapdf/layout/test_column_box.rb +7 -13
- data/test/hexapdf/layout/test_container_box.rb +1 -1
- data/test/hexapdf/layout/test_frame.rb +7 -46
- data/test/hexapdf/layout/test_image_box.rb +14 -6
- data/test/hexapdf/layout/test_list_box.rb +26 -27
- data/test/hexapdf/layout/test_table_box.rb +47 -54
- data/test/hexapdf/layout/test_text_box.rb +83 -83
- data/test/hexapdf/test_composer.rb +20 -5
- data/test/hexapdf/test_parser.rb +8 -0
- data/test/hexapdf/test_serializer.rb +1 -0
- data/test/hexapdf/type/test_file_specification.rb +2 -1
- metadata +2 -2
data/lib/hexapdf/layout/frame.rb
CHANGED
@@ -45,7 +45,7 @@ module HexaPDF
|
|
45
45
|
#
|
46
46
|
# == Usage
|
47
47
|
#
|
48
|
-
# After a Frame object is initialized, it is ready for
|
48
|
+
# After a Frame object is initialized, it is ready for fitting boxes in it and drawing them.
|
49
49
|
#
|
50
50
|
# The explicit way of drawing a box follows these steps:
|
51
51
|
#
|
@@ -65,10 +65,6 @@ module HexaPDF
|
|
65
65
|
# splitting is successful, the first box can be drawn (Make sure that the second box is
|
66
66
|
# handled correctly). Otherwise, start over with #find_next_region.
|
67
67
|
#
|
68
|
-
# For applications where splitting is not necessary, an easier way is to just use #draw and
|
69
|
-
# #find_next_region together, as #draw calls #fit if the box was not fit into the current
|
70
|
-
# region.
|
71
|
-
#
|
72
68
|
# == Used Box Properties
|
73
69
|
#
|
74
70
|
# The style properties 'position', 'align', 'valign', 'margin' and 'mask_mode' are taken into
|
@@ -88,80 +84,6 @@ module HexaPDF
|
|
88
84
|
|
89
85
|
include HexaPDF::Utils
|
90
86
|
|
91
|
-
# Stores the result of fitting a box in a Frame.
|
92
|
-
class FitResult
|
93
|
-
|
94
|
-
# The frame into which the box was fitted.
|
95
|
-
attr_accessor :frame
|
96
|
-
|
97
|
-
# The box that was fitted into the frame.
|
98
|
-
attr_accessor :box
|
99
|
-
|
100
|
-
# The horizontal position where the box will be drawn.
|
101
|
-
attr_accessor :x
|
102
|
-
|
103
|
-
# The vertical position where the box will be drawn.
|
104
|
-
attr_accessor :y
|
105
|
-
|
106
|
-
# The available width in the frame for this particular box.
|
107
|
-
attr_accessor :available_width
|
108
|
-
|
109
|
-
# The available height in the frame for this particular box.
|
110
|
-
attr_accessor :available_height
|
111
|
-
|
112
|
-
# The rectangle (a Geom2D::Rectangle object) that will be removed from the frame when
|
113
|
-
# drawing the box.
|
114
|
-
attr_accessor :mask
|
115
|
-
|
116
|
-
# Initialize the result object for the given frame and box.
|
117
|
-
def initialize(frame, box)
|
118
|
-
@frame = frame
|
119
|
-
@box = box
|
120
|
-
@available_width = 0
|
121
|
-
@available_height = 0
|
122
|
-
@success = false
|
123
|
-
end
|
124
|
-
|
125
|
-
# Marks the fitting status as success.
|
126
|
-
def success!
|
127
|
-
@success = true
|
128
|
-
end
|
129
|
-
|
130
|
-
# Returns +true+ if fitting was successful.
|
131
|
-
def success?
|
132
|
-
@success
|
133
|
-
end
|
134
|
-
|
135
|
-
# Draws the #box onto the canvas at (#x + *dx*, #y + *dy*).
|
136
|
-
#
|
137
|
-
# The relative offset (dx, dy) is useful when rendering results that were accumulated and
|
138
|
-
# then need to be moved because the container holding them changes its position.
|
139
|
-
#
|
140
|
-
# The configuration option "debug" can be used to add visual debug output with respect to
|
141
|
-
# box placement.
|
142
|
-
def draw(canvas, dx: 0, dy: 0)
|
143
|
-
doc = canvas.context.document
|
144
|
-
if doc.config['debug']
|
145
|
-
name = (frame.parent_boxes + [box]).map do |box|
|
146
|
-
box.class.to_s.sub(/.*::/, '')
|
147
|
-
end.join('-') << "##{box.object_id}"
|
148
|
-
name = "#{name} (#{(x + dx).to_i},#{(y + dy).to_i}-#{mask.width.to_i}x#{mask.height.to_i})"
|
149
|
-
ocg = doc.optional_content.ocg(name)
|
150
|
-
canvas.optional_content(ocg) do
|
151
|
-
canvas.translate(dx, dy) do
|
152
|
-
canvas.fill_color("green").stroke_color("darkgreen").
|
153
|
-
opacity(fill_alpha: 0.1, stroke_alpha: 0.2).
|
154
|
-
draw(:geom2d, object: mask, path_only: true).fill_stroke
|
155
|
-
end
|
156
|
-
end
|
157
|
-
page = "Page #{canvas.context.index + 1}" rescue "XObject"
|
158
|
-
doc.optional_content.default_configuration.add_ocg_to_ui(ocg, path: ['Debug', page])
|
159
|
-
end
|
160
|
-
box.draw(canvas, x + dx, y + dy)
|
161
|
-
end
|
162
|
-
|
163
|
-
end
|
164
|
-
|
165
87
|
# The x-coordinate of the bottom-left corner.
|
166
88
|
attr_reader :left
|
167
89
|
|
@@ -205,7 +127,7 @@ module HexaPDF
|
|
205
127
|
|
206
128
|
# An array of box objects representing the parent boxes.
|
207
129
|
#
|
208
|
-
# The immediate parent is the last array entry, the top
|
130
|
+
# The immediate parent is the last array entry, the top-most parent the first one. All boxes
|
209
131
|
# that are fitted into this frame have to be child boxes of the immediate parent box.
|
210
132
|
attr_reader :parent_boxes
|
211
133
|
|
@@ -253,16 +175,15 @@ module HexaPDF
|
|
253
175
|
@context&.document
|
254
176
|
end
|
255
177
|
|
256
|
-
# Fits the given box into the current region of available space and returns
|
257
|
-
# object.
|
178
|
+
# Fits the given box into the current region of available space and returns the associated
|
179
|
+
# Box::FitResult object.
|
258
180
|
#
|
259
181
|
# Fitting a box takes the style properties 'position', 'align', 'valign', 'margin', and
|
260
182
|
# 'mask_mode' into account.
|
261
183
|
#
|
262
|
-
# Use the FitResult#success? method to determine whether fitting was successful.
|
184
|
+
# Use the Box::FitResult#success? method to determine whether fitting was successful.
|
263
185
|
def fit(box)
|
264
|
-
|
265
|
-
return fit_result if full?
|
186
|
+
return Box::FitResult.new(box, frame: self) if full?
|
266
187
|
|
267
188
|
margin = box.style.margin if box.style.margin?
|
268
189
|
|
@@ -277,7 +198,7 @@ module HexaPDF
|
|
277
198
|
|
278
199
|
aw = width - x
|
279
200
|
ah = height - y
|
280
|
-
box.fit(aw, ah, self)
|
201
|
+
fit_result = box.fit(aw, ah, self)
|
281
202
|
fit_result.success!
|
282
203
|
|
283
204
|
x += left
|
@@ -294,7 +215,8 @@ module HexaPDF
|
|
294
215
|
ah -= margin_top = margin.top unless float_equal(@y, @bottom + @height)
|
295
216
|
end
|
296
217
|
|
297
|
-
fit_result
|
218
|
+
fit_result = box.fit(aw, ah, self)
|
219
|
+
return fit_result if fit_result.failure?
|
298
220
|
|
299
221
|
width = box.width
|
300
222
|
height = box.height
|
@@ -372,27 +294,24 @@ module HexaPDF
|
|
372
294
|
create_rectangle(@x, @y - available_height, @x + available_width, @y)
|
373
295
|
end
|
374
296
|
|
375
|
-
fit_result.available_width = aw
|
376
|
-
fit_result.available_height = ah
|
377
297
|
fit_result.x = x
|
378
298
|
fit_result.y = y
|
379
299
|
fit_result.mask = rectangle
|
380
300
|
fit_result
|
381
301
|
end
|
382
302
|
|
383
|
-
# Tries to split the box of the given FitResult into two parts and returns both parts.
|
303
|
+
# Tries to split the box of the given Box::FitResult into two parts and returns both parts.
|
384
304
|
#
|
385
305
|
# See Box#split for further details.
|
386
306
|
def split(fit_result)
|
387
|
-
fit_result.box.split
|
307
|
+
fit_result.box.split
|
388
308
|
end
|
389
309
|
|
390
|
-
# Draws the box of the given FitResult onto the canvas at the fitted position.
|
310
|
+
# Draws the box of the given Box::FitResult onto the canvas at the fitted position.
|
391
311
|
#
|
392
312
|
# After a box is successfully drawn, the frame's shape is adjusted to remove the occupied
|
393
313
|
# area.
|
394
314
|
def draw(canvas, fit_result)
|
395
|
-
return if fit_result.box.height == 0 || fit_result.box.width == 0
|
396
315
|
fit_result.draw(canvas)
|
397
316
|
remove_area(fit_result.mask)
|
398
317
|
end
|
@@ -464,7 +383,7 @@ module HexaPDF
|
|
464
383
|
# Since not all text may start at the top of the frame, the offset argument can be used to
|
465
384
|
# specify a vertical offset from the top of the frame where layouting should start.
|
466
385
|
#
|
467
|
-
# To be compatible with TextLayouter, the top
|
386
|
+
# To be compatible with TextLayouter, the top-left corner of the bounding box of the frame's
|
468
387
|
# shape is the origin of the coordinate system for the width specification, with positive
|
469
388
|
# x-values to the right and positive y-values downwards.
|
470
389
|
#
|
@@ -79,9 +79,11 @@ module HexaPDF
|
|
79
79
|
false
|
80
80
|
end
|
81
81
|
|
82
|
+
private
|
83
|
+
|
82
84
|
# Fits the image into the current region of the frame, taking the initially set width and
|
83
85
|
# height into account (see the class description for details).
|
84
|
-
def
|
86
|
+
def fit_content(available_width, available_height, _frame)
|
85
87
|
image_width = @image.width.to_f
|
86
88
|
image_height = @image.height.to_f
|
87
89
|
image_ratio = image_width / image_height
|
@@ -103,12 +105,10 @@ module HexaPDF
|
|
103
105
|
@height = image_height * ratio + rh
|
104
106
|
end
|
105
107
|
|
106
|
-
|
108
|
+
fit_result.success! if float_compare(@width, available_width) <= 0 &&
|
107
109
|
float_compare(@height, available_height) <= 0
|
108
110
|
end
|
109
111
|
|
110
|
-
private
|
111
|
-
|
112
112
|
# Draws the image onto the canvas at position [x, y].
|
113
113
|
def draw_content(canvas, x, y)
|
114
114
|
canvas.image(@image, at: [x, y], width: content_width, height: content_height)
|
@@ -186,20 +186,12 @@ module HexaPDF
|
|
186
186
|
super && (!@results || @results.all? {|result| result.box_fitter.fit_results.empty? })
|
187
187
|
end
|
188
188
|
|
189
|
-
|
190
|
-
def fit(available_width, available_height, frame)
|
191
|
-
@width = if @initial_width > 0
|
192
|
-
@initial_width
|
193
|
-
else
|
194
|
-
(style.position == :flow ? frame.width : available_width)
|
195
|
-
end
|
196
|
-
height = if @initial_height > 0
|
197
|
-
@initial_height - reserved_height
|
198
|
-
else
|
199
|
-
(style.position == :flow ? frame.y - frame.bottom : available_height) - reserved_height
|
200
|
-
end
|
189
|
+
private
|
201
190
|
|
191
|
+
# Fits the list box into the current region of the frame.
|
192
|
+
def fit_content(_available_width, _available_height, frame)
|
202
193
|
width = @width - reserved_width
|
194
|
+
height = @height - reserved_height
|
203
195
|
left = (style.position == :flow ? frame.left : frame.x) + reserved_width_left
|
204
196
|
top = frame.y - reserved_height_top
|
205
197
|
|
@@ -243,7 +235,7 @@ module HexaPDF
|
|
243
235
|
Array(child).each {|ibox| box_fitter.fit(ibox) }
|
244
236
|
item_result.box_fitter = box_fitter
|
245
237
|
item_result.height = [item_result.height.to_i, box_fitter.content_heights[0]].max
|
246
|
-
@results << item_result
|
238
|
+
@results << item_result unless box_fitter.fit_results.empty?
|
247
239
|
|
248
240
|
top -= item_result.height + item_spacing
|
249
241
|
height -= item_result.height + item_spacing
|
@@ -253,15 +245,13 @@ module HexaPDF
|
|
253
245
|
|
254
246
|
@height = @results.sum(&:height) + (@results.count - 1) * item_spacing + reserved_height
|
255
247
|
|
256
|
-
@
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
248
|
+
if @results.size == @children.size && @results.all? {|r| r.box_fitter.success? }
|
249
|
+
fit_result.success!
|
250
|
+
elsif !@results.empty? && !@results[0].box_fitter.fit_results.empty?
|
251
|
+
fit_result.overflow!
|
252
|
+
end
|
261
253
|
end
|
262
254
|
|
263
|
-
private
|
264
|
-
|
265
255
|
# Removes the +content_indentation+ from the left side of the given shape (a Geom2D::PolygonSet).
|
266
256
|
def remove_indent_from_frame_shape(shape)
|
267
257
|
polygon_index = 0
|
@@ -307,7 +297,7 @@ module HexaPDF
|
|
307
297
|
end
|
308
298
|
|
309
299
|
# Splits the content of the list box. This method is called from Box#split.
|
310
|
-
def split_content
|
300
|
+
def split_content
|
311
301
|
remaining_boxes = @results[-1].box_fitter.remaining_boxes
|
312
302
|
first_is_split_box = !remaining_boxes.empty?
|
313
303
|
children = (remaining_boxes.empty? ? [] : [remaining_boxes]) + @children[@results.size..-1]
|
@@ -361,17 +351,9 @@ module HexaPDF
|
|
361
351
|
|
362
352
|
# Draws the list items onto the canvas at position [x, y].
|
363
353
|
def draw_content(canvas, x, y)
|
364
|
-
|
365
|
-
raise HexaPDF::Error, "Some items don't fit into box with limited height and " \
|
366
|
-
"style property overflow is set to :error"
|
367
|
-
end
|
354
|
+
translate = style.position != :flow && (x != @fit_x || y != @fit_y)
|
368
355
|
|
369
|
-
translate
|
370
|
-
|
371
|
-
if translate
|
372
|
-
canvas.save_graphics_state
|
373
|
-
canvas.translate(x - @draw_pos_x, y - @draw_pos_y)
|
374
|
-
end
|
356
|
+
canvas.save_graphics_state.translate(x - @fit_x, y - @fit_y) if translate
|
375
357
|
|
376
358
|
@results.each do |item_result|
|
377
359
|
box_fitter = item_result.box_fitter
|
data/lib/hexapdf/layout/style.rb
CHANGED
@@ -393,7 +393,7 @@ module HexaPDF
|
|
393
393
|
# The object resolved in this way needs to respond to #call(canvas, box) where +canvas+ is the
|
394
394
|
# HexaPDF::Content::Canvas object on which it should be drawn and +box+ is a box-like object
|
395
395
|
# (e.g. Box or TextFragment). The coordinate system is translated so that the origin is at the
|
396
|
-
# bottom
|
396
|
+
# bottom-left corner of the box during the drawing operations.
|
397
397
|
class Layers
|
398
398
|
|
399
399
|
# Creates a new Layers object popuplated with the given +layers+.
|
@@ -1254,7 +1254,11 @@ module HexaPDF
|
|
1254
1254
|
# doesn't. If a box doesn't support this value, it is positioned as if the value :default
|
1255
1255
|
# was set.
|
1256
1256
|
#
|
1257
|
-
#
|
1257
|
+
# Notes:
|
1258
|
+
#
|
1259
|
+
# * The properties #align and #valign are not used with this value.
|
1260
|
+
# * The rectangular area of the box is the rectangle containing all the flowed content.
|
1261
|
+
# That rectangle is used for drawing the border, background and so on.
|
1258
1262
|
#
|
1259
1263
|
# Examples:
|
1260
1264
|
#
|
@@ -1264,8 +1268,8 @@ module HexaPDF
|
|
1264
1268
|
# composer.lorem_ipsum(position: :flow)
|
1265
1269
|
#
|
1266
1270
|
# [x, y]::
|
1267
|
-
# Position the box with the bottom
|
1268
|
-
# the bottom
|
1271
|
+
# Position the box with the bottom-left corner at the given absolute position relative to
|
1272
|
+
# the bottom-left corner of the frame.
|
1269
1273
|
#
|
1270
1274
|
# Examples:
|
1271
1275
|
#
|
@@ -211,56 +211,55 @@ module HexaPDF
|
|
211
211
|
@height = height
|
212
212
|
end
|
213
213
|
|
214
|
+
# :nodoc:
|
215
|
+
def inspect
|
216
|
+
"<Cell (#{row},#{column}) #{row_span}x#{col_span} #{Array(children).map(&:class)}>"
|
217
|
+
end
|
218
|
+
|
219
|
+
private
|
220
|
+
|
214
221
|
# Fits the children of the table cell into the given rectangular area.
|
215
|
-
def
|
216
|
-
@width = available_width
|
222
|
+
def fit_content(available_width, available_height, frame)
|
217
223
|
width = available_width - reserved_width
|
218
|
-
height = available_height - reserved_height
|
219
|
-
return
|
224
|
+
height = @used_height = available_height - reserved_height
|
225
|
+
return if width <= 0 || height <= 0
|
220
226
|
|
221
227
|
frame = frame.child_frame(0, 0, width, height, box: self)
|
222
228
|
case children
|
223
229
|
when Box
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
230
|
+
child_result = frame.fit(children)
|
231
|
+
if child_result.success?
|
232
|
+
@preferred_width = child_result.x + child_result.box.width + reserved_width
|
233
|
+
@height = @preferred_height = child_result.box.height + reserved_height
|
234
|
+
@fit_results = [child_result]
|
235
|
+
fit_result.success!
|
236
|
+
end
|
229
237
|
when Array
|
230
238
|
box_fitter = BoxFitter.new([frame])
|
231
239
|
children.each {|box| box_fitter.fit(box) }
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
240
|
+
if box_fitter.success?
|
241
|
+
max_x_result = box_fitter.fit_results.max_by {|result| result.x + result.box.width }
|
242
|
+
@preferred_width = max_x_result.x + max_x_result.box.width + reserved_width
|
243
|
+
@height = @preferred_height = box_fitter.content_heights[0] + reserved_height
|
244
|
+
@fit_results = box_fitter.fit_results
|
245
|
+
fit_result.success!
|
246
|
+
end
|
237
247
|
else
|
238
248
|
@preferred_width = reserved_width
|
239
249
|
@height = @preferred_height = reserved_height
|
240
250
|
@fit_results = []
|
241
|
-
|
251
|
+
fit_result.success!
|
242
252
|
end
|
243
253
|
end
|
244
254
|
|
245
|
-
# :nodoc:
|
246
|
-
def inspect
|
247
|
-
"<Cell (#{row},#{column}) #{row_span}x#{col_span} #{Array(children).map(&:class)}>"
|
248
|
-
end
|
249
|
-
|
250
|
-
private
|
251
|
-
|
252
255
|
# Draws the content of the cell.
|
253
256
|
def draw_content(canvas, x, y)
|
254
257
|
return if @fit_results.empty?
|
255
258
|
|
256
259
|
# available_width is always equal to content_width but we need to adjust for the
|
257
260
|
# difference in the y direction between fitting and drawing
|
258
|
-
y -= (@
|
259
|
-
@fit_results.each
|
260
|
-
#fit_result.x += x
|
261
|
-
#fit_result.y += y
|
262
|
-
fit_result.draw(canvas, dx: x, dy: y)
|
263
|
-
end
|
261
|
+
y -= (@used_height - content_height)
|
262
|
+
@fit_results.each {|fit_result| fit_result.draw(canvas, dx: x, dy: y) }
|
264
263
|
end
|
265
264
|
|
266
265
|
end
|
@@ -393,7 +392,7 @@ module HexaPDF
|
|
393
392
|
else
|
394
393
|
column_info[cell.column].last
|
395
394
|
end
|
396
|
-
unless cell.fit(available_cell_width, available_height, frame)
|
395
|
+
unless cell.fit(available_cell_width, available_height, frame).success?
|
397
396
|
row_fit = false
|
398
397
|
break
|
399
398
|
end
|
@@ -589,24 +588,23 @@ module HexaPDF
|
|
589
588
|
super && (!@last_fitted_row_index || @last_fitted_row_index < 0)
|
590
589
|
end
|
591
590
|
|
592
|
-
|
593
|
-
def fit(available_width, available_height, frame)
|
594
|
-
return false if (@initial_width > 0 && @initial_width > available_width) ||
|
595
|
-
(@initial_height > 0 && @initial_height > available_height)
|
591
|
+
private
|
596
592
|
|
593
|
+
# Fits the table into the current region of the frame.
|
594
|
+
def fit_content(_available_width, _available_height, frame)
|
597
595
|
# Adjust reserved width/height to include space used by the edge cells for their border
|
598
596
|
# since cell borders are drawn on the bounds and not inside.
|
599
597
|
# This uses the top-left and bottom-right cells and so might not be correct in all cases.
|
600
598
|
@cell_tl_border_width = @cells[0, 0].style.border.width
|
601
599
|
cell_br_border_width = @cells[-1, -1].style.border.width
|
602
|
-
rw =
|
603
|
-
rh =
|
600
|
+
rw = (@cell_tl_border_width.left + cell_br_border_width.right) / 2.0
|
601
|
+
rh = (@cell_tl_border_width.top + cell_br_border_width.bottom) / 2.0
|
604
602
|
|
605
|
-
width =
|
606
|
-
height =
|
603
|
+
width = @width - reserved_width - rw
|
604
|
+
height = @height - reserved_height - rh
|
607
605
|
used_height = 0
|
608
606
|
columns = calculate_column_widths(width)
|
609
|
-
return
|
607
|
+
return if columns.empty?
|
610
608
|
|
611
609
|
frame = frame.child_frame(box: self)
|
612
610
|
@special_cells_fit_not_successful = false
|
@@ -616,18 +614,21 @@ module HexaPDF
|
|
616
614
|
height -= special_used_height
|
617
615
|
used_height += special_used_height
|
618
616
|
@special_cells_fit_not_successful = (last_fitted_row_index != special_cells.number_of_rows - 1)
|
619
|
-
return
|
617
|
+
return nil if @special_cells_fit_not_successful
|
620
618
|
end
|
621
619
|
|
622
620
|
main_used_height, @last_fitted_row_index = @cells.fit_rows(@start_row_index, height, columns, frame)
|
623
621
|
used_height += main_used_height
|
624
622
|
|
625
|
-
|
626
|
-
|
627
|
-
@fit_successful = (@last_fitted_row_index == @cells.number_of_rows - 1)
|
628
|
-
end
|
623
|
+
update_content_width { columns[-1].sum + rw }
|
624
|
+
update_content_height { used_height + rh }
|
629
625
|
|
630
|
-
|
626
|
+
if @last_fitted_row_index == @cells.number_of_rows - 1
|
627
|
+
fit_result.success!
|
628
|
+
elsif @last_fitted_row_index >= 0
|
629
|
+
fit_result.overflow!
|
630
|
+
end
|
631
|
+
end
|
631
632
|
|
632
633
|
# Calculates and returns the x-coordinates and widths of all columns based on the given total
|
633
634
|
# available width.
|
@@ -649,20 +650,16 @@ module HexaPDF
|
|
649
650
|
end
|
650
651
|
|
651
652
|
# Splits the content of the table box. This method is called from Box#split.
|
652
|
-
def split_content
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
footer_cells = @footer ? Cells.new(@footer.call(self), cell_style: @cell_style) : nil
|
663
|
-
box.instance_variable_set(:@footer_cells, footer_cells)
|
664
|
-
[self, box]
|
665
|
-
end
|
653
|
+
def split_content
|
654
|
+
box = create_split_box
|
655
|
+
box.instance_variable_set(:@start_row_index, @last_fitted_row_index + 1)
|
656
|
+
box.instance_variable_set(:@last_fitted_row_index, -1)
|
657
|
+
box.instance_variable_set(:@special_cells_fit_not_successful, nil)
|
658
|
+
header_cells = @header ? Cells.new(@header.call(self), cell_style: @cell_style) : nil
|
659
|
+
box.instance_variable_set(:@header_cells, header_cells)
|
660
|
+
footer_cells = @footer ? Cells.new(@footer.call(self), cell_style: @cell_style) : nil
|
661
|
+
box.instance_variable_set(:@footer_cells, footer_cells)
|
662
|
+
[self, box]
|
666
663
|
end
|
667
664
|
|
668
665
|
# Draws the child boxes onto the canvas at position [x, y].
|