Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,317 @@
package Spreadsheet::WriteExcel::BIFFwriter;
###############################################################################
#
# BIFFwriter - An abstract base class for Excel workbooks and worksheets.
#
#
# Used in conjunction with Spreadsheet::WriteExcel
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
use Exporter;
use strict;
use vars qw($VERSION @ISA);
@ISA = qw(Exporter);
$VERSION = '2.40';
###############################################################################
#
# Class data.
#
my $byte_order = '';
my $BIFF_version = 0x0600;
###############################################################################
#
# new()
#
# Constructor
#
sub new {
my $class = $_[0];
my $self = {
_byte_order => '',
_data => '',
_datasize => 0,
_limit => 8224,
_ignore_continue => 0,
};
bless $self, $class;
$self->_set_byte_order();
return $self;
}
###############################################################################
#
# _set_byte_order()
#
# Determine the byte order and store it as class data to avoid
# recalculating it for each call to new().
#
sub _set_byte_order {
my $self = shift;
if ($byte_order eq ''){
# Check if "pack" gives the required IEEE 64bit float
my $teststr = pack "d", 1.2345;
my @hexdata =(0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F);
my $number = pack "C8", @hexdata;
if ($number eq $teststr) {
$byte_order = 0; # Little Endian
}
elsif ($number eq reverse($teststr)){
$byte_order = 1; # Big Endian
}
else {
# Give up. I'll fix this in a later version.
croak ( "Required floating point format not supported " .
"on this platform. See the portability section " .
"of the documentation."
);
}
}
$self->{_byte_order} = $byte_order;
}
###############################################################################
#
# _prepend($data)
#
# General storage function
#
sub _prepend {
my $self = shift;
my $data = join('', @_);
$data = $self->_add_continue($data) if length($data) > $self->{_limit};
$self->{_data} = $data . $self->{_data};
$self->{_datasize} += length($data);
return $data;
}
###############################################################################
#
# _append($data)
#
# General storage function
#
sub _append {
my $self = shift;
my $data = join('', @_);
$data = $self->_add_continue($data) if length($data) > $self->{_limit};
$self->{_data} = $self->{_data} . $data;
$self->{_datasize} += length($data);
return $data;
}
###############################################################################
#
# _store_bof($type)
#
# $type = 0x0005, Workbook
# $type = 0x0010, Worksheet
# $type = 0x0020, Chart
#
# Writes Excel BOF record to indicate the beginning of a stream or
# sub-stream in the BIFF file.
#
sub _store_bof {
my $self = shift;
my $record = 0x0809; # Record identifier
my $length = 0x0010; # Number of bytes to follow
my $version = $BIFF_version;
my $type = $_[0];
# According to the SDK $build and $year should be set to zero.
# However, this throws a warning in Excel 5. So, use these
# magic numbers.
my $build = 0x0DBB;
my $year = 0x07CC;
my $bfh = 0x00000041;
my $sfo = 0x00000006;
my $header = pack("vv", $record, $length);
my $data = pack("vvvvVV", $version, $type, $build, $year, $bfh, $sfo);
$self->_prepend($header, $data);
}
###############################################################################
#
# _store_eof()
#
# Writes Excel EOF record to indicate the end of a BIFF stream.
#
sub _store_eof {
my $self = shift;
my $record = 0x000A; # Record identifier
my $length = 0x0000; # Number of bytes to follow
my $header = pack("vv", $record, $length);
$self->_append($header);
}
###############################################################################
#
# _add_continue()
#
# Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In
# Excel 97 the limit is 8228 bytes. Records that are longer than these limits
# must be split up into CONTINUE blocks.
#
# This function take a long BIFF record and inserts CONTINUE records as
# necessary.
#
# Some records have their own specialised Continue blocks so there is also an
# option to bypass this function.
#
sub _add_continue {
my $self = shift;
my $data = $_[0];
my $limit = $self->{_limit};
my $record = 0x003C; # Record identifier
my $header;
my $tmp;
# Skip this if another method handles the continue blocks.
return $data if $self->{_ignore_continue};
# The first 2080/8224 bytes remain intact. However, we have to change
# the length field of the record.
#
$tmp = substr($data, 0, $limit, "");
substr($tmp, 2, 2, pack("v", $limit-4));
# Strip out chunks of 2080/8224 bytes +4 for the header.
while (length($data) > $limit) {
$header = pack("vv", $record, $limit);
$tmp .= $header;
$tmp .= substr($data, 0, $limit, "");
}
# Mop up the last of the data
$header = pack("vv", $record, length($data));
$tmp .= $header;
$tmp .= $data;
return $tmp ;
}
###############################################################################
#
# _add_mso_generic()
#
# Create a mso structure that is part of an Escher drawing object. These are
# are used for images, comments and filters. This generic method is used by
# other methods to create specific mso records.
#
# Returns the packed record.
#
sub _add_mso_generic {
my $self = shift;
my $type = $_[0];
my $version = $_[1];
my $instance = $_[2];
my $data = $_[3];
my $length = defined $_[4] ? $_[4] : length($data);
# The header contains version and instance info packed into 2 bytes.
my $header = $version | ($instance << 4);
my $record = pack "vvV", $header, $type, $length;
$record .= $data;
return $record;
}
###############################################################################
#
# For debugging
#
sub _hexout {
my $self = shift;
print +(caller(1))[3], "\n";
my $data = join '', @_;
my @bytes = unpack("H*", $data) =~ /../g;
while (@bytes > 16) {
print join " ", splice @bytes, 0, 16;
print "\n";
}
print join " ", @bytes, "\n\n";
}
1;
__END__
=encoding latin1
=head1 NAME
BIFFwriter - An abstract base class for Excel workbooks and worksheets.
=head1 SYNOPSIS
See the documentation for Spreadsheet::WriteExcel
=head1 DESCRIPTION
This module is used in conjunction with Spreadsheet::WriteExcel.
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

View File

