hexapdf 0.32.2 → 0.34.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +104 -1
- data/README.md +9 -0
- data/examples/002-graphics.rb +15 -17
- data/examples/003-arcs.rb +9 -9
- data/examples/009-text_layouter_alignment.rb +1 -1
- data/examples/010-text_layouter_inline_boxes.rb +2 -2
- data/examples/011-text_layouter_line_wrapping.rb +1 -1
- data/examples/012-text_layouter_styling.rb +7 -7
- data/examples/013-text_layouter_shapes.rb +1 -1
- data/examples/014-text_in_polygon.rb +1 -1
- data/examples/015-boxes.rb +8 -7
- data/examples/016-frame_automatic_box_placement.rb +2 -2
- data/examples/017-frame_text_flow.rb +2 -1
- data/examples/018-composer.rb +1 -1
- data/examples/020-column_box.rb +2 -1
- data/examples/025-table_box.rb +46 -0
- data/examples/026-optional_content.rb +55 -0
- data/examples/027-composer_optional_content.rb +83 -0
- data/lib/hexapdf/cli/command.rb +12 -3
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/form.rb +5 -5
- data/lib/hexapdf/cli/inspect.rb +5 -7
- data/lib/hexapdf/composer.rb +106 -53
- data/lib/hexapdf/configuration.rb +65 -40
- data/lib/hexapdf/content/canvas.rb +445 -267
- data/lib/hexapdf/content/color_space.rb +72 -25
- data/lib/hexapdf/content/graphic_object/arc.rb +57 -24
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -23
- data/lib/hexapdf/content/graphic_object/geom2d.rb +47 -6
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +58 -36
- data/lib/hexapdf/content/graphic_object.rb +6 -7
- data/lib/hexapdf/content/graphics_state.rb +54 -45
- data/lib/hexapdf/content/operator.rb +54 -54
- data/lib/hexapdf/content/parser.rb +2 -2
- data/lib/hexapdf/content/processor.rb +15 -15
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/content.rb +5 -0
- data/lib/hexapdf/dictionary.rb +7 -5
- data/lib/hexapdf/dictionary_fields.rb +43 -16
- data/lib/hexapdf/digital_signature/cms_handler.rb +2 -2
- data/lib/hexapdf/digital_signature/handler.rb +1 -1
- data/lib/hexapdf/digital_signature/pkcs1_handler.rb +2 -3
- data/lib/hexapdf/digital_signature/signature.rb +6 -6
- data/lib/hexapdf/digital_signature/signatures.rb +13 -12
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +14 -5
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -4
- data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +4 -4
- data/lib/hexapdf/digital_signature/signing.rb +4 -0
- data/lib/hexapdf/digital_signature/verification_result.rb +3 -4
- data/lib/hexapdf/digital_signature.rb +7 -2
- data/lib/hexapdf/document/destinations.rb +12 -11
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/layout.rb +170 -39
- data/lib/hexapdf/document/pages.rb +4 -3
- data/lib/hexapdf/document.rb +96 -55
- data/lib/hexapdf/encryption/aes.rb +5 -5
- data/lib/hexapdf/encryption/arc4.rb +1 -1
- data/lib/hexapdf/encryption/fast_aes.rb +2 -2
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +11 -21
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +31 -24
- data/lib/hexapdf/encryption/standard_security_handler.rb +45 -36
- data/lib/hexapdf/encryption.rb +7 -2
- data/lib/hexapdf/error.rb +18 -0
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.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/pass_through.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/filter.rb +55 -6
- data/lib/hexapdf/font/cmap/parser.rb +2 -2
- data/lib/hexapdf/font/cmap.rb +1 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
- 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 +3 -3
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +3 -0
- data/lib/hexapdf/font/true_type_wrapper.rb +17 -4
- data/lib/hexapdf/font/type1_wrapper.rb +19 -4
- data/lib/hexapdf/font_loader/from_configuration.rb +5 -2
- data/lib/hexapdf/font_loader/from_file.rb +5 -5
- data/lib/hexapdf/font_loader/standard14.rb +3 -3
- data/lib/hexapdf/font_loader.rb +3 -0
- data/lib/hexapdf/image_loader/jpeg.rb +2 -2
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +13 -0
- data/lib/hexapdf/layout/box.rb +32 -5
- data/lib/hexapdf/layout/box_fitter.rb +2 -2
- data/lib/hexapdf/layout/column_box.rb +20 -5
- data/lib/hexapdf/layout/frame.rb +53 -18
- data/lib/hexapdf/layout/image_box.rb +5 -0
- data/lib/hexapdf/layout/inline_box.rb +21 -9
- data/lib/hexapdf/layout/list_box.rb +50 -20
- data/lib/hexapdf/layout/page_style.rb +6 -5
- data/lib/hexapdf/layout/style.rb +64 -9
- data/lib/hexapdf/layout/table_box.rb +684 -0
- data/lib/hexapdf/layout/text_box.rb +12 -3
- data/lib/hexapdf/layout/text_fragment.rb +29 -3
- data/lib/hexapdf/layout/text_layouter.rb +32 -8
- data/lib/hexapdf/layout.rb +1 -0
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +18 -7
- data/lib/hexapdf/parser.rb +7 -7
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/rectangle.rb +1 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +1 -1
- data/lib/hexapdf/revisions.rb +3 -3
- data/lib/hexapdf/serializer.rb +15 -15
- data/lib/hexapdf/stream.rb +5 -4
- data/lib/hexapdf/tokenizer.rb +14 -14
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +22 -22
- data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/field.rb +2 -2
- data/lib/hexapdf/type/acro_form/form.rb +1 -1
- data/lib/hexapdf/type/acro_form/signature_field.rb +4 -4
- data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form.rb +1 -1
- data/lib/hexapdf/type/action.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/set_ocg_state.rb +86 -0
- data/lib/hexapdf/type/actions/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +2 -1
- data/lib/hexapdf/type/annotation.rb +3 -3
- data/lib/hexapdf/type/annotations/link.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/text.rb +2 -3
- data/lib/hexapdf/type/annotations/widget.rb +2 -2
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +11 -2
- data/lib/hexapdf/type/cid_font.rb +18 -4
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +2 -2
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +2 -2
- data/lib/hexapdf/type/font_type0.rb +3 -3
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +76 -6
- 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 +1 -1
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/mark_information.rb +1 -1
- data/lib/hexapdf/type/names.rb +2 -2
- data/lib/hexapdf/type/object_stream.rb +2 -1
- data/lib/hexapdf/type/optional_content_configuration.rb +170 -0
- data/lib/hexapdf/type/optional_content_group.rb +370 -0
- data/lib/hexapdf/type/optional_content_membership.rb +63 -0
- data/lib/hexapdf/type/optional_content_properties.rb +158 -0
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/page.rb +46 -21
- data/lib/hexapdf/type/page_label.rb +5 -9
- data/lib/hexapdf/type/page_tree_node.rb +1 -1
- data/lib/hexapdf/type/resources.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +2 -2
- data/lib/hexapdf/type/viewer_preferences.rb +1 -1
- data/lib/hexapdf/type/xref_stream.rb +2 -2
- data/lib/hexapdf/type.rb +4 -0
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -2
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +4 -4
- data/lib/hexapdf/xref_section.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +11 -1
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +7 -0
- data/test/hexapdf/content/test_canvas.rb +49 -1
- data/test/hexapdf/digital_signature/test_signatures.rb +22 -0
- data/test/hexapdf/document/test_files.rb +2 -2
- data/test/hexapdf/document/test_layout.rb +105 -2
- data/test/hexapdf/document/test_pages.rb +6 -6
- data/test/hexapdf/encryption/test_security_handler.rb +12 -11
- data/test/hexapdf/encryption/test_standard_security_handler.rb +35 -23
- data/test/hexapdf/font/test_true_type_wrapper.rb +18 -1
- data/test/hexapdf/font/test_type1_wrapper.rb +15 -1
- data/test/hexapdf/layout/test_box.rb +14 -5
- data/test/hexapdf/layout/test_column_box.rb +65 -21
- data/test/hexapdf/layout/test_frame.rb +27 -15
- data/test/hexapdf/layout/test_image_box.rb +4 -0
- data/test/hexapdf/layout/test_inline_box.rb +17 -3
- data/test/hexapdf/layout/test_list_box.rb +84 -33
- data/test/hexapdf/layout/test_page_style.rb +3 -2
- data/test/hexapdf/layout/test_style.rb +60 -0
- data/test/hexapdf/layout/test_table_box.rb +728 -0
- data/test/hexapdf/layout/test_text_box.rb +26 -0
- data/test/hexapdf/layout/test_text_fragment.rb +33 -0
- data/test/hexapdf/layout/test_text_layouter.rb +36 -5
- data/test/hexapdf/test_composer.rb +10 -0
- data/test/hexapdf/test_dictionary.rb +10 -0
- data/test/hexapdf/test_dictionary_fields.rb +4 -1
- data/test/hexapdf/test_document.rb +5 -0
- data/test/hexapdf/test_filter.rb +8 -0
- data/test/hexapdf/test_importer.rb +9 -0
- data/test/hexapdf/test_object.rb +16 -5
- data/test/hexapdf/test_stream.rb +7 -0
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +13 -5
- data/test/hexapdf/type/acro_form/test_form.rb +4 -3
- data/test/hexapdf/type/actions/test_set_ocg_state.rb +40 -0
- data/test/hexapdf/type/test_catalog.rb +11 -0
- data/test/hexapdf/type/test_form.rb +119 -0
- data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
- data/test/hexapdf/type/test_optional_content_group.rb +158 -0
- data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
- data/test/hexapdf/type/test_page.rb +20 -6
- metadata +28 -8
@@ -196,15 +196,15 @@ describe HexaPDF::Document::Pages do
|
|
196
196
|
end
|
197
197
|
|
198
198
|
it "works for multiple page label entries" do
|
199
|
-
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 2, {S: :
|
199
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 2, {S: :D}, 7, {S: :A}]}
|
200
200
|
result = @doc.pages.each_labelling_range.to_a
|
201
|
-
assert_equal([[0, 2, {S: :r}], [2, 5, {S: :
|
201
|
+
assert_equal([[0, 2, {S: :r}], [2, 5, {S: :D}], [7, 3, {S: :A}]],
|
202
202
|
result.map {|s, c, l| [s, c, l.value] })
|
203
203
|
end
|
204
204
|
|
205
205
|
it "returns a zero or negative count for the last range if there aren't enough pages" do
|
206
206
|
assert_equal(10, @doc.pages.count)
|
207
|
-
@doc.catalog[:PageLabels] = {Nums: [0, {S: :
|
207
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :D}, 10, {S: :r}]}
|
208
208
|
assert_equal(0, @doc.pages.each_labelling_range.to_a[-1][1])
|
209
209
|
@doc.catalog[:PageLabels][:Nums][2] = 11
|
210
210
|
assert_equal(-1, @doc.pages.each_labelling_range.to_a[-1][1])
|
@@ -221,19 +221,19 @@ describe HexaPDF::Document::Pages do
|
|
221
221
|
|
222
222
|
it "adds an entry for the range starting at 0 if it doesn't exist" do
|
223
223
|
label = @doc.pages.add_labelling_range(5)
|
224
|
-
assert_equal([{S: :
|
224
|
+
assert_equal([{S: :D}, label],
|
225
225
|
@doc.catalog.page_labels[:Nums].value.values_at(1, 3))
|
226
226
|
end
|
227
227
|
end
|
228
228
|
|
229
229
|
describe "delete_labelling_range" do
|
230
230
|
before do
|
231
|
-
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 5, {S: :
|
231
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 5, {S: :D}]}
|
232
232
|
end
|
233
233
|
|
234
234
|
it "deletes the labelling range for a given start index" do
|
235
235
|
label = @doc.pages.delete_labelling_range(5)
|
236
|
-
assert_equal({S: :
|
236
|
+
assert_equal({S: :D}, label)
|
237
237
|
end
|
238
238
|
|
239
239
|
it "deletes the labelling range for 0 if it is the last, together with the number tree" do
|
@@ -17,13 +17,6 @@ describe HexaPDF::Encryption::EncryptionDictionary do
|
|
17
17
|
assert(@dict.must_be_indirect?)
|
18
18
|
end
|
19
19
|
|
20
|
-
it "validates the /V value" do
|
21
|
-
@dict[:V] = 1
|
22
|
-
assert(@dict.validate)
|
23
|
-
@dict[:V] = 3
|
24
|
-
refute(@dict.validate)
|
25
|
-
end
|
26
|
-
|
27
20
|
it "validates the /Length field when /V=2" do
|
28
21
|
@dict[:V] = 2
|
29
22
|
refute(@dict.validate)
|
@@ -240,6 +233,7 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
240
233
|
[:identity, 128, {V: 4, StrF: :Mine, CF: {Mine: {CFM: :None}}}],
|
241
234
|
[:identity, 128, {V: 4, CF: {Mine: {CFM: :AESV2}}}],
|
242
235
|
].each do |alg, length, dict|
|
236
|
+
dict[:Filter] = :Test
|
243
237
|
@enc.strf = alg
|
244
238
|
@enc.set_up_encryption(key_length: length, algorithm: (alg == :identity ? :aes : alg))
|
245
239
|
@obj[:X] = @enc.encrypt_string('data', @obj)
|
@@ -249,7 +243,7 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
249
243
|
end
|
250
244
|
|
251
245
|
it "selects the correct algorithm for string, stream and embedded file decryption" do
|
252
|
-
@handler.set_up_decryption({V: 4, StrF: :Mine, StmF: :Mine, EFF: :Mine,
|
246
|
+
@handler.set_up_decryption({Filter: :Test, V: 4, StrF: :Mine, StmF: :Mine, EFF: :Mine,
|
253
247
|
CF: {Mine: {CFM: :V2}}})
|
254
248
|
assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:embedded_file_algorithm))
|
255
249
|
assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:string_algorithm))
|
@@ -263,16 +257,23 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
263
257
|
@handler.encryption_details)
|
264
258
|
end
|
265
259
|
|
260
|
+
it "fails if the encryption dictionary is not valid" do
|
261
|
+
exp = assert_raises(HexaPDF::Error) do
|
262
|
+
@handler.set_up_decryption({V: 5})
|
263
|
+
end
|
264
|
+
assert_match(/Validation error for encryption dictionary.*Required field Filter/i, exp.message)
|
265
|
+
end
|
266
|
+
|
266
267
|
it "fails for unsupported /V values in the dict" do
|
267
268
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
268
|
-
@handler.set_up_decryption({V: 3})
|
269
|
+
@handler.set_up_decryption({Filter: :Text, V: 3})
|
269
270
|
end
|
270
271
|
assert_match(/Unsupported encryption version/i, exp.message)
|
271
272
|
end
|
272
273
|
|
273
274
|
it "fails for unsupported crypt filter encryption methods" do
|
274
275
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
275
|
-
@handler.set_up_decryption({V: 4, StrF: :Mine, CF: {Mine: {CFM: :Unknown}}})
|
276
|
+
@handler.set_up_decryption({Filter: :Test, V: 4, StrF: :Mine, CF: {Mine: {CFM: :Unknown}}})
|
276
277
|
end
|
277
278
|
assert_match(/Unsupported encryption method/i, exp.message)
|
278
279
|
end
|
@@ -280,7 +281,7 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
280
281
|
|
281
282
|
describe "decrypt" do
|
282
283
|
before do
|
283
|
-
@handler.set_up_decryption({V: 1})
|
284
|
+
@handler.set_up_decryption({Filter: :Test, V: 1})
|
284
285
|
@encrypted = @handler.encrypt_string('string', @obj)
|
285
286
|
@obj.value = {Key: @encrypted.dup, Array: [@encrypted.dup], Hash: {Another: @encrypted.dup}}
|
286
287
|
end
|
@@ -21,13 +21,6 @@ describe HexaPDF::Encryption::StandardEncryptionDictionary do
|
|
21
21
|
@dict[:Perms] = 'test' * 8
|
22
22
|
end
|
23
23
|
|
24
|
-
it "validates the /R value" do
|
25
|
-
@dict[:R] = 2
|
26
|
-
assert(@dict.validate)
|
27
|
-
@dict[:R] = 5
|
28
|
-
refute(@dict.validate)
|
29
|
-
end
|
30
|
-
|
31
24
|
[:U, :O].each do |field|
|
32
25
|
it "validates the length of /#{field} field for R <= 4" do
|
33
26
|
@dict[field] = 'test'
|
@@ -35,19 +28,36 @@ describe HexaPDF::Encryption::StandardEncryptionDictionary do
|
|
35
28
|
end
|
36
29
|
end
|
37
30
|
|
38
|
-
|
39
|
-
|
31
|
+
describe "validation for R=6" do
|
32
|
+
before do
|
40
33
|
@dict[:R] = 6
|
41
|
-
@dict[
|
42
|
-
|
34
|
+
@dict[:U] = 't' * 48
|
35
|
+
@dict[:O] = 't' * 48
|
36
|
+
@dict[:UE] = 't' * 32
|
37
|
+
@dict[:OE] = 't' * 32
|
38
|
+
@dict[:Perms] = 't' * 16
|
43
39
|
end
|
44
|
-
end
|
45
40
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
41
|
+
[:U, :O, :UE, :OE, :Perms].each do |field|
|
42
|
+
it "validates the length of /#{field}" do
|
43
|
+
@dict[field] = 'test'
|
44
|
+
refute(@dict.validate)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
[:U, :O].each do |field|
|
49
|
+
it "auto-corrects /#{field} if it is larger and only padded with 0 bytes" do
|
50
|
+
@dict[field] = 't' * 48 + "\x00" * 20
|
51
|
+
assert(@dict.validate)
|
52
|
+
assert_equal('t' * 48, @dict[field])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
[:UE, :OE, :Perms].each do |field|
|
57
|
+
it "validates the existence of the /#{field} field" do
|
58
|
+
@dict.delete(field)
|
59
|
+
refute(@dict.validate)
|
60
|
+
end
|
51
61
|
end
|
52
62
|
end
|
53
63
|
end
|
@@ -211,22 +221,24 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
211
221
|
describe "prepare_decryption" do
|
212
222
|
it "fails if the /Filter value is incorrect" do
|
213
223
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
214
|
-
@handler.set_up_decryption({Filter: :NonStandard, V: 2
|
224
|
+
@handler.set_up_decryption({Filter: :NonStandard, V: 2, R: 4, O: 't' * 32, U: 't' * 32, P: 0,
|
225
|
+
Length: 128})
|
215
226
|
end
|
216
227
|
assert_match(/Invalid \/Filter value NonStandard/i, exp.message)
|
217
228
|
end
|
218
229
|
|
219
230
|
it "fails if the /R value is incorrect" do
|
220
231
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
221
|
-
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 5
|
232
|
+
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 5, O: 't' * 32, U: 't' * 32, P: 0,
|
233
|
+
Length: 128})
|
222
234
|
end
|
223
235
|
assert_match(/Invalid \/R value 5/i, exp.message)
|
224
236
|
end
|
225
237
|
|
226
238
|
it "fails if the supplied password is invalid" do
|
227
239
|
exp = assert_raises(HexaPDF::EncryptionError) do
|
228
|
-
@handler.set_up_decryption({Filter: :Standard, V:
|
229
|
-
UE: 'a' * 32, OE: 'a' * 32})
|
240
|
+
@handler.set_up_decryption({Filter: :Standard, V: 5, R: 6, U: 'a' * 48, O: 'a' * 48,
|
241
|
+
UE: 'a' * 32, OE: 'a' * 32, P: 0, Perms: 'a' * 16})
|
230
242
|
end
|
231
243
|
assert_match(/Invalid password/i, exp.message)
|
232
244
|
end
|
@@ -234,7 +246,7 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
234
246
|
it "assigns empty strings to the trailer's ID field if it is missing" do
|
235
247
|
refute(@document.trailer.key?(:ID))
|
236
248
|
exp = assert_raises(HexaPDF::EncryptionError) do
|
237
|
-
@handler.set_up_decryption({Filter: :Standard, V: 1, R: 2, U: 'a' *
|
249
|
+
@handler.set_up_decryption({Filter: :Standard, V: 1, R: 2, U: 'a' * 32, O: 'a' * 32, P: 15})
|
238
250
|
end
|
239
251
|
assert_match(/Invalid password/i, exp.message)
|
240
252
|
assert_equal(['', ''], @document.trailer[:ID].value)
|
@@ -286,7 +298,7 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
286
298
|
it "returns an array of permission symbols" do
|
287
299
|
perms = @handler.class::Permissions::MODIFY_CONTENT | @handler.class::Permissions::COPY_CONTENT
|
288
300
|
@handler.set_up_encryption(permissions: perms)
|
289
|
-
assert_equal([:copy_content, :modify_content], @handler.permissions.sort)
|
301
|
+
assert_equal([:copy_content, :extract_content, :modify_content], @handler.permissions.sort)
|
290
302
|
end
|
291
303
|
|
292
304
|
describe "handling of metadata streams" do
|
@@ -66,6 +66,20 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
describe "custom_glyph" do
|
70
|
+
it "returns the specified glyph object" do
|
71
|
+
glyph = @font_wrapper.custom_glyph(0, "str")
|
72
|
+
assert_equal(0, glyph.id)
|
73
|
+
assert_equal("str", glyph.str)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "fails if an invalid glyph id is specified" do
|
77
|
+
exp = assert_raises(HexaPDF::Error) { @font_wrapper.custom_glyph(-5, 'c') }
|
78
|
+
assert_match(/Glyph ID -5 is invalid for font 'Ubuntu-Title'/, exp.message)
|
79
|
+
assert_raises(HexaPDF::Error) { @font_wrapper.custom_glyph(9999, 'c') }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
69
83
|
describe "encode" do
|
70
84
|
it "returns the encoded glyph ID for fonts that are subset" do
|
71
85
|
code = @font_wrapper.encode(@font_wrapper.glyph(3))
|
@@ -83,7 +97,10 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
83
97
|
end
|
84
98
|
|
85
99
|
it "raises an error if an InvalidGlyph is encoded" do
|
86
|
-
assert_raises(HexaPDF::
|
100
|
+
exp = assert_raises(HexaPDF::MissingGlyphError) do
|
101
|
+
@font_wrapper.encode(@font_wrapper.decode_utf8("ö").first)
|
102
|
+
end
|
103
|
+
assert_match(/No glyph for "ö" in font 'Ubuntu-Title'/, exp.message)
|
87
104
|
end
|
88
105
|
end
|
89
106
|
|
@@ -65,6 +65,19 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
describe "custom_glyph" do
|
69
|
+
it "returns the specified glyph object" do
|
70
|
+
glyph = @times_wrapper.custom_glyph(:question, "str")
|
71
|
+
assert_equal(:question, glyph.name)
|
72
|
+
assert_equal("str", glyph.str)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "fails if the provided glyph name is not available for the font" do
|
76
|
+
exp = assert_raises(HexaPDF::Error) { @times_wrapper.custom_glyph(:handicap, 'c') }
|
77
|
+
assert_match(/Glyph named :handicap not found in font 'Times Roman'/, exp.message)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
68
81
|
describe "encode" do
|
69
82
|
describe "uses WinAnsiEncoding as initial encoding for non-symbolic fonts" do
|
70
83
|
it "returns the PDF font dictionary using WinAnsiEncoding and encoded glyph" do
|
@@ -75,7 +88,8 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
75
88
|
end
|
76
89
|
|
77
90
|
it "fails if an InvalidGlyph is encoded" do
|
78
|
-
assert_raises(HexaPDF::
|
91
|
+
exp = assert_raises(HexaPDF::MissingGlyphError) { @times_wrapper.encode(@times_wrapper.glyph(:ffi)) }
|
92
|
+
assert_match(/No glyph for "ffi" in font 'Times Roman'/, exp.message)
|
79
93
|
end
|
80
94
|
|
81
95
|
it "fails if the encoding does not support the given glyph" do
|
@@ -56,8 +56,8 @@ describe HexaPDF::Layout::Box do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
it "allows setting custom properties" do
|
59
|
-
|
60
|
-
assert_equal({'key' => :value},
|
59
|
+
assert_equal({}, create_box(properties: nil).properties)
|
60
|
+
assert_equal({'key' => :value}, create_box(properties: {'key' => :value}).properties)
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -150,6 +150,10 @@ describe HexaPDF::Layout::Box do
|
|
150
150
|
end
|
151
151
|
|
152
152
|
describe "draw" do
|
153
|
+
before do
|
154
|
+
@canvas = HexaPDF::Document.new.pages.add.canvas
|
155
|
+
end
|
156
|
+
|
153
157
|
it "draws the box onto the canvas" do
|
154
158
|
box = create_box(width: 150, height: 130) do |canvas, _|
|
155
159
|
canvas.line_width(15)
|
@@ -161,7 +165,6 @@ describe HexaPDF::Layout::Box do
|
|
161
165
|
box.style.underlays.add {|canvas, _| canvas.line_width(10) }
|
162
166
|
box.style.overlays.add {|canvas, _| canvas.line_width(20) }
|
163
167
|
|
164
|
-
@canvas = HexaPDF::Document.new.pages.add.canvas
|
165
168
|
box.draw(@canvas, 5, 5)
|
166
169
|
assert_operators(@canvas.contents, [[:save_graphics_state],
|
167
170
|
[:set_graphics_state_parameters, [:GS1]],
|
@@ -195,7 +198,6 @@ describe HexaPDF::Layout::Box do
|
|
195
198
|
end
|
196
199
|
|
197
200
|
it "draws nothing onto the canvas if the box is empty" do
|
198
|
-
@canvas = HexaPDF::Document.new.pages.add.canvas
|
199
201
|
box = create_box
|
200
202
|
box.draw(@canvas, 5, 5)
|
201
203
|
assert_operators(@canvas.contents, [])
|
@@ -204,10 +206,17 @@ describe HexaPDF::Layout::Box do
|
|
204
206
|
refute(box.style.border?)
|
205
207
|
refute(box.style.overlays?)
|
206
208
|
end
|
209
|
+
|
210
|
+
it "wraps the box in optional content markers if the optional_content property is set" do
|
211
|
+
box = create_box(properties: {'optional_content' => 'Text'})
|
212
|
+
box.draw(@canvas, 0, 0)
|
213
|
+
assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]],
|
214
|
+
[:end_marked_content]])
|
215
|
+
end
|
207
216
|
end
|
208
217
|
|
209
218
|
describe "empty?" do
|
210
|
-
it "is
|
219
|
+
it "is empty when no drawing operation is specified" do
|
211
220
|
assert(create_box.empty?)
|
212
221
|
refute(create_box {}.empty?)
|
213
222
|
refute(create_box(style: {background_color: [5]}).empty?)
|
@@ -22,7 +22,7 @@ describe HexaPDF::Layout::ColumnBox do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def check_box(box, width, height, fit_pos = nil)
|
25
|
-
assert(box.fit(@frame.available_width, @frame.available_height, @frame), "box fit
|
25
|
+
assert(box.fit(@frame.available_width, @frame.available_height, @frame), "box didn't fit")
|
26
26
|
assert_equal(width, box.width, "box width")
|
27
27
|
assert_equal(height, box.height, "box height")
|
28
28
|
if fit_pos
|
@@ -46,6 +46,24 @@ describe HexaPDF::Layout::ColumnBox do
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
describe "empty?" do
|
50
|
+
it "is empty if nothing is fit yet" do
|
51
|
+
assert(create_box.empty?)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "is empty if no box fits" do
|
55
|
+
box = create_box(children: [@fixed_size_boxes[0]], columns: [10])
|
56
|
+
box.fit(@frame.available_width, @frame.available_height, @frame)
|
57
|
+
assert(box.empty?)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "is not empty if at least one box fits" do
|
61
|
+
box = create_box(children: [@fixed_size_boxes[0]], columns: [30])
|
62
|
+
check_box(box, 30, 10)
|
63
|
+
refute(box.empty?)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
49
67
|
describe "fit" do
|
50
68
|
[:default, :flow].each do |position|
|
51
69
|
it "respects the set initial width, position #{position}" do
|
@@ -132,27 +150,53 @@ describe HexaPDF::Layout::ColumnBox do
|
|
132
150
|
assert_equal(5, box_b.children.size)
|
133
151
|
end
|
134
152
|
|
135
|
-
|
136
|
-
|
137
|
-
|
153
|
+
describe "draw_content" do
|
154
|
+
before do
|
155
|
+
@canvas = HexaPDF::Document.new.pages.add.canvas
|
156
|
+
end
|
157
|
+
|
158
|
+
it "draws the result onto the canvas" do
|
159
|
+
box = create_box(children: @fixed_size_boxes)
|
160
|
+
box.fit(100, 100, @frame)
|
161
|
+
box.draw(@canvas, 0, 100 - box.height)
|
162
|
+
operators = 90.step(to: 20, by: -10).map do |y|
|
163
|
+
[[:save_graphics_state],
|
164
|
+
[:concatenate_matrix, [1, 0, 0, 1, 0, y]],
|
165
|
+
[:move_to, [0, 0]],
|
166
|
+
[:end_path],
|
167
|
+
[:restore_graphics_state]]
|
168
|
+
end
|
169
|
+
operators.concat(90.step(to: 30, by: -10).map do |y|
|
170
|
+
[[:save_graphics_state],
|
171
|
+
[:concatenate_matrix, [1, 0, 0, 1, 55, y]],
|
172
|
+
[:move_to, [0, 0]],
|
173
|
+
[:end_path],
|
174
|
+
[:restore_graphics_state]]
|
175
|
+
end)
|
176
|
+
operators.flatten!(1)
|
177
|
+
assert_operators(@canvas.contents, operators)
|
178
|
+
end
|
138
179
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
180
|
+
it "takes a different final location into account" do
|
181
|
+
box = create_box(children: @fixed_size_boxes[0, 2])
|
182
|
+
box.fit(100, 100, @frame)
|
183
|
+
box.draw(@canvas, 20, 10)
|
184
|
+
operators = [
|
185
|
+
[:save_graphics_state],
|
186
|
+
[:concatenate_matrix, [1, 0, 0, 1, 20, -80]],
|
187
|
+
[:save_graphics_state],
|
188
|
+
[:concatenate_matrix, [1, 0, 0, 1, 0, 90]],
|
189
|
+
[:move_to, [0, 0]],
|
190
|
+
[:end_path],
|
191
|
+
[:restore_graphics_state],
|
192
|
+
[:save_graphics_state],
|
193
|
+
[:concatenate_matrix, [1, 0, 0, 1, 55, 90]],
|
194
|
+
[:move_to, [0, 0]],
|
195
|
+
[:end_path],
|
196
|
+
[:restore_graphics_state],
|
197
|
+
[:restore_graphics_state],
|
198
|
+
]
|
199
|
+
assert_operators(@canvas.contents, operators)
|
147
200
|
end
|
148
|
-
operators.concat(90.step(to: 30, by: -10).map do |y|
|
149
|
-
[[:save_graphics_state],
|
150
|
-
[:concatenate_matrix, [1, 0, 0, 1, 55, y]],
|
151
|
-
[:move_to, [0, 0]],
|
152
|
-
[:end_path],
|
153
|
-
[:restore_graphics_state]]
|
154
|
-
end)
|
155
|
-
operators.flatten!(1)
|
156
|
-
assert_operators(@canvas.contents, operators)
|
157
201
|
end
|
158
202
|
end
|
@@ -5,31 +5,31 @@ require 'hexapdf/layout/frame'
|
|
5
5
|
require 'hexapdf/layout/box'
|
6
6
|
require 'hexapdf/document'
|
7
7
|
|
8
|
-
describe HexaPDF::Layout::Frame do
|
8
|
+
describe HexaPDF::Layout::Frame::FitResult do
|
9
9
|
it "shows the box's mask area on #draw when using debug output" do
|
10
10
|
doc = HexaPDF::Document.new(config: {'debug' => true})
|
11
11
|
canvas = doc.pages.add.canvas
|
12
12
|
box = HexaPDF::Layout::Box.create(width: 20, height: 20) {}
|
13
13
|
result = HexaPDF::Layout::Frame::FitResult.new(box)
|
14
|
-
result.mask = Geom2D::
|
14
|
+
result.mask = Geom2D::Rectangle(0, 0, 20, 20)
|
15
15
|
result.x = result.y = 0
|
16
16
|
result.draw(canvas)
|
17
17
|
assert_equal(<<~CONTENTS, canvas.contents)
|
18
|
+
/OC /P1 BDC
|
18
19
|
q
|
19
20
|
0.0 0.501961 0.0 rg
|
20
21
|
0.0 0.392157 0.0 RG
|
21
22
|
/GS1 gs
|
22
|
-
0 0
|
23
|
-
0 20 l
|
24
|
-
20 20 l
|
25
|
-
20 0 l
|
26
|
-
h
|
23
|
+
0 0 20 20 re
|
27
24
|
B
|
28
25
|
Q
|
26
|
+
EMC
|
29
27
|
q
|
30
28
|
1 0 0 1 0 0 cm
|
31
29
|
Q
|
32
30
|
CONTENTS
|
31
|
+
ocg = doc.optional_content.ocgs.first
|
32
|
+
assert_equal([['Debug', ocg]], doc.optional_content.default_configuration[:Order])
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -38,6 +38,14 @@ describe HexaPDF::Layout::Frame do
|
|
38
38
|
@frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150)
|
39
39
|
end
|
40
40
|
|
41
|
+
it "allows accessing the context's document" do
|
42
|
+
assert_nil(@frame.document)
|
43
|
+
context = Minitest::Mock.new
|
44
|
+
context.expect(:document, :document)
|
45
|
+
assert_equal(:document, HexaPDF::Layout::Frame.new(0, 0, 10, 10, context: context).document)
|
46
|
+
context.verify
|
47
|
+
end
|
48
|
+
|
41
49
|
it "allows access to the bounding box attributes" do
|
42
50
|
assert_equal(5, @frame.left)
|
43
51
|
assert_equal(10, @frame.bottom)
|
@@ -90,7 +98,11 @@ describe HexaPDF::Layout::Frame do
|
|
90
98
|
refute_nil(fit_result)
|
91
99
|
@frame.draw(@canvas, fit_result)
|
92
100
|
assert_equal(mask, fit_result.mask.bbox.to_a)
|
93
|
-
|
101
|
+
if @frame.shape.respond_to?(:polygons)
|
102
|
+
assert_equal(points, @frame.shape.polygons.map(&:to_a))
|
103
|
+
else
|
104
|
+
assert_equal(points, [@frame.shape.to_a])
|
105
|
+
end
|
94
106
|
@canvas.verify
|
95
107
|
end
|
96
108
|
|
@@ -99,9 +111,9 @@ describe HexaPDF::Layout::Frame do
|
|
99
111
|
areas.each do |area|
|
100
112
|
@frame.remove_area(
|
101
113
|
case area
|
102
|
-
when :left then Geom2D::
|
103
|
-
when :right then Geom2D::
|
104
|
-
when :top then Geom2D::
|
114
|
+
when :left then Geom2D::Rectangle(10, 10, 10, 100)
|
115
|
+
when :right then Geom2D::Rectangle(100, 10, 10, 100)
|
116
|
+
when :top then Geom2D::Rectangle(10, 100, 100, 10)
|
105
117
|
end
|
106
118
|
)
|
107
119
|
end
|
@@ -323,7 +335,7 @@ describe HexaPDF::Layout::Frame do
|
|
323
335
|
end
|
324
336
|
|
325
337
|
it "can't fit the box if there is no available space" do
|
326
|
-
@frame.remove_area(Geom2D::
|
338
|
+
@frame.remove_area(Geom2D::Rectangle(0, 0, 110, 110))
|
327
339
|
box = HexaPDF::Layout::Box.create
|
328
340
|
refute(@frame.fit(box).success?)
|
329
341
|
end
|
@@ -388,7 +400,7 @@ describe HexaPDF::Layout::Frame do
|
|
388
400
|
# +--------+
|
389
401
|
it "works for a region with a hole" do
|
390
402
|
frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
|
391
|
-
frame.remove_area(Geom2D::
|
403
|
+
frame.remove_area(Geom2D::Rectangle(20, 20, 60, 60))
|
392
404
|
check_regions(frame, [[0, 100, 100, 20], [0, 100, 20, 100],
|
393
405
|
[0, 80, 20, 80], [0, 20, 100, 20]])
|
394
406
|
end
|
@@ -400,7 +412,7 @@ describe HexaPDF::Layout::Frame do
|
|
400
412
|
# +--------+
|
401
413
|
it "works for a u-shaped frame" do
|
402
414
|
frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
|
403
|
-
frame.remove_area(Geom2D::
|
415
|
+
frame.remove_area(Geom2D::Rectangle(30, 60, 40, 40))
|
404
416
|
check_regions(frame, [[0, 100, 30, 100], [0, 60, 100, 60]])
|
405
417
|
end
|
406
418
|
|
@@ -415,7 +427,7 @@ describe HexaPDF::Layout::Frame do
|
|
415
427
|
it "works for a complicated frame" do
|
416
428
|
frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
|
417
429
|
top_cut = Geom2D::Polygon([20, 100], [20, 80], [40, 80], [40, 90], [60, 90], [60, 100])
|
418
|
-
left_cut = Geom2D::
|
430
|
+
left_cut = Geom2D::Rectangle(0, 20, 30, 20)
|
419
431
|
frame.remove_area(Geom2D::PolygonSet(top_cut, left_cut))
|
420
432
|
|
421
433
|
check_regions(frame, [[0, 100, 20, 60], [0, 90, 20, 50], [0, 80, 100, 40],
|
@@ -24,20 +24,28 @@ describe HexaPDF::Layout::InlineBox do
|
|
24
24
|
assert_equal(:top, ibox.valign)
|
25
25
|
end
|
26
26
|
|
27
|
+
it "fails if the wrapped box has not width set" do
|
28
|
+
box = HexaPDF::Document.new.layout.text("test is not going good")
|
29
|
+
assert_raises(HexaPDF::Error) { inline_box(box) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "fit_wrapped_box" do
|
27
34
|
it "automatically fits the provided box into a frame" do
|
28
35
|
ibox = inline_box(HexaPDF::Document.new.layout.text("test is going good", width: 20))
|
36
|
+
ibox.fit_wrapped_box(nil)
|
29
37
|
assert_equal(20, ibox.width)
|
30
38
|
assert_equal(45, ibox.height)
|
31
39
|
end
|
32
40
|
|
33
41
|
it "fails if the wrapped box could not be fit" do
|
34
42
|
box = HexaPDF::Document.new.layout.text("test is not going good", width: 1)
|
35
|
-
assert_raises(HexaPDF::Error) { inline_box(box) }
|
43
|
+
assert_raises(HexaPDF::Error) { inline_box(box).fit_wrapped_box(nil) }
|
36
44
|
end
|
37
45
|
|
38
46
|
it "fails if the height is not set explicitly and during fitting" do
|
39
47
|
assert_raises(HexaPDF::Error) do
|
40
|
-
inline_box(HexaPDF::Layout::Box.create(width: 10))
|
48
|
+
inline_box(HexaPDF::Layout::Box.create(width: 10)).fit_wrapped_box(nil)
|
41
49
|
end
|
42
50
|
end
|
43
51
|
end
|
@@ -45,7 +53,9 @@ describe HexaPDF::Layout::InlineBox do
|
|
45
53
|
it "draws the wrapped box at the correct position" do
|
46
54
|
doc = HexaPDF::Document.new
|
47
55
|
canvas = doc.pages.add.canvas
|
48
|
-
inline_box(doc.layout.text("", width: 20, margin: [15, 10]))
|
56
|
+
box = inline_box(doc.layout.text("", width: 20, margin: [15, 10]))
|
57
|
+
box.fit_wrapped_box(nil)
|
58
|
+
box.draw(canvas, 100, 200)
|
49
59
|
assert_equal("q\n1 0 0 1 110 -99785 cm\nQ\n", canvas.contents)
|
50
60
|
end
|
51
61
|
|
@@ -54,6 +64,10 @@ describe HexaPDF::Layout::InlineBox do
|
|
54
64
|
refute(HexaPDF::Layout::InlineBox.create(width: 10, height: 15) {}.empty?)
|
55
65
|
end
|
56
66
|
|
67
|
+
it "returns the style of the box" do
|
68
|
+
assert_same(@box.box.style, @box.style)
|
69
|
+
end
|
70
|
+
|
57
71
|
describe "valign" do
|
58
72
|
it "has a default value of :baseline" do
|
59
73
|
assert_equal(:baseline, @box.valign)
|