Class: Origami::Dictionary

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

Overview

Class representing a Dictionary Object. Dictionaries are containers associating a Name to an embedded Object.

Direct Known Subclasses

Action, Action::GoToE::EmbeddedTarget, Action::Launch::WindowsLaunchParams, Action::RichMediaExecute::Command, AnimationStyle3D, Annotation, Annotation::AdditionalActions, Annotation::AppearanceCharacteristics, Annotation::AppearanceDictionary, Annotation::Artwork3D::Activation, Annotation::BorderEffect, Annotation::BorderStyle, Annotation::RichMedia::Activation, Annotation::RichMedia::Animation, Annotation::RichMedia::Configuration, Annotation::RichMedia::Content, Annotation::RichMedia::CuePoint, Annotation::RichMedia::Deactivation, Annotation::RichMedia::Instance, Annotation::RichMedia::Parameters, Annotation::RichMedia::Position, Annotation::RichMedia::Presentation, Annotation::RichMedia::Settings, Annotation::RichMedia::Window, Background3D, Catalog, CatalogAdditionalActions, Collection, Collection::Color, Collection::Folder, Collection::Item, Collection::Navigator, Collection::Schema, Collection::Sort, Collection::Split, Collection::Subitem, CrossSection3D, DestinationDictionary, DeveloperExtension, EmbeddedFileParameters, Encoding, Encryption::CryptFilterDictionary, Encryption::EncryptionDictionary, Extensions, FDF::Dictionary, FDF::Field, FDF::IconFit, FDF::JavaScript, FDF::NamedPageReference, FDF::Page, FDF::Template, Field::AdditionalActions, Field::CertificateSeedValue, Field::SignatureLock, Field::SignatureSeedValue, Field::Subform, FileSpec, Filter::CCITTFax::DecodeParms, Filter::Crypt::DecodeParms, Filter::DCT::DecodeParms, Filter::Flate::DecodeParms, Filter::JBIG2::DecodeParms, Filter::LZW::DecodeParms, Font, FontDescriptor, Function::Exponential, Function::Stitching, Graphics::ExtGState, Graphics::FormXObject::Group, Graphics::FormXObject::Reference, Graphics::Pattern::Shading, Graphics::Pattern::Shading::Axial, Graphics::Pattern::Shading::FunctionBased, Graphics::Pattern::Shading::Radial, Graphics::ReferenceDictionary, InteractiveForm, LightingScheme3D, Linearization, Measurement3D, Metadata, NameTreeNode, Names, Node3D, NumberTreeNode, Outline, OutlineItem, OutputIntent, PPKLite::AddressList, PPKLite::Catalog, PPKLite::Certificate, PPKLite::PPK, PPKLite::User, PPKLite::UserList, Page, Page::AdditionalActions, Page::BoxColorInformation, Page::BoxStyle, Page::NavigationNode, PageLabel, PageTreeNode, Perms, Projection3D, Reference3D, RenderMode3D, Requirement, Requirement::Handler, Resources, Signature::BuildData, Signature::BuildProperties, Signature::DigitalSignature, Signature::Reference, Units3D, UsageRights::TransformParams, View3D, ViewerPreferences, WebCapture::Command, WebCapture::CommandSettings, WebCapture::ContentSet, WebCapture::SourceInformation, WebCapture::SpiderInfo

Constant Summary collapse

TOKENS =

:nodoc:

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

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, #indirect?, #indirect_parent, #logicalize, #logicalize!, #native_type, #post_build, #pre_build, #reference, #set_document, #set_indirect, skip_until_next_obj, #solve, #to_o, #type, typeof, #version_required, #xrefs

Constructor Details

#initialize(hash = {}, parser = nil) ⇒ Dictionary

Creates a new Dictionary.

hash

The hash representing the new Dictionary.

Raises:

  • (TypeError)


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
79
80
81
# File 'lib/origami/dictionary.rb', line 47

def initialize(hash = {}, parser = nil)
    raise TypeError, "Expected type Hash, received #{hash.class}." unless hash.is_a?(Hash)
    super()

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

    hash.each_pair do |k,v|
        next if k.nil?

        # Turns the values into Objects.
        key, value = k.to_o, v.to_o

        if Origami::OPTIONS[:enable_type_guessing]
            hint_type = guess_value_type(key, value)

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

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

        # Cache keys and values for fast search.
        cache_key(key)
        cache_value(value)

        self[key] = value
    end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(field, *args) ⇒ Object

:nodoc:

Raises:

  • (NoMethodError)


201
202
203
204
205
206
207
208
209
210
# File 'lib/origami/dictionary.rb', line 201

def method_missing(field, *args) #:nodoc:
    raise NoMethodError, "No method `#{field}' for #{self.class}" unless field.to_s[0,1] =~ /[A-Z]/

    if field.to_s[-1,1] == '='
        self[field.to_s[0..-2].to_sym] = args.first
    else
        obj = self[field];
        obj.is_a?(Reference) ? obj.solve : obj
    end
end

Instance Attribute Details

#names_cacheObject (readonly)

Returns the value of attribute names_cache.



41
42
43
# File 'lib/origami/dictionary.rb', line 41

def names_cache
  @names_cache
end

#strings_cacheObject (readonly)

Returns the value of attribute strings_cache.



41
42
43
# File 'lib/origami/dictionary.rb', line 41

def strings_cache
  @strings_cache
