hexapdf 0.42.0 → 0.44.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/Rakefile +1 -1
  4. data/examples/030-pdfa.rb +1 -0
  5. data/lib/hexapdf/composer.rb +1 -0
  6. data/lib/hexapdf/dictionary.rb +3 -3
  7. data/lib/hexapdf/document/files.rb +7 -2
  8. data/lib/hexapdf/document/metadata.rb +12 -1
  9. data/lib/hexapdf/document.rb +14 -1
  10. data/lib/hexapdf/encryption.rb +17 -0
  11. data/lib/hexapdf/layout/box.rb +161 -61
  12. data/lib/hexapdf/layout/box_fitter.rb +4 -3
  13. data/lib/hexapdf/layout/column_box.rb +23 -25
  14. data/lib/hexapdf/layout/container_box.rb +3 -3
  15. data/lib/hexapdf/layout/frame.rb +13 -95
  16. data/lib/hexapdf/layout/image_box.rb +4 -4
  17. data/lib/hexapdf/layout/line.rb +4 -0
  18. data/lib/hexapdf/layout/list_box.rb +12 -20
  19. data/lib/hexapdf/layout/style.rb +5 -1
  20. data/lib/hexapdf/layout/table_box.rb +48 -55
  21. data/lib/hexapdf/layout/text_box.rb +38 -39
  22. data/lib/hexapdf/parser.rb +23 -17
  23. data/lib/hexapdf/type/acro_form/form.rb +78 -27
  24. data/lib/hexapdf/type/file_specification.rb +9 -5
  25. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  26. data/lib/hexapdf/version.rb +1 -1
  27. data/test/hexapdf/document/test_files.rb +5 -0
  28. data/test/hexapdf/document/test_metadata.rb +21 -0
  29. data/test/hexapdf/layout/test_box.rb +82 -37
  30. data/test/hexapdf/layout/test_box_fitter.rb +10 -3
  31. data/test/hexapdf/layout/test_column_box.rb +7 -13
  32. data/test/hexapdf/layout/test_container_box.rb +1 -1
  33. data/test/hexapdf/layout/test_frame.rb +0 -48
  34. data/test/hexapdf/layout/test_image_box.rb +14 -6
  35. data/test/hexapdf/layout/test_list_box.rb +25 -26
  36. data/test/hexapdf/layout/test_table_box.rb +39 -53
  37. data/test/hexapdf/layout/test_text_box.rb +65 -67
  38. data/test/hexapdf/test_composer.rb +6 -0
  39. data/test/hexapdf/test_dictionary.rb +6 -4
  40. data/test/hexapdf/test_parser.rb +20 -0
  41. data/test/hexapdf/type/acro_form/test_form.rb +63 -2
  42. data/test/hexapdf/type/test_file_specification.rb +2 -1
  43. metadata +2 -2
@@ -138,31 +138,29 @@ module HexaPDF
138
138
  super && (!@box_fitter || @box_fitter.fit_results.empty?)
139
139
  end
140
140
 
141
+ private
142
+
141
143
  # Fits the column box into the current region of the frame.
142
144
  #
143
- # If the style property 'position' is set to :flow, the columns might not be rectangles but
144
- # arbitrary (sets of) polygons since the +frame+s shape is taken into account.
145
- def fit(available_width, available_height, frame)
146
- return false if @initial_height > available_height || @initial_width > available_width
147
-
145
+ def fit_content(available_width, available_height, frame)
148
146
  initial_fit_successful = (@equal_height && @columns.size > 1 ? nil : false)
149
147
  tries = 0
150
- @width = if style.position == :flow
151
- (@initial_width > 0 ? @initial_width : frame.width) - reserved_width
152
- else
153
- (@initial_width > 0 ? @initial_width : available_width) - reserved_width
154
- end
148
+ width = if style.position == :flow
149
+ (@initial_width > 0 ? @initial_width : frame.width) - reserved_width
150
+ else
151
+ (@initial_width > 0 ? @initial_width : available_width) - reserved_width
152
+ end
155
153
  height = if style.position == :flow
156
- (@initial_height > 0 ? @initial_height : frame.height) - reserved_height
154
+ (@initial_height > 0 ? @initial_height : frame.y - frame.bottom) - reserved_height
157
155
  else
158
156
  (@initial_height > 0 ? @initial_height : available_height) - reserved_height
159
157
  end
160
158
 
