origami 1.0.2

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 (108) hide show
  1. data/COPYING.LESSER +165 -0
  2. data/README +77 -0
  3. data/VERSION +1 -0
  4. data/bin/config/pdfcop.conf.yml +237 -0
  5. data/bin/gui/about.rb +46 -0
  6. data/bin/gui/config.rb +132 -0
  7. data/bin/gui/file.rb +385 -0
  8. data/bin/gui/hexdump.rb +74 -0
  9. data/bin/gui/hexview.rb +91 -0
  10. data/bin/gui/imgview.rb +72 -0
  11. data/bin/gui/menu.rb +392 -0
  12. data/bin/gui/properties.rb +132 -0
  13. data/bin/gui/signing.rb +635 -0
  14. data/bin/gui/textview.rb +107 -0
  15. data/bin/gui/treeview.rb +409 -0
  16. data/bin/gui/walker.rb +282 -0
  17. data/bin/gui/xrefs.rb +79 -0
  18. data/bin/pdf2graph +121 -0
  19. data/bin/pdf2ruby +353 -0
  20. data/bin/pdfcocoon +104 -0
  21. data/bin/pdfcop +455 -0
  22. data/bin/pdfdecompress +104 -0
  23. data/bin/pdfdecrypt +95 -0
  24. data/bin/pdfencrypt +112 -0
  25. data/bin/pdfextract +221 -0
  26. data/bin/pdfmetadata +123 -0
  27. data/bin/pdfsh +13 -0
  28. data/bin/pdfwalker +7 -0
  29. data/bin/shell/.irbrc +104 -0
  30. data/bin/shell/console.rb +136 -0
  31. data/bin/shell/hexdump.rb +83 -0
  32. data/origami.rb +36 -0
  33. data/origami/3d.rb +239 -0
  34. data/origami/acroform.rb +321 -0
  35. data/origami/actions.rb +299 -0
  36. data/origami/adobe/fdf.rb +259 -0
  37. data/origami/adobe/ppklite.rb +489 -0
  38. data/origami/annotations.rb +775 -0
  39. data/origami/array.rb +187 -0
  40. data/origami/boolean.rb +101 -0
  41. data/origami/catalog.rb +486 -0
  42. data/origami/destinations.rb +213 -0
  43. data/origami/dictionary.rb +188 -0
  44. data/origami/docmdp.rb +96 -0
  45. data/origami/encryption.rb +1293 -0
  46. data/origami/export.rb +283 -0
  47. data/origami/file.rb +222 -0
  48. data/origami/filters.rb +250 -0
  49. data/origami/filters/ascii.rb +189 -0
  50. data/origami/filters/ccitt.rb +515 -0
  51. data/origami/filters/crypt.rb +47 -0
  52. data/origami/filters/dct.rb +61 -0
  53. data/origami/filters/flate.rb +112 -0
  54. data/origami/filters/jbig2.rb +63 -0
  55. data/origami/filters/jpx.rb +53 -0
  56. data/origami/filters/lzw.rb +195 -0
  57. data/origami/filters/predictors.rb +276 -0
  58. data/origami/filters/runlength.rb +117 -0
  59. data/origami/font.rb +209 -0
  60. data/origami/functions.rb +93 -0
  61. data/origami/graphics.rb +33 -0
  62. data/origami/graphics/colors.rb +191 -0
  63. data/origami/graphics/instruction.rb +126 -0
  64. data/origami/graphics/path.rb +154 -0
  65. data/origami/graphics/patterns.rb +180 -0
  66. data/origami/graphics/state.rb +164 -0
  67. data/origami/graphics/text.rb +224 -0
  68. data/origami/graphics/xobject.rb +493 -0
  69. data/origami/header.rb +90 -0
  70. data/origami/linearization.rb +318 -0
  71. data/origami/metadata.rb +114 -0
  72. data/origami/name.rb +170 -0
  73. data/origami/null.rb +75 -0
  74. data/origami/numeric.rb +188 -0
  75. data/origami/obfuscation.rb +233 -0
  76. data/origami/object.rb +527 -0
  77. data/origami/outline.rb +59 -0
  78. data/origami/page.rb +559 -0
  79. data/origami/parser.rb +268 -0
  80. data/origami/parsers/fdf.rb +45 -0
  81. data/origami/parsers/pdf.rb +27 -0
  82. data/origami/parsers/pdf/linear.rb +113 -0
  83. data/origami/parsers/ppklite.rb +86 -0
  84. data/origami/pdf.rb +1144 -0
  85. data/origami/reference.rb +113 -0
  86. data/origami/signature.rb +474 -0
  87. data/origami/stream.rb +575 -0
  88. data/origami/string.rb +416 -0
  89. data/origami/trailer.rb +173 -0
  90. data/origami/webcapture.rb +87 -0
  91. data/origami/xfa.rb +3027 -0
  92. data/origami/xreftable.rb +447 -0
  93. data/templates/patterns.rb +66 -0
  94. data/templates/widgets.rb +173 -0
  95. data/templates/xdp.rb +92 -0
  96. data/tests/dataset/test.dummycrt +28 -0
  97. data/tests/dataset/test.dummykey +27 -0
  98. data/tests/tc_actions.rb +32 -0
  99. data/tests/tc_annotations.rb +85 -0
  100. data/tests/tc_pages.rb +37 -0
  101. data/tests/tc_pdfattach.rb +24 -0
  102. data/tests/tc_pdfencrypt.rb +110 -0
  103. data/tests/tc_pdfnew.rb +32 -0
  104. data/tests/tc_pdfparse.rb +98 -0
  105. data/tests/tc_pdfsig.rb +37 -0
  106. data/tests/tc_streams.rb +129 -0
  107. data/tests/ts_pdf.rb +45 -0
  108. metadata +193 -0
