hexapdf 0.43.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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/examples/030-pdfa.rb +1 -0
  4. data/lib/hexapdf/composer.rb +1 -0
  5. data/lib/hexapdf/document/files.rb +7 -2
  6. data/lib/hexapdf/document/metadata.rb +12 -1
  7. data/lib/hexapdf/layout/box.rb +160 -61
  8. data/lib/hexapdf/layout/box_fitter.rb +1 -0
  9. data/lib/hexapdf/layout/column_box.rb +22 -24
  10. data/lib/hexapdf/layout/container_box.rb +2 -2
  11. data/lib/hexapdf/layout/frame.rb +13 -95
  12. data/lib/hexapdf/layout/image_box.rb +4 -4
  13. data/lib/hexapdf/layout/list_box.rb +11 -19
  14. data/lib/hexapdf/layout/style.rb +5 -1
  15. data/lib/hexapdf/layout/table_box.rb +48 -55
  16. data/lib/hexapdf/layout/text_box.rb +27 -42
  17. data/lib/hexapdf/parser.rb +5 -2
  18. data/lib/hexapdf/type/file_specification.rb +9 -5
  19. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  20. data/lib/hexapdf/version.rb +1 -1
  21. data/test/hexapdf/document/test_files.rb +5 -0
  22. data/test/hexapdf/document/test_metadata.rb +21 -0
  23. data/test/hexapdf/layout/test_box.rb +82 -37
  24. data/test/hexapdf/layout/test_box_fitter.rb +7 -0
  25. data/test/hexapdf/layout/test_column_box.rb +7 -13
  26. data/test/hexapdf/layout/test_container_box.rb +1 -1
  27. data/test/hexapdf/layout/test_frame.rb +0 -48
  28. data/test/hexapdf/layout/test_image_box.rb +14 -6
  29. data/test/hexapdf/layout/test_list_box.rb +25 -26
  30. data/test/hexapdf/layout/test_table_box.rb +39 -53
  31. data/test/hexapdf/layout/test_text_box.rb +38 -66
  32. data/test/hexapdf/test_composer.rb +6 -0
  33. data/test/hexapdf/test_parser.rb +8 -0
  34. data/test/hexapdf/type/test_file_specification.rb +2 -1
  35. metadata +2 -2
@@ -187,6 +187,27 @@ describe HexaPDF::Document::Metadata do
187
187
  assert_equal(metadata, @doc.catalog[:Metadata].stream.sub(/(?<=id=")\w+/, ''))
188
188
  end
189
189
 
190
+ it "writes the custom metadata" do
191
+ @metadata.delete
192
+ @metadata.custom_metadata("<rdf:Description>Test</rdf:Description>")
193
+ @metadata.custom_metadata("<rdf:Description>Test2</rdf:Description>")
194
+ @doc.write(StringIO.new, update_fields: false)
195
+ metadata = <<~XMP
196
+ <?xpacket begin="" id=""?>
197
+ <x:xmpmeta xmlns:x="adobe:ns:meta/">
198
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
199
+ <rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
200
+ <pdf:Producer>HexaPDF version #{HexaPDF::VERSION}</pdf:Producer>
201
+ </rdf:Description>
202
+ <rdf:Description>Test</rdf:Description>
203
+ <rdf:Description>Test2</rdf:Description>
204
+ </rdf:RDF>
205
+ </x:xmpmeta>
206
+ <?xpacket end="r"?>
207
+ XMP
208
+ assert_equal(metadata, @doc.catalog[:Metadata].stream.sub(/(?<=id=")\w+/, ''))
209
+ end
210
+
190
211
  it "writes the XMP metadata" do
191
212
  title = HexaPDF::Document::Metadata::LocalizedString.new('Der Titel')
192
213
  title.language = 'de'
@@ -4,6 +4,37 @@ 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
@@ -78,7 +109,7 @@ describe HexaPDF::Layout::Box do
78
109
  describe "fit" do
79
110
  it "fits a fixed sized box" do
80
111
  box = create_box(width: 50, height: 50, style: {padding: 5})
81
- assert(box.fit(100, 100, @frame))
112
+ assert(box.fit(100, 100, @frame).success?)
82
113
  assert_equal(50, box.width)
83
114
  assert_equal(50, box.height)
84
115
  assert_equal(5, box.instance_variable_get(:@fit_x))
@@ -87,28 +118,45 @@ describe HexaPDF::Layout::Box do
87
118
 
88
119
  it "uses the maximum available width" do
89
120
  box = create_box(height: 50)
90
- assert(box.fit(100, 100, @frame))
121
+ assert(box.fit(100, 100, @frame).success?)
91
122
  assert_equal(100, box.width)
92
123
  assert_equal(50, box.height)
93
124
  end
94
125
 
95
126
  it "uses the maximum available height" do
96
127
  box = create_box(width: 50)
97
- assert(box.fit(100, 100, @frame))
128
+ assert(box.fit(100, 100, @frame).success?)
98
129
  assert_equal(50, box.width)
99
130
  assert_equal(100, box.height)
100
131
  end
101
132
 
102
133
  it "uses float comparison" do
103
134
  box = create_box(width: 50.0000002, height: 49.9999996)
104
- assert(box.fit(50, 50, @frame))
135
+ assert(box.fit(50, 50, @frame).success?)
105
136
  assert_equal(50.0000002, box.width)
106
137
  assert_equal(49.9999996, box.height)
107
138
  end
108
139
 
109
- it "returns false if the box doesn't fit" do
140
+ it "fails if the box doesn't fit, position != :flow and its width is greater than the available width" do
110
141
  box = create_box(width: 101)
111
- refute(box.fit(100, 100, @frame))
142
+ assert(box.fit(100, 100, @frame).failure?)
143
+ end
144
+
145
+ it "fails if the box doesn't fit, position != :flow and its width is greater than the available width" do
146
+ box = create_box(height: 101)
147
+ assert(box.fit(100, 100, @frame).failure?)
148
+ end
149
+
150
+ it "fails if its content width is zero" do
151
+ box = create_box(height: 100)
152
+ box.style.padding = [0, 100]
153
+ assert(box.fit(150, 150, @frame).failure?)
154
+ end
155
+
156
+ it "fails if its content height is zero" do
157
+ box = create_box(width: 100)
158
+ box.style.padding = [100, 0]
159
+ assert(box.fit(150, 150, @frame).failure?)
112
160
  end
113
161
 
114
162
  it "can use the #content_width/#content_height helper methods" do
@@ -116,9 +164,9 @@ describe HexaPDF::Layout::Box do
116
164
  box.define_singleton_method(:fit_content) do |_aw, _ah, _frame|
117
165
  update_content_width { 10 }
118
166
  update_content_height { 20 }
119
- true
167
+ fit_result.success!
120
168
  end
121
- assert(box.fit(100, 100, @frame))
169
+ assert(box.fit(100, 100, @frame).success?)
122
170
  assert_equal(10, box.width)
123
171
  assert_equal(20, box.height)
124
172
 
@@ -126,49 +174,40 @@ describe HexaPDF::Layout::Box do
126
174
  box.define_singleton_method(:fit_content) do |_aw, _ah, _frame|
127
175
  update_content_width { 10 }
128
176
  update_content_height { 20 }
129
- true
177
+ fit_result.success!
130
178
  end
131
- assert(box.fit(100, 100, @frame))
179
+ assert(box.fit(100, 100, @frame).success?)
132
180
  assert_equal(30, box.width)
133
181
  assert_equal(50, box.height)
134
182
  end
135
183
  end
136
184
 
137
185
  describe "split" do
138
- before do
139
- @box = create_box(width: 100, height: 100)
140
- @box.fit(100, 100, @frame)
141
- end
142
-
143
186
  it "doesn't need to be split if it completely fits" do
144
- assert_equal([@box, nil], @box.split(100, 100, @frame))
145
- end
146
-
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))
150
- end
151
-
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))
187
+ box = create_box(width: 100, height: 100)
188
+ box.fit(100, 100, @frame)
189
+ assert_equal([box, nil], box.split)
155
190
  end
156
191
 
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))
192
+ it "is not split if no part of it fits" do
193
+ box = create_box(width: 150)
194
+ box.fit(100, 100, @frame)
195
+ assert_equal([nil, box], box.split)
160
196
  end
