origamindee 3.0.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.
Files changed (139) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +89 -0
  3. data/COPYING.LESSER +165 -0
  4. data/README.md +131 -0
  5. data/bin/config/pdfcop.conf.yml +236 -0
  6. data/bin/pdf2pdfa +87 -0
  7. data/bin/pdf2ruby +333 -0
  8. data/bin/pdfcop +476 -0
  9. data/bin/pdfdecompress +97 -0
  10. data/bin/pdfdecrypt +91 -0
  11. data/bin/pdfencrypt +113 -0
  12. data/bin/pdfexplode +223 -0
  13. data/bin/pdfextract +277 -0
  14. data/bin/pdfmetadata +143 -0
  15. data/bin/pdfsh +12 -0
  16. data/bin/shell/console.rb +128 -0
  17. data/bin/shell/hexdump.rb +59 -0
  18. data/bin/shell/irbrc +69 -0
  19. data/examples/README.md +34 -0
  20. data/examples/attachments/attachment.rb +38 -0
  21. data/examples/attachments/nested_document.rb +51 -0
  22. data/examples/encryption/encryption.rb +28 -0
  23. data/examples/events/events.rb +72 -0
  24. data/examples/flash/flash.rb +37 -0
  25. data/examples/flash/helloworld.swf +0 -0
  26. data/examples/forms/javascript.rb +54 -0
  27. data/examples/forms/xfa.rb +115 -0
  28. data/examples/javascript/hello_world.rb +22 -0
  29. data/examples/javascript/js_emulation.rb +54 -0
  30. data/examples/loop/goto.rb +32 -0
  31. data/examples/loop/named.rb +33 -0
  32. data/examples/signature/signature.rb +65 -0
  33. data/examples/uri/javascript.rb +56 -0
  34. data/examples/uri/open-uri.rb +21 -0
  35. data/examples/uri/submitform.rb +47 -0
  36. data/lib/origami/3d.rb +364 -0
  37. data/lib/origami/acroform.rb +321 -0
  38. data/lib/origami/actions.rb +318 -0
  39. data/lib/origami/annotations.rb +711 -0
  40. data/lib/origami/array.rb +242 -0
  41. data/lib/origami/boolean.rb +90 -0
  42. data/lib/origami/catalog.rb +418 -0
  43. data/lib/origami/collections.rb +144 -0
  44. data/lib/origami/compound.rb +161 -0
  45. data/lib/origami/destinations.rb +252 -0
  46. data/lib/origami/dictionary.rb +192 -0
  47. data/lib/origami/encryption.rb +1084 -0
  48. data/lib/origami/extensions/fdf.rb +347 -0
  49. data/lib/origami/extensions/ppklite.rb +422 -0
  50. data/lib/origami/filespec.rb +197 -0
  51. data/lib/origami/filters/ascii.rb +211 -0
  52. data/lib/origami/filters/ccitt/tables.rb +267 -0
  53. data/lib/origami/filters/ccitt.rb +357 -0
  54. data/lib/origami/filters/crypt.rb +38 -0
  55. data/lib/origami/filters/dct.rb +54 -0
  56. data/lib/origami/filters/flate.rb +69 -0
  57. data/lib/origami/filters/jbig2.rb +57 -0
  58. data/lib/origami/filters/jpx.rb +47 -0
  59. data/lib/origami/filters/lzw.rb +170 -0
  60. data/lib/origami/filters/predictors.rb +292 -0
  61. data/lib/origami/filters/runlength.rb +129 -0
  62. data/lib/origami/filters.rb +364 -0
  63. data/lib/origami/font.rb +196 -0
  64. data/lib/origami/functions.rb +79 -0
  65. data/lib/origami/graphics/colors.rb +230 -0
  66. data/lib/origami/graphics/instruction.rb +98 -0
  67. data/lib/origami/graphics/path.rb +182 -0
  68. data/lib/origami/graphics/patterns.rb +174 -0
  69. data/lib/origami/graphics/render.rb +62 -0
  70. data/lib/origami/graphics/state.rb +149 -0
  71. data/lib/origami/graphics/text.rb +225 -0
  72. data/lib/origami/graphics/xobject.rb +918 -0
  73. data/lib/origami/graphics.rb +38 -0
  74. data/lib/origami/header.rb +75 -0
  75. data/lib/origami/javascript.rb +713 -0
  76. data/lib/origami/linearization.rb +330 -0
  77. data/lib/origami/metadata.rb +172 -0
  78. data/lib/origami/name.rb +135 -0
  79. data/lib/origami/null.rb +65 -0
  80. data/lib/origami/numeric.rb +181 -0
  81. data/lib/origami/obfuscation.rb +245 -0
  82. data/lib/origami/object.rb +760 -0
  83. data/lib/origami/optionalcontent.rb +183 -0
  84. data/lib/origami/outline.rb +54 -0
  85. data/lib/origami/outputintents.rb +85 -0
  86. data/lib/origami/page.rb +722 -0
  87. data/lib/origami/parser.rb +269 -0
  88. data/lib/origami/parsers/fdf.rb +56 -0
  89. data/lib/origami/parsers/pdf/lazy.rb +176 -0
  90. data/lib/origami/parsers/pdf/linear.rb +122 -0
  91. data/lib/origami/parsers/pdf.rb +118 -0
  92. data/lib/origami/parsers/ppklite.rb +57 -0
  93. data/lib/origami/pdf.rb +1108 -0
  94. data/lib/origami/reference.rb +134 -0
  95. data/lib/origami/signature.rb +702 -0
  96. data/lib/origami/stream.rb +705 -0
  97. data/lib/origami/string.rb +444 -0
  98. data/lib/origami/template/patterns.rb +56 -0
  99. data/lib/origami/template/widgets.rb +151 -0
  100. data/lib/origami/trailer.rb +190 -0
  101. data/lib/origami/tree.rb +62 -0
  102. data/lib/origami/version.rb +23 -0
  103. data/lib/origami/webcapture.rb +100 -0
  104. data/lib/origami/xfa/config.rb +453 -0
  105. data/lib/origami/xfa/connectionset.rb +146 -0
  106. data/lib/origami/xfa/datasets.rb +49 -0
  107. data/lib/origami/xfa/localeset.rb +42 -0
  108. data/lib/origami/xfa/package.rb +59 -0
  109. data/lib/origami/xfa/pdf.rb +73 -0
  110. data/lib/origami/xfa/signature.rb +42 -0
  111. data/lib/origami/xfa/sourceset.rb +43 -0
  112. data/lib/origami/xfa/stylesheet.rb +44 -0
  113. data/lib/origami/xfa/template.rb +1691 -0
  114. data/lib/origami/xfa/xdc.rb +42 -0
  115. data/lib/origami/xfa/xfa.rb +146 -0
  116. data/lib/origami/xfa/xfdf.rb +43 -0
  117. data/lib/origami/xfa/xmpmeta.rb +43 -0
  118. data/lib/origami/xfa.rb +62 -0
  119. data/lib/origami/xreftable.rb +557 -0
  120. data/lib/origami.rb +47 -0
  121. data/test/dataset/calc.pdf +85 -0
  122. data/test/dataset/crypto.pdf +36 -0
  123. data/test/dataset/empty.pdf +49 -0
  124. data/test/test_actions.rb +27 -0
  125. data/test/test_annotations.rb +68 -0
  126. data/test/test_forms.rb +30 -0
  127. data/test/test_native_types.rb +83 -0
  128. data/test/test_object_tree.rb +33 -0
  129. data/test/test_pages.rb +60 -0
  130. data/test/test_pdf.rb +20 -0
  131. data/test/test_pdf_attachment.rb +34 -0
  132. data/test/test_pdf_create.rb +24 -0
  133. data/test/test_pdf_encrypt.rb +102 -0
  134. data/test/test_pdf_parse.rb +134 -0
  135. data/test/test_pdf_parse_lazy.rb +69 -0
  136. data/test/test_pdf_sign.rb +97 -0
  137. data/test/test_streams.rb +184 -0
  138. data/test/test_xrefs.rb +67 -0
  139. metadata +280 -0
