hexapdf 0.45.0 → 0.47.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +120 -47
- data/examples/019-acro_form.rb +5 -0
- data/lib/hexapdf/cli/inspect.rb +5 -0
- data/lib/hexapdf/composer.rb +1 -1
- data/lib/hexapdf/configuration.rb +19 -0
- data/lib/hexapdf/digital_signature/cms_handler.rb +31 -3
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +9 -1
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +5 -1
- data/lib/hexapdf/document/layout.rb +48 -27
- data/lib/hexapdf/document.rb +24 -2
- data/lib/hexapdf/encryption/standard_security_handler.rb +32 -26
- data/lib/hexapdf/importer.rb +15 -5
- data/lib/hexapdf/layout/box.rb +25 -28
- data/lib/hexapdf/layout/frame.rb +1 -1
- data/lib/hexapdf/layout/inline_box.rb +17 -23
- data/lib/hexapdf/layout/list_box.rb +24 -29
- data/lib/hexapdf/layout/page_style.rb +23 -16
- data/lib/hexapdf/layout/style.rb +2 -2
- data/lib/hexapdf/layout/table_box.rb +57 -10
- data/lib/hexapdf/layout/text_box.rb +2 -6
- data/lib/hexapdf/parser.rb +5 -1
- data/lib/hexapdf/revisions.rb +1 -1
- data/lib/hexapdf/stream.rb +3 -3
- data/lib/hexapdf/task/optimize.rb +4 -4
- data/lib/hexapdf/tokenizer.rb +3 -2
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +8 -4
- data/lib/hexapdf/type/acro_form/button_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/choice_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/field.rb +8 -0
- data/lib/hexapdf/type/acro_form/form.rb +10 -6
- data/lib/hexapdf/type/acro_form/signature_field.rb +2 -1
- data/lib/hexapdf/type/acro_form/text_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +11 -3
- data/lib/hexapdf/type/annotations/widget.rb +4 -2
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +1 -0
- data/test/data/standard-security-handler/bothpwd-aes-256bit-V5-R5.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-aes-256bit-V5-R5.pdf +44 -0
- data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5-R5.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-256bit-V5-R5.pdf +0 -0
- data/test/hexapdf/digital_signature/common.rb +66 -84
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +7 -0
- data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +9 -0
- data/test/hexapdf/digital_signature/test_cms_handler.rb +41 -1
- data/test/hexapdf/digital_signature/test_handler.rb +2 -1
- data/test/hexapdf/digital_signature/test_signatures.rb +4 -4
- data/test/hexapdf/document/test_layout.rb +28 -5
- data/test/hexapdf/encryption/test_standard_security_handler.rb +5 -2
- data/test/hexapdf/layout/test_box.rb +12 -5
- data/test/hexapdf/layout/test_frame.rb +12 -2
- data/test/hexapdf/layout/test_inline_box.rb +17 -28
- data/test/hexapdf/layout/test_list_box.rb +5 -5
- data/test/hexapdf/layout/test_page_style.rb +7 -2
- data/test/hexapdf/layout/test_table_box.rb +52 -0
- data/test/hexapdf/layout/test_text_box.rb +3 -9
- data/test/hexapdf/layout/test_text_layouter.rb +0 -3
- data/test/hexapdf/task/test_optimize.rb +2 -0
- data/test/hexapdf/test_document.rb +30 -3
- data/test/hexapdf/test_importer.rb +24 -0
- data/test/hexapdf/test_revisions.rb +54 -41
- data/test/hexapdf/test_writer.rb +11 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +22 -5
- data/test/hexapdf/type/acro_form/test_form.rb +9 -5
- data/test/hexapdf/type/acro_form/test_signature_field.rb +3 -1
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +14 -1
- data/test/hexapdf/type/annotations/test_widget.rb +4 -0
- metadata +6 -2
|
@@ -511,6 +511,58 @@ describe HexaPDF::Layout::TableBox do
|
|
|
511
511
|
[0, 66, 39.75, 22], [39.75, 66, 39.75, 22], [79.5, 44, 39.75, 44], [119.25, 66, 39.75, 22]])
|
|
512
512
|
end
|
|
513
513
|
|
|
514
|
+
describe "row spans" do
|
|
515
|
+
# ----------
|
|
516
|
+
# | a | b |
|
|
517
|
+
# | a | |
|
|
518
|
+
# | a |----|
|
|
519
|
+
# | a | c |
|
|
520
|
+
# | a | |
|
|
521
|
+
# ----------
|
|
522
|
+
it "works if content of a row span cell is larger than the rows" do
|
|
523
|
+
cells = [[{row_span: 2, content: @fixed_size_boxes[0..2]}, @fixed_size_boxes[3]],
|
|
524
|
+
[@fixed_size_boxes[4]]]
|
|
525
|
+
check_box(create_box(cells: cells, cell_style: {padding: 0, border: {width: 0}}),
|
|
526
|
+
:success, 160, 30,
|
|
527
|
+
[[0, 0, 80, 30], [80, 0, 80, 15], [0, 0, 80, 30], [80, 15, 80, 15]])
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
# ----------
|
|
531
|
+
# | a | b |
|
|
532
|
+
# | |----|
|
|
533
|
+
# | | c |
|
|
534
|
+
# ----------
|
|
535
|
+
it "works if content of a row span cell is smaller than the rows" do
|
|
536
|
+
cells = [[{row_span: 2, content: @fixed_size_boxes[0]}, @fixed_size_boxes[3]],
|
|
537
|
+
[@fixed_size_boxes[4]]]
|
|
538
|
+
check_box(create_box(cells: cells, cell_style: {padding: 0, border: {width: 0}}),
|
|
539
|
+
:success, 160, 20,
|
|
540
|
+
[[0, 0, 80, 20], [80, 0, 80, 10], [0, 0, 80, 20], [80, 10, 80, 10]])
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
# -----------------
|
|
544
|
+
# | a | b | c | d |
|
|
545
|
+
# | a | b |---| d |
|
|
546
|
+
# | a | b | e | |
|
|
547
|
+
# | a | | e | |
|
|
548
|
+
# --------| e | |
|
|
549
|
+
# | f | g | e | |
|
|
550
|
+
# ----------------
|
|
551
|
+
it "works if multiple, possibly overlapping row spans are involved" do
|
|
552
|
+
cells = [[{row_span: 2, content: @fixed_size_boxes[0..2]},
|
|
553
|
+
{row_span: 2, content: @fixed_size_boxes[3..4]},
|
|
554
|
+
@fixed_size_boxes[5],
|
|
555
|
+
{row_span: 3, content: @fixed_size_boxes[6..7]}],
|
|
556
|
+
[{row_span: 2, content: @fixed_size_boxes[8, 3]}],
|
|
557
|
+
[@fixed_size_boxes[11], @fixed_size_boxes[12]]]
|
|
558
|
+
check_box(create_box(cells: cells, cell_style: {padding: 0, border: {width: 0}}),
|
|
559
|
+
:success, 160, 40,
|
|
560
|
+
[[0, 0, 40, 30], [40, 0, 40, 30], [80, 0, 40, 10], [120, 0, 40, 40],
|
|
561
|
+
[0, 0, 40, 30], [40, 0, 40, 30], [80, 10, 40, 30], [120, 0, 40, 40],
|
|
562
|
+
[0, 30, 40, 10], [40, 30, 40, 10], [80, 10, 40, 30], [120, 0, 40, 40]])
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
|
|
514
566
|
it "fits a table with header rows" do
|
|
515
567
|
result = [[0, 0, 80, 10], [80, 0, 80, 10], [0, 10, 80, 10], [80, 10, 80, 10]]
|
|
516
568
|
header = lambda {|_| [@fixed_size_boxes[10, 2], @fixed_size_boxes[12, 2]] }
|
|
@@ -178,15 +178,12 @@ describe HexaPDF::Layout::TextBox do
|
|
|
178
178
|
assert_operators(@canvas.contents, [[:save_graphics_state],
|
|
179
179
|
[:restore_graphics_state],
|
|
180
180
|
[:save_graphics_state],
|
|
181
|
-
[:
|
|
182
|
-
[:save_graphics_state],
|
|
183
|
-
[:append_rectangle, [0, 0, 10, 10]],
|
|
181
|
+
[:append_rectangle, [5, 10, 10, 10]],
|
|
184
182
|
[:clip_path_non_zero],
|
|
185
183
|
[:end_path],
|
|
186
|
-
[:append_rectangle, [
|
|
184
|
+
[:append_rectangle, [5.5, 10.5, 9.0, 9.0]],
|
|
187
185
|
[:stroke_path],
|
|
188
186
|
[:restore_graphics_state],
|
|
189
|
-
[:restore_graphics_state],
|
|
190
187
|
[:save_graphics_state],
|
|
191
188
|
[:restore_graphics_state]])
|
|
192
189
|
end
|
|
@@ -195,7 +192,7 @@ describe HexaPDF::Layout::TextBox do
|
|
|
195
192
|
@frame.remove_area(Geom2D::Rectangle(0, 0, 40, 100))
|
|
196
193
|
box = create_box([@inline_box], style: {position: :flow, border: {width: 1}})
|
|
197
194
|
box.fit(60, 100, @frame)
|
|
198
|
-
box.draw(@canvas,
|
|
195
|
+
box.draw(@canvas, 40, 88)
|
|
199
196
|
assert_operators(@canvas.contents, [[:save_graphics_state],
|
|
200
197
|
[:append_rectangle, [40, 88, 12, 12]],
|
|
201
198
|
[:clip_path_non_zero],
|
|
@@ -207,9 +204,6 @@ describe HexaPDF::Layout::TextBox do
|
|
|
207
204
|
[:restore_graphics_state],
|
|
208
205
|
[:save_graphics_state],
|
|
209
206
|
[:concatenate_matrix, [1, 0, 0, 1, 41, 89]],
|
|
210
|
-
[:save_graphics_state],
|
|
211
|
-
[:concatenate_matrix, [1, 0, 0, 1, 0, 0]],
|
|
212
|
-
[:restore_graphics_state],
|
|
213
207
|
[:restore_graphics_state],
|
|
214
208
|
[:save_graphics_state],
|
|
215
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
|
|
@@ -8,6 +8,7 @@ describe HexaPDF::Task::Optimize do
|
|
|
8
8
|
class TestType < HexaPDF::Dictionary
|
|
9
9
|
|
|
10
10
|
define_type :Test
|
|
11
|
+
define_field :Type, type: Symbol, default: type
|
|
11
12
|
define_field :Optional, type: Symbol, default: :Optional
|
|
12
13
|
|
|
13
14
|
end
|
|
@@ -46,6 +47,7 @@ describe HexaPDF::Task::Optimize do
|
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
def assert_default_deleted
|
|
50
|
+
assert(@doc.object(1).key?(:Type))
|
|
49
51
|
refute(@doc.object(1).key?(:Optional))
|
|
50
52
|
end
|
|
51
53
|
|
|
@@ -497,10 +497,10 @@ describe HexaPDF::Document do
|
|
|
497
497
|
|
|
498
498
|
it "returns all signature fields of the document" do
|
|
499
499
|
form = @doc.acro_form(create: true)
|
|
500
|
-
sig1 = @doc.add({FT: :Sig, T: 'sig1', V: :sig1})
|
|
501
|
-
sig2 = @doc.add({FT: :Sig, T: 'sig2', V: :sig2})
|
|
500
|
+
sig1 = @doc.add({FT: :Sig, T: 'sig1', V: {k: :sig1}})
|
|
501
|
+
sig2 = @doc.add({FT: :Sig, T: 'sig2', V: {k: :sig2}})
|
|
502
502
|
form.root_fields << sig1 << sig2
|
|
503
|
-
assert_equal([:sig1, :sig2], @doc.signatures.to_a)
|
|
503
|
+
assert_equal([{k: :sig1}, {k: :sig2}], @doc.signatures.to_a)
|
|
504
504
|
end
|
|
505
505
|
|
|
506
506
|
it "allows to conveniently sign a document" do
|
|
@@ -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
|
|
@@ -130,6 +147,13 @@ describe HexaPDF::Importer do
|
|
|
130
147
|
assert_nil(obj[:pages])
|
|
131
148
|
end
|
|
132
149
|
|
|
150
|
+
it "handles null values correctly" do
|
|
151
|
+
@source.add(@hash)
|
|
152
|
+
@source.delete(@hash)
|
|
153
|
+
obj = @importer.import(@obj)
|
|
154
|
+
assert_nil(obj[:hash])
|
|
155
|
+
end
|
|
156
|
+
|
|
133
157
|
it "imports Page objects correctly by copying the inherited values" do
|
|
134
158
|
page = @importer.import(@source.pages[0])
|
|
135
159
|
assert_equal(90, page[:Rotate])
|
|
@@ -357,46 +357,59 @@ describe HexaPDF::Revisions do
|
|
|
357
357
|
end
|
|
358
358
|
end
|
|
359
359
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
data/test/hexapdf/test_writer.rb
CHANGED
|
@@ -64,7 +64,7 @@ describe HexaPDF::Writer do
|
|
|
64
64
|
20
|
|
65
65
|
endobj
|
|
66
66
|
3 0 obj
|
|
67
|
-
<</Type/XRef/
|
|
67
|
+
<</Size 6/Type/XRef/W[1 1 2]/Index[0 4 5 1]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 31>>stream
|
|
68
68
|
x\xDAcb`\xF8\xFF\x9F\x89\x89\x95\x91\x91\xE9\x7F\x19\x03\x03\x13\x83\x10\x88he`\x00\x00B4\x04\x1E
|
|
69
69
|
endstream
|
|
70
70
|
endobj
|
|
@@ -80,7 +80,7 @@ describe HexaPDF::Writer do
|
|
|
80
80
|
endstream
|
|
81
81
|
endobj
|
|
82
82
|
4 0 obj
|
|
83
|
-
<</
|
|
83
|
+
<</Size 7/Root<</Type/Catalog>>/Info 6 0 R/Prev 141/Type/XRef/W[1 2 2]/Index[2 1 4 1 6 1]/Filter/FlateDecode/DecodeParms<</Columns 5/Predictor 12>>/Length 22>>stream
|
|
84
84
|
x\xDAcbdlg``b`\xB0\x04\x93\x93\x18\x18\x00\f\e\x01[
|
|
85
85
|
endstream
|
|
86
86
|
endobj
|
|
@@ -270,4 +270,13 @@ describe HexaPDF::Writer do
|
|
|
270
270
|
doc = HexaPDF::Document.new(io: io)
|
|
271
271
|
refute(doc.trailer.key?(:XRefStm))
|
|
272
272
|
end
|
|
273
|
+
|
|
274
|
+
it "removes the /Type entry in a non-xref stream trailer" do
|
|
275
|
+
io = StringIO.new
|
|
276
|
+
doc = HexaPDF::Document.new
|
|
277
|
+
doc.trailer[:Type] = :XRef
|
|
278
|
+
doc.write(io)
|
|
279
|
+
doc = HexaPDF::Document.new(io: io)
|
|
280
|
+
refute(doc.trailer.key?(:Type))
|
|
281
|
+
end
|
|
273
282
|
end
|
|
@@ -291,6 +291,22 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
291
291
|
assert_equal(:XObject, @widget[:AP][:N][:Other].type)
|
|
292
292
|
end
|
|
293
293
|
|
|
294
|
+
it "uses the field's value or :Yes for the on state if the appearance dictionary doesn't contain a name for it" do
|
|
295
|
+
@widget[:AP][:N].delete(:Yes)
|
|
296
|
+
@generator.create_appearances
|
|
297
|
+
assert_equal(:XObject, @widget[:AP][:N][:Yes].type)
|
|
298
|
+
|
|
299
|
+
@widget[:AP][:N].delete(:Yes)
|
|
300
|
+
@field[:V] = nil
|
|
301
|
+
@generator.create_appearances
|
|
302
|
+
assert_equal(:XObject, @widget[:AP][:N][:Yes].type)
|
|
303
|
+
|
|
304
|
+
@widget[:AP][:N].delete(:Yes)
|
|
305
|
+
@field[:V] = "other" # some PDFs use a string instead of the correct symbol
|
|
306
|
+
@generator.create_appearances
|
|
307
|
+
assert_equal(:XObject, @widget[:AP][:N][:other].type)
|
|
308
|
+
end
|
|
309
|
+
|
|
294
310
|
it "creates the needed appearance streams" do
|
|
295
311
|
@widget[:AP][:N].delete(:Off)
|
|
296
312
|
@generator.create_appearances
|
|
@@ -327,11 +343,6 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
327
343
|
[:end_text],
|
|
328
344
|
[:restore_graphics_state]])
|
|
329
345
|
end
|
|
330
|
-
|
|
331
|
-
it "fails if the appearance dictionary doesn't contain a name for the on state" do
|
|
332
|
-
@widget[:AP][:N].delete(:Yes)
|
|
333
|
-
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
334
|
-
end
|
|
335
346
|
end
|
|
336
347
|
|
|
337
348
|
describe "radio button" do
|
|
@@ -441,6 +452,12 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
441
452
|
assert_equal(form, @widget[:AP][:N])
|
|
442
453
|
refute(form.key?(:key))
|
|
443
454
|
assert_match(/test1/, form.contents)
|
|
455
|
+
|
|
456
|
+
form.delete(:Type)
|
|
457
|
+
@widget[:AP][:N] = @doc.wrap(form, type: HexaPDF::Type::Annotation)
|
|
458
|
+
@field[:V] = 'test2'
|
|
459
|
+
@generator.create_appearances
|
|
460
|
+
assert_match(/test2/, form.contents)
|
|
444
461
|
end
|
|
445
462
|
|
|
446
463
|
describe "takes the rotation into account" 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])
|
|
@@ -498,11 +507,6 @@ describe HexaPDF::Type::AcroForm::Form do
|
|
|
498
507
|
assert(@acro_form.validate)
|
|
499
508
|
end
|
|
500
509
|
|
|
501
|
-
it "set the default appearance string, though optional, to a valid value to avoid problems" do
|
|
502
|
-
assert(@acro_form.validate)
|
|
503
|
-
assert_equal("0.0 g /F1 0 Tf", @acro_form[:DA])
|
|
504
|
-
end
|
|
505
|
-
|
|
506
510
|
describe "field hierarchy validation" do
|
|
507
511
|
before do
|
|
508
512
|
@acro_form[:Fields] = [
|
|
@@ -31,7 +31,9 @@ describe HexaPDF::Type::AcroForm::SignatureField do
|
|
|
31
31
|
|
|
32
32
|
it "gets the field value" do
|
|
33
33
|
@field[:V] = {Empty: :True}
|
|
34
|
-
|
|
34
|
+
value = @field.field_value
|
|
35
|
+
assert_kind_of(HexaPDF::DigitalSignature::Signature, value)
|
|
36
|
+
assert_equal({Empty: :True}, value)
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
it "validates the value of the /FT field" do
|
|
@@ -102,9 +102,22 @@ describe HexaPDF::Type::AcroForm::VariableTextField do
|
|
|
102
102
|
@field.parse_default_appearance_string)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
it "
|
|
105
|
+
it "sets a standard /DA value if no other /DA is found" do
|
|
106
106
|
@doc.acro_form.delete(:DA)
|
|
107
|
+
assert_equal([:F1, 0, HexaPDF::Content::ColorSpace.prenormalized_device_color([0])],
|
|
108
|
+
@field.parse_default_appearance_string)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "converts the /DA to a string in case an invalid PDF uses a Symbol" do
|
|
112
|
+
@field[:DA] = :"1 g /F1 20 Tf"
|
|
113
|
+
assert_equal([:F1, 20, @color], @field.parse_default_appearance_string)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "fails if no /DA value is set and no default appearance string should be set" do
|
|
117
|
+
@doc.acro_form.delete(:DA)
|
|
118
|
+
@doc.config['acro_form.fallback_default_appearance'] = nil
|
|
107
119
|
assert_raises(HexaPDF::Error) { @field.parse_default_appearance_string }
|
|
108
120
|
end
|
|
121
|
+
|
|
109
122
|
end
|
|
110
123
|
end
|
|
@@ -194,6 +194,8 @@ describe HexaPDF::Type::Annotations::Widget do
|
|
|
194
194
|
|
|
195
195
|
it "returns the default size if none is set" do
|
|
196
196
|
assert_equal(0, @widget.marker_style.size)
|
|
197
|
+
@widget.form_field[:DA] = "0.0 g"
|
|
198
|
+
assert_equal(0, @widget.marker_style.size)
|
|
197
199
|
end
|
|
198
200
|
|
|
199
201
|
it "sets the given size" do
|
|
@@ -212,6 +214,8 @@ describe HexaPDF::Type::Annotations::Widget do
|
|
|
212
214
|
|
|
213
215
|
it "returns the default color if none is set" do
|
|
214
216
|
assert_equal([0], @widget.marker_style.color.components)
|
|
217
|
+
@widget.form_field[:DA] = "/ZaDb 10 Tfg"
|
|
218
|
+
assert_equal([0], @widget.marker_style.color.components)
|
|
215
219
|
end
|
|
216
220
|
|
|
217
221
|
it "sets the given color" do
|
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.
|
|
4
|
+
version: 0.47.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-
|
|
11
|
+
date: 2024-09-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cmdparse
|
|
@@ -594,21 +594,25 @@ files:
|
|
|
594
594
|
- test/data/minimal.pdf
|
|
595
595
|
- test/data/standard-security-handler/README
|
|
596
596
|
- test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf
|
|
597
|
+
- test/data/standard-security-handler/bothpwd-aes-256bit-V5-R5.pdf
|
|
597
598
|
- test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf
|
|
598
599
|
- test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf
|
|
599
600
|
- test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf
|
|
600
601
|
- test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf
|
|
601
602
|
- test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf
|
|
603
|
+
- test/data/standard-security-handler/nopwd-aes-256bit-V5-R5.pdf
|
|
602
604
|
- test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf
|
|
603
605
|
- test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf
|
|
604
606
|
- test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf
|
|
605
607
|
- test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf
|
|
606
608
|
- test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf
|
|
609
|
+
- test/data/standard-security-handler/ownerpwd-aes-256bit-V5-R5.pdf
|
|
607
610
|
- test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf
|
|
608
611
|
- test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf
|
|
609
612
|
- test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf
|
|
610
613
|
- test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf
|
|
611
614
|
- test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf
|
|
615
|
+
- test/data/standard-security-handler/userpwd-aes-256bit-V5-R5.pdf
|
|
612
616
|
- test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf
|
|
613
617
|
- test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf
|
|
614
618
|
- test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf
|