161
197
 
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))
198
+ it "is not split if a height for the box is specified and it doesn't completely fit" do
199
+ box = create_box(height: 50)
200
+ box.define_singleton_method(:fit_content) {|*| fit_result.overflow! }
201
+ box.fit(100, 100, @frame)
202
+ assert_equal([box, nil], box.split)
165
203
  end
166
204
 
167
- it "can't be split if it doesn't (completely) fit as the default implementation " \
205
+ it "can't be split if it doesn't completely fit as the default implementation " \
168
206
  "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))
207
+ box = create_box
208
+ box.define_singleton_method(:fit_content) {|*| fit_result.overflow! }
209
+ box.fit(100, 100, @frame)
210
+ assert_equal([nil, box], box.split)
172
211
  end
173
212
  end
174
213
 
@@ -177,7 +216,7 @@ describe HexaPDF::Layout::Box do
177
216
  box.fit(100, 100, @frame)
178
217
  cloned_box = box.send(:create_split_box)
179
218
  assert(cloned_box.split_box?)
180
- refute(cloned_box.instance_variable_get(:@fit_successful))
219
+ refute_same(box.fit_result, cloned_box.fit_result)
181
220
  assert_equal(0, cloned_box.width)
182
221
  assert_equal(0, cloned_box.height)
183
222
  end
@@ -240,6 +279,12 @@ describe HexaPDF::Layout::Box do
240
279
  refute(box.style.overlays?)
241
280
  end
242
281
 
282
+ it "raises an error if the style property :overflow is set to error and the box doesn't completely fit" do
283
+ box = create_box(height: 50, style: {overflow: :error})
284
+ box.fit_result.overflow!
285
+ assert_raises(HexaPDF::Error) { box.draw(@canvas, 0, 0) }
286
+ end
287
+
243
288
  it "wraps the box in optional content markers if the optional_content property is set" do
244
289
  box = create_box(properties: {'optional_content' => 'Text'})
245
290
  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)
@@ -454,23 +423,6 @@ describe HexaPDF::Layout::Frame do
454
423
  box = HexaPDF::Layout::Box.create
455
424
  refute(@frame.fit(box).success?)
456
425
  end
457
-
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
473
- end
474
426
  end
475
427
 
476
428
  describe "split" do
@@ -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,9 +134,9 @@ 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, 2])
138
+ assert(box.fit(100, 20, @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)
@@ -140,9 +145,9 @@ describe HexaPDF::Layout::ListBox do
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