hexapdf 0.43.0 → 0.45.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/examples/027-composer_optional_content.rb +6 -4
  4. data/examples/030-pdfa.rb +13 -11
  5. data/lib/hexapdf/composer.rb +23 -0
  6. data/lib/hexapdf/content/canvas.rb +3 -3
  7. data/lib/hexapdf/content/canvas_composer.rb +1 -0
  8. data/lib/hexapdf/document/files.rb +7 -2
  9. data/lib/hexapdf/document/layout.rb +15 -3
  10. data/lib/hexapdf/document/metadata.rb +12 -1
  11. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  12. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  13. data/lib/hexapdf/layout/box.rb +180 -66
  14. data/lib/hexapdf/layout/box_fitter.rb +1 -0
  15. data/lib/hexapdf/layout/column_box.rb +18 -28
  16. data/lib/hexapdf/layout/container_box.rb +6 -6
  17. data/lib/hexapdf/layout/frame.rb +13 -94
  18. data/lib/hexapdf/layout/image_box.rb +4 -4
  19. data/lib/hexapdf/layout/list_box.rb +13 -31
  20. data/lib/hexapdf/layout/style.rb +8 -4
  21. data/lib/hexapdf/layout/table_box.rb +55 -58
  22. data/lib/hexapdf/layout/text_box.rb +84 -71
  23. data/lib/hexapdf/layout/text_fragment.rb +1 -1
  24. data/lib/hexapdf/layout/text_layouter.rb +7 -8
  25. data/lib/hexapdf/parser.rb +5 -2
  26. data/lib/hexapdf/rectangle.rb +4 -4
  27. data/lib/hexapdf/type/file_specification.rb +9 -5
  28. data/lib/hexapdf/type/form.rb +2 -2
  29. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  30. data/lib/hexapdf/version.rb +1 -1
  31. data/test/hexapdf/content/test_canvas_composer.rb +13 -8
  32. data/test/hexapdf/document/test_files.rb +5 -0
  33. data/test/hexapdf/document/test_layout.rb +16 -0
  34. data/test/hexapdf/document/test_metadata.rb +21 -0
  35. data/test/hexapdf/layout/test_box.rb +93 -37
  36. data/test/hexapdf/layout/test_box_fitter.rb +7 -0
  37. data/test/hexapdf/layout/test_column_box.rb +7 -13
  38. data/test/hexapdf/layout/test_container_box.rb +1 -1
  39. data/test/hexapdf/layout/test_frame.rb +7 -46
  40. data/test/hexapdf/layout/test_image_box.rb +14 -6
  41. data/test/hexapdf/layout/test_list_box.rb +26 -27
  42. data/test/hexapdf/layout/test_table_box.rb +47 -54
  43. data/test/hexapdf/layout/test_text_box.rb +83 -83
  44. data/test/hexapdf/test_composer.rb +20 -5
  45. data/test/hexapdf/test_parser.rb +8 -0
  46. data/test/hexapdf/test_serializer.rb +1 -0
  47. data/test/hexapdf/type/test_file_specification.rb +2 -1
  48. metadata +2 -2
@@ -4,11 +4,45 @@ require 'test_helper'
4
4
  require 'hexapdf/document'
5
5
  require 'hexapdf/layout/box'
6
6
 
