hexapdf 0.44.0 → 0.46.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +106 -47
  3. data/examples/019-acro_form.rb +5 -0
  4. data/examples/027-composer_optional_content.rb +6 -4
  5. data/examples/030-pdfa.rb +12 -11
  6. data/lib/hexapdf/cli/inspect.rb +5 -0
  7. data/lib/hexapdf/composer.rb +23 -1
  8. data/lib/hexapdf/configuration.rb +8 -0
  9. data/lib/hexapdf/content/canvas.rb +3 -3
  10. data/lib/hexapdf/content/canvas_composer.rb +1 -0
  11. data/lib/hexapdf/digital_signature/cms_handler.rb +31 -3
  12. data/lib/hexapdf/digital_signature/signing/default_handler.rb +9 -1
  13. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +5 -1
  14. data/lib/hexapdf/document/layout.rb +63 -30
  15. data/lib/hexapdf/document.rb +24 -2
  16. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  17. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  18. data/lib/hexapdf/importer.rb +15 -5
  19. data/lib/hexapdf/layout/box.rb +48 -36
  20. data/lib/hexapdf/layout/column_box.rb +3 -11
  21. data/lib/hexapdf/layout/container_box.rb +4 -4
  22. data/lib/hexapdf/layout/frame.rb +7 -6
  23. data/lib/hexapdf/layout/inline_box.rb +17 -23
  24. data/lib/hexapdf/layout/list_box.rb +27 -42
  25. data/lib/hexapdf/layout/page_style.rb +23 -16
  26. data/lib/hexapdf/layout/style.rb +5 -5
  27. data/lib/hexapdf/layout/table_box.rb +14 -10
  28. data/lib/hexapdf/layout/text_box.rb +60 -36
  29. data/lib/hexapdf/layout/text_fragment.rb +1 -1
  30. data/lib/hexapdf/layout/text_layouter.rb +7 -8
  31. data/lib/hexapdf/parser.rb +5 -1
  32. data/lib/hexapdf/rectangle.rb +4 -4
  33. data/lib/hexapdf/revisions.rb +1 -1
  34. data/lib/hexapdf/stream.rb +3 -3
  35. data/lib/hexapdf/tokenizer.rb +3 -2
  36. data/lib/hexapdf/type/acro_form/button_field.rb +2 -0
  37. data/lib/hexapdf/type/acro_form/choice_field.rb +2 -0
  38. data/lib/hexapdf/type/acro_form/field.rb +8 -0
  39. data/lib/hexapdf/type/acro_form/form.rb +2 -1
  40. data/lib/hexapdf/type/acro_form/text_field.rb +2 -0
  41. data/lib/hexapdf/type/form.rb +2 -2
  42. data/lib/hexapdf/version.rb +1 -1
  43. data/test/hexapdf/content/test_canvas_composer.rb +13 -8
  44. data/test/hexapdf/digital_signature/common.rb +66 -84
  45. data/test/hexapdf/digital_signature/signing/test_default_handler.rb +7 -0
  46. data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +9 -0
  47. data/test/hexapdf/digital_signature/test_cms_handler.rb +41 -1
  48. data/test/hexapdf/digital_signature/test_handler.rb +2 -1
  49. data/test/hexapdf/document/test_layout.rb +44 -5
  50. data/test/hexapdf/layout/test_box.rb +23 -5
  51. data/test/hexapdf/layout/test_frame.rb +21 -2
  52. data/test/hexapdf/layout/test_inline_box.rb +17 -28
  53. data/test/hexapdf/layout/test_list_box.rb +8 -8
  54. data/test/hexapdf/layout/test_page_style.rb +7 -2
  55. data/test/hexapdf/layout/test_table_box.rb +8 -1
  56. data/test/hexapdf/layout/test_text_box.rb +51 -29
  57. data/test/hexapdf/layout/test_text_layouter.rb +0 -3
  58. data/test/hexapdf/test_composer.rb +14 -5
  59. data/test/hexapdf/test_document.rb +27 -0
  60. data/test/hexapdf/test_importer.rb +17 -0
  61. data/test/hexapdf/test_revisions.rb +54 -41
  62. data/test/hexapdf/test_serializer.rb +1 -0
  63. data/test/hexapdf/type/acro_form/test_form.rb +9 -0
  64. metadata +2 -2
@@ -65,13 +65,13 @@ describe HexaPDF::Layout::ListBox do
65
65
  describe "fit" do
