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.

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

#<=>, #copy, #indirect_parent, #is_indirect?, #pdf, #pdf_version_required, #post_build, #pre_build, #reference, #set_indirect, #set_pdf, #size, skip_until_next_obj, #solve, #to_o, #type, typeof, #xrefs

Methods inherited from Hash

#to_o

Constructor Details

#initialize(hash = {}) ⇒ Dictionary

Creates a new Dictionary.

hash

The hash representing the new Dictionary.

Raises:

  • (TypeError)


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
# File 'lib/origami/dictionary.rb', line 48

def initialize(hash = {})
  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|
    @names_cache.push(k.to_o)
    case val = v.to_o
      when String then @strings_cache.push(val)
      when Name then @names_cache.push(val)
      when Reference then
        (@xref_cache[val] ||= []).push(self)
      when Dictionary,Array then 
        @strings_cache.concat(val.strings_cache)
        @names_cache.concat(val.names_cache)
        @xref_cache.update(val.xref_cache) do |ref, cache1, cache2|
          cache1.concat(cache2)  
        end

        val.strings_cache.clear
        val.names_cache.clear
        val.xref_cache.clear
    end

    self[k.to_o] = val unless k.nil?
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(field, *args) ⇒ Object

:nodoc:

Raises:

  • (NoMethodError)


186
187
188
189
190
191
192
193
194
195
# File 'lib/origami/dictionary.rb', line 186

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.



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

def names_cache
  @names_cache
end

#strings_cacheObject (readonly)

Returns the value of attribute strings_cache.



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

def strings_cache
  @strings_cache
end

#xref_cacheObject (readonly)

Returns the value of attribute xref_cache.



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

def xref_cache
  @xref_cache
end

Class Method Details

.parse(stream) ⇒ Object

:nodoc:



79
80
81
82
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
113
114
115
116
# File 'lib/origami/dictionary.rb', line 79

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

  if stream.skip(@@regexp_open).nil?
    raise InvalidDictionaryObjectError, "No token '#{TOKENS.first}' found"
  end
    
  pairs = {}
  while stream.skip(@@regexp_close).nil? do
    key = Name.parse(stream)
    
    type = Object.typeof(stream)
    if type.nil?
      raise InvalidDictionaryObjectError, "Invalid object for field #{key.to_s}"
    end
    value = type.parse(stream)
    
    pairs[key] = value
  end
 
  dict = 
    if Origami::OPTIONS[:enable_type_guessing]
      type = pairs[Name.new(:Type)]
      if type.is_a?(Name) and DICT_SPECIAL_TYPES.include?(type.value)
        DICT_SPECIAL_TYPES[type.value].new(pairs)
      else
        Dictionary.new(pairs)
      end

    else
      Dictionary.new(pairs)
    end

  dict.file_offset = offset

  dict
end

Instance Method Details

#[](key) ⇒ Object



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

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

#[]=(key, val) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/origami/dictionary.rb', line 149

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 not val.nil?
    val = val.to_o
    super(key,val)
    
    key.parent = self
    val.parent = self unless val.is_indirect? or val.parent.equal?(self)

    val
  else
    delete(key)
  end
end

#delete(key) ⇒ Object



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

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

#has_key?(key) ⇒ Boolean

Returns:



172
173
174
# File 'lib/origami/dictionary.rb', line 172

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

#map!(&b) ⇒ Object



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

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

#merge(dict) ⇒ Object



145
146
147
# File 'lib/origami/dictionary.rb', line 145

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

#real_typeObject



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

def real_type ; Dictionary end

#to_obfuscated_strObject



117
118
119
120
121
122
123
124
125
126
127
# File 'lib/origami/obfuscation.rb', line 117

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

  content << TOKENS.last
  super(content)
end

#to_s(indent = 1) ⇒ Object

:nodoc:



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/origami/dictionary.rb', line 120

def to_s(indent = 1)  #:nodoc:
  if indent > 0
    content = TOKENS.first + EOL
    self.each_pair do |key,value|
      content << "\t" * indent + key.to_s + " " + (value.is_a?(Dictionary) ? value.to_s(indent + 1) : value.to_s) + EOL
    end
    
    content << "\t" * (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(0) : value.to_s}"
    end
    content << TOKENS.last
  end

  super(content)
end