origami 1.2.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/README.md +112 -0
  4. data/bin/config/pdfcop.conf.yml +232 -233
  5. data/bin/gui/about.rb +27 -37
  6. data/bin/gui/config.rb +108 -117
  7. data/bin/gui/file.rb +416 -365
  8. data/bin/gui/gtkhex.rb +1138 -1153
  9. data/bin/gui/hexview.rb +55 -57
  10. data/bin/gui/imgview.rb +48 -51
  11. data/bin/gui/menu.rb +388 -386
  12. data/bin/gui/properties.rb +114 -130
  13. data/bin/gui/signing.rb +571 -617
  14. data/bin/gui/textview.rb +77 -95
  15. data/bin/gui/treeview.rb +382 -387
  16. data/bin/gui/walker.rb +227 -232
  17. data/bin/gui/xrefs.rb +56 -60
  18. data/bin/pdf2pdfa +53 -57
  19. data/bin/pdf2ruby +212 -228
  20. data/bin/pdfcop +338 -348
  21. data/bin/pdfdecompress +58 -65
  22. data/bin/pdfdecrypt +56 -60
  23. data/bin/pdfencrypt +75 -80
  24. data/bin/pdfexplode +185 -182
  25. data/bin/pdfextract +201 -218
  26. data/bin/pdfmetadata +83 -82
  27. data/bin/pdfsh +4 -5
  28. data/bin/pdfwalker +1 -2
  29. data/bin/shell/.irbrc +45 -82
  30. data/bin/shell/console.rb +105 -130
  31. data/bin/shell/hexdump.rb +40 -64
  32. data/examples/README.md +34 -0
  33. data/examples/attachments/attachment.rb +38 -0
  34. data/examples/attachments/nested_document.rb +51 -0
  35. data/examples/encryption/encryption.rb +28 -0
  36. data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
  37. data/examples/flash/flash.rb +37 -0
  38. data/{samples → examples}/flash/helloworld.swf +0 -0
  39. data/examples/forms/javascript.rb +54 -0
  40. data/examples/forms/xfa.rb +115 -0
  41. data/examples/javascript/hello_world.rb +22 -0
  42. data/examples/javascript/js_emulation.rb +54 -0
  43. data/examples/loop/goto.rb +32 -0
  44. data/examples/loop/named.rb +33 -0
  45. data/examples/signature/signature.rb +65 -0
  46. data/examples/uri/javascript.rb +56 -0
  47. data/examples/uri/open-uri.rb +21 -0
  48. data/examples/uri/submitform.rb +47 -0
  49. data/lib/origami.rb +29 -42
  50. data/lib/origami/3d.rb +350 -225
  51. data/lib/origami/acroform.rb +262 -288
  52. data/lib/origami/actions.rb +268 -288
  53. data/lib/origami/annotations.rb +697 -722
  54. data/lib/origami/array.rb +258 -184
  55. data/lib/origami/boolean.rb +74 -84
  56. data/lib/origami/catalog.rb +397 -434
  57. data/lib/origami/collections.rb +144 -0
  58. data/lib/origami/destinations.rb +233 -194
  59. data/lib/origami/dictionary.rb +253 -232
  60. data/lib/origami/encryption.rb +1274 -1243
  61. data/lib/origami/export.rb +232 -268
  62. data/lib/origami/extensions/fdf.rb +307 -220
  63. data/lib/origami/extensions/ppklite.rb +368 -435
  64. data/lib/origami/filespec.rb +197 -0
  65. data/lib/origami/filters.rb +301 -295
  66. data/lib/origami/filters/ascii.rb +177 -180
  67. data/lib/origami/filters/ccitt.rb +528 -535
  68. data/lib/origami/filters/crypt.rb +26 -35
  69. data/lib/origami/filters/dct.rb +46 -52
  70. data/lib/origami/filters/flate.rb +95 -94
  71. data/lib/origami/filters/jbig2.rb +49 -55
  72. data/lib/origami/filters/jpx.rb +38 -44
  73. data/lib/origami/filters/lzw.rb +189 -183
  74. data/lib/origami/filters/predictors.rb +221 -235
  75. data/lib/origami/filters/runlength.rb +103 -104
  76. data/lib/origami/font.rb +173 -186
  77. data/lib/origami/functions.rb +67 -81
  78. data/lib/origami/graphics.rb +25 -21
  79. data/lib/origami/graphics/colors.rb +178 -187
  80. data/lib/origami/graphics/instruction.rb +79 -85
  81. data/lib/origami/graphics/path.rb +142 -148
  82. data/lib/origami/graphics/patterns.rb +160 -167
  83. data/lib/origami/graphics/render.rb +43 -50
  84. data/lib/origami/graphics/state.rb +138 -153
  85. data/lib/origami/graphics/text.rb +188 -205
  86. data/lib/origami/graphics/xobject.rb +819 -815
  87. data/lib/origami/header.rb +63 -78
  88. data/lib/origami/javascript.rb +596 -597
  89. data/lib/origami/linearization.rb +285 -290
  90. data/lib/origami/metadata.rb +139 -148
  91. data/lib/origami/name.rb +112 -148
  92. data/lib/origami/null.rb +53 -62
  93. data/lib/origami/numeric.rb +162 -175
  94. data/lib/origami/obfuscation.rb +186 -174
  95. data/lib/origami/object.rb +593 -573
  96. data/lib/origami/outline.rb +42 -47
  97. data/lib/origami/outputintents.rb +73 -82
  98. data/lib/origami/page.rb +703 -592
  99. data/lib/origami/parser.rb +238 -290
  100. data/lib/origami/parsers/fdf.rb +41 -33
  101. data/lib/origami/parsers/pdf.rb +75 -95
  102. data/lib/origami/parsers/pdf/lazy.rb +137 -0
  103. data/lib/origami/parsers/pdf/linear.rb +64 -66
  104. data/lib/origami/parsers/ppklite.rb +34 -70
  105. data/lib/origami/pdf.rb +1030 -1005
  106. data/lib/origami/reference.rb +102 -102
  107. data/lib/origami/signature.rb +591 -609
  108. data/lib/origami/stream.rb +668 -551
  109. data/lib/origami/string.rb +397 -373
  110. data/lib/origami/template/patterns.rb +56 -0
  111. data/lib/origami/template/widgets.rb +151 -0
  112. data/lib/origami/trailer.rb +144 -158
  113. data/lib/origami/tree.rb +62 -0
  114. data/lib/origami/version.rb +23 -0
  115. data/lib/origami/webcapture.rb +88 -79
  116. data/lib/origami/xfa.rb +2863 -2882
  117. data/lib/origami/xreftable.rb +472 -384
  118. data/test/dataset/calc.pdf +85 -0
  119. data/test/dataset/crypto.pdf +82 -0
  120. data/test/dataset/empty.pdf +49 -0
  121. data/test/test_actions.rb +27 -0
  122. data/test/test_annotations.rb +90 -0
  123. data/test/test_pages.rb +31 -0
  124. data/test/test_pdf.rb +16 -0
  125. data/test/test_pdf_attachment.rb +34 -0
  126. data/test/test_pdf_create.rb +24 -0
  127. data/test/test_pdf_encrypt.rb +95 -0
  128. data/test/test_pdf_parse.rb +96 -0
  129. data/test/test_pdf_sign.rb +58 -0
  130. data/test/test_streams.rb +182 -0
  131. data/test/test_xrefs.rb +67 -0
  132. metadata +88 -58
  133. data/README +0 -67
  134. data/bin/pdf2graph +0 -121
  135. data/bin/pdfcocoon +0 -104
  136. data/lib/origami/file.rb +0 -233
  137. data/samples/README.txt +0 -45
  138. data/samples/actions/launch/calc.rb +0 -87
  139. data/samples/actions/launch/winparams.rb +0 -22
  140. data/samples/actions/loop/loopgoto.rb +0 -24
  141. data/samples/actions/loop/loopnamed.rb +0 -21
  142. data/samples/actions/named/named.rb +0 -31
  143. data/samples/actions/samba/smbrelay.rb +0 -26
  144. data/samples/actions/webbug/submitform.js +0 -26
  145. data/samples/actions/webbug/webbug-browser.rb +0 -68
  146. data/samples/actions/webbug/webbug-js.rb +0 -67
  147. data/samples/actions/webbug/webbug-reader.rb +0 -90
  148. data/samples/attachments/attach.rb +0 -40
  149. data/samples/attachments/attached.txt +0 -1
  150. data/samples/crypto/crypto.rb +0 -28
  151. data/samples/digsig/signed.rb +0 -46
  152. data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
  153. data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
  154. data/samples/exploits/exploit_customdictopen.rb +0 -55
  155. data/samples/exploits/getannots.rb +0 -69
  156. data/samples/flash/flash.rb +0 -31
  157. data/samples/javascript/attached.txt +0 -1
  158. data/samples/javascript/js.rb +0 -52
  159. data/templates/patterns.rb +0 -66
  160. data/templates/widgets.rb +0 -173
  161. data/templates/xdp.rb +0 -92
  162. data/test/ts_pdf.rb +0 -50