@@ -0,0 +1,134 @@
1
+ require 'minitest/autorun'
2
+
3
+ class TestPDFParser < Minitest::Test
4
+ def setup
5
+ @files =
6
+ %w{
7
+ dataset/empty.pdf
8
+ dataset/calc.pdf
9
+ dataset/crypto.pdf
10
+ }
11
+ end
12
+
13
+ def test_parse_pdf
14
+ @files.each do |file|
15
+ pdf = PDF.read(File.join(__dir__, file), ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
16
+
17
+ assert_instance_of PDF, pdf
18
+
19
+ pdf.each_object do |object|
20
+ assert_kind_of Origami::Object, object
21
+ end
22
+ end
23
+ end
24
+
25
+ def test_parse_dictionary
26
+ dict = Dictionary.parse("<</Ref 2 0 R/N null/Pi 3.14 /D <<>>>>")
27
+
28
+ assert_instance_of Dictionary, dict
29
+ assert_instance_of Dictionary, dict[:D]
30
+ assert_instance_of Null, dict[:N]
31
+ assert_instance_of Reference, dict[:Ref]
32
+ assert_equal dict.size, 4
33
+ assert_raises(InvalidReferenceError) { dict[:Ref].solve }
34
+ assert dict[:Pi] == 3.14
35
+ end
36
+
37
+ def test_parse_array
38
+ array = Origami::Array.parse("[/Test (abc) .2 \n 799 [<<>>]]")
39
+
40
+ assert array.all?{|e| e.is_a?(Origami::Object)}
41
+ assert_equal array.length, 5
42
+ assert_raises(InvalidArrayObjectError) { Origami::Array.parse("[1 ") }
43
+ assert_raises(InvalidArrayObjectError) { Origami::Array.parse("") }
44
+ end
45
+
46
+ def test_parse_string
47
+ str = LiteralString.parse("(\\122\\125by\\n)")
48
+ assert_instance_of LiteralString, str
49
+ assert_equal str.value, "RUby\n"
50
+
51
+ assert_raises(InvalidLiteralStringObjectError) { LiteralString.parse("") }
52
+ assert_raises(InvalidLiteralStringObjectError) { LiteralString.parse("(test") }
53
+ assert_equal "((O))", LiteralString.parse("(((O)))").value
54
+ assert_equal LiteralString.parse("(ABC\\\nDEF\\\r\nGHI)").value, "ABCDEFGHI"
55
+ assert_equal LiteralString.parse('(\r\n\b\t\f\\(\\)\\x\\\\)').value, "\r\n\b\t\f()x\\"
56
+ assert_equal LiteralString.parse('(\r\n\b\t\f\\\\\\(\\))').to_s, '(\r\n\b\t\f\\\\\\(\\))'
57
+
58
+ str = HexaString.parse("<52 55 62 79 0A>")
59
+ assert_instance_of HexaString, str
60
+ assert_equal str.value, "RUby\n"
61
+
62
+ assert_equal HexaString.parse("<4>").value, 0x40.chr
63
+
64
+ assert_raises(InvalidHexaStringObjectError) { HexaString.parse("") }
65
+ assert_raises(InvalidHexaStringObjectError) { HexaString.parse("<12") }
66
+ assert_raises(InvalidHexaStringObjectError) { HexaString.parse("<12X>") }
67
+ end
68
+
69
+ def test_parse_bool
70
+ b_true = Boolean.parse("true")
71
+ b_false = Boolean.parse("false")
72
+
73
+ assert_instance_of Boolean, b_true
74
+ assert_instance_of Boolean, b_false
75
+
76
+ assert b_false.false?
77
+ refute b_true.false?
78
+
79
+ assert_raises(InvalidBooleanObjectError) { Boolean.parse("") }
80
+ assert_raises(InvalidBooleanObjectError) { Boolean.parse("tru") }
81
+ end
82
+
83
+ def test_parse_real
84
+ real = Real.parse("-3.141592653")
85
+ assert_instance_of Real, real
86
+ assert_equal real, -3.141592653
87
+
88
+ real = Real.parse("+.00200")
89
+ assert_instance_of Real, real
90
+ assert_equal real, 0.002
91
+
92
+ assert_raises(InvalidRealObjectError) { Real.parse("") }
93
+ assert_raises(InvalidRealObjectError) { Real.parse(".") }
94
+ assert_raises(InvalidRealObjectError) { Real.parse("+0x1") }
95
+ end
96
+
97
+ def test_parse_int
98
+ int = Origami::Integer.parse("02000000000000")
99
+ assert_instance_of Origami::Integer, int
100
+ assert_equal int, 2000000000000
101
+
102
+ int = Origami::Integer.parse("-98")
103
+ assert_instance_of Origami::Integer, int
104
+ assert_equal int, -98
105
+
106
+ assert_raises(Origami::InvalidIntegerObjectError) { Origami::Integer.parse("") }
107
+ assert_raises(Origami::InvalidIntegerObjectError) { Origami::Integer.parse("+-1") }
108
+ assert_raises(Origami::InvalidIntegerObjectError) { Origami::Integer.parse("ABC") }
109
+ end
110
+
111
+ def test_parse_name
112
+ name = Name.parse("/#52#55#62#79#0A")
113
+ assert_instance_of Name, name
114
+ assert_equal name.value, :"RUby\n"
115
+
116
+ name = Name.parse("/")
117
+ assert_instance_of Name, name
118
+ assert_equal :"", name.value
119
+
120
+ assert_raises(Origami::InvalidNameObjectError) { Name.parse("") }
121
+ assert_raises(Origami::InvalidNameObjectError) { Name.parse("test") }
122
+ end
123
+
124
+ def test_parse_reference
125
+ ref = Reference.parse("199 1 R")
126
+ assert_instance_of Reference, ref
127
+
128
+ assert_equal [199, 1], ref.to_a
129
+ assert_raises(InvalidReferenceError) { ref.solve }
130
+ assert_raises(InvalidReferenceError) { Reference.parse("-2 0 R") }
131
+ assert_raises(InvalidReferenceError) { Reference.parse("0 R") }
132
+ assert_raises(InvalidReferenceError) { Reference.parse("") }
133
+ end
134
+ end
@@ -0,0 +1,69 @@
1
+ require 'minitest/autorun'
2
+ require 'stringio'
3
+
4
+ class TestPDFLazyParser < Minitest::Test
5
+
6
+ def setup
7
+ @files =
8
+ %w{
9
+ dataset/empty.pdf
10
+ dataset/calc.pdf
11
+ dataset/crypto.pdf
12
+ }
13
+
14
+ end
15
+
16
+ def test_parse_pdf_lazy
17
+ @files.each do |file|
18
+ pdf = PDF.read(File.join(__dir__, file),
19
+ ignore_errors: false,
20
+ lazy: true,
21
+ verbosity: Parser::VERBOSE_QUIET)
22
+
23
+ assert_instance_of PDF, pdf
24
+
25
+ pdf.each_object do |object|
26
+ assert_kind_of Origami::Object, object
27
+ end
28
+
29
+ assert_instance_of Catalog, pdf.Catalog
30
+
31
+ pdf.each_page do |page|
32
+ assert_kind_of Page, page
33
+ end
34
+ end
35
+ end
36
+
37
+ def test_save_pdf_lazy
38
+ @files.each do |file|
39
+ pdf = PDF.read(File.join(__dir__, file),
40
+ ignore_errors: false,
41
+ lazy: true,
42
+ verbosity: Parser::VERBOSE_QUIET)
43
+
44
+ pdf.save(StringIO.new)
45
+ end
46
+ end
47
+
48
+ def test_random_access
49
+ io = StringIO.new
50
+ stream = Stream.new("abc")
51
+
52
+ PDF.create(io) do |pdf|
53
+ pdf.insert(stream)
54
+ end
55
+
56
+ io = io.reopen(io.string, 'r')
57
+
58
+ pdf = PDF.read(io, ignore_errors: false,
59
+ lazy: true,
60
+ verbosity: Parser::VERBOSE_QUIET)
61
+
62
+ non_existent = pdf[42]
63
+ existent = pdf[stream.reference]
64
+
65
+ assert_nil non_existent
66
+ assert_instance_of Stream, existent
67
+ assert_equal stream.data, existent.data
68
+ end
69
+ end
@@ -0,0 +1,97 @@
1
+ require 'minitest/autorun'
2
+ require 'stringio'
3
+ require 'openssl'
4
+
5
+ class TestSign < Minitest::Test
6
+
7
+ def create_self_signed_ca_certificate(key_size, expires)
8
+ key = OpenSSL::PKey::RSA.new key_size
9
+
10
+ name = OpenSSL::X509::Name.parse 'CN=origami/DC=example'
11
+
12
+ cert = OpenSSL::X509::Certificate.new
13
+ cert.version = 2
14
+ cert.serial = 0
15
+ cert.not_before = Time.now
16
+ cert.not_after = Time.now + expires
17
+
18
+ cert.public_key = key.public_key
19
+ cert.subject = name
20
+
21
+ extension_factory = OpenSSL::X509::ExtensionFactory.new
22
+ extension_factory.issuer_certificate = cert
23
+ extension_factory.subject_certificate = cert
24
+
25
+ cert.add_extension extension_factory.create_extension('basicConstraints', 'CA:TRUE', true)
26
+ cert.add_extension extension_factory.create_extension('keyUsage', 'digitalSignature,keyCertSign')
27
+ cert.add_extension extension_factory.create_extension('subjectKeyIdentifier', 'hash')
28
+
29
+ cert.issuer = name
30
+ cert.sign key, OpenSSL::Digest::SHA256.new
31
+
32
+ [ cert, key ]
33
+ end
34
+
35
+ def setup
36
+ @cert, @key = create_self_signed_ca_certificate(1024, 3600)
37
+ @other_cert, @other_key = create_self_signed_ca_certificate(1024, 3600)
38
+ end
39
+
40
+ def setup_document_with_annotation
41
+ document = PDF.read(File.join(__dir__, "dataset/calc.pdf"),
42
+ ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
43
+
44
+ annotation = Annotation::Widget::Signature.new.set_indirect(true)
45
+ annotation.Rect = Rectangle[llx: 89.0, lly: 386.0, urx: 190.0, ury: 353.0]
46
+
47
+ document.append_page do |page|
48
+ page.add_annotation(annotation)
49
+ end
50
+
51
+ [ document, annotation ]
52
+ end
53
+
54
+ def sign_document_with_method(method)
55
+ document, annotation = setup_document_with_annotation
56
+
57
+ document.sign(@cert, @key,
58
+ method: method,
59
+ annotation: annotation,
60
+ issuer: "Guillaume Delugré",
61
+ location: "France",
62
+ contact: "origami@localhost",
63
+ reason: "Example"
64
+ )
65
+
66
+ assert document.frozen?
67
+ assert document.signed?
68
+
69
+ output = StringIO.new
70
+ document.save(output)
71
+
72
+ document = PDF.read(output.reopen(output.string,'r'), verbosity: Parser::VERBOSE_QUIET)
73
+
74
+ refute document.verify
75
+ assert document.verify(allow_self_signed: true)
76
+ assert document.verify(trusted_certs: [@cert])
77
+ refute document.verify(trusted_certs: [@other_cert])
78
+
79
+ result = document.verify do |ctx|
80
+ ctx.error == OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT and ctx.current_cert.to_pem == @cert.to_pem
81
+ end
82
+
83
+ assert result
84
+ end
85
+
86
+ def test_sign_pkcs7_sha1
87
+ sign_document_with_method(Signature::PKCS7_SHA1)
88
+ end
89
+
90
+ def test_sign_pkcs7_detached
91
+ sign_document_with_method(Signature::PKCS7_DETACHED)
92
+ end
93
+
94
+ def test_sign_x509_sha1
95
+ sign_document_with_method(Signature::PKCS1_RSA_SHA1)
96
+ end
97
+ end
@@ -0,0 +1,184 @@
1
+ require 'minitest/autorun'
2
+ require 'stringio'
3
+
4
+ class TestStreams < Minitest::Test
5
+ def setup
6
+ @target = PDF.new
7
+ @output = StringIO.new
8
+ @data = "0123456789" * 1024
9
+ end
10
+
11
+ def test_predictors
12
+ stm = Stream.new(@data, :Filter => :FlateDecode)
13
+ stm.set_predictor(Filter::Predictor::TIFF)
14
+ raw = stm.encoded_data
15
+ stm.data = nil
16
+ stm.encoded_data = raw
17
+
18
+ assert_equal @data, stm.data
19
+
20
+ stm = Stream.new(@data, :Filter => :FlateDecode)
21
+ stm.set_predictor(Filter::Predictor::PNG_SUB)
22
+ raw = stm.encoded_data
23
+ stm.data = nil
24
+ stm.encoded_data = raw
25
+
26
+ assert_equal @data, stm.data
27
+
28
+ stm = Stream.new(@data, :Filter => :FlateDecode)
29
+ stm.set_predictor(Filter::Predictor::PNG_UP)
30
+ raw = stm.encoded_data
31
+ stm.data = nil
32
+ stm.encoded_data = raw
33
+
34
+ assert_equal stm.data, @data
35
+
36
+ stm = Stream.new(@data, :Filter => :FlateDecode)
37
+ stm.set_predictor(Filter::Predictor::PNG_AVERAGE)
38
+ raw = stm.encoded_data
39
+ stm.data = nil
40
+ stm.encoded_data = raw
41
+
42
+ assert_equal stm.data, @data
43
+
44
+ stm = Stream.new(@data, :Filter => :FlateDecode)
45
+ stm.set_predictor(Filter::Predictor::PNG_PAETH)
46
+ raw = stm.encoded_data
47
+ stm.data = nil
48
+ stm.encoded_data = raw
49
+
50
+ assert_equal stm.data, @data
51
+ end
52
+
53
+ def test_filter_flate
54
+ stm = Stream.new(@data, :Filter => :FlateDecode)
55
+ raw = stm.encoded_data
56
+ stm.data = nil
57
+ stm.encoded_data = raw
58
+
59
+ assert_equal stm.data, @data
60
+
61
+ assert_equal Filter::Flate.decode(Filter::Flate.encode("")), ""
62
+ end
63
+
64
+ def test_filter_asciihex
65
+ stm = Stream.new(@data, :Filter => :ASCIIHexDecode)
66
+ raw = stm.encoded_data
67
+ stm.data = nil
68
+ stm.encoded_data = raw
69
+
70
+ assert_equal stm.data, @data
71
+
72
+ assert_raises(Filter::InvalidASCIIHexStringError) do
73
+ Filter::ASCIIHex.decode("123456789ABCDEFGHIJKL")
74
+ end
75
+
76
+ assert_equal Filter::ASCIIHex.decode(Filter::ASCIIHex.encode("")), ""
77
+ end
78
+
79
+ def test_filter_ascii85
80
+ stm = Stream.new(@data, :Filter => :ASCII85Decode)
81
+ raw = stm.encoded_data
82
+ stm.data = nil
83
+ stm.encoded_data = raw
84
+
85
+ assert_equal stm.data, @data
86
+
87
+ assert_raises(Filter::InvalidASCII85StringError) do
88
+ Filter::ASCII85.decode("ABCD\x01")
89
+ end
90
+
91
+ assert_equal Filter::ASCII85.decode(Filter::ASCII85.encode("")), ""
92
+ end
93
+
94
+ def test_filter_rle
95
+ stm = Stream.new(@data, :Filter => :RunLengthDecode)
96
+ raw = stm.encoded_data
97
+ stm.data = nil
98
+ stm.encoded_data = raw
99
+
100
+ assert_equal stm.data, @data
101
+
102
+ assert_raises(Filter::InvalidRunLengthDataError) do
103
+ Filter::RunLength.decode("\x7f")
104
+ end
105
+
106
+ assert_equal Filter::RunLength.decode(Filter::RunLength.encode("")), ""
107
+ end
108
+
109
+ def test_filter_lzw
110
+ stm = Stream.new(@data, :Filter => :LZWDecode)
111
+ raw = stm.encoded_data
112
+ stm.data = nil
113
+ stm.encoded_data = raw
114
+
115
+ assert_equal stm.data, @data
116
+
117
+ assert_raises(Filter::InvalidLZWDataError) do
118
+ Filter::LZW.decode("abcd")
119
+ end
120
+
121
+ assert_equal Filter::LZW.decode(Filter::LZW.encode("")), ""
122
+ end
123
+
124
+ def test_filter_ccittfax
125
+ stm = Stream.new(@data[0, 216], :Filter => :CCITTFaxDecode)
126
+
127
+ raw = stm.encoded_data
128
+ stm.data = nil
129
+ stm.encoded_data = raw
130
+
131
+ assert_equal stm.data, @data[0, 216]
132
+
133
+ assert_raises(Filter::InvalidCCITTFaxDataError) do
134
+ Filter::CCITTFax.decode("abcd")
135
+ end
136
+
137
+ assert_equal Filter::CCITTFax.decode(Filter::CCITTFax.encode("")), ""
138
+ end
139
+
140
+ def test_stream
141
+ chain = %i[FlateDecode LZWDecode ASCIIHexDecode]
142
+
143
+ stm = Stream.new(@data, Filter: chain)
144
+ @target << stm
145
+ @target.save(@output)
146
+
147
+ assert stm.Length == stm.encoded_data.length
148
+ assert_equal stm.filters, chain
149
+ assert_equal stm.data, @data
150
+ end
151
+
152
+ def test_object_stream
153
+ objstm = ObjectStream.new
154
+ objstm.Filter = %i[FlateDecode ASCIIHexDecode RunLengthDecode]
155
+
156
+ @target << objstm
157
+
158
+ assert_raises(InvalidObjectError) do
159
+ objstm.insert Stream.new
160
+ end
161
+
162
+ 3.times do
163
+ objstm.insert HexaString.new(@data)
164
+ end
165
+
166
+ assert_equal objstm.objects.size, 3
167
+
168
+ objstm.each_object do |object|
169
+ assert_instance_of HexaString, object
170
+ assert_equal object.parent, objstm
171
+ assert objstm.include?(object.no)
172
+ assert_equal objstm.extract(object.no), object
173
+ assert_equal objstm.extract_by_index(objstm.index(object.no)), object
174
+ end
175
+
176
+ objstm.delete(objstm.objects.first.no)
177
+ assert_equal objstm.objects.size, 2
178
+
179
+ @target.save(@output)
180
+
181
+ assert_instance_of Origami::Integer, objstm.N
182
+ assert_equal objstm.N, objstm.objects.size
183
+ end
184
+ end
@@ -0,0 +1,67 @@
1
+ require 'minitest/autorun'
2
+ require 'stringio'
3
+ require 'strscan'
4
+
5
+ class TestXrefs < MiniTest::Test
6
+
7
+ def setup
8
+ @target = PDF.new
9
+ end
10
+
11
+ def test_xreftable
12
+ output = StringIO.new
13
+
14
+ @target.save(output)
15
+ output.reopen(output.string, 'r')
16
+
17
+ pdf = PDF.read(output, verbosity: Parser::VERBOSE_QUIET, ignore_errors: false)
18
+
19
+ xreftable = pdf.revisions.last.xreftable
20
+ assert_instance_of XRef::Section, xreftable
21
+
22
+ pdf.root_objects.each do |object|
23
+ xref = xreftable.find(object.no)
24
+
25
+ assert_instance_of XRef, xref
26
+ assert xref.used?
27
+
28
+ assert_equal xref.offset, object.file_offset
29
+ end
30
+ end
31
+
32
+ def test_xrefstream
33
+ output = StringIO.new
34
+ objstm = ObjectStream.new
35
+ objstm.Filter = :FlateDecode
36
+
37
+ @target.insert objstm
38
+
39
+ 3.times do
40
+ objstm.insert Null.new
41
+ end
42
+
43
+ @target.save(output)
44
+ output = output.reopen(output.string, 'r')
45
+
46
+ pdf = PDF.read(output, verbosity: Parser::VERBOSE_QUIET, ignore_errors: false)
47
+ xrefstm = pdf.revisions.last.xrefstm
48
+
49
+ assert_instance_of XRefStream, xrefstm
50
+ assert xrefstm.entries.all?{ |xref| xref.is_a?(XRef) or xref.is_a?(XRefToCompressedObject) }
51
+
52
+ pdf.each_object(compressed: true) do |object|
53
+ xref = xrefstm.find(object.no)
54
+
55
+ if object.parent.is_a?(ObjectStream)
56
+ assert_instance_of XRefToCompressedObject, xref
57
+ assert_equal xref.objstmno, object.parent.no
58
+ assert_equal xref.index, object.parent.index(object.no)
59
+ else
60
+ assert_instance_of XRef, xref
61
+ assert_equal xref.offset, object.file_offset
62
+ end
63
+ end
64
+
65
+ assert_instance_of Catalog, xrefstm.Root
66
+ end
67
+ end