hexapdf 0.33.0 → 0.34.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 (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