@@ -1,210 +1,284 @@
1
1
  =begin
2
2
 
3
- = File
4
- array.rb
5
-
6
- = Info
7
- This file is part of Origami, PDF manipulation framework for Ruby
8
- Copyright (C) 2010 Guillaume DelugrÈ <guillaume AT security-labs DOT 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/>.
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
23
5
 
24
- =end
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
25
10
 
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
26
15
 
27
- module Origami
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
28
20
 
29
- class InvalidArrayObjectError < InvalidObjectError #:nodoc:
30
- end
31
21
 
32
- #
33
- # Class representing an Array Object.
34
- # Arrays contain a set of Object.
35
- #
36
- class Array < ::Array
37
- include Origami::Object
38
-
39
- TOKENS = %w{ [ ] } #:nodoc:
40
- @@regexp_open = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.first) + WHITESPACES)
41
- @@regexp_close = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.last) + WHITESPACES)
22
+ module Origami
42
23
 
43
- attr_reader :strings_cache, :names_cache, :xref_cache
24
+ class InvalidArrayObjectError < InvalidObjectError #:nodoc:
25
+ end
44
26
 
45
27
  #
46
- # Creates a new PDF Array Object.
47
- # _data_:: An array of objects.
28
+ # Class representing an Array Object.
29
+ # Arrays contain a set of Object.
48
30
  #
