hexapdf 0.33.0 → 0.34.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -1
  3. data/examples/026-optional_content.rb +55 -0
  4. data/examples/027-composer_optional_content.rb +83 -0
  5. data/lib/hexapdf/cli/command.rb +7 -1
  6. data/lib/hexapdf/cli/fonts.rb +1 -1
  7. data/lib/hexapdf/cli/inspect.rb +2 -4
  8. data/lib/hexapdf/composer.rb +2 -1
  9. data/lib/hexapdf/configuration.rb +21 -1
  10. data/lib/hexapdf/content/canvas.rb +52 -0
  11. data/lib/hexapdf/content/operator.rb +2 -0
  12. data/lib/hexapdf/dictionary.rb +1 -0
  13. data/lib/hexapdf/dictionary_fields.rb +1 -2
  14. data/lib/hexapdf/digital_signature/verification_result.rb +1 -2
  15. data/lib/hexapdf/document/layout.rb +3 -0
  16. data/lib/hexapdf/document/pages.rb +1 -1
  17. data/lib/hexapdf/document.rb +7 -0
  18. data/lib/hexapdf/encryption/ruby_aes.rb +10 -20
  19. data/lib/hexapdf/layout/box.rb +23 -3
  20. data/lib/hexapdf/layout/column_box.rb +2 -1
  21. data/lib/hexapdf/layout/frame.rb +23 -6
  22. data/lib/hexapdf/layout/inline_box.rb +20 -9
  23. data/lib/hexapdf/layout/list_box.rb +34 -20
  24. data/lib/hexapdf/layout/page_style.rb +2 -1
  25. data/lib/hexapdf/layout/style.rb +46 -6
  26. data/lib/hexapdf/layout/table_box.rb +9 -7
  27. data/lib/hexapdf/layout/text_box.rb +9 -2
  28. data/lib/hexapdf/layout/text_fragment.rb +28 -2
  29. data/lib/hexapdf/layout/text_layouter.rb +21 -5
  30. data/lib/hexapdf/stream.rb +1 -2
  31. data/lib/hexapdf/type/actions/set_ocg_state.rb +86 -0
  32. data/lib/hexapdf/type/actions.rb +1 -0
  33. data/lib/hexapdf/type/annotations/text.rb +1 -2
  34. data/lib/hexapdf/type/catalog.rb +10 -1
  35. data/lib/hexapdf/type/cid_font.rb +15 -1
  36. data/lib/hexapdf/type/form.rb +75 -5
  37. data/lib/hexapdf/type/optional_content_configuration.rb +170 -0
  38. data/lib/hexapdf/type/optional_content_group.rb +370 -0
  39. data/lib/hexapdf/type/optional_content_membership.rb +63 -0
  40. data/lib/hexapdf/type/optional_content_properties.rb +158 -0
  41. data/lib/hexapdf/type/page.rb +27 -11
  42. data/lib/hexapdf/type/page_label.rb +4 -8
  43. data/lib/hexapdf/type.rb +4 -0
  44. data/lib/hexapdf/utils/pdf_doc_encoding.rb +0 -1
  45. data/lib/hexapdf/version.rb +1 -1
  46. data/test/hexapdf/content/test_canvas.rb +49 -0
  47. data/test/hexapdf/document/test_layout.rb +7 -2
  48. data/test/hexapdf/document/test_pages.rb +6 -6
  49. data/test/hexapdf/layout/test_box.rb +13 -4
  50. data/test/hexapdf/layout/test_frame.rb +13 -1
  51. data/test/hexapdf/layout/test_inline_box.rb +17 -8
  52. data/test/hexapdf/layout/test_list_box.rb +48 -31
  53. data/test/hexapdf/layout/test_style.rb +10 -0
  54. data/test/hexapdf/layout/test_table_box.rb +32 -26
  55. data/test/hexapdf/layout/test_text_box.rb +8 -0
  56. data/test/hexapdf/layout/test_text_fragment.rb +33 -0
  57. data/test/hexapdf/layout/test_text_layouter.rb +32 -5
  58. data/test/hexapdf/test_composer.rb +10 -0
  59. data/test/hexapdf/test_dictionary.rb +10 -0
  60. data/test/hexapdf/test_document.rb +4 -0
  61. data/test/hexapdf/test_writer.rb +3 -3
  62. data/test/hexapdf/type/actions/test_set_ocg_state.rb +40 -0
  63. data/test/hexapdf/type/test_catalog.rb +11 -0
  64. data/test/hexapdf/type/test_form.rb +119 -0
  65. data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
  66. data/test/hexapdf/type/test_optional_content_group.rb +158 -0
  67. data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
  68. data/test/hexapdf/type/test_page.rb +2 -2
  69. metadata +14 -3