66
66
  [:default, :flow].each do |position|
67
67
  it "respects the set initial width, position #{position}" do
68
- box = create_box(children: @text_boxes[0, 2], width: 50, style: {position: position})
69
- check_box(box, 50, 80)
68
+ box = create_box(children: @text_boxes[0, 2], width: 55, style: {position: position})
69
+ check_box(box, 55, 80)
70
70
  end
71
71
 
72
72
  it "respects the set initial height, position #{position}" do
73
- box = create_box(children: @text_boxes[0, 2], height: 50, style: {position: position})
74
- check_box(box, 100, 40)
73
+ box = create_box(children: @text_boxes[0, 2], height: 55, style: {position: position})
74
+ check_box(box, 100, 55)
75
75
  end
76
76
 
77
77
  it "respects the set initial height even when it doesn't fit completely" do
@@ -123,7 +123,7 @@ describe HexaPDF::Layout::ListBox do
123
123
 
124
124
  it "fails if not even a part of the first list item fits" do
125
125
  box = create_box(children: @text_boxes[0, 2], height: 5)
126
- check_box(box, 100, 0, status: :failure)
126
+ check_box(box, 100, 5, status: :failure)
127
127
  end
128
128
 
129
129
  it "fails for unknown marker types" do
@@ -134,13 +134,13 @@ describe HexaPDF::Layout::ListBox do
134
134
 
135
135
  describe "split" do
136
136
  it "splits before a list item if no part of it will fit" do
137
- box = create_box(children: @text_boxes[0, 2])
138
- assert(box.fit(100, 20, @frame).overflow?)
137
+ box = create_box(children: @text_boxes[0, 3])
138
+ assert(box.fit(100, 22, @frame).overflow?)
139
139
  box_a, box_b = box.split
140
140
  assert_same(box, box_a)
141
141
  assert_equal(:show_first_marker, box_b.split_box?)
142
142
  assert_equal(1, box_a.instance_variable_get(:@results)[0].box_fitter.fit_results.size)
143
- assert_equal(1, box_b.children.size)
143
+ assert_equal(2, box_b.children.size)
144
144
  assert_equal(2, box_b.start_number)
145
145
  end
146
146
 
@@ -44,8 +44,13 @@ describe HexaPDF::Layout::PageStyle do
44
44
 
45
45
  it "works when no template is set" do
46
46
  style = HexaPDF::Layout::PageStyle.new
47
- page = style.create_page(@doc)
48
- assert_equal("", page.contents)
47
+ page1 = style.create_page(@doc)
48
+ frame1 = style.frame
49
+ assert_equal("", page1.contents)
50
+ assert_equal(523.275591, style.frame.width)
51
+
52
+ page2 = style.create_page(@doc)
53
+ refute_same(frame1, style.frame)
49
54
  end
50
55
 
51
56
  it "creates a default frame if none is set beforehand or during template execution" do
@@ -116,7 +116,14 @@ describe HexaPDF::Layout::TableBox::Cell do
116
116
  assert_equal(12, cell.preferred_height)
117
117
  end
118
118
 
119
- it "doesn't fit anything if the available width or height are too small" do
119
+ it "doesn't fit children that are too big" do
120
+ cell = create_cell(children: HexaPDF::Layout::Box.create(width: 300, height: 20))
121
+ assert(cell.fit(100, 100, @frame).failure?)
122
+ cell = create_cell(children: [HexaPDF::Layout::Box.create(width: 300, height: 20)])
123
+ assert(cell.fit(100, 100, @frame).failure?)
124
+ end
125
+
126
+ it "doesn't fit anything if the available width or height are too small even if there are no children" do
120
127
  cell = create_cell(children: nil)
121
128
  assert(cell.fit(10, 100, @frame).failure?)
122
129
  assert(cell.fit(100, 10, @frame).failure?)
@@ -42,31 +42,32 @@ describe HexaPDF::Layout::TextBox do
42
42
  end
43
43
 
44
44
  it "respects the set width and height" do
45
- box = create_box([@inline_box], width: 40, height: 50, style: {padding: 10})
45
+ box = create_box([@inline_box] * 5, width: 44, height: 50,
46
+ style: {padding: 10, text_align: :right, text_valign: :bottom})
46
47
  assert(box.fit(100, 100, @frame).success?)
47
- assert_equal(40, box.width)
48
+ assert_equal(44, box.width)
48
49
  assert_equal(50, box.height)
