Class: Pdfrb::Model::Cos::Dictionary

Inherits:
Object
  • Object
show all
Defined in:
lib/pdfrb/model/cos/dictionary.rb

Overview

Common Object Syntax dictionary (s7.3.7). Wraps a Hash of Symbol -> PDF value. Subclassed by every semantic Type::*.

Field metadata can come from two sources (mutually exclusive per field; last declaration wins):

* `define_field` — hand-coded (HexaPDF shape).
* `arlington_object "Name"` — auto-loaded from vendored TSVs
via Pdfrb::Arlington::Loader. Calls `define_field` under
the hood with types translated to Ruby classes.

Direct Known Subclasses

Stream, Type::Action, Type::Annotation, Type::AnnotationProjectionDict, Type::Appearance, Type::AppearanceCharacteristics, Type::AppearanceSubDict, Type::AppearanceTrapNet, Type::AppearanceTrapNetSubDict, Type::BorderEffect, Type::BorderStyling, Type::BoxColorInfo, Type::BoxStyle, Type::CIDFontDescriptorMetrics, Type::CIDSystemInfo, Type::CalGray, Type::CalRGB, Type::Catalog, Type::CharProcMap, Type::Collection, Type::CollectionField, Type::CollectionItem, Type::CollectionSchema, Type::CollectionSort, Type::CryptFilter, Type::CryptFilterMap, Type::CryptFilterPublicKey, Type::CryptFilterPublicKeyMap, Type::DeviceN, Type::DeviceNMixingHints, Type::DeviceNProcess, Type::DocMDPTransformParameters, Type::DocumentSecurityStore, Type::EmbeddedFileParameter, Type::EncryptionPublicKey, Type::EncryptionStandard, Type::ExData3DMarkup, Type::ExDataMarkupGeo, Type::ExDataProjection, Type::Field, Type::FieldMDPTransformParameters, Type::FileSpecEF, Type::FileSpecRF, Type::FileSpecification, Type::FileTrailer, Type::Font, Type::FontDescriptor, Type::FontEncoding, Type::Function, Type::GraphicsStateParameter, Type::Halftone, Type::IconFit, Type::Info, Type::InteractiveForm, Type::InteriorColor, Type::Lab, Type::LineEndingStyling, Type::MDPDict, Type::MarkInformation, Type::MarkedContentReference, Type::Measure, Type::MediaCriteria, Type::MediaOffsetFrame, Type::MediaOffsetMarker, Type::MediaOffsetTime, Type::MediaPlayerInfo, Type::MediaPlayers, Type::MediaScreenParameters, Type::Movie, Type::Names, Type::Namespace, Type::ObjectReference, Type::OptContentCreatorInfo, Type::OptContentExport, Type::OptContentLanguage, Type::OptContentPageElement, Type::OptContentPrint, Type::OptContentUsageApplication, Type::OptContentView, Type::OptionalContentConfiguration, Type::OptionalContentGroup, Type::OptionalContentMembership, Type::OptionalContentProperties, Type::Outline, Type::OutlineItem, Type::OutputIntent, Type::OutputIntentsContainer, Type::Page, Type::PageLabel, Type::PageTreeNode, Type::Pattern, Type::PrinterMarkSubDict, Type::Rendition, Type::Resources, Type::RichMediaActivation, Type::RichMediaAnimation, Type::RichMediaCommand, Type::RichMediaConfiguration, Type::RichMediaContent, Type::RichMediaCuePoint, Type::RichMediaDeactivation, Type::RichMediaInstance, Type::RichMediaPresentation, Type::RichMediaSettings, Type::RichMediaWindow, Type::Separation, Type::SigFieldLock, Type::SigFieldSeedValue, Type::Signature, Type::SignatureBuildDataAppDict, Type::SignatureBuildDataDict, Type::SignatureBuildDataSigQDict, Type::SignatureBuildPropDict, Type::SignatureReferenceDocMDP, Type::SignatureReferenceFieldMDP, Type::SignatureReferenceIdentity, Type::SignatureReferenceUR, Type::SoftwareIdentifier, Type::StructElem, Type::StructTreeRoot, Type::StructureAttributes, Type::StructureElementKid, Type::ThreeDActivation, Type::ThreeDAnimationStyle, Type::ThreeDBackground, Type::ThreeDCrossSection, Type::ThreeDLightingScheme, Type::ThreeDMeasure, Type::ThreeDNode, Type::ThreeDReference, Type::ThreeDRenderMode, Type::ThreeDUnits, Type::ThreeDView, Type::Transition, Type::URIDict, Type::URLAlias, Type::URTransformParameters, Type::ViewerPreferences

Instance Attribute Summary

Attributes inherited from Object

#document, #gen, #oid, #value

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Object

#==, define_type, #deref, #indirect?, #must_be_indirect?, pdf_type

Constructor Details

#initialize(value = {}, oid: 0, gen: 0, document: nil) ⇒ Dictionary

Returns a new instance of Dictionary.



181
182
183
184
185
186
187
188
189
# File 'lib/pdfrb/model/cos/dictionary.rb', line 181

