lib/writeexcel/properties.rb in writeexcel-0.5.0 vs lib/writeexcel/properties.rb in writeexcel-0.6.0

- old
+ new

@@ -1,240 +1,242 @@ -# -*- coding: utf-8 -*- -############################################################################### -# -# Properties - A module for creating Excel property sets. -# -# -# Used in conjunction with WriteExcel -# -# Copyright 2000-2010, John McNamara. -# -# original written in Perl by John McNamara -# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp -# - -require 'date' - -############################################################################### -# -# create_summary_property_set(). -# -# Create the SummaryInformation property set. This is mainly used for the -# Title, Subject, Author, Keywords, Comments, Last author keywords and the -# creation date. -# -def create_summary_property_set(properties) #:nodoc: - byte_order = [0xFFFE].pack('v') - version = [0x0000].pack('v') - system_id = [0x00020105].pack('V') - class_id = ['00000000000000000000000000000000'].pack('H*') - num_property_sets = [0x0001].pack('V') - format_id = ['E0859FF2F94F6810AB9108002B27B3D9'].pack('H*') - offset = [0x0030].pack('V') - num_property = [properties.size].pack('V') - property_offsets = '' - - # Create the property set data block and calculate the offsets into it. - property_data, offsets = pack_property_data(properties) - - # Create the property type and offsets based on the previous calculation. - 0.upto(properties.size - 1) do |i| - property_offsets += [properties[i][0], offsets[i]].pack('VV') - end - - # Size of size (4 bytes) + num_property (4 bytes) + the data structures. - size = 8 + (property_offsets).bytesize + property_data.bytesize - size = [size].pack('V') - - byte_order + - version + - system_id + - class_id + - num_property_sets + - format_id + - offset + - size + - num_property + - property_offsets + - property_data -end - - -############################################################################### -# -# Create the DocSummaryInformation property set. This is mainly used for the -# Manager, Company and Category keywords. -# -# The DocSummary also contains a stream for user defined properties. However -# this is a little arcane and probably not worth the implementation effort. -# -def create_doc_summary_property_set(properties) #:nodoc: - byte_order = [0xFFFE].pack('v') - version = [0x0000].pack('v') - system_id = [0x00020105].pack('V') - class_id = ['00000000000000000000000000000000'].pack('H*') - num_property_sets = [0x0002].pack('V') - - format_id_0 = ['02D5CDD59C2E1B10939708002B2CF9AE'].pack('H*') - format_id_1 = ['05D5CDD59C2E1B10939708002B2CF9AE'].pack('H*') - offset_0 = [0x0044].pack('V') - num_property_0 = [properties.size].pack('V') - property_offsets_0 = '' - - # Create the property set data block and calculate the offsets into it. - property_data_0, offsets = pack_property_data(properties) - - # Create the property type and offsets based on the previous calculation. - 0.upto(properties.size-1) do |i| - property_offsets_0 += [properties[i][0], offsets[i]].pack('VV') - end - - # Size of size (4 bytes) + num_property (4 bytes) + the data structures. - data_len = 8 + (property_offsets_0).bytesize + property_data_0.bytesize - size_0 = [data_len].pack('V') - - # The second property set offset is at the end of the first property set. - offset_1 = [0x0044 + data_len].pack('V') - - # We will use a static property set stream rather than try to generate it. - property_data_1 = [%w( - 98 00 00 00 03 00 00 00 00 00 00 00 20 00 00 00 - 01 00 00 00 36 00 00 00 02 00 00 00 3E 00 00 00 - 01 00 00 00 02 00 00 00 0A 00 00 00 5F 50 49 44 - 5F 47 55 49 44 00 02 00 00 00 E4 04 00 00 41 00 - 00 00 4E 00 00 00 7B 00 31 00 36 00 43 00 34 00 - 42 00 38 00 33 00 42 00 2D 00 39 00 36 00 35 00 - 46 00 2D 00 34 00 42 00 32 00 31 00 2D 00 39 00 - 30 00 33 00 44 00 2D 00 39 00 31 00 30 00 46 00 - 41 00 44 00 46 00 41 00 37 00 30 00 31 00 42 00 - 7D 00 00 00 00 00 00 00 2D 00 39 00 30 00 33 00 - ).join('')].pack('H*') - - byte_order + - version + - system_id + - class_id + - num_property_sets + - format_id_0 + - offset_0 + - format_id_1 + - offset_1 + - size_0 + - num_property_0 + - property_offsets_0 + - property_data_0 + - property_data_1 -end - - -############################################################################### -# -# _pack_property_data(). -# -# Create a packed property set structure. Strings are null terminated and -# padded to a 4 byte boundary. We also use this function to keep track of the -# property offsets within the data structure. These offsets are used by the -# calling functions. Currently we only need to handle 4 property types: -# VT_I2, VT_LPSTR, VT_FILETIME. -# -def pack_property_data(properties, offset = 0) #:nodoc: - packed_property = '' - data = '' - offsets = [] - - # Get the strings codepage from the first property. - codepage = properties[0][2] - - # The properties start after 8 bytes for size + num_properties + 8 bytes - # for each propety type/offset pair. - offset += 8 * (properties.size + 1) - - properties.each do |property| - offsets.push(offset) - - property_type = property[1] - - if property_type == 'VT_I2' - packed_property = pack_VT_I2(property[2]) - elsif property_type == 'VT_LPSTR' - packed_property = pack_VT_LPSTR(property[2], codepage) - elsif property_type == 'VT_FILETIME' - packed_property = pack_VT_FILETIME(property[2]) - else - raise "Unknown property type: '#{property_type}'\n" - end - - offset += packed_property.bytesize - data += packed_property - end - - [data, offsets] -end - -############################################################################### -# -# _pack_VT_I2(). -# -# Pack an OLE property type: VT_I2, 16-bit signed integer. -# -def pack_VT_I2(value) #:nodoc: - type = 0x0002 - data = [type, value].pack('VV') -end - -############################################################################### -# -# _pack_VT_LPSTR(). -# -# Pack an OLE property type: VT_LPSTR, String in the Codepage encoding. -# The strings are null terminated and padded to a 4 byte boundary. -# -def pack_VT_LPSTR(str, codepage) #:nodoc: - type = 0x001E - string = str.force_encoding('BINARY') + "\0".encode('BINARY') - - if codepage == 0x04E4 - # Latin1 - length = string.bytesize - elsif codepage == 0xFDE9 - # utf8 - length = string.bytesize - else - raise "Unknown codepage: #{codepage}\n" - end - - # Pack the data. - data = [type, length].pack('VV') - data += string - - # The packed data has to null padded to a 4 byte boundary. - if (extra = length % 4) != 0 - data += "\0" * (4 - extra) - end - data -end - -############################################################################### -# -# _pack_VT_FILETIME(). -# -# Pack an OLE property type: VT_FILETIME. -# -def pack_VT_FILETIME(localtime) #:nodoc: - type = 0x0040 - - epoch = DateTime.new(1601, 1, 1) - - datetime = DateTime.new( - localtime.year, - localtime.mon, - localtime.mday, - localtime.hour, - localtime.min, - localtime.sec, - localtime.usec - ) - bignum = (datetime - epoch) * 86400 * 1e7.to_i - high, low = bignum.divmod 1 << 32 - - [type].pack('V') + [low, high].pack('V2') -end +# -*- coding: utf-8 -*- +############################################################################### +# +# Properties - A module for creating Excel property sets. +# +# +# Used in conjunction with WriteExcel +# +# Copyright 2000-2010, John McNamara. +# +# original written in Perl by John McNamara +# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp +# + +require 'date' + +############################################################################### +# +# create_summary_property_set(). +# +# Create the SummaryInformation property set. This is mainly used for the +# Title, Subject, Author, Keywords, Comments, Last author keywords and the +# creation date. +# +def create_summary_property_set(properties) #:nodoc: + byte_order = [0xFFFE].pack('v') + version = [0x0000].pack('v') + system_id = [0x00020105].pack('V') + class_id = ['00000000000000000000000000000000'].pack('H*') + num_property_sets = [0x0001].pack('V') + format_id = ['E0859FF2F94F6810AB9108002B27B3D9'].pack('H*') + offset = [0x0030].pack('V') + num_property = [properties.size].pack('V') + property_offsets = '' + + # Create the property set data block and calculate the offsets into it. + property_data, offsets = pack_property_data(properties) + + # Create the property type and offsets based on the previous calculation. + 0.upto(properties.size - 1) do |i| + property_offsets += [properties[i][0], offsets[i]].pack('VV') + end + + # Size of size (4 bytes) + num_property (4 bytes) + the data structures. + size = 8 + (property_offsets).bytesize + property_data.bytesize + size = [size].pack('V') + + byte_order + + version + + system_id + + class_id + + num_property_sets + + format_id + + offset + + size + + num_property + + property_offsets + + property_data +end + + +############################################################################### +# +# Create the DocSummaryInformation property set. This is mainly used for the +# Manager, Company and Category keywords. +# +# The DocSummary also contains a stream for user defined properties. However +# this is a little arcane and probably not worth the implementation effort. +# +def create_doc_summary_property_set(properties) #:nodoc: + byte_order = [0xFFFE].pack('v') + version = [0x0000].pack('v') + system_id = [0x00020105].pack('V') + class_id = ['00000000000000000000000000000000'].pack('H*') + num_property_sets = [0x0002].pack('V') + + format_id_0 = ['02D5CDD59C2E1B10939708002B2CF9AE'].pack('H*') + format_id_1 = ['05D5CDD59C2E1B10939708002B2CF9AE'].pack('H*') + offset_0 = [0x0044].pack('V') + num_property_0 = [properties.size].pack('V') + property_offsets_0 = '' + + # Create the property set data block and calculate the offsets into it. + property_data_0, offsets = pack_property_data(properties) + + # Create the property type and offsets based on the previous calculation. + 0.upto(properties.size-1) do |i| + property_offsets_0 += [properties[i][0], offsets[i]].pack('VV') + end + + # Size of size (4 bytes) + num_property (4 bytes) + the data structures. + data_len = 8 + (property_offsets_0).bytesize + property_data_0.bytesize + size_0 = [data_len].pack('V') + + # The second property set offset is at the end of the first property set. + offset_1 = [0x0044 + data_len].pack('V') + + # We will use a static property set stream rather than try to generate it. + property_data_1 = [%w( + 98 00 00 00 03 00 00 00 00 00 00 00 20 00 00 00 + 01 00 00 00 36 00 00 00 02 00 00 00 3E 00 00 00 + 01 00 00 00 02 00 00 00 0A 00 00 00 5F 50 49 44 + 5F 47 55 49 44 00 02 00 00 00 E4 04 00 00 41 00 + 00 00 4E 00 00 00 7B 00 31 00 36 00 43 00 34 00 + 42 00 38 00 33 00 42 00 2D 00 39 00 36 00 35 00 + 46 00 2D 00 34 00 42 00 32 00 31 00 2D 00 39 00 + 30 00 33 00 44 00 2D 00 39 00 31 00 30 00 46 00 + 41 00 44 00 46 00 41 00 37 00 30 00 31 00 42 00 + 7D 00 00 00 00 00 00 00 2D 00 39 00 30 00 33 00 + ).join('')].pack('H*') + + byte_order + + version + + system_id + + class_id + + num_property_sets + + format_id_0 + + offset_0 + + format_id_1 + + offset_1 + + size_0 + + num_property_0 + + property_offsets_0 + + property_data_0 + + property_data_1 +end + + +############################################################################### +# +# _pack_property_data(). +# +# Create a packed property set structure. Strings are null terminated and +# padded to a 4 byte boundary. We also use this function to keep track of the +# property offsets within the data structure. These offsets are used by the +# calling functions. Currently we only need to handle 4 property types: +# VT_I2, VT_LPSTR, VT_FILETIME. +# +def pack_property_data(properties, offset = 0) #:nodoc: + packed_property = '' + data = '' + offsets = [] + + # Get the strings codepage from the first property. + codepage = properties[0][2] + + # The properties start after 8 bytes for size + num_properties + 8 bytes + # for each propety type/offset pair. + offset += 8 * (properties.size + 1) + + properties.each do |property| + offsets.push(offset) + + property_type = property[1] + + if property_type == 'VT_I2' + packed_property = pack_VT_I2(property[2]) + elsif property_type == 'VT_LPSTR' + packed_property = pack_VT_LPSTR(property[2], codepage) + elsif property_type == 'VT_FILETIME' + packed_property = pack_VT_FILETIME(property[2]) + else + raise "Unknown property type: '#{property_type}'\n" + end + + offset += packed_property.bytesize + data += packed_property + end + + [data, offsets] +end + +############################################################################### +# +# _pack_VT_I2(). +# +# Pack an OLE property type: VT_I2, 16-bit signed integer. +# +def pack_VT_I2(value) #:nodoc: + type = 0x0002 + data = [type, value].pack('VV') +end + +############################################################################### +# +# _pack_VT_LPSTR(). +# +# Pack an OLE property type: VT_LPSTR, String in the Codepage encoding. +# The strings are null terminated and padded to a 4 byte boundary. +# +def pack_VT_LPSTR(str, codepage) #:nodoc: + type = 0x001E + string = + ruby_18 { "#{str}\0" } || + ruby_19 { str.force_encoding('BINARY') + "\0".encode('BINARY') } + + if codepage == 0x04E4 + # Latin1 + length = string.bytesize + elsif codepage == 0xFDE9 + # utf8 + length = string.bytesize + else + raise "Unknown codepage: #{codepage}\n" + end + + # Pack the data. + data = [type, length].pack('VV') + data += string + + # The packed data has to null padded to a 4 byte boundary. + if (extra = length % 4) != 0 + data += "\0" * (4 - extra) + end + data +end + +############################################################################### +# +# _pack_VT_FILETIME(). +# +# Pack an OLE property type: VT_FILETIME. +# +def pack_VT_FILETIME(localtime) #:nodoc: + type = 0x0040 + + epoch = DateTime.new(1601, 1, 1) + + datetime = DateTime.new( + localtime.year, + localtime.mon, + localtime.mday, + localtime.hour, + localtime.min, + localtime.sec, + localtime.usec + ) + bignum = (datetime - epoch) * 86400 * 1e7.to_i + high, low = bignum.divmod 1 << 32 + + [type].pack('V') + [low, high].pack('V2') +end