Class: HexaPDF::Dictionary

Inherits:
Object
  • Object
show all
Includes:
DictionaryFields
Defined in:
lib/hexapdf/dictionary.rb

Overview

Implementation of the PDF dictionary type.

Subclasses should use the available class method ::define_field to create fields according to the PDF specification. This allows, among other things, automatic type checking and basic validation.

Fields defined in superclasses are inherited by their subclasses. This avoids duplicating basic field information. If fields differ from their superclass definition, they can be defined again in the subclass.

See: PDF2.0 s7.3.7

Direct Known Subclasses

HexaPDF::DigitalSignature::Signature, HexaPDF::DigitalSignature::Signature::SignatureReference, HexaPDF::DigitalSignature::Signature::TransformParams, Encryption::EncryptionDictionary, NameTreeNode, NumberTreeNode, Stream, Type::AcroForm::Field, Type::AcroForm::Form, Type::AcroForm::SignatureField::CertificateSeedValueDictionary, Type::AcroForm::SignatureField::LockDictionary, Type::AcroForm::SignatureField::SeedValueDictionary, Type::Action, Type::Actions::Launch::WinParameters, Type::Annotation, Type::Annotation::AppearanceDictionary, Type::Annotation::Border, Type::Annotations::MarkupAnnotation::ExData, Type::Annotations::Widget::AppearanceCharacteristics, Type::CIDFont::CIDSystemInfo, Type::Catalog, Type::EmbeddedFile::MacInfo, Type::EmbeddedFile::Parameters, Type::FileSpecification, Type::FileSpecification::EFDictionary, Type::Font, Type::FontDescriptor, Type::Form::Group, Type::Form::Reference, Type::GraphicsStateParameter, Type::IconFit, Type::Info, Type::MarkInformation, Type::Names, Type::OptionalContentConfiguration, Type::OptionalContentConfiguration::UsageApplication, Type::OptionalContentGroup, Type::OptionalContentGroup::OptionalContentUsage, Type::OptionalContentGroup::OptionalContentUsage::CreatorInfo, Type::OptionalContentGroup::OptionalContentUsage::Export, Type::OptionalContentGroup::OptionalContentUsage::Language, Type::OptionalContentGroup::OptionalContentUsage::PageElement, Type::OptionalContentGroup::OptionalContentUsage::Print, Type::OptionalContentGroup::OptionalContentUsage::User, Type::OptionalContentGroup::OptionalContentUsage::View, Type::OptionalContentGroup::OptionalContentUsage::Zoom, Type::OptionalContentMembership, Type::OptionalContentProperties, Type::Outline, Type::OutlineItem, Type::OutputIntent, Type::OutputIntent::DestOutputProfileRef, Type::Page, Type::PageLabel, Type::PageTreeNode, Type::Resources, Type::Trailer, Type::ViewerPreferences

Constant Summary

Constants included from DictionaryFields

HexaPDF::DictionaryFields::Boolean, HexaPDF::DictionaryFields::PDFByteString, HexaPDF::DictionaryFields::PDFDate

Instance Attribute Summary

Attributes inherited from Object

#data, #document, #must_be_indirect

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Object

#<=>, #==, #cache, #cached?, #clear_cache, deep_copy, #deep_copy, #document?, #eql?, #gen, #gen=, #hash, #indirect?, #initialize, #inspect, make_direct, #must_be_indirect?, #null?, #oid, #oid=, #validate, #value, #value=

Constructor Details

This class inherits a constructor from HexaPDF::Object

Class Method Details

.define_field(name, type:, required: false, default: nil, indirect: nil, allowed_values: nil, version: '1.0') ⇒ Object

Defines an entry for the field name and returns the initalized HexaPDF::DictionaryFields::Field object. A suitable converter module (see HexaPDF::DictionaryFields::Field#converter) is selected based on the type argument.

Options:

type

The class (or an array of classes) that a value of this field must have. Here is a mapping from PDF object types to classes:

Boolean

[TrueClass, FalseClass] (or use the Boolean constant)

Integer

Integer

Real

Float

String

String (for text strings), PDFByteString (for binary strings)

Date

PDFDate

Name

Symbol

Array

PDFArray or Array

Dictionary

Dictionary (or any subclass) or Hash

Stream

Stream (or any subclass)

Null

NilClass

If an array of classes is provided, the value can be an instance of any of these classes.

If a Symbol object instead of a class is provided, the class is looked up using the ‘object.type_map’ global configuration option when necessary to support lazy loading.

Note that if multiple types are allowed and one of the allowed types is Dictionary (or a Symbol), it has to be the first in the list. Otherwise automatic type conversion functions won’t work correctly.

required

Specifies whether this field is required, either true or false.

default

Specifies the default value for the field, if any.

indirect

Specifies whether the value (or the values in the array value) of this field has to be an indirect object (true), a direct object (false) or if it doesn’t matter (unspecified or nil).

allowed_values

An array of allowed values for this field.

version

Specifies the minimum version of the PDF specification needed for this value.



100
101
102
103
104
105
# File 'lib/hexapdf/dictionary.rb', line 100

def self.define_field(name, type:, required: false, default: nil, indirect: nil,
                      allowed_values: nil, version: '1.0')
  @fields ||= {}
  @fields[name] = Field.new(type, required: required, default: default, indirect: indirect,
                            allowed_values: allowed_values, version: version)
end

.define_type(type) ⇒ Object

Defines the static PDF type of the class in cases where this is possible, i.e. when the class implements one specific PDF type (e.g. the HexaPDF::Type::Catalog class).



129
130
131
# File 'lib/hexapdf/dictionary.rb', line 129

def self.define_type(type)
  @type = type
end

.each_field(&block) ⇒ Object

:call-seq:

class.each_field {|name, data| block }   -> class
class.each_field                         -> Enumerator

Calls the block once for each field defined either in this class or in one of the ancestor classes.



121
122
123
124
125
# File 'lib/hexapdf/dictionary.rb', line 121

def self.each_field(&block) # :yields: name, data
  return to_enum(__method__) unless block_given?
  superclass.each_field(&block) if self != Dictionary && superclass != Dictionary
  @fields.each(&block) if defined?(@fields)
end

.field(name) ⇒ Object

Returns the field entry for the given field name.

The ancestor classes are also searched for such a field entry if none is found for the current class.



111
112
113
# File 'lib/hexapdf/dictionary.rb', line 111

def self.field(name)
  @fields&.[](name) || superclass.field(name)
end

.typeObject

Returns the statically defined PDF type of the class.

See ::define_type



136
137
138
# File 'lib/hexapdf/dictionary.rb', line 136

def self.type
  defined?(@type) && @type
end

Instance Method Details

#[](name) ⇒ Object

Returns the value for the given dictionary entry.

This method should be used instead of direct access to the value because it provides numerous advantages:

  • References are automatically resolved.

  • Returns the native Ruby object for values with class HexaPDF::Object. However, all subclasses of HexaPDF::Object are returned as is (it makes no sense, for example, to return the hash that describes the Catalog instead of the Catalog object).

  • Automatically wraps hash values in specific subclasses of this class if field information is available (see ::define_field).

  • Returns the default value if one is specified and no value is available.

Note: If field information is available for the entry, a Hash or Array value will always be wrapped by Dictionary or PDFArray. Otherwise, the value will be returned as-is.

Note: This method may throw a “can’t add a new key into hash during iteration” error in certain cases because it potentially modifies the underlying hash!



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/hexapdf/dictionary.rb', line 161

def [](name)
  field = self.class.field(name)
  data = if key?(name)
           value[name]
         elsif field&.default?
           value[name] = field.default
         end
  value[name] = data = document.deref(data) if data.kind_of?(HexaPDF::Reference)
  if data.instance_of?(HexaPDF::Object) || (data.kind_of?(HexaPDF::Object) && data.value.nil?)
    data = data.value
  end
  if (result = field&.convert(data, document))
    self[name] = data = result
  end
  data
end

#[]=(name, data) ⇒ Object

Stores the data under name in the dictionary. Name has to be a Symbol.

If the current value for this name has the class HexaPDF::Object (and only this, no subclasses) and the given value has not (including subclasses), the value is stored inside the HexaPDF::Object.



183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/hexapdf/dictionary.rb', line 183

def []=(name, data)
  unless name.kind_of?(Symbol)
    raise ArgumentError, "Only Symbol (Name) keys are allowed to be used in PDF dictionaries"
  end

  if value[name].instance_of?(HexaPDF::Object) && !data.kind_of?(HexaPDF::Object) &&
      !data.kind_of?(HexaPDF::Reference)
    value[name].value = data
  else
    value[name] = data
  end
end

#delete(name) ⇒ Object

Deletes the name-value pair from the dictionary and returns the value. If such a pair does not exist, nil is returned.



203
204
205
# File 'lib/hexapdf/dictionary.rb', line 203

def delete(name)
  value.delete(name) { nil }
end

#eachObject

:call-seq:

dict.each {|name, value| block}    -> dict
dict.each                          -> Enumerator

Calls the given block once for every name-value entry that is stored in the dictionary.

Note that the yielded value is already preprocessed like in #[].



214
215
216
217
218
# File 'lib/hexapdf/dictionary.rb', line 214

def each
  return to_enum(__method__) unless block_given?
  value.each_key {|name| yield(name, self[name]) }
  self
end

#empty?Boolean

Returns true if the dictionary contains no entries.

Returns:



227
228
229
# File 'lib/hexapdf/dictionary.rb', line 227

def empty?
  value.empty?
end

#key?(key) ⇒ Boolean

Returns true if the given key is present in the dictionary and not nil.

Returns:



197
198
199
# File 'lib/hexapdf/dictionary.rb', line 197

def key?(key)
  !value[key].nil?
end

#to_hashObject

Returns a hash containing the preprocessed values (like in #[]).



232
233
234
# File 'lib/hexapdf/dictionary.rb', line 232

def to_hash
  value.each_with_object({}) {|(k, _), h| h[k] = self[k] }
end

#typeObject

Returns, in order or availability, the value of ::type, the /Type field or the result of Object#type.



222
223
224
# File 'lib/hexapdf/dictionary.rb', line 222

def type
  self.class.type || self[:Type] || super
end