hexapdf 0.11.9 → 0.13.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 +157 -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 +22 -11
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/form.rb +240 -0
- data/lib/hexapdf/cli/image2pdf.rb +3 -2
- data/lib/hexapdf/cli/images.rb +1 -1
- data/lib/hexapdf/cli/info.rb +52 -3
- data/lib/hexapdf/cli/inspect.rb +31 -9
- data/lib/hexapdf/cli/merge.rb +2 -2
- 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 +81 -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 +4 -4
- 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 +5 -5
- data/lib/hexapdf/dictionary_fields.rb +2 -10
- data/lib/hexapdf/document.rb +45 -17
- data/lib/hexapdf/document/files.rb +1 -2
- 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 +2 -2
- 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 +2 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +2 -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 +2 -5
- 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 +9 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +7 -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 +2 -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 +3 -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 +68 -52
- 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 +4 -3
- 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 +2 -2
- data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
- data/lib/hexapdf/layout/style.rb +24 -24
- 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 +4 -3
- 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 +32 -27
- data/lib/hexapdf/parser.rb +69 -6
- data/lib/hexapdf/pdf_array.rb +10 -3
- 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 +30 -22
- data/lib/hexapdf/serializer.rb +2 -2
- 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 +7 -5
- data/lib/hexapdf/tokenizer.rb +5 -4
- 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 +405 -0
- data/lib/hexapdf/type/acro_form/button_field.rb +305 -0
- data/lib/hexapdf/type/acro_form/choice_field.rb +220 -0
- data/lib/hexapdf/type/acro_form/field.rb +250 -17
- data/lib/hexapdf/type/acro_form/form.rb +159 -7
- data/lib/hexapdf/type/acro_form/text_field.rb +187 -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 +4 -3
- 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 +238 -2
- data/lib/hexapdf/type/catalog.rb +23 -3
- 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 +2 -2
- data/lib/hexapdf/type/font.rb +18 -1
- data/lib/hexapdf/type/font_descriptor.rb +2 -2
- data/lib/hexapdf/type/font_simple.rb +4 -2
- data/lib/hexapdf/type/font_true_type.rb +7 -3
- data/lib/hexapdf/type/font_type0.rb +2 -2
- data/lib/hexapdf/type/font_type1.rb +16 -1
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +12 -2
- 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 +5 -3
- 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 +36 -12
- data/lib/hexapdf/type/page_tree_node.rb +37 -16
- data/lib/hexapdf/type/resources.rb +17 -3
- data/lib/hexapdf/type/trailer.rb +4 -6
- 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 +19 -16
- 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/common_tokenizer_tests.rb +6 -1
- data/test/hexapdf/content/common.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 +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/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_true_type_wrapper.rb +10 -7
- data/test/hexapdf/font/test_type1_wrapper.rb +33 -8
- data/test/hexapdf/layout/test_style.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +3 -4
- 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 +16 -4
- data/test/hexapdf/test_object.rb +44 -26
- data/test/hexapdf/test_parser.rb +125 -55
- data/test/hexapdf/test_pdf_array.rb +7 -0
- data/test/hexapdf/test_rectangle.rb +14 -0
- data/test/hexapdf/test_revision.rb +3 -0
- 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 +521 -0
- data/test/hexapdf/type/acro_form/test_button_field.rb +281 -0
- data/test/hexapdf/type/acro_form/test_choice_field.rb +137 -0
- data/test/hexapdf/type/acro_form/test_field.rb +163 -6
- data/test/hexapdf/type/acro_form/test_form.rb +189 -22
- data/test/hexapdf/type/acro_form/test_text_field.rb +121 -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_simple.rb +2 -1
- data/test/hexapdf/type/test_font_true_type.rb +6 -0
- data/test/hexapdf/type/test_font_type1.rb +8 -0
- data/test/hexapdf/type/test_form.rb +19 -1
- data/test/hexapdf/type/test_image.rb +7 -0
- data/test/hexapdf/type/test_page.rb +45 -7
- data/test/hexapdf/type/test_page_tree_node.rb +62 -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 +15 -1
- data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
- data/test/test_helper.rb +1 -1
- metadata +34 -21
- data/lib/hexapdf/filter/dct_decode.rb +0 -60
|
@@ -222,7 +222,7 @@ describe HexaPDF::DictionaryFields do
|
|
|
222
222
|
|
|
223
223
|
it "allows conversion to a Rectangle from an Array" do
|
|
224
224
|
doc = Minitest::Mock.new
|
|
225
|
-
doc.expect(:wrap, :data, [[0, 1, 2, 3], type: HexaPDF::Rectangle])
|
|
225
|
+
doc.expect(:wrap, :data, [[0, 1, 2, 3], {type: HexaPDF::Rectangle}])
|
|
226
226
|
@field.convert([0, 1, 2, 3], doc)
|
|
227
227
|
doc.verify
|
|
228
228
|
end
|
|
@@ -230,7 +230,7 @@ describe HexaPDF::DictionaryFields do
|
|
|
230
230
|
it "allows conversion to a Rectangle from a HexaPDF::PDFArray" do
|
|
231
231
|
data = HexaPDF::PDFArray.new([0, 1, 2, 3])
|
|
232
232
|
doc = Minitest::Mock.new
|
|
233
|
-
doc.expect(:wrap, :data, [data, type: HexaPDF::Rectangle])
|
|
233
|
+
doc.expect(:wrap, :data, [data, {type: HexaPDF::Rectangle}])
|
|
234
234
|
@field.convert(data, doc)
|
|
235
235
|
doc.verify
|
|
236
236
|
end
|
|
@@ -441,21 +441,21 @@ describe HexaPDF::Document do
|
|
|
441
441
|
|
|
442
442
|
describe "validate" do
|
|
443
443
|
before do
|
|
444
|
-
@doc.
|
|
444
|
+
@doc.validate # to create a valid document
|
|
445
445
|
end
|
|
446
446
|
|
|
447
447
|
it "validates indirect objects" do
|
|
448
|
-
obj = @doc.add({Type: :
|
|
448
|
+
obj = @doc.add({Type: :Page, MediaBox: [1, 1, 1, 1], Parent: @doc.pages.root})
|
|
449
449
|
refute(@doc.validate(auto_correct: false))
|
|
450
450
|
|
|
451
451
|
called = false
|
|
452
|
-
assert(@doc.validate {|o| assert_same(obj, o); called = true })
|
|
452
|
+
assert(@doc.validate {|_, _, o| assert_same(obj, o); called = true })
|
|
453
453
|
assert(called)
|
|
454
454
|
end
|
|
455
455
|
|
|
456
456
|
it "validates the trailer object" do
|
|
457
457
|
@doc.trailer[:ID] = :Symbol
|
|
458
|
-
refute(@doc.validate {|obj| assert_same(@doc.trailer, obj) })
|
|
458
|
+
refute(@doc.validate {|_, _, obj| assert_same(@doc.trailer, obj) })
|
|
459
459
|
end
|
|
460
460
|
|
|
461
461
|
it "validates only loaded objects" do
|
|
@@ -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_object.rb
CHANGED
|
@@ -6,15 +6,6 @@ require 'hexapdf/reference'
|
|
|
6
6
|
|
|
7
7
|
describe HexaPDF::Object do
|
|
8
8
|
describe "class.deep_copy" do
|
|
9
|
-
it "handles not-duplicatable classes" do
|
|
10
|
-
assert_equal(5, HexaPDF::Object.deep_copy(5))
|
|
11
|
-
assert_equal(5.5, HexaPDF::Object.deep_copy(5.5))
|
|
12
|
-
assert_nil(HexaPDF::Object.deep_copy(nil))
|
|
13
|
-
assert_equal(true, HexaPDF::Object.deep_copy(true))
|
|
14
|
-
assert_equal(false, HexaPDF::Object.deep_copy(false))
|
|
15
|
-
assert_equal(:Name, HexaPDF::Object.deep_copy(:Name))
|
|
16
|
-
end
|
|
17
|
-
|
|
18
9
|
it "handles general, duplicatable classes" do
|
|
19
10
|
x = "test"
|
|
20
11
|
assert_equal("test", HexaPDF::Object.deep_copy(x))
|
|
@@ -103,30 +94,57 @@ describe HexaPDF::Object do
|
|
|
103
94
|
end
|
|
104
95
|
|
|
105
96
|
describe "validate" do
|
|
106
|
-
|
|
107
|
-
obj = HexaPDF::Object.new(5)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
97
|
+
before do
|
|
98
|
+
@obj = HexaPDF::Object.new(5)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "invokes perform_validation correctly" do
|
|
102
|
+
invoked = false
|
|
103
|
+
@obj.define_singleton_method(:perform_validation) { invoked = true }
|
|
104
|
+
assert(@obj.validate)
|
|
105
|
+
assert(invoked)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "yields all arguments yieled by perform_validation" do
|
|
109
|
+
invoked = []
|
|
110
|
+
@obj.define_singleton_method(:perform_validation) do |&block|
|
|
111
|
+
block.call("error", true, :object)
|
|
112
|
+
end
|
|
113
|
+
assert(@obj.validate {|*a| invoked << a })
|
|
114
|
+
assert_equal([["error", true, :object]], invoked)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "provides self as third argument if none is yielded by perform_validation" do
|
|
118
|
+
invoked = []
|
|
119
|
+
@obj.define_singleton_method(:perform_validation) do |&block|
|
|
111
120
|
block.call("error", true)
|
|
112
121
|
end
|
|
113
|
-
assert(obj.validate {|*a| invoked
|
|
114
|
-
assert_equal([
|
|
115
|
-
|
|
122
|
+
assert(@obj.validate {|*a| invoked << a })
|
|
123
|
+
assert_equal([["error", true, @obj]], invoked)
|
|
124
|
+
end
|
|
116
125
|
|
|
117
|
-
|
|
126
|
+
it "yields all problems when auto_correct is true" do
|
|
127
|
+
invoked = []
|
|
128
|
+
@obj.define_singleton_method(:perform_validation) do |&block|
|
|
129
|
+
invoked << :before
|
|
130
|
+
block.call("error", false)
|
|
131
|
+
invoked << :after
|
|
132
|
+
block.call("error2", true)
|
|
133
|
+
invoked << :last
|
|
134
|
+
end
|
|
135
|
+
refute(@obj.validate)
|
|
136
|
+
assert_equal([:before, :after, :last], invoked)
|
|
118
137
|
end
|
|
119
138
|
|
|
120
|
-
it "stops
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
invoked[:before] = true
|
|
139
|
+
it "stops at the first uncorrectable problem if auto_correct is false" do
|
|
140
|
+
invoked = []
|
|
141
|
+
@obj.define_singleton_method(:perform_validation) do |&block|
|
|
142
|
+
invoked << :before
|
|
125
143
|
block.call("error", false)
|
|
126
|
-
invoked
|
|
144
|
+
invoked << :after
|
|
127
145
|
end
|
|
128
|
-
refute(obj.validate
|
|
129
|
-
|
|
146
|
+
refute(@obj.validate(auto_correct: false))
|
|
147
|
+
assert_equal([:before], invoked)
|
|
130
148
|
end
|
|
131
149
|
end
|
|
132
150
|
|
data/test/hexapdf/test_parser.rb
CHANGED
|
@@ -8,6 +8,7 @@ require 'stringio'
|
|
|
8
8
|
describe HexaPDF::Parser do
|
|
9
9
|
before do
|
|
10
10
|
@document = HexaPDF::Document.new
|
|
11
|
+
@document.config['parser.try_xref_reconstruction'] = false
|
|
11
12
|
@document.add(@document.wrap(10, oid: 1, gen: 0))
|
|
12
13
|
|
|
13
14
|
create_parser(<<~EOF)
|
|
@@ -132,6 +133,48 @@ describe HexaPDF::Parser do
|
|
|
132
133
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object(0) }
|
|
133
134
|
assert_match(/stream.*followed by.*endstream/i, exp.message)
|
|
134
135
|
end
|
|
136
|
+
|
|
137
|
+
describe "with strict parsing" do
|
|
138
|
+
before do
|
|
139
|
+
@document.config['parser.on_correctable_error'] = proc { true }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "fails if an empty indirect object is found" do
|
|
143
|
+
create_parser("1 0 obj\nendobj")
|
|
144
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object }
|
|
145
|
+
assert_match(/no indirect object value/i, exp.message)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it "fails if keyword stream is followed only by CR without LF" do
|
|
149
|
+
create_parser("1 0 obj<</Length 2>> stream\r12\nendstream endobj")
|
|
150
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object }
|
|
151
|
+
assert_match(/not CR alone/, exp.message)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "fails if the stream length value is invalid" do
|
|
155
|
+
create_parser("1 0 obj<</Length 4>> stream\n12endstream endobj")
|
|
156
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object }
|
|
157
|
+
assert_match(/invalid stream length/i, exp.message)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "fails if the keyword endobj is mangled" do
|
|
161
|
+
create_parser("1 0 obj\n<< >>\nendobjd\n")
|
|
162
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object }
|
|
163
|
+
assert_match(/keyword endobj/, exp.message)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "fails if the keyword endobj is missing" do
|
|
167
|
+
create_parser("1 0 obj\n<< >>")
|
|
168
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object }
|
|
169
|
+
assert_match(/keyword endobj/, exp.message)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "fails if there is data between 'endstream' and 'endobj'" do
|
|
173
|
+
create_parser("1 0 obj\n<< >>\nstream\nendstream\ntest\nendobj\n")
|
|
174
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object(0) }
|
|
175
|
+
assert_match(/keyword endobj/, exp.message)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
135
178
|
end
|
|
136
179
|
|
|
137
180
|
describe "load_object" do
|
|
@@ -205,7 +248,7 @@ describe HexaPDF::Parser do
|
|
|
205
248
|
end
|
|
206
249
|
|
|
207
250
|
it "ignores garbage at the end of the file" do
|
|
208
|
-
create_parser("startxref\n5\n%%EOF"
|
|
251
|
+
create_parser("startxref\n5\n%%EOF" << "\nhallo" * 150)
|
|
209
252
|
assert_equal(5, @parser.startxref_offset)
|
|
210
253
|
end
|
|
211
254
|
|
|
@@ -215,9 +258,9 @@ describe HexaPDF::Parser do
|
|
|
215
258
|
end
|
|
216
259
|
|
|
217
260
|
it "finds the startxref anywhere in file" do
|
|
218
|
-
create_parser("startxref\n5\n%%EOF"
|
|
261
|
+
create_parser("startxref\n5\n%%EOF" << "\nhallo" * 5000)
|
|
219
262
|
assert_equal(5, @parser.startxref_offset)
|
|
220
|
-
create_parser("startxref\n5\n%%EOF\n"
|
|
263
|
+
create_parser("startxref\n5\n%%EOF\n" << "h" * 1017)
|
|
221
264
|
assert_equal(5, @parser.startxref_offset)
|
|
222
265
|
end
|
|
223
266
|
|
|
@@ -231,6 +274,10 @@ describe HexaPDF::Parser do
|
|
|
231
274
|
create_parser("startxref\n5")
|
|
232
275
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.startxref_offset }
|
|
233
276
|
assert_match(/end-of-file marker not found/, exp.message)
|
|
277
|
+
|
|
278
|
+
create_parser("")
|
|
279
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.startxref_offset }
|
|
280
|
+
assert_match(/end-of-file marker not found/, exp.message)
|
|
234
281
|
end
|
|
235
282
|
|
|
236
283
|
it "fails if the startxref keyword is missing" do
|
|
@@ -238,6 +285,13 @@ describe HexaPDF::Parser do
|
|
|
238
285
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.startxref_offset }
|
|
239
286
|
assert_match(/missing startxref/, exp.message)
|
|
240
287
|
end
|
|
288
|
+
|
|
289
|
+
it "fails on strict parsing if the startxref is not in the last part of the file" do
|
|
290
|
+
@document.config['parser.on_correctable_error'] = proc { true }
|
|
291
|
+
create_parser("startxref\n5\n%%EOF" << "\nhallo" * 5000)
|
|
292
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.startxref_offset }
|
|
293
|
+
assert_match(/end-of-file marker not found/, exp.message)
|
|
294
|
+
end
|
|
241
295
|
end
|
|
242
296
|
|
|
243
297
|
describe "file_header_version" do
|
|
@@ -251,8 +305,14 @@ describe HexaPDF::Parser do
|
|
|
251
305
|
assert_match(/file header/, exp.message)
|
|
252
306
|
end
|
|
253
307
|
|
|
308
|
+
it "fails if the header is missing" do
|
|
309
|
+
create_parser("no header")
|
|
310
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.file_header_version }
|
|
311
|
+
assert_match(/file header/, exp.message)
|
|
312
|
+
end
|
|
313
|
+
|
|
254
314
|
it "ignores junk at the beginning of the file and correctly calculates offset" do
|
|
255
|
-
create_parser("junk" * 200
|
|
315
|
+
create_parser("junk" * 200 << "\n%PDF-1.4\n")
|
|
256
316
|
assert_equal('1.4', @parser.file_header_version)
|
|
257
317
|
assert_equal(801, @parser.instance_variable_get(:@header_offset))
|
|
258
318
|
end
|
|
@@ -308,6 +368,12 @@ describe HexaPDF::Parser do
|
|
|
308
368
|
assert_match(/invalid cross-reference subsection/i, exp.message)
|
|
309
369
|
end
|
|
310
370
|
|
|
371
|
+
it "fails if a sub section entry is mangled" do
|
|
372
|
+
create_parser("xref\n0 2\n000a000000 00000 n\n0000000000 65535 n\ntrailer\n<<>>\n")
|
|
373
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(0) }
|
|
374
|
+
assert_match(/invalid cross-reference entry/i, exp.message)
|
|
375
|
+
end
|
|
376
|
+
|
|
311
377
|
it "fails if there is no trailer" do
|
|
312
378
|
create_parser("xref\n0 1\n0000000000 00000 n \n")
|
|
313
379
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(0) }
|
|
@@ -319,6 +385,30 @@ describe HexaPDF::Parser do
|
|
|
319
385
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(0) }
|
|
320
386
|
assert_match(/dictionary/, exp.message)
|
|
321
387
|
end
|
|
388
|
+
|
|
389
|
+
describe "with strict parsing" do
|
|
390
|
+
before do
|
|
391
|
+
@document.config['parser.on_correctable_error'] = proc { true }
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
it "fails if xref type=n with offset=0" do
|
|
395
|
+
create_parser("xref\n0 2\n0000000000 00000 n \n0000000000 00000 n \ntrailer\n<<>>\n")
|
|
396
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(0) }
|
|
397
|
+
assert_match(/invalid.*cross-reference entry/i, exp.message)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
it " fails xref type=n with gen>65535" do
|
|
401
|
+
create_parser("xref\n0 2\n0000000000 00000 n \n0000000000 65536 n \ntrailer\n<<>>\n")
|
|
402
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(0) }
|
|
403
|
+
assert_match(/invalid.*cross-reference entry/i, exp.message)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
it "fails if trailing second whitespace is missing" do
|
|
407
|
+
create_parser("xref\n0 1\n0000000000 00000 n\ntrailer\n<<>>\n")
|
|
408
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(0) }
|
|
409
|
+
assert_match(/invalid.*cross-reference entry/i, exp.message)
|
|
410
|
+
end
|
|
411
|
+
end
|
|
322
412
|
end
|
|
323
413
|
|
|
324
414
|
describe "load_revision" do
|
|
@@ -338,75 +428,55 @@ describe HexaPDF::Parser do
|
|
|
338
428
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.load_revision(10) }
|
|
339
429
|
assert_match(/not a cross-reference stream/, exp.message)
|
|
340
430
|
end
|
|
341
|
-
end
|
|
342
431
|
|
|
343
|
-
|
|
344
|
-
before do
|
|
432
|
+
it "fails on strict parsing if the cross-reference stream doesn't contain an entry for itself" do
|
|
345
433
|
@document.config['parser.on_correctable_error'] = proc { true }
|
|
434
|
+
create_parser("2 0 obj\n<</Type/XRef/Length 3/W [1 1 1]/Size 1>>" \
|
|
435
|
+
"stream\n\x01\x0A\x00\nendstream endobj")
|
|
436
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.load_revision(0) }
|
|
437
|
+
assert_match(/entry for itself/, exp.message)
|
|
346
438
|
end
|
|
439
|
+
end
|
|
347
440
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
it "parse_xref_section_and_trailer fails if xref type=n with offset=0" do
|
|
355
|
-
create_parser("xref\n0 2\n0000000000 00000 n \n0000000000 00000 n \ntrailer\n<<>>\n")
|
|
356
|
-
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(0) }
|
|
357
|
-
assert_match(/invalid.*cross-reference entry/i, exp.message)
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
it "parse_xref_section_and_trailer fails xref type=n with gen>65535" do
|
|
361
|
-
create_parser("xref\n0 2\n0000000000 00000 n \n0000000000 65536 n \ntrailer\n<<>>\n")
|
|
362
|
-
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(0) }
|
|
363
|
-
assert_match(/invalid.*cross-reference entry/i, exp.message)
|
|
441
|
+
describe "reconstruct_revision" do
|
|
442
|
+
before do
|
|
443
|
+
@document.config['parser.try_xref_reconstruction'] = true
|
|
444
|
+
@xref = HexaPDF::XRefSection.in_use_entry(1, 0, 100)
|
|
364
445
|
end
|
|
365
446
|
|
|
366
|
-
it "
|
|
367
|
-
create_parser("
|
|
368
|
-
|
|
369
|
-
assert_match(/invalid.*cross-reference subsection entry/i, exp.message)
|
|
447
|
+
it "serially parses the contents" do
|
|
448
|
+
create_parser("1 0 obj\n5\nendobj\n1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
|
|
449
|
+
assert_equal(6, @parser.load_object(@xref).value)
|
|
370
450
|
end
|
|
371
451
|
|
|
372
|
-
it "
|
|
373
|
-
create_parser("1 0 obj\nendobj")
|
|
374
|
-
|
|
375
|
-
assert_match(/no indirect object value/i, exp.message)
|
|
452
|
+
it "ignores parts where the starting line is split across lines" do
|
|
453
|
+
create_parser("1 0 obj\n5\nendobj\n1 0\nobj\n6\nendobj\ntrailer\n<</Size 1>>")
|
|
454
|
+
assert_equal(5, @parser.load_object(@xref).value)
|
|
376
455
|
end
|
|
377
456
|
|
|
378
|
-
it "
|
|
379
|
-
create_parser("1
|
|
380
|
-
|
|
381
|
-
assert_match(/not CR alone/, exp.message)
|
|
457
|
+
it "ignores invalid objects" do
|
|
458
|
+
create_parser("1 x obj\n5\nendobj\n1 0 xobj\n6\nendobj\n1 0 obj 4\nendobj\ntrailer\n<</Size 1>>")
|
|
459
|
+
assert_equal(4, @parser.load_object(@xref).value)
|
|
382
460
|
end
|
|
383
461
|
|
|
384
|
-
it "
|
|
385
|
-
create_parser("1 0 obj
|
|
386
|
-
|
|
387
|
-
assert_match(/invalid stream length/i, exp.message)
|
|
462
|
+
it "ignores invalid lines" do
|
|
463
|
+
create_parser("1 0 obj\n5\nendobj\nhello there\n1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
|
|
464
|
+
assert_equal(6, @parser.load_object(@xref).value)
|
|
388
465
|
end
|
|
389
466
|
|
|
390
|
-
it "
|
|
391
|
-
create_parser("1
|
|
392
|
-
|
|
393
|
-
assert_match(/keyword endobj/, exp.message)
|
|
394
|
-
create_parser("1 0 obj\n<< >>")
|
|
395
|
-
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object }
|
|
396
|
-
assert_match(/keyword endobj/, exp.message)
|
|
467
|
+
it "uses the last trailer" do
|
|
468
|
+
create_parser("trailer <</Size 1>>\ntrailer <</Size 2/Prev 342>>")
|
|
469
|
+
assert_equal({Size: 2}, @parser.reconstructed_revision.trailer.value)
|
|
397
470
|
end
|
|
398
471
|
|
|
399
|
-
it "
|
|
400
|
-
create_parser("1
|
|
401
|
-
|
|
402
|
-
assert_match(/keyword endobj/, exp.message)
|
|
472
|
+
it "uses the first trailer in case of a linearized file" do
|
|
473
|
+
create_parser("trailer <</Size 1/Prev 342>>\ntrailer <</Size 2>>")
|
|
474
|
+
assert_equal({Size: 1}, @parser.reconstructed_revision.trailer.value)
|
|
403
475
|
end
|
|
404
476
|
|
|
405
|
-
it "
|
|
406
|
-
create_parser("
|
|
407
|
-
|
|
408
|
-
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.load_revision(0) }
|
|
409
|
-
assert_match(/entry for itself/, exp.message)
|
|
477
|
+
it "fails if no valid trailer is found" do
|
|
478
|
+
create_parser("1 0 obj\n5\nendobj")
|
|
479
|
+
assert_raises(HexaPDF::MalformedPDFError) { @parser.load_object(@xref) }
|
|
410
480
|
end
|
|
411
481
|
end
|
|
412
482
|
end
|
|
@@ -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)
|
|
@@ -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])
|