def initialize(value = {}, oid: 0, gen: 0, document: nil)
  value ||= {}
  unless value.is_a?(::Hash)
    raise ArgumentError, "Dictionary value must be a Hash, got #{value.class}"
  end

  super(value, oid: oid, gen: gen, document: document)
  apply_required_defaults
end

Class Method Details

.arlington_available?Boolean

Returns:

  • (Boolean)


108
109
110
111
112
113
# File 'lib/pdfrb/model/cos/dictionary.rb', line 108

def arlington_available?
  Pdfrb.const_defined?(:Arlington) &&
    Pdfrb::Arlington.const_defined?(:Loader)
rescue StandardError
  false
end

.arlington_default(field_def) ⇒ Object



168
169
170
171
172
# File 'lib/pdfrb/model/cos/dictionary.rb', line 168

def arlington_default(field_def)
  return nil if field_def.default_value.nil?

  field_def.default_value
end

.arlington_key_to_symbol(key) ⇒ Object



158
159
160
# File 'lib/pdfrb/model/cos/dictionary.rb', line 158

def arlington_key_to_symbol(key)
  key.to_sym
end

.arlington_loaded_for?(_name) ⇒ Boolean

Returns:

  • (Boolean)


98
99
100
# File 'lib/pdfrb/model/cos/dictionary.rb', line 98

def arlington_loaded_for?(_name)
  @arlington_loaded_name == name
end

.arlington_mark_loaded(name) ⇒ Object



103
104
105
# File 'lib/pdfrb/model/cos/dictionary.rb', line 103

def arlington_mark_loaded(name)
  @arlington_loaded_name = name
end

.arlington_object(name, version: "latest") ⇒ Object

Pull field metadata from the named Arlington TSV. Idempotent. Silently no-ops when the Arlington layer or the TSV is unavailable, so hand-coded subclasses still work.

Also registers self in Pdfrb::Model::Type.arlington_registry under name so field-link resolution can find this class when other Types reference it.



79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/pdfrb/model/cos/dictionary.rb', line 79

def arlington_object(name, version: "latest")
  return if arlington_loaded_for?(name)
  return unless arlington_available?

  definition = Pdfrb::Arlington::Loader.object_definition(name, version: version)
  return unless definition

  arlington_mark_loaded(name)
  Pdfrb::Model::Type.register_arlington(name, self) if Pdfrb::Model.const_defined?(:Type)
  definition.each_field do |field_def|
    define_field_from_arlington(field_def)
  end
end

.arlington_one_type_to_ruby(t) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/pdfrb/model/cos/dictionary.rb', line 137

def arlington_one_type_to_ruby(t)
  case t
  when :boolean then [TrueClass, FalseClass]
  when :integer then Integer
  when :number then [Integer, Float]
  when :name then Symbol
  when :string, :"string-ascii", :"string-text" then String
  when :"string-byte" then Fields::PDFByteString
  when :array then Pdfrb::Model::PdfArray
  when :dictionary then Pdfrb::Model::Cos::Dictionary
  when :stream then Pdfrb::Model::Cos::Stream
  when :null then NilClass
  when :date then Fields::PDFDate
  when :rectangle then Pdfrb::Model::Rectangle
  when :matrix then Pdfrb::Model::Matrix
  when :bitmask then Integer
  when :"name-tree", :"number-tree" then Pdfrb::Model::Cos::Dictionary
  end
end

.arlington_required?(field_def) ⇒ Boolean

Returns:

  • (Boolean)


163
164
165
# File 'lib/pdfrb/model/cos/dictionary.rb', line 163

def arlington_required?(field_def)
  field_def.required_literal == true
end

.arlington_types_to_ruby(field_def) ⇒ Object



132
133
134
# File 'lib/pdfrb/model/cos/dictionary.rb', line 132

def arlington_types_to_ruby(field_def)
  field_def.types.map { |t| arlington_one_type_to_ruby(t) }.flatten.compact.uniq
end

.arlington_version(field_def) ⇒ Object



175
176
177
# File 'lib/pdfrb/model/cos/dictionary.rb', line 175

def arlington_version(field_def)
  field_def.since_version.to_s
end

.define_field(pdf_name, type:, required: false, default: nil, indirect: nil, allowed_values: nil, version: "1.0", arlington: nil) ⇒ Object

Declare a field on this class. Mirrors HexaPDF's define_field shape; we add an arlington: slot for the source FieldDefinition.



44
45
46
47
48
49
50
51
52
# File 'lib/pdfrb/model/cos/dictionary.rb', line 44

def define_field(pdf_name, type:, required: false, default: nil,
                 indirect: nil, allowed_values: nil, version: "1.0",
                 arlington: nil)
  own_fields[pdf_name] = Fields::Field.new(
    pdf_name, type: type, required: required, default: default,
    indirect: indirect, allowed_values: allowed_values,
    version: version, arlington: arlington
  )
end

.define_field_from_arlington(field_def) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/pdfrb/model/cos/dictionary.rb', line 116

