hexapdf 0.42.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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].