origami 2.0.0 → 2.0.1

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/bin/gui/config.rb +2 -1
  4. data/bin/gui/file.rb +118 -240
  5. data/bin/gui/gtkhex.rb +5 -5
  6. data/bin/gui/hexview.rb +20 -16
  7. data/bin/gui/imgview.rb +1 -1
  8. data/bin/gui/menu.rb +138 -158
  9. data/bin/gui/properties.rb +46 -48
  10. data/bin/gui/signing.rb +183 -214
  11. data/bin/gui/textview.rb +1 -1
  12. data/bin/gui/treeview.rb +13 -7
  13. data/bin/gui/walker.rb +102 -71
  14. data/bin/gui/xrefs.rb +1 -1
  15. data/bin/pdf2ruby +3 -3
  16. data/bin/pdfcop +18 -11
  17. data/bin/pdfextract +14 -5
  18. data/bin/pdfmetadata +3 -3
  19. data/bin/shell/console.rb +8 -8
  20. data/bin/shell/hexdump.rb +4 -4
  21. data/examples/attachments/nested_document.rb +1 -1
  22. data/examples/javascript/hello_world.rb +3 -3
  23. data/lib/origami.rb +0 -1
  24. data/lib/origami/acroform.rb +3 -3
  25. data/lib/origami/array.rb +1 -3
  26. data/lib/origami/boolean.rb +1 -3
  27. data/lib/origami/catalog.rb +3 -9
  28. data/lib/origami/destinations.rb +2 -2
  29. data/lib/origami/dictionary.rb +15 -29
  30. data/lib/origami/encryption.rb +334 -692
  31. data/lib/origami/extensions/fdf.rb +3 -2
  32. data/lib/origami/extensions/ppklite.rb +5 -9
  33. data/lib/origami/filespec.rb +2 -2
  34. data/lib/origami/filters.rb +54 -36
  35. data/lib/origami/filters/ascii.rb +67 -49
  36. data/lib/origami/filters/ccitt.rb +4 -236
  37. data/lib/origami/filters/ccitt/tables.rb +267 -0
  38. data/lib/origami/filters/crypt.rb +1 -1
  39. data/lib/origami/filters/dct.rb +0 -1
  40. data/lib/origami/filters/flate.rb +3 -43
  41. data/lib/origami/filters/lzw.rb +62 -99
  42. data/lib/origami/filters/predictors.rb +135 -105
  43. data/lib/origami/filters/runlength.rb +34 -22
  44. data/lib/origami/graphics.rb +2 -2
  45. data/lib/origami/graphics/colors.rb +89 -63
  46. data/lib/origami/graphics/path.rb +14 -14
  47. data/lib/origami/graphics/patterns.rb +31 -33
  48. data/lib/origami/graphics/render.rb +0 -1
  49. data/lib/origami/graphics/state.rb +9 -9
  50. data/lib/origami/graphics/text.rb +17 -17
  51. data/lib/origami/graphics/xobject.rb +102 -92
  52. data/lib/origami/javascript.rb +91 -68
  53. data/lib/origami/linearization.rb +22 -20
  54. data/lib/origami/metadata.rb +1 -1
  55. data/lib/origami/name.rb +1 -3
  56. data/lib/origami/null.rb +1 -3
  57. data/lib/origami/numeric.rb +3 -13
  58. data/lib/origami/object.rb +100 -72
  59. data/lib/origami/page.rb +24 -28
  60. data/lib/origami/parser.rb +34 -51
  61. data/lib/origami/parsers/fdf.rb +2 -2
  62. data/lib/origami/parsers/pdf.rb +41 -18
  63. data/lib/origami/parsers/pdf/lazy.rb +83 -46
  64. data/lib/origami/parsers/pdf/linear.rb +19 -10
  65. data/lib/origami/parsers/ppklite.rb +1 -1
  66. data/lib/origami/pdf.rb +150 -206
  67. data/lib/origami/reference.rb +4 -6
  68. data/lib/origami/signature.rb +76 -48
  69. data/lib/origami/stream.rb +69 -63
  70. data/lib/origami/string.rb +2 -19
  71. data/lib/origami/trailer.rb +25 -22
  72. data/lib/origami/version.rb +1 -1
  73. data/lib/origami/xfa.rb +6 -4
  74. data/lib/origami/xreftable.rb +29 -29
  75. data/test/test_annotations.rb +16 -38
  76. data/test/test_pdf_attachment.rb +1 -1
  77. data/test/test_pdf_parse.rb +1 -1
  78. data/test/test_xrefs.rb +2 -2
  79. metadata +4 -4
  80. data/lib/origami/export.rb +0 -247
