hexapdf 0.12.3 → 0.14.3
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 +132 -0
- data/examples/019-acro_form.rb +41 -4
- data/lib/hexapdf/cli/command.rb +4 -2
- data/lib/hexapdf/cli/image2pdf.rb +2 -1
- data/lib/hexapdf/cli/info.rb +51 -2
- data/lib/hexapdf/cli/inspect.rb +30 -8
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/split.rb +74 -14
- data/lib/hexapdf/configuration.rb +15 -0
- data/lib/hexapdf/content/graphic_object/arc.rb +3 -3
- data/lib/hexapdf/dictionary.rb +12 -6
- data/lib/hexapdf/dictionary_fields.rb +2 -10
- data/lib/hexapdf/document.rb +41 -16
- data/lib/hexapdf/document/files.rb +0 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +1 -0
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -0
- data/lib/hexapdf/font/cmap.rb +1 -4
- data/lib/hexapdf/font/true_type/subsetter.rb +16 -3
- data/lib/hexapdf/font/true_type/table/head.rb +1 -0
- data/lib/hexapdf/font/true_type/table/os2.rb +2 -0
- data/lib/hexapdf/font/true_type/table/post.rb +15 -10
- data/lib/hexapdf/font_loader/from_configuration.rb +2 -2
- data/lib/hexapdf/font_loader/from_file.rb +18 -8
- data/lib/hexapdf/image_loader/png.rb +3 -2
- data/lib/hexapdf/importer.rb +3 -2
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/style.rb +23 -23
- data/lib/hexapdf/layout/text_layouter.rb +2 -2
- data/lib/hexapdf/layout/text_shaper.rb +3 -2
- data/lib/hexapdf/object.rb +52 -25
- data/lib/hexapdf/parser.rb +107 -7
- data/lib/hexapdf/pdf_array.rb +15 -5
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +37 -10
- data/lib/hexapdf/task/optimize.rb +6 -4
- data/lib/hexapdf/tokenizer.rb +22 -0
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +130 -27
- data/lib/hexapdf/type/acro_form/button_field.rb +5 -2
- data/lib/hexapdf/type/acro_form/choice_field.rb +68 -14
- data/lib/hexapdf/type/acro_form/field.rb +35 -5
- data/lib/hexapdf/type/acro_form/form.rb +139 -14
- data/lib/hexapdf/type/acro_form/text_field.rb +70 -4
- data/lib/hexapdf/type/actions/uri.rb +3 -2
- data/lib/hexapdf/type/annotations/widget.rb +3 -4
- data/lib/hexapdf/type/catalog.rb +2 -2
- data/lib/hexapdf/type/cid_font.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +4 -2
- data/lib/hexapdf/type/font_true_type.rb +6 -2
- data/lib/hexapdf/type/font_type0.rb +4 -4
- data/lib/hexapdf/type/form.rb +6 -2
- data/lib/hexapdf/type/image.rb +2 -2
- data/lib/hexapdf/type/page.rb +21 -12
- data/lib/hexapdf/type/page_tree_node.rb +29 -5
- data/lib/hexapdf/type/resources.rb +5 -0
- data/lib/hexapdf/type/trailer.rb +2 -3
- data/lib/hexapdf/utils/object_hash.rb +0 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +18 -15
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
- data/test/hexapdf/content/test_canvas.rb +3 -3
- data/test/hexapdf/content/test_color_space.rb +1 -1
- data/test/hexapdf/encryption/test_aes.rb +4 -4
- data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
- data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_post.rb +1 -1
- data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +7 -3
- data/test/hexapdf/font_loader/test_from_file.rb +7 -0
- data/test/hexapdf/layout/test_text_layouter.rb +12 -5
- data/test/hexapdf/test_configuration.rb +2 -2
- data/test/hexapdf/test_dictionary.rb +8 -1
- data/test/hexapdf/test_dictionary_fields.rb +9 -2
- data/test/hexapdf/test_document.rb +18 -10
- data/test/hexapdf/test_object.rb +71 -26
- data/test/hexapdf/test_parser.rb +205 -51
- data/test/hexapdf/test_pdf_array.rb +8 -1
- data/test/hexapdf/test_revisions.rb +35 -0
- data/test/hexapdf/test_serializer.rb +7 -0
- data/test/hexapdf/test_tokenizer.rb +28 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +288 -35
- data/test/hexapdf/type/acro_form/test_button_field.rb +15 -0
- data/test/hexapdf/type/acro_form/test_choice_field.rb +92 -9
- data/test/hexapdf/type/acro_form/test_field.rb +39 -0
- data/test/hexapdf/type/acro_form/test_form.rb +87 -15
- data/test/hexapdf/type/acro_form/test_text_field.rb +77 -1
- data/test/hexapdf/type/test_font_simple.rb +2 -1
- data/test/hexapdf/type/test_font_true_type.rb +6 -0
- data/test/hexapdf/type/test_form.rb +8 -1
- data/test/hexapdf/type/test_page.rb +8 -1
- data/test/hexapdf/type/test_page_tree_node.rb +42 -0
- data/test/hexapdf/type/test_resources.rb +6 -0
- data/test/hexapdf/utils/test_bit_field.rb +2 -0
- data/test/hexapdf/utils/test_object_hash.rb +5 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
- data/test/test_helper.rb +2 -0
- metadata +6 -12
data/test/hexapdf/test_writer.rb
CHANGED
|
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
|
|
|
40
40
|
219
|
|
41
41
|
%%EOF
|
|
42
42
|
3 0 obj
|
|
43
|
-
<</Producer(HexaPDF version 0.
|
|
43
|
+
<</Producer(HexaPDF version 0.14.3)>>
|
|
44
44
|
endobj
|
|
45
45
|
xref
|
|
46
46
|
3 1
|
|
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
|
|
|
72
72
|
141
|
|
73
73
|
%%EOF
|
|
74
74
|
6 0 obj
|
|
75
|
-
<</Producer(HexaPDF version 0.
|
|
75
|
+
<</Producer(HexaPDF version 0.14.3)>>
|
|
76
76
|
endobj
|
|
77
77
|
2 0 obj
|
|
78
78
|
<</Length 10>>stream
|
|
@@ -25,14 +25,6 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
25
25
|
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
it "fails for unsupported choice fields" do
|
|
29
|
-
@field = @doc.wrap(@field, type: :XXAcroFormField, subtype: :Ch)
|
|
30
|
-
@field[:FT] = :Ch
|
|
31
|
-
@field.initialize_as_list_box
|
|
32
|
-
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
33
|
-
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
34
|
-
end
|
|
35
|
-
|
|
36
28
|
it "fails for unsupported field types" do
|
|
37
29
|
@field[:FT] = :Unknown
|
|
38
30
|
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
@@ -121,6 +113,24 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
121
113
|
[:curve_to, [8.236068, 7.249543, 9.0, 8.572712, 9.0, 10.0]],
|
|
122
114
|
[:stroke_path], [:restore_graphics_state]])
|
|
123
115
|
end
|
|
116
|
+
|
|
117
|
+
it "handles the special case of a comb field" do
|
|
118
|
+
@field = @doc.add({FT: :Tx, MaxLen: 4}, type: :XXAcroFormField, subtype: :Tx)
|
|
119
|
+
@field.initialize_as_comb_text_field
|
|
120
|
+
@widget = @field.create_widget(@page, Rect: [0, 0, 10, 20])
|
|
121
|
+
@xform = @doc.add({Type: :XObject, Subtype: :Form, BBox: @widget[:Rect]})
|
|
122
|
+
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
123
|
+
@widget.border_style(width: 2)
|
|
124
|
+
execute
|
|
125
|
+
assert_operators(@xform.stream,
|
|
126
|
+
[[:save_graphics_state],
|
|
127
|
+
[:set_line_width, [2]],
|
|
128
|
+
[:append_rectangle, [1, 1, 8, 18]],
|
|
129
|
+
[:move_to, [2.5, 2]], [:line_to, [2.5, 20.0]],
|
|
130
|
+
[:move_to, [5.0, 2]], [:line_to, [5.0, 20.0]],
|
|
131
|
+
[:move_to, [7.5, 2]], [:line_to, [7.5, 20.0]],
|
|
132
|
+
[:stroke_path], [:restore_graphics_state]])
|
|
133
|
+
end
|
|
124
134
|
end
|
|
125
135
|
|
|
126
136
|
describe "draw_marker" do
|
|
@@ -342,7 +352,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
342
352
|
end
|
|
343
353
|
end
|
|
344
354
|
|
|
345
|
-
describe "text
|
|
355
|
+
describe "text fields" do
|
|
346
356
|
before do
|
|
347
357
|
@form.set_default_appearance_string
|
|
348
358
|
@field = @doc.add({FT: :Tx}, type: :XXAcroFormField, subtype: :Tx)
|
|
@@ -392,35 +402,60 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
392
402
|
assert_equal(@doc.acro_form.default_resources[:Font][:F1], form[:Resources][:Font][:F1])
|
|
393
403
|
end
|
|
394
404
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
405
|
+
it "re-uses the existing form XObject" do
|
|
406
|
+
@field[:V] = 'test'
|
|
407
|
+
@generator.create_appearances
|
|
408
|
+
form = @widget[:AP][:N]
|
|
409
|
+
form[:key] = :value
|
|
410
|
+
form.delete(:Subtype)
|
|
411
|
+
@widget[:AP][:N] = @doc.wrap(form, type: HexaPDF::Dictionary)
|
|
402
412
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
end
|
|
413
|
+
@field[:V] = 'test1'
|
|
414
|
+
@generator.create_appearances
|
|
415
|
+
assert_equal(form, @widget[:AP][:N])
|
|
416
|
+
refute(form.key?(:key))
|
|
417
|
+
assert_match(/test1/, form.contents)
|
|
418
|
+
end
|
|
410
419
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
420
|
+
describe "font size calculation" do
|
|
421
|
+
before do
|
|
422
|
+
@widget[:Rect].height = 20
|
|
423
|
+
@widget[:Rect].width = 100
|
|
424
|
+
@field.field_value = ''
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
it "uses the non-zero font size" do
|
|
428
|
+
@field.set_default_appearance_string(font_size: 10)
|
|
429
|
+
@generator.create_appearances
|
|
430
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
431
|
+
[:set_font_and_size, [:F1, 10]],
|
|
432
|
+
range: 5)
|
|
422
433
|
end
|
|
423
434
|
|
|
435
|
+
it "calculates the font size based on the rectangle height and border width" do
|
|
436
|
+
@generator.create_appearances
|
|
437
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
438
|
+
[:set_font_and_size, [:F1, 12.923875]],
|
|
439
|
+
range: 5)
|
|
440
|
+
@widget.border_style(width: 2, color: :transparent)
|
|
441
|
+
@generator.create_appearances
|
|
442
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
443
|
+
[:set_font_and_size, [:F1, 11.487889]],
|
|
444
|
+
range: 5)
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
it " in case of mulitline auto-sizing" do
|
|
448
|
+
@field.initialize_as_multiline_text_field
|
|
449
|
+
@field[:V] = 'a'
|
|
450
|
+
@field.set_default_appearance_string(font_size: 0)
|
|
451
|
+
@generator.create_appearances
|
|
452
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
453
|
+
[:set_font_and_size, [:F1, 12]],
|
|
454
|
+
range: 6)
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
describe "single line text fields" do
|
|
424
459
|
describe "quadding e.g. text alignment" do
|
|
425
460
|
before do
|
|
426
461
|
@field.field_value = 'Test'
|
|
@@ -478,7 +513,166 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
478
513
|
[:restore_graphics_state],
|
|
479
514
|
[:end_marked_content]])
|
|
480
515
|
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
describe "multiline text fields" do
|
|
519
|
+
before do
|
|
520
|
+
@field.set_default_appearance_string(font_size: 10)
|
|
521
|
+
@field.initialize_as_multiline_text_field
|
|
522
|
+
@widget[:Rect].height = 30
|
|
523
|
+
@widget[:Rect].width = 100
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
describe "quadding e.g. text alignment" do
|
|
527
|
+
before do
|
|
528
|
+
@field[:V] = "Test\nValue"
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
it "works for left aligned text" do
|
|
532
|
+
@field.text_alignment(:left)
|
|
533
|
+
@generator.create_appearances
|
|
534
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
535
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 16.195]],
|
|
536
|
+
range: 9)
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
it "works for right aligned text" do
|
|
540
|
+
@field.text_alignment(:right)
|
|
541
|
+
@generator.create_appearances
|
|
542
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
543
|
+
[:set_text_matrix, [1, 0, 0, 1, 78.55, 16.195]],
|
|
544
|
+
range: 9)
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
it "works for center aligned text" do
|
|
548
|
+
@field.text_alignment(:center)
|
|
549
|
+
@generator.create_appearances
|
|
550
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
551
|
+
[:set_text_matrix, [1, 0, 0, 1, 40.275, 16.195]],
|
|
552
|
+
range: 9)
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
it "creates the /N appearance stream according to the set string" do
|
|
557
|
+
@field.field_value = "Test\nValue"
|
|
558
|
+
@generator.create_appearances
|
|
559
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
560
|
+
[[:begin_marked_content, [:Tx]],
|
|
561
|
+
[:save_graphics_state],
|
|
562
|
+
[:append_rectangle, [1, 1, 98, 28]],
|
|
563
|
+
[:clip_path_non_zero],
|
|
564
|
+
[:end_path],
|
|
565
|
+
[:save_graphics_state],
|
|
566
|
+
[:set_leading, [11.5625]],
|
|
567
|
+
[:set_font_and_size, [:F1, 10]],
|
|
568
|
+
[:begin_text],
|
|
569
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 16.195]],
|
|
570
|
+
[:show_text, ['Test']],
|
|
571
|
+
[:move_text_next_line],
|
|
572
|
+
[:show_text, ['Value']],
|
|
573
|
+
[:end_text],
|
|
574
|
+
[:restore_graphics_state],
|
|
575
|
+
[:restore_graphics_state],
|
|
576
|
+
[:end_marked_content]])
|
|
577
|
+
|
|
578
|
+
@field.field_value = "Test\nTest\nTest"
|
|
579
|
+
@field.set_default_appearance_string(font_size: 0)
|
|
580
|
+
@generator.create_appearances
|
|
581
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
582
|
+
[[:begin_marked_content, [:Tx]],
|
|
583
|
+
[:save_graphics_state],
|
|
584
|
+
[:append_rectangle, [1, 1, 98, 28]],
|
|
585
|
+
[:clip_path_non_zero],
|
|
586
|
+
[:end_path],
|
|
587
|
+
[:save_graphics_state],
|
|
588
|
+
[:set_leading, [9.25]],
|
|
589
|
+
[:set_font_and_size, [:F1, 8]],
|
|
590
|
+
[:begin_text],
|
|
591
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 18.556]],
|
|
592
|
+
[:show_text, ['Test']],
|
|
593
|
+
[:move_text_next_line],
|
|
594
|
+
[:show_text, ['Test']],
|
|
595
|
+
[:move_text_next_line],
|
|
596
|
+
[:show_text, ['Test']],
|
|
597
|
+
[:end_text],
|
|
598
|
+
[:restore_graphics_state],
|
|
599
|
+
[:restore_graphics_state],
|
|
600
|
+
[:end_marked_content]],
|
|
601
|
+
)
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
describe "comb text fields" do
|
|
606
|
+
before do
|
|
607
|
+
@field.set_default_appearance_string(font_size: 10)
|
|
608
|
+
@field.initialize_as_comb_text_field
|
|
609
|
+
@field[:MaxLen] = 10
|
|
610
|
+
@widget[:Rect].height = 20
|
|
611
|
+
@widget[:Rect].width = 100
|
|
612
|
+
end
|
|
481
613
|
|
|
614
|
+
describe "quadding e.g. text alignment" do
|
|
615
|
+
before do
|
|
616
|
+
@field[:V] = 'Test'
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
it "works for left aligned text" do
|
|
620
|
+
@field.text_alignment(:left)
|
|
621
|
+
@generator.create_appearances
|
|
622
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
623
|
+
[:set_text_matrix, [1, 0, 0, 1, 2.945, 6.41]],
|
|
624
|
+
range: 7)
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
it "works for right aligned text" do
|
|
628
|
+
@field.text_alignment(:right)
|
|
629
|
+
@generator.create_appearances
|
|
630
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
631
|
+
[:set_text_matrix, [1, 0, 0, 1, 62.945, 6.41]],
|
|
632
|
+
range: 7)
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
it "works for center aligned text" do
|
|
636
|
+
@field.text_alignment(:center)
|
|
637
|
+
@generator.create_appearances
|
|
638
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
639
|
+
[:set_text_matrix, [1, 0, 0, 1, 32.945, 6.41]],
|
|
640
|
+
range: 7)
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
it "handles centering like Adobe, e.g. shift left, when text cannot be completely centered" do
|
|
644
|
+
@field.field_value = 'Hello'
|
|
645
|
+
@field.text_alignment(:center)
|
|
646
|
+
@generator.create_appearances
|
|
647
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
648
|
+
[:set_text_matrix, [1, 0, 0, 1, 22.39, 6.41]],
|
|
649
|
+
range: 7)
|
|
650
|
+
end
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
it "creates the /N appearance stream according to the set string" do
|
|
654
|
+
@field.field_value = 'Text'
|
|
655
|
+
@generator.create_appearances
|
|
656
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
657
|
+
[[:begin_marked_content, [:Tx]],
|
|
658
|
+
[:save_graphics_state],
|
|
659
|
+
[:append_rectangle, [1, 1, 98, 18]],
|
|
660
|
+
[:clip_path_non_zero],
|
|
661
|
+
[:end_path],
|
|
662
|
+
[:set_font_and_size, [:F1, 10]],
|
|
663
|
+
[:begin_text],
|
|
664
|
+
[:set_text_matrix, [1, 0, 0, 1, 2.945, 6.41]],
|
|
665
|
+
[:show_text_with_positioning, [['T', -416.5, 'e', -472, 'x', -611, 't']]],
|
|
666
|
+
[:end_text],
|
|
667
|
+
[:restore_graphics_state],
|
|
668
|
+
[:end_marked_content]])
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
it "fails if the /MaxLen key is not set" do
|
|
672
|
+
@field.delete(:MaxLen)
|
|
673
|
+
@field[:V] = 't'
|
|
674
|
+
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
675
|
+
end
|
|
482
676
|
end
|
|
483
677
|
|
|
484
678
|
describe "choice fields" do
|
|
@@ -486,16 +680,75 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
486
680
|
@form.set_default_appearance_string
|
|
487
681
|
field = @doc.add({FT: :Ch}, type: :XXAcroFormField, subtype: :Ch)
|
|
488
682
|
field.initialize_as_combo_box
|
|
683
|
+
field.flag(:edit)
|
|
684
|
+
field.field_value = 'Test'
|
|
489
685
|
widget = field.create_widget(@page, Rect: [0, 0, 0, 0])
|
|
490
686
|
generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(widget)
|
|
491
687
|
generator.create_appearances
|
|
492
688
|
assert_kind_of(HexaPDF::Type::Form, widget[:AP][:N])
|
|
493
689
|
end
|
|
690
|
+
|
|
691
|
+
describe "list boxes" do
|
|
692
|
+
before do
|
|
693
|
+
@field = @doc.add({FT: :Ch}, type: :XXAcroFormField, subtype: :Ch)
|
|
694
|
+
@field.initialize_as_list_box
|
|
695
|
+
@field.flag(:multi_select)
|
|
696
|
+
@field.option_items = ['a', 'b', 'c']
|
|
697
|
+
@widget = @field.create_widget(@page, Rect: [0, 0, 90, 36])
|
|
698
|
+
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
it "uses a fixed font size for list box items if auto-sizing is used" do
|
|
702
|
+
@field.set_default_appearance_string(font_size: 0)
|
|
703
|
+
@generator.create_appearances
|
|
704
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
705
|
+
[:set_font_and_size, [:F1, 12]],
|
|
706
|
+
range: 8)
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
it "uses the set values instead of the ones from /I if in conflict" do
|
|
710
|
+
@field[:I] = [0, 1]
|
|
711
|
+
@field[:V] = ['b']
|
|
712
|
+
@generator.create_appearances
|
|
713
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
714
|
+
[[:set_device_rgb_non_stroking_color, [0.6, 0.756863, 0.854902]],
|
|
715
|
+
[:append_rectangle, [1, 7.25, 88, 13.875]],
|
|
716
|
+
[:fill_path_non_zero]],
|
|
717
|
+
range: 5..7)
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
it "creates the /N appearance stream" do
|
|
721
|
+
@field[:I] = [1, 2]
|
|
722
|
+
@field[:V] = ['b', 'c']
|
|
723
|
+
@generator.create_appearances
|
|
724
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
725
|
+
[[:begin_marked_content, [:Tx]],
|
|
726
|
+
[:save_graphics_state],
|
|
727
|
+
[:append_rectangle, [1, 1, 88, 34]],
|
|
728
|
+
[:clip_path_non_zero], [:end_path],
|
|
729
|
+
[:set_device_rgb_non_stroking_color, [0.6, 0.756863, 0.854902]],
|
|
730
|
+
[:append_rectangle, [1, 7.25, 88, 13.875]],
|
|
731
|
+
[:append_rectangle, [1, -6.625, 88, 13.875]],
|
|
732
|
+
[:fill_path_non_zero],
|
|
733
|
+
[:save_graphics_state],
|
|
734
|
+
[:set_leading, [13.875]],
|
|
735
|
+
[:set_font_and_size, [:F1, 12]],
|
|
736
|
+
[:set_device_gray_non_stroking_color, [0.0]],
|
|
737
|
+
[:begin_text],
|
|
738
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 23.609]],
|
|
739
|
+
[:show_text, ["a"]],
|
|
740
|
+
[:move_text_next_line],
|
|
741
|
+
[:show_text, ["b"]],
|
|
742
|
+
[:end_text],
|
|
743
|
+
[:restore_graphics_state], [:restore_graphics_state],
|
|
744
|
+
[:end_marked_content]])
|
|
745
|
+
end
|
|
746
|
+
end
|
|
494
747
|
end
|
|
495
748
|
|
|
496
749
|
describe "font resolution in case the referenced font is not usable" do
|
|
497
750
|
before do
|
|
498
|
-
@doc.config['acro_form.fallback_font'] = ['Times', variant: :none]
|
|
751
|
+
@doc.config['acro_form.fallback_font'] = ['Times', {variant: :none}]
|
|
499
752
|
@field[:V] = 'Test'
|
|
500
753
|
end
|
|
501
754
|
|
|
@@ -233,6 +233,14 @@ describe HexaPDF::Type::AcroForm::ButtonField do
|
|
|
233
233
|
refute_same(off, widget.appearance.normal_appearance[:Off])
|
|
234
234
|
end
|
|
235
235
|
|
|
236
|
+
it "always generates appearances if force is true" do
|
|
237
|
+
widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
|
|
238
|
+
@field.create_appearances
|
|
239
|
+
yes = widget.appearance.normal_appearance[:Yes]
|
|
240
|
+
@field.create_appearances(force: true)
|
|
241
|
+
refute_same(yes, widget.appearance.normal_appearance[:Yes])
|
|
242
|
+
end
|
|
243
|
+
|
|
236
244
|
it "fails for unsupported button types" do
|
|
237
245
|
@field.flag(:push_button)
|
|
238
246
|
@field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
|
|
@@ -263,6 +271,13 @@ describe HexaPDF::Type::AcroForm::ButtonField do
|
|
|
263
271
|
@field.update_widgets
|
|
264
272
|
assert_equal(:Yes, widget[:AS])
|
|
265
273
|
end
|
|
274
|
+
|
|
275
|
+
it "creates the appearances if necessary" do
|
|
276
|
+
widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
|
|
277
|
+
assert_nil(widget[:AP][:N][:Yes])
|
|
278
|
+
@field.update_widgets
|
|
279
|
+
assert(widget[:AP][:N][:Yes])
|
|
280
|
+
end
|
|
266
281
|
end
|
|
267
282
|
|
|
268
283
|
describe "validation" do
|
|
@@ -36,23 +36,34 @@ describe HexaPDF::Type::AcroForm::ChoiceField do
|
|
|
36
36
|
|
|
37
37
|
describe "field_value=" do
|
|
38
38
|
before do
|
|
39
|
-
@field.option_items = ["test", "other"]
|
|
39
|
+
@field.option_items = ["test", "something", "other", "neu"]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "updates the widgets to reflect the changed value" do
|
|
43
|
+
@field.initialize_as_combo_box
|
|
44
|
+
widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
|
|
45
|
+
@field.set_default_appearance_string
|
|
46
|
+
@field.field_value = 'test'
|
|
47
|
+
assert(widget[:AP][:N])
|
|
40
48
|
end
|
|
41
49
|
|
|
42
50
|
describe "combo_box" do
|
|
43
51
|
before do
|
|
44
52
|
@field.initialize_as_combo_box
|
|
53
|
+
@field[:I] = 2
|
|
45
54
|
end
|
|
46
55
|
|
|
47
56
|
it "can set the value for an uneditable combo box" do
|
|
48
57
|
@field.field_value = 'test'
|
|
49
58
|
assert_equal("test", @field[:V])
|
|
59
|
+
assert_nil(@field[:I])
|
|
50
60
|
end
|
|
51
61
|
|
|
52
62
|
it "can set the value for an editable combo box" do
|
|
53
63
|
@field.flag(:edit)
|
|
54
64
|
@field.field_value = 'another'
|
|
55
65
|
assert_equal("another", @field[:V])
|
|
66
|
+
assert_nil(@field[:I])
|
|
56
67
|
end
|
|
57
68
|
|
|
58
69
|
it "fails if mulitple values are provided for a combo box" do
|
|
@@ -76,8 +87,18 @@ describe HexaPDF::Type::AcroForm::ChoiceField do
|
|
|
76
87
|
|
|
77
88
|
it "can set a multiple values if the list box is a multi-select" do
|
|
78
89
|
@field.flag(:multi_select)
|
|
79
|
-
@field.field_value = ['
|
|
80
|
-
assert_equal(['
|
|
90
|
+
@field.field_value = ['other', 'test']
|
|
91
|
+
assert_equal(['other', 'test'], @field[:V].value)
|
|
92
|
+
assert_equal([0, 2], @field[:I].value)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "can read and set the top index" do
|
|
96
|
+
assert_raises(ArgumentError) { @field.list_box_top_index = 4 }
|
|
97
|
+
@field.delete(:Opt)
|
|
98
|
+
assert_raises(ArgumentError) { @field.list_box_top_index = 0 }
|
|
99
|
+
@field.option_items = [1, 2, 3, 4]
|
|
100
|
+
@field.list_box_top_index = 2
|
|
101
|
+
assert_equal(2, @field.list_box_top_index)
|
|
81
102
|
end
|
|
82
103
|
|
|
83
104
|
it "fails if mulitple values are provided but the list box is not a multi-select" do
|
|
@@ -97,10 +118,29 @@ describe HexaPDF::Type::AcroForm::ChoiceField do
|
|
|
97
118
|
assert_raises(HexaPDF::Error) { @field.default_field_value = 'unknown' }
|
|
98
119
|
end
|
|
99
120
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
121
|
+
describe "option items" do
|
|
122
|
+
before do
|
|
123
|
+
@items = [["a", "Zx"], "\xFE\xFF".b << "Töne".encode('UTF-16BE').b, "H\xe4llo".b,]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "sets the option items" do
|
|
127
|
+
@field.option_items = @items
|
|
128
|
+
assert_equal(@items, @field[:Opt].value)
|
|
129
|
+
|
|
130
|
+
@field.flag(:sort)
|
|
131
|
+
@field.option_items = @items
|
|
132
|
+
assert_equal(@items.values_at(2, 1, 0), @field[:Opt].value)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "can retrieve the option items" do
|
|
136
|
+
@field[:Opt] = @items
|
|
137
|
+
assert_equal(["Zx", "Töne", "Hällo"], @field.option_items)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "can retrieve the export values" do
|
|
141
|
+
@field[:Opt] = @items
|
|
142
|
+
assert_equal(["a", "Töne", "Hällo"], @field.export_values)
|
|
143
|
+
end
|
|
104
144
|
end
|
|
105
145
|
|
|
106
146
|
it "returns the correct concrete field type" do
|
|
@@ -121,8 +161,51 @@ describe HexaPDF::Type::AcroForm::ChoiceField do
|
|
|
121
161
|
assert(@field[:AP][:N])
|
|
122
162
|
end
|
|
123
163
|
|
|
124
|
-
it "
|
|
125
|
-
|
|
164
|
+
it "works for list box fields" do
|
|
165
|
+
@field.initialize_as_list_box
|
|
166
|
+
@field.set_default_appearance_string
|
|
167
|
+
@field.create_appearances
|
|
168
|
+
assert(@field[:AP][:N])
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "only creates a new appearance if the involved dictionary values have changed per widget" do
|
|
172
|
+
@field.initialize_as_list_box
|
|
173
|
+
@field.set_default_appearance_string
|
|
174
|
+
@field.create_appearances
|
|
175
|
+
appearance_stream = @field[:AP][:N].raw_stream
|
|
176
|
+
|
|
177
|
+
@field.create_appearances
|
|
178
|
+
assert_same(appearance_stream, @field[:AP][:N].raw_stream)
|
|
179
|
+
|
|
180
|
+
do_check = lambda do
|
|
181
|
+
@field.create_appearances
|
|
182
|
+
refute_same(appearance_stream, @field[:AP][:N].raw_stream)
|
|
183
|
+
appearance_stream = @field[:AP][:N].raw_stream
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
@field.option_items = ['a', 'b', 'c']
|
|
187
|
+
do_check.call
|
|
188
|
+
|
|
189
|
+
@field.list_box_top_index = 2
|
|
190
|
+
do_check.call
|
|
191
|
+
|
|
192
|
+
@field.field_value = 'b'
|
|
193
|
+
do_check.call
|
|
194
|
+
|
|
195
|
+
widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
|
|
196
|
+
assert_nil(widget[:AP])
|
|
197
|
+
@field.create_appearances
|
|
198
|
+
refute_nil(widget[:AP][:N])
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it "force the creation of appearance streams when force: true" do
|
|
202
|
+
@field.initialize_as_list_box
|
|
203
|
+
@field.set_default_appearance_string
|
|
204
|
+
@field.create_appearances
|
|
205
|
+
appearance_stream = @field[:AP][:N].raw_stream
|
|
206
|
+
|
|
207
|
+
@field.create_appearances(force: true)
|
|
208
|
+
refute_same(appearance_stream, @field[:AP][:N].raw_stream)
|
|
126
209
|
end
|
|
127
210
|
end
|
|
128
211
|
|