49
- assert_equal([10], box.instance_variable_get(:@result).lines.map(&:width))
50
+ assert_equal([20, 20, 10], box.instance_variable_get(:@result).lines.map(&:width))
50
51
  end
51
52
 
52
- it "fits into the frame's outline" do
53
- @frame.remove_area(Geom2D::Rectangle(0, 80, 20, 20))
54
- @frame.remove_area(Geom2D::Rectangle(80, 70, 20, 20))
55
- box = create_box([@inline_box] * 20, style: {position: :flow})
56
- assert(box.fit(100, 100, @frame).success?)
57
- assert_equal(100, box.width)
58
- assert_equal(30, box.height)
59
- end
53
+ describe "style option last_line_gap" do
54
+ it "is taken into account" do
55
+ box = create_box([@inline_box] * 5, style: {last_line_gap: true, line_spacing: :double})
56
+ assert(box.fit(100, 100, @frame).success?)
57
+ assert_equal(50, box.width)
58
+ assert_equal(20, box.height)
59
+ end
60
60
 
61
- it "takes the style option last_line_gap into account" do
62
- box = create_box([@inline_box] * 5, style: {last_line_gap: true, line_spacing: :double})
63
- assert(box.fit(100, 100, @frame).success?)
64
- assert_equal(50, box.width)
65
- assert_equal(20, box.height)
61
+ it "will have no effect for fixed-height boxes" do
62
+ box = create_box([@inline_box] * 5, height: 40, style: {last_line_gap: true, line_spacing: :double})
63
+ assert(box.fit(100, 100, @frame).success?)
64
+ assert_equal(50, box.width)
65
+ assert_equal(40, box.height)
66
+ end
66
67
  end
67
68
 
68
- it "uses the whole available width when aligning to the center or right" do
69
- [:center, :right].each do |align|
69
+ it "uses the whole available width when aligning to the center, right or justified" do
70
+ [:center, :right, :justify].each do |align|
70
71
  box = create_box([@inline_box], style: {text_align: align})
71
72
  assert(box.fit(100, 100, @frame).success?)
72
73
  assert_equal(100, box.width)
@@ -103,6 +104,33 @@ describe HexaPDF::Layout::TextBox do
103
104
  assert(box.fit(100, 100, @frame).success?)
104
105
  end
105
106
 
107
+ describe "position :flow" do
108
+ it "fits into the frame's outline" do
109
+ @frame.remove_area(Geom2D::Rectangle(0, 80, 20, 20))
110
+ @frame.remove_area(Geom2D::Rectangle(80, 70, 20, 20))
111
+ box = create_box([@inline_box] * 20, style: {position: :flow})
112
+ assert(box.fit(100, 100, @frame).success?)
113
+ assert_equal(100, box.width)
114
+ assert_equal(30, box.height)
115
+ end
116
+
117
+ it "respects a set initial height" do
118
+ box = create_box([@inline_box] * 20, height: 13, style: {position: :flow})
119
+ assert(box.fit(100, 100, @frame).overflow?)
120
+ assert_equal(100, box.width)
121
+ assert_equal(13, box.height)
122
+ end
123
+
124
+ it "respects top/bottom padding/border" do
125
+ @frame.remove_area(Geom2D::Rectangle(0, 80, 20, 20))
126
+ box = create_box([@inline_box] * 20, style: {position: :flow, padding: 10, border: {width: 2}})
127
+ assert(box.fit(100, 100, @frame).success?)
128
+ assert_equal(124, box.width)
129
+ assert_equal(54, box.height)
130
+ assert_equal([80, 100, 20], box.instance_variable_get(:@result).lines.map(&:width))
131
+ end
132
+ end
133
+
106
134
  it "fails if no item of the text box fits due to the width" do
107
135
  box = create_box([@inline_box])
108
136
  assert(box.fit(5, 20, @frame).failure?)
@@ -150,15 +178,12 @@ describe HexaPDF::Layout::TextBox do
150
178
  assert_operators(@canvas.contents, [[:save_graphics_state],
151
179
  [:restore_graphics_state],
152
180
  [:save_graphics_state],
153
- [:concatenate_matrix, [1, 0, 0, 1, 5, 10]],
154
- [:save_graphics_state],
155
- [:append_rectangle, [0, 0, 10, 10]],
181
+ [:append_rectangle, [5, 10, 10, 10]],
156
182
  [:clip_path_non_zero],
157
183
  [:end_path],
158
- [:append_rectangle, [0.5, 0.5, 9.0, 9.0]],
184
+ [:append_rectangle, [5.5, 10.5, 9.0, 9.0]],
159
185
  [:stroke_path],
160
186
  [:restore_graphics_state],
161
- [:restore_graphics_state],
162
187
  [:save_graphics_state],
163
188
  [:restore_graphics_state]])
