origami 1.2.7 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +66 -0
- data/README.md +112 -0
- data/bin/config/pdfcop.conf.yml +232 -233
- data/bin/gui/about.rb +27 -37
- data/bin/gui/config.rb +108 -117
- data/bin/gui/file.rb +416 -365
- data/bin/gui/gtkhex.rb +1138 -1153
- data/bin/gui/hexview.rb +55 -57
- data/bin/gui/imgview.rb +48 -51
- data/bin/gui/menu.rb +388 -386
- data/bin/gui/properties.rb +114 -130
- data/bin/gui/signing.rb +571 -617
- data/bin/gui/textview.rb +77 -95
- data/bin/gui/treeview.rb +382 -387
- data/bin/gui/walker.rb +227 -232
- data/bin/gui/xrefs.rb +56 -60
- data/bin/pdf2pdfa +53 -57
- data/bin/pdf2ruby +212 -228
- data/bin/pdfcop +338 -348
- data/bin/pdfdecompress +58 -65
- data/bin/pdfdecrypt +56 -60
- data/bin/pdfencrypt +75 -80
- data/bin/pdfexplode +185 -182
- data/bin/pdfextract +201 -218
- data/bin/pdfmetadata +83 -82
- data/bin/pdfsh +4 -5
- data/bin/pdfwalker +1 -2
- data/bin/shell/.irbrc +45 -82
- data/bin/shell/console.rb +105 -130
- data/bin/shell/hexdump.rb +40 -64
- data/examples/README.md +34 -0
- data/examples/attachments/attachment.rb +38 -0
- data/examples/attachments/nested_document.rb +51 -0
- data/examples/encryption/encryption.rb +28 -0
- data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
- data/examples/flash/flash.rb +37 -0
- data/{samples → examples}/flash/helloworld.swf +0 -0
- data/examples/forms/javascript.rb +54 -0
- data/examples/forms/xfa.rb +115 -0
- data/examples/javascript/hello_world.rb +22 -0
- data/examples/javascript/js_emulation.rb +54 -0
- data/examples/loop/goto.rb +32 -0
- data/examples/loop/named.rb +33 -0
- data/examples/signature/signature.rb +65 -0
- data/examples/uri/javascript.rb +56 -0
- data/examples/uri/open-uri.rb +21 -0
- data/examples/uri/submitform.rb +47 -0
- data/lib/origami.rb +29 -42
- data/lib/origami/3d.rb +350 -225
- data/lib/origami/acroform.rb +262 -288
- data/lib/origami/actions.rb +268 -288
- data/lib/origami/annotations.rb +697 -722
- data/lib/origami/array.rb +258 -184
- data/lib/origami/boolean.rb +74 -84
- data/lib/origami/catalog.rb +397 -434
- data/lib/origami/collections.rb +144 -0
- data/lib/origami/destinations.rb +233 -194
- data/lib/origami/dictionary.rb +253 -232
- data/lib/origami/encryption.rb +1274 -1243
- data/lib/origami/export.rb +232 -268
- data/lib/origami/extensions/fdf.rb +307 -220
- data/lib/origami/extensions/ppklite.rb +368 -435
- data/lib/origami/filespec.rb +197 -0
- data/lib/origami/filters.rb +301 -295
- data/lib/origami/filters/ascii.rb +177 -180
- data/lib/origami/filters/ccitt.rb +528 -535
- data/lib/origami/filters/crypt.rb +26 -35
- data/lib/origami/filters/dct.rb +46 -52
- data/lib/origami/filters/flate.rb +95 -94
- data/lib/origami/filters/jbig2.rb +49 -55
- data/lib/origami/filters/jpx.rb +38 -44
- data/lib/origami/filters/lzw.rb +189 -183
- data/lib/origami/filters/predictors.rb +221 -235
- data/lib/origami/filters/runlength.rb +103 -104
- data/lib/origami/font.rb +173 -186
- data/lib/origami/functions.rb +67 -81
- data/lib/origami/graphics.rb +25 -21
- data/lib/origami/graphics/colors.rb +178 -187
- data/lib/origami/graphics/instruction.rb +79 -85
- data/lib/origami/graphics/path.rb +142 -148
- data/lib/origami/graphics/patterns.rb +160 -167
- data/lib/origami/graphics/render.rb +43 -50
- data/lib/origami/graphics/state.rb +138 -153
- data/lib/origami/graphics/text.rb +188 -205
- data/lib/origami/graphics/xobject.rb +819 -815
- data/lib/origami/header.rb +63 -78
- data/lib/origami/javascript.rb +596 -597
- data/lib/origami/linearization.rb +285 -290
- data/lib/origami/metadata.rb +139 -148
- data/lib/origami/name.rb +112 -148
- data/lib/origami/null.rb +53 -62
- data/lib/origami/numeric.rb +162 -175
- data/lib/origami/obfuscation.rb +186 -174
- data/lib/origami/object.rb +593 -573
- data/lib/origami/outline.rb +42 -47
- data/lib/origami/outputintents.rb +73 -82
- data/lib/origami/page.rb +703 -592
- data/lib/origami/parser.rb +238 -290
- data/lib/origami/parsers/fdf.rb +41 -33
- data/lib/origami/parsers/pdf.rb +75 -95
- data/lib/origami/parsers/pdf/lazy.rb +137 -0
- data/lib/origami/parsers/pdf/linear.rb +64 -66
- data/lib/origami/parsers/ppklite.rb +34 -70
- data/lib/origami/pdf.rb +1030 -1005
- data/lib/origami/reference.rb +102 -102
- data/lib/origami/signature.rb +591 -609
- data/lib/origami/stream.rb +668 -551
- data/lib/origami/string.rb +397 -373
- data/lib/origami/template/patterns.rb +56 -0
- data/lib/origami/template/widgets.rb +151 -0
- data/lib/origami/trailer.rb +144 -158
- data/lib/origami/tree.rb +62 -0
- data/lib/origami/version.rb +23 -0
- data/lib/origami/webcapture.rb +88 -79
- data/lib/origami/xfa.rb +2863 -2882
- data/lib/origami/xreftable.rb +472 -384
- data/test/dataset/calc.pdf +85 -0
- data/test/dataset/crypto.pdf +82 -0
- data/test/dataset/empty.pdf +49 -0
- data/test/test_actions.rb +27 -0
- data/test/test_annotations.rb +90 -0
- data/test/test_pages.rb +31 -0
- data/test/test_pdf.rb +16 -0
- data/test/test_pdf_attachment.rb +34 -0
- data/test/test_pdf_create.rb +24 -0
- data/test/test_pdf_encrypt.rb +95 -0
- data/test/test_pdf_parse.rb +96 -0
- data/test/test_pdf_sign.rb +58 -0
- data/test/test_streams.rb +182 -0
- data/test/test_xrefs.rb +67 -0
- metadata +88 -58
- data/README +0 -67
- data/bin/pdf2graph +0 -121
- data/bin/pdfcocoon +0 -104
- data/lib/origami/file.rb +0 -233
- data/samples/README.txt +0 -45
- data/samples/actions/launch/calc.rb +0 -87
- data/samples/actions/launch/winparams.rb +0 -22
- data/samples/actions/loop/loopgoto.rb +0 -24
- data/samples/actions/loop/loopnamed.rb +0 -21
- data/samples/actions/named/named.rb +0 -31
- data/samples/actions/samba/smbrelay.rb +0 -26
- data/samples/actions/webbug/submitform.js +0 -26
- data/samples/actions/webbug/webbug-browser.rb +0 -68
- data/samples/actions/webbug/webbug-js.rb +0 -67
- data/samples/actions/webbug/webbug-reader.rb +0 -90
- data/samples/attachments/attach.rb +0 -40
- data/samples/attachments/attached.txt +0 -1
- data/samples/crypto/crypto.rb +0 -28
- data/samples/digsig/signed.rb +0 -46
- data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
- data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
- data/samples/exploits/exploit_customdictopen.rb +0 -55
- data/samples/exploits/getannots.rb +0 -69
- data/samples/flash/flash.rb +0 -31
- data/samples/javascript/attached.txt +0 -1
- data/samples/javascript/js.rb +0 -52
- data/templates/patterns.rb +0 -66
- data/templates/widgets.rb +0 -173
- data/templates/xdp.rb +0 -92
- data/test/ts_pdf.rb +0 -50
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require 'stringio'
|
|
3
|
+
|
|
4
|
+
class TestEncryption < Minitest::Test
|
|
5
|
+
def setup
|
|
6
|
+
@target = PDF.read(File.join(__dir__, "dataset/calc.pdf"),
|
|
7
|
+
ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
|
|
8
|
+
@output = StringIO.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_encrypt_rc4_40b
|
|
12
|
+
@output.string = ""
|
|
13
|
+
@target.encrypt(cipher: 'rc4', key_size: 40).save(@output)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_encrypt_rc4_128b
|
|
17
|
+
@output.string = ""
|
|
18
|
+
@target.encrypt(cipher: 'rc4').save(@output)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_encrypt_aes_128b
|
|
22
|
+
@output.string = ""
|
|
23
|
+
@target.encrypt(cipher: 'aes').save(@output)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_decrypt_rc4_40b
|
|
27
|
+
@output.string = ""
|
|
28
|
+
|
|
29
|
+
pdf = PDF.new.encrypt(cipher: 'rc4', key_size: 40)
|
|
30
|
+
pdf.Catalog[:Test] = "test"
|
|
31
|
+
pdf.save(@output)
|
|
32
|
+
|
|
33
|
+
refute_equal pdf.Catalog[:Test], "test"
|
|
34
|
+
|
|
35
|
+
@output = @output.reopen(@output.string, "r")
|
|
36
|
+
pdf = PDF.read(@output, ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
|
|
37
|
+
|
|
38
|
+
assert_equal pdf.Catalog[:Test], "test"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_decrypt_rc4_128b
|
|
42
|
+
@output.string = ""
|
|
43
|
+
pdf = PDF.new.encrypt(cipher: 'rc4')
|
|
44
|
+
pdf.Catalog[:Test] = "test"
|
|
45
|
+
pdf.save(@output)
|
|
46
|
+
|
|
47
|
+
refute_equal pdf.Catalog[:Test], "test"
|
|
48
|
+
|
|
49
|
+
@output.reopen(@output.string, "r")
|
|
50
|
+
pdf = PDF.read(@output, ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
|
|
51
|
+
|
|
52
|
+
assert_equal pdf.Catalog[:Test], "test"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_decrypt_aes_128b
|
|
56
|
+
@output.string = ""
|
|
57
|
+
pdf = PDF.new.encrypt(cipher: 'aes')
|
|
58
|
+
pdf.Catalog[:Test] = "test"
|
|
59
|
+
pdf.save(@output)
|
|
60
|
+
|
|
61
|
+
refute_equal pdf.Catalog[:Test], "test"
|
|
62
|
+
|
|
63
|
+
@output = @output.reopen(@output.string, "r")
|
|
64
|
+
pdf = PDF.read(@output, ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
|
|
65
|
+
|
|
66
|
+
assert_equal pdf.Catalog[:Test], "test"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_decrypt_aes_256b
|
|
70
|
+
@output.string = ""
|
|
71
|
+
pdf = PDF.new.encrypt(cipher: 'aes', key_size: 256)
|
|
72
|
+
pdf.Catalog[:Test] = "test"
|
|
73
|
+
pdf.save(@output)
|
|
74
|
+
|
|
75
|
+
refute_equal pdf.Catalog[:Test], "test"
|
|
76
|
+
|
|
77
|
+
@output = @output.reopen(@output.string, "r")
|
|
78
|
+
pdf = PDF.read(@output, ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
|
|
79
|
+
|
|
80
|
+
assert_equal pdf.Catalog[:Test], "test"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_crypt_filter
|
|
84
|
+
@output.string = ""
|
|
85
|
+
pdf = PDF.new.encrypt(cipher: 'aes', key_size: 128)
|
|
86
|
+
|
|
87
|
+
pdf.Catalog[:S1] = Stream.new("test", :Filter => :Crypt)
|
|
88
|
+
pdf.Catalog[:S2] = Stream.new("test")
|
|
89
|
+
|
|
90
|
+
pdf.save(@output)
|
|
91
|
+
|
|
92
|
+
assert_equal pdf.Catalog.S1.encoded_data, "test"
|
|
93
|
+
refute_equal pdf.Catalog.S2.encoded_data, "test"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
|
|
3
|
+
class TestPDFParser < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@data =
|
|
6
|
+
%w{
|
|
7
|
+
dataset/empty.pdf
|
|
8
|
+
dataset/calc.pdf
|
|
9
|
+
dataset/crypto.pdf
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@dict = StringScanner.new "<</Ref 2 0 R/N null/Pi 3.14 /D <<>>>>"
|
|
13
|
+
|
|
14
|
+
@literalstring = StringScanner.new "(\\122\\125by\\n)"
|
|
15
|
+
@hexastring = StringScanner.new "<52 55 62 79 0A>"
|
|
16
|
+
@true = StringScanner.new "true"
|
|
17
|
+
@false = StringScanner.new "false"
|
|
18
|
+
@real = StringScanner.new "-3.141592653"
|
|
19
|
+
@int = StringScanner.new "00000000002000000000000"
|
|
20
|
+
@name = StringScanner.new "/#52#55#62#79#0A"
|
|
21
|
+
@ref = StringScanner.new "199 1 R"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_parse_pdf
|
|
25
|
+
@data.each do |file|
|
|
26
|
+
pdf = PDF.read(File.join(__dir__, file), ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
|
|
27
|
+
|
|
28
|
+
assert_instance_of PDF, pdf
|
|
29
|
+
|
|
30
|
+
pdf.each_object do |object|
|
|
31
|
+
assert_kind_of Origami::Object, object
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_parse_dictionary
|
|
37
|
+
dict = Dictionary.parse(@dict)
|
|
38
|
+
|
|
39
|
+
assert_instance_of Dictionary, dict
|
|
40
|
+
assert_instance_of Dictionary, dict[:D]
|
|
41
|
+
assert_instance_of Null, dict[:N]
|
|
42
|
+
assert_instance_of Reference, dict[:Ref]
|
|
43
|
+
assert_raises(InvalidReferenceError) { dict[:Ref].solve }
|
|
44
|
+
assert dict[:Pi] == 3.14
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_parse_string
|
|
48
|
+
str = LiteralString.parse(@literalstring)
|
|
49
|
+
assert_instance_of LiteralString, str
|
|
50
|
+
assert_equal str.value, "RUby\n"
|
|
51
|
+
|
|
52
|
+
str = HexaString.parse(@hexastring)
|
|
53
|
+
assert_instance_of HexaString, str
|
|
54
|
+
assert_equal str.value, "RUby\n"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def test_parse_bool
|
|
58
|
+
b_true = Boolean.parse(@true)
|
|
59
|
+
b_false = Boolean.parse(@false)
|
|
60
|
+
|
|
61
|
+
assert_instance_of Boolean, b_true
|
|
62
|
+
assert_instance_of Boolean, b_false
|
|
63
|
+
|
|
64
|
+
assert b_false.false?
|
|
65
|
+
assert (not b_true.false?)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_parse_real
|
|
69
|
+
real = Real.parse(@real)
|
|
70
|
+
assert_instance_of Real, real
|
|
71
|
+
|
|
72
|
+
assert_equal real, -3.141592653
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_parse_int
|
|
76
|
+
int = Origami::Integer.parse(@int)
|
|
77
|
+
assert_instance_of Origami::Integer, int
|
|
78
|
+
|
|
79
|
+
assert_equal int, 2000000000000
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_parse_name
|
|
83
|
+
name = Name.parse(@name)
|
|
84
|
+
assert_instance_of Name, name
|
|
85
|
+
|
|
86
|
+
assert_equal name.value, :"RUby\n"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_parse_reference
|
|
90
|
+
ref = Reference.parse(@ref)
|
|
91
|
+
assert_instance_of Reference, ref
|
|
92
|
+
|
|
93
|
+
assert_equal [199, 1], ref.to_a
|
|
94
|
+
assert_raises(InvalidReferenceError) { ref.solve }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require 'stringio'
|
|
3
|
+
require 'openssl'
|
|
4
|
+
|
|
5
|
+
class TestSign < Minitest::Test
|
|
6
|
+
|
|
7
|
+
def setup
|
|
8
|
+
@target = PDF.read(File.join(__dir__, "dataset/calc.pdf"),
|
|
9
|
+
ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
|
|
10
|
+
@output = StringIO.new
|
|
11
|
+
|
|
12
|
+
@key = OpenSSL::PKey::RSA.new 2048
|
|
13
|
+
|
|
14
|
+
name = OpenSSL::X509::Name.parse 'CN=origami/DC=example'
|
|
15
|
+
|
|
16
|
+
@cert = OpenSSL::X509::Certificate.new
|
|
17
|
+
@cert.version = 2
|
|
18
|
+
@cert.serial = 0
|
|
19
|
+
@cert.not_before = Time.now
|
|
20
|
+
@cert.not_after = Time.now + 3600
|
|
21
|
+
|
|
22
|
+
@cert.public_key = @key.public_key
|
|
23
|
+
@cert.subject = name
|
|
24
|
+
|
|
25
|
+
extension_factory = OpenSSL::X509::ExtensionFactory.new nil, @cert
|
|
26
|
+
|
|
27
|
+
@cert.add_extension extension_factory.create_extension('basicConstraints', 'CA:TRUE', true)
|
|
28
|
+
@cert.add_extension extension_factory.create_extension('keyUsage', 'digitalSignature')
|
|
29
|
+
@cert.add_extension extension_factory.create_extension('subjectKeyIdentifier', 'hash')
|
|
30
|
+
|
|
31
|
+
@cert.issuer = name
|
|
32
|
+
@cert.sign @key, OpenSSL::Digest::SHA256.new
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_sign
|
|
36
|
+
sig_annot = Annotation::Widget::Signature.new.set_indirect(true)
|
|
37
|
+
sig_annot.Rect = Rectangle[llx: 89.0, lly: 386.0, urx: 190.0, ury: 353.0]
|
|
38
|
+
|
|
39
|
+
@target.append_page do |page|
|
|
40
|
+
page.add_annotation(sig_annot)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
@target.sign(@cert, @key,
|
|
44
|
+
annotation: sig_annot,
|
|
45
|
+
issuer: "Guillaume Delugré",
|
|
46
|
+
location: "France",
|
|
47
|
+
contact: "origami@localhost",
|
|
48
|
+
reason: "Example"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
assert @target.frozen?
|
|
52
|
+
assert @target.signed?
|
|
53
|
+
|
|
54
|
+
@target.save(@output)
|
|
55
|
+
|
|
56
|
+
assert PDF.read(@output.reopen(@output.string,'r'), verbosity: Parser::VERBOSE_QUIET).verify
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,182 @@
|
|
|
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
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_filter_asciihex
|
|
63
|
+
stm = Stream.new(@data, :Filter => :ASCIIHexDecode)
|
|
64
|
+
raw = stm.encoded_data
|
|
65
|
+
stm.data = nil
|
|
66
|
+
stm.encoded_data = raw
|
|
67
|
+
|
|
68
|
+
assert_equal stm.data, @data
|
|
69
|
+
|
|
70
|
+
assert_raises(Filter::InvalidASCIIHexStringError) do
|
|
71
|
+
Filter::ASCIIHex.decode("123456789ABCDEFGHIJKL")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
assert_equal Filter::ASCIIHex.decode(""), ""
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_filter_ascii85
|
|
78
|
+
stm = Stream.new(@data, :Filter => :ASCII85Decode)
|
|
79
|
+
raw = stm.encoded_data
|
|
80
|
+
stm.data = nil
|
|
81
|
+
stm.encoded_data = raw
|
|
82
|
+
|
|
83
|
+
assert_equal stm.data, @data
|
|
84
|
+
|
|
85
|
+
assert_raises(Filter::InvalidASCII85StringError) do
|
|
86
|
+
Filter::ASCII85.decode("ABCD\x01")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
assert_equal Filter::ASCII85.decode(""), ""
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_filter_rle
|
|
93
|
+
stm = Stream.new(@data, :Filter => :RunLengthDecode)
|
|
94
|
+
raw = stm.encoded_data
|
|
95
|
+
stm.data = nil
|
|
96
|
+
stm.encoded_data = raw
|
|
97
|
+
|
|
98
|
+
assert_equal stm.data, @data
|
|
99
|
+
|
|
100
|
+
assert_raises(Filter::InvalidRunLengthDataError) do
|
|
101
|
+
Filter::RunLength.decode("\x7f")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
assert_equal Filter::RunLength.decode(""), ""
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_filter_lzw
|
|
108
|
+
stm = Stream.new(@data, :Filter => :LZWDecode)
|
|
109
|
+
raw = stm.encoded_data
|
|
110
|
+
stm.data = nil
|
|
111
|
+
stm.encoded_data = raw
|
|
112
|
+
|
|
113
|
+
assert_equal stm.data, @data
|
|
114
|
+
|
|
115
|
+
assert_raises(Filter::InvalidLZWDataError) do
|
|
116
|
+
Filter::LZW.decode("abcd")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
assert_equal Filter::LZW.decode(""), ""
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def test_filter_ccittfax
|
|
123
|
+
stm = Stream.new(@data[0, 216], :Filter => :CCITTFaxDecode)
|
|
124
|
+
|
|
125
|
+
raw = stm.encoded_data
|
|
126
|
+
stm.data = nil
|
|
127
|
+
stm.encoded_data = raw
|
|
128
|
+
|
|
129
|
+
assert_equal stm.data, @data[0, 216]
|
|
130
|
+
|
|
131
|
+
assert_raises(Filter::InvalidCCITTFaxDataError) do
|
|
132
|
+
Filter::CCITTFax.decode("abcd")
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
assert_equal Filter::CCITTFax.decode(""), ""
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def test_stream
|
|
139
|
+
chain = %i[FlateDecode LZWDecode ASCIIHexDecode]
|
|
140
|
+
|
|
141
|
+
stm = Stream.new(@data, Filter: chain)
|
|
142
|
+
@target << stm
|
|
143
|
+
@target.save(@output)
|
|
144
|
+
|
|
145
|
+
assert stm.Length == stm.encoded_data.length
|
|
146
|
+
assert_equal stm.filters, chain
|
|
147
|
+
assert_equal stm.data, @data
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def test_object_stream
|
|
151
|
+
objstm = ObjectStream.new
|
|
152
|
+
objstm.Filter = %i[FlateDecode ASCIIHexDecode RunLengthDecode]
|
|
153
|
+
|
|
154
|
+
@target << objstm
|
|
155
|
+
|
|
156
|
+
assert_raises(InvalidObjectError) do
|
|
157
|
+
objstm.insert Stream.new
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
3.times do
|
|
161
|
+
objstm.insert HexaString.new(@data)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
assert_equal objstm.objects.size, 3
|
|
165
|
+
|
|
166
|
+
objstm.each_object do |object|
|
|
167
|
+
assert_instance_of HexaString, object
|
|
168
|
+
assert_equal object.parent, objstm
|
|
169
|
+
assert objstm.include?(object.no)
|
|
170
|
+
assert_equal objstm.extract(object.no), object
|
|
171
|
+
assert_equal objstm.extract_by_index(objstm.index(object.no)), object
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
objstm.delete(objstm.objects.first.no)
|
|
175
|
+
assert_equal objstm.objects.size, 2
|
|
176
|
+
|
|
177
|
+
@target.save(@output)
|
|
178
|
+
|
|
179
|
+
assert_instance_of Origami::Integer, objstm.N
|
|
180
|
+
assert_equal objstm.N, objstm.objects.size
|
|
181
|
+
end
|
|
182
|
+
end
|
data/test/test_xrefs.rb
ADDED
|
@@ -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?(XRefToCompressedObj) }
|
|
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 XRefToCompressedObj, 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
|