@@ -0,0 +1,170 @@
1
+ =begin
2
+
3
+ = File
4
+ name.rb
5
+
6
+ = Info
7
+ This file is part of Origami, PDF manipulation framework for Ruby
8
+ Copyright (C) 2010 Guillaume Delugré <guillaume@security-labs.org>
9
+ All right reserved.
10
+
11
+ Origami is free software: you can redistribute it and/or modify
12
+ it under the terms of the GNU Lesser General Public License as published by
13
+ the Free Software Foundation, either version 3 of the License, or
14
+ (at your option) any later version.
15
+
16
+ Origami is distributed in the hope that it will be useful,
17
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ GNU Lesser General Public License for more details.
20
+
21
+ You should have received a copy of the GNU Lesser General Public License
22
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
+
24
+ =end
25
+
26
+ if RUBY_VERSION < '1.9'
27
+ class EmptySymbol
28
+ def ==(sym)
29
+ sym.is_a?(EmptySymbol)
30
+ end
31
+
32
+ def id2name
33
+ ""
34
+ end
35
+ alias to_s id2name
36
+
37
+ def to_sym
38
+ self
39
+ end
40
+
41
+ def to_o
42
+ Name.new("")
43
+ end
44
+
45
+ def inspect
46
+ ":"
47
+ end
48
+ end
49
+ end
50
+
51
+ module Origami
52
+
53
+ REGULARCHARS = "([^ \\t\\r\\n\\0\\[\\]<>()%\\/]|#[a-fA-F0-9][a-fA-F0-9])*" #:nodoc:
54
+
55
+ class InvalidNameObjectError < InvalidObjectError #:nodoc:
56
+ end
57
+
58
+ #
59
+ # Class representing a Name Object.
60
+ # Name objects are strings which identify some PDF file inner structures.
61
+ #
62
+ class Name #< DelegateClass(Symbol)
63
+
64
+ include Origami::Object
65
+
66
+ TOKENS = %w{ / } #:nodoc:
67
+
68
+ @@regexp = Regexp.new(WHITESPACES + TOKENS.first + "(" + REGULARCHARS + ")" + WHITESPACES) #:nodoc
69
+
70
+ #
71
+ # Creates a new Name.
72
+ # _name_:: A symbol representing the new Name value.
73
+ #
74
+ def initialize(name = "")
75
+ unless name.is_a?(Symbol) or name.is_a?(::String)
76
+ raise TypeError, "Expected type Symbol or String, received #{name.class}."
77
+ end
78
+
79
+ @value = name.to_s
80
+
81
+ super()
82
+ end
83
+
84
+ if RUBY_VERSION < '1.9'
85
+ def value
86
+ ( @value.empty? ) ? EmptySymbol.new : @value.to_sym
87
+ end
88
+ else
89
+ def value
90
+ @value.to_sym
91
+ end
92
+ end
93
+
94
+ def ==(object) #:nodoc:
95
+ @value.to_sym == object
96
+ end
97
+
98
+ def eql?(object) #:nodoc:
99
+ object.is_a?(Name) and self.to_s == object.to_s
100
+ end
101
+
102
+ def hash #:nodoc:
103
+ @value.hash
104
+ end
105
+
106
+ def to_s #:nodoc:
107
+ super(TOKENS.first + Name.expand(@value))
108
+ end
109
+
110
+ def self.parse(stream) #:nodoc:
111
+
112
+ offset = stream.pos
113
+
114
+ name =
115
+ if stream.scan(@@regexp).nil?
116
+ raise InvalidNameObjectError, "Bad name format"
117
+ else
118
+ value = stream[2]
119
+
120
+ Name.new(value.include?('#') ? contract(value) : value)
121
+ end
122
+
123
+ name.file_offset = offset
124
+
125
+ name
126
+ end
127
+
128
+ def self.contract(name) #:nodoc:
129
+
130
+ i = 0
131
+ while i < name.length
132
+
133
+ if name[i,1] == "#"
134
+ digits = name[i+1, 2]
135
+
136
+ unless /^[A-Za-z0-9]{2}$/ === digits
137
+ raise InvalidNameObjectError, "Irregular use of # token"
138
+ end
139
+
140
+ char = digits.hex.chr
141
+
142
+ if char == "\0"
143
+ raise InvalidNameObjectError, "Null byte forbidden inside name definition"
144
+ end
145
+
146
+ name[i, 3] = char
147
+ end
148
+
149
+ i = i + 1
150
+ end
151
+
152
+ name
153
+ end
154
+
155
+ def self.expand(name) #:nodoc:
156
+
157
+ forbiddenchars = /[ #\t\r\n\0\[\]<>()%\/]/
158
+
159
+ name.gsub!(forbiddenchars) do |c|
160
+ "#" + c[0].ord.to_s(16).rjust(2,"0")
161
+ end
162
+
163
+ name
164
+ end
165
+
166
+ def real_type ; Name end
167
+
168
+ end
169
+
170
+ end
@@ -0,0 +1,75 @@
1
+ =begin
2
+
3
+ = File
4
+ null.rb
5
+
6
+ = Info
7
+ This file is part of Origami, PDF manipulation framework for Ruby
8
+ Copyright (C) 2010 Guillaume Delugr� <guillaume@security-labs.org>
9
+ All right reserved.
10
+
11
+ Origami is free software: you can redistribute it and/or modify
12
+ it under the terms of the GNU Lesser General Public License as published by
13
+ the Free Software Foundation, either version 3 of the License, or
14
+ (at your option) any later version.
15
+
16
+ Origami is distributed in the hope that it will be useful,
17
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ GNU Lesser General Public License for more details.
20
+
21
+ You should have received a copy of the GNU Lesser General Public License
22
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
+
24
+ =end
25
+
26
+ module Origami
27
+
28
+ class InvalidNullObjectError < InvalidObjectError #:nodoc:
29
+ end
30
+
31
+ #
32
+ # Class representing Null Object.
33
+ #
34
+ class Null
35
+
36
+ include Origami::Object
37
+
38
+ TOKENS = %w{ null } #:nodoc:
39
+
40
+ @@regexp = Regexp.new(WHITESPACES + TOKENS.first)
41
+
42
+ def initialize
43
+ super
44
+ end
45
+
46
+ def self.parse(stream) #:nodoc:
47
+
48
+ offset = stream.pos
49
+
50
+ if stream.skip(@@regexp).nil?
51
+ raise InvalidNullObjectError
52
+ end
53
+
54
+ null = Null.new
55
+ null.file_offset = offset
56
+
57
+ null
58
+ end
59
+
60
+ #
61
+ # Returns *nil*.
62
+ #
63
+ def value
64
+ nil
65
+ end
66
+
67
+ def to_s #:nodoc:
68
+ super(TOKENS.first)
69
+ end
70
+
71
+ def real_type ; Null end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,188 @@
1
+ =begin
2
+
3
+ = File
4
+ numeric.rb
5
+
6
+ = Info
7
+ Origami is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Origami is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+
22
+ require 'delegate'
23
+
24
+ module Origami
25
+
26
+ class InvalidIntegerObjectError < InvalidObjectError #:nodoc:
27
+ end
28
+
29
+ #
30
+ # Class representing a PDF number (Integer, or Real).
31
+ #
32
+ module Number
33
+ include Origami::Object
34
+
35
+ def ~
36
+ self.class.new(~self.to_i)
37
+ end
38
+
39
+ def |(val)
40
+ self.class.new(self.to_i | val)
41
+ end
42
+
43
+ def &(val)
44
+ self.class.new(self.to_i & val)
45
+ end
46
+
47
+ def ^(val)
48
+ self.class.new(self.to_i ^ val)
49
+ end
50
+
51
+ def <<(val)
52
+ self.class.new(self.to_i << val)
53
+ end
54
+
55
+ def >>(val)
56
+ self.class.new(self.to_i >> val)
57
+ end
58
+
59
+ def +(val)
60
+ self.class.new(self.to_i + val)
61
+ end
62
+
63
+ def -(val)
64
+ self.class.new(self.to_i - val)
65
+ end
66
+
67
+ def -@
68
+ self.class.new(-self.to_i)
69
+ end
70
+
71
+ def *(val)
72
+ self.class.new(self.to_i * val)
73
+ end
74
+
75
+ def /(val)
76
+ self.class.new(self.to_i / val)
77
+ end
78
+
79
+ def abs
80
+ self.class.new(self.to_i.abs)
81
+ end
82
+
83
+ def **(val)
84
+ self.class.new(self.to_i ** val)
85
+ end
86
+
87
+ def to_s
88
+ super(value.to_s)
89
+ end
90
+
91
+ def real_type ; Number end
92
+ end
93
+
94
+ #
95
+ # Class representing an Integer Object.
96
+ #
97
+ class Integer < DelegateClass(Bignum)
98
+
99
+ include Number
100
+
101
+ TOKENS = [ "(\\+|-)?[\\d]+[^.]?" ] #:nodoc:
102
+
103
+ REGEXP_TOKEN = Regexp.new(TOKENS.first)
104
+
105
+ @@regexp = Regexp.new(WHITESPACES + "((\\+|-)?[\\d]+)")
106
+
107
+ #
108
+ # Creates a new Integer from a Ruby Fixnum / Bignum.
109
+ # _i_:: The Integer value.
110
+ #
111
+ def initialize(i = 0)
112
+
113
+ unless i.is_a?(::Integer)
114
+ raise TypeError, "Expected type Fixnum or Bignum, received #{i.class}."
115
+ end
116
+
117
+ super(i)
118
+ end
119
+
120
+ def self.parse(stream) #:nodoc:
121
+
122
+ offset = stream.pos
123
+
124
+ if not stream.scan(@@regexp)
125
+ raise InvalidIntegerObjectError, "Invalid integer format"
126
+ end
127
+
128
+ value = stream[2].to_i
129
+ int = Integer.new(value)
130
+ int.file_offset = offset
131
+
132
+ int
133
+ end
134
+
135
+ alias value to_i
136
+
137
+ end
138
+
139
+ class InvalidRealObjectError < InvalidObjectError #:nodoc:
140
+ end
141
+
142
+ #
143
+ # Class representing a Real number Object.
144
+ # PDF real numbers are arbitrary precision numbers, depending on architectures.
145
+ #
146
+ class Real < DelegateClass(Float)
147
+
148
+ include Number
149
+
150
+ TOKENS = [ "(\\+|-)?([\\d]*\\.[\\d]+|[\\d]+\\.[\\d]*)([eE](\\+|-)?[\\d]+)?" ] #:nodoc:
151
+
152
+ REGEXP_TOKEN = Regexp.new(TOKENS.first)
153
+
154
+ @@regexp = Regexp.new(WHITESPACES + "(" + TOKENS.first + ")")
155
+
156
+ #
157
+ # Creates a new Real from a Ruby Float.
158
+ # _f_:: The new Real value.
159
+ #
160
+ def initialize(f = 0)
161
+
162
+ unless f.is_a?(Float)
163
+ raise TypeError, "Expected type Float, received #{f.class}."
164
+ end
165
+
166
+ super(f)
167
+ end
168
+
169
+ def self.parse(stream) #:nodoc:
170
+
171
+ offset = stream.pos
172
+
173
+ if not stream.scan(@@regexp)
174
+ raise InvalidRealObjectError, "Invalid real number format"
175
+ end
176
+
177
+ value = stream[2].to_f
178
+ real = Real.new(value)
179
+ real.file_offset = offset
180
+
181
+ real
182
+ end
183
+
184
+ alias value to_f
185
+
186
+ end
187
+
188
+ end
@@ -0,0 +1,233 @@
1
+ module Origami
2
+
3
+ module Obfuscator
4
+
5
+ WHITECHARS = [ " ", "\t", "\r", "\n", "\0" ]
6
+ OBJECTS = [ Array, Boolean, Dictionary, Integer, Name, Null, Stream, String, Real, Reference ]
7
+ MAX_INT = 0xFFFFFFFF
8
+ PRINTABLE = ("!".."9").to_a + (':'..'Z').to_a + ('['..'z').to_a + ('{'..'~').to_a
9
+ FILTERS = [ :FlateDecode, :RunLengthDecode, :LZWDecode, :ASCIIHexDecode, :ASCII85Decode ]
10
+
11
+ def self.junk_spaces(max_size = 3)
12
+ length = rand(max_size) + 1
13
+
14
+ ::Array.new(length) { WHITECHARS[rand(WHITECHARS.size)] }.join
15
+ end
16
+
17
+ def self.junk_comment(max_size = 15)
18
+ length = rand(max_size) + 1
19
+
20
+ junk_comment = ::Array.new(length) {
21
+ byte = rand(256).chr until (not byte.nil? and byte != "\n" and byte != "\r"); byte
22
+ }.join
23
+
24
+ "%#{junk_comment}#{EOL}"
25
+ end
26
+
27
+ def self.junk_object(type = nil)
28
+
29
+ if type.nil?
30
+ type = OBJECTS[rand(OBJECTS.size)]
31
+ end
32
+
33
+ unless type.include?(Origami::Object)
34
+ raise TypeError, "Not a valid object type"
35
+ end
36
+
37
+ Obfuscator.send("junk_#{type.to_s.split('::').last.downcase}")
38
+ end
39
+
40
+ def self.junk_array(max_size = 5)
41
+ length = rand(max_size) + 1
42
+
43
+ ::Array.new(length) {
44
+ obj = Obfuscator.junk_object until (not obj.nil? and not obj.is_a?(Stream)) ; obj
45
+ }.to_o
46
+ end
47
+
48
+ def self.junk_boolean
49
+ Boolean.new(rand(2).zero?)
50
+ end
51
+
52
+ def self.junk_dictionary(max_size = 5)
53
+ length = rand(max_size) + 1
54
+
55
+ hash = Hash.new
56
+ length.times do
57
+ obj = Obfuscator.junk_object
58
+ hash[Obfuscator.junk_name] = obj unless obj.is_a?(Stream)
59
+ end
60
+
61
+ hash.to_o
62
+ end
63
+
64
+ def self.junk_integer(max = MAX_INT)
65
+ Integer.new(rand(max + 1))
66
+ end
67
+
68
+ def self.junk_name(max_size = 8)
69
+ length = rand(max_size) + 1
70
+
71
+ Name.new(::Array.new(length) { PRINTABLE[rand(PRINTABLE.size)] }.join)
72
+ end
73
+
74
+ def self.junk_null
75
+ Null.new
76
+ end
77
+
78
+ def self.junk_stream(max_data_size = 200)
79
+
80
+ chainlen = rand(2) + 1
81
+ chain = ::Array.new(chainlen) { FILTERS[rand(FILTERS.size)] }
82
+
83
+ length = rand(max_data_size) + 1
84
+ junk_data = ::Array.new(length) { rand(256).chr }.join
85
+
86
+ stm = Stream.new
87
+ stm.dictionary = Obfuscator.junk_dictionary(5)
88
+ stm.setFilter(chain)
89
+ stm.data = junk_data
90
+
91
+ stm
92
+ end
93
+
94
+ def self.junk_string(max_size = 10)
95
+ length = rand(max_size) + 1
96
+
97
+ strtype = (rand(2).zero?) ? ByteString : HexaString
98
+
99
+ strtype.new(::Array.new(length) { PRINTABLE[rand(PRINTABLE.size)] }.join)
100
+ end
101
+
102
+ def self.junk_real
103
+ Real.new(rand * rand(MAX_INT + 1))
104
+ end
105
+
106
+ def self.junk_reference(max_no = 300, max_gen = 1)
107
+ no = rand(max_no) + 1
108
+ gen = rand(max_gen)
109
+
110
+ Reference.new(no, gen)
111
+ end
112
+
113
+ end
114
+
115
+ class Dictionary
116
+
117
+ def to_obfuscated_str
118
+ content = TOKENS.first + Obfuscator.junk_spaces
119
+ self.each_pair { |key, value|
120
+ content << Obfuscator.junk_spaces +
121
+ key.to_obfuscated_str + Obfuscator.junk_spaces +
122
+ value.to_obfuscated_str + Obfuscator.junk_spaces
123
+ }
124
+
125
+ content << TOKENS.last
126
+ super(content)
127
+ end
128
+
129
+ end
130
+
131
+ module Object
132
+ alias :to_obfuscated_str :to_s
133
+ end
134
+
135
+ class Array
136
+ def to_obfuscated_str
137
+ content = TOKENS.first + Obfuscator.junk_spaces
138
+ self.each { |entry|
139
+ content << entry.to_o.to_obfuscated_str + Obfuscator.junk_spaces
140
+ }
141
+
142
+ content << TOKENS.last
143
+
144
+ super(content)
145
+ end
146
+ end
147
+
148
+ class Null
149
+ alias :to_obfuscated_str :to_s
150
+ end
151
+
152
+ class Boolean
153
+ alias :to_obfuscated_str :to_s
154
+ end
155
+
156
+ class Integer
157
+ alias :to_obfuscated_str :to_s
158
+ end
159
+
160
+ class Real
161
+ alias :to_obfuscated_str :to_s
162
+ end
163
+
164
+ class Reference
165
+ def to_obfuscated_str
166
+ refstr = refno.to_s + Obfuscator.junk_spaces + refgen.to_s + Obfuscator.junk_spaces + "R"
167
+
168
+ super(refstr)
169
+ end
170
+ end
171
+
172
+ class ByteString
173
+ def to_obfuscated_str
174
+ to_s
175
+ end
176
+ end
177
+
178
+ class HexaString
179
+ def to_obfuscated_str
180
+ to_s
181
+ end
182
+ end
183
+
184
+ class Name
185
+ def to_obfuscated_str(prop = 2)
186
+ name = @value.dup
187
+
188
+ forbiddenchars = [ " ","#","\t","\r","\n","\0","[","]","<",">","(",")","%","/","\\" ]
189
+
190
+ name.gsub!(/./) do |c|
191
+ if rand(prop) == 0 or forbiddenchars.include?(c)
192
+ hexchar = c[0].to_s(base=16)
193
+ hexchar = "0" + hexchar if hexchar.length < 2
194
+
195
+ '#' + hexchar
196
+ else
197
+ c
198
+ end
199
+ end
200
+
201
+ super(TOKENS.first + name)
202
+ end
203
+ end
204
+
205
+ class Stream
206
+ def to_obfuscated_str
207
+ content = ""
208
+
209
+ content << @dictionary.to_obfuscated_str
210
+ content << "stream" + EOL
211
+ content << self.rawdata
212
+ content << EOL << TOKENS.last
213
+
214
+ super(content)
215
+ end
216
+ end
217
+
218
+ class Trailer
219
+
220
+ def to_obfuscated_str
221
+ content = ""
222
+ if self.has_dictionary?
223
+ content << TOKENS.first << EOL << @dictionary.to_obfuscated_str << EOL
224
+ end
225
+
226
+ content << XREF_TOKEN << EOL << @startxref.to_s << EOL << TOKENS.last << EOL
227
+
228
+ content
229
+ end
230
+
231
+ end
232
+
233
+ end