49
- def initialize(data = [])
50
- raise TypeError, "Expected type Array, received #{data.class}." unless data.is_a?(::Array)
51
- super()
52
-
53
- @strings_cache = []
54
- @names_cache = []
55
- @xref_cache = {}
56
-
57
- i = 0
58
- while i < data.size
59
- case val = data[i].to_o
60
- when String then @strings_cache.push(val)
61
- when Name then @names_cache.push(val)
62
- when Reference then
63
- (@xref_cache[val] ||= []).push(self)
64
- when Dictionary,Array then
65
- @strings_cache.concat(val.strings_cache)
66
- @names_cache.concat(val.names_cache)
67
- @xref_cache.update(val.xref_cache) do |ref, cache1, cache2|
68
- cache1.concat(cache2)
31
+ class Array < ::Array
32
+ include Origami::Object
33
+ using TypeConversion
34
+
35
+ TOKENS = %w{ [ ] } #:nodoc:
36
+ @@regexp_open = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.first) + WHITESPACES)
37
+ @@regexp_close = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.last) + WHITESPACES)
38
+
39
+ attr_reader :strings_cache, :names_cache, :xref_cache
40
+
41
+ #
42
+ # Creates a new PDF Array Object.
43
+ # _data_:: An array of objects.
44
+ #
45
+ def initialize(data = [], parser = nil, hint_type: nil)
46
+ raise TypeError, "Expected type Array, received #{data.class}." unless data.is_a?(::Array)
47
+ super()
48
+
49
+ @strings_cache = []
50
+ @names_cache = []
51
+ @xref_cache = {}
52
+
53
+ data.each_with_index do |value, index|
54
+ value = value.to_o
55
+
56
+ if Origami::OPTIONS[:enable_type_guessing]
57
+ index_type = hint_type.is_a?(::Array) ? hint_type[index % hint_type.size] : hint_type
58
+ if index_type.is_a?(::Array) and not value.is_a?(Reference)
59
+ index_type = index_type.find {|type| type < value.class }
60
+ end
61
+
62
+ if index_type.is_a?(Class) and index_type < value.class
63
+ value = value.cast_to(index_type, parser)
64
+ end
65
+
66
+ if index_type and parser and Origami::OPTIONS[:enable_type_propagation]
67
+ if value.is_a?(Reference)
68
+ parser.defer_type_cast(value, index_type)
69
+ end
70
+ end
71
+ end
72
+
73
+ # Cache object value for fast search.
74
+ cache_value(value)
75
+
76
+ self.push(value)
69
77
  end