161
- columns = calculate_columns(@width)
162
- return false if columns.empty?
159
+ columns = calculate_columns(width)
160
+ return if columns.empty?
163
161
 
164
162
  left = (style.position == :flow ? frame.left : frame.x) + reserved_width_left
165
- top = (style.position == :flow ? frame.bottom + frame.height : frame.y) - reserved_height_top
163
+ top = frame.y - reserved_height_top
166
164
  successful_height = height
167
165
  unsuccessful_height = 0
168
166
 
@@ -186,7 +184,7 @@ module HexaPDF
186
184
 
187
185
  children.each {|box| @box_fitter.fit(box) }
188
186
 
189
- fit_successful = @box_fitter.fit_successful?
187
+ fit_successful = @box_fitter.success?
190
188
  initial_fit_successful = fit_successful if initial_fit_successful.nil?
191
189
 
192
190
  if fit_successful
@@ -206,16 +204,16 @@ module HexaPDF
206
204
  tries += 1
207
205
  end
208
206
 
209
- @width = columns[-1].sum + reserved_width
210
- @height = (@initial_height > 0 ? @initial_height : @box_fitter.content_heights.max + reserved_height)
211
- @draw_pos_x = frame.x + reserved_width_left
212
- @draw_pos_y = frame.y - @height + reserved_height_bottom
207
+ update_content_width { columns[-1].sum }
208
+ update_content_height { @box_fitter.content_heights.max }
213
209
 
214
- @box_fitter.fit_successful?
210
+ if @box_fitter.success?
211
+ fit_result.success!
212
+ elsif !@box_fitter.fit_results.empty?
213
+ fit_result.overflow!
214
+ end
215
215
  end
216
216
 
217
- private
218
-
219
217
  # Calculates the x-coordinates and widths of all columns based on the given total available
220
218
  # width.
221
219
  #
@@ -241,7 +239,7 @@ module HexaPDF
241
239
  end
242
240
 
243
241
  # Splits the content of the column box. This method is called from Box#split.
244
- def split_content(_available_width, _available_height, _frame)
242
+ def split_content
245
243
  box = create_split_box
246
244
  box.instance_variable_set(:@children, @box_fitter.remaining_boxes)
247
245
  [self, box]
@@ -249,8 +247,8 @@ module HexaPDF
249
247
 
250
248
  # Draws the child boxes onto the canvas at position [x, y].
251
249
  def draw_content(canvas, x, y)
252
- if style.position != :flow && (x != @draw_pos_x || y != @draw_pos_y)
253
- canvas.translate(x - @draw_pos_x, y - @draw_pos_y) do
250
+ if style.position != :flow && (x != @fit_x || y != @fit_y)
251
+ canvas.translate(x - @fit_x, y - @fit_y) do
254
252
  @box_fitter.fit_results.each {|result| result.draw(canvas) }
255
253
  end
256
254
  else
@@ -137,17 +137,17 @@ module HexaPDF
137
137
  @box_fitter = BoxFitter.new([my_frame])
138
138
  children.each {|box| @box_fitter.fit(box) }
139
139
 
140
- if @box_fitter.fit_successful?
140
+ if @box_fitter.success?
141
141
  update_content_width do
142
142
  result = @box_fitter.fit_results.max_by {|r| r.mask.x + r.mask.width }
143
143
  children.empty? ? 0 : result.mask.x + result.mask.width - my_frame.left
144
144
  end
145
145
  update_content_height { @box_fitter.content_heights.max }
146
- true
146
+ fit_result.success!
147
147
  end
148
148
  end
149
149
 
150
- # Draws the image onto the canvas at position [x, y].
150
+ # Draws the children onto the canvas at position [x, y].
151
151
  def draw_content(canvas, x, y)
152
152
  dx = x - @fit_x
153
153
  dy = y - @fit_y
@@ -45,7 +45,7 @@ module HexaPDF
45
45
  #
46
46
  # == Usage
47
47
  #
48
- # After a Frame object is initialized, it is ready for drawing boxes on it.
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
  #
@@ -54,7 +54,7 @@ module HexaPDF
54
54
  #
55
55
  # The method #fit is also called for absolutely positioned boxes but since these boxes are not
56
56
  # subject to the normal constraints, the provided available width and height are the width and
57
- # height inside the frame to the right and top of the bottom-left corner of the box.
57
+ # height inside the frame to the right and top of the bottom left corner of the box.
58
58
  #
59
59
  # * If the box didn't fit, call #find_next_region to determine the next region for placing the
60
60
  # box. If a new region was found, start over with #fit. Otherwise the frame has no more space
@@ -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,84 +84,10 @@ 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
- # The x-coordinate of the bottom-left corner.
87
+ # The x-coordinate of the bottom left corner.
166
88
  attr_reader :left
167
89
 
168
- # The y-coordinate of the bottom-left corner.
90
+ # The y-coordinate of the bottom left corner.
169
91
  attr_reader :bottom
170
92
 
171
93
  # The width of the frame.
@@ -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 a FitResult
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
- fit_result = FitResult.new(self, box)
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,7 @@ module HexaPDF
294
215
  ah -= margin_top = margin.top unless float_equal(@y, @bottom + @height)
295
216
  end
296
217
 
297
- fit_result.success! if box.fit(aw, ah, self)
218
+ fit_result = box.fit(aw, ah, self)
298
219
 
299
220
  width = box.width
300
221
  height = box.height
@@ -372,27 +293,24 @@ module HexaPDF
372
293
  create_rectangle(@x, @y - available_height, @x + available_width, @y)
373
294
  end
374
295
 
375
- fit_result.available_width = aw
376
- fit_result.available_height = ah
377
296
  fit_result.x = x
378
297
  fit_result.y = y
379
298
  fit_result.mask = rectangle
380
299
  fit_result
381
300
  end
382
301
 
383
- # Tries to split the box of the given FitResult into two parts and returns both parts.
302
+ # Tries to split the box of the given Box::FitResult into two parts and returns both parts.
384
303
  #
385
304
  # See Box#split for further details.
386
305
  def split(fit_result)
387
- fit_result.box.split(fit_result.available_width, fit_result.available_height, self)
306
+ fit_result.box.split
388
307
  end
389
308
 
390
- # Draws the box of the given FitResult onto the canvas at the fitted position.
309
+ # Draws the box of the given Box::FitResult onto the canvas at the fitted position.
391
310
  #
392
311
  # After a box is successfully drawn, the frame's shape is adjusted to remove the occupied
393
312
  # area.
394
313
  def draw(canvas, fit_result)
395
- return if fit_result.box.height == 0 || fit_result.box.width == 0
396
314
  fit_result.draw(canvas)
397
315
  remove_area(fit_result.mask)
398
316
  end
@@ -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 fit(available_width, available_height, _frame)
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
- @fit_successful = float_compare(@width, available_width) <= 0 &&
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)
@@ -173,6 +173,10 @@ module HexaPDF
173
173
  attr_accessor :items
174
174
 
175
175
  # An optional horizontal offset that should be taken into account when positioning the line.
176
+ #
177
+ # This offset always describes the offset from the left side (and not, for example, the offset
178
+ # from the right side of another line even if those two lines are actually on the same
179
+ # horizontal level).
176
180
  attr_accessor :x_offset
177
181
 
178
182
  # An optional vertical offset that should be taken into account when positioning the line.
@@ -186,8 +186,10 @@ module HexaPDF
186
186
  super && (!@results || @results.all? {|result| result.box_fitter.fit_results.empty? })
187
187
  end
188
188
 
189
+ private
190
+
189
191
  # Fits the list box into the current region of the frame.
190
- def fit(available_width, available_height, frame)
192
+ def fit_content(available_width, available_height, frame)
191
193
  @width = if @initial_width > 0
192
194
  @initial_width
193
195
  else
@@ -248,20 +250,18 @@ module HexaPDF
248
250
  top -= item_result.height + item_spacing
249
251
  height -= item_result.height + item_spacing
250
252
 
251
- break if !box_fitter.fit_successful? || height <= 0
253
+ break if !box_fitter.success? || height <= 0
252
254
  end
253
255
 
254
256
  @height = @results.sum(&:height) + (@results.count - 1) * item_spacing + reserved_height
255
257
 
256
- @draw_pos_x = frame.x + reserved_width_left
257
- @draw_pos_y = frame.y - @height + reserved_height_bottom
258
- @all_items_fitted = @results.all? {|r| r.box_fitter.fit_successful? } &&
259
- @results.size == @children.size
260
- @fit_successful = @all_items_fitted || (@initial_height > 0 && style.overflow == :truncate)
258
+ if @results.size == @children.size && @results.all? {|r| r.box_fitter.success? }
259
+ fit_result.success!
260
+ elsif !@results.empty? && !@results[0].box_fitter.fit_results.empty?
261
+ fit_result.overflow!
262
+ end
261
263
  end
