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,527 @@
1
+ =begin
2
+
3
+ = File
4
+ object.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
+ class Bignum #:nodoc:
23
+ def to_o
24
+ Origami::Integer.new(self)
25
+ end
26
+ end
27
+
28
+ class Fixnum #:nodoc:
29
+ def to_o
30
+ Origami::Integer.new(self)
31
+ end
32
+ end
33
+
34
+ class Array #:nodoc:
35
+ def to_o
36
+ Origami::Array.new(self)
37
+ end
38
+
39
+ def shuffle
40
+ sort_by { rand }
41
+ end
42
+
43
+ def shuffle!
44
+ self.replace shuffle
45
+ end
46
+ end
47
+
48
+ class Float #:nodoc:
49
+ def to_o
50
+ Origami::Real.new(self)
51
+ end
52
+ end
53
+
54
+ class Hash #:nodoc:
55
+ def to_o
56
+ Origami::Dictionary.new(self)
57
+ end
58
+ end
59
+
60
+ class TrueClass #:nodoc:
61
+ def to_o
62
+ Origami::Boolean.new(true)
63
+ end
64
+ end
65
+
66
+ class FalseClass #:nodoc:
67
+ def to_o
68
+ Origami::Boolean.new(false)
69
+ end
70
+ end
71
+
72
+ class NilClass #:nodoc:
73
+ def to_o
74
+ Origami::Null.new
75
+ end
76
+ end
77
+
78
+ class Symbol #:nodoc:
79
+ def to_o
80
+ Origami::Name.new(self)
81
+ end
82
+
83
+ def value
84
+ self
85
+ end
86
+
87
+ end
88
+
89
+ class String #:nodoc:
90
+ def to_o
91
+ Origami::ByteString.new(self)
92
+ end
93
+
94
+ def is_binary_data?
95
+ ( self.count( "\x00" ) > 0 ) unless empty?
96
+ end
97
+ end
98
+
99
+ #
100
+ # Module for parsing/generating PDF files.
101
+ #
102
+ module Origami
103
+
104
+ #
105
+ # Mixin' module for objects which can store their options into an inner Dictionary.
106
+ #
107
+ module StandardObject #:nodoc:
108
+
109
+ DEFAULT_ATTRIBUTES = { :Type => Object, :Version => "1.1" } #:nodoc:
110
+
111
+ def self.included(receiver) #:nodoc:
112
+ receiver.instance_variable_set(:@fields, Hash.new(DEFAULT_ATTRIBUTES))
113
+ receiver.extend(ClassMethods)
114
+ end
115
+
116
+ module ClassMethods #:nodoc:all
117
+
118
+ def inherited(subclass)
119
+ subclass.instance_variable_set(:@fields, Marshal.load(Marshal.dump(@fields)))
120
+ end
121
+
122
+ def fields
123
+ @fields
124
+ end
125
+
126
+ def field(name, attributes)
127
+
128
+ if not @fields.has_key?(name)
129
+ @fields[name] = attributes
130
+ else
131
+ @fields[name].merge! attributes
132
+ end
133
+
134
+ define_field_methods(name)
135
+ end
136
+
137
+ def define_field_methods(field)
138
+ reader = lambda { obj = self[field]; obj.is_a?(Reference) ? obj.solve : obj }
139
+ writer = lambda { |value| self[field] = value }
140
+ set = lambda { |value| self[field] = value; self }
141
+
142
+ send(:define_method, field.id2name, reader)
143
+ send(:define_method, field.id2name + "=", writer)
144
+ send(:define_method, "set" + field.id2name, set)
145
+ end
146
+
147
+ #
148
+ # Returns an array of required fields for the current Object.
149
+ #
150
+ def required_fields
151
+ fields = []
152
+ @fields.each_pair { |name, attributes|
153
+ fields << name if attributes[:Required] == true
154
+ }
155
+
156
+ fields
157
+ end
158
+
159
+ end
160
+
161
+ def pre_build #:nodoc:
162
+
163
+ set_default_values
164
+ do_type_check if Origami::OPTIONS[:enable_type_checking] == true
165
+
166
+ super
167
+ end
168
+
169
+ #
170
+ # Check if an attribute is set in the current Object.
171
+ # _attr_:: The attribute name.
172
+ #
173
+ def has_field? (field)
174
+ not self[field].nil?
175
+ end
176
+
177
+ #
178
+ # Returns the version and level required by the current Object.
179
+ #
180
+ def pdf_version_required #:nodoc:
181
+ max = [ 1.0, 0 ]
182
+
183
+ self.each_key do |field|
184
+ attributes = self.class.fields[field.value]
185
+
186
+ current_version = attributes.has_key?(:Version) ? attributes[:Version].to_f : 0
187
+ current_level = attributes[:ExtensionLevel] || 0
188
+ current = [ current_version, current_level ]
189
+
190
+ max = current if (current <=> max) > 0
191
+
192
+ sub = self[field.value].pdf_version_required
193
+ max = sub if (sub <=> max) > 0
194
+ end
195
+
196
+ max
197
+ end
198
+
199
+ def set_default_value(field) #:nodoc:
200
+ if self.class.fields[field][:Default]
201
+ self[field] = self.class.fields[field][:Default]
202
+ self[field].pre_build
203
+ end
204
+ end
205
+
206
+ def set_default_values #:nodoc:
207
+ self.class.required_fields.each do |field|
208
+ set_default_value(field) unless has_field?(field)
209
+ end
210
+ end
211
+
212
+ def do_type_check #:nodoc:
213
+ self.class.fields.each_pair do |field, attributes|
214
+
215
+ if not self[field].nil? and not attributes[:Type].nil?
216
+ types = attributes[:Type].is_a?(::Array) ? attributes[:Type] : [ attributes[:Type] ]
217
+ if not self[field].is_a?(Reference) and types.all? {|type| not self[field].is_a?(type)}
218
+ puts "Warning: in object #{self.class}, field `#{field.to_s}' has unexpected type #{self[field].class}"
219
+ end
220
+ end
221
+ end
222
+ end
223
+
224
+ end
225
+
226
+ class InvalidObjectError < Exception #:nodoc:
227
+ end
228
+
229
+ class UnterminatedObjectError < Exception #:nodoc:
230
+ attr_reader :obj
231
+ def initialize(msg,obj)
232
+ super(msg)
233
+ @obj = obj
234
+ end
235
+ end
236
+
237
+ #
238
+ # Parent module representing a PDF Object.
239
+ # PDF specification declares a set of primitive object types :
240
+ # * Null
241
+ # * Boolean
242
+ # * Integer
243
+ # * Real
244
+ # * Name
245
+ # * String
246
+ # * Array
247
+ # * Dictionary
248
+ # * Stream
249
+ #
250
+ module Object
251
+
252
+ TOKENS = %w{ obj endobj } #:nodoc:
253
+
254
+ @@regexp_obj = Regexp.new(WHITESPACES + "(\\d+)" + WHITESPACES + "(\\d+)" + WHITESPACES + TOKENS.first + WHITESPACES)
255
+ @@regexp_endobj = Regexp.new(WHITESPACES + TOKENS.last + WHITESPACES)
256
+
257
+ attr_accessor :no, :generation, :file_offset, :objstm_offset
258
+ attr_accessor :parent
259
+
260
+ #
261
+ # Creates a new PDF Object.
262
+ #
263
+ def initialize(*cons)
264
+ @indirect = false
265
+ @no, @generation = 0, 0
266
+
267
+ super(*cons) unless cons.empty?
268
+ end
269
+
270
+ #
271
+ # Sets whether the object is indirect or not.
272
+ # Indirect objects are allocated numbers at build time.
273
+ #
274
+ def set_indirect(dir)
275
+ unless dir == true or dir == false
276
+ raise TypeError, "The argument must be boolean"
277
+ end
278
+
279
+ @indirect = dir
280
+ self
281
+ end
282
+
283
+ #
284
+ # Generic method called just before the object is finalized.
285
+ # At this time, no number nor generation allocation has yet been done.
286
+ #
287
+ def pre_build
288
+ self
289
+ end
290
+
291
+ #
292
+ # Generic method called just after the object is finalized.
293
+ # At this time, any indirect object has its own number and generation identifier.
294
+ #
295
+ def post_build
296
+ self
297
+ end
298
+
299
+ #
300
+ # Compare two objects from their respective numbers.
301
+ #
302
+ def <=>(obj)
303
+ [@no, @generation] <=> [obj.no, obj.generation]
304
+ end
305
+
306
+ #
307
+ # Returns whether the objects is indirect, which means that it is not embedded into another object.
308
+ #
309
+ def is_indirect?
310
+ @indirect
311
+ end
312
+
313
+ #
314
+ # Deep copy of an object.
315
+ #
316
+ def copy
317
+ Marshal.load(Marshal.dump(self))
318
+ end
319
+
320
+ #
321
+ # Returns an indirect reference to this object, or a Null object is this object is not indirect.
322
+ #
323
+ def reference
324
+ unless self.is_indirect?
325
+ raise InvalidObjectError, "Cannot reference a direct object"
326
+ end
327
+
328
+ ref = Reference.new(@no, @generation)
329
+ ref.parent = self
330
+
331
+ ref
332
+ end
333
+
334
+ #
335
+ # Returns an array of references pointing to the current object.
336
+ #
337
+ def xrefs
338
+ unless self.is_indirect?
339
+ raise InvalidObjectError, "Cannot find xrefs to a direct object"
340
+ end
341
+
342
+ if self.pdf.nil?
343
+ raise InvalidObjectError, "Not attached to any PDF"
344
+ end
345
+
346
+ xref_cache = Hash.new([])
347
+ @pdf.root_objects.each do |obj|
348
+ case obj
349
+ when Dictionary,Array then
350
+ xref_cache.update(obj.xref_cache) do |ref, cache1, cache2|
351
+ cache1.concat(cache2)
352
+ end
353
+
354
+ when Stream then
355
+ obj.dictionary.xref_cache.each do |ref, cache|
356
+ cache.map!{obj}
357
+ end
358
+
359
+ xref_cache.update(obj.dictionary.xref_cache) do |ref, cache1, cache2|
360
+ cache1.concat(cache2)
361
+ end
362
+ end
363
+ end
364
+
365
+ xref_cache[self.reference]
366
+ end
367
+
368
+ #
369
+ # Returns the indirect object which contains this object.
370
+ # If the current object is already indirect, returns self.
371
+ #
372
+ def indirect_parent
373
+ obj = self
374
+ obj = obj.parent until obj.is_indirect?
375
+
376
+ obj
377
+ end
378
+
379
+ #
380
+ # Returns self.
381
+ #
382
+ def to_o
383
+ self
384
+ end
385
+
386
+ #
387
+ # Returns self.
388
+ #
389
+ def solve
390
+ self
391
+ end
392
+
393
+ #
394
+ # Returns the size of this object once converted to PDF code.
395
+ #
396
+ def size
397
+ to_s.size
398
+ end
399
+
400
+ #
401
+ # Returns the PDF which the object belongs to.
402
+ #
403
+ def pdf
404
+ if self.is_indirect? then @pdf
405
+ else
406
+ @parent.pdf
407
+ end
408
+ end
409
+
410
+ def set_pdf(pdf)
411
+ if self.is_indirect? then @pdf = pdf
412
+ else
413
+ raise InvalidObjectError, "You cannot set the PDF parent of a direct object"
414
+ end
415
+ end
416
+
417
+ class << self
418
+
419
+ def typeof(stream, noref = false) #:nodoc:
420
+ stream.skip(REGEXP_WHITESPACES)
421
+
422
+ case stream.peek(1)
423
+ when '/' then return Name
424
+ when '<'
425
+ return (stream.peek(2) == '<<') ? Stream : HexaString
426
+ when '(' then return ByteString
427
+ when '[' then return Origami::Array
428
+ when 'n' then
429
+ return Null if stream.peek(4) == 'null'
430
+ when 't' then
431
+ return Boolean if stream.peek(4) == 'true'
432
+ when 'f' then
433
+ return Boolean if stream.peek(5) == 'false'
434
+ else
435
+ if not noref and stream.check(Reference::REGEXP_TOKEN) then return Reference
436
+ elsif stream.check(Real::REGEXP_TOKEN) then return Real
437
+ elsif stream.check(Integer::REGEXP_TOKEN) then return Integer
438
+ else
439
+ nil
440
+ end
441
+ end
442
+
443
+ nil
444
+ end
445
+
446
+ def parse(stream) #:nodoc:
447
+ offset = stream.pos
448
+
449
+ #
450
+ # End of body ?
451
+ #
452
+ return nil if stream.match?(/xref/) or stream.match?(/trailer/) or stream.match?(/startxref/)
453
+
454
+ if stream.scan(@@regexp_obj).nil?
455
+ raise InvalidObjectError,
456
+ "Object shall begin with '%d %d obj' statement"
457
+ end
458
+
459
+ no = stream[2].to_i
460
+ gen = stream[4].to_i
461
+
462
+ type = typeof(stream)
463
+ if type.nil?
464
+ raise InvalidObjectError,
465
+ "Cannot determine object (no:#{no},gen:#{gen}) type"
466
+ end
467
+
468
+ begin
469
+ newObj = type.parse(stream)
470
+ rescue Exception => e
471
+ raise InvalidObjectError,
472
+ "Failed to parse object (no:#{no},gen:#{gen})\n\t -> [#{e.class}] #{e.message}"
473
+ end
474
+
475
+ newObj.set_indirect(true)
476
+ newObj.no = no
477
+ newObj.generation = gen
478
+ newObj.file_offset = offset
479
+
480
+ if stream.skip(@@regexp_endobj).nil?
481
+ raise UnterminatedObjectError.new("Object shall end with 'endobj' statement", newObj)
482
+ end
483
+
484
+ newObj
485
+ end
486
+
487
+ def skip_until_next_obj(stream) #:nodoc:
488
+ stream.pos += 1 until stream.match?(@@regexp_obj) or
489
+ stream.match?(/xref/) or stream.match?(/trailer/) or stream.match?(/startxref/) or
490
+ stream.eos?
491
+
492
+ not stream.eos?
493
+ end
494
+
495
+ end
496
+
497
+ def pdf_version_required #:nodoc:
498
+ [ 1.0, 0 ]
499
+ end
500
+
501
+ #
502
+ # Returns the symbol type of this Object.
503
+ #
504
+ def type
505
+ self.class.to_s.split("::").last.to_sym
506
+ end
507
+ alias real_type type
508
+
509
+ #
510
+ # Outputs this object into PDF code.
511
+ # _data_:: The object data.
512
+ #
513
+ def to_s(data)
514
+
515
+ content = ""
516
+ content << "#{no} #{generation} obj" << EOL if self.is_indirect?
517
+ content << data
518
+ content << EOL << "endobj" << EOL if self.is_indirect?
519
+
520
+ content
521
+ end
522
+
523
+ alias output to_s
524
+
525
+ end
526
+
527
+ end