78
+ end
79
+
80
+ def pre_build
81
+ self.map!{|obj| obj.to_o}
70
82
 
71
- val.strings_cache.clear
72
- val.names_cache.clear
73
- val.xref_cache.clear
83
+ super
74
84
  end
75
85
 
76
- self[i] = val
77
- i = i + 1
78
- end
79
-
80
- end
81
-
82
- def pre_build
83
- self.map!{|obj| obj.to_o}
84
-
85
- super
86
- end
87
-
88
- def self.parse(stream, parser = nil) #:nodoc:
89
- data = []
90
- offset = stream.pos
91
-
92
- if not stream.skip(@@regexp_open)
93
- raise InvalidArrayObjectError, "No token '#{TOKENS.first}' found"
94
- end
95
-
96
- while stream.skip(@@regexp_close).nil? do
97
-
98
- type = Object.typeof(stream)
99
- if type.nil?
100
- raise InvalidArrayObjectError, "Bad embedded object format"
86
+ def self.parse(stream, parser = nil, hint_type: nil) #:nodoc:
87
+ data = []
88
+ offset = stream.pos
89
+
90
+ if not stream.skip(@@regexp_open)
91
+ raise InvalidArrayObjectError, "No token '#{TOKENS.first}' found"
92
+ end
93
+
94
+ while stream.skip(@@regexp_close).nil? do
95
+ type = Object.typeof(stream)
96
+ raise InvalidArrayObjectError, "Bad embedded object format" if type.nil?
97
+
98
+ value = type.parse(stream, parser)
99
+ data << value
100
+ end
101
+
102
+ array = Array.new(data, parser, hint_type: hint_type)
103
+ array.file_offset = offset
104
+
105
+ array
106
+ end
107
+
108
+ #
109
+ # Converts self into a Ruby array.
110
+ #
111
+ def to_a
112
+ super.map(&:value)
113
+ end
114
+ alias value to_a
115
+
116
+ def to_s #:nodoc:
117
+ content = "#{TOKENS.first} "
118
+ self.each do |entry|
119
+ entry = entry.to_o
120
+
121
+ case entry
122
+ when Dictionary # Do not indent dictionaries inside of arrays.
123
+ content << entry.to_s(indent: 0) << ' '
124
+ else
125
+ content << entry.to_s << ' '
126
+ end
127
+ end
128
+ content << TOKENS.last
129
+
130
+ super(content)
131
+ end
132
+
133
+ def +(other)
134
+ a = Origami::Array.new(self.to_a + other.to_a)
135
+ a.no, a.generation = @no, @generation
136
+
137
+ a
138
+ end
139
+
140
+ def <<(item)
141
+ obj = item.to_o
142
+ obj.parent = self unless obj.indirect?
143
+
144
+ super(obj)
145
+ end
146
+ alias push <<
147
+
148
+ def []=(key,val)
149
+ key, val = key.to_o, val.to_o
150
+ super(key.to_o, val.to_o)
151
+
152
+ val.parent = self unless val.indirect? or val.parent.equal?(self)
153
+
154
+ val
155
+ end
156
+
157
+ def copy
158
+ copy = self.class.new
159
+ self.each do |obj|
160
+ copy << obj.copy
161
+ end
162
+
163
+ copy.parent = @parent
164
+ copy.no, copy.generation = @no, @generation
165
+ copy.set_indirect(true) if self.indirect?
166
+ copy.set_document(@document) if self.indirect?
167
+ copy
168
+ end
169
+
170
+ def cast_to(type, parser = nil)
171
+ super(type)
172
+
173
+ cast = type.new(self, parser)
174
+ cast.parent = self.parent
175
+ cast.no, cast.generation = self.no, self.generation
176
+ if self.indirect?
177
+ cast.set_indirect(true)
178
+ cast.set_document(self.document)
179
+ cast.file_offset = self.file_offset # cast can replace self
180
+ end
181
+
182
+ cast
183
+ end
184
+
185
+ def self.native_type ; Origami::Array end
186
+
187
+ #
188
+ # Parameterized Array class with additional typing information.
189
+ # Example: Array.of(Integer)
190
+ #
191
+ def self.of(klass, *klasses, length: nil)
192
+ Class.new(self) do
193
+ const_set('ARRAY_TYPE', (klasses.empty? and not klass.is_a?(::Array)) ? klass : [ klass ].concat(klasses))
194
+ const_set('STATIC_LENGTH', length)
195
+
196
+ def initialize(data = [], parser = nil)
197
+ super(data, parser, hint_type: self.class.const_get('ARRAY_TYPE'))
198
+ end
199
+
200
+ def pre_build #:nodoc:
201
+ do_type_check if Origami::OPTIONS[:enable_type_checking]
202
+
203
+ super
204
+ end
205
+
206
+ def self.parse(stream, parser = nil)
207
+ super(stream, parser, hint_type: const_get('ARRAY_TYPE'))
208
+ end
209
+
210
+ def do_type_check #:nodoc:
211
+ static_length = self.class.const_get('STATIC_LENGTH')
212
+ array_type = self.class.const_get('ARRAY_TYPE')
213
+
214
+ if static_length and self.length != static_length
215
+ STDERR.puts "Warning: object #{self.class.name} has unexpected length #{self.length} (should be #{static_length})"
216
+ end
217
+
218
+ self.each_with_index do |object, index|
219
+ index_type = array_type.is_a?(::Array) ? array_type[index % array_type.size] : array_type
220
+
221
+ begin
222
+ object_value = object.solve
223
+ rescue InvalidReferenceError
224
+ STDERR.puts "Warning: in object #{self.class}, invalid reference at index #{index}"
225
+ next
226
+ end
227
+
228
+ unless object_value.is_a?(index_type)
229
+ STDERR.puts "Warning: object #{self.class.name || 'Array'} should be composed of #{index_type.name} at index #{index} (got #{object_value.type} instead)"
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ private
237
+
238
+ def cache_value(value)
239
+ case value
240
+ when String then @strings_cache.push(value)
241
+ when Name then @names_cache.push(value)
242
+ when Reference then
243
+ (@xref_cache[value] ||= []).push(self)
244
+ when Dictionary, Array
245
+ @strings_cache.concat(value.strings_cache)
246
+ @names_cache.concat(value.names_cache)
247
+ @xref_cache.update(value.xref_cache) do |_ref, cache1, cache2|
248
+ cache1.concat(cache2)
249
+ end
250
+
251
+ value.strings_cache.clear
252
+ value.names_cache.clear
253
+ value.xref_cache.clear
254
+ end
101
255
  end
