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

Inherits:
Object
  • Object
show all
Extended by:
ArlingtonBacked
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::AFEmbeddedFileParameter, Type::AFFileSpecEF, Type::Action, Type::ActionSound, Type::AddActionCatalog, Type::AddActionFormField, Type::AddActionPageObject, Type::AddActionScreenAnnotation, Type::AddActionWidgetAnnotation, Type::AlternateImage, Type::Annotation, Type::AnnotationProjectionDict, Type::Appearance, Type::AppearanceCharacteristics, Type::AppearancePrinterMarkDict, Type::AppearanceSubDict, Type::AppearanceTrapNet, Type::AppearanceTrapNetSubDict, Type::AppleSupplementalText, Type::AuthCode, Type::Bead, Type::BorderEffect, Type::BorderStyle, Type::BorderStyling, Type::BoxColorInfo, Type::BoxStyle, Type::CCITTFaxDecodeParms, Type::CIDFontDescriptorMetrics, Type::CIDSystemInfo, Type::CalGray, Type::CalRGB, Type::Catalog, Type::CertSeedValue, Type::CharProcMap, Type::ClassMap, Type::Collection, Type::CollectionColors, Type::CollectionField, Type::CollectionFolder, Type::CollectionItem, Type::CollectionSchema, Type::CollectionSort, Type::CollectionSplit, Type::CollectionSubitem, Type::ColorSpaceMap, Type::ColorantsDict, Type::CryptDecodeParms, Type::CryptFilter, Type::CryptFilterMap, Type::CryptFilterPublicKey, Type::CryptFilterPublicKeyMap, Type::DCTDecodeParms, Type::DPMMetadataStream, Type::DPart, Type::DPartRoot, Type::Data, Type::DestOutputProfile, Type::DestinationDict, Type::DestsMap, Type::DevExtensions, Type::DeviceN, Type::DeviceNMixingHints, Type::DeviceNProcess, Type::DictionaryOfDictionaries, Type::DictionaryOfFunctions, Type::DocMDPTransformParameters, Type::DocTimeStamp, Type::DocumentSecurityStore, Type::Dss, Type::EmbeddedFileParameter, Type::EncryptedPayload, Type::EncryptionPublicKey, Type::EncryptionStandard, Type::ExData3DMarkup, Type::ExDataMarkupGeo, Type::ExDataProjection, Type::Extensions, Type::FDDict, Type::Field, Type::FieldMDPTransformParameters, Type::FileSpecEF, Type::FileSpecRF, Type::FileSpecification, Type::FileTrailer, Type::FixedPrint, Type::FlateDecodeParms, Type::FloatingWindowParameters, Type::Font, Type::FontDescriptor, Type::FontEncoding, Type::Function, Type::GTSProcStepsGroup, Type::GeographicCoordinateSystem, Type::GeospatialMeasure, Type::GraphicsStateParameter, Type::GraphicsStateParameterMap, Type::GroupAttributes, Type::Halftone, Type::IconFit, Type::Info, Type::InteractiveForm, Type::InteriorColor, Type::JBIG2DecodeParms, Type::Lab, Type::LegalAttestation, Type::LineEndingStyling, Type::LinearizationParameterDict, Type::MDPDict, Type::Mac, Type::MarkInformation, Type::MarkedContentReference, Type::Measure, Type::MediaClipDataMHBE, Type::MediaClipSectionMHBE, Type::MediaCriteria, Type::MediaDuration, Type::MediaOffsetFrame, Type::MediaOffsetMarker, Type::MediaOffsetTime, Type::MediaPermissions, Type::MediaPlayParameters, Type::MediaPlayParametersMH, Type::MediaPlayerInfo, Type::MediaPlayers, Type::MediaScreenParameters, Type::MediaScreenParametersMHBE, Type::MicrosoftWindowsLaunchParam, Type::MinimumBitDepth, Type::MinimumScreenSize, Type::Movie, Type::MovieActivation, Type::Names, Type::Namespace, Type::NavNode, Type::Navigator, Type::NumberFormat, Type::OPIVersion13Dict, Type::OPIVersion20Dict, Type::ObjectReference, Type::OptContentCreatorInfo, Type::OptContentExport, Type::OptContentLanguage, Type::OptContentPageElement, Type::OptContentPrint, Type::OptContentUsage, Type::OptContentUsageApplication, Type::OptContentUser, Type::OptContentView, Type::OptContentZoom, Type::OptionalContentConfiguration, Type::OptionalContentGroup, Type::OptionalContentMembership, Type::OptionalContentProperties, Type::Outline, Type::OutlineItem, Type::OutputIntent, Type::OutputIntentsContainer, Type::Page, Type::PageLabel, Type::PagePieceInfo, Type::PageTreeNode, Type::PaperMetaData, Type::Pattern, Type::PatternMap, Type::Permissions, Type::PointData, Type::PrinterMarkSubDict, Type::ProjectedCoordinateSystem, Type::Projection, Type::Rendition, Type::RenditionBE, Type::RenditionMH, Type::RequirementsHandler, Type::Resources, Type::RichMediaActivation, Type::RichMediaAnimation, Type::RichMediaCommand, Type::RichMediaConfiguration, Type::RichMediaContent, Type::RichMediaCuePoint, Type::RichMediaDeactivation, Type::RichMediaHeight, Type::RichMediaInstance, Type::RichMediaParams, Type::RichMediaPosition, Type::RichMediaPresentation, Type::RichMediaSettings, Type::RichMediaWidth, Type::RichMediaWindow, Type::RoleMap, Type::RoleMapNS, Type::Separation, Type::Shading, Type::ShadingMap, Type::SigFieldLock, Type::SigFieldSeedValue, Type::Signature, Type::SignatureBuildDataAppDict, Type::SignatureBuildDataDict, Type::SignatureBuildDataSigQDict, Type::SignatureBuildPropDict, Type::SignatureReferenceDocMDP, Type::SignatureReferenceFieldMDP, Type::SignatureReferenceIdentity, Type::SignatureReferenceUR, Type::SlideShow, Type::SoftMaskAlpha, Type::SoftMaskLuminosity, Type::SoftwareIdentifier, Type::Solidities, Type::SourceInformation, Type::SpectralData, Type::StreamDict, Type::StructElem, Type::StructTreeRoot, Type::StructureAttributes, Type::StructureElementKid, Type::StructureReference, Type::StyleDict, Type::SubjectDN, Type::Target, Type::Thread, Type::ThreeDActivation, Type::ThreeDAnimationStyle, Type::ThreeDBackground, Type::ThreeDCrossSection, Type::ThreeDLightingScheme, Type::ThreeDMeasure, Type::ThreeDNode, Type::ThreeDReference, Type::ThreeDRenderMode, Type::ThreeDUnits, Type::ThreeDView, Type::ThreeDViewAddEntries, Type::TimeStampDict, Type::Timespan, Type::Transition, Type::TrapRegion, Type::URIDict, Type::URLAlias, Type::URTransformParameters, Type::UniversalDictionary, Type::UserProperty, Type::VRIMap, Type::ViewParams, Type::ViewerPreferences, Type::Viewport, Type::Vri, Type::WebCaptureCommand, Type::WebCaptureCommandSettings, Type::WebCaptureInfo, Type::XObjectMap

Instance Attribute Summary

Attributes inherited from Object

#document, #gen, #oid, #value

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ArlingtonBacked

arlington_object

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.



204
205
206
207
208
209
210
211
212
# File 'lib/pdfrb/model/cos/dictionary.rb', line 204

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

.apply_arlington_definition(definition) ⇒ Object

Merge the TSV's field definitions into the class's field set (ArlingtonBacked#arlington_object hook).



78
79
80
81
82
# File 'lib/pdfrb/model/cos/dictionary.rb', line 78

def apply_arlington_definition(definition)
  definition.each_field do |field_def|
    define_field_from_arlington(field_def)
  end
end

.arlington_default(field_def) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/pdfrb/model/cos/dictionary.rb', line 168

def arlington_default(field_def)
  raw = field_def.default_value
  return nil if raw.nil?
  return raw unless raw.is_a?(::String)

  if raw.start_with?("[") && raw.end_with?("]")
    return raw[1..-2].split.then { |tokens| tokens.map { |t| arlington_scalar(t) } }
  end

  scalar = arlington_scalar(raw)
  return scalar.to_sym if scalar.is_a?(::String) && field_def.types_raw.include?("name")

  scalar
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_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_scalar(token) ⇒ Object

TSV default cells are raw strings; convert per scalar shape so typed access returns Integer/Symbol/etc.



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

def arlington_scalar(token)
  case token
  when "true" then true
  when "false" then false
  when "null" then nil
  when /\A-?\d+\z/ then Integer(token, 10)
  when /\A-?\d+\.\d+\z/ then Float(token)
  else token
  end
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



198
199
200
# File 'lib/pdfrb/model/cos/dictionary.rb', line 198

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.



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

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

Merge Arlington field metadata with any hand-coded define_field declaration. Hand-coded fields win: if the class already declares this key explicitly (with a type), we rebuild the Field preserving the hand-coded type/default/required and only attach the Arlington def for reference.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/pdfrb/model/cos/dictionary.rb', line 96

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)
  existing = own_fields[key]
  if existing
    own_fields[key] = merged_field(existing, field_def)
    return
  end

  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. A subclass field with the same name shadows (replaces) the inherited one entirely.



67
68
69
70
71
72
73
74
# File 'lib/pdfrb/model/cos/dictionary.rb', line 67

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 unless own_fields.key?(n) }
  end
  own_fields.each { |n, f| yield n, f }
end

.field(name) ⇒ Object

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



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

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



39
40
41
# File 'lib/pdfrb/model/cos/dictionary.rb', line 39

def lookup_type(symbol)
  TYPE_MAP[symbol]
end

.merged_field(existing, field_def) ⇒ Object



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

def merged_field(existing, field_def)
  Fields::Field.new(
    existing.pdf_name,
    type: existing.type,
    required: existing.required,
    default: existing.default,
    indirect: existing.indirect,
    allowed_values: existing.allowed_values,
    version: arlington_version(field_def),
    arlington: field_def
  )
end

.own_fieldsObject



85
86
87
# File 'lib/pdfrb/model/cos/dictionary.rb', line 85

def own_fields
  @own_fields ||= {}
end

.register_type(symbol, klass = self) ⇒ Object



34
35
36
37
# File 'lib/pdfrb/model/cos/dictionary.rb', line 34

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.



30
31
32
# File 'lib/pdfrb/model/cos/dictionary.rb', line 30

def type_map
  TYPE_MAP
end

Instance Method Details

#[](name) ⇒ Object

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



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/pdfrb/model/cos/dictionary.rb', line 216

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)


237
238
239
240
241
# File 'lib/pdfrb/model/cos/dictionary.rb', line 237

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

  @value[name] = value
end

#delete(name) ⇒ Object



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

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.



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

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.



264
265
266
267
268
269
# File 'lib/pdfrb/model/cos/dictionary.rb', line 264

def each_raw
  return enum_for(:each_raw) unless block_given?

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

#empty?Boolean

Returns:

  • (Boolean)


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

def empty?; @value.empty?; end

#key?(name) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#keysObject



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

def keys; @value.keys; end

#pdf_typeObject



274
275
276
# File 'lib/pdfrb/model/cos/dictionary.rb', line 274

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.



280
281
282
283
284
285
286
# File 'lib/pdfrb/model/cos/dictionary.rb', line 280

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