@@ -0,0 +1,94 @@
package Spreadsheet::WriteExcel::Big;
###############################################################################
#
# WriteExcel::Big
#
# Spreadsheet::WriteExcel - Write formatted text and numbers to a
# cross-platform Excel binary file.
#
# Copyright 2000-2010, John McNamara.
#
#
require Exporter;
use strict;
use Spreadsheet::WriteExcel::Workbook;
use vars qw($VERSION @ISA);
@ISA = qw(Spreadsheet::WriteExcel::Workbook Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
# Constructor. Thin wrapper for a Workbook object.
#
# This module is no longer required directly and its use is deprecated. See
# the Pod documentation below.
#
sub new {
my $class = shift;
my $self = Spreadsheet::WriteExcel::Workbook->new(@_);
# Check for file creation failures before re-blessing
bless $self, $class if defined $self;
return $self;
}
1;
__END__
=encoding latin1
=head1 NAME
Big - A class for creating Excel files > 7MB.
=head1 SYNOPSIS
Use of this module is deprecated. See below.
=head1 DESCRIPTION
The module was a sub-class of Spreadsheet::WriteExcel used for creating Excel files greater than 7MB. However, it is no longer required and is now deprecated.
As of version 2.17 Spreadsheet::WriteExcel can create files larger than 7MB directly if OLE::Storage_Lite is installed.
This module only exists for backwards compatibility. If your programs use ::Big you should convert them to use Spreadsheet::WritExcel directly.
=head1 REQUIREMENTS
L<OLE::Storage_Lite>.
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,194 @@
package Spreadsheet::WriteExcel::Chart::Area;
###############################################################################
#
# Area - A writer class for Excel Area charts.
#
# Used in conjunction with Spreadsheet::WriteExcel::Chart.
#
# See formatting note in Spreadsheet::WriteExcel::Chart.
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
require Exporter;
use strict;
use Spreadsheet::WriteExcel::Chart;
use vars qw($VERSION @ISA);
@ISA = qw(Spreadsheet::WriteExcel::Chart Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
#
sub new {
my $class = shift;
my $self = Spreadsheet::WriteExcel::Chart->new( @_ );
bless $self, $class;
return $self;
}
###############################################################################
#
# _store_chart_type()
#
# Implementation of the abstract method from the specific chart class.
#
# Write the AREA chart BIFF record. Defines a area chart type.
#
sub _store_chart_type {
my $self = shift;
my $record = 0x101A; # Record identifier.
my $length = 0x0002; # Number of bytes to follow.
my $grbit = 0x0001; # Option flags.
my $header = pack 'vv', $record, $length;
my $data = pack 'v', $grbit;
$self->_append( $header, $data );
}
1;
__END__
=head1 NAME
Area - A writer class for Excel Area charts.
=head1 SYNOPSIS
To create a simple Excel file with a Area chart using Spreadsheet::WriteExcel:
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart.xls' );
my $worksheet = $workbook->add_worksheet();
my $chart = $workbook->add_chart( type => 'area' );
# Configure the chart.
$chart->add_series(
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Add the worksheet data the chart refers to.
my $data = [
[ 'Category', 2, 3, 4, 5, 6, 7 ],
[ 'Value', 1, 4, 5, 2, 1, 5 ],
];
$worksheet->write( 'A1', $data );
__END__
=head1 DESCRIPTION
This module implements Area charts for L<Spreadsheet::WriteExcel>. The chart object is created via the Workbook C<add_chart()> method:
my $chart = $workbook->add_chart( type => 'area' );
Once the object is created it can be configured via the following methods that are common to all chart classes:
$chart->add_series();
$chart->set_x_axis();
$chart->set_y_axis();
$chart->set_title();
These methods are explained in detail in L<Spreadsheet::WriteExcel::Chart>. Class specific methods or settings, if any, are explained below.
=head1 Area Chart Methods
There aren't currently any area chart specific methods. See the TODO section of L<Spreadsheet::WriteExcel::Chart>.
=head1 EXAMPLE
Here is a complete example that demonstrates most of the available features when creating a chart.
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart_area.xls' );
my $worksheet = $workbook->add_worksheet();
my $bold = $workbook->add_format( bold => 1 );
# Add the worksheet data that the charts will refer to.
my $headings = [ 'Number', 'Sample 1', 'Sample 2' ];
my $data = [
[ 2, 3, 4, 5, 6, 7 ],
[ 1, 4, 5, 2, 1, 5 ],
[ 3, 6, 7, 5, 4, 3 ],
];
$worksheet->write( 'A1', $headings, $bold );
$worksheet->write( 'A2', $data );
# Create a new chart object. In this case an embedded chart.
my $chart = $workbook->add_chart( type => 'area', embedded => 1 );
# Configure the first series. (Sample 1)
$chart->add_series(
name => 'Sample 1',
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Configure the second series. (Sample 2)
$chart->add_series(
name => 'Sample 2',
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$C$2:$C$7',
);
# Add a chart title and some axis labels.
$chart->set_title ( name => 'Results of sample analysis' );
$chart->set_x_axis( name => 'Test number' );
$chart->set_y_axis( name => 'Sample length (cm)' );
# Insert the chart into the worksheet (with an offset).
$worksheet->insert_chart( 'D2', $chart, 25, 10 );
__END__
=begin html
<p>This will produce a chart that looks like this:</p>
<p><center><img src="http://homepage.eircom.net/~jmcnamara/perl/images/area1.jpg" width="527" height="320" alt="Chart example." /></center></p>
=end html
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

View File

@@ -0,0 +1,228 @@
package Spreadsheet::WriteExcel::Chart::Bar;
###############################################################################
#
# Bar - A writer class for Excel Bar charts.
#
# Used in conjunction with Spreadsheet::WriteExcel::Chart.
#
# See formatting note in Spreadsheet::WriteExcel::Chart.
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
require Exporter;
use strict;
use Spreadsheet::WriteExcel::Chart;
use vars qw($VERSION @ISA);
@ISA = qw(Spreadsheet::WriteExcel::Chart Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
#
sub new {
my $class = shift;
my $self = Spreadsheet::WriteExcel::Chart->new( @_ );
bless $self, $class;
# The axis positions are reversed for a bar chart so we change the config.
my $c = $self->{_config};
$c->{_x_axis_text} = [ 0x2D, 0x6D9, 0x5F, 0x1CC, 0x281, 0x0, 90 ];
$c->{_x_axis_text_pos} = [ 2, 2, 0, 0, 0x17, 0x2A ];
$c->{_y_axis_text} = [ 0x078A, 0x0DFC, 0x011D, 0x9C, 0x0081, 0x0000 ];
$c->{_y_axis_text_pos} = [ 2, 2, 0, 0, 0x45, 0x17 ];
return $self;
}
###############################################################################
#
# _store_chart_type()
#
# Implementation of the abstract method from the specific chart class.
#
# Write the BAR chart BIFF record. Defines a bar or column chart type.
#
sub _store_chart_type {
my $self = shift;
my $record = 0x1017; # Record identifier.
my $length = 0x0006; # Number of bytes to follow.
my $pcOverlap = 0x0000; # Space between bars.
my $pcGap = 0x0096; # Space between cats.
my $grbit = 0x0001; # Option flags.
my $header = pack 'vv', $record, $length;
my $data = '';
$data .= pack 'v', $pcOverlap;
$data .= pack 'v', $pcGap;
$data .= pack 'v', $grbit;
$self->_append( $header, $data );
}
###############################################################################
#
# _set_embedded_config_data()
#
# Override some of the default configuration data for an embedded chart.
#
sub _set_embedded_config_data {
my $self = shift;
# Set the parent configuration first.
$self->SUPER::_set_embedded_config_data();
# The axis positions are reversed for a bar chart so we change the config.
my $c = $self->{_config};
$c->{_x_axis_text} = [ 0x57, 0x5BC, 0xB5, 0x214, 0x281, 0x0, 90 ];
$c->{_x_axis_text_pos} = [ 2, 2, 0, 0, 0x17, 0x2A ];
$c->{_y_axis_text} = [ 0x074A, 0x0C8F, 0x021F, 0x123, 0x81, 0x0000 ];
$c->{_y_axis_text_pos} = [ 2, 2, 0, 0, 0x45, 0x17 ];
}
1;
__END__
=head1 NAME
Bar - A writer class for Excel Bar charts.
=head1 SYNOPSIS
To create a simple Excel file with a Bar chart using Spreadsheet::WriteExcel:
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart.xls' );
my $worksheet = $workbook->add_worksheet();
my $chart = $workbook->add_chart( type => 'bar' );
# Configure the chart.
$chart->add_series(
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Add the worksheet data the chart refers to.
my $data = [
[ 'Category', 2, 3, 4, 5, 6, 7 ],
[ 'Value', 1, 4, 5, 2, 1, 5 ],
];
$worksheet->write( 'A1', $data );
__END__
=head1 DESCRIPTION
This module implements Bar charts for L<Spreadsheet::WriteExcel>. The chart object is created via the Workbook C<add_chart()> method:
my $chart = $workbook->add_chart( type => 'bar' );
Once the object is created it can be configured via the following methods that are common to all chart classes:
$chart->add_series();
$chart->set_x_axis();
$chart->set_y_axis();
$chart->set_title();
These methods are explained in detail in L<Spreadsheet::WriteExcel::Chart>. Class specific methods or settings, if any, are explained below.
=head1 Bar Chart Methods
There aren't currently any bar chart specific methods. See the TODO section of L<Spreadsheet::WriteExcel::Chart>.
=head1 EXAMPLE
Here is a complete example that demonstrates most of the available features when creating a chart.
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart_bar.xls' );
my $worksheet = $workbook->add_worksheet();
my $bold = $workbook->add_format( bold => 1 );
# Add the worksheet data that the charts will refer to.
my $headings = [ 'Number', 'Sample 1', 'Sample 2' ];
my $data = [
[ 2, 3, 4, 5, 6, 7 ],
[ 1, 4, 5, 2, 1, 5 ],
[ 3, 6, 7, 5, 4, 3 ],
];
$worksheet->write( 'A1', $headings, $bold );
$worksheet->write( 'A2', $data );
# Create a new chart object. In this case an embedded chart.
my $chart = $workbook->add_chart( type => 'bar', embedded => 1 );
# Configure the first series. (Sample 1)
$chart->add_series(
name => 'Sample 1',
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Configure the second series. (Sample 2)
$chart->add_series(
name => 'Sample 2',
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$C$2:$C$7',
);
# Add a chart title and some axis labels.
$chart->set_title ( name => 'Results of sample analysis' );
$chart->set_x_axis( name => 'Test number' );
$chart->set_y_axis( name => 'Sample length (cm)' );
# Insert the chart into the worksheet (with an offset).
$worksheet->insert_chart( 'D2', $chart, 25, 10 );
__END__
=begin html
<p>This will produce a chart that looks like this:</p>
<p><center><img src="http://homepage.eircom.net/~jmcnamara/perl/images/bar1.jpg" width="527" height="320" alt="Chart example." /></center></p>
=end html
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

View File

@@ -0,0 +1,199 @@
package Spreadsheet::WriteExcel::Chart::Column;
###############################################################################
#
# Column - A writer class for Excel Column charts.
#
# Used in conjunction with Spreadsheet::WriteExcel::Chart.
#
# See formatting note in Spreadsheet::WriteExcel::Chart.
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
require Exporter;
use strict;
use Spreadsheet::WriteExcel::Chart;
use vars qw($VERSION @ISA);
@ISA = qw(Spreadsheet::WriteExcel::Chart Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
#
sub new {
my $class = shift;
my $self = Spreadsheet::WriteExcel::Chart->new( @_ );
bless $self, $class;
return $self;
}
###############################################################################
#
# _store_chart_type()
#
# Implementation of the abstract method from the specific chart class.
#
# Write the BAR chart BIFF record. Defines a bar or column chart type.
#
sub _store_chart_type {
my $self = shift;
my $record = 0x1017; # Record identifier.
my $length = 0x0006; # Number of bytes to follow.
my $pcOverlap = 0x0000; # Space between bars.
my $pcGap = 0x0096; # Space between cats.
my $grbit = 0x0000; # Option flags.
my $header = pack 'vv', $record, $length;
my $data = '';
$data .= pack 'v', $pcOverlap;
$data .= pack 'v', $pcGap;
$data .= pack 'v', $grbit;
$self->_append( $header, $data );
}
1;
__END__
=head1 NAME
Column - A writer class for Excel Column charts.
=head1 SYNOPSIS
To create a simple Excel file with a Column chart using Spreadsheet::WriteExcel:
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart.xls' );
my $worksheet = $workbook->add_worksheet();
my $chart = $workbook->add_chart( type => 'column' );
# Configure the chart.
$chart->add_series(
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Add the worksheet data the chart refers to.
my $data = [
[ 'Category', 2, 3, 4, 5, 6, 7 ],
[ 'Value', 1, 4, 5, 2, 1, 5 ],
];
$worksheet->write( 'A1', $data );
__END__
=head1 DESCRIPTION
This module implements Column charts for L<Spreadsheet::WriteExcel>. The chart object is created via the Workbook C<add_chart()> method:
my $chart = $workbook->add_chart( type => 'column' );
Once the object is created it can be configured via the following methods that are common to all chart classes:
$chart->add_series();
$chart->set_x_axis();
$chart->set_y_axis();
$chart->set_title();
These methods are explained in detail in L<Spreadsheet::WriteExcel::Chart>. Class specific methods or settings, if any, are explained below.
=head1 Column Chart Methods
There aren't currently any column chart specific methods. See the TODO section of L<Spreadsheet::WriteExcel::Chart>.
=head1 EXAMPLE
Here is a complete example that demonstrates most of the available features when creating a chart.
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart_column.xls' );
my $worksheet = $workbook->add_worksheet();
my $bold = $workbook->add_format( bold => 1 );
# Add the worksheet data that the charts will refer to.
my $headings = [ 'Number', 'Sample 1', 'Sample 2' ];
my $data = [
[ 2, 3, 4, 5, 6, 7 ],
[ 1, 4, 5, 2, 1, 5 ],
[ 3, 6, 7, 5, 4, 3 ],
];
$worksheet->write( 'A1', $headings, $bold );
$worksheet->write( 'A2', $data );
# Create a new chart object. In this case an embedded chart.
my $chart = $workbook->add_chart( type => 'column', embedded => 1 );
# Configure the first series. (Sample 1)
$chart->add_series(
name => 'Sample 1',
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Configure the second series. (Sample 2)
$chart->add_series(
name => 'Sample 2',
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$C$2:$C$7',
);
# Add a chart title and some axis labels.
$chart->set_title ( name => 'Results of sample analysis' );
$chart->set_x_axis( name => 'Test number' );
$chart->set_y_axis( name => 'Sample length (cm)' );
# Insert the chart into the worksheet (with an offset).
$worksheet->insert_chart( 'D2', $chart, 25, 10 );
__END__
=begin html
<p>This will produce a chart that looks like this:</p>
<p><center><img src="http://homepage.eircom.net/~jmcnamara/perl/images/column1.jpg" width="527" height="320" alt="Chart example." /></center></p>
=end html
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

View File

@@ -0,0 +1,119 @@
package Spreadsheet::WriteExcel::Chart::External;
###############################################################################
#
# External - A writer class for Excel external charts.
#
# Used in conjunction with Spreadsheet::WriteExcel
#
# perltidy with options: -mbl=2 -pt=0 -nola
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
require Exporter;
use strict;
use Spreadsheet::WriteExcel::Chart;
use vars qw($VERSION @ISA);
@ISA = qw(Spreadsheet::WriteExcel::Chart Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
#
sub new {
my $class = shift;
my $external_filename = shift;
my $self = Spreadsheet::WriteExcel::Chart->new( @_ );
$self->{_filename} = $external_filename;
$self->{_external_bin} = 1;
bless $self, $class;
$self->_initialize(); # Requires overridden initialize().
return $self;
}
###############################################################################
#
# _initialize()
#
# Read all the data into memory for the external binary style chart.
#
#
sub _initialize {
my $self = shift;
my $filename = $self->{_filename};
my $filehandle = FileHandle->new( $filename )
or die "Couldn't open $filename in add_chart_ext(): $!.\n";
binmode( $filehandle );
$self->{_filehandle} = $filehandle;
$self->{_datasize} = -s $filehandle;
$self->{_using_tmpfile} = 0;
# Read the entire external chart binary into the data buffer.
# This will be retrieved by _get_data() when the chart is closed().
read( $self->{_filehandle}, $self->{_data}, $self->{_datasize} );
}
###############################################################################
#
# _close()
#
# We don't need to create or store Chart data structures when using an
# external binary, so we have a default close method.
#
sub _close {
my $self = shift;
return undef;
}
1;
__END__
=head1 NAME
External - A writer class for Excel external charts.
=head1 SYNOPSIS
This module is used to include external charts in Spreadsheet::WriteExcel.
=head1 DESCRIPTION
This module is used to include external charts in L<Spreadsheet::WriteExcel>. It is an internal module and isn't used directly by the end user.
It is semi-deprecated in favour of using "native" charts. See L<Spreadsheet::WriteExcel::Chart>.
For information on how to used external charts see the C<external_charts.txt> (or C<.pod>) in the C<external_charts> directory of the distro.
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

View File

@@ -0,0 +1,194 @@
package Spreadsheet::WriteExcel::Chart::Line;
###############################################################################
#
# Line - A writer class for Excel Line charts.
#
# Used in conjunction with Spreadsheet::WriteExcel::Chart.
#
# See formatting note in Spreadsheet::WriteExcel::Chart.
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
require Exporter;
use strict;
use Spreadsheet::WriteExcel::Chart;
use vars qw($VERSION @ISA);
@ISA = qw(Spreadsheet::WriteExcel::Chart Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
#
sub new {
my $class = shift;
my $self = Spreadsheet::WriteExcel::Chart->new( @_ );
bless $self, $class;
return $self;
}
###############################################################################
#
# _store_chart_type()
#
# Implementation of the abstract method from the specific chart class.
#
# Write the LINE chart BIFF record. Defines a line chart type.
#
sub _store_chart_type {
my $self = shift;
my $record = 0x1018; # Record identifier.
my $length = 0x0002; # Number of bytes to follow.
my $grbit = 0x0000; # Option flags.
my $header = pack 'vv', $record, $length;
my $data = pack 'v', $grbit;
$self->_append( $header, $data );
}
1;
__END__
=head1 NAME
Line - A writer class for Excel Line charts.
=head1 SYNOPSIS
To create a simple Excel file with a Line chart using Spreadsheet::WriteExcel:
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart.xls' );
my $worksheet = $workbook->add_worksheet();
my $chart = $workbook->add_chart( type => 'line' );
# Configure the chart.
$chart->add_series(
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Add the worksheet data the chart refers to.
my $data = [
[ 'Category', 2, 3, 4, 5, 6, 7 ],
[ 'Value', 1, 4, 5, 2, 1, 5 ],
];
$worksheet->write( 'A1', $data );
__END__
=head1 DESCRIPTION
This module implements Line charts for L<Spreadsheet::WriteExcel>. The chart object is created via the Workbook C<add_chart()> method:
my $chart = $workbook->add_chart( type => 'line' );
Once the object is created it can be configured via the following methods that are common to all chart classes:
$chart->add_series();
$chart->set_x_axis();
$chart->set_y_axis();
$chart->set_title();
These methods are explained in detail in L<Spreadsheet::WriteExcel::Chart>. Class specific methods or settings, if any, are explained below.
=head1 Line Chart Methods
There aren't currently any line chart specific methods. See the TODO section of L<Spreadsheet::WriteExcel::Chart>.
=head1 EXAMPLE
Here is a complete example that demonstrates most of the available features when creating a chart.
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart_line.xls' );
my $worksheet = $workbook->add_worksheet();
my $bold = $workbook->add_format( bold => 1 );
# Add the worksheet data that the charts will refer to.
my $headings = [ 'Number', 'Sample 1', 'Sample 2' ];
my $data = [
[ 2, 3, 4, 5, 6, 7 ],
[ 1, 4, 5, 2, 1, 5 ],
[ 3, 6, 7, 5, 4, 3 ],
];
$worksheet->write( 'A1', $headings, $bold );
$worksheet->write( 'A2', $data );
# Create a new chart object. In this case an embedded chart.
my $chart = $workbook->add_chart( type => 'line', embedded => 1 );
# Configure the first series. (Sample 1)
$chart->add_series(
name => 'Sample 1',
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Configure the second series. (Sample 2)
$chart->add_series(
name => 'Sample 2',
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$C$2:$C$7',
);
# Add a chart title and some axis labels.
$chart->set_title ( name => 'Results of sample analysis' );
$chart->set_x_axis( name => 'Test number' );
$chart->set_y_axis( name => 'Sample length (cm)' );
# Insert the chart into the worksheet (with an offset).
$worksheet->insert_chart( 'D2', $chart, 25, 10 );
__END__
=begin html
<p>This will produce a chart that looks like this:</p>
<p><center><img src="http://homepage.eircom.net/~jmcnamara/perl/images/line1.jpg" width="527" height="320" alt="Chart example." /></center></p>
=end html
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

View File

@@ -0,0 +1,217 @@
package Spreadsheet::WriteExcel::Chart::Pie;
###############################################################################
#
# Pie - A writer class for Excel Pie charts.
#
# Used in conjunction with Spreadsheet::WriteExcel::Chart.
#
# See formatting note in Spreadsheet::WriteExcel::Chart.
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
require Exporter;
use strict;
use Spreadsheet::WriteExcel::Chart;
use vars qw($VERSION @ISA);
@ISA = qw(Spreadsheet::WriteExcel::Chart Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
#
sub new {
my $class = shift;
my $self = Spreadsheet::WriteExcel::Chart->new( @_ );
$self->{_vary_data_color} = 1;
bless $self, $class;
return $self;
}
###############################################################################
#
# _store_chart_type()
#
# Implementation of the abstract method from the specific chart class.
#
# Write the Pie chart BIFF record.
#
sub _store_chart_type {
my $self = shift;
my $record = 0x1019; # Record identifier.
my $length = 0x0006; # Number of bytes to follow.
my $angle = 0x0000; # Angle.
my $donut = 0x0000; # Donut hole size.
my $grbit = 0x0002; # Option flags.
my $header = pack 'vv', $record, $length;
my $data = '';
$data .= pack 'v', $angle;
$data .= pack 'v', $donut;
$data .= pack 'v', $grbit;
$self->_append( $header, $data );
}
###############################################################################
#
# _store_axisparent_stream(). Overridden.
#
# Write the AXISPARENT chart substream.
#
# A Pie chart has no X or Y axis so we override this method to remove them.
#
sub _store_axisparent_stream {
my $self = shift;
$self->_store_axisparent( @{ $self->{_config}->{_axisparent} } );
$self->_store_begin();
$self->_store_pos( @{ $self->{_config}->{_axisparent_pos} } );
$self->_store_chartformat_stream();
$self->_store_end();
}
1;
__END__
=head1 NAME
Pie - A writer class for Excel Pie charts.
=head1 SYNOPSIS
To create a simple Excel file with a Pie chart using Spreadsheet::WriteExcel:
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart.xls' );
my $worksheet = $workbook->add_worksheet();
my $chart = $workbook->add_chart( type => 'pie' );
# Configure the chart.
$chart->add_series(
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Add the worksheet data the chart refers to.
my $data = [
[ 'Category', 2, 3, 4, 5, 6, 7 ],
[ 'Value', 1, 4, 5, 2, 1, 5 ],
];
$worksheet->write( 'A1', $data );
__END__
=head1 DESCRIPTION
This module implements Pie charts for L<Spreadsheet::WriteExcel>. The chart object is created via the Workbook C<add_chart()> method:
my $chart = $workbook->add_chart( type => 'pie' );
Once the object is created it can be configured via the following methods that are common to all chart classes:
$chart->add_series();
$chart->set_title();
These methods are explained in detail in L<Spreadsheet::WriteExcel::Chart>. Class specific methods or settings, if any, are explained below.
=head1 Pie Chart Methods
There aren't currently any pie chart specific methods. See the TODO section of L<Spreadsheet::WriteExcel::Chart>.
A Pie chart doesn't have an X or Y axis so the following common chart methods are ignored.
$chart->set_x_axis();
$chart->set_y_axis();
=head1 EXAMPLE
Here is a complete example that demonstrates most of the available features when creating a chart.
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart_pie.xls' );
my $worksheet = $workbook->add_worksheet();
my $bold = $workbook->add_format( bold => 1 );
# Add the worksheet data that the charts will refer to.
my $headings = [ 'Category', 'Values' ];
my $data = [
[ 'Apple', 'Cherry', 'Pecan' ],
[ 60, 30, 10 ],
];
$worksheet->write( 'A1', $headings, $bold );
$worksheet->write( 'A2', $data );
# Create a new chart object. In this case an embedded chart.
my $chart = $workbook->add_chart( type => 'pie', embedded => 1 );
# Configure the series.
$chart->add_series(
name => 'Pie sales data',
categories => '=Sheet1!$A$2:$A$4',
values => '=Sheet1!$B$2:$B$4',
);
# Add a title.
$chart->set_title( name => 'Popular Pie Types' );
# Insert the chart into the worksheet (with an offset).
$worksheet->insert_chart( 'C2', $chart, 25, 10 );
__END__
=begin html
<p>This will produce a chart that looks like this:</p>
<p><center><img src="http://homepage.eircom.net/~jmcnamara/perl/images/pie1.jpg" width="527" height="320" alt="Chart example." /></center></p>
=end html
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

View File

@@ -0,0 +1,245 @@
package Spreadsheet::WriteExcel::Chart::Scatter;
###############################################################################
#
# Scatter - A writer class for Excel Scatter charts.
#
# Used in conjunction with Spreadsheet::WriteExcel::Chart.
#
# See formatting note in Spreadsheet::WriteExcel::Chart.
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
require Exporter;
use strict;
use Spreadsheet::WriteExcel::Chart;
use vars qw($VERSION @ISA);
@ISA = qw(Spreadsheet::WriteExcel::Chart Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
#
sub new {
my $class = shift;
my $self = Spreadsheet::WriteExcel::Chart->new( @_ );
bless $self, $class;
return $self;
}
###############################################################################
#
# _store_chart_type()
#
# Implementation of the abstract method from the specific chart class.
#
# Write the SCATTER chart BIFF record. Defines a scatter chart type.
#
sub _store_chart_type {
my $self = shift;
my $record = 0x101B; # Record identifier.
my $length = 0x0006; # Number of bytes to follow.
my $bubble_ratio = 0x0064; # Bubble ratio.
my $bubble_type = 0x0001; # Bubble type.
my $grbit = 0x0000; # Option flags.
my $header = pack 'vv', $record, $length;
my $data = '';
$data .= pack 'v', $bubble_ratio;
$data .= pack 'v', $bubble_type;
$data .= pack 'v', $grbit;
$self->_append( $header, $data );
}
###############################################################################
#
# _store_axis_category_stream(). Overridden.
#
# Write the AXIS chart substream for the chart category.
#
# For a Scatter chart the category stream is replace with a values stream. We
# override this method and turn it into a values stream.
#
sub _store_axis_category_stream {
my $self = shift;
$self->_store_axis( 0 );
$self->_store_begin();
$self->_store_valuerange();
$self->_store_tick();
$self->_store_end();
}
###############################################################################
#
# _store_marker_dataformat_stream(). Overridden.
#
# This is an implementation of the parent abstract method to define
# properties of markers, linetypes, pie formats and other.
#
sub _store_marker_dataformat_stream {
my $self = shift;
$self->_store_dataformat( 0x0000, 0xFFFD, 0x0000 );
$self->_store_begin();
$self->_store_3dbarshape();
$self->_store_lineformat( 0x00000000, 0x0005, 0xFFFF, 0x0008, 0x004D );
$self->_store_areaformat( 0x00FFFFFF, 0x0000, 0x01, 0x01, 0x4E, 0x4D );
$self->_store_pieformat();
$self->_store_markerformat( 0x00, 0x00, 0x02, 0x01, 0x4D, 0x4D, 0x3C );
$self->_store_end();
}
1;
__END__
=head1 NAME
Scatter - A writer class for Excel Scatter charts.
=head1 SYNOPSIS
To create a simple Excel file with a Scatter chart using Spreadsheet::WriteExcel:
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart.xls' );
my $worksheet = $workbook->add_worksheet();
my $chart = $workbook->add_chart( type => 'scatter' );
# Configure the chart.
$chart->add_series(
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Add the worksheet data the chart refers to.
my $data = [
[ 'Category', 2, 3, 4, 5, 6, 7 ],
[ 'Value', 1, 4, 5, 2, 1, 5 ],
];
$worksheet->write( 'A1', $data );
__END__
=head1 DESCRIPTION
This module implements Scatter charts for L<Spreadsheet::WriteExcel>. The chart object is created via the Workbook C<add_chart()> method:
my $chart = $workbook->add_chart( type => 'scatter' );
Once the object is created it can be configured via the following methods that are common to all chart classes:
$chart->add_series();
$chart->set_x_axis();
$chart->set_y_axis();
$chart->set_title();
These methods are explained in detail in L<Spreadsheet::WriteExcel::Chart>. Class specific methods or settings, if any, are explained below.
=head1 Scatter Chart Methods
There aren't currently any scatter chart specific methods. See the TODO section of L<Spreadsheet::WriteExcel::Chart>.
=head1 EXAMPLE
Here is a complete example that demonstrates most of the available features when creating a chart.
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart_scatter.xls' );
my $worksheet = $workbook->add_worksheet();
my $bold = $workbook->add_format( bold => 1 );
# Add the worksheet data that the charts will refer to.
my $headings = [ 'Number', 'Sample 1', 'Sample 2' ];
my $data = [
[ 2, 3, 4, 5, 6, 7 ],
[ 1, 4, 5, 2, 1, 5 ],
[ 3, 6, 7, 5, 4, 3 ],
];
$worksheet->write( 'A1', $headings, $bold );
$worksheet->write( 'A2', $data );
# Create a new chart object. In this case an embedded chart.
my $chart = $workbook->add_chart( type => 'scatter', embedded => 1 );
# Configure the first series. (Sample 1)
$chart->add_series(
name => 'Sample 1',
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$B$2:$B$7',
);
# Configure the second series. (Sample 2)
$chart->add_series(
name => 'Sample 2',
categories => '=Sheet1!$A$2:$A$7',
values => '=Sheet1!$C$2:$C$7',
);
# Add a chart title and some axis labels.
$chart->set_title ( name => 'Results of sample analysis' );
$chart->set_x_axis( name => 'Test number' );
$chart->set_y_axis( name => 'Sample length (cm)' );
# Insert the chart into the worksheet (with an offset).
$worksheet->insert_chart( 'D2', $chart, 25, 10 );
__END__
=begin html
<p>This will produce a chart that looks like this:</p>
<p><center><img src="http://homepage.eircom.net/~jmcnamara/perl/images/scatter1.jpg" width="527" height="320" alt="Chart example." /></center></p>
=end html
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

View File

@@ -0,0 +1,257 @@
package Spreadsheet::WriteExcel::Chart::Stock;
###############################################################################
#
# Stock - A writer class for Excel Stock charts.
#
# Used in conjunction with Spreadsheet::WriteExcel::Chart.
#
# See formatting note in Spreadsheet::WriteExcel::Chart.
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
require Exporter;
use strict;
use Spreadsheet::WriteExcel::Chart;
use vars qw($VERSION @ISA);
@ISA = qw(Spreadsheet::WriteExcel::Chart Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
#
sub new {
my $class = shift;
my $self = Spreadsheet::WriteExcel::Chart->new( @_ );
bless $self, $class;
return $self;
}
###############################################################################
#
# _store_chart_type()
#
# Implementation of the abstract method from the specific chart class.
#
# Write the LINE chart BIFF record. A stock chart uses the same LINE record
# as a line chart but with additional DROPBAR and CHARTLINE records to define
# the stock style.
#
sub _store_chart_type {
my $self = shift;
my $record = 0x1018; # Record identifier.
my $length = 0x0002; # Number of bytes to follow.
my $grbit = 0x0000; # Option flags.
my $header = pack 'vv', $record, $length;
my $data = pack 'v', $grbit;
$self->_append( $header, $data );
}
###############################################################################
#
# _store_marker_dataformat_stream(). Overridden.
#
# This is an implementation of the parent abstract method to define
# properties of markers, linetypes, pie formats and other.
#
sub _store_marker_dataformat_stream {
my $self = shift;
$self->_store_dropbar();
$self->_store_begin();
$self->_store_lineformat( 0x00000000, 0x0000, 0xFFFF, 0x0001, 0x004F );
$self->_store_areaformat( 0x00FFFFFF, 0x0000, 0x01, 0x01, 0x09, 0x08 );
$self->_store_end();
$self->_store_dropbar();
$self->_store_begin();
$self->_store_lineformat( 0x00000000, 0x0000, 0xFFFF, 0x0001, 0x004F );
$self->_store_areaformat( 0x0000, 0x00FFFFFF, 0x01, 0x01, 0x08, 0x09 );
$self->_store_end();
$self->_store_chartline();
$self->_store_lineformat( 0x00000000, 0x0000, 0xFFFF, 0x0000, 0x004F );
$self->_store_dataformat( 0x0000, 0xFFFD, 0x0000 );
$self->_store_begin();
$self->_store_3dbarshape();
$self->_store_lineformat( 0x00000000, 0x0005, 0xFFFF, 0x0000, 0x004F );
$self->_store_areaformat( 0x00000000, 0x0000, 0x00, 0x01, 0x4D, 0x4D );
$self->_store_pieformat();
$self->_store_markerformat( 0x00, 0x00, 0x00, 0x00, 0x4D, 0x4D, 0x3C );
$self->_store_end();
}
1;
__END__
=head1 NAME
Stock - A writer class for Excel Stock charts.
=head1 SYNOPSIS
To create a simple Excel file with a Stock chart using Spreadsheet::WriteExcel:
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart.xls' );
my $worksheet = $workbook->add_worksheet();
my $chart = $workbook->add_chart( type => 'stock' );
# Add a series for each Open-High-Low-Close.
$chart->add_series( categories => '=Sheet1!$A$2:$A$6', values => '=Sheet1!$B$2:$B$6' );
$chart->add_series( categories => '=Sheet1!$A$2:$A$6', values => '=Sheet1!$C$2:$C$6' );
$chart->add_series( categories => '=Sheet1!$A$2:$A$6', values => '=Sheet1!$D$2:$D$6' );
$chart->add_series( categories => '=Sheet1!$A$2:$A$6', values => '=Sheet1!$E$2:$E$6' );
# Add the worksheet data the chart refers to.
# ... See the full example below.
__END__
=head1 DESCRIPTION
This module implements Stock charts for L<Spreadsheet::WriteExcel>. The chart object is created via the Workbook C<add_chart()> method:
my $chart = $workbook->add_chart( type => 'stock' );
Once the object is created it can be configured via the following methods that are common to all chart classes:
$chart->add_series();
$chart->set_x_axis();
$chart->set_y_axis();
$chart->set_title();
These methods are explained in detail in L<Spreadsheet::WriteExcel::Chart>. Class specific methods or settings, if any, are explained below.
=head1 Stock Chart Methods
There aren't currently any stock chart specific methods. See the TODO section of L<Spreadsheet::WriteExcel::Chart>.
The default Stock chart is an Open-High-Low-Close chart. A series must be added for each of these data sources.
The default Stock chart is in black and white. User defined colours will be added at a later stage.
=head1 EXAMPLE
Here is a complete example that demonstrates most of the available features when creating a Stock chart.
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel;
my $workbook = Spreadsheet::WriteExcel->new( 'chart_stock_ex.xls' );
my $worksheet = $workbook->add_worksheet();
my $bold = $workbook->add_format( bold => 1 );
my $date_format = $workbook->add_format( num_format => 'dd/mm/yyyy' );
# Add the worksheet data that the charts will refer to.
my $headings = [ 'Date', 'Open', 'High', 'Low', 'Close' ];
my @data = (
[ '2009-08-23', 110.75, 113.48, 109.05, 109.40 ],
[ '2009-08-24', 111.24, 111.60, 103.57, 104.87 ],
[ '2009-08-25', 104.96, 108.00, 103.88, 106.00 ],
[ '2009-08-26', 104.95, 107.95, 104.66, 107.91 ],
[ '2009-08-27', 108.10, 108.62, 105.69, 106.15 ],
);
$worksheet->write( 'A1', $headings, $bold );
my $row = 1;
for my $data ( @data ) {
$worksheet->write( $row, 0, $data->[0], $date_format );
$worksheet->write( $row, 1, $data->[1] );
$worksheet->write( $row, 2, $data->[2] );
$worksheet->write( $row, 3, $data->[3] );
$worksheet->write( $row, 4, $data->[4] );
$row++;
}
# Create a new chart object. In this case an embedded chart.
my $chart = $workbook->add_chart( type => 'stock', embedded => 1 );
# Add a series for each of the Open-High-Low-Close columns.
$chart->add_series(
categories => '=Sheet1!$A$2:$A$6',
values => '=Sheet1!$B$2:$B$6',
name => 'Open',
);
$chart->add_series(
categories => '=Sheet1!$A$2:$A$6',
values => '=Sheet1!$C$2:$C$6',
name => 'High',
);
$chart->add_series(
categories => '=Sheet1!$A$2:$A$6',
values => '=Sheet1!$D$2:$D$6',
name => 'Low',
);
$chart->add_series(
categories => '=Sheet1!$A$2:$A$6',
values => '=Sheet1!$E$2:$E$6',
name => 'Close',
);
# Add a chart title and some axis labels.
$chart->set_title( name => 'Open-High-Low-Close', );
$chart->set_x_axis( name => 'Date', );
$chart->set_y_axis( name => 'Share price', );
# Insert the chart into the worksheet (with an offset).
$worksheet->insert_chart( 'F2', $chart, 25, 10 );
__END__
=begin html
<p>This will produce a chart that looks like this:</p>
<p><center><img src="http://homepage.eircom.net/~jmcnamara/perl/images/stock1.jpg" width="527" height="320" alt="Chart example." /></center></p>
=end html
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,814 @@
package Spreadsheet::WriteExcel::Format;
###############################################################################
#
# Format - A class for defining Excel formatting.
#
#
# Used in conjunction with Spreadsheet::WriteExcel
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
use Exporter;
use strict;
use Carp;
use vars qw($AUTOLOAD $VERSION @ISA);
@ISA = qw(Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
# Constructor
#
sub new {
my $class = shift;
my $self = {
_xf_index => shift || 0,
_type => 0,
_font_index => 0,
_font => 'Arial',
_size => 10,
_bold => 0x0190,
_italic => 0,
_color => 0x7FFF,
_underline => 0,
_font_strikeout => 0,
_font_outline => 0,
_font_shadow => 0,
_font_script => 0,
_font_family => 0,
_font_charset => 0,
_font_encoding => 0,
_num_format => 0,
_num_format_enc => 0,
_hidden => 0,
_locked => 1,
_text_h_align => 0,
_text_wrap => 0,
_text_v_align => 2,
_text_justlast => 0,
_rotation => 0,
_fg_color => 0x40,
_bg_color => 0x41,
_pattern => 0,
_bottom => 0,
_top => 0,
_left => 0,
_right => 0,
_bottom_color => 0x40,
_top_color => 0x40,
_left_color => 0x40,
_right_color => 0x40,
_indent => 0,
_shrink => 0,
_merge_range => 0,
_reading_order => 0,
_diag_type => 0,
_diag_color => 0x40,
_diag_border => 0,
_font_only => 0,
# Temp code to prevent merged formats in non-merged cells.
_used_merge => 0,
};
bless $self, $class;
# Set properties passed to Workbook::add_format()
$self->set_format_properties(@_) if @_;
return $self;
}
###############################################################################
#
# copy($format)
#
# Copy the attributes of another Spreadsheet::WriteExcel::Format object.
#
sub copy {
my $self = shift;
my $other = $_[0];
return unless defined $other;
return unless (ref($self) eq ref($other));
# Store the properties that we don't want overwritten.
my $xf = $self->{_xf_index};
my $merge_range = $self->{_merge_range};
my $used_merge = $self->{_used_merge};
%$self = %$other; # Copy properties
# Restore saved properties.
$self->{_xf_index} = $xf;
$self->{_merge_range} = $merge_range;
$self->{_used_merge} = $used_merge;
}
###############################################################################
#
# get_xf($style)
#
# Generate an Excel BIFF XF record.
#
sub get_xf {
use integer; # Avoid << shift bug in Perl 5.6.0 on HP-UX
my $self = shift;
my $record; # Record identifier
my $length; # Number of bytes to follow
my $ifnt; # Index to FONT record
my $ifmt; # Index to FORMAT record
my $style; # Style and other options
my $align; # Alignment
my $indent; #
my $icv; # fg and bg pattern colors
my $border1; # Border line options
my $border2; # Border line options
my $border3; # Border line options
# Set the type of the XF record and some of the attributes.
if ($self->{_type} == 0xFFF5) {
$style = 0xFFF5;
}
else {
$style = $self->{_locked};
$style |= $self->{_hidden} << 1;
}
# Flags to indicate if attributes have been set.
my $atr_num = ($self->{_num_format} != 0);
my $atr_fnt = ($self->{_font_index} != 0);
my $atr_alc = ($self->{_text_h_align} != 0 ||
$self->{_text_v_align} != 2 ||
$self->{_shrink} != 0 ||
$self->{_merge_range} != 0 ||
$self->{_text_wrap} != 0 ||
$self->{_indent} != 0) ? 1 : 0;
my $atr_bdr = ($self->{_bottom} != 0 ||
$self->{_top} != 0 ||
$self->{_left} != 0 ||
$self->{_right} != 0 ||
$self->{_diag_type} != 0) ? 1: 0;
my $atr_pat = ($self->{_fg_color} != 0x40 ||
$self->{_bg_color} != 0x41 ||
$self->{_pattern} != 0x00) ? 1 : 0;
my $atr_prot = ($self->{_hidden} != 0 ||
$self->{_locked} != 1) ? 1 : 0;
# Set attribute changed flags for the style formats.
if ($self->{_xf_index} != 0 and $self->{_type} == 0xFFF5) {
if ($self->{_xf_index} >= 16) {
$atr_num = 0;
$atr_fnt = 1;
}
else {
$atr_num = 1;
$atr_fnt = 0;
}
$atr_alc = 1;
$atr_bdr = 1;
$atr_pat = 1;
$atr_prot = 1;
}
# Set a default diagonal border style if none was specified.
$self->{_diag_border} = 1 if !$self->{_diag_border} and $self->{_diag_type};
# Reset the default colours for the non-font properties
$self->{_fg_color} = 0x40 if $self->{_fg_color} == 0x7FFF;
$self->{_bg_color} = 0x41 if $self->{_bg_color} == 0x7FFF;
$self->{_bottom_color} = 0x40 if $self->{_bottom_color} == 0x7FFF;
$self->{_top_color} = 0x40 if $self->{_top_color} == 0x7FFF;
$self->{_left_color} = 0x40 if $self->{_left_color} == 0x7FFF;
$self->{_right_color} = 0x40 if $self->{_right_color} == 0x7FFF;
$self->{_diag_color} = 0x40 if $self->{_diag_color} == 0x7FFF;
# Zero the default border colour if the border has not been set.
$self->{_bottom_color} = 0 if $self->{_bottom} == 0;
$self->{_top_color} = 0 if $self->{_top} == 0;
$self->{_right_color} = 0 if $self->{_right} == 0;
$self->{_left_color} = 0 if $self->{_left} == 0;
$self->{_diag_color} = 0 if $self->{_diag_type} == 0;
# The following 2 logical statements take care of special cases in relation
# to cell colours and patterns:
# 1. For a solid fill (_pattern == 1) Excel reverses the role of foreground
# and background colours.
# 2. If the user specifies a foreground or background colour without a
# pattern they probably wanted a solid fill, so we fill in the defaults.
#
if ($self->{_pattern} <= 0x01 and
$self->{_bg_color} != 0x41 and
$self->{_fg_color} == 0x40 )
{
$self->{_fg_color} = $self->{_bg_color};
$self->{_bg_color} = 0x40;
$self->{_pattern} = 1;
}
if ($self->{_pattern} <= 0x01 and
$self->{_bg_color} == 0x41 and
$self->{_fg_color} != 0x40 )
{
$self->{_bg_color} = 0x40;
$self->{_pattern} = 1;
}
# Set default alignment if indent is set.
$self->{_text_h_align} = 1 if $self->{_indent} and
$self->{_text_h_align} == 0;
$record = 0x00E0;
$length = 0x0014;
$ifnt = $self->{_font_index};
$ifmt = $self->{_num_format};
$align = $self->{_text_h_align};
$align |= $self->{_text_wrap} << 3;
$align |= $self->{_text_v_align} << 4;
$align |= $self->{_text_justlast} << 7;
$align |= $self->{_rotation} << 8;
$indent = $self->{_indent};
$indent |= $self->{_shrink} << 4;
$indent |= $self->{_merge_range} << 5;
$indent |= $self->{_reading_order} << 6;
$indent |= $atr_num << 10;
$indent |= $atr_fnt << 11;
$indent |= $atr_alc << 12;
$indent |= $atr_bdr << 13;
$indent |= $atr_pat << 14;
$indent |= $atr_prot << 15;
$border1 = $self->{_left};
$border1 |= $self->{_right} << 4;
$border1 |= $self->{_top} << 8;
$border1 |= $self->{_bottom} << 12;
$border2 = $self->{_left_color};
$border2 |= $self->{_right_color} << 7;
$border2 |= $self->{_diag_type} << 14;
$border3 = $self->{_top_color};
$border3 |= $self->{_bottom_color} << 7;
$border3 |= $self->{_diag_color} << 14;
$border3 |= $self->{_diag_border} << 21;
$border3 |= $self->{_pattern} << 26;
$icv = $self->{_fg_color};
$icv |= $self->{_bg_color} << 7;
my $header = pack("vv", $record, $length);
my $data = pack("vvvvvvvVv", $ifnt, $ifmt, $style,
$align, $indent,
$border1, $border2, $border3,
$icv);
return($header . $data);
}
###############################################################################
#
# Note to porters. The majority of the set_property() methods are created
# dynamically via Perl' AUTOLOAD sub, see below. You may prefer/have to specify
# them explicitly in other implementation languages.
#
###############################################################################
#
# get_font()
#
# Generate an Excel BIFF FONT record.
#
sub get_font {
my $self = shift;
my $record; # Record identifier
my $length; # Record length
my $dyHeight; # Height of font (1/20 of a point)
my $grbit; # Font attributes
my $icv; # Index to color palette
my $bls; # Bold style
my $sss; # Superscript/subscript
my $uls; # Underline
my $bFamily; # Font family
my $bCharSet; # Character set
my $reserved; # Reserved
my $cch; # Length of font name
my $rgch; # Font name
my $encoding; # Font name character encoding
$dyHeight = $self->{_size} * 20;
$icv = $self->{_color};
$bls = $self->{_bold};
$sss = $self->{_font_script};
$uls = $self->{_underline};
$bFamily = $self->{_font_family};
$bCharSet = $self->{_font_charset};
$rgch = $self->{_font};
$encoding = $self->{_font_encoding};
# Handle utf8 strings in perl 5.8.
if ($] >= 5.008) {
require Encode;
if (Encode::is_utf8($rgch)) {
$rgch = Encode::encode("UTF-16BE", $rgch);
$encoding = 1;
}
}
$cch = length $rgch;
# Handle Unicode font names.
if ($encoding == 1) {
croak "Uneven number of bytes in Unicode font name" if $cch % 2;
$cch /= 2 if $encoding;
$rgch = pack 'v*', unpack 'n*', $rgch;
}
$record = 0x31;
$length = 0x10 + length $rgch;
$reserved = 0x00;
$grbit = 0x00;
$grbit |= 0x02 if $self->{_italic};
$grbit |= 0x08 if $self->{_font_strikeout};
$grbit |= 0x10 if $self->{_font_outline};
$grbit |= 0x20 if $self->{_font_shadow};
my $header = pack("vv", $record, $length);
my $data = pack("vvvvvCCCCCC", $dyHeight, $grbit, $icv, $bls,
$sss, $uls, $bFamily,
$bCharSet, $reserved, $cch, $encoding);
return($header . $data . $rgch);
}
###############################################################################
#
# get_font_key()
#
# Returns a unique hash key for a font. Used by Workbook->_store_all_fonts()
#
sub get_font_key {
my $self = shift;
# The following elements are arranged to increase the probability of
# generating a unique key. Elements that hold a large range of numbers
# e.g. _color are placed between two binary elements such as _italic
#
my $key = "$self->{_font}$self->{_size}";
$key .= "$self->{_font_script}$self->{_underline}";
$key .= "$self->{_font_strikeout}$self->{_bold}$self->{_font_outline}";
$key .= "$self->{_font_family}$self->{_font_charset}";
$key .= "$self->{_font_shadow}$self->{_color}$self->{_italic}";
$key .= "$self->{_font_encoding}";
$key =~ s/ /_/g; # Convert the key to a single word
return $key;
}
###############################################################################
#
# get_xf_index()
#
# Returns the index used by Worksheet->_XF()
#
sub get_xf_index {
my $self = shift;
return $self->{_xf_index};
}
###############################################################################
#
# _get_color()
#
# Used in conjunction with the set_xxx_color methods to convert a color
# string into a number. Color range is 0..63 but we will restrict it
# to 8..63 to comply with Gnumeric. Colors 0..7 are repeated in 8..15.
#
sub _get_color {
my %colors = (
aqua => 0x0F,
cyan => 0x0F,
black => 0x08,
blue => 0x0C,
brown => 0x10,
magenta => 0x0E,
fuchsia => 0x0E,
gray => 0x17,
grey => 0x17,
green => 0x11,
lime => 0x0B,
navy => 0x12,
orange => 0x35,
pink => 0x21,
purple => 0x14,
red => 0x0A,
silver => 0x16,
white => 0x09,
yellow => 0x0D,
);
# Return the default color, 0x7FFF, if undef,
return 0x7FFF unless defined $_[0];
# or the color string converted to an integer,
return $colors{lc($_[0])} if exists $colors{lc($_[0])};
# or the default color if string is unrecognised,
return 0x7FFF if ($_[0] =~ m/\D/);
# or an index < 8 mapped into the correct range,
return $_[0] + 8 if $_[0] < 8;
# or the default color if arg is outside range,
return 0x7FFF if $_[0] > 63;
# or an integer in the valid range
return $_[0];
}
###############################################################################
#
# set_type()
#
# Set the XF object type as 0 = cell XF or 0xFFF5 = style XF.
#
sub set_type {
my $self = shift;
my $type = $_[0];
if (defined $_[0] and $_[0] eq 0) {
$self->{_type} = 0x0000;
}
else {
$self->{_type} = 0xFFF5;
}
}
###############################################################################
#
# set_align()
#
# Set cell alignment.
#
sub set_align {
my $self = shift;
my $location = $_[0];
return if not defined $location; # No default
return if $location =~ m/\d/; # Ignore numbers
$location = lc($location);
$self->set_text_h_align(1) if ($location eq 'left');
$self->set_text_h_align(2) if ($location eq 'centre');
$self->set_text_h_align(2) if ($location eq 'center');
$self->set_text_h_align(3) if ($location eq 'right');
$self->set_text_h_align(4) if ($location eq 'fill');
$self->set_text_h_align(5) if ($location eq 'justify');
$self->set_text_h_align(6) if ($location eq 'center_across');
$self->set_text_h_align(6) if ($location eq 'centre_across');
$self->set_text_h_align(6) if ($location eq 'merge'); # S:WE name
$self->set_text_h_align(7) if ($location eq 'distributed');
$self->set_text_h_align(7) if ($location eq 'equal_space'); # ParseExcel
$self->set_text_v_align(0) if ($location eq 'top');
$self->set_text_v_align(1) if ($location eq 'vcentre');
$self->set_text_v_align(1) if ($location eq 'vcenter');
$self->set_text_v_align(2) if ($location eq 'bottom');
$self->set_text_v_align(3) if ($location eq 'vjustify');
$self->set_text_v_align(4) if ($location eq 'vdistributed');
$self->set_text_v_align(4) if ($location eq 'vequal_space'); # ParseExcel
}
###############################################################################
#
# set_valign()
#
# Set vertical cell alignment. This is required by the set_format_properties()
# method to differentiate between the vertical and horizontal properties.
#
sub set_valign {
my $self = shift;
$self->set_align(@_);
}
###############################################################################
#
# set_center_across()
#
# Implements the Excel5 style "merge".
#
sub set_center_across {
my $self = shift;
$self->set_text_h_align(6);
}
###############################################################################
#
# set_merge()
#
# This was the way to implement a merge in Excel5. However it should have been
# called "center_across" and not "merge".
# This is now deprecated. Use set_center_across() or better merge_range().
#
#
sub set_merge {
my $self = shift;
$self->set_text_h_align(6);
}
###############################################################################
#
# set_bold()
#
# Bold has a range 0x64..0x3E8.
# 0x190 is normal. 0x2BC is bold. So is an excessive use of AUTOLOAD.
#
sub set_bold {
my $self = shift;
my $weight = $_[0];
$weight = 0x2BC if not defined $weight; # Bold text
$weight = 0x2BC if $weight == 1; # Bold text
$weight = 0x190 if $weight == 0; # Normal text
$weight = 0x190 if $weight < 0x064; # Lower bound
$weight = 0x190 if $weight > 0x3E8; # Upper bound
$self->{_bold} = $weight;
}
###############################################################################
#
# set_border($style)
#
# Set cells borders to the same style
#
sub set_border {
my $self = shift;
my $style = $_[0];
$self->set_bottom($style);
$self->set_top($style);
$self->set_left($style);
$self->set_right($style);
}
###############################################################################
#
# set_border_color($color)
#
# Set cells border to the same color
#
sub set_border_color {
my $self = shift;
my $color = $_[0];
$self->set_bottom_color($color);
$self->set_top_color($color);
$self->set_left_color($color);
$self->set_right_color($color);
}
###############################################################################
#
# set_rotation($angle)
#
# Set the rotation angle of the text. An alignment property.
#
sub set_rotation {
my $self = shift;
my $rotation = $_[0];
# Argument should be a number
return if $rotation !~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;
# The arg type can be a double but the Excel dialog only allows integers.
$rotation = int $rotation;
if ($rotation == 270) {
$rotation = 255;
}
elsif ($rotation >= -90 or $rotation <= 90) {
$rotation = -$rotation +90 if $rotation < 0;
}
else {
carp "Rotation $rotation outside range: -90 <= angle <= 90";
$rotation = 0;
}
$self->{_rotation} = $rotation;
}
###############################################################################
#
# set_format_properties()
#
# Convert hashes of properties to method calls.
#
sub set_format_properties {
my $self = shift;
my %properties = @_; # Merge multiple hashes into one
while (my($key, $value) = each(%properties)) {
# Strip leading "-" from Tk style properties e.g. -color => 'red'.
$key =~ s/^-//;
# Create a sub to set the property.
my $sub = \&{"set_$key"};
$sub->($self, $value);
}
}
# Renamed rarely used set_properties() to set_format_properties() to avoid
# confusion with Workbook method of the same name. The following acts as an
# alias for any code that uses the old name.
*set_properties = *set_format_properties;
###############################################################################
#
# AUTOLOAD. Deus ex machina.
#
# Dynamically create set methods that aren't already defined.
#
sub AUTOLOAD {
my $self = shift;
# Ignore calls to DESTROY
return if $AUTOLOAD =~ /::DESTROY$/;
# Check for a valid method names, i.e. "set_xxx_yyy".
$AUTOLOAD =~ /.*::set(\w+)/ or die "Unknown method: $AUTOLOAD\n";
# Match the attribute, i.e. "_xxx_yyy".
my $attribute = $1;
# Check that the attribute exists
exists $self->{$attribute} or die "Unknown method: $AUTOLOAD\n";
# The attribute value
my $value;
# There are two types of set methods: set_property() and
# set_property_color(). When a method is AUTOLOADED we store a new anonymous
# sub in the appropriate slot in the symbol table. The speeds up subsequent
# calls to the same method.
#
no strict 'refs'; # To allow symbol table hackery
if ($AUTOLOAD =~ /.*::set\w+color$/) {
# For "set_property_color" methods
$value = _get_color($_[0]);
*{$AUTOLOAD} = sub {
my $self = shift;
$self->{$attribute} = _get_color($_[0]);
};
}
else {
$value = $_[0];
$value = 1 if not defined $value; # The default value is always 1
*{$AUTOLOAD} = sub {
my $self = shift;
my $value = shift;
$value = 1 if not defined $value;
$self->{$attribute} = $value;
};
}
$self->{$attribute} = $value;
}
1;
__END__
=encoding latin1
=head1 NAME
Format - A class for defining Excel formatting.
=head1 SYNOPSIS
See the documentation for Spreadsheet::WriteExcel
=head1 DESCRIPTION
This module is used in conjunction with Spreadsheet::WriteExcel.
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,450 @@
package Spreadsheet::WriteExcel::OLEwriter;
###############################################################################
#
# OLEwriter - A writer class to store BIFF data in a OLE compound storage file.
#
#
# Used in conjunction with Spreadsheet::WriteExcel
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
# Documentation after __END__
#
use Exporter;
use strict;
use Carp;
use FileHandle;
use vars qw($VERSION @ISA);
@ISA = qw(Exporter);
$VERSION = '2.40';
###############################################################################
#
# new()
#
# Constructor
#
sub new {
my $class = shift;
my $self = {
_olefilename => $_[0],
_filehandle => "",
_fileclosed => 0,
_internal_fh => 0,
_biff_only => 0,
_size_allowed => 0,
_biffsize => 0,
_booksize => 0,
_big_blocks => 0,
_list_blocks => 0,
_root_start => 0,
_block_count => 4,
};
bless $self, $class;
$self->_initialize();
return $self;
}
###############################################################################
#
# _initialize()
#
# Create a new filehandle or use the provided filehandle.
#
sub _initialize {
my $self = shift;
my $olefile = $self->{_olefilename};
my $fh;
# If the filename is a reference it is assumed that it is a valid
# filehandle, if not we create a filehandle.
#
if (ref($olefile)) {
$fh = $olefile;
}
else{
# Create a new file, open for writing
$fh = FileHandle->new("> $olefile");
# Workbook.pm also checks this but something may have happened since
# then.
if (not defined $fh) {
croak "Can't open $olefile. It may be in use or protected.\n";
}
# binmode file whether platform requires it or not
binmode($fh);
$self->{_internal_fh} = 1;
}
# Store filehandle
$self->{_filehandle} = $fh;
}
###############################################################################
#
# set_size($biffsize)
#
# Set the size of the data to be written to the OLE stream
#
# $big_blocks = (109 depot block x (128 -1 marker word)
# - (1 x end words)) = 13842
# $maxsize = $big_blocks * 512 bytes = 7087104
#
sub set_size {
my $self = shift;
my $maxsize = 7_087_104; # Use Spreadsheet::WriteExcel::Big to exceed this
if ($_[0] > $maxsize) {
return $self->{_size_allowed} = 0;
}
$self->{_biffsize} = $_[0];
# Set the min file size to 4k to avoid having to use small blocks
if ($_[0] > 4096) {
$self->{_booksize} = $_[0];
}
else {
$self->{_booksize} = 4096;
}
return $self->{_size_allowed} = 1;
}
###############################################################################
#
# _calculate_sizes()
#
# Calculate various sizes needed for the OLE stream
#
sub _calculate_sizes {
my $self = shift;
my $datasize = $self->{_booksize};
if ($datasize % 512 == 0) {
$self->{_big_blocks} = $datasize/512;
}
else {
$self->{_big_blocks} = int($datasize/512) +1;
}
# There are 127 list blocks and 1 marker blocks for each big block
# depot + 1 end of chain block
$self->{_list_blocks} = int(($self->{_big_blocks})/127) +1;
$self->{_root_start} = $self->{_big_blocks};
}
###############################################################################
#
# close()
#
# Write root entry, big block list and close the filehandle.
# This routine is used to explicitly close the open filehandle without
# having to wait for DESTROY.
#
sub close {
my $self = shift;
return if not $self->{_size_allowed};
$self->_write_padding() if not $self->{_biff_only};
$self->_write_property_storage() if not $self->{_biff_only};
$self->_write_big_block_depot() if not $self->{_biff_only};
my $close = 1; # Default to no error for external filehandles.
# Close the filehandle if it was created internally.
$close = CORE::close($self->{_filehandle}) if $self->{_internal_fh};
$self->{_fileclosed} = 1;
return $close;
}
###############################################################################
#
# DESTROY()
#
# Close the filehandle if it hasn't already been explicitly closed.
#
sub DESTROY {
my $self = shift;
local ($@, $!, $^E, $?);
$self->close() unless $self->{_fileclosed};
}
###############################################################################
#
# write($data)
#
# Write BIFF data to OLE file.
#
sub write {
my $self = shift;
# Protect print() from -l on the command line.
local $\ = undef;
print {$self->{_filehandle}} $_[0];
}
###############################################################################
#
# write_header()
#
# Write OLE header block.
#
sub write_header {
my $self = shift;
return if $self->{_biff_only};
$self->_calculate_sizes();
my $root_start = $self->{_root_start};
my $num_lists = $self->{_list_blocks};
my $id = pack("NN", 0xD0CF11E0, 0xA1B11AE1);
my $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
my $unknown2 = pack("vv", 0x3E, 0x03);
my $unknown3 = pack("v", -2);
my $unknown4 = pack("v", 0x09);
my $unknown5 = pack("VVV", 0x06, 0x00, 0x00);
my $num_bbd_blocks = pack("V", $num_lists);
my $root_startblock = pack("V", $root_start);
my $unknown6 = pack("VV", 0x00, 0x1000);
my $sbd_startblock = pack("V", -2);
my $unknown7 = pack("VVV", 0x00, -2 ,0x00);
my $unused = pack("V", -1);
# Protect print() from -l on the command line.
local $\ = undef;
print {$self->{_filehandle}} $id;
print {$self->{_filehandle}} $unknown1;
print {$self->{_filehandle}} $unknown2;
print {$self->{_filehandle}} $unknown3;
print {$self->{_filehandle}} $unknown4;
print {$self->{_filehandle}} $unknown5;
print {$self->{_filehandle}} $num_bbd_blocks;
print {$self->{_filehandle}} $root_startblock;
print {$self->{_filehandle}} $unknown6;
print {$self->{_filehandle}} $sbd_startblock;
print {$self->{_filehandle}} $unknown7;
for (1..$num_lists) {
$root_start++;
print {$self->{_filehandle}} pack("V", $root_start);
}
for ($num_lists..108) {
print {$self->{_filehandle}} $unused;
}
}
###############################################################################
#
# _write_big_block_depot()
#
# Write big block depot.
#
sub _write_big_block_depot {
my $self = shift;
my $num_blocks = $self->{_big_blocks};
my $num_lists = $self->{_list_blocks};
my $total_blocks = $num_lists *128;
my $used_blocks = $num_blocks + $num_lists +2;
my $marker = pack("V", -3);
my $end_of_chain = pack("V", -2);
my $unused = pack("V", -1);
# Protect print() from -l on the command line.
local $\ = undef;
for my $i (1..$num_blocks-1) {
print {$self->{_filehandle}} pack("V",$i);
}
print {$self->{_filehandle}} $end_of_chain;
print {$self->{_filehandle}} $end_of_chain;
for (1..$num_lists) {
print {$self->{_filehandle}} $marker;
}
for ($used_blocks..$total_blocks) {
print {$self->{_filehandle}} $unused;
}
}
###############################################################################
#
# _write_property_storage()
#
# Write property storage. TODO: add summary sheets
#
sub _write_property_storage {
my $self = shift;
my $rootsize = -2;
my $booksize = $self->{_booksize};
################# name type dir start size
$self->_write_pps('Root Entry', 0x05, 1, -2, 0x00);
$self->_write_pps('Workbook', 0x02, -1, 0x00, $booksize);
$self->_write_pps('', 0x00, -1, 0x00, 0x0000);
$self->_write_pps('', 0x00, -1, 0x00, 0x0000);
}
###############################################################################
#
# _write_pps()
#
# Write property sheet in property storage
#
sub _write_pps {
my $self = shift;
my $name = $_[0];
my @name = ();
my $length = 0;
if ($name ne '') {
$name = $_[0] . "\0";
# Simulate a Unicode string
@name = map(ord, split('', $name));
$length = length($name) * 2;
}
my $rawname = pack("v*", @name);
my $zero = pack("C", 0);
my $pps_sizeofname = pack("v", $length); #0x40
my $pps_type = pack("v", $_[1]); #0x42
my $pps_prev = pack("V", -1); #0x44
my $pps_next = pack("V", -1); #0x48
my $pps_dir = pack("V", $_[2]); #0x4c
my $unknown1 = pack("V", 0);
my $pps_ts1s = pack("V", 0); #0x64
my $pps_ts1d = pack("V", 0); #0x68
my $pps_ts2s = pack("V", 0); #0x6c
my $pps_ts2d = pack("V", 0); #0x70
my $pps_sb = pack("V", $_[3]); #0x74
my $pps_size = pack("V", $_[4]); #0x78
# Protect print() from -l on the command line.
local $\ = undef;
print {$self->{_filehandle}} $rawname;
print {$self->{_filehandle}} $zero x (64 -$length);
print {$self->{_filehandle}} $pps_sizeofname;
print {$self->{_filehandle}} $pps_type;
print {$self->{_filehandle}} $pps_prev;
print {$self->{_filehandle}} $pps_next;
print {$self->{_filehandle}} $pps_dir;
print {$self->{_filehandle}} $unknown1 x 5;
print {$self->{_filehandle}} $pps_ts1s;
print {$self->{_filehandle}} $pps_ts1d;
print {$self->{_filehandle}} $pps_ts2d;
print {$self->{_filehandle}} $pps_ts2d;
print {$self->{_filehandle}} $pps_sb;
print {$self->{_filehandle}} $pps_size;
print {$self->{_filehandle}} $unknown1;
}
###############################################################################
#
# _write_padding()
#
# Pad the end of the file
#
sub _write_padding {
my $self = shift;
my $biffsize = $self->{_biffsize};
my $min_size;
if ($biffsize < 4096) {
$min_size = 4096;
}
else {
$min_size = 512;
}
# Protect print() from -l on the command line.
local $\ = undef;
if ($biffsize % $min_size != 0) {
my $padding = $min_size - ($biffsize % $min_size);
print {$self->{_filehandle}} "\0" x $padding;
}
}
1;
__END__
=encoding latin1
=head1 NAME
OLEwriter - A writer class to store BIFF data in a OLE compound storage file.
=head1 SYNOPSIS
See the documentation for Spreadsheet::WriteExcel
=head1 DESCRIPTION
This module is used in conjunction with Spreadsheet::WriteExcel.
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.

View File

@@ -0,0 +1,353 @@
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.40';
# 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 property 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__
=encoding latin1
=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
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.

View File

@@ -0,0 +1,943 @@
package Spreadsheet::WriteExcel::Utility;
###############################################################################
#
# Utility - Helper functions for Spreadsheet::WriteExcel.
#
# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
#
#
use Exporter;
use strict;
use autouse 'Date::Calc' => qw(Delta_DHMS Decode_Date_EU Decode_Date_US);
use autouse 'Date::Manip' => qw(ParseDate Date_Init);
# Do all of the export preparation
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
# Row and column functions
my @rowcol = qw(
xl_rowcol_to_cell
xl_cell_to_rowcol
xl_range_formula
xl_inc_row
xl_dec_row
xl_inc_col
xl_dec_col
);
# Date and Time functions
my @dates = qw(
xl_date_list
xl_date_1904
xl_parse_time
xl_parse_date
xl_parse_date_init
xl_decode_date_EU
xl_decode_date_US
);
@ISA = qw(Exporter);
@EXPORT_OK = ();
@EXPORT = (@rowcol, @dates);
%EXPORT_TAGS = (
rowcol => \@rowcol,
dates => \@dates
);
$VERSION = '2.40';
=encoding latin1
=head1 NAME
Utility - Helper functions for Spreadsheet::WriteExcel.
=head1 SYNOPSIS
Functions to help with some common tasks when using Spreadsheet::WriteExcel.
These functions mainly relate to dealing with rows and columns in A1 notation and to handling dates and times.
use Spreadsheet::WriteExcel::Utility; # Import everything
($row, $col) = xl_cell_to_rowcol('C2'); # (1, 2)
$str = xl_rowcol_to_cell(1, 2); # C2
$str = xl_inc_col('Z1' ); # AA1
$str = xl_dec_col('AA1' ); # Z1
$date = xl_date_list(2002, 1, 1); # 37257
$date = xl_parse_date("11 July 1997"); # 35622
$time = xl_parse_time('3:21:36 PM'); # 0.64
$date = xl_decode_date_EU("13 May 2002"); # 37389
=head1 DESCRIPTION
This module provides a set of functions to help with some common tasks encountered when using the Spreadsheet::WriteExcel module. The two main categories of function are:
Row and column functions: these are used to deal with Excel's A1 representation of cells. The functions in this category are:
xl_rowcol_to_cell
xl_cell_to_rowcol
xl_range_formula
xl_inc_row
xl_dec_row
xl_inc_col
xl_dec_col
Date and Time functions: these are used to convert dates and times to the numeric format used by Excel. The functions in this category are:
xl_date_list
xl_date_1904
xl_parse_time
xl_parse_date
xl_parse_date_init
xl_decode_date_EU
xl_decode_date_US
All of these functions are exported by default. However, you can use import lists if you wish to limit the functions that are imported:
use Spreadsheet::WriteExcel::Utility; # Import everything
use Spreadsheet::WriteExcel::Utility qw(xl_date_list); # xl_date_list only
use Spreadsheet::WriteExcel::Utility qw(:rowcol); # Row/col functions
use Spreadsheet::WriteExcel::Utility qw(:dates); # Date functions
=head1 ROW AND COLUMN FUNCTIONS
Spreadsheet::WriteExcel supports two forms of notation to designate the position of cells: Row-column notation and A1 notation.
Row-column notation uses a zero based index for both row and column while A1 notation uses the standard Excel alphanumeric sequence of column letter and 1-based row. Columns range from A to IV i.e. 0 to 255, rows range from 1 to 16384 in Excel 5 and 65536 in Excel 97. For example:
(0, 0) # The top left cell in row-column notation.
('A1') # The top left cell in A1 notation.
(1999, 29) # Row-column notation.
('AD2000') # The same cell in A1 notation.
Row-column notation is useful if you are referring to cells programmatically:
for my $i (0 .. 9) {
$worksheet->write($i, 0, 'Hello'); # Cells A1 to A10
}
A1 notation is useful for setting up a worksheet manually and for working with formulas:
$worksheet->write('H1', 200);
$worksheet->write('H2', '=H7+1');
The functions in the following sections can be used for dealing with A1 notation, for example:
($row, $col) = xl_cell_to_rowcol('C2'); # (1, 2)
$str = xl_rowcol_to_cell(1, 2); # C2
Cell references in Excel can be either relative or absolute. Absolute references are prefixed by the dollar symbol as shown below:
A1 # Column and row are relative
$A1 # Column is absolute and row is relative
A$1 # Column is relative and row is absolute
$A$1 # Column and row are absolute
An absolute reference only has an effect if the cell is copied. Refer to the Excel documentation for further details. All of the following functions support absolute references.
=cut
###############################################################################
###############################################################################
=head2 xl_rowcol_to_cell($row, $col, $row_absolute, $col_absolute)
Parameters: $row: Integer
$col: Integer
$row_absolute: Boolean (1/0) [optional, default is 0]
$col_absolute: Boolean (1/0) [optional, default is 0]
Returns: A string in A1 cell notation
This function converts a zero based row and column cell reference to a A1 style string:
$str = xl_rowcol_to_cell(0, 0); # A1
$str = xl_rowcol_to_cell(0, 1); # B1
$str = xl_rowcol_to_cell(1, 0); # A2
The optional parameters C<$row_absolute> and C<$col_absolute> can be used to indicate if the row or column is absolute:
$str = xl_rowcol_to_cell(0, 0, 0, 1); # $A1
$str = xl_rowcol_to_cell(0, 0, 1, 0); # A$1
$str = xl_rowcol_to_cell(0, 0, 1, 1); # $A$1
See L<ROW AND COLUMN FUNCTIONS> for an explanation of absolute cell references.
=cut
###############################################################################
#
# xl_rowcol_to_cell($row, $col, $row_absolute, $col_absolute)
#
sub xl_rowcol_to_cell {
my $row = $_[0];
my $col = $_[1];
my $row_abs = $_[2] ? '$' : '';
my $col_abs = $_[3] ? '$' : '';
my $col_str = '';
# Change from 0-indexed to 1 indexed.
$row++;
$col++;
while ( $col ) {
# Set remainder from 1 .. 26
my $remainder = $col % 26 || 26;
# Convert the $remainder to a character. C-ishly.
my $col_letter = chr( ord( 'A' ) + $remainder - 1 );
# Accumulate the column letters, right to left.
$col_str = $col_letter . $col_str;
# Get the next order of magnitude.
$col = int( ( $col - 1 ) / 26 );
}
return $col_abs . $col_str . $row_abs . $row;
}
###############################################################################
###############################################################################
=head2 xl_cell_to_rowcol($string)
Parameters: $string String in A1 format
Returns: List ($row, $col)
This function converts an Excel cell reference in A1 notation to a zero based row and column. The function will also handle Excel's absolute, C<$>, cell notation.
my ($row, $col) = xl_cell_to_rowcol('A1'); # (0, 0)
my ($row, $col) = xl_cell_to_rowcol('B1'); # (0, 1)
my ($row, $col) = xl_cell_to_rowcol('C2'); # (1, 2)
my ($row, $col) = xl_cell_to_rowcol('$C2' ); # (1, 2)
my ($row, $col) = xl_cell_to_rowcol('C$2' ); # (1, 2)
my ($row, $col) = xl_cell_to_rowcol('$C$2'); # (1, 2)
=cut
###############################################################################
#
# xl_cell_to_rowcol($string)
#
# Returns: ($row, $col, $row_absolute, $col_absolute)
#
# The $row_absolute and $col_absolute parameters aren't documented because they
# mainly used internally and aren't very useful to the user.
#
sub xl_cell_to_rowcol {
my $cell = shift;
$cell =~ /(\$?)([A-Z]{1,3})(\$?)(\d+)/;
my $col_abs = $1 eq "" ? 0 : 1;
my $col = $2;
my $row_abs = $3 eq "" ? 0 : 1;
my $row = $4;
# Convert base26 column string to number
# All your Base are belong to us.
my @chars = split //, $col;
my $expn = 0;
$col = 0;
while (@chars) {
my $char = pop(@chars); # LS char first
$col += (ord($char) -ord('A') +1) * (26**$expn);
$expn++;
}
# Convert 1-index to zero-index
$row--;
$col--;
return $row, $col, $row_abs, $col_abs;
}
###############################################################################
###############################################################################
=head2 xl_range_formula($sheetname, $row_1, $row_2, $col_1, $col_2)
Parameters: $sheetname String
$row_1: Integer
$row_2: Integer
$col_1: Integer
$col_2: Integer
Returns: A worksheet range formula as a string.
This function converts zero based row and column cell references to an A1 style formula string:
my $str = xl_range_formula('Sheet1', 0, 9, 0, 0); # =Sheet1!$A$1:$A$10
my $str = xl_range_formula('Sheet2', 6, 65, 1, 1); # =Sheet2!$B$7:$B$66
my $str = xl_range_formula('New data', 1, 8, 2, 2); # ='New data'!$C$2:$C$9
This is useful for setting ranges in Chart objects:
$chart->add_series(
categories => xl_range_formula('Sheet1', 1, 9, 0, 0),
values => xl_range_formula('Sheet1', 1, 9, 1, 1),
);
# Which is the same as:
$chart->add_series(
categories => '=Sheet1!$A$2:$A$10',
values => '=Sheet1!$B$2:$B$10',
);
=cut
###############################################################################
#
# xl_range_formula($sheetname, $row_1, $row_2, $col_1, $col_2)
#
sub xl_range_formula {
my ($sheetname, $row_1, $row_2, $col_1, $col_2) = @_;
# Use Excel's conventions and quote the sheet name if it contains any
# non-word character or if it isn't already quoted.
if ($sheetname =~ /\W/ && $sheetname !~ /^'/) {
$sheetname = q(') . $sheetname . q(');
}
my $range1 = xl_rowcol_to_cell($row_1, $col_1, 1, 1);
my $range2 = xl_rowcol_to_cell($row_2, $col_2, 1, 1);
return '=' . $sheetname . '!' . $range1 . ':' . $range2;
}
###############################################################################
###############################################################################
=head2 xl_inc_row($string)
Parameters: $string, a string in A1 format
Returns: Incremented string in A1 format
This functions takes a cell reference string in A1 notation and increments the row. The function will also handle Excel's absolute, C<$>, cell notation:
my $str = xl_inc_row('A1' ); # A2
my $str = xl_inc_row('B$2' ); # B$3
my $str = xl_inc_row('$C3' ); # $C4
my $str = xl_inc_row('$D$4'); # $D$5
=cut
###############################################################################
#
# xl_inc_row($string)
#
sub xl_inc_row {
my $cell = shift;
my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
return xl_rowcol_to_cell(++$row, $col, $row_abs, $col_abs);
}
###############################################################################
###############################################################################
=head2 xl_dec_row($string)
Parameters: $string, a string in A1 format
Returns: Decremented string in A1 format
This functions takes a cell reference string in A1 notation and decrements the row. The function will also handle Excel's absolute, C<$>, cell notation:
my $str = xl_dec_row('A2' ); # A1
my $str = xl_dec_row('B$3' ); # B$2
my $str = xl_dec_row('$C4' ); # $C3
my $str = xl_dec_row('$D$5'); # $D$4
=cut
###############################################################################
#
# xl_dec_row($string)
#
# Decrements the row number of an Excel cell reference in A1 notation.
# For example C4 to C3
#
# Returns: a cell reference string.
#
sub xl_dec_row {
my $cell = shift;
my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
return xl_rowcol_to_cell(--$row, $col, $row_abs, $col_abs);
}
###############################################################################
###############################################################################
=head2 xl_inc_col($string)
Parameters: $string, a string in A1 format
Returns: Incremented string in A1 format
This functions takes a cell reference string in A1 notation and increments the column. The function will also handle Excel's absolute, C<$>, cell notation:
my $str = xl_inc_col('A1' ); # B1
my $str = xl_inc_col('Z1' ); # AA1
my $str = xl_inc_col('$B1' ); # $C1
my $str = xl_inc_col('$D$5'); # $E$5
=cut
###############################################################################
#
# xl_inc_col($string)
#
# Increments the column number of an Excel cell reference in A1 notation.
# For example C3 to D3
#
# Returns: a cell reference string.
#
sub xl_inc_col {
my $cell = shift;
my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
return xl_rowcol_to_cell($row, ++$col, $row_abs, $col_abs);
}
###############################################################################
###############################################################################
=head2 xl_dec_col($string)
Parameters: $string, a string in A1 format
Returns: Decremented string in A1 format
This functions takes a cell reference string in A1 notation and decrements the column. The function will also handle Excel's absolute, C<$>, cell notation:
my $str = xl_dec_col('B1' ); # A1
my $str = xl_dec_col('AA1' ); # Z1
my $str = xl_dec_col('$C1' ); # $B1
my $str = xl_dec_col('$E$5'); # $D$5
=cut
###############################################################################
#
# xl_dec_col($string)
#
sub xl_dec_col {
my $cell = shift;
my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
return xl_rowcol_to_cell($row, --$col, $row_abs, $col_abs);
}
=head1 TIME AND DATE FUNCTIONS
Dates and times in Excel are represented by real numbers, for example "Jan 1 2001 12:30 AM" is represented by the number 36892.521.
The integer part of the number stores the number of days since the epoch and the fractional part stores the percentage of the day in seconds.
The epoch can be either 1900 or 1904. Excel for Windows uses 1900 and Excel for Macintosh uses 1904. The epochs are:
1900: 0 January 1900 i.e. 31 December 1899
1904: 1 January 1904
Excel on Windows and the Macintosh will convert automatically between one system and the other. By default Spreadsheet::WriteExcel uses the 1900 format. To use the 1904 epoch you must use the C<set_1904()> workbook method, see the Spreadsheet::WriteExcel documentation.
There are two things to note about the 1900 date format. The first is that the epoch starts on 0 January 1900. The second is that the year 1900 is erroneously but deliberately treated as a leap year. Therefore you must add an extra day to dates after 28 February 1900. The functions in the following section will deal with these issues automatically. The reason for this anomaly is explained at http://support.microsoft.com/support/kb/articles/Q181/3/70.asp
Note, a date or time in Excel is like any other number. To display the number as a date you must apply a number format to it: Refer to the C<set_num_format()> method in the Spreadsheet::WriteExcel documentation:
$date = xl_date_list(2001, 1, 1, 12, 30);
$format->set_num_format('mmm d yyyy hh:mm AM/PM');
$worksheet->write('A1', $date , $format); # Jan 1 2001 12:30 AM
To use these functions you must install the C<Date::Manip> and C<Date::Calc> modules. See L<REQUIREMENTS> and the individual requirements of each functions.
See also the DateTime::Format::Excel module,http://search.cpan.org/search?dist=DateTime-Format-Excel which is part of the DateTime project and which deals specifically with converting dates and times to and from Excel's format.
=cut
###############################################################################
###############################################################################
=head2 xl_date_list($years, $months, $days, $hours, $minutes, $seconds)
Parameters: $years: Integer
$months: Integer [optional, default is 1]
$days: Integer [optional, default is 1]
$hours: Integer [optional, default is 0]
$minutes: Integer [optional, default is 0]
$seconds: Float [optional, default is 0]
Returns: A number that represents an Excel date
or undef for an invalid date.
Requires: Date::Calc
This function converts an array of data into a number that represents an Excel date. All of the parameters are optional except for C<$years>.
$date1 = xl_date_list(2002, 1, 2); # 2 Jan 2002
$date2 = xl_date_list(2002, 1, 2, 12); # 2 Jan 2002 12:00 pm
$date3 = xl_date_list(2002, 1, 2, 12, 30); # 2 Jan 2002 12:30 pm
$date4 = xl_date_list(2002, 1, 2, 12, 30, 45); # 2 Jan 2002 12:30:45 pm
This function can be used in conjunction with functions that parse date and time strings. In fact it is used in most of the following functions.
=cut
###############################################################################
#
# xl_date_list($years, $months, $days, $hours, $minutes, $seconds)
#
sub xl_date_list {
return undef unless @_;
my $years = $_[0];
my $months = $_[1] || 1;
my $days = $_[2] || 1;
my $hours = $_[3] || 0;
my $minutes = $_[4] || 0;
my $seconds = $_[5] || 0;
my @date = ($years, $months, $days, $hours, $minutes, $seconds);
my @epoch = (1899, 12, 31, 0, 0, 0);
($days, $hours, $minutes, $seconds) = Delta_DHMS(@epoch, @date);
my $date = $days + ($hours*3600 +$minutes*60 +$seconds)/(24*60*60);
# Add a day for Excel's missing leap day in 1900
$date++ if ($date > 59);
return $date;
}
###############################################################################
###############################################################################
=head2 xl_parse_time($string)
Parameters: $string, a textual representation of a time
Returns: A number that represents an Excel time
or undef for an invalid time.
This function converts a time string into a number that represents an Excel time. The following time formats are valid:
hh:mm [AM|PM]
hh:mm [AM|PM]
hh:mm:ss [AM|PM]
hh:mm:ss.ss [AM|PM]
The meridian, AM or PM, is optional and case insensitive. A 24 hour time is assumed if the meridian is omitted
$time1 = xl_parse_time('12:18');
$time2 = xl_parse_time('12:18:14');
$time3 = xl_parse_time('12:18:14 AM');
$time4 = xl_parse_time('1:18:14 AM');
Time in Excel is expressed as a fraction of the day in seconds. Therefore you can calculate an Excel time as follows:
$time = ($hours*3600 +$minutes*60 +$seconds)/(24*60*60);
=cut
###############################################################################
#
# xl_parse_time($string)
#
sub xl_parse_time {
my $time = shift;
if ($time =~ /(\d{1,2}):(\d\d):?((?:\d\d)(?:\.\d+)?)?(?:\s+)?(am|pm)?/i) {
my $hours = $1;
my $minutes = $2;
my $seconds = $3 || 0;
my $meridian = lc($4) || '';
# Normalise midnight and midday
$hours = 0 if ($hours == 12 && $meridian ne '');
# Add 12 hours to the pm times. Note: 12.00 pm has been set to 0.00.
$hours += 12 if $meridian eq 'pm';
# Calculate the time as a fraction of 24 hours in seconds
return ($hours*3600 +$minutes*60 +$seconds)/(24*60*60);
}
else {
return undef; # Not a valid time string
}
}
###############################################################################
###############################################################################
=head2 xl_parse_date($string)
Parameters: $string, a textual representation of a date and time
Returns: A number that represents an Excel date
or undef for an invalid date.
Requires: Date::Manip and Date::Calc
This function converts a date and time string into a number that represents an Excel date.
The parsing is performed using the C<ParseDate()> function of the Date::Manip module. Refer to the Date::Manip documentation for further information about the date and time formats that can be parsed. In order to use this function you will probably have to initialise some Date::Manip variables via the C<xl_parse_date_init()> function, see below.
xl_parse_date_init("TZ=GMT","DateFormat=non-US");
$date1 = xl_parse_date("11/7/97");
$date2 = xl_parse_date("Friday 11 July 1997");
$date3 = xl_parse_date("10:30 AM Friday 11 July 1997");
$date4 = xl_parse_date("Today");
$date5 = xl_parse_date("Yesterday");
Note, if you parse a string that represents a time but not a date this function will add the current date. If you want the time without the date you can do something like the following:
$time = xl_parse_date("10:30 AM");
$time -= int($time);
=cut
###############################################################################
#
# xl_parse_date($string)
#
sub xl_parse_date {
my $date = ParseDate($_[0]);
return undef unless defined $date;
# Unpack the return value from ParseDate()
my ($years, $months, $days, $hours, undef, $minutes, undef, $seconds) =
unpack("A4 A2 A2 A2 C A2 C A2", $date);
# Convert to Excel date
return xl_date_list($years, $months, $days, $hours, $minutes, $seconds);
}
###############################################################################
###############################################################################
=head2 xl_parse_date_init("variable=value", ...)
Parameters: A list of Date::Manip variable strings
Returns: A list of all the Date::Manip strings
Requires: Date::Manip
This function is used to initialise variables required by the Date::Manip module. You should call this function before calling C<xl_parse_date()>. It need only be called once.
This function is a thin wrapper for the C<Date::Manip::Date_Init()> function. You can use C<Date_Init()> directly if you wish. Refer to the Date::Manip documentation for further information.
xl_parse_date_init("TZ=MST","DateFormat=US");
$date1 = xl_parse_date("11/7/97"); # November 7th 1997
xl_parse_date_init("TZ=GMT","DateFormat=non-US");
$date1 = xl_parse_date("11/7/97"); # July 11th 1997
=cut
###############################################################################
#
# xl_parse_date_init("variable=value", ...)
#
sub xl_parse_date_init {
Date_Init(@_); # How lazy is that.
}
###############################################################################
###############################################################################
=head2 xl_decode_date_EU($string)
Parameters: $string, a textual representation of a date and time
Returns: A number that represents an Excel date
or undef for an invalid date.
Requires: Date::Calc
This function converts a date and time string into a number that represents an Excel date.
The date parsing is performed using the C<Decode_Date_EU()> function of the Date::Calc module. Refer to the Date::Calc for further information about the date formats that can be parsed. Also note the following from the Date::Calc documentation:
"If the year is given as one or two digits only (i.e., if the year is less than 100), it is mapped to the window 1970 -2069 as follows":
0 E<lt>= $year E<lt> 70 ==> $year += 2000;
70 E<lt>= $year E<lt> 100 ==> $year += 1900;
The time portion of the string is parsed using the C<xl_parse_time()> function described above.
Note: the EU in the function name means that a European date format is assumed if it is not clear from the string. See the first example below.
$date1 = xl_decode_date_EU("11/7/97"); #11 July 1997
$date2 = xl_decode_date_EU("Sat 12 Sept 1998");
$date3 = xl_decode_date_EU("4:30 AM Sat 12 Sept 1998");
=cut
###############################################################################
#
# xl_decode_date_EU($string)
#
sub xl_decode_date_EU {
return undef unless @_;
my $date = shift;
my @date;
my $time = 0;
# Remove and decode the time portion of the string
if ($date =~ s/(\d{1,2}:\d\d:?(\d\d(\.\d+)?)?(\s+)?(am|pm)?)//i) {
$time = xl_parse_time($1);
return undef unless defined $time;
}
# Return if the string is now blank, i.e. it contained a time only.
return $time if $date =~ /^\s*$/;
# Decode the date portion of the string
@date = Decode_Date_EU($date);
return undef unless @date;
return xl_date_list(@date) + $time;
}
###############################################################################
###############################################################################
=head2 xl_decode_date_US($string)
Parameters: $string, a textual representation of a date and time
Returns: A number that represents an Excel date
or undef for an invalid date.
Requires: Date::Calc
This function converts a date and time string into a number that represents an Excel date.
The date parsing is performed using the C<Decode_Date_US()> function of the Date::Calc module. Refer to the Date::Calc for further information about the date formats that can be parsed. Also note the following from the Date::Calc documentation:
"If the year is given as one or two digits only (i.e., if the year is less than 100), it is mapped to the window 1970 -2069 as follows":
0 <= $year < 70 ==> $year += 2000;
70 <= $year < 100 ==> $year += 1900;
The time portion of the string is parsed using the C<xl_parse_time()> function described above.
Note: the US in the function name means that an American date format is assumed if it is not clear from the string. See the first example below.
$date1 = xl_decode_date_US("11/7/97"); # 7 November 1997
$date2 = xl_decode_date_US("12 Sept Saturday 1998");
$date3 = xl_decode_date_US("4:30 AM 12 Sept Sat 1998");
=cut
###############################################################################
#
# xl_decode_date_US($string)
#
sub xl_decode_date_US {
return undef unless @_;
my $date = shift;
my @date;
my $time = 0;
# Remove and decode the time portion of the string
if ($date =~ s/(\d{1,2}:\d\d:?(\d\d(\.\d+)?)?(\s+)?(am|pm)?)//i) {
$time = xl_parse_time($1);
return undef unless defined $time;
}
# Return if the string is now blank, i.e. it contained a time only.
return $time if $date =~ /^\s*$/;
# Decode the date portion of the string
@date = Decode_Date_US($date);
return undef unless @date;
return xl_date_list(@date) + $time;
}
###############################################################################
###############################################################################
=head2 xl_date_1904($date)
Parameters: $date, an Excel date with a 1900 epoch
Returns: an Excel date with a 1904 epoch or zero if
the $date is before 1904
This function converts an Excel date based on the 1900 epoch into a date based on the 1904 epoch.
$date1 = xl_date_list(2002, 1, 13); # 13 Jan 2002, 1900 epoch
$date2 = xl_date_1904($date1); # 13 Jan 2002, 1904 epoch
See also the C<set_1904()> workbook method in the Spreadsheet::WriteExcel documentation.
=cut
###############################################################################
#
# xl_decode_date_US($string)
#
sub xl_date_1904 {
my $date = $_[0] || 0;
if ($date < 1462) {
# before 1904
$date = 0;
}
else {
$date -= 1462;
}
return $date;
}
=head1 REQUIREMENTS
The date and time functions require functions from the C<Date::Manip> and C<Date::Calc> modules. The required functions are "autoused" from these modules so that you do not have to install them unless you wish to use the date and time routines. Therefore it is possible to use the row and column functions without having C<Date::Manip> and C<Date::Calc> installed.
For more information about "autousing" refer to the documentation on the C<autouse> pragma.
=head1 BUGS
When using the autoused functions from C<Date::Manip> and C<Date::Calc> on Perl 5.6.0 with C<-w> you will get a warning like this:
"Subroutine xxx redefined ..."
The current workaround for this is to put C<use warnings;> near the beginning of your program.
=head1 AUTHOR
John McNamara jmcnamara@cpan.org
=head1 COPYRIGHT
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.
=cut
1;
__END__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff