Class: Origami::Array

Inherits:
Array
  • Object
show all
Includes:
Object
Defined in:
lib/origami/array.rb,
lib/origami/obfuscation.rb

Overview

Class representing an Array Object. Arrays contain a set of Object.

Direct Known Subclasses

Destination

Constant Summary collapse

TOKENS =

:nodoc:

%w{ [ ] }
@@regexp_open =
Regexp.new(WHITESPACES + Regexp.escape(TOKENS.first) + WHITESPACES)
@@regexp_close =
Regexp.new(WHITESPACES + Regexp.escape(TOKENS.last) + WHITESPACES)

Instance Attribute Summary collapse

Attributes included from Object

#file_offset, #generation, #no, #objstm_offset, #parent

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Object

#<=>, #document, #export, included, #indirect?, #indirect_parent, #logicalize, #logicalize!, #native_type, #post_build, #reference, #set_document, #set_indirect, skip_until_next_obj, #solve, #to_o, #type, typeof, #version_required, #xrefs

Constructor Details

#initialize(data = [], parser = nil, hint_type: nil) ⇒ Array

Creates a new PDF Array Object.

data

An array of objects.

Raises:

  • (TypeError)


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/origami/array.rb', line 45

def initialize(data = [], parser = nil, hint_type: nil)
    raise TypeError, "Expected type Array, received #{data.class}." unless data.is_a?(::Array)
    super()

    @strings_cache = []
    @names_cache = []
    @xref_cache = {}

    data.each_with_index do |value, index|
        value = value.to_o

        if Origami::OPTIONS[:enable_type_guessing]
            index_type = hint_type.is_a?(::Array) ? hint_type[index % hint_type.size] : hint_type
            if index_type.is_a?(::Array) and not value.is_a?(Reference)
                index_type = index_type.find {|type| type < value.class }
            end

            if index_type.is_a?(Class) and index_type < value.class
                value = value.cast_to(index_type, parser)
            end

            if index_type and parser and Origami::OPTIONS[:enable_type_propagation]
                if value.is_a?(Reference)
                    parser.defer_type_cast(value, index_type)
                end
            end
        end

        # Cache object value for fast search.
        cache_value(value)

        self.push(value)
    end
end

Instance Attribute Details

#names_cacheObject (readonly)

Returns the value of attribute names_cache.



39
40
41
# File 'lib/origami/array.rb', line 39

def names_cache
  @names_cache
end

#strings_cacheObject (readonly)

Returns the value of attribute strings_cache.



39
40
41
# File 'lib/origami/array.rb', line 39

def strings_cache
  @strings_cache
end

#xref_cacheObject (readonly)

Returns the value of attribute xref_cache.



39
40
41
# File 'lib/origami/array.rb', line 39

def xref_cache
  @xref_cache
end

Class Method Details

.of(klass, *klasses, length: nil) ⇒ Object

Parameterized Array class with additional typing information. Example: Array.of(Integer)



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/origami/array.rb', line 189

def self.of(klass, *klasses, length: nil)
    Class.new(self) do
        const_set('ARRAY_TYPE', (klasses.empty? and not klass.is_a?(::Array)) ? klass : [ klass ].concat(klasses))
        const_set('STATIC_LENGTH', length)

        def initialize(data = [], parser = nil)
            super(data, parser, hint_type: self.class.const_get('ARRAY_TYPE'))
        end

        def pre_build #:nodoc:
            do_type_check if Origami::OPTIONS[:enable_type_checking]

            super
        end

        def self.parse(stream, parser = nil)
            super(stream, parser, hint_type: const_get('ARRAY_TYPE'))
        end

        def do_type_check #:nodoc:
            static_length = self.class.const_get('STATIC_LENGTH')
            array_type = self.class.const_get('ARRAY_TYPE')

            if static_length and self.length != static_length
                STDERR.puts "Warning: object #{self.class.name} has unexpected length #{self.length} (should be #{static_length})"
            end

            self.each_with_index do |object, index|
                index_type = array_type.is_a?(::Array) ? array_type[index % array_type.size] : array_type

                begin
                    object_value = object.solve
                rescue InvalidReferenceError
                    STDERR.puts "Warning: in object #{self.class}, invalid reference at index #{index}"
                    next
                end

                unless object_value.is_a?(index_type)
                    STDERR.puts "Warning: object #{self.class.name || 'Array'} should be composed of #{index_type.name} at index #{index} (got #{object_value.type} instead)"
                end
            end
        end
    end
end

.parse(stream, parser = nil, hint_type: nil) ⇒ Object

:nodoc:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/origami/array.rb', line 86

def self.parse(stream, parser = nil, hint_type: nil) #:nodoc:
    data = []
    offset = stream.pos

    if not stream.skip(@@regexp_open)
        raise InvalidArrayObjectError, "No token '#{TOKENS.first}' found"
    end

    while stream.skip(@@regexp_close).nil? do
        type = Object.typeof(stream)
        raise InvalidArrayObjectError, "Bad embedded object format" if type.nil?

        value = type.parse(stream, parser)
        data << value
    end

    array = Array.new(data, parser, hint_type: hint_type)
    array.file_offset = offset

    array
end

Instance Method Details

#+(other) ⇒ Object



133
134
135
136
137
138
# File 'lib/origami/array.rb', line 133

def +(other)
    a = Origami::Array.new(self.to_a + other.to_a)
    a.no, a.generation = @no, @generation

    a
end

#<<(item) ⇒ Object Also known as: push



140
141
142
143
144
145
# File 'lib/origami/array.rb', line 140

def <<(item)
    obj = item.to_o
    obj.parent = self unless obj.indirect?

    super(obj)
end

#[]=(key, val) ⇒ Object



148
149
150
151
152
153
154
155
# File 'lib/origami/array.rb', line 148

def []=(key,val)
    key, val = key.to_o, val.to_o
    super(key.to_o, val.to_o)

    val.parent = self unless val.indirect? or val.parent.equal?(self)

    val
end

#cast_to(type, parser = nil) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/origami/array.rb', line 170

def cast_to(type, parser = nil)
    super(type)

    cast = type.new(self.copy, parser)
    cast.parent = self.parent
    cast.no, cast.generation = self.no, self.generation
    if self.indirect?
        cast.set_indirect(true)
        cast.set_document(self.document)
        cast.file_offset = self.file_offset # cast can replace self
    end

    cast
end

#copyObject



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/origami/array.rb', line 157

def copy
    copy = self.class.new
    self.each do |obj|
        copy << obj.copy
    end

    copy.parent = @parent
    copy.no, copy.generation = @no, @generation
    copy.set_indirect(true) if self.indirect?
    copy.set_document(@document) if self.indirect?
    copy
end

#pre_buildObject



80
81
82
83
84
# File 'lib/origami/array.rb', line 80

def pre_build
    self.map!{|obj| obj.to_o}

    super
end

#to_aObject Also known as: value

Converts self into a Ruby array.



111
112
113
# File 'lib/origami/array.rb', line 111

def to_a
    super.map(&:value)
end

#to_obfuscated_strObject



154
155
156
157
158
159
160
161
162
163
# File 'lib/origami/obfuscation.rb', line 154

def to_obfuscated_str
    content = TOKENS.first + Obfuscator.junk_spaces
    self.each do |entry|
        content << entry.to_o.to_obfuscated_str + Obfuscator.junk_spaces
    end

    content << TOKENS.last

    super(content)
end

#to_sObject

:nodoc:



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/origami/array.rb', line 116

def to_s #:nodoc:
    content = "#{TOKENS.first} "
    self.each do |entry|
        entry = entry.to_o

        case entry
        when Dictionary # Do not indent dictionaries inside of arrays.
            content << entry.to_s(indent: 0) << ' '
        else
            content << entry.to_s << ' '
        end
    end
    content << TOKENS.last

    super(content)
end