262
264
 
263
- private
264
-
265
265
  # Removes the +content_indentation+ from the left side of the given shape (a Geom2D::PolygonSet).
266
266
  def remove_indent_from_frame_shape(shape)
267
267
  polygon_index = 0
@@ -307,7 +307,7 @@ module HexaPDF
307
307
  end
308
308
 
309
309
  # Splits the content of the list box. This method is called from Box#split.
310
- def split_content(_available_width, _available_height, _frame)
310
+ def split_content
311
311
  remaining_boxes = @results[-1].box_fitter.remaining_boxes
312
312
  first_is_split_box = !remaining_boxes.empty?
313
313
  children = (remaining_boxes.empty? ? [] : [remaining_boxes]) + @children[@results.size..-1]
@@ -361,17 +361,9 @@ module HexaPDF
361
361
 
362
362
  # Draws the list items onto the canvas at position [x, y].
363
363
  def draw_content(canvas, x, y)
364
- if !@all_items_fitted && (@initial_height > 0 && style.overflow == :error)
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
364
+ translate = style.position != :flow && (x != @fit_x || y != @fit_y)
368
365
 
369
- translate = style.position != :flow && (x != @draw_pos_x || y != @draw_pos_y)
370
-
371
- if translate
372
- canvas.save_graphics_state
373
- canvas.translate(x - @draw_pos_x, y - @draw_pos_y)
374
- end
366
+ canvas.save_graphics_state.translate(x - @fit_x, y - @fit_y) if translate
375
367
 
376
368
  @results.each do |item_result|
377
369
  box_fitter = item_result.box_fitter
@@ -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
- # Note that the properties #align and #valign are not used with this value!
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
  #
@@ -211,21 +211,27 @@ 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 fit(available_width, available_height, frame)
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 false if width <= 0 || height <= 0
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
- fit_result = frame.fit(children)
225
- @preferred_width = fit_result.x + fit_result.box.width + reserved_width
226
- @height = @preferred_height = fit_result.box.height + reserved_height
227
- @fit_results = [fit_result]
228
- @fit_successful = fit_result.success?
230
+ child_result = frame.fit(children)
231
+ @preferred_width = child_result.x + child_result.box.width + reserved_width
232
+ @height = @preferred_height = child_result.box.height + reserved_height
233
+ @fit_results = [child_result]
234
+ fit_result.success! if child_result.success?
229
235
  when Array
230
236
  box_fitter = BoxFitter.new([frame])
231
237
  children.each {|box| box_fitter.fit(box) }
@@ -233,34 +239,23 @@ module HexaPDF
233
239
  @preferred_width = max_x_result.x + max_x_result.box.width + reserved_width
234
240
  @height = @preferred_height = box_fitter.content_heights[0] + reserved_height
235
241
  @fit_results = box_fitter.fit_results
236
- @fit_successful = box_fitter.fit_successful?
242
+ fit_result.success! if box_fitter.success?
237
243
  else
238
244
  @preferred_width = reserved_width
239
245
  @height = @preferred_height = reserved_height
240
246
  @fit_results = []
241
- @fit_successful = true
247
+ fit_result.success!
242
248
  end
243
249
  end
244
250
 
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
251
  # Draws the content of the cell.
253
252
  def draw_content(canvas, x, y)
254
253
  return if @fit_results.empty?
255
254
 
256
255
  # available_width is always equal to content_width but we need to adjust for the
257
256
  # difference in the y direction between fitting and drawing
258
- y -= (@fit_results[0].available_height - content_height)
259
- @fit_results.each do |fit_result|
260
- #fit_result.x += x
261
- #fit_result.y += y
262
- fit_result.draw(canvas, dx: x, dy: y)
263
- end
257
+ y -= (@used_height - content_height)
258
+ @fit_results.each {|fit_result| fit_result.draw(canvas, dx: x, dy: y) }
264
259
  end
265
260
 
266
261
  end
@@ -393,7 +388,7 @@ module HexaPDF
393
388
  else
394
389
  column_info[cell.column].last
395
390
  end
396
- unless cell.fit(available_cell_width, available_height, frame)
391
+ unless cell.fit(available_cell_width, available_height, frame).success?
397
392
  row_fit = false
398
393
  break
399
394
  end
@@ -589,24 +584,23 @@ module HexaPDF
589
584
  super && (!@last_fitted_row_index || @last_fitted_row_index < 0)
590
585
  end
591
586
 