def define_field_from_arlington(field_def)
  types = arlington_types_to_ruby(field_def)
  return if types.empty?

  key = arlington_key_to_symbol(field_def.key)
  define_field(
    key,
    type: types.length == 1 ? types.first : types,
    required: arlington_required?(field_def),
    default: arlington_default(field_def),
    version: arlington_version(field_def),
    arlington: field_def
  )
end

.each_fieldObject

Iterate (name, Field) pairs across the inheritance chain.



63
64
65
66
67
68
69
70
# File 'lib/pdfrb/model/cos/dictionary.rb', line 63

def each_field
  return enum_for(:each_field) unless block_given?

  if superclass <= Pdfrb::Model::Cos::Dictionary
    superclass.each_field { |n, f| yield n, f }
  end
  own_fields.each { |n, f| yield n, f }
end

.field(name) ⇒ Object

Look up a field by name; walks the inheritance chain.



55
56
57
58
59
60
# File 'lib/pdfrb/model/cos/dictionary.rb', line 55

def field(name)
  return own_fields[name] if own_fields.key?(name)
  return superclass.field(name) if superclass <= Pdfrb::Model::Cos::Dictionary

  nil
end

.lookup_type(symbol) ⇒ Object



37
38
39
# File 'lib/pdfrb/model/cos/dictionary.rb', line 37

def lookup_type(symbol)
  TYPE_MAP[symbol]
end

.own_fieldsObject



93
94
95
# File 'lib/pdfrb/model/cos/dictionary.rb', line 93

def own_fields
  @own_fields ||= {}
end

.register_type(symbol, klass = self) ⇒ Object



32
33
34
35
# File 'lib/pdfrb/model/cos/dictionary.rb', line 32

def register_type(symbol, klass = self)
  TYPE_MAP[symbol] = klass
  klass.define_type(symbol) unless klass.pdf_type
end

.type_mapObject

/Type-symbol -> Dictionary subclass. Populated by register_type. Used by Document#wrap to dispatch.



28
29
30
# File 'lib/pdfrb/model/cos/dictionary.rb', line 28

def type_map
  TYPE_MAP
end

Instance Method Details

#[](name) ⇒ Object

Typed access. Resolves References, wraps in field-specific subclass, applies default, runs converters.



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/pdfrb/model/cos/dictionary.rb', line 193

def [](name)
  name = name.to_sym unless name.is_a?(::Symbol)
  field = self.class.field(name)
  data = @value[name]
  data = field.default if data.nil? && field&.default?
  return nil if data.nil?

  data = dereference(data)
  data = data.value if unbox_object?(data)

  return data unless field

  converted = Fields.apply(field, data, document)
  if converted
    @value[name] = converted
    converted
  else
    data
  end
end

#[]=(name, value) ⇒ Object

Raises:

  • (ArgumentError)


214
215
216
217
218
# File 'lib/pdfrb/model/cos/dictionary.rb', line 214

def []=(name, value)
  raise ArgumentError, "Dictionary keys must be Symbols" unless name.is_a?(::Symbol)

  @value[name] = value
end

#delete(name) ⇒ Object



225
226
227
228
# File 'lib/pdfrb/model/cos/dictionary.rb', line 225

def delete(name)
  name = name.to_sym unless name.is_a?(::Symbol)
  @value.delete(name)
end

#eachObject

Iterate (Symbol, processed-value) pairs. Slower than #each_raw because it runs the full accessor pipeline; use that when you don't need wrapping.



233
234
235
236
237
238
# File 'lib/pdfrb/model/cos/dictionary.rb', line 233

def each
  return enum_for(:each) unless block_given?

  @value.each_key { |k| yield(k, self[k]) }
  self
end

#each_rawObject

Iterate (Symbol, raw-value) pairs without accessor pipeline.



241
242
243
244
245
246
# File 'lib/pdfrb/model/cos/dictionary.rb', line 241

def each_raw
  return enum_for(:each_raw) unless block_given?

  @value.each { |k, v| yield(k, v) }
  self
end

#empty?Boolean

Returns:

  • (Boolean)


249
# File 'lib/pdfrb/model/cos/dictionary.rb', line 249

def empty?; @value.empty?; end

#key?(name) ⇒ Boolean

Returns:

  • (Boolean)


220
221
222
223
# File 'lib/pdfrb/model/cos/dictionary.rb', line 220

def key?(name)
  name = name.to_sym unless name.is_a?(::Symbol)
  !@value[name].nil?
end

#keysObject



248
# File 'lib/pdfrb/model/cos/dictionary.rb', line 248

def keys; @value.keys; end

#pdf_typeObject



251
252
253
# File 'lib/pdfrb/model/cos/dictionary.rb', line 251

def pdf_type
  self.class.pdf_type || self[:Type]
end

#validateObject

Walk every declared field and yield (message, correctable) for each violation. Returns an Enumerator if no block given.



257
258
259
260
261
262
263
# File 'lib/pdfrb/model/cos/dictionary.rb', line 257

def validate
  return enum_for(:validate) unless block_given?

  self.class.each_field do |name, field|
    validate_one(name, field) { |*a| yield(*a) }
  end
end