@@ -23,16 +23,6 @@ module Origami
23
23
  begin
24
24
  require 'v8'
25
25
 
26
- class V8::Object
27
- #def inspect
28
- # case self
29
- # when V8::Array,V8::Function then super
30
- # else
31
- # "{#{self.to_a.map{|k,v| "#{k}:#{v.inspect}"}.join(', ')}}"
32
- # end
33
- #end
34
- end
35
-
36
26
  class PDF
37
27
 
38
28
  module JavaScript
@@ -105,37 +95,43 @@ module Origami
105
95
 
106
96
  def self.check_method_args(args, def_args)
107
97
  if args.first.is_a?(V8::Object)
108
- args = args.first
109
- members = args.entries.map{|k,v| k}
110
- argv = []
111
- def_args.each do |def_arg|
112
- raise MissingArgError if def_arg.required and not members.include?(def_arg.name)
113
-
114
- if members.include?(def_arg.name)
115
- arg = args[def_arg.name]
116
- raise TypeError if def_arg.type and not arg.is_a?(def_arg.type)
117
- else
118
- arg = def_arg.default
119
- end
120
-
121
- argv.push(arg)
122
- end
123
-
124
- args = argv
98
+ check_method_named_args(args.first, def_args)
125
99
  else
126
- i = 0
127
- def_args.each do |def_arg|
128
- raise MissingArgError if def_arg.required and i >= args.length
129
- raise TypeError if def_arg.type and not args[i].is_a?(def_arg.type)
100
+ check_method_ordered_args(args, def_args)
101
+ end
102
+ end
130
103
 
131
- args.push(def_arg.default) if i >= args.length
104
+ def self.check_method_named_args(object, def_args)
105
+ members = object.entries.map {|k, _| k}
106
+ argv = []
107
+ def_args.each do |def_arg|
108
+ raise MissingArgError if def_arg.required and not members.include?(def_arg.name)
132
109
 
133
- i = i + 1
110
+ if members.include?(def_arg.name)
111
+ arg = object[def_arg.name]
112
+ raise TypeError if def_arg.type and not arg.is_a?(def_arg.type)
113
+ else
114
+ arg = def_arg.default
134
115
  end
116
+
117
+ argv.push(arg)
118
+ end
119
+
120
+ argv
121
+ end
122
+ private_class_method :check_method_named_args
123
+
124
+ def self.check_method_ordered_args(args, def_args)
125
+ def_args.each_with_index do |def_arg, index|
126
+ raise MissingArgError if def_arg.required and index >= args.length
127
+ raise TypeError if def_arg.type and not args[index].is_a?(def_arg.type)
128
+
129
+ args.push(def_arg.default) if index >= args.length
135
130
  end
136
131
 
137
132
  args
138
133
  end
134
+ private_class_method :check_method_ordered_args
139
135
 
140
136
  def self.acro_method(name, *def_args, &b)
141
137
  define_method(name) do |*args|
@@ -158,7 +154,9 @@ module Origami
158
154
  )
159
155
  end
160
156
 
161
- raise NotAllowedError
157
+ unless @engine.privileged?
158
+ raise NotAllowedError, "Security settings prevent access to this property or method."
159
+ end
162
160
 