@@ -103,8 +103,7 @@ module HexaPDF
103
103
  end
104
104
  end
105
105
 
106
- # :nodoc:
107
- NUMBERING_STYLE_MAPPING = {
106
+ NUMBERING_STYLE_MAPPING = { # :nodoc:
108
107
  decimal: :D, D: :D,
109
108
  uppercase_roman: :R, R: :R,
110
109
  lowercase_roman: :r, r: :r,
@@ -113,8 +112,7 @@ module HexaPDF
113
112
  none: nil
114
113
  }
115
114
 
116
- # :nodoc:
117
- REVERSE_NUMBERING_STYLE_MAPPING = Hash[*NUMBERING_STYLE_MAPPING.flatten.reverse]
115
+ REVERSE_NUMBERING_STYLE_MAPPING = Hash[*NUMBERING_STYLE_MAPPING.flatten.reverse] # :nodoc:
118
116
 
119
117
  # :call-seq:
120
118
  # page_label.numbering_style -> numbering_style
@@ -174,8 +172,7 @@ module HexaPDF
174
172
 
175
173
  private
176
174
 
177
- # :nodoc:
178
- ALPHABET = ('A'..'Z').to_a
175
+ ALPHABET = ('A'..'Z').to_a # :nodoc:
179
176
 
180
177
  # Maps the given number to uppercase (or, if +lowercase+ is +true+, lowercase) letters (e.g. 1
181
178
  # -> A, 27 -> AA, 28 -> AB, ...).
@@ -188,8 +185,7 @@ module HexaPDF
188
185
  lowercase ? result.downcase : result
189
186
  end
190
187
 
191
- # :nodoc:
192
- ROMAN_NUMERAL_MAPPING = {
188
+ ROMAN_NUMERAL_MAPPING = { # :nodoc:
193
189
  1000 => "M",
194
190
  900 => "CM",
195
191
  500 => "D",
data/lib/hexapdf/type.rb CHANGED
@@ -76,6 +76,10 @@ module HexaPDF
76
76
  autoload(:OutlineItem, 'hexapdf/type/outline_item')
77
77
  autoload(:PageLabel, 'hexapdf/type/page_label')
78
78
  autoload(:MarkInformation, 'hexapdf/type/mark_information')
79
+ autoload(:OptionalContentGroup, 'hexapdf/type/optional_content_group')
80
+ autoload(:OptionalContentMembership, 'hexapdf/type/optional_content_membership')
81
+ autoload(:OptionalContentProperties, 'hexapdf/type/optional_content_properties')
82
+ autoload(:OptionalContentConfiguration, 'hexapdf/type/optional_content_configuration')
79
83
 
80
84
  end
81
85
 
@@ -52,7 +52,6 @@ module HexaPDF
52
52
  # See: PDF2.0 s7.9.2, D.1, D.3
53
53
  module PDFDocEncoding
54
54
 
55
- # :nodoc:
56
55
  CHARACTER_MAP = %W[\uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD
57
56
  \uFFFD \u0009 \u000A \uFFFD \uFFFD \u000D \uFFFD \uFFFD
58
57
  \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.33.0'
40
+ VERSION = '0.34.0'
41
41
 
42
42
  end
@@ -71,6 +71,13 @@ describe HexaPDF::Content::Canvas do
71
71
  end
72
72
  end
73
73
 
74
+ describe "pos" do
75
+ it "returns the transformed position" do
76
+ @canvas.translate(9, 4)
77
+ assert_equal([10, 5], @canvas.pos(1, 1))
78
+ end
79
+ end
80
+
74
81
  describe "save_graphics_state" do
75
82
  it "invokes the operator implementation" do
76
83
  assert_operator_invoked(:q) { @canvas.save_graphics_state }
@@ -1280,6 +1287,48 @@ describe HexaPDF::Content::Canvas do
1280
1287
  end
1281
1288
  end
1282
1289
 
1290
+ describe "optional_content" do
1291
+ it "invokes the marked-sequence operator implementation" do
1292
+ assert_operator_invoked(:BDC, :OC, :P1) { @canvas.optional_content('Test') }
1293
+ end
1294
+
1295
+ it "is serialized correctly when no block is used" do
1296
+ @canvas.optional_content('Test')
1297
+ assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]]])
1298
+ end
1299
+
1300
+ it "is serialized correctly when a block is used" do
1301
+ @canvas.optional_content('Test') {}
1302
+ assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]],
1303
+ [:end_marked_content]])
1304
+ end
1305
+
1306
+ it "uses the provided OCG dictionary" do
1307
+ ocg = @doc.optional_content.add_ocg('Test')
1308
+ @canvas.optional_content(ocg)
1309
+ assert_equal(ocg, @page.resources.property_list(:P1))
1310
+ end
1311
+
1312
+ it "uses an existing OCG specified by name" do
1313
+ ocg = @doc.optional_content.add_ocg('Test')
1314
+ @canvas.optional_content('Test')
1315
+ assert_equal(ocg, @page.resources.property_list(:P1))
1316
+ end
1317
+
1318
+ it "creates an OCG if the named one doesn't yet exist" do
1319
+ @canvas.optional_content('Test')
1320
+ assert_equal(@doc.optional_content.ocg('Test'), @page.resources.property_list(:P1))
1321
+ end
1322
+
1323
+ it "always creates a new OCG if use_existing_ocg is false" do
1324
+ ocg = @doc.optional_content.add_ocg('Test')
1325
+ @canvas.optional_content('Test', use_existing_ocg: false)
1326
+ pl_item = @page.resources.property_list(:P1)
1327
+ refute_equal(ocg, pl_item)
1328
+ assert_equal(@doc.optional_content.ocgs.last, pl_item)
1329
+ end
1330
+ end
1331
+
1283
1332
  describe "color_from_specification "do