7
+ describe HexaPDF::Layout::Box::FitResult do
8
+ it "shows the box's mask area on #draw when using debug output" do
9
+ doc = HexaPDF::Document.new(config: {'debug' => true})
10
+ canvas = doc.pages.add.canvas
11
+ box = HexaPDF::Layout::Box.create(width: 20, height: 20) {}
12
+ frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150)
13
+ result = HexaPDF::Layout::Box::FitResult.new(box, frame: frame)
14
+ result.mask = Geom2D::Rectangle(0, 0, 20, 20)
15
+ result.x = result.y = 0
16
+ result.draw(canvas, dx: 10, dy: 15)
17
+ assert_equal(<<~CONTENTS, canvas.contents)
18
+ /OC /P1 BDC
19
+ q
20
+ 1 0 0 1 10 15 cm
21
+ 0.0 0.501961 0.0 rg
22
+ 0.0 0.392157 0.0 RG
23
+ /GS1 gs
24
+ 0 0 20 20 re
25
+ B
26
+ Q
27
+ EMC
28
+ q
29
+ 1 0 0 1 10 15 cm
30
+ Q
31
+ CONTENTS
32
+ ocg = doc.optional_content.ocgs.first
33
+ assert_equal([['Debug', ['Page 1', ocg]]], doc.optional_content.default_configuration[:Order])
34
+ assert_match(/10,15-20x20/, ocg.name)
35
+ end
36
+ end
37
+
7
38
  describe HexaPDF::Layout::Box do
8
39
  before do
9
40
  @frame = Object.new
10
41
  def @frame.x; 0; end
11
42
  def @frame.y; 100; end
43
+ def @frame.bottom; 40; end
44
+ def @frame.width; 150; end
45
+ def @frame.height; 150; end
12
46
  end
13
47
 
14
48
  def create_box(**args, &block)
@@ -78,7 +112,7 @@ describe HexaPDF::Layout::Box do
78
112
  describe "fit" do
79
113
  it "fits a fixed sized box" do
80
114
  box = create_box(width: 50, height: 50, style: {padding: 5})
81
- assert(box.fit(100, 100, @frame))
115
+ assert(box.fit(100, 100, @frame).success?)
82
116
  assert_equal(50, box.width)
83
117
  assert_equal(50, box.height)
84
118
  assert_equal(5, box.instance_variable_get(:@fit_x))
@@ -87,28 +121,53 @@ describe HexaPDF::Layout::Box do
87
121
 
88
122
  it "uses the maximum available width" do
89
123
  box = create_box(height: 50)
90
- assert(box.fit(100, 100, @frame))
124
+ assert(box.fit(100, 100, @frame).success?)
91
125
  assert_equal(100, box.width)
92
126
  assert_equal(50, box.height)
93
127
  end
94
128
 
95
129
  it "uses the maximum available height" do
96
130
  box = create_box(width: 50)
97
- assert(box.fit(100, 100, @frame))
131
+ assert(box.fit(100, 100, @frame).success?)
98
132
  assert_equal(50, box.width)
99
133
  assert_equal(100, box.height)
100
134
  end
101
135
 
136
+ it "use the frame's width and its remaining height for position=:flow boxes" do
137
+ box = create_box(style: {position: :flow})
138
+ box.define_singleton_method(:supports_position_flow?) { true }
139
+ assert(box.fit(100, 100, @frame).success?)
140
+ assert_equal(150, box.width)
141
+ assert_equal(60, box.height)
142
+ end
143
+
102
144
  it "uses float comparison" do
103
145
  box = create_box(width: 50.0000002, height: 49.9999996)
104
- assert(box.fit(50, 50, @frame))
146
+ assert(box.fit(50, 50, @frame).success?)
105
147
  assert_equal(50.0000002, box.width)
106
148
  assert_equal(49.9999996, box.height)
107
149
  end
108
150
 
109
- it "returns false if the box doesn't fit" do
151
+ it "fails if the box doesn't fit, position != :flow and its width is greater than the available width" do
110
152
  box = create_box(width: 101)
111
- refute(box.fit(100, 100, @frame))
153
+ assert(box.fit(100, 100, @frame).failure?)
154
+ end
155
+
156
+ it "fails if the box doesn't fit, position != :flow and its width is greater than the available width" do
157
+ box = create_box(height: 101)
158
+ assert(box.fit(100, 100, @frame).failure?)
159
+ end
160
+
161
+ it "fails if its content width is zero" do
162
+ box = create_box(height: 100)
163
+ box.style.padding = [0, 100]
164
+ assert(box.fit(150, 150, @frame).failure?)
165
+ end
166
+
167
+ it "fails if its content height is zero" do
168
+ box = create_box(width: 100)
169
+ box.style.padding = [100, 0]
170
+ assert(box.fit(150, 150, @frame).failure?)
112
171
  end
113
172
 
114
173
  it "can use the #content_width/#content_height helper methods" do
@@ -116,9 +175,9 @@ describe HexaPDF::Layout::Box do
116
175
  box.define_singleton_method(:fit_content) do |_aw, _ah, _frame|
117
176
  update_content_width { 10 }
118
177
  update_content_height { 20 }
119
- true
178
+ fit_result.success!
120
179
  end
121
- assert(box.fit(100, 100, @frame))
180
+ assert(box.fit(100, 100, @frame).success?)
122
181
  assert_equal(10, box.width)
123
182
  assert_equal(20, box.height)
124
183
 
@@ -126,49 +185,40 @@ describe HexaPDF::Layout::Box do
126
185
  box.define_singleton_method(:fit_content) do |_aw, _ah, _frame|
127
186
  update_content_width { 10 }
128
187
  update_content_height { 20 }
129
- true
188
+ fit_result.success!
130
189
  end
131
- assert(box.fit(100, 100, @frame))
190
+ assert(box.fit(100, 100, @frame).success?)
132
191
  assert_equal(30, box.width)
133
192
  assert_equal(50, box.height)
134
193
  end
135
194
  end
136
195
 
137
196
  describe "split" do
138
- before do
139
- @box = create_box(width: 100, height: 100)
140
- @box.fit(100, 100, @frame)
141
- end
142
-
143
197
  it "doesn't need to be split if it completely fits" do
144
- assert_equal([@box, nil], @box.split(100, 100, @frame))
198
+ box = create_box(width: 100, height: 100)
199
+ box.fit(100, 100, @frame)
200
+ assert_equal([box, nil], box.split)
145
201
  end
146
202
 
147
- it "can't be split if it doesn't (completely) fit and its width is greater than the available width" do
148
- @box.fit(90, 100, nil)
149
- assert_equal([nil, @box], @box.split(50, 150, @frame))
203
+ it "is not split if no part of it fits" do
204
+ box = create_box(width: 150)
205
+ box.fit(100, 100, @frame)
206
+ assert_equal([nil, box], box.split)
150
207
  end
151
208
 
152
- it "can't be split if it doesn't (completely) fit and its height is greater than the available height" do
153
- @box.fit(90, 100, nil)
154
- assert_equal([nil, @box], @box.split(150, 50, @frame))
155
- end
156
-
157
- it "can't be split if it doesn't (completely) fit and its content width is zero" do
158
- box = create_box(width: 0, height: 100)
159
- assert_equal([nil, box], box.split(150, 150, @frame))
160
- end
161
-
162
- it "can't be split if it doesn't (completely) fit and its content height is zero" do
163
- box = create_box(width: 100, height: 0)
164
- assert_equal([nil, box], box.split(150, 150, @frame))
209
+ it "is not split if a height for the box is specified and it doesn't completely fit" do
210
+ box = create_box(height: 50)
211
+ box.define_singleton_method(:fit_content) {|*| fit_result.overflow! }
212
+ box.fit(100, 100, @frame)
213
+ assert_equal([box, nil], box.split)
165
214
  end
166
215
 
167
- it "can't be split if it doesn't (completely) fit as the default implementation " \
216
+ it "can't be split if it doesn't completely fit as the default implementation " \
168
217
  "knows nothing about the content" do
169
- @box.style.position = :flow # make sure we would generally be splitable
170
- @box.fit(90, 100, nil)
171
- assert_equal([nil, @box], @box.split(150, 150, @frame))
218
+ box = create_box
219
+ box.define_singleton_method(:fit_content) {|*| fit_result.overflow! }
220
+ box.fit(100, 100, @frame)
221
+ assert_equal([nil, box], box.split)
172
222
  end
173
223
  end
174
224
 
@@ -177,7 +227,7 @@ describe HexaPDF::Layout::Box do
177
227
  box.fit(100, 100, @frame)
178
228
  cloned_box = box.send(:create_split_box)
179
229
  assert(cloned_box.split_box?)
180
- refute(cloned_box.instance_variable_get(:@fit_successful))
230
+ refute_same(box.fit_result, cloned_box.fit_result)
181
231
  assert_equal(0, cloned_box.width)
182
232
  assert_equal(0, cloned_box.height)
183
233
  end
@@ -240,6 +290,12 @@ describe HexaPDF::Layout::Box do
240
290
  refute(box.style.overlays?)
241
291
  end
242
292
 
293
+ it "raises an error if the style property :overflow is set to error and the box doesn't completely fit" do
294
+ box = create_box(height: 50, style: {overflow: :error})
295
+ box.fit_result.overflow!
296
+ assert_raises(HexaPDF::Error) { box.draw(@canvas, 0, 0) }
297
+ end
298
+
243
299
  it "wraps the box in optional content markers if the optional_content property is set" do
244
300
  box = create_box(properties: {'optional_content' => 'Text'})
245
301
  box.draw(@canvas, 0, 0)
@@ -50,6 +50,13 @@ describe HexaPDF::Layout::BoxFitter do
50
50
  check_result(10, 20, 0, 0, 100, 130, 100, 110, content_heights: [90, 40])
51
51
  end
52
52
 
53
+ it "correctly handles truncated boxes" do
54
+ box = HexaPDF::Layout::Box.new(height: 50) {}
55
+ box.define_singleton_method(:fit_content) {|*| fit_result.overflow! }
56
+ @box_fitter.fit(box)
57
+ check_result(10, 40, content_heights: [50, 0])
58
+ end
59
+
53
60
  it "fails when some boxes can't be fitted" do
54
61
  fit_box(9)
55
62
  fit_box(70)
@@ -22,7 +22,7 @@ describe HexaPDF::Layout::ColumnBox do
22
22
  end
23
23
 
24
24
  def check_box(box, width, height, fit_pos = nil)
25
- assert(box.fit(@frame.available_width, @frame.available_height, @frame), "box didn't fit")
25
+ assert(box.fit(@frame.available_width, @frame.available_height, @frame).success?, "box didn't fit")
26
26
  assert_equal(width, box.width, "box width")
27
27
  assert_equal(height, box.height, "box height")
28
28
  if fit_pos
@@ -73,9 +73,6 @@ describe HexaPDF::Layout::ColumnBox do
73
73
  box = create_box(columns: 1, children: @fixed_size_boxes[0..0], width: 50,
74
74
  style: {position: position})
75
75
  check_box(box, 50, 10)
76
-
77
- box = create_box(children: @fixed_size_boxes[0..0], width: 110)
78
- refute(box.fit(@frame.available_width, @frame.available_height, @frame))
79
76
  end
80
77
 
81
78
  it "respects the set initial height, position #{position}" do
@@ -86,9 +83,6 @@ describe HexaPDF::Layout::ColumnBox do
86
83
  box = create_box(children: @text_boxes[0..1], height: 50, equal_height: true,
87
84
  style: {position: position})
88
85
  check_box(box, 100, 50)
89
-
90
- box = create_box(children: @fixed_size_boxes[0..0], height: 110)
91
- refute(box.fit(@frame.available_width, @frame.available_height, @frame))
92
86
  end
93
87
 
94
88
  it "respects the border and padding around all columns, position #{position}" do
@@ -150,15 +144,15 @@ describe HexaPDF::Layout::ColumnBox do
150
144
 
151
145
  it "fails if the necessary width is larger than the available one" do
152
146
  box = create_box(children: @fixed_size_boxes[0..2], columns: 4, gaps: [40])
153
- refute(box.fit(100, 100, @frame))
147
+ assert(box.fit(100, 100, @frame).failure?)
154
148
  end
155
149
  end
156
150
  end
157
151
 
158
152
  it "splits the children if they are too big to fill the colums" do
159
- box = create_box(children: @fixed_size_boxes, width: 50, height: 50)
160
- box.fit(100, 100, @frame)
161
- box_a, box_b = box.split(100, 100, @frame)
153
+ box = create_box(children: @fixed_size_boxes, width: 50)
154
+ assert(box.fit(100, 50, @frame).overflow?)
155
+ box_a, box_b = box.split
162
156
  assert_same(box, box_a)
163
157
  assert(box_b.split_box?)
164
158
  assert_equal(5, box_b.children.size)
@@ -171,7 +165,7 @@ describe HexaPDF::Layout::ColumnBox do
171
165
 
172
166
  it "draws the result onto the canvas" do
173
167
  box = create_box(children: @fixed_size_boxes)
174
- box.fit(100, 100, @frame)
168
+ assert(box.fit(100, 100, @frame).success?)
175
169
  box.draw(@canvas, 0, 100 - box.height)
176
170
  operators = 90.step(to: 20, by: -10).map do |y|
177
171
  [[:save_graphics_state],
@@ -193,7 +187,7 @@ describe HexaPDF::Layout::ColumnBox do
193
187
 
194
188
  it "takes a different final location into account" do
195
189
  box = create_box(children: @fixed_size_boxes[0, 2], style: {padding: [2, 4, 6, 8]})
196
- box.fit(100, 100, @frame)
190
+ assert(box.fit(100, 100, @frame).success?)
197
191
  box.draw(@canvas, 20, 10)
198
192
  operators = [
199
193
  [:save_graphics_state],
@@ -15,7 +15,7 @@ describe HexaPDF::Layout::ContainerBox do
15
15
  end
16
16
 
17
17
  def check_box(box, width, height, fit_pos = nil)
18
- assert(box.fit(@frame.available_width, @frame.available_height, @frame), "box didn't fit")
18
+ assert(box.fit(@frame.available_width, @frame.available_height, @frame).success?, "box didn't fit")
19
19
  assert_equal(width, box.width, "box width")
20
20
  assert_equal(height, box.height, "box height")
21
21
  if fit_pos
@@ -5,37 +5,6 @@ require 'hexapdf/layout/frame'
5
5
  require 'hexapdf/layout/box'
6
6
  require 'hexapdf/document'
7
7
 
8
- describe HexaPDF::Layout::Frame::FitResult do
9
- it "shows the box's mask area on #draw when using debug output" do
10
- doc = HexaPDF::Document.new(config: {'debug' => true})
11
- canvas = doc.pages.add.canvas
12
- box = HexaPDF::Layout::Box.create(width: 20, height: 20) {}
13
- frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150)
14
- result = HexaPDF::Layout::Frame::FitResult.new(frame, box)
15
- result.mask = Geom2D::Rectangle(0, 0, 20, 20)
16
- result.x = result.y = 0
17
- result.draw(canvas, dx: 10, dy: 15)
18
- assert_equal(<<~CONTENTS, canvas.contents)
19
- /OC /P1 BDC
20
- q
21
- 1 0 0 1 10 15 cm
22
- 0.0 0.501961 0.0 rg
23
- 0.0 0.392157 0.0 RG
24
- /GS1 gs
25
- 0 0 20 20 re
26
- B
27
- Q
28
- EMC
29
- q
30
- 1 0 0 1 10 15 cm
31
- Q
32
- CONTENTS
33
- ocg = doc.optional_content.ocgs.first
34
- assert_equal([['Debug', ['Page 1', ocg]]], doc.optional_content.default_configuration[:Order])
35
- assert_match(/10,15-20x20/, ocg.name)
36
- end
37
- end
38
-
39
8
  describe HexaPDF::Layout::Frame do
40
9
  before do
41
10
  @frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150)
@@ -455,21 +424,13 @@ describe HexaPDF::Layout::Frame do
455
424
  refute(@frame.fit(box).success?)
456
425
  end
457
426
 
458
- it "handles (but doesn't draw) the box if the its height or width is zero" do
459
- result = Minitest::Mock.new
460
- box = Minitest::Mock.new
461
-
462
- result.expect(:box, box)
463
- box.expect(:height, 0)
464
- @frame.draw(@canvas, result)
465
-
466
- result.expect(:box, box)
467
- box.expect(:height, 5)
468
- result.expect(:box, box)
469
- box.expect(:width, 0)
470
- @frame.draw(@canvas, result)
471
-
472
- result.verify
427
+ it "doesn't do post-fitting tasks if fitting is a failure" do
428
+ box = HexaPDF::Layout::Box.create(width: 400)
429
+ result = @frame.fit(box)
430
+ assert(result.failure?)
431
+ assert_nil(result.x)
432
+ assert_nil(result.y)
433
+ assert_nil(result.mask)
473
434
  end
474
435
  end
475
436
 
@@ -9,6 +9,9 @@ describe HexaPDF::Layout::ImageBox do
9
9
  @image = HexaPDF::Stream.new({Subtype: :Image}, stream: '')
10
10
  @image.define_singleton_method(:width) { 40 }
11
11
  @image.define_singleton_method(:height) { 20 }
12
+ @frame = Object.new
13
+ def @frame.x; 0; end
14
+ def @frame.y; 100; end
12
15
  end
13
16
 
14
17
  def create_box(**kwargs)
@@ -25,35 +28,40 @@ describe HexaPDF::Layout::ImageBox do
25
28
  describe "fit" do
26
29
  it "fits with fixed dimensions" do
27
30
  box = create_box(width: 50, height: 30, style: {padding: [10, 4, 6, 2]})
28
- assert(box.fit(100, 100, nil))
31
+ assert(box.fit(100, 100, @frame).success?)
29
32
  assert_equal(50, box.width)
30
33
  assert_equal(30, box.height)
31
34
  end
32
35
 
33
36
  it "fits with a fixed width" do
34
37
  box = create_box(width: 60, style: {padding: [10, 4, 6, 2]})
35
- assert(box.fit(100, 100, nil))
38
+ assert(box.fit(100, 100, @frame).success?)
36
39
  assert_equal(60, box.width)
37
40
  assert_equal(43, box.height)
38
41
  end
39
42
 
40
43
  it "fits with a fixed height" do
41
44
  box = create_box(height: 40, style: {padding: [10, 4, 6, 2]})
42
- assert(box.fit(100, 100, nil))
45
+ assert(box.fit(100, 100, @frame).success?)
43
46
  assert_equal(54, box.width)
44
47
  assert_equal(40, box.height)
45
48
  end
46
49
 
47
50
  it "fits with auto-scaling to available space" do
48
51
  box = create_box(style: {padding: [10, 4, 6, 2]})
49
- assert(box.fit(100, 100, nil))
52
+ assert(box.fit(100, 100, @frame).success?)
50
53
  assert_equal(100, box.width)
51
54
  assert_equal(63, box.height)
52
55
 
53
- assert(box.fit(100, 30, nil))
56
+ assert(box.fit(100, 30, @frame).success?)
54
57
  assert_equal(34, box.width)
55
58
  assert_equal(30, box.height)
56
59
  end
60
+
61
+ it "fails if one of the calculated dimensions is larger than the available space" do
62
+ box = create_box(height: 60)
63
+ assert(box.fit(100, 100, @frame).failure?)
64
+ end
57
65
  end
58
66
 
59
67
  it "always returns false for empty?" do
@@ -63,7 +71,7 @@ describe HexaPDF::Layout::ImageBox do
63
71
  describe "draw" do
64
72
  it "draws the image" do
65
73
  box = create_box(height: 40, style: {padding: [10, 4, 6, 2]})
