hexapdf 0.14.4 → 0.15.4
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 +68 -0
- data/lib/hexapdf/cli/form.rb +30 -8
- data/lib/hexapdf/configuration.rb +18 -3
- data/lib/hexapdf/content/canvas.rb +1 -0
- data/lib/hexapdf/encryption/standard_security_handler.rb +16 -0
- data/lib/hexapdf/error.rb +4 -3
- data/lib/hexapdf/parser.rb +18 -6
- data/lib/hexapdf/revision.rb +16 -0
- data/lib/hexapdf/type/acro_form.rb +1 -0
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +29 -17
- data/lib/hexapdf/type/acro_form/button_field.rb +8 -4
- data/lib/hexapdf/type/acro_form/field.rb +1 -0
- data/lib/hexapdf/type/acro_form/form.rb +37 -0
- data/lib/hexapdf/type/acro_form/signature_field.rb +223 -0
- data/lib/hexapdf/type/annotation.rb +18 -9
- data/lib/hexapdf/type/annotations/widget.rb +3 -1
- data/lib/hexapdf/type/font_descriptor.rb +9 -2
- data/lib/hexapdf/type/page.rb +81 -0
- data/lib/hexapdf/utils/graphics_helpers.rb +4 -4
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/test_canvas.rb +21 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +27 -0
- data/test/hexapdf/test_parser.rb +23 -3
- data/test/hexapdf/test_revision.rb +21 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +21 -2
- data/test/hexapdf/type/acro_form/test_button_field.rb +13 -7
- data/test/hexapdf/type/acro_form/test_field.rb +5 -0
- data/test/hexapdf/type/acro_form/test_form.rb +46 -2
- data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
- data/test/hexapdf/type/annotations/test_widget.rb +2 -0
- data/test/hexapdf/type/test_annotation.rb +24 -10
- data/test/hexapdf/type/test_font_descriptor.rb +7 -0
- data/test/hexapdf/type/test_page.rb +187 -49
- data/test/hexapdf/utils/test_graphics_helpers.rb +8 -0
- metadata +4 -2
|
@@ -44,6 +44,13 @@ describe HexaPDF::Type::FontDescriptor do
|
|
|
44
44
|
refute(@font_desc.validate)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
it "deletes the /FontWeight value if it doesn't contain a valid value" do
|
|
48
|
+
@font_desc[:FontWeight] = 350
|
|
49
|
+
refute(@font_desc.validate(auto_correct: false))
|
|
50
|
+
assert(@font_desc.validate)
|
|
51
|
+
refute(@font_desc.key?(:FontWeight))
|
|
52
|
+
end
|
|
53
|
+
|
|
47
54
|
it "updates the /Descent value if it is not a negative number" do
|
|
48
55
|
@font_desc[:Descent] = 5
|
|
49
56
|
refute(@font_desc.validate(auto_correct: false))
|
|
@@ -26,7 +26,7 @@ describe HexaPDF::Type::Page do
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# Asserts that the page's contents contains the operators.
|
|
29
|
-
def
|
|
29
|
+
def assert_page_operators(page, operators)
|
|
30
30
|
processor = TestHelper::OperatorRecorder.new
|
|
31
31
|
page.process_contents(processor)
|
|
32
32
|
assert_equal(operators, processor.recorded_ops)
|
|
@@ -293,8 +293,8 @@ describe HexaPDF::Type::Page do
|
|
|
293
293
|
it "parses the contents and processes it" do
|
|
294
294
|
page = @doc.pages.add
|
|
295
295
|
page[:Contents] = @doc.wrap({}, stream: 'q 10 w Q')
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
assert_page_operators(page, [[:save_graphics_state], [:set_line_width, [10]],
|
|
297
|
+
[:restore_graphics_state]])
|
|
298
298
|
end
|
|
299
299
|
end
|
|
300
300
|
|
|
@@ -333,60 +333,60 @@ describe HexaPDF::Type::Page do
|
|
|
333
333
|
|
|
334
334
|
it "works correctly if invoked on an empty page, using type :page in first invocation" do
|
|
335
335
|
@page.canvas.line_width = 10
|
|
336
|
-
|
|
336
|
+
assert_page_operators(@page, [[:set_line_width, [10]]])
|
|
337
337
|
|
|
338
338
|
@page.canvas(type: :overlay).line_width = 5
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
339
|
+
assert_page_operators(@page, [[:save_graphics_state], [:restore_graphics_state],
|
|
340
|
+
[:save_graphics_state], [:set_line_width, [10]],
|
|
341
|
+
[:restore_graphics_state], [:save_graphics_state],
|
|
342
|
+
[:set_line_width, [5]], [:restore_graphics_state]])
|
|
343
343
|
|
|
344
344
|
@page.canvas(type: :underlay).line_width = 2
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
345
|
+
assert_page_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
|
|
346
|
+
[:restore_graphics_state], [:save_graphics_state],
|
|
347
|
+
[:set_line_width, [10]],
|
|
348
|
+
[:restore_graphics_state], [:save_graphics_state],
|
|
349
|
+
[:set_line_width, [5]], [:restore_graphics_state]])
|
|
350
350
|
end
|
|
351
351
|
|
|
352
352
|
it "works correctly if invoked on an empty page, using type :underlay in first invocation" do
|
|
353
353
|
@page.canvas(type: :underlay).line_width = 2
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
354
|
+
assert_page_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
|
|
355
|
+
[:restore_graphics_state], [:save_graphics_state],
|
|
356
|
+
[:restore_graphics_state], [:save_graphics_state],
|
|
357
|
+
[:restore_graphics_state]])
|
|
358
358
|
|
|
359
359
|
@page.canvas.line_width = 10
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
360
|
+
assert_page_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
|
|
361
|
+
[:restore_graphics_state], [:save_graphics_state],
|
|
362
|
+
[:set_line_width, [10]], [:restore_graphics_state],
|
|
363
|
+
[:save_graphics_state], [:restore_graphics_state]])
|
|
364
364
|
|
|
365
365
|
@page.canvas(type: :overlay).line_width = 5
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
366
|
+
assert_page_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
|
|
367
|
+
[:restore_graphics_state], [:save_graphics_state],
|
|
368
|
+
[:set_line_width, [10]],
|
|
369
|
+
[:restore_graphics_state], [:save_graphics_state],
|
|
370
|
+
[:set_line_width, [5]], [:restore_graphics_state]])
|
|
371
371
|
end
|
|
372
372
|
|
|
373
373
|
it "works correctly if invoked on a page with existing contents" do
|
|
374
374
|
@page.contents = "10 w"
|
|
375
375
|
|
|
376
376
|
@page.canvas(type: :overlay).line_width = 5
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
377
|
+
assert_page_operators(@page, [[:save_graphics_state], [:restore_graphics_state],
|
|
378
|
+
[:save_graphics_state], [:set_line_width, [10]],
|
|
379
|
+
[:restore_graphics_state],
|
|
380
|
+
[:save_graphics_state], [:set_line_width, [5]],
|
|
381
|
+
[:restore_graphics_state]])
|
|
382
382
|
|
|
383
383
|
@page.canvas(type: :underlay).line_width = 2
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
384
|
+
assert_page_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
|
|
385
|
+
[:restore_graphics_state], [:save_graphics_state],
|
|
386
|
+
[:set_line_width, [10]],
|
|
387
|
+
[:restore_graphics_state],
|
|
388
|
+
[:save_graphics_state], [:set_line_width, [5]],
|
|
389
|
+
[:restore_graphics_state]])
|
|
390
390
|
end
|
|
391
391
|
|
|
392
392
|
it "works correctly if the page has its origin not at (0,0)" do
|
|
@@ -395,20 +395,20 @@ describe HexaPDF::Type::Page do
|
|
|
395
395
|
@page.canvas(type: :page).line_width = 2
|
|
396
396
|
@page.canvas(type: :overlay).line_width = 2
|
|
397
397
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
398
|
+
assert_page_operators(@page, [[:save_graphics_state],
|
|
399
|
+
[:concatenate_matrix, [1, 0, 0, 1, -10, -5]],
|
|
400
|
+
[:set_line_width, [2]],
|
|
401
|
+
[:restore_graphics_state],
|
|
402
402
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
403
|
+
[:save_graphics_state],
|
|
404
|
+
[:concatenate_matrix, [1, 0, 0, 1, -10, -5]],
|
|
405
|
+
[:set_line_width, [2]],
|
|
406
|
+
[:restore_graphics_state],
|
|
407
407
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
408
|
+
[:save_graphics_state],
|
|
409
|
+
[:concatenate_matrix, [1, 0, 0, 1, -10, -5]],
|
|
410
|
+
[:set_line_width, [2]],
|
|
411
|
+
[:restore_graphics_state]])
|
|
412
412
|
end
|
|
413
413
|
|
|
414
414
|
it "fails if the page canvas is requested for a page with existing contents" do
|
|
@@ -446,4 +446,142 @@ describe HexaPDF::Type::Page do
|
|
|
446
446
|
refute_same(form.raw_stream, page[:Contents].raw_stream)
|
|
447
447
|
end
|
|
448
448
|
end
|
|
449
|
+
|
|
450
|
+
describe "flatten_annotations" do
|
|
451
|
+
before do
|
|
452
|
+
@page = @doc.pages.add
|
|
453
|
+
@appearance = @doc.add({Type: :XObject, Subtype: :Form, BBox: [-10, -5, 50, 20]}, stream: "")
|
|
454
|
+
@annot1 = @doc.add({Type: :Annot, Subtype: :Text, Rect: [100, 100, 160, 125], AP: {N: @appearance}})
|
|
455
|
+
@annot2 = @doc.add({Rect: [10, 10, 70, 35], AP: {N: @appearance}})
|
|
456
|
+
@page[:Annots] = [@annot1, @annot2]
|
|
457
|
+
@canvas = @page.canvas(type: :overlay)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
it "does nothing if the page doesn't have any annotations" do
|
|
461
|
+
@page.delete(:Annots)
|
|
462
|
+
result = @page.flatten_annotations
|
|
463
|
+
assert(result.empty?)
|
|
464
|
+
assert_operators(@canvas.contents, [])
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
it "flattens all annotations of the page by default" do
|
|
468
|
+
result = @page.flatten_annotations
|
|
469
|
+
assert(result.empty?)
|
|
470
|
+
assert_operators(@canvas.contents, [[:save_graphics_state],
|
|
471
|
+
[:save_graphics_state],
|
|
472
|
+
[:concatenate_matrix, [1.0, 0, 0, 1.0, 110, 105]],
|
|
473
|
+
[:paint_xobject, [:XO1]],
|
|
474
|
+
[:restore_graphics_state],
|
|
475
|
+
[:save_graphics_state],
|
|
476
|
+
[:concatenate_matrix, [1.0, 0, 0, 1.0, 20, 15]],
|
|
477
|
+
[:paint_xobject, [:XO1]],
|
|
478
|
+
[:restore_graphics_state],
|
|
479
|
+
[:restore_graphics_state]])
|
|
480
|
+
assert(@annot1.null?)
|
|
481
|
+
assert(@annot2.null?)
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
it "only deletes the widget annotation of a form field even if it is embedded in the field object" do
|
|
485
|
+
form = @doc.acro_form(create: true)
|
|
486
|
+
field = form.create_text_field('test')
|
|
487
|
+
widget = field.create_widget(@page, Rect: [200, 200, 250, 215])
|
|
488
|
+
field.field_value = 'hello'
|
|
489
|
+
|
|
490
|
+
assert_same(widget.data, field.data)
|
|
491
|
+
result = @page.flatten_annotations([widget])
|
|
492
|
+
assert(result.empty?)
|
|
493
|
+
refute(field.null?)
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
it "does nothing if a given annotation is not part of the page" do
|
|
497
|
+
annots = [{Test: :Object}]
|
|
498
|
+
result = @page.flatten_annotations(annots)
|
|
499
|
+
assert_equal(annots, result)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
it "deletes hidden annotations but doesn't include them in the content stream" do
|
|
503
|
+
@annot1.flag(:hidden)
|
|
504
|
+
result = @page.flatten_annotations
|
|
505
|
+
assert(result.empty?)
|
|
506
|
+
assert(@annot1.null?)
|
|
507
|
+
assert_operators(@canvas.contents, [[:save_graphics_state],
|
|
508
|
+
[:save_graphics_state],
|
|
509
|
+
[:concatenate_matrix, [1.0, 0, 0, 1.0, 20, 15]],
|
|
510
|
+
[:paint_xobject, [:XO1]],
|
|
511
|
+
[:restore_graphics_state],
|
|
512
|
+
[:restore_graphics_state]])
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
it "deletes invisible annotations but doesn't include them in the content stream" do
|
|
516
|
+
@annot1.flag(:invisible)
|
|
517
|
+
result = @page.flatten_annotations
|
|
518
|
+
assert(result.empty?)
|
|
519
|
+
assert(@annot1.null?)
|
|
520
|
+
assert_operators(@canvas.contents, [[:save_graphics_state],
|
|
521
|
+
[:save_graphics_state],
|
|
522
|
+
[:concatenate_matrix, [1.0, 0, 0, 1.0, 20, 15]],
|
|
523
|
+
[:paint_xobject, [:XO1]],
|
|
524
|
+
[:restore_graphics_state],
|
|
525
|
+
[:restore_graphics_state]])
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
it "ignores annotations without appearane stream" do
|
|
529
|
+
@annot1.delete(:AP)
|
|
530
|
+
result = @page.flatten_annotations
|
|
531
|
+
assert_equal([@annot1], result)
|
|
532
|
+
refute(@annot1.empty?)
|
|
533
|
+
assert_operators(@canvas.contents, [[:save_graphics_state],
|
|
534
|
+
[:save_graphics_state],
|
|
535
|
+
[:concatenate_matrix, [1.0, 0, 0, 1.0, 20, 15]],
|
|
536
|
+
[:paint_xobject, [:XO1]],
|
|
537
|
+
[:restore_graphics_state],
|
|
538
|
+
[:restore_graphics_state]])
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
it "adjusts the position to counter the translation in #canvas based on the page's media box" do
|
|
542
|
+
@page[:MediaBox] = [-15, -15, 100, 100]
|
|
543
|
+
@page.flatten_annotations
|
|
544
|
+
assert_operators(@canvas.contents, [:concatenate_matrix, [1, 0, 0, 1, 15, 15]], range: 1)
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
it "adjusts the position in case the form /Matrix has an offset" do
|
|
548
|
+
@appearance[:Matrix] = [1, 0, 0, 1, 15, 15]
|
|
549
|
+
@page.flatten_annotations
|
|
550
|
+
assert_operators(@canvas.contents, [:concatenate_matrix, [1, 0, 0, 1, 95, 90]], range: 2)
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
it "adjusts the position for an appearance with a 90 degree rotation" do
|
|
554
|
+
@appearance[:Matrix] = [0, 1, -1, 0, 0, 0]
|
|
555
|
+
@annot1[:Rect] = [100, 100, 125, 160]
|
|
556
|
+
@page.flatten_annotations
|
|
557
|
+
assert_operators(@canvas.contents, [:concatenate_matrix, [1, 0, 0, 1, 120, 110]], range: 2)
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
it "adjusts the position for an appearance with a -90 degree rotation" do
|
|
561
|
+
@appearance[:Matrix] = [0, -1, 1, 0, 0, 0]
|
|
562
|
+
@annot1[:Rect] = [100, 100, 125, 160]
|
|
563
|
+
@page.flatten_annotations
|
|
564
|
+
assert_operators(@canvas.contents, [:concatenate_matrix, [1, 0, 0, 1, 105, 150]], range: 2)
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
it "adjusts the position for an appearance with a 180 degree rotation" do
|
|
568
|
+
@appearance[:Matrix] = [-1, 0, 0, -1, 0, 0]
|
|
569
|
+
@page.flatten_annotations
|
|
570
|
+
assert_operators(@canvas.contents, [:concatenate_matrix, [1, 0, 0, 1, 150, 120]], range: 2)
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
it "ignores an appearance with a rotation that is not a mulitple of 90" do
|
|
574
|
+
@appearance[:Matrix] = [-1, 0.5, 0.5, -1, 0, 0]
|
|
575
|
+
result = @page.flatten_annotations
|
|
576
|
+
assert_equal([@annot1, @annot2], result)
|
|
577
|
+
assert_operators(@canvas.contents, [[:save_graphics_state], [:restore_graphics_state]])
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
it "scales the appearance to fit into the annotations's rectangle" do
|
|
581
|
+
@annot1[:Rect] = [100, 100, 130, 150]
|
|
582
|
+
@page.flatten_annotations
|
|
583
|
+
assert_operators(@canvas.contents, [:concatenate_matrix, [0.5, 0, 0, 2, 110, 105]], range: 2)
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
end
|
|
449
587
|
end
|
|
@@ -15,9 +15,17 @@ describe HexaPDF::Utils::GraphicsHelpers do
|
|
|
15
15
|
assert_equal([10, 12], calculate_dimensions(5, 6, rwidth: 10))
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
it "returns the requested width and the given height if width is zero" do
|
|
19
|
+
assert_equal([10, 6], calculate_dimensions(0, 6, rwidth: 10))
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
it "returns the requested height and an adjusted width" do
|
|
19
23
|
assert_equal([10, 12], calculate_dimensions(5, 6, rheight: 12))
|
|
20
24
|
end
|
|
25
|
+
|
|
26
|
+
it "returns the requested height and the given width if height is zero" do
|
|
27
|
+
assert_equal([5, 12], calculate_dimensions(5, 0, rheight: 12))
|
|
28
|
+
end
|
|
21
29
|
end
|
|
22
30
|
|
|
23
31
|
describe "point_on_line" 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.15.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Thomas Leitner
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-05-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cmdparse
|
|
@@ -359,6 +359,7 @@ files:
|
|
|
359
359
|
- lib/hexapdf/type/acro_form/choice_field.rb
|
|
360
360
|
- lib/hexapdf/type/acro_form/field.rb
|
|
361
361
|
- lib/hexapdf/type/acro_form/form.rb
|
|
362
|
+
- lib/hexapdf/type/acro_form/signature_field.rb
|
|
362
363
|
- lib/hexapdf/type/acro_form/text_field.rb
|
|
363
364
|
- lib/hexapdf/type/acro_form/variable_text_field.rb
|
|
364
365
|
- lib/hexapdf/type/action.rb
|
|
@@ -595,6 +596,7 @@ files:
|
|
|
595
596
|
- test/hexapdf/type/acro_form/test_choice_field.rb
|
|
596
597
|
- test/hexapdf/type/acro_form/test_field.rb
|
|
597
598
|
- test/hexapdf/type/acro_form/test_form.rb
|
|
599
|
+
- test/hexapdf/type/acro_form/test_signature_field.rb
|
|
598
600
|
- test/hexapdf/type/acro_form/test_text_field.rb
|
|
599
601
|
- test/hexapdf/type/acro_form/test_variable_text_field.rb
|
|
600
602
|
- test/hexapdf/type/actions/test_launch.rb
|