1284
1333
  it "accepts a color string" do
1285
1334
  assert_equal([1, 0, 0], @canvas.color_from_specification("red").components)
@@ -219,6 +219,10 @@ describe HexaPDF::Document::Layout do
219
219
  box = @layout.text_box("Test", box_style: :named)
220
220
  assert_equal(20, box.style.font_size)
221
221
  end
222
+
223
+ it "raises an error if the to-be-used style doesn't exist" do
224
+ assert_raises(HexaPDF::Error) { @layout.text_box("Test", style: :unknown) }
225
+ end
222
226
  end
223
227
 
224
228
  describe "formatted_text" do
@@ -305,9 +309,10 @@ describe HexaPDF::Document::Layout do
305
309
 
306
310
  it "allows creating an inline box through a hash with a :box key" do
307
311
  block = lambda {|item| item.box(:base, width: 5, height: 15) }
308
- box = @layout.formatted_text_box([{box: :list, width: 10, block: block}])
312
+ box = @layout.formatted_text_box([{box: :column, columns: 1, width: 100, block: block}])
309
313
  ibox = box.instance_variable_get(:@items).first
310
- assert_equal(10, ibox.width)
314
+ ibox.fit_wrapped_box(nil)
315
+ assert_equal(100, ibox.width)
311
316
  assert_equal(15, ibox.height)
312
317
  end
313
318
 
@@ -196,15 +196,15 @@ describe HexaPDF::Document::Pages do
196
196
  end
197
197
 
198
198
  it "works for multiple page label entries" do
199
- @doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 2, {S: :d}, 7, {S: :A}]}
199
+ @doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 2, {S: :D}, 7, {S: :A}]}
200
200
  result = @doc.pages.each_labelling_range.to_a
