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)



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
233
234
235
236
237
238
239
240
241
# File 'lib/origami/array.rb', line 198

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
# 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?
end

#cast_to(type, parser = nil) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/origami/array.rb', line 179

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

#concat(*arys) ⇒ Object



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

def concat(*arys)
    arys.each do |ary|
        ary.each do |e|
            val = e.to_o
            val.parent = self unless val.indirect?

            self.push(val)
        end
    end
end

#copyObject



166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/origami/array.rb', line 166

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