102
-
103
- value = type.parse(stream, parser)
104
- data << value
105
- end
106
-
107
- array = Array.new(data)
108
- array.file_offset = offset
109
-
110
- array
111
256
  end
112
-
257
+
113
258
  #
114
- # Converts self into a Ruby array.
259
+ # Class representing a location on a page or a bounding box.
115
260
  #
116
- def to_a
117
- super.map { |item|
118
- item.is_a?(Origami::Object) ? item.value : item
119
- }
120
- end
121
-
122
- def to_s #:nodoc:
123
- content = "#{TOKENS.first} "
124
- self.each { |entry|
125
- content << entry.to_o.to_s + ' '
126
- }
127
- content << TOKENS.last
128
-
129
- super(content)
130
- end
131
-
132
- def +(other)
133
-
134
- a = Origami::Array.new(self.to_a + other.to_a, is_indirect?)
135
- a.no, a.generation = @no, @generation
136
-
137
- return a
138
- end
261
+ class Rectangle < Array.of(Number, length: 4)
139
262
 
140
- def <<(item)
141
- obj = item.to_o
142
- obj.parent = self unless obj.is_indirect?
263
+ def self.[](coords)
264
+ corners =
265
+ if [ :llx, :lly, :urx, :ury ].all? {|p| coords.include?(p)}
266
+ coords.values_at(:llx, :lly, :urx, :ury)
267
+ elsif [ :width, :height ].all? {|p| coords.include?(p)}
268
+ width, height = coords.values_at(:width, :height)
269
+ x = coords.fetch(:x, 0)
270
+ y = coords.fetch(:y, 0)
271
+ [ x, y, x+width, y+height ]
272
+ else
273
+ raise ArgumentError, "Bad arguments for #{self.class}: #{coords.inspect}"
274
+ end
143
275
 