66
- box.fit(100, 100, nil)
74
+ box.fit(100, 100, @frame)
67
75
 
68
76
  @canvas = HexaPDF::Document.new.pages.add.canvas
69
77
  box.draw(@canvas, 0, 0)
@@ -19,8 +19,9 @@ describe HexaPDF::Layout::ListBox do
19
19
  HexaPDF::Layout::ListBox.new(content_indentation: 10, **kwargs)
20
20
  end
21
21
 
22
- def check_box(box, width, height, fit_pos = nil)
23
- assert(box.fit(@frame.available_width, @frame.available_height, @frame), "box didn't fit")
22
+ def check_box(box, width, height, status: :success, fit_pos: nil)
23
+ result = box.fit(@frame.available_width, @frame.available_height, @frame)
24
+ assert_equal(result.status, status)
24
25
  assert_equal(width, box.width, "box width")
25
26
  assert_equal(height, box.height, "box height")
26
27
  if fit_pos
@@ -73,45 +74,44 @@ describe HexaPDF::Layout::ListBox do
73
74
  check_box(box, 100, 40)
74
75
  end
75
76
 
76
- it "respects the set initial height and the property overflow=:truncate" do
77
- box = create_box(children: @text_boxes[0, 2], height: 20,
78
- style: {overflow: :truncate, position: position})
79
- check_box(box, 100, 20)
77
+ it "respects the set initial height even when it doesn't fit completely" do
78
+ box = create_box(children: @text_boxes[0, 2], height: 20, style: {position: position})
79
+ check_box(box, 100, 20, status: :overflow)
80
80
  end
81
81
 
82
82
  it "respects the border and padding around all list items, position #{position}" do
83
83
  box = create_box(children: @text_boxes[0, 2],
84
84
  style: {border: {width: [5, 4, 3, 2]}, padding: [5, 4, 3, 2], position: position})
85
- check_box(box, 100, 76, [[14, 60], [14, 30]])
85
+ check_box(box, 100, 76, fit_pos: [[14, 60], [14, 30]])
86
86
  end
87
87
  end
88
88
 
89
89
  it "uses the frame's current cursor position and available width/height when position=:default" do
90
90
  @frame.remove_area(Geom2D::Polygon([0, 0], [10, 0], [10, 90], [100, 90], [100, 100], [0, 100]))
91
91
  box = create_box(children: @text_boxes[0, 2])
92
- check_box(box, 90, 40, [[20, 70], [20, 50]])
92
+ check_box(box, 90, 40, fit_pos: [[20, 70], [20, 50]])
93
93
  end
94
94
 
95
95
  it "respects the frame's shape when style position=:flow" do
96
96
  @frame.remove_area(Geom2D::Polygon([0, 0], [0, 40], [40, 40], [40, 0]))
97
97
  box = create_box(children: @text_boxes[0, 4], style: {position: :flow})
98
- check_box(box, 100, 90, [[10, 80], [10, 60], [10, 40], [50, 10]])
98
+ check_box(box, 100, 90, fit_pos: [[10, 80], [10, 60], [10, 40], [50, 10]])
99
99
  end
100
100
 
101
101
  it "calculates the correct height if the marker is higher than the content" do
102
102
  box = create_box(children: @text_boxes[0, 1], content_indentation: 20,
103
103
  style: {font_size: 30})
104
- check_box(box, 100, 27, [[20, 80]])
104
+ check_box(box, 100, 27, fit_pos: [[20, 80]])
105
105
  end
106
106
 
107
107
  it "respects the content indentation" do
108
108
  box = create_box(children: @text_boxes[0, 1], content_indentation: 30)
109
- check_box(box, 100, 30, [[30, 70]])
109
+ check_box(box, 100, 30, fit_pos: [[30, 70]])
110
110
  end
111
111
 
112
112
  it "respects the spacing between list items" do
113
113
  box = create_box(children: @text_boxes[0, 2], item_spacing: 30)