end

#xref_cacheObject (readonly)

Returns the value of attribute xref_cache.



41
42
43
# File 'lib/origami/dictionary.rb', line 41

def xref_cache
  @xref_cache
end

Class Method Details

.add_type_info(klass, key, value) ⇒ Object

:nodoc:

Raises:

  • (TypeError)


228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/origami/dictionary.rb', line 228

def self.add_type_info(klass, key, value) #:nodoc:
    raise TypeError, "Invalid class #{klass}" unless klass.is_a?(Class) and klass < Dictionary

    key, value = key.to_o, value.to_o

    # Inherit the superclass type information.
    if not @@cast_fingerprints.key?(klass) and @@cast_fingerprints.key?(klass.superclass)
        @@cast_fingerprints[klass] = @@cast_fingerprints[klass.superclass].dup
    end

    @@cast_fingerprints[klass] ||= {}
    @@cast_fingerprints[klass][key] = value

    @@cast_keys.push(key) unless @@cast_keys.include?(key)
end

.guess_type(hash) ⇒ Object

:nodoc:



244
245
246
247
248
249
250
251
252
253
254
# File 'lib/origami/dictionary.rb', line 244

def self.guess_type(hash) #:nodoc:
    best_type = self

    @@cast_fingerprints.each_pair do |klass, keys|
        next unless klass < best_type

        best_type = klass if keys.all? { |k,v| hash[k] == v }
    end

    best_type
end

.hint_type(name) ⇒ Object

:nodoc:



256
# File 'lib/origami/dictionary.rb', line 256

def self.hint_type(name); nil end

.native_typeObject



226
# File 'lib/origami/dictionary.rb', line 226

def self.native_type; Dictionary end

.parse(stream, parser = nil) ⇒ Object

:nodoc:



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/origami/dictionary.rb', line 83

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

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

    hash = {}
    while stream.skip(@@regexp_close).nil? do
        key = Name.parse(stream, parser)

        type = Object.typeof(stream)
        raise InvalidDictionaryObjectError, "Invalid object for field #{key.to_s}" if type.nil?

        value = type.parse(stream, parser)
        hash[key] = value
    end

    if Origami::OPTIONS[:enable_type_guessing] and not (@@cast_keys & hash.keys).empty?
        dict_type = self.guess_type(hash)
    else
        dict_type = self
    end

    # Creates the Dictionary.
    dict = dict_type.new(hash, parser)

    dict.file_offset = offset
    dict
end

Instance Method Details

#[](key) ⇒ Object



165
166
167
# File 'lib/origami/dictionary.rb', line 165

def [](key)
    super(key.to_o)
end

#[]=(key, val) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/origami/dictionary.rb', line 145

def []=(key,val)
    unless key.is_a?(Symbol) or key.is_a?(Name)
        fail "Expecting a Name for a Dictionary entry, found #{key.class} instead."
    end

    key = key.to_o
    if val.nil?
        delete(key)
        return
    end

    val = val.to_o
    super(key,val)

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

    val
end

#cast_to(type, parser = nil) ⇒ Object



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

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

    cast = type.new(self, 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



212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/origami/dictionary.rb', line 212

def copy
    copy = self.class.new
    self.each_pair do |k,v|
        copy[k] = v.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

#delete(key) ⇒ Object



175
176
177
# File 'lib/origami/dictionary.rb', line 175

def delete(key)
    super(key.to_o)
end

#key?(key) ⇒ Boolean Also known as: include?, has_key?

Returns:



169
170
171
# File 'lib/origami/dictionary.rb', line 169

def key?(key)
    super(key.to_o)
end

#map!(&b) ⇒ Object



135
136
137
138
139
# File 'lib/origami/dictionary.rb', line 135

def map!(&b)
    self.each_pair do |k,v|
        self[k] = b.call(v)
    end
end

#merge(dict) ⇒ Object



141
142
143
# File 'lib/origami/dictionary.rb', line 141

def merge(dict)
    Dictionary.new(super(dict))
end

#to_hObject Also known as: value



196
197
198
# File 'lib/origami/dictionary.rb', line 196

def to_h
    Hash[self.to_a.map!{|k, v| [ k.value, v.value ]}]
end

#to_obfuscated_strObject



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/origami/obfuscation.rb', line 136

def to_obfuscated_str
    content = TOKENS.first + Obfuscator.junk_spaces
    self.each_pair do |key, value|
        content << Obfuscator.junk_spaces +
          key.to_obfuscated_str + Obfuscator.junk_spaces +
          value.to_obfuscated_str + Obfuscator.junk_spaces
    end

    content << TOKENS.last
    super(content)
end

#to_s(indent: 1, tab: "\t") ⇒ Object

:nodoc:



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

def to_s(indent: 1, tab: "\t") #:nodoc:
    if indent > 0
        content = TOKENS.first + EOL
        self.each_pair do |key,value|
            content << tab * indent << key.to_s << ' '
            content << (value.is_a?(Dictionary) ? value.to_s(indent: indent+1) : value.to_s)
            content << EOL
        end

        content << tab * (indent - 1) << TOKENS.last
    else
        content = TOKENS.first.dup
        self.each_pair do |key,value|
            content << "#{key.to_s} #{value.is_a?(Dictionary) ? value.to_s(indent: 0) : value.to_s}"
        end
        content << TOKENS.last
    end

    super(content)
end