592
- # Fits the table into the current region of the frame.
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)
587
+ private
596
588
 
589
+ # Fits the table into the current region of the frame.
590
+ def fit_content(_available_width, _available_height, frame)
597
591
  # Adjust reserved width/height to include space used by the edge cells for their border
598
592
  # since cell borders are drawn on the bounds and not inside.
599
- # This uses the top-left and bottom-right cells and so might not be correct in all cases.
593
+ # This uses the top left and bottom right cells and so might not be correct in all cases.
600
594
  @cell_tl_border_width = @cells[0, 0].style.border.width
601
595
  cell_br_border_width = @cells[-1, -1].style.border.width
602
- rw = reserved_width + (@cell_tl_border_width.left + cell_br_border_width.right) / 2.0
603
- rh = reserved_height + (@cell_tl_border_width.top + cell_br_border_width.bottom) / 2.0
596
+ rw = (@cell_tl_border_width.left + cell_br_border_width.right) / 2.0
597
+ rh = (@cell_tl_border_width.top + cell_br_border_width.bottom) / 2.0
604
598
 
605
- width = (@initial_width > 0 ? @initial_width : available_width) - rw
606
- height = (@initial_height > 0 ? @initial_height : available_height) - rh
599
+ width = @width - reserved_width - rw
600
+ height = @height - reserved_height - rh
607
601
  used_height = 0
608
602
  columns = calculate_column_widths(width)
609
- return false if columns.empty?
603
+ return if columns.empty?
610
604
 
611
605
  frame = frame.child_frame(box: self)
612
606
  @special_cells_fit_not_successful = false
@@ -616,18 +610,21 @@ module HexaPDF
616
610
  height -= special_used_height
617
611
  used_height += special_used_height
618
612
  @special_cells_fit_not_successful = (last_fitted_row_index != special_cells.number_of_rows - 1)
619
- return false if @special_cells_fit_not_successful
613
+ return nil if @special_cells_fit_not_successful
620
614
  end
621
615
 
622
616
  main_used_height, @last_fitted_row_index = @cells.fit_rows(@start_row_index, height, columns, frame)
623
617
  used_height += main_used_height
624
618
 
625
- @width = (@initial_width > 0 ? @initial_width : columns[-1].sum + rw)
626
- @height = (@initial_height > 0 ? @initial_height : used_height + rh)
627
- @fit_successful = (@last_fitted_row_index == @cells.number_of_rows - 1)
628
- end
619
+ update_content_width { columns[-1].sum + rw }
620
+ update_content_height { used_height + rh }
629
621
 
630
- private
622
+ if @last_fitted_row_index == @cells.number_of_rows - 1
623
+ fit_result.success!
624
+ elsif @last_fitted_row_index >= 0
625
+ fit_result.overflow!
626
+ end
627
+ end
631
628
 
632
629
  # Calculates and returns the x-coordinates and widths of all columns based on the given total
633
630
  # available width.
@@ -649,20 +646,16 @@ module HexaPDF
649
646
  end
650
647
 
651
648
  # Splits the content of the table box. This method is called from Box#split.
652
- def split_content(_available_width, _available_height, _frame)
653
- if @special_cells_fit_not_successful || @last_fitted_row_index < 0
654
- [nil, self]
655
- else
656
- box = create_split_box
657
- box.instance_variable_set(:@start_row_index, @last_fitted_row_index + 1)
658
- box.instance_variable_set(:@last_fitted_row_index, -1)
659
- box.instance_variable_set(:@special_cells_fit_not_successful, nil)
660
- header_cells = @header ? Cells.new(@header.call(self), cell_style: @cell_style) : nil
661
- box.instance_variable_set(:@header_cells, header_cells)
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
649
+ def split_content
650
+ box = create_split_box
651
+ box.instance_variable_set(:@start_row_index, @last_fitted_row_index + 1)
652
+ box.instance_variable_set(:@last_fitted_row_index, -1)
653
+ box.instance_variable_set(:@special_cells_fit_not_successful, nil)
654
+ header_cells = @header ? Cells.new(@header.call(self), cell_style: @cell_style) : nil
655
+ box.instance_variable_set(:@header_cells, header_cells)
656
+ footer_cells = @footer ? Cells.new(@footer.call(self), cell_style: @cell_style) : nil
657
+ box.instance_variable_set(:@footer_cells, footer_cells)
658
+ [self, box]
666
659
  end
667
660
 
668
661
  # Draws the child boxes onto the canvas at position [x, y].