201
- assert_equal([[0, 2, {S: :r}], [2, 5, {S: :d}], [7, 3, {S: :A}]],
201
+ assert_equal([[0, 2, {S: :r}], [2, 5, {S: :D}], [7, 3, {S: :A}]],
202
202
  result.map {|s, c, l| [s, c, l.value] })
203
203
  end
204
204
 
205
205
  it "returns a zero or negative count for the last range if there aren't enough pages" do
206
206
  assert_equal(10, @doc.pages.count)
207
- @doc.catalog[:PageLabels] = {Nums: [0, {S: :d}, 10, {S: :r}]}
207
+ @doc.catalog[:PageLabels] = {Nums: [0, {S: :D}, 10, {S: :r}]}
208
208
  assert_equal(0, @doc.pages.each_labelling_range.to_a[-1][1])
209
209
  @doc.catalog[:PageLabels][:Nums][2] = 11
210
210
  assert_equal(-1, @doc.pages.each_labelling_range.to_a[-1][1])
@@ -221,19 +221,19 @@ describe HexaPDF::Document::Pages do
221
221
 
222
222
  it "adds an entry for the range starting at 0 if it doesn't exist" do
223
223
  label = @doc.pages.add_labelling_range(5)
224
- assert_equal([{S: :d}, label],
224
+ assert_equal([{S: :D}, label],
225
225
  @doc.catalog.page_labels[:Nums].value.values_at(1, 3))
226
226
  end
227
227
  end
228
228
 
229
229
  describe "delete_labelling_range" do
230
230
  before do
231
- @doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 5, {S: :d}]}
231
+ @doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 5, {S: :D}]}
232
232
  end
233
233
 
234
234
  it "deletes the labelling range for a given start index" do
235
235
  label = @doc.pages.delete_labelling_range(5)
236
- assert_equal({S: :d}, label)
236
+ assert_equal({S: :D}, label)
237
237
  end
238
238
 
239
239
  it "deletes the labelling range for 0 if it is the last, together with the number tree" do
@@ -56,8 +56,8 @@ describe HexaPDF::Layout::Box do
56
56
  end
57
57
 
58
58
  it "allows setting custom properties" do
59
- box = create_box(properties: {'key' => :value})
60
- assert_equal({'key' => :value}, box.properties)
59
+ assert_equal({}, create_box(properties: nil).properties)
60
+ assert_equal({'key' => :value}, create_box(properties: {'key' => :value}).properties)
61
61
  end
62
62
  end
63
63
 
@@ -150,6 +150,10 @@ describe HexaPDF::Layout::Box do
150
150
  end
151
151
 
152
152
  describe "draw" do
153
+ before do
154
+ @canvas = HexaPDF::Document.new.pages.add.canvas
155
+ end
156
+
153
157
  it "draws the box onto the canvas" do
154
158
  box = create_box(width: 150, height: 130) do |canvas, _|
155
159
  canvas.line_width(15)
@@ -161,7 +165,6 @@ describe HexaPDF::Layout::Box do
161
165
  box.style.underlays.add {|canvas, _| canvas.line_width(10) }
162
166
  box.style.overlays.add {|canvas, _| canvas.line_width(20) }
163
167
 
164
- @canvas = HexaPDF::Document.new.pages.add.canvas
165
168
  box.draw(@canvas, 5, 5)
166
169
  assert_operators(@canvas.contents, [[:save_graphics_state],
167
170
  [:set_graphics_state_parameters, [:GS1]],
@@ -195,7 +198,6 @@ describe HexaPDF::Layout::Box do
195
198
  end
196
199
 
197
200
  it "draws nothing onto the canvas if the box is empty" do
198
- @canvas = HexaPDF::Document.new.pages.add.canvas
199
201
  box = create_box
200
202
  box.draw(@canvas, 5, 5)
201
203
  assert_operators(@canvas.contents, [])
@@ -204,6 +206,13 @@ describe HexaPDF::Layout::Box do
204
206
  refute(box.style.border?)
205
207
  refute(box.style.overlays?)
206
208
  end
209
+
210
+ it "wraps the box in optional content markers if the optional_content property is set" do
211
+ box = create_box(properties: {'optional_content' => 'Text'})
212
+ box.draw(@canvas, 0, 0)
213
+ assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]],
214
+ [:end_marked_content]])
215
+ end
207
216
  end
208
217
 
209
218
  describe "empty?" do
@@ -5,7 +5,7 @@ require 'hexapdf/layout/frame'
5
5
  require 'hexapdf/layout/box'
6
6
  require 'hexapdf/document'
7
7
 
8
- describe HexaPDF::Layout::Frame do
8
+ describe HexaPDF::Layout::Frame::FitResult do
9
9
  it "shows the box's mask area on #draw when using debug output" do
10
10
  doc = HexaPDF::Document.new(config: {'debug' => true})
11
11
  canvas = doc.pages.add.canvas
@@ -15,6 +15,7 @@ describe HexaPDF::Layout::Frame do
15
15
  result.x = result.y = 0
16
16
  result.draw(canvas)
17
17
  assert_equal(<<~CONTENTS, canvas.contents)
18
+ /OC /P1 BDC
18
19
  q
19
20
  0.0 0.501961 0.0 rg
20
21
  0.0 0.392157 0.0 RG
@@ -22,10 +23,13 @@ describe HexaPDF::Layout::Frame do
22
23
  0 0 20 20 re
23
24
  B
24
25
  Q
26
+ EMC
25
27
  q
26
28
  1 0 0 1 0 0 cm
27
29
  Q
28
30
  CONTENTS
31
+ ocg = doc.optional_content.ocgs.first
32
+ assert_equal([['Debug', ocg]], doc.optional_content.default_configuration[:Order])
29
33
  end
30
34
  end
31
35
 
@@ -34,6 +38,14 @@ describe HexaPDF::Layout::Frame do
34
38
  @frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150)
35
39
  end
36
40
 
41
+ it "allows accessing the context's document" do
42
+ assert_nil(@frame.document)
43
+ context = Minitest::Mock.new
44
+ context.expect(:document, :document)
45
+ assert_equal(:document, HexaPDF::Layout::Frame.new(0, 0, 10, 10, context: context).document)
46
+ context.verify
47
+ end
48
+
37
49
  it "allows access to the bounding box attributes" do
38
50
  assert_equal(5, @frame.left)
39
51
  assert_equal(10, @frame.bottom)
@@ -24,25 +24,28 @@ describe HexaPDF::Layout::InlineBox do
24
24
  assert_equal(:top, ibox.valign)
25
25
  end
26
26
 
27
+ it "fails if the wrapped box has not width set" do
28
+ box = HexaPDF::Document.new.layout.text("test is not going good")
29
+ assert_raises(HexaPDF::Error) { inline_box(box) }
30
+ end
31
+ end
32
+
33
+ describe "fit_wrapped_box" do
27
34
  it "automatically fits the provided box into a frame" do
28
35
  ibox = inline_box(HexaPDF::Document.new.layout.text("test is going good", width: 20))
36
+ ibox.fit_wrapped_box(nil)
29
37
  assert_equal(20, ibox.width)
30
38
  assert_equal(45, ibox.height)
31
39
  end
32
40
 
33
- it "fails if the wrapped box has not width set" do
34
- box = HexaPDF::Document.new.layout.text("test is not going good")
35
- assert_raises(HexaPDF::Error) { inline_box(box) }
36
- end
37
-
38
41
  it "fails if the wrapped box could not be fit" do
39
42
  box = HexaPDF::Document.new.layout.text("test is not going good", width: 1)
40
- assert_raises(HexaPDF::Error) { inline_box(box) }
43
+ assert_raises(HexaPDF::Error) { inline_box(box).fit_wrapped_box(nil) }
41
44
  end
42
45
 
43
46
  it "fails if the height is not set explicitly and during fitting" do
44
47
  assert_raises(HexaPDF::Error) do
45
- inline_box(HexaPDF::Layout::Box.create(width: 10))
48
+ inline_box(HexaPDF::Layout::Box.create(width: 10)).fit_wrapped_box(nil)
46
49
  end
47
50
  end
48
51
  end
@@ -50,7 +53,9 @@ describe HexaPDF::Layout::InlineBox do
50
53
  it "draws the wrapped box at the correct position" do
51
54
  doc = HexaPDF::Document.new
52
55
  canvas = doc.pages.add.canvas
53
- inline_box(doc.layout.text("", width: 20, margin: [15, 10])).draw(canvas, 100, 200)
56
+ box = inline_box(doc.layout.text("", width: 20, margin: [15, 10]))
57
+ box.fit_wrapped_box(nil)
58
+ box.draw(canvas, 100, 200)
54
59
  assert_equal("q\n1 0 0 1 110 -99785 cm\nQ\n", canvas.contents)
55
60
  end
56
61
 
@@ -59,6 +64,10 @@ describe HexaPDF::Layout::InlineBox do
59
64
  refute(HexaPDF::Layout::InlineBox.create(width: 10, height: 15) {}.empty?)
60
65
  end
61
66
 
67
+ it "returns the style of the box" do
68
+ assert_same(@box.box.style, @box.style)
69
+ end
70
+
62
71
  describe "valign" do
63
72
  it "has a default value of :baseline" do
