hexapdf 0.12.0 → 0.14.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 +126 -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/content/parser.rb +1 -1
- data/lib/hexapdf/dictionary.rb +4 -4
- data/lib/hexapdf/dictionary_fields.rb +1 -9
- 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/encoding/base.rb +8 -0
- data/lib/hexapdf/font/encoding/difference_encoding.rb +6 -0
- 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/type1_wrapper.rb +1 -1
- data/lib/hexapdf/image_loader/png.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 +87 -3
- data/lib/hexapdf/pdf_array.rb +11 -4
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +6 -4
- data/lib/hexapdf/tokenizer.rb +4 -3
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +132 -28
- data/lib/hexapdf/type/acro_form/button_field.rb +21 -13
- 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 +15 -2
- data/lib/hexapdf/type/image.rb +2 -2
- data/lib/hexapdf/type/page.rb +37 -13
- data/lib/hexapdf/type/page_tree_node.rb +29 -5
- data/lib/hexapdf/type/resources.rb +1 -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 +6 -1
- 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/encoding/test_base.rb +10 -0
- data/test/hexapdf/font/encoding/test_difference_encoding.rb +8 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +4 -3
- data/test/hexapdf/layout/test_style.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +12 -5
- data/test/hexapdf/test_configuration.rb +2 -2
- data/test/hexapdf/test_dictionary.rb +3 -1
- data/test/hexapdf/test_dictionary_fields.rb +2 -2
- data/test/hexapdf/test_document.rb +18 -10
- data/test/hexapdf/test_object.rb +71 -26
- data/test/hexapdf/test_parser.rb +159 -53
- data/test/hexapdf/test_pdf_array.rb +8 -1
- data/test/hexapdf/test_revisions.rb +35 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +296 -38
- data/test/hexapdf/type/acro_form/test_button_field.rb +22 -2
- 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 +26 -1
- data/test/hexapdf/type/test_page.rb +45 -7
- data/test/hexapdf/type/test_page_tree_node.rb +42 -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 -11
|
@@ -107,6 +107,13 @@ describe HexaPDF::PDFArray do
|
|
|
107
107
|
assert_equal([1, :data, @array[2]], @array[0, 5])
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
+
it "allows deleting an object" do
|
|
111
|
+
obj = @array.value[1]
|
|
112
|
+
assert_same(obj, @array.delete(obj))
|
|
113
|
+
ref = HexaPDF::Object.new(:test, oid: 1)
|
|
114
|
+
assert_equal(ref, @array.delete(ref))
|
|
115
|
+
end
|
|
116
|
+
|
|
110
117
|
describe "slice!" do
|
|
111
118
|
it "allows deleting a single element" do
|
|
112
119
|
@array.slice!(2)
|
|
@@ -157,6 +164,6 @@ describe HexaPDF::PDFArray do
|
|
|
157
164
|
end
|
|
158
165
|
|
|
159
166
|
it "can be converted to a simple array" do
|
|
160
|
-
assert_equal(@array
|
|
167
|
+
assert_equal([1, :data, "deref", @array[3]], @array.to_ary)
|
|
161
168
|
end
|
|
162
169
|
end
|
|
@@ -158,4 +158,39 @@ describe HexaPDF::Revisions do
|
|
|
158
158
|
doc = HexaPDF::Document.new(io: io)
|
|
159
159
|
assert_equal(2, doc.revisions.count)
|
|
160
160
|
end
|
|
161
|
+
|
|
162
|
+
it "uses the reconstructed revision if errors are found when loading from an IO" do
|
|
163
|
+
io = StringIO.new(<<~EOF)
|
|
164
|
+
%PDF-1.7
|
|
165
|
+
1 0 obj
|
|
166
|
+
10
|
|
167
|
+
endobj
|
|
168
|
+
|
|
169
|
+
xref
|
|
170
|
+
0 2
|
|
171
|
+
0000000000 65535 f
|
|
172
|
+
0000000009 00000 n
|
|
173
|
+
trailer
|
|
174
|
+
<< /Size 5 >>
|
|
175
|
+
startxref
|
|
176
|
+
28
|
|
177
|
+
%%EOF
|
|
178
|
+
|
|
179
|
+
2 0 obj
|
|
180
|
+
300
|
|
181
|
+
endobj
|
|
182
|
+
|
|
183
|
+
xref
|
|
184
|
+
2 1
|
|
185
|
+
0000000301 00000 n
|
|
186
|
+
trailer
|
|
187
|
+
<< /Size 3 /Prev 100>>
|
|
188
|
+
startxref
|
|
189
|
+
139
|
|
190
|
+
%%EOF
|
|
191
|
+
EOF
|
|
192
|
+
doc = HexaPDF::Document.new(io: io)
|
|
193
|
+
assert_equal(2, doc.revisions.count)
|
|
194
|
+
assert_same(doc.revisions[0].trailer.value, doc.revisions[1].trailer.value)
|
|
195
|
+
end
|
|
161
196
|
end
|
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.0)>>
|
|
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.0)>>
|
|
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,58 @@ 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
|
-
@field.field_value = ''
|
|
401
|
-
end
|
|
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
|
|
402
410
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
end
|
|
411
|
+
@field[:V] = 'test1'
|
|
412
|
+
@generator.create_appearances
|
|
413
|
+
assert_same(form, @widget[:AP][:N])
|
|
414
|
+
refute(form.key?(:key))
|
|
415
|
+
assert_match(/test1/, form.contents)
|
|
416
|
+
end
|
|
410
417
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
418
|
+
describe "font size calculation" do
|
|
419
|
+
before do
|
|
420
|
+
@widget[:Rect].height = 20
|
|
421
|
+
@widget[:Rect].width = 100
|
|
422
|
+
@field.field_value = ''
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
it "uses the non-zero font size" do
|
|
426
|
+
@field.set_default_appearance_string(font_size: 10)
|
|
427
|
+
@generator.create_appearances
|
|
428
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
429
|
+
[:set_font_and_size, [:F1, 10]],
|
|
430
|
+
range: 5)
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
it "calculates the font size based on the rectangle height and border width" do
|
|
434
|
+
@generator.create_appearances
|
|
435
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
436
|
+
[:set_font_and_size, [:F1, 12.923875]],
|
|
437
|
+
range: 5)
|
|
438
|
+
@widget.border_style(width: 2, color: :transparent)
|
|
439
|
+
@generator.create_appearances
|
|
440
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
441
|
+
[:set_font_and_size, [:F1, 11.487889]],
|
|
442
|
+
range: 5)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
it " in case of mulitline auto-sizing" do
|
|
446
|
+
@field.initialize_as_multiline_text_field
|
|
447
|
+
@field[:V] = 'a'
|
|
448
|
+
@field.set_default_appearance_string(font_size: 0)
|
|
449
|
+
@generator.create_appearances
|
|
450
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
451
|
+
[:set_font_and_size, [:F1, 12]],
|
|
452
|
+
range: 6)
|
|
422
453
|
end
|
|
454
|
+
end
|
|
423
455
|
|
|
456
|
+
describe "single line text fields" do
|
|
424
457
|
describe "quadding e.g. text alignment" do
|
|
425
458
|
before do
|
|
426
459
|
@field.field_value = 'Test'
|
|
@@ -478,7 +511,166 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
478
511
|
[:restore_graphics_state],
|
|
479
512
|
[:end_marked_content]])
|
|
480
513
|
end
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
describe "multiline text fields" do
|
|
517
|
+
before do
|
|
518
|
+
@field.set_default_appearance_string(font_size: 10)
|
|
519
|
+
@field.initialize_as_multiline_text_field
|
|
520
|
+
@widget[:Rect].height = 30
|
|
521
|
+
@widget[:Rect].width = 100
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
describe "quadding e.g. text alignment" do
|
|
525
|
+
before do
|
|
526
|
+
@field[:V] = "Test\nValue"
|
|
527
|
+
end
|
|
481
528
|
|
|
529
|
+
it "works for left aligned text" do
|
|
530
|
+
@field.text_alignment(:left)
|
|
531
|
+
@generator.create_appearances
|
|
532
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
533
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 16.195]],
|
|
534
|
+
range: 9)
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
it "works for right aligned text" do
|
|
538
|
+
@field.text_alignment(:right)
|
|
539
|
+
@generator.create_appearances
|
|
540
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
541
|
+
[:set_text_matrix, [1, 0, 0, 1, 78.55, 16.195]],
|
|
542
|
+
range: 9)
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
it "works for center aligned text" do
|
|
546
|
+
@field.text_alignment(:center)
|
|
547
|
+
@generator.create_appearances
|
|
548
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
549
|
+
[:set_text_matrix, [1, 0, 0, 1, 40.275, 16.195]],
|
|
550
|
+
range: 9)
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
it "creates the /N appearance stream according to the set string" do
|
|
555
|
+
@field.field_value = "Test\nValue"
|
|
556
|
+
@generator.create_appearances
|
|
557
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
558
|
+
[[:begin_marked_content, [:Tx]],
|
|
559
|
+
[:save_graphics_state],
|
|
560
|
+
[:append_rectangle, [1, 1, 98, 28]],
|
|
561
|
+
[:clip_path_non_zero],
|
|
562
|
+
[:end_path],
|
|
563
|
+
[:save_graphics_state],
|
|
564
|
+
[:set_leading, [11.5625]],
|
|
565
|
+
[:set_font_and_size, [:F1, 10]],
|
|
566
|
+
[:begin_text],
|
|
567
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 16.195]],
|
|
568
|
+
[:show_text, ['Test']],
|
|
569
|
+
[:move_text_next_line],
|
|
570
|
+
[:show_text, ['Value']],
|
|
571
|
+
[:end_text],
|
|
572
|
+
[:restore_graphics_state],
|
|
573
|
+
[:restore_graphics_state],
|
|
574
|
+
[:end_marked_content]])
|
|
575
|
+
|
|
576
|
+
@field.field_value = "Test\nTest\nTest"
|
|
577
|
+
@field.set_default_appearance_string(font_size: 0)
|
|
578
|
+
@generator.create_appearances
|
|
579
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
580
|
+
[[:begin_marked_content, [:Tx]],
|
|
581
|
+
[:save_graphics_state],
|
|
582
|
+
[:append_rectangle, [1, 1, 98, 28]],
|
|
583
|
+
[:clip_path_non_zero],
|
|
584
|
+
[:end_path],
|
|
585
|
+
[:save_graphics_state],
|
|
586
|
+
[:set_leading, [9.25]],
|
|
587
|
+
[:set_font_and_size, [:F1, 8]],
|
|
588
|
+
[:begin_text],
|
|
589
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 18.556]],
|
|
590
|
+
[:show_text, ['Test']],
|
|
591
|
+
[:move_text_next_line],
|
|
592
|
+
[:show_text, ['Test']],
|
|
593
|
+
[:move_text_next_line],
|
|
594
|
+
[:show_text, ['Test']],
|
|
595
|
+
[:end_text],
|
|
596
|
+
[:restore_graphics_state],
|
|
597
|
+
[:restore_graphics_state],
|
|
598
|
+
[:end_marked_content]],
|
|
599
|
+
)
|
|
600
|
+
end
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
describe "comb text fields" do
|
|
604
|
+
before do
|
|
605
|
+
@field.set_default_appearance_string(font_size: 10)
|
|
606
|
+
@field.initialize_as_comb_text_field
|
|
607
|
+
@field[:MaxLen] = 10
|
|
608
|
+
@widget[:Rect].height = 20
|
|
609
|
+
@widget[:Rect].width = 100
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
describe "quadding e.g. text alignment" do
|
|
613
|
+
before do
|
|
614
|
+
@field[:V] = 'Test'
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
it "works for left aligned text" do
|
|
618
|
+
@field.text_alignment(:left)
|
|
619
|
+
@generator.create_appearances
|
|
620
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
621
|
+
[:set_text_matrix, [1, 0, 0, 1, 2.945, 6.41]],
|
|
622
|
+
range: 7)
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
it "works for right aligned text" do
|
|
626
|
+
@field.text_alignment(:right)
|
|
627
|
+
@generator.create_appearances
|
|
628
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
629
|
+
[:set_text_matrix, [1, 0, 0, 1, 62.945, 6.41]],
|
|
630
|
+
range: 7)
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
it "works for center aligned text" do
|
|
634
|
+
@field.text_alignment(:center)
|
|
635
|
+
@generator.create_appearances
|
|
636
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
637
|
+
[:set_text_matrix, [1, 0, 0, 1, 32.945, 6.41]],
|
|
638
|
+
range: 7)
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
it "handles centering like Adobe, e.g. shift left, when text cannot be completely centered" do
|
|
642
|
+
@field.field_value = 'Hello'
|
|
643
|
+
@field.text_alignment(:center)
|
|
644
|
+
@generator.create_appearances
|
|
645
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
646
|
+
[:set_text_matrix, [1, 0, 0, 1, 22.39, 6.41]],
|
|
647
|
+
range: 7)
|
|
648
|
+
end
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
it "creates the /N appearance stream according to the set string" do
|
|
652
|
+
@field.field_value = 'Text'
|
|
653
|
+
@generator.create_appearances
|
|
654
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
655
|
+
[[:begin_marked_content, [:Tx]],
|
|
656
|
+
[:save_graphics_state],
|
|
657
|
+
[:append_rectangle, [1, 1, 98, 18]],
|
|
658
|
+
[:clip_path_non_zero],
|
|
659
|
+
[:end_path],
|
|
660
|
+
[:set_font_and_size, [:F1, 10]],
|
|
661
|
+
[:begin_text],
|
|
662
|
+
[:set_text_matrix, [1, 0, 0, 1, 2.945, 6.41]],
|
|
663
|
+
[:show_text_with_positioning, [['T', -416.5, 'e', -472, 'x', -611, 't']]],
|
|
664
|
+
[:end_text],
|
|
665
|
+
[:restore_graphics_state],
|
|
666
|
+
[:end_marked_content]])
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
it "fails if the /MaxLen key is not set" do
|
|
670
|
+
@field.delete(:MaxLen)
|
|
671
|
+
@field[:V] = 't'
|
|
672
|
+
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
673
|
+
end
|
|
482
674
|
end
|
|
483
675
|
|
|
484
676
|
describe "choice fields" do
|
|
@@ -486,27 +678,93 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
|
486
678
|
@form.set_default_appearance_string
|
|
487
679
|
field = @doc.add({FT: :Ch}, type: :XXAcroFormField, subtype: :Ch)
|
|
488
680
|
field.initialize_as_combo_box
|
|
681
|
+
field.flag(:edit)
|
|
682
|
+
field.field_value = 'Test'
|
|
489
683
|
widget = field.create_widget(@page, Rect: [0, 0, 0, 0])
|
|
490
684
|
generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(widget)
|
|
491
685
|
generator.create_appearances
|
|
492
686
|
assert_kind_of(HexaPDF::Type::Form, widget[:AP][:N])
|
|
493
687
|
end
|
|
688
|
+
|
|
689
|
+
describe "list boxes" do
|
|
690
|
+
before do
|
|
691
|
+
@field = @doc.add({FT: :Ch}, type: :XXAcroFormField, subtype: :Ch)
|
|
692
|
+
@field.initialize_as_list_box
|
|
693
|
+
@field.flag(:multi_select)
|
|
694
|
+
@field.option_items = ['a', 'b', 'c']
|
|
695
|
+
@widget = @field.create_widget(@page, Rect: [0, 0, 90, 36])
|
|
696
|
+
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
it "uses a fixed font size for list box items if auto-sizing is used" do
|
|
700
|
+
@field.set_default_appearance_string(font_size: 0)
|
|
701
|
+
@generator.create_appearances
|
|
702
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
703
|
+
[:set_font_and_size, [:F1, 12]],
|
|
704
|
+
range: 8)
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
it "uses the set values instead of the ones from /I if in conflict" do
|
|
708
|
+
@field[:I] = [0, 1]
|
|
709
|
+
@field[:V] = ['b']
|
|
710
|
+
@generator.create_appearances
|
|
711
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
712
|
+
[[:set_device_rgb_non_stroking_color, [0.6, 0.756863, 0.854902]],
|
|
713
|
+
[:append_rectangle, [1, 7.25, 88, 13.875]],
|
|
714
|
+
[:fill_path_non_zero]],
|
|
715
|
+
range: 5..7)
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
it "creates the /N appearance stream" do
|
|
719
|
+
@field[:I] = [1, 2]
|
|
720
|
+
@field[:V] = ['b', 'c']
|
|
721
|
+
@generator.create_appearances
|
|
722
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
723
|
+
[[:begin_marked_content, [:Tx]],
|
|
724
|
+
[:save_graphics_state],
|
|
725
|
+
[:append_rectangle, [1, 1, 88, 34]],
|
|
726
|
+
[:clip_path_non_zero], [:end_path],
|
|
727
|
+
[:set_device_rgb_non_stroking_color, [0.6, 0.756863, 0.854902]],
|
|
728
|
+
[:append_rectangle, [1, 7.25, 88, 13.875]],
|
|
729
|
+
[:append_rectangle, [1, -6.625, 88, 13.875]],
|
|
730
|
+
[:fill_path_non_zero],
|
|
731
|
+
[:save_graphics_state],
|
|
732
|
+
[:set_leading, [13.875]],
|
|
733
|
+
[:set_font_and_size, [:F1, 12]],
|
|
734
|
+
[:set_device_gray_non_stroking_color, [0.0]],
|
|
735
|
+
[:begin_text],
|
|
736
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 23.609]],
|
|
737
|
+
[:show_text, ["a"]],
|
|
738
|
+
[:move_text_next_line],
|
|
739
|
+
[:show_text, ["b"]],
|
|
740
|
+
[:end_text],
|
|
741
|
+
[:restore_graphics_state], [:restore_graphics_state],
|
|
742
|
+
[:end_marked_content]])
|
|
743
|
+
end
|
|
744
|
+
end
|
|
494
745
|
end
|
|
495
746
|
|
|
496
747
|
describe "font resolution in case the referenced font is not usable" do
|
|
497
748
|
before do
|
|
498
|
-
|
|
499
|
-
@field
|
|
749
|
+
@doc.config['acro_form.fallback_font'] = ['Times', {variant: :none}]
|
|
750
|
+
@field[:V] = 'Test'
|
|
500
751
|
end
|
|
501
752
|
|
|
502
|
-
it "uses the fallback font if
|
|
503
|
-
@
|
|
753
|
+
it "uses the fallback font if the font is not usable" do
|
|
754
|
+
def (@form.default_resources.font(:F1)).font_wrapper; nil; end
|
|
504
755
|
@generator.create_appearances
|
|
505
756
|
assert_equal(:'Times-Roman', @widget[:AP][:N][:Resources][:Font][:F2][:BaseFont])
|
|
506
757
|
end
|
|
507
758
|
|
|
759
|
+
it "uses the fallback font if the font is not found" do
|
|
760
|
+
@form.default_resources[:Font].delete(:F1)
|
|
761
|
+
@generator.create_appearances
|
|
762
|
+
assert_equal(:'Times-Roman', @widget[:AP][:N][:Resources][:Font][:F1][:BaseFont])
|
|
763
|
+
end
|
|
764
|
+
|
|
508
765
|
it "fails if fallback fonts are disabled" do
|
|
509
766
|
@doc.config['acro_form.fallback_font'] = nil
|
|
767
|
+
@form.default_resources[:Font].delete(:F1)
|
|
510
768
|
msg = assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
511
769
|
assert_match(/Font.*not usable/, msg.message)
|
|
512
770
|
end
|