114
- check_box(box, 100, 70, [[10, 80], [10, 30]])
114
+ check_box(box, 100, 70, fit_pos: [[10, 80], [10, 30]])
115
115
  end
116
116
 
117
117
  it "creates a new box for each marker even if the marker is the same" do
@@ -121,6 +121,11 @@ describe HexaPDF::Layout::ListBox do
121
121
  refute_same(results[0].marker, results[1].marker)
122
122
  end
123
123
 
124
+ it "fails if not even a part of the first list item fits" do
125
+ box = create_box(children: @text_boxes[0, 2], height: 5)
126
+ check_box(box, 100, 0, status: :failure)
127
+ end
128
+
124
129
  it "fails for unknown marker types" do
125
130
  box = create_box(children: @text_boxes[0, 1], marker_type: :unknown)
126
131
  assert_raises(HexaPDF::Error) { box.fit(100, 100, @frame) }
@@ -129,20 +134,20 @@ describe HexaPDF::Layout::ListBox do
129
134
 
130
135
  describe "split" do
131
136
  it "splits before a list item if no part of it will fit" do
132
- box = create_box(children: @text_boxes[0, 2], height: 20)
133
- box.fit(100, 100, @frame)
134
- box_a, box_b = box.split(100, 100, @frame)
137
+ box = create_box(children: @text_boxes[0, 3])
138
+ assert(box.fit(100, 22, @frame).overflow?)
139
+ box_a, box_b = box.split
135
140
  assert_same(box, box_a)
136
141
  assert_equal(:show_first_marker, box_b.split_box?)
137
142
  assert_equal(1, box_a.instance_variable_get(:@results)[0].box_fitter.fit_results.size)
138
- assert_equal(1, box_b.children.size)
143
+ assert_equal(2, box_b.children.size)
139
144
  assert_equal(2, box_b.start_number)
140
145
  end
141
146
 
142
147
  it "splits a list item if some part of it will fit" do
143
- box = create_box(children: @text_boxes[0, 2], height: 10)
144
- box.fit(100, 100, @frame)
145
- box_a, box_b = box.split(100, 100, @frame)
148
+ box = create_box(children: @text_boxes[0, 2])
149
+ assert(box.fit(100, 10, @frame).overflow?)
150
+ box_a, box_b = box.split
146
151
  assert_same(box, box_a)
147
152
  assert_equal(:hide_first_marker, box_b.split_box?)
148
153
  assert_equal(1, box_a.instance_variable_get(:@results)[0].box_fitter.fit_results.size)
@@ -152,8 +157,8 @@ describe HexaPDF::Layout::ListBox do
152
157
 
153
158
  it "splits a list item containg multiple boxes along box lines" do
154
159
  box = create_box(children: [@text_boxes[0], @text_boxes[1, 2]])
155
- box.fit(100, 40, @frame)
156
- box_a, box_b = box.split(100, 40, @frame)
160
+ assert(box.fit(100, 40, @frame).overflow?)
161
+ box_a, box_b = box.split
157
162
  assert_same(box, box_a)
158
163
  assert_equal(:hide_first_marker, box_b.split_box?)
159
164
  assert_equal(1, box_a.instance_variable_get(:@results)[1].box_fitter.fit_results.size)
@@ -338,11 +343,5 @@ describe HexaPDF::Layout::ListBox do
338
343
  assert_operators(@canvas.contents, [:set_font_and_size, [:F1, 5]], range: 1)
339
344
  assert_equal(:ZapfDingbats, @canvas.resources.font(:F1)[:BaseFont])
340
345
  end
341
-
342
- it "fails if the initial height is set and property overflow is set to :error" do
343
- box = create_box(children: @fixed_size_boxes[0, 2], height: 10)
344
- box.fit(100, 100, @frame)
345
- assert_raises(HexaPDF::Error) { box.draw(@canvas, 0, 100 - box.height) }
346
- end
347
346
  end
348
347
  end