64
73
  assert_equal(:baseline, @box.valign)
@@ -6,7 +6,9 @@ require 'hexapdf/layout/list_box'
6
6
 
7
7
  describe HexaPDF::Layout::ListBox do
8
8
  before do
9
- @frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
9
+ @doc = HexaPDF::Document.new
10
+ @page = @doc.pages.add
11
+ @frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100, context: @page)
10
12
  inline_box = HexaPDF::Layout::InlineBox.create(width: 10, height: 10) {}
11
13
  @text_boxes = 5.times.map do
12
14
  HexaPDF::Layout::TextBox.new(items: [inline_box] * 15, style: {position: :default})
@@ -23,8 +25,8 @@ describe HexaPDF::Layout::ListBox do
23
25
  assert_equal(height, box.height, "box height")
24
26
  if fit_pos
25
27
  results = box.instance_variable_get(:@results)
26
- results.each_with_index do |box_fitter, item_index|
27
- box_fitter.fit_results.each_with_index do |fit_result, result_index|
28
+ results.each_with_index do |item_result, item_index|
29
+ item_result.box_fitter.fit_results.each_with_index do |fit_result, result_index|
28
30
  x, y = fit_pos.shift
29
31
  assert_equal(x, fit_result.x, "item #{item_index}, result #{result_index}, x")
30
32
  assert_equal(y, fit_result.y, "item #{item_index}, result #{result_index}, y")
@@ -55,7 +57,7 @@ describe HexaPDF::Layout::ListBox do
55
57
  it "is empty if nothing could be fit" do
56
58
  box = create_box(children: [@text_boxes[0]], width: 5)
57
59
  box.fit(@frame.available_width, @frame.available_height, @frame)
58
- assert(create_box.empty?)
60
+ assert(box.empty?)
59
61
  end
60
62
  end
61
63
 
@@ -90,6 +92,12 @@ describe HexaPDF::Layout::ListBox do
90
92
  check_box(box, 100, 90, [[10, 80], [10, 60], [10, 40], [50, 10]])
91
93
  end
92
94
 
95
+ it "calculates the correct height if the marker is higher than the content" do
96
+ box = create_box(children: @text_boxes[0, 1], content_indentation: 20,
97
+ style: {font_size: 30})
98
+ check_box(box, 100, 27, [[20, 80]])
99
+ end
100
+
93
101
  it "respects the content indentation" do
94
102
  box = create_box(children: @text_boxes[0, 1], content_indentation: 30)
95
103
  check_box(box, 100, 30, [[30, 70]])
@@ -99,6 +107,11 @@ describe HexaPDF::Layout::ListBox do
99
107
  box = create_box(children: @text_boxes[0, 2], item_spacing: 30)
100
108
  check_box(box, 100, 70, [[10, 80], [10, 30]])
101
109
  end
110
+
111
+ it "fails for unknown item types" do
112
+ box = create_box(children: @text_boxes[0, 1], item_type: :unknown)
113
+ assert_raises(HexaPDF::Error) { box.fit(100, 100, @frame) }
114
+ end
102
115
  end
103
116
 
104
117
  describe "split" do
@@ -108,7 +121,7 @@ describe HexaPDF::Layout::ListBox do
108
121
  box_a, box_b = box.split(100, 100, @frame)
109
122
  assert_same(box, box_a)
110
123
  assert_equal(:show_first_marker, box_b.split_box?)
111
- assert_equal(1, box_a.instance_variable_get(:@results)[0].fit_results.size)
124
+ assert_equal(1, box_a.instance_variable_get(:@results)[0].box_fitter.fit_results.size)
112
125
  assert_equal(1, box_b.children.size)
113
126
  assert_equal(2, box_b.start_number)
114
127
  end
@@ -119,7 +132,7 @@ describe HexaPDF::Layout::ListBox do
119
132
  box_a, box_b = box.split(100, 100, @frame)
120
133
  assert_same(box, box_a)
121
134
  assert_equal(:hide_first_marker, box_b.split_box?)
122
- assert_equal(1, box_a.instance_variable_get(:@results)[0].fit_results.size)
135
+ assert_equal(1, box_a.instance_variable_get(:@results)[0].box_fitter.fit_results.size)
123
136
  assert_equal(2, box_b.children.size)
124
137
  assert_equal(1, box_b.start_number)
125
138
  end
@@ -127,20 +140,22 @@ describe HexaPDF::Layout::ListBox do
127
140
 
128
141
  describe "draw" do
129
142
  before do
130
- @canvas = HexaPDF::Document.new.pages.add.canvas
143
+ @canvas = @page.canvas
131
144
  draw_block = lambda {|canvas, box| }
132
145
  @fixed_size_boxes = 5.times.map { HexaPDF::Layout::Box.new(width: 20, height: 10, &draw_block) }
133
146
  end
134
147
 
135
148
  it "draws the result" do
136
- box = create_box(children: @fixed_size_boxes[0, 2])
149
+ box = create_box(children: @fixed_size_boxes[0, 2],
150
+ style: {font_size: 11, fill_color: 0.5})
137
151
  box.fit(100, 100, @frame)
138
152
  box.draw(@canvas, 0, 100 - box.height)
139
153
  operators = [
140
154
  [:save_graphics_state],
141
- [:set_font_and_size, [:F1, 10]],
155
+ [:set_font_and_size, [:F1, 11]],
156
+ [:set_device_gray_non_stroking_color, [0.5]],
142
157
  [:begin_text],
143
- [:set_text_matrix, [1, 0, 0, 1, 1.5, 93.17]],
158
+ [:set_text_matrix, [1, 0, 0, 1, 1.15, 92.487]],
144
159
  [:show_text, ["\x95".b]],
145
160
  [:end_text],
146
161
  [:restore_graphics_state],
@@ -149,9 +164,10 @@ describe HexaPDF::Layout::ListBox do
149
164
  [:restore_graphics_state],
150
165
 
151
166
  [:save_graphics_state],
152
- [:set_font_and_size, [:F1, 10]],
167
+ [:set_font_and_size, [:F1, 11]],
168
+ [:set_device_gray_non_stroking_color, [0.5]],
153
169
  [:begin_text],
154
- [:set_text_matrix, [1, 0, 0, 1, 1.5, 83.17]],
170
+ [:set_text_matrix, [1, 0, 0, 1, 1.15, 82.487]],
155
171
  [:show_text, ["\x95".b]],
156
172
  [:end_text],
157
173
  [:restore_graphics_state],
@@ -162,16 +178,18 @@ describe HexaPDF::Layout::ListBox do
162
178
  assert_operators(@canvas.contents, operators)
163
179
  end
164
180
 
165
- it "draws a cicle as marker" do
166
- box = create_box(children: @fixed_size_boxes[0, 1], item_type: :circle)
181
+ it "draws a circle as marker" do
182
+ box = create_box(children: @fixed_size_boxes[0, 1], item_type: :circle,
183
+ style: {font_size: 11, fill_color: 0.5})
167
184
  box.fit(100, 100, @frame)
168
185
  box.draw(@canvas, 0, 100 - box.height)
169
186
  operators = [
170
187
  [:save_graphics_state],
171
- [:set_font_and_size, [:F1, 5]],
172
- [:set_text_rise, [-5.555556]],
188
+ [:set_font_and_size, [:F1, 5.5]],
189
+ [:set_text_rise, [-6.111111]],
190
+ [:set_device_gray_non_stroking_color, [0.5]],
173
191
  [:begin_text],
174
- [:set_text_matrix, [1, 0, 0, 1, 0.635, 100]],
192
+ [:set_text_matrix, [1, 0, 0, 1, 0.1985, 100]],
175
193
  [:show_text, ["m".b]],
176
194
  [:end_text],
177
195
  [:restore_graphics_state],
@@ -183,15 +201,17 @@ describe HexaPDF::Layout::ListBox do
183
201
  end
184
202
 
185
203
  it "draws a square as marker" do
186
- box = create_box(children: @fixed_size_boxes[0, 1], item_type: :square)
204
+ box = create_box(children: @fixed_size_boxes[0, 1], item_type: :square,
205
+ style: {font_size: 11, fill_color: 0.5})
187
206
  box.fit(100, 100, @frame)
188
207
  box.draw(@canvas, 0, 100 - box.height)
189
208
  operators = [
190
209
  [:save_graphics_state],
191
- [:set_font_and_size, [:F1, 5]],
192
- [:set_text_rise, [-5.555556]],
210
+ [:set_font_and_size, [:F1, 5.5]],
211
+ [:set_text_rise, [-6.111111]],
212
+ [:set_device_gray_non_stroking_color, [0.5]],
193
213
  [:begin_text],
194
- [:set_text_matrix, [1, 0, 0, 1, 1.195, 100]],
214
+ [:set_text_matrix, [1, 0, 0, 1, 0.8145, 100]],
195
215
  [:show_text, ["n".b]],
196
216
  [:end_text],
197
217
  [:restore_graphics_state],
@@ -204,14 +224,16 @@ describe HexaPDF::Layout::ListBox do
204
224
 
205
225
  it "draws decimal numbers as marker" do
206
226
  box = create_box(children: @fixed_size_boxes[0, 2], item_type: :decimal,
227
+ style: {font_size: 11, fill_color: 0.5},
207
228
  content_indentation: 20)
208
229
  box.fit(100, 100, @frame)
209
230
  box.draw(@canvas, 0, 100 - box.height)
210
231
  operators = [
211
232
  [:save_graphics_state],
212
- [:set_font_and_size, [:F1, 10]],
233
+ [:set_font_and_size, [:F1, 11]],
234
+ [:set_device_gray_non_stroking_color, [0.5]],
213
235
  [:begin_text],
214
- [:set_text_matrix, [1, 0, 0, 1, 7.5, 93.17]],
236
+ [:set_text_matrix, [1, 0, 0, 1, 6.75, 92.487]],
215
237
  [:show_text, ["1.".b]],
216
238
  [:end_text],
217
239
  [:restore_graphics_state],
@@ -220,9 +242,10 @@ describe HexaPDF::Layout::ListBox do
220
242
  [:restore_graphics_state],
221
243
 
222
244
  [:save_graphics_state],
223
- [:set_font_and_size, [:F1, 10]],
245
+ [:set_font_and_size, [:F1, 11]],
246
+ [:set_device_gray_non_stroking_color, [0.5]],
224
247
  [:begin_text],
225
- [:set_text_matrix, [1, 0, 0, 1, 7.5, 83.17]],
248
+ [:set_text_matrix, [1, 0, 0, 1, 6.75, 82.487]],
226
249
  [:show_text, ["2.".b]],
227
250
  [:end_text],
228
251
  [:restore_graphics_state],
@@ -272,11 +295,5 @@ describe HexaPDF::Layout::ListBox do
272
295
  ]
273
296
  assert_operators(@canvas.contents, operators)
274
297
  end
275
-
276
- it "fails for unknown item types" do
277
- box = create_box(children: @fixed_size_boxes[0, 1], item_type: :unknown)
278
- box.fit(100, 100, @frame)
279
- assert_raises(HexaPDF::Error) { box.draw(@canvas, 0, 0) }
280
- end
281
298
  end
282
299
  end
@@ -580,7 +580,10 @@ describe HexaPDF::Layout::Style::LinkLayer do
580
580
  it "fails if more than one possible target is chosen" do
581
581
  assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(dest: true, uri: true) }
582
582
  assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(dest: true, file: true) }
583
+ assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(dest: true, action: true) }
583
584
  assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(uri: true, file: true) }
585
+ assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(uri: true, action: true) }
586
+ assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(file: true, action: true) }
584
587
  end
585
588
 
586
589
  it "fails if an invalid border is provided" do
@@ -656,6 +659,12 @@ describe HexaPDF::Layout::Style::LinkLayer do
656
659
  assert_nil(annot[:Dest])
657
660
  end
658
661
 
662
+ it "works for actions" do
663
+ annot = call_link(action: {Type: :Action, S: :SetOCGState})
664
+ assert_equal({Type: :Action, S: :SetOCGState}, annot[:A].value)
665
+ assert_nil(annot[:Dest])
666
+ end
667
+
659
668
  it "works for destinations set via the 'link' custom box property" do
660
669
  @box.properties['link'] = [@canvas.context, :FitH]
661
670
  annot = call_link({})
@@ -773,6 +782,7 @@ describe HexaPDF::Layout::Style do
773
782
  refute(@style.subscript)
774
783
  refute(@style.superscript)
775
784
  refute(@style.last_line_gap)
785
+ refute(@style.fill_horizontal)
776
786
  assert_kind_of(HexaPDF::Layout::Style::Layers, @style.underlays)
777
787
  assert_kind_of(HexaPDF::Layout::Style::Layers, @style.overlays)
778
788
  end