lib/writeexcel/properties.rb in writeexcel-0.1.0 vs lib/writeexcel/properties.rb in writeexcel-0.3.0
- old
+ new
@@ -1,250 +1,251 @@
-###############################################################################
-#
-# Properties - A module for creating Excel property sets.
-#
-#
-# Used in conjunction with Spreadsheet::WriteExcel
-#
-# Copyright 2000-2008, 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)
- 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 = 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).length + property_data.length
- size = [size].pack('V')
-
- return 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)
- 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 = 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).length + property_data_0.length
- 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*')
-
- return 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)
- 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.length
- data = data + packed_property
- end
-
- return [data, offsets]
-end
-
-###############################################################################
-#
-# _pack_VT_I2().
-#
-# Pack an OLE property type: VT_I2, 16-bit signed integer.
-#
-def pack_VT_I2(value)
- 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)
- type = 0x001E
- string = str + "\0"
-
- if codepage == 0x04E4
- # Latin1
- byte_string = string
- length = byte_string.length
- elsif codepage == 0xFDE9
- # UTF-8
- nonAscii = /[^!"#\$%&'\(\)\*\+,\-\.\/\:\;<=>\?@0-9A-Za-z_\[\\\]^` ~\0\n]/
- if string =~ nonAscii
- require 'jcode'
- byte_string = string
- length = byte_string.jlength
- else
- byte_string = string
- length = byte_string.length
- end
- else
- raise "Unknown codepage: codepage\n"
- end
-
- # Pack the data.
- data = [type, length].pack('VV')
- data = data + byte_string
-
- # The packed data has to null padded to a 4 byte boundary.
- if (extra = length % 4) != 0
- data = data + "\0" * (4 - extra)
- end
- return data
-end
-
-###############################################################################
-#
-# _pack_VT_FILETIME().
-#
-# Pack an OLE property type: VT_FILETIME.
-#
-def pack_VT_FILETIME(localtime)
- 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
+###############################################################################
+#
+# 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 = 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).length + property_data.length
+ size = [size].pack('V')
+
+ return 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 = 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).length + property_data_0.length
+ 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*')
+
+ return 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.length
+ data = data + packed_property
+ end
+
+ return [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 + "\0"
+
+ if codepage == 0x04E4
+ # Latin1
+ byte_string = string
+ length = byte_string.length
+ elsif codepage == 0xFDE9
+ # UTF-8
+ nonAscii = /[^!"#\$%&'\(\)\*\+,\-\.\/\:\;<=>\?@0-9A-Za-z_\[\\\]^` ~\0\n]/
+ if string =~ nonAscii
+# $KCODE = 'u'
+ require 'jcode'
+ byte_string = string
+ length = byte_string.jlength
+ else
+ byte_string = string
+ length = byte_string.length
+ end
+ else
+ raise "Unknown codepage: codepage\n"
+ end
+
+ # Pack the data.
+ data = [type, length].pack('VV')
+ data = data + byte_string
+
+ # The packed data has to null padded to a 4 byte boundary.
+ if (extra = length % 4) != 0
+ data = data + "\0" * (4 - extra)
+ end
+ return 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