hexapdf 0.11.9 → 0.12.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 +82 -0
- data/LICENSE +1 -1
- data/examples/001-hello_world.rb +1 -1
- data/examples/002-graphics.rb +1 -1
- data/examples/003-arcs.rb +1 -1
- data/examples/004-optimizing.rb +1 -1
- data/examples/005-merging.rb +1 -1
- data/examples/006-standard_pdf_fonts.rb +1 -1
- data/examples/007-truetype.rb +1 -1
- data/examples/008-show_char_bboxes.rb +1 -1
- data/examples/009-text_layouter_alignment.rb +1 -1
- data/examples/010-text_layouter_inline_boxes.rb +1 -1
- data/examples/011-text_layouter_line_wrapping.rb +1 -1
- data/examples/012-text_layouter_styling.rb +1 -1
- data/examples/013-text_layouter_shapes.rb +1 -1
- data/examples/014-text_in_polygon.rb +1 -1
- data/examples/015-boxes.rb +1 -1
- data/examples/016-frame_automatic_box_placement.rb +1 -1
- data/examples/017-frame_text_flow.rb +1 -1
- data/examples/018-composer.rb +1 -1
- data/examples/019-acro_form.rb +51 -0
- data/lib/hexapdf.rb +1 -1
- data/lib/hexapdf/cli.rb +3 -1
- data/lib/hexapdf/cli/batch.rb +1 -1
- data/lib/hexapdf/cli/command.rb +18 -9
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/form.rb +240 -0
- data/lib/hexapdf/cli/image2pdf.rb +1 -1
- data/lib/hexapdf/cli/images.rb +1 -1
- data/lib/hexapdf/cli/info.rb +1 -1
- data/lib/hexapdf/cli/inspect.rb +1 -1
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/modify.rb +1 -1
- data/lib/hexapdf/cli/optimize.rb +1 -1
- data/lib/hexapdf/cli/split.rb +1 -1
- data/lib/hexapdf/cli/watermark.rb +1 -1
- data/lib/hexapdf/composer.rb +2 -2
- data/lib/hexapdf/configuration.rb +66 -11
- data/lib/hexapdf/content.rb +3 -1
- data/lib/hexapdf/content/canvas.rb +5 -18
- data/lib/hexapdf/content/color_space.rb +111 -32
- data/lib/hexapdf/content/graphic_object.rb +1 -1
- data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
- data/lib/hexapdf/content/graphics_state.rb +1 -1
- data/lib/hexapdf/content/operator.rb +9 -9
- data/lib/hexapdf/content/parser.rb +18 -5
- data/lib/hexapdf/content/processor.rb +1 -1
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/data_dir.rb +1 -1
- data/lib/hexapdf/dictionary.rb +1 -1
- data/lib/hexapdf/dictionary_fields.rb +1 -1
- data/lib/hexapdf/document.rb +14 -5
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/images.rb +1 -1
- data/lib/hexapdf/document/pages.rb +3 -14
- data/lib/hexapdf/encryption.rb +1 -1
- data/lib/hexapdf/encryption/aes.rb +1 -1
- data/lib/hexapdf/encryption/arc4.rb +1 -1
- data/lib/hexapdf/encryption/fast_aes.rb +1 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +1 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
- data/lib/hexapdf/error.rb +1 -1
- data/lib/hexapdf/filter.rb +3 -3
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/encryption.rb +1 -1
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/{jpx_decode.rb → pass_through.rb} +5 -5
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/font/cmap.rb +1 -1
- data/lib/hexapdf/font/cmap/parser.rb +1 -1
- data/lib/hexapdf/font/cmap/writer.rb +1 -1
- data/lib/hexapdf/font/encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/base.rb +1 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +1 -1
- data/lib/hexapdf/font/true_type.rb +1 -1
- data/lib/hexapdf/font/true_type/builder.rb +1 -1
- data/lib/hexapdf/font/true_type/font.rb +1 -1
- data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
- data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
- data/lib/hexapdf/font/true_type/table.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
- data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
- data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
- data/lib/hexapdf/font/true_type/table/head.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
- data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
- data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
- data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
- data/lib/hexapdf/font/true_type/table/name.rb +1 -1
- data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
- data/lib/hexapdf/font/true_type/table/post.rb +1 -1
- data/lib/hexapdf/font/true_type_wrapper.rb +54 -51
- data/lib/hexapdf/font/type1.rb +1 -1
- data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
- data/lib/hexapdf/font/type1_wrapper.rb +67 -51
- data/lib/hexapdf/font_loader.rb +1 -1
- data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
- data/lib/hexapdf/font_loader/from_file.rb +1 -1
- data/lib/hexapdf/font_loader/standard14.rb +1 -1
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/image_loader/jpeg.rb +1 -1
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +1 -1
- data/lib/hexapdf/importer.rb +2 -4
- data/lib/hexapdf/layout.rb +1 -1
- data/lib/hexapdf/layout/box.rb +1 -1
- data/lib/hexapdf/layout/frame.rb +1 -1
- data/lib/hexapdf/layout/image_box.rb +1 -1
- data/lib/hexapdf/layout/inline_box.rb +1 -1
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
- data/lib/hexapdf/layout/style.rb +1 -1
- data/lib/hexapdf/layout/text_box.rb +1 -1
- data/lib/hexapdf/layout/text_fragment.rb +1 -1
- data/lib/hexapdf/layout/text_layouter.rb +1 -1
- data/lib/hexapdf/layout/text_shaper.rb +1 -1
- data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +2 -2
- data/lib/hexapdf/parser.rb +4 -3
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/rectangle.rb +31 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +2 -1
- data/lib/hexapdf/revisions.rb +1 -1
- data/lib/hexapdf/serializer.rb +1 -1
- data/lib/hexapdf/stream.rb +1 -1
- data/lib/hexapdf/task.rb +1 -1
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +1 -1
- data/lib/hexapdf/tokenizer.rb +1 -1
- data/lib/hexapdf/type.rb +1 -1
- data/lib/hexapdf/type/acro_form.rb +7 -1
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +401 -0
- data/lib/hexapdf/type/acro_form/button_field.rb +300 -0
- data/lib/hexapdf/type/acro_form/choice_field.rb +220 -0
- data/lib/hexapdf/type/acro_form/field.rb +220 -17
- data/lib/hexapdf/type/acro_form/form.rb +157 -7
- data/lib/hexapdf/type/acro_form/text_field.rb +186 -0
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +122 -0
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/actions.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +1 -1
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
- data/lib/hexapdf/type/actions/launch.rb +1 -1
- data/lib/hexapdf/type/actions/uri.rb +1 -1
- data/lib/hexapdf/type/annotation.rb +73 -3
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/annotations/link.rb +2 -2
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/text.rb +1 -1
- data/lib/hexapdf/type/annotations/widget.rb +239 -2
- data/lib/hexapdf/type/catalog.rb +21 -1
- data/lib/hexapdf/type/cid_font.rb +1 -1
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font.rb +18 -1
- data/lib/hexapdf/type/font_descriptor.rb +2 -2
- data/lib/hexapdf/type/font_simple.rb +1 -1
- data/lib/hexapdf/type/font_true_type.rb +1 -1
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/font_type1.rb +16 -1
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +1 -1
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/type/icon_fit.rb +1 -1
- data/lib/hexapdf/type/image.rb +3 -1
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/names.rb +1 -1
- data/lib/hexapdf/type/object_stream.rb +1 -1
- data/lib/hexapdf/type/page.rb +1 -1
- data/lib/hexapdf/type/page_tree_node.rb +8 -11
- data/lib/hexapdf/type/resources.rb +16 -3
- data/lib/hexapdf/type/trailer.rb +2 -3
- data/lib/hexapdf/type/viewer_preferences.rb +1 -1
- data/lib/hexapdf/type/xref_stream.rb +1 -1
- data/lib/hexapdf/utils/bit_field.rb +38 -24
- data/lib/hexapdf/utils/bit_stream.rb +1 -1
- data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
- data/lib/hexapdf/utils/lru_cache.rb +1 -1
- data/lib/hexapdf/utils/math_helpers.rb +1 -1
- data/lib/hexapdf/utils/object_hash.rb +1 -1
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
- data/lib/hexapdf/version.rb +2 -2
- data/lib/hexapdf/writer.rb +1 -1
- data/lib/hexapdf/xref_section.rb +1 -1
- data/test/hexapdf/content/common.rb +2 -2
- data/test/hexapdf/content/test_color_space.rb +71 -8
- data/test/hexapdf/content/test_operator.rb +22 -22
- data/test/hexapdf/content/test_parser.rb +14 -0
- data/test/hexapdf/document/test_fonts.rb +1 -1
- data/test/hexapdf/document/test_pages.rb +6 -6
- data/test/hexapdf/font/test_true_type_wrapper.rb +10 -7
- data/test/hexapdf/font/test_type1_wrapper.rb +32 -8
- data/test/hexapdf/test_document.rb +12 -0
- data/test/hexapdf/test_parser.rb +10 -0
- data/test/hexapdf/test_rectangle.rb +14 -0
- data/test/hexapdf/test_revision.rb +3 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +515 -0
- data/test/hexapdf/type/acro_form/test_button_field.rb +276 -0
- data/test/hexapdf/type/acro_form/test_choice_field.rb +137 -0
- data/test/hexapdf/type/acro_form/test_field.rb +124 -6
- data/test/hexapdf/type/acro_form/test_form.rb +189 -22
- data/test/hexapdf/type/acro_form/test_text_field.rb +119 -0
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +77 -0
- data/test/hexapdf/type/annotations/test_text.rb +1 -1
- data/test/hexapdf/type/annotations/test_widget.rb +199 -0
- data/test/hexapdf/type/test_annotation.rb +45 -0
- data/test/hexapdf/type/test_catalog.rb +18 -0
- data/test/hexapdf/type/test_font.rb +5 -0
- data/test/hexapdf/type/test_font_type1.rb +8 -0
- data/test/hexapdf/type/test_image.rb +7 -0
- data/test/hexapdf/type/test_page_tree_node.rb +20 -12
- data/test/hexapdf/type/test_resources.rb +20 -0
- data/test/hexapdf/type/test_trailer.rb +4 -0
- data/test/hexapdf/utils/test_bit_field.rb +13 -1
- data/test/test_helper.rb +1 -1
- metadata +37 -18
- data/lib/hexapdf/filter/dct_decode.rb +0 -60
|
@@ -12,6 +12,14 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
|
12
12
|
@symbol_wrapper = HexaPDF::Font::Type1Wrapper.new(@doc, FONT_SYMBOL)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
it "can be used with an existing PDF object" do
|
|
16
|
+
font = @doc.add({Type: :Font, Subtype: :Type1, Encoding: :WinAnsiEncoding,
|
|
17
|
+
BaseFont: :"Times-Roman"})
|
|
18
|
+
wrapper = HexaPDF::Font::Type1Wrapper.new(@doc, FONT_TIMES, pdf_object: font)
|
|
19
|
+
assert_equal([:T, :e, :s, :t], wrapper.decode_utf8("Test").map(&:name))
|
|
20
|
+
assert_equal("a", wrapper.encode(wrapper.glyph(:a)))
|
|
21
|
+
end
|
|
22
|
+
|
|
15
23
|
it "returns 1 for the scaling factor" do
|
|
16
24
|
assert_equal(1, @times_wrapper.scaling_factor)
|
|
17
25
|
end
|
|
@@ -21,6 +29,10 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
|
21
29
|
assert_equal([:T, :e, :s, :t], @times_wrapper.decode_utf8("Test").map(&:name))
|
|
22
30
|
end
|
|
23
31
|
|
|
32
|
+
it "falls back to the internal font encoding if the Unicode codepoint is not mapped" do
|
|
33
|
+
assert_equal([:Delta, :Delta], @symbol_wrapper.decode_utf8("D∆").map(&:name))
|
|
34
|
+
end
|
|
35
|
+
|
|
24
36
|
it "UTF-8 characters for which no glyph name exists, are mapped to InvalidGlyph objects" do
|
|
25
37
|
glyphs = @times_wrapper.decode_utf8("😁")
|
|
26
38
|
assert_equal(1, glyphs.length)
|
|
@@ -58,7 +70,7 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
|
58
70
|
code = @times_wrapper.encode(@times_wrapper.glyph(:a))
|
|
59
71
|
@doc.dispatch_message(:complete_objects)
|
|
60
72
|
assert_equal("a", code)
|
|
61
|
-
assert_equal(:WinAnsiEncoding, @times_wrapper.
|
|
73
|
+
assert_equal(:WinAnsiEncoding, @times_wrapper.pdf_object[:Encoding])
|
|
62
74
|
end
|
|
63
75
|
|
|
64
76
|
it "fails if an InvalidGlyph is encoded" do
|
|
@@ -70,13 +82,25 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
|
70
82
|
end
|
|
71
83
|
end
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
it "uses the font's internal encoding for fonts with the Special character set" do
|
|
86
|
+
code = @symbol_wrapper.encode(@symbol_wrapper.glyph(:plus))
|
|
87
|
+
@doc.dispatch_message(:complete_objects)
|
|
88
|
+
assert_equal("+", code)
|
|
89
|
+
assert_nil(@symbol_wrapper.pdf_object[:Encoding])
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "uses an empty encoding as initial encoding if a custom encoding is needed" do
|
|
93
|
+
wrapper = HexaPDF::Font::Type1Wrapper.new(@doc, FONT_TIMES, custom_encoding: true)
|
|
94
|
+
code = wrapper.encode(wrapper.glyph(:plus))
|
|
95
|
+
@doc.dispatch_message(:complete_objects)
|
|
96
|
+
assert_equal("\x21", code)
|
|
97
|
+
assert_equal({Differences: [32, :space, :plus]}, wrapper.pdf_object[:Encoding].value)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe "creates the necessary PDF dictionaries" do
|
|
102
|
+
it "sets the circular reference" do
|
|
103
|
+
assert_same(@times_wrapper, @times_wrapper.pdf_object.font_wrapper)
|
|
80
104
|
end
|
|
81
105
|
end
|
|
82
106
|
end
|
|
@@ -584,6 +584,18 @@ describe HexaPDF::Document do
|
|
|
584
584
|
end
|
|
585
585
|
end
|
|
586
586
|
|
|
587
|
+
describe "acro_form" do
|
|
588
|
+
it "returns the main AcroForm object" do
|
|
589
|
+
assert_nil(@doc.acro_form)
|
|
590
|
+
@doc.catalog[:AcroForm] = 5
|
|
591
|
+
assert_equal(5, @doc.acro_form)
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
it "create the AcroForm object if instructed" do
|
|
595
|
+
assert_equal(:XXAcroForm, @doc.acro_form(create: true).type)
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
|
|
587
599
|
describe "listener interface" do
|
|
588
600
|
it "allows registering and dispatching messages" do
|
|
589
601
|
args = []
|
data/test/hexapdf/test_parser.rb
CHANGED
|
@@ -231,6 +231,10 @@ describe HexaPDF::Parser do
|
|
|
231
231
|
create_parser("startxref\n5")
|
|
232
232
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.startxref_offset }
|
|
233
233
|
assert_match(/end-of-file marker not found/, exp.message)
|
|
234
|
+
|
|
235
|
+
create_parser("")
|
|
236
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.startxref_offset }
|
|
237
|
+
assert_match(/end-of-file marker not found/, exp.message)
|
|
234
238
|
end
|
|
235
239
|
|
|
236
240
|
it "fails if the startxref keyword is missing" do
|
|
@@ -251,6 +255,12 @@ describe HexaPDF::Parser do
|
|
|
251
255
|
assert_match(/file header/, exp.message)
|
|
252
256
|
end
|
|
253
257
|
|
|
258
|
+
it "fails if the header is missing" do
|
|
259
|
+
create_parser("no header")
|
|
260
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.file_header_version }
|
|
261
|
+
assert_match(/file header/, exp.message)
|
|
262
|
+
end
|
|
263
|
+
|
|
254
264
|
it "ignores junk at the beginning of the file and correctly calculates offset" do
|
|
255
265
|
create_parser("junk" * 200 + "\n%PDF-1.4\n")
|
|
256
266
|
assert_equal('1.4', @parser.file_header_version)
|
|
@@ -36,6 +36,20 @@ describe HexaPDF::Rectangle do
|
|
|
36
36
|
assert_equal(4, rect.height)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
it "allows setting all fields of the rectangle" do
|
|
40
|
+
rect = HexaPDF::Rectangle.new([2, 1, 0, 5])
|
|
41
|
+
rect.left = 5
|
|
42
|
+
rect.right = 1
|
|
43
|
+
rect.bottom = 2
|
|
44
|
+
rect.top = 3
|
|
45
|
+
assert_equal([5, 2, 1, 3], rect.value)
|
|
46
|
+
|
|
47
|
+
rect.width = 10
|
|
48
|
+
assert_equal(15, rect.right)
|
|
49
|
+
rect.height = 10
|
|
50
|
+
assert_equal(12, rect.top)
|
|
51
|
+
end
|
|
52
|
+
|
|
39
53
|
it "allows comparison to arrays" do
|
|
40
54
|
rect = HexaPDF::Rectangle.new([0, 1, 2, 5])
|
|
41
55
|
assert(rect == [0, 1, 2, 5])
|
|
@@ -114,12 +114,14 @@ describe HexaPDF::Revision do
|
|
|
114
114
|
@rev.delete(@ref, mark_as_free: false)
|
|
115
115
|
refute(@rev.object?(@ref))
|
|
116
116
|
assert(@obj.null?)
|
|
117
|
+
assert_raises(HexaPDF::Error) { @obj.document }
|
|
117
118
|
end
|
|
118
119
|
|
|
119
120
|
it "deletes objects specified by object number" do
|
|
120
121
|
@rev.delete(@ref.oid, mark_as_free: false)
|
|
121
122
|
refute(@rev.object?(@ref.oid))
|
|
122
123
|
assert(@obj.null?)
|
|
124
|
+
assert_raises(HexaPDF::Error) { @obj.document }
|
|
123
125
|
end
|
|
124
126
|
|
|
125
127
|
it "marks the object as PDF null object when using mark_as_free=true" do
|
|
@@ -127,6 +129,7 @@ describe HexaPDF::Revision do
|
|
|
127
129
|
@rev.delete(@ref)
|
|
128
130
|
assert(@rev.object(@ref).null?)
|
|
129
131
|
assert(@obj.null?)
|
|
132
|
+
assert_raises(HexaPDF::Error) { @obj.document }
|
|
130
133
|
end
|
|
131
134
|
end
|
|
132
135
|
|
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.12.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.12.0)>>
|
|
76
76
|
endobj
|
|
77
77
|
2 0 obj
|
|
78
78
|
<</Length 10>>stream
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require_relative '../../content/common'
|
|
5
|
+
require 'hexapdf/document'
|
|
6
|
+
require 'hexapdf/type/acro_form/appearance_generator'
|
|
7
|
+
|
|
8
|
+
describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
9
|
+
before do
|
|
10
|
+
@doc = HexaPDF::Document.new
|
|
11
|
+
@page = @doc.pages.add
|
|
12
|
+
@form = @doc.acro_form(create: true)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe "create_appearances" do
|
|
16
|
+
before do
|
|
17
|
+
@field = @doc.add({FT: :Btn}, type: :XXAcroFormField, subtype: :Btn)
|
|
18
|
+
@widget = @doc.wrap({Parent: @field, Type: :Annot, Subtype: :Widget})
|
|
19
|
+
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "fails for unsupported button fields" do
|
|
23
|
+
@field.flag(:push_button)
|
|
24
|
+
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
25
|
+
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
26
|
+
end
|
|
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
|
+
it "fails for unsupported field types" do
|
|
37
|
+
@field[:FT] = :Unknown
|
|
38
|
+
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "background color and border" do
|
|
43
|
+
before do
|
|
44
|
+
@field = @doc.add({FT: :Btn}, type: :XXAcroFormField, subtype: :Btn)
|
|
45
|
+
@widget = @field.create_widget(@page, defaults: false, Rect: [0, 0, 10, 20])
|
|
46
|
+
@xform = @doc.add({Type: :XObject, Subtype: :Form, BBox: @widget[:Rect]})
|
|
47
|
+
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def execute(circular = false)
|
|
51
|
+
@generator.send(:apply_background_and_border, @widget.border_style, @xform.canvas,
|
|
52
|
+
circular: circular)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "applies no background color or border if none is set" do
|
|
56
|
+
execute
|
|
57
|
+
assert_operators(@xform.stream, [])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "applies a background color if one set" do
|
|
61
|
+
@widget.background_color(0.5)
|
|
62
|
+
execute
|
|
63
|
+
execute(true)
|
|
64
|
+
assert_operators(@xform.stream,
|
|
65
|
+
[[:save_graphics_state],
|
|
66
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
67
|
+
[:append_rectangle, [0, 0, 10, 20]],
|
|
68
|
+
[:fill_path_non_zero], [:restore_graphics_state],
|
|
69
|
+
|
|
70
|
+
[:save_graphics_state],
|
|
71
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
72
|
+
[:move_to, [10.0, 10.0]],
|
|
73
|
+
[:curve_to, [10.0, 11.78411, 9.045085, 13.438072, 7.5, 14.330127]],
|
|
74
|
+
[:curve_to, [5.954915, 15.222182, 4.045085, 15.222182, 2.5, 14.330127]],
|
|
75
|
+
[:curve_to, [0.954915, 13.438072, 0.0, 11.78411, 0.0, 10.0]],
|
|
76
|
+
[:curve_to, [-0.0, 8.21589, 0.954915, 6.561928, 2.5, 5.669873]],
|
|
77
|
+
[:curve_to, [4.045085, 4.777818, 5.954915, 4.777818, 7.5, 5.669873]],
|
|
78
|
+
[:curve_to, [9.045085, 6.561928, 10.0, 8.21589, 10.0, 10.0]],
|
|
79
|
+
[:close_subpath], [:fill_path_non_zero], [:restore_graphics_state]])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "sets the border color and width correctly" do
|
|
83
|
+
@widget.border_style(color: 0.5, width: 4)
|
|
84
|
+
execute
|
|
85
|
+
execute(true)
|
|
86
|
+
assert_operators(@xform.stream,
|
|
87
|
+
[[:save_graphics_state],
|
|
88
|
+
[:set_device_gray_stroking_color, [0.5]],
|
|
89
|
+
[:set_line_width, [4]],
|
|
90
|
+
[:append_rectangle, [2, 2, 6, 16]],
|
|
91
|
+
[:stroke_path], [:restore_graphics_state],
|
|
92
|
+
|
|
93
|
+
[:save_graphics_state],
|
|
94
|
+
[:set_device_gray_stroking_color, [0.5]],
|
|
95
|
+
[:set_line_width, [4]],
|
|
96
|
+
[:move_to, [8.0, 10.0]],
|
|
97
|
+
[:curve_to, [8.0, 11.070466, 7.427051, 12.062843, 6.5, 12.598076]],
|
|
98
|
+
[:curve_to, [5.572949, 13.133309, 4.427051, 13.133309, 3.5, 12.598076]],
|
|
99
|
+
[:curve_to, [2.572949, 12.062843, 2.0, 11.070466, 2.0, 10.0]],
|
|
100
|
+
[:curve_to, [2.0, 8.929534, 2.572949, 7.937157, 3.5, 7.401924]],
|
|
101
|
+
[:curve_to, [4.427051, 6.866691, 5.572949, 6.866691, 6.5, 7.401924]],
|
|
102
|
+
[:curve_to, [7.427051, 7.937157, 8.0, 8.929534, 8.0, 10.0]],
|
|
103
|
+
[:close_subpath], [:stroke_path], [:restore_graphics_state]])
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "handles the case of an underlined border" do
|
|
107
|
+
@widget.border_style(style: :underlined, width: 2)
|
|
108
|
+
execute
|
|
109
|
+
execute(true)
|
|
110
|
+
assert_operators(@xform.stream,
|
|
111
|
+
[[:save_graphics_state],
|
|
112
|
+
[:set_line_width, [2]],
|
|
113
|
+
[:move_to, [1, 1]], [:line_to, [9.0, 1]],
|
|
114
|
+
[:stroke_path], [:restore_graphics_state],
|
|
115
|
+
|
|
116
|
+
[:save_graphics_state],
|
|
117
|
+
[:set_line_width, [2]],
|
|
118
|
+
[:move_to, [1.0, 10.0]],
|
|
119
|
+
[:curve_to, [1.0, 8.572712, 1.763932, 7.249543, 3.0, 6.535898]],
|
|
120
|
+
[:curve_to, [4.236068, 5.822254, 5.763932, 5.822254, 7.0, 6.535898]],
|
|
121
|
+
[:curve_to, [8.236068, 7.249543, 9.0, 8.572712, 9.0, 10.0]],
|
|
122
|
+
[:stroke_path], [:restore_graphics_state]])
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
describe "draw_marker" do
|
|
127
|
+
before do
|
|
128
|
+
@field = @doc.add({FT: :Btn}, type: :XXAcroFormField, subtype: :Btn)
|
|
129
|
+
@widget = @field.create_widget(@page, defaults: false, Rect: [0, 0, 10, 20])
|
|
130
|
+
@xform = @doc.add({Type: :XObject, Subtype: :Form, BBox: @widget[:Rect]})
|
|
131
|
+
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def execute
|
|
135
|
+
@generator.send(:draw_marker, @xform.canvas, @widget[:Rect], @widget.border_style.width,
|
|
136
|
+
@widget.marker_style)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "handles the marker :circle specially for radio button widgets" do
|
|
140
|
+
@field.initialize_as_radio_button
|
|
141
|
+
@widget.marker_style(style: :circle, color: 0.5)
|
|
142
|
+
execute
|
|
143
|
+
assert_operators(@xform.stream,
|
|
144
|
+
[[:set_device_gray_non_stroking_color, [0.5]],
|
|
145
|
+
[:move_to, [7.0, 10.0]],
|
|
146
|
+
[:curve_to, [7.0, 10.713644, 6.618034, 11.375229, 6.0, 11.732051]],
|
|
147
|
+
[:curve_to, [5.381966, 12.088873, 4.618034, 12.088873, 4.0, 11.732051]],
|
|
148
|
+
[:curve_to, [3.381966, 11.375229, 3.0, 10.713644, 3.0, 10.0]],
|
|
149
|
+
[:curve_to, [3.0, 9.286356, 3.381966, 8.624771, 4.0, 8.267949]],
|
|
150
|
+
[:curve_to, [4.618034, 7.911127, 5.381966, 7.911127, 6.0, 8.267949]],
|
|
151
|
+
[:curve_to, [6.618034, 8.624771, 7.0, 9.286356, 7.0, 10.0]],
|
|
152
|
+
[:close_subpath],
|
|
153
|
+
[:fill_path_non_zero]])
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "handles the marker :cross specially" do
|
|
157
|
+
@widget.marker_style(style: :cross, color: 0.5)
|
|
158
|
+
execute
|
|
159
|
+
assert_operators(@xform.stream,
|
|
160
|
+
[[:set_device_gray_stroking_color, [0.5]],
|
|
161
|
+
[:move_to, [1, 1]], [:line_to, [9, 19]],
|
|
162
|
+
[:move_to, [1, 19]], [:line_to, [9, 1]],
|
|
163
|
+
[:stroke_path]])
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
describe "handles the normal markers by drawing them using the ZapfDingbats font" do
|
|
167
|
+
it "works with font auto-sizing" do
|
|
168
|
+
@widget.marker_style(style: :check, color: 0.5, size: 0)
|
|
169
|
+
execute
|
|
170
|
+
assert_operators(@xform.stream,
|
|
171
|
+
[[:set_font_and_size, [:F1, 8]],
|
|
172
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
173
|
+
[:begin_text],
|
|
174
|
+
[:set_text_matrix, [1, 0, 0, 1, 1.616, 7.236]],
|
|
175
|
+
[:show_text, ["4"]],
|
|
176
|
+
[:end_text]])
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it "works with a fixed font size" do
|
|
180
|
+
@widget.marker_style(style: :check, color: 0.5, size: 5)
|
|
181
|
+
execute
|
|
182
|
+
assert_operators(@xform.stream,
|
|
183
|
+
[[:set_font_and_size, [:F1, 5]],
|
|
184
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
185
|
+
[:begin_text],
|
|
186
|
+
[:set_text_matrix, [1, 0, 0, 1, 2.885, 8.2725]],
|
|
187
|
+
[:show_text, ["4"]],
|
|
188
|
+
[:end_text]])
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
describe "button fields" do
|
|
194
|
+
before do
|
|
195
|
+
@field = @doc.add({FT: :Btn}, type: :XXAcroFormField, subtype: :Btn)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
describe "check box" do
|
|
199
|
+
before do
|
|
200
|
+
@widget = @field.create_widget(@page, Rect: [0, 0, 0, 0])
|
|
201
|
+
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
202
|
+
@field.field_value = :Off
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it "updates the widgets' /AS entry to point to the selected appearance" do
|
|
206
|
+
@generator.create_appearances
|
|
207
|
+
assert_equal(@field[:V], @widget[:AS])
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it "set the print flag on the widgets" do
|
|
211
|
+
@generator.create_appearances
|
|
212
|
+
assert(@widget.flagged?(:print))
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it "adjusts the /Rect if width is zero" do
|
|
216
|
+
@generator.create_appearances
|
|
217
|
+
assert_equal(12, @widget[:Rect].width)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it "adjusts the /Rect if height is zero" do
|
|
221
|
+
@generator.create_appearances
|
|
222
|
+
assert_equal(12, @widget[:Rect].height)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it "creates the needed appearance streams" do
|
|
226
|
+
@generator.create_appearances
|
|
227
|
+
assert_equal(:XObject, @widget[:AP][:N][:Off].type)
|
|
228
|
+
assert_equal(:XObject, @widget[:AP][:N][:Yes].type)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
it "creates the /Off appearance stream" do
|
|
232
|
+
@generator.create_appearances
|
|
233
|
+
assert_operators(@widget[:AP][:N][:Off].stream,
|
|
234
|
+
[[:save_graphics_state],
|
|
235
|
+
[:set_device_gray_non_stroking_color, [1.0]],
|
|
236
|
+
[:append_rectangle, [0, 0, 12, 12]],
|
|
237
|
+
[:fill_path_non_zero],
|
|
238
|
+
[:append_rectangle, [0.5, 0.5, 11, 11]],
|
|
239
|
+
[:stroke_path], [:restore_graphics_state]])
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it "creates the /Yes appearance stream" do
|
|
243
|
+
@generator.create_appearances
|
|
244
|
+
assert_operators(@widget[:AP][:N][:Yes].stream,
|
|
245
|
+
[[:save_graphics_state],
|
|
246
|
+
[:set_device_gray_non_stroking_color, [1.0]],
|
|
247
|
+
[:append_rectangle, [0, 0, 12, 12]],
|
|
248
|
+
[:fill_path_non_zero],
|
|
249
|
+
[:append_rectangle, [0.5, 0.5, 11, 11]],
|
|
250
|
+
[:stroke_path], [:restore_graphics_state],
|
|
251
|
+
|
|
252
|
+
[:save_graphics_state],
|
|
253
|
+
[:set_font_and_size, [:F1, 10]],
|
|
254
|
+
[:begin_text],
|
|
255
|
+
[:set_text_matrix, [1, 0, 0, 1, 1.77, 2.545]],
|
|
256
|
+
[:show_text, ["4"]],
|
|
257
|
+
[:end_text],
|
|
258
|
+
[:restore_graphics_state]])
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
it "fails if the appearance dictionaries are not set up" do
|
|
262
|
+
@widget[:AP][:N].delete(:Off)
|
|
263
|
+
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
describe "radio button" do
|
|
268
|
+
before do
|
|
269
|
+
@field.initialize_as_radio_button
|
|
270
|
+
@widget = @field.create_widget(@page, Rect: [0, 0, 0, 0], value: :radio)
|
|
271
|
+
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
it "updates the widgets' /AS entry to point to the selected appearance" do
|
|
275
|
+
@field.field_value = :radio
|
|
276
|
+
@generator.create_appearances
|
|
277
|
+
assert_equal(@field[:V], @widget[:AS])
|
|
278
|
+
|
|
279
|
+
@field.create_widget(@page, value: :other)
|
|
280
|
+
@field.field_value = :other
|
|
281
|
+
@generator.create_appearances
|
|
282
|
+
assert_equal(:Off, @widget[:AS])
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
it "set the print flag on the widgets" do
|
|
286
|
+
@generator.create_appearances
|
|
287
|
+
assert(@widget.flagged?(:print))
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
it "adjusts the /Rect if width is zero" do
|
|
291
|
+
@generator.create_appearances
|
|
292
|
+
assert_equal(12, @widget[:Rect].width)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
it "adjusts the /Rect if height is zero" do
|
|
296
|
+
@generator.create_appearances
|
|
297
|
+
assert_equal(12, @widget[:Rect].height)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it "creates the needed appearance streams" do
|
|
301
|
+
@generator.create_appearances
|
|
302
|
+
assert_equal(:XObject, @widget[:AP][:N][:Off].type)
|
|
303
|
+
assert_equal(:XObject, @widget[:AP][:N][:radio].type)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it "creates the /Off appearance stream" do
|
|
307
|
+
@widget.marker_style(style: :cross)
|
|
308
|
+
@generator.create_appearances
|
|
309
|
+
assert_operators(@widget[:AP][:N][:Off].stream,
|
|
310
|
+
[[:save_graphics_state],
|
|
311
|
+
[:set_device_gray_non_stroking_color, [1.0]],
|
|
312
|
+
[:append_rectangle, [0, 0, 12, 12]],
|
|
313
|
+
[:fill_path_non_zero],
|
|
314
|
+
[:append_rectangle, [0.5, 0.5, 11, 11]],
|
|
315
|
+
[:stroke_path], [:restore_graphics_state]])
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
it "creates the appearance stream according to the set value" do
|
|
319
|
+
@widget.marker_style(style: :check)
|
|
320
|
+
@generator.create_appearances
|
|
321
|
+
assert_operators(@widget[:AP][:N][:radio].stream,
|
|
322
|
+
[[:save_graphics_state],
|
|
323
|
+
[:set_device_gray_non_stroking_color, [1.0]],
|
|
324
|
+
[:append_rectangle, [0, 0, 12, 12]],
|
|
325
|
+
[:fill_path_non_zero],
|
|
326
|
+
[:append_rectangle, [0.5, 0.5, 11, 11]],
|
|
327
|
+
[:stroke_path], [:restore_graphics_state],
|
|
328
|
+
|
|
329
|
+
[:save_graphics_state],
|
|
330
|
+
[:set_font_and_size, [:F1, 10]],
|
|
331
|
+
[:begin_text],
|
|
332
|
+
[:set_text_matrix, [1, 0, 0, 1, 1.77, 2.545]],
|
|
333
|
+
[:show_text, ["4"]],
|
|
334
|
+
[:end_text],
|
|
335
|
+
[:restore_graphics_state]])
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
it "fails if the appearance dictionaries are not set up" do
|
|
339
|
+
@widget[:AP][:N].delete(:radio)
|
|
340
|
+
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
describe "text field" do
|
|
346
|
+
before do
|
|
347
|
+
@form.set_default_appearance_string
|
|
348
|
+
@field = @doc.add({FT: :Tx}, type: :XXAcroFormField, subtype: :Tx)
|
|
349
|
+
@widget = @field.create_widget(@page, Rect: [0, 0, 0, 0])
|
|
350
|
+
@generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
it "updates the widgets to use the :N appearance stream" do
|
|
354
|
+
@generator.create_appearances
|
|
355
|
+
assert_equal(:N, @widget[:AS])
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
it "set the print flag on the widgets" do
|
|
359
|
+
@generator.create_appearances
|
|
360
|
+
assert(@widget.flagged?(:print))
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
describe "it adjusts the :Rect when necessary" do
|
|
364
|
+
before do
|
|
365
|
+
@widget.border_style(width: 3)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
it "uses a default width if the width is zero" do
|
|
369
|
+
@generator.create_appearances
|
|
370
|
+
assert_equal(@doc.config['acro_form.text_field.default_width'], @widget[:Rect].width)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
it "uses the font size of the /DA if non-zero as basis for the height if it is zero" do
|
|
374
|
+
@field.set_default_appearance_string(font_size: 10)
|
|
375
|
+
@generator.create_appearances
|
|
376
|
+
assert_equal(15.25, @widget[:Rect].height)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it "uses a default font size as basis for the height if it and the set font size are zero" do
|
|
380
|
+
assert_equal(0, @field.parse_default_appearance_string[1])
|
|
381
|
+
@generator.create_appearances
|
|
382
|
+
assert_equal(15.25, @widget[:Rect].height)
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
it "adds an appropriate form XObject" do
|
|
387
|
+
@generator.create_appearances
|
|
388
|
+
form = @widget[:AP][:N]
|
|
389
|
+
assert_equal(:XObject, form.type)
|
|
390
|
+
assert_equal(:Form, form[:Subtype])
|
|
391
|
+
assert_equal([0, 0, @widget[:Rect].width, @widget[:Rect].height], form[:BBox])
|
|
392
|
+
assert_equal(@doc.acro_form.default_resources[:Font][:F1], form[:Resources][:Font][:F1])
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
describe "single line text fields" do
|
|
396
|
+
describe "font size calculation" do
|
|
397
|
+
before do
|
|
398
|
+
@widget[:Rect].height = 20
|
|
399
|
+
@widget[:Rect].width = 100
|
|
400
|
+
@field.field_value = ''
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
it "uses the non-zero font size" do
|
|
404
|
+
@field.set_default_appearance_string(font_size: 10)
|
|
405
|
+
@generator.create_appearances
|
|
406
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
407
|
+
[:set_font_and_size, [:F1, 10]],
|
|
408
|
+
range: 5)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
it "calculates the font size based on the rectangle height and border width" do
|
|
412
|
+
@generator.create_appearances
|
|
413
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
414
|
+
[:set_font_and_size, [:F1, 12.923875]],
|
|
415
|
+
range: 5)
|
|
416
|
+
@widget.border_style(width: 2, color: :transparent)
|
|
417
|
+
@generator.create_appearances
|
|
418
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
419
|
+
[:set_font_and_size, [:F1, 11.487889]],
|
|
420
|
+
range: 5)
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
describe "quadding e.g. text alignment" do
|
|
425
|
+
before do
|
|
426
|
+
@field.field_value = 'Test'
|
|
427
|
+
@field.set_default_appearance_string(font_size: 10)
|
|
428
|
+
@widget[:Rect].height = 20
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
it "works for left aligned text" do
|
|
432
|
+
@field.text_alignment(:left)
|
|
433
|
+
@generator.create_appearances
|
|
434
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
435
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 6.41]],
|
|
436
|
+
range: 7)
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
it "works for right aligned text" do
|
|
440
|
+
@field.text_alignment(:right)
|
|
441
|
+
@generator.create_appearances
|
|
442
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
443
|
+
[:set_text_matrix, [1, 0, 0, 1, 78.55, 6.41]],
|
|
444
|
+
range: 7)
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
it "works for center aligned text" do
|
|
448
|
+
@field.text_alignment(:center)
|
|
449
|
+
@generator.create_appearances
|
|
450
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
451
|
+
[:set_text_matrix, [1, 0, 0, 1, 40.275, 6.41]],
|
|
452
|
+
range: 7)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
it "vertically aligns to the font descender if the text is too high" do
|
|
456
|
+
@widget[:Rect].height = 5
|
|
457
|
+
@generator.create_appearances
|
|
458
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
459
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 3.07]],
|
|
460
|
+
range: 7)
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
it "creates the /N appearance stream according to the set string" do
|
|
465
|
+
@field.field_value = 'Text'
|
|
466
|
+
@generator.create_appearances
|
|
467
|
+
assert_operators(@widget[:AP][:N].stream,
|
|
468
|
+
[[:begin_marked_content, [:Tx]],
|
|
469
|
+
[:save_graphics_state],
|
|
470
|
+
[:append_rectangle, [1, 1, 98, 9.25]],
|
|
471
|
+
[:clip_path_non_zero],
|
|
472
|
+
[:end_path],
|
|
473
|
+
[:set_font_and_size, [:F1, 6.641436]],
|
|
474
|
+
[:begin_text],
|
|
475
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 3.240724]],
|
|
476
|
+
[:show_text, ["Text"]],
|
|
477
|
+
[:end_text],
|
|
478
|
+
[:restore_graphics_state],
|
|
479
|
+
[:end_marked_content]])
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
describe "choice fields" do
|
|
485
|
+
it "works for combo boxes by using the text appearance method" do
|
|
486
|
+
@form.set_default_appearance_string
|
|
487
|
+
field = @doc.add({FT: :Ch}, type: :XXAcroFormField, subtype: :Ch)
|
|
488
|
+
field.initialize_as_combo_box
|
|
489
|
+
widget = field.create_widget(@page, Rect: [0, 0, 0, 0])
|
|
490
|
+
generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(widget)
|
|
491
|
+
generator.create_appearances
|
|
492
|
+
assert_kind_of(HexaPDF::Type::Form, widget[:AP][:N])
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
describe "font resolution in case the referenced font is not usable" do
|
|
497
|
+
before do
|
|
498
|
+
def (@form.default_resources.font(:F1)).font_wrapper; nil; end
|
|
499
|
+
@field.field_value = 'Test'
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
it "uses the fallback font if configured" do
|
|
503
|
+
@doc.config['acro_form.fallback_font'] = ['Times', variant: :none]
|
|
504
|
+
@generator.create_appearances
|
|
505
|
+
assert_equal(:'Times-Roman', @widget[:AP][:N][:Resources][:Font][:F2][:BaseFont])
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
it "fails if fallback fonts are disabled" do
|
|
509
|
+
@doc.config['acro_form.fallback_font'] = nil
|
|
510
|
+
msg = assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
|
511
|
+
assert_match(/Font.*not usable/, msg.message)
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
end
|