164
189
  end
@@ -167,21 +192,18 @@ describe HexaPDF::Layout::TextBox do
167
192
  @frame.remove_area(Geom2D::Rectangle(0, 0, 40, 100))
168
193
  box = create_box([@inline_box], style: {position: :flow, border: {width: 1}})
169
194
  box.fit(60, 100, @frame)
170
- box.draw(@canvas, 0, 90)
195
+ box.draw(@canvas, 40, 88)
171
196
  assert_operators(@canvas.contents, [[:save_graphics_state],
172
- [:append_rectangle, [40, 90, 10, 10]],
197
+ [:append_rectangle, [40, 88, 12, 12]],
173
198
  [:clip_path_non_zero],
174
199
  [:end_path],
175
- [:append_rectangle, [40.5, 90.5, 9.0, 9.0]],
200
+ [:append_rectangle, [40.5, 88.5, 11.0, 11.0]],
176
201
  [:stroke_path],
177
202
  [:restore_graphics_state],
178
203
  [:save_graphics_state],
179
204
  [:restore_graphics_state],
180
205
  [:save_graphics_state],
181
206
  [:concatenate_matrix, [1, 0, 0, 1, 41, 89]],
182
- [:save_graphics_state],
183
- [:concatenate_matrix, [1, 0, 0, 1, 0, 0]],
184
- [:restore_graphics_state],
185
207
  [:restore_graphics_state],
186
208
  [:save_graphics_state],
187
209
  [:restore_graphics_state]])
@@ -812,11 +812,8 @@ describe HexaPDF::Layout::TextLayouter do
812
812
  [:restore_graphics_state],
813
813
  [:save_graphics_state],
814
814
  [:concatenate_matrix, [1, 0, 0, 1, 10, -40]],
815
- [:save_graphics_state],
816
- [:concatenate_matrix, [1, 0, 0, 1, 0, 0]],
817
815
  [:set_line_width, [2]],
818
816
  [:restore_graphics_state],
819
- [:restore_graphics_state],
820
817
  [:save_graphics_state],
821
818
  [:restore_graphics_state]])
822
819
  end
@@ -119,6 +119,13 @@ describe HexaPDF::Composer do
119
119
  end
120
120
  end
121
121
 
122
+ describe "styles" do
123
+ it "delegates to layout.styles" do
124
+ @composer.styles(base: {font_size: 30}, other: {font_size: 40})
125
+ assert_equal([:base, :other], @composer.document.layout.styles.keys)
126
+ end
127
+ end
128
+
122
129
  describe "page_style" do
123
130
  it "returns the page style if no argument or block is given" do
124
131
  page_style = @composer.page_style(:default)
@@ -225,8 +232,9 @@ describe HexaPDF::Composer do
225
232
  first_page_contents = @composer.canvas.contents
226
233
  @composer.draw_box(create_box(height: 400))
227
234
 
228
- box = create_box(height: 400)
229
- box.define_singleton_method(:split) do |*|
235
+ box = create_box
236
+ box.define_singleton_method(:fit_content) {|*| fit_result.overflow! }
237
+ box.define_singleton_method(:split_content) do |*|
230
238
  [box, HexaPDF::Layout::Box.new(height: 100) {}]
231
239
  end
232
240
  @composer.draw_box(box)
@@ -235,7 +243,7 @@ describe HexaPDF::Composer do
235
243
  [:concatenate_matrix, [1, 0, 0, 1, 36, 405.889764]],
236
244
  [:restore_graphics_state],
237
245
  [:save_graphics_state],
238
- [:concatenate_matrix, [1, 0, 0, 1, 36, 5.889764]],
246
+ [:concatenate_matrix, [1, 0, 0, 1, 36, 36]],
239
247
  [:restore_graphics_state]])