144
- super(obj)
145
- end
146
-
147
- def []=(key,val)
148
- key, val = key.to_o, val.to_o
149
- super(key.to_o,val.to_o)
150
-
151
- val.parent = self unless val.is_indirect? or val.parent.equal?(self)
152
-
153
- val
154
- end
155
-
156
- alias value to_a
157
-
158
- def copy
159
- copy = self.class.new
160
- self.each do |obj|
161
- copy << obj.copy
162
- end
163
-
164
- copy.parent = @parent
165
- copy.no, copy.generation = @no, @generation
166
- copy.set_indirect(true) if is_indirect?
167
- copy.set_pdf(@pdf) if is_indirect?
168
- copy
169
- end
276
+ unless corners.all? { |corner| corner.is_a?(Numeric) }
277
+ raise TypeError, "All coords must be numbers"
278
+ end
170
279
 
171
- def self.native_type ; Origami::Array end
172
-
173
- end
174
-
175
- #
176
- # Class representing a location on a page or a bounding box.
177
- #
178
- class Rectangle < Array
179
-
180
- class << self
181
-
182
- def [](coords)
183
- corners =
184
- if [ :llx, :lly, :urx, :ury ].all? {|p| coords.include?(p)}
185
- coords.values_at(:llx, :lly, :urx, :ury)
186
- elsif [ :width, :height ].all? {|p| coords.include?(p)}
187
- width, height = coords.values_at(:width, :height)
188
- x = coords.values_at(:x).first || 0
189
- y = coords.values_at(:y).first || 0
190
- [ x, y, x+width, y+height ]
191
- else
192
- raise ArgumentError, "Bad arguments for #{self.class}: #{coords.inspect}"
193
- end
194
-
195
- unless corners.all? { |corner| corner.is_a?(Numeric) }
196
- raise TypeError, "All coords must be numbers"
280
+ Rectangle.new(corners)
197
281
  end
198
-
199
- Rectangle.new(*corners)
200
- end
201
-
202
- end
203
-
204
- def initialize(lowerleftx, lowerlefty, upperrightx, upperrighty)
205
- super([ lowerleftx, lowerlefty, upperrightx, upperrighty ])
206
282
  end
207
-
208
- end
209
283
 
210
284
  end