163
161
  args = AcrobatObject.check_method_args(args, def_args)
164
162
  self.instance_exec(*args, &b) if b
@@ -230,12 +228,12 @@ module Origami
230
228
 
231
229
  acro_method 'setPersistent',
232
230
  Arg[name: 'cVariable', required: true],
233
- Arg[name: 'bPersist', required: true] do |cVariable, bPersist|
231
+ Arg[name: 'bPersist', required: true] do |cVariable, _bPersist|
234
232
 
235
233
  raise GeneralError unless @vars.include?(cVariable)
236
234
  end
237
235
 
238
- acro_method 'subscribe',
236
+ acro_method 'subscribe',
239
237
  Arg[name: 'cVariable', required: true],
240
238
  Arg[name: 'fCallback', type: V8::Function, require: true] do |cVariable, fCallback|
241
239
 
@@ -344,7 +342,7 @@ module Origami
344
342
 
345
343
  acro_method 'getDataObjectContents',
346
344
  Arg[name: 'cName', type: ::String, required: true],
347
- Arg[name: 'bAllowAuth', default: false] do |cName, bAllowAuth|
345
+ Arg[name: 'bAllowAuth', default: false] do |cName, _bAllowAuth|
348
346
 
349
347
  file_desc = @pdf.resolve_name(Names::EMBEDDED_FILES, cName)
350
348
 
@@ -359,7 +357,7 @@ module Origami
359
357
  Arg[name: 'cName', type: ::String, required: true],
360
358
  Arg[name: 'cDIPath' ],
361
359
  Arg[name: 'bAllowAuth'],
362
- Arg[name: 'nLaunch'] do |cName, cDIPath, bAllowAuth, nLaunch|
360
+ Arg[name: 'nLaunch'] do |cName, _cDIPath, _bAllowAuth, _nLaunch|
363
361
 
364
362
  file_desc = @pdf.resolve_name(Names::EMBEDDED_FILES, cName)
365
363
 
@@ -454,7 +452,7 @@ module Origami
454
452
  class Console < AcrobatObject
455
453
  def println(*args)
456
454
  raise MissingArgError unless args.length > 0
457
-
455
+
458
456
  @engine.options[:console].puts(args.first.to_s)
459
457
  end
460
458
 
@@ -466,14 +464,14 @@ module Origami
466
464
  class Util < AcrobatObject
467
465
  acro_method 'streamFromString',
468
466
  Arg[name: 'cString', type: ::Object, required: true],
469
- Arg[name: 'cCharset', type: ::Object, default: 'utf-8'] do |cString, cCharset|
467
+ Arg[name: 'cCharset', type: ::Object, default: 'utf-8'] do |cString, _cCharset|
470
468
 
471
469
  ReadStream.new(@engine, cString.to_s)
472
470
  end
473
471
 
474
472
  acro_method 'stringFromStream',
475
473
  Arg[name: 'oStream', type: ReadStream, required: true],
476
- Arg[name: 'cCharset', type: ::Object, default: 'utf-8'] do |oStream, cCharset|
474
+ Arg[name: 'cCharset', type: ::Object, default: 'utf-8'] do |oStream, _cCharset|
477
475
 
478
476
  oStream.instance_variable_get(:@data).dup
479
477
  end
@@ -500,32 +498,46 @@ module Origami
500
498
  end
501
499
 
502
500
  def type