240
248
  assert_operators(@composer.canvas.contents,
241
249
  [[:save_graphics_state],
@@ -273,9 +281,10 @@ describe HexaPDF::Composer do
273
281
  box = create_box(height: 400)
274
282
  assert_same(box, @composer.draw_box(box))
275
283
 
276
- box = create_box(height: 400)
277
284
  split_box = create_box(height: 100)
278
- box.define_singleton_method(:split) {|*| [box, split_box] }
285
+ box = create_box
286
+ box.define_singleton_method(:fit_content) {|*| fit_result.overflow! }
287
+ box.define_singleton_method(:split_content) {|*| [box, split_box] }
279
288
  assert_same(split_box, @composer.draw_box(box))
280
289
  end
281
290
 
@@ -568,4 +568,31 @@ describe HexaPDF::Document do
568
568
  it "can be inspected and the output is not too large" do
569
569
  assert_match(/HexaPDF::Document:\d+/, @doc.inspect)
570
570
  end
571
+
572
+ describe "duplicate" do
573
+ it "creates an in-memory copy" do
574
+ doc = HexaPDF::Document.new
575
+ doc.pages.add.canvas.line_width(10)
576
+ doc.trailer.info[:Author] = 'HexaPDF'
577
+ doc.dispatch_message(:complete_objects)
578
+
579
+ dupped = doc.duplicate
580
+ assert_equal('HexaPDF', dupped.trailer.info[:Author])
581
+ doc.pages[0].canvas.line_cap_style(:round)
582
+ assert_equal("10 w\n", dupped.pages[0].contents)
583
+ end
584
+
585
+ it "doesn't copy the encryption state" do
586
+ doc = HexaPDF::Document.new
587
+ doc.pages.add.canvas.line_width(10)
588
+ doc.encrypt
589
+ io = StringIO.new
590
+ doc.write(io)
591
+
592
+ doc = HexaPDF::Document.new(io: io)
593
+ dupped = doc.duplicate
594
+ assert_equal("10 w\n", dupped.pages[0].contents)
595
+ refute(dupped.encrypted?)
596
+ end
597
+ end
571
598
  end
@@ -47,6 +47,13 @@ describe HexaPDF::Importer do
47
47
  refute_same(obj1, obj2)
48
48
  refute_same(obj1[:ref], obj2[:ref])
49
49
  end
50
+
51
+ it "duplicates the whole document" do
52
+ trailer = HexaPDF::Importer.copy(@dest, @source.trailer, allow_all: true)
53
+ refute_same(@source.catalog, trailer[:Root])
54
+ refute_same(@source.pages.root, trailer[:Root][:Pages])
55
+ assert_equal(90, trailer[:Root][:Pages][:Kids][0][:Rotate])
56
+ end
50
57
  end
51
58
 
52
59
  describe "import" do
@@ -121,6 +128,16 @@ describe HexaPDF::Importer do
121
128
  refute_same(dst_obj.data.stream, src_obj.data.stream)
122
129
  end
123
130
 
131
+ it "duplicates the stream if it is a FiberDoubleForString, e.g. when using Canvas" do
132
+ src_page = @source.pages[0]
133
+ src_page.canvas.line_width(10)
134
+ dst_page = @importer.import(src_page)
135
+ refute_same(dst_page, src_page)
136
+ refute_same(dst_page[:Contents].data.stream, src_page[:Contents].data.stream)
137
+ src_page.canvas.line_width(20)
138
+ assert_equal("10 w\n", dst_page.contents)
139
+ end
140
+
124
141
  it "does not import objects of type Catalog or Pages" do
125
142
  @obj[:catalog] = @source.catalog
126
143
  @obj[:pages] = @source.catalog.pages
@@ -357,46 +357,59 @@ describe HexaPDF::Revisions do
357
357
  end
358
358
  end
359
359
 
360
- it "merges the two revisions of a linearized PDF into one" do
361
- io = StringIO.new(<<~EOF)
362
- %PDF-1.2
363
- 5 0 obj
364
- <</Linearized 1>>
365
- endobj
366
- xref
367
- 5 1
368
- 0000000009 00000 n
369
- trailer
370
- <</ID[(a)(b)]/Info 1 0 R/Root 2 0 R/Size 6/Prev 394>>
371
- %
372
- 1 0 obj
373
- <</ModDate(D:20221205233910+01'00')/Producer(HexaPDF version 0.27.0)>>
374
- endobj
375
- 2 0 obj
376
- <</Type/Catalog/Pages 3 0 R>>
377
- endobj
378
- 3 0 obj
379
- <</Type/Pages/Kids[4 0 R]/Count 1>>
380
- endobj
381
- 4 0 obj
382
- <</Type/Page/MediaBox[0 0 595 842]/Parent 3 0 R/Resources<<>>>>
383
- endobj
384
- xref
385
- 0 5
386
- 0000000000 65535 f
387
- 0000000133 00000 n
388
- 0000000219 00000 n
389
- 0000000264 00000 n
390
- 0000000315 00000 n
391
- trailer
392
- <</ID[(a)(b)]/Info 1 0 R/Root 2 0 R/Size 5>>
393
- startxref
394
- 41
395
- %%EOF
396
- EOF
397
- doc = HexaPDF::Document.new(io: io, config: {'parser.try_xref_reconstruction' => false})
398
- assert(doc.revisions.parser.linearized?)
399
- assert_equal(1, doc.revisions.count)
400
- assert_same(5, doc.revisions.current.xref_section.max_oid)
360
+ describe "linearzied PDFs" do
361
+ before do
362
+ @io = StringIO.new(<<~EOF)
363
+ %PDF-1.2
364
+ 5 0 obj
365
+ <</Linearized 1>>
366
+ endobj
367
+ xref
368
+ 5 1
369
+ 0000000009 00000 n
370
+ trailer
371
+ <</ID[(a)(b)]/Info 1 0 R/Root 2 0 R/Size 6/Prev 394>>
372
+ %
373
+ 1 0 obj
374
+ <</ModDate(D:20221205233910+01'00')/Producer(HexaPDF version 0.27.0)>>
375
+ endobj
376
+ 2 0 obj
377
+ <</Type/Catalog/Pages 3 0 R>>
378
+ endobj
379
+ 3 0 obj
380
+ <</Type/Pages/Kids[4 0 R]/Count 1>>
381
+ endobj
382
+ 4 0 obj
383
+ <</Type/Page/MediaBox[0 0 595 842]/Parent 3 0 R/Resources<<>>>>
384
+ endobj
385
+ xref
386
+ 0 5
387
+ 0000000000 65535 f
388
+ 0000000133 00000 n
389
+ 0000000219 00000 n
390
+ 0000000264 00000 n
391
+ 0000000315 00000 n
392
+ trailer
393
+ <</ID[(a)(b)]/Info 1 0 R/Root 2 0 R/Size 5>>
394
+ startxref
395
+ 41
396
+ %%EOF
397
+ EOF
398
+ end
399
+
400
+ it "merges the two revisions of a linearized PDF into one" do
401
+ doc = HexaPDF::Document.new(io: @io, config: {'parser.try_xref_reconstruction' => false})
402
+ assert(doc.revisions.parser.linearized?)
403
+ assert_equal(1, doc.revisions.count)
404
+ assert_same(5, doc.revisions.current.xref_section.max_oid)
405
+ end
406
+
407
+ it "works for a fake linearized PDF where the first xref section isn't actually used" do
408
+ @io.string[-9..-1] = "394\n%%EOF\n"
409
+ doc = HexaPDF::Document.new(io: @io, config: {'parser.try_xref_reconstruction' => false})
410
+ assert(doc.revisions.parser.linearized?)
411
+ assert_equal(1, doc.revisions.count)
412
+ assert_same(4, doc.revisions.current.xref_section.max_oid)
413
+ end
401
414
  end
402
415
  end
@@ -89,6 +89,7 @@ describe HexaPDF::Serializer do
89
89
  assert_serialized('/ ', :"")
90
90
  assert_serialized('/H#c3#b6#c3#9fgang', :Hößgang)
91
91
  assert_serialized('/H#e8lp', "H\xE8lp".force_encoding('BINARY').intern)
92
+ assert_serialized('/#00#09#0a#0c#0d#20', :"\x00\t\n\f\r ")
92
93
  end
93
94
 
94
95
  it "serializes arrays" do
@@ -274,6 +274,15 @@ describe HexaPDF::Type::AcroForm::Form do
274
274
  assert(obj.null?)
275
275
  end
276
276
 
277
+ it "deletes a field with an embedded widget annotation" do
278
+ widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
279
+ assert_equal(widget, @field)
280
+ refute(@doc.pages[0][:Annots].empty?)
281
+ @acro_form.delete_field(@field)
282
+ assert(@doc.pages[0][:Annots].empty?)
283
+ assert(@field.null?)
284
+ end
285
+
277
286
  it "deletes all widget annotations from the document and the annotation array" do
278
287
  widget1 = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
279
288
  widget2 = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hexapdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.44.0
4
+ version: 0.46.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Leitner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-05 00:00:00.000000000 Z
11
+ date: 2024-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse