Class: Ole::Types::PropertySet

Inherits:
Object
  • Object
show all
Includes:
Enumerable, Constants
Defined in:
lib/ole/types/property_set.rb

Overview

The PropertySet class currently supports readonly access to the properties serialized in “property set” streams, such as the file “005SummaryInformation”, in OLE files.

Think it has its roots in MFC property set serialization.

See poi.apache.org/hpsf/internals.html for details

Defined Under Namespace

Modules: Constants Classes: Section

Constant Summary collapse

HEADER_SIZE =
28
HEADER_PACK =
"vvVa#{Clsid::SIZE}V"
OS_MAP =
{
	0 => :win16,
	1 => :mac,
	2 => :win32,
	0x20001 => :ooffice, # open office on linux...
}
DATA =

define a smattering of the property set guids.

{
	Clsid.parse('{f29f85e0-4ff9-1068-ab91-08002b27b3d9}') => ['FMTID_SummaryInformation', {
		2  => 'doc_title',
		3  => 'doc_subject',
		4  => 'doc_author',
		5  => 'doc_keywords',
		6  => 'doc_comments',
		7  => 'doc_template',
		8  => 'doc_last_author',
		9  => 'doc_rev_number',
		10 => 'doc_edit_time',
		11 => 'doc_last_printed',
		12 => 'doc_created_time',
		13 => 'doc_last_saved_time',
		14 => 'doc_page_count',
		15 => 'doc_word_count',
		16 => 'doc_char_count',
		18 => 'doc_app_name',
		19 => 'security'
	}],
	Clsid.parse('{d5cdd502-2e9c-101b-9397-08002b2cf9ae}') => ['FMTID_DocSummaryInfo', {
		2  => 'doc_category',
		3  => 'doc_presentation_target',
		4  => 'doc_byte_count',
		5  => 'doc_line_count',
		6  => 'doc_para_count',
		7  => 'doc_slide_count',
		8  => 'doc_note_count',
		9  => 'doc_hidden_count',
		10 => 'mmclips',
		11 => 'scale_crop',
		12 => 'heading_pairs',
		13 => 'doc_part_titles',
		14 => 'doc_manager',
		15 => 'doc_company',
		16 => 'links_up_to_date'
	}],
	Clsid.parse('{d5cdd505-2e9c-101b-9397-08002b2cf9ae}') => ['FMTID_UserDefinedProperties', {}]
}
PROPERTY_MAP =

create an inverted map of names to guid/key pairs

DATA.inject({}) do |h1, (guid, data)|
	data[1].inject(h1) { |h2, (id, name)| h2.update name => [guid, id] }
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Enumerable

#group_by, #sum

Constructor Details

#initialize(io) ⇒ PropertySet

Returns a new instance of PropertySet.



142
143
144
145
146
147
148
# File 'lib/ole/types/property_set.rb', line 142

def initialize io
	@io = io
	load_header io.read(HEADER_SIZE)
	load_section_list io.read(@num_sections * Section::SIZE)
	# expect no gap between last section and start of data.
	#Log.warn "gap between section list and property data" unless io.pos == @sections.map(&:offset).min
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
# File 'lib/ole/types/property_set.rb', line 173

def method_missing name, *args, &block
	if name.to_s =~ /(.*)=$/
		return super unless args.length == 1
		return super unless PROPERTY_MAP[$1]
		self[$1] = args.first
	else
		return super unless args.length == 0
		return super unless PROPERTY_MAP[name.to_s]
		self[name]
	end
end

Instance Attribute Details

#guidObject (readonly)

Returns the value of attribute guid.



140
141
142
# File 'lib/ole/types/property_set.rb', line 140

def guid
  @guid
end

#ioObject (readonly)

Returns the value of attribute io.



140
141
142
# File 'lib/ole/types/property_set.rb', line 140

def io
  @io
end

#osObject (readonly)

Returns the value of attribute os.



140
141
142
# File 'lib/ole/types/property_set.rb', line 140

def os
  @os
end

#sectionsObject (readonly)

Returns the value of attribute sections.



140
141
142
# File 'lib/ole/types/property_set.rb', line 140

def sections
  @sections
end

#signatureObject (readonly)

Returns the value of attribute signature.



140
141
142
# File 'lib/ole/types/property_set.rb', line 140

def signature
  @signature
end

#unknownObject (readonly)

Returns the value of attribute unknown.



140
141
142
# File 'lib/ole/types/property_set.rb', line 140

def unknown
  @unknown
end

Instance Method Details

#[](key) ⇒ Object



161
162
163
164
165
# File 'lib/ole/types/property_set.rb', line 161

def [] key
	pair = PROPERTY_MAP[key.to_s] or return nil
	section = @sections.find { |s| s.guid == pair.first } or return nil
	section[pair.last]
end

#[]=(key, value) ⇒ Object



167
168
169
170
171
# File 'lib/ole/types/property_set.rb', line 167

def []= key, value
	pair = PROPERTY_MAP[key.to_s] or return nil
	section = @sections.find { |s| s.guid == pair.first } or return nil
	section[pair.last] = value
end

#eachObject



185
186
187
188
189
190
191
192
193
194
# File 'lib/ole/types/property_set.rb', line 185

def each
	@sections.each do |section|
		next unless pair = DATA[section.guid]
		map = pair.last
		section.each do |id, value|
			name = map[id] or next
			yield name, value
		end
	end
end

#load_header(str) ⇒ Object



150
151
152
153
154
155
# File 'lib/ole/types/property_set.rb', line 150

def load_header str
	@signature, @unknown, @os_id, @guid, @num_sections = str.unpack HEADER_PACK
	# should i check that unknown == 0? it usually is. so is the guid actually
	@guid = Clsid.load @guid
	@os = OS_MAP[@os_id] || Log.warn("unknown operating system id #{@os_id}")
end

#load_section_list(str) ⇒ Object



157
158
159
# File 'lib/ole/types/property_set.rb', line 157

def load_section_list str
	@sections = str.to_enum(:each_chunk, Section::SIZE).map { |s| Section.new s, self }
end

#to_hObject



196
197
198
# File 'lib/ole/types/property_set.rb', line 196

def to_h
	inject({}) { |hash, (name, value)| hash.update name.to_sym => value }
end