503
- (if @field.has_key?(:FT)
504
- case @field.FT.value
505
- when PDF::Field::Type::BUTTON
506
- if @fields.has_key?(:Ff)
507
- flags = @field.Ff.value
508
-
509
- if (flags & Origami::Annotation::Widget::Button::Flags::PUSHBUTTON) != 0
510
- 'button'
511
- elsif (flags & Origami::Annotation::Widget::Button::Flags::RADIO) != 0
512
- 'radiobox'
513
- else
514
- 'checkbox'
515
- end
516
- end
517
- when PDF::Field::Type::TEXT then 'text'
518
- when PDF::Field::Type::SIGNATURE then 'signature'
519
- when PDF::Field::Type::CHOICE
520
- if @field.has_key?(:Ff)
521
- if (@field.Ff.value & Origami::Annotation::Widget::Choice::Flags::COMBO).zero?
522
- 'listbox'
523
- else
524
- 'combobox'
525
- end
526
- end
527
- end
528
- end).to_s
501
+ return '' unless @field.key?(:FT)
502
+
503
+ type_name =
504
+ case @field.FT.value
505
+ when PDF::Field::Type::BUTTON
506
+ button_type
507
+
508
+ when PDF::Field::Type::TEXT then 'text'
509
+ when PDF::Field::Type::SIGNATURE then 'signature'
510
+ when PDF::Field::Type::CHOICE
511
+ choice_type
512
+ end
513
+
514
+ type_name.to_s
515
+ end
516
+
517
+ private
518
+
519
+ def button_type
520
+ return if @field.key?(:Ff) and not @field.Ff.is_a?(Integer)
521
+
522
+ flags = @field.Ff.to_i
523
+
524
+ if (flags & Annotation::Widget::Button::Flags::PUSHBUTTON) != 0
525
+ 'button'
526
+ elsif (flags & Annotation::Widget::Button::Flags::RADIO) != 0
527
+ 'radiobox'
528
+ else
529
+ 'checkbox'
530
+ end
531
+ end
532
+
533
+ def choice_type
534
+ return if @field.key?(:Ff) and not @field.Ff.is_a?(Integer)
535
+
536
+ if (@field.Ff.to_i & Annotation::Widget::Choice::Flags::COMBO) != 0
537
+ 'combobox'
538
+ else
539
+ 'listbox'
540
+ end
529
541
  end
530
542
  end
531
543
 
@@ -553,6 +565,7 @@ module Origami
553
565
  attr_reader :doc
554
566
  attr_reader :context
555
567
  attr_reader :options
568
+ attr_reader :privileged_mode
556
569
  attr_reader :parseInt
557
570
 
558
571
  def initialize(pdf)
@@ -563,16 +576,26 @@ module Origami
563
576
  viewerVariation: JavaScript::Viewers::ADOBE_READER,
564
577
  platform: JavaScript::Platforms::WINDOWS,
565
578
  console: STDOUT,
566
- log_method_calls: false
579
+ log_method_calls: false,
580
+ privileged_mode: false
567
581
  }
568
582
 
569
583
  @doc = JavaScript::Doc.new(self, pdf)
570
584
  @context = V8::Context.new(with: @doc)
585
+ @privileged_mode = @options[:privileged_mode]
571
586
 
572
587
  @parseInt = V8::Context.new['parseInt']
573
588
  @hooks = {}
574
589
  end
575
590
 
591
+ #
592
+ # Returns true if the engine is set to execute in privileged mode.
593
+ # Allows execution of security protected methods.
594
+ #
595
+ def privileged?
596
+ @privileged_mode
597
+ end
598
+
576
599
  #
577
600
  # Evaluates a JavaScript code in the current context.
578
601
  #
@@ -50,29 +50,17 @@ module Origami
50
50
  #
51
51
  prev_trailer = @revisions.first.trailer
52
52
 
53
- lin_dict = @revisions.first.objects.min_by{|obj| obj.file_offset}
54
- hints = lin_dict[:H]
53
+ linear_dict = @revisions.first.objects.min_by{|obj| obj.file_offset}
55
54
 
56
55
  #
57
56
  # Removes hint streams used by linearization.
58
57
  #
59
- if hints.is_a?(::Array)
60
- if hints.length > 0 and hints[0].is_a?(Integer)
61
- hint_stream = get_object_by_offset(hints[0])
62
- delete_object(hint_stream.reference) if hint_stream.is_a?(Stream)
63
- end
64
-
65
- if hints.length > 2 and hints[2].is_a?(Integer)
66
- overflow_stream = get_object_by_offset(hints[2])
67
- delete_object(overflow_stream.reference) if overflow_stream.is_a?(Stream)
68
- end
69
- end
58
+ delete_hint_streams(linear_dict)
70
59
 
71
60
  #
72
61
  # Update the trailer.
73
62
  #
74
63
  last_trailer = (@revisions.last.trailer ||= Trailer.new)
75
-
76
64
  last_trailer.dictionary ||= Dictionary.new
77
65
 
78
66
  if prev_trailer.has_dictionary?
@@ -98,13 +86,30 @@ module Origami
98
86
  #
99
87
  # Remove the linearization revision.
100
88
  #
101
- @revisions.first.body.delete(lin_dict.reference)
89
+ @revisions.first.body.delete(linear_dict.reference)
102
90
  @revisions.last.body.merge! @revisions.first.body
103
91
 
104
92
  remove_revision(0)
105
93
 
106
94
  self
107
95
  end
96
+
97
+ private
98
+
99
+ #
100
+ # Strip the document from Hint streams given a linearization dictionary.
101
+ #
102
+ def delete_hint_streams(linearization_dict)
103
+ hints = linearization_dict[:H]
104
+ return unless hints.is_a?(Array)
105
+
106
+ hints.each_slice(2) do |offset, _length|
107
+ next unless offset.is_a?(Integer)
108
+
109
+ stream = get_object_by_offset(offset)
110
+ delete_object(stream.reference) if stream.is_a?(Stream)
111
+ end
112
+ end
108
113
  end
109
114
 
110
115
  #
@@ -192,10 +197,9 @@ module Origami
192
197
  data << [ item_data ].pack("B*")
193
198
  end
194
199
 
195
- i = 0
196
200
  nitems = self.class.nb_entry_items
197
- @entries.each do |entry|
198
- for no in (1..items)
201
+ @entries.each_with_index do |entry, i|
202
+ for no in (1..nitems)
199
203
  unless entry.include?(no)
200
204
  raise InvalidHintTableError, "Missing item #{no} in entry #{i} of #{self.class}"
201
205
  end
@@ -209,8 +213,6 @@ module Origami
209
213
 
210
214
  data << [ item_data ].pack("B*")
211
215
  end
212
-
213
- i = i + 1
214
216
  end
215
217
 
216
218
  data
@@ -87,7 +87,7 @@ module Origami
87
87
  #
88
88
  def create_metadata(info = {})
89
89
  skeleton = <<-XMP
90
- <?packet begin="#{"\xef\xbb\xbf"}" id="W5M0MpCehiHzreSzNTczkc9d"?>
90
+ <?packet begin="\xef\xbb\xbf" id="W5M0MpCehiHzreSzNTczkc9d"?>
91
91
  <x:xmpmeta xmlns:x="adobe:ns:meta/">
92
92
  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
93
93
  <rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
@@ -76,7 +76,7 @@ module Origami
76
76
  super(TOKENS.first + Name.expand(@value))
77
77
  end
78
78
 
79
- def self.parse(stream, parser = nil) #:nodoc:
79
+ def self.parse(stream, _parser = nil) #:nodoc:
80
80
  offset = stream.pos
81
81
 
82
82
  name =
@@ -127,8 +127,6 @@ module Origami
127
127
  "#" + c.ord.to_s(16).rjust(2,"0")
128
128
  end
129
129
  end
130
-
131
- def self.native_type ; Name end
132
130
  end
133
131
 
134
132
  end
@@ -36,7 +36,7 @@ module Origami
36
36
  super
37
37
  end
38
38
 
39
- def self.parse(stream, parser = nil) #:nodoc:
39
+ def self.parse(stream, _parser = nil) #:nodoc:
40
40
  offset = stream.pos
41
41
 
42
42
  if stream.skip(@@regexp).nil?
@@ -59,8 +59,6 @@ module Origami
59
59
  def to_s #:nodoc:
60
60
  super(TOKENS.first)
61
61
  end
62
-
63
- def self.native_type ; Null end
64
62
  end
65
63
 
66
64
  end
@@ -86,22 +86,12 @@ module Origami
86
86
  def to_s
87
87
  super(value.to_s)
88
88
  end
89
-
90
- module ClassMethods #:nodoc:all
91
- def native_type; Number end
92
- end
93
-
94
- def self.included(receiver) #:nodoc:
95
- receiver.extend(ClassMethods)
96
- end
97
-
98
- def self.native_type; Number end #:nodoc:
99
89
  end
100
90
 
101
91
  #
102
92
  # Class representing an Integer Object.
103
93
  #
104
- class Integer < DelegateClass(Bignum)
94
+ class Integer < DelegateClass(::Integer)
105
95
  include Number
106
96
 
107
97
  TOKENS = [ "(\\+|-)?[\\d]+[^.]?" ] #:nodoc:
@@ -121,7 +111,7 @@ module Origami
121
111
  super(i)
122
112
  end
123
113
 
124
- def self.parse(stream, parser = nil) #:nodoc:
114
+ def self.parse(stream, _parser = nil) #:nodoc:
125
115
  offset = stream.pos
126
116
 
127
117
  if not stream.scan(@@regexp)
@@ -165,7 +155,7 @@ module Origami
165
155
  super(f)
166
156
  end
167
157
 
168
- def self.parse(stream, parser = nil) #:nodoc:
158
+ def self.parse(stream, _parser = nil) #:nodoc:
169
159
  offset = stream.pos
170
160
 
171
161
  if not stream.scan(@@regexp)
@@ -25,13 +25,7 @@ module Origami
25
25
 
26
26
  module TypeConversion
27
27
 
28
- refine ::Bignum do
29
- def to_o
30
- Origami::Integer.new(self)
31
- end
32
- end
33
-
34
- refine ::Fixnum do
28
+ refine ::Integer do
35
29
  def to_o
36
30
  Origami::Integer.new(self)
37
31
  end
@@ -92,6 +86,30 @@ module Origami
92
86
  class Error < StandardError
93
87
  end
94
88
 
89
+ #
90
+ # Provides an easier syntax for field access.
91
+ # The object must have the defined the methods #[] and #[]=.
92
+ #
93
+ # Once included, object.Field will automatically resolve to object[:Field].
94
+ # References are automatically followed.
95
+ #
96
+ module FieldAccessor
97
+ def method_missing(field, *args)
98
+ raise NoMethodError, "No method `#{field}' for #{self.class}" unless field =~ /^[[:upper:]]/
99
+
100
+ if field[-1] == '='
101
+ self[field[0..-2].to_sym] = args.first
102
+ else
103
+ object = self[field]
104
+ object.is_a?(Reference) ? object.solve : object
105
+ end
106
+ end
107
+
108
+ def respond_to_missing?(field, *)
109
+ not (field =~ /^[[:upper:]]/).nil? or super
110
+ end
111
+ end
112
+
95
113
  #
96
114
  # Mixin' module for objects which can store their options into an inner Dictionary.
97
115
  #
@@ -114,9 +132,12 @@ module Origami
114
132
  @fields
115
133
  end
116
134
 
135
+ #
136
+ # Define a new field with given attributes.
137
+ #
117
138
  def field(name, attributes)
118
139
  if attributes[:Required] == true and attributes.has_key?(:Default) and attributes[:Type] == Name
119
- self.add_type_info(self, name, attributes[:Default])
140
+ self.add_type_signature(name, attributes[:Default])
120
141
  end
121
142
 
122
143
  if @fields.has_key?(name)
@@ -128,7 +149,30 @@ module Origami
128
149
  define_field_methods(name)
129
150
  end
130
151
 
131
- def define_field_methods(field)
152
+ #
153
+ # Returns an array of required fields for the current Object.
154
+ #
155
+ def required_fields
156
+ fields = []
157
+ @fields.each_pair do |name, attributes|
158
+ fields << name if attributes[:Required] == true
159
+ end
160
+
161
+ fields
162
+ end
163
+
164
+ #
165
+ # Returns the expected type for a field name.
166
+ #
167
+ def hint_type(name)
168
+ if @fields.has_key?(name)
169
+ @fields[name][:Type]
170
+ end
171
+ end
172
+
173
+ private
174
+
175
+ def define_field_methods(field) #:nodoc:
132
176
 
133
177
  #
134
178
  # Getter method.
@@ -157,24 +201,6 @@ module Origami
157
201
  self
158
202
  end
159
203
  end
160
-
161
- #
162
- # Returns an array of required fields for the current Object.
163
- #
164
- def required_fields
165
- fields = []
166
- @fields.each_pair do |name, attributes|
167
- fields << name if attributes[:Required] == true
168
- end
169
-
170
- fields
171
- end
172
-
173
- def hint_type(name)
174
- if @fields.has_key?(name)
175
- @fields[name][:Type]
176
- end
177
- end
178
204
  end
179
205
 
180
206
  def pre_build #:nodoc:
@@ -184,14 +210,6 @@ module Origami
184
210
  super
185
211
  end
186
212
 
187
- #
188
- # Check if an attribute is set in the current Object.
189
- # _attr_:: The attribute name.
190
- #
191
- def has_field? (field)
192
- not self[field].nil?
193
- end
194
-
195
213
  #
196
214
  # Returns the version and level required by the current Object.
197
215
  #
@@ -218,6 +236,8 @@ module Origami
218
236
  max
219
237
  end
220
238
 
239
+ private
240
+
221
241
  def set_default_value(field) #:nodoc:
222
242
  if self.class.fields[field][:Default]
223
243
  self[field] = self.class.fields[field][:Default]
@@ -227,7 +247,7 @@ module Origami
227
247
 
228
248
  def set_default_values #:nodoc:
229
249
  self.class.required_fields.each do |field|
230
- set_default_value(field) unless has_field?(field)
250
+ set_default_value(field) unless self.key?(field)
231
251
  end
232
252
  end
233
253
 
@@ -238,18 +258,18 @@ module Origami
238
258
  begin
239
259
  field_value = self[field].solve
240
260
  rescue InvalidReferenceError
241
- STDERR.puts "Warning: in object #{self.class}, field `#{field.to_s}' is an invalid reference (#{self[field].to_s})"
261
+ STDERR.puts "Warning: in object #{self.class}, field `#{field}' is an invalid reference (#{self[field]})"
242
262
  next
243
263
  end
244
264
 
245
265
  types = attributes[:Type].is_a?(::Array) ? attributes[:Type] : [ attributes[:Type] ]
246
266
 
247
267
  unless types.any? {|type| not type.is_a?(Class) or field_value.is_a?(type.native_type)}
248
- STDERR.puts "Warning: in object #{self.class}, field `#{field.to_s}' has unexpected type #{field_value.class}"
268
+ STDERR.puts "Warning: in object #{self.class}, field `#{field}' has unexpected type #{field_value.class}"
249
269
  end
250
270
 
251
271
  if attributes.key?(:Assert) and not (attributes[:Assert] === field_value)
252
- STDERR.puts "Warning: assertion failed for field `#{field.to_s}' in object #{self.class}"
272
+ STDERR.puts "Warning: assertion failed for field `#{field}' in object #{self.class}"
253
273
  end
254
274
  end
255
275
  end
@@ -267,7 +287,7 @@ module Origami
267
287
  end
268
288
  end
269
289
 
270
- WHITESPACES = "([ \\f\\t\\r\\n\\0]|%[^\\n]*\\n)*" #:nodoc:
290
+ WHITESPACES = "([ \\f\\t\\r\\n\\0]|%[^\\n\\r]*(\\r\\n|\\r|\\n))*" #:nodoc:
271
291
  WHITECHARS_NORET = "[ \\f\\t\\0]*" #:nodoc:
272
292
  EOL = "\r\n" #:nodoc:
273
293
  WHITECHARS = "[ \\f\\t\\r\\n\\0]*" #:nodoc:
@@ -296,6 +316,36 @@ module Origami
296
316
  attr_accessor :no, :generation, :file_offset, :objstm_offset
297
317
  attr_accessor :parent
298
318
 
319
+ #
320
+ # Modules or classes including this module are considered native types.
321
+ #
322
+ def self.included(base)
323
+ base.class_variable_set(:@@native_type, base)
324
+ base.extend(ClassMethods)
325
+ end
326
+
327
+ module ClassMethods
328
+ # Returns the native type of the derived class or module.
329
+ def native_type
330
+ self.class_variable_get(:@@native_type)
331
+ end
332
+
333
+ private
334
+
335
+ # Propagate native type to submodules.
336
+ def included(klass)
337
+ klass.class_variable_set(:@@native_type, self)
338
+ klass.extend(ClassMethods)
339
+ end
340
+ end
341
+
342
+ #
343
+ # Returns the native type of the Object.
344
+ #
345
+ def native_type
346
+ self.class.native_type
347
+ end
348
+
299
349
  #
300
350
  # Creates a new PDF Object.
301
351
  #
@@ -395,31 +445,18 @@ module Origami
395
445
  #
396
446
  def xrefs
397
447
  raise InvalidObjectError, "Cannot find xrefs to a direct object" unless self.indirect?
448
+ raise InvalidObjectError, "Not attached to any document" if self.document.nil?
398
449
 
399
- if self.document.nil?
400
- raise InvalidObjectError, "Not attached to any document"
401
- end
402
-
403
- refs = []
404
- @document.root_objects.each do |obj|
405
- if obj.is_a?(ObjectStream)
406
- obj.each do |child|
407
- case child
450
+ @document.each_object(compressed: true)
451
+ .flat_map { |object|
452
+ case object
453
+ when Stream
454
+ object.dictionary.xref_cache[self.reference]
408
455
  when Dictionary, Array
409
- refs.concat child.xref_cache[self.reference] if child.xref_cache.key?(self.reference)
456
+ object.xref_cache[self.reference]
410
457
  end
411
- end
412
- end
413
-
414
- obj = obj.dictionary if obj.is_a?(Stream)
415
-
416
- case obj
417
- when Dictionary, Array
418
- refs.concat obj.xref_cache[self.reference] if obj.xref_cache.key?(self.reference)
419
- end
420
- end
421
-
422
- refs
458
+ }
459
+ .compact!
423
460
  end
424
461
 
425
462
  #
@@ -459,7 +496,7 @@ module Origami
459
496
 
460
497
  if obj.is_a?(ObjectStream)
461
498
  obj.each do |subobj|
462
- resolve_all_references[obj, browsed, ref_cache]
499
+ resolve_all_references[subobj, browsed, ref_cache]
463
500
  end
464
501
  end
465
502
 
@@ -629,15 +666,6 @@ module Origami
629
666
  name.split("::").last.to_sym
630
667
  end
631
668
 
632
- def self.native_type; Origami::Object end #:nodoc:
633
-
634
- #
635
- # Returns the native PDF type of this Object.
636
- #
637
- def native_type
638
- self.class.native_type
639
- end
640
-
641
669
  def cast_to(type, _parser = nil) #:nodoc:
642
670
  if type.native_type != self.native_type
643
671
  raise TypeError, "Incompatible cast from #{self.class} to #{type}"