| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- # DISCLAIMER OF WARRANTY
- # Because this software is licensed free of charge, there is no warranty for the software,
- # to the extent permitted by applicable law. Except when otherwise stated in writing
- # the copyright holders and/or other parties provide the software "as is" without
- # warranty of any kind, either expressed or implied, including, but not limited to,
- # the implied warranties of merchantability and fitness for a particular purpose.
- # The entire risk as to the quality and performance of the software is with you.
- # Should the software prove defective, you assume the cost of all necessary
- # servicing, repair, or correction.
- # In no event unless required by applicable law or agreed to in writing will any
- # copyright holder, or any other party who may modify and/or redistribute the software
- # as permitted by the above licence, be liable to you for damages, including any general,
- # special, incidental, or consequential damages arising out of the use or inability
- # to use the software (including but not limited to loss of data or data being rendered
- # inaccurate or losses sustained by you or third parties or a failure of the software
- # to operate with any other software), even if such holder or other party
- # has been advised of the possibility of such damages.
- # AUTHOR
- # John McNamara jmcnamara@cpan.org
- # COPYRIGHT
- # Copyright MM-MMX, John McNamara.
- # All Rights Reserved. This module is free software. It may be used,
- # redistributed and/or modified under the terms of
- # the Artistic License(full text of the Artistic License http://dev.perl.org/licenses/artistic.html).
- package Spreadsheet::WriteExcel::Properties;
- ###############################################################################
- #
- # Properties - A module for creating Excel property sets.
- #
- #
- # Used in conjunction with Spreadsheet::WriteExcel
- #
- # Copyright 2000-2010, John McNamara.
- #
- # Documentation after __END__
- #
- use Exporter;
- use strict;
- use Carp;
- use POSIX 'fmod';
- use Time::Local 'timelocal';
- use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
- @ISA = qw(Exporter);
- $VERSION = '2.37';
- # Set up the exports.
- my @all_functions = qw(
- create_summary_property_set
- create_doc_summary_property_set
- _pack_property_data
- _pack_VT_I2
- _pack_VT_LPSTR
- _pack_VT_FILETIME
- );
- my @pps_summaries = qw(
- create_summary_property_set
- create_doc_summary_property_set
- );
- @EXPORT = ();
- @EXPORT_OK = (@all_functions);
- %EXPORT_TAGS = (testing => \@all_functions,
- property_sets => \@pps_summaries,
- );
- ###############################################################################
- #
- # 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.
- #
- sub create_summary_property_set {
- my @properties = @{$_[0]};
- my $byte_order = pack 'v', 0xFFFE;
- my $version = pack 'v', 0x0000;
- my $system_id = pack 'V', 0x00020105;
- my $class_id = pack 'H*', '00000000000000000000000000000000';
- my $num_property_sets = pack 'V', 0x0001;
- my $format_id = pack 'H*', 'E0859FF2F94F6810AB9108002B27B3D9';
- my $offset = pack 'V', 0x0030;
- my $num_property = pack 'V', scalar @properties;
- my $property_offsets = '';
- # Create the property set data block and calculate the offsets into it.
- my ($property_data, $offsets) = _pack_property_data(\@properties);
- # Create the property type and offsets based on the previous calculation.
- for my $i (0 .. @properties -1) {
- $property_offsets .= pack('VV', $properties[$i]->[0], $offsets->[$i]);
- }
- # Size of $size (4 bytes) + $num_property (4 bytes) + the data structures.
- my $size = 8 + length($property_offsets) + length($property_data);
- $size = pack 'V', $size;
- return $byte_order .
- $version .
- $system_id .
- $class_id .
- $num_property_sets .
- $format_id .
- $offset .
- $size .
- $num_property .
- $property_offsets .
- $property_data;
- }
- ###############################################################################
- #
- # 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.
- #
- sub create_doc_summary_property_set {
- my @properties = @{$_[0]};
- my $byte_order = pack 'v', 0xFFFE;
- my $version = pack 'v', 0x0000;
- my $system_id = pack 'V', 0x00020105;
- my $class_id = pack 'H*', '00000000000000000000000000000000';
- my $num_property_sets = pack 'V', 0x0002;
- my $format_id_0 = pack 'H*', '02D5CDD59C2E1B10939708002B2CF9AE';
- my $format_id_1 = pack 'H*', '05D5CDD59C2E1B10939708002B2CF9AE';
- my $offset_0 = pack 'V', 0x0044;
- my $num_property_0 = pack 'V', scalar @properties;
- my $property_offsets_0 = '';
- # Create the property set data block and calculate the offsets into it.
- my ($property_data_0, $offsets) = _pack_property_data(\@properties);
- # Create the property type and offsets based on the previous calculation.
- for my $i (0 .. @properties -1) {
- $property_offsets_0 .= pack('VV', $properties[$i]->[0], $offsets->[$i]);
- }
- # Size of $size (4 bytes) + $num_property (4 bytes) + the data structures.
- my $data_len = 8 + length($property_offsets_0) + length($property_data_0);
- my $size_0 = pack 'V', $data_len;
- # The second property set offset is at the end of the first property set.
- my $offset_1 = pack 'V', 0x0044 + $data_len;
- # We will use a static property set stream rather than try to generate it.
- my $property_data_1 = pack 'H*', join '', qw (
- 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
- );
- 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;
- }
- ###############################################################################
- #
- # _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.
- #
- sub _pack_property_data {
- my @properties = @{$_[0]};
- my $offset = $_[1] || 0;
- my $packed_property = '';
- my $data = '';
- my @offsets;
- # Get the strings codepage from the first property.
- my $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 + 1);
- for my $property (@properties) {
- push @offsets, $offset;
- my $property_type = $property->[1];
- if ($property_type eq 'VT_I2') {
- $packed_property = _pack_VT_I2($property->[2]);
- }
- elsif ($property_type eq 'VT_LPSTR') {
- $packed_property = _pack_VT_LPSTR($property->[2], $codepage);
- }
- elsif ($property_type eq 'VT_FILETIME') {
- $packed_property = _pack_VT_FILETIME($property->[2]);
- }
- else {
- croak "Unknown property type: $property_type\n";
- }
- $offset += length $packed_property;
- $data .= $packed_property;
- }
- return $data, \@offsets;
- }
- ###############################################################################
- #
- # _pack_VT_I2().
- #
- # Pack an OLE property type: VT_I2, 16-bit signed integer.
- #
- sub _pack_VT_I2 {
- my $type = 0x0002;
- my $value = $_[0];
- my $data = pack 'VV', $type, $value;
- return $data;
- }
- ###############################################################################
- #
- # _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.
- #
- sub _pack_VT_LPSTR {
- my $type = 0x001E;
- my $string = $_[0] . "\0";
- my $codepage = $_[1];
- my $length;
- my $byte_string;
- if ($codepage == 0x04E4) {
- # Latin1
- $byte_string = $string;
- $length = length $byte_string;
- }
- elsif ($codepage == 0xFDE9) {
- # UTF-8
- if ( $] > 5.008 ) {
- require Encode;
- if (Encode::is_utf8($string)) {
- $byte_string = Encode::encode_utf8($string);
- }
- else {
- $byte_string = $string;
- }
- }
- else {
- $byte_string = $string;
- }
- $length = length $byte_string;
- }
- else {
- croak "Unknown codepage: $codepage\n";
- }
- # Pack the data.
- my $data = pack 'VV', $type, $length;
- $data .= $byte_string;
- # The packed data has to null padded to a 4 byte boundary.
- if (my $extra = $length % 4) {
- $data .= "\0" x (4 - $extra);
- }
- return $data;
- }
- ###############################################################################
- #
- # _pack_VT_FILETIME().
- #
- # Pack an OLE property type: VT_FILETIME.
- #
- sub _pack_VT_FILETIME {
- my $type = 0x0040;
- my $localtime = $_[0];
- # Convert from localtime to seconds.
- my $seconds = Time::Local::timelocal(@{$localtime});
- # Add the number of seconds between the 1601 and 1970 epochs.
- $seconds += 11644473600;
- # The FILETIME seconds are in units of 100 nanoseconds.
- my $nanoseconds = $seconds * 1E7;
- # Pack the total nanoseconds into 64 bits.
- my $time_hi = int($nanoseconds / 2**32);
- my $time_lo = POSIX::fmod($nanoseconds, 2**32);
- my $data = pack 'VVV', $type, $time_lo, $time_hi;
- return $data;
- }
- 1;
- __END__
- =head1 NAME
- Properties - A module for creating Excel property sets.
- =head1 SYNOPSIS
- See the C<set_properties()> method in the Spreadsheet::WriteExcel documentation.
- =head1 DESCRIPTION
- This module is used in conjunction with Spreadsheet::WriteExcel.
- =head1 AUTHOR
- John McNamara jmcnamara@cpan.org
- =head1 COPYRIGHT
- © MM-MMX, John McNamara.
- All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.
|