auto commit on 2019-07-05 18:23
authorKilian Saffran <ksaffran@dks.lu>
Fri, 5 Jul 2019 16:23:19 +0000 (18:23 +0200)
committerKilian Saffran <ksaffran@dks.lu>
Fri, 5 Jul 2019 16:23:19 +0000 (18:23 +0200)
59 files changed:
.DS_Store [new file with mode: 0644]
SolanaPMT.fmp12
solana_bin.tar.gz [new file with mode: 0644]
tools/.DS_Store [new file with mode: 0644]
tools/Module/Sommaire.pm [new file with mode: 0644]
tools/find_sommaire_files.pl
tools/lib/perl5/Excel/Writer/XLSX.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chart.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chart/Area.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chart/Bar.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chart/Column.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chart/Doughnut.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chart/Line.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chart/Pie.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chart/Radar.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chart/Scatter.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chart/Stock.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Chartsheet.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Drawing.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Examples.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Format.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/App.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/Comments.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/ContentTypes.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/Core.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/Custom.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/Packager.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/Relationships.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/SharedStrings.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/Styles.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/Table.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/Theme.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/VML.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Package/XMLwriter.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Shape.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Utility.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Workbook.pm [deleted file]
tools/lib/perl5/Excel/Writer/XLSX/Worksheet.pm [deleted file]
tools/notused/DepList.pm [new file with mode: 0644]
tools/notused/SIngleOLDFichesSaveAsPDF.pl [new file with mode: 0644]
tools/notused/depencency.pl [new file with mode: 0644]
tools/notused/import_sommaire_data.pl [new file with mode: 0644]
tools/notused/readEmailDataFromActiveSheet2.pl [new file with mode: 0644]
tools/solanapmt/img/Microsoft_Excel_Import.svg [new file with mode: 0644]
tools/sommaire_files_to_excel.pl
tools/used/.DS_Store [new file with mode: 0644]
tools/used/ExcelDruckenGetData.pl [new file with mode: 0644]
tools/used/ExcelSaveAsPDF.pl [new file with mode: 0644]
tools/used/FichesSaveAsPDF.pl [new file with mode: 0644]
tools/used/Project/cloneproject.pl [new file with mode: 0644]
tools/used/Project/rename_path.pl [new file with mode: 0644]
tools/used/Project/seticon.py [new file with mode: 0644]
tools/used/find_sommaire_files.pl [new file with mode: 0644]
tools/used/getopenxlsfiles.pl [new file with mode: 0644]
tools/used/pdfinfo.pl [new file with mode: 0644]
tools/used/readEmailDataFromActiveSheet.pl [new file with mode: 0644]
tools/used/sommaire_files_to_excel.pl [new file with mode: 0644]
tools/used/xlsexport_rapport_data.pl [new file with mode: 0644]
tools/used/xlsimport_fiche_data.pl [new file with mode: 0644]

diff --git a/.DS_Store b/.DS_Store
new file mode 100644 (file)
index 0000000..2bc9579
Binary files /dev/null and b/.DS_Store differ
index 89e523d..f8a024a 100755 (executable)
Binary files a/SolanaPMT.fmp12 and b/SolanaPMT.fmp12 differ
diff --git a/solana_bin.tar.gz b/solana_bin.tar.gz
new file mode 100644 (file)
index 0000000..a7c9dbf
Binary files /dev/null and b/solana_bin.tar.gz differ
diff --git a/tools/.DS_Store b/tools/.DS_Store
new file mode 100644 (file)
index 0000000..1bac115
Binary files /dev/null and b/tools/.DS_Store differ
diff --git a/tools/Module/Sommaire.pm b/tools/Module/Sommaire.pm
new file mode 100644 (file)
index 0000000..8952454
--- /dev/null
@@ -0,0 +1,189 @@
+package Module::Test;
+
+use strict;
+use warnings;
+use parent qw(Plack::Component);
+use Plack::Request;
+use Data::Dumper;
+use File::Find::Rule;
+use Image::ExifTool;
+use URI::Encode qw/uri_encode/;
+use utf8;
+use open ':std', ':encoding(UTF-8)';
+use Encode qw (encode decode);
+use POSIX;
+
+sub call {
+    my($self, $env) = @_;
+    #$self->_app->($env);
+    if (($env->{REMOTE_ADDR} =~ "^127\.0\.") && 
+               ($env->{REMOTE_ADDR} =~ "^10\.") && 
+               ($env->{REMOTE_ADDR} =~ "^172\.16\.") && 
+               ($env->{REMOTE_ADDR} =~ "^192\.168\.")) {
+               return [
+       404,
+       [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate' ], 
+               [ "Sorry no remote access allowed!" ]
+       ];
+       }
+    $self->{projectpath} = $ENV{HOME}.'/Desktop/1-Neuer Server/A=Architecture';
+    if ($env->{PATH_INFO} =~ /^\/tolist/){ #parameters: project= , optional: type=xlsx
+      return $self->sommaire($env);
+    }
+    if ($env->{PATH_INFO} =~ /^\/projects/){
+      return $self->listprojects($env);
+    }
+}
+
+sub sommaire(){
+       my $self = shift;
+       my $env = shift;
+  my $ret = {};
+  my $ct = 'application/json';
+  my $req = Plack::Request->new($env);
+  if (exists($req->query_parameters->{project}) || ( -d $self->{projectpath}.'/'.$req->query_parameters->{project})){
+    my $outdata = ();
+    my @files = File::Find::Rule->file()->name( qr/\.(xlsx|xls|doc|docx|png|jpg|jpeg|dwg|pla|pln)$/ )->in( $self->{projectpath}.'/'.$req->query_parameters->{project} );
+  #print "Total Files:".scalar(@files)."\n";
+    my $cnt = 0;
+    foreach my $f (@files){
+           my $fname = basename($f);
+         #print $cnt." - "$fname."\n";
+           if ( ($fname =~ /^\./ ) || ($fname =~ /^\~/ ) || ($f =~ /\.app\// ) || ($f =~ /\/\./)) { next;}
+                       my ($y,$m,$d);
+                       if ($fname =~ /\^/){
+                               ($y,$m,$d) = $fname =~ m/.*\^(\d{2,})(\d{2,})(\d{2,}).*/;
+                       } else {
+                               ($y,$m,$d) = $fname =~ m/.*\((\d{2,})(\d{2,})(\d{2,})\).*/;
+                       }
+                       #my $srtdate = $y.$m.$d;
+                       my $senddate = $d.'.'.$m.'.20'.$y;
+                       $outdata->{$cnt}->{senddate} = $senddate; 
+                       $outdata->{$cnt}->{filename} = $fname;
+                       my ($t,$s,$g,$mx,$x) = $self->getfileinfo($f);
+                       $outdata->{$cnt}->{title} = $t;
+                       $outdata->{$cnt}->{description} = $s;
+                       $outdata->{$cnt}->{path} = $f;
+                       $outdata->{$cnt}->{size} = $g;
+                       $outdata->{$cnt}->{moddate} = $mx;
+                       $outdata->{$cnt}->{ftype} = $x;
+
+           $cnt++;
+  }
+  if (exists($req->query_parameters->{type}) && $req->query_parameters->{type} eq "xlsx"){
+    #my $workbook = Excel::Writer::XLSX->new( $self->{projectpath}.'/'.$req->query_parameters->{project}.'/sommaire.xlsx' );
+    #foreach my $o (sort {$a <=> $b} keys(%{$outdata})){
+    #my $worksheet = $workbook->add_worksheet();
+    # #my $col = 0;
+         # $worksheet->write( $row, $col, $outdata->{$o}->{senddate} );
+         # #$col++;
+         # $worksheet->write( $row, $col, decode("UTF-8",$outdata->{$o}->{filename}) );
+         # #$col++;
+         # $worksheet->write( $row, $col, decode("UTF-8",$outdata->{$o}->{title}) );
+         # $col++;
+         # $worksheet->write( $row, $col, decode("UTF-8",$outdata->{$o}->{description}) );
+         # $col++;
+         # $worksheet->write( $row, $col, decode("UTF-8",$outdata->{$o}->{path}));
+         # $col++;
+         # $worksheet->write( $row, $col, $outdata->{$o}->{size} );
+         # $col++;
+         # $worksheet->write( $row, $col, $outdata->{$o}->{moddate} );
+         # $col++;
+         # $worksheet->write( $row, $col, $outdata->{$o}->{ftype} );
+         # $row++;
+    #}
+    #$workbook->close();
+  }else{
+    open(SOM,">".$self->{projectpath}.'/'.$req->query_parameters->{project}.'/sommaire.csv');
+    foreach my $o (sort {$a <=> $b} keys(%{$outdata})){
+           print SOM $outdata->{$o}->{senddate}.";";
+           print SOM decode("UTF-8",$outdata->{$o}->{filename}).";";
+           print SOM decode("UTF-8",$outdata->{$o}->{title}).";";
+           print SOM decode("UTF-8",$outdata->{$o}->{description}).";";
+         print SOM decode("UTF-8",$outdata->{$o}->{path}).";";
+           print SOM $outdata->{$o}->{size}.";";
+           print SOM $outdata->{$o}->{moddate}.";";
+           print SOM $outdata->{$o}->{ftype}."\n";
+    }
+    close(SOM);
+  }
+}
+  return [
+    200,
+     [ 'Content-Type' => $ct,'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ $ret ]
+  ];
+}
+
+sub listprojects(){
+  my $self = shift;
+       my $env = shift;
+  my $ct = 'application/json';
+  my $request = Plack::Request->new($env);
+  my @pr = ();
+  my $ret = {};
+  opendir(PDIR,$self->{projectpath});
+  while (my $l = readdir(PDIR)){
+    if ($l =~ /^./){next;}
+    if (! -d $self->{projectpath}.'/'.$l){ next;}
+    push(@pr,$l);
+  } 
+  closedir(PDIR);
+  $ret->{projects} = \@pr;
+  return [
+    200,
+     [ 'Content-Type' => $ct,'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ $ret ]
+  ];
+  
+}
+
+sub getfileinfo(){
+  my $self = shift;
+       my $file = shift;
+       my $title = "";
+       my $subject = "";
+       my $info = Image::ExifTool::ImageInfo($file);
+       foreach my $k (keys(%{$info})){
+               #print $k."=>".$info->{$k}."\n";
+               if ($k eq "Title"){
+                       $title = $info->{$k};
+               }
+               if ($k eq "Subject"){
+                       $subject = $info->{$k};
+               } 
+               
+       }
+       my @st = stat($file);
+       my $size = $st[7];
+       $size = $size/1024;
+       $size =~ s/\.\d+//g;
+       $size = $size." KB";
+       my $md = &udatetohr($st[9]);
+       my $ftype = $self->getfiletype($file);
+       return ($title,$subject,$size,$md,$ftype);
+}
+
+sub udatetohr(){
+  my $self = shift;
+       my $udate = shift;
+       my @d = localtime($udate);
+       $d[5] = $d[5] + 1900;
+       $d[4] = $d[4] + 1;
+       if ($d[4] < 10){$d[4] = '0'.$d[4];}
+       if ($d[3] < 10){$d[3] = '0'.$d[3];}
+       if ($d[2] < 10){$d[2] = '0'.$d[2];}
+       if ($d[1] < 10){$d[1] = '0'.$d[1];}
+  return ($d[3].'.'.$d[4].'.'.$d[5].' '.$d[2].':'.$d[1]);
+}
+
+sub getfiletype(){
+  my $self = shift;
+       my $ft = shift;
+       my $cmd = 'file -b "'.$ft.'"';
+       my $ftype = `$cmd`;
+       chomp($ftype);
+       return $ftype;
+}
+
+1;
\ No newline at end of file
index 1e64d37..d731fba 100755 (executable)
@@ -13,11 +13,11 @@ my $project = "";
 my @filter = ();
 my @excludes = ();
 GetOptions("project|n=s" => \$project,"projectpath|p=s" => \$ppath,"filter|f=s" => \@filter , "exclude|e=s"=> \@excludes);
-open(DD,">/Users/kilian/test.log");
-print DD "project: ".$project."\n";
-print DD "path: ".$ppath."\n";
-print DD "filter: ".Dumper(@filter)."\n";
-print DD "excludes: ".Dumper(@excludes)."\n";
+open(DD,">/Users/kilian/test.log");
+print DD "project: ".$project."\n";
+print DD "path: ".$ppath."\n";
+print DD "filter: ".Dumper(@filter)."\n";
+print DD "excludes: ".Dumper(@excludes)."\n";
 if (! -d $ppath){
        print "$ppath does not exist!\n";
        exit(1);
@@ -26,9 +26,9 @@ my $fil = $filter[0];
 my $outdata = ();
 
 my @files = File::Find::Rule->file()->in( $ppath );
-print DD "Total Files:".scalar(@files)."\n";
+print DD "Total Files:".scalar(@files)."\n";
 my $cnt = 0;
-print DD "Filters".scalar(@filter)."\n";
+print DD "Filters".scalar(@filter)."\n";
 foreach my $f (@files){
        my $fname = basename($f);
        #print $cnt." - ".$fname."\n";
@@ -85,7 +85,7 @@ foreach my $f (@files){
        }
        $cnt++;
 }
-print DD Dumper($outdata)."\n";
+print DD Dumper($outdata)."\n";
 #print keys(%{$outdata})." after filter!\n";
 foreach my $o (keys(%{$outdata})){
        foreach my $excl (@excludes){
@@ -107,7 +107,9 @@ foreach my $o (sort {$a <=> $b} keys(%{$outdata})){
                  $outdata->{$o}->{ftype}."<|>\n";
 }
 
-close(DD);
+#close(DD);
+
+
 sub getfileinfo(){
        my $file = shift;
        my $title = "";
diff --git a/tools/lib/perl5/Excel/Writer/XLSX.pm b/tools/lib/perl5/Excel/Writer/XLSX.pm
deleted file mode 100644 (file)
index 99e59ac..0000000
+++ /dev/null
@@ -1,7490 +0,0 @@
-package Excel::Writer::XLSX;
-
-###############################################################################
-#
-# Excel::Writer::XLSX - Create a new file in the Excel 2007+ XLSX format.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-use 5.008002;
-use strict;
-use warnings;
-use Exporter;
-
-use strict;
-use Excel::Writer::XLSX::Workbook;
-
-our @ISA     = qw(Excel::Writer::XLSX::Workbook Exporter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# new()
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Workbook->new( @_ );
-
-    # Check for file creation failures before re-blessing
-    bless $self, $class if defined $self;
-
-    return $self;
-}
-
-
-1;
-
-
-__END__
-
-
-
-=head1 NAME
-
-Excel::Writer::XLSX - Create a new file in the Excel 2007+ XLSX format.
-
-=head1 SYNOPSIS
-
-To write a string, a formatted string, a number and a formula to the first worksheet in an Excel workbook called perl.xlsx:
-
-    use Excel::Writer::XLSX;
-
-    # Create a new Excel workbook
-    my $workbook = Excel::Writer::XLSX->new( 'perl.xlsx' );
-
-    # Add a worksheet
-    $worksheet = $workbook->add_worksheet();
-
-    #  Add and define a format
-    $format = $workbook->add_format();
-    $format->set_bold();
-    $format->set_color( 'red' );
-    $format->set_align( 'center' );
-
-    # Write a formatted and unformatted string, row and column notation.
-    $col = $row = 0;
-    $worksheet->write( $row, $col, 'Hi Excel!', $format );
-    $worksheet->write( 1, $col, 'Hi Excel!' );
-
-    # Write a number and a formula using A1 notation
-    $worksheet->write( 'A3', 1.2345 );
-    $worksheet->write( 'A4', '=SIN(PI()/4)' );
-
-    $workbook->close();
-
-
-
-=head1 DESCRIPTION
-
-The C<Excel::Writer::XLSX> module can be used to create an Excel file in the 2007+ XLSX format.
-
-The XLSX format is the Office Open XML (OOXML) format used by Excel 2007 and later.
-
-Multiple worksheets can be added to a workbook and formatting can be applied to cells. Text, numbers, and formulas can be written to the cells.
-
-This module cannot, as yet, be used to write to an existing Excel XLSX file.
-
-
-
-
-=head1 Excel::Writer::XLSX and Spreadsheet::WriteExcel
-
-C<Excel::Writer::XLSX> uses the same interface as the L<Spreadsheet::WriteExcel> module which produces an Excel file in binary XLS format.
-
-Excel::Writer::XLSX supports all of the features of Spreadsheet::WriteExcel and in some cases has more functionality. For more details see L</Compatibility with Spreadsheet::WriteExcel>.
-
-The main advantage of the XLSX format over the XLS format is that it allows a larger number of rows and columns in a worksheet. The XLSX file format also produces much smaller files than the XLS file format.
-
-
-
-
-=head1 QUICK START
-
-Excel::Writer::XLSX tries to provide an interface to as many of Excel's features as possible. As a result there is a lot of documentation to accompany the interface and it can be difficult at first glance to see what it important and what is not. So for those of you who prefer to assemble Ikea furniture first and then read the instructions, here are four easy steps:
-
-1. Create a new Excel I<workbook> (i.e. file) using C<new()>.
-
-2. Add a worksheet to the new workbook using C<add_worksheet()>.
-
-3. Write to the worksheet using C<write()>.
-
-4. C<close()> the file.
-
-Like this:
-
-    use Excel::Writer::XLSX;                                   # Step 0
-
-    my $workbook = Excel::Writer::XLSX->new( 'perl.xlsx' );    # Step 1
-    $worksheet = $workbook->add_worksheet();                   # Step 2
-    $worksheet->write( 'A1', 'Hi Excel!' );                    # Step 3
-
-    $workbook->close();                                        # Step 4
-
-
-This will create an Excel file called C<perl.xlsx> with a single worksheet and the text C<'Hi Excel!'> in the relevant cell. And that's it. Okay, so there is actually a zeroth step as well, but C<use module> goes without saying. There are many examples that come with the distribution and which you can use to get you started. See L</EXAMPLES>.
-
-Those of you who read the instructions first and assemble the furniture afterwards will know how to proceed. ;-)
-
-
-
-
-=head1 WORKBOOK METHODS
-
-The Excel::Writer::XLSX module provides an object oriented interface to a new Excel workbook. The following methods are available through a new workbook.
-
-    new()
-    add_worksheet()
-    add_format()
-    add_chart()
-    add_shape()
-    add_vba_project()
-    set_vba_name()
-    close()
-    set_properties()
-    set_custom_property()
-    define_name()
-    set_tempdir()
-    set_custom_color()
-    sheets()
-    get_worksheet_by_name()
-    set_1904()
-    set_optimization()
-    set_calc_mode()
-    get_default_url_format()
-
-If you are unfamiliar with object oriented interfaces or the way that they are implemented in Perl have a look at C<perlobj> and C<perltoot> in the main Perl documentation.
-
-
-
-
-=head2 new()
-
-A new Excel workbook is created using the C<new()> constructor which accepts either a filename or a filehandle as a parameter. The following example creates a new Excel file based on a filename:
-
-    my $workbook  = Excel::Writer::XLSX->new( 'filename.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    $worksheet->write( 0, 0, 'Hi Excel!' );
-    $workbook->close();
-
-
-Here are some other examples of using C<new()> with filenames:
-
-    my $workbook1 = Excel::Writer::XLSX->new( $filename );
-    my $workbook2 = Excel::Writer::XLSX->new( '/tmp/filename.xlsx' );
-    my $workbook3 = Excel::Writer::XLSX->new( "c:\\tmp\\filename.xlsx" );
-    my $workbook4 = Excel::Writer::XLSX->new( 'c:\tmp\filename.xlsx' );
-
-The last two examples demonstrates how to create a file on DOS or Windows where it is necessary to either escape the directory separator C<\> or to use single quotes to ensure that it isn't interpolated. For more information see C<perlfaq5: Why can't I use "C:\temp\foo" in DOS paths?>.
-
-It is recommended that the filename uses the extension C<.xlsx> rather than C<.xls> since the latter causes an Excel warning when used with the XLSX format.
-
-The C<new()> constructor returns a Excel::Writer::XLSX object that you can use to add worksheets and store data. It should be noted that although C<my> is not specifically required it defines the scope of the new workbook variable and, in the majority of cases, ensures that the workbook is closed properly without explicitly calling the C<close()> method.
-
-If the file cannot be created, due to file permissions or some other reason,  C<new> will return C<undef>. Therefore, it is good practice to check the return value of C<new> before proceeding. As usual the Perl variable C<$!> will be set if there is a file creation error. You will also see one of the warning messages detailed in L</DIAGNOSTICS>:
-
-    my $workbook = Excel::Writer::XLSX->new( 'protected.xlsx' );
-    die "Problems creating new Excel file: $!" unless defined $workbook;
-
-You can also pass a valid filehandle to the C<new()> constructor. For example in a CGI program you could do something like this:
-
-    binmode( STDOUT );
-    my $workbook = Excel::Writer::XLSX->new( \*STDOUT );
-
-The requirement for C<binmode()> is explained below.
-
-See also, the C<cgi.pl> program in the C<examples> directory of the distro.
-
-In C<mod_perl> programs where you will have to do something like the following:
-
-    # mod_perl 1
-    ...
-    tie *XLSX, 'Apache';
-    binmode( XLSX );
-    my $workbook = Excel::Writer::XLSX->new( \*XLSX );
-    ...
-
-    # mod_perl 2
-    ...
-    tie *XLSX => $r;    # Tie to the Apache::RequestRec object
-    binmode( *XLSX );
-    my $workbook = Excel::Writer::XLSX->new( \*XLSX );
-    ...
-
-See also, the C<mod_perl1.pl> and C<mod_perl2.pl> programs in the C<examples> directory of the distro.
-
-Filehandles can also be useful if you want to stream an Excel file over a socket or if you want to store an Excel file in a scalar.
-
-For example here is a way to write an Excel file to a scalar:
-
-    #!/usr/bin/perl -w
-
-    use strict;
-    use Excel::Writer::XLSX;
-
-    open my $fh, '>', \my $str or die "Failed to open filehandle: $!";
-
-    my $workbook  = Excel::Writer::XLSX->new( $fh );
-    my $worksheet = $workbook->add_worksheet();
-
-    $worksheet->write( 0, 0, 'Hi Excel!' );
-
-    $workbook->close();
-
-    # The Excel file in now in $str. Remember to binmode() the output
-    # filehandle before printing it.
-    binmode STDOUT;
-    print $str;
-
-See also the C<write_to_scalar.pl> and C<filehandle.pl> programs in the C<examples> directory of the distro.
-
-B<Note about the requirement for> C<binmode()>. An Excel file is comprised of binary data. Therefore, if you are using a filehandle you should ensure that you C<binmode()> it prior to passing it to C<new()>.You should do this regardless of whether you are on a Windows platform or not.
-
-You don't have to worry about C<binmode()> if you are using filenames instead of filehandles. Excel::Writer::XLSX performs the C<binmode()> internally when it converts the filename to a filehandle. For more information about C<binmode()> see C<perlfunc> and C<perlopentut> in the main Perl documentation.
-
-
-
-
-
-=head2 add_worksheet( $sheetname )
-
-At least one worksheet should be added to a new workbook. A worksheet is used to write data into cells:
-
-    $worksheet1 = $workbook->add_worksheet();               # Sheet1
-    $worksheet2 = $workbook->add_worksheet( 'Foglio2' );    # Foglio2
-    $worksheet3 = $workbook->add_worksheet( 'Data' );       # Data
-    $worksheet4 = $workbook->add_worksheet();               # Sheet4
-
-If C<$sheetname> is not specified the default Excel convention will be followed, i.e. Sheet1, Sheet2, etc.
-
-The worksheet name must be a valid Excel worksheet name, i.e. it cannot contain any of the following characters, C<[ ] : * ? / \> and it must be less than 32 characters. In addition, you cannot use the same, case insensitive, C<$sheetname> for more than one worksheet.
-
-
-
-
-=head2 add_format( %properties )
-
-The C<add_format()> method can be used to create new Format objects which are used to apply formatting to a cell. You can either define the properties at creation time via a hash of property values or later via method calls.
-
-    $format1 = $workbook->add_format( %props );    # Set properties at creation
-    $format2 = $workbook->add_format();            # Set properties later
-
-See the L</CELL FORMATTING> section for more details about Format properties and how to set them.
-
-
-
-
-=head2 add_chart( %properties )
-
-This method is use to create a new chart either as a standalone worksheet (the default) or as an embeddable object that can be inserted into a worksheet via the C<insert_chart()> Worksheet method.
-
-    my $chart = $workbook->add_chart( type => 'column' );
-
-The properties that can be set are:
-
-    type     (required)
-    subtype  (optional)
-    name     (optional)
-    embedded (optional)
-
-=over
-
-=item * C<type>
-
-This is a required parameter. It defines the type of chart that will be created.
-
-    my $chart = $workbook->add_chart( type => 'line' );
-
-The available types are:
-
-    area
-    bar
-    column
-    line
-    pie
-    doughnut
-    scatter
-    stock
-
-=item * C<subtype>
-
-Used to define a chart subtype where available.
-
-    my $chart = $workbook->add_chart( type => 'bar', subtype => 'stacked' );
-
-See the L<Excel::Writer::XLSX::Chart> documentation for a list of available chart subtypes.
-
-=item * C<name>
-
-Set the name for the chart sheet. The name property is optional and if it isn't supplied will default to C<Chart1 .. n>. The name must be a valid Excel worksheet name. See C<add_worksheet()> for more details on valid sheet names. The C<name> property can be omitted for embedded charts.
-
-    my $chart = $workbook->add_chart( type => 'line', name => 'Results Chart' );
-
-=item * C<embedded>
-
-Specifies that the Chart object will be inserted in a worksheet via the C<insert_chart()> Worksheet method. It is an error to try insert a Chart that doesn't have this flag set.
-
-    my $chart = $workbook->add_chart( type => 'line', embedded => 1 );
-
-    # Configure the chart.
-    ...
-
-    # Insert the chart into the a worksheet.
-    $worksheet->insert_chart( 'E2', $chart );
-
-=back
-
-See Excel::Writer::XLSX::Chart for details on how to configure the chart object once it is created. See also the C<chart_*.pl> programs in the examples directory of the distro.
-
-
-
-=head2 add_shape( %properties )
-
-The C<add_shape()> method can be used to create new shapes that may be inserted into a worksheet.
-
-You can either define the properties at creation time via a hash of property values or later via method calls.
-
-    # Set properties at creation.
-    $plus = $workbook->add_shape(
-        type   => 'plus',
-        id     => 3,
-        width  => $pw,
-        height => $ph
-    );
-
-
-    # Default rectangle shape. Set properties later.
-    $rect =  $workbook->add_shape();
-
-See L<Excel::Writer::XLSX::Shape> for details on how to configure the shape object once it is created.
-
-See also the C<shape*.pl> programs in the examples directory of the distro.
-
-
-
-=head2 add_vba_project( 'vbaProject.bin' )
-
-The C<add_vba_project()> method can be used to add macros or functions to an Excel::Writer::XLSX file using a binary VBA project file that has been extracted from an existing Excel C<xlsm> file.
-
-    my $workbook  = Excel::Writer::XLSX->new( 'file.xlsm' );
-
-    $workbook->add_vba_project( './vbaProject.bin' );
-
-The supplied C<extract_vba> utility can be used to extract the required C<vbaProject.bin> file from an existing Excel file:
-
-    $ extract_vba file.xlsm
-    Extracted 'vbaProject.bin' successfully
-
-Macros can be tied to buttons using the worksheet C<insert_button()> method (see the L</WORKSHEET METHODS> section for details):
-
-    $worksheet->insert_button( 'C2', { macro => 'my_macro' } );
-
-Note, Excel uses the file extension C<xlsm> instead of C<xlsx> for files that contain macros. It is advisable to follow the same convention.
-
-See also the C<macros.pl> example file and the L<WORKING WITH VBA MACROS>.
-
-
-
-=head2 set_vba_name()
-
-The C<set_vba_name()> method can be used to set the VBA codename for the workbook. This is sometimes required when a C<vbaProject macro> included via C<add_vba_project()> refers to the workbook. The default Excel VBA name of C<ThisWorkbook> is used if a user defined name isn't specified. See also L<WORKING WITH VBA MACROS>.
-
-
-=head2 close()
-
-In general your Excel file will be closed automatically when your program ends or when the Workbook object goes out of scope. However it is recommended to explicitly call the C<close()> method close the Excel file and avoid the potential issues outlined below. The C<close()> method is called like this:
-
-    $workbook->close();
-
-The return value of C<close()> is the same as that returned by perl when it closes the file created by C<new()>. This allows you to handle error conditions in the usual way:
-
-    $workbook->close() or die "Error closing file: $!";
-
-An explicit C<close()> is required if the file must be closed prior to performing some external action on it such as copying it, reading its size or attaching it to an email.
-
-In addition, C<close()> may be required to prevent perl's garbage collector from disposing of the Workbook, Worksheet and Format objects in the wrong order. Situations where this can occur are:
-
-=over 4
-
-=item *
-
-If C<my()> was not used to declare the scope of a workbook variable created using C<new()>.
-
-=item *
-
-If the C<new()>, C<add_worksheet()> or C<add_format()> methods are called in subroutines.
-
-=back
-
-The reason for this is that Excel::Writer::XLSX relies on Perl's C<DESTROY> mechanism to trigger destructor methods in a specific sequence. This may not happen in cases where the Workbook, Worksheet and Format variables are not lexically scoped or where they have different lexical scopes.
-
-To avoid these issues it is recommended that you always close the Excel::Writer::XLSX filehandle using C<close()>.
-
-
-
-
-=head2 set_size( $width, $height )
-
-The C<set_size()> method can be used to set the size of a workbook window.
-
-    $workbook->set_size(1200, 800);
-
-The Excel window size was used in Excel 2007 to define the width and height of a workbook window within the Multiple Document Interface (MDI). In later versions of Excel for Windows this interface was dropped. This method is currently only useful when setting the window size in Excel for Mac 2011. The units are pixels and the default size is 1073 x 644.
-
-Note, this doesn't equate exactly to the Excel for Mac pixel size since it is based on the original Excel 2007 for Windows sizing.
-
-
-
-
-=head2 set_properties()
-
-The C<set_properties> method can be used to set the document properties of the Excel file created by C<Excel::Writer::XLSX>. These properties are visible when you use the C<< Office Button -> Prepare -> Properties >> option in Excel and are also available to external applications that read or index Windows files.
-
-The properties should be passed in hash format as follows:
-
-    $workbook->set_properties(
-        title    => 'This is an example spreadsheet',
-        author   => 'John McNamara',
-        comments => 'Created with Perl and Excel::Writer::XLSX',
-    );
-
-The properties that can be set are:
-
-    title
-    subject
-    author
-    manager
-    company
-    category
-    keywords
-    comments
-    status
-    hyperlink_base
-    created - File create date. Such be an aref of gmtime() values.
-
-See also the C<properties.pl> program in the examples directory of the distro.
-
-
-
-
-=head2 set_custom_property( $name, $value, $type)
-
-The C<set_custom_property> method can be used to set one of more custom document properties not covered by the C<set_properties()> method above. These properties are visible when you use the C<< Office Button -> Prepare -> Properties -> Advanced Properties -> Custom >> option in Excel and are also available to external applications that read or index Windows files.
-
-The C<set_custom_property> method takes 3 parameters:
-
-    $workbook-> set_custom_property( $name, $value, $type);
-
-Where the available types are:
-
-    text
-    date
-    number
-    bool
-
-For example:
-
-    $workbook->set_custom_property( 'Checked by',      'Eve',                  'text'   );
-    $workbook->set_custom_property( 'Date completed',  '2016-12-12T23:00:00Z', 'date'   );
-    $workbook->set_custom_property( 'Document number', '12345' ,               'number' );
-    $workbook->set_custom_property( 'Reference',       '1.2345',               'number' );
-    $workbook->set_custom_property( 'Has review',      1,                      'bool'   );
-    $workbook->set_custom_property( 'Signed off',      0,                      'bool'   );
-    $workbook->set_custom_property( 'Department',      $some_string,           'text'   );
-    $workbook->set_custom_property( 'Scale',           '1.2345678901234',      'number' );
-
-Dates should by in ISO8601 C<yyyy-mm-ddThh:mm:ss.sssZ> date format in Zulu time, as shown above.
-
-The C<text> and C<number> types are optional since they can usually be inferred from the data:
-
-    $workbook->set_custom_property( 'Checked by', 'Eve'    );
-    $workbook->set_custom_property( 'Reference',  '1.2345' );
-
-
-The C<$name> and C<$value> parameters are limited to 255 characters by Excel.
-
-
-
-
-=head2 define_name()
-
-This method is used to defined a name that can be used to represent a value, a single cell or a range of cells in a workbook.
-
-For example to set a global/workbook name:
-
-    # Global/workbook names.
-    $workbook->define_name( 'Exchange_rate', '=0.96' );
-    $workbook->define_name( 'Sales',         '=Sheet1!$G$1:$H$10' );
-
-It is also possible to define a local/worksheet name by prefixing the name with the sheet name using the syntax C<sheetname!definedname>:
-
-    # Local/worksheet name.
-    $workbook->define_name( 'Sheet2!Sales',  '=Sheet2!$G$1:$G$10' );
-
-If the sheet name contains spaces or special characters you must enclose it in single quotes like in Excel:
-
-    $workbook->define_name( "'New Data'!Sales",  '=Sheet2!$G$1:$G$10' );
-
-See the defined_name.pl program in the examples dir of the distro.
-
-Refer to the following to see Excel's syntax rules for defined names: L<http://office.microsoft.com/en-001/excel-help/define-and-use-names-in-formulas-HA010147120.aspx#BMsyntax_rules_for_names>
-
-
-
-
-=head2 set_tempdir()
-
-C<Excel::Writer::XLSX> stores worksheet data in temporary files prior to assembling the final workbook.
-
-The C<File::Temp> module is used to create these temporary files. File::Temp uses C<File::Spec> to determine an appropriate location for these files such as C</tmp> or C<c:\windows\temp>. You can find out which directory is used on your system as follows:
-
-    perl -MFile::Spec -le "print File::Spec->tmpdir()"
-
-If the default temporary file directory isn't accessible to your application, or doesn't contain enough space, you can specify an alternative location using the C<set_tempdir()> method:
-
-    $workbook->set_tempdir( '/tmp/writeexcel' );
-    $workbook->set_tempdir( 'c:\windows\temp\writeexcel' );
-
-The directory for the temporary file must exist, C<set_tempdir()> will not create a new directory.
-
-
-
-
-
-=head2 set_custom_color( $index, $red, $green, $blue )
-
-The method is maintained for backward compatibility with Spreadsheet::WriteExcel. Excel::Writer::XLSX programs don't require this method and colours can be specified using a Html style C<#RRGGBB> value, see L</WORKING WITH COLOURS>.
-
-
-
-
-=head2 sheets( 0, 1, ... )
-
-The C<sheets()> method returns a list, or a sliced list, of the worksheets in a workbook.
-
-If no arguments are passed the method returns a list of all the worksheets in the workbook. This is useful if you want to repeat an operation on each worksheet:
-
-    for $worksheet ( $workbook->sheets() ) {
-        print $worksheet->get_name();
-    }
-
-
-You can also specify a slice list to return one or more worksheet objects:
-
-    $worksheet = $workbook->sheets( 0 );
-    $worksheet->write( 'A1', 'Hello' );
-
-
-Or since the return value from C<sheets()> is a reference to a worksheet object you can write the above example as:
-
-    $workbook->sheets( 0 )->write( 'A1', 'Hello' );
-
-
-The following example returns the first and last worksheet in a workbook:
-
-    for $worksheet ( $workbook->sheets( 0, -1 ) ) {
-        # Do something
-    }
-
-
-Array slices are explained in the C<perldata> manpage.
-
-
-
-
-=head2 get_worksheet_by_name()
-
-The C<get_worksheet_by_name()> function return a worksheet or chartsheet object in the workbook using the sheetname:
-
-    $worksheet = $workbook->get_worksheet_by_name('Sheet1');
-
-
-
-
-=head2 set_1904()
-
-Excel stores dates as real numbers where the integer part stores the number of days since the epoch and the fractional part stores the percentage of the day. The epoch can be either 1900 or 1904. Excel for Windows uses 1900 and Excel for Macintosh uses 1904. However, Excel on either platform will convert automatically between one system and the other.
-
-Excel::Writer::XLSX stores dates in the 1900 format by default. If you wish to change this you can call the C<set_1904()> workbook method. You can query the current value by calling the C<get_1904()> workbook method. This returns 0 for 1900 and 1 for 1904.
-
-See also L</DATES AND TIME IN EXCEL> for more information about working with Excel's date system.
-
-In general you probably won't need to use C<set_1904()>.
-
-
-
-
-=head2 set_optimization()
-
-The C<set_optimization()> method is used to turn on optimizations in the Excel::Writer::XLSX module. Currently there is only one optimization available and that is to reduce memory usage.
-
-    $workbook->set_optimization();
-
-
-See L</SPEED AND MEMORY USAGE> for more background information.
-
-Note, that with this optimization turned on a row of data is written and then discarded when a cell in a new row is added via one of the Worksheet C<write_*()> methods. As such data should be written in sequential row order once the optimization is turned on.
-
-This method must be called before any calls to C<add_worksheet()>.
-
-
-
-=head2 set_calc_mode( $mode )
-
-Set the calculation mode for formulas in the workbook. This is mainly of use for workbooks with slow formulas where you want to allow the user to calculate them manually.
-
-The mode parameter can be one of the following strings:
-
-=over
-
-=item C<auto>
-
-The default. Excel will re-calculate formulas when a formula or a value affecting the formula changes.
-
-=item C<manual>
-
-Only re-calculate formulas when the user requires it. Generally by pressing F9.
-
-=item C<auto_except_tables>
-
-Excel will automatically re-calculate formulas except for tables.
-
-=back
-
-
-
-
-=head2 get_default_url_format()
-
-The C<get_default_url_format()> method gets a copy of the default url format used when a user defined format isn't specified with the worksheet C<write_url()> method. The format is the hyperlink style defined by Excel for the default theme:
-
-    my $url_format = $workbook->get_default_url_format();
-
-
-
-
-=head1 WORKSHEET METHODS
-
-A new worksheet is created by calling the C<add_worksheet()> method from a workbook object:
-
-    $worksheet1 = $workbook->add_worksheet();
-    $worksheet2 = $workbook->add_worksheet();
-
-The following methods are available through a new worksheet:
-
-    write()
-    write_number()
-    write_string()
-    write_rich_string()
-    keep_leading_zeros()
-    write_blank()
-    write_row()
-    write_col()
-    write_date_time()
-    write_url()
-    write_url_range()
-    write_formula()
-    write_boolean()
-    write_comment()
-    show_comments()
-    set_comments_author()
-    add_write_handler()
-    insert_image()
-    insert_chart()
-    insert_shape()
-    insert_button()
-    data_validation()
-    conditional_formatting()
-    add_sparkline()
-    add_table()
-    get_name()
-    activate()
-    select()
-    hide()
-    set_first_sheet()
-    protect()
-    set_selection()
-    set_row()
-    set_default_row()
-    set_column()
-    outline_settings()
-    freeze_panes()
-    split_panes()
-    merge_range()
-    merge_range_type()
-    set_zoom()
-    right_to_left()
-    hide_zero()
-    set_tab_color()
-    autofilter()
-    filter_column()
-    filter_column_list()
-    set_vba_name()
-
-
-
-=head2 Cell notation
-
-Excel::Writer::XLSX 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. 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', '=H1+1' );
-
-In formulas and applicable methods you can also use the C<A:A> column notation:
-
-    $worksheet->write( 'A1', '=SUM(B:B)' );
-
-The C<Excel::Writer::XLSX::Utility> module that is included in the distro contains helper functions for dealing with A1 notation, for example:
-
-    use Excel::Writer::XLSX::Utility;
-
-    ( $row, $col ) = xl_cell_to_rowcol( 'C2' );    # (1, 2)
-    $str           = xl_rowcol_to_cell( 1, 2 );    # C2
-
-For simplicity, the parameter lists for the worksheet method calls in the following sections are given in terms of row-column notation. In all cases it is also possible to use A1 notation.
-
-Note: in Excel it is also possible to use a R1C1 notation. This is not supported by Excel::Writer::XLSX.
-
-
-
-
-=head2 write( $row, $column, $token, $format )
-
-Excel makes a distinction between data types such as strings, numbers, blanks, formulas and hyperlinks. To simplify the process of writing data the C<write()> method acts as a general alias for several more specific methods:
-
-    write_string()
-    write_number()
-    write_blank()
-    write_formula()
-    write_url()
-    write_row()
-    write_col()
-
-The general rule is that if the data looks like a I<something> then a I<something> is written. Here are some examples in both row-column and A1 notation:
-
-                                                        # Same as:
-    $worksheet->write( 0, 0, 'Hello'                 ); # write_string()
-    $worksheet->write( 1, 0, 'One'                   ); # write_string()
-    $worksheet->write( 2, 0,  2                      ); # write_number()
-    $worksheet->write( 3, 0,  3.00001                ); # write_number()
-    $worksheet->write( 4, 0,  ""                     ); # write_blank()
-    $worksheet->write( 5, 0,  ''                     ); # write_blank()
-    $worksheet->write( 6, 0,  undef                  ); # write_blank()
-    $worksheet->write( 7, 0                          ); # write_blank()
-    $worksheet->write( 8, 0,  'http://www.perl.com/' ); # write_url()
-    $worksheet->write( 'A9',  'ftp://ftp.cpan.org/'  ); # write_url()
-    $worksheet->write( 'A10', 'internal:Sheet1!A1'   ); # write_url()
-    $worksheet->write( 'A11', 'external:c:\foo.xlsx' ); # write_url()
-    $worksheet->write( 'A12', '=A3 + 3*A4'           ); # write_formula()
-    $worksheet->write( 'A13', '=SIN(PI()/4)'         ); # write_formula()
-    $worksheet->write( 'A14', \@array                ); # write_row()
-    $worksheet->write( 'A15', [\@array]              ); # write_col()
-
-    # And if the keep_leading_zeros property is set:
-    $worksheet->write( 'A16', '2'                    ); # write_number()
-    $worksheet->write( 'A17', '02'                   ); # write_string()
-    $worksheet->write( 'A18', '00002'                ); # write_string()
-
-    # Write an array formula. Not available in Spreadsheet::WriteExcel.
-    $worksheet->write( 'A19', '{=SUM(A1:B1*A2:B2)}'  ); # write_formula()
-
-
-The "looks like" rule is defined by regular expressions:
-
-C<write_number()> if C<$token> is a number based on the following regex: C<$token =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/>.
-
-C<write_string()> if C<keep_leading_zeros()> is set and C<$token> is an integer with leading zeros based on the following regex: C<$token =~ /^0\d+$/>.
-
-C<write_blank()> if C<$token> is undef or a blank string: C<undef>, C<""> or C<''>.
-
-C<write_url()> if C<$token> is a http, https, ftp or mailto URL based on the following regexes: C<$token =~ m|^[fh]tt?ps?://|> or C<$token =~ m|^mailto:|>.
-
-C<write_url()> if C<$token> is an internal or external sheet reference based on the following regex: C<$token =~ m[^(in|ex)ternal:]>.
-
-C<write_formula()> if the first character of C<$token> is C<"=">.
-
-C<write_array_formula()> if the C<$token> matches C</^{=.*}$/>.
-
-C<write_row()> if C<$token> is an array ref.
-
-C<write_col()> if C<$token> is an array ref of array refs.
-
-C<write_string()> if none of the previous conditions apply.
-
-The C<$format> parameter is optional. It should be a valid Format object, see L</CELL FORMATTING>:
-
-    my $format = $workbook->add_format();
-    $format->set_bold();
-    $format->set_color( 'red' );
-    $format->set_align( 'center' );
-
-    $worksheet->write( 4, 0, 'Hello', $format );    # Formatted string
-
-The write() method will ignore empty strings or C<undef> tokens unless a format is also supplied. As such you needn't worry about special handling for empty or C<undef> values in your data. See also the C<write_blank()> method.
-
-One problem with the C<write()> method is that occasionally data looks like a number but you don't want it treated as a number. For example, zip codes or ID numbers often start with a leading zero. If you write this data as a number then the leading zero(s) will be stripped. You can change this default behaviour by using the C<keep_leading_zeros()> method. While this property is in place any integers with leading zeros will be treated as strings and the zeros will be preserved. See the C<keep_leading_zeros()> section for a full discussion of this issue.
-
-You can also add your own data handlers to the C<write()> method using C<add_write_handler()>.
-
-The C<write()> method will also handle Unicode strings in C<UTF-8> format.
-
-The C<write> methods return:
-
-    0 for success.
-   -1 for insufficient number of arguments.
-   -2 for row or column out of bounds.
-   -3 for string too long.
-
-
-
-
-=head2 write_number( $row, $column, $number, $format )
-
-Write an integer or a float to the cell specified by C<$row> and C<$column>:
-
-    $worksheet->write_number( 0, 0, 123456 );
-    $worksheet->write_number( 'A2', 2.3451 );
-
-See the note about L</Cell notation>. The C<$format> parameter is optional.
-
-In general it is sufficient to use the C<write()> method.
-
-B<Note>: some versions of Excel 2007 do not display the calculated values of formulas written by Excel::Writer::XLSX. Applying all available Service Packs to Excel should fix this.
-
-
-
-=head2 write_string( $row, $column, $string, $format )
-
-Write a string to the cell specified by C<$row> and C<$column>:
-
-    $worksheet->write_string( 0, 0, 'Your text here' );
-    $worksheet->write_string( 'A2', 'or here' );
-
-The maximum string size is 32767 characters. However the maximum string segment that Excel can display in a cell is 1000. All 32767 characters can be displayed in the formula bar.
-
-The C<$format> parameter is optional.
-
-The C<write()> method will also handle strings in C<UTF-8> format. See also the C<unicode_*.pl> programs in the examples directory of the distro.
-
-In general it is sufficient to use the C<write()> method. However, you may sometimes wish to use the C<write_string()> method to write data that looks like a number but that you don't want treated as a number. For example, zip codes or phone numbers:
-
-    # Write as a plain string
-    $worksheet->write_string( 'A1', '01209' );
-
-However, if the user edits this string Excel may convert it back to a number. To get around this you can use the Excel text format C<@>:
-
-    # Format as a string. Doesn't change to a number when edited
-    my $format1 = $workbook->add_format( num_format => '@' );
-    $worksheet->write_string( 'A2', '01209', $format1 );
-
-See also the note about L</Cell notation>.
-
-
-
-
-=head2 write_rich_string( $row, $column, $format, $string, ..., $cell_format )
-
-The C<write_rich_string()> method is used to write strings with multiple formats. For example to write the string "This is B<bold> and this is I<italic>" you would use the following:
-
-    my $bold   = $workbook->add_format( bold   => 1 );
-    my $italic = $workbook->add_format( italic => 1 );
-
-    $worksheet->write_rich_string( 'A1',
-        'This is ', $bold, 'bold', ' and this is ', $italic, 'italic' );
-
-The basic rule is to break the string into fragments and put a C<$format> object before the fragment that you want to format. For example:
-
-    # Unformatted string.
-      'This is an example string'
-
-    # Break it into fragments.
-      'This is an ', 'example', ' string'
-
-    # Add formatting before the fragments you want formatted.
-      'This is an ', $format, 'example', ' string'
-
-    # In Excel::Writer::XLSX.
-    $worksheet->write_rich_string( 'A1',
-        'This is an ', $format, 'example', ' string' );
-
-String fragments that don't have a format are given a default format. So for example when writing the string "Some B<bold> text" you would use the first example below but it would be equivalent to the second:
-
-    # With default formatting:
-    my $bold    = $workbook->add_format( bold => 1 );
-
-    $worksheet->write_rich_string( 'A1',
-        'Some ', $bold, 'bold', ' text' );
-
-    # Or more explicitly:
-    my $bold    = $workbook->add_format( bold => 1 );
-    my $default = $workbook->add_format();
-
-    $worksheet->write_rich_string( 'A1',
-        $default, 'Some ', $bold, 'bold', $default, ' text' );
-
-As with Excel, only the font properties of the format such as font name, style, size, underline, color and effects are applied to the string fragments. Other features such as border, background, text wrap and alignment must be applied to the cell.
-
-The C<write_rich_string()> method allows you to do this by using the last argument as a cell format (if it is a format object). The following example centers a rich string in the cell:
-
-    my $bold   = $workbook->add_format( bold  => 1 );
-    my $center = $workbook->add_format( align => 'center' );
-
-    $worksheet->write_rich_string( 'A5',
-        'Some ', $bold, 'bold text', ' centered', $center );
-
-See the C<rich_strings.pl> example in the distro for more examples.
-
-    my $bold   = $workbook->add_format( bold        => 1 );
-    my $italic = $workbook->add_format( italic      => 1 );
-    my $red    = $workbook->add_format( color       => 'red' );
-    my $blue   = $workbook->add_format( color       => 'blue' );
-    my $center = $workbook->add_format( align       => 'center' );
-    my $super  = $workbook->add_format( font_script => 1 );
-
-
-    # Write some strings with multiple formats.
-    $worksheet->write_rich_string( 'A1',
-        'This is ', $bold, 'bold', ' and this is ', $italic, 'italic' );
-
-    $worksheet->write_rich_string( 'A3',
-        'This is ', $red, 'red', ' and this is ', $blue, 'blue' );
-
-    $worksheet->write_rich_string( 'A5',
-        'Some ', $bold, 'bold text', ' centered', $center );
-
-    $worksheet->write_rich_string( 'A7',
-        $italic, 'j = k', $super, '(n-1)', $center );
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/rich_strings.jpg" width="640" height="420" alt="Output from rich_strings.pl" /></center></p>
-
-=end html
-
-As with C<write_sting()> the maximum string size is 32767 characters. See also the note about L</Cell notation>.
-
-
-
-
-=head2 keep_leading_zeros()
-
-This method changes the default handling of integers with leading zeros when using the C<write()> method.
-
-The C<write()> method uses regular expressions to determine what type of data to write to an Excel worksheet. If the data looks like a number it writes a number using C<write_number()>. One problem with this approach is that occasionally data looks like a number but you don't want it treated as a number.
-
-Zip codes and ID numbers, for example, often start with a leading zero. If you write this data as a number then the leading zero(s) will be stripped. This is the also the default behaviour when you enter data manually in Excel.
-
-To get around this you can use one of three options. Write a formatted number, write the number as a string or use the C<keep_leading_zeros()> method to change the default behaviour of C<write()>:
-
-    # Implicitly write a number, the leading zero is removed: 1209
-    $worksheet->write( 'A1', '01209' );
-
-    # Write a zero padded number using a format: 01209
-    my $format1 = $workbook->add_format( num_format => '00000' );
-    $worksheet->write( 'A2', '01209', $format1 );
-
-    # Write explicitly as a string: 01209
-    $worksheet->write_string( 'A3', '01209' );
-
-    # Write implicitly as a string: 01209
-    $worksheet->keep_leading_zeros();
-    $worksheet->write( 'A4', '01209' );
-
-
-The above code would generate a worksheet that looked like the following:
-
-     -----------------------------------------------------------
-    |   |     A     |     B     |     C     |     D     | ...
-     -----------------------------------------------------------
-    | 1 |      1209 |           |           |           | ...
-    | 2 |     01209 |           |           |           | ...
-    | 3 | 01209     |           |           |           | ...
-    | 4 | 01209     |           |           |           | ...
-
-
-The examples are on different sides of the cells due to the fact that Excel displays strings with a left justification and numbers with a right justification by default. You can change this by using a format to justify the data, see L</CELL FORMATTING>.
-
-It should be noted that if the user edits the data in examples C<A3> and C<A4> the strings will revert back to numbers. Again this is Excel's default behaviour. To avoid this you can use the text format C<@>:
-
-    # Format as a string (01209)
-    my $format2 = $workbook->add_format( num_format => '@' );
-    $worksheet->write_string( 'A5', '01209', $format2 );
-
-The C<keep_leading_zeros()> property is off by default. The C<keep_leading_zeros()> method takes 0 or 1 as an argument. It defaults to 1 if an argument isn't specified:
-
-    $worksheet->keep_leading_zeros();       # Set on
-    $worksheet->keep_leading_zeros( 1 );    # Set on
-    $worksheet->keep_leading_zeros( 0 );    # Set off
-
-See also the C<add_write_handler()> method.
-
-
-=head2 write_blank( $row, $column, $format )
-
-Write a blank cell specified by C<$row> and C<$column>:
-
-    $worksheet->write_blank( 0, 0, $format );
-
-This method is used to add formatting to a cell which doesn't contain a string or number value.
-
-Excel differentiates between an "Empty" cell and a "Blank" cell. An "Empty" cell is a cell which doesn't contain data whilst a "Blank" cell is a cell which doesn't contain data but does contain formatting. Excel stores "Blank" cells but ignores "Empty" cells.
-
-As such, if you write an empty cell without formatting it is ignored:
-
-    $worksheet->write( 'A1', undef, $format );    # write_blank()
-    $worksheet->write( 'A2', undef );             # Ignored
-
-This seemingly uninteresting fact means that you can write arrays of data without special treatment for C<undef> or empty string values.
-
-See the note about L</Cell notation>.
-
-
-
-
-=head2 write_row( $row, $column, $array_ref, $format )
-
-The C<write_row()> method can be used to write a 1D or 2D array of data in one go. This is useful for converting the results of a database query into an Excel worksheet. You must pass a reference to the array of data rather than the array itself. The C<write()> method is then called for each element of the data. For example:
-
-    @array = ( 'awk', 'gawk', 'mawk' );
-    $array_ref = \@array;
-
-    $worksheet->write_row( 0, 0, $array_ref );
-
-    # The above example is equivalent to:
-    $worksheet->write( 0, 0, $array[0] );
-    $worksheet->write( 0, 1, $array[1] );
-    $worksheet->write( 0, 2, $array[2] );
-
-
-Note: For convenience the C<write()> method behaves in the same way as C<write_row()> if it is passed an array reference. Therefore the following two method calls are equivalent:
-
-    $worksheet->write_row( 'A1', $array_ref );    # Write a row of data
-    $worksheet->write(     'A1', $array_ref );    # Same thing
-
-As with all of the write methods the C<$format> parameter is optional. If a format is specified it is applied to all the elements of the data array.
-
-Array references within the data will be treated as columns. This allows you to write 2D arrays of data in one go. For example:
-
-    @eec =  (
-                ['maggie', 'milly', 'molly', 'may'  ],
-                [13,       14,      15,      16     ],
-                ['shell',  'star',  'crab',  'stone']
-            );
-
-    $worksheet->write_row( 'A1', \@eec );
-
-
-Would produce a worksheet as follows:
-
-     -----------------------------------------------------------
-    |   |    A    |    B    |    C    |    D    |    E    | ...
-     -----------------------------------------------------------
-    | 1 | maggie  | 13      | shell   | ...     |  ...    | ...
-    | 2 | milly   | 14      | star    | ...     |  ...    | ...
-    | 3 | molly   | 15      | crab    | ...     |  ...    | ...
-    | 4 | may     | 16      | stone   | ...     |  ...    | ...
-    | 5 | ...     | ...     | ...     | ...     |  ...    | ...
-    | 6 | ...     | ...     | ...     | ...     |  ...    | ...
-
-
-To write the data in a row-column order refer to the C<write_col()> method below.
-
-Any C<undef> values in the data will be ignored unless a format is applied to the data, in which case a formatted blank cell will be written. In either case the appropriate row or column value will still be incremented.
-
-To find out more about array references refer to C<perlref> and C<perlreftut> in the main Perl documentation. To find out more about 2D arrays or "lists of lists" refer to C<perllol>.
-
-The C<write_row()> method returns the first error encountered when writing the elements of the data or zero if no errors were encountered. See the return values described for the C<write()> method above.
-
-See also the C<write_arrays.pl> program in the C<examples> directory of the distro.
-
-The C<write_row()> method allows the following idiomatic conversion of a text file to an Excel file:
-
-    #!/usr/bin/perl -w
-
-    use strict;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'file.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    open INPUT, 'file.txt' or die "Couldn't open file: $!";
-
-    $worksheet->write( $. -1, 0, [split] ) while <INPUT>;
-
-    $workbook->close();
-
-
-
-=head2 write_col( $row, $column, $array_ref, $format )
-
-The C<write_col()> method can be used to write a 1D or 2D array of data in one go. This is useful for converting the results of a database query into an Excel worksheet. You must pass a reference to the array of data rather than the array itself. The C<write()> method is then called for each element of the data. For example:
-
-    @array = ( 'awk', 'gawk', 'mawk' );
-    $array_ref = \@array;
-
-    $worksheet->write_col( 0, 0, $array_ref );
-
-    # The above example is equivalent to:
-    $worksheet->write( 0, 0, $array[0] );
-    $worksheet->write( 1, 0, $array[1] );
-    $worksheet->write( 2, 0, $array[2] );
-
-As with all of the write methods the C<$format> parameter is optional. If a format is specified it is applied to all the elements of the data array.
-
-Array references within the data will be treated as rows. This allows you to write 2D arrays of data in one go. For example:
-
-    @eec =  (
-                ['maggie', 'milly', 'molly', 'may'  ],
-                [13,       14,      15,      16     ],
-                ['shell',  'star',  'crab',  'stone']
-            );
-
-    $worksheet->write_col( 'A1', \@eec );
-
-
-Would produce a worksheet as follows:
-
-     -----------------------------------------------------------
-    |   |    A    |    B    |    C    |    D    |    E    | ...
-     -----------------------------------------------------------
-    | 1 | maggie  | milly   | molly   | may     |  ...    | ...
-    | 2 | 13      | 14      | 15      | 16      |  ...    | ...
-    | 3 | shell   | star    | crab    | stone   |  ...    | ...
-    | 4 | ...     | ...     | ...     | ...     |  ...    | ...
-    | 5 | ...     | ...     | ...     | ...     |  ...    | ...
-    | 6 | ...     | ...     | ...     | ...     |  ...    | ...
-
-
-To write the data in a column-row order refer to the C<write_row()> method above.
-
-Any C<undef> values in the data will be ignored unless a format is applied to the data, in which case a formatted blank cell will be written. In either case the appropriate row or column value will still be incremented.
-
-As noted above the C<write()> method can be used as a synonym for C<write_row()> and C<write_row()> handles nested array refs as columns. Therefore, the following two method calls are equivalent although the more explicit call to C<write_col()> would be preferable for maintainability:
-
-    $worksheet->write_col( 'A1', $array_ref     ); # Write a column of data
-    $worksheet->write(     'A1', [ $array_ref ] ); # Same thing
-
-To find out more about array references refer to C<perlref> and C<perlreftut> in the main Perl documentation. To find out more about 2D arrays or "lists of lists" refer to C<perllol>.
-
-The C<write_col()> method returns the first error encountered when writing the elements of the data or zero if no errors were encountered. See the return values described for the C<write()> method above.
-
-See also the C<write_arrays.pl> program in the C<examples> directory of the distro.
-
-
-
-
-=head2 write_date_time( $row, $col, $date_string, $format )
-
-The C<write_date_time()> method can be used to write a date or time to the cell specified by C<$row> and C<$column>:
-
-    $worksheet->write_date_time( 'A1', '2004-05-13T23:20', $date_format );
-
-The C<$date_string> should be in the following format:
-
-    yyyy-mm-ddThh:mm:ss.sss
-
-This conforms to an ISO8601 date but it should be noted that the full range of ISO8601 formats are not supported.
-
-The following variations on the C<$date_string> parameter are permitted:
-
-    yyyy-mm-ddThh:mm:ss.sss         # Standard format
-    yyyy-mm-ddT                     # No time
-              Thh:mm:ss.sss         # No date
-    yyyy-mm-ddThh:mm:ss.sssZ        # Additional Z (but not time zones)
-    yyyy-mm-ddThh:mm:ss             # No fractional seconds
-    yyyy-mm-ddThh:mm                # No seconds
-
-Note that the C<T> is required in all cases.
-
-A date should always have a C<$format>, otherwise it will appear as a number, see L</DATES AND TIME IN EXCEL> and L</CELL FORMATTING>. Here is a typical example:
-
-    my $date_format = $workbook->add_format( num_format => 'mm/dd/yy' );
-    $worksheet->write_date_time( 'A1', '2004-05-13T23:20', $date_format );
-
-Valid dates should be in the range 1900-01-01 to 9999-12-31, for the 1900 epoch and 1904-01-01 to 9999-12-31, for the 1904 epoch. As with Excel, dates outside these ranges will be written as a string.
-
-See also the date_time.pl program in the C<examples> directory of the distro.
-
-
-
-
-=head2 write_url( $row, $col, $url, $format, $label )
-
-Write a hyperlink to a URL in the cell specified by C<$row> and C<$column>. The hyperlink is comprised of two elements: the visible label and the invisible link. The visible label is the same as the link unless an alternative label is specified. The C<$label> parameter is optional. The label is written using the C<write()> method. Therefore it is possible to write strings, numbers or formulas as labels.
-
-The C<$format> parameter is also optional and the default Excel hyperlink style will be used if it isn't specified. If required you can access the default url format using the Workbook C<get_default_url_format> method:
-
-    my $url_format = $workbook->get_default_url_format();
-
-There are four web style URI's supported: C<http://>, C<https://>, C<ftp://> and C<mailto:>:
-
-    $worksheet->write_url( 0, 0, 'ftp://www.perl.org/' );
-    $worksheet->write_url( 'A3', 'http://www.perl.com/' );
-    $worksheet->write_url( 'A4', 'mailto:jmcnamara@cpan.org' );
-
-You can display an alternative string using the C<$label> parameter:
-
-    $worksheet->write_url( 1, 0, 'http://www.perl.com/', undef, 'Perl' );
-
-If you wish to have some other cell data such as a number or a formula you can overwrite the cell using another call to C<write_*()>:
-
-    $worksheet->write_url( 'A1', 'http://www.perl.com/' );
-
-    # Overwrite the URL string with a formula. The cell is still a link.
-    # Note the use of the default url format for consistency with other links.
-    my $url_format = $workbook->get_default_url_format();
-    $worksheet->write_formula( 'A1', '=1+1', $url_format );
-
-There are two local URIs supported: C<internal:> and C<external:>. These are used for hyperlinks to internal worksheet references or external workbook and worksheet references:
-
-    $worksheet->write_url( 'A6',  'internal:Sheet2!A1' );
-    $worksheet->write_url( 'A7',  'internal:Sheet2!A1' );
-    $worksheet->write_url( 'A8',  'internal:Sheet2!A1:B2' );
-    $worksheet->write_url( 'A9',  q{internal:'Sales Data'!A1} );
-    $worksheet->write_url( 'A10', 'external:c:\temp\foo.xlsx' );
-    $worksheet->write_url( 'A11', 'external:c:\foo.xlsx#Sheet2!A1' );
-    $worksheet->write_url( 'A12', 'external:..\foo.xlsx' );
-    $worksheet->write_url( 'A13', 'external:..\foo.xlsx#Sheet2!A1' );
-    $worksheet->write_url( 'A13', 'external:\\\\NET\share\foo.xlsx' );
-
-All of the these URI types are recognised by the C<write()> method, see above.
-
-Worksheet references are typically of the form C<Sheet1!A1>. You can also refer to a worksheet range using the standard Excel notation: C<Sheet1!A1:B2>.
-
-In external links the workbook and worksheet name must be separated by the C<#> character: C<external:Workbook.xlsx#Sheet1!A1'>.
-
-You can also link to a named range in the target worksheet. For example say you have a named range called C<my_name> in the workbook C<c:\temp\foo.xlsx> you could link to it as follows:
-
-    $worksheet->write_url( 'A14', 'external:c:\temp\foo.xlsx#my_name' );
-
-Excel requires that worksheet names containing spaces or non alphanumeric characters are single quoted as follows C<'Sales Data'!A1>. If you need to do this in a single quoted string then you can either escape the single quotes C<\'> or use the quote operator C<q{}> as described in C<perlop> in the main Perl documentation.
-
-Links to network files are also supported. MS/Novell Network files normally begin with two back slashes as follows C<\\NETWORK\etc>. In order to generate this in a single or double quoted string you will have to escape the backslashes,  C<'\\\\NETWORK\etc'>.
-
-If you are using double quote strings then you should be careful to escape anything that looks like a metacharacter. For more information see C<perlfaq5: Why can't I use "C:\temp\foo" in DOS paths?>.
-
-Finally, you can avoid most of these quoting problems by using forward slashes. These are translated internally to backslashes:
-
-    $worksheet->write_url( 'A14', "external:c:/temp/foo.xlsx" );
-    $worksheet->write_url( 'A15', 'external://NETWORK/share/foo.xlsx' );
-
-Note: Excel::Writer::XLSX will escape the following characters in URLs as required by Excel: C<< \s " < > \ [  ] ` ^ { } >> unless the URL already contains C<%xx> style escapes. In which case it is assumed that the URL was escaped correctly by the user and will by passed directly to Excel.
-
-Excel limits hyperlink links and anchor/locations to 255 characters each.
-
-See also, the note about L</Cell notation>.
-
-
-
-
-=head2 write_formula( $row, $column, $formula, $format, $value )
-
-Write a formula or function to the cell specified by C<$row> and C<$column>:
-
-    $worksheet->write_formula( 0, 0, '=$B$3 + B4' );
-    $worksheet->write_formula( 1, 0, '=SIN(PI()/4)' );
-    $worksheet->write_formula( 2, 0, '=SUM(B1:B5)' );
-    $worksheet->write_formula( 'A4', '=IF(A3>1,"Yes", "No")' );
-    $worksheet->write_formula( 'A5', '=AVERAGE(1, 2, 3, 4)' );
-    $worksheet->write_formula( 'A6', '=DATEVALUE("1-Jan-2001")' );
-
-Array formulas are also supported:
-
-    $worksheet->write_formula( 'A7', '{=SUM(A1:B1*A2:B2)}' );
-
-See also the C<write_array_formula()> method below.
-
-See the note about L</Cell notation>. For more information about writing Excel formulas see L</FORMULAS AND FUNCTIONS IN EXCEL>
-
-If required, it is also possible to specify the calculated value of the formula. This is occasionally necessary when working with non-Excel applications that don't calculate the value of the formula. The calculated C<$value> is added at the end of the argument list:
-
-    $worksheet->write( 'A1', '=2+2', $format, 4 );
-
-However, this probably isn't something that you will ever need to do. If you do use this feature then do so with care.
-
-
-
-
-=head2 write_array_formula($first_row, $first_col, $last_row, $last_col, $formula, $format, $value)
-
-Write an array formula to a cell range. In Excel an array formula is a formula that performs a calculation on a set of values. It can return a single value or a range of values.
-
-An array formula is indicated by a pair of braces around the formula: C<{=SUM(A1:B1*A2:B2)}>.  If the array formula returns a single value then the C<$first_> and C<$last_> parameters should be the same:
-
-    $worksheet->write_array_formula('A1:A1', '{=SUM(B1:C1*B2:C2)}');
-
-It this case however it is easier to just use the C<write_formula()> or C<write()> methods:
-
-    # Same as above but more concise.
-    $worksheet->write( 'A1', '{=SUM(B1:C1*B2:C2)}' );
-    $worksheet->write_formula( 'A1', '{=SUM(B1:C1*B2:C2)}' );
-
-For array formulas that return a range of values you must specify the range that the return values will be written to:
-
-    $worksheet->write_array_formula( 'A1:A3',    '{=TREND(C1:C3,B1:B3)}' );
-    $worksheet->write_array_formula( 0, 0, 2, 0, '{=TREND(C1:C3,B1:B3)}' );
-
-If required, it is also possible to specify the calculated value of the formula. This is occasionally necessary when working with non-Excel applications that don't calculate the value of the formula. However, using this parameter only writes a single value to the upper left cell in the result array. For a multi-cell array formula where the results are required, the other result values can be specified by using C<write_number()> to write to the appropriate cell:
-
-    # Specify the result for a single cell range.
-    $worksheet->write_array_formula( 'A1:A3', '{=SUM(B1:C1*B2:C2)}, $format, 2005 );
-
-    # Specify the results for a multi cell range.
-    $worksheet->write_array_formula( 'A1:A3', '{=TREND(C1:C3,B1:B3)}', $format, 105 );
-    $worksheet->write_number( 'A2', 12, format );
-    $worksheet->write_number( 'A3', 14, format );
-
-In addition, some early versions of Excel 2007 don't calculate the values of array formulas when they aren't supplied. Installing the latest Office Service Pack should fix this issue.
-
-See also the C<array_formula.pl> program in the C<examples> directory of the distro.
-
-Note: Array formulas are not supported by Spreadsheet::WriteExcel.
-
-
-
-
-=head2 write_boolean( $row, $column, $value, $format )
-
-Write an Excel boolean value to the cell specified by C<$row> and C<$column>:
-
-    $worksheet->write_boolean( 'A1', 1          );  # TRUE
-    $worksheet->write_boolean( 'A2', 0          );  # FALSE
-    $worksheet->write_boolean( 'A3', undef      );  # FALSE
-    $worksheet->write_boolean( 'A3', 0, $format );  # FALSE, with format.
-
-A C<$value> that is true or false using Perl's rules will be written as an Excel boolean C<TRUE> or C<FALSE> value.
-
-See the note about L</Cell notation>.
-
-
-
-
-=head2 store_formula( $formula )
-
-Deprecated. This is a Spreadsheet::WriteExcel method that is no longer required by Excel::Writer::XLSX. See below.
-
-
-
-
-=head2 repeat_formula( $row, $col, $formula, $format )
-
-Deprecated. This is a Spreadsheet::WriteExcel method that is no longer required by Excel::Writer::XLSX.
-
-In Spreadsheet::WriteExcel it was computationally expensive to write formulas since they were parsed by a recursive descent parser. The C<store_formula()> and C<repeat_formula()> methods were used as a way of avoiding the overhead of repeated formulas by reusing a pre-parsed formula.
-
-In Excel::Writer::XLSX this is no longer necessary since it is just as quick to write a formula as it is to write a string or a number.
-
-The methods remain for backward compatibility but new Excel::Writer::XLSX programs shouldn't use them.
-
-
-
-
-
-=head2 write_comment( $row, $column, $string, ... )
-
-The C<write_comment()> method is used to add a comment to a cell. A cell comment is indicated in Excel by a small red triangle in the upper right-hand corner of the cell. Moving the cursor over the red triangle will reveal the comment.
-
-The following example shows how to add a comment to a cell:
-
-    $worksheet->write        ( 2, 2, 'Hello' );
-    $worksheet->write_comment( 2, 2, 'This is a comment.' );
-
-As usual you can replace the C<$row> and C<$column> parameters with an C<A1> cell reference. See the note about L</Cell notation>.
-
-    $worksheet->write        ( 'C3', 'Hello');
-    $worksheet->write_comment( 'C3', 'This is a comment.' );
-
-The C<write_comment()> method will also handle strings in C<UTF-8> format.
-
-    $worksheet->write_comment( 'C3', "\x{263a}" );       # Smiley
-    $worksheet->write_comment( 'C4', 'Comment ca va?' );
-
-In addition to the basic 3 argument form of C<write_comment()> you can pass in several optional key/value pairs to control the format of the comment. For example:
-
-    $worksheet->write_comment( 'C3', 'Hello', visible => 1, author => 'Perl' );
-
-Most of these options are quite specific and in general the default comment behaves will be all that you need. However, should you need greater control over the format of the cell comment the following options are available:
-
-    author
-    visible
-    x_scale
-    width
-    y_scale
-    height
-    color
-    start_cell
-    start_row
-    start_col
-    x_offset
-    y_offset
-    font
-    font_size
-
-
-=over 4
-
-=item Option: author
-
-This option is used to indicate who is the author of the cell comment. Excel displays the author of the comment in the status bar at the bottom of the worksheet. This is usually of interest in corporate environments where several people might review and provide comments to a workbook.
-
-    $worksheet->write_comment( 'C3', 'Atonement', author => 'Ian McEwan' );
-
-The default author for all cell comments can be set using the C<set_comments_author()> method (see below).
-
-    $worksheet->set_comments_author( 'Perl' );
-
-
-=item Option: visible
-
-This option is used to make a cell comment visible when the worksheet is opened. The default behaviour in Excel is that comments are initially hidden. However, it is also possible in Excel to make individual or all comments visible. In Excel::Writer::XLSX individual comments can be made visible as follows:
-
-    $worksheet->write_comment( 'C3', 'Hello', visible => 1 );
-
-It is possible to make all comments in a worksheet visible using the C<show_comments()> worksheet method (see below). Alternatively, if all of the cell comments have been made visible you can hide individual comments:
-
-    $worksheet->write_comment( 'C3', 'Hello', visible => 0 );
-
-
-=item Option: x_scale
-
-This option is used to set the width of the cell comment box as a factor of the default width.
-
-    $worksheet->write_comment( 'C3', 'Hello', x_scale => 2 );
-    $worksheet->write_comment( 'C4', 'Hello', x_scale => 4.2 );
-
-
-=item Option: width
-
-This option is used to set the width of the cell comment box explicitly in pixels.
-
-    $worksheet->write_comment( 'C3', 'Hello', width => 200 );
-
-
-=item Option: y_scale
-
-This option is used to set the height of the cell comment box as a factor of the default height.
-
-    $worksheet->write_comment( 'C3', 'Hello', y_scale => 2 );
-    $worksheet->write_comment( 'C4', 'Hello', y_scale => 4.2 );
-
-
-=item Option: height
-
-This option is used to set the height of the cell comment box explicitly in pixels.
-
-    $worksheet->write_comment( 'C3', 'Hello', height => 200 );
-
-
-=item Option: color
-
-This option is used to set the background colour of cell comment box. You can use one of the named colours recognised by Excel::Writer::XLSX or a Html style C<#RRGGBB> colour. See L</WORKING WITH COLOURS>.
-
-    $worksheet->write_comment( 'C3', 'Hello', color => 'green' );
-    $worksheet->write_comment( 'C4', 'Hello', color => '#FF6600' ); # Orange
-
-
-=item Option: start_cell
-
-This option is used to set the cell in which the comment will appear. By default Excel displays comments one cell to the right and one cell above the cell to which the comment relates. However, you can change this behaviour if you wish. In the following example the comment which would appear by default in cell C<D2> is moved to C<E2>.
-
-    $worksheet->write_comment( 'C3', 'Hello', start_cell => 'E2' );
-
-
-=item Option: start_row
-
-This option is used to set the row in which the comment will appear. See the C<start_cell> option above. The row is zero indexed.
-
-    $worksheet->write_comment( 'C3', 'Hello', start_row => 0 );
-
-
-=item Option: start_col
-
-This option is used to set the column in which the comment will appear. See the C<start_cell> option above. The column is zero indexed.
-
-    $worksheet->write_comment( 'C3', 'Hello', start_col => 4 );
-
-
-=item Option: x_offset
-
-This option is used to change the x offset, in pixels, of a comment within a cell:
-
-    $worksheet->write_comment( 'C3', $comment, x_offset => 30 );
-
-
-=item Option: y_offset
-
-This option is used to change the y offset, in pixels, of a comment within a cell:
-
-    $worksheet->write_comment('C3', $comment, x_offset => 30);
-
-=item Option: font
-
-This option is used to change the font used in the comment from 'Tahoma' which is the default.
-
-    $worksheet->write_comment('C3', $comment, font => 'Calibri');
-
-=item Option: font_size
-
-This option is used to change the font size used in the comment from 8 which is the default.
-
-    $worksheet->write_comment('C3', $comment, font_size => 20);
-
-
-=back
-
-You can apply as many of these options as you require.
-
-B<Note about using options that adjust the position of the cell comment such as start_cell, start_row, start_col, x_offset and y_offset>: Excel only displays offset cell comments when they are displayed as "visible". Excel does B<not> display hidden cells as moved when you mouse over them.
-
-B<Note about row height and comments>. If you specify the height of a row that contains a comment then Excel::Writer::XLSX will adjust the height of the comment to maintain the default or user specified dimensions. However, the height of a row can also be adjusted automatically by Excel if the text wrap property is set or large fonts are used in the cell. This means that the height of the row is unknown to the module at run time and thus the comment box is stretched with the row. Use the C<set_row()> method to specify the row height explicitly and avoid this problem.
-
-
-
-
-=head2 show_comments()
-
-This method is used to make all cell comments visible when a worksheet is opened.
-
-    $worksheet->show_comments();
-
-Individual comments can be made visible using the C<visible> parameter of the C<write_comment> method (see above):
-
-    $worksheet->write_comment( 'C3', 'Hello', visible => 1 );
-
-If all of the cell comments have been made visible you can hide individual comments as follows:
-
-    $worksheet->show_comments();
-    $worksheet->write_comment( 'C3', 'Hello', visible => 0 );
-
-
-
-=head2 set_comments_author()
-
-This method is used to set the default author of all cell comments.
-
-    $worksheet->set_comments_author( 'Perl' );
-
-Individual comment authors can be set using the C<author> parameter of the C<write_comment> method (see above).
-
-The default comment author is an empty string, C<''>, if no author is specified.
-
-
-
-
-=head2 add_write_handler( $re, $code_ref )
-
-This method is used to extend the Excel::Writer::XLSX write() method to handle user defined data.
-
-If you refer to the section on C<write()> above you will see that it acts as an alias for several more specific C<write_*> methods. However, it doesn't always act in exactly the way that you would like it to.
-
-One solution is to filter the input data yourself and call the appropriate C<write_*> method. Another approach is to use the C<add_write_handler()> method to add your own automated behaviour to C<write()>.
-
-The C<add_write_handler()> method take two arguments, C<$re>, a regular expression to match incoming data and C<$code_ref> a callback function to handle the matched data:
-
-    $worksheet->add_write_handler( qr/^\d\d\d\d$/, \&my_write );
-
-(In the these examples the C<qr> operator is used to quote the regular expression strings, see L<perlop> for more details).
-
-The method is used as follows. say you wished to write 7 digit ID numbers as a string so that any leading zeros were preserved*, you could do something like the following:
-
-    $worksheet->add_write_handler( qr/^\d{7}$/, \&write_my_id );
-
-
-    sub write_my_id {
-        my $worksheet = shift;
-        return $worksheet->write_string( @_ );
-    }
-
-* You could also use the C<keep_leading_zeros()> method for this.
-
-Then if you call C<write()> with an appropriate string it will be handled automatically:
-
-    # Writes 0000000. It would normally be written as a number; 0.
-    $worksheet->write( 'A1', '0000000' );
-
-The callback function will receive a reference to the calling worksheet and all of the other arguments that were passed to C<write()>. The callback will see an C<@_> argument list that looks like the following:
-
-    $_[0]   A ref to the calling worksheet. *
-    $_[1]   Zero based row number.
-    $_[2]   Zero based column number.
-    $_[3]   A number or string or token.
-    $_[4]   A format ref if any.
-    $_[5]   Any other arguments.
-    ...
-
-    *  It is good style to shift this off the list so the @_ is the same
-       as the argument list seen by write().
-
-Your callback should C<return()> the return value of the C<write_*> method that was called or C<undef> to indicate that you rejected the match and want C<write()> to continue as normal.
-
-So for example if you wished to apply the previous filter only to ID values that occur in the first column you could modify your callback function as follows:
-
-
-    sub write_my_id {
-        my $worksheet = shift;
-        my $col       = $_[1];
-
-        if ( $col == 0 ) {
-            return $worksheet->write_string( @_ );
-        }
-        else {
-            # Reject the match and return control to write()
-            return undef;
-        }
-    }
-
-Now, you will get different behaviour for the first column and other columns:
-
-    $worksheet->write( 'A1', '0000000' );    # Writes 0000000
-    $worksheet->write( 'B1', '0000000' );    # Writes 0
-
-
-You may add more than one handler in which case they will be called in the order that they were added.
-
-Note, the C<add_write_handler()> method is particularly suited for handling dates.
-
-See the C<write_handler 1-4> programs in the C<examples> directory for further examples.
-
-
-
-
-=head2 insert_image( $row, $col, $filename, $x, $y, $x_scale, $y_scale )
-
-This method can be used to insert a image into a worksheet. The image can be in PNG, JPEG or BMP format. The C<$x>, C<$y>, C<$x_scale> and C<$y_scale> parameters are optional.
-
-    $worksheet1->insert_image( 'A1', 'perl.bmp' );
-    $worksheet2->insert_image( 'A1', '../images/perl.bmp' );
-    $worksheet3->insert_image( 'A1', '.c:\images\perl.bmp' );
-
-The parameters C<$x> and C<$y> can be used to specify an offset from the top left hand corner of the cell specified by C<$row> and C<$col>. The offset values are in pixels.
-
-    $worksheet1->insert_image('A1', 'perl.bmp', 32, 10);
-
-The offsets can be greater than the width or height of the underlying cell. This can be occasionally useful if you wish to align two or more images relative to the same cell.
-
-The parameters C<$x_scale> and C<$y_scale> can be used to scale the inserted image horizontally and vertically:
-
-    # Scale the inserted image: width x 2.0, height x 0.8
-    $worksheet->insert_image( 'A1', 'perl.bmp', 0, 0, 2, 0.8 );
-
-Note: you must call C<set_row()> or C<set_column()> before C<insert_image()> if you wish to change the default dimensions of any of the rows or columns that the image occupies. The height of a row can also change if you use a font that is larger than the default. This in turn will affect the scaling of your image. To avoid this you should explicitly set the height of the row using C<set_row()> if it contains a font size that will change the row height.
-
-BMP images must be 24 bit, true colour, bitmaps. In general it is best to avoid BMP images since they aren't compressed.
-
-
-
-
-=head2 insert_chart( $row, $col, $chart, $x, $y, $x_scale, $y_scale )
-
-This method can be used to insert a Chart object into a worksheet. The Chart must be created by the C<add_chart()> Workbook method and it must have the C<embedded> option set.
-
-    my $chart = $workbook->add_chart( type => 'line', embedded => 1 );
-
-    # Configure the chart.
-    ...
-
-    # Insert the chart into the a worksheet.
-    $worksheet->insert_chart( 'E2', $chart );
-
-See C<add_chart()> for details on how to create the Chart object and L<Excel::Writer::XLSX::Chart> for details on how to configure it. See also the C<chart_*.pl> programs in the examples directory of the distro.
-
-The C<$x>, C<$y>, C<$x_scale> and C<$y_scale> parameters are optional.
-
-The parameters C<$x> and C<$y> can be used to specify an offset from the top left hand corner of the cell specified by C<$row> and C<$col>. The offset values are in pixels.
-
-    $worksheet1->insert_chart( 'E2', $chart, 3, 3 );
-
-The parameters C<$x_scale> and C<$y_scale> can be used to scale the inserted chart horizontally and vertically:
-
-    # Scale the width by 120% and the height by 150%
-    $worksheet->insert_chart( 'E2', $chart, 0, 0, 1.2, 1.5 );
-
-=head2 insert_shape( $row, $col, $shape, $x, $y, $x_scale, $y_scale )
-
-This method can be used to insert a Shape object into a worksheet. The Shape must be created by the C<add_shape()> Workbook method.
-
-    my $shape = $workbook->add_shape( name => 'My Shape', type => 'plus' );
-
-    # Configure the shape.
-    $shape->set_text('foo');
-    ...
-
-    # Insert the shape into the a worksheet.
-    $worksheet->insert_shape( 'E2', $shape );
-
-See C<add_shape()> for details on how to create the Shape object and L<Excel::Writer::XLSX::Shape> for details on how to configure it.
-
-The C<$x>, C<$y>, C<$x_scale> and C<$y_scale> parameters are optional.
-
-The parameters C<$x> and C<$y> can be used to specify an offset from the top left hand corner of the cell specified by C<$row> and C<$col>. The offset values are in pixels.
-
-    $worksheet1->insert_shape( 'E2', $chart, 3, 3 );
-
-The parameters C<$x_scale> and C<$y_scale> can be used to scale the inserted shape horizontally and vertically:
-
-    # Scale the width by 120% and the height by 150%
-    $worksheet->insert_shape( 'E2', $shape, 0, 0, 1.2, 1.5 );
-
-See also the C<shape*.pl> programs in the examples directory of the distro.
-
-
-
-
-=head2 insert_button( $row, $col, { %properties })
-
-The C<insert_button()> method can be used to insert an Excel form button into a worksheet.
-
-This method is generally only useful when used in conjunction with the Workbook C<add_vba_project()> method to tie the button to a macro from an embedded VBA project:
-
-    my $workbook  = Excel::Writer::XLSX->new( 'file.xlsm' );
-    ...
-    $workbook->add_vba_project( './vbaProject.bin' );
-
-    $worksheet->insert_button( 'C2', { macro => 'my_macro' } );
-
-The properties of the button that can be set are:
-
-    macro
-    caption
-    width
-    height
-    x_scale
-    y_scale
-    x_offset
-    y_offset
-
-
-=over
-
-=item Option: macro
-
-This option is used to set the macro that the button will invoke when the user clicks on it. The macro should be included using the Workbook C<add_vba_project()> method shown above.
-
-    $worksheet->insert_button( 'C2', { macro => 'my_macro' } );
-
-The default macro is C<ButtonX_Click> where X is the button number.
-
-=item Option: caption
-
-This option is used to set the caption on the button. The default is C<Button X> where X is the button number.
-
-    $worksheet->insert_button( 'C2', { macro => 'my_macro', caption => 'Hello' } );
-
-=item Option: width
-
-This option is used to set the width of the button in pixels.
-
-    $worksheet->insert_button( 'C2', { macro => 'my_macro', width => 128 } );
-
-The default button width is 64 pixels which is the width of a default cell.
-
-=item Option: height
-
-This option is used to set the height of the button in pixels.
-
-    $worksheet->insert_button( 'C2', { macro => 'my_macro', height => 40 } );
-
-The default button height is 20 pixels which is the height of a default cell.
-
-=item Option: x_scale
-
-This option is used to set the width of the button as a factor of the default width.
-
-    $worksheet->insert_button( 'C2', { macro => 'my_macro', x_scale => 2.0 );
-
-=item Option: y_scale
-
-This option is used to set the height of the button as a factor of the default height.
-
-    $worksheet->insert_button( 'C2', { macro => 'my_macro', y_scale => 2.0 );
-
-
-=item Option: x_offset
-
-This option is used to change the x offset, in pixels, of a button within a cell:
-
-    $worksheet->insert_button( 'C2', { macro => 'my_macro', x_offset => 2 );
-
-=item Option: y_offset
-
-This option is used to change the y offset, in pixels, of a comment within a cell.
-
-=back
-
-
-Note: Button is the only Excel form element that is available in Excel::Writer::XLSX. Form elements represent a lot of work to implement and the underlying VML syntax isn't very much fun.
-
-
-
-
-=head2 data_validation()
-
-The C<data_validation()> method is used to construct an Excel data validation or to limit the user input to a dropdown list of values.
-
-
-    $worksheet->data_validation('B3',
-        {
-            validate => 'integer',
-            criteria => '>',
-            value    => 100,
-        });
-
-    $worksheet->data_validation('B5:B9',
-        {
-            validate => 'list',
-            value    => ['open', 'high', 'close'],
-        });
-
-This method contains a lot of parameters and is described in detail in a separate section L</DATA VALIDATION IN EXCEL>.
-
-
-See also the C<data_validate.pl> program in the examples directory of the distro
-
-
-
-
-=head2 conditional_formatting()
-
-The C<conditional_formatting()> method is used to add formatting to a cell or range of cells based on user defined criteria.
-
-    $worksheet->conditional_formatting( 'A1:J10',
-        {
-            type     => 'cell',
-            criteria => '>=',
-            value    => 50,
-            format   => $format1,
-        }
-    );
-
-This method contains a lot of parameters and is described in detail in a separate section L<CONDITIONAL FORMATTING IN EXCEL>.
-
-See also the C<conditional_format.pl> program in the examples directory of the distro
-
-
-
-
-=head2 add_sparkline()
-
-The C<add_sparkline()> worksheet method is used to add sparklines to a cell or a range of cells.
-
-    $worksheet->add_sparkline(
-        {
-            location => 'F2',
-            range    => 'Sheet1!A2:E2',
-            type     => 'column',
-            style    => 12,
-        }
-    );
-
-This method contains a lot of parameters and is described in detail in a separate section L</SPARKLINES IN EXCEL>.
-
-See also the C<sparklines1.pl> and C<sparklines2.pl> example programs in the C<examples> directory of the distro.
-
-B<Note:> Sparklines are a feature of Excel 2010+ only. You can write them to an XLSX file that can be read by Excel 2007 but they won't be displayed.
-
-
-
-
-=head2 add_table()
-
-The C<add_table()> method is used to group a range of cells into an Excel Table.
-
-    $worksheet->add_table( 'B3:F7', { ... } );
-
-This method contains a lot of parameters and is described in detail in a separate section L<TABLES IN EXCEL>.
-
-See also the C<tables.pl> program in the examples directory of the distro
-
-
-
-=head2 get_name()
-
-The C<get_name()> method is used to retrieve the name of a worksheet. For example:
-
-    for my $sheet ( $workbook->sheets() ) {
-        print $sheet->get_name();
-    }
-
-For reasons related to the design of Excel::Writer::XLSX and to the internals of Excel there is no C<set_name()> method. The only way to set the worksheet name is via the C<add_worksheet()> method.
-
-
-
-
-=head2 activate()
-
-The C<activate()> method is used to specify which worksheet is initially visible in a multi-sheet workbook:
-
-    $worksheet1 = $workbook->add_worksheet( 'To' );
-    $worksheet2 = $workbook->add_worksheet( 'the' );
-    $worksheet3 = $workbook->add_worksheet( 'wind' );
-
-    $worksheet3->activate();
-
-This is similar to the Excel VBA activate method. More than one worksheet can be selected via the C<select()> method, see below, however only one worksheet can be active.
-
-The default active worksheet is the first worksheet.
-
-
-
-
-=head2 select()
-
-The C<select()> method is used to indicate that a worksheet is selected in a multi-sheet workbook:
-
-    $worksheet1->activate();
-    $worksheet2->select();
-    $worksheet3->select();
-
-A selected worksheet has its tab highlighted. Selecting worksheets is a way of grouping them together so that, for example, several worksheets could be printed in one go. A worksheet that has been activated via the C<activate()> method will also appear as selected.
-
-
-
-
-=head2 hide()
-
-The C<hide()> method is used to hide a worksheet:
-
-    $worksheet2->hide();
-
-You may wish to hide a worksheet in order to avoid confusing a user with intermediate data or calculations.
-
-A hidden worksheet can not be activated or selected so this method is mutually exclusive with the C<activate()> and C<select()> methods. In addition, since the first worksheet will default to being the active worksheet, you cannot hide the first worksheet without activating another sheet:
-
-    $worksheet2->activate();
-    $worksheet1->hide();
-
-
-
-
-=head2 set_first_sheet()
-
-The C<activate()> method determines which worksheet is initially selected. However, if there are a large number of worksheets the selected worksheet may not appear on the screen. To avoid this you can select which is the leftmost visible worksheet using C<set_first_sheet()>:
-
-    for ( 1 .. 20 ) {
-        $workbook->add_worksheet;
-    }
-
-    $worksheet21 = $workbook->add_worksheet();
-    $worksheet22 = $workbook->add_worksheet();
-
-    $worksheet21->set_first_sheet();
-    $worksheet22->activate();
-
-This method is not required very often. The default value is the first worksheet.
-
-
-
-
-=head2 protect( $password, \%options )
-
-The C<protect()> method is used to protect a worksheet from modification:
-
-    $worksheet->protect();
-
-The C<protect()> method also has the effect of enabling a cell's C<locked> and C<hidden> properties if they have been set. A I<locked> cell cannot be edited and this property is on by default for all cells. A I<hidden> cell will display the results of a formula but not the formula itself.
-
-See the C<protection.pl> program in the examples directory of the distro for an illustrative example and the C<set_locked> and C<set_hidden> format methods in L</CELL FORMATTING>.
-
-You can optionally add a password to the worksheet protection:
-
-    $worksheet->protect( 'drowssap' );
-
-Passing the empty string C<''> is the same as turning on protection without a password.
-
-Note, the worksheet level password in Excel provides very weak protection. It does not encrypt your data and is very easy to deactivate. Full workbook encryption is not supported by C<Excel::Writer::XLSX> since it requires a completely different file format and would take several man months to implement.
-
-You can specify which worksheet elements you wish to protect by passing a hash_ref with any or all of the following keys:
-
-    # Default shown.
-    %options = (
-        objects               => 0,
-        scenarios             => 0,
-        format_cells          => 0,
-        format_columns        => 0,
-        format_rows           => 0,
-        insert_columns        => 0,
-        insert_rows           => 0,
-        insert_hyperlinks     => 0,
-        delete_columns        => 0,
-        delete_rows           => 0,
-        select_locked_cells   => 1,
-        sort                  => 0,
-        autofilter            => 0,
-        pivot_tables          => 0,
-        select_unlocked_cells => 1,
-    );
-
-The default boolean values are shown above. Individual elements can be protected as follows:
-
-    $worksheet->protect( 'drowssap', { insert_rows => 1 } );
-
-
-
-
-=head2 set_selection( $first_row, $first_col, $last_row, $last_col )
-
-This method can be used to specify which cell or cells are selected in a worksheet. The most common requirement is to select a single cell, in which case C<$last_row> and C<$last_col> can be omitted. The active cell within a selected range is determined by the order in which C<$first> and C<$last> are specified. It is also possible to specify a cell or a range using A1 notation. See the note about L</Cell notation>.
-
-Examples:
-
-    $worksheet1->set_selection( 3, 3 );          # 1. Cell D4.
-    $worksheet2->set_selection( 3, 3, 6, 6 );    # 2. Cells D4 to G7.
-    $worksheet3->set_selection( 6, 6, 3, 3 );    # 3. Cells G7 to D4.
-    $worksheet4->set_selection( 'D4' );          # Same as 1.
-    $worksheet5->set_selection( 'D4:G7' );       # Same as 2.
-    $worksheet6->set_selection( 'G7:D4' );       # Same as 3.
-
-The default cell selections is (0, 0), 'A1'.
-
-
-
-
-=head2 set_row( $row, $height, $format, $hidden, $level, $collapsed )
-
-This method can be used to change the default properties of a row. All parameters apart from C<$row> are optional.
-
-The most common use for this method is to change the height of a row:
-
-    $worksheet->set_row( 0, 20 );    # Row 1 height set to 20
-
-If you wish to set the format without changing the height you can pass C<undef> as the height parameter:
-
-    $worksheet->set_row( 0, undef, $format );
-
-The C<$format> parameter will be applied to any cells in the row that don't have a format. For example
-
-    $worksheet->set_row( 0, undef, $format1 );    # Set the format for row 1
-    $worksheet->write( 'A1', 'Hello' );           # Defaults to $format1
-    $worksheet->write( 'B1', 'Hello', $format2 ); # Keeps $format2
-
-If you wish to define a row format in this way you should call the method before any calls to C<write()>. Calling it afterwards will overwrite any format that was previously specified.
-
-The C<$hidden> parameter should be set to 1 if you wish to hide a row. This can be used, for example, to hide intermediary steps in a complicated calculation:
-
-    $worksheet->set_row( 0, 20,    $format, 1 );
-    $worksheet->set_row( 1, undef, undef,   1 );
-
-The C<$level> parameter is used to set the outline level of the row. Outlines are described in L</OUTLINES AND GROUPING IN EXCEL>. Adjacent rows with the same outline level are grouped together into a single outline.
-
-The following example sets an outline level of 1 for rows 2 and 3 (zero-indexed):
-
-    $worksheet->set_row( 1, undef, undef, 0, 1 );
-    $worksheet->set_row( 2, undef, undef, 0, 1 );
-
-The C<$hidden> parameter can also be used to hide collapsed outlined rows when used in conjunction with the C<$level> parameter.
-
-    $worksheet->set_row( 1, undef, undef, 1, 1 );
-    $worksheet->set_row( 2, undef, undef, 1, 1 );
-
-For collapsed outlines you should also indicate which row has the collapsed C<+> symbol using the optional C<$collapsed> parameter.
-
-    $worksheet->set_row( 3, undef, undef, 0, 0, 1 );
-
-For a more complete example see the C<outline.pl> and C<outline_collapsed.pl> programs in the examples directory of the distro.
-
-Excel allows up to 7 outline levels. Therefore the C<$level> parameter should be in the range C<0 E<lt>= $level E<lt>= 7>.
-
-
-
-
-=head2 set_column( $first_col, $last_col, $width, $format, $hidden, $level, $collapsed )
-
-This method can be used to change the default properties of a single column or a range of columns. All parameters apart from C<$first_col> and C<$last_col> are optional.
-
-If C<set_column()> is applied to a single column the value of C<$first_col> and C<$last_col> should be the same. In the case where C<$last_col> is zero it is set to the same value as C<$first_col>.
-
-It is also possible, and generally clearer, to specify a column range using the form of A1 notation used for columns. See the note about L</Cell notation>.
-
-Examples:
-
-    $worksheet->set_column( 0, 0, 20 );    # Column  A   width set to 20
-    $worksheet->set_column( 1, 3, 30 );    # Columns B-D width set to 30
-    $worksheet->set_column( 'E:E', 20 );   # Column  E   width set to 20
-    $worksheet->set_column( 'F:H', 30 );   # Columns F-H width set to 30
-
-The width corresponds to the column width value that is specified in Excel. It is approximately equal to the length of a string in the default font of Calibri 11. Unfortunately, there is no way to specify "AutoFit" for a column in the Excel file format. This feature is only available at runtime from within Excel.
-
-As usual the C<$format> parameter is optional, for additional information, see L</CELL FORMATTING>. If you wish to set the format without changing the width you can pass C<undef> as the width parameter:
-
-    $worksheet->set_column( 0, 0, undef, $format );
-
-The C<$format> parameter will be applied to any cells in the column that don't have a format. For example
-
-    $worksheet->set_column( 'A:A', undef, $format1 );    # Set format for col 1
-    $worksheet->write( 'A1', 'Hello' );                  # Defaults to $format1
-    $worksheet->write( 'A2', 'Hello', $format2 );        # Keeps $format2
-
-If you wish to define a column format in this way you should call the method before any calls to C<write()>. If you call it afterwards it won't have any effect.
-
-A default row format takes precedence over a default column format
-
-    $worksheet->set_row( 0, undef, $format1 );           # Set format for row 1
-    $worksheet->set_column( 'A:A', undef, $format2 );    # Set format for col 1
-    $worksheet->write( 'A1', 'Hello' );                  # Defaults to $format1
-    $worksheet->write( 'A2', 'Hello' );                  # Defaults to $format2
-
-The C<$hidden> parameter should be set to 1 if you wish to hide a column. This can be used, for example, to hide intermediary steps in a complicated calculation:
-
-    $worksheet->set_column( 'D:D', 20,    $format, 1 );
-    $worksheet->set_column( 'E:E', undef, undef,   1 );
-
-The C<$level> parameter is used to set the outline level of the column. Outlines are described in L</OUTLINES AND GROUPING IN EXCEL>. Adjacent columns with the same outline level are grouped together into a single outline.
-
-The following example sets an outline level of 1 for columns B to G:
-
-    $worksheet->set_column( 'B:G', undef, undef, 0, 1 );
-
-The C<$hidden> parameter can also be used to hide collapsed outlined columns when used in conjunction with the C<$level> parameter.
-
-    $worksheet->set_column( 'B:G', undef, undef, 1, 1 );
-
-For collapsed outlines you should also indicate which row has the collapsed C<+> symbol using the optional C<$collapsed> parameter.
-
-    $worksheet->set_column( 'H:H', undef, undef, 0, 0, 1 );
-
-For a more complete example see the C<outline.pl> and C<outline_collapsed.pl> programs in the examples directory of the distro.
-
-Excel allows up to 7 outline levels. Therefore the C<$level> parameter should be in the range C<0 E<lt>= $level E<lt>= 7>.
-
-
-
-
-=head2 set_default_row( $height, $hide_unused_rows )
-
-The C<set_default_row()> method is used to set the limited number of default row properties allowed by Excel. These are the default height and the option to hide unused rows.
-
-    $worksheet->set_default_row( 24 );  # Set the default row height to 24.
-
-The option to hide unused rows is used by Excel as an optimisation so that the user can hide a large number of rows without generating a very large file with an entry for each hidden row.
-
-    $worksheet->set_default_row( undef, 1 );
-
-See the C<hide_row_col.pl> example program.
-
-
-
-
-=head2 outline_settings( $visible, $symbols_below, $symbols_right, $auto_style )
-
-The C<outline_settings()> method is used to control the appearance of outlines in Excel. Outlines are described in L</OUTLINES AND GROUPING IN EXCEL>.
-
-The C<$visible> parameter is used to control whether or not outlines are visible. Setting this parameter to 0 will cause all outlines on the worksheet to be hidden. They can be unhidden in Excel by means of the "Show Outline Symbols" command button. The default setting is 1 for visible outlines.
-
-    $worksheet->outline_settings( 0 );
-
-The C<$symbols_below> parameter is used to control whether the row outline symbol will appear above or below the outline level bar. The default setting is 1 for symbols to appear below the outline level bar.
-
-The C<$symbols_right> parameter is used to control whether the column outline symbol will appear to the left or the right of the outline level bar. The default setting is 1 for symbols to appear to the right of the outline level bar.
-
-The C<$auto_style> parameter is used to control whether the automatic outline generator in Excel uses automatic styles when creating an outline. This has no effect on a file generated by C<Excel::Writer::XLSX> but it does have an effect on how the worksheet behaves after it is created. The default setting is 0 for "Automatic Styles" to be turned off.
-
-The default settings for all of these parameters correspond to Excel's default parameters.
-
-
-The worksheet parameters controlled by C<outline_settings()> are rarely used.
-
-
-
-
-=head2 freeze_panes( $row, $col, $top_row, $left_col )
-
-This method can be used to divide a worksheet into horizontal or vertical regions known as panes and to also "freeze" these panes so that the splitter bars are not visible. This is the same as the C<Window-E<gt>Freeze Panes> menu command in Excel
-
-The parameters C<$row> and C<$col> are used to specify the location of the split. It should be noted that the split is specified at the top or left of a cell and that the method uses zero based indexing. Therefore to freeze the first row of a worksheet it is necessary to specify the split at row 2 (which is 1 as the zero-based index). This might lead you to think that you are using a 1 based index but this is not the case.
-
-You can set one of the C<$row> and C<$col> parameters as zero if you do not want either a vertical or horizontal split.
-
-Examples:
-
-    $worksheet->freeze_panes( 1, 0 );    # Freeze the first row
-    $worksheet->freeze_panes( 'A2' );    # Same using A1 notation
-    $worksheet->freeze_panes( 0, 1 );    # Freeze the first column
-    $worksheet->freeze_panes( 'B1' );    # Same using A1 notation
-    $worksheet->freeze_panes( 1, 2 );    # Freeze first row and first 2 columns
-    $worksheet->freeze_panes( 'C2' );    # Same using A1 notation
-
-The parameters C<$top_row> and C<$left_col> are optional. They are used to specify the top-most or left-most visible row or column in the scrolling region of the panes. For example to freeze the first row and to have the scrolling region begin at row twenty:
-
-    $worksheet->freeze_panes( 1, 0, 20, 0 );
-
-You cannot use A1 notation for the C<$top_row> and C<$left_col> parameters.
-
-
-See also the C<panes.pl> program in the C<examples> directory of the distribution.
-
-
-
-
-=head2 split_panes( $y, $x, $top_row, $left_col )
-
-
-This method can be used to divide a worksheet into horizontal or vertical regions known as panes. This method is different from the C<freeze_panes()> method in that the splits between the panes will be visible to the user and each pane will have its own scroll bars.
-
-The parameters C<$y> and C<$x> are used to specify the vertical and horizontal position of the split. The units for C<$y> and C<$x> are the same as those used by Excel to specify row height and column width. However, the vertical and horizontal units are different from each other. Therefore you must specify the C<$y> and C<$x> parameters in terms of the row heights and column widths that you have set or the default values which are C<15> for a row and C<8.43> for a column.
-
-You can set one of the C<$y> and C<$x> parameters as zero if you do not want either a vertical or horizontal split. The parameters C<$top_row> and C<$left_col> are optional. They are used to specify the top-most or left-most visible row or column in the bottom-right pane.
-
-Example:
-
-    $worksheet->split_panes( 15, 0,   );    # First row
-    $worksheet->split_panes( 0,  8.43 );    # First column
-    $worksheet->split_panes( 15, 8.43 );    # First row and column
-
-You cannot use A1 notation with this method.
-
-See also the C<freeze_panes()> method and the C<panes.pl> program in the C<examples> directory of the distribution.
-
-
-
-
-=head2 merge_range( $first_row, $first_col, $last_row, $last_col, $token, $format )
-
-The C<merge_range()> method allows you to merge cells that contain other types of alignment in addition to the merging:
-
-    my $format = $workbook->add_format(
-        border => 6,
-        valign => 'vcenter',
-        align  => 'center',
-    );
-
-    $worksheet->merge_range( 'B3:D4', 'Vertical and horizontal', $format );
-
-C<merge_range()> writes its C<$token> argument using the worksheet C<write()> method. Therefore it will handle numbers, strings, formulas or urls as required. If you need to specify the required C<write_*()> method use the C<merge_range_type()> method, see below.
-
-The full possibilities of this method are shown in the C<merge3.pl> to C<merge6.pl> programs in the C<examples> directory of the distribution.
-
-
-
-
-=head2 merge_range_type( $type, $first_row, $first_col, $last_row, $last_col, ... )
-
-The C<merge_range()> method, see above, uses C<write()> to insert the required data into to a merged range. However, there may be times where this isn't what you require so as an alternative the C<merge_range_type ()> method allows you to specify the type of data you wish to write. For example:
-
-    $worksheet->merge_range_type( 'number',  'B2:C2', 123,    $format1 );
-    $worksheet->merge_range_type( 'string',  'B4:C4', 'foo',  $format2 );
-    $worksheet->merge_range_type( 'formula', 'B6:C6', '=1+2', $format3 );
-
-The C<$type> must be one of the following, which corresponds to a C<write_*()> method:
-
-    'number'
-    'string'
-    'formula'
-    'array_formula'
-    'blank'
-    'rich_string'
-    'date_time'
-    'url'
-
-Any arguments after the range should be whatever the appropriate method accepts:
-
-    $worksheet->merge_range_type( 'rich_string', 'B8:C8',
-                                  'This is ', $bold, 'bold', $format4 );
-
-Note, you must always pass a C<$format> object as an argument, even if it is a default format.
-
-
-
-
-=head2 set_zoom( $scale )
-
-Set the worksheet zoom factor in the range C<10 E<lt>= $scale E<lt>= 400>:
-
-    $worksheet1->set_zoom( 50 );
-    $worksheet2->set_zoom( 75 );
-    $worksheet3->set_zoom( 300 );
-    $worksheet4->set_zoom( 400 );
-
-The default zoom factor is 100. You cannot zoom to "Selection" because it is calculated by Excel at run-time.
-
-Note, C<set_zoom()> does not affect the scale of the printed page. For that you should use C<set_print_scale()>.
-
-
-
-
-=head2 right_to_left()
-
-The C<right_to_left()> method is used to change the default direction of the worksheet from left-to-right, with the A1 cell in the top left, to right-to-left, with the A1 cell in the top right.
-
-    $worksheet->right_to_left();
-
-This is useful when creating Arabic, Hebrew or other near or far eastern worksheets that use right-to-left as the default direction.
-
-
-
-
-=head2 hide_zero()
-
-The C<hide_zero()> method is used to hide any zero values that appear in cells.
-
-    $worksheet->hide_zero();
-
-In Excel this option is found under Tools->Options->View.
-
-
-
-
-=head2 set_tab_color()
-
-The C<set_tab_color()> method is used to change the colour of the worksheet tab. You can use one of the standard colour names provided by the Format object or a Html style C<#RRGGBB> colour. See L</WORKING WITH COLOURS>.
-
-    $worksheet1->set_tab_color( 'red' );
-    $worksheet2->set_tab_color( '#FF6600' );
-
-See the C<tab_colors.pl> program in the examples directory of the distro.
-
-
-
-
-=head2 autofilter( $first_row, $first_col, $last_row, $last_col )
-
-This method allows an autofilter to be added to a worksheet. An autofilter is a way of adding drop down lists to the headers of a 2D range of worksheet data. This allows users to filter the data based on simple criteria so that some data is shown and some is hidden.
-
-To add an autofilter to a worksheet:
-
-    $worksheet->autofilter( 0, 0, 10, 3 );
-    $worksheet->autofilter( 'A1:D11' );    # Same as above in A1 notation.
-
-Filter conditions can be applied using the C<filter_column()> or C<filter_column_list()> method.
-
-See the C<autofilter.pl> program in the examples directory of the distro for a more detailed example.
-
-
-
-
-=head2 filter_column( $column, $expression )
-
-The C<filter_column> method can be used to filter columns in a autofilter range based on simple conditions.
-
-B<NOTE:> It isn't sufficient to just specify the filter condition. You must also hide any rows that don't match the filter condition. Rows are hidden using the C<set_row()> C<visible> parameter. C<Excel::Writer::XLSX> cannot do this automatically since it isn't part of the file format. See the C<autofilter.pl> program in the examples directory of the distro for an example.
-
-The conditions for the filter are specified using simple expressions:
-
-    $worksheet->filter_column( 'A', 'x > 2000' );
-    $worksheet->filter_column( 'B', 'x > 2000 and x < 5000' );
-
-The C<$column> parameter can either be a zero indexed column number or a string column name.
-
-The following operators are available:
-
-    Operator        Synonyms
-       ==           =   eq  =~
-       !=           <>  ne  !=
-       >
-       <
-       >=
-       <=
-
-       and          &&
-       or           ||
-
-The operator synonyms are just syntactic sugar to make you more comfortable using the expressions. It is important to remember that the expressions will be interpreted by Excel and not by perl.
-
-An expression can comprise a single statement or two statements separated by the C<and> and C<or> operators. For example:
-
-    'x <  2000'
-    'x >  2000'
-    'x == 2000'
-    'x >  2000 and x <  5000'
-    'x == 2000 or  x == 5000'
-
-Filtering of blank or non-blank data can be achieved by using a value of C<Blanks> or C<NonBlanks> in the expression:
-
-    'x == Blanks'
-    'x == NonBlanks'
-
-Excel also allows some simple string matching operations:
-
-    'x =~ b*'   # begins with b
-    'x !~ b*'   # doesn't begin with b
-    'x =~ *b'   # ends with b
-    'x !~ *b'   # doesn't end with b
-    'x =~ *b*'  # contains b
-    'x !~ *b*'  # doesn't contains b
-
-You can also use C<*> to match any character or number and C<?> to match any single character or number. No other regular expression quantifier is supported by Excel's filters. Excel's regular expression characters can be escaped using C<~>.
-
-The placeholder variable C<x> in the above examples can be replaced by any simple string. The actual placeholder name is ignored internally so the following are all equivalent:
-
-    'x     < 2000'
-    'col   < 2000'
-    'Price < 2000'
-
-Also, note that a filter condition can only be applied to a column in a range specified by the C<autofilter()> Worksheet method.
-
-See the C<autofilter.pl> program in the examples directory of the distro for a more detailed example.
-
-B<Note> L<Spreadsheet::WriteExcel> supports Top 10 style filters. These aren't currently supported by Excel::Writer::XLSX but may be added later.
-
-
-=head2 filter_column_list( $column, @matches )
-
-Prior to Excel 2007 it was only possible to have either 1 or 2 filter conditions such as the ones shown above in the C<filter_column> method.
-
-Excel 2007 introduced a new list style filter where it is possible to specify 1 or more 'or' style criteria. For example if your column contained data for the first six months the initial data would be displayed as all selected as shown on the left. Then if you selected 'March', 'April' and 'May' they would be displayed as shown on the right.
-
-    No criteria selected      Some criteria selected.
-
-    [/] (Select all)          [X] (Select all)
-    [/] January               [ ] January
-    [/] February              [ ] February
-    [/] March                 [/] March
-    [/] April                 [/] April
-    [/] May                   [/] May
-    [/] June                  [ ] June
-
-The C<filter_column_list()> method can be used to represent these types of filters:
-
-    $worksheet->filter_column_list( 'A', 'March', 'April', 'May' );
-
-The C<$column> parameter can either be a zero indexed column number or a string column name.
-
-One or more criteria can be selected:
-
-    $worksheet->filter_column_list( 0, 'March' );
-    $worksheet->filter_column_list( 1, 100, 110, 120, 130 );
-
-B<NOTE:> It isn't sufficient to just specify the filter condition. You must also hide any rows that don't match the filter condition. Rows are hidden using the C<set_row()> C<visible> parameter. C<Excel::Writer::XLSX> cannot do this automatically since it isn't part of the file format. See the C<autofilter.pl> program in the examples directory of the distro for an example.
-
-
-
-
-=head2 convert_date_time( $date_string )
-
-The C<convert_date_time()> method is used internally by the C<write_date_time()> method to convert date strings to a number that represents an Excel date and time.
-
-It is exposed as a public method for utility purposes.
-
-The C<$date_string> format is detailed in the C<write_date_time()> method.
-
-=head2 Worksheet set_vba_name()
-
-The Worksheet C<set_vba_name()> method can be used to set the VBA codename for the
-worksheet (there is a similar method for the workbook VBA name). This is sometimes required when a C<vbaProject> macro included via C<add_vba_project()> refers to the worksheet. The default Excel VBA name of C<Sheet1>, etc., is used if a user defined name isn't specified.
-
-See also L<WORKING WITH VBA MACROS>.
-
-
-
-=head1 PAGE SET-UP METHODS
-
-Page set-up methods affect the way that a worksheet looks when it is printed. They control features such as page headers and footers and margins. These methods are really just standard worksheet methods. They are documented here in a separate section for the sake of clarity.
-
-The following methods are available for page set-up:
-
-    set_landscape()
-    set_portrait()
-    set_page_view()
-    set_paper()
-    center_horizontally()
-    center_vertically()
-    set_margins()
-    set_header()
-    set_footer()
-    repeat_rows()
-    repeat_columns()
-    hide_gridlines()
-    print_row_col_headers()
-    print_area()
-    print_across()
-    fit_to_pages()
-    set_start_page()
-    set_print_scale()
-    print_black_and_white()
-    set_h_pagebreaks()
-    set_v_pagebreaks()
-
-A common requirement when working with Excel::Writer::XLSX is to apply the same page set-up features to all of the worksheets in a workbook. To do this you can use the C<sheets()> method of the C<workbook> class to access the array of worksheets in a workbook:
-
-    for $worksheet ( $workbook->sheets() ) {
-        $worksheet->set_landscape();
-    }
-
-
-
-
-=head2 set_landscape()
-
-This method is used to set the orientation of a worksheet's printed page to landscape:
-
-    $worksheet->set_landscape();    # Landscape mode
-
-
-
-
-=head2 set_portrait()
-
-This method is used to set the orientation of a worksheet's printed page to portrait. The default worksheet orientation is portrait, so you won't generally need to call this method.
-
-    $worksheet->set_portrait();    # Portrait mode
-
-
-
-=head2 set_page_view()
-
-This method is used to display the worksheet in "Page View/Layout" mode.
-
-    $worksheet->set_page_view();
-
-
-
-=head2 set_paper( $index )
-
-This method is used to set the paper format for the printed output of a worksheet. The following paper styles are available:
-
-    Index   Paper format            Paper size
-    =====   ============            ==========
-      0     Printer default         -
-      1     Letter                  8 1/2 x 11 in
-      2     Letter Small            8 1/2 x 11 in
-      3     Tabloid                 11 x 17 in
-      4     Ledger                  17 x 11 in
-      5     Legal                   8 1/2 x 14 in
-      6     Statement               5 1/2 x 8 1/2 in
-      7     Executive               7 1/4 x 10 1/2 in
-      8     A3                      297 x 420 mm
-      9     A4                      210 x 297 mm
-     10     A4 Small                210 x 297 mm
-     11     A5                      148 x 210 mm
-     12     B4                      250 x 354 mm
-     13     B5                      182 x 257 mm
-     14     Folio                   8 1/2 x 13 in
-     15     Quarto                  215 x 275 mm
-     16     -                       10x14 in
-     17     -                       11x17 in
-     18     Note                    8 1/2 x 11 in
-     19     Envelope  9             3 7/8 x 8 7/8
-     20     Envelope 10             4 1/8 x 9 1/2
-     21     Envelope 11             4 1/2 x 10 3/8
-     22     Envelope 12             4 3/4 x 11
-     23     Envelope 14             5 x 11 1/2
-     24     C size sheet            -
-     25     D size sheet            -
-     26     E size sheet            -
-     27     Envelope DL             110 x 220 mm
-     28     Envelope C3             324 x 458 mm
-     29     Envelope C4             229 x 324 mm
-     30     Envelope C5             162 x 229 mm
-     31     Envelope C6             114 x 162 mm
-     32     Envelope C65            114 x 229 mm
-     33     Envelope B4             250 x 353 mm
-     34     Envelope B5             176 x 250 mm
-     35     Envelope B6             176 x 125 mm
-     36     Envelope                110 x 230 mm
-     37     Monarch                 3.875 x 7.5 in
-     38     Envelope                3 5/8 x 6 1/2 in
-     39     Fanfold                 14 7/8 x 11 in
-     40     German Std Fanfold      8 1/2 x 12 in
-     41     German Legal Fanfold    8 1/2 x 13 in
-
-
-Note, it is likely that not all of these paper types will be available to the end user since it will depend on the paper formats that the user's printer supports. Therefore, it is best to stick to standard paper types.
-
-    $worksheet->set_paper( 1 );    # US Letter
-    $worksheet->set_paper( 9 );    # A4
-
-If you do not specify a paper type the worksheet will print using the printer's default paper.
-
-
-
-
-=head2 center_horizontally()
-
-Center the worksheet data horizontally between the margins on the printed page:
-
-    $worksheet->center_horizontally();
-
-
-
-
-=head2 center_vertically()
-
-Center the worksheet data vertically between the margins on the printed page:
-
-    $worksheet->center_vertically();
-
-
-
-
-=head2 set_margins( $inches )
-
-There are several methods available for setting the worksheet margins on the printed page:
-
-    set_margins()        # Set all margins to the same value
-    set_margins_LR()     # Set left and right margins to the same value
-    set_margins_TB()     # Set top and bottom margins to the same value
-    set_margin_left();   # Set left margin
-    set_margin_right();  # Set right margin
-    set_margin_top();    # Set top margin
-    set_margin_bottom(); # Set bottom margin
-
-All of these methods take a distance in inches as a parameter. Note: 1 inch = 25.4mm. C<;-)> The default left and right margin is 0.7 inch. The default top and bottom margin is 0.75 inch. Note, these defaults are different from the defaults used in the binary file format by Spreadsheet::WriteExcel.
-
-
-
-=head2 set_header( $string, $margin )
-
-Headers and footers are generated using a C<$string> which is a combination of plain text and control characters. The C<$margin> parameter is optional.
-
-The available control character are:
-
-    Control             Category            Description
-    =======             ========            ===========
-    &L                  Justification       Left
-    &C                                      Center
-    &R                                      Right
-
-    &P                  Information         Page number
-    &N                                      Total number of pages
-    &D                                      Date
-    &T                                      Time
-    &F                                      File name
-    &A                                      Worksheet name
-    &Z                                      Workbook path
-
-    &fontsize           Font                Font size
-    &"font,style"                           Font name and style
-    &U                                      Single underline
-    &E                                      Double underline
-    &S                                      Strikethrough
-    &X                                      Superscript
-    &Y                                      Subscript
-
-    &[Picture]          Images              Image placeholder
-    &G                                      Same as &[Picture]
-
-    &&                  Miscellaneous       Literal ampersand &
-
-
-Text in headers and footers can be justified (aligned) to the left, center and right by prefixing the text with the control characters C<&L>, C<&C> and C<&R>.
-
-For example (with ASCII art representation of the results):
-
-    $worksheet->set_header('&LHello');
-
-     ---------------------------------------------------------------
-    |                                                               |
-    | Hello                                                         |
-    |                                                               |
-
-
-    $worksheet->set_header('&CHello');
-
-     ---------------------------------------------------------------
-    |                                                               |
-    |                          Hello                                |
-    |                                                               |
-
-
-    $worksheet->set_header('&RHello');
-
-     ---------------------------------------------------------------
-    |                                                               |
-    |                                                         Hello |
-    |                                                               |
-
-
-For simple text, if you do not specify any justification the text will be centred. However, you must prefix the text with C<&C> if you specify a font name or any other formatting:
-
-    $worksheet->set_header('Hello');
-
-     ---------------------------------------------------------------
-    |                                                               |
-    |                          Hello                                |
-    |                                                               |
-
-
-You can have text in each of the justification regions:
-
-    $worksheet->set_header('&LCiao&CBello&RCielo');
-
-     ---------------------------------------------------------------
-    |                                                               |
-    | Ciao                     Bello                          Cielo |
-    |                                                               |
-
-
-The information control characters act as variables that Excel will update as the workbook or worksheet changes. Times and dates are in the users default format:
-
-    $worksheet->set_header('&CPage &P of &N');
-
-     ---------------------------------------------------------------
-    |                                                               |
-    |                        Page 1 of 6                            |
-    |                                                               |
-
-
-    $worksheet->set_header('&CUpdated at &T');
-
-     ---------------------------------------------------------------
-    |                                                               |
-    |                    Updated at 12:30 PM                        |
-    |                                                               |
-
-
-Images can be inserted using the options shown below. Each image must have a placeholder in header string using the C<&[Picture]> or C<&G> control characters:
-
-    $worksheet->set_header( '&L&G', 0.3, { image_left => 'logo.jpg' });
-
-
-
-You can specify the font size of a section of the text by prefixing it with the control character C<&n> where C<n> is the font size:
-
-    $worksheet1->set_header( '&C&30Hello Big' );
-    $worksheet2->set_header( '&C&10Hello Small' );
-
-You can specify the font of a section of the text by prefixing it with the control sequence C<&"font,style"> where C<fontname> is a font name such as "Courier New" or "Times New Roman" and C<style> is one of the standard Windows font descriptions: "Regular", "Italic", "Bold" or "Bold Italic":
-
-    $worksheet1->set_header( '&C&"Courier New,Italic"Hello' );
-    $worksheet2->set_header( '&C&"Courier New,Bold Italic"Hello' );
-    $worksheet3->set_header( '&C&"Times New Roman,Regular"Hello' );
-
-It is possible to combine all of these features together to create sophisticated headers and footers. As an aid to setting up complicated headers and footers you can record a page set-up as a macro in Excel and look at the format strings that VBA produces. Remember however that VBA uses two double quotes C<""> to indicate a single double quote. For the last example above the equivalent VBA code looks like this:
-
-    .LeftHeader   = ""
-    .CenterHeader = "&""Times New Roman,Regular""Hello"
-    .RightHeader  = ""
-
-
-To include a single literal ampersand C<&> in a header or footer you should use a double ampersand C<&&>:
-
-    $worksheet1->set_header('&CCuriouser && Curiouser - Attorneys at Law');
-
-As stated above the margin parameter is optional. As with the other margins the value should be in inches. The default header and footer margin is 0.3 inch. Note, the default margin is different from the default used in the binary file format by Spreadsheet::WriteExcel. The header and footer margin size can be set as follows:
-
-    $worksheet->set_header( '&CHello', 0.75 );
-
-The header and footer margins are independent of the top and bottom margins.
-
-The available options are:
-
-=over
-
-=item * C<image_left> The path to the image. Requires a C<&G> or C<&[Picture]> placeholder.
-
-=item * C<image_center> Same as above.
-
-=item * C<image_right> Same as above.
-
-=item * C<scale_with_doc> Scale header with document. Defaults to true.
-
-=item * C<align_with_margins> Align header to margins. Defaults to true.
-
-=back
-
-The image options must have an accompanying C<&[Picture]> or C<&G> control
-character in the header string:
-
-    $worksheet->set_header(
-        '&L&[Picture]&C&[Picture]&R&[Picture]',
-        undef, # If you don't want to change the margin.
-        {
-            image_left   => 'red.jpg',
-            image_center => 'blue.jpg',
-            image_right  => 'yellow.jpg'
-        }
-      );
-
-
-Note, the header or footer string must be less than 255 characters. Strings longer than this will not be written and a warning will be generated.
-
-The C<set_header()> method can also handle Unicode strings in C<UTF-8> format.
-
-    $worksheet->set_header( "&C\x{263a}" )
-
-
-See, also the C<headers.pl> program in the C<examples> directory of the distribution.
-
-
-
-
-=head2 set_footer( $string, $margin )
-
-The syntax of the C<set_footer()> method is the same as C<set_header()>,  see above.
-
-
-
-
-=head2 repeat_rows( $first_row, $last_row )
-
-Set the number of rows to repeat at the top of each printed page.
-
-For large Excel documents it is often desirable to have the first row or rows of the worksheet print out at the top of each page. This can be achieved by using the C<repeat_rows()> method. The parameters C<$first_row> and C<$last_row> are zero based. The C<$last_row> parameter is optional if you only wish to specify one row:
-
-    $worksheet1->repeat_rows( 0 );    # Repeat the first row
-    $worksheet2->repeat_rows( 0, 1 ); # Repeat the first two rows
-
-
-
-
-=head2 repeat_columns( $first_col, $last_col )
-
-Set the columns to repeat at the left hand side of each printed page.
-
-For large Excel documents it is often desirable to have the first column or columns of the worksheet print out at the left hand side of each page. This can be achieved by using the C<repeat_columns()> method. The parameters C<$first_column> and C<$last_column> are zero based. The C<$last_column> parameter is optional if you only wish to specify one column. You can also specify the columns using A1 column notation, see the note about L</Cell notation>.
-
-    $worksheet1->repeat_columns( 0 );        # Repeat the first column
-    $worksheet2->repeat_columns( 0, 1 );     # Repeat the first two columns
-    $worksheet3->repeat_columns( 'A:A' );    # Repeat the first column
-    $worksheet4->repeat_columns( 'A:B' );    # Repeat the first two columns
-
-
-
-
-=head2 hide_gridlines( $option )
-
-This method is used to hide the gridlines on the screen and printed page. Gridlines are the lines that divide the cells on a worksheet. Screen and printed gridlines are turned on by default in an Excel worksheet. If you have defined your own cell borders you may wish to hide the default gridlines.
-
-    $worksheet->hide_gridlines();
-
-The following values of C<$option> are valid:
-
-    0 : Don't hide gridlines
-    1 : Hide printed gridlines only
-    2 : Hide screen and printed gridlines
-
-If you don't supply an argument or use C<undef> the default option is 1, i.e. only the printed gridlines are hidden.
-
-
-
-
-=head2 print_row_col_headers()
-
-Set the option to print the row and column headers on the printed page.
-
-An Excel worksheet looks something like the following;
-
-     ------------------------------------------
-    |   |   A   |   B   |   C   |   D   |  ...
-     ------------------------------------------
-    | 1 |       |       |       |       |  ...
-    | 2 |       |       |       |       |  ...
-    | 3 |       |       |       |       |  ...
-    | 4 |       |       |       |       |  ...
-    |...|  ...  |  ...  |  ...  |  ...  |  ...
-
-The headers are the letters and numbers at the top and the left of the worksheet. Since these headers serve mainly as a indication of position on the worksheet they generally do not appear on the printed page. If you wish to have them printed you can use the C<print_row_col_headers()> method :
-
-    $worksheet->print_row_col_headers();
-
-Do not confuse these headers with page headers as described in the C<set_header()> section above.
-
-
-
-
-=head2 print_area( $first_row, $first_col, $last_row, $last_col )
-
-This method is used to specify the area of the worksheet that will be printed. All four parameters must be specified. You can also use A1 notation, see the note about L</Cell notation>.
-
-
-    $worksheet1->print_area( 'A1:H20' );    # Cells A1 to H20
-    $worksheet2->print_area( 0, 0, 19, 7 ); # The same
-    $worksheet2->print_area( 'A:H' );       # Columns A to H if rows have data
-
-
-
-
-=head2 print_across()
-
-The C<print_across> method is used to change the default print direction. This is referred to by Excel as the sheet "page order".
-
-    $worksheet->print_across();
-
-The default page order is shown below for a worksheet that extends over 4 pages. The order is called "down then across":
-
-    [1] [3]
-    [2] [4]
-
-However, by using the C<print_across> method the print order will be changed to "across then down":
-
-    [1] [2]
-    [3] [4]
-
-
-
-
-=head2 fit_to_pages( $width, $height )
-
-The C<fit_to_pages()> method is used to fit the printed area to a specific number of pages both vertically and horizontally. If the printed area exceeds the specified number of pages it will be scaled down to fit. This guarantees that the printed area will always appear on the specified number of pages even if the page size or margins change.
-
-    $worksheet1->fit_to_pages( 1, 1 );    # Fit to 1x1 pages
-    $worksheet2->fit_to_pages( 2, 1 );    # Fit to 2x1 pages
-    $worksheet3->fit_to_pages( 1, 2 );    # Fit to 1x2 pages
-
-The print area can be defined using the C<print_area()> method as described above.
-
-A common requirement is to fit the printed output to I<n> pages wide but have the height be as long as necessary. To achieve this set the C<$height> to zero:
-
-    $worksheet1->fit_to_pages( 1, 0 );    # 1 page wide and as long as necessary
-
-Note that although it is valid to use both C<fit_to_pages()> and C<set_print_scale()> on the same worksheet only one of these options can be active at a time. The last method call made will set the active option.
-
-Note that C<fit_to_pages()> will override any manual page breaks that are defined in the worksheet.
-
-Note: When using C<fit_to_pages()> it may also be required to set the printer paper size using C<set_paper()> or else Excel will default to "US Letter".
-
-
-=head2 set_start_page( $start_page )
-
-The C<set_start_page()> method is used to set the number of the starting page when the worksheet is printed out. The default value is 1.
-
-    $worksheet->set_start_page( 2 );
-
-
-
-
-=head2 set_print_scale( $scale )
-
-Set the scale factor of the printed page. Scale factors in the range C<10 E<lt>= $scale E<lt>= 400> are valid:
-
-    $worksheet1->set_print_scale( 50 );
-    $worksheet2->set_print_scale( 75 );
-    $worksheet3->set_print_scale( 300 );
-    $worksheet4->set_print_scale( 400 );
-
-The default scale factor is 100. Note, C<set_print_scale()> does not affect the scale of the visible page in Excel. For that you should use C<set_zoom()>.
-
-Note also that although it is valid to use both C<fit_to_pages()> and C<set_print_scale()> on the same worksheet only one of these options can be active at a time. The last method call made will set the active option.
-
-
-
-
-=head2 print_black_and_white()
-
-Set the option to print the worksheet in black and white:
-
-    $worksheet->print_black_and_white();
-
-
-
-
-=head2 set_h_pagebreaks( @breaks )
-
-Add horizontal page breaks to a worksheet. A page break causes all the data that follows it to be printed on the next page. Horizontal page breaks act between rows. To create a page break between rows 20 and 21 you must specify the break at row 21. However in zero index notation this is actually row 20. So you can pretend for a small while that you are using 1 index notation:
-
-    $worksheet1->set_h_pagebreaks( 20 );    # Break between row 20 and 21
-
-The C<set_h_pagebreaks()> method will accept a list of page breaks and you can call it more than once:
-
-    $worksheet2->set_h_pagebreaks( 20,  40,  60,  80,  100 );    # Add breaks
-    $worksheet2->set_h_pagebreaks( 120, 140, 160, 180, 200 );    # Add some more
-
-Note: If you specify the "fit to page" option via the C<fit_to_pages()> method it will override all manual page breaks.
-
-There is a silent limitation of about 1000 horizontal page breaks per worksheet in line with an Excel internal limitation.
-
-
-
-
-=head2 set_v_pagebreaks( @breaks )
-
-Add vertical page breaks to a worksheet. A page break causes all the data that follows it to be printed on the next page. Vertical page breaks act between columns. To create a page break between columns 20 and 21 you must specify the break at column 21. However in zero index notation this is actually column 20. So you can pretend for a small while that you are using 1 index notation:
-
-    $worksheet1->set_v_pagebreaks(20); # Break between column 20 and 21
-
-The C<set_v_pagebreaks()> method will accept a list of page breaks and you can call it more than once:
-
-    $worksheet2->set_v_pagebreaks( 20,  40,  60,  80,  100 );    # Add breaks
-    $worksheet2->set_v_pagebreaks( 120, 140, 160, 180, 200 );    # Add some more
-
-Note: If you specify the "fit to page" option via the C<fit_to_pages()> method it will override all manual page breaks.
-
-
-
-
-=head1 CELL FORMATTING
-
-This section describes the methods and properties that are available for formatting cells in Excel. The properties of a cell that can be formatted include: fonts, colours, patterns, borders, alignment and number formatting.
-
-
-=head2 Creating and using a Format object
-
-Cell formatting is defined through a Format object. Format objects are created by calling the workbook C<add_format()> method as follows:
-
-    my $format1 = $workbook->add_format();            # Set properties later
-    my $format2 = $workbook->add_format( %props );    # Set at creation
-
-The format object holds all the formatting properties that can be applied to a cell, a row or a column. The process of setting these properties is discussed in the next section.
-
-Once a Format object has been constructed and its properties have been set it can be passed as an argument to the worksheet C<write> methods as follows:
-
-    $worksheet->write( 0, 0, 'One', $format );
-    $worksheet->write_string( 1, 0, 'Two', $format );
-    $worksheet->write_number( 2, 0, 3, $format );
-    $worksheet->write_blank( 3, 0, $format );
-
-Formats can also be passed to the worksheet C<set_row()> and C<set_column()> methods to define the default property for a row or column.
-
-    $worksheet->set_row( 0, 15, $format );
-    $worksheet->set_column( 0, 0, 15, $format );
-
-
-
-
-=head2 Format methods and Format properties
-
-The following table shows the Excel format categories, the formatting properties that can be applied and the equivalent object method:
-
-
-    Category   Description       Property        Method Name
-    --------   -----------       --------        -----------
-    Font       Font type         font            set_font()
-               Font size         size            set_size()
-               Font color        color           set_color()
-               Bold              bold            set_bold()
-               Italic            italic          set_italic()
-               Underline         underline       set_underline()
-               Strikeout         font_strikeout  set_font_strikeout()
-               Super/Subscript   font_script     set_font_script()
-               Outline           font_outline    set_font_outline()
-               Shadow            font_shadow     set_font_shadow()
-
-    Number     Numeric format    num_format      set_num_format()
-
-    Protection Lock cells        locked          set_locked()
-               Hide formulas     hidden          set_hidden()
-
-    Alignment  Horizontal align  align           set_align()
-               Vertical align    valign          set_align()
-               Rotation          rotation        set_rotation()
-               Text wrap         text_wrap       set_text_wrap()
-               Justify last      text_justlast   set_text_justlast()
-               Center across     center_across   set_center_across()
-               Indentation       indent          set_indent()
-               Shrink to fit     shrink          set_shrink()
-
-    Pattern    Cell pattern      pattern         set_pattern()
-               Background color  bg_color        set_bg_color()
-               Foreground color  fg_color        set_fg_color()
-
-    Border     Cell border       border          set_border()
-               Bottom border     bottom          set_bottom()
-               Top border        top             set_top()
-               Left border       left            set_left()
-               Right border      right           set_right()
-               Border color      border_color    set_border_color()
-               Bottom color      bottom_color    set_bottom_color()
-               Top color         top_color       set_top_color()
-               Left color        left_color      set_left_color()
-               Right color       right_color     set_right_color()
-               Diagonal type     diag_type       set_diag_type()
-               Diagonal border   diag_border     set_diag_border()
-               Diagonal color    diag_color      set_diag_color()
-
-There are two ways of setting Format properties: by using the object method interface or by setting the property directly. For example, a typical use of the method interface would be as follows:
-
-    my $format = $workbook->add_format();
-    $format->set_bold();
-    $format->set_color( 'red' );
-
-By comparison the properties can be set directly by passing a hash of properties to the Format constructor:
-
-    my $format = $workbook->add_format( bold => 1, color => 'red' );
-
-or after the Format has been constructed by means of the C<set_format_properties()> method as follows:
-
-    my $format = $workbook->add_format();
-    $format->set_format_properties( bold => 1, color => 'red' );
-
-You can also store the properties in one or more named hashes and pass them to the required method:
-
-    my %font = (
-        font  => 'Calibri',
-        size  => 12,
-        color => 'blue',
-        bold  => 1,
-    );
-
-    my %shading = (
-        bg_color => 'green',
-        pattern  => 1,
-    );
-
-
-    my $format1 = $workbook->add_format( %font );            # Font only
-    my $format2 = $workbook->add_format( %font, %shading );  # Font and shading
-
-
-The provision of two ways of setting properties might lead you to wonder which is the best way. The method mechanism may be better if you prefer setting properties via method calls (which the author did when the code was first written) otherwise passing properties to the constructor has proved to be a little more flexible and self documenting in practice. An additional advantage of working with property hashes is that it allows you to share formatting between workbook objects as shown in the example above.
-
-The Perl/Tk style of adding properties is also supported:
-
-    my %font = (
-        -font  => 'Calibri',
-        -size  => 12,
-        -color => 'blue',
-        -bold  => 1,
-    );
-
-
-
-
-=head2 Working with formats
-
-The default format is Calibri 11 with all other properties off.
-
-Each unique format in Excel::Writer::XLSX must have a corresponding Format object. It isn't possible to use a Format with a write() method and then redefine the Format for use at a later stage. This is because a Format is applied to a cell not in its current state but in its final state. Consider the following example:
-
-    my $format = $workbook->add_format();
-    $format->set_bold();
-    $format->set_color( 'red' );
-    $worksheet->write( 'A1', 'Cell A1', $format );
-    $format->set_color( 'green' );
-    $worksheet->write( 'B1', 'Cell B1', $format );
-
-Cell A1 is assigned the Format C<$format> which is initially set to the colour red. However, the colour is subsequently set to green. When Excel displays Cell A1 it will display the final state of the Format which in this case will be the colour green.
-
-In general a method call without an argument will turn a property on, for example:
-
-    my $format1 = $workbook->add_format();
-    $format1->set_bold();       # Turns bold on
-    $format1->set_bold( 1 );    # Also turns bold on
-    $format1->set_bold( 0 );    # Turns bold off
-
-
-
-
-=head1 FORMAT METHODS
-
-The Format object methods are described in more detail in the following sections. In addition, there is a Perl program called C<formats.pl> in the C<examples> directory of the WriteExcel distribution. This program creates an Excel workbook called C<formats.xlsx> which contains examples of almost all the format types.
-
-The following Format methods are available:
-
-    set_font()
-    set_size()
-    set_color()
-    set_bold()
-    set_italic()
-    set_underline()
-    set_font_strikeout()
-    set_font_script()
-    set_font_outline()
-    set_font_shadow()
-    set_num_format()
-    set_locked()
-    set_hidden()
-    set_align()
-    set_rotation()
-    set_text_wrap()
-    set_text_justlast()
-    set_center_across()
-    set_indent()
-    set_shrink()
-    set_pattern()
-    set_bg_color()
-    set_fg_color()
-    set_border()
-    set_bottom()
-    set_top()
-    set_left()
-    set_right()
-    set_border_color()
-    set_bottom_color()
-    set_top_color()
-    set_left_color()
-    set_right_color()
-    set_diag_type()
-    set_diag_border()
-    set_diag_color()
-
-
-The above methods can also be applied directly as properties. For example C<< $format->set_bold() >> is equivalent to C<< $workbook->add_format(bold => 1) >>.
-
-
-=head2 set_format_properties( %properties )
-
-The properties of an existing Format object can be also be set by means of C<set_format_properties()>:
-
-    my $format = $workbook->add_format();
-    $format->set_format_properties( bold => 1, color => 'red' );
-
-However, this method is here mainly for legacy reasons. It is preferable to set the properties in the format constructor:
-
-    my $format = $workbook->add_format( bold => 1, color => 'red' );
-
-
-=head2 set_font( $fontname )
-
-    Default state:      Font is Calibri
-    Default action:     None
-    Valid args:         Any valid font name
-
-Specify the font used:
-
-    $format->set_font('Times New Roman');
-
-Excel can only display fonts that are installed on the system that it is running on. Therefore it is best to use the fonts that come as standard such as 'Calibri', 'Times New Roman' and 'Courier New'. See also the Fonts worksheet created by formats.pl
-
-
-
-
-=head2 set_size()
-
-    Default state:      Font size is 10
-    Default action:     Set font size to 1
-    Valid args:         Integer values from 1 to as big as your screen.
-
-
-Set the font size. Excel adjusts the height of a row to accommodate the largest font size in the row. You can also explicitly specify the height of a row using the set_row() worksheet method.
-
-    my $format = $workbook->add_format();
-    $format->set_size( 30 );
-
-
-
-
-
-=head2 set_color()
-
-    Default state:      Excels default color, usually black
-    Default action:     Set the default color
-    Valid args:         Integers from 8..63 or the following strings:
-                        'black'
-                        'blue'
-                        'brown'
-                        'cyan'
-                        'gray'
-                        'green'
-                        'lime'
-                        'magenta'
-                        'navy'
-                        'orange'
-                        'pink'
-                        'purple'
-                        'red'
-                        'silver'
-                        'white'
-                        'yellow'
-
-Set the font colour. The C<set_color()> method is used as follows:
-
-    my $format = $workbook->add_format();
-    $format->set_color( 'red' );
-    $worksheet->write( 0, 0, 'wheelbarrow', $format );
-
-Note: The C<set_color()> method is used to set the colour of the font in a cell. To set the colour of a cell use the C<set_bg_color()> and C<set_pattern()> methods.
-
-For additional examples see the 'Named colors' and 'Standard colors' worksheets created by formats.pl in the examples directory.
-
-See also L</WORKING WITH COLOURS>.
-
-
-
-
-=head2 set_bold()
-
-    Default state:      bold is off
-    Default action:     Turn bold on
-    Valid args:         0, 1
-
-Set the bold property of the font:
-
-    $format->set_bold();  # Turn bold on
-
-
-
-
-=head2 set_italic()
-
-    Default state:      Italic is off
-    Default action:     Turn italic on
-    Valid args:         0, 1
-
-Set the italic property of the font:
-
-    $format->set_italic();  # Turn italic on
-
-
-
-
-=head2 set_underline()
-
-    Default state:      Underline is off
-    Default action:     Turn on single underline
-    Valid args:         0  = No underline
-                        1  = Single underline
-                        2  = Double underline
-                        33 = Single accounting underline
-                        34 = Double accounting underline
-
-Set the underline property of the font.
-
-    $format->set_underline();   # Single underline
-
-
-
-
-=head2 set_font_strikeout()
-
-    Default state:      Strikeout is off
-    Default action:     Turn strikeout on
-    Valid args:         0, 1
-
-Set the strikeout property of the font.
-
-
-
-
-=head2 set_font_script()
-
-    Default state:      Super/Subscript is off
-    Default action:     Turn Superscript on
-    Valid args:         0  = Normal
-                        1  = Superscript
-                        2  = Subscript
-
-Set the superscript/subscript property of the font.
-
-
-
-
-=head2 set_font_outline()
-
-    Default state:      Outline is off
-    Default action:     Turn outline on
-    Valid args:         0, 1
-
-Macintosh only.
-
-
-
-
-=head2 set_font_shadow()
-
-    Default state:      Shadow is off
-    Default action:     Turn shadow on
-    Valid args:         0, 1
-
-Macintosh only.
-
-
-
-
-=head2 set_num_format()
-
-    Default state:      General format
-    Default action:     Format index 1
-    Valid args:         See the following table
-
-This method is used to define the numerical format of a number in Excel. It controls whether a number is displayed as an integer, a floating point number, a date, a currency value or some other user defined format.
-
-The numerical format of a cell can be specified by using a format string or an index to one of Excel's built-in formats:
-
-    my $format1 = $workbook->add_format();
-    my $format2 = $workbook->add_format();
-    $format1->set_num_format( 'd mmm yyyy' );    # Format string
-    $format2->set_num_format( 0x0f );            # Format index
-
-    $worksheet->write( 0, 0, 36892.521, $format1 );    # 1 Jan 2001
-    $worksheet->write( 0, 0, 36892.521, $format2 );    # 1-Jan-01
-
-
-Using format strings you can define very sophisticated formatting of numbers.
-
-    $format01->set_num_format( '0.000' );
-    $worksheet->write( 0, 0, 3.1415926, $format01 );    # 3.142
-
-    $format02->set_num_format( '#,##0' );
-    $worksheet->write( 1, 0, 1234.56, $format02 );      # 1,235
-
-    $format03->set_num_format( '#,##0.00' );
-    $worksheet->write( 2, 0, 1234.56, $format03 );      # 1,234.56
-
-    $format04->set_num_format( '$0.00' );
-    $worksheet->write( 3, 0, 49.99, $format04 );        # $49.99
-
-    # Note you can use other currency symbols such as the pound or yen as well.
-    # Other currencies may require the use of Unicode.
-
-    $format07->set_num_format( 'mm/dd/yy' );
-    $worksheet->write( 6, 0, 36892.521, $format07 );    # 01/01/01
-
-    $format08->set_num_format( 'mmm d yyyy' );
-    $worksheet->write( 7, 0, 36892.521, $format08 );    # Jan 1 2001
-
-    $format09->set_num_format( 'd mmmm yyyy' );
-    $worksheet->write( 8, 0, 36892.521, $format09 );    # 1 January 2001
-
-    $format10->set_num_format( 'dd/mm/yyyy hh:mm AM/PM' );
-    $worksheet->write( 9, 0, 36892.521, $format10 );    # 01/01/2001 12:30 AM
-
-    $format11->set_num_format( '0 "dollar and" .00 "cents"' );
-    $worksheet->write( 10, 0, 1.87, $format11 );        # 1 dollar and .87 cents
-
-    # Conditional numerical formatting.
-    $format12->set_num_format( '[Green]General;[Red]-General;General' );
-    $worksheet->write( 11, 0, 123, $format12 );         # > 0 Green
-    $worksheet->write( 12, 0, -45, $format12 );         # < 0 Red
-    $worksheet->write( 13, 0, 0,   $format12 );         # = 0 Default colour
-
-    # Zip code
-    $format13->set_num_format( '00000' );
-    $worksheet->write( 14, 0, '01209', $format13 );
-
-
-The number system used for dates is described in L</DATES AND TIME IN EXCEL>.
-
-The colour format should have one of the following values:
-
-    [Black] [Blue] [Cyan] [Green] [Magenta] [Red] [White] [Yellow]
-
-Alternatively you can specify the colour based on a colour index as follows: C<[Color n]>, where n is a standard Excel colour index - 7. See the 'Standard colors' worksheet created by formats.pl.
-
-For more information refer to the documentation on formatting in the C<docs> directory of the Excel::Writer::XLSX distro, the Excel on-line help or L<http://office.microsoft.com/en-gb/assistance/HP051995001033.aspx>.
-
-You should ensure that the format string is valid in Excel prior to using it in WriteExcel.
-
-Excel's built-in formats are shown in the following table:
-
-    Index   Index   Format String
-    0       0x00    General
-    1       0x01    0
-    2       0x02    0.00
-    3       0x03    #,##0
-    4       0x04    #,##0.00
-    5       0x05    ($#,##0_);($#,##0)
-    6       0x06    ($#,##0_);[Red]($#,##0)
-    7       0x07    ($#,##0.00_);($#,##0.00)
-    8       0x08    ($#,##0.00_);[Red]($#,##0.00)
-    9       0x09    0%
-    10      0x0a    0.00%
-    11      0x0b    0.00E+00
-    12      0x0c    # ?/?
-    13      0x0d    # ??/??
-    14      0x0e    m/d/yy
-    15      0x0f    d-mmm-yy
-    16      0x10    d-mmm
-    17      0x11    mmm-yy
-    18      0x12    h:mm AM/PM
-    19      0x13    h:mm:ss AM/PM
-    20      0x14    h:mm
-    21      0x15    h:mm:ss
-    22      0x16    m/d/yy h:mm
-    ..      ....    ...........
-    37      0x25    (#,##0_);(#,##0)
-    38      0x26    (#,##0_);[Red](#,##0)
-    39      0x27    (#,##0.00_);(#,##0.00)
-    40      0x28    (#,##0.00_);[Red](#,##0.00)
-    41      0x29    _(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)
-    42      0x2a    _($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)
-    43      0x2b    _(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)
-    44      0x2c    _($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)
-    45      0x2d    mm:ss
-    46      0x2e    [h]:mm:ss
-    47      0x2f    mm:ss.0
-    48      0x30    ##0.0E+0
-    49      0x31    @
-
-
-For examples of these formatting codes see the 'Numerical formats' worksheet created by formats.pl. See also the number_formats1.html and the number_formats2.html documents in the C<docs> directory of the distro.
-
-Note 1. Numeric formats 23 to 36 are not documented by Microsoft and may differ in international versions.
-
-Note 2. The dollar sign appears as the defined local currency symbol.
-
-
-
-
-=head2 set_locked()
-
-    Default state:      Cell locking is on
-    Default action:     Turn locking on
-    Valid args:         0, 1
-
-This property can be used to prevent modification of a cells contents. Following Excel's convention, cell locking is turned on by default. However, it only has an effect if the worksheet has been protected, see the worksheet C<protect()> method.
-
-    my $locked = $workbook->add_format();
-    $locked->set_locked( 1 );    # A non-op
-
-    my $unlocked = $workbook->add_format();
-    $locked->set_locked( 0 );
-
-    # Enable worksheet protection
-    $worksheet->protect();
-
-    # This cell cannot be edited.
-    $worksheet->write( 'A1', '=1+2', $locked );
-
-    # This cell can be edited.
-    $worksheet->write( 'A2', '=1+2', $unlocked );
-
-Note: This offers weak protection even with a password, see the note in relation to the C<protect()> method.
-
-
-
-
-=head2 set_hidden()
-
-    Default state:      Formula hiding is off
-    Default action:     Turn hiding on
-    Valid args:         0, 1
-
-This property is used to hide a formula while still displaying its result. This is generally used to hide complex calculations from end users who are only interested in the result. It only has an effect if the worksheet has been protected, see the worksheet C<protect()> method.
-
-    my $hidden = $workbook->add_format();
-    $hidden->set_hidden();
-
-    # Enable worksheet protection
-    $worksheet->protect();
-
-    # The formula in this cell isn't visible
-    $worksheet->write( 'A1', '=1+2', $hidden );
-
-
-Note: This offers weak protection even with a password, see the note in relation to the C<protect()> method.
-
-
-
-
-=head2 set_align()
-
-    Default state:      Alignment is off
-    Default action:     Left alignment
-    Valid args:         'left'              Horizontal
-                        'center'
-                        'right'
-                        'fill'
-                        'justify'
-                        'center_across'
-
-                        'top'               Vertical
-                        'vcenter'
-                        'bottom'
-                        'vjustify'
-
-This method is used to set the horizontal and vertical text alignment within a cell. Vertical and horizontal alignments can be combined. The method is used as follows:
-
-    my $format = $workbook->add_format();
-    $format->set_align( 'center' );
-    $format->set_align( 'vcenter' );
-    $worksheet->set_row( 0, 30 );
-    $worksheet->write( 0, 0, 'X', $format );
-
-Text can be aligned across two or more adjacent cells using the C<center_across> property. However, for genuine merged cells it is better to use the C<merge_range()> worksheet method.
-
-The C<vjustify> (vertical justify) option can be used to provide automatic text wrapping in a cell. The height of the cell will be adjusted to accommodate the wrapped text. To specify where the text wraps use the C<set_text_wrap()> method.
-
-
-For further examples see the 'Alignment' worksheet created by formats.pl.
-
-
-
-
-=head2 set_center_across()
-
-    Default state:      Center across selection is off
-    Default action:     Turn center across on
-    Valid args:         1
-
-Text can be aligned across two or more adjacent cells using the C<set_center_across()> method. This is an alias for the C<set_align('center_across')> method call.
-
-Only one cell should contain the text, the other cells should be blank:
-
-    my $format = $workbook->add_format();
-    $format->set_center_across();
-
-    $worksheet->write( 1, 1, 'Center across selection', $format );
-    $worksheet->write_blank( 1, 2, $format );
-
-See also the C<merge1.pl> to C<merge6.pl> programs in the C<examples> directory and the C<merge_range()> method.
-
-
-
-=head2 set_text_wrap()
-
-    Default state:      Text wrap is off
-    Default action:     Turn text wrap on
-    Valid args:         0, 1
-
-
-Here is an example using the text wrap property, the escape character C<\n> is used to indicate the end of line:
-
-    my $format = $workbook->add_format();
-    $format->set_text_wrap();
-    $worksheet->write( 0, 0, "It's\na bum\nwrap", $format );
-
-Excel will adjust the height of the row to accommodate the wrapped text. A similar effect can be obtained without newlines using the C<set_align('vjustify')> method. See the C<textwrap.pl> program in the C<examples> directory.
-
-
-
-
-=head2 set_rotation()
-
-    Default state:      Text rotation is off
-    Default action:     None
-    Valid args:         Integers in the range -90 to 90 and 270
-
-Set the rotation of the text in a cell. The rotation can be any angle in the range -90 to 90 degrees.
-
-    my $format = $workbook->add_format();
-    $format->set_rotation( 30 );
-    $worksheet->write( 0, 0, 'This text is rotated', $format );
-
-
-The angle 270 is also supported. This indicates text where the letters run from top to bottom.
-
-
-
-=head2 set_indent()
-
-    Default state:      Text indentation is off
-    Default action:     Indent text 1 level
-    Valid args:         Positive integers
-
-
-This method can be used to indent text. The argument, which should be an integer, is taken as the level of indentation:
-
-
-    my $format = $workbook->add_format();
-    $format->set_indent( 2 );
-    $worksheet->write( 0, 0, 'This text is indented', $format );
-
-
-Indentation is a horizontal alignment property. It will override any other horizontal properties but it can be used in conjunction with vertical properties.
-
-
-
-
-=head2 set_shrink()
-
-    Default state:      Text shrinking is off
-    Default action:     Turn "shrink to fit" on
-    Valid args:         1
-
-
-This method can be used to shrink text so that it fits in a cell.
-
-
-    my $format = $workbook->add_format();
-    $format->set_shrink();
-    $worksheet->write( 0, 0, 'Honey, I shrunk the text!', $format );
-
-
-
-
-=head2 set_text_justlast()
-
-    Default state:      Justify last is off
-    Default action:     Turn justify last on
-    Valid args:         0, 1
-
-
-Only applies to Far Eastern versions of Excel.
-
-
-
-
-=head2 set_pattern()
-
-    Default state:      Pattern is off
-    Default action:     Solid fill is on
-    Valid args:         0 .. 18
-
-Set the background pattern of a cell.
-
-Examples of the available patterns are shown in the 'Patterns' worksheet created by formats.pl. However, it is unlikely that you will ever need anything other than Pattern 1 which is a solid fill of the background color.
-
-
-
-
-=head2 set_bg_color()
-
-    Default state:      Color is off
-    Default action:     Solid fill.
-    Valid args:         See set_color()
-
-The C<set_bg_color()> method can be used to set the background colour of a pattern. Patterns are defined via the C<set_pattern()> method. If a pattern hasn't been defined then a solid fill pattern is used as the default.
-
-Here is an example of how to set up a solid fill in a cell:
-
-    my $format = $workbook->add_format();
-
-    $format->set_pattern();    # This is optional when using a solid fill
-
-    $format->set_bg_color( 'green' );
-    $worksheet->write( 'A1', 'Ray', $format );
-
-For further examples see the 'Patterns' worksheet created by formats.pl.
-
-
-
-
-=head2 set_fg_color()
-
-    Default state:      Color is off
-    Default action:     Solid fill.
-    Valid args:         See set_color()
-
-
-The C<set_fg_color()> method can be used to set the foreground colour of a pattern.
-
-For further examples see the 'Patterns' worksheet created by formats.pl.
-
-
-
-
-=head2 set_border()
-
-    Also applies to:    set_bottom()
-                        set_top()
-                        set_left()
-                        set_right()
-
-    Default state:      Border is off
-    Default action:     Set border type 1
-    Valid args:         0-13, See below.
-
-A cell border is comprised of a border on the bottom, top, left and right. These can be set to the same value using C<set_border()> or individually using the relevant method calls shown above.
-
-The following shows the border styles sorted by Excel::Writer::XLSX index number:
-
-    Index   Name            Weight   Style
-    =====   =============   ======   ===========
-    0       None            0
-    1       Continuous      1        -----------
-    2       Continuous      2        -----------
-    3       Dash            1        - - - - - -
-    4       Dot             1        . . . . . .
-    5       Continuous      3        -----------
-    6       Double          3        ===========
-    7       Continuous      0        -----------
-    8       Dash            2        - - - - - -
-    9       Dash Dot        1        - . - . - .
-    10      Dash Dot        2        - . - . - .
-    11      Dash Dot Dot    1        - . . - . .
-    12      Dash Dot Dot    2        - . . - . .
-    13      SlantDash Dot   2        / - . / - .
-
-
-The following shows the borders sorted by style:
-
-    Name            Weight   Style         Index
-    =============   ======   ===========   =====
-    Continuous      0        -----------   7
-    Continuous      1        -----------   1
-    Continuous      2        -----------   2
-    Continuous      3        -----------   5
-    Dash            1        - - - - - -   3
-    Dash            2        - - - - - -   8
-    Dash Dot        1        - . - . - .   9
-    Dash Dot        2        - . - . - .   10
-    Dash Dot Dot    1        - . . - . .   11
-    Dash Dot Dot    2        - . . - . .   12
-    Dot             1        . . . . . .   4
-    Double          3        ===========   6
-    None            0                      0
-    SlantDash Dot   2        / - . / - .   13
-
-
-The following shows the borders in the order shown in the Excel Dialog.
-
-    Index   Style             Index   Style
-    =====   =====             =====   =====
-    0       None              12      - . . - . .
-    7       -----------       13      / - . / - .
-    4       . . . . . .       10      - . - . - .
-    11      - . . - . .       8       - - - - - -
-    9       - . - . - .       2       -----------
-    3       - - - - - -       5       -----------
-    1       -----------       6       ===========
-
-
-Examples of the available border styles are shown in the 'Borders' worksheet created by formats.pl.
-
-
-
-
-=head2 set_border_color()
-
-    Also applies to:    set_bottom_color()
-                        set_top_color()
-                        set_left_color()
-                        set_right_color()
-
-    Default state:      Color is off
-    Default action:     Undefined
-    Valid args:         See set_color()
-
-
-Set the colour of the cell borders. A cell border is comprised of a border on the bottom, top, left and right. These can be set to the same colour using C<set_border_color()> or individually using the relevant method calls shown above. Examples of the border styles and colours are shown in the 'Borders' worksheet created by formats.pl.
-
-
-=head2 set_diag_type()
-
-    Default state:      Diagonal border is off.
-    Default action:     None.
-    Valid args:         1-3, See below.
-
-Set the diagonal border type for the cell. Three types of diagonal borders are available in Excel:
-
-   1: From bottom left to top right.
-   2: From top left to bottom right.
-   3: Same as 1 and 2 combined.
-
-For example:
-
-    $format->set_diag_type( 3 );
-
-
-
-=head2 set_diag_border()
-
-    Default state:      Border is off
-    Default action:     Set border type 1
-    Valid args:         0-13, See below.
-
-Set the diagonal border style. Same as the parameter to C<set_border()> above.
-
-
-
-
-=head2 set_diag_color()
-
-    Default state:      Color is off
-    Default action:     Undefined
-    Valid args:         See set_color()
-
-
-Set the colour of the diagonal cell border:
-
-    $format->set_diag_type( 3 );
-    $format->set_diag_border( 7 );
-    $format->set_diag_color( 'red' );
-
-
-
-=head2 copy( $format )
-
-This method is used to copy all of the properties from one Format object to another:
-
-    my $lorry1 = $workbook->add_format();
-    $lorry1->set_bold();
-    $lorry1->set_italic();
-    $lorry1->set_color( 'red' );    # lorry1 is bold, italic and red
-
-    my $lorry2 = $workbook->add_format();
-    $lorry2->copy( $lorry1 );
-    $lorry2->set_color( 'yellow' );    # lorry2 is bold, italic and yellow
-
-The C<copy()> method is only useful if you are using the method interface to Format properties. It generally isn't required if you are setting Format properties directly using hashes.
-
-
-Note: this is not a copy constructor, both objects must exist prior to copying.
-
-
-
-
-=head1 UNICODE IN EXCEL
-
-The following is a brief introduction to handling Unicode in C<Excel::Writer::XLSX>.
-
-I<For a more general introduction to Unicode handling in Perl see> L<perlunitut> and L<perluniintro>.
-
-Excel::Writer::XLSX writer differs from Spreadsheet::WriteExcel in that it only handles Unicode data in C<UTF-8> format and doesn't try to handle legacy UTF-16 Excel formats.
-
-If the data is in C<UTF-8> format then Excel::Writer::XLSX will handle it automatically.
-
-If you are dealing with non-ASCII characters that aren't in C<UTF-8> then perl provides useful tools in the guise of the C<Encode> module to help you to convert to the required format. For example:
-
-    use Encode 'decode';
-
-    my $string = 'some string with koi8-r characters';
-       $string = decode('koi8-r', $string); # koi8-r to utf8
-
-Alternatively you can read data from an encoded file and convert it to C<UTF-8> as you read it in:
-
-
-    my $file = 'unicode_koi8r.txt';
-    open FH, '<:encoding(koi8-r)', $file or die "Couldn't open $file: $!\n";
-
-    my $row = 0;
-    while ( <FH> ) {
-        # Data read in is now in utf8 format.
-        chomp;
-        $worksheet->write( $row++, 0, $_ );
-    }
-
-These methodologies are explained in more detail in L<perlunitut>, L<perluniintro> and L<perlunicode>.
-
-If the program contains UTF-8 text then you will also need to add C<use utf8> to the includes:
-
-    use utf8;
-
-    ...
-
-    $worksheet->write( 'A1', 'Some UTF-8 string' );
-
-
-See also the C<unicode_*.pl> programs in the examples directory of the distro.
-
-
-
-
-=head1 WORKING WITH COLOURS
-
-Throughout Excel::Writer::XLSX colours can be specified using a Html style C<#RRGGBB> value. For example with a Format object:
-
-    $format->set_font_color( '#FF0000' );
-
-For backward compatibility a limited number of color names are supported:
-
-    $format->set_font_color( 'red' );
-
-The color names supported are:
-
-    black
-    blue
-    brown
-    cyan
-    gray
-    green
-    lime
-    magenta
-    navy
-    orange
-    pink
-    purple
-    red
-    silver
-    white
-    yellow
-
-See also C<colors.pl> in the C<examples> directory.
-
-
-=head1 DATES AND TIME IN EXCEL
-
-There are two important things to understand about dates and times in Excel:
-
-=over 4
-
-=item 1 A date/time in Excel is a real number plus an Excel number format.
-
-=item 2 Excel::Writer::XLSX doesn't automatically convert date/time strings in C<write()> to an Excel date/time.
-
-=back
-
-These two points are explained in more detail below along with some suggestions on how to convert times and dates to the required format.
-
-
-=head2 An Excel date/time is a number plus a format
-
-If you write a date string with C<write()> then all you will get is a string:
-
-    $worksheet->write( 'A1', '02/03/04' );   # !! Writes a string not a date. !!
-
-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.
-
-A date or time in Excel is just like any other number. To have the number display as a date you must apply an Excel number format to it. Here are some examples.
-
-    #!/usr/bin/perl -w
-
-    use strict;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'date_examples.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    $worksheet->set_column( 'A:A', 30 );    # For extra visibility.
-
-    my $number = 39506.5;
-
-    $worksheet->write( 'A1', $number );             #   39506.5
-
-    my $format2 = $workbook->add_format( num_format => 'dd/mm/yy' );
-    $worksheet->write( 'A2', $number, $format2 );    #  28/02/08
-
-    my $format3 = $workbook->add_format( num_format => 'mm/dd/yy' );
-    $worksheet->write( 'A3', $number, $format3 );    #  02/28/08
-
-    my $format4 = $workbook->add_format( num_format => 'd-m-yyyy' );
-    $worksheet->write( 'A4', $number, $format4 );    #  28-2-2008
-
-    my $format5 = $workbook->add_format( num_format => 'dd/mm/yy hh:mm' );
-    $worksheet->write( 'A5', $number, $format5 );    #  28/02/08 12:00
-
-    my $format6 = $workbook->add_format( num_format => 'd mmm yyyy' );
-    $worksheet->write( 'A6', $number, $format6 );    # 28 Feb 2008
-
-    my $format7 = $workbook->add_format( num_format => 'mmm d yyyy hh:mm AM/PM' );
-    $worksheet->write('A7', $number , $format7);     #  Feb 28 2008 12:00 PM
-
-    $workbook->close();
-
-=head2 Excel::Writer::XLSX doesn't automatically convert date/time strings
-
-Excel::Writer::XLSX doesn't automatically convert input date strings into Excel's formatted date numbers due to the large number of possible date formats and also due to the possibility of misinterpretation.
-
-For example, does C<02/03/04> mean March 2 2004, February 3 2004 or even March 4 2002.
-
-Therefore, in order to handle dates you will have to convert them to numbers and apply an Excel format. Some methods for converting dates are listed in the next section.
-
-The most direct way is to convert your dates to the ISO8601 C<yyyy-mm-ddThh:mm:ss.sss> date format and use the C<write_date_time()> worksheet method:
-
-    $worksheet->write_date_time( 'A2', '2001-01-01T12:20', $format );
-
-See the C<write_date_time()> section of the documentation for more details.
-
-A general methodology for handling date strings with C<write_date_time()> is:
-
-    1. Identify incoming date/time strings with a regex.
-    2. Extract the component parts of the date/time using the same regex.
-    3. Convert the date/time to the ISO8601 format.
-    4. Write the date/time using write_date_time() and a number format.
-
-Here is an example:
-
-    #!/usr/bin/perl -w
-
-    use strict;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'example.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    # Set the default format for dates.
-    my $date_format = $workbook->add_format( num_format => 'mmm d yyyy' );
-
-    # Increase column width to improve visibility of data.
-    $worksheet->set_column( 'A:C', 20 );
-
-    # Simulate reading from a data source.
-    my $row = 0;
-
-    while ( <DATA> ) {
-        chomp;
-
-        my $col  = 0;
-        my @data = split ' ';
-
-        for my $item ( @data ) {
-
-            # Match dates in the following formats: d/m/yy, d/m/yyyy
-            if ( $item =~ qr[^(\d{1,2})/(\d{1,2})/(\d{4})$] ) {
-
-                # Change to the date format required by write_date_time().
-                my $date = sprintf "%4d-%02d-%02dT", $3, $2, $1;
-
-                $worksheet->write_date_time( $row, $col++, $date,
-                    $date_format );
-            }
-            else {
-
-                # Just plain data
-                $worksheet->write( $row, $col++, $item );
-            }
-        }
-        $row++;
-    }
-
-    $workbook->close();
-
-    __DATA__
-    Item    Cost    Date
-    Book    10      1/9/2007
-    Beer    4       12/9/2007
-    Bed     500     5/10/2007
-
-For a slightly more advanced solution you can modify the C<write()> method to handle date formats of your choice via the C<add_write_handler()> method. See the C<add_write_handler()> section of the docs and the write_handler3.pl and write_handler4.pl programs in the examples directory of the distro.
-
-
-=head2 Converting dates and times to an Excel date or time
-
-The C<write_date_time()> method above is just one way of handling dates and times.
-
-You can also use the C<convert_date_time()> worksheet method to convert from an ISO8601 style date string to an Excel date and time number.
-
-The L<Excel::Writer::XLSX::Utility> module which is included in the distro has date/time handling functions:
-
-    use Excel::Writer::XLSX::Utility;
-
-    $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
-
-Note: some of these functions require additional CPAN modules.
-
-For date conversions using the CPAN C<DateTime> framework see L<DateTime::Format::Excel> L<http://search.cpan.org/search?dist=DateTime-Format-Excel>.
-
-
-
-
-=head1 OUTLINES AND GROUPING IN EXCEL
-
-
-Excel allows you to group rows or columns so that they can be hidden or displayed with a single mouse click. This feature is referred to as outlines.
-
-Outlines can reduce complex data down to a few salient sub-totals or summaries.
-
-This feature is best viewed in Excel but the following is an ASCII representation of what a worksheet with three outlines might look like. Rows 3-4 and rows 7-8 are grouped at level 2. Rows 2-9 are grouped at level 1. The lines at the left hand side are called outline level bars.
-
-
-            ------------------------------------------
-     1 2 3 |   |   A   |   B   |   C   |   D   |  ...
-            ------------------------------------------
-      _    | 1 |   A   |       |       |       |  ...
-     |  _  | 2 |   B   |       |       |       |  ...
-     | |   | 3 |  (C)  |       |       |       |  ...
-     | |   | 4 |  (D)  |       |       |       |  ...
-     | -   | 5 |   E   |       |       |       |  ...
-     |  _  | 6 |   F   |       |       |       |  ...
-     | |   | 7 |  (G)  |       |       |       |  ...
-     | |   | 8 |  (H)  |       |       |       |  ...
-     | -   | 9 |   I   |       |       |       |  ...
-     -     | . |  ...  |  ...  |  ...  |  ...  |  ...
-
-
-Clicking the minus sign on each of the level 2 outlines will collapse and hide the data as shown in the next figure. The minus sign changes to a plus sign to indicate that the data in the outline is hidden.
-
-            ------------------------------------------
-     1 2 3 |   |   A   |   B   |   C   |   D   |  ...
-            ------------------------------------------
-      _    | 1 |   A   |       |       |       |  ...
-     |     | 2 |   B   |       |       |       |  ...
-     | +   | 5 |   E   |       |       |       |  ...
-     |     | 6 |   F   |       |       |       |  ...
-     | +   | 9 |   I   |       |       |       |  ...
-     -     | . |  ...  |  ...  |  ...  |  ...  |  ...
-
-
-Clicking on the minus sign on the level 1 outline will collapse the remaining rows as follows:
-
-            ------------------------------------------
-     1 2 3 |   |   A   |   B   |   C   |   D   |  ...
-            ------------------------------------------
-           | 1 |   A   |       |       |       |  ...
-     +     | . |  ...  |  ...  |  ...  |  ...  |  ...
-
-
-Grouping in C<Excel::Writer::XLSX> is achieved by setting the outline level via the C<set_row()> and C<set_column()> worksheet methods:
-
-    set_row( $row, $height, $format, $hidden, $level, $collapsed )
-    set_column( $first_col, $last_col, $width, $format, $hidden, $level, $collapsed )
-
-The following example sets an outline level of 1 for rows 2 and 3 (zero-indexed) and columns B to G. The parameters C<$height> and C<$XF> are assigned default values since they are undefined:
-
-    $worksheet->set_row( 1, undef, undef, 0, 1 );
-    $worksheet->set_row( 2, undef, undef, 0, 1 );
-    $worksheet->set_column( 'B:G', undef, undef, 0, 1 );
-
-Excel allows up to 7 outline levels. Therefore the C<$level> parameter should be in the range C<0 E<lt>= $level E<lt>= 7>.
-
-Rows and columns can be collapsed by setting the C<$hidden> flag for the hidden rows/columns and setting the C<$collapsed> flag for the row/column that has the collapsed C<+> symbol:
-
-    $worksheet->set_row( 1, undef, undef, 1, 1 );
-    $worksheet->set_row( 2, undef, undef, 1, 1 );
-    $worksheet->set_row( 3, undef, undef, 0, 0, 1 );          # Collapsed flag.
-
-    $worksheet->set_column( 'B:G', undef, undef, 1, 1 );
-    $worksheet->set_column( 'H:H', undef, undef, 0, 0, 1 );   # Collapsed flag.
-
-Note: Setting the C<$collapsed> flag is particularly important for compatibility with OpenOffice.org and Gnumeric.
-
-For a more complete example see the C<outline.pl> and C<outline_collapsed.pl> programs in the examples directory of the distro.
-
-Some additional outline properties can be set via the C<outline_settings()> worksheet method, see above.
-
-
-
-
-=head1 DATA VALIDATION IN EXCEL
-
-Data validation is a feature of Excel which allows you to restrict the data that a users enters in a cell and to display help and warning messages. It also allows you to restrict input to values in a drop down list.
-
-A typical use case might be to restrict data in a cell to integer values in a certain range, to provide a help message to indicate the required value and to issue a warning if the input data doesn't meet the stated criteria. In Excel::Writer::XLSX we could do that as follows:
-
-    $worksheet->data_validation('B3',
-        {
-            validate        => 'integer',
-            criteria        => 'between',
-            minimum         => 1,
-            maximum         => 100,
-            input_title     => 'Input an integer:',
-            input_message   => 'Between 1 and 100',
-            error_message   => 'Sorry, try again.',
-        });
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/validation_example.jpg" alt="The output from the above example"/></center></p>
-
-=end html
-
-For more information on data validation see the following Microsoft support article "Description and examples of data validation in Excel": L<http://support.microsoft.com/kb/211485>.
-
-The following sections describe how to use the C<data_validation()> method and its various options.
-
-
-=head2 data_validation( $row, $col, { parameter => 'value', ... } )
-
-The C<data_validation()> method is used to construct an Excel data validation.
-
-It can be applied to a single cell or a range of cells. You can pass 3 parameters such as C<($row, $col, {...})> or 5 parameters such as C<($first_row, $first_col, $last_row, $last_col, {...})>. You can also use C<A1> style notation. For example:
-
-    $worksheet->data_validation( 0, 0,       {...} );
-    $worksheet->data_validation( 0, 0, 4, 1, {...} );
-
-    # Which are the same as:
-
-    $worksheet->data_validation( 'A1',       {...} );
-    $worksheet->data_validation( 'A1:B5',    {...} );
-
-See also the note about L</Cell notation> for more information.
-
-
-The last parameter in C<data_validation()> must be a hash ref containing the parameters that describe the type and style of the data validation. The allowable parameters are:
-
-    validate
-    criteria
-    value | minimum | source
-    maximum
-    ignore_blank
-    dropdown
-
-    input_title
-    input_message
-    show_input
-
-    error_title
-    error_message
-    error_type
-    show_error
-
-These parameters are explained in the following sections. Most of the parameters are optional, however, you will generally require the three main options C<validate>, C<criteria> and C<value>.
-
-    $worksheet->data_validation('B3',
-        {
-            validate => 'integer',
-            criteria => '>',
-            value    => 100,
-        });
-
-The C<data_validation> method returns:
-
-     0 for success.
-    -1 for insufficient number of arguments.
-    -2 for row or column out of bounds.
-    -3 for incorrect parameter or value.
-
-
-=head2 validate
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<validate> parameter is used to set the type of data that you wish to validate. It is always required and it has no default value. Allowable values are:
-
-    any
-    integer
-    decimal
-    list
-    date
-    time
-    length
-    custom
-
-=over
-
-=item * B<any> is used to specify that the type of data is unrestricted. This is useful to display an input message without restricting the data that can be entered.
-
-=item * B<integer> restricts the cell to integer values. Excel refers to this as 'whole number'.
-
-    validate => 'integer',
-    criteria => '>',
-    value    => 100,
-
-=item * B<decimal> restricts the cell to decimal values.
-
-    validate => 'decimal',
-    criteria => '>',
-    value    => 38.6,
-
-=item * B<list> restricts the cell to a set of user specified values. These can be passed in an array ref or as a cell range (named ranges aren't currently supported):
-
-    validate => 'list',
-    value    => ['open', 'high', 'close'],
-    # Or like this:
-    value    => 'B1:B3',
-
-Excel requires that range references are only to cells on the same worksheet.
-
-=item * B<date> restricts the cell to date values. Dates in Excel are expressed as integer values but you can also pass an ISO8601 style string as used in C<write_date_time()>. See also L</DATES AND TIME IN EXCEL> for more information about working with Excel's dates.
-
-    validate => 'date',
-    criteria => '>',
-    value    => 39653, # 24 July 2008
-    # Or like this:
-    value    => '2008-07-24T',
-
-=item * B<time> restricts the cell to time values. Times in Excel are expressed as decimal values but you can also pass an ISO8601 style string as used in C<write_date_time()>. See also L</DATES AND TIME IN EXCEL> for more information about working with Excel's times.
-
-    validate => 'time',
-    criteria => '>',
-    value    => 0.5, # Noon
-    # Or like this:
-    value    => 'T12:00:00',
-
-=item * B<length> restricts the cell data based on an integer string length. Excel refers to this as 'Text length'.
-
-    validate => 'length',
-    criteria => '>',
-    value    => 10,
-
-=item * B<custom> restricts the cell based on an external Excel formula that returns a C<TRUE/FALSE> value.
-
-    validate => 'custom',
-    value    => '=IF(A10>B10,TRUE,FALSE)',
-
-=back
-
-
-=head2 criteria
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<criteria> parameter is used to set the criteria by which the data in the cell is validated. It is almost always required except for the C<list> and C<custom> validate options. It has no default value. Allowable values are:
-
-    'between'
-    'not between'
-    'equal to'                  |  '=='  |  '='
-    'not equal to'              |  '!='  |  '<>'
-    'greater than'              |  '>'
-    'less than'                 |  '<'
-    'greater than or equal to'  |  '>='
-    'less than or equal to'     |  '<='
-
-You can either use Excel's textual description strings, in the first column above, or the more common symbolic alternatives. The following are equivalent:
-
-    validate => 'integer',
-    criteria => 'greater than',
-    value    => 100,
-
-    validate => 'integer',
-    criteria => '>',
-    value    => 100,
-
-The C<list> and C<custom> validate options don't require a C<criteria>. If you specify one it will be ignored.
-
-    validate => 'list',
-    value    => ['open', 'high', 'close'],
-
-    validate => 'custom',
-    value    => '=IF(A10>B10,TRUE,FALSE)',
-
-
-=head2 value | minimum | source
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<value> parameter is used to set the limiting value to which the C<criteria> is applied. It is always required and it has no default value. You can also use the synonyms C<minimum> or C<source> to make the validation a little clearer and closer to Excel's description of the parameter:
-
-    # Use 'value'
-    validate => 'integer',
-    criteria => '>',
-    value    => 100,
-
-    # Use 'minimum'
-    validate => 'integer',
-    criteria => 'between',
-    minimum  => 1,
-    maximum  => 100,
-
-    # Use 'source'
-    validate => 'list',
-    source   => '$B$1:$B$3',
-
-
-=head2 maximum
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<maximum> parameter is used to set the upper limiting value when the C<criteria> is either C<'between'> or C<'not between'>:
-
-    validate => 'integer',
-    criteria => 'between',
-    minimum  => 1,
-    maximum  => 100,
-
-
-=head2 ignore_blank
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<ignore_blank> parameter is used to toggle on and off the 'Ignore blank' option in the Excel data validation dialog. When the option is on the data validation is not applied to blank data in the cell. It is on by default.
-
-    ignore_blank => 0,  # Turn the option off
-
-
-=head2 dropdown
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<dropdown> parameter is used to toggle on and off the 'In-cell dropdown' option in the Excel data validation dialog. When the option is on a dropdown list will be shown for C<list> validations. It is on by default.
-
-    dropdown => 0,      # Turn the option off
-
-
-=head2 input_title
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<input_title> parameter is used to set the title of the input message that is displayed when a cell is entered. It has no default value and is only displayed if the input message is displayed. See the C<input_message> parameter below.
-
-    input_title   => 'This is the input title',
-
-The maximum title length is 32 characters.
-
-
-=head2 input_message
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<input_message> parameter is used to set the input message that is displayed when a cell is entered. It has no default value.
-
-    validate      => 'integer',
-    criteria      => 'between',
-    minimum       => 1,
-    maximum       => 100,
-    input_title   => 'Enter the applied discount:',
-    input_message => 'between 1 and 100',
-
-The message can be split over several lines using newlines, C<"\n"> in double quoted strings.
-
-    input_message => "This is\na test.",
-
-The maximum message length is 255 characters.
-
-
-=head2 show_input
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<show_input> parameter is used to toggle on and off the 'Show input message when cell is selected' option in the Excel data validation dialog. When the option is off an input message is not displayed even if it has been set using C<input_message>. It is on by default.
-
-    show_input => 0,      # Turn the option off
-
-
-=head2 error_title
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<error_title> parameter is used to set the title of the error message that is displayed when the data validation criteria is not met. The default error title is 'Microsoft Excel'.
-
-    error_title   => 'Input value is not valid',
-
-The maximum title length is 32 characters.
-
-
-=head2 error_message
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<error_message> parameter is used to set the error message that is displayed when a cell is entered. The default error message is "The value you entered is not valid.\nA user has restricted values that can be entered into the cell.".
-
-    validate      => 'integer',
-    criteria      => 'between',
-    minimum       => 1,
-    maximum       => 100,
-    error_title   => 'Input value is not valid',
-    error_message => 'It should be an integer between 1 and 100',
-
-The message can be split over several lines using newlines, C<"\n"> in double quoted strings.
-
-    input_message => "This is\na test.",
-
-The maximum message length is 255 characters.
-
-
-=head2 error_type
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<error_type> parameter is used to specify the type of error dialog that is displayed. There are 3 options:
-
-    'stop'
-    'warning'
-    'information'
-
-The default is C<'stop'>.
-
-
-=head2 show_error
-
-This parameter is passed in a hash ref to C<data_validation()>.
-
-The C<show_error> parameter is used to toggle on and off the 'Show error alert after invalid data is entered' option in the Excel data validation dialog. When the option is off an error message is not displayed even if it has been set using C<error_message>. It is on by default.
-
-    show_error => 0,      # Turn the option off
-
-=head2 Data Validation Examples
-
-Example 1. Limiting input to an integer greater than a fixed value.
-
-    $worksheet->data_validation('A1',
-        {
-            validate        => 'integer',
-            criteria        => '>',
-            value           => 0,
-        });
-
-Example 2. Limiting input to an integer greater than a fixed value where the value is referenced from a cell.
-
-    $worksheet->data_validation('A2',
-        {
-            validate        => 'integer',
-            criteria        => '>',
-            value           => '=E3',
-        });
-
-Example 3. Limiting input to a decimal in a fixed range.
-
-    $worksheet->data_validation('A3',
-        {
-            validate        => 'decimal',
-            criteria        => 'between',
-            minimum         => 0.1,
-            maximum         => 0.5,
-        });
-
-Example 4. Limiting input to a value in a dropdown list.
-
-    $worksheet->data_validation('A4',
-        {
-            validate        => 'list',
-            source          => ['open', 'high', 'close'],
-        });
-
-Example 5. Limiting input to a value in a dropdown list where the list is specified as a cell range.
-
-    $worksheet->data_validation('A5',
-        {
-            validate        => 'list',
-            source          => '=$E$4:$G$4',
-        });
-
-Example 6. Limiting input to a date in a fixed range.
-
-    $worksheet->data_validation('A6',
-        {
-            validate        => 'date',
-            criteria        => 'between',
-            minimum         => '2008-01-01T',
-            maximum         => '2008-12-12T',
-        });
-
-Example 7. Displaying a message when the cell is selected.
-
-    $worksheet->data_validation('A7',
-        {
-            validate      => 'integer',
-            criteria      => 'between',
-            minimum       => 1,
-            maximum       => 100,
-            input_title   => 'Enter an integer:',
-            input_message => 'between 1 and 100',
-        });
-
-See also the C<data_validate.pl> program in the examples directory of the distro.
-
-
-
-
-=head1 CONDITIONAL FORMATTING IN EXCEL
-
-Conditional formatting is a feature of Excel which allows you to apply a format to a cell or a range of cells based on a certain criteria.
-
-For example the following criteria is used to highlight cells >= 50 in red in the C<conditional_format.pl> example from the distro:
-
-    # Write a conditional format over a range.
-    $worksheet->conditional_formatting( 'B3:K12',
-        {
-            type     => 'cell',
-            criteria => '>=',
-            value    => 50,
-            format   => $format1,
-        }
-    );
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/conditional_example.jpg" alt="The output from the above example"/></center></p>
-
-=end html
-
-
-
-=head2 conditional_formatting( $row, $col, { parameter => 'value', ... } )
-
-The C<conditional_formatting()> method is used to apply formatting  based on user defined criteria to an Excel::Writer::XLSX file.
-
-It can be applied to a single cell or a range of cells. You can pass 3 parameters such as C<($row, $col, {...})> or 5 parameters such as C<($first_row, $first_col, $last_row, $last_col, {...})>. You can also use C<A1> style notation. For example:
-
-    $worksheet->conditional_formatting( 0, 0,       {...} );
-    $worksheet->conditional_formatting( 0, 0, 4, 1, {...} );
-
-    # Which are the same as:
-
-    $worksheet->conditional_formatting( 'A1',       {...} );
-    $worksheet->conditional_formatting( 'A1:B5',    {...} );
-
-See also the note about L</Cell notation> for more information.
-
-Using C<A1> style notation is also possible to specify non-contiguous ranges, separated by a comma. For example:
-
-    $worksheet->conditional_formatting( 'A1:D5,A8:D12', {...} );
-
-The last parameter in C<conditional_formatting()> must be a hash ref containing the parameters that describe the type and style of the data validation. The main parameters are:
-
-    type
-    format
-    criteria
-    value
-    minimum
-    maximum
-
-Other, less commonly used parameters are:
-
-    min_type
-    mid_type
-    max_type
-    min_value
-    mid_value
-    max_value
-    min_color
-    mid_color
-    max_color
-    bar_color
-    bar_only
-    bar_solid
-    bar_negative_color
-    bar_border_color
-    bar_negative_border_color
-    bar_negative_color_same
-    bar_negative_border_color_same
-    bar_no_border
-    bar_direction
-    bar_axis_position
-    bar_axis_color
-    data_bar_2010
-    icon_style
-    icons
-    reverse_icons
-    icons_only
-    stop_if_true
-    multi_range
-
-Additional parameters which are used for specific conditional format types are shown in the relevant sections below.
-
-=head2 type
-
-This parameter is passed in a hash ref to C<conditional_formatting()>.
-
-The C<type> parameter is used to set the type of conditional formatting that you wish to apply. It is always required and it has no default value. Allowable C<type> values and their associated parameters are:
-
-    Type            Parameters
-    ====            ==========
-    cell            criteria
-                    value
-                    minimum
-                    maximum
-                    format
-
-    date            criteria
-                    value
-                    minimum
-                    maximum
-                    format
-
-    time_period     criteria
-                    format
-
-    text            criteria
-                    value
-                    format
-
-    average         criteria
-                    format
-
-    duplicate       format
-
-    unique          format
-
-    top             criteria
-                    value
-                    format
-
-    bottom          criteria
-                    value
-                    format
-
-    blanks          format
-
-    no_blanks       format
-
-    errors          format
-
-    no_errors       format
-
-    formula         criteria
-                    format
-
-    2_color_scale   min_type
-                    max_type
-                    min_value
-                    max_value
-                    min_color
-                    max_color
-
-    3_color_scale   min_type
-                    mid_type
-                    max_type
-                    min_value
-                    mid_value
-                    max_value
-                    min_color
-                    mid_color
-                    max_color
-
-    data_bar        min_type
-                    max_type
-                    min_value
-                    max_value
-                    bar_only
-                    bar_color
-                    bar_solid*
-                    bar_negative_color*
-                    bar_border_color*
-                    bar_negative_border_color*
-                    bar_negative_color_same*
-                    bar_negative_border_color_same*
-                    bar_no_border*
-                    bar_direction*
-                    bar_axis_position*
-                    bar_axis_color*
-                    data_bar_2010*
-
-    icon_set        icon_style
-                    reverse_icons
-                    icons
-                    icons_only
-
-Data bar parameters marked with (*) are only available in Excel 2010 and later. Files that use these properties can still be opened in Excel 2007 but the data bars will be displayed without them.
-
-
-=head2 type => 'cell'
-
-This is the most common conditional formatting type. It is used when a format is applied to a cell based on a simple criterion. For example:
-
-    $worksheet->conditional_formatting( 'A1',
-        {
-            type     => 'cell',
-            criteria => 'greater than',
-            value    => 5,
-            format   => $red_format,
-        }
-    );
-
-Or, using the C<between> criteria:
-
-    $worksheet->conditional_formatting( 'C1:C4',
-        {
-            type     => 'cell',
-            criteria => 'between',
-            minimum  => 20,
-            maximum  => 30,
-            format   => $green_format,
-        }
-    );
-
-
-=head2 criteria
-
-The C<criteria> parameter is used to set the criteria by which the cell data will be evaluated. It has no default value. The most common criteria as applied to C<< { type => 'cell' } >> are:
-
-    'between'
-    'not between'
-    'equal to'                  |  '=='  |  '='
-    'not equal to'              |  '!='  |  '<>'
-    'greater than'              |  '>'
-    'less than'                 |  '<'
-    'greater than or equal to'  |  '>='
-    'less than or equal to'     |  '<='
-
-You can either use Excel's textual description strings, in the first column above, or the more common symbolic alternatives.
-
-Additional criteria which are specific to other conditional format types are shown in the relevant sections below.
-
-
-=head2 value
-
-The C<value> is generally used along with the C<criteria> parameter to set the rule by which the cell data  will be evaluated.
-
-    type     => 'cell',
-    criteria => '>',
-    value    => 5
-    format   => $format,
-
-The C<value> property can also be an cell reference.
-
-    type     => 'cell',
-    criteria => '>',
-    value    => '$C$1',
-    format   => $format,
-
-
-=head2 format
-
-The C<format> parameter is used to specify the format that will be applied to the cell when the conditional formatting criterion is met. The format is created using the C<add_format()> method in the same way as cell formats:
-
-    $format = $workbook->add_format( bold => 1, italic => 1 );
-
-    $worksheet->conditional_formatting( 'A1',
-        {
-            type     => 'cell',
-            criteria => '>',
-            value    => 5
-            format   => $format,
-        }
-    );
-
-The conditional format follows the same rules as in Excel: it is superimposed over the existing cell format and not all font and border properties can be modified. Font properties that can't be modified are font name, font size, superscript and subscript. The border property that cannot be modified is diagonal borders.
-
-Excel specifies some default formats to be used with conditional formatting. You can replicate them using the following Excel::Writer::XLSX formats:
-
-    # Light red fill with dark red text.
-
-    my $format1 = $workbook->add_format(
-        bg_color => '#FFC7CE',
-        color    => '#9C0006',
-    );
-
-    # Light yellow fill with dark yellow text.
-
-    my $format2 = $workbook->add_format(
-        bg_color => '#FFEB9C',
-        color    => '#9C6500',
-    );
-
-    # Green fill with dark green text.
-
-    my $format3 = $workbook->add_format(
-        bg_color => '#C6EFCE',
-        color    => '#006100',
-    );
-
-
-=head2 minimum
-
-The C<minimum> parameter is used to set the lower limiting value when the C<criteria> is either C<'between'> or C<'not between'>:
-
-    validate => 'integer',
-    criteria => 'between',
-    minimum  => 1,
-    maximum  => 100,
-
-
-=head2 maximum
-
-The C<maximum> parameter is used to set the upper limiting value when the C<criteria> is either C<'between'> or C<'not between'>. See the previous example.
-
-
-=head2 type => 'date'
-
-The C<date> type is the same as the C<cell> type and uses the same criteria and values. However it allows the C<value>, C<minimum> and C<maximum> properties to be specified in the ISO8601 C<yyyy-mm-ddThh:mm:ss.sss> date format which is detailed in the C<write_date_time()> method.
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'date',
-            criteria => 'greater than',
-            value    => '2011-01-01T',
-            format   => $format,
-        }
-    );
-
-
-=head2 type => 'time_period'
-
-The C<time_period> type is used to specify Excel's "Dates Occurring" style conditional format.
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'time_period',
-            criteria => 'yesterday',
-            format   => $format,
-        }
-    );
-
-The period is set in the C<criteria> and can have one of the following values:
-
-        criteria => 'yesterday',
-        criteria => 'today',
-        criteria => 'last 7 days',
-        criteria => 'last week',
-        criteria => 'this week',
-        criteria => 'next week',
-        criteria => 'last month',
-        criteria => 'this month',
-        criteria => 'next month'
-
-
-=head2 type => 'text'
-
-The C<text> type is used to specify Excel's "Specific Text" style conditional format. It is used to do simple string matching using the C<criteria> and C<value> parameters:
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'text',
-            criteria => 'containing',
-            value    => 'foo',
-            format   => $format,
-        }
-    );
-
-The C<criteria> can have one of the following values:
-
-    criteria => 'containing',
-    criteria => 'not containing',
-    criteria => 'begins with',
-    criteria => 'ends with',
-
-The C<value> parameter should be a string or single character.
-
-
-=head2 type => 'average'
-
-The C<average> type is used to specify Excel's "Average" style conditional format.
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'average',
-            criteria => 'above',
-            format   => $format,
-        }
-    );
-
-The type of average for the conditional format range is specified by the C<criteria>:
-
-    criteria => 'above',
-    criteria => 'below',
-    criteria => 'equal or above',
-    criteria => 'equal or below',
-    criteria => '1 std dev above',
-    criteria => '1 std dev below',
-    criteria => '2 std dev above',
-    criteria => '2 std dev below',
-    criteria => '3 std dev above',
-    criteria => '3 std dev below',
-
-
-
-=head2 type => 'duplicate'
-
-The C<duplicate> type is used to highlight duplicate cells in a range:
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'duplicate',
-            format   => $format,
-        }
-    );
-
-
-=head2 type => 'unique'
-
-The C<unique> type is used to highlight unique cells in a range:
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'unique',
-            format   => $format,
-        }
-    );
-
-
-=head2 type => 'top'
-
-The C<top> type is used to specify the top C<n> values by number or percentage in a range:
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'top',
-            value    => 10,
-            format   => $format,
-        }
-    );
-
-The C<criteria> can be used to indicate that a percentage condition is required:
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'top',
-            value    => 10,
-            criteria => '%',
-            format   => $format,
-        }
-    );
-
-
-=head2 type => 'bottom'
-
-The C<bottom> type is used to specify the bottom C<n> values by number or percentage in a range.
-
-It takes the same parameters as C<top>, see above.
-
-
-=head2 type => 'blanks'
-
-The C<blanks> type is used to highlight blank cells in a range:
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'blanks',
-            format   => $format,
-        }
-    );
-
-
-=head2 type => 'no_blanks'
-
-The C<no_blanks> type is used to highlight non blank cells in a range:
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'no_blanks',
-            format   => $format,
-        }
-    );
-
-
-=head2 type => 'errors'
-
-The C<errors> type is used to highlight error cells in a range:
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'errors',
-            format   => $format,
-        }
-    );
-
-
-=head2 type => 'no_errors'
-
-The C<no_errors> type is used to highlight non error cells in a range:
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'no_errors',
-            format   => $format,
-        }
-    );
-
-
-
-=head2 type => 'formula'
-
-The C<formula> type is used to specify a conditional format based on a user defined formula:
-
-    $worksheet->conditional_formatting( 'A1:A4',
-        {
-            type     => 'formula',
-            criteria => '=$A$1 > 5',
-            format   => $format,
-        }
-    );
-
-The formula is specified in the C<criteria>.
-
-
-=head2 type => '2_color_scale'
-
-The C<2_color_scale> type is used to specify Excel's "2 Color Scale" style conditional format.
-
-    $worksheet->conditional_formatting( 'A1:A12',
-        {
-            type  => '2_color_scale',
-        }
-    );
-
-This conditional type can be modified with C<min_type>, C<max_type>, C<min_value>, C<max_value>, C<min_color> and C<max_color>, see below.
-
-
-=head2 type => '3_color_scale'
-
-The C<3_color_scale> type is used to specify Excel's "3 Color Scale" style conditional format.
-
-    $worksheet->conditional_formatting( 'A1:A12',
-        {
-            type  => '3_color_scale',
-        }
-    );
-
-This conditional type can be modified with C<min_type>, C<mid_type>, C<max_type>, C<min_value>, C<mid_value>, C<max_value>, C<min_color>, C<mid_color> and C<max_color>, see below.
-
-
-=head2 type => 'data_bar'
-
-The C<data_bar> type is used to specify Excel's "Data Bar" style conditional format.
-
-    $worksheet->conditional_formatting( 'A1:A12',
-        {
-            type  => 'data_bar',
-        }
-    );
-
-This data bar conditional type can be modified with the following parameters, which are explained in the sections below. These properties were available in the original xlsx file specification used in Excel 2007::
-
-    min_type
-    max_type
-    min_value
-    max_value
-    bar_color
-    bar_only
-
-In Excel 2010 additional data bar properties were added such as solid (non-gradient) bars and control over how negative values are displayed. These properties can be set using the following parameters:
-
-    bar_solid
-    bar_negative_color
-    bar_border_color
-    bar_negative_border_color
-    bar_negative_color_same
-    bar_negative_border_color_same
-    bar_no_border
-    bar_direction
-    bar_axis_position
-    bar_axis_color
-    data_bar_2010
-
-Files that use these Excel 2010 properties can still be opened in Excel 2007 but the data bars will be displayed without them.
-
-
-
-=head2 type => 'icon_set'
-
-The C<icon_set> type is used to specify a conditional format with a set of icons such as traffic lights or arrows:
-
-    $worksheet->conditional_formatting( 'A1:C1',
-        {
-            type         => 'icon_set',
-            icon_style   => '3_traffic_lights',
-        }
-    );
-
-The icon set style is specified by the C<icon_style> parameter. Valid options are:
-
-    3_arrows
-    3_arrows_gray
-    3_flags
-    3_signs
-    3_symbols
-    3_symbols_circled
-    3_traffic_lights
-    3_traffic_lights_rimmed
-
-    4_arrows
-    4_arrows_gray
-    4_ratings
-    4_red_to_black
-    4_traffic_lights
-
-    5_arrows
-    5_arrows_gray
-    5_quarters
-    5_ratings
-
-The criteria, type and value of each icon can be specified using the C<icon> array of hash refs with optional C<criteria>, C<type> and C<value> parameters:
-
-    $worksheet->conditional_formatting( 'A1:D1',
-        {
-            type         => 'icon_set',
-            icon_style   => '4_red_to_black',
-            icons        => [ {criteria => '>',  type => 'number',     value => 90},
-                              {criteria => '>=', type => 'percentile', value => 50},
-                              {criteria => '>',  type => 'percent',    value => 25},
-                            ],
-        }
-    );
-
-
-The C<icons criteria> parameter should be either C<< >= >> or C<< > >>. The default C<criteria> is C<< >= >>.
-
-The C<icons type> parameter should be one of the following values:
-
-    number
-    percentile
-    percent
-    formula
-
-The default C<type> is C<percent>.
-
-The C<icons value> parameter can be a value or formula:
-
-    $worksheet->conditional_formatting( 'A1:D1',
-        {
-            type         => 'icon_set',
-            icon_style   => '4_red_to_black',
-            icons        => [ {value => 90},
-                              {value => 50},
-                              {value => 25},
-                            ],
-        }
-    );
-
-Note: The C<icons> parameters should start with the highest value and with each subsequent one being lower. The default C<value> is C<(n * 100) / number_of_icons>. The lowest number icon in an icon set has properties defined by Excel. Therefore in a C<n> icon set, there is no C<n-1> hash of parameters.
-
-The order of the icons can be reversed using the C<reverse_icons> parameter:
-
-    $worksheet->conditional_formatting( 'A1:C1',
-        {
-            type          => 'icon_set',
-            icon_style    => '3_arrows',
-            reverse_icons => 1,
-        }
-    );
-
-The icons can be displayed without the cell value using the C<icons_only> parameter:
-
-    $worksheet->conditional_formatting( 'A1:C1',
-        {
-            type         => 'icon_set',
-            icon_style   => '3_flags',
-            icons_only   => 1,
-        }
-    );
-
-
-
-
-=head2 min_type, mid_type, max_type
-
-The C<min_type> and C<max_type> properties are available when the conditional formatting type is C<2_color_scale>, C<3_color_scale> or C<data_bar>. The C<mid_type> is available for C<3_color_scale>. The properties are used as follows:
-
-    $worksheet->conditional_formatting( 'A1:A12',
-        {
-            type      => '2_color_scale',
-            min_type  => 'percent',
-            max_type  => 'percent',
-        }
-    );
-
-The available min/mid/max types are:
-
-    min        (for min_type only)
-    num
-    percent
-    percentile
-    formula
-    max        (for max_type only)
-
-
-=head2 min_value, mid_value, max_value
-
-The C<min_value> and C<max_value> properties are available when the conditional formatting type is C<2_color_scale>, C<3_color_scale> or C<data_bar>. The C<mid_value> is available for C<3_color_scale>. The properties are used as follows:
-
-    $worksheet->conditional_formatting( 'A1:A12',
-        {
-            type       => '2_color_scale',
-            min_value  => 10,
-            max_value  => 90,
-        }
-    );
-
-=head2 min_color, mid_color,  max_color, bar_color
-
-The C<min_color> and C<max_color> properties are available when the conditional formatting type is C<2_color_scale>, C<3_color_scale> or C<data_bar>. The C<mid_color> is available for C<3_color_scale>. The properties are used as follows:
-
-    $worksheet->conditional_formatting( 'A1:A12',
-        {
-            type      => '2_color_scale',
-            min_color => "#C5D9F1",
-            max_color => "#538ED5",
-        }
-    );
-
-The color can be specified as an Excel::Writer::XLSX color index or, more usefully, as a HTML style RGB hex number, as shown above.
-
-
-=head2 bar_only
-
-The C<bar_only> parameter property displays a bar data but not the data in the cells:
-
-    $worksheet->conditional_formatting( 'D3:D14',
-        {
-            type     => 'data_bar',
-            bar_only => 1
-        }
-    );
-
-
-=head2 bar_solid
-
-The C<bar_solid> parameter turns on a solid (non-gradient) fill for data bars:
-
-
-    $worksheet->conditional_formatting( 'H3:H14',
-        {
-            type      => 'data_bar',
-            bar_solid => 1
-        }
-    );
-
-Note, this property is only visible in Excel 2010 and later.
-
-
-=head2 bar_negative_color
-
-The C<bar_negative_color> parameter is used to set the color fill for the negative portion of a data bar.
-
-The color can be specified as an Excel::Writer::XLSX color index or as a HTML style RGB hex number, as shown in the other examples.
-
-Note, this property is only visible in Excel 2010 and later.
-
-
-=head2 bar_border_color
-
-The C<bar_border_color> parameter is used to set the border color of a data bar.
-
-The color can be specified as an Excel::Writer::XLSX color index or as a HTML style RGB hex number, as shown in the other examples.
-
-Note, this property is only visible in Excel 2010 and later.
-
-
-=head2 bar_negative_border_color
-
-The C<bar_negative_border_color> parameter is used to set the border color of the negative portion of a data bar.
-
-The color can be specified as an Excel::Writer::XLSX color index or as a HTML style RGB hex number, as shown in the other examples.
-
-Note, this property is only visible in Excel 2010 and later.
-
-
-=head2 bar_negative_color_same
-
-The C<bar_negative_color_same> parameter sets the fill color for the negative portion of a data bar to be the same as the fill color for the positive portion of the data bar:
-
-    $worksheet->conditional_formatting( 'N3:N14',
-        {
-            type                           => 'data_bar',
-            bar_negative_color_same        => 1,
-            bar_negative_border_color_same => 1
-        }
-    );
-
-Note, this property is only visible in Excel 2010 and later.
-
-
-=head2 bar_negative_border_color_same
-
-The C<bar_negative_border_color_same> parameter sets the border color for the negative portion of a data bar to be the same as the border color for the positive portion of the data bar.
-
-Note, this property is only visible in Excel 2010 and later.
-
-
-=head2 bar_no_border
-
-The C<bar_no_border> parameter turns off the border of a data bar.
-
-
-Note, this property is only visible in Excel 2010 and later, however the default in Excel 2007 is not to have a border.
-
-
-=head2 bar_direction
-
-The C<bar_direction> parameter sets the direction for data bars. This property can be either C<left> for left-to-right or C<right> for right-to-left. If the property isn't set then Excel will adjust the position automatically based on the context:
-
-    $worksheet->conditional_formatting( 'J3:J14',
-        {
-            type          => 'data_bar',
-            bar_direction => 'right'
-        }
-    );
-
-Note, this property is only visible in Excel 2010 and later.
-
-
-=head2 bar_axis_position
-
-The C<bar_axis_position> parameter sets the position within the cells for the axis that is shown in data bars when there are negative values to display. The property can be either C<middle> or C<none>. If the property isn't set then Excel will position the axis based on the range of positive and negative values.
-
-Note, this property is only visible in Excel 2010 and later.
-
-
-=head2 bar_axis_color
-
-The C<bar_axis_color> parameter sets the color for the axis that is shown in data bars when there are negative values to display.
-
-The color can be specified as an Excel::Writer::XLSX color index or as a HTML style RGB hex number, as shown in the other examples.
-
-Note, this property is only visible in Excel 2010 and later.
-
-
-=head2 data_bar_2010
-
-The C<data_bar_2010> parameter sets Excel 2010 style data bars even when Excel 2010 specific properties aren't used. This can be used to create consistency across all the data bar formatting in a worksheet:
-
-    $worksheet->conditional_formatting( 'L3:L14',
-        {
-            type          => 'data_bar',
-            data_bar_2010 => 1
-        }
-    );
-
-Note, this property is only visible in Excel 2010 and later.
-
-
-=head2 stop_if_true
-
-The C<stop_if_true> parameter, if set to a true value, will enable the "stop if true" feature on the conditional formatting rule, so that subsequent rules are not examined for any cell on which the conditions for this rule are met.
-
-
-=head2 Conditional Formatting Examples
-
-Example 1. Highlight cells greater than an integer value.
-
-    $worksheet->conditional_formatting( 'A1:F10',
-        {
-            type     => 'cell',
-            criteria => 'greater than',
-            value    => 5,
-            format   => $format,
-        }
-    );
-
-Example 2. Highlight cells greater than a value in a reference cell.
-
-    $worksheet->conditional_formatting( 'A1:F10',
-        {
-            type     => 'cell',
-            criteria => 'greater than',
-            value    => '$H$1',
-            format   => $format,
-        }
-    );
-
-Example 3. Highlight cells greater than a certain date:
-
-    $worksheet->conditional_formatting( 'A1:F10',
-        {
-            type     => 'date',
-            criteria => 'greater than',
-            value    => '2011-01-01T',
-            format   => $format,
-        }
-    );
-
-Example 4. Highlight cells with a date in the last seven days:
-
-    $worksheet->conditional_formatting( 'A1:F10',
-        {
-            type     => 'time_period',
-            criteria => 'last 7 days',
-            format   => $format,
-        }
-    );
-
-Example 5. Highlight cells with strings starting with the letter C<b>:
-
-    $worksheet->conditional_formatting( 'A1:F10',
-        {
-            type     => 'text',
-            criteria => 'begins with',
-            value    => 'b',
-            format   => $format,
-        }
-    );
-
-Example 6. Highlight cells that are 1 std deviation above the average for the range:
-
-    $worksheet->conditional_formatting( 'A1:F10',
-        {
-            type     => 'average',
-            format   => $format,
-        }
-    );
-
-Example 7. Highlight duplicate cells in a range:
-
-    $worksheet->conditional_formatting( 'A1:F10',
-        {
-            type     => 'duplicate',
-            format   => $format,
-        }
-    );
-
-Example 8. Highlight unique cells in a range.
-
-    $worksheet->conditional_formatting( 'A1:F10',
-        {
-            type     => 'unique',
-            format   => $format,
-        }
-    );
-
-Example 9. Highlight the top 10 cells.
-
-    $worksheet->conditional_formatting( 'A1:F10',
-        {
-            type     => 'top',
-            value    => 10,
-            format   => $format,
-        }
-    );
-
-
-Example 10. Highlight blank cells.
-
-    $worksheet->conditional_formatting( 'A1:F10',
-        {
-            type     => 'blanks',
-            format   => $format,
-        }
-    );
-
-Example 11. Set traffic light icons in 3 cells:
-
-    $worksheet->conditional_formatting( 'A1:C1',
-        {
-            type         => 'icon_set',
-            icon_style   => '3_traffic_lights',
-        }
-    );
-
-
-See also the C<conditional_format.pl> example program in C<EXAMPLES>.
-
-
-
-
-=head1 SPARKLINES IN EXCEL
-
-Sparklines are a feature of Excel 2010+ which allows you to add small charts to worksheet cells. These are useful for showing visual trends in data in a compact format.
-
-In Excel::Writer::XLSX Sparklines can be added to cells using the C<add_sparkline()> worksheet method:
-
-    $worksheet->add_sparkline(
-        {
-            location => 'F2',
-            range    => 'Sheet1!A2:E2',
-            type     => 'column',
-            style    => 12,
-        }
-    );
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/sparklines1.jpg" alt="Sparklines example."/></center></p>
-
-=end html
-
-B<Note:> Sparklines are a feature of Excel 2010+ only. You can write them to an XLSX file that can be read by Excel 2007 but they won't be displayed.
-
-
-=head2 add_sparkline( { parameter => 'value', ... } )
-
-The C<add_sparkline()> worksheet method is used to add sparklines to a cell or a range of cells.
-
-The parameters to C<add_sparkline()> must be passed in a hash ref. The main sparkline parameters are:
-
-    location        (required)
-    range           (required)
-    type
-    style
-
-    markers
-    negative_points
-    axis
-    reverse
-
-Other, less commonly used parameters are:
-
-    high_point
-    low_point
-    first_point
-    last_point
-    max
-    min
-    empty_cells
-    show_hidden
-    date_axis
-    weight
-
-    series_color
-    negative_color
-    markers_color
-    first_color
-    last_color
-    high_color
-    low_color
-
-These parameters are explained in the sections below:
-
-=head2 location
-
-This is the cell where the sparkline will be displayed:
-
-    location => 'F1'
-
-The C<location> should be a single cell. (For multiple cells see L<Grouped Sparklines> below).
-
-To specify the location in row-column notation use the C<xl_rowcol_to_cell()> function from the L<Excel::Writer::XLSX::Utility> module.
-
-    use Excel::Writer::XLSX::Utility ':rowcol';
-    ...
-    location => xl_rowcol_to_cell( 0, 5 ), # F1
-
-
-=head2 range
-
-This specifies the cell data range that the sparkline will plot:
-
-    $worksheet->add_sparkline(
-        {
-            location => 'F1',
-            range    => 'A1:E1',
-        }
-    );
-
-The C<range> should be a 2D array. (For 3D arrays of cells see L<Grouped Sparklines> below).
-
-If C<range> is not on the same worksheet you can specify its location using the usual Excel notation:
-
-            range => 'Sheet1!A1:E1',
-
-If the worksheet contains spaces or special characters you should quote the worksheet name in the same way that Excel does:
-
-            range => q('Monthly Data'!A1:E1),
-
-To specify the location in row-column notation use the C<xl_range()> or C<xl_range_formula()> functions from the L<Excel::Writer::XLSX::Utility> module.
-
-    use Excel::Writer::XLSX::Utility ':rowcol';
-    ...
-    range => xl_range( 1, 1,  0, 4 ),                   # 'A1:E1'
-    range => xl_range_formula( 'Sheet1', 0, 0,  0, 4 ), # 'Sheet1!A2:E2'
-
-=head2 type
-
-Specifies the type of sparkline. There are 3 available sparkline types:
-
-    line    (default)
-    column
-    win_loss
-
-For example:
-
-    {
-        location => 'F1',
-        range    => 'A1:E1',
-        type     => 'column',
-    }
-
-
-=head2 style
-
-Excel provides 36 built-in Sparkline styles in 6 groups of 6. The C<style> parameter can be used to replicate these and should be a corresponding number from 1 .. 36.
-
-    {
-        location => 'A14',
-        range    => 'Sheet2!A2:J2',
-        style    => 3,
-    }
-
-The style number starts in the top left of the style grid and runs left to right. The default style is 1. It is possible to override colour elements of the sparklines using the C<*_color> parameters below.
-
-=head2 markers
-
-Turn on the markers for C<line> style sparklines.
-
-    {
-        location => 'A6',
-        range    => 'Sheet2!A1:J1',
-        markers  => 1,
-    }
-
-Markers aren't shown in Excel for C<column> and C<win_loss> sparklines.
-
-=head2 negative_points
-
-Highlight negative values in a sparkline range. This is usually required with C<win_loss> sparklines.
-
-    {
-        location        => 'A21',
-        range           => 'Sheet2!A3:J3',
-        type            => 'win_loss',
-        negative_points => 1,
-    }
-
-=head2 axis
-
-Display a horizontal axis in the sparkline:
-
-    {
-        location => 'A10',
-        range    => 'Sheet2!A1:J1',
-        axis     => 1,
-    }
-
-=head2 reverse
-
-Plot the data from right-to-left instead of the default left-to-right:
-
-    {
-        location => 'A24',
-        range    => 'Sheet2!A4:J4',
-        type     => 'column',
-        reverse  => 1,
-    }
-
-=head2 weight
-
-Adjust the default line weight (thickness) for C<line> style sparklines.
-
-     weight => 0.25,
-
-The weight value should be one of the following values allowed by Excel:
-
-    0.25  0.5   0.75
-    1     1.25
-    2.25
-    3
-    4.25
-    6
-
-=head2 high_point, low_point, first_point, last_point
-
-Highlight points in a sparkline range.
-
-        high_point  => 1,
-        low_point   => 1,
-        first_point => 1,
-        last_point  => 1,
-
-
-=head2 max, min
-
-Specify the maximum and minimum vertical axis values:
-
-        max         => 0.5,
-        min         => -0.5,
-
-As a special case you can set the maximum and minimum to be for a group of sparklines rather than one:
-
-        max         => 'group',
-
-See L<Grouped Sparklines> below.
-
-=head2 empty_cells
-
-Define how empty cells are handled in a sparkline.
-
-    empty_cells => 'zero',
-
-The available options are:
-
-    gaps   : show empty cells as gaps (the default).
-    zero   : plot empty cells as 0.
-    connect: Connect points with a line ("line" type  sparklines only).
-
-=head2 show_hidden
-
-Plot data in hidden rows and columns:
-
-    show_hidden => 1,
-
-Note, this option is off by default.
-
-=head2 date_axis
-
-Specify an alternative date axis for the sparkline. This is useful if the data being plotted isn't at fixed width intervals:
-
-    {
-        location  => 'F3',
-        range     => 'A3:E3',
-        date_axis => 'A4:E4',
-    }
-
-The number of cells in the date range should correspond to the number of cells in the data range.
-
-
-=head2 series_color
-
-It is possible to override the colour of a sparkline style using the following parameters:
-
-    series_color
-    negative_color
-    markers_color
-    first_color
-    last_color
-    high_color
-    low_color
-
-The color should be specified as a HTML style C<#rrggbb> hex value:
-
-    {
-        location     => 'A18',
-        range        => 'Sheet2!A2:J2',
-        type         => 'column',
-        series_color => '#E965E0',
-    }
-
-=head2 Grouped Sparklines
-
-The C<add_sparkline()> worksheet method can be used multiple times to write as many sparklines as are required in a worksheet.
-
-However, it is sometimes necessary to group contiguous sparklines so that changes that are applied to one are applied to all. In Excel this is achieved by selecting a 3D range of cells for the data C<range> and a 2D range of cells for the C<location>.
-
-In Excel::Writer::XLSX, you can simulate this by passing an array refs of values to C<location> and C<range>:
-
-    {
-        location => [ 'A27',          'A28',          'A29'          ],
-        range    => [ 'Sheet2!A5:J5', 'Sheet2!A6:J6', 'Sheet2!A7:J7' ],
-        markers  => 1,
-    }
-
-=head2 Sparkline examples
-
-See the C<sparklines1.pl> and C<sparklines2.pl> example programs in the C<examples> directory of the distro.
-
-
-
-
-=head1 TABLES IN EXCEL
-
-Tables in Excel are a way of grouping a range of cells into a single entity that has common formatting or that can be referenced from formulas. Tables can have column headers, autofilters, total rows, column formulas and default formatting.
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/tables.jpg" width="640" height="420" alt="Output from tables.pl" /></center></p>
-
-=end html
-
-
-For more information see "An Overview of Excel Tables" L<http://office.microsoft.com/en-us/excel-help/overview-of-excel-tables-HA010048546.aspx>.
-
-Note, tables don't work in Excel::Writer::XLSX when C<set_optimization()> mode in on.
-
-
-=head2 add_table( $row1, $col1, $row2, $col2, { parameter => 'value', ... })
-
-Tables are added to a worksheet using the C<add_table()> method:
-
-    $worksheet->add_table( 'B3:F7', { %parameters } );
-
-The data range can be specified in 'A1' or 'row/col' notation (see also the note about L</Cell notation> for more information):
-
-
-    $worksheet->add_table( 'B3:F7' );
-    # Same as:
-    $worksheet->add_table(  2, 1, 6, 5 );
-
-The last parameter in C<add_table()> should be a hash ref containing the parameters that describe the table options and data. The available parameters are:
-
-        data
-        autofilter
-        header_row
-        banded_columns
-        banded_rows
-        first_column
-        last_column
-        style
-        total_row
-        columns
-        name
-
-The table parameters are detailed below. There are no required parameters and the hash ref isn't required if no options are specified.
-
-
-
-=head2 data
-
-The C<data> parameter can be used to specify the data in the cells of the table.
-
-    my $data = [
-        [ 'Apples',  10000, 5000, 8000, 6000 ],
-        [ 'Pears',   2000,  3000, 4000, 5000 ],
-        [ 'Bananas', 6000,  6000, 6500, 6000 ],
-        [ 'Oranges', 500,   300,  200,  700 ],
-
-    ];
-
-    $worksheet->add_table( 'B3:F7', { data => $data } );
-
-Table data can also be written separately, as an array or individual cells.
-
-    # These two statements are the same as the single statement above.
-    $worksheet->add_table( 'B3:F7' );
-    $worksheet->write_col( 'B4', $data );
-
-Writing the cell data separately is occasionally required when you need to control the C<write_*()> method used to populate the cells or if you wish to tweak the cell formatting.
-
-The C<data> structure should be an array ref of array refs holding row data as shown above.
-
-=head2 header_row
-
-The C<header_row> parameter can be used to turn on or off the header row in the table. It is on by default.
-
-    $worksheet->add_table( 'B4:F7', { header_row => 0 } ); # Turn header off.
-
-The header row will contain default captions such as C<Column 1>, C<Column 2>,  etc. These captions can be overridden using the C<columns> parameter below.
-
-
-=head2 autofilter
-
-The C<autofilter> parameter can be used to turn on or off the autofilter in the header row. It is on by default.
-
-    $worksheet->add_table( 'B3:F7', { autofilter => 0 } ); # Turn autofilter off.
-
-The C<autofilter> is only shown if the C<header_row> is on. Filters within the table are not supported.
-
-
-=head2 banded_rows
-
-The C<banded_rows> parameter can be used to used to create rows of alternating colour in the table. It is on by default.
-
-    $worksheet->add_table( 'B3:F7', { banded_rows => 0 } );
-
-
-=head2 banded_columns
-
-The C<banded_columns> parameter can be used to used to create columns of alternating colour in the table. It is off by default.
-
-    $worksheet->add_table( 'B3:F7', { banded_columns => 1 } );
-
-
-=head2 first_column
-
-The C<first_column> parameter can be used to highlight the first column of the table. The type of highlighting will depend on the C<style> of the table. It may be bold text or a different colour. It is off by default.
-
-    $worksheet->add_table( 'B3:F7', { first_column => 1 } );
-
-
-=head2 last_column
-
-The C<last_column> parameter can be used to highlight the last column of the table. The type of highlighting will depend on the C<style> of the table. It may be bold text or a different colour. It is off by default.
-
-    $worksheet->add_table( 'B3:F7', { last_column => 1 } );
-
-
-=head2 style
-
-The C<style> parameter can be used to set the style of the table. Standard Excel table format names should be used (with matching capitalisation):
-
-    $worksheet11->add_table(
-        'B3:F7',
-        {
-            data      => $data,
-            style     => 'Table Style Light 11',
-        }
-    );
-
-The default table style is 'Table Style Medium 9'.
-
-
-=head2 name
-
-By default tables are named C<Table1>, C<Table2>, etc. The C<name> parameter can be used to set the name of the table:
-
-    $worksheet->add_table( 'B3:F7', { name => 'SalesData' } );
-
-If you override the table name you must ensure that it doesn't clash with an existing table name and that it follows Excel's requirements for table names L<http://office.microsoft.com/en-001/excel-help/define-and-use-names-in-formulas-HA010147120.aspx#BMsyntax_rules_for_names>.
-
-If you need to know the name of the table, for example to use it in a formula, you can get it as follows:
-
-    my $table      = $worksheet2->add_table( 'B3:F7' );
-    my $table_name = $table->{_name};
-
-
-=head2 total_row
-
-The C<total_row> parameter can be used to turn on the total row in the last row of a table. It is distinguished from the other rows by a different formatting and also with dropdown C<SUBTOTAL> functions.
-
-    $worksheet->add_table( 'B3:F7', { total_row => 1 } );
-
-The default total row doesn't have any captions or functions. These must by specified via the C<columns> parameter below.
-
-=head2 columns
-
-The C<columns> parameter can be used to set properties for columns within the table.
-
-The sub-properties that can be set are:
-
-    header
-    formula
-    total_string
-    total_function
-    total_value
-    format
-    header_format
-
-The column data must be specified as an array ref of hash refs. For example to override the default 'Column n' style table headers:
-
-    $worksheet->add_table(
-        'B3:F7',
-        {
-            data    => $data,
-            columns => [
-                { header => 'Product' },
-                { header => 'Quarter 1' },
-                { header => 'Quarter 2' },
-                { header => 'Quarter 3' },
-                { header => 'Quarter 4' },
-            ]
-        }
-    );
-
-If you don't wish to specify properties for a specific column you pass an empty hash ref and the defaults will be applied:
-
-            ...
-            columns => [
-                { header => 'Product' },
-                { header => 'Quarter 1' },
-                { },                        # Defaults to 'Column 3'.
-                { header => 'Quarter 3' },
-                { header => 'Quarter 4' },
-            ]
-            ...
-
-
-Column formulas can by applied using the C<formula> column property:
-
-    $worksheet8->add_table(
-        'B3:G7',
-        {
-            data    => $data,
-            columns => [
-                { header => 'Product' },
-                { header => 'Quarter 1' },
-                { header => 'Quarter 2' },
-                { header => 'Quarter 3' },
-                { header => 'Quarter 4' },
-                {
-                    header  => 'Year',
-                    formula => '=SUM(Table8[@[Quarter 1]:[Quarter 4]])'
-                },
-            ]
-        }
-    );
-
-The Excel 2007 C<[#This Row]> and Excel 2010 C<@> structural references are supported within the formula.
-
-As stated above the C<total_row> table parameter turns on the "Total" row in the table but it doesn't populate it with any defaults. Total captions and functions must be specified via the C<columns> property and the C<total_string>, C<total_function> and C<total_value> sub properties:
-
-    $worksheet10->add_table(
-        'B3:F8',
-        {
-            data      => $data,
-            total_row => 1,
-            columns   => [
-                { header => 'Product',   total_string   => 'Totals' },
-                { header => 'Quarter 1', total_function => 'sum' },
-                { header => 'Quarter 2', total_function => 'sum' },
-                { header => 'Quarter 3', total_function => 'sum' },
-                { header => 'Quarter 4', total_function => 'sum' },
-            ]
-        }
-    );
-
-The supported totals row C<SUBTOTAL> functions are:
-
-        average
-        count_nums
-        count
-        max
-        min
-        std_dev
-        sum
-        var
-
-User defined functions or formulas aren't supported.
-
-It is also possible to set a calculated value for the C<total_function> using the C<total_value> sub property. This is only necessary when creating workbooks for applications that cannot calculate the value of formulas automatically. This is similar to setting the C<value> optional property in C<write_formula()>:
-
-    $worksheet10->add_table(
-        'B3:F8',
-        {
-            data      => $data,
-            total_row => 1,
-            columns   => [
-                { total_string   => 'Totals' },
-                { total_function => 'sum', total_value => 100 },
-                { total_function => 'sum', total_value => 200 },
-                { total_function => 'sum', total_value => 100 },
-                { total_function => 'sum', total_value => 400 },
-            ]
-        }
-    );
-
-
-
-
-Formatting can also be applied to columns, to the column data using C<format> and to the header using C<header_format>:
-
-    my $currency_format = $workbook->add_format( num_format => '$#,##0' );
-
-    $worksheet->add_table(
-        'B3:D8',
-        {
-            data      => $data,
-            total_row => 1,
-            columns   => [
-                { header => 'Product', total_string => 'Totals' },
-                {
-                    header         => 'Quarter 1',
-                    total_function => 'sum',
-                    format         => $currency_format,
-                },
-                {
-                    header         => 'Quarter 2',
-                    header_format  => $bold,
-                    total_function => 'sum',
-                    format         => $currency_format,
-                },
-            ]
-        }
-    );
-
-Standard Excel::Writer::XLSX format objects can be used. However, they should be limited to numerical formats for the columns and simple formatting like text wrap for the headers. Overriding other table formatting may produce inconsistent results.
-
-
-
-=head1 FORMULAS AND FUNCTIONS IN EXCEL
-
-
-
-
-=head2 Introduction
-
-The following is a brief introduction to formulas and functions in Excel and Excel::Writer::XLSX.
-
-A formula is a string that begins with an equals sign:
-
-    '=A1+B1'
-    '=AVERAGE(1, 2, 3)'
-
-The formula can contain numbers, strings, boolean values, cell references, cell ranges and functions. Named ranges are not supported. Formulas should be written as they appear in Excel, that is cells and functions must be in uppercase.
-
-Cells in Excel are referenced using the A1 notation system where the column is designated by a letter and the row by a number. Columns range from A to XFD i.e. 0 to 16384, rows range from 1 to 1048576. The C<Excel::Writer::XLSX::Utility> module that is included in the distro contains helper functions for dealing with A1 notation, for example:
-
-    use Excel::Writer::XLSX::Utility;
-
-    ( $row, $col ) = xl_cell_to_rowcol( 'C2' );    # (1, 2)
-    $str = xl_rowcol_to_cell( 1, 2 );              # C2
-
-The Excel C<$> notation in cell references is also supported. This allows you to specify whether a row or column is relative or absolute. This only has an effect if the cell is copied. The following examples show relative and absolute values.
-
-    '=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
-
-Formulas can also refer to cells in other worksheets of the current workbook. For example:
-
-    '=Sheet2!A1'
-    '=Sheet2!A1:A5'
-    '=Sheet2:Sheet3!A1'
-    '=Sheet2:Sheet3!A1:A5'
-    q{='Test Data'!A1}
-    q{='Test Data1:Test Data2'!A1}
-
-The sheet reference and the cell reference are separated by C<!> the exclamation mark symbol. If worksheet names contain spaces, commas or parentheses then Excel requires that the name is enclosed in single quotes as shown in the last two examples above. In order to avoid using a lot of escape characters you can use the quote operator C<q{}> to protect the quotes. See C<perlop> in the main Perl documentation. Only valid sheet names that have been added using the C<add_worksheet()> method can be used in formulas. You cannot reference external workbooks.
-
-
-The following table lists the operators that are available in Excel's formulas. The majority of the operators are the same as Perl's, differences are indicated:
-
-    Arithmetic operators:
-    =====================
-    Operator  Meaning                   Example
-       +      Addition                  1+2
-       -      Subtraction               2-1
-       *      Multiplication            2*3
-       /      Division                  1/4
-       ^      Exponentiation            2^3      # Equivalent to **
-       -      Unary minus               -(1+2)
-       %      Percent (Not modulus)     13%
-
-
-    Comparison operators:
-    =====================
-    Operator  Meaning                   Example
-        =     Equal to                  A1 =  B1 # Equivalent to ==
-        <>    Not equal to              A1 <> B1 # Equivalent to !=
-        >     Greater than              A1 >  B1
-        <     Less than                 A1 <  B1
-        >=    Greater than or equal to  A1 >= B1
-        <=    Less than or equal to     A1 <= B1
-
-
-    String operator:
-    ================
-    Operator  Meaning                   Example
-        &     Concatenation             "Hello " & "World!" # [1]
-
-
-    Reference operators:
-    ====================
-    Operator  Meaning                   Example
-        :     Range operator            A1:A4               # [2]
-        ,     Union operator            SUM(1, 2+2, B3)     # [3]
-
-
-    Notes:
-    [1]: Equivalent to "Hello " . "World!" in Perl.
-    [2]: This range is equivalent to cells A1, A2, A3 and A4.
-    [3]: The comma behaves like the list separator in Perl.
-
-The range and comma operators can have different symbols in non-English versions of Excel, see below.
-
-For a general introduction to Excel's formulas and an explanation of the syntax of the function refer to the Excel help files or the following: L<http://office.microsoft.com/en-us/assistance/CH062528031033.aspx>.
-
-In most cases a formula in Excel can be used directly in the C<write_formula> method. However, there are a few potential issues and differences that the user should be aware of. These are explained in the following sections.
-
-
-=head2 Non US Excel functions and syntax
-
-
-Excel stores formulas in the format of the US English version, regardless of the language or locale of the end-user's version of Excel. Therefore all formula function names written using Excel::Writer::XLSX must be in English:
-
-    worksheet->write_formula('A1', '=SUM(1, 2, 3)');   # OK
-    worksheet->write_formula('A2', '=SOMME(1, 2, 3)'); # French. Error on load.
-
-Also, formulas must be written with the US style separator/range operator which is a comma (not semi-colon). Therefore a formula with multiple values should be written as follows:
-
-    worksheet->write_formula('A1', '=SUM(1, 2, 3)'); # OK
-    worksheet->write_formula('A2', '=SUM(1; 2; 3)'); # Semi-colon. Error on load.
-
-If you have a non-English version of Excel you can use the following multi-lingual Formula Translator (L<http://en.excel-translator.de/language/>) to help you convert the formula. It can also replace semi-colons with commas.
-
-
-=head2 Formulas added in Excel 2010 and later
-
-Excel 2010 and later added functions which weren't defined in the original file specification. These functions are referred to by Microsoft as I<future> functions. Examples of these functions are C<ACOT>, C<CHISQ.DIST.RT> , C<CONFIDENCE.NORM>, C<STDEV.P>, C<STDEV.S> and C<WORKDAY.INTL>.
-
-When written using C<write_formula()> these functions need to be fully qualified with a C<_xlfn.> (or other) prefix as they are shown the list below. For example:
-
-    worksheet->write_formula('A1', '=_xlfn.STDEV.S(B1:B10)')
-
-They will appear without the prefix in Excel.
-
-The following list is taken from the MS XLSX extensions documentation on future functions: L<http://msdn.microsoft.com/en-us/library/dd907480%28v=office.12%29.aspx>:
-
-    _xlfn.ACOT
-    _xlfn.ACOTH
-    _xlfn.AGGREGATE
-    _xlfn.ARABIC
-    _xlfn.BASE
-    _xlfn.BETA.DIST
-    _xlfn.BETA.INV
-    _xlfn.BINOM.DIST
-    _xlfn.BINOM.DIST.RANGE
-    _xlfn.BINOM.INV
-    _xlfn.BITAND
-    _xlfn.BITLSHIFT
-    _xlfn.BITOR
-    _xlfn.BITRSHIFT
-    _xlfn.BITXOR
-    _xlfn.CEILING.MATH
-    _xlfn.CEILING.PRECISE
-    _xlfn.CHISQ.DIST
-    _xlfn.CHISQ.DIST.RT
-    _xlfn.CHISQ.INV
-    _xlfn.CHISQ.INV.RT
-    _xlfn.CHISQ.TEST
-    _xlfn.COMBINA
-    _xlfn.CONFIDENCE.NORM
-    _xlfn.CONFIDENCE.T
-    _xlfn.COT
-    _xlfn.COTH
-    _xlfn.COVARIANCE.P
-    _xlfn.COVARIANCE.S
-    _xlfn.CSC
-    _xlfn.CSCH
-    _xlfn.DAYS
-    _xlfn.DECIMAL
-    ECMA.CEILING
-    _xlfn.ERF.PRECISE
-    _xlfn.ERFC.PRECISE
-    _xlfn.EXPON.DIST
-    _xlfn.F.DIST
-    _xlfn.F.DIST.RT
-    _xlfn.F.INV
-    _xlfn.F.INV.RT
-    _xlfn.F.TEST
-    _xlfn.FILTERXML
-    _xlfn.FLOOR.MATH
-    _xlfn.FLOOR.PRECISE
-    _xlfn.FORECAST.ETS
-    _xlfn.FORECAST.ETS.CONFINT
-    _xlfn.FORECAST.ETS.SEASONALITY
-    _xlfn.FORECAST.ETS.STAT
-    _xlfn.FORECAST.LINEAR
-    _xlfn.FORMULATEXT
-    _xlfn.GAMMA
-    _xlfn.GAMMA.DIST
-    _xlfn.GAMMA.INV
-    _xlfn.GAMMALN.PRECISE
-    _xlfn.GAUSS
-    _xlfn.HYPGEOM.DIST
-    _xlfn.IFNA
-    _xlfn.IMCOSH
-    _xlfn.IMCOT
-    _xlfn.IMCSC
-    _xlfn.IMCSCH
-    _xlfn.IMSEC
-    _xlfn.IMSECH
-    _xlfn.IMSINH
-    _xlfn.IMTAN
-    _xlfn.ISFORMULA
-    ISO.CEILING
-    _xlfn.ISOWEEKNUM
-    _xlfn.LOGNORM.DIST
-    _xlfn.LOGNORM.INV
-    _xlfn.MODE.MULT
-    _xlfn.MODE.SNGL
-    _xlfn.MUNIT
-    _xlfn.NEGBINOM.DIST
-    NETWORKDAYS.INTL
-    _xlfn.NORM.DIST
-    _xlfn.NORM.INV
-    _xlfn.NORM.S.DIST
-    _xlfn.NORM.S.INV
-    _xlfn.NUMBERVALUE
-    _xlfn.PDURATION
-    _xlfn.PERCENTILE.EXC
-    _xlfn.PERCENTILE.INC
-    _xlfn.PERCENTRANK.EXC
-    _xlfn.PERCENTRANK.INC
-    _xlfn.PERMUTATIONA
-    _xlfn.PHI
-    _xlfn.POISSON.DIST
-    _xlfn.QUARTILE.EXC
-    _xlfn.QUARTILE.INC
-    _xlfn.QUERYSTRING
-    _xlfn.RANK.AVG
-    _xlfn.RANK.EQ
-    _xlfn.RRI
-    _xlfn.SEC
-    _xlfn.SECH
-    _xlfn.SHEET
-    _xlfn.SHEETS
-    _xlfn.SKEW.P
-    _xlfn.STDEV.P
-    _xlfn.STDEV.S
-    _xlfn.T.DIST
-    _xlfn.T.DIST.2T
-    _xlfn.T.DIST.RT
-    _xlfn.T.INV
-    _xlfn.T.INV.2T
-    _xlfn.T.TEST
-    _xlfn.UNICHAR
-    _xlfn.UNICODE
-    _xlfn.VAR.P
-    _xlfn.VAR.S
-    _xlfn.WEBSERVICE
-    _xlfn.WEIBULL.DIST
-    WORKDAY.INTL
-    _xlfn.XOR
-    _xlfn.Z.TEST
-
-
-=head2 Using Tables in Formulas
-
-Worksheet tables can be added with Excel::Writer::XLSX using the C<add_table()> method:
-
-    worksheet->add_table('B3:F7', {options});
-
-By default tables are named C<Table1>, C<Table2>, etc., in the order that they are added. However it can also be set by the user using the C<name> parameter:
-
-    worksheet->add_table('B3:F7', {'name': 'SalesData'});
-
-If you need to know the name of the table, for example to use it in a formula,
-you can get it as follows:
-
-    table = worksheet->add_table('B3:F7');
-    table_name = table->{_name};
-
-When used in a formula a table name such as C<TableX> should be referred to as C<TableX[]> (like a Perl array):
-
-    worksheet->write_formula('A5', '=VLOOKUP("Sales", Table1[], 2, FALSE');
-
-
-=head2 Dealing with #NAME? errors
-
-If there is an error in the syntax of a formula it is usually displayed in
-Excel as C<#NAME?>. If you encounter an error like this you can debug it as
-follows:
-
-=over
-
-=item 1. Ensure the formula is valid in Excel by copying and pasting it into a cell. Note, this should be done in Excel and not other applications such as OpenOffice or LibreOffice since they may have slightly different syntax.
-
-=item 2. Ensure the formula is using comma separators instead of semi-colons, see L<Non US Excel functions and syntax> above.
-
-=item 3. Ensure the formula is in English, see L<Non US Excel functions and syntax> above.
-
-=item 4. Ensure that the formula doesn't contain an Excel 2010+ future function as listed in L<Formulas added in Excel 2010 and later> above. If it does then ensure that the correct prefix is used.
-
-=back
-
-Finally if you have completed all the previous steps and still get a C<#NAME?> error you can examine a valid Excel file to see what the correct syntax should be. To do this you should create a valid formula in Excel and save the file. You can then examine the XML in the unzipped file.
-
-The following shows how to do that using Linux C<unzip> and libxml's xmllint
-L<http://xmlsoft.org/xmllint.html> to format the XML for clarity:
-
-    $ unzip myfile.xlsx -d myfile
-    $ xmllint --format myfile/xl/worksheets/sheet1.xml | grep '<f>'
-
-            <f>SUM(1, 2, 3)</f>
-
-
-=head2 Formula Results
-
-Excel::Writer::XLSX doesn't calculate the result of a formula and instead stores the value 0 as the formula result. It then sets a global flag in the XLSX file to say that all formulas and functions should be recalculated when the file is opened.
-
-This is the method recommended in the Excel documentation and in general it works fine with spreadsheet applications. However, applications that don't have a facility to calculate formulas will only display the 0 results. Examples of such applications are Excel Viewer, PDF Converters, and some mobile device applications.
-
-If required, it is also possible to specify the calculated result of the
-formula using the optional last C<value> parameter in C<write_formula>:
-
-    worksheet->write_formula('A1', '=2+2', num_format, 4);
-
-The C<value> parameter can be a number, a string, a boolean sting (C<'TRUE'> or C<'FALSE'>) or one of the following Excel error codes:
-
-    #DIV/0!
-    #N/A
-    #NAME?
-    #NULL!
-    #NUM!
-    #REF!
-    #VALUE!
-
-It is also possible to specify the calculated result of an array formula created with C<write_array_formula>:
-
-    # Specify the result for a single cell range.
-    worksheet->write_array_formula('A1:A1', '{=SUM(B1:C1*B2:C2)}', format, 2005);
-
-However, using this parameter only writes a single value to the upper left cell in the result array. For a multi-cell array formula where the results are required, the other result values can be specified by using C<write_number()> to write to the appropriate cell:
-
-    # Specify the results for a multi cell range.
-    worksheet->write_array_formula('A1:A3', '{=TREND(C1:C3,B1:B3)}', format, 15);
-    worksheet->write_number('A2', 12, format);
-    worksheet->write_number('A3', 14, format);
-
-
-
-
-=head1 WORKING WITH VBA MACROS
-
-An Excel C<xlsm> file is exactly the same as a C<xlsx> file except that is includes an additional C<vbaProject.bin> file which contains functions and/or macros. Excel uses a different extension to differentiate between the two file formats since files containing macros are usually subject to additional security checks.
-
-The C<vbaProject.bin> file is a binary OLE COM container. This was the format used in older C<xls> versions of Excel prior to Excel 2007. Unlike all of the other components of an xlsx/xlsm file the data isn't stored in XML format. Instead the functions and macros as stored as pre-parsed binary format. As such it wouldn't be feasible to define macros and create a C<vbaProject.bin> file from scratch (at least not in the remaining lifespan and interest levels of the author).
-
-Instead a workaround is used to extract C<vbaProject.bin> files from existing xlsm files and then add these to Excel::Writer::XLSX files.
-
-
-=head2 The extract_vba utility
-
-The C<extract_vba> utility is used to extract the C<vbaProject.bin> binary from an Excel 2007+ xlsm file. The utility is included in the Excel::Writer::XLSX bin directory and is also installed as a standalone executable file:
-
-    $ extract_vba macro_file.xlsm
-    Extracted: vbaProject.bin
-
-
-=head2 Adding the VBA macros to a Excel::Writer::XLSX file
-
-Once the C<vbaProject.bin> file has been extracted it can be added to the Excel::Writer::XLSX workbook using the C<add_vba_project()> method:
-
-    $workbook->add_vba_project( './vbaProject.bin' );
-
-If the VBA file contains functions you can then refer to them in calculations using C<write_formula>:
-
-    $worksheet->write_formula( 'A1', '=MyMortgageCalc(200000, 25)' );
-
-Excel files that contain functions and macros should use an C<xlsm> extension or else Excel will complain and possibly not open the file:
-
-    my $workbook  = Excel::Writer::XLSX->new( 'file.xlsm' );
-
-It is also possible to assign a macro to a button that is inserted into a
-worksheet using the C<insert_button()> method:
-
-    my $workbook  = Excel::Writer::XLSX->new( 'file.xlsm' );
-    ...
-    $workbook->add_vba_project( './vbaProject.bin' );
-
-    $worksheet->insert_button( 'C2', { macro => 'my_macro' } );
-
-
-It may be necessary to specify a more explicit macro name prefixed by the workbook VBA name as follows:
-
-    $worksheet->insert_button( 'C2', { macro => 'ThisWorkbook.my_macro' } );
-
-See the C<macros.pl> from the examples directory for a working example.
-
-Note: Button is the only VBA Control supported by Excel::Writer::XLSX. Due to the large effort in implementation (1+ man months) it is unlikely that any other form elements will be added in the future.
-
-
-=head2 Setting the VBA codenames
-
-VBA macros generally refer to workbook and worksheet objects. If the VBA codenames aren't specified then Excel::Writer::XLSX will use the Excel defaults of C<ThisWorkbook> and C<Sheet1>, C<Sheet2> etc.
-
-If the macro uses other codenames you can set them using the workbook and worksheet C<set_vba_name()> methods as follows:
-
-      $workbook->set_vba_name( 'MyWorkbook' );
-      $worksheet->set_vba_name( 'MySheet' );
-
-You can find the names that are used in the VBA editor or by unzipping the C<xlsm> file and grepping the files. The following shows how to do that using libxml's xmllint L<http://xmlsoft.org/xmllint.html> to format the XML for clarity:
-
-    $ unzip myfile.xlsm -d myfile
-    $ xmllint --format `find myfile -name "*.xml" | xargs` | grep "Pr.*codeName"
-
-      <workbookPr codeName="MyWorkbook" defaultThemeVersion="124226"/>
-      <sheetPr codeName="MySheet"/>
-
-
-Note: This step is particularly important for macros created with non-English versions of Excel.
-
-
-
-=head2 What to do if it doesn't work
-
-This feature should be considered experimental and there is no guarantee that it will work in all cases. Some effort may be required and some knowledge of VBA will certainly help. If things don't work out here are some things to try:
-
-=over
-
-=item *
-
-Start with a simple macro file, ensure that it works and then add complexity.
-
-=item *
-
-Try to extract the macros from an Excel 2007 file. The method should work with macros from later versions (it was also tested with Excel 2010 macros). However there may be features in the macro files of more recent version of Excel that aren't backward compatible.
-
-=item *
-
-Check the code names that macros use to refer to the workbook and worksheets (see the previous section above). In general VBA uses a code name of C<ThisWorkbook> to refer to the current workbook and the sheet name (such as C<Sheet1>) to refer to the worksheets. These are the defaults used by Excel::Writer::XLSX. If the macro uses other names then you can specify these using the workbook and worksheet C<set_vba_name()> methods:
-
-      $workbook>set_vba_name( 'MyWorkbook' );
-      $worksheet->set_vba_name( 'MySheet' );
-
-=back
-
-
-=head1 EXAMPLES
-
-See L<Excel::Writer::XLSX::Examples> for a full list of examples.
-
-
-=head2 Example 1
-
-The following example shows some of the basic features of Excel::Writer::XLSX.
-
-
-    #!/usr/bin/perl -w
-
-    use strict;
-    use Excel::Writer::XLSX;
-
-    # Create a new workbook called simple.xlsx and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'simple.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    # The general syntax is write($row, $column, $token). Note that row and
-    # column are zero indexed
-
-    # Write some text
-    $worksheet->write( 0, 0, 'Hi Excel!' );
-
-
-    # Write some numbers
-    $worksheet->write( 2, 0, 3 );
-    $worksheet->write( 3, 0, 3.00000 );
-    $worksheet->write( 4, 0, 3.00001 );
-    $worksheet->write( 5, 0, 3.14159 );
-
-
-    # Write some formulas
-    $worksheet->write( 7, 0, '=A3 + A6' );
-    $worksheet->write( 8, 0, '=IF(A5>3,"Yes", "No")' );
-
-
-    # Write a hyperlink
-    my $hyperlink_format = $workbook->add_format(
-        color     => 'blue',
-        underline => 1,
-    );
-
-    $worksheet->write( 10, 0, 'http://www.perl.com/', $hyperlink_format );
-
-    $workbook->close();
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/a_simple.jpg" width="640" height="420" alt="Output from a_simple.pl" /></center></p>
-
-=end html
-
-
-
-
-=head2 Example 2
-
-The following is a general example which demonstrates some features of working with multiple worksheets.
-
-    #!/usr/bin/perl -w
-
-    use strict;
-    use Excel::Writer::XLSX;
-
-    # Create a new Excel workbook
-    my $workbook = Excel::Writer::XLSX->new( 'regions.xlsx' );
-
-    # Add some worksheets
-    my $north = $workbook->add_worksheet( 'North' );
-    my $south = $workbook->add_worksheet( 'South' );
-    my $east  = $workbook->add_worksheet( 'East' );
-    my $west  = $workbook->add_worksheet( 'West' );
-
-    # Add a Format
-    my $format = $workbook->add_format();
-    $format->set_bold();
-    $format->set_color( 'blue' );
-
-    # Add a caption to each worksheet
-    for my $worksheet ( $workbook->sheets() ) {
-        $worksheet->write( 0, 0, 'Sales', $format );
-    }
-
-    # Write some data
-    $north->write( 0, 1, 200000 );
-    $south->write( 0, 1, 100000 );
-    $east->write( 0, 1, 150000 );
-    $west->write( 0, 1, 100000 );
-
-    # Set the active worksheet
-    $south->activate();
-
-    # Set the width of the first column
-    $south->set_column( 0, 0, 20 );
-
-    # Set the active cell
-    $south->set_selection( 0, 1 );
-
-    $workbook->close();
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/regions.jpg" width="640" height="420" alt="Output from regions.pl" /></center></p>
-
-=end html
-
-
-
-
-=head2 Example 3
-
-Example of how to add conditional formatting to an Excel::Writer::XLSX file. The example below highlights cells that have a value greater than or equal to 50 in red and cells below that value in green.
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'conditional_format.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-
-    # This example below highlights cells that have a value greater than or
-    # equal to 50 in red and cells below that value in green.
-
-    # Light red fill with dark red text.
-    my $format1 = $workbook->add_format(
-        bg_color => '#FFC7CE',
-        color    => '#9C0006',
-
-    );
-
-    # Green fill with dark green text.
-    my $format2 = $workbook->add_format(
-        bg_color => '#C6EFCE',
-        color    => '#006100',
-
-    );
-
-    # Some sample data to run the conditional formatting against.
-    my $data = [
-        [ 34, 72,  38, 30, 75, 48, 75, 66, 84, 86 ],
-        [ 6,  24,  1,  84, 54, 62, 60, 3,  26, 59 ],
-        [ 28, 79,  97, 13, 85, 93, 93, 22, 5,  14 ],
-        [ 27, 71,  40, 17, 18, 79, 90, 93, 29, 47 ],
-        [ 88, 25,  33, 23, 67, 1,  59, 79, 47, 36 ],
-        [ 24, 100, 20, 88, 29, 33, 38, 54, 54, 88 ],
-        [ 6,  57,  88, 28, 10, 26, 37, 7,  41, 48 ],
-        [ 52, 78,  1,  96, 26, 45, 47, 33, 96, 36 ],
-        [ 60, 54,  81, 66, 81, 90, 80, 93, 12, 55 ],
-        [ 70, 5,   46, 14, 71, 19, 66, 36, 41, 21 ],
-    ];
-
-    my $caption = 'Cells with values >= 50 are in light red. '
-      . 'Values < 50 are in light green';
-
-    # Write the data.
-    $worksheet->write( 'A1', $caption );
-    $worksheet->write_col( 'B3', $data );
-
-    # Write a conditional format over a range.
-    $worksheet->conditional_formatting( 'B3:K12',
-        {
-            type     => 'cell',
-            criteria => '>=',
-            value    => 50,
-            format   => $format1,
-        }
-    );
-
-    # Write another conditional format over the same range.
-    $worksheet->conditional_formatting( 'B3:K12',
-        {
-            type     => 'cell',
-            criteria => '<',
-            value    => 50,
-            format   => $format2,
-        }
-    );
-
-    $workbook->close();
-
-=begin html
-
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/conditional_format.jpg" width="640" height="420" alt="Output from conditional_format.pl" /></center></p>
-
-
-=end html
-
-
-
-
-=head2 Example 4
-
-The following is a simple example of using functions.
-
-    #!/usr/bin/perl -w
-
-    use strict;
-    use Excel::Writer::XLSX;
-
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'stats.xlsx' );
-    my $worksheet = $workbook->add_worksheet( 'Test data' );
-
-    # Set the column width for columns 1
-    $worksheet->set_column( 0, 0, 20 );
-
-
-    # Create a format for the headings
-    my $format = $workbook->add_format();
-    $format->set_bold();
-
-
-    # Write the sample data
-    $worksheet->write( 0, 0, 'Sample', $format );
-    $worksheet->write( 0, 1, 1 );
-    $worksheet->write( 0, 2, 2 );
-    $worksheet->write( 0, 3, 3 );
-    $worksheet->write( 0, 4, 4 );
-    $worksheet->write( 0, 5, 5 );
-    $worksheet->write( 0, 6, 6 );
-    $worksheet->write( 0, 7, 7 );
-    $worksheet->write( 0, 8, 8 );
-
-    $worksheet->write( 1, 0, 'Length', $format );
-    $worksheet->write( 1, 1, 25.4 );
-    $worksheet->write( 1, 2, 25.4 );
-    $worksheet->write( 1, 3, 24.8 );
-    $worksheet->write( 1, 4, 25.0 );
-    $worksheet->write( 1, 5, 25.3 );
-    $worksheet->write( 1, 6, 24.9 );
-    $worksheet->write( 1, 7, 25.2 );
-    $worksheet->write( 1, 8, 24.8 );
-
-    # Write some statistical functions
-    $worksheet->write( 4, 0, 'Count', $format );
-    $worksheet->write( 4, 1, '=COUNT(B1:I1)' );
-
-    $worksheet->write( 5, 0, 'Sum', $format );
-    $worksheet->write( 5, 1, '=SUM(B2:I2)' );
-
-    $worksheet->write( 6, 0, 'Average', $format );
-    $worksheet->write( 6, 1, '=AVERAGE(B2:I2)' );
-
-    $worksheet->write( 7, 0, 'Min', $format );
-    $worksheet->write( 7, 1, '=MIN(B2:I2)' );
-
-    $worksheet->write( 8, 0, 'Max', $format );
-    $worksheet->write( 8, 1, '=MAX(B2:I2)' );
-
-    $worksheet->write( 9, 0, 'Standard Deviation', $format );
-    $worksheet->write( 9, 1, '=STDEV(B2:I2)' );
-
-    $worksheet->write( 10, 0, 'Kurtosis', $format );
-    $worksheet->write( 10, 1, '=KURT(B2:I2)' );
-
-    $workbook->close();
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/stats.jpg" width="640" height="420" alt="Output from stats.pl" /></center></p>
-
-=end html
-
-
-
-
-=head2 Example 5
-
-The following example converts a tab separated file called C<tab.txt> into an Excel file called C<tab.xlsx>.
-
-    #!/usr/bin/perl -w
-
-    use strict;
-    use Excel::Writer::XLSX;
-
-    open( TABFILE, 'tab.txt' ) or die "tab.txt: $!";
-
-    my $workbook  = Excel::Writer::XLSX->new( 'tab.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    # Row and column are zero indexed
-    my $row = 0;
-
-    while ( <TABFILE> ) {
-        chomp;
-
-        # Split on single tab
-        my @fields = split( '\t', $_ );
-
-        my $col = 0;
-        for my $token ( @fields ) {
-            $worksheet->write( $row, $col, $token );
-            $col++;
-        }
-        $row++;
-    }
-
-    $workbook->close();
-
-NOTE: This is a simple conversion program for illustrative purposes only. For converting a CSV or Tab separated or any other type of delimited text file to Excel I recommend the more rigorous csv2xls program that is part of H.Merijn Brand's L<Text::CSV_XS> module distro.
-
-See the examples/csv2xls link here: L<http://search.cpan.org/~hmbrand/Text-CSV_XS/MANIFEST>.
-
-
-
-
-=head2 Additional Examples
-
-The following is a description of the example files that are provided
-in the standard Excel::Writer::XLSX distribution. They demonstrate the
-different features and options of the module. See L<Excel::Writer::XLSX::Examples> for more details.
-
-    Getting started
-    ===============
-    a_simple.pl             A simple demo of some of the features.
-    bug_report.pl           A template for submitting bug reports.
-    demo.pl                 A demo of some of the available features.
-    formats.pl              All the available formatting on several worksheets.
-    regions.pl              A simple example of multiple worksheets.
-    stats.pl                Basic formulas and functions.
-
-
-    Intermediate
-    ============
-    autofilter.pl           Examples of worksheet autofilters.
-    array_formula.pl        Examples of how to write array formulas.
-    cgi.pl                  A simple CGI program.
-    chart_area.pl           A demo of area style charts.
-    chart_bar.pl            A demo of bar (vertical histogram) style charts.
-    chart_column.pl         A demo of column (histogram) style charts.
-    chart_line.pl           A demo of line style charts.
-    chart_pie.pl            A demo of pie style charts.
-    chart_doughnut.pl       A demo of doughnut style charts.
-    chart_radar.pl          A demo of radar style charts.
-    chart_scatter.pl        A demo of scatter style charts.
-    chart_secondary_axis.pl A demo of a line chart with a secondary axis.
-    chart_combined.pl       A demo of a combined column and line chart.
-    chart_pareto.pl         A demo of a combined Pareto chart.
-    chart_stock.pl          A demo of stock style charts.
-    chart_data_table.pl     A demo of a chart with a data table on the axis.
-    chart_data_tools.pl     A demo of charts with data highlighting options.
-    chart_clustered.pl      A demo of a chart with a clustered axis.
-    chart_styles.pl         A demo of the available chart styles.
-    colors.pl               A demo of the colour palette and named colours.
-    comments1.pl            Add comments to worksheet cells.
-    comments2.pl            Add comments with advanced options.
-    conditional_format.pl   Add conditional formats to a range of cells.
-    data_validate.pl        An example of data validation and dropdown lists.
-    date_time.pl            Write dates and times with write_date_time().
-    defined_name.pl         Example of how to create defined names.
-    diag_border.pl          A simple example of diagonal cell borders.
-    filehandle.pl           Examples of working with filehandles.
-    headers.pl              Examples of worksheet headers and footers.
-    hide_row_col.pl         Example of hiding rows and columns.
-    hide_sheet.pl           Simple example of hiding a worksheet.
-    hyperlink1.pl           Shows how to create web hyperlinks.
-    hyperlink2.pl           Examples of internal and external hyperlinks.
-    indent.pl               An example of cell indentation.
-    macros.pl               An example of adding macros from an existing file.
-    merge1.pl               A simple example of cell merging.
-    merge2.pl               A simple example of cell merging with formatting.
-    merge3.pl               Add hyperlinks to merged cells.
-    merge4.pl               An advanced example of merging with formatting.
-    merge5.pl               An advanced example of merging with formatting.
-    merge6.pl               An example of merging with Unicode strings.
-    mod_perl1.pl            A simple mod_perl 1 program.
-    mod_perl2.pl            A simple mod_perl 2 program.
-    panes.pl                An examples of how to create panes.
-    outline.pl              An example of outlines and grouping.
-    outline_collapsed.pl    An example of collapsed outlines.
-    protection.pl           Example of cell locking and formula hiding.
-    rich_strings.pl         Example of strings with multiple formats.
-    right_to_left.pl        Change default sheet direction to right to left.
-    sales.pl                An example of a simple sales spreadsheet.
-    shape1.pl               Insert shapes in worksheet.
-    shape2.pl               Insert shapes in worksheet. With properties.
-    shape3.pl               Insert shapes in worksheet. Scaled.
-    shape4.pl               Insert shapes in worksheet. With modification.
-    shape5.pl               Insert shapes in worksheet. With connections.
-    shape6.pl               Insert shapes in worksheet. With connections.
-    shape7.pl               Insert shapes in worksheet. One to many connections.
-    shape8.pl               Insert shapes in worksheet. One to many connections.
-    shape_all.pl            Demo of all the available shape and connector types.
-    sparklines1.pl          Simple sparklines demo.
-    sparklines2.pl          Sparklines demo showing formatting options.
-    stats_ext.pl            Same as stats.pl with external references.
-    stocks.pl               Demonstrates conditional formatting.
-    tab_colors.pl           Example of how to set worksheet tab colours.
-    tables.pl               Add Excel tables to a worksheet.
-    write_handler1.pl       Example of extending the write() method. Step 1.
-    write_handler2.pl       Example of extending the write() method. Step 2.
-    write_handler3.pl       Example of extending the write() method. Step 3.
-    write_handler4.pl       Example of extending the write() method. Step 4.
-    write_to_scalar.pl      Example of writing an Excel file to a Perl scalar.
-
-    Unicode
-    =======
-    unicode_2022_jp.pl      Japanese: ISO-2022-JP.
-    unicode_8859_11.pl      Thai:     ISO-8859_11.
-    unicode_8859_7.pl       Greek:    ISO-8859_7.
-    unicode_big5.pl         Chinese:  BIG5.
-    unicode_cp1251.pl       Russian:  CP1251.
-    unicode_cp1256.pl       Arabic:   CP1256.
-    unicode_cyrillic.pl     Russian:  Cyrillic.
-    unicode_koi8r.pl        Russian:  KOI8-R.
-    unicode_polish_utf8.pl  Polish :  UTF8.
-    unicode_shift_jis.pl    Japanese: Shift JIS.
-
-
-
-
-=head1 LIMITATIONS
-
-The following limits are imposed by Excel 2007+:
-
-    Description                             Limit
-    --------------------------------------  ------
-    Maximum number of chars in a string     32,767
-    Maximum number of columns               16,384
-    Maximum number of rows                  1,048,576
-    Maximum chars in a sheet name           31
-    Maximum chars in a header/footer        254
-
-    Maximum characters in hyperlink url     255
-    Maximum characters in hyperlink anchor  255
-    Maximum number of unique hyperlinks*    65,530
-
-* Per worksheet. Excel allows a greater number of non-unique hyperlinks if they are contiguous and can be grouped into a single range. This will be supported in a later version of Excel::Writer::XLSX if possible.
-
-
-
-
-=head1 Compatibility with Spreadsheet::WriteExcel
-
-The C<Excel::Writer::XLSX> module is a drop-in replacement for C<Spreadsheet::WriteExcel>.
-
-It supports all of the features of Spreadsheet::WriteExcel with some minor differences noted below.
-
-    Workbook Methods            Support
-    ================            ======
-    new()                       Yes
-    add_worksheet()             Yes
-    add_format()                Yes
-    add_chart()                 Yes
-    add_shape()                 Yes. Not in Spreadsheet::WriteExcel.
-    add_vba_project()           Yes. Not in Spreadsheet::WriteExcel.
-    close()                     Yes
-    set_properties()            Yes
-    define_name()               Yes
-    set_tempdir()               Yes
-    set_custom_color()          Yes
-    sheets()                    Yes
-    set_1904()                  Yes
-    set_optimization()          Yes. Not required in Spreadsheet::WriteExcel.
-    add_chart_ext()             Not supported. Not required in Excel::Writer::XLSX.
-    compatibility_mode()        Deprecated. Not required in Excel::Writer::XLSX.
-    set_codepage()              Deprecated. Not required in Excel::Writer::XLSX.
-
-
-    Worksheet Methods           Support
-    =================           =======
-    write()                     Yes
-    write_number()              Yes
-    write_string()              Yes
-    write_rich_string()         Yes. Not in Spreadsheet::WriteExcel.
-    write_blank()               Yes
-    write_row()                 Yes
-    write_col()                 Yes
-    write_date_time()           Yes
-    write_url()                 Yes
-    write_formula()             Yes
-    write_array_formula()       Yes. Not in Spreadsheet::WriteExcel.
-    keep_leading_zeros()        Yes
-    write_comment()             Yes
-    show_comments()             Yes
-    set_comments_author()       Yes
-    add_write_handler()         Yes
-    insert_image()              Yes.
-    insert_chart()              Yes
-    insert_shape()              Yes. Not in Spreadsheet::WriteExcel.
-    insert_button()             Yes. Not in Spreadsheet::WriteExcel.
-    data_validation()           Yes
-    conditional_formatting()    Yes. Not in Spreadsheet::WriteExcel.
-    add_sparkline()             Yes. Not in Spreadsheet::WriteExcel.
-    add_table()                 Yes. Not in Spreadsheet::WriteExcel.
-    get_name()                  Yes
-    activate()                  Yes
-    select()                    Yes
-    hide()                      Yes
-    set_first_sheet()           Yes
-    protect()                   Yes
-    set_selection()             Yes
-    set_row()                   Yes.
-    set_column()                Yes.
-    set_default_row()           Yes. Not in Spreadsheet::WriteExcel.
-    outline_settings()          Yes
-    freeze_panes()              Yes
-    split_panes()               Yes
-    merge_range()               Yes
-    merge_range_type()          Yes. Not in Spreadsheet::WriteExcel.
-    set_zoom()                  Yes
-    right_to_left()             Yes
-    hide_zero()                 Yes
-    set_tab_color()             Yes
-    autofilter()                Yes
-    filter_column()             Yes
-    filter_column_list()        Yes. Not in Spreadsheet::WriteExcel.
-    write_utf16be_string()      Deprecated. Use Perl utf8 strings instead.
-    write_utf16le_string()      Deprecated. Use Perl utf8 strings instead.
-    store_formula()             Deprecated. See docs.
-    repeat_formula()            Deprecated. See docs.
-    write_url_range()           Not supported. Not required in Excel::Writer::XLSX.
-
-    Page Set-up Methods         Support
-    ===================         =======
-    set_landscape()             Yes
-    set_portrait()              Yes
-    set_page_view()             Yes
-    set_paper()                 Yes
-    center_horizontally()       Yes
-    center_vertically()         Yes
-    set_margins()               Yes
-    set_header()                Yes
-    set_footer()                Yes
-    repeat_rows()               Yes
-    repeat_columns()            Yes
-    hide_gridlines()            Yes
-    print_row_col_headers()     Yes
-    print_area()                Yes
-    print_across()              Yes
-    fit_to_pages()              Yes
-    set_start_page()            Yes
-    set_print_scale()           Yes
-    set_h_pagebreaks()          Yes
-    set_v_pagebreaks()          Yes
-
-    Format Methods              Support
-    ==============              =======
-    set_font()                  Yes
-    set_size()                  Yes
-    set_color()                 Yes
-    set_bold()                  Yes
-    set_italic()                Yes
-    set_underline()             Yes
-    set_font_strikeout()        Yes
-    set_font_script()           Yes
-    set_font_outline()          Yes
-    set_font_shadow()           Yes
-    set_num_format()            Yes
-    set_locked()                Yes
-    set_hidden()                Yes
-    set_align()                 Yes
-    set_rotation()              Yes
-    set_text_wrap()             Yes
-    set_text_justlast()         Yes
-    set_center_across()         Yes
-    set_indent()                Yes
-    set_shrink()                Yes
-    set_pattern()               Yes
-    set_bg_color()              Yes
-    set_fg_color()              Yes
-    set_border()                Yes
-    set_bottom()                Yes
-    set_top()                   Yes
-    set_left()                  Yes
-    set_right()                 Yes
-    set_border_color()          Yes
-    set_bottom_color()          Yes
-    set_top_color()             Yes
-    set_left_color()            Yes
-    set_right_color()           Yes
-
-
-
-
-=head1 REQUIREMENTS
-
-L<http://search.cpan.org/search?dist=Archive-Zip/>.
-
-Perl 5.8.2.
-
-
-
-
-=head1 SPEED AND MEMORY USAGE
-
-C<Spreadsheet::WriteExcel> was written to optimise speed and reduce memory usage. However, these design goals meant that it wasn't easy to implement features that many users requested such as writing formatting and data separately.
-
-As a result C<Excel::Writer::XLSX> takes a different design approach and holds a lot more data in memory so that it is functionally more flexible.
-
-The effect of this is that Excel::Writer::XLSX is about 30% slower than Spreadsheet::WriteExcel and uses 5 times more memory.
-
-In addition the extended row and column ranges in Excel 2007+ mean that it is possible to run out of memory creating large files. This was almost never an issue with Spreadsheet::WriteExcel.
-
-This memory usage can be reduced almost completely by using the Workbook C<set_optimization()> method:
-
-    $workbook->set_optimization();
-
-This also gives an increase in performance to within 1-10% of Spreadsheet::WriteExcel, see below.
-
-The trade-off is that you won't be able to take advantage of any new features that manipulate cell data after it is written. One such feature is Tables.
-
-
-=head2 Performance figures
-
-The performance figures below show execution speed and memory usage for 60 columns x N rows for a 50/50 mixture of strings and numbers. Percentage speeds are relative to Spreadsheet::WriteExcel.
-
-    Excel::Writer::XLSX
-         Rows  Time (s)    Memory (bytes)  Rel. Time
-          400      0.66         6,586,254       129%
-          800      1.26        13,099,422       125%
-         1600      2.55        26,126,361       123%
-         3200      5.16        52,211,284       125%
-         6400     10.47       104,401,428       128%
-        12800     21.48       208,784,519       131%
-        25600     43.90       417,700,746       126%
-        51200     88.52       835,900,298       126%
-
-    Excel::Writer::XLSX + set_optimisation()
-         Rows  Time (s)    Memory (bytes)  Rel. Time
-          400      0.70            63,059       135%
-          800      1.10            63,059       110%
-         1600      2.30            63,062       111%
-         3200      4.44            63,062       107%
-         6400      8.91            63,062       109%
-        12800     17.69            63,065       108%
-        25600     35.15            63,065       101%
-        51200     70.67            63,065       101%
-
-    Spreadsheet::WriteExcel
-         Rows  Time (s)    Memory (bytes)
-          400      0.51         1,265,583
-          800      1.01         2,424,855
-         1600      2.07         4,743,400
-         3200      4.14         9,411,139
-         6400      8.20        18,766,915
-        12800     16.39        37,478,468
-        25600     34.72        75,044,423
-        51200     70.21       150,543,431
-
-
-=head1 DOWNLOADING
-
-The latest version of this module is always available at: L<http://search.cpan.org/search?dist=Excel-Writer-XLSX/>.
-
-
-
-
-=head1 INSTALLATION
-
-The module can be installed using the standard Perl procedure:
-
-            perl Makefile.PL
-            make
-            make test
-            make install    # You may need to be sudo/root
-
-
-
-
-=head1 DIAGNOSTICS
-
-
-=over 4
-
-=item Filename required by Excel::Writer::XLSX->new()
-
-A filename must be given in the constructor.
-
-=item Can't open filename. It may be in use or protected.
-
-The file cannot be opened for writing. The directory that you are writing to may be protected or the file may be in use by another program.
-
-
-=item Can't call method "XXX" on an undefined value at someprogram.pl.
-
-On Windows this is usually caused by the file that you are trying to create clashing with a version that is already open and locked by Excel.
-
-=item The file you are trying to open 'file.xls' is in a different format than specified by the file extension.
-
-This warning occurs when you create an XLSX file but give it an xls extension.
-
-=back
-
-
-
-
-=head1 WRITING EXCEL FILES
-
-Depending on your requirements, background and general sensibilities you may prefer one of the following methods of getting data into Excel:
-
-=over 4
-
-=item * Spreadsheet::WriteExcel
-
-This module is the precursor to Excel::Writer::XLSX and uses the same interface. It produces files in the Excel Biff xls format that was used in Excel versions 97-2003. These files can still be read by Excel 2007 but have some limitations in relation to the number of rows and columns that the format supports.
-
-L<Spreadsheet::WriteExcel>.
-
-=item * Win32::OLE module and office automation
-
-This requires a Windows platform and an installed copy of Excel. This is the most powerful and complete method for interfacing with Excel.
-
-L<Win32::OLE>
-
-=item * CSV, comma separated variables or text
-
-Excel will open and automatically convert files with a C<csv> extension.
-
-To create CSV files refer to the L<Text::CSV_XS> module.
-
-
-=item * DBI with DBD::ADO or DBD::ODBC
-
-Excel files contain an internal index table that allows them to act like a database file. Using one of the standard Perl database modules you can connect to an Excel file as a database.
-
-
-=back
-
-For other Perl-Excel modules try the following search: L<http://search.cpan.org/search?mode=module&query=excel>.
-
-
-
-
-=head1 READING EXCEL FILES
-
-To read data from Excel files try:
-
-=over 4
-
-=item * Spreadsheet::XLSX
-
-A module for reading formatted or unformatted data form XLSX files.
-
-L<Spreadsheet::XLSX>
-
-=item * SimpleXlsx
-
-A lightweight module for reading data from XLSX files.
-
-L<SimpleXlsx>
-
-=item * Spreadsheet::ParseExcel
-
-This module can read  data from an Excel XLS file but it doesn't support the XLSX format.
-
-L<Spreadsheet::ParseExcel>
-
-=item * Win32::OLE module and office automation (reading)
-
-See above.
-
-=item * DBI with DBD::ADO or DBD::ODBC.
-
-See above.
-
-=back
-
-
-For other Perl-Excel modules try the following search: L<http://search.cpan.org/search?mode=module&query=excel>.
-
-
-=head1 BUGS
-
-=over
-
-=item * Memory usage is very high for large worksheets.
-
-If you run out of memory creating large worksheets use the C<set_optimization()> method. See L</SPEED AND MEMORY USAGE> for more information.
-
-=item * Perl packaging programs can't find chart modules.
-
-When using Excel::Writer::XLSX charts with Perl packagers such as PAR or Cava you should explicitly include the chart that you are trying to create in your C<use> statements. This isn't a bug as such but it might help someone from banging their head off a wall:
-
-    ...
-    use Excel::Writer::XLSX;
-    use Excel::Writer::XLSX::Chart::Column;
-    ...
-
-=back
-
-
-If you wish to submit a bug report run the C<bug_report.pl> program in the C<examples> directory of the distro.
-
-
-
-
-The bug tracker is on Github: L<https://github.com/jmcnamara/excel-writer-xlsx/issues>.
-
-
-=head1 TO DO
-
-The roadmap is as follows:
-
-=over 4
-
-=item * New separated data/formatting API to allow cells to be formatted after data is added.
-
-=item * More charting features.
-
-=back
-
-
-
-
-
-=head1 REPOSITORY
-
-The Excel::Writer::XLSX source code in host on github: L<http://github.com/jmcnamara/excel-writer-xlsx>.
-
-
-
-
-=head1 MAILING LIST
-
-There is a Google group for discussing and asking questions about Excel::Writer::XLSX. This is a good place to search to see if your question has been asked before:  L<http://groups.google.com/group/spreadsheet-writeexcel>.
-
-
-
-
-=head1 DONATIONS and SPONSORSHIP
-
-If you'd care to donate to the Excel::Writer::XLSX project or sponsor a new feature, you can do so via PayPal: L<http://tinyurl.com/7ayes>.
-
-
-
-
-=head1 SEE ALSO
-
-Spreadsheet::WriteExcel: L<http://search.cpan.org/dist/Spreadsheet-WriteExcel>.
-
-Spreadsheet::ParseExcel: L<http://search.cpan.org/dist/Spreadsheet-ParseExcel>.
-
-Spreadsheet::XLSX: L<http://search.cpan.org/dist/Spreadsheet-XLSX>.
-
-
-
-=head1 ACKNOWLEDGEMENTS
-
-
-The following people contributed to the debugging, testing or enhancement of Excel::Writer::XLSX:
-
-Rob Messer of IntelliSurvey gave me the initial prompt to port Spreadsheet::WriteExcel to the XLSX format. IntelliSurvey (L<http://www.intellisurvey.com>) also sponsored large files optimisations and the charting feature.
-
-Bariatric Advantage (L<http://www.bariatricadvantage.com>) sponsored work on chart formatting.
-
-Eric Johnson provided the ability to use secondary axes with charts.  Thanks to Foxtons (L<http://foxtons.co.uk>) for sponsoring this work.
-
-BuildFax (L<http://www.buildfax.com>) sponsored the Tables feature and the Chart point formatting feature.
-
-
-
-
-=head1 DISCLAIMER OF WARRANTY
-
-Because this software is licensed free of charge, there is no warranty for the software, to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide the software "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the software is with you. Should the software prove defective, you assume the cost of all necessary servicing, repair, or correction.
-
-In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute the software as permitted by the above licence, be liable to you for damages, including any general, special, incidental, or consequential damages arising out of the use or inability to use the software (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties or a failure of the software to operate with any other software), even if such holder or other party has been advised of the possibility of such damages.
-
-
-
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-    Wilderness for miles, eyes so mild and wise
-    Oasis child, born and so wild
-    Don't I know you better than the rest
-    All deception, all deception from you
-
-    Any way you run, you run before us
-    Black and white horse arching among us
-    Any way you run, you run before us
-    Black and white horse arching among us
-
-      -- Beach House
-
-
-
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chart.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chart.pm
deleted file mode 100644 (file)
index 6e76df5..0000000
+++ /dev/null
@@ -1,8664 +0,0 @@
-package Excel::Writer::XLSX::Chart;
-
-###############################################################################
-#
-# Chart - A class for writing Excel Charts.
-#
-#
-# Used in conjunction with Excel::Writer::XLSX.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Format;
-use Excel::Writer::XLSX::Package::XMLwriter;
-use Excel::Writer::XLSX::Utility qw(xl_cell_to_rowcol
-  xl_rowcol_to_cell
-  xl_col_to_name xl_range
-  xl_range_formula
-  quote_sheetname );
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# factory()
-#
-# Factory method for returning chart objects based on their class type.
-#
-sub factory {
-
-    my $current_class  = shift;
-    my $chart_subclass = shift;
-
-    $chart_subclass = ucfirst lc $chart_subclass;
-
-    my $module = "Excel::Writer::XLSX::Chart::" . $chart_subclass;
-
-    eval "require $module";
-
-    # TODO. Need to re-raise this error from Workbook::add_chart().
-    die "Chart type '$chart_subclass' not supported in add_chart()\n" if $@;
-
-    my $fh = undef;
-    return $module->new( $fh, @_ );
-}
-
-
-###############################################################################
-#
-# new()
-#
-# Default constructor for sub-classes.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_subtype}           = shift;
-    $self->{_sheet_type}        = 0x0200;
-    $self->{_orientation}       = 0x0;
-    $self->{_series}            = [];
-    $self->{_embedded}          = 0;
-    $self->{_id}                = -1;
-    $self->{_series_index}      = 0;
-    $self->{_style_id}          = 2;
-    $self->{_axis_ids}          = [];
-    $self->{_axis2_ids}         = [];
-    $self->{_cat_has_num_fmt}   = 0;
-    $self->{_requires_category} = 0;
-    $self->{_legend_position}   = 'right';
-    $self->{_cat_axis_position} = 'b';
-    $self->{_val_axis_position} = 'l';
-    $self->{_formula_ids}       = {};
-    $self->{_formula_data}      = [];
-    $self->{_horiz_cat_axis}    = 0;
-    $self->{_horiz_val_axis}    = 1;
-    $self->{_protection}        = 0;
-    $self->{_chartarea}         = {};
-    $self->{_plotarea}          = {};
-    $self->{_x_axis}            = {};
-    $self->{_y_axis}            = {};
-    $self->{_y2_axis}           = {};
-    $self->{_x2_axis}           = {};
-    $self->{_chart_name}        = '';
-    $self->{_show_blanks}       = 'gap';
-    $self->{_show_hidden_data}  = 0;
-    $self->{_show_crosses}      = 1;
-    $self->{_width}             = 480;
-    $self->{_height}            = 288;
-    $self->{_x_scale}           = 1;
-    $self->{_y_scale}           = 1;
-    $self->{_x_offset}          = 0;
-    $self->{_y_offset}          = 0;
-    $self->{_table}             = undef;
-    $self->{_smooth_allowed}    = 0;
-    $self->{_cross_between}     = 'between';
-    $self->{_date_category}     = 0;
-    $self->{_already_inserted}  = 0;
-    $self->{_combined}          = undef;
-    $self->{_is_secondary}      = 0;
-
-    $self->{_label_positions}          = {};
-    $self->{_label_position_default}   = '';
-
-    bless $self, $class;
-    $self->_set_default_properties();
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration();
-
-    # Write the c:chartSpace element.
-    $self->_write_chart_space();
-
-    # Write the c:lang element.
-    $self->_write_lang();
-
-    # Write the c:style element.
-    $self->_write_style();
-
-    # Write the c:protection element.
-    $self->_write_protection();
-
-    # Write the c:chart element.
-    $self->_write_chart();
-
-    # Write the c:spPr element for the chartarea formatting.
-    $self->_write_sp_pr( $self->{_chartarea} );
-
-    # Write the c:printSettings element.
-    $self->_write_print_settings() if $self->{_embedded};
-
-    # Close the worksheet tag.
-    $self->xml_end_tag( 'c:chartSpace' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# Public methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# add_series()
-#
-# Add a series and it's properties to a chart.
-#
-sub add_series {
-
-    my $self = shift;
-    my %arg  = @_;
-
-    # Check that the required input has been specified.
-    if ( !exists $arg{values} ) {
-        croak "Must specify 'values' in add_series()";
-    }
-
-    if ( $self->{_requires_category} && !exists $arg{categories} ) {
-        croak "Must specify 'categories' in add_series() for this chart type";
-    }
-
-    if ( @{ $self->{_series} } == 255 ) {
-        carp "The maxiumn number of series that can be added to an "
-          . "Excel Chart is 255";
-        return
-    }
-
-    # Convert aref params into a formula string.
-    my $values     = $self->_aref_to_formula( $arg{values} );
-    my $categories = $self->_aref_to_formula( $arg{categories} );
-
-    # Switch name and name_formula parameters if required.
-    my ( $name, $name_formula ) =
-      $self->_process_names( $arg{name}, $arg{name_formula} );
-
-    # Get an id for the data equivalent to the range formula.
-    my $cat_id  = $self->_get_data_id( $categories,   $arg{categories_data} );
-    my $val_id  = $self->_get_data_id( $values,       $arg{values_data} );
-    my $name_id = $self->_get_data_id( $name_formula, $arg{name_data} );
-
-    # Set the line properties for the series.
-    my $line = $self->_get_line_properties( $arg{line} );
-
-    # Allow 'border' as a synonym for 'line' in bar/column style charts.
-    if ( $arg{border} ) {
-        $line = $self->_get_line_properties( $arg{border} );
-    }
-
-    # Set the fill properties for the series.
-    my $fill = $self->_get_fill_properties( $arg{fill} );
-
-    # Set the pattern properties for the series.
-    my $pattern = $self->_get_pattern_properties( $arg{pattern} );
-
-    # Set the gradient fill properties for the series.
-    my $gradient = $self->_get_gradient_properties( $arg{gradient} );
-
-    # Pattern fill overrides solid fill.
-    if ( $pattern ) {
-        $fill = undef;
-    }
-
-    # Gradient fill overrides solid and pattern fills.
-    if ( $gradient ) {
-        $pattern = undef;
-        $fill    = undef;
-    }
-
-    # Set the marker properties for the series.
-    my $marker = $self->_get_marker_properties( $arg{marker} );
-
-    # Set the trendline properties for the series.
-    my $trendline = $self->_get_trendline_properties( $arg{trendline} );
-
-    # Set the line smooth property for the series.
-    my $smooth = $arg{smooth};
-
-    # Set the error bars properties for the series.
-    my $y_error_bars = $self->_get_error_bars_properties( $arg{y_error_bars} );
-    my $x_error_bars = $self->_get_error_bars_properties( $arg{x_error_bars} );
-
-    # Set the point properties for the series.
-    my $points = $self->_get_points_properties($arg{points});
-
-    # Set the labels properties for the series.
-    my $labels = $self->_get_labels_properties( $arg{data_labels} );
-
-    # Set the "invert if negative" fill property.
-    my $invert_if_neg = $arg{invert_if_negative};
-
-    # Set the secondary axis properties.
-    my $x2_axis = $arg{x2_axis};
-    my $y2_axis = $arg{y2_axis};
-
-    # Store secondary status for combined charts.
-    if ($x2_axis || $y2_axis) {
-        $self->{_is_secondary} = 1;
-    }
-
-    # Set the gap for Bar/Column charts.
-    if ( defined $arg{gap} ) {
-        if ($y2_axis) {
-            $self->{_series_gap_2} = $arg{gap};
-        }
-        else {
-            $self->{_series_gap_1} = $arg{gap};
-        }
-    }
-
-    # Set the overlap for Bar/Column charts.
-    if ( defined $arg{overlap} ) {
-        if ($y2_axis) {
-            $self->{_series_overlap_2} = $arg{overlap};
-        }
-        else {
-            $self->{_series_overlap_1} = $arg{overlap};
-        }
-    }
-
-    # Add the user supplied data to the internal structures.
-    %arg = (
-        _values        => $values,
-        _categories    => $categories,
-        _name          => $name,
-        _name_formula  => $name_formula,
-        _name_id       => $name_id,
-        _val_data_id   => $val_id,
-        _cat_data_id   => $cat_id,
-        _line          => $line,
-        _fill          => $fill,
-        _pattern       => $pattern,
-        _gradient      => $gradient,
-        _marker        => $marker,
-        _trendline     => $trendline,
-        _smooth        => $smooth,
-        _labels        => $labels,
-        _invert_if_neg => $invert_if_neg,
-        _x2_axis       => $x2_axis,
-        _y2_axis       => $y2_axis,
-        _points        => $points,
-        _error_bars =>
-          { _x_error_bars => $x_error_bars, _y_error_bars => $y_error_bars },
-    );
-
-
-    push @{ $self->{_series} }, \%arg;
-}
-
-
-###############################################################################
-#
-# set_x_axis()
-#
-# Set the properties of the X-axis.
-#
-sub set_x_axis {
-
-    my $self = shift;
-
-    my $axis = $self->_convert_axis_args( $self->{_x_axis}, @_ );
-
-    $self->{_x_axis} = $axis;
-}
-
-
-###############################################################################
-#
-# set_y_axis()
-#
-# Set the properties of the Y-axis.
-#
-sub set_y_axis {
-
-    my $self = shift;
-
-    my $axis = $self->_convert_axis_args( $self->{_y_axis}, @_ );
-
-    $self->{_y_axis} = $axis;
-}
-
-
-###############################################################################
-#
-# set_x2_axis()
-#
-# Set the properties of the secondary X-axis.
-#
-sub set_x2_axis {
-
-    my $self = shift;
-
-    my $axis = $self->_convert_axis_args( $self->{_x2_axis}, @_ );
-
-    $self->{_x2_axis} = $axis;
-}
-
-
-###############################################################################
-#
-# set_y2_axis()
-#
-# Set the properties of the secondary Y-axis.
-#
-sub set_y2_axis {
-
-    my $self = shift;
-
-    my $axis = $self->_convert_axis_args( $self->{_y2_axis}, @_ );
-
-    $self->{_y2_axis} = $axis;
-}
-
-
-###############################################################################
-#
-# set_title()
-#
-# Set the properties of the chart title.
-#
-sub set_title {
-
-    my $self = shift;
-    my %arg  = @_;
-
-    my ( $name, $name_formula ) =
-      $self->_process_names( $arg{name}, $arg{name_formula} );
-
-    my $data_id = $self->_get_data_id( $name_formula, $arg{data} );
-
-    $self->{_title_name}    = $name;
-    $self->{_title_formula} = $name_formula;
-    $self->{_title_data_id} = $data_id;
-
-    # Set the font properties if present.
-    $self->{_title_font} = $self->_convert_font_args( $arg{name_font} );
-
-    # Set the title layout.
-    $self->{_title_layout} = $self->_get_layout_properties( $arg{layout}, 1 );
-
-    # Set the title overlay option.
-    $self->{_title_overlay} = $arg{overlay};
-
-    # Set the no automatic title option.
-    $self->{_title_none} = $arg{none};
-}
-
-
-###############################################################################
-#
-# set_legend()
-#
-# Set the properties of the chart legend.
-#
-sub set_legend {
-
-    my $self = shift;
-    my %arg  = @_;
-
-    $self->{_legend_position}      = $arg{position} || 'right';
-    $self->{_legend_delete_series} = $arg{delete_series};
-    $self->{_legend_font}          = $self->_convert_font_args( $arg{font} );
-
-    # Set the legend layout.
-    $self->{_legend_layout} = $self->_get_layout_properties( $arg{layout} );
-
-    # Turn off the legend.
-    if ( $arg{none} ) {
-        $self->{_legend_position} = 'none';
-    }
-}
-
-
-###############################################################################
-#
-# set_plotarea()
-#
-# Set the properties of the chart plotarea.
-#
-sub set_plotarea {
-
-    my $self = shift;
-
-    # Convert the user defined properties to internal properties.
-    $self->{_plotarea} = $self->_get_area_properties( @_ );
-}
-
-
-###############################################################################
-#
-# set_chartarea()
-#
-# Set the properties of the chart chartarea.
-#
-sub set_chartarea {
-
-    my $self = shift;
-
-    # Convert the user defined properties to internal properties.
-    $self->{_chartarea} = $self->_get_area_properties( @_ );
-}
-
-
-###############################################################################
-#
-# set_style()
-#
-# Set on of the 48 built-in Excel chart styles. The default style is 2.
-#
-sub set_style {
-
-    my $self = shift;
-    my $style_id = defined $_[0] ? $_[0] : 2;
-
-    if ( $style_id < 0 || $style_id > 48 ) {
-        $style_id = 2;
-    }
-
-    $self->{_style_id} = $style_id;
-}
-
-
-###############################################################################
-#
-# show_blanks_as()
-#
-# Set the option for displaying blank data in a chart. The default is 'gap'.
-#
-sub show_blanks_as {
-
-    my $self   = shift;
-    my $option = shift;
-
-    return unless $option;
-
-    my %valid = (
-        gap  => 1,
-        zero => 1,
-        span => 1,
-
-    );
-
-    if ( !exists $valid{$option} ) {
-        warn "Unknown show_blanks_as() option '$option'\n";
-        return;
-    }
-
-    $self->{_show_blanks} = $option;
-}
-
-
-###############################################################################
-#
-# show_hidden_data()
-#
-# Display data in hidden rows or columns.
-#
-sub show_hidden_data {
-
-    my $self = shift;
-
-    $self->{_show_hidden_data} = 1;
-}
-
-
-###############################################################################
-#
-# set_size()
-#
-# Set dimensions or scale for the chart.
-#
-sub set_size {
-
-    my $self = shift;
-    my %args = @_;
-
-    $self->{_width}    = $args{width}    if $args{width};
-    $self->{_height}   = $args{height}   if $args{height};
-    $self->{_x_scale}  = $args{x_scale}  if $args{x_scale};
-    $self->{_y_scale}  = $args{y_scale}  if $args{y_scale};
-    $self->{_x_offset} = $args{x_offset} if $args{x_offset};
-    $self->{_y_offset} = $args{y_offset} if $args{y_offset};
-
-}
-
-# Backward compatibility with poorly chosen method name.
-*size = *set_size;
-
-
-###############################################################################
-#
-# set_table()
-#
-# Set properties for an axis data table.
-#
-sub set_table {
-
-    my $self = shift;
-    my %args = @_;
-
-    my %table = (
-        _horizontal => 1,
-        _vertical   => 1,
-        _outline    => 1,
-        _show_keys  => 0,
-    );
-
-    $table{_horizontal} = $args{horizontal} if defined $args{horizontal};
-    $table{_vertical}   = $args{vertical}   if defined $args{vertical};
-    $table{_outline}    = $args{outline}    if defined $args{outline};
-    $table{_show_keys}  = $args{show_keys}  if defined $args{show_keys};
-    $table{_font}       = $self->_convert_font_args( $args{font} );
-
-    $self->{_table} = \%table;
-}
-
-
-###############################################################################
-#
-# set_up_down_bars()
-#
-# Set properties for the chart up-down bars.
-#
-sub set_up_down_bars {
-
-    my $self = shift;
-    my %args = @_;
-
-    # Map border to line.
-    if ( defined $args{up}->{border} ) {
-        $args{up}->{line} = $args{up}->{border};
-    }
-    if ( defined $args{down}->{border} ) {
-        $args{down}->{line} = $args{down}->{border};
-    }
-
-    # Set the up and down bar properties.
-    my $up_line   = $self->_get_line_properties( $args{up}->{line} );
-    my $down_line = $self->_get_line_properties( $args{down}->{line} );
-    my $up_fill   = $self->_get_fill_properties( $args{up}->{fill} );
-    my $down_fill = $self->_get_fill_properties( $args{down}->{fill} );
-
-    $self->{_up_down_bars} = {
-        _up => {
-            _line => $up_line,
-            _fill => $up_fill,
-        },
-        _down => {
-            _line => $down_line,
-            _fill => $down_fill,
-        },
-    };
-}
-
-
-###############################################################################
-#
-# set_drop_lines()
-#
-# Set properties for the chart drop lines.
-#
-sub set_drop_lines {
-
-    my $self = shift;
-    my %args = @_;
-
-    # Set the drop line properties.
-    my $line = $self->_get_line_properties( $args{line} );
-
-    $self->{_drop_lines} = { _line => $line };
-}
-
-
-###############################################################################
-#
-# set_high_low_lines()
-#
-# Set properties for the chart high-low lines.
-#
-sub set_high_low_lines {
-
-    my $self = shift;
-    my %args = @_;
-
-    # Set the drop line properties.
-    my $line = $self->_get_line_properties( $args{line} );
-
-    $self->{_hi_low_lines} = { _line => $line };
-}
-
-
-###############################################################################
-#
-# combine()
-#
-# Add another chart to create a combined chart.
-#
-sub combine {
-
-    my $self  = shift;
-    my $chart = shift;
-
-    $self->{_combined} = $chart;
-}
-
-
-###############################################################################
-#
-# Internal methods. The following section of methods are used for the internal
-# structuring of the Chart object and file format.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _convert_axis_args()
-#
-# Convert user defined axis values into private hash values.
-#
-sub _convert_axis_args {
-
-    my $self = shift;
-    my $axis = shift;
-    my %arg  = ( %{ $axis->{_defaults} }, @_ );
-
-    my ( $name, $name_formula ) =
-      $self->_process_names( $arg{name}, $arg{name_formula} );
-
-    my $data_id = $self->_get_data_id( $name_formula, $arg{data} );
-
-    $axis = {
-        _defaults          => $axis->{_defaults},
-        _name              => $name,
-        _formula           => $name_formula,
-        _data_id           => $data_id,
-        _reverse           => $arg{reverse},
-        _min               => $arg{min},
-        _max               => $arg{max},
-        _minor_unit        => $arg{minor_unit},
-        _major_unit        => $arg{major_unit},
-        _minor_unit_type   => $arg{minor_unit_type},
-        _major_unit_type   => $arg{major_unit_type},
-        _log_base          => $arg{log_base},
-        _crossing          => $arg{crossing},
-        _position_axis     => $arg{position_axis},
-        _position          => $arg{position},
-        _label_position    => $arg{label_position},
-        _num_format        => $arg{num_format},
-        _num_format_linked => $arg{num_format_linked},
-        _interval_unit     => $arg{interval_unit},
-        _interval_tick     => $arg{interval_tick},
-        _visible           => defined $arg{visible} ? $arg{visible} : 1,
-        _text_axis         => 0,
-    };
-
-    # Map major_gridlines properties.
-    if ( $arg{major_gridlines} && $arg{major_gridlines}->{visible} ) {
-        $axis->{_major_gridlines} =
-          $self->_get_gridline_properties( $arg{major_gridlines} );
-    }
-
-    # Map minor_gridlines properties.
-    if ( $arg{minor_gridlines} && $arg{minor_gridlines}->{visible} ) {
-        $axis->{_minor_gridlines} =
-          $self->_get_gridline_properties( $arg{minor_gridlines} );
-    }
-
-    # Convert the display units.
-    $axis->{_display_units} = $self->_get_display_units( $arg{display_units} );
-    if ( defined $arg{display_units_visible} ) {
-        $axis->{_display_units_visible} = $arg{display_units_visible};
-    }
-    else {
-        $axis->{_display_units_visible} = 1;
-    }
-
-    # Only use the first letter of bottom, top, left or right.
-    if ( defined $axis->{_position} ) {
-        $axis->{_position} = substr lc $axis->{_position}, 0, 1;
-    }
-
-    # Set the position for a category axis on or between the tick marks.
-    if ( defined $axis->{_position_axis} ) {
-        if ( $axis->{_position_axis} eq 'on_tick' ) {
-            $axis->{_position_axis} = 'midCat';
-        }
-        elsif ( $axis->{_position_axis} eq 'between' ) {
-
-            # Doesn't need to be modified.
-        }
-        else {
-            # Otherwise use the default value.
-            $axis->{_position_axis} = undef;
-        }
-    }
-
-    # Set the category axis as a date axis.
-    if ( $arg{date_axis} ) {
-        $self->{_date_category} = 1;
-    }
-
-    # Set the category axis as a text axis.
-    if ( $arg{text_axis} ) {
-        $self->{_date_category} = 0;
-        $axis->{_text_axis} = 1;
-    }
-
-
-    # Set the font properties if present.
-    $axis->{_num_font}  = $self->_convert_font_args( $arg{num_font} );
-    $axis->{_name_font} = $self->_convert_font_args( $arg{name_font} );
-
-    # Set the axis name layout.
-    $axis->{_layout} = $self->_get_layout_properties( $arg{name_layout}, 1 );
-
-    # Set the line properties for the axis.
-    $axis->{_line} = $self->_get_line_properties( $arg{line} );
-
-    # Set the fill properties for the axis.
-    $axis->{_fill} = $self->_get_fill_properties( $arg{fill} );
-
-    # Set the tick marker types.
-    $axis->{_minor_tick_mark} = $self->_get_tick_type($arg{minor_tick_mark});
-    $axis->{_major_tick_mark} = $self->_get_tick_type($arg{major_tick_mark});
-
-
-    return $axis;
-}
-
-
-###############################################################################
-#
-# _convert_fonts_args()
-#
-# Convert user defined font values into private hash values.
-#
-sub _convert_font_args {
-
-    my $self = shift;
-    my $args = shift;
-
-    return unless $args;
-
-    my $font = {
-        _name         => $args->{name},
-        _color        => $args->{color},
-        _size         => $args->{size},
-        _bold         => $args->{bold},
-        _italic       => $args->{italic},
-        _underline    => $args->{underline},
-        _pitch_family => $args->{pitch_family},
-        _charset      => $args->{charset},
-        _baseline     => $args->{baseline} || 0,
-        _rotation     => $args->{rotation},
-    };
-
-    # Convert font size units.
-    $font->{_size} *= 100 if $font->{_size};
-
-    # Convert rotation into 60,000ths of a degree.
-    if ( $font->{_rotation} ) {
-        $font->{_rotation} = 60_000 * int( $font->{_rotation} );
-    }
-
-    return $font;
-}
-
-
-###############################################################################
-#
-# _aref_to_formula()
-#
-# Convert and aref of row col values to a range formula.
-#
-sub _aref_to_formula {
-
-    my $self = shift;
-    my $data = shift;
-
-    # If it isn't an array ref it is probably a formula already.
-    return $data if !ref $data;
-
-    my $formula = xl_range_formula( @$data );
-
-    return $formula;
-}
-
-
-###############################################################################
-#
-# _process_names()
-#
-# Switch name and name_formula parameters if required.
-#
-sub _process_names {
-
-    my $self         = shift;
-    my $name         = shift;
-    my $name_formula = shift;
-
-    if ( defined $name ) {
-
-        if ( ref $name eq 'ARRAY' ) {
-            my $cell = xl_rowcol_to_cell( $name->[1], $name->[2], 1, 1 );
-            $name_formula = quote_sheetname( $name->[0] ) . '!' . $cell;
-            $name         = '';
-        }
-        elsif ( $name =~ m/^=[^!]+!\$/ ) {
-
-            # Name looks like a formula, use it to set name_formula.
-            $name_formula = $name;
-            $name         = '';
-        }
-    }
-
-    return ( $name, $name_formula );
-}
-
-
-###############################################################################
-#
-# _get_data_type()
-#
-# Find the overall type of the data associated with a series.
-#
-# TODO. Need to handle date type.
-#
-sub _get_data_type {
-
-    my $self = shift;
-    my $data = shift;
-
-    # Check for no data in the series.
-    return 'none' if !defined $data;
-    return 'none' if @$data == 0;
-
-    if (ref $data->[0] eq 'ARRAY') {
-        return 'multi_str'
-    }
-
-    # If the token isn't a number assume it is a string.
-    for my $token ( @$data ) {
-        next if !defined $token;
-        return 'str'
-          if $token !~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;
-    }
-
-    # The series data was all numeric.
-    return 'num';
-}
-
-
-###############################################################################
-#
-# _get_data_id()
-#
-# Assign an id to a each unique series formula or title/axis formula. Repeated
-# formulas such as for categories get the same id. If the series or title
-# has user specified data associated with it then that is also stored. This
-# data is used to populate cached Excel data when creating a chart.
-# If there is no user defined data then it will be populated by the parent
-# workbook in Workbook::_add_chart_data()
-#
-sub _get_data_id {
-
-    my $self    = shift;
-    my $formula = shift;
-    my $data    = shift;
-    my $id;
-
-    # Ignore series without a range formula.
-    return unless $formula;
-
-    # Strip the leading '=' from the formula.
-    $formula =~ s/^=//;
-
-    # Store the data id in a hash keyed by the formula and store the data
-    # in a separate array with the same id.
-    if ( !exists $self->{_formula_ids}->{$formula} ) {
-
-        # Haven't seen this formula before.
-        $id = @{ $self->{_formula_data} };
-
-        push @{ $self->{_formula_data} }, $data;
-        $self->{_formula_ids}->{$formula} = $id;
-    }
-    else {
-
-        # Formula already seen. Return existing id.
-        $id = $self->{_formula_ids}->{$formula};
-
-        # Store user defined data if it isn't already there.
-        if ( !defined $self->{_formula_data}->[$id] ) {
-            $self->{_formula_data}->[$id] = $data;
-        }
-    }
-
-    return $id;
-}
-
-
-###############################################################################
-#
-# _get_color()
-#
-# Convert the user specified colour index or string to a rgb colour.
-#
-sub _get_color {
-
-    my $self  = shift;
-    my $color = shift;
-
-    # Convert a HTML style #RRGGBB color.
-    if ( defined $color and $color =~ /^#[0-9a-fA-F]{6}$/ ) {
-        $color =~ s/^#//;
-        return uc $color;
-    }
-
-    my $index = &Excel::Writer::XLSX::Format::_get_color( $color );
-
-    # Set undefined colors to black.
-    if ( !$index ) {
-        $index = 0x08;
-        warn "Unknown color '$color' used in chart formatting. "
-          . "Converting to black.\n";
-    }
-
-    return $self->_get_palette_color( $index );
-}
-
-
-###############################################################################
-#
-# _get_palette_color()
-#
-# Convert from an Excel internal colour index to a XML style #RRGGBB index
-# based on the default or user defined values in the Workbook palette.
-# Note: This version doesn't add an alpha channel.
-#
-sub _get_palette_color {
-
-    my $self    = shift;
-    my $index   = shift;
-    my $palette = $self->{_palette};
-
-    # Adjust the colour index.
-    $index -= 8;
-
-    # Palette is passed in from the Workbook class.
-    my @rgb = @{ $palette->[$index] };
-
-    return sprintf "%02X%02X%02X", @rgb[0, 1, 2];
-}
-
-
-###############################################################################
-#
-# _get_swe_line_pattern()
-#
-# Get the Spreadsheet::WriteExcel line pattern for backward compatibility.
-#
-sub _get_swe_line_pattern {
-
-    my $self    = shift;
-    my $value   = lc shift;
-    my $default = 'solid';
-    my $pattern;
-
-    my %patterns = (
-        0              => 'solid',
-        1              => 'dash',
-        2              => 'dot',
-        3              => 'dash_dot',
-        4              => 'long_dash_dot_dot',
-        5              => 'none',
-        6              => 'solid',
-        7              => 'solid',
-        8              => 'solid',
-        'solid'        => 'solid',
-        'dash'         => 'dash',
-        'dot'          => 'dot',
-        'dash-dot'     => 'dash_dot',
-        'dash-dot-dot' => 'long_dash_dot_dot',
-        'none'         => 'none',
-        'dark-gray'    => 'solid',
-        'medium-gray'  => 'solid',
-        'light-gray'   => 'solid',
-    );
-
-    if ( exists $patterns{$value} ) {
-        $pattern = $patterns{$value};
-    }
-    else {
-        $pattern = $default;
-    }
-
-    return $pattern;
-}
-
-
-###############################################################################
-#
-# _get_swe_line_weight()
-#
-# Get the Spreadsheet::WriteExcel line weight for backward compatibility.
-#
-sub _get_swe_line_weight {
-
-    my $self    = shift;
-    my $value   = lc shift;
-    my $default = 1;
-    my $weight;
-
-    my %weights = (
-        1          => 0.25,
-        2          => 1,
-        3          => 2,
-        4          => 3,
-        'hairline' => 0.25,
-        'narrow'   => 1,
-        'medium'   => 2,
-        'wide'     => 3,
-    );
-
-    if ( exists $weights{$value} ) {
-        $weight = $weights{$value};
-    }
-    else {
-        $weight = $default;
-    }
-
-    return $weight;
-}
-
-
-###############################################################################
-#
-# _get_line_properties()
-#
-# Convert user defined line properties to the structure required internally.
-#
-sub _get_line_properties {
-
-    my $self = shift;
-    my $line = shift;
-
-    return { _defined => 0 } unless $line;
-
-    # Copy the user supplied properties.
-    $line = { %$line };
-
-    my %dash_types = (
-        solid               => 'solid',
-        round_dot           => 'sysDot',
-        square_dot          => 'sysDash',
-        dash                => 'dash',
-        dash_dot            => 'dashDot',
-        long_dash           => 'lgDash',
-        long_dash_dot       => 'lgDashDot',
-        long_dash_dot_dot   => 'lgDashDotDot',
-        dot                 => 'dot',
-        system_dash_dot     => 'sysDashDot',
-        system_dash_dot_dot => 'sysDashDotDot',
-    );
-
-    # Check the dash type.
-    my $dash_type = $line->{dash_type};
-
-    if ( defined $dash_type ) {
-        if ( exists $dash_types{$dash_type} ) {
-            $line->{dash_type} = $dash_types{$dash_type};
-        }
-        else {
-            warn "Unknown dash type '$dash_type'\n";
-            return;
-        }
-    }
-
-    $line->{_defined} = 1;
-
-    return $line;
-}
-
-
-###############################################################################
-#
-# _get_fill_properties()
-#
-# Convert user defined fill properties to the structure required internally.
-#
-sub _get_fill_properties {
-
-    my $self = shift;
-    my $fill = shift;
-
-    return { _defined => 0 } unless $fill;
-
-    $fill->{_defined} = 1;
-
-    return $fill;
-}
-
-
-###############################################################################
-#
-# _get_pattern_properties()
-#
-# Convert user defined pattern properties to the structure required internally.
-#
-sub _get_pattern_properties {
-
-    my $self    = shift;
-    my $args    = shift;
-    my $pattern = {};
-
-    return unless $args;
-
-    # Check the pattern type is present.
-    if ( !$args->{pattern} ) {
-        carp "Pattern must include 'pattern'";
-        return;
-    }
-
-    # Check the foreground color is present.
-    if ( !$args->{fg_color} ) {
-        carp "Pattern must include 'fg_color'";
-        return;
-    }
-
-    my %types = (
-        'percent_5'                 => 'pct5',
-        'percent_10'                => 'pct10',
-        'percent_20'                => 'pct20',
-        'percent_25'                => 'pct25',
-        'percent_30'                => 'pct30',
-        'percent_40'                => 'pct40',
-
-        'percent_50'                => 'pct50',
-        'percent_60'                => 'pct60',
-        'percent_70'                => 'pct70',
-        'percent_75'                => 'pct75',
-        'percent_80'                => 'pct80',
-        'percent_90'                => 'pct90',
-
-        'light_downward_diagonal'   => 'ltDnDiag',
-        'light_upward_diagonal'     => 'ltUpDiag',
-        'dark_downward_diagonal'    => 'dkDnDiag',
-        'dark_upward_diagonal'      => 'dkUpDiag',
-        'wide_downward_diagonal'    => 'wdDnDiag',
-        'wide_upward_diagonal'      => 'wdUpDiag',
-
-        'light_vertical'            => 'ltVert',
-        'light_horizontal'          => 'ltHorz',
-        'narrow_vertical'           => 'narVert',
-        'narrow_horizontal'         => 'narHorz',
-        'dark_vertical'             => 'dkVert',
-        'dark_horizontal'           => 'dkHorz',
-
-        'dashed_downward_diagonal'  => 'dashDnDiag',
-        'dashed_upward_diagonal'    => 'dashUpDiag',
-        'dashed_horizontal'         => 'dashHorz',
-        'dashed_vertical'           => 'dashVert',
-        'small_confetti'            => 'smConfetti',
-        'large_confetti'            => 'lgConfetti',
-
-        'zigzag'                    => 'zigZag',
-        'wave'                      => 'wave',
-        'diagonal_brick'            => 'diagBrick',
-        'horizontal_brick'          => 'horzBrick',
-        'weave'                     => 'weave',
-        'plaid'                     => 'plaid',
-
-        'divot'                     => 'divot',
-        'dotted_grid'               => 'dotGrid',
-        'dotted_diamond'            => 'dotDmnd',
-        'shingle'                   => 'shingle',
-        'trellis'                   => 'trellis',
-        'sphere'                    => 'sphere',
-
-        'small_grid'                => 'smGrid',
-        'large_grid'                => 'lgGrid',
-        'small_check'               => 'smCheck',
-        'large_check'               => 'lgCheck',
-        'outlined_diamond'          => 'openDmnd',
-        'solid_diamond'             => 'solidDmnd',
-    );
-
-    # Check for valid types.
-    my $pattern_type = $args->{pattern};
-
-    if ( exists $types{$pattern_type} ) {
-        $pattern->{pattern} = $types{$pattern_type};
-    }
-    else {
-        carp "Unknown pattern type '$pattern_type'";
-        return;
-    }
-
-    # Specify a default background color.
-    if ( !$args->{bg_color} ) {
-        $pattern->{bg_color} = '#FFFFFF';
-    }
-    else {
-        $pattern->{bg_color} = $args->{bg_color};
-    }
-
-    $pattern->{fg_color} = $args->{fg_color};
-
-    return $pattern;
-}
-
-
-###############################################################################
-#
-# _get_gradient_properties()
-#
-# Convert user defined gradient to the structure required internally.
-#
-sub _get_gradient_properties {
-
-    my $self     = shift;
-    my $args     = shift;
-    my $gradient = {};
-
-    my %types    = (
-        linear      => 'linear',
-        radial      => 'circle',
-        rectangular => 'rect',
-        path        => 'shape'
-    );
-
-    return unless $args;
-
-    # Check the colors array exists and is valid.
-    if ( !$args->{colors} || ref $args->{colors} ne 'ARRAY' ) {
-        carp "Gradient must include colors array";
-        return;
-    }
-
-    # Check the colors array has the required number of entries.
-    if ( @{ $args->{colors} } < 2 ) {
-        carp "Gradient colors array must at least 2 values";
-        return;
-    }
-
-    $gradient->{_colors} = $args->{colors};
-
-    if ( $args->{positions} ) {
-
-        # Check the positions array has the right number of entries.
-        if ( @{ $args->{positions} } != @{ $args->{colors} } ) {
-            carp "Gradient positions not equal to number of colors";
-            return;
-        }
-
-        # Check the positions are in the correct range.
-        for my $pos ( @{ $args->{positions} } ) {
-            if ( $pos < 0 || $pos > 100 ) {
-                carp "Gradient position '", $pos,
-                  "' must be in range 0 <= pos <= 100";
-                return;
-            }
-        }
-
-        $gradient->{_positions} = $args->{positions};
-    }
-    else {
-        # Use the default gradient positions.
-        if ( @{ $args->{colors} } == 2 ) {
-            $gradient->{_positions} = [ 0, 100 ];
-        }
-        elsif ( @{ $args->{colors} } == 3 ) {
-            $gradient->{_positions} = [ 0, 50, 100 ];
-        }
-        elsif ( @{ $args->{colors} } == 4 ) {
-            $gradient->{_positions} = [ 0, 33, 66, 100 ];
-        }
-        else {
-            carp "Must specify gradient positions";
-            return;
-        }
-    }
-
-    # Set the gradient angle.
-    if ( defined $args->{angle} ) {
-        my $angle = $args->{angle};
-
-        if ( $angle < 0 || $angle > 359.9 ) {
-            carp "Gradient angle '", $angle,
-              "' must be in range 0 <= pos < 360";
-            return;
-        }
-        $gradient->{_angle} = $angle;
-    }
-    else {
-        $gradient->{_angle} = 90;
-    }
-
-    # Set the gradient type.
-    if ( defined $args->{type} ) {
-        my $type = $args->{type};
-
-        if ( !exists $types{$type} ) {
-            carp "Unknown gradient type '", $type, "'";
-            return;
-        }
-        $gradient->{_type} = $types{$type};
-    }
-    else {
-        $gradient->{_type} = 'linear';
-    }
-
-    return $gradient;
-}
-
-
-###############################################################################
-#
-# _get_marker_properties()
-#
-# Convert user defined marker properties to the structure required internally.
-#
-sub _get_marker_properties {
-
-    my $self   = shift;
-    my $marker = shift;
-
-    return if !$marker && ref $marker ne 'HASH';
-
-    # Copy the user supplied properties.
-    $marker = { %$marker };
-
-    my %types = (
-        automatic  => 'automatic',
-        none       => 'none',
-        square     => 'square',
-        diamond    => 'diamond',
-        triangle   => 'triangle',
-        x          => 'x',
-        star       => 'star',
-        dot        => 'dot',
-        short_dash => 'dot',
-        dash       => 'dash',
-        long_dash  => 'dash',
-        circle     => 'circle',
-        plus       => 'plus',
-        picture    => 'picture',
-    );
-
-    # Check for valid types.
-    my $marker_type = $marker->{type};
-
-    if ( defined $marker_type ) {
-        if ( $marker_type eq 'automatic' ) {
-            $marker->{automatic} = 1;
-        }
-
-        if ( exists $types{$marker_type} ) {
-            $marker->{type} = $types{$marker_type};
-        }
-        else {
-            warn "Unknown marker type '$marker_type'\n";
-            return;
-        }
-    }
-
-    # Set the line properties for the marker..
-    my $line = $self->_get_line_properties( $marker->{line} );
-
-    # Allow 'border' as a synonym for 'line'.
-    if ( $marker->{border} ) {
-        $line = $self->_get_line_properties( $marker->{border} );
-    }
-
-    # Set the fill properties for the marker.
-    my $fill = $self->_get_fill_properties( $marker->{fill} );
-
-    # Set the pattern properties for the series.
-    my $pattern = $self->_get_pattern_properties( $marker->{pattern} );
-
-    # Set the gradient fill properties for the series.
-    my $gradient = $self->_get_gradient_properties( $marker->{gradient} );
-
-    # Pattern fill overrides solid fill.
-    if ( $pattern ) {
-        $fill = undef;
-    }
-
-    # Gradient fill overrides solid and pattern fills.
-    if ( $gradient ) {
-        $pattern = undef;
-        $fill    = undef;
-    }
-
-    $marker->{_line}     = $line;
-    $marker->{_fill}     = $fill;
-    $marker->{_pattern}  = $pattern;
-    $marker->{_gradient} = $gradient;
-
-    return $marker;
-}
-
-
-###############################################################################
-#
-# _get_trendline_properties()
-#
-# Convert user defined trendline properties to the structure required
-# internally.
-#
-sub _get_trendline_properties {
-
-    my $self      = shift;
-    my $trendline = shift;
-
-    return if !$trendline && ref $trendline ne 'HASH';
-
-    # Copy the user supplied properties.
-    $trendline = { %$trendline };
-
-    my %types = (
-        exponential    => 'exp',
-        linear         => 'linear',
-        log            => 'log',
-        moving_average => 'movingAvg',
-        polynomial     => 'poly',
-        power          => 'power',
-    );
-
-    # Check the trendline type.
-    my $trend_type = $trendline->{type};
-
-    if ( exists $types{$trend_type} ) {
-        $trendline->{type} = $types{$trend_type};
-    }
-    else {
-        warn "Unknown trendline type '$trend_type'\n";
-        return;
-    }
-
-    # Set the line properties for the trendline..
-    my $line = $self->_get_line_properties( $trendline->{line} );
-
-    # Allow 'border' as a synonym for 'line'.
-    if ( $trendline->{border} ) {
-        $line = $self->_get_line_properties( $trendline->{border} );
-    }
-
-    # Set the fill properties for the trendline.
-    my $fill = $self->_get_fill_properties( $trendline->{fill} );
-
-    # Set the pattern properties for the series.
-    my $pattern = $self->_get_pattern_properties( $trendline->{pattern} );
-
-    # Set the gradient fill properties for the series.
-    my $gradient = $self->_get_gradient_properties( $trendline->{gradient} );
-
-    # Pattern fill overrides solid fill.
-    if ( $pattern ) {
-        $fill = undef;
-    }
-
-    # Gradient fill overrides solid and pattern fills.
-    if ( $gradient ) {
-        $pattern = undef;
-        $fill    = undef;
-    }
-
-    $trendline->{_line}     = $line;
-    $trendline->{_fill}     = $fill;
-    $trendline->{_pattern}  = $pattern;
-    $trendline->{_gradient} = $gradient;
-
-    return $trendline;
-}
-
-
-###############################################################################
-#
-# _get_error_bars_properties()
-#
-# Convert user defined error bars properties to structure required internally.
-#
-sub _get_error_bars_properties {
-
-    my $self = shift;
-    my $args = shift;
-
-    return if !$args && ref $args ne 'HASH';
-
-    # Copy the user supplied properties.
-    $args = { %$args };
-
-
-    # Default values.
-    my $error_bars = {
-        _type         => 'fixedVal',
-        _value        => 1,
-        _endcap       => 1,
-        _direction    => 'both',
-        _plus_values  => [1],
-        _minus_values => [1],
-        _plus_data    => [],
-        _minus_data   => [],
-    };
-
-    my %types = (
-        fixed              => 'fixedVal',
-        percentage         => 'percentage',
-        standard_deviation => 'stdDev',
-        standard_error     => 'stdErr',
-        custom             => 'cust',
-    );
-
-    # Check the error bars type.
-    my $error_type = $args->{type};
-
-    if ( exists $types{$error_type} ) {
-        $error_bars->{_type} = $types{$error_type};
-    }
-    else {
-        warn "Unknown error bars type '$error_type'\n";
-        return;
-    }
-
-    # Set the value for error types that require it.
-    if ( defined $args->{value} ) {
-        $error_bars->{_value} = $args->{value};
-    }
-
-    # Set the end-cap style.
-    if ( defined $args->{end_style} ) {
-        $error_bars->{_endcap} = $args->{end_style};
-    }
-
-    # Set the error bar direction.
-    if ( defined $args->{direction} ) {
-        if ( $args->{direction} eq 'minus' ) {
-            $error_bars->{_direction} = 'minus';
-        }
-        elsif ( $args->{direction} eq 'plus' ) {
-            $error_bars->{_direction} = 'plus';
-        }
-        else {
-            # Default to 'both'.
-        }
-    }
-
-    # Set any custom values.
-    if ( defined $args->{plus_values} ) {
-        $error_bars->{_plus_values} = $args->{plus_values};
-    }
-    if ( defined $args->{minus_values} ) {
-        $error_bars->{_minus_values} = $args->{minus_values};
-    }
-    if ( defined $args->{plus_data} ) {
-        $error_bars->{_plus_data} = $args->{plus_data};
-    }
-    if ( defined $args->{minus_data} ) {
-        $error_bars->{_minus_data} = $args->{minus_data};
-    }
-
-    # Set the line properties for the error bars.
-    $error_bars->{_line} = $self->_get_line_properties( $args->{line} );
-
-    return $error_bars;
-}
-
-
-###############################################################################
-#
-# _get_gridline_properties()
-#
-# Convert user defined gridline properties to the structure required internally.
-#
-sub _get_gridline_properties {
-
-    my $self = shift;
-    my $args = shift;
-    my $gridline;
-
-    # Set the visible property for the gridline.
-    $gridline->{_visible} = $args->{visible};
-
-    # Set the line properties for the gridline..
-    $gridline->{_line} = $self->_get_line_properties( $args->{line} );
-
-    return $gridline;
-}
-
-
-###############################################################################
-#
-# _get_labels_properties()
-#
-# Convert user defined labels properties to the structure required internally.
-#
-sub _get_labels_properties {
-
-    my $self   = shift;
-    my $labels = shift;
-
-    return if !$labels && ref $labels ne 'HASH';
-
-    # Copy the user supplied properties.
-    $labels = { %$labels };
-
-    # Map user defined label positions to Excel positions.
-    if ( my $position = $labels->{position} ) {
-
-        if ( exists $self->{_label_positions}->{$position} ) {
-            if ($position eq $self->{_label_position_default}) {
-                $labels->{position} = undef;
-            }
-            else {
-                $labels->{position} = $self->{_label_positions}->{$position};
-            }
-        }
-        else {
-            carp "Unsupported label position '$position' for this chart type";
-            return undef
-        }
-    }
-
-    # Map the user defined label separator to the Excel separator.
-    if ( my $separator = $labels->{separator} ) {
-
-        my %separators = (
-            ','  => ', ',
-            ';'  => '; ',
-            '.'  => '. ',
-            "\n" => "\n",
-            ' '  => ' '
-        );
-
-        if ( exists $separators{$separator} ) {
-            $labels->{separator} = $separators{$separator};
-        }
-        else {
-            carp "Unsupported label separator";
-            return undef
-        }
-    }
-
-    if ($labels->{font}) {
-        $labels->{font} = $self->_convert_font_args( $labels->{font} );
-    }
-
-    return $labels;
-}
-
-
-###############################################################################
-#
-# _get_area_properties()
-#
-# Convert user defined area properties to the structure required internally.
-#
-sub _get_area_properties {
-
-    my $self = shift;
-    my %arg  = @_;
-    my $area = {};
-
-
-    # Map deprecated Spreadsheet::WriteExcel fill colour.
-    if ( $arg{color} ) {
-        $arg{fill}->{color} = $arg{color};
-    }
-
-    # Map deprecated Spreadsheet::WriteExcel line_weight.
-    if ( $arg{line_weight} ) {
-        my $width = $self->_get_swe_line_weight( $arg{line_weight} );
-        $arg{border}->{width} = $width;
-    }
-
-    # Map deprecated Spreadsheet::WriteExcel line_pattern.
-    if ( $arg{line_pattern} ) {
-        my $pattern = $self->_get_swe_line_pattern( $arg{line_pattern} );
-
-        if ( $pattern eq 'none' ) {
-            $arg{border}->{none} = 1;
-        }
-        else {
-            $arg{border}->{dash_type} = $pattern;
-        }
-    }
-
-    # Map deprecated Spreadsheet::WriteExcel line colour.
-    if ( $arg{line_color} ) {
-        $arg{border}->{color} = $arg{line_color};
-    }
-
-
-    # Handle Excel::Writer::XLSX style properties.
-
-    # Set the line properties for the chartarea.
-    my $line = $self->_get_line_properties( $arg{line} );
-
-    # Allow 'border' as a synonym for 'line'.
-    if ( $arg{border} ) {
-        $line = $self->_get_line_properties( $arg{border} );
-    }
-
-    # Set the fill properties for the chartarea.
-    my $fill = $self->_get_fill_properties( $arg{fill} );
-
-    # Set the pattern properties for the series.
-    my $pattern = $self->_get_pattern_properties( $arg{pattern} );
-
-    # Set the gradient fill properties for the series.
-    my $gradient = $self->_get_gradient_properties( $arg{gradient} );
-
-    # Pattern fill overrides solid fill.
-    if ( $pattern ) {
-        $fill = undef;
-    }
-
-    # Gradient fill overrides solid and pattern fills.
-    if ( $gradient ) {
-        $pattern = undef;
-        $fill    = undef;
-    }
-
-    # Set the plotarea layout.
-    my $layout = $self->_get_layout_properties( $arg{layout} );
-
-    $area->{_line}     = $line;
-    $area->{_fill}     = $fill;
-    $area->{_pattern}  = $pattern;
-    $area->{_gradient} = $gradient;
-    $area->{_layout}   = $layout;
-
-    return $area;
-}
-
-
-###############################################################################
-#
-# _get_layout_properties()
-#
-# Convert user defined layout properties to the format required internally.
-#
-sub _get_layout_properties {
-
-    my $self    = shift;
-    my $args    = shift;
-    my $is_text = shift;
-    my $layout  = {};
-    my @properties;
-    my %allowable;
-
-    return if !$args;
-
-    if ( $is_text ) {
-        @properties = ( 'x', 'y' );
-    }
-    else {
-        @properties = ( 'x', 'y', 'width', 'height' );
-    }
-
-    # Check for valid properties.
-    @allowable{@properties} = undef;
-
-    for my $key ( keys %$args ) {
-
-        if ( !exists $allowable{$key} ) {
-            warn "Property '$key' not allowed in layout options\n";
-            return;
-        }
-    }
-
-    # Set the layout properties.
-    for my $property ( @properties ) {
-
-        if ( !exists $args->{$property} ) {
-            warn "Property '$property' must be specified in layout options\n";
-            return;
-        }
-
-        my $value = $args->{$property};
-
-        if ( $value !~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ ) {
-            warn "Property '$property' value '$value' must be numeric"
-              . " in layout options\n";
-            return;
-        }
-
-        if ( $value < 0 || $value > 1 ) {
-            warn "Property '$property' value '$value' must be in range "
-              . "0 < x <= 1 in layout options\n";
-            return;
-        }
-
-        # Convert to the format used by Excel for easier testing
-        $layout->{$property} = sprintf "%.17g", $value;
-    }
-
-    return $layout;
-}
-
-
-###############################################################################
-#
-# _get_points_properties()
-#
-# Convert user defined points properties to structure required internally.
-#
-sub _get_points_properties {
-
-    my $self        = shift;
-    my $user_points = shift;
-    my @points;
-
-    return unless $user_points;
-
-    for my $user_point ( @$user_points ) {
-
-        my $point;
-
-        if ( defined $user_point ) {
-
-            # Set the line properties for the point.
-            my $line = $self->_get_line_properties( $user_point->{line} );
-
-            # Allow 'border' as a synonym for 'line'.
-            if ( $user_point->{border} ) {
-                $line = $self->_get_line_properties( $user_point->{border} );
-            }
-
-            # Set the fill properties for the chartarea.
-            my $fill = $self->_get_fill_properties( $user_point->{fill} );
-
-
-            # Set the pattern properties for the series.
-            my $pattern =
-              $self->_get_pattern_properties( $user_point->{pattern} );
-
-            # Set the gradient fill properties for the series.
-            my $gradient =
-              $self->_get_gradient_properties( $user_point->{gradient} );
-
-            # Pattern fill overrides solid fill.
-            if ( $pattern ) {
-                $fill = undef;
-            }
-
-            # Gradient fill overrides solid and pattern fills.
-            if ( $gradient ) {
-                $pattern = undef;
-                $fill    = undef;
-            }
-                        # Gradient fill overrides solid fill.
-            if ( $gradient ) {
-                $fill = undef;
-            }
-
-            $point->{_line}     = $line;
-            $point->{_fill}     = $fill;
-            $point->{_pattern}  = $pattern;
-            $point->{_gradient} = $gradient;
-        }
-
-        push @points, $point;
-    }
-
-    return \@points;
-}
-
-
-###############################################################################
-#
-# _get_display_units()
-#
-# Convert user defined display units to internal units.
-#
-sub _get_display_units {
-
-    my $self          = shift;
-    my $display_units = shift;
-
-    return if !$display_units;
-
-    my %types = (
-        'hundreds'          => 'hundreds',
-        'thousands'         => 'thousands',
-        'ten_thousands'     => 'tenThousands',
-        'hundred_thousands' => 'hundredThousands',
-        'millions'          => 'millions',
-        'ten_millions'      => 'tenMillions',
-        'hundred_millions'  => 'hundredMillions',
-        'billions'          => 'billions',
-        'trillions'         => 'trillions',
-    );
-
-    if ( exists $types{$display_units} ) {
-        $display_units = $types{$display_units};
-    }
-    else {
-        warn "Unknown display_units type '$display_units'\n";
-        return;
-    }
-
-    return $display_units;
-}
-
-
-
-###############################################################################
-#
-# _get_tick_type()
-#
-# Convert user tick types to internal units.
-#
-sub _get_tick_type {
-
-    my $self      = shift;
-    my $tick_type = shift;
-
-    return if !$tick_type;
-
-    my %types = (
-        'outside' => 'out',
-        'inside'  => 'in',
-        'none'    => 'none',
-        'cross'   => 'cross',
-    );
-
-    if ( exists $types{$tick_type} ) {
-        $tick_type = $types{$tick_type};
-    }
-    else {
-        warn "Unknown tick_type type '$tick_type'\n";
-        return;
-    }
-
-    return $tick_type;
-}
-
-
-###############################################################################
-#
-# _get_primary_axes_series()
-#
-# Returns series which use the primary axes.
-#
-sub _get_primary_axes_series {
-
-    my $self = shift;
-    my @primary_axes_series;
-
-    for my $series ( @{ $self->{_series} } ) {
-        push @primary_axes_series, $series unless $series->{_y2_axis};
-    }
-
-    return @primary_axes_series;
-}
-
-
-###############################################################################
-#
-# _get_secondary_axes_series()
-#
-# Returns series which use the secondary axes.
-#
-sub _get_secondary_axes_series {
-
-    my $self = shift;
-    my @secondary_axes_series;
-
-    for my $series ( @{ $self->{_series} } ) {
-        push @secondary_axes_series, $series if $series->{_y2_axis};
-    }
-
-    return @secondary_axes_series;
-}
-
-
-###############################################################################
-#
-# _add_axis_ids()
-#
-# Add unique ids for primary or secondary axes
-#
-sub _add_axis_ids {
-
-    my $self       = shift;
-    my %args       = @_;
-    my $chart_id   = 5001 + $self->{_id};
-    my $axis_count = 1 + @{ $self->{_axis2_ids} } + @{ $self->{_axis_ids} };
-
-    my $id1 = sprintf '%04d%04d', $chart_id, $axis_count;
-    my $id2 = sprintf '%04d%04d', $chart_id, $axis_count + 1;
-
-    push @{ $self->{_axis_ids} },  $id1, $id2 if $args{primary_axes};
-    push @{ $self->{_axis2_ids} }, $id1, $id2 if !$args{primary_axes};
-}
-
-
-##############################################################################
-#
-# _get_font_style_attributes.
-#
-# Get the font style attributes from a font hashref.
-#
-sub _get_font_style_attributes {
-
-    my $self = shift;
-    my $font = shift;
-
-    return () unless $font;
-
-    my @attributes;
-    push @attributes, ( 'sz' => $font->{_size} )   if $font->{_size};
-    push @attributes, ( 'b'  => $font->{_bold} )   if defined $font->{_bold};
-    push @attributes, ( 'i'  => $font->{_italic} ) if defined $font->{_italic};
-    push @attributes, ( 'u' => 'sng' ) if defined $font->{_underline};
-
-    # Turn off baseline when testing fonts that don't have it.
-    if ($font->{_baseline} != -1) {
-        push @attributes, ( 'baseline' => $font->{_baseline} );
-    }
-
-    return @attributes;
-}
-
-
-##############################################################################
-#
-# _get_font_latin_attributes.
-#
-# Get the font latin attributes from a font hashref.
-#
-sub _get_font_latin_attributes {
-
-    my $self = shift;
-    my $font = shift;
-
-    return () unless $font;
-
-    my @attributes;
-    push @attributes, ( 'typeface' => $font->{_name} ) if $font->{_name};
-
-    push @attributes, ( 'pitchFamily' => $font->{_pitch_family} )
-      if defined $font->{_pitch_family};
-
-    push @attributes, ( 'charset' => $font->{_charset} )
-      if defined $font->{_charset};
-
-    return @attributes;
-}
-
-
-###############################################################################
-#
-# Config data.
-#
-###############################################################################
-
-###############################################################################
-#
-# _set_default_properties()
-#
-# Setup the default properties for a chart.
-#
-sub _set_default_properties {
-
-    my $self = shift;
-
-    # Set the default axis properties.
-    $self->{_x_axis}->{_defaults} = {
-        num_format      => 'General',
-        major_gridlines => { visible => 0 }
-    };
-
-    $self->{_y_axis}->{_defaults} = {
-        num_format      => 'General',
-        major_gridlines => { visible => 1 }
-    };
-
-    $self->{_x2_axis}->{_defaults} = {
-        num_format     => 'General',
-        label_position => 'none',
-        crossing       => 'max',
-        visible        => 0
-    };
-
-    $self->{_y2_axis}->{_defaults} = {
-        num_format      => 'General',
-        major_gridlines => { visible => 0 },
-        position        => 'right',
-        visible         => 1
-    };
-
-    $self->set_x_axis();
-    $self->set_y_axis();
-
-    $self->set_x2_axis();
-    $self->set_y2_axis();
-}
-
-
-###############################################################################
-#
-# _set_embedded_config_data()
-#
-# Setup the default configuration data for an embedded chart.
-#
-sub _set_embedded_config_data {
-
-    my $self = shift;
-
-    $self->{_embedded} = 1;
-}
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-##############################################################################
-#
-# _write_chart_space()
-#
-# Write the <c:chartSpace> element.
-#
-sub _write_chart_space {
-
-    my $self    = shift;
-    my $schema  = 'http://schemas.openxmlformats.org/';
-    my $xmlns_c = $schema . 'drawingml/2006/chart';
-    my $xmlns_a = $schema . 'drawingml/2006/main';
-    my $xmlns_r = $schema . 'officeDocument/2006/relationships';
-
-    my @attributes = (
-        'xmlns:c' => $xmlns_c,
-        'xmlns:a' => $xmlns_a,
-        'xmlns:r' => $xmlns_r,
-    );
-
-    $self->xml_start_tag( 'c:chartSpace', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_lang()
-#
-# Write the <c:lang> element.
-#
-sub _write_lang {
-
-    my $self = shift;
-    my $val  = 'en-US';
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:lang', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_style()
-#
-# Write the <c:style> element.
-#
-sub _write_style {
-
-    my $self     = shift;
-    my $style_id = $self->{_style_id};
-
-    # Don't write an element for the default style, 2.
-    return if $style_id == 2;
-
-    my @attributes = ( 'val' => $style_id );
-
-    $self->xml_empty_tag( 'c:style', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_chart()
-#
-# Write the <c:chart> element.
-#
-sub _write_chart {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'c:chart' );
-
-    # Write the chart title elements.
-
-    if ( $self->{_title_none} ) {
-
-        # Turn off the title.
-        $self->_write_auto_title_deleted();
-    }
-    else {
-        my $title;
-        if ( $title = $self->{_title_formula} ) {
-            $self->_write_title_formula(
-
-                $title,
-                $self->{_title_data_id},
-                undef,
-                $self->{_title_font},
-                $self->{_title_layout},
-                $self->{_title_overlay}
-            );
-        }
-        elsif ( $title = $self->{_title_name} ) {
-            $self->_write_title_rich(
-
-                $title,
-                undef,
-                $self->{_title_font},
-                $self->{_title_layout},
-                $self->{_title_overlay}
-            );
-        }
-    }
-
-    # Write the c:plotArea element.
-    $self->_write_plot_area();
-
-    # Write the c:legend element.
-    $self->_write_legend();
-
-    # Write the c:plotVisOnly element.
-    $self->_write_plot_vis_only();
-
-    # Write the c:dispBlanksAs element.
-    $self->_write_disp_blanks_as();
-
-    $self->xml_end_tag( 'c:chart' );
-}
-
-
-##############################################################################
-#
-# _write_disp_blanks_as()
-#
-# Write the <c:dispBlanksAs> element.
-#
-sub _write_disp_blanks_as {
-
-    my $self = shift;
-    my $val  = $self->{_show_blanks};
-
-    # Ignore the default value.
-    return if $val eq 'gap';
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:dispBlanksAs', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_plot_area()
-#
-# Write the <c:plotArea> element.
-#
-sub _write_plot_area {
-
-    my $self = shift;
-    my $second_chart = $self->{_combined};
-
-    $self->xml_start_tag( 'c:plotArea' );
-
-    # Write the c:layout element.
-    $self->_write_layout( $self->{_plotarea}->{_layout}, 'plot' );
-
-    # Write the subclass chart type elements for primary and secondary axes.
-    $self->_write_chart_type( primary_axes => 1 );
-    $self->_write_chart_type( primary_axes => 0 );
-
-
-    # Configure a combined chart if present.
-    if ( $second_chart ) {
-
-        # Secondary axis has unique id otherwise use same as primary.
-        if ( $second_chart->{_is_secondary} ) {
-            $second_chart->{_id} = 1000 + $self->{_id};
-        }
-        else {
-            $second_chart->{_id} = $self->{_id};
-        }
-
-        # Shart the same filehandle for writing.
-        $second_chart->{_fh} = $self->{_fh};
-
-        # Share series index with primary chart.
-        $second_chart->{_series_index} = $self->{_series_index};
-
-        # Write the subclass chart type elements for combined chart.
-        $second_chart->_write_chart_type( primary_axes => 1 );
-        $second_chart->_write_chart_type( primary_axes => 0 );
-    }
-
-    # Write the category and value elements for the primary axes.
-    my @args = (
-        x_axis   => $self->{_x_axis},
-        y_axis   => $self->{_y_axis},
-        axis_ids => $self->{_axis_ids}
-    );
-
-    if ( $self->{_date_category} ) {
-        $self->_write_date_axis( @args );
-    }
-    else {
-        $self->_write_cat_axis( @args );
-    }
-
-    $self->_write_val_axis( @args );
-
-    # Write the category and value elements for the secondary axes.
-    @args = (
-        x_axis   => $self->{_x2_axis},
-        y_axis   => $self->{_y2_axis},
-        axis_ids => $self->{_axis2_ids}
-    );
-
-    $self->_write_val_axis( @args );
-
-    # Write the secondary axis for the secondary chart.
-    if ( $second_chart && $second_chart->{_is_secondary} ) {
-
-        @args = (
-             x_axis   => $second_chart->{_x2_axis},
-             y_axis   => $second_chart->{_y2_axis},
-             axis_ids => $second_chart->{_axis2_ids}
-            );
-
-        $second_chart->_write_val_axis( @args );
-    }
-
-
-    if ( $self->{_date_category} ) {
-        $self->_write_date_axis( @args );
-    }
-    else {
-        $self->_write_cat_axis( @args );
-    }
-
-    # Write the c:dTable element.
-    $self->_write_d_table();
-
-    # Write the c:spPr element for the plotarea formatting.
-    $self->_write_sp_pr( $self->{_plotarea} );
-
-    $self->xml_end_tag( 'c:plotArea' );
-}
-
-
-##############################################################################
-#
-# _write_layout()
-#
-# Write the <c:layout> element.
-#
-sub _write_layout {
-
-    my $self   = shift;
-    my $layout = shift;
-    my $type   = shift;
-
-    if ( !$layout ) {
-        # Automatic layout.
-        $self->xml_empty_tag( 'c:layout' );
-    }
-    else {
-        # User defined manual layout.
-        $self->xml_start_tag( 'c:layout' );
-        $self->_write_manual_layout( $layout, $type );
-        $self->xml_end_tag( 'c:layout' );
-    }
-}
-
-##############################################################################
-#
-# _write_manual_layout()
-#
-# Write the <c:manualLayout> element.
-#
-sub _write_manual_layout {
-
-    my $self   = shift;
-    my $layout = shift;
-    my $type   = shift;
-
-    $self->xml_start_tag( 'c:manualLayout' );
-
-    # Plotarea has a layoutTarget element.
-    if ( $type eq 'plot' ) {
-        $self->xml_empty_tag( 'c:layoutTarget', ( 'val' => 'inner' ) );
-    }
-
-    # Set the x, y positions.
-    $self->xml_empty_tag( 'c:xMode', ( 'val' => 'edge' ) );
-    $self->xml_empty_tag( 'c:yMode', ( 'val' => 'edge' ) );
-    $self->xml_empty_tag( 'c:x', ( 'val' => $layout->{x} ) );
-    $self->xml_empty_tag( 'c:y', ( 'val' => $layout->{y} ) );
-
-    # For plotarea and legend set the width and height.
-    if ( $type ne 'text' ) {
-        $self->xml_empty_tag( 'c:w', ( 'val' => $layout->{width} ) );
-        $self->xml_empty_tag( 'c:h', ( 'val' => $layout->{height} ) );
-    }
-
-    $self->xml_end_tag( 'c:manualLayout' );
-}
-
-##############################################################################
-#
-# _write_chart_type()
-#
-# Write the chart type element. This method should be overridden by the
-# subclasses.
-#
-sub _write_chart_type {
-
-    my $self = shift;
-}
-
-
-##############################################################################
-#
-# _write_grouping()
-#
-# Write the <c:grouping> element.
-#
-sub _write_grouping {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:grouping', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_series()
-#
-# Write the series elements.
-#
-sub _write_series {
-
-    my $self   = shift;
-    my $series = shift;
-
-    $self->_write_ser( $series );
-}
-
-
-##############################################################################
-#
-# _write_ser()
-#
-# Write the <c:ser> element.
-#
-sub _write_ser {
-
-    my $self   = shift;
-    my $series = shift;
-    my $index  = $self->{_series_index}++;
-
-    $self->xml_start_tag( 'c:ser' );
-
-    # Write the c:idx element.
-    $self->_write_idx( $index );
-
-    # Write the c:order element.
-    $self->_write_order( $index );
-
-    # Write the series name.
-    $self->_write_series_name( $series );
-
-    # Write the c:spPr element.
-    $self->_write_sp_pr( $series );
-
-    # Write the c:marker element.
-    $self->_write_marker( $series->{_marker} );
-
-    # Write the c:invertIfNegative element.
-    $self->_write_c_invert_if_negative( $series->{_invert_if_neg} );
-
-    # Write the c:dPt element.
-    $self->_write_d_pt( $series->{_points} );
-
-    # Write the c:dLbls element.
-    $self->_write_d_lbls( $series->{_labels} );
-
-    # Write the c:trendline element.
-    $self->_write_trendline( $series->{_trendline} );
-
-    # Write the c:errBars element.
-    $self->_write_error_bars( $series->{_error_bars} );
-
-    # Write the c:cat element.
-    $self->_write_cat( $series );
-
-    # Write the c:val element.
-    $self->_write_val( $series );
-
-    # Write the c:smooth element.
-    if ( $self->{_smooth_allowed} ) {
-        $self->_write_c_smooth( $series->{_smooth} );
-    }
-
-    $self->xml_end_tag( 'c:ser' );
-}
-
-
-##############################################################################
-#
-# _write_idx()
-#
-# Write the <c:idx> element.
-#
-sub _write_idx {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:idx', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_order()
-#
-# Write the <c:order> element.
-#
-sub _write_order {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:order', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_series_name()
-#
-# Write the series name.
-#
-sub _write_series_name {
-
-    my $self   = shift;
-    my $series = shift;
-
-    my $name;
-    if ( $name = $series->{_name_formula} ) {
-        $self->_write_tx_formula( $name, $series->{_name_id} );
-    }
-    elsif ( $name = $series->{_name} ) {
-        $self->_write_tx_value( $name );
-    }
-
-}
-
-
-##############################################################################
-#
-# _write_cat()
-#
-# Write the <c:cat> element.
-#
-sub _write_cat {
-
-    my $self    = shift;
-    my $series  = shift;
-    my $formula = $series->{_categories};
-    my $data_id = $series->{_cat_data_id};
-    my $data;
-
-    if ( defined $data_id ) {
-        $data = $self->{_formula_data}->[$data_id];
-    }
-
-    # Ignore <c:cat> elements for charts without category values.
-    return unless $formula;
-
-    $self->xml_start_tag( 'c:cat' );
-
-    # Check the type of cached data.
-    my $type = $self->_get_data_type( $data );
-
-    if ( $type eq 'str' ) {
-
-        $self->{_cat_has_num_fmt} = 0;
-
-        # Write the c:numRef element.
-        $self->_write_str_ref( $formula, $data, $type );
-    }
-    elsif ( $type eq 'multi_str') {
-
-        $self->{_cat_has_num_fmt} = 0;
-
-        # Write the c:multiLvLStrRef element.
-        $self->_write_multi_lvl_str_ref( $formula, $data );
-    }
-    else {
-
-        $self->{_cat_has_num_fmt} = 1;
-
-        # Write the c:numRef element.
-        $self->_write_num_ref( $formula, $data, $type );
-    }
-
-
-    $self->xml_end_tag( 'c:cat' );
-}
-
-
-##############################################################################
-#
-# _write_val()
-#
-# Write the <c:val> element.
-#
-sub _write_val {
-
-    my $self    = shift;
-    my $series  = shift;
-    my $formula = $series->{_values};
-    my $data_id = $series->{_val_data_id};
-    my $data    = $self->{_formula_data}->[$data_id];
-
-    $self->xml_start_tag( 'c:val' );
-
-    # Unlike Cat axes data should only be numeric.
-
-    # Write the c:numRef element.
-    $self->_write_num_ref( $formula, $data, 'num' );
-
-    $self->xml_end_tag( 'c:val' );
-}
-
-
-##############################################################################
-#
-# _write_num_ref()
-#
-# Write the <c:numRef> element.
-#
-sub _write_num_ref {
-
-    my $self    = shift;
-    my $formula = shift;
-    my $data    = shift;
-    my $type    = shift;
-
-    $self->xml_start_tag( 'c:numRef' );
-
-    # Write the c:f element.
-    $self->_write_series_formula( $formula );
-
-    if ( $type eq 'num' ) {
-
-        # Write the c:numCache element.
-        $self->_write_num_cache( $data );
-    }
-    elsif ( $type eq 'str' ) {
-
-        # Write the c:strCache element.
-        $self->_write_str_cache( $data );
-    }
-
-    $self->xml_end_tag( 'c:numRef' );
-}
-
-
-##############################################################################
-#
-# _write_str_ref()
-#
-# Write the <c:strRef> element.
-#
-sub _write_str_ref {
-
-    my $self    = shift;
-    my $formula = shift;
-    my $data    = shift;
-    my $type    = shift;
-
-    $self->xml_start_tag( 'c:strRef' );
-
-    # Write the c:f element.
-    $self->_write_series_formula( $formula );
-
-    if ( $type eq 'num' ) {
-
-        # Write the c:numCache element.
-        $self->_write_num_cache( $data );
-    }
-    elsif ( $type eq 'str' ) {
-
-        # Write the c:strCache element.
-        $self->_write_str_cache( $data );
-    }
-
-    $self->xml_end_tag( 'c:strRef' );
-}
-
-
-##############################################################################
-#
-# _write_multi_lvl_str_ref()
-#
-# Write the <c:multiLvLStrRef> element.
-#
-sub _write_multi_lvl_str_ref {
-
-    my $self    = shift;
-    my $formula = shift;
-    my $data    = shift;
-    my $count   = @$data;
-
-    return if !$count;
-
-    $self->xml_start_tag( 'c:multiLvlStrRef' );
-
-    # Write the c:f element.
-    $self->_write_series_formula( $formula );
-
-    $self->xml_start_tag( 'c:multiLvlStrCache' );
-
-    # Write the c:ptCount element.
-    $count = @{ $data->[-1] };
-    $self->_write_pt_count( $count );
-
-    # Write the data arrays in reverse order.
-    for my $aref ( reverse @$data ) {
-        $self->xml_start_tag( 'c:lvl' );
-
-        for my $i ( 0 .. @$aref - 1 ) {
-            # Write the c:pt element.
-            $self->_write_pt( $i, $aref->[$i] );
-        }
-
-        $self->xml_end_tag( 'c:lvl' );
-    }
-
-    $self->xml_end_tag( 'c:multiLvlStrCache' );
-
-    $self->xml_end_tag( 'c:multiLvlStrRef' );
-}
-
-
-##############################################################################
-#
-# _write_series_formula()
-#
-# Write the <c:f> element.
-#
-sub _write_series_formula {
-
-    my $self    = shift;
-    my $formula = shift;
-
-    # Strip the leading '=' from the formula.
-    $formula =~ s/^=//;
-
-    $self->xml_data_element( 'c:f', $formula );
-}
-
-
-##############################################################################
-#
-# _write_axis_ids()
-#
-# Write the <c:axId> elements for the primary or secondary axes.
-#
-sub _write_axis_ids {
-
-    my $self = shift;
-    my %args = @_;
-
-    # Generate the axis ids.
-    $self->_add_axis_ids( %args );
-
-    if ( $args{primary_axes} ) {
-
-        # Write the axis ids for the primary axes.
-        $self->_write_axis_id( $self->{_axis_ids}->[0] );
-        $self->_write_axis_id( $self->{_axis_ids}->[1] );
-    }
-    else {
-        # Write the axis ids for the secondary axes.
-        $self->_write_axis_id( $self->{_axis2_ids}->[0] );
-        $self->_write_axis_id( $self->{_axis2_ids}->[1] );
-    }
-}
-
-
-##############################################################################
-#
-# _write_axis_id()
-#
-# Write the <c:axId> element.
-#
-sub _write_axis_id {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:axId', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_cat_axis()
-#
-# Write the <c:catAx> element. Usually the X axis.
-#
-sub _write_cat_axis {
-
-    my $self     = shift;
-    my %args     = @_;
-    my $x_axis   = $args{x_axis};
-    my $y_axis   = $args{y_axis};
-    my $axis_ids = $args{axis_ids};
-
-    # if there are no axis_ids then we don't need to write this element
-    return unless $axis_ids;
-    return unless scalar @$axis_ids;
-
-    my $position = $self->{_cat_axis_position};
-    my $horiz    = $self->{_horiz_cat_axis};
-
-    # Overwrite the default axis position with a user supplied value.
-    $position = $x_axis->{_position} || $position;
-
-    $self->xml_start_tag( 'c:catAx' );
-
-    $self->_write_axis_id( $axis_ids->[0] );
-
-    # Write the c:scaling element.
-    $self->_write_scaling( $x_axis->{_reverse} );
-
-    $self->_write_delete( 1 ) unless $x_axis->{_visible};
-
-    # Write the c:axPos element.
-    $self->_write_axis_pos( $position, $y_axis->{_reverse} );
-
-    # Write the c:majorGridlines element.
-    $self->_write_major_gridlines( $x_axis->{_major_gridlines} );
-
-    # Write the c:minorGridlines element.
-    $self->_write_minor_gridlines( $x_axis->{_minor_gridlines} );
-
-    # Write the axis title elements.
-    my $title;
-    if ( $title = $x_axis->{_formula} ) {
-
-        $self->_write_title_formula( $title, $x_axis->{_data_id}, $horiz,
-            $x_axis->{_name_font}, $x_axis->{_layout} );
-    }
-    elsif ( $title = $x_axis->{_name} ) {
-        $self->_write_title_rich( $title, $horiz, $x_axis->{_name_font},
-            $x_axis->{_layout} );
-    }
-
-    # Write the c:numFmt element.
-    $self->_write_cat_number_format( $x_axis );
-
-    # Write the c:majorTickMark element.
-    $self->_write_major_tick_mark( $x_axis->{_major_tick_mark} );
-
-    # Write the c:minorTickMark element.
-    $self->_write_minor_tick_mark( $x_axis->{_minor_tick_mark} );
-
-    # Write the c:tickLblPos element.
-    $self->_write_tick_label_pos( $x_axis->{_label_position} );
-
-    # Write the c:spPr element for the axis line.
-    $self->_write_sp_pr( $x_axis );
-
-    # Write the axis font elements.
-    $self->_write_axis_font( $x_axis->{_num_font} );
-
-    # Write the c:crossAx element.
-    $self->_write_cross_axis( $axis_ids->[1] );
-
-    if ( $self->{_show_crosses} || $x_axis->{_visible} ) {
-
-        # Note, the category crossing comes from the value axis.
-        if ( !defined $y_axis->{_crossing} || $y_axis->{_crossing} eq 'max' ) {
-
-            # Write the c:crosses element.
-            $self->_write_crosses( $y_axis->{_crossing} );
-        }
-        else {
-
-            # Write the c:crossesAt element.
-            $self->_write_c_crosses_at( $y_axis->{_crossing} );
-        }
-    }
-
-    # Write the c:auto element.
-    if (!$x_axis->{_text_axis}) {
-        $self->_write_auto( 1 );
-    }
-
-    # Write the c:labelAlign element.
-    $self->_write_label_align( 'ctr' );
-
-    # Write the c:labelOffset element.
-    $self->_write_label_offset( 100 );
-
-    # Write the c:tickLblSkip element.
-    $self->_write_tick_lbl_skip( $x_axis->{_interval_unit} );
-
-    # Write the c:tickMarkSkip element.
-    $self->_write_tick_mark_skip( $x_axis->{_interval_tick} );
-
-    $self->xml_end_tag( 'c:catAx' );
-}
-
-
-##############################################################################
-#
-# _write_val_axis()
-#
-# Write the <c:valAx> element. Usually the Y axis.
-#
-# TODO. Maybe should have a _write_cat_val_axis() method as well for scatter.
-#
-sub _write_val_axis {
-
-    my $self     = shift;
-    my %args     = @_;
-    my $x_axis   = $args{x_axis};
-    my $y_axis   = $args{y_axis};
-    my $axis_ids = $args{axis_ids};
-    my $position = $args{position} || $self->{_val_axis_position};
-    my $horiz    = $self->{_horiz_val_axis};
-
-    return unless $axis_ids && scalar @$axis_ids;
-
-    # Overwrite the default axis position with a user supplied value.
-    $position = $y_axis->{_position} || $position;
-
-    $self->xml_start_tag( 'c:valAx' );
-
-    $self->_write_axis_id( $axis_ids->[1] );
-
-    # Write the c:scaling element.
-    $self->_write_scaling(
-        $y_axis->{_reverse}, $y_axis->{_min},
-        $y_axis->{_max},     $y_axis->{_log_base}
-    );
-
-    $self->_write_delete( 1 ) unless $y_axis->{_visible};
-
-    # Write the c:axPos element.
-    $self->_write_axis_pos( $position, $x_axis->{_reverse} );
-
-    # Write the c:majorGridlines element.
-    $self->_write_major_gridlines( $y_axis->{_major_gridlines} );
-
-    # Write the c:minorGridlines element.
-    $self->_write_minor_gridlines( $y_axis->{_minor_gridlines} );
-
-    # Write the axis title elements.
-    my $title;
-    if ( $title = $y_axis->{_formula} ) {
-        $self->_write_title_formula( $title, $y_axis->{_data_id}, $horiz,
-            $y_axis->{_name_font}, $y_axis->{_layout} );
-    }
-    elsif ( $title = $y_axis->{_name} ) {
-        $self->_write_title_rich( $title, $horiz, $y_axis->{_name_font},
-            $y_axis->{_layout} );
-    }
-
-    # Write the c:numberFormat element.
-    $self->_write_number_format( $y_axis );
-
-    # Write the c:majorTickMark element.
-    $self->_write_major_tick_mark( $y_axis->{_major_tick_mark} );
-
-    # Write the c:minorTickMark element.
-    $self->_write_minor_tick_mark( $y_axis->{_minor_tick_mark} );
-
-    # Write the c:tickLblPos element.
-    $self->_write_tick_label_pos( $y_axis->{_label_position} );
-
-    # Write the c:spPr element for the axis line.
-    $self->_write_sp_pr( $y_axis );
-
-    # Write the axis font elements.
-    $self->_write_axis_font( $y_axis->{_num_font} );
-
-    # Write the c:crossAx element.
-    $self->_write_cross_axis( $axis_ids->[0] );
-
-    # Note, the category crossing comes from the value axis.
-    if ( !defined $x_axis->{_crossing} || $x_axis->{_crossing} eq 'max' ) {
-
-        # Write the c:crosses element.
-        $self->_write_crosses( $x_axis->{_crossing} );
-    }
-    else {
-
-        # Write the c:crossesAt element.
-        $self->_write_c_crosses_at( $x_axis->{_crossing} );
-    }
-
-    # Write the c:crossBetween element.
-    $self->_write_cross_between( $x_axis->{_position_axis} );
-
-    # Write the c:majorUnit element.
-    $self->_write_c_major_unit( $y_axis->{_major_unit} );
-
-    # Write the c:minorUnit element.
-    $self->_write_c_minor_unit( $y_axis->{_minor_unit} );
-
-    # Write the c:dispUnits element.
-    $self->_write_disp_units( $y_axis->{_display_units},
-        $y_axis->{_display_units_visible} );
-
-    $self->xml_end_tag( 'c:valAx' );
-}
-
-
-##############################################################################
-#
-# _write_cat_val_axis()
-#
-# Write the <c:valAx> element. This is for the second valAx in scatter plots.
-# Usually the X axis.
-#
-sub _write_cat_val_axis {
-
-    my $self     = shift;
-    my %args     = @_;
-    my $x_axis   = $args{x_axis};
-    my $y_axis   = $args{y_axis};
-    my $axis_ids = $args{axis_ids};
-    my $position = $args{position} || $self->{_val_axis_position};
-    my $horiz    = $self->{_horiz_val_axis};
-
-    return unless $axis_ids && scalar @$axis_ids;
-
-    # Overwrite the default axis position with a user supplied value.
-    $position = $x_axis->{_position} || $position;
-
-    $self->xml_start_tag( 'c:valAx' );
-
-    $self->_write_axis_id( $axis_ids->[0] );
-
-    # Write the c:scaling element.
-    $self->_write_scaling(
-        $x_axis->{_reverse}, $x_axis->{_min},
-        $x_axis->{_max},     $x_axis->{_log_base}
-    );
-
-    $self->_write_delete( 1 ) unless $x_axis->{_visible};
-
-    # Write the c:axPos element.
-    $self->_write_axis_pos( $position, $y_axis->{_reverse} );
-
-    # Write the c:majorGridlines element.
-    $self->_write_major_gridlines( $x_axis->{_major_gridlines} );
-
-    # Write the c:minorGridlines element.
-    $self->_write_minor_gridlines( $x_axis->{_minor_gridlines} );
-
-    # Write the axis title elements.
-    my $title;
-    if ( $title = $x_axis->{_formula} ) {
-        $self->_write_title_formula( $title, $x_axis->{_data_id}, $horiz,
-            $x_axis->{_name_font}, $x_axis->{_layout} );
-    }
-    elsif ( $title = $x_axis->{_name} ) {
-        $self->_write_title_rich( $title, $horiz, $x_axis->{_name_font},
-            $x_axis->{_layout} );
-    }
-
-    # Write the c:numberFormat element.
-    $self->_write_number_format( $x_axis );
-
-    # Write the c:majorTickMark element.
-    $self->_write_major_tick_mark( $x_axis->{_major_tick_mark} );
-
-    # Write the c:minorTickMark element.
-    $self->_write_minor_tick_mark( $x_axis->{_minor_tick_mark} );
-
-    # Write the c:tickLblPos element.
-    $self->_write_tick_label_pos( $x_axis->{_label_position} );
-
-    # Write the c:spPr element for the axis line.
-    $self->_write_sp_pr( $x_axis );
-
-    # Write the axis font elements.
-    $self->_write_axis_font( $x_axis->{_num_font} );
-
-    # Write the c:crossAx element.
-    $self->_write_cross_axis( $axis_ids->[1] );
-
-    # Note, the category crossing comes from the value axis.
-    if ( !defined $y_axis->{_crossing} || $y_axis->{_crossing} eq 'max' ) {
-
-        # Write the c:crosses element.
-        $self->_write_crosses( $y_axis->{_crossing} );
-    }
-    else {
-
-        # Write the c:crossesAt element.
-        $self->_write_c_crosses_at( $y_axis->{_crossing} );
-    }
-
-    # Write the c:crossBetween element.
-    $self->_write_cross_between( $y_axis->{_position_axis} );
-
-    # Write the c:majorUnit element.
-    $self->_write_c_major_unit( $x_axis->{_major_unit} );
-
-    # Write the c:minorUnit element.
-    $self->_write_c_minor_unit( $x_axis->{_minor_unit} );
-
-    # Write the c:dispUnits element.
-    $self->_write_disp_units( $x_axis->{_display_units},
-        $x_axis->{_display_units_visible} );
-
-    $self->xml_end_tag( 'c:valAx' );
-}
-
-
-##############################################################################
-#
-# _write_date_axis()
-#
-# Write the <c:dateAx> element. Usually the X axis.
-#
-sub _write_date_axis {
-
-    my $self     = shift;
-    my %args     = @_;
-    my $x_axis   = $args{x_axis};
-    my $y_axis   = $args{y_axis};
-    my $axis_ids = $args{axis_ids};
-
-    return unless $axis_ids && scalar @$axis_ids;
-
-    my $position = $self->{_cat_axis_position};
-
-    # Overwrite the default axis position with a user supplied value.
-    $position = $x_axis->{_position} || $position;
-
-    $self->xml_start_tag( 'c:dateAx' );
-
-    $self->_write_axis_id( $axis_ids->[0] );
-
-    # Write the c:scaling element.
-    $self->_write_scaling(
-        $x_axis->{_reverse}, $x_axis->{_min},
-        $x_axis->{_max},     $x_axis->{_log_base}
-    );
-
-    $self->_write_delete( 1 ) unless $x_axis->{_visible};
-
-    # Write the c:axPos element.
-    $self->_write_axis_pos( $position, $y_axis->{_reverse} );
-
-    # Write the c:majorGridlines element.
-    $self->_write_major_gridlines( $x_axis->{_major_gridlines} );
-
-    # Write the c:minorGridlines element.
-    $self->_write_minor_gridlines( $x_axis->{_minor_gridlines} );
-
-    # Write the axis title elements.
-    my $title;
-    if ( $title = $x_axis->{_formula} ) {
-        $self->_write_title_formula( $title, $x_axis->{_data_id}, undef,
-            $x_axis->{_name_font}, $x_axis->{_layout} );
-    }
-    elsif ( $title = $x_axis->{_name} ) {
-        $self->_write_title_rich( $title, undef, $x_axis->{_name_font},
-            $x_axis->{_layout} );
-    }
-
-    # Write the c:numFmt element.
-    $self->_write_number_format( $x_axis );
-
-    # Write the c:majorTickMark element.
-    $self->_write_major_tick_mark( $x_axis->{_major_tick_mark} );
-
-    # Write the c:minorTickMark element.
-    $self->_write_minor_tick_mark( $x_axis->{_minor_tick_mark} );
-
-    # Write the c:tickLblPos element.
-    $self->_write_tick_label_pos( $x_axis->{_label_position} );
-
-    # Write the c:spPr element for the axis line.
-    $self->_write_sp_pr( $x_axis );
-
-    # Write the axis font elements.
-    $self->_write_axis_font( $x_axis->{_num_font} );
-
-    # Write the c:crossAx element.
-    $self->_write_cross_axis( $axis_ids->[1] );
-
-    if ( $self->{_show_crosses} || $x_axis->{_visible} ) {
-
-        # Note, the category crossing comes from the value axis.
-        if ( !defined $y_axis->{_crossing} || $y_axis->{_crossing} eq 'max' ) {
-
-            # Write the c:crosses element.
-            $self->_write_crosses( $y_axis->{_crossing} );
-        }
-        else {
-
-            # Write the c:crossesAt element.
-            $self->_write_c_crosses_at( $y_axis->{_crossing} );
-        }
-    }
-
-    # Write the c:auto element.
-    $self->_write_auto( 1 );
-
-    # Write the c:labelOffset element.
-    $self->_write_label_offset( 100 );
-
-    # Write the c:tickLblSkip element.
-    $self->_write_tick_lbl_skip( $x_axis->{_interval_unit} );
-
-    # Write the c:tickMarkSkip element.
-    $self->_write_tick_mark_skip( $x_axis->{_interval_tick} );
-
-    # Write the c:majorUnit element.
-    $self->_write_c_major_unit( $x_axis->{_major_unit} );
-
-    # Write the c:majorTimeUnit element.
-    if ( defined $x_axis->{_major_unit} ) {
-        $self->_write_c_major_time_unit( $x_axis->{_major_unit_type} );
-    }
-
-    # Write the c:minorUnit element.
-    $self->_write_c_minor_unit( $x_axis->{_minor_unit} );
-
-    # Write the c:minorTimeUnit element.
-    if ( defined $x_axis->{_minor_unit} ) {
-        $self->_write_c_minor_time_unit( $x_axis->{_minor_unit_type} );
-    }
-
-    $self->xml_end_tag( 'c:dateAx' );
-}
-
-
-##############################################################################
-#
-# _write_scaling()
-#
-# Write the <c:scaling> element.
-#
-sub _write_scaling {
-
-    my $self     = shift;
-    my $reverse  = shift;
-    my $min      = shift;
-    my $max      = shift;
-    my $log_base = shift;
-
-    $self->xml_start_tag( 'c:scaling' );
-
-    # Write the c:logBase element.
-    $self->_write_c_log_base( $log_base );
-
-    # Write the c:orientation element.
-    $self->_write_orientation( $reverse );
-
-    # Write the c:max element.
-    $self->_write_c_max( $max );
-
-    # Write the c:min element.
-    $self->_write_c_min( $min );
-
-    $self->xml_end_tag( 'c:scaling' );
-}
-
-
-##############################################################################
-#
-# _write_c_log_base()
-#
-# Write the <c:logBase> element.
-#
-sub _write_c_log_base {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return unless $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:logBase', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_orientation()
-#
-# Write the <c:orientation> element.
-#
-sub _write_orientation {
-
-    my $self    = shift;
-    my $reverse = shift;
-    my $val     = 'minMax';
-
-    $val = 'maxMin' if $reverse;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:orientation', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_c_max()
-#
-# Write the <c:max> element.
-#
-sub _write_c_max {
-
-    my $self = shift;
-    my $max  = shift;
-
-    return unless defined $max;
-
-    my @attributes = ( 'val' => $max );
-
-    $self->xml_empty_tag( 'c:max', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_c_min()
-#
-# Write the <c:min> element.
-#
-sub _write_c_min {
-
-    my $self = shift;
-    my $min  = shift;
-
-    return unless defined $min;
-
-    my @attributes = ( 'val' => $min );
-
-    $self->xml_empty_tag( 'c:min', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_axis_pos()
-#
-# Write the <c:axPos> element.
-#
-sub _write_axis_pos {
-
-    my $self    = shift;
-    my $val     = shift;
-    my $reverse = shift;
-
-    if ( $reverse ) {
-        $val = 'r' if $val eq 'l';
-        $val = 't' if $val eq 'b';
-    }
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:axPos', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_number_format()
-#
-# Write the <c:numberFormat> element. Note: It is assumed that if a user
-# defined number format is supplied (i.e., non-default) then the sourceLinked
-# attribute is 0. The user can override this if required.
-#
-sub _write_number_format {
-
-    my $self          = shift;
-    my $axis          = shift;
-    my $format_code   = $axis->{_num_format};
-    my $source_linked = 1;
-
-    # Check if a user defined number format has been set.
-    if ( $format_code ne $axis->{_defaults}->{num_format} ) {
-        $source_linked = 0;
-    }
-
-    # User override of sourceLinked.
-    if ( $axis->{_num_format_linked} ) {
-        $source_linked = 1;
-    }
-
-    my @attributes = (
-        'formatCode'   => $format_code,
-        'sourceLinked' => $source_linked,
-    );
-
-    $self->xml_empty_tag( 'c:numFmt', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_cat_number_format()
-#
-# Write the <c:numFmt> element. Special case handler for category axes which
-# don't always have a number format.
-#
-sub _write_cat_number_format {
-
-    my $self           = shift;
-    my $axis           = shift;
-    my $format_code    = $axis->{_num_format};
-    my $source_linked  = 1;
-    my $default_format = 1;
-
-    # Check if a user defined number format has been set.
-    if ( $format_code ne $axis->{_defaults}->{num_format} ) {
-        $source_linked  = 0;
-        $default_format = 0;
-    }
-
-    # User override of linkedSource.
-    if ( $axis->{_num_format_linked} ) {
-        $source_linked = 1;
-    }
-
-    # Skip if cat doesn't have a num format (unless it is non-default).
-    if ( !$self->{_cat_has_num_fmt} && $default_format ) {
-        return;
-    }
-
-    my @attributes = (
-        'formatCode'   => $format_code,
-        'sourceLinked' => $source_linked,
-    );
-
-    $self->xml_empty_tag( 'c:numFmt', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_number_format()
-#
-# Write the <c:numberFormat> element for data labels.
-#
-sub _write_data_label_number_format {
-
-    my $self          = shift;
-    my $format_code   = shift;
-    my $source_linked = 0;
-
-    my @attributes = (
-        'formatCode'   => $format_code,
-        'sourceLinked' => $source_linked,
-    );
-
-    $self->xml_empty_tag( 'c:numFmt', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_major_tick_mark()
-#
-# Write the <c:majorTickMark> element.
-#
-sub _write_major_tick_mark {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return unless $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:majorTickMark', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_minor_tick_mark()
-#
-# Write the <c:minorTickMark> element.
-#
-sub _write_minor_tick_mark {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return unless $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:minorTickMark', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_tick_label_pos()
-#
-# Write the <c:tickLblPos> element.
-#
-sub _write_tick_label_pos {
-
-    my $self = shift;
-    my $val = shift || 'nextTo';
-
-    if ( $val eq 'next_to' ) {
-        $val = 'nextTo';
-    }
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:tickLblPos', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_cross_axis()
-#
-# Write the <c:crossAx> element.
-#
-sub _write_cross_axis {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:crossAx', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_crosses()
-#
-# Write the <c:crosses> element.
-#
-sub _write_crosses {
-
-    my $self = shift;
-    my $val = shift || 'autoZero';
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:crosses', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_c_crosses_at()
-#
-# Write the <c:crossesAt> element.
-#
-sub _write_c_crosses_at {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:crossesAt', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_auto()
-#
-# Write the <c:auto> element.
-#
-sub _write_auto {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:auto', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_label_align()
-#
-# Write the <c:labelAlign> element.
-#
-sub _write_label_align {
-
-    my $self = shift;
-    my $val  = 'ctr';
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:lblAlgn', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_label_offset()
-#
-# Write the <c:labelOffset> element.
-#
-sub _write_label_offset {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:lblOffset', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_tick_lbl_skip()
-#
-# Write the <c:tickLblSkip> element.
-#
-sub _write_tick_lbl_skip {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return unless $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:tickLblSkip', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_tick_mark_skip()
-#
-# Write the <c:tickMarkSkip> element.
-#
-sub _write_tick_mark_skip {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return unless $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:tickMarkSkip', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_major_gridlines()
-#
-# Write the <c:majorGridlines> element.
-#
-sub _write_major_gridlines {
-
-    my $self      = shift;
-    my $gridlines = shift;
-
-    return unless $gridlines;
-    return unless $gridlines->{_visible};
-
-    if ( $gridlines->{_line}->{_defined} ) {
-        $self->xml_start_tag( 'c:majorGridlines' );
-
-        # Write the c:spPr element.
-        $self->_write_sp_pr( $gridlines );
-
-        $self->xml_end_tag( 'c:majorGridlines' );
-    }
-    else {
-        $self->xml_empty_tag( 'c:majorGridlines' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_minor_gridlines()
-#
-# Write the <c:minorGridlines> element.
-#
-sub _write_minor_gridlines {
-
-    my $self      = shift;
-    my $gridlines = shift;
-
-    return unless $gridlines;
-    return unless $gridlines->{_visible};
-
-    if ( $gridlines->{_line}->{_defined} ) {
-        $self->xml_start_tag( 'c:minorGridlines' );
-
-        # Write the c:spPr element.
-        $self->_write_sp_pr( $gridlines );
-
-        $self->xml_end_tag( 'c:minorGridlines' );
-    }
-    else {
-        $self->xml_empty_tag( 'c:minorGridlines' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_cross_between()
-#
-# Write the <c:crossBetween> element.
-#
-sub _write_cross_between {
-
-    my $self = shift;
-
-    my $val = shift || $self->{_cross_between};
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:crossBetween', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_c_major_unit()
-#
-# Write the <c:majorUnit> element.
-#
-sub _write_c_major_unit {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return unless $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:majorUnit', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_c_minor_unit()
-#
-# Write the <c:minorUnit> element.
-#
-sub _write_c_minor_unit {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return unless $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:minorUnit', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_c_major_time_unit()
-#
-# Write the <c:majorTimeUnit> element.
-#
-sub _write_c_major_time_unit {
-
-    my $self = shift;
-    my $val = shift || 'days';
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:majorTimeUnit', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_c_minor_time_unit()
-#
-# Write the <c:minorTimeUnit> element.
-#
-sub _write_c_minor_time_unit {
-
-    my $self = shift;
-    my $val = shift || 'days';
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:minorTimeUnit', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_legend()
-#
-# Write the <c:legend> element.
-#
-sub _write_legend {
-
-    my $self          = shift;
-    my $position      = $self->{_legend_position};
-    my $font          = $self->{_legend_font};
-    my @delete_series = ();
-    my $overlay       = 0;
-
-    if ( defined $self->{_legend_delete_series}
-        && ref $self->{_legend_delete_series} eq 'ARRAY' )
-    {
-        @delete_series = @{ $self->{_legend_delete_series} };
-    }
-
-    if ( $position =~ s/^overlay_// ) {
-        $overlay = 1;
-    }
-
-    my %allowed = (
-        right  => 'r',
-        left   => 'l',
-        top    => 't',
-        bottom => 'b',
-    );
-
-    return if $position eq 'none';
-    return unless exists $allowed{$position};
-
-    $position = $allowed{$position};
-
-    $self->xml_start_tag( 'c:legend' );
-
-    # Write the c:legendPos element.
-    $self->_write_legend_pos( $position );
-
-    # Remove series labels from the legend.
-    for my $index ( @delete_series ) {
-
-        # Write the c:legendEntry element.
-        $self->_write_legend_entry( $index );
-    }
-
-    # Write the c:layout element.
-    $self->_write_layout( $self->{_legend_layout}, 'legend' );
-
-    # Write the c:txPr element.
-    if ( $font ) {
-        $self->_write_tx_pr( undef, $font );
-    }
-
-    # Write the c:overlay element.
-    $self->_write_overlay() if $overlay;
-
-    $self->xml_end_tag( 'c:legend' );
-}
-
-
-##############################################################################
-#
-# _write_legend_pos()
-#
-# Write the <c:legendPos> element.
-#
-sub _write_legend_pos {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:legendPos', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_legend_entry()
-#
-# Write the <c:legendEntry> element.
-#
-sub _write_legend_entry {
-
-    my $self  = shift;
-    my $index = shift;
-
-    $self->xml_start_tag( 'c:legendEntry' );
-
-    # Write the c:idx element.
-    $self->_write_idx( $index );
-
-    # Write the c:delete element.
-    $self->_write_delete( 1 );
-
-    $self->xml_end_tag( 'c:legendEntry' );
-}
-
-
-##############################################################################
-#
-# _write_overlay()
-#
-# Write the <c:overlay> element.
-#
-sub _write_overlay {
-
-    my $self = shift;
-    my $val  = 1;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:overlay', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_plot_vis_only()
-#
-# Write the <c:plotVisOnly> element.
-#
-sub _write_plot_vis_only {
-
-    my $self = shift;
-    my $val  = 1;
-
-    # Ignore this element if we are plotting hidden data.
-    return if $self->{_show_hidden_data};
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:plotVisOnly', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_print_settings()
-#
-# Write the <c:printSettings> element.
-#
-sub _write_print_settings {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'c:printSettings' );
-
-    # Write the c:headerFooter element.
-    $self->_write_header_footer();
-
-    # Write the c:pageMargins element.
-    $self->_write_page_margins();
-
-    # Write the c:pageSetup element.
-    $self->_write_page_setup();
-
-    $self->xml_end_tag( 'c:printSettings' );
-}
-
-
-##############################################################################
-#
-# _write_header_footer()
-#
-# Write the <c:headerFooter> element.
-#
-sub _write_header_footer {
-
-    my $self = shift;
-
-    $self->xml_empty_tag( 'c:headerFooter' );
-}
-
-
-##############################################################################
-#
-# _write_page_margins()
-#
-# Write the <c:pageMargins> element.
-#
-sub _write_page_margins {
-
-    my $self   = shift;
-    my $b      = 0.75;
-    my $l      = 0.7;
-    my $r      = 0.7;
-    my $t      = 0.75;
-    my $header = 0.3;
-    my $footer = 0.3;
-
-    my @attributes = (
-        'b'      => $b,
-        'l'      => $l,
-        'r'      => $r,
-        't'      => $t,
-        'header' => $header,
-        'footer' => $footer,
-    );
-
-    $self->xml_empty_tag( 'c:pageMargins', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_page_setup()
-#
-# Write the <c:pageSetup> element.
-#
-sub _write_page_setup {
-
-    my $self = shift;
-
-    $self->xml_empty_tag( 'c:pageSetup' );
-}
-
-
-##############################################################################
-#
-# _write_auto_title_deleted()
-#
-# Write the <c:autoTitleDeleted> element.
-#
-sub _write_auto_title_deleted {
-
-    my $self = shift;
-
-    my @attributes = ( 'val' => 1 );
-
-    $self->xml_empty_tag( 'c:autoTitleDeleted', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_title_rich()
-#
-# Write the <c:title> element for a rich string.
-#
-sub _write_title_rich {
-
-    my $self    = shift;
-    my $title   = shift;
-    my $horiz   = shift;
-    my $font    = shift;
-    my $layout  = shift;
-    my $overlay = shift;
-
-    $self->xml_start_tag( 'c:title' );
-
-    # Write the c:tx element.
-    $self->_write_tx_rich( $title, $horiz, $font );
-
-    # Write the c:layout element.
-    $self->_write_layout( $layout, 'text' );
-
-    # Write the c:overlay element.
-    $self->_write_overlay() if $overlay;
-
-    $self->xml_end_tag( 'c:title' );
-}
-
-
-##############################################################################
-#
-# _write_title_formula()
-#
-# Write the <c:title> element for a rich string.
-#
-sub _write_title_formula {
-
-    my $self    = shift;
-    my $title   = shift;
-    my $data_id = shift;
-    my $horiz   = shift;
-    my $font    = shift;
-    my $layout  = shift;
-    my $overlay = shift;
-
-    $self->xml_start_tag( 'c:title' );
-
-    # Write the c:tx element.
-    $self->_write_tx_formula( $title, $data_id );
-
-    # Write the c:layout element.
-    $self->_write_layout( $layout, 'text' );
-
-    # Write the c:overlay element.
-    $self->_write_overlay() if $overlay;
-
-    # Write the c:txPr element.
-    $self->_write_tx_pr( $horiz, $font );
-
-    $self->xml_end_tag( 'c:title' );
-}
-
-
-##############################################################################
-#
-# _write_tx_rich()
-#
-# Write the <c:tx> element.
-#
-sub _write_tx_rich {
-
-    my $self  = shift;
-    my $title = shift;
-    my $horiz = shift;
-    my $font  = shift;
-
-    $self->xml_start_tag( 'c:tx' );
-
-    # Write the c:rich element.
-    $self->_write_rich( $title, $horiz, $font );
-
-    $self->xml_end_tag( 'c:tx' );
-}
-
-
-##############################################################################
-#
-# _write_tx_value()
-#
-# Write the <c:tx> element with a simple value such as for series names.
-#
-sub _write_tx_value {
-
-    my $self  = shift;
-    my $title = shift;
-
-    $self->xml_start_tag( 'c:tx' );
-
-    # Write the c:v element.
-    $self->_write_v( $title );
-
-    $self->xml_end_tag( 'c:tx' );
-}
-
-
-##############################################################################
-#
-# _write_tx_formula()
-#
-# Write the <c:tx> element.
-#
-sub _write_tx_formula {
-
-    my $self    = shift;
-    my $title   = shift;
-    my $data_id = shift;
-    my $data;
-
-    if ( defined $data_id ) {
-        $data = $self->{_formula_data}->[$data_id];
-    }
-
-    $self->xml_start_tag( 'c:tx' );
-
-    # Write the c:strRef element.
-    $self->_write_str_ref( $title, $data, 'str' );
-
-    $self->xml_end_tag( 'c:tx' );
-}
-
-
-##############################################################################
-#
-# _write_rich()
-#
-# Write the <c:rich> element.
-#
-sub _write_rich {
-
-    my $self     = shift;
-    my $title    = shift;
-    my $horiz    = shift;
-    my $rotation = undef;
-    my $font     = shift;
-
-    if ( $font && exists $font->{_rotation} ) {
-        $rotation = $font->{_rotation};
-    }
-
-    $self->xml_start_tag( 'c:rich' );
-
-    # Write the a:bodyPr element.
-    $self->_write_a_body_pr( $rotation, $horiz );
-
-    # Write the a:lstStyle element.
-    $self->_write_a_lst_style();
-
-    # Write the a:p element.
-    $self->_write_a_p_rich( $title, $font );
-
-    $self->xml_end_tag( 'c:rich' );
-}
-
-
-##############################################################################
-#
-# _write_a_body_pr()
-#
-# Write the <a:bodyPr> element.
-sub _write_a_body_pr {
-
-    my $self  = shift;
-    my $rot   = shift;
-    my $horiz = shift;
-
-    my @attributes = ();
-
-    if ( !defined $rot && $horiz ) {
-        $rot = -5400000;
-    }
-
-    push @attributes, ( 'rot' => $rot ) if defined $rot;
-    push @attributes, ( 'vert' => 'horz' ) if $horiz;
-
-    $self->xml_empty_tag( 'a:bodyPr', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_a_lst_style()
-#
-# Write the <a:lstStyle> element.
-#
-sub _write_a_lst_style {
-
-    my $self = shift;
-
-    $self->xml_empty_tag( 'a:lstStyle' );
-}
-
-
-##############################################################################
-#
-# _write_a_p_rich()
-#
-# Write the <a:p> element for rich string titles.
-#
-sub _write_a_p_rich {
-
-    my $self  = shift;
-    my $title = shift;
-    my $font  = shift;
-
-    $self->xml_start_tag( 'a:p' );
-
-    # Write the a:pPr element.
-    $self->_write_a_p_pr_rich( $font );
-
-    # Write the a:r element.
-    $self->_write_a_r( $title, $font );
-
-    $self->xml_end_tag( 'a:p' );
-}
-
-
-##############################################################################
-#
-# _write_a_p_formula()
-#
-# Write the <a:p> element for formula titles.
-#
-sub _write_a_p_formula {
-
-    my $self = shift;
-    my $font = shift;
-
-    $self->xml_start_tag( 'a:p' );
-
-    # Write the a:pPr element.
-    $self->_write_a_p_pr_formula( $font );
-
-    # Write the a:endParaRPr element.
-    $self->_write_a_end_para_rpr();
-
-    $self->xml_end_tag( 'a:p' );
-}
-
-
-##############################################################################
-#
-# _write_a_p_pr_rich()
-#
-# Write the <a:pPr> element for rich string titles.
-#
-sub _write_a_p_pr_rich {
-
-    my $self = shift;
-    my $font = shift;
-
-    $self->xml_start_tag( 'a:pPr' );
-
-    # Write the a:defRPr element.
-    $self->_write_a_def_rpr( $font );
-
-    $self->xml_end_tag( 'a:pPr' );
-}
-
-
-##############################################################################
-#
-# _write_a_p_pr_formula()
-#
-# Write the <a:pPr> element for formula titles.
-#
-sub _write_a_p_pr_formula {
-
-    my $self = shift;
-    my $font = shift;
-
-    $self->xml_start_tag( 'a:pPr' );
-
-    # Write the a:defRPr element.
-    $self->_write_a_def_rpr( $font );
-
-    $self->xml_end_tag( 'a:pPr' );
-}
-
-
-##############################################################################
-#
-# _write_a_def_rpr()
-#
-# Write the <a:defRPr> element.
-#
-sub _write_a_def_rpr {
-
-    my $self      = shift;
-    my $font      = shift;
-    my $has_color = 0;
-
-    my @style_attributes = $self->_get_font_style_attributes( $font );
-    my @latin_attributes = $self->_get_font_latin_attributes( $font );
-
-    $has_color = 1 if $font && $font->{_color};
-
-    if ( @latin_attributes || $has_color ) {
-        $self->xml_start_tag( 'a:defRPr', @style_attributes );
-
-
-        if ( $has_color ) {
-            $self->_write_a_solid_fill( { color => $font->{_color} } );
-        }
-
-        if ( @latin_attributes ) {
-            $self->_write_a_latin( @latin_attributes );
-        }
-
-        $self->xml_end_tag( 'a:defRPr' );
-    }
-    else {
-        $self->xml_empty_tag( 'a:defRPr', @style_attributes );
-    }
-}
-
-
-##############################################################################
-#
-# _write_a_end_para_rpr()
-#
-# Write the <a:endParaRPr> element.
-#
-sub _write_a_end_para_rpr {
-
-    my $self = shift;
-    my $lang = 'en-US';
-
-    my @attributes = ( 'lang' => $lang );
-
-    $self->xml_empty_tag( 'a:endParaRPr', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_a_r()
-#
-# Write the <a:r> element.
-#
-sub _write_a_r {
-
-    my $self  = shift;
-    my $title = shift;
-    my $font  = shift;
-
-    $self->xml_start_tag( 'a:r' );
-
-    # Write the a:rPr element.
-    $self->_write_a_r_pr( $font );
-
-    # Write the a:t element.
-    $self->_write_a_t( $title );
-
-    $self->xml_end_tag( 'a:r' );
-}
-
-
-##############################################################################
-#
-# _write_a_r_pr()
-#
-# Write the <a:rPr> element.
-#
-sub _write_a_r_pr {
-
-    my $self      = shift;
-    my $font      = shift;
-    my $has_color = 0;
-    my $lang      = 'en-US';
-
-    my @style_attributes = $self->_get_font_style_attributes( $font );
-    my @latin_attributes = $self->_get_font_latin_attributes( $font );
-
-    $has_color = 1 if $font && $font->{_color};
-
-    # Add the lang type to the attributes.
-    @style_attributes = ( 'lang' => $lang, @style_attributes );
-
-
-    if ( @latin_attributes || $has_color ) {
-        $self->xml_start_tag( 'a:rPr', @style_attributes );
-
-
-        if ( $has_color ) {
-            $self->_write_a_solid_fill( { color => $font->{_color} } );
-        }
-
-        if ( @latin_attributes ) {
-            $self->_write_a_latin( @latin_attributes );
-        }
-
-        $self->xml_end_tag( 'a:rPr' );
-    }
-    else {
-        $self->xml_empty_tag( 'a:rPr', @style_attributes );
-    }
-
-
-}
-
-
-##############################################################################
-#
-# _write_a_t()
-#
-# Write the <a:t> element.
-#
-sub _write_a_t {
-
-    my $self  = shift;
-    my $title = shift;
-
-    $self->xml_data_element( 'a:t', $title );
-}
-
-
-##############################################################################
-#
-# _write_tx_pr()
-#
-# Write the <c:txPr> element.
-#
-sub _write_tx_pr {
-
-    my $self     = shift;
-    my $horiz    = shift;
-    my $font     = shift;
-    my $rotation = undef;
-
-    if ( $font && exists $font->{_rotation} ) {
-        $rotation = $font->{_rotation};
-    }
-
-    $self->xml_start_tag( 'c:txPr' );
-
-    # Write the a:bodyPr element.
-    $self->_write_a_body_pr( $rotation, $horiz );
-
-    # Write the a:lstStyle element.
-    $self->_write_a_lst_style();
-
-    # Write the a:p element.
-    $self->_write_a_p_formula( $font );
-
-    $self->xml_end_tag( 'c:txPr' );
-}
-
-
-##############################################################################
-#
-# _write_marker()
-#
-# Write the <c:marker> element.
-#
-sub _write_marker {
-
-    my $self = shift;
-    my $marker = shift || $self->{_default_marker};
-
-    return unless $marker;
-    return if $marker->{automatic};
-
-    $self->xml_start_tag( 'c:marker' );
-
-    # Write the c:symbol element.
-    $self->_write_symbol( $marker->{type} );
-
-    # Write the c:size element.
-    my $size = $marker->{size};
-    $self->_write_marker_size( $size ) if $size;
-
-    # Write the c:spPr element.
-    $self->_write_sp_pr( $marker );
-
-    $self->xml_end_tag( 'c:marker' );
-}
-
-
-##############################################################################
-#
-# _write_marker_size()
-#
-# Write the <c:size> element.
-#
-sub _write_marker_size {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:size', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_symbol()
-#
-# Write the <c:symbol> element.
-#
-sub _write_symbol {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:symbol', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_sp_pr()
-#
-# Write the <c:spPr> element.
-#
-sub _write_sp_pr {
-
-    my $self   = shift;
-    my $series = shift;
-
-    if (    !$series->{_line}->{_defined}
-        and !$series->{_fill}->{_defined}
-        and !$series->{_pattern}
-        and !$series->{_gradient} )
-    {
-        return;
-    }
-
-
-    $self->xml_start_tag( 'c:spPr' );
-
-    # Write the fill elements for solid charts such as pie/doughnut and bar.
-    if ( $series->{_fill}->{_defined} ) {
-
-        if ( $series->{_fill}->{none} ) {
-
-            # Write the a:noFill element.
-            $self->_write_a_no_fill();
-        }
-        else {
-            # Write the a:solidFill element.
-            $self->_write_a_solid_fill( $series->{_fill} );
-        }
-    }
-
-    if ( $series->{_pattern} ) {
-
-        # Write the a:pattFill element.
-        $self->_write_a_patt_fill( $series->{_pattern} );
-    }
-
-    if ( $series->{_gradient} ) {
-
-        # Write the a:gradFill element.
-        $self->_write_a_grad_fill( $series->{_gradient} );
-    }
-
-
-    # Write the a:ln element.
-    if ( $series->{_line}->{_defined} ) {
-        $self->_write_a_ln( $series->{_line} );
-    }
-
-    $self->xml_end_tag( 'c:spPr' );
-}
-
-
-##############################################################################
-#
-# _write_a_ln()
-#
-# Write the <a:ln> element.
-#
-sub _write_a_ln {
-
-    my $self       = shift;
-    my $line       = shift;
-    my @attributes = ();
-
-    # Add the line width as an attribute.
-    if ( my $width = $line->{width} ) {
-
-        # Round width to nearest 0.25, like Excel.
-        $width = int( ( $width + 0.125 ) * 4 ) / 4;
-
-        # Convert to internal units.
-        $width = int( 0.5 + ( 12700 * $width ) );
-
-        @attributes = ( 'w' => $width );
-    }
-
-    $self->xml_start_tag( 'a:ln', @attributes );
-
-    # Write the line fill.
-    if ( $line->{none} ) {
-
-        # Write the a:noFill element.
-        $self->_write_a_no_fill();
-    }
-    elsif ( $line->{color} ) {
-
-        # Write the a:solidFill element.
-        $self->_write_a_solid_fill( $line );
-    }
-
-    # Write the line/dash type.
-    if ( my $type = $line->{dash_type} ) {
-
-        # Write the a:prstDash element.
-        $self->_write_a_prst_dash( $type );
-    }
-
-    $self->xml_end_tag( 'a:ln' );
-}
-
-
-##############################################################################
-#
-# _write_a_no_fill()
-#
-# Write the <a:noFill> element.
-#
-sub _write_a_no_fill {
-
-    my $self = shift;
-
-    $self->xml_empty_tag( 'a:noFill' );
-}
-
-
-##############################################################################
-#
-# _write_a_solid_fill()
-#
-# Write the <a:solidFill> element.
-#
-sub _write_a_solid_fill {
-
-    my $self = shift;
-    my $fill = shift;
-
-    $self->xml_start_tag( 'a:solidFill' );
-
-    if ( $fill->{color} ) {
-
-        my $color = $self->_get_color( $fill->{color} );
-
-        # Write the a:srgbClr element.
-        $self->_write_a_srgb_clr( $color, $fill->{transparency} );
-    }
-
-    $self->xml_end_tag( 'a:solidFill' );
-}
-
-
-##############################################################################
-#
-# _write_a_srgb_clr()
-#
-# Write the <a:srgbClr> element.
-#
-sub _write_a_srgb_clr {
-
-    my $self         = shift;
-    my $color        = shift;
-    my $transparency = shift;
-
-    my @attributes = ( 'val' => $color );
-
-    if ( $transparency ) {
-        $self->xml_start_tag( 'a:srgbClr', @attributes );
-
-        # Write the a:alpha element.
-        $self->_write_a_alpha( $transparency );
-
-        $self->xml_end_tag( 'a:srgbClr' );
-    }
-    else {
-        $self->xml_empty_tag( 'a:srgbClr', @attributes );
-    }
-}
-
-
-##############################################################################
-#
-# _write_a_alpha()
-#
-# Write the <a:alpha> element.
-#
-sub _write_a_alpha {
-
-    my $self = shift;
-    my $val  = shift;
-
-    $val = ( 100 - int( $val ) ) * 1000;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'a:alpha', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_a_prst_dash()
-#
-# Write the <a:prstDash> element.
-#
-sub _write_a_prst_dash {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'a:prstDash', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_trendline()
-#
-# Write the <c:trendline> element.
-#
-sub _write_trendline {
-
-    my $self      = shift;
-    my $trendline = shift;
-
-    return unless $trendline;
-
-    $self->xml_start_tag( 'c:trendline' );
-
-    # Write the c:name element.
-    $self->_write_name( $trendline->{name} );
-
-    # Write the c:spPr element.
-    $self->_write_sp_pr( $trendline );
-
-    # Write the c:trendlineType element.
-    $self->_write_trendline_type( $trendline->{type} );
-
-    # Write the c:order element for polynomial trendlines.
-    if ( $trendline->{type} eq 'poly' ) {
-        $self->_write_trendline_order( $trendline->{order} );
-    }
-
-    # Write the c:period element for moving average trendlines.
-    if ( $trendline->{type} eq 'movingAvg' ) {
-        $self->_write_period( $trendline->{period} );
-    }
-
-    # Write the c:forward element.
-    $self->_write_forward( $trendline->{forward} );
-
-    # Write the c:backward element.
-    $self->_write_backward( $trendline->{backward} );
-
-    if ( defined $trendline->{intercept} ) {
-        # Write the c:intercept element.
-        $self->_write_intercept( $trendline->{intercept} );
-    }
-
-    if ($trendline->{display_r_squared}) {
-        # Write the c:dispRSqr element.
-        $self->_write_disp_rsqr();
-    }
-
-    if ($trendline->{display_equation}) {
-        # Write the c:dispEq element.
-        $self->_write_disp_eq();
-
-        # Write the c:trendlineLbl element.
-        $self->_write_trendline_lbl();
-    }
-
-    $self->xml_end_tag( 'c:trendline' );
-}
-
-
-##############################################################################
-#
-# _write_trendline_type()
-#
-# Write the <c:trendlineType> element.
-#
-sub _write_trendline_type {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:trendlineType', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_name()
-#
-# Write the <c:name> element.
-#
-sub _write_name {
-
-    my $self = shift;
-    my $data = shift;
-
-    return unless defined $data;
-
-    $self->xml_data_element( 'c:name', $data );
-}
-
-
-##############################################################################
-#
-# _write_trendline_order()
-#
-# Write the <c:order> element.
-#
-sub _write_trendline_order {
-
-    my $self = shift;
-    my $val = defined $_[0] ? $_[0] : 2;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:order', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_period()
-#
-# Write the <c:period> element.
-#
-sub _write_period {
-
-    my $self = shift;
-    my $val = defined $_[0] ? $_[0] : 2;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:period', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_forward()
-#
-# Write the <c:forward> element.
-#
-sub _write_forward {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return unless $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:forward', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_backward()
-#
-# Write the <c:backward> element.
-#
-sub _write_backward {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return unless $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:backward', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_intercept()
-#
-# Write the <c:intercept> element.
-#
-sub _write_intercept {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:intercept', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_disp_eq()
-#
-# Write the <c:dispEq> element.
-#
-sub _write_disp_eq {
-
-    my $self = shift;
-
-    my @attributes = ( 'val' => 1 );
-
-    $self->xml_empty_tag( 'c:dispEq', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_disp_rsqr()
-#
-# Write the <c:dispRSqr> element.
-#
-sub _write_disp_rsqr {
-
-    my $self = shift;
-
-    my @attributes = ( 'val' => 1 );
-
-    $self->xml_empty_tag( 'c:dispRSqr', @attributes );
-}
-
-##############################################################################
-#
-# _write_trendline_lbl()
-#
-# Write the <c:trendlineLbl> element.
-#
-sub _write_trendline_lbl {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'c:trendlineLbl' );
-
-    # Write the c:layout element.
-    $self->_write_layout();
-
-    # Write the c:numFmt element.
-    $self->_write_trendline_num_fmt();
-
-    $self->xml_end_tag( 'c:trendlineLbl' );
-}
-
-##############################################################################
-#
-# _write_trendline_num_fmt()
-#
-# Write the <c:numFmt> element.
-#
-sub _write_trendline_num_fmt {
-
-    my $self          = shift;
-    my $format_code   = 'General';
-    my $source_linked = 0;
-
-    my @attributes = (
-        'formatCode'   => $format_code,
-        'sourceLinked' => $source_linked,
-    );
-
-    $self->xml_empty_tag( 'c:numFmt', @attributes );
-}
-
-##############################################################################
-#
-# _write_hi_low_lines()
-#
-# Write the <c:hiLowLines> element.
-#
-sub _write_hi_low_lines {
-
-    my $self = shift;
-
-    my $hi_low_lines = $self->{_hi_low_lines};
-
-    return unless $hi_low_lines;
-
-    if ( $hi_low_lines->{_line}->{_defined} ) {
-
-        $self->xml_start_tag( 'c:hiLowLines' );
-
-        # Write the c:spPr element.
-        $self->_write_sp_pr( $hi_low_lines );
-
-        $self->xml_end_tag( 'c:hiLowLines' );
-    }
-    else {
-        $self->xml_empty_tag( 'c:hiLowLines' );
-    }
-}
-
-
-#############################################################################
-#
-# _write_drop_lines()
-#
-# Write the <c:dropLines> element.
-#
-sub _write_drop_lines {
-
-    my $self = shift;
-
-    my $drop_lines = $self->{_drop_lines};
-
-    return unless $drop_lines;
-
-    if ( $drop_lines->{_line}->{_defined} ) {
-
-        $self->xml_start_tag( 'c:dropLines' );
-
-        # Write the c:spPr element.
-        $self->_write_sp_pr( $drop_lines );
-
-        $self->xml_end_tag( 'c:dropLines' );
-    }
-    else {
-        $self->xml_empty_tag( 'c:dropLines' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_overlap()
-#
-# Write the <c:overlap> element.
-#
-sub _write_overlap {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return if !defined $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:overlap', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_num_cache()
-#
-# Write the <c:numCache> element.
-#
-sub _write_num_cache {
-
-    my $self  = shift;
-    my $data  = shift;
-    my $count = @$data;
-
-    $self->xml_start_tag( 'c:numCache' );
-
-    # Write the c:formatCode element.
-    $self->_write_format_code( 'General' );
-
-    # Write the c:ptCount element.
-    $self->_write_pt_count( $count );
-
-    for my $i ( 0 .. $count - 1 ) {
-        my $token = $data->[$i];
-
-        # Write non-numeric data as 0.
-        if ( defined $token
-            && $token !~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ )
-        {
-            $token = 0;
-        }
-
-        # Write the c:pt element.
-        $self->_write_pt( $i, $token );
-    }
-
-    $self->xml_end_tag( 'c:numCache' );
-}
-
-
-##############################################################################
-#
-# _write_str_cache()
-#
-# Write the <c:strCache> element.
-#
-sub _write_str_cache {
-
-    my $self  = shift;
-    my $data  = shift;
-    my $count = @$data;
-
-    $self->xml_start_tag( 'c:strCache' );
-
-    # Write the c:ptCount element.
-    $self->_write_pt_count( $count );
-
-    for my $i ( 0 .. $count - 1 ) {
-
-        # Write the c:pt element.
-        $self->_write_pt( $i, $data->[$i] );
-    }
-
-    $self->xml_end_tag( 'c:strCache' );
-}
-
-
-##############################################################################
-#
-# _write_format_code()
-#
-# Write the <c:formatCode> element.
-#
-sub _write_format_code {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'c:formatCode', $data );
-}
-
-
-##############################################################################
-#
-# _write_pt_count()
-#
-# Write the <c:ptCount> element.
-#
-sub _write_pt_count {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:ptCount', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_pt()
-#
-# Write the <c:pt> element.
-#
-sub _write_pt {
-
-    my $self  = shift;
-    my $idx   = shift;
-    my $value = shift;
-
-    return if !defined $value;
-
-    my @attributes = ( 'idx' => $idx );
-
-    $self->xml_start_tag( 'c:pt', @attributes );
-
-    # Write the c:v element.
-    $self->_write_v( $value );
-
-    $self->xml_end_tag( 'c:pt' );
-}
-
-
-##############################################################################
-#
-# _write_v()
-#
-# Write the <c:v> element.
-#
-sub _write_v {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'c:v', $data );
-}
-
-
-##############################################################################
-#
-# _write_protection()
-#
-# Write the <c:protection> element.
-#
-sub _write_protection {
-
-    my $self = shift;
-
-    return unless $self->{_protection};
-
-    $self->xml_empty_tag( 'c:protection' );
-}
-
-
-##############################################################################
-#
-# _write_d_pt()
-#
-# Write the <c:dPt> elements.
-#
-sub _write_d_pt {
-
-    my $self   = shift;
-    my $points = shift;
-    my $index  = -1;
-
-    return unless $points;
-
-    for my $point ( @$points ) {
-
-        $index++;
-        next unless $point;
-
-        $self->_write_d_pt_point( $index, $point );
-    }
-}
-
-
-##############################################################################
-#
-# _write_d_pt_point()
-#
-# Write an individual <c:dPt> element.
-#
-sub _write_d_pt_point {
-
-    my $self   = shift;
-    my $index = shift;
-    my $point = shift;
-
-        $self->xml_start_tag( 'c:dPt' );
-
-        # Write the c:idx element.
-        $self->_write_idx( $index );
-
-        # Write the c:spPr element.
-        $self->_write_sp_pr( $point );
-
-        $self->xml_end_tag( 'c:dPt' );
-
-}
-
-
-##############################################################################
-#
-# _write_d_lbls()
-#
-# Write the <c:dLbls> element.
-#
-sub _write_d_lbls {
-
-    my $self   = shift;
-    my $labels = shift;
-
-    return unless $labels;
-
-    $self->xml_start_tag( 'c:dLbls' );
-
-    # Write the c:numFmt element.
-    if ( $labels->{num_format} ) {
-        $self->_write_data_label_number_format( $labels->{num_format} );
-    }
-
-    # Write the data label font elements.
-    if ($labels->{font} ) {
-        $self->_write_axis_font( $labels->{font} );
-    }
-
-    # Write the c:dLblPos element.
-    $self->_write_d_lbl_pos( $labels->{position} ) if $labels->{position};
-
-    # Write the c:showLegendKey element.
-    $self->_write_show_legend_key() if $labels->{legend_key};
-
-    # Write the c:showVal element.
-    $self->_write_show_val() if $labels->{value};
-
-    # Write the c:showCatName element.
-    $self->_write_show_cat_name() if $labels->{category};
-
-    # Write the c:showSerName element.
-    $self->_write_show_ser_name() if $labels->{series_name};
-
-    # Write the c:showPercent element.
-    $self->_write_show_percent() if $labels->{percentage};
-
-    # Write the c:separator element.
-    $self->_write_separator($labels->{separator}) if $labels->{separator};
-
-    # Write the c:showLeaderLines element.
-    $self->_write_show_leader_lines() if $labels->{leader_lines};
-
-    $self->xml_end_tag( 'c:dLbls' );
-}
-
-##############################################################################
-#
-# _write_show_legend_key()
-#
-# Write the <c:showLegendKey> element.
-#
-sub _write_show_legend_key {
-
-    my $self = shift;
-    my $val  = 1;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:showLegendKey', @attributes );
-}
-
-##############################################################################
-#
-# _write_show_val()
-#
-# Write the <c:showVal> element.
-#
-sub _write_show_val {
-
-    my $self = shift;
-    my $val  = 1;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:showVal', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_show_cat_name()
-#
-# Write the <c:showCatName> element.
-#
-sub _write_show_cat_name {
-
-    my $self = shift;
-    my $val  = 1;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:showCatName', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_show_ser_name()
-#
-# Write the <c:showSerName> element.
-#
-sub _write_show_ser_name {
-
-    my $self = shift;
-    my $val  = 1;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:showSerName', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_show_percent()
-#
-# Write the <c:showPercent> element.
-#
-sub _write_show_percent {
-
-    my $self = shift;
-    my $val  = 1;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:showPercent', @attributes );
-}
-
-##############################################################################
-#
-# _write_separator()
-#
-# Write the <c:separator> element.
-#
-sub _write_separator {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'c:separator', $data );
-}
-
-##############################################################################
-#
-# _write_show_leader_lines()
-#
-# Write the <c:showLeaderLines> element.
-#
-sub _write_show_leader_lines {
-
-    my $self = shift;
-    my $val  = 1;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:showLeaderLines', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_d_lbl_pos()
-#
-# Write the <c:dLblPos> element.
-#
-sub _write_d_lbl_pos {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:dLblPos', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_delete()
-#
-# Write the <c:delete> element.
-#
-sub _write_delete {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:delete', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_c_invert_if_negative()
-#
-# Write the <c:invertIfNegative> element.
-#
-sub _write_c_invert_if_negative {
-
-    my $self   = shift;
-    my $invert = shift;
-    my $val    = 1;
-
-    return unless $invert;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:invertIfNegative', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_axis_font()
-#
-# Write the axis font elements.
-#
-sub _write_axis_font {
-
-    my $self = shift;
-    my $font = shift;
-
-    return unless $font;
-
-    $self->xml_start_tag( 'c:txPr' );
-    $self->_write_a_body_pr($font->{_rotation});
-    $self->_write_a_lst_style();
-    $self->xml_start_tag( 'a:p' );
-
-    $self->_write_a_p_pr_rich( $font );
-
-    $self->_write_a_end_para_rpr();
-    $self->xml_end_tag( 'a:p' );
-    $self->xml_end_tag( 'c:txPr' );
-}
-
-
-##############################################################################
-#
-# _write_a_latin()
-#
-# Write the <a:latin> element.
-#
-sub _write_a_latin {
-
-    my $self       = shift;
-    my @attributes = @_;
-
-    $self->xml_empty_tag( 'a:latin', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_d_table()
-#
-# Write the <c:dTable> element.
-#
-sub _write_d_table {
-
-    my $self  = shift;
-    my $table = $self->{_table};
-
-    return if !$table;
-
-    $self->xml_start_tag( 'c:dTable' );
-
-    if ( $table->{_horizontal} ) {
-
-        # Write the c:showHorzBorder element.
-        $self->_write_show_horz_border();
-    }
-
-    if ( $table->{_vertical} ) {
-
-        # Write the c:showVertBorder element.
-        $self->_write_show_vert_border();
-    }
-
-    if ( $table->{_outline} ) {
-
-        # Write the c:showOutline element.
-        $self->_write_show_outline();
-    }
-
-    if ( $table->{_show_keys} ) {
-
-        # Write the c:showKeys element.
-        $self->_write_show_keys();
-    }
-
-    if ( $table->{_font} ) {
-        # Write the table font.
-        $self->_write_tx_pr( undef, $table->{_font} );
-    }
-
-    $self->xml_end_tag( 'c:dTable' );
-}
-
-
-##############################################################################
-#
-# _write_show_horz_border()
-#
-# Write the <c:showHorzBorder> element.
-#
-sub _write_show_horz_border {
-
-    my $self = shift;
-
-    my @attributes = ( 'val' => 1 );
-
-    $self->xml_empty_tag( 'c:showHorzBorder', @attributes );
-}
-
-##############################################################################
-#
-# _write_show_vert_border()
-#
-# Write the <c:showVertBorder> element.
-#
-sub _write_show_vert_border {
-
-    my $self = shift;
-
-    my @attributes = ( 'val' => 1 );
-
-    $self->xml_empty_tag( 'c:showVertBorder', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_show_outline()
-#
-# Write the <c:showOutline> element.
-#
-sub _write_show_outline {
-
-    my $self = shift;
-
-    my @attributes = ( 'val' => 1 );
-
-    $self->xml_empty_tag( 'c:showOutline', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_show_keys()
-#
-# Write the <c:showKeys> element.
-#
-sub _write_show_keys {
-
-    my $self = shift;
-
-    my @attributes = ( 'val' => 1 );
-
-    $self->xml_empty_tag( 'c:showKeys', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_error_bars()
-#
-# Write the X and Y error bars.
-#
-sub _write_error_bars {
-
-    my $self       = shift;
-    my $error_bars = shift;
-
-    return unless $error_bars;
-
-    if ( $error_bars->{_x_error_bars} ) {
-        $self->_write_err_bars( 'x', $error_bars->{_x_error_bars} );
-    }
-
-    if ( $error_bars->{_y_error_bars} ) {
-        $self->_write_err_bars( 'y', $error_bars->{_y_error_bars} );
-    }
-
-}
-
-
-##############################################################################
-#
-# _write_err_bars()
-#
-# Write the <c:errBars> element.
-#
-sub _write_err_bars {
-
-    my $self       = shift;
-    my $direction  = shift;
-    my $error_bars = shift;
-
-    return unless $error_bars;
-
-    $self->xml_start_tag( 'c:errBars' );
-
-    # Write the c:errDir element.
-    $self->_write_err_dir( $direction );
-
-    # Write the c:errBarType element.
-    $self->_write_err_bar_type( $error_bars->{_direction} );
-
-    # Write the c:errValType element.
-    $self->_write_err_val_type( $error_bars->{_type} );
-
-    if ( !$error_bars->{_endcap} ) {
-
-        # Write the c:noEndCap element.
-        $self->_write_no_end_cap();
-    }
-
-    if ( $error_bars->{_type} eq 'stdErr' ) {
-
-        # Don't need to write a c:errValType tag.
-    }
-    elsif ( $error_bars->{_type} eq 'cust' ) {
-
-        # Write the custom error tags.
-        $self->_write_custom_error( $error_bars );
-    }
-    else {
-        # Write the c:val element.
-        $self->_write_error_val( $error_bars->{_value} );
-    }
-
-    # Write the c:spPr element.
-    $self->_write_sp_pr( $error_bars );
-
-    $self->xml_end_tag( 'c:errBars' );
-}
-
-
-##############################################################################
-#
-# _write_err_dir()
-#
-# Write the <c:errDir> element.
-#
-sub _write_err_dir {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:errDir', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_err_bar_type()
-#
-# Write the <c:errBarType> element.
-#
-sub _write_err_bar_type {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:errBarType', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_err_val_type()
-#
-# Write the <c:errValType> element.
-#
-sub _write_err_val_type {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:errValType', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_no_end_cap()
-#
-# Write the <c:noEndCap> element.
-#
-sub _write_no_end_cap {
-
-    my $self = shift;
-
-    my @attributes = ( 'val' => 1 );
-
-    $self->xml_empty_tag( 'c:noEndCap', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_error_val()
-#
-# Write the <c:val> element for error bars.
-#
-sub _write_error_val {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:val', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_custom_error()
-#
-# Write the custom error bars tags.
-#
-sub _write_custom_error {
-
-    my $self       = shift;
-    my $error_bars = shift;
-
-    if ( $error_bars->{_plus_values} ) {
-
-        # Write the c:plus element.
-        $self->xml_start_tag( 'c:plus' );
-
-        if ( ref $error_bars->{_plus_values} eq 'ARRAY' ) {
-            $self->_write_num_lit( $error_bars->{_plus_values} );
-        }
-        else {
-            $self->_write_num_ref( $error_bars->{_plus_values},
-                $error_bars->{_plus_data}, 'num' );
-        }
-
-        $self->xml_end_tag( 'c:plus' );
-    }
-
-    if ( $error_bars->{_minus_values} ) {
-
-        # Write the c:minus element.
-        $self->xml_start_tag( 'c:minus' );
-
-        if ( ref $error_bars->{_minus_values} eq 'ARRAY' ) {
-            $self->_write_num_lit( $error_bars->{_minus_values} );
-        }
-        else {
-            $self->_write_num_ref( $error_bars->{_minus_values},
-                $error_bars->{_minus_data}, 'num' );
-        }
-
-        $self->xml_end_tag( 'c:minus' );
-    }
-}
-
-
-
-##############################################################################
-#
-# _write_num_lit()
-#
-# Write the <c:numLit> element for literal number list elements.
-#
-sub _write_num_lit {
-
-    my $self = shift;
-    my $data  = shift;
-    my $count = @$data;
-
-
-    # Write the c:numLit element.
-    $self->xml_start_tag( 'c:numLit' );
-
-    # Write the c:formatCode element.
-    $self->_write_format_code( 'General' );
-
-    # Write the c:ptCount element.
-    $self->_write_pt_count( $count );
-
-    for my $i ( 0 .. $count - 1 ) {
-        my $token = $data->[$i];
-
-        # Write non-numeric data as 0.
-        if ( defined $token
-            && $token !~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ )
-        {
-            $token = 0;
-        }
-
-        # Write the c:pt element.
-        $self->_write_pt( $i, $token );
-    }
-
-    $self->xml_end_tag( 'c:numLit' );
-
-
-}
-
-
-##############################################################################
-#
-# _write_up_down_bars()
-#
-# Write the <c:upDownBars> element.
-#
-sub _write_up_down_bars {
-
-    my $self         = shift;
-    my $up_down_bars = $self->{_up_down_bars};
-
-    return unless $up_down_bars;
-
-    $self->xml_start_tag( 'c:upDownBars' );
-
-    # Write the c:gapWidth element.
-    $self->_write_gap_width( 150 );
-
-    # Write the c:upBars element.
-    $self->_write_up_bars( $up_down_bars->{_up} );
-
-    # Write the c:downBars element.
-    $self->_write_down_bars( $up_down_bars->{_down} );
-
-    $self->xml_end_tag( 'c:upDownBars' );
-}
-
-
-##############################################################################
-#
-# _write_gap_width()
-#
-# Write the <c:gapWidth> element.
-#
-sub _write_gap_width {
-
-    my $self = shift;
-    my $val  = shift;
-
-    return if !defined $val;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:gapWidth', @attributes );
-}
-
-##############################################################################
-#
-# _write_up_bars()
-#
-# Write the <c:upBars> element.
-#
-sub _write_up_bars {
-
-    my $self   = shift;
-    my $format = shift;
-
-    if ( $format->{_line}->{_defined} || $format->{_fill}->{_defined} ) {
-
-        $self->xml_start_tag( 'c:upBars' );
-
-        # Write the c:spPr element.
-        $self->_write_sp_pr( $format );
-
-        $self->xml_end_tag( 'c:upBars' );
-    }
-    else {
-        $self->xml_empty_tag( 'c:upBars' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_down_bars()
-#
-# Write the <c:downBars> element.
-#
-sub _write_down_bars {
-
-    my $self   = shift;
-    my $format = shift;
-
-    if ( $format->{_line}->{_defined} || $format->{_fill}->{_defined} ) {
-
-        $self->xml_start_tag( 'c:downBars' );
-
-        # Write the c:spPr element.
-        $self->_write_sp_pr( $format );
-
-        $self->xml_end_tag( 'c:downBars' );
-    }
-    else {
-        $self->xml_empty_tag( 'c:downBars' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_c_smooth()
-#
-# Write the <c:smooth> element.
-#
-sub _write_c_smooth {
-
-    my $self    = shift;
-    my $smooth  = shift;
-
-    return unless $smooth;
-
-    my @attributes = ( 'val' => 1 );
-
-    $self->xml_empty_tag( 'c:smooth', @attributes );
-}
-
-##############################################################################
-#
-# _write_disp_units()
-#
-# Write the <c:dispUnits> element.
-#
-sub _write_disp_units {
-
-    my $self    = shift;
-    my $units   = shift;
-    my $display = shift;
-
-    return if not $units;
-
-    my @attributes = ( 'val' => $units );
-
-    $self->xml_start_tag( 'c:dispUnits' );
-
-    $self->xml_empty_tag( 'c:builtInUnit', @attributes );
-
-    if ( $display ) {
-        $self->xml_start_tag( 'c:dispUnitsLbl' );
-        $self->xml_empty_tag( 'c:layout' );
-        $self->xml_end_tag( 'c:dispUnitsLbl' );
-    }
-
-    $self->xml_end_tag( 'c:dispUnits' );
-}
-
-
-##############################################################################
-#
-# _write_a_grad_fill()
-#
-# Write the <a:gradFill> element.
-#
-sub _write_a_grad_fill {
-
-    my $self     = shift;
-    my $gradient = shift;
-
-
-    my @attributes = (
-        'flip'         => 'none',
-        'rotWithShape' => 1,
-    );
-
-
-    if ( $gradient->{_type} eq 'linear' ) {
-        @attributes = ();
-    }
-
-    $self->xml_start_tag( 'a:gradFill', @attributes );
-
-    # Write the a:gsLst element.
-    $self->_write_a_gs_lst( $gradient );
-
-    if ( $gradient->{_type} eq 'linear' ) {
-        # Write the a:lin element.
-        $self->_write_a_lin( $gradient->{_angle} );
-    }
-    else {
-        # Write the a:path element.
-        $self->_write_a_path( $gradient->{_type} );
-
-        # Write the a:tileRect element.
-        $self->_write_a_tile_rect( $gradient->{_type} );
-    }
-
-    $self->xml_end_tag( 'a:gradFill' );
-}
-
-
-##############################################################################
-#
-# _write_a_gs_lst()
-#
-# Write the <a:gsLst> element.
-#
-sub _write_a_gs_lst {
-
-    my $self      = shift;
-    my $gradient  = shift;
-    my $positions = $gradient->{_positions};
-    my $colors    = $gradient->{_colors};
-
-    $self->xml_start_tag( 'a:gsLst' );
-
-    for my $i ( 0 .. @$colors -1 ) {
-
-        my $pos = int($positions->[$i] * 1000);
-
-        my @attributes = ( 'pos' => $pos );
-        $self->xml_start_tag( 'a:gs', @attributes );
-
-        my $color = $self->_get_color( $colors->[$i] );
-
-        # Write the a:srgbClr element.
-        # TODO: Wait for a feature request to support transparency.
-        $self->_write_a_srgb_clr( $color );
-
-        $self->xml_end_tag( 'a:gs' );
-    }
-
-    $self->xml_end_tag( 'a:gsLst' );
-}
-
-
-##############################################################################
-#
-# _write_a_lin()
-#
-# Write the <a:lin> element.
-#
-sub _write_a_lin {
-
-    my $self   = shift;
-    my $angle  = shift;
-    my $scaled = 0;
-
-    $angle = int( 60000 * $angle );
-
-    my @attributes = (
-        'ang'    => $angle,
-        'scaled' => $scaled,
-    );
-
-    $self->xml_empty_tag( 'a:lin', @attributes );
-}
-
-##############################################################################
-#
-# _write_a_path()
-#
-# Write the <a:path> element.
-#
-sub _write_a_path {
-
-    my $self = shift;
-    my $type = shift;
-
-
-    my @attributes = ( 'path' => $type );
-
-    $self->xml_start_tag( 'a:path', @attributes );
-
-    # Write the a:fillToRect element.
-    $self->_write_a_fill_to_rect( $type );
-
-    $self->xml_end_tag( 'a:path' );
-}
-
-
-##############################################################################
-#
-# _write_a_fill_to_rect()
-#
-# Write the <a:fillToRect> element.
-#
-sub _write_a_fill_to_rect {
-
-    my $self       = shift;
-    my $type       = shift;
-    my @attributes = ();
-
-    if ( $type eq 'shape' ) {
-        @attributes = (
-            'l' => 50000,
-            't' => 50000,
-            'r' => 50000,
-            'b' => 50000,
-        );
-
-    }
-    else {
-        @attributes = (
-            'l' => 100000,
-            't' => 100000,
-        );
-    }
-
-
-    $self->xml_empty_tag( 'a:fillToRect', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_a_tile_rect()
-#
-# Write the <a:tileRect> element.
-#
-sub _write_a_tile_rect {
-
-    my $self       = shift;
-    my $type       = shift;
-    my @attributes = ();
-
-    if ( $type eq 'shape' ) {
-        @attributes = ();
-    }
-    else {
-        @attributes = (
-            'r' => -100000,
-            'b' => -100000,
-        );
-    }
-
-    $self->xml_empty_tag( 'a:tileRect', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_a_patt_fill()
-#
-# Write the <a:pattFill> element.
-#
-sub _write_a_patt_fill {
-
-    my $self     = shift;
-    my $pattern  = shift;
-
-    my @attributes = ( 'prst' => $pattern->{pattern} );
-
-    $self->xml_start_tag( 'a:pattFill', @attributes );
-
-    # Write the a:fgClr element.
-    $self->_write_a_fg_clr( $pattern->{fg_color} );
-
-    # Write the a:bgClr element.
-    $self->_write_a_bg_clr( $pattern->{bg_color} );
-
-    $self->xml_end_tag( 'a:pattFill' );
-}
-
-
-##############################################################################
-#
-# _write_a_fg_clr()
-#
-# Write the <a:fgClr> element.
-#
-sub _write_a_fg_clr {
-
-    my $self  = shift;
-    my $color = shift;
-
-    $color = $self->_get_color( $color );
-
-    $self->xml_start_tag( 'a:fgClr' );
-
-    # Write the a:srgbClr element.
-    $self->_write_a_srgb_clr( $color );
-
-    $self->xml_end_tag( 'a:fgClr' );
-}
-
-
-
-##############################################################################
-#
-# _write_a_bg_clr()
-#
-# Write the <a:bgClr> element.
-#
-sub _write_a_bg_clr {
-
-    my $self  = shift;
-    my $color = shift;
-
-    $color = $self->_get_color( $color );
-
-    $self->xml_start_tag( 'a:bgClr' );
-
-    # Write the a:srgbClr element.
-    $self->_write_a_srgb_clr( $color );
-
-    $self->xml_end_tag( 'a:bgClr' );
-}
-
-
-1;
-
-__END__
-
-
-=head1 NAME
-
-Chart - A class for writing Excel Charts.
-
-=head1 SYNOPSIS
-
-To create a simple Excel file with a chart using Excel::Writer::XLSX:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    # 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 );
-
-    # Add a worksheet chart.
-    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',
-    );
-
-    $workbook->close();
-
-    __END__
-
-
-=head1 DESCRIPTION
-
-The C<Chart> module is an abstract base class for modules that implement charts in L<Excel::Writer::XLSX>. The information below is applicable to all of the available subclasses.
-
-The C<Chart> module isn't used directly. A chart object is created via the Workbook C<add_chart()> method where the chart type is specified:
-
-    my $chart = $workbook->add_chart( type => 'column' );
-
-Currently the supported chart types are:
-
-=over
-
-=item * C<area>
-
-Creates an Area (filled line) style chart. See L<Excel::Writer::XLSX::Chart::Area>.
-
-=item * C<bar>
-
-Creates a Bar style (transposed histogram) chart. See L<Excel::Writer::XLSX::Chart::Bar>.
-
-=item * C<column>
-
-Creates a column style (histogram) chart. See L<Excel::Writer::XLSX::Chart::Column>.
-
-=item * C<line>
-
-Creates a Line style chart. See L<Excel::Writer::XLSX::Chart::Line>.
-
-=item * C<pie>
-
-Creates a Pie style chart. See L<Excel::Writer::XLSX::Chart::Pie>.
-
-=item * C<doughnut>
-
-Creates a Doughnut style chart. See L<Excel::Writer::XLSX::Chart::Doughnut>.
-
-=item * C<scatter>
-
-Creates a Scatter style chart. See L<Excel::Writer::XLSX::Chart::Scatter>.
-
-=item * C<stock>
-
-Creates a Stock style chart. See L<Excel::Writer::XLSX::Chart::Stock>.
-
-=item * C<radar>
-
-Creates a Radar style chart. See L<Excel::Writer::XLSX::Chart::Radar>.
-
-=back
-
-Chart subtypes are also supported in some cases:
-
-    $workbook->add_chart( type => 'bar', subtype => 'stacked' );
-
-The currently available subtypes are:
-
-    area
-        stacked
-        percent_stacked
-
-    bar
-        stacked
-        percent_stacked
-
-    column
-        stacked
-        percent_stacked
-
-    scatter
-        straight_with_markers
-        straight
-        smooth_with_markers
-        smooth
-
-    radar
-        with_markers
-        filled
-
-More charts and sub-types will be supported in time. See the L</TODO> section.
-
-
-=head1 CHART METHODS
-
-Methods that are common to all chart types are documented below. See the documentation for each of the above chart modules for chart specific information.
-
-=head2 add_series()
-
-In an Excel chart a "series" is a collection of information such as values, X axis labels and the formatting that define which data is plotted.
-
-With an Excel::Writer::XLSX chart object the C<add_series()> method is used to set the properties for a series:
-
-    $chart->add_series(
-        categories => '=Sheet1!$A$2:$A$10', # Optional.
-        values     => '=Sheet1!$B$2:$B$10', # Required.
-        line       => { color => 'blue' },
-    );
-
-The properties that can be set are:
-
-=over
-
-=item * C<values>
-
-This is the most important property of a series and must be set for every chart object. It links the chart with the worksheet data that it displays. A formula or array ref can be used for the data range, see below.
-
-=item * C<categories>
-
-This sets the chart category labels. The category is more or less the same as the X axis. In most chart types the C<categories> property is optional and the chart will just assume a sequential series from C<1 .. n>.
-
-=item * C<name>
-
-Set the name for the series. The name is displayed in the chart legend and in the formula bar. The name property is optional and if it isn't supplied it will default to C<Series 1 .. n>.
-
-=item * C<line>
-
-Set the properties of the series line type such as colour and width. See the L</CHART FORMATTING> section below.
-
-=item * C<border>
-
-Set the border properties of the series such as colour and style. See the L</CHART FORMATTING> section below.
-
-=item * C<fill>
-
-Set the fill properties of the series such as colour. See the L</CHART FORMATTING> section below.
-
-=item * C<pattern>
-
-Set the pattern properties of the series. See the L</CHART FORMATTING> section below.
-
-=item * C<gradien>
-
-Set the gradient properties of the series. See the L</CHART FORMATTING> section below.
-
-=item * C<marker>
-
-Set the properties of the series marker such as style and colour. See the L</SERIES OPTIONS> section below.
-
-=item * C<trendline>
-
-Set the properties of the series trendline such as linear, polynomial and moving average types. See the L</SERIES OPTIONS> section below.
-
-=item * C<smooth>
-
-The C<smooth> option is used to set the smooth property of a line series. See the L</SERIES OPTIONS> section below.
-
-=item * C<y_error_bars>
-
-Set vertical error bounds for a chart series. See the L</SERIES OPTIONS> section below.
-
-=item * C<x_error_bars>
-
-Set horizontal error bounds for a chart series. See the L</SERIES OPTIONS> section below.
-
-=item * C<data_labels>
-
-Set data labels for the series. See the L</SERIES OPTIONS> section below.
-
-=item * C<points>
-
-Set properties for individual points in a series. See the L</SERIES OPTIONS> section below.
-
-=item * C<invert_if_negative>
-
-Invert the fill colour for negative values. Usually only applicable to column and bar charts.
-
-=item * C<overlap>
-
-Set the overlap between series in a Bar/Column chart. The range is +/- 100. Default is 0.
-
-    overlap => 20,
-
-Note, it is only necessary to apply this property to one series of the chart.
-
-=item * C<gap>
-
-Set the gap between series in a Bar/Column chart. The range is 0 to 500. Default is 150.
-
-    gap => 200,
-
-Note, it is only necessary to apply this property to one series of the chart.
-
-=back
-
-The C<categories> and C<values> can take either a range formula such as C<=Sheet1!$A$2:$A$7> or, more usefully when generating the range programmatically, an array ref with zero indexed row/column values:
-
-     [ $sheetname, $row_start, $row_end, $col_start, $col_end ]
-
-The following are equivalent:
-
-    $chart->add_series( categories => '=Sheet1!$A$2:$A$7'      ); # Same as ...
-    $chart->add_series( categories => [ 'Sheet1', 1, 6, 0, 0 ] ); # Zero-indexed.
-
-You can add more than one series to a chart. In fact, some chart types such as C<stock> require it. The series numbering and order in the Excel chart will be the same as the order in which they are added in Excel::Writer::XLSX.
-
-    # Add the first series.
-    $chart->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-        name       => 'Test data series 1',
-    );
-
-    # Add another series. Same categories. Different range values.
-    $chart->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-        name       => 'Test data series 2',
-    );
-
-It is also possible to specify non-contiguous ranges:
-
-    $chart->add_series(
-        categories      => '=(Sheet1!$A$1:$A$9,Sheet1!$A$14:$A$25)',
-        values          => '=(Sheet1!$B$1:$B$9,Sheet1!$B$14:$B$25)',
-    );
-
-
-=head2 set_x_axis()
-
-The C<set_x_axis()> method is used to set properties of the X axis.
-
-    $chart->set_x_axis( name => 'Quarterly results' );
-
-The properties that can be set are:
-
-    name
-    name_font
-    name_layout
-    num_font
-    num_format
-    line
-    fill
-    pattern
-    gradient
-    min
-    max
-    minor_unit
-    major_unit
-    interval_unit
-    interval_tick
-    crossing
-    reverse
-    position_axis
-    log_base
-    label_position
-    major_gridlines
-    minor_gridlines
-    visible
-    date_axis
-    text_axis
-    minor_unit_type
-    major_unit_type
-    minor_tick_mark
-    major_tick_mark
-    display_units
-    display_units_visible
-
-These are explained below. Some properties are only applicable to value or category axes, as indicated. See L<Value and Category Axes> for an explanation of Excel's distinction between the axis types.
-
-=over
-
-=item * C<name>
-
-
-Set the name (title or caption) for the axis. The name is displayed below the X axis. The C<name> property is optional. The default is to have no axis name. (Applicable to category and value axes).
-
-    $chart->set_x_axis( name => 'Quarterly results' );
-
-The name can also be a formula such as C<=Sheet1!$A$1>.
-
-=item * C<name_font>
-
-Set the font properties for the axis title. (Applicable to category and value axes).
-
-    $chart->set_x_axis( name_font => { name => 'Arial', size => 10 } );
-
-=item * C<name_layout>
-
-Set the C<(x, y)> position of the axis caption in chart relative units. (Applicable to category and value axes).
-
-    $chart->set_x_axis(
-        name        => 'X axis',
-        name_layout => {
-            x => 0.34,
-            y => 0.85,
-        }
-    );
-
-See the L</CHART LAYOUT> section below.
-
-=item * C<num_font>
-
-Set the font properties for the axis numbers. (Applicable to category and value axes).
-
-    $chart->set_x_axis( num_font => { bold => 1, italic => 1 } );
-
-See the L</CHART FONTS> section below.
-
-=item * C<num_format>
-
-Set the number format for the axis. (Applicable to category and value axes).
-
-    $chart->set_x_axis( num_format => '#,##0.00' );
-    $chart->set_y_axis( num_format => '0.00%'    );
-
-The number format is similar to the Worksheet Cell Format C<num_format> apart from the fact that a format index cannot be used. The explicit format string must be used as shown above. See L<Excel::Writer::XLSX/set_num_format()> for more information.
-
-=item * C<line>
-
-Set the properties of the axis line type such as colour and width. See the L</CHART FORMATTING> section below.
-
-    $chart->set_x_axis( line => { none => 1 });
-
-
-=item * C<fill>
-
-Set the fill properties of the axis such as colour. See the L</CHART FORMATTING> section below. Note, in Excel the axis fill is applied to the area of the numbers of the axis and not to the area of the axis bounding box. That background is set from the chartarea fill.
-
-=item * C<pattern>
-
-Set the pattern properties of the axis such as colour. See the L</CHART FORMATTING> section below.
-
-=item * C<gradient>
-
-Set the gradient properties of the axis such as colour. See the L</CHART FORMATTING> section below.
-
-=item * C<min>
-
-Set the minimum value for the axis range. (Applicable to value axes only.)
-
-    $chart->set_x_axis( min => 20 );
-
-=item * C<max>
-
-Set the maximum value for the axis range. (Applicable to value axes only.)
-
-    $chart->set_x_axis( max => 80 );
-
-=item * C<minor_unit>
-
-Set the increment of the minor units in the axis range. (Applicable to value axes only.)
-
-    $chart->set_x_axis( minor_unit => 0.4 );
-
-=item * C<major_unit>
-
-Set the increment of the major units in the axis range. (Applicable to value axes only.)
-
-    $chart->set_x_axis( major_unit => 2 );
-
-=item * C<interval_unit>
-
-Set the interval unit for a category axis. (Applicable to category axes only.)
-
-    $chart->set_x_axis( interval_unit => 2 );
-
-=item * C<interval_tick>
-
-Set the tick interval for a category axis. (Applicable to category axes only.)
-
-    $chart->set_x_axis( interval_tick => 4 );
-
-=item * C<crossing>
-
-Set the position where the y axis will cross the x axis. (Applicable to category and value axes.)
-
-The C<crossing> value can either be the string C<'max'> to set the crossing at the maximum axis value or a numeric value.
-
-    $chart->set_x_axis( crossing => 3 );
-    # or
-    $chart->set_x_axis( crossing => 'max' );
-
-B<For category axes the numeric value must be an integer> to represent the category number that the axis crosses at. For value axes it can have any value associated with the axis.
-
-If crossing is omitted (the default) the crossing will be set automatically by Excel based on the chart data.
-
-=item * C<position_axis>
-
-Position the axis on or between the axis tick marks. (Applicable to category axes only.)
-
-There are two allowable values C<on_tick> and C<between>:
-
-    $chart->set_x_axis( position_axis => 'on_tick' );
-    $chart->set_x_axis( position_axis => 'between' );
-
-=item * C<reverse>
-
-Reverse the order of the axis categories or values. (Applicable to category and value axes.)
-
-    $chart->set_x_axis( reverse => 1 );
-
-=item * C<log_base>
-
-Set the log base of the axis range. (Applicable to value axes only.)
-
-    $chart->set_x_axis( log_base => 10 );
-
-=item * C<label_position>
-
-Set the "Axis labels" position for the axis. The following positions are available:
-
-    next_to (the default)
-    high
-    low
-    none
-
-=item * C<major_gridlines>
-
-Configure the major gridlines for the axis. The available properties are:
-
-    visible
-    line
-
-For example:
-
-    $chart->set_x_axis(
-        major_gridlines => {
-            visible => 1,
-            line    => { color => 'red', width => 1.25, dash_type => 'dash' }
-        }
-    );
-
-The C<visible> property is usually on for the X-axis but it depends on the type of chart.
-
-The C<line> property sets the gridline properties such as colour and width. See the L</CHART FORMATTING> section below.
-
-=item * C<minor_gridlines>
-
-This takes the same options as C<major_gridlines> above.
-
-The minor gridline C<visible> property is off by default for all chart types.
-
-=item * C<visible>
-
-Configure the visibility of the axis.
-
-    $chart->set_x_axis( visible => 0 );
-
-
-=item * C<date_axis>
-
-This option is used to treat a category axis with date or time data as a Date Axis. (Applicable to category axes only.)
-
-    $chart->set_x_axis( date_axis => 1 );
-
-This option also allows you to set C<max> and C<min> values for a category axis which isn't allowed by Excel for non-date category axes.
-
-See L<Date Category Axes> for more details.
-
-=item * C<text_axis>
-
-This option is used to treat a category axis explicitly as a Text Axis. (Applicable to category axes only.)
-
-    $chart->set_x_axis( text_axis => 1 );
-
-
-=item * C<minor_unit_type>
-
-For C<date_axis> axes, see above, this option is used to set the type of the minor units. (Applicable to date category axes only.)
-
-    $chart->set_x_axis(
-        date_axis         => 1,
-        minor_unit        => 4,
-        minor_unit_type   => 'months',
-    );
-
-The allowable values for this option are C<days>, C<months> and C<years>.
-
-=item * C<major_unit_type>
-
-Same as C<minor_unit_type>, see above, but for major axes unit types.
-
-More than one property can be set in a call to C<set_x_axis()>:
-
-    $chart->set_x_axis(
-        name => 'Quarterly results',
-        min  => 10,
-        max  => 80,
-    );
-
-=item * C<major_tick_mark>
-
-Set the axis major tick mark type to one of the following values:
-
-    none
-    inside
-    outside
-    cross   (inside and outside)
-
-For example:
-
-    $chart->set_x_axis( major_tick_mark => 'none',
-                        minor_tick_mark => 'inside' );
-
-=item * C<minor_tick_mark>
-
-Set the axis minor tick mark type. Same as C<major_tick_mark>, see above.
-
-=item * C<display_units>
-
-Set the display units for the axis. This can be useful if the axis numbers are very large but you don't want to represent them in scientific notation. (Applicable to value axes only.) The available display units are:
-
-    hundreds
-    thousands
-    ten_thousands
-    hundred_thousands
-    millions
-    ten_millions
-    hundred_millions
-    billions
-    trillions
-
-Example:
-
-    $chart->set_x_axis( display_units => 'thousands' )
-    $chart->set_y_axis( display_units => 'millions' )
-
-
-* C<display_units_visible>
-
-Control the visibility of the display units turned on by the previous option. This option is on by default. (Applicable to value axes only.)::
-
-    $chart->set_x_axis( display_units         => 'thousands',
-                        display_units_visible => 0 )
-
-=back
-
-=head2 set_y_axis()
-
-The C<set_y_axis()> method is used to set properties of the Y axis. The properties that can be set are the same as for C<set_x_axis>, see above.
-
-
-=head2 set_x2_axis()
-
-The C<set_x2_axis()> method is used to set properties of the secondary X axis.
-The properties that can be set are the same as for C<set_x_axis>, see above.
-The default properties for this axis are:
-
-    label_position => 'none',
-    crossing       => 'max',
-    visible        => 0,
-
-
-=head2 set_y2_axis()
-
-The C<set_y2_axis()> method is used to set properties of the secondary Y axis.
-The properties that can be set are the same as for C<set_x_axis>, see above.
-The default properties for this axis are:
-
-    major_gridlines => { visible => 0 }
-
-
-=head2 combine()
-
-The chart C<combine()> method is used to combine two charts of different
-types, for example a column and line chart:
-
-    my $column_chart = $workbook->add_chart( type => 'column', embedded => 1 );
-
-    # Configure the data series for the primary chart.
-    $column_chart->add_series(...);
-
-    # Create a new column chart. This will use this as the secondary chart.
-    my $line_chart = $workbook->add_chart( type => 'line', embedded => 1 );
-
-    # Configure the data series for the secondary chart.
-    $line_chart->add_series(...);
-
-    # Combine the charts.
-    $column_chart->combine( $line_chart );
-
-See L<Combined Charts> for more details.
-
-
-=head2 set_size()
-
-The C<set_size()> method is used to set the dimensions of the chart. The size properties that can be set are:
-
-     width
-     height
-     x_scale
-     y_scale
-     x_offset
-     y_offset
-
-The C<width> and C<height> are in pixels. The default chart width is 480 pixels and the default height is 288 pixels. The size of the chart can be modified by setting the C<width> and C<height> or by setting the C<x_scale> and C<y_scale>:
-
-    $chart->set_size( width => 720, height => 576 );
-
-    # Same as:
-
-    $chart->set_size( x_scale => 1.5, y_scale => 2 );
-
-The C<x_offset> and C<y_offset> position the top left corner of the chart in the cell that it is inserted into.
-
-
-Note: the C<x_scale>, C<y_scale>, C<x_offset> and C<y_offset> parameters can also be set via the C<insert_chart()> method:
-
-    $worksheet->insert_chart( 'E2', $chart, 2, 4, 1.5, 2 );
-
-
-=head2 set_title()
-
-The C<set_title()> method is used to set properties of the chart title.
-
-    $chart->set_title( name => 'Year End Results' );
-
-The properties that can be set are:
-
-=over
-
-=item * C<name>
-
-Set the name (title) for the chart. The name is displayed above the chart. The name can also be a formula such as C<=Sheet1!$A$1>. The name property is optional. The default is to have no chart title.
-
-=item * C<name_font>
-
-Set the font properties for the chart title. See the L</CHART FONTS> section below.
-
-=item * C<overlay>
-
-Allow the title to be overlaid on the chart. Generally used with the layout property below.
-
-=item * C<layout>
-
-Set the C<(x, y)> position of the title in chart relative units:
-
-    $chart->set_title(
-        name    => 'Title',
-        overlay => 1,
-        layout  => {
-            x => 0.42,
-            y => 0.14,
-        }
-    );
-
-See the L</CHART LAYOUT> section below.
-
-=item * C<none>
-
-By default Excel adds an automatic chart title to charts with a single series and a user defined series name. The C<none> option turns this default title off. It also turns off all other C<set_title()> options.
-
-    $chart->set_title( none => 1 );
-
-=back
-
-
-=head2 set_legend()
-
-The C<set_legend()> method is used to set properties of the chart legend.
-
-
-The properties that can be set are:
-
-=over
-
-=item * C<none>
-
-The C<none> option turns off the chart legend. In Excel chart legends are on by default:
-
-    $chart->set_legend( none => 1 );
-
-Note, for backward compatibility, it is also possible to turn off the legend via the C<position> property:
-
-    $chart->set_legend( position => 'none' );
-
-=item * C<position>
-
-Set the position of the chart legend.
-
-    $chart->set_legend( position => 'bottom' );
-
-The default legend position is C<right>. The available positions are:
-
-    top
-    bottom
-    left
-    right
-    overlay_left
-    overlay_right
-    none
-
-=item * C<layout>
-
-Set the C<(x, y)> position of the legend in chart relative units:
-
-    $chart->set_legend(
-        layout => {
-            x      => 0.80,
-            y      => 0.37,
-            width  => 0.12,
-            height => 0.25,
-        }
-    );
-
-See the L</CHART LAYOUT> section below.
-
-
-=item * C<delete_series>
-
-This allows you to remove 1 or more series from the legend (the series will still display on the chart). This property takes an array ref as an argument and the series are zero indexed:
-
-    # Delete/hide series index 0 and 2 from the legend.
-    $chart->set_legend( delete_series => [0, 2] );
-
-=item * C<font>
-
-Set the font properties of the chart legend:
-
-    $chart->set_legend( font => { bold => 1, italic => 1 } );
-
-See the L</CHART FONTS> section below.
-
-
-=back
-
-
-=head2 set_chartarea()
-
-The C<set_chartarea()> method is used to set the properties of the chart area.
-
-    $chart->set_chartarea(
-        border => { none  => 1 },
-        fill   => { color => 'red' }
-    );
-
-The properties that can be set are:
-
-=over
-
-=item * C<border>
-
-Set the border properties of the chartarea such as colour and style. See the L</CHART FORMATTING> section below.
-
-=item * C<fill>
-
-Set the fill properties of the chartarea such as colour. See the L</CHART FORMATTING> section below.
-
-=item * C<pattern>
-
-Set the pattern fill properties of the chartarea. See the L</CHART FORMATTING> section below.
-
-=item * C<gradient>
-
-Set the gradient fill properties of the chartarea. See the L</CHART FORMATTING> section below.
-
-
-=back
-
-=head2 set_plotarea()
-
-The C<set_plotarea()> method is used to set properties of the plot area of a chart.
-
-    $chart->set_plotarea(
-        border => { color => 'yellow', width => 1, dash_type => 'dash' },
-        fill   => { color => '#92D050' }
-    );
-
-The properties that can be set are:
-
-=over
-
-=item * C<border>
-
-Set the border properties of the plotarea such as colour and style. See the L</CHART FORMATTING> section below.
-
-=item * C<fill>
-
-Set the fill properties of the plotarea such as colour. See the L</CHART FORMATTING> section below.
-
-
-=item * C<pattern>
-
-Set the pattern fill properties of the plotarea. See the L</CHART FORMATTING> section below.
-
-=item * C<gradient>
-
-Set the gradient fill properties of the plotarea. See the L</CHART FORMATTING> section below.
-
-=item * C<layout>
-
-Set the C<(x, y)> position of the plotarea in chart relative units:
-
-    $chart->set_plotarea(
-        layout => {
-            x      => 0.35,
-            y      => 0.26,
-            width  => 0.62,
-            height => 0.50,
-        }
-    );
-
-See the L</CHART LAYOUT> section below.
-
-=back
-
-
-=head2 set_style()
-
-The C<set_style()> method is used to set the style of the chart to one of the 42 built-in styles available on the 'Design' tab in Excel:
-
-    $chart->set_style( 4 );
-
-The default style is 2.
-
-
-=head2 set_table()
-
-The C<set_table()> method adds a data table below the horizontal axis with the data used to plot the chart.
-
-    $chart->set_table();
-
-The available options, with default values are:
-
-    vertical   => 1    # Display vertical lines in the table.
-    horizontal => 1    # Display horizontal lines in the table.
-    outline    => 1    # Display an outline in the table.
-    show_keys  => 0    # Show the legend keys with the table data.
-    font       => {}   # Standard chart font properties.
-
-The data table can only be shown with Bar, Column, Line, Area and stock charts. For font properties see the L</CHART FONTS> section below.
-
-
-=head2 set_up_down_bars
-
-The C<set_up_down_bars()> method adds Up-Down bars to Line charts to indicate the difference between the first and last data series.
-
-    $chart->set_up_down_bars();
-
-It is possible to format the up and down bars to add C<fill>, C<pattern>, C<gradient> and C<border> properties if required. See the L</CHART FORMATTING> section below.
-
-    $chart->set_up_down_bars(
-        up   => { fill => { color => 'green' } },
-        down => { fill => { color => 'red' } },
-    );
-
-Up-down bars can only be applied to Line charts and to Stock charts (by default).
-
-
-=head2 set_drop_lines
-
-The C<set_drop_lines()> method adds Drop Lines to charts to show the Category value of points in the data.
-
-    $chart->set_drop_lines();
-
-It is possible to format the Drop Line C<line> properties if required. See the L</CHART FORMATTING> section below.
-
-    $chart->set_drop_lines( line => { color => 'red', dash_type => 'square_dot' } );
-
-Drop Lines are only available in Line, Area and Stock charts.
-
-
-=head2 set_high_low_lines
-
-The C<set_high_low_lines()> method adds High-Low lines to charts to show the maximum and minimum values of points in a Category.
-
-    $chart->set_high_low_lines();
-
-It is possible to format the High-Low Line C<line> properties if required. See the L</CHART FORMATTING> section below.
-
-    $chart->set_high_low_lines( line => { color => 'red' } );
-
-High-Low Lines are only available in Line and Stock charts.
-
-
-=head2 show_blanks_as()
-
-The C<show_blanks_as()> method controls how blank data is displayed in a chart.
-
-    $chart->show_blanks_as( 'span' );
-
-The available options are:
-
-        gap    # Blank data is shown as a gap. The default.
-        zero   # Blank data is displayed as zero.
-        span   # Blank data is connected with a line.
-
-
-=head2 show_hidden_data()
-
-Display data in hidden rows or columns on the chart.
-
-    $chart->show_hidden_data();
-
-
-=head1 SERIES OPTIONS
-
-This section details the following properties of C<add_series()> in more detail:
-
-    marker
-    trendline
-    y_error_bars
-    x_error_bars
-    data_labels
-    points
-    smooth
-
-=head2 Marker
-
-The marker format specifies the properties of the markers used to distinguish series on a chart. In general only Line and Scatter chart types and trendlines use markers.
-
-The following properties can be set for C<marker> formats in a chart.
-
-    type
-    size
-    border
-    fill
-    pattern
-    gradient
-
-The C<type> property sets the type of marker that is used with a series.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        marker     => { type => 'diamond' },
-    );
-
-The following C<type> properties can be set for C<marker> formats in a chart. These are shown in the same order as in the Excel format dialog.
-
-    automatic
-    none
-    square
-    diamond
-    triangle
-    x
-    star
-    short_dash
-    long_dash
-    circle
-    plus
-
-The C<automatic> type is a special case which turns on a marker using the default marker style for the particular series number.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        marker     => { type => 'automatic' },
-    );
-
-If C<automatic> is on then other marker properties such as size, border or fill cannot be set.
-
-The C<size> property sets the size of the marker and is generally used in conjunction with C<type>.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        marker     => { type => 'diamond', size => 7 },
-    );
-
-Nested C<border> and C<fill> properties can also be set for a marker. See the L</CHART FORMATTING> section below.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        marker     => {
-            type    => 'square',
-            size    => 5,
-            border  => { color => 'red' },
-            fill    => { color => 'yellow' },
-        },
-    );
-
-
-=head2 Trendline
-
-A trendline can be added to a chart series to indicate trends in the data such as a moving average or a polynomial fit.
-
-The following properties can be set for trendlines in a chart series.
-
-    type
-    order               (for polynomial trends)
-    period              (for moving average)
-    forward             (for all except moving average)
-    backward            (for all except moving average)
-    name
-    line
-    intercept           (for exponential, linear and polynomial only)
-    display_equation    (for all except moving average)
-    display_r_squared   (for all except moving average)
-
-
-The C<type> property sets the type of trendline in the series.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        trendline  => { type => 'linear' },
-    );
-
-The available C<trendline> types are:
-
-    exponential
-    linear
-    log
-    moving_average
-    polynomial
-    power
-
-A C<polynomial> trendline can also specify the C<order> of the polynomial. The default value is 2.
-
-    $chart->add_series(
-        values    => '=Sheet1!$B$1:$B$5',
-        trendline => {
-            type  => 'polynomial',
-            order => 3,
-        },
-    );
-
-A C<moving_average> trendline can also specify the C<period> of the moving average. The default value is 2.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        trendline  => {
-            type   => 'moving_average',
-            period => 3,
-        },
-    );
-
-The C<forward> and C<backward> properties set the forecast period of the trendline.
-
-    $chart->add_series(
-        values    => '=Sheet1!$B$1:$B$5',
-        trendline => {
-            type     => 'linear',
-            forward  => 0.5,
-            backward => 0.5,
-        },
-    );
-
-The C<name> property sets an optional name for the trendline that will appear in the chart legend. If it isn't specified the Excel default name will be displayed. This is usually a combination of the trendline type and the series name.
-
-    $chart->add_series(
-        values    => '=Sheet1!$B$1:$B$5',
-        trendline => {
-            type => 'linear',
-            name => 'Interpolated trend',
-        },
-    );
-
-The C<intercept> property sets the point where the trendline crosses the Y (value) axis:
-
-    $chart->add_series(
-        values    => '=Sheet1!$B$1:$B$5',
-        trendline => {
-            type      => 'linear',
-            intercept => 0.8,
-        },
-    );
-
-
-The C<display_equation> property displays the trendline equation on the chart.
-
-    $chart->add_series(
-        values    => '=Sheet1!$B$1:$B$5',
-        trendline => {
-            type             => 'linear',
-            display_equation => 1,
-        },
-    );
-
-The C<display_r_squared> property displays the R squared value of the trendline on the chart.
-
-    $chart->add_series(
-        values    => '=Sheet1!$B$1:$B$5',
-        trendline => {
-            type              => 'linear',
-            display_r_squared => 1
-        },
-    );
-
-
-Several of these properties can be set in one go:
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        trendline  => {
-            type              => 'polynomial',
-            name              => 'My trend name',
-            order             => 2,
-            forward           => 0.5,
-            backward          => 0.5,
-            intercept         => 1.5,
-            display_equation  => 1,
-            display_r_squared => 1,
-            line              => {
-                color     => 'red',
-                width     => 1,
-                dash_type => 'long_dash',
-            }
-        },
-    );
-
-Trendlines cannot be added to series in a stacked chart or pie chart, radar chart, doughnut or (when implemented) to 3D, or surface charts.
-
-=head2 Error Bars
-
-Error bars can be added to a chart series to indicate error bounds in the data. The error bars can be vertical C<y_error_bars> (the most common type) or horizontal C<x_error_bars> (for Bar and Scatter charts only).
-
-The following properties can be set for error bars in a chart series.
-
-    type
-    value        (for all types except standard error and custom)
-    plus_values  (for custom only)
-    minus_values (for custom only)
-    direction
-    end_style
-    line
-
-The C<type> property sets the type of error bars in the series.
-
-    $chart->add_series(
-        values       => '=Sheet1!$B$1:$B$5',
-        y_error_bars => { type => 'standard_error' },
-    );
-
-The available error bars types are available:
-
-    fixed
-    percentage
-    standard_deviation
-    standard_error
-    custom
-
-All error bar types, except for C<standard_error> and C<custom> must also have a value associated with it for the error bounds:
-
-    $chart->add_series(
-        values       => '=Sheet1!$B$1:$B$5',
-        y_error_bars => {
-            type  => 'percentage',
-            value => 5,
-        },
-    );
-
-The C<custom> error bar type must specify C<plus_values> and C<minus_values> which should either by a C<Sheet1!$A$1:$A$5> type range formula or an arrayref of
-values:
-
-    $chart->add_series(
-        categories   => '=Sheet1!$A$1:$A$5',
-        values       => '=Sheet1!$B$1:$B$5',
-        y_error_bars => {
-            type         => 'custom',
-            plus_values  => '=Sheet1!$C$1:$C$5',
-            minus_values => '=Sheet1!$D$1:$D$5',
-        },
-    );
-
-    # or
-
-
-    $chart->add_series(
-        categories   => '=Sheet1!$A$1:$A$5',
-        values       => '=Sheet1!$B$1:$B$5',
-        y_error_bars => {
-            type         => 'custom',
-            plus_values  => [1, 1, 1, 1, 1],
-            minus_values => [2, 2, 2, 2, 2],
-        },
-    );
-
-Note, as in Excel the items in the C<minus_values> do not need to be negative.
-
-The C<direction> property sets the direction of the error bars. It should be one of the following:
-
-    plus    # Positive direction only.
-    minus   # Negative direction only.
-    both    # Plus and minus directions, The default.
-
-The C<end_style> property sets the style of the error bar end cap. The options are 1 (the default) or 0 (for no end cap):
-
-    $chart->add_series(
-        values       => '=Sheet1!$B$1:$B$5',
-        y_error_bars => {
-            type      => 'fixed',
-            value     => 2,
-            end_style => 0,
-            direction => 'minus'
-        },
-    );
-
-
-
-=head2 Data Labels
-
-Data labels can be added to a chart series to indicate the values of the plotted data points.
-
-The following properties can be set for C<data_labels> formats in a chart.
-
-    value
-    category
-    series_name
-    position
-    percentage
-    leader_lines
-    separator
-    legend_key
-    num_format
-    font
-
-The C<value> property turns on the I<Value> data label for a series.
-
-    $chart->add_series(
-        values      => '=Sheet1!$B$1:$B$5',
-        data_labels => { value => 1 },
-    );
-
-The C<category> property turns on the I<Category Name> data label for a series.
-
-    $chart->add_series(
-        values      => '=Sheet1!$B$1:$B$5',
-        data_labels => { category => 1 },
-    );
-
-
-The C<series_name> property turns on the I<Series Name> data label for a series.
-
-    $chart->add_series(
-        values      => '=Sheet1!$B$1:$B$5',
-        data_labels => { series_name => 1 },
-    );
-
-The C<position> property is used to position the data label for a series.
-
-    $chart->add_series(
-        values      => '=Sheet1!$B$1:$B$5',
-        data_labels => { value => 1, position => 'center' },
-    );
-
-In Excel the data label positions vary for different chart types. The allowable positions are:
-
-    |  Position     |  Line     |  Bar      |  Pie      |  Area     |
-    |               |  Scatter  |  Column   |  Doughnut |  Radar    |
-    |               |  Stock    |           |           |           |
-    |---------------|-----------|-----------|-----------|-----------|
-    |  center       |  Yes      |  Yes      |  Yes      |  Yes*     |
-    |  right        |  Yes*     |           |           |           |
-    |  left         |  Yes      |           |           |           |
-    |  above        |  Yes      |           |           |           |
-    |  below        |  Yes      |           |           |           |
-    |  inside_base  |           |  Yes      |           |           |
-    |  inside_end   |           |  Yes      |  Yes      |           |
-    |  outside_end  |           |  Yes*     |  Yes      |           |
-    |  best_fit     |           |           |  Yes*     |           |
-
-Note: The * indicates the default position for each chart type in Excel, if a position isn't specified.
-
-The C<percentage> property is used to turn on the display of data labels as a I<Percentage> for a series. It is mainly used for pie and doughnut charts.
-
-    $chart->add_series(
-        values      => '=Sheet1!$B$1:$B$5',
-        data_labels => { percentage => 1 },
-    );
-
-The C<leader_lines> property is used to turn on  I<Leader Lines> for the data label for a series. It is mainly used for pie charts.
-
-    $chart->add_series(
-        values      => '=Sheet1!$B$1:$B$5',
-        data_labels => { value => 1, leader_lines => 1 },
-    );
-
-Note: Even when leader lines are turned on they aren't automatically visible in Excel or Excel::Writer::XLSX. Due to an Excel limitation (or design) leader lines only appear if the data label is moved manually or if the data labels are very close and need to be adjusted automatically.
-
-The C<separator> property is used to change the separator between multiple data label items:
-
-    $chart->add_series(
-        values      => '=Sheet1!$B$1:$B$5',
-        data_labels => { percentage => 1 },
-        data_labels => { value => 1, category => 1, separator => "\n" },
-    );
-
-The separator value must be one of the following strings:
-
-            ','
-            ';'
-            '.'
-            "\n"
-            ' '
-
-The C<legend_key> property is used to turn on  I<Legend Key> for the data label for a series:
-
-    $chart->add_series(
-        values      => '=Sheet1!$B$1:$B$5',
-        data_labels => { value => 1, legend_key => 1 },
-    );
-
-
-The C<num_format> property is used to set the number format for the data labels.
-
-    $chart->add_series(
-        values      => '=Sheet1!$A$1:$A$5',
-        data_labels => { value => 1, num_format => '#,##0.00' },
-    );
-
-The number format is similar to the Worksheet Cell Format C<num_format> apart from the fact that a format index cannot be used. The explicit format string must be used as shown above. See L<Excel::Writer::XLSX/set_num_format()> for more information.
-
-The C<font> property is used to set the font properties of the data labels in a series:
-
-    $chart->add_series(
-        values      => '=Sheet1!$A$1:$A$5',
-        data_labels => {
-            value => 1,
-            font  => { name => 'Consolas' }
-        },
-    );
-
-The C<font> property is also used to rotate the data labels in a series:
-
-    $chart->add_series(
-        values      => '=Sheet1!$A$1:$A$5',
-        data_labels => {
-            value => 1,
-            font  => { rotation => 45 }
-        },
-    );
-
-See the L</CHART FONTS> section below.
-
-
-=head2 Points
-
-In general formatting is applied to an entire series in a chart. However, it is occasionally required to format individual points in a series. In particular this is required for Pie and Doughnut charts where each segment is represented by a point.
-
-In these cases it is possible to use the C<points> property of C<add_series()>:
-
-    $chart->add_series(
-        values => '=Sheet1!$A$1:$A$3',
-        points => [
-            { fill => { color => '#FF0000' } },
-            { fill => { color => '#CC0000' } },
-            { fill => { color => '#990000' } },
-        ],
-    );
-
-The C<points> property takes an array ref of format options (see the L</CHART FORMATTING> section below). To assign default properties to points in a series pass C<undef> values in the array ref:
-
-    # Format point 3 of 3 only.
-    $chart->add_series(
-        values => '=Sheet1!$A$1:$A$3',
-        points => [
-            undef,
-            undef,
-            { fill => { color => '#990000' } },
-        ],
-    );
-
-    # Format the first point only.
-    $chart->add_series(
-        values => '=Sheet1!$A$1:$A$3',
-        points => [ { fill => { color => '#FF0000' } } ],
-    );
-
-=head2 Smooth
-
-The C<smooth> option is used to set the smooth property of a line series. It is only applicable to the C<Line> and C<Scatter> chart types.
-
-    $chart->add_series( values => '=Sheet1!$C$1:$C$5',
-                        smooth => 1 );
-
-
-=head1 CHART FORMATTING
-
-The following chart formatting properties can be set for any chart object that they apply to (and that are supported by Excel::Writer::XLSX) such as chart lines, column fill areas, plot area borders, markers, gridlines and other chart elements documented above.
-
-    line
-    border
-    fill
-    pattern
-    gradient
-
-Chart formatting properties are generally set using hash refs.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        line       => { color => 'blue' },
-    );
-
-In some cases the format properties can be nested. For example a C<marker> may contain C<border> and C<fill> sub-properties.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        line       => { color => 'blue' },
-        marker     => {
-            type    => 'square',
-            size    => 5,
-            border  => { color => 'red' },
-            fill    => { color => 'yellow' },
-        },
-    );
-
-=head2 Line
-
-The line format is used to specify properties of line objects that appear in a chart such as a plotted line on a chart or a border.
-
-The following properties can be set for C<line> formats in a chart.
-
-    none
-    color
-    width
-    dash_type
-
-
-The C<none> property is uses to turn the C<line> off (it is always on by default except in Scatter charts). This is useful if you wish to plot a series with markers but without a line.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        line       => { none => 1 },
-    );
-
-
-The C<color> property sets the color of the C<line>.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        line       => { color => 'red' },
-    );
-
-The available colours are shown in the main L<Excel::Writer::XLSX> documentation. It is also possible to set the colour of a line with a HTML style RGB colour:
-
-    $chart->add_series(
-        line       => { color => '#FF0000' },
-    );
-
-
-The C<width> property sets the width of the C<line>. It should be specified in increments of 0.25 of a point as in Excel.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        line       => { width => 3.25 },
-    );
-
-The C<dash_type> property sets the dash style of the line.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        line       => { dash_type => 'dash_dot' },
-    );
-
-The following C<dash_type> values are available. They are shown in the order that they appear in the Excel dialog.
-
-    solid
-    round_dot
-    square_dot
-    dash
-    dash_dot
-    long_dash
-    long_dash_dot
-    long_dash_dot_dot
-
-The default line style is C<solid>.
-
-More than one C<line> property can be specified at a time:
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        line       => {
-            color     => 'red',
-            width     => 1.25,
-            dash_type => 'square_dot',
-        },
-    );
-
-=head2 Border
-
-The C<border> property is a synonym for C<line>.
-
-It can be used as a descriptive substitute for C<line> in chart types such as Bar and Column that have a border and fill style rather than a line style. In general chart objects with a C<border> property will also have a fill property.
-
-
-=head2 Solid Fill
-
-The fill format is used to specify filled areas of chart objects such as the interior of a column or the background of the chart itself.
-
-The following properties can be set for C<fill> formats in a chart.
-
-    none
-    color
-    transparency
-
-The C<none> property is used to turn the C<fill> property off (it is generally on by default).
-
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        fill       => { none => 1 },
-    );
-
-The C<color> property sets the colour of the C<fill> area.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        fill       => { color => 'red' },
-    );
-
-The available colours are shown in the main L<Excel::Writer::XLSX> documentation. It is also possible to set the colour of a fill with a HTML style RGB colour:
-
-    $chart->add_series(
-        fill       => { color => '#FF0000' },
-    );
-
-The C<transparency> property sets the transparency of the solid fill color in the integer range 1 - 100:
-
-    $chart->set_chartarea( fill => { color => 'yellow', transparency => 75 } );
-
-The C<fill> format is generally used in conjunction with a C<border> format which has the same properties as a C<line> format.
-
-    $chart->add_series(
-        values     => '=Sheet1!$B$1:$B$5',
-        border     => { color => 'red' },
-        fill       => { color => 'yellow' },
-    );
-
-
-
-=head2 Pattern Fill
-
-The pattern fill format is used to specify pattern filled areas of chart objects such as the interior of a column or the background of the chart itself.
-
-The following properties can be set for C<pattern> fill formats in a chart:
-
-    pattern:   the pattern to be applied (required)
-    fg_color:  the foreground color of the pattern (required)
-    bg_color:  the background color (optional, defaults to white)
-
-
-For example:
-
-    $chart->set_plotarea(
-        pattern => {
-            pattern  => 'percent_5',
-            fg_color => 'red',
-            bg_color => 'yellow',
-        }
-    );
-
-The following patterns can be applied:
-
-    percent_5
-    percent_10
-    percent_20
-    percent_25
-    percent_30
-    percent_40
-    percent_50
-    percent_60
-    percent_70
-    percent_75
-    percent_80
-    percent_90
-    light_downward_diagonal
-    light_upward_diagonal
-    dark_downward_diagonal
-    dark_upward_diagonal
-    wide_downward_diagonal
-    wide_upward_diagonal
-    light_vertical
-    light_horizontal
-    narrow_vertical
-    narrow_horizontal
-    dark_vertical
-    dark_horizontal
-    dashed_downward_diagonal
-    dashed_upward_diagonal
-    dashed_horizontal
-    dashed_vertical
-    small_confetti
-    large_confetti
-    zigzag
-    wave
-    diagonal_brick
-    horizontal_brick
-    weave
-    plaid
-    divot
-    dotted_grid
-    dotted_diamond
-    shingle
-    trellis
-    sphere
-    small_grid
-    large_grid
-    small_check
-    large_check
-    outlined_diamond
-    solid_diamond
-
-
-The foreground color, C<fg_color>, is a required parameter and can be a Html style C<#RRGGBB> string or a limited number of named colors. The available colours are shown in the main L<Excel::Writer::XLSX> documentation.
-
-The background color, C<bg_color>, is optional and defaults to black.
-
-If a pattern fill is used on a chart object it overrides the solid fill properties of the object.
-
-
-=head2 Gradient Fill
-
-The gradient fill format is used to specify gradient filled areas of chart objects such as the interior of a column or the background of the chart itself.
-
-
-The following properties can be set for C<gradient> fill formats in a chart:
-
-    colors:    a list of colors
-    positions: an optional list of positions for the colors
-    type:      the optional type of gradient fill
-    angle:     the optional angle of the linear fill
-
-The C<colors> property sets a list of colors that define the C<gradient>:
-
-    $chart->set_plotarea(
-        gradient => { colors => [ '#DDEBCF', '#9CB86E', '#156B13' ] }
-    );
-
-Excel allows between 2 and 10 colors in a gradient but it is unlikely that you will require more than 2 or 3.
-
-As with solid or pattern fill it is also possible to set the colors of a gradient with a Html style C<#RRGGBB> string or a limited number of named colors. The available colours are shown in the main L<Excel::Writer::XLSX> documentation:
-
-    $chart->add_series(
-        values   => '=Sheet1!$A$1:$A$5',
-        gradient => { colors => [ 'red', 'green' ] }
-    );
-
-The C<positions> defines an optional list of positions, between 0 and 100, of
-where the colors in the gradient are located. Default values are provided for
-C<colors> lists of between 2 and 4 but they can be specified if required:
-
-    $chart->add_series(
-        values   => '=Sheet1!$A$1:$A$5',
-        gradient => {
-            colors    => [ '#DDEBCF', '#156B13' ],
-            positions => [ 10,        90 ],
-        }
-    );
-
-The C<type> property can have one of the following values:
-
-    linear        (the default)
-    radial
-    rectangular
-    path
-
-For example:
-
-    $chart->add_series(
-        values   => '=Sheet1!$A$1:$A$5',
-        gradient => {
-            colors => [ '#DDEBCF', '#9CB86E', '#156B13' ],
-            type   => 'radial'
-        }
-    );
-
-If C<type> isn't specified it defaults to C<linear>.
-
-For a C<linear> fill the angle of the gradient can also be specified:
-
-    $chart->add_series(
-        values   => '=Sheet1!$A$1:$A$5',
-        gradient => { colors => [ '#DDEBCF', '#9CB86E', '#156B13' ],
-                      angle => 30 }
-    );
-
-The default angle is 90 degrees.
-
-If gradient fill is used on a chart object it overrides the solid fill and pattern fill properties of the object.
-
-
-
-
-=head1 CHART FONTS
-
-The following font properties can be set for any chart object that they apply to (and that are supported by Excel::Writer::XLSX) such as chart titles, axis labels, axis numbering and data labels. They correspond to the equivalent Worksheet cell Format object properties. See L<Excel::Writer::XLSX/FORMAT_METHODS> for more information.
-
-    name
-    size
-    bold
-    italic
-    underline
-    rotation
-    color
-
-The following explains the available font properties:
-
-=over
-
-=item * C<name>
-
-Set the font name:
-
-    $chart->set_x_axis( num_font => { name => 'Arial' } );
-
-=item * C<size>
-
-Set the font size:
-
-    $chart->set_x_axis( num_font => { name => 'Arial', size => 10 } );
-
-=item * C<bold>
-
-Set the font bold property, should be 0 or 1:
-
-    $chart->set_x_axis( num_font => { bold => 1 } );
-
-=item * C<italic>
-
-Set the font italic property, should be 0 or 1:
-
-    $chart->set_x_axis( num_font => { italic => 1 } );
-
-=item * C<underline>
-
-Set the font underline property, should be 0 or 1:
-
-    $chart->set_x_axis( num_font => { underline => 1 } );
-
-=item * C<rotation>
-
-Set the font rotation in the range -90 to 90:
-
-    $chart->set_x_axis( num_font => { rotation => 45 } );
-
-This is useful for displaying large axis data such as dates in a more compact format.
-
-=item * C<color>
-
-Set the font color property. Can be a color index, a color name or HTML style RGB colour:
-
-    $chart->set_x_axis( num_font => { color => 'red' } );
-    $chart->set_y_axis( num_font => { color => '#92D050' } );
-
-=back
-
-Here is an example of Font formatting in a Chart program:
-
-    # Format the chart title.
-    $chart->set_title(
-        name      => 'Sales Results Chart',
-        name_font => {
-            name  => 'Calibri',
-            color => 'yellow',
-        },
-    );
-
-    # Format the X-axis.
-    $chart->set_x_axis(
-        name      => 'Month',
-        name_font => {
-            name  => 'Arial',
-            color => '#92D050'
-        },
-        num_font => {
-            name  => 'Courier New',
-            color => '#00B0F0',
-        },
-    );
-
-    # Format the Y-axis.
-    $chart->set_y_axis(
-        name      => 'Sales (1000 units)',
-        name_font => {
-            name      => 'Century',
-            underline => 1,
-            color     => 'red'
-        },
-        num_font => {
-            bold   => 1,
-            italic => 1,
-            color  => '#7030A0',
-        },
-    );
-
-
-
-=head1 CHART LAYOUT
-
-The position of the chart in the worksheet is controlled by the C<set_size()> method shown above.
-
-It is also possible to change the layout of the following chart sub-objects:
-
-    plotarea
-    legend
-    title
-    x_axis caption
-    y_axis caption
-
-Here are some examples:
-
-    $chart->set_plotarea(
-        layout => {
-            x      => 0.35,
-            y      => 0.26,
-            width  => 0.62,
-            height => 0.50,
-        }
-    );
-
-    $chart->set_legend(
-        layout => {
-            x      => 0.80,
-            y      => 0.37,
-            width  => 0.12,
-            height => 0.25,
-        }
-    );
-
-    $chart->set_title(
-        name   => 'Title',
-        layout => {
-            x => 0.42,
-            y => 0.14,
-        }
-    );
-
-    $chart->set_x_axis(
-        name        => 'X axis',
-        name_layout => {
-            x => 0.34,
-            y => 0.85,
-        }
-    );
-
-Note that it is only possible to change the width and height for the C<plotarea> and C<legend> objects. For the other text based objects the width and height are changed by the font dimensions.
-
-The layout units must be a float in the range C<0 < x <= 1> and are expressed as a percentage of the chart dimensions as shown below:
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/layout.png" width="826" height="423" alt="Chart object layout." /></center></p>
-
-=end html
-
-From this the layout units are calculated as follows:
-
-    layout:
-        width  = w / W
-        height = h / H
-        x      = a / W
-        y      = b / H
-
-These units are slightly cumbersome but are required by Excel so that the chart object positions remain relative to each other if the chart is resized by the user.
-
-Note that for C<plotarea> the origin is the top left corner in the plotarea itself and does not take into account the axes.
-
-
-=head1 WORKSHEET METHODS
-
-In Excel a chartsheet (i.e, a chart that isn't embedded) shares properties with data worksheets such as tab selection, headers, footers, margins, and print properties.
-
-In Excel::Writer::XLSX you can set chartsheet properties using the same methods that are used for Worksheet objects.
-
-The following Worksheet methods are also available through a non-embedded Chart object:
-
-    get_name()
-    activate()
-    select()
-    hide()
-    set_first_sheet()
-    protect()
-    set_zoom()
-    set_tab_color()
-
-    set_landscape()
-    set_portrait()
-    set_paper()
-    set_margins()
-    set_header()
-    set_footer()
-
-See L<Excel::Writer::XLSX> for a detailed explanation of these methods.
-
-=head1 EXAMPLE
-
-Here is a complete example that demonstrates some of the available features when creating a chart.
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-
-    ];
-
-    $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.
-    $chart->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-
-    # 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 (mm)' );
-
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart->set_style( 11 );
-
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D2', $chart, 25, 10 );
-
-    $workbook->close();
-
-    __END__
-
-=begin html
-
-<p>This will produce a chart that looks like this:</p>
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/area1.jpg" width="527" height="320" alt="Chart example." /></center></p>
-
-=end html
-
-
-=head1 Value and Category Axes
-
-Excel differentiates between a chart axis that is used for series B<categories> and an axis that is used for series B<values>.
-
-In the example above the X axis is the category axis and each of the values is evenly spaced. The Y axis (in this case) is the value axis and points are displayed according to their value.
-
-Since Excel treats the axes differently it also handles their formatting differently and exposes different properties for each.
-
-As such some of C<Excel::Writer::XLSX> axis properties can be set for a value axis, some can be set for a category axis and some properties can be set for both.
-
-For example the C<min> and C<max> properties can only be set for value axes and C<reverse> can be set for both. The type of axis that a property applies to is shown in the C<set_x_axis()> section of the documentation above.
-
-Some charts such as C<Scatter> and C<Stock> have two value axes.
-
-Date Axes are a special type of category axis which are explained below.
-
-=head1 Date Category Axes
-
-Date Category Axes are category axes that display time or date information. In Excel::Writer::XLSX Date Category Axes are set using the C<date_axis> option:
-
-    $chart->set_x_axis( date_axis => 1 );
-
-In general you should also specify a number format for a date axis although Excel will usually default to the same format as the data being plotted:
-
-    $chart->set_x_axis(
-        date_axis         => 1,
-        num_format        => 'dd/mm/yyyy',
-    );
-
-Excel doesn't normally allow minimum and maximum values to be set for category axes. However, date axes are an exception. The C<min> and C<max> values should be set as Excel times or dates:
-
-    $chart->set_x_axis(
-        date_axis         => 1,
-        min               => $worksheet->convert_date_time('2013-01-02T'),
-        max               => $worksheet->convert_date_time('2013-01-09T'),
-        num_format        => 'dd/mm/yyyy',
-    );
-
-For date axes it is also possible to set the type of the major and minor units:
-
-    $chart->set_x_axis(
-        date_axis         => 1,
-        minor_unit        => 4,
-        minor_unit_type   => 'months',
-        major_unit        => 1,
-        major_unit_type   => 'years',
-        num_format        => 'dd/mm/yyyy',
-    );
-
-
-=head1 Secondary Axes
-
-It is possible to add a secondary axis of the same type to a chart by setting the C<y2_axis> or C<x2_axis> property of the series:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_secondary_axis.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    # Add the worksheet data that the charts will refer to.
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-
-    ];
-
-    $worksheet->write( 'A1', $data );
-
-    # Create a new chart object. In this case an embedded chart.
-    my $chart = $workbook->add_chart( type => 'line', embedded => 1 );
-
-    # Configure a series with a secondary axis
-    $chart->add_series(
-        values  => '=Sheet1!$A$1:$A$6',
-        y2_axis => 1,
-    );
-
-    $chart->add_series(
-        values => '=Sheet1!$B$1:$B$6',
-    );
-
-
-    # Insert the chart into the worksheet.
-    $worksheet->insert_chart( 'D2', $chart );
-
-    $workbook->close();
-
-    __END__
-
-It is also possible to have a secondary, combined, chart either with a shared or secondary axis, see below.
-
-=head1 Combined Charts
-
-It is also possible to combine two different chart types, for example a column and line chart to create a Pareto chart using the Chart C<combine()> method:
-
-
-=begin html
-
-<p><center><img src="https://raw.githubusercontent.com/jmcnamara/XlsxWriter/master/dev/docs/source/_images/chart_pareto.png" alt="Chart image." /></center></p>
-
-=end html
-
-
-Here is a simpler example:
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_combined.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-
-    ];
-
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write( 'A2', $data );
-
-    #
-    # In the first example we will create a combined column and line chart.
-    # They will share the same X and Y axes.
-    #
-
-    # Create a new column chart. This will use this as the primary chart.
-    my $column_chart = $workbook->add_chart( type => 'column', embedded => 1 );
-
-    # Configure the data series for the primary chart.
-    $column_chart->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-
-    # Create a new column chart. This will use this as the secondary chart.
-    my $line_chart = $workbook->add_chart( type => 'line', embedded => 1 );
-
-    # Configure the data series for the secondary chart.
-    $line_chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-    );
-
-    # Combine the charts.
-    $column_chart->combine( $line_chart );
-
-    # Add a chart title and some axis labels. Note, this is done via the
-    # primary chart.
-    $column_chart->set_title( name => 'Combined chart - same Y axis' );
-    $column_chart->set_x_axis( name => 'Test number' );
-    $column_chart->set_y_axis( name => 'Sample length (mm)' );
-
-
-    # Insert the chart into the worksheet
-    $worksheet->insert_chart( 'E2', $column_chart );
-
-    $workbook->close();
-
-=begin html
-
-<p><center><img src="https://raw.githubusercontent.com/jmcnamara/XlsxWriter/master/dev/docs/source/_images/chart_combined1.png" alt="Chart image." /></center></p>
-
-=end html
-
-
-
-The secondary chart can also be placed on a secondary axis using the methods shown in the previous section.
-
-In this case it is just necessary to add a C<y2_axis> parameter to the series and, if required, add a title using C<set_y2_axis()> B<of the secondary chart>. The following are the additions to the previous example to place the secondary chart on the secondary axis:
-
-    ...
-
-    $line_chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-        y2_axis    => 1,
-    );
-
-    ...
-
-    # Note: the y2 properites are on the secondary chart.
-    $line_chart2->set_y2_axis( name => 'Target length (mm)' );
-
-
-=begin html
-
-<p><center><img src="https://raw.githubusercontent.com/jmcnamara/XlsxWriter/master/dev/docs/source/_images/chart_combined2.png" alt="Chart image." /></center></p>
-
-=end html
-
-
-The examples above use the concept of a I<primary> and I<secondary> chart. The primary chart is the chart that defines the primary X and Y axis. It is also used for setting all chart properties apart from the secondary data series. For example the chart title and axes properties should be set via the primary chart (except for the the secondary C<y2> axis properties which should be applied to the secondary chart).
-
-See also C<chart_combined.pl> and C<chart_pareto.pl> examples in the distro for more detailed
-examples.
-
-There are some limitations on combined charts:
-
-=over
-
-=item * Pie charts cannot currently be combined.
-
-=item * Scatter charts cannot currently be used as a primary chart but they can be used as a secondary chart.
-
-=item * Bar charts can only combined secondary charts on a secondary axis. This is an Excel limitation.
-
-=back
-
-
-
-=head1 TODO
-
-Chart features that are on the TODO list and will hopefully be added are:
-
-=over
-
-=item * Add more chart sub-types.
-
-=item * Additional formatting options.
-
-=item * More axis controls.
-
-=item * 3D charts.
-
-=item * Additional chart types.
-
-=back
-
-If you are interested in sponsoring a feature to have it implemented or expedited let me know.
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chart/Area.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chart/Area.pm
deleted file mode 100644 (file)
index 5c88804..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-package Excel::Writer::XLSX::Chart::Area;
-
-###############################################################################
-#
-# Area - A class for writing Excel Area charts.
-#
-# Used in conjunction with Excel::Writer::XLSX::Chart.
-#
-# See formatting note in Excel::Writer::XLSX::Chart.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Chart;
-
-our @ISA     = qw(Excel::Writer::XLSX::Chart);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# new()
-#
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Chart->new( @_ );
-
-    $self->{_subtype}       = $self->{_subtype} || 'standard';
-    $self->{_cross_between} = 'midCat';
-    $self->{_show_crosses}  = 0;
-
-    # Override and reset the default axis values.
-    if ( $self->{_subtype} eq 'percent_stacked' ) {
-        $self->{_y_axis}->{_defaults}->{num_format} = '0%';
-    }
-
-    $self->set_y_axis();
-
-    # Sset the available data label positions for this chart type.
-    $self->{_label_position_default} = 'center';
-    $self->{_label_positions} = { center => 'ctr' };
-
-    bless $self, $class;
-    return $self;
-}
-
-
-##############################################################################
-#
-# _write_chart_type()
-#
-# Override the virtual superclass method with a chart specific method.
-#
-sub _write_chart_type {
-
-    my $self = shift;
-
-    # Write the c:areaChart element.
-    $self->_write_area_chart( @_ );
-}
-
-
-##############################################################################
-#
-# _write_area_chart()
-#
-# Write the <c:areaChart> element.
-#
-sub _write_area_chart {
-
-    my $self = shift;
-    my %args = @_;
-
-    my @series;
-    if ( $args{primary_axes} ) {
-        @series = $self->_get_primary_axes_series;
-    }
-    else {
-        @series = $self->_get_secondary_axes_series;
-    }
-
-    return unless scalar @series;
-
-    my $subtype = $self->{_subtype};
-
-    $subtype = 'percentStacked' if $subtype eq 'percent_stacked';
-
-    $self->xml_start_tag( 'c:areaChart' );
-
-    # Write the c:grouping element.
-    $self->_write_grouping( $subtype );
-
-    # Write the series elements.
-    $self->_write_series( $_ ) for @series;
-
-    # Write the c:dropLines element.
-    $self->_write_drop_lines();
-
-    # Write the c:axId elements
-    $self->_write_axis_ids( %args );
-
-    $self->xml_end_tag( 'c:areaChart' );
-}
-
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Area - A class for writing Excel Area charts.
-
-=head1 SYNOPSIS
-
-To create a simple Excel file with an Area chart using Excel::Writer::XLSX:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    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<Excel::Writer::XLSX>. 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<Excel::Writer::XLSX::Chart>. Class specific methods or settings, if any, are explained below.
-
-=head1 Area Chart Subtypes
-
-
-The C<Area> chart module also supports the following sub-types:
-
-    stacked
-    percent_stacked
-
-These can be specified at creation time via the C<add_chart()> Worksheet method:
-
-    my $chart = $workbook->add_chart( type => 'area', subtype => 'stacked' );
-
-=head1 EXAMPLE
-
-Here is a complete example that demonstrates most of the available features when creating a chart.
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_area.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2, 3, 4, 5, 6, 7 ],
-        [ 40, 40, 50, 30, 25, 50 ],
-        [ 30, 25, 30, 10,  5, 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 => 'area', embedded => 1 );
-
-    # Configure the first series.
-    $chart->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-
-    # 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 (mm)' );
-
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart->set_style( 11 );
-
-    # 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://jmcnamara.github.io/excel-writer-xlsx/images/examples/area1.jpg" width="483" height="291" alt="Chart example." /></center></p>
-
-=end html
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
-
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chart/Bar.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chart/Bar.pm
deleted file mode 100644 (file)
index 35ff2da..0000000
+++ /dev/null
@@ -1,352 +0,0 @@
-package Excel::Writer::XLSX::Chart::Bar;
-
-###############################################################################
-#
-# Bar - A class for writing Excel Bar charts.
-#
-# Used in conjunction with Excel::Writer::XLSX::Chart.
-#
-# See formatting note in Excel::Writer::XLSX::Chart.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Chart;
-
-our @ISA     = qw(Excel::Writer::XLSX::Chart);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# new()
-#
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Chart->new( @_ );
-
-    $self->{_subtype}           = $self->{_subtype} || 'clustered';
-    $self->{_cat_axis_position} = 'l';
-    $self->{_val_axis_position} = 'b';
-    $self->{_horiz_val_axis}    = 0;
-    $self->{_horiz_cat_axis}    = 1;
-    $self->{_show_crosses}      = 0;
-
-    # Override and reset the default axis values.
-    $self->{_x_axis}->{_defaults}->{major_gridlines} = { visible => 1 };
-    $self->{_y_axis}->{_defaults}->{major_gridlines} = { visible => 0 };
-
-    if ( $self->{_subtype} eq 'percent_stacked' ) {
-        $self->{_x_axis}->{_defaults}->{num_format} = '0%';
-    }
-
-    $self->set_x_axis();
-    $self->set_y_axis();
-
-    # Set the available data label positions for this chart type.
-    $self->{_label_position_default} = 'outside_end';
-    $self->{_label_positions} = {
-        center      => 'ctr',
-        inside_base => 'inBase',
-        inside_end  => 'inEnd',
-        outside_end => 'outEnd',
-    };
-
-    bless $self, $class;
-    return $self;
-}
-
-
-###############################################################################
-#
-# combine()
-#
-# Override parent method to add an extra check that is required for Bar
-# charts to ensure that their combined chart is on a secondary axis.
-#
-sub combine {
-
-    my $self  = shift;
-    my $chart = shift;
-
-    if (!$chart->{_is_secondary}) {
-        carp 'Charts combined with Bar charts must be on a secondary axis';
-        return;
-    }
-
-    $self->{_combined} = $chart;
-}
-
-
-##############################################################################
-#
-# _write_chart_type()
-#
-# Override the virtual superclass method with a chart specific method.
-#
-sub _write_chart_type {
-
-    my $self = shift;
-    my %args = @_;
-
-    if ( $args{primary_axes} ) {
-
-        # Reverse X and Y axes for Bar charts.
-        my $tmp = $self->{_y_axis};
-        $self->{_y_axis} = $self->{_x_axis};
-        $self->{_x_axis} = $tmp;
-
-        if ( $self->{_y2_axis}->{_position} eq 'r' ) {
-            $self->{_y2_axis}->{_position} = 't';
-        }
-    }
-
-    # Write the c:barChart element.
-    $self->_write_bar_chart( @_ );
-}
-
-
-##############################################################################
-#
-# _write_bar_chart()
-#
-# Write the <c:barChart> element.
-#
-sub _write_bar_chart {
-
-    my $self = shift;
-    my %args = @_;
-
-    my @series;
-    if ( $args{primary_axes} ) {
-        @series = $self->_get_primary_axes_series;
-    }
-    else {
-        @series = $self->_get_secondary_axes_series;
-    }
-
-    return unless scalar @series;
-
-    my $subtype = $self->{_subtype};
-    $subtype = 'percentStacked' if $subtype eq 'percent_stacked';
-
-    # Set a default overlap for stacked charts.
-    if ($self->{_subtype} =~ /stacked/) {
-        if (!defined $self->{_series_overlap_1}) {
-            $self->{_series_overlap_1} = 100;
-        }
-    }
-
-    $self->xml_start_tag( 'c:barChart' );
-
-    # Write the c:barDir element.
-    $self->_write_bar_dir();
-
-    # Write the c:grouping element.
-    $self->_write_grouping( $subtype );
-
-    # Write the c:ser elements.
-    $self->_write_ser( $_ ) for @series;
-
-    if ( $args{primary_axes} ) {
-        # Write the c:gapWidth element.
-        $self->_write_gap_width( $self->{_series_gap_1} );
-
-        # Write the c:overlap element.
-        $self->_write_overlap( $self->{_series_overlap_1} );
-    }
-    else {
-        # Write the c:gapWidth element.
-        $self->_write_gap_width( $self->{_series_gap_2} );
-
-        # Write the c:overlap element.
-        $self->_write_overlap( $self->{_series_overlap_2} );
-    }
-
-    # Write the c:axId elements
-    $self->_write_axis_ids( %args );
-
-    $self->xml_end_tag( 'c:barChart' );
-}
-
-
-##############################################################################
-#
-# _write_bar_dir()
-#
-# Write the <c:barDir> element.
-#
-sub _write_bar_dir {
-
-    my $self = shift;
-    my $val  = 'bar';
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:barDir', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_err_dir()
-#
-# Write the <c:errDir> element. Overridden from Chart class since it is not
-# used in Bar charts.
-#
-sub _write_err_dir {}
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Bar - A class for writing Excel Bar charts.
-
-=head1 SYNOPSIS
-
-To create a simple Excel file with a Bar chart using Excel::Writer::XLSX:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    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<Excel::Writer::XLSX>. 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<Excel::Writer::XLSX::Chart>. Class specific methods or settings, if any, are explained below.
-
-=head1 Bar Chart Subtypes
-
-The C<Bar> chart module also supports the following sub-types:
-
-    stacked
-    percent_stacked
-
-These can be specified at creation time via the C<add_chart()> Worksheet method:
-
-    my $chart = $workbook->add_chart( type => 'bar', subtype => 'stacked' );
-
-=head1 EXAMPLE
-
-Here is a complete example that demonstrates most of the available features when creating a chart.
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_bar.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-
-    ];
-
-    $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.
-    $chart->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-
-    # 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 (mm)' );
-
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart->set_style( 11 );
-
-    # 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://jmcnamara.github.io/excel-writer-xlsx/images/examples/bar1.jpg" width="483" height="291" alt="Chart example." /></center></p>
-
-=end html
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
-
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chart/Column.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chart/Column.pm
deleted file mode 100644 (file)
index 34d60ca..0000000
+++ /dev/null
@@ -1,312 +0,0 @@
-package Excel::Writer::XLSX::Chart::Column;
-
-###############################################################################
-#
-# Column - A class for writing Excel Column charts.
-#
-# Used in conjunction with Excel::Writer::XLSX::Chart.
-#
-# See formatting note in Excel::Writer::XLSX::Chart.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Chart;
-
-our @ISA     = qw(Excel::Writer::XLSX::Chart);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# new()
-#
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Chart->new( @_ );
-
-    $self->{_subtype} = $self->{_subtype} || 'clustered';
-    $self->{_horiz_val_axis} = 0;
-
-    # Override and reset the default axis values.
-    if ( $self->{_subtype} eq 'percent_stacked' ) {
-        $self->{_y_axis}->{_defaults}->{num_format} = '0%';
-    }
-
-    $self->set_y_axis();
-
-    # Set the available data label positions for this chart type.
-    $self->{_label_position_default} = 'outside_end';
-    $self->{_label_positions} = {
-        center      => 'ctr',
-        inside_base => 'inBase',
-        inside_end  => 'inEnd',
-        outside_end => 'outEnd',
-    };
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-##############################################################################
-#
-# _write_chart_type()
-#
-# Override the virtual superclass method with a chart specific method.
-#
-sub _write_chart_type {
-
-    my $self = shift;
-
-    # Write the c:barChart element.
-    $self->_write_bar_chart( @_ );
-}
-
-
-##############################################################################
-#
-# _write_bar_chart()
-#
-# Write the <c:barChart> element.
-#
-sub _write_bar_chart {
-
-    my $self = shift;
-    my %args = @_;
-
-    my @series;
-    if ( $args{primary_axes} ) {
-        @series = $self->_get_primary_axes_series;
-    }
-    else {
-        @series = $self->_get_secondary_axes_series;
-    }
-
-    return unless scalar @series;
-
-    my $subtype = $self->{_subtype};
-    $subtype = 'percentStacked' if $subtype eq 'percent_stacked';
-
-    # Set a default overlap for stacked charts.
-    if ($self->{_subtype} =~ /stacked/) {
-        if (!defined $self->{_series_overlap_1}) {
-            $self->{_series_overlap_1} = 100;
-        }
-    }
-
-    $self->xml_start_tag( 'c:barChart' );
-
-    # Write the c:barDir element.
-    $self->_write_bar_dir();
-
-    # Write the c:grouping element.
-    $self->_write_grouping( $subtype );
-
-    # Write the c:ser elements.
-    $self->_write_ser( $_ ) for @series;
-
-    if ( $args{primary_axes} ) {
-        # Write the c:gapWidth element.
-        $self->_write_gap_width( $self->{_series_gap_1} );
-
-        # Write the c:overlap element.
-        $self->_write_overlap( $self->{_series_overlap_1} );
-    }
-    else {
-        # Write the c:gapWidth element.
-        $self->_write_gap_width( $self->{_series_gap_2} );
-
-        # Write the c:overlap element.
-        $self->_write_overlap( $self->{_series_overlap_2} );
-    }
-
-    # Write the c:axId elements
-    $self->_write_axis_ids( %args );
-
-    $self->xml_end_tag( 'c:barChart' );
-}
-
-
-##############################################################################
-#
-# _write_bar_dir()
-#
-# Write the <c:barDir> element.
-#
-sub _write_bar_dir {
-
-    my $self = shift;
-    my $val  = 'col';
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:barDir', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_err_dir()
-#
-# Write the <c:errDir> element. Overridden from Chart class since it is not
-# used in Bar charts.
-#
-sub _write_err_dir {}
-
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Column - A class for writing Excel Column charts.
-
-=head1 SYNOPSIS
-
-To create a simple Excel file with a Column chart using Excel::Writer::XLSX:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    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<Excel::Writer::XLSX>. 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<Excel::Writer::XLSX::Chart>. Class specific methods or settings, if any, are explained below.
-
-=head1 Column Chart Subtypes
-
-The C<Column> chart module also supports the following sub-types:
-
-    stacked
-    percent_stacked
-
-These can be specified at creation time via the C<add_chart()> Worksheet method:
-
-    my $chart = $workbook->add_chart( type => 'column', subtype => 'stacked' );
-
-=head1 EXAMPLE
-
-Here is a complete example that demonstrates most of the available features when creating a chart.
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_column.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2, 3, 4, 5, 6, 7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-
-    ];
-
-    $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.
-    $chart->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-
-    # 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 (mm)' );
-
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart->set_style( 11 );
-
-    # 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://jmcnamara.github.io/excel-writer-xlsx/images/examples/column1.jpg" width="483" height="291" alt="Chart example." /></center></p>
-
-=end html
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
-
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chart/Doughnut.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chart/Doughnut.pm
deleted file mode 100644 (file)
index ec3d0be..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-package Excel::Writer::XLSX::Chart::Doughnut;
-
-###############################################################################
-#
-# Doughnut - A class for writing Excel Doughnut charts.
-#
-# Used in conjunction with Excel::Writer::XLSX::Chart.
-#
-# See formatting note in Excel::Writer::XLSX::Chart.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Chart::Pie;
-
-our @ISA     = qw(Excel::Writer::XLSX::Chart::Pie);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# new()
-#
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Chart::Pie->new( @_ );
-
-    $self->{_vary_data_color} = 1;
-    $self->{_hole_size}       = 50;
-    $self->{_rotation}        = 0;
-
-    bless $self, $class;
-    return $self;
-}
-
-
-###############################################################################
-#
-# set_hole_size()
-#
-# Set the Doughnut chart hole size.
-#
-sub set_hole_size {
-
-    my $self = shift;
-    my $size = shift;
-
-    return if !defined $size;
-
-    if ( $size >= 10 && $size <= 90 ) {
-        $self->{_hole_size} = $size;
-    }
-    else {
-        carp "Hole size $size outside Excel range: 10 <= size <= 90";
-    }
-}
-
-
-##############################################################################
-#
-# _write_chart_type()
-#
-# Override the virtual superclass method with a chart specific method.
-#
-sub _write_chart_type {
-
-    my $self = shift;
-
-    # Write the c:doughnutChart element.
-    $self->_write_doughnut_chart( @_ );
-}
-
-
-##############################################################################
-#
-# _write_doughnut_chart()
-#
-# Write the <c:doughnutChart> element. Over-ridden method to remove axis_id code
-# since Doughnut charts don't require val and cat axes.
-#
-sub _write_doughnut_chart {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'c:doughnutChart' );
-
-    # Write the c:varyColors element.
-    $self->_write_vary_colors();
-
-    # Write the series elements.
-    $self->_write_ser( $_ ) for @{ $self->{_series} };
-
-    # Write the c:firstSliceAng element.
-    $self->_write_first_slice_ang();
-
-    # Write the c:holeSize element.
-    $self->_write_hole_size();
-
-    $self->xml_end_tag( 'c:doughnutChart' );
-}
-
-
-##############################################################################
-#
-# _write_hole_size()
-#
-# Write the <c:holeSize> element.
-#
-sub _write_hole_size {
-
-    my $self = shift;
-    my $val  = $self->{_hole_size};
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:holeSize', @attributes );
-}
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Doughnut - A class for writing Excel Doughnut charts.
-
-=head1 SYNOPSIS
-
-To create a simple Excel file with a Doughnut chart using Excel::Writer::XLSX:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    my $chart     = $workbook->add_chart( type => 'doughnut' );
-
-    # 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 Doughnut charts for L<Excel::Writer::XLSX>. The chart object is created via the Workbook C<add_chart()> method:
-
-    my $chart = $workbook->add_chart( type => 'doughnut' );
-
-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<Excel::Writer::XLSX::Chart>. Class specific methods or settings, if any, are explained below.
-
-=head1 Doughnut Chart Methods
-
-=head2 set_rotation()
-
-The C<set_rotation()> method is used to set the rotation of the first segment of a Pie/Doughnut chart. This has the effect of rotating the entire chart:
-
-    $chart->set_rotation( 90 );
-
-The angle of rotation must be C<< 0 <= rotation <= 360 >>.
-
-
-=head2 set_hole_size()
-
-The C<set_hole_size()> method is used to set the hole size of a Doughnut chart:
-
-    $chart->set_hole_size( 33 );
-
-The the hole size must be a percentage in the range  C<< 10 <= size <= 90 >>.
-
-
-=head2 User defined colors
-
-It is possible to define chart colors for most types of Excel::Writer::XLSX charts via the add_series() method. However, Pie/Doughnut charts are a special case since each segment is represented as a point so it is necessary to assign formatting to each point in the series:
-
-    $chart->add_series(
-        values => '=Sheet1!$A$1:$A$3',
-        points => [
-            { fill => { color => '#FF0000' } },
-            { fill => { color => '#CC0000' } },
-            { fill => { color => '#990000' } },
-        ],
-    );
-
-See the main L<Excel::Writer::XLSX::Chart> documentation for more details.
-
-Doughnut charts support leader lines:
-
-    $chart->add_series(
-        name        => 'Doughnut sales data',
-        categories  => [ 'Sheet1', 1, 3, 0, 0 ],
-        values      => [ 'Sheet1', 1, 3, 1, 1 ],
-        data_labels => {
-            series_name  => 1,
-            percentage   => 1,
-            leader_lines => 1,
-            position     => 'outside_end'
-        },
-    );
-
-Note: Even when leader lines are turned on they aren't automatically visible in Excel or Excel::Writer::XLSX. Due to an Excel limitation (or design) leader lines only appear if the data label is moved manually or if the data labels are very close and need to be adjusted automatically.
-
-=head2 Unsupported Methods
-
-A Doughnut 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
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_doughnut.xlsx' );
-    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 = [
-        [ 'Glazed', 'Chocolate', 'Cream' ],
-        [ 50,       35,          15      ],
-    ];
-
-    $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 => 'doughnut', embedded => 1 );
-
-    # Configure the series. Note the use of the array ref to define ranges:
-    # [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart->add_series(
-        name       => 'Doughnut sales data',
-        categories => [ 'Sheet1', 1, 3, 0, 0 ],
-        values     => [ 'Sheet1', 1, 3, 1, 1 ],
-    );
-
-    # Add a title.
-    $chart->set_title( name => 'Popular Doughnut Types' );
-
-    # Set an Excel chart style. Colors with white outline and shadow.
-    $chart->set_style( 10 );
-
-    # 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://jmcnamara.github.io/excel-writer-xlsx/images/examples/doughnut1.jpg" width="483" height="291" alt="Chart example." /></center></p>
-
-=end html
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chart/Line.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chart/Line.pm
deleted file mode 100644 (file)
index 52843bc..0000000
+++ /dev/null
@@ -1,301 +0,0 @@
-package Excel::Writer::XLSX::Chart::Line;
-
-###############################################################################
-#
-# Line - A class for writing Excel Line charts.
-#
-# Used in conjunction with Excel::Writer::XLSX::Chart.
-#
-# See formatting note in Excel::Writer::XLSX::Chart.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Chart;
-
-our @ISA     = qw(Excel::Writer::XLSX::Chart);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# new()
-#
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Chart->new( @_ );
-
-    $self->{_default_marker} = { type => 'none' };
-    $self->{_smooth_allowed} = 1;
-
-    # Set the available data label positions for this chart type.
-    $self->{_label_position_default} = 'right';
-    $self->{_label_positions} = {
-        center      => 'ctr',
-        right       => 'r',
-        left        => 'l',
-        above       => 't',
-        below       => 'b',
-        # For backward compatibility.
-        top         => 't',
-        bottom      => 'b',
-    };
-
-    bless $self, $class;
-    return $self;
-}
-
-
-##############################################################################
-#
-# _write_chart_type()
-#
-# Override the virtual superclass method with a chart specific method.
-#
-sub _write_chart_type {
-
-    my $self = shift;
-
-    # Write the c:lineChart element.
-    $self->_write_line_chart( @_ );
-}
-
-
-##############################################################################
-#
-# _write_line_chart()
-#
-# Write the <c:lineChart> element.
-#
-sub _write_line_chart {
-
-    my $self = shift;
-    my %args = @_;
-
-    my @series;
-    if ( $args{primary_axes} ) {
-        @series = $self->_get_primary_axes_series;
-    }
-    else {
-        @series = $self->_get_secondary_axes_series;
-    }
-
-    return unless scalar @series;
-
-    $self->xml_start_tag( 'c:lineChart' );
-
-    # Write the c:grouping element.
-    $self->_write_grouping( 'standard' );
-
-    # Write the series elements.
-    $self->_write_series( $_ ) for @series;
-
-    # Write the c:dropLines element.
-    $self->_write_drop_lines();
-
-    # Write the c:hiLowLines element.
-    $self->_write_hi_low_lines();
-
-    # Write the c:upDownBars element.
-    $self->_write_up_down_bars();
-
-    # Write the c:marker element.
-    $self->_write_marker_value();
-
-    # Write the c:axId elements
-    $self->_write_axis_ids( %args );
-
-    $self->xml_end_tag( 'c:lineChart' );
-}
-
-
-##############################################################################
-#
-# _write_d_pt_point()
-#
-# Write an individual <c:dPt> element. Override the parent method to add
-# markers.
-#
-sub _write_d_pt_point {
-
-    my $self   = shift;
-    my $index = shift;
-    my $point = shift;
-
-        $self->xml_start_tag( 'c:dPt' );
-
-        # Write the c:idx element.
-        $self->_write_idx( $index );
-
-        $self->xml_start_tag( 'c:marker' );
-
-        # Write the c:spPr element.
-        $self->_write_sp_pr( $point );
-
-        $self->xml_end_tag( 'c:marker' );
-
-        $self->xml_end_tag( 'c:dPt' );
-}
-
-##############################################################################
-#
-# _write_marker_value()
-#
-# Write the <c:marker> element without a sub-element.
-#
-sub _write_marker_value {
-
-    my $self  = shift;
-
-    my @attributes = ( 'val' => 1 );
-
-    $self->xml_empty_tag( 'c:marker', @attributes );
-}
-
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Line - A class for writing Excel Line charts.
-
-=head1 SYNOPSIS
-
-To create a simple Excel file with a Line chart using Excel::Writer::XLSX:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    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<Excel::Writer::XLSX>. 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<Excel::Writer::XLSX::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<Excel::Writer::XLSX::Chart>.
-
-=head1 EXAMPLE
-
-Here is a complete example that demonstrates most of the available features when creating a chart.
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_line.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2, 3, 4, 5, 6, 7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-
-    ];
-
-    $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.
-    $chart->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-
-    # 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 (mm)' );
-
-    # Set an Excel chart style. Colors with white outline and shadow.
-    $chart->set_style( 10 );
-
-    # 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://jmcnamara.github.io/excel-writer-xlsx/images/examples/line1.jpg" width="483" height="291" alt="Chart example." /></center></p>
-
-=end html
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chart/Pie.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chart/Pie.pm
deleted file mode 100644 (file)
index 4a5e731..0000000
+++ /dev/null
@@ -1,503 +0,0 @@
-package Excel::Writer::XLSX::Chart::Pie;
-
-###############################################################################
-#
-# Pie - A class for writing Excel Pie charts.
-#
-# Used in conjunction with Excel::Writer::XLSX::Chart.
-#
-# See formatting note in Excel::Writer::XLSX::Chart.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Chart;
-
-our @ISA     = qw(Excel::Writer::XLSX::Chart);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# new()
-#
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Chart->new( @_ );
-
-    $self->{_vary_data_color} = 1;
-    $self->{_rotation}        = 0;
-
-    # Set the available data label positions for this chart type.
-    $self->{_label_position_default} = 'best_fit';
-    $self->{_label_positions} = {
-        center      => 'ctr',
-        inside_end  => 'inEnd',
-        outside_end => 'outEnd',
-        best_fit    => 'bestFit',
-    };
-
-    bless $self, $class;
-    return $self;
-}
-
-
-###############################################################################
-#
-# set_rotation()
-#
-# Set the Pie/Doughnut chart rotation: the angle of the first slice.
-#
-sub set_rotation {
-
-    my $self     = shift;
-    my $rotation = shift;
-
-    return if !defined $rotation;
-
-    if ( $rotation >= 0 && $rotation <= 360 ) {
-        $self->{_rotation} = $rotation;
-    }
-    else {
-        carp "Chart rotation $rotation outside range: 0 <= rotation <= 360";
-    }
-}
-
-
-##############################################################################
-#
-# _write_chart_type()
-#
-# Override the virtual superclass method with a chart specific method.
-#
-sub _write_chart_type {
-
-    my $self = shift;
-
-    # Write the c:pieChart element.
-    $self->_write_pie_chart( @_ );
-}
-
-
-##############################################################################
-#
-# _write_pie_chart()
-#
-# Write the <c:pieChart> element.  Over-ridden method to remove axis_id code
-# since Pie charts don't require val and cat axes.
-#
-sub _write_pie_chart {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'c:pieChart' );
-
-    # Write the c:varyColors element.
-    $self->_write_vary_colors();
-
-    # Write the series elements.
-    $self->_write_ser( $_ ) for @{ $self->{_series} };
-
-    # Write the c:firstSliceAng element.
-    $self->_write_first_slice_ang();
-
-    $self->xml_end_tag( 'c:pieChart' );
-}
-
-
-###############################################################################
-#
-# combine()
-#
-# Override parent method to add a warning.
-#
-sub combine {
-
-    my $self  = shift;
-    my $chart = shift;
-
-    carp "Combined chart not currently supported for Pie charts";
-    return;
-}
-
-
-##############################################################################
-#
-# _write_plot_area().
-#
-# Over-ridden method to remove the cat_axis() and val_axis() code since
-# Pie/Doughnut charts don't require those axes.
-#
-# Write the <c:plotArea> element.
-#
-sub _write_plot_area {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'c:plotArea' );
-
-    # Write the c:layout element.
-    $self->_write_layout( $self->{_plotarea}->{_layout}, 'plot' );
-
-    # Write the subclass chart type element.
-    $self->_write_chart_type();
-
-    # Write the c:spPr element for the plotarea formatting.
-    $self->_write_sp_pr( $self->{_plotarea} );
-
-    $self->xml_end_tag( 'c:plotArea' );
-}
-
-
-##############################################################################
-#
-# _write_legend().
-#
-# Over-ridden method to add <c:txPr> to legend.
-#
-# Write the <c:legend> element.
-#
-sub _write_legend {
-
-    my $self          = shift;
-    my $position      = $self->{_legend_position};
-    my $font          = $self->{_legend_font};
-    my @delete_series = ();
-    my $overlay       = 0;
-
-    if ( defined $self->{_legend_delete_series}
-        && ref $self->{_legend_delete_series} eq 'ARRAY' )
-    {
-        @delete_series = @{ $self->{_legend_delete_series} };
-    }
-
-    if ( $position =~ s/^overlay_// ) {
-        $overlay = 1;
-    }
-
-    my %allowed = (
-        right  => 'r',
-        left   => 'l',
-        top    => 't',
-        bottom => 'b',
-    );
-
-    return if $position eq 'none';
-    return unless exists $allowed{$position};
-
-    $position = $allowed{$position};
-
-    $self->xml_start_tag( 'c:legend' );
-
-    # Write the c:legendPos element.
-    $self->_write_legend_pos( $position );
-
-    # Remove series labels from the legend.
-    for my $index ( @delete_series ) {
-
-        # Write the c:legendEntry element.
-        $self->_write_legend_entry( $index );
-    }
-
-    # Write the c:layout element.
-    $self->_write_layout( $self->{_legend_layout}, 'legend' );
-
-    # Write the c:overlay element.
-    $self->_write_overlay() if $overlay;
-
-    # Write the c:txPr element. Over-ridden.
-    $self->_write_tx_pr_legend( 0, $font );
-
-    $self->xml_end_tag( 'c:legend' );
-}
-
-
-##############################################################################
-#
-# _write_tx_pr_legend()
-#
-# Write the <c:txPr> element for legends.
-#
-sub _write_tx_pr_legend {
-
-    my $self     = shift;
-    my $horiz    = shift;
-    my $font     = shift;
-    my $rotation = undef;
-
-    if ( $font && exists $font->{_rotation} ) {
-        $rotation = $font->{_rotation};
-    }
-
-    $self->xml_start_tag( 'c:txPr' );
-
-    # Write the a:bodyPr element.
-    $self->_write_a_body_pr( $rotation, $horiz );
-
-    # Write the a:lstStyle element.
-    $self->_write_a_lst_style();
-
-    # Write the a:p element.
-    $self->_write_a_p_legend( $font );
-
-    $self->xml_end_tag( 'c:txPr' );
-}
-
-
-##############################################################################
-#
-# _write_a_p_legend()
-#
-# Write the <a:p> element for legends.
-#
-sub _write_a_p_legend {
-
-    my $self = shift;
-    my $font = shift;
-
-    $self->xml_start_tag( 'a:p' );
-
-    # Write the a:pPr element.
-    $self->_write_a_p_pr_legend( $font );
-
-    # Write the a:endParaRPr element.
-    $self->_write_a_end_para_rpr();
-
-    $self->xml_end_tag( 'a:p' );
-}
-
-
-##############################################################################
-#
-# _write_a_p_pr_legend()
-#
-# Write the <a:pPr> element for legends.
-#
-sub _write_a_p_pr_legend {
-
-    my $self = shift;
-    my $font = shift;
-    my $rtl  = 0;
-
-    my @attributes = ( 'rtl' => $rtl );
-
-    $self->xml_start_tag( 'a:pPr', @attributes );
-
-    # Write the a:defRPr element.
-    $self->_write_a_def_rpr( $font );
-
-    $self->xml_end_tag( 'a:pPr' );
-}
-
-
-##############################################################################
-#
-# _write_vary_colors()
-#
-# Write the <c:varyColors> element.
-#
-sub _write_vary_colors {
-
-    my $self = shift;
-    my $val  = 1;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:varyColors', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_first_slice_ang()
-#
-# Write the <c:firstSliceAng> element.
-#
-sub _write_first_slice_ang {
-
-    my $self = shift;
-    my $val  = $self->{_rotation};
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:firstSliceAng', @attributes );
-}
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Pie - A class for writing Excel Pie charts.
-
-=head1 SYNOPSIS
-
-To create a simple Excel file with a Pie chart using Excel::Writer::XLSX:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    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<Excel::Writer::XLSX>. 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<Excel::Writer::XLSX::Chart>. Class specific methods or settings, if any, are explained below.
-
-=head1 Pie Chart Methods
-
-=head2 set_rotation()
-
-The C<set_rotation()> method is used to set the rotation of the first segment of a Pie/Doughnut chart. This has the effect of rotating the entire chart:
-
-    $chart->set_rotation( 90 );
-
-The angle of rotation must be C<< 0 <= rotation <= 360 >>.
-
-
-=head2 User defined colors
-
-It is possible to define chart colors for most types of Excel::Writer::XLSX charts via the add_series() method. However, Pie/Doughnut charts are a special case since each segment is represented as a point so it is necessary to assign formatting to each point in the series:
-
-    $chart->add_series(
-        values => '=Sheet1!$A$1:$A$3',
-        points => [
-            { fill => { color => '#FF0000' } },
-            { fill => { color => '#CC0000' } },
-            { fill => { color => '#990000' } },
-        ],
-    );
-
-See the main L<Excel::Writer::XLSX::Chart> documentation for more details.
-
-Pie charts support leader lines:
-
-    $chart->add_series(
-        name        => 'Pie sales data',
-        categories  => [ 'Sheet1', 1, 3, 0, 0 ],
-        values      => [ 'Sheet1', 1, 3, 1, 1 ],
-        data_labels => {
-            series_name  => 1,
-            percentage   => 1,
-            leader_lines => 1,
-            position     => 'outside_end'
-        },
-    );
-
-Note: Even when leader lines are turned on they aren't automatically visible in Excel or Excel::Writer::XLSX. Due to an Excel limitation (or design) leader lines only appear if the data label is moved manually or if the data labels are very close and need to be adjusted automatically.
-
-=head2 Unsupported Methods
-
-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
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_pie.xlsx' );
-    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. Note the use of the array ref to define ranges:
-    # [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart->add_series(
-        name       => 'Pie sales data',
-        categories => [ 'Sheet1', 1, 3, 0, 0 ],
-        values     => [ 'Sheet1', 1, 3, 1, 1 ],
-    );
-
-    # Add a title.
-    $chart->set_title( name => 'Popular Pie Types' );
-
-    # Set an Excel chart style. Colors with white outline and shadow.
-    $chart->set_style( 10 );
-
-    # 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://jmcnamara.github.io/excel-writer-xlsx/images/examples/pie1.jpg" width="483" height="291" alt="Chart example." /></center></p>
-
-=end html
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chart/Radar.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chart/Radar.pm
deleted file mode 100644 (file)
index 3be8f5b..0000000
+++ /dev/null
@@ -1,275 +0,0 @@
-package Excel::Writer::XLSX::Chart::Radar;
-
-###############################################################################
-#
-# Radar - A class for writing Excel Radar charts.
-#
-# Used in conjunction with Excel::Writer::XLSX::Chart.
-#
-# See formatting note in Excel::Writer::XLSX::Chart.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Chart;
-
-our @ISA     = qw(Excel::Writer::XLSX::Chart);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# new()
-#
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Chart->new( @_ );
-
-    $self->{_subtype} = $self->{_subtype} || 'marker';
-
-    if ( $self->{_subtype} eq 'marker' ) {
-        $self->{_default_marker} = { type => 'none' };
-    }
-
-    # Override and reset the default axis values.
-    $self->{_x_axis}->{_defaults}->{major_gridlines} = { visible => 1 };
-    $self->set_x_axis();
-
-    # Hardcode major_tick_mark for now until there is an accessor.
-    $self->{_y_axis}->{_major_tick_mark} = 'cross';
-
-    # Set the available data label positions for this chart type.
-    $self->{_label_position_default} = 'center';
-    $self->{_label_positions} = { center => 'ctr' };
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-##############################################################################
-#
-# _write_chart_type()
-#
-# Override the virtual superclass method with a chart specific method.
-#
-sub _write_chart_type {
-
-    my $self = shift;
-
-    # Write the c:radarChart element.
-    $self->_write_radar_chart( @_ );
-}
-
-
-##############################################################################
-#
-# _write_radar_chart()
-#
-# Write the <c:radarChart> element.
-#
-sub _write_radar_chart {
-
-    my $self = shift;
-    my %args = @_;
-
-    my @series;
-    if ( $args{primary_axes} ) {
-        @series = $self->_get_primary_axes_series;
-    }
-    else {
-        @series = $self->_get_secondary_axes_series;
-    }
-
-    return unless scalar @series;
-
-    $self->xml_start_tag( 'c:radarChart' );
-
-    # Write the c:radarStyle element.
-    $self->_write_radar_style();
-
-    # Write the series elements.
-    $self->_write_series( $_ ) for @series;
-
-    # Write the c:axId elements
-    $self->_write_axis_ids( %args );
-
-    $self->xml_end_tag( 'c:radarChart' );
-}
-
-
-##############################################################################
-#
-# _write_radar_style()
-#
-# Write the <c:radarStyle> element.
-#
-sub _write_radar_style {
-
-    my $self = shift;
-    my $val  = 'marker';
-
-    if ( $self->{_subtype} eq 'filled' ) {
-        $val = 'filled';
-    }
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:radarStyle', @attributes );
-}
-
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Radar - A class for writing Excel Radar charts.
-
-=head1 SYNOPSIS
-
-To create a simple Excel file with a Radar chart using Excel::Writer::XLSX:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    my $chart     = $workbook->add_chart( type => 'radar' );
-
-    # 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 Radar charts for L<Excel::Writer::XLSX>. The chart object is created via the Workbook C<add_chart()> method:
-
-    my $chart = $workbook->add_chart( type => 'radar' );
-
-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<Excel::Writer::XLSX::Chart>. Class specific methods or settings, if any, are explained below.
-
-=head1 Radar Chart Methods
-
-The C<Radar> chart module also supports the following sub-types:
-
-    with_markers
-    filled
-
-These can be specified at creation time via the C<add_chart()> Worksheet method:
-
-    my $chart = $workbook->add_chart( type => 'radar', subtype => 'filled' );
-
-=head1 EXAMPLE
-
-Here is a complete example that demonstrates most of the available features when creating a chart.
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_radar.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-        [ 25, 40, 50, 30, 50, 40 ],
-
-    ];
-
-    $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 => 'radar', embedded => 1 );
-
-    # Configure the first series.
-    $chart->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-
-    # 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 (mm)' );
-
-    # Set an Excel chart style. Colors with white outline and shadow.
-    $chart->set_style( 10 );
-
-    # 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://jmcnamara.github.io/excel-writer-xlsx/images/examples/radar1.jpg" width="483" height="291" alt="Chart example." /></center></p>
-
-=end html
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chart/Scatter.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chart/Scatter.pm
deleted file mode 100644 (file)
index 1e82b1b..0000000
+++ /dev/null
@@ -1,571 +0,0 @@
-package Excel::Writer::XLSX::Chart::Scatter;
-
-###############################################################################
-#
-# Scatter - A class for writing Excel Scatter charts.
-#
-# Used in conjunction with Excel::Writer::XLSX::Chart.
-#
-# See formatting note in Excel::Writer::XLSX::Chart.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Chart;
-
-our @ISA     = qw(Excel::Writer::XLSX::Chart);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# new()
-#
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Chart->new( @_ );
-
-    $self->{_subtype}           = $self->{_subtype} || 'marker_only';
-    $self->{_cross_between}     = 'midCat';
-    $self->{_horiz_val_axis}    = 0;
-    $self->{_val_axis_postion}  = 'b';
-    $self->{_smooth_allowed}    = 1;
-    $self->{_requires_category} = 1;
-
-    # Set the available data label positions for this chart type.
-    $self->{_label_position_default} = 'right';
-    $self->{_label_positions} = {
-        center      => 'ctr',
-        right       => 'r',
-        left        => 'l',
-        above       => 't',
-        below       => 'b',
-        # For backward compatibility.
-        top         => 't',
-        bottom      => 'b',
-    };
-
-    bless $self, $class;
-    return $self;
-}
-
-
-###############################################################################
-#
-# combine()
-#
-# Override parent method to add a warning.
-#
-sub combine {
-
-    my $self  = shift;
-    my $chart = shift;
-
-    carp 'Combined chart not currently supported with scatter chart ' .
-      'as the primary chart';
-    return;
-}
-
-
-##############################################################################
-#
-# _write_chart_type()
-#
-# Override the virtual superclass method with a chart specific method.
-#
-sub _write_chart_type {
-
-    my $self = shift;
-
-    # Write the c:scatterChart element.
-    $self->_write_scatter_chart( @_ );
-}
-
-
-##############################################################################
-#
-# _write_scatter_chart()
-#
-# Write the <c:scatterChart> element.
-#
-sub _write_scatter_chart {
-
-    my $self = shift;
-    my %args = @_;
-
-    my @series;
-    if ( $args{primary_axes} ) {
-        @series = $self->_get_primary_axes_series;
-    }
-    else {
-        @series = $self->_get_secondary_axes_series;
-    }
-
-    return unless scalar @series;
-
-    my $style   = 'lineMarker';
-    my $subtype = $self->{_subtype};
-
-    # Set the user defined chart subtype.
-
-    if ($subtype eq 'marker_only') {
-        $style = 'lineMarker';
-    }
-
-    if ($subtype eq 'straight_with_markers') {
-        $style = 'lineMarker';
-    }
-
-    if ($subtype eq 'straight') {
-        $style = 'lineMarker';
-        $self->{_default_marker} = { type => 'none' };
-    }
-
-    if ($subtype eq 'smooth_with_markers') {
-        $style = 'smoothMarker';
-    }
-
-    if ($subtype eq 'smooth') {
-        $style = 'smoothMarker';
-        $self->{_default_marker} = { type => 'none' };
-    }
-
-    # Add default formatting to the series data.
-    $self->_modify_series_formatting();
-
-    $self->xml_start_tag( 'c:scatterChart' );
-
-    # Write the c:scatterStyle element.
-    $self->_write_scatter_style( $style );
-
-    # Write the series elements.
-    $self->_write_ser( $_ ) for @series;
-
-    # Write the c:axId elements
-    $self->_write_axis_ids( %args );
-
-    $self->xml_end_tag( 'c:scatterChart' );
-}
-
-
-##############################################################################
-#
-# _write_ser()
-#
-# Over-ridden to write c:xVal/c:yVal instead of c:cat/c:val elements.
-#
-# Write the <c:ser> element.
-#
-sub _write_ser {
-
-    my $self   = shift;
-    my $series = shift;
-    my $index  = $self->{_series_index}++;
-
-    $self->xml_start_tag( 'c:ser' );
-
-    # Write the c:idx element.
-    $self->_write_idx( $index );
-
-    # Write the c:order element.
-    $self->_write_order( $index );
-
-    # Write the series name.
-    $self->_write_series_name( $series );
-
-    # Write the c:spPr element.
-    $self->_write_sp_pr( $series );
-
-    # Write the c:marker element.
-    $self->_write_marker( $series->{_marker} );
-
-    # Write the c:dPt element.
-    $self->_write_d_pt( $series->{_points} );
-
-    # Write the c:dLbls element.
-    $self->_write_d_lbls( $series->{_labels} );
-
-    # Write the c:trendline element.
-    $self->_write_trendline( $series->{_trendline} );
-
-    # Write the c:errBars element.
-    $self->_write_error_bars( $series->{_error_bars} );
-
-    # Write the c:xVal element.
-    $self->_write_x_val( $series );
-
-    # Write the c:yVal element.
-    $self->_write_y_val( $series );
-
-    # Write the c:smooth element.
-    if ( $self->{_subtype} =~ /smooth/ && !defined $series->{_smooth} ) {
-        # Default is on for smooth scatter charts.
-        $self->_write_c_smooth( 1 );
-    }
-    else {
-        $self->_write_c_smooth( $series->{_smooth} );
-    }
-
-    $self->xml_end_tag( 'c:ser' );
-}
-
-
-##############################################################################
-#
-# _write_plot_area()
-#
-# Over-ridden to have 2 valAx elements for scatter charts instead of
-# catAx/valAx.
-#
-# Write the <c:plotArea> element.
-#
-sub _write_plot_area {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'c:plotArea' );
-
-    # Write the c:layout element.
-    $self->_write_layout( $self->{_plotarea}->{_layout}, 'plot' );
-
-    # Write the subclass chart type elements for primary and secondary axes.
-    $self->_write_chart_type( primary_axes => 1 );
-    $self->_write_chart_type( primary_axes => 0 );
-
-    # Write c:catAx and c:valAx elements for series using primary axes.
-    $self->_write_cat_val_axis(
-        x_axis   => $self->{_x_axis},
-        y_axis   => $self->{_y_axis},
-        axis_ids => $self->{_axis_ids},
-        position => 'b',
-    );
-
-    my $tmp = $self->{_horiz_val_axis};
-    $self->{_horiz_val_axis} = 1;
-    $self->_write_val_axis(
-        x_axis   => $self->{_x_axis},
-        y_axis   => $self->{_y_axis},
-        axis_ids => $self->{_axis_ids},
-        position => 'l',
-    );
-    $self->{_horiz_val_axis} = $tmp;
-
-    # Write c:valAx and c:catAx elements for series using secondary axes.
-    $self->_write_cat_val_axis(
-        x_axis   => $self->{_x2_axis},
-        y_axis   => $self->{_y2_axis},
-        axis_ids => $self->{_axis2_ids},
-        position => 'b',
-    );
-    $self->{_horiz_val_axis} = 1;
-    $self->_write_val_axis(
-        x_axis   => $self->{_x2_axis},
-        y_axis   => $self->{_y2_axis},
-        axis_ids => $self->{_axis2_ids},
-        position => 'l',
-    );
-
-    # Write the c:spPr element for the plotarea formatting.
-    $self->_write_sp_pr( $self->{_plotarea} );
-
-    $self->xml_end_tag( 'c:plotArea' );
-}
-
-
-##############################################################################
-#
-# _write_x_val()
-#
-# Write the <c:xVal> element.
-#
-sub _write_x_val {
-
-    my $self    = shift;
-    my $series  = shift;
-    my $formula = $series->{_categories};
-    my $data_id = $series->{_cat_data_id};
-    my $data    = $self->{_formula_data}->[$data_id];
-
-    $self->xml_start_tag( 'c:xVal' );
-
-    # Check the type of cached data.
-    my $type = $self->_get_data_type( $data );
-
-    # TODO. Can a scatter plot have non-numeric data.
-
-    if ( $type eq 'str' ) {
-
-        # Write the c:numRef element.
-        $self->_write_str_ref( $formula, $data, $type );
-    }
-    else {
-
-        # Write the c:numRef element.
-        $self->_write_num_ref( $formula, $data, $type );
-    }
-
-    $self->xml_end_tag( 'c:xVal' );
-}
-
-
-##############################################################################
-#
-# _write_y_val()
-#
-# Write the <c:yVal> element.
-#
-sub _write_y_val {
-
-    my $self    = shift;
-    my $series  = shift;
-    my $formula = $series->{_values};
-    my $data_id = $series->{_val_data_id};
-    my $data    = $self->{_formula_data}->[$data_id];
-
-    $self->xml_start_tag( 'c:yVal' );
-
-    # Unlike Cat axes data should only be numeric.
-
-    # Write the c:numRef element.
-    $self->_write_num_ref( $formula, $data, 'num' );
-
-    $self->xml_end_tag( 'c:yVal' );
-}
-
-
-##############################################################################
-#
-# _write_scatter_style()
-#
-# Write the <c:scatterStyle> element.
-#
-sub _write_scatter_style {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'c:scatterStyle', @attributes );
-}
-
-
-##############################################################################
-#
-# _modify_series_formatting()
-#
-# Add default formatting to the series data unless it has already been
-# specified by the user.
-#
-sub _modify_series_formatting {
-
-    my $self    = shift;
-    my $subtype = $self->{_subtype};
-
-    # The default scatter style "markers only" requires a line type.
-    if ( $subtype eq 'marker_only' ) {
-
-        # Go through each series and define default values.
-        for my $series ( @{ $self->{_series} } ) {
-
-            # Set a line type unless there is already a user defined type.
-            if ( !$series->{_line}->{_defined} ) {
-                $series->{_line} = {
-                    width    => 2.25,
-                    none     => 1,
-                    _defined => 1,
-                };
-            }
-        }
-    }
-}
-
-
-##############################################################################
-#
-# _write_d_pt_point()
-#
-# Write an individual <c:dPt> element. Override the parent method to add
-# markers.
-#
-sub _write_d_pt_point {
-
-    my $self   = shift;
-    my $index = shift;
-    my $point = shift;
-
-        $self->xml_start_tag( 'c:dPt' );
-
-        # Write the c:idx element.
-        $self->_write_idx( $index );
-
-        $self->xml_start_tag( 'c:marker' );
-
-        # Write the c:spPr element.
-        $self->_write_sp_pr( $point );
-
-        $self->xml_end_tag( 'c:marker' );
-
-        $self->xml_end_tag( 'c:dPt' );
-}
-
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Scatter - A class for writing Excel Scatter charts.
-
-=head1 SYNOPSIS
-
-To create a simple Excel file with a Scatter chart using Excel::Writer::XLSX:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    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<Excel::Writer::XLSX>. 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<Excel::Writer::XLSX::Chart>. Class specific methods or settings, if any, are explained below.
-
-=head1 Scatter Chart Subtypes
-
-The C<Scatter> chart module also supports the following sub-types:
-
-    markers_only (the default)
-    straight_with_markers
-    straight
-    smooth_with_markers
-    smooth
-
-These can be specified at creation time via the C<add_chart()> Worksheet method:
-
-    my $chart = $workbook->add_chart(
-        type    => 'scatter',
-        subtype => 'straight_with_markers'
-    );
-
-=head1 EXAMPLE
-
-Here is a complete example that demonstrates most of the available features when creating a chart.
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_scatter.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2, 3, 4, 5, 6, 7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-
-    ];
-
-    $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.
-    $chart->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-
-    # 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 (mm)' );
-
-    # Set an Excel chart style. Colors with white outline and shadow.
-    $chart->set_style( 10 );
-
-    # 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://jmcnamara.github.io/excel-writer-xlsx/images/examples/scatter1.jpg" width="483" height="291" alt="Chart example." /></center></p>
-
-=end html
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chart/Stock.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chart/Stock.pm
deleted file mode 100644 (file)
index dcd0950..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-package Excel::Writer::XLSX::Chart::Stock;
-
-###############################################################################
-#
-# Stock - A class for writing Excel Stock charts.
-#
-# Used in conjunction with Excel::Writer::XLSX::Chart.
-#
-# See formatting note in Excel::Writer::XLSX::Chart.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Chart;
-
-our @ISA     = qw(Excel::Writer::XLSX::Chart);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# new()
-#
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Chart->new( @_ );
-    $self->{_show_crosses}  = 0;
-    $self->{_hi_low_lines}  = {};
-    $self->{_date_category} = 1;
-
-    # Override and reset the default axis values.
-    $self->{_x_axis}->{_defaults}->{num_format}  = 'dd/mm/yyyy';
-    $self->{_x2_axis}->{_defaults}->{num_format} = 'dd/mm/yyyy';
-    $self->set_x_axis();
-    $self->set_x2_axis();
-
-    # Set the available data label positions for this chart type.
-    $self->{_label_position_default} = 'right';
-    $self->{_label_positions} = {
-        center      => 'ctr',
-        right       => 'r',
-        left        => 'l',
-        above       => 't',
-        below       => 'b',
-        # For backward compatibility.
-        top         => 't',
-        bottom      => 'b',
-    };
-
-    bless $self, $class;
-    return $self;
-}
-
-
-##############################################################################
-#
-# _write_chart_type()
-#
-# Override the virtual superclass method with a chart specific method.
-#
-sub _write_chart_type {
-
-    my $self = shift;
-
-    # Write the c:stockChart element.
-    $self->_write_stock_chart( @_ );
-}
-
-
-##############################################################################
-#
-# _write_stock_chart()
-#
-# Write the <c:stockChart> element.
-# Overridden to add hi_low_lines(). TODO. Refactor up into the SUPER class.
-#
-sub _write_stock_chart {
-
-    my $self = shift;
-    my %args = @_;
-
-    my @series;
-    if ( $args{primary_axes} ) {
-        @series = $self->_get_primary_axes_series;
-    }
-    else {
-        @series = $self->_get_secondary_axes_series;
-    }
-
-    return unless scalar @series;
-
-    # Add default formatting to the series data.
-    $self->_modify_series_formatting();
-
-    $self->xml_start_tag( 'c:stockChart' );
-
-    # Write the series elements.
-    $self->_write_ser( $_ ) for @series;
-
-    # Write the c:dropLines element.
-    $self->_write_drop_lines();
-
-    # Write the c:hiLowLines element.
-    $self->_write_hi_low_lines() if $args{primary_axes};
-
-    # Write the c:upDownBars element.
-    $self->_write_up_down_bars();
-
-    # Write the c:axId elements
-    $self->_write_axis_ids( %args );
-
-    $self->xml_end_tag( 'c:stockChart' );
-}
-
-
-##############################################################################
-#
-# _modify_series_formatting()
-#
-# Add default formatting to the series data.
-#
-sub _modify_series_formatting {
-
-    my $self = shift;
-
-    my $index = 0;
-    for my $series ( @{ $self->{_series} } ) {
-        if ( $index % 4 != 3 ) {
-            if ( !$series->{_line}->{_defined} ) {
-                $series->{_line} = {
-                    width    => 2.25,
-                    none     => 1,
-                    _defined => 1,
-                };
-            }
-
-            if ( !$series->{_marker} ) {
-                if ( $index % 4 == 2 ) {
-                    $series->{_marker} = { type => 'dot', size => 3 };
-                }
-                else {
-                    $series->{_marker} = { type => 'none' };
-
-                }
-            }
-        }
-        $index++;
-    }
-}
-
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Stock - A class for writing Excel Stock charts.
-
-=head1 SYNOPSIS
-
-To create a simple Excel file with a Stock chart using Excel::Writer::XLSX:
-
-    #!/usr/bin/perl -w
-
-    use strict;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'chart.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    my $chart = $workbook->add_chart( type => 'stock' );
-
-    # Add a series for each 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'
-    );
-
-    # Add the worksheet data the chart refers to.
-    # ... See the full example below.
-
-    __END__
-
-
-=head1 DESCRIPTION
-
-This module implements Stock charts for L<Excel::Writer::XLSX>. 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<Excel::Writer::XLSX::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<Excel::Writer::XLSX::Chart>.
-
-The default Stock chart is a High-Low-Close chart. A series must be added for each of these data sources.
-
-
-=head1 EXAMPLE
-
-Here is a complete example that demonstrates most of the available features when creating a Stock chart.
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    use Excel::Writer::XLSX;
-
-    my $workbook    = Excel::Writer::XLSX->new( 'chart_stock.xlsx' );
-    my $worksheet   = $workbook->add_worksheet();
-    my $bold        = $workbook->add_format( bold => 1 );
-    my $date_format = $workbook->add_format( num_format => 'dd/mm/yyyy' );
-    my $chart       = $workbook->add_chart( type => 'stock', embedded => 1 );
-
-
-    # Add the worksheet data that the charts will refer to.
-    my $headings = [ 'Date', 'High', 'Low', 'Close' ];
-    my $data = [
-
-        [ '2007-01-01T', '2007-01-02T', '2007-01-03T', '2007-01-04T', '2007-01-05T' ],
-        [ 27.2,  25.03, 19.05, 20.34, 18.5 ],
-        [ 23.49, 19.55, 15.12, 17.84, 16.34 ],
-        [ 25.45, 23.05, 17.32, 20.45, 17.34 ],
-
-    ];
-
-    $worksheet->write( 'A1', $headings, $bold );
-
-    for my $row ( 0 .. 4 ) {
-        $worksheet->write_date_time( $row+1, 0, $data->[0]->[$row], $date_format );
-        $worksheet->write( $row+1, 1, $data->[1]->[$row] );
-        $worksheet->write( $row+1, 2, $data->[2]->[$row] );
-        $worksheet->write( $row+1, 3, $data->[3]->[$row] );
-
-    }
-
-    $worksheet->set_column( 'A:D', 11 );
-
-    # Add a series for each of the High-Low-Close columns.
-    $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',
-    );
-
-    # Add a chart title and some axis labels.
-    $chart->set_title ( name => 'High-Low-Close', );
-    $chart->set_x_axis( name => 'Date', );
-    $chart->set_y_axis( name => 'Share price', );
-
-
-    $worksheet->insert_chart( 'E9', $chart );
-
-    __END__
-
-=begin html
-
-<p>This will produce a chart that looks like this:</p>
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/stock1.jpg" width="483" height="291" alt="Chart example." /></center></p>
-
-=end html
-
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVIII, 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.
-
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Chartsheet.pm b/tools/lib/perl5/Excel/Writer/XLSX/Chartsheet.pm
deleted file mode 100644 (file)
index 2ef1d7d..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-package Excel::Writer::XLSX::Chartsheet;
-
-###############################################################################
-#
-# Chartsheet - A class for writing the Excel XLSX Chartsheet files.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Exporter;
-use Excel::Writer::XLSX::Worksheet;
-
-our @ISA     = qw(Excel::Writer::XLSX::Worksheet);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Worksheet->new( @_ );
-
-    $self->{_drawing}           = 1;
-    $self->{_is_chartsheet}     = 1;
-    $self->{_chart}             = undef;
-    $self->{_charts}            = [1];
-    $self->{_zoom_scale_normal} = 0;
-    $self->{_orientation}       = 0;
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration;
-
-    # Write the root chartsheet element.
-    $self->_write_chartsheet();
-
-    # Write the worksheet properties.
-    $self->_write_sheet_pr();
-
-    # Write the sheet view properties.
-    $self->_write_sheet_views();
-
-    # Write the sheetProtection element.
-    $self->_write_sheet_protection();
-
-    # Write the printOptions element.
-    $self->_write_print_options();
-
-    # Write the worksheet page_margins.
-    $self->_write_page_margins();
-
-    # Write the worksheet page setup.
-    $self->_write_page_setup();
-
-    # Write the headerFooter element.
-    $self->_write_header_footer();
-
-    # Write the drawing element.
-    $self->_write_drawings();
-
-    # Close the worksheet tag.
-    $self->xml_end_tag( 'chartsheet' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# Public methods.
-#
-###############################################################################
-
-# Over-ride parent protect() method to protect both worksheet and chart.
-sub protect {
-
-    my $self     = shift;
-    my $password = shift || '';
-    my $options  = shift || {};
-
-    $self->{_chart}->{_protection} = 1;
-
-    $options->{sheet}     = 0;
-    $options->{content}   = 1;
-    $options->{scenarios} = 1;
-
-    $self->SUPER::protect( $password, $options );
-}
-
-
-###############################################################################
-#
-# Encapsulated Chart methods.
-#
-###############################################################################
-
-sub add_series         { return shift->{_chart}->add_series( @_ ) }
-sub combine            { return shift->{_chart}->combine( @_ ) }
-sub set_x_axis         { return shift->{_chart}->set_x_axis( @_ ) }
-sub set_y_axis         { return shift->{_chart}->set_y_axis( @_ ) }
-sub set_x2_axis        { return shift->{_chart}->set_x2_axis( @_ ) }
-sub set_y2_axis        { return shift->{_chart}->set_y2_axis( @_ ) }
-sub set_title          { return shift->{_chart}->set_title( @_ ) }
-sub set_legend         { return shift->{_chart}->set_legend( @_ ) }
-sub set_plotarea       { return shift->{_chart}->set_plotarea( @_ ) }
-sub set_chartarea      { return shift->{_chart}->set_chartarea( @_ ) }
-sub set_style          { return shift->{_chart}->set_style( @_ ) }
-sub show_blanks_as     { return shift->{_chart}->show_blanks_as( @_ ) }
-sub show_hidden_data   { return shift->{_chart}->show_hidden_data( @_ ) }
-sub set_size           { return shift->{_chart}->set_size( @_ ) }
-sub set_table          { return shift->{_chart}->set_table( @_ ) }
-sub set_up_down_bars   { return shift->{_chart}->set_up_down_bars( @_ ) }
-sub set_drop_lines     { return shift->{_chart}->set_drop_lines( @_ ) }
-sub set_high_low_lines { return shift->{_chart}->high_low_lines( @_ ) }
-
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _prepare_chart()
-#
-# Set up chart/drawings.
-#
-sub _prepare_chart {
-
-    my $self       = shift;
-    my $index      = shift;
-    my $chart_id   = shift;
-    my $drawing_id = shift;
-
-    $self->{_chart}->{_id} = $chart_id -1;
-
-    my $drawing = Excel::Writer::XLSX::Drawing->new();
-    $self->{_drawing} = $drawing;
-    $self->{_drawing}->{_orientation} = $self->{_orientation};
-
-    push @{ $self->{_external_drawing_links} },
-      [ '/drawing', '../drawings/drawing' . $drawing_id . '.xml' ];
-
-    push @{ $self->{_drawing_links} },
-      [ '/chart', '../charts/chart' . $chart_id . '.xml' ];
-}
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _write_chartsheet()
-#
-# Write the <chartsheet> element. This is the root element of Chartsheet.
-#
-sub _write_chartsheet {
-
-    my $self                   = shift;
-    my $schema                 = 'http://schemas.openxmlformats.org/';
-    my $xmlns                  = $schema . 'spreadsheetml/2006/main';
-    my $xmlns_r                = $schema . 'officeDocument/2006/relationships';
-    my $xmlns_mc               = $schema . 'markup-compatibility/2006';
-    my $xmlns_mv               = 'urn:schemas-microsoft-com:mac:vml';
-    my $mc_ignorable           = 'mv';
-    my $mc_preserve_attributes = 'mv:*';
-
-    my @attributes = (
-        'xmlns'   => $xmlns,
-        'xmlns:r' => $xmlns_r,
-    );
-
-    $self->xml_start_tag( 'chartsheet', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_sheet_pr()
-#
-# Write the <sheetPr> element for Sheet level properties.
-#
-sub _write_sheet_pr {
-
-    my $self       = shift;
-    my @attributes = ();
-
-
-    push @attributes, ( 'filterMode' => 1 ) if $self->{_filter_on};
-
-    if ( $self->{_fit_page} || $self->{_tab_color} ) {
-        $self->xml_start_tag( 'sheetPr', @attributes );
-        $self->_write_tab_color();
-        $self->_write_page_set_up_pr();
-        $self->xml_end_tag( 'sheetPr' );
-    }
-    else {
-        $self->xml_empty_tag( 'sheetPr', @attributes );
-    }
-}
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Chartsheet - A class for writing the Excel XLSX Chartsheet files.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Drawing.pm b/tools/lib/perl5/Excel/Writer/XLSX/Drawing.pm
deleted file mode 100644 (file)
index 1816043..0000000
+++ /dev/null
@@ -1,1430 +0,0 @@
-package Excel::Writer::XLSX::Drawing;
-
-###############################################################################
-#
-# Drawing - A class for writing the Excel XLSX drawing.xml file.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Package::XMLwriter;
-use Excel::Writer::XLSX::Worksheet;
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_drawings}    = [];
-    $self->{_embedded}    = 0;
-    $self->{_orientation} = 0;
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration;
-
-    # Write the xdr:wsDr element.
-    $self->_write_drawing_workspace();
-
-    if ( $self->{_embedded} ) {
-
-        my $index = 0;
-        for my $dimensions ( @{ $self->{_drawings} } ) {
-
-            # Write the xdr:twoCellAnchor element.
-            $self->_write_two_cell_anchor( ++$index, @$dimensions );
-        }
-
-    }
-    else {
-        my $index = 0;
-
-        # Write the xdr:absoluteAnchor element.
-        $self->_write_absolute_anchor( ++$index );
-    }
-
-    $self->xml_end_tag( 'xdr:wsDr' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# _add_drawing_object()
-#
-# Add a chart, image or shape sub object to the drawing.
-#
-sub _add_drawing_object {
-
-    my $self = shift;
-
-    push @{ $self->{_drawings} }, [@_];
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-##############################################################################
-#
-# _write_drawing_workspace()
-#
-# Write the <xdr:wsDr> element.
-#
-sub _write_drawing_workspace {
-
-    my $self      = shift;
-    my $schema    = 'http://schemas.openxmlformats.org/drawingml/';
-    my $xmlns_xdr = $schema . '2006/spreadsheetDrawing';
-    my $xmlns_a   = $schema . '2006/main';
-
-    my @attributes = (
-        'xmlns:xdr' => $xmlns_xdr,
-        'xmlns:a'   => $xmlns_a,
-    );
-
-    $self->xml_start_tag( 'xdr:wsDr', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_two_cell_anchor()
-#
-# Write the <xdr:twoCellAnchor> element.
-#
-sub _write_two_cell_anchor {
-
-    my $self            = shift;
-    my $index           = shift;
-    my $type            = shift;
-    my $col_from        = shift;
-    my $row_from        = shift;
-    my $col_from_offset = shift;
-    my $row_from_offset = shift;
-    my $col_to          = shift;
-    my $row_to          = shift;
-    my $col_to_offset   = shift;
-    my $row_to_offset   = shift;
-    my $col_absolute    = shift;
-    my $row_absolute    = shift;
-    my $width           = shift;
-    my $height          = shift;
-    my $description     = shift;
-    my $shape           = shift;
-
-    my @attributes = ();
-
-
-    # Add attribute for images.
-    if ( $type == 2 ) {
-        push @attributes, ( editAs => 'oneCell' );
-    }
-
-    # Add editAs attribute for shapes.
-    push @attributes, ( editAs => $shape->{_editAs} ) if $shape->{_editAs};
-
-    $self->xml_start_tag( 'xdr:twoCellAnchor', @attributes );
-
-    # Write the xdr:from element.
-    $self->_write_from(
-        $col_from,
-        $row_from,
-        $col_from_offset,
-        $row_from_offset,
-
-    );
-
-    # Write the xdr:from element.
-    $self->_write_to(
-        $col_to,
-        $row_to,
-        $col_to_offset,
-        $row_to_offset,
-
-    );
-
-    if ( $type == 1 ) {
-
-        # Graphic frame.
-
-        # Write the xdr:graphicFrame element for charts.
-        $self->_write_graphic_frame( $index, $description );
-    }
-    elsif ( $type == 2 ) {
-
-        # Write the xdr:pic element.
-        $self->_write_pic( $index, $col_absolute, $row_absolute, $width,
-            $height, $description );
-    }
-    else {
-
-        # Write the xdr:sp element for shapes.
-        $self->_write_sp( $index, $col_absolute, $row_absolute, $width, $height,
-            $shape );
-    }
-
-    # Write the xdr:clientData element.
-    $self->_write_client_data();
-
-    $self->xml_end_tag( 'xdr:twoCellAnchor' );
-}
-
-
-##############################################################################
-#
-# _write_absolute_anchor()
-#
-# Write the <xdr:absoluteAnchor> element.
-#
-sub _write_absolute_anchor {
-
-    my $self  = shift;
-    my $index = shift;
-
-    $self->xml_start_tag( 'xdr:absoluteAnchor' );
-
-    # Different co-ordinates for horizonatal (= 0) and vertical (= 1).
-    if ( $self->{_orientation} == 0 ) {
-
-        # Write the xdr:pos element.
-        $self->_write_pos( 0, 0 );
-
-        # Write the xdr:ext element.
-        $self->_write_ext( 9308969, 6078325 );
-
-    }
-    else {
-
-        # Write the xdr:pos element.
-        $self->_write_pos( 0, -47625 );
-
-        # Write the xdr:ext element.
-        $self->_write_ext( 6162675, 6124575 );
-
-    }
-
-
-    # Write the xdr:graphicFrame element.
-    $self->_write_graphic_frame( $index );
-
-    # Write the xdr:clientData element.
-    $self->_write_client_data();
-
-    $self->xml_end_tag( 'xdr:absoluteAnchor' );
-}
-
-
-##############################################################################
-#
-# _write_from()
-#
-# Write the <xdr:from> element.
-#
-sub _write_from {
-
-    my $self       = shift;
-    my $col        = shift;
-    my $row        = shift;
-    my $col_offset = shift;
-    my $row_offset = shift;
-
-    $self->xml_start_tag( 'xdr:from' );
-
-    # Write the xdr:col element.
-    $self->_write_col( $col );
-
-    # Write the xdr:colOff element.
-    $self->_write_col_off( $col_offset );
-
-    # Write the xdr:row element.
-    $self->_write_row( $row );
-
-    # Write the xdr:rowOff element.
-    $self->_write_row_off( $row_offset );
-
-    $self->xml_end_tag( 'xdr:from' );
-}
-
-
-##############################################################################
-#
-# _write_to()
-#
-# Write the <xdr:to> element.
-#
-sub _write_to {
-
-    my $self       = shift;
-    my $col        = shift;
-    my $row        = shift;
-    my $col_offset = shift;
-    my $row_offset = shift;
-
-    $self->xml_start_tag( 'xdr:to' );
-
-    # Write the xdr:col element.
-    $self->_write_col( $col );
-
-    # Write the xdr:colOff element.
-    $self->_write_col_off( $col_offset );
-
-    # Write the xdr:row element.
-    $self->_write_row( $row );
-
-    # Write the xdr:rowOff element.
-    $self->_write_row_off( $row_offset );
-
-    $self->xml_end_tag( 'xdr:to' );
-}
-
-
-##############################################################################
-#
-# _write_col()
-#
-# Write the <xdr:col> element.
-#
-sub _write_col {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'xdr:col', $data );
-}
-
-
-##############################################################################
-#
-# _write_col_off()
-#
-# Write the <xdr:colOff> element.
-#
-sub _write_col_off {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'xdr:colOff', $data );
-}
-
-
-##############################################################################
-#
-# _write_row()
-#
-# Write the <xdr:row> element.
-#
-sub _write_row {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'xdr:row', $data );
-}
-
-
-##############################################################################
-#
-# _write_row_off()
-#
-# Write the <xdr:rowOff> element.
-#
-sub _write_row_off {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'xdr:rowOff', $data );
-}
-
-
-##############################################################################
-#
-# _write_pos()
-#
-# Write the <xdr:pos> element.
-#
-sub _write_pos {
-
-    my $self = shift;
-    my $x    = shift;
-    my $y    = shift;
-
-    my @attributes = (
-        'x' => $x,
-        'y' => $y,
-    );
-
-    $self->xml_empty_tag( 'xdr:pos', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_ext()
-#
-# Write the <xdr:ext> element.
-#
-sub _write_ext {
-
-    my $self = shift;
-    my $cx   = shift;
-    my $cy   = shift;
-
-    my @attributes = (
-        'cx' => $cx,
-        'cy' => $cy,
-    );
-
-    $self->xml_empty_tag( 'xdr:ext', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_graphic_frame()
-#
-# Write the <xdr:graphicFrame> element.
-#
-sub _write_graphic_frame {
-
-    my $self  = shift;
-    my $index = shift;
-    my $name  = shift;
-    my $macro = '';
-
-    my @attributes = ( 'macro' => $macro );
-
-    $self->xml_start_tag( 'xdr:graphicFrame', @attributes );
-
-    # Write the xdr:nvGraphicFramePr element.
-    $self->_write_nv_graphic_frame_pr( $index, $name );
-
-    # Write the xdr:xfrm element.
-    $self->_write_xfrm();
-
-    # Write the a:graphic element.
-    $self->_write_atag_graphic( $index );
-
-    $self->xml_end_tag( 'xdr:graphicFrame' );
-}
-
-
-##############################################################################
-#
-# _write_nv_graphic_frame_pr()
-#
-# Write the <xdr:nvGraphicFramePr> element.
-#
-sub _write_nv_graphic_frame_pr {
-
-    my $self  = shift;
-    my $index = shift;
-    my $name  = shift;
-
-    if ( !$name ) {
-        $name = 'Chart ' . $index;
-    }
-
-    $self->xml_start_tag( 'xdr:nvGraphicFramePr' );
-
-    # Write the xdr:cNvPr element.
-    $self->_write_c_nv_pr( $index + 1, $name );
-
-    # Write the xdr:cNvGraphicFramePr element.
-    $self->_write_c_nv_graphic_frame_pr();
-
-    $self->xml_end_tag( 'xdr:nvGraphicFramePr' );
-}
-
-
-##############################################################################
-#
-# _write_c_nv_pr()
-#
-# Write the <xdr:cNvPr> element.
-#
-sub _write_c_nv_pr {
-
-    my $self  = shift;
-    my $id    = shift;
-    my $name  = shift;
-    my $descr = shift;
-
-    my @attributes = (
-        'id'   => $id,
-        'name' => $name,
-    );
-
-    # Add description attribute for images.
-    if ( defined $descr ) {
-        push @attributes, ( descr => $descr );
-    }
-
-    $self->xml_empty_tag( 'xdr:cNvPr', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_c_nv_graphic_frame_pr()
-#
-# Write the <xdr:cNvGraphicFramePr> element.
-#
-sub _write_c_nv_graphic_frame_pr {
-
-    my $self = shift;
-
-    if ( $self->{_embedded} ) {
-        $self->xml_empty_tag( 'xdr:cNvGraphicFramePr' );
-    }
-    else {
-        $self->xml_start_tag( 'xdr:cNvGraphicFramePr' );
-
-        # Write the a:graphicFrameLocks element.
-        $self->_write_a_graphic_frame_locks();
-
-        $self->xml_end_tag( 'xdr:cNvGraphicFramePr' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_a_graphic_frame_locks()
-#
-# Write the <a:graphicFrameLocks> element.
-#
-sub _write_a_graphic_frame_locks {
-
-    my $self   = shift;
-    my $no_grp = 1;
-
-    my @attributes = ( 'noGrp' => $no_grp );
-
-    $self->xml_empty_tag( 'a:graphicFrameLocks', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_xfrm()
-#
-# Write the <xdr:xfrm> element.
-#
-sub _write_xfrm {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'xdr:xfrm' );
-
-    # Write the xfrmOffset element.
-    $self->_write_xfrm_offset();
-
-    # Write the xfrmOffset element.
-    $self->_write_xfrm_extension();
-
-    $self->xml_end_tag( 'xdr:xfrm' );
-}
-
-
-##############################################################################
-#
-# _write_xfrm_offset()
-#
-# Write the <a:off> xfrm sub-element.
-#
-sub _write_xfrm_offset {
-
-    my $self = shift;
-    my $x    = 0;
-    my $y    = 0;
-
-    my @attributes = (
-        'x' => $x,
-        'y' => $y,
-    );
-
-    $self->xml_empty_tag( 'a:off', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_xfrm_extension()
-#
-# Write the <a:ext> xfrm sub-element.
-#
-sub _write_xfrm_extension {
-
-    my $self = shift;
-    my $x    = 0;
-    my $y    = 0;
-
-    my @attributes = (
-        'cx' => $x,
-        'cy' => $y,
-    );
-
-    $self->xml_empty_tag( 'a:ext', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_atag_graphic()
-#
-# Write the <a:graphic> element.
-#
-sub _write_atag_graphic {
-
-    my $self  = shift;
-    my $index = shift;
-
-    $self->xml_start_tag( 'a:graphic' );
-
-    # Write the a:graphicData element.
-    $self->_write_atag_graphic_data( $index );
-
-    $self->xml_end_tag( 'a:graphic' );
-}
-
-
-##############################################################################
-#
-# _write_atag_graphic_data()
-#
-# Write the <a:graphicData> element.
-#
-sub _write_atag_graphic_data {
-
-    my $self  = shift;
-    my $index = shift;
-    my $uri   = 'http://schemas.openxmlformats.org/drawingml/2006/chart';
-
-    my @attributes = ( 'uri' => $uri, );
-
-    $self->xml_start_tag( 'a:graphicData', @attributes );
-
-    # Write the c:chart element.
-    $self->_write_c_chart( 'rId' . $index );
-
-    $self->xml_end_tag( 'a:graphicData' );
-}
-
-
-##############################################################################
-#
-# _write_c_chart()
-#
-# Write the <c:chart> element.
-#
-sub _write_c_chart {
-
-    my $self    = shift;
-    my $r_id    = shift;
-    my $schema  = 'http://schemas.openxmlformats.org/';
-    my $xmlns_c = $schema . 'drawingml/2006/chart';
-    my $xmlns_r = $schema . 'officeDocument/2006/relationships';
-
-
-    my @attributes = (
-        'xmlns:c' => $xmlns_c,
-        'xmlns:r' => $xmlns_r,
-        'r:id'    => $r_id,
-    );
-
-    $self->xml_empty_tag( 'c:chart', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_client_data()
-#
-# Write the <xdr:clientData> element.
-#
-sub _write_client_data {
-
-    my $self = shift;
-
-    $self->xml_empty_tag( 'xdr:clientData' );
-}
-
-
-##############################################################################
-#
-# _write_sp()
-#
-# Write the <xdr:sp> element.
-#
-sub _write_sp {
-
-    my $self         = shift;
-    my $index        = shift;
-    my $col_absolute = shift;
-    my $row_absolute = shift;
-    my $width        = shift;
-    my $height       = shift;
-    my $shape        = shift;
-
-    if ( $shape->{_connect} ) {
-        my @attributes = ( macro => '' );
-        $self->xml_start_tag( 'xdr:cxnSp', @attributes );
-
-        # Write the xdr:nvCxnSpPr element.
-        $self->_write_nv_cxn_sp_pr( $index, $shape );
-
-        # Write the xdr:spPr element.
-        $self->_write_xdr_sp_pr( $index, $col_absolute, $row_absolute, $width,
-            $height, $shape );
-
-        $self->xml_end_tag( 'xdr:cxnSp' );
-    }
-    else {
-
-        # Add attribute for shapes.
-        my @attributes = ( macro => '', textlink => '' );
-        $self->xml_start_tag( 'xdr:sp', @attributes );
-
-        # Write the xdr:nvSpPr element.
-        $self->_write_nv_sp_pr( $index, $shape );
-
-        # Write the xdr:spPr element.
-        $self->_write_xdr_sp_pr( $index, $col_absolute, $row_absolute, $width,
-            $height, $shape );
-
-        # Write the xdr:txBody element.
-        if ( $shape->{_text} ) {
-            $self->_write_txBody( $col_absolute, $row_absolute, $width, $height,
-                $shape );
-        }
-
-        $self->xml_end_tag( 'xdr:sp' );
-    }
-}
-##############################################################################
-#
-# _write_nv_cxn_sp_pr()
-#
-# Write the <xdr:nvCxnSpPr> element.
-#
-sub _write_nv_cxn_sp_pr {
-
-    my $self  = shift;
-    my $index = shift;
-    my $shape = shift;
-
-    $self->xml_start_tag( 'xdr:nvCxnSpPr' );
-
-    $shape->{_name} = join( ' ', $shape->{_type}, $index )
-      unless defined $shape->{_name};
-    $self->_write_c_nv_pr( $shape->{_id}, $shape->{_name} );
-
-    $self->xml_start_tag( 'xdr:cNvCxnSpPr' );
-
-    my @attributes = ( noChangeShapeType => '1' );
-    $self->xml_empty_tag( 'a:cxnSpLocks', @attributes );
-
-    if ( $shape->{_start} ) {
-        @attributes =
-          ( 'id' => $shape->{_start}, 'idx' => $shape->{_start_index} );
-        $self->xml_empty_tag( 'a:stCxn', @attributes );
-    }
-
-    if ( $shape->{_end} ) {
-        @attributes = ( 'id' => $shape->{_end}, 'idx' => $shape->{_end_index} );
-        $self->xml_empty_tag( 'a:endCxn', @attributes );
-    }
-    $self->xml_end_tag( 'xdr:cNvCxnSpPr' );
-    $self->xml_end_tag( 'xdr:nvCxnSpPr' );
-}
-
-
-##############################################################################
-#
-# _write_nv_sp_pr()
-#
-# Write the <xdr:NvSpPr> element.
-#
-sub _write_nv_sp_pr {
-
-    my $self  = shift;
-    my $index = shift;
-    my $shape = shift;
-
-    my @attributes = ();
-
-    $self->xml_start_tag( 'xdr:nvSpPr' );
-
-    my $shape_name = $shape->{_type} . ' ' . $index;
-
-    $self->_write_c_nv_pr( $shape->{_id}, $shape_name );
-
-    @attributes = ( 'txBox' => 1 ) if $shape->{_txBox};
-
-    $self->xml_start_tag( 'xdr:cNvSpPr', @attributes );
-
-    @attributes = ( noChangeArrowheads => '1' );
-
-    $self->xml_empty_tag( 'a:spLocks', @attributes );
-
-    $self->xml_end_tag( 'xdr:cNvSpPr' );
-    $self->xml_end_tag( 'xdr:nvSpPr' );
-}
-
-
-##############################################################################
-#
-# _write_pic()
-#
-# Write the <xdr:pic> element.
-#
-sub _write_pic {
-
-    my $self         = shift;
-    my $index        = shift;
-    my $col_absolute = shift;
-    my $row_absolute = shift;
-    my $width        = shift;
-    my $height       = shift;
-    my $description  = shift;
-
-    $self->xml_start_tag( 'xdr:pic' );
-
-    # Write the xdr:nvPicPr element.
-    $self->_write_nv_pic_pr( $index, $description );
-
-    # Write the xdr:blipFill element.
-    $self->_write_blip_fill( $index );
-
-    # Pictures are rectangle shapes by default.
-    my $shape = { _type => 'rect' };
-
-    # Write the xdr:spPr element.
-    $self->_write_sp_pr( $col_absolute, $row_absolute, $width, $height,
-        $shape );
-
-    $self->xml_end_tag( 'xdr:pic' );
-}
-
-
-##############################################################################
-#
-# _write_nv_pic_pr()
-#
-# Write the <xdr:nvPicPr> element.
-#
-sub _write_nv_pic_pr {
-
-    my $self        = shift;
-    my $index       = shift;
-    my $description = shift;
-
-    $self->xml_start_tag( 'xdr:nvPicPr' );
-
-    # Write the xdr:cNvPr element.
-    $self->_write_c_nv_pr( $index + 1, 'Picture ' . $index, $description );
-
-    # Write the xdr:cNvPicPr element.
-    $self->_write_c_nv_pic_pr();
-
-    $self->xml_end_tag( 'xdr:nvPicPr' );
-}
-
-
-##############################################################################
-#
-# _write_c_nv_pic_pr()
-#
-# Write the <xdr:cNvPicPr> element.
-#
-sub _write_c_nv_pic_pr {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'xdr:cNvPicPr' );
-
-    # Write the a:picLocks element.
-    $self->_write_a_pic_locks();
-
-    $self->xml_end_tag( 'xdr:cNvPicPr' );
-}
-
-
-##############################################################################
-#
-# _write_a_pic_locks()
-#
-# Write the <a:picLocks> element.
-#
-sub _write_a_pic_locks {
-
-    my $self             = shift;
-    my $no_change_aspect = 1;
-
-    my @attributes = ( 'noChangeAspect' => $no_change_aspect );
-
-    $self->xml_empty_tag( 'a:picLocks', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_blip_fill()
-#
-# Write the <xdr:blipFill> element.
-#
-sub _write_blip_fill {
-
-    my $self  = shift;
-    my $index = shift;
-
-    $self->xml_start_tag( 'xdr:blipFill' );
-
-    # Write the a:blip element.
-    $self->_write_a_blip( $index );
-
-    # Write the a:stretch element.
-    $self->_write_a_stretch();
-
-    $self->xml_end_tag( 'xdr:blipFill' );
-}
-
-
-##############################################################################
-#
-# _write_a_blip()
-#
-# Write the <a:blip> element.
-#
-sub _write_a_blip {
-
-    my $self    = shift;
-    my $index   = shift;
-    my $schema  = 'http://schemas.openxmlformats.org/officeDocument/';
-    my $xmlns_r = $schema . '2006/relationships';
-    my $r_embed = 'rId' . $index;
-
-    my @attributes = (
-        'xmlns:r' => $xmlns_r,
-        'r:embed' => $r_embed,
-    );
-
-    $self->xml_empty_tag( 'a:blip', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_a_stretch()
-#
-# Write the <a:stretch> element.
-#
-sub _write_a_stretch {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'a:stretch' );
-
-    # Write the a:fillRect element.
-    $self->_write_a_fill_rect();
-
-    $self->xml_end_tag( 'a:stretch' );
-}
-
-
-##############################################################################
-#
-# _write_a_fill_rect()
-#
-# Write the <a:fillRect> element.
-#
-sub _write_a_fill_rect {
-
-    my $self = shift;
-
-    $self->xml_empty_tag( 'a:fillRect' );
-}
-
-
-##############################################################################
-#
-# _write_sp_pr()
-#
-# Write the <xdr:spPr> element, for charts.
-#
-sub _write_sp_pr {
-
-    my $self         = shift;
-    my $col_absolute = shift;
-    my $row_absolute = shift;
-    my $width        = shift;
-    my $height       = shift;
-    my $shape        = shift || {};
-
-    $self->xml_start_tag( 'xdr:spPr' );
-
-    # Write the a:xfrm element.
-    $self->_write_a_xfrm( $col_absolute, $row_absolute, $width, $height );
-
-    # Write the a:prstGeom element.
-    $self->_write_a_prst_geom( $shape );
-
-    $self->xml_end_tag( 'xdr:spPr' );
-}
-
-
-##############################################################################
-#
-# _write_xdr_sp_pr()
-#
-# Write the <xdr:spPr> element for shapes.
-#
-sub _write_xdr_sp_pr {
-
-    my $self         = shift;
-    my $index        = shift;
-    my $col_absolute = shift;
-    my $row_absolute = shift;
-    my $width        = shift;
-    my $height       = shift;
-    my $shape        = shift;
-
-    my @attributes = ( 'bwMode' => 'auto' );
-
-    $self->xml_start_tag( 'xdr:spPr', @attributes );
-
-    # Write the a:xfrm element.
-    $self->_write_a_xfrm( $col_absolute, $row_absolute, $width, $height,
-        $shape );
-
-    # Write the a:prstGeom element.
-    $self->_write_a_prst_geom( $shape );
-
-    my $fill = $shape->{_fill};
-
-    if ( length $fill > 1 ) {
-
-        # Write the a:solidFill element.
-        $self->_write_a_solid_fill( $fill );
-    }
-    else {
-        $self->xml_empty_tag( 'a:noFill' );
-    }
-
-    # Write the a:ln element.
-    $self->_write_a_ln( $shape );
-
-    $self->xml_end_tag( 'xdr:spPr' );
-}
-
-##############################################################################
-#
-# _write_a_xfrm()
-#
-# Write the <a:xfrm> element.
-#
-sub _write_a_xfrm {
-
-    my $self         = shift;
-    my $col_absolute = shift;
-    my $row_absolute = shift;
-    my $width        = shift;
-    my $height       = shift;
-    my $shape        = shift || {};
-    my @attributes   = ();
-
-    my $rotation = $shape->{_rotation} || 0;
-    $rotation *= 60000;
-
-    push( @attributes, ( 'rot'   => $rotation ) ) if $rotation;
-    push( @attributes, ( 'flipH' => 1 ) )         if $shape->{_flip_h};
-    push( @attributes, ( 'flipV' => 1 ) )         if $shape->{_flip_v};
-
-    $self->xml_start_tag( 'a:xfrm', @attributes );
-
-    # Write the a:off element.
-    $self->_write_a_off( $col_absolute, $row_absolute );
-
-    # Write the a:ext element.
-    $self->_write_a_ext( $width, $height );
-
-    $self->xml_end_tag( 'a:xfrm' );
-}
-
-
-##############################################################################
-#
-# _write_a_off()
-#
-# Write the <a:off> element.
-#
-sub _write_a_off {
-
-    my $self = shift;
-    my $x    = shift;
-    my $y    = shift;
-
-    my @attributes = (
-        'x' => $x,
-        'y' => $y,
-    );
-
-    $self->xml_empty_tag( 'a:off', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_a_ext()
-#
-# Write the <a:ext> element.
-#
-sub _write_a_ext {
-
-    my $self = shift;
-    my $cx   = shift;
-    my $cy   = shift;
-
-    my @attributes = (
-        'cx' => $cx,
-        'cy' => $cy,
-    );
-
-    $self->xml_empty_tag( 'a:ext', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_a_prst_geom()
-#
-# Write the <a:prstGeom> element.
-#
-sub _write_a_prst_geom {
-
-    my $self = shift;
-    my $shape = shift || {};
-
-    my @attributes = ();
-
-    @attributes = ( 'prst' => $shape->{_type} ) if $shape->{_type};
-
-    $self->xml_start_tag( 'a:prstGeom', @attributes );
-
-    # Write the a:avLst element.
-    $self->_write_a_av_lst( $shape );
-
-    $self->xml_end_tag( 'a:prstGeom' );
-}
-
-
-##############################################################################
-#
-# _write_a_av_lst()
-#
-# Write the <a:avLst> element.
-#
-sub _write_a_av_lst {
-
-    my $self        = shift;
-    my $shape       = shift || {};
-    my $adjustments = [];
-
-    if ( defined $shape->{_adjustments} ) {
-        $adjustments = $shape->{_adjustments};
-    }
-
-    if ( @$adjustments ) {
-        $self->xml_start_tag( 'a:avLst' );
-
-        my $i = 0;
-        foreach my $adj ( @{$adjustments} ) {
-            $i++;
-
-            # Only connectors have multiple adjustments.
-            my $suffix = $shape->{_connect} ? $i : '';
-
-            # Scale Adjustments: 100,000 = 100%.
-            my $adj_int = int( $adj * 1000 );
-
-            my @attributes =
-              ( name => 'adj' . $suffix, fmla => "val $adj_int" );
-
-            $self->xml_empty_tag( 'a:gd', @attributes );
-        }
-        $self->xml_end_tag( 'a:avLst' );
-    }
-    else {
-        $self->xml_empty_tag( 'a:avLst' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_a_solid_fill()
-#
-# Write the <a:solidFill> element.
-#
-sub _write_a_solid_fill {
-
-    my $self = shift;
-    my $rgb  = shift;
-
-    $rgb = '000000' unless defined $rgb;
-
-    my @attributes = ( 'val' => $rgb );
-
-    $self->xml_start_tag( 'a:solidFill' );
-
-    $self->xml_empty_tag( 'a:srgbClr', @attributes );
-
-    $self->xml_end_tag( 'a:solidFill' );
-}
-
-
-##############################################################################
-#
-# _write_a_ln()
-#
-# Write the <a:ln> element.
-#
-sub _write_a_ln {
-
-    my $self = shift;
-    my $shape = shift || {};
-
-    my $weight = $shape->{_line_weight};
-
-    my @attributes = ( 'w' => $weight * 9525 );
-
-    $self->xml_start_tag( 'a:ln', @attributes );
-
-    my $line = $shape->{_line};
-
-    if ( length $line > 1 ) {
-
-        # Write the a:solidFill element.
-        $self->_write_a_solid_fill( $line );
-    }
-    else {
-        $self->xml_empty_tag( 'a:noFill' );
-    }
-
-    if ( $shape->{_line_type} ) {
-
-        @attributes = ( 'val' => $shape->{_line_type} );
-        $self->xml_empty_tag( 'a:prstDash', @attributes );
-    }
-
-    if ( $shape->{_connect} ) {
-        $self->xml_empty_tag( 'a:round' );
-    }
-    else {
-        @attributes = ( 'lim' => 800000 );
-        $self->xml_empty_tag( 'a:miter', @attributes );
-    }
-
-    $self->xml_empty_tag( 'a:headEnd' );
-    $self->xml_empty_tag( 'a:tailEnd' );
-
-    $self->xml_end_tag( 'a:ln' );
-}
-
-
-##############################################################################
-#
-# _write_txBody
-#
-# Write the <xdr:txBody> element.
-#
-sub _write_txBody {
-
-    my $self         = shift;
-    my $col_absolute = shift;
-    my $row_absolute = shift;
-    my $width        = shift;
-    my $height       = shift;
-    my $shape        = shift;
-
-    my @attributes = (
-        vertOverflow => "clip",
-        wrap         => "square",
-        lIns         => "27432",
-        tIns         => "22860",
-        rIns         => "27432",
-        bIns         => "22860",
-        anchor       => $shape->{_valign},
-        upright      => "1",
-    );
-
-    $self->xml_start_tag( 'xdr:txBody' );
-    $self->xml_empty_tag( 'a:bodyPr', @attributes );
-    $self->xml_empty_tag( 'a:lstStyle' );
-
-    $self->xml_start_tag( 'a:p' );
-
-    my $rotation = $shape->{_format}->{_rotation};
-    $rotation = 0 unless defined $rotation;
-    $rotation *= 60000;
-
-    @attributes = ( algn => $shape->{_align}, rtl => $rotation );
-    $self->xml_start_tag( 'a:pPr', @attributes );
-
-    @attributes = ( sz => "1000" );
-    $self->xml_empty_tag( 'a:defRPr', @attributes );
-
-    $self->xml_end_tag( 'a:pPr' );
-    $self->xml_start_tag( 'a:r' );
-
-    my $size = $shape->{_format}->{_size};
-    $size = 8 unless defined $size;
-    $size *= 100;
-
-    my $bold = $shape->{_format}->{_bold};
-    $bold = 0 unless defined $bold;
-
-    my $italic = $shape->{_format}->{_italic};
-    $italic = 0 unless defined $italic;
-
-    my $underline = $shape->{_format}->{_underline};
-    $underline = $underline ? 'sng' : 'none';
-
-    my $strike = $shape->{_format}->{_font_strikeout};
-    $strike = $strike ? 'Strike' : 'noStrike';
-
-    @attributes = (
-        lang     => "en-US",
-        sz       => $size,
-        b        => $bold,
-        i        => $italic,
-        u        => $underline,
-        strike   => $strike,
-        baseline => 0,
-    );
-
-    $self->xml_start_tag( 'a:rPr', @attributes );
-
-    my $color = $shape->{_format}->{_color};
-    if ( defined $color ) {
-        $color = $shape->_get_palette_color( $color );
-        $color =~ s/^FF//;    # Remove leading FF from rgb for shape color.
-    }
-    else {
-        $color = '000000';
-    }
-
-    $self->_write_a_solid_fill( $color );
-
-    my $font = $shape->{_format}->{_font};
-    $font = 'Calibri' unless defined $font;
-    @attributes = ( typeface => $font );
-    $self->xml_empty_tag( 'a:latin', @attributes );
-
-    $self->xml_empty_tag( 'a:cs', @attributes );
-
-    $self->xml_end_tag( 'a:rPr' );
-
-    $self->xml_data_element( 'a:t', $shape->{_text} );
-
-    $self->xml_end_tag( 'a:r' );
-    $self->xml_end_tag( 'a:p' );
-    $self->xml_end_tag( 'xdr:txBody' );
-
-}
-
-
-1;
-__END__
-
-=pod
-
-=head1 NAME
-
-Drawing - A class for writing the Excel XLSX drawing.xml file.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Examples.pm b/tools/lib/perl5/Excel/Writer/XLSX/Examples.pm
deleted file mode 100644 (file)
index ecbee7b..0000000
+++ /dev/null
@@ -1,11227 +0,0 @@
-package Excel::Writer::XLSX::Examples;
-
-###############################################################################
-#
-# Examples - Excel::Writer::XLSX examples.
-#
-# A documentation only module showing the examples that are
-# included in the Excel::Writer::XLSX distribution. This
-# file was generated automatically via the gen_examples_pod.pl
-# program that is also included in the examples directory.
-#
-# Copyright 2000-2016, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-use strict;
-use warnings;
-
-our $VERSION = '0.98';
-
-1;
-
-__END__
-
-=pod
-
-=encoding ISO8859-1
-
-=head1 NAME
-
-Examples - Excel::Writer::XLSX example programs.
-
-=head1 DESCRIPTION
-
-This is a documentation only module showing the examples that are
-included in the L<Excel::Writer::XLSX> distribution.
-
-This file was auto-generated via the gen_examples_pod.pl
-program that is also included in the examples directory.
-
-=head1 Example programs
-
-The following is a list of the 87 example programs that are included in the Excel::Writer::XLSX distribution.
-
-=over
-
-=item * L<Example: a_simple.pl> A simple demo of some of the features.
-
-=item * L<Example: bug_report.pl> A template for submitting bug reports.
-
-=item * L<Example: demo.pl> A demo of some of the available features.
-
-=item * L<Example: formats.pl> All the available formatting on several worksheets.
-
-=item * L<Example: regions.pl> A simple example of multiple worksheets.
-
-=item * L<Example: stats.pl> Basic formulas and functions.
-
-=item * L<Example: autofilter.pl> Examples of worksheet autofilters.
-
-=item * L<Example: array_formula.pl> Examples of how to write array formulas.
-
-=item * L<Example: cgi.pl> A simple CGI program.
-
-=item * L<Example: chart_area.pl> A demo of area style charts.
-
-=item * L<Example: chart_bar.pl> A demo of bar (vertical histogram) style charts.
-
-=item * L<Example: chart_column.pl> A demo of column (histogram) style charts.
-
-=item * L<Example: chart_line.pl> A demo of line style charts.
-
-=item * L<Example: chart_pie.pl> A demo of pie style charts.
-
-=item * L<Example: chart_doughnut.pl> A demo of dougnut style charts.
-
-=item * L<Example: chart_radar.pl> A demo of radar style charts.
-
-=item * L<Example: chart_scatter.pl> A demo of scatter style charts.
-
-=item * L<Example: chart_secondary_axis.pl> A demo of a line chart with a secondary axis.
-
-=item * L<Example: chart_combined.pl> A demo of a combined column and line chart.
-
-=item * L<Example: chart_pareto.pl> A demo of a combined Pareto chart.
-
-=item * L<Example: chart_stock.pl> A demo of stock style charts.
-
-=item * L<Example: chart_data_table.pl> A demo of a chart with a data table on the axis.
-
-=item * L<Example: chart_data_tools.pl> A demo of charts with data highlighting options.
-
-=item * L<Example: chart_clustered.pl> A demo of a chart with a clustered axis.
-
-=item * L<Example: chart_styles.pl> A demo of the available chart styles.
-
-=item * L<Example: colors.pl> A demo of the colour palette and named colours.
-
-=item * L<Example: comments1.pl> Add comments to worksheet cells.
-
-=item * L<Example: comments2.pl> Add comments with advanced options.
-
-=item * L<Example: conditional_format.pl> Add conditional formats to a range of cells.
-
-=item * L<Example: data_validate.pl> An example of data validation and dropdown lists.
-
-=item * L<Example: date_time.pl> Write dates and times with write_date_time().
-
-=item * L<Example: defined_name.pl> Example of how to create defined names.
-
-=item * L<Example: diag_border.pl> A simple example of diagonal cell borders.
-
-=item * L<Example: filehandle.pl> Examples of working with filehandles.
-
-=item * L<Example: headers.pl> Examples of worksheet headers and footers.
-
-=item * L<Example: hide_row_col.pl> Example of hiding rows and columns.
-
-=item * L<Example: hide_sheet.pl> Simple example of hiding a worksheet.
-
-=item * L<Example: hyperlink1.pl> Shows how to create web hyperlinks.
-
-=item * L<Example: hyperlink2.pl> Examples of internal and external hyperlinks.
-
-=item * L<Example: indent.pl> An example of cell indentation.
-
-=item * L<Example: macros.pl> An example of adding macros from an existing file.
-
-=item * L<Example: merge1.pl> A simple example of cell merging.
-
-=item * L<Example: merge2.pl> A simple example of cell merging with formatting.
-
-=item * L<Example: merge3.pl> Add hyperlinks to merged cells.
-
-=item * L<Example: merge4.pl> An advanced example of merging with formatting.
-
-=item * L<Example: merge5.pl> An advanced example of merging with formatting.
-
-=item * L<Example: merge6.pl> An example of merging with Unicode strings.
-
-=item * L<Example: mod_perl1.pl> A simple mod_perl 1 program.
-
-=item * L<Example: mod_perl2.pl> A simple mod_perl 2 program.
-
-=item * L<Example: outline.pl> An example of outlines and grouping.
-
-=item * L<Example: outline_collapsed.pl> An example of collapsed outlines.
-
-=item * L<Example: panes.pl> An example of how to create panes.
-
-=item * L<Example: properties.pl> Add document properties to a workbook.
-
-=item * L<Example: protection.pl> Example of cell locking and formula hiding.
-
-=item * L<Example: rich_strings.pl> Example of strings with multiple formats.
-
-=item * L<Example: right_to_left.pl> Change default sheet direction to right to left.
-
-=item * L<Example: sales.pl> An example of a simple sales spreadsheet.
-
-=item * L<Example: shape1.pl> Insert shapes in worksheet.
-
-=item * L<Example: shape2.pl> Insert shapes in worksheet. With properties.
-
-=item * L<Example: shape3.pl> Insert shapes in worksheet. Scaled.
-
-=item * L<Example: shape4.pl> Insert shapes in worksheet. With modification.
-
-=item * L<Example: shape5.pl> Insert shapes in worksheet. With connections.
-
-=item * L<Example: shape6.pl> Insert shapes in worksheet. With connections.
-
-=item * L<Example: shape7.pl> Insert shapes in worksheet. One to many connections.
-
-=item * L<Example: shape8.pl> Insert shapes in worksheet. One to many connections.
-
-=item * L<Example: shape_all.pl> Demo of all the available shape and connector types.
-
-=item * L<Example: sparklines1.pl> Simple sparklines demo.
-
-=item * L<Example: sparklines2.pl> Sparklines demo showing formatting options.
-
-=item * L<Example: stats_ext.pl> Same as stats.pl with external references.
-
-=item * L<Example: stocks.pl> Demonstrates conditional formatting.
-
-=item * L<Example: tab_colors.pl> Example of how to set worksheet tab colours.
-
-=item * L<Example: tables.pl> Add Excel tables to a worksheet.
-
-=item * L<Example: write_handler1.pl> Example of extending the write() method. Step 1.
-
-=item * L<Example: write_handler2.pl> Example of extending the write() method. Step 2.
-
-=item * L<Example: write_handler3.pl> Example of extending the write() method. Step 3.
-
-=item * L<Example: write_handler4.pl> Example of extending the write() method. Step 4.
-
-=item * L<Example: write_to_scalar.pl> Example of writing an Excel file to a Perl scalar.
-
-=item * L<Example: unicode_2022_jp.pl> Japanese: ISO-2022-JP.
-
-=item * L<Example: unicode_8859_11.pl> Thai:     ISO-8859_11.
-
-=item * L<Example: unicode_8859_7.pl> Greek:    ISO-8859_7.
-
-=item * L<Example: unicode_big5.pl> Chinese:  BIG5.
-
-=item * L<Example: unicode_cp1251.pl> Russian:  CP1251.
-
-=item * L<Example: unicode_cp1256.pl> Arabic:   CP1256.
-
-=item * L<Example: unicode_cyrillic.pl> Russian:  Cyrillic.
-
-=item * L<Example: unicode_koi8r.pl> Russian:  KOI8-R.
-
-=item * L<Example: unicode_polish_utf8.pl> Polish :  UTF8.
-
-=item * L<Example: unicode_shift_jis.pl> Japanese: Shift JIS.
-
-=back
-
-=head2 Example: a_simple.pl
-
-
-
-A simple example of how to use the Excel::Writer::XLSX module to
-write text and numbers to an Excel xlsx file.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/a_simple.jpg" width="640" height="420" alt="Output from a_simple.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    #######################################################################
-    #
-    # A simple example of how to use the Excel::Writer::XLSX module to
-    # write text and numbers to an Excel xlsx file.
-    #
-    # reverse ('(c)'), March 2001, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook called simple.xls and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'a_simple.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    # The general syntax is write($row, $column, $token). Note that row and
-    # column are zero indexed
-    #
-    
-    # Write some text
-    $worksheet->write( 0, 0, "Hi Excel!" );
-    
-    
-    # Write some numbers
-    $worksheet->write( 2, 0, 3 );          # Writes 3
-    $worksheet->write( 3, 0, 3.00000 );    # Writes 3
-    $worksheet->write( 4, 0, 3.00001 );    # Writes 3.00001
-    $worksheet->write( 5, 0, 3.14159 );    # TeX revision no.?
-    
-    
-    # Write some formulas
-    $worksheet->write( 7, 0, '=A3 + A6' );
-    $worksheet->write( 8, 0, '=IF(A5>3,"Yes", "No")' );
-    
-    
-    # Write a hyperlink
-    $worksheet->write( 10, 0, 'http://www.perl.com/' );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/a_simple.pl>
-
-=head2 Example: bug_report.pl
-
-
-
-A template for submitting a bug report.
-
-Run this program and read the output from the command line.
-
-
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # A template for submitting a bug report.
-    #
-    # Run this program and read the output from the command line.
-    #
-    # reverse ('(c)'), March 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    
-    use strict;
-    
-    print << 'HINTS_1';
-    
-    REPORTING A BUG OR ASKING A QUESTION
-    
-        Feel free to report bugs or ask questions. However, to save time
-        consider the following steps first:
-    
-        Read the documentation:
-    
-            The Excel::Writer::XLSX documentation has been refined in
-            response to user questions. Therefore, if you have a question it is
-            possible that someone else has asked it before you and that it is
-            already addressed in the documentation. Since there is a lot of
-            documentation to get through you should at least read the table of
-            contents and search for keywords that you are interested in.
-    
-        Look at the example programs:
-    
-            There are over 80 example programs shipped with the standard
-            Excel::Writer::XLSX distribution. Many of these were created
-            in response to user questions. Try to identify an example program
-            that corresponds to your query and adapt it to your needs.
-    
-    HINTS_1
-    print "Press enter ..."; <STDIN>;
-    
-    print << 'HINTS_2';
-    
-        If you submit a bug report here are some pointers.
-    
-        1.  Put "Excel::Writer::XLSX:" at the beginning of the subject line.
-            This helps to filter genuine messages from spam.
-    
-        2.  Describe the problems as clearly and as concisely as possible.
-    
-        3.  Send a sample program. It is often easier to describe a problem in
-            code than in written prose.
-    
-        4.  The sample program should be as small as possible to demonstrate the
-            problem. Don't copy and past large sections of your program. The
-            program should also be self contained and working.
-    
-        A sample bug report is generated below. If you use this format then it
-        will help to analyse your question and respond to it more quickly.
-    
-        Please don't send patches without contacting the author first.
-    
-    
-    HINTS_2
-    print "Press enter ..."; <STDIN>;
-    
-    
-    print << 'EMAIL';
-    
-    =======================================================================
-    
-    To:      John McNamara <jmcnamara@cpan.org>
-    Subject: Excel::Writer::XLSX: Problem with something.
-    
-    Hi John,
-    
-    I am using Excel::Writer::XLSX and I have encountered a problem. I
-    want it to do SOMETHING but the module appears to do SOMETHING_ELSE.
-    
-    Here is some code that demonstrates the problem.
-    
-        #!/usr/bin/perl -w
-    
-        use strict;
-        use Excel::Writer::XLSX;
-    
-        my $workbook  = Excel::Writer::XLSX->new("reload.xls");
-        my $worksheet = $workbook->add_worksheet();
-    
-        $worksheet->write(0, 0, "Hi Excel!");
-    
-        $workbook->close();
-    
-        __END__
-    
-    My automatically generated system details are as follows:
-    EMAIL
-    
-    
-    print "\n    Perl version   : $]";
-    print "\n    OS name        : $^O";
-    print "\n    Module versions: (not all are required)\n";
-    
-    
-    my @modules = qw(
-                      Excel::Writer::XLSX
-                      Spreadsheet::WriteExcel
-                      Archive::Zip
-                      XML::Writer
-                      IO::File
-                      File::Temp
-                    );
-    
-    
-    for my $module (@modules) {
-        my $version;
-        eval "require $module";
-    
-        if (not $@) {
-            $version = $module->VERSION;
-            $version = '(unknown)' if not defined $version;
-        }
-        else {
-            $version = '(not installed)';
-        }
-    
-        printf "%21s%-24s\t%s\n", "", $module, $version;
-    }
-    
-    
-    print << "BYE";
-    Yours etc.,
-    
-    A. Person
-    --
-    
-    BYE
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/bug_report.pl>
-
-=head2 Example: demo.pl
-
-
-
-A simple demo of some of the features of Excel::Writer::XLSX.
-
-This program is used to create the project screenshot for Freshmeat:
-L<http://freshmeat.net/projects/writeexcel/>
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/demo.jpg" width="640" height="420" alt="Output from demo.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    #######################################################################
-    #
-    # A simple demo of some of the features of Excel::Writer::XLSX.
-    #
-    # This program is used to create the project screenshot for Freshmeat:
-    # L<http://freshmeat.net/projects/writeexcel/>
-    #
-    # reverse ('(c)'), October 2001, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    my $workbook   = Excel::Writer::XLSX->new( 'demo.xlsx' );
-    my $worksheet  = $workbook->add_worksheet( 'Demo' );
-    my $worksheet2 = $workbook->add_worksheet( 'Another sheet' );
-    my $worksheet3 = $workbook->add_worksheet( 'And another' );
-    
-    my $bold = $workbook->add_format( bold => 1 );
-    
-    
-    #######################################################################
-    #
-    # Write a general heading
-    #
-    $worksheet->set_column( 'A:A', 36, $bold );
-    $worksheet->set_column( 'B:B', 20 );
-    $worksheet->set_row( 0, 40 );
-    
-    my $heading = $workbook->add_format(
-        bold  => 1,
-        color => 'blue',
-        size  => 16,
-        merge => 1,
-        align => 'vcenter',
-    );
-    
-    my @headings = ( 'Features of Excel::Writer::XLSX', '' );
-    $worksheet->write_row( 'A1', \@headings, $heading );
-    
-    
-    #######################################################################
-    #
-    # Some text examples
-    #
-    my $text_format = $workbook->add_format(
-        bold   => 1,
-        italic => 1,
-        color  => 'red',
-        size   => 18,
-        font   => 'Lucida Calligraphy'
-    );
-    
-    
-    $worksheet->write( 'A2', "Text" );
-    $worksheet->write( 'B2', "Hello Excel" );
-    $worksheet->write( 'A3', "Formatted text" );
-    $worksheet->write( 'B3', "Hello Excel", $text_format );
-    $worksheet->write( 'A4', "Unicode text" );
-    $worksheet->write( 'B4', "\x{0410} \x{0411} \x{0412} \x{0413} \x{0414}" );
-    
-    #######################################################################
-    #
-    # Some numeric examples
-    #
-    my $num1_format = $workbook->add_format( num_format => '$#,##0.00' );
-    my $num2_format = $workbook->add_format( num_format => ' d mmmm yyy' );
-    
-    
-    $worksheet->write( 'A5', "Numbers" );
-    $worksheet->write( 'B5', 1234.56 );
-    $worksheet->write( 'A6', "Formatted numbers" );
-    $worksheet->write( 'B6', 1234.56, $num1_format );
-    $worksheet->write( 'A7', "Formatted numbers" );
-    $worksheet->write( 'B7', 37257, $num2_format );
-    
-    
-    #######################################################################
-    #
-    # Formulae
-    #
-    $worksheet->set_selection( 'B8' );
-    $worksheet->write( 'A8', 'Formulas and functions, "=SIN(PI()/4)"' );
-    $worksheet->write( 'B8', '=SIN(PI()/4)' );
-    
-    
-    #######################################################################
-    #
-    # Hyperlinks
-    #
-    $worksheet->write( 'A9', "Hyperlinks" );
-    $worksheet->write( 'B9', 'http://www.perl.com/' );
-    
-    
-    #######################################################################
-    #
-    # Images
-    #
-    $worksheet->write( 'A10', "Images" );
-    $worksheet->insert_image( 'B10', 'republic.png', 16, 8 );
-    
-    
-    #######################################################################
-    #
-    # Misc
-    #
-    $worksheet->write( 'A18', "Page/printer setup" );
-    $worksheet->write( 'A19', "Multiple worksheets" );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/demo.pl>
-
-=head2 Example: formats.pl
-
-
-
-Examples of formatting using the Excel::Writer::XLSX module.
-
-This program demonstrates almost all possible formatting options. It is worth
-running this program and viewing the output Excel file if you are interested
-in the various formatting possibilities.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/formats.jpg" width="640" height="420" alt="Output from formats.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # Examples of formatting using the Excel::Writer::XLSX module.
-    #
-    # This program demonstrates almost all possible formatting options. It is worth
-    # running this program and viewing the output Excel file if you are interested
-    # in the various formatting possibilities.
-    #
-    # reverse ('(c)'), September 2002, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    my $workbook = Excel::Writer::XLSX->new( 'formats.xlsx' );
-    
-    # Some common formats
-    my $center = $workbook->add_format( align => 'center' );
-    my $heading = $workbook->add_format( align => 'center', bold => 1 );
-    
-    # The named colors
-    my %colors = (
-        0x08, 'black',
-        0x0C, 'blue',
-        0x10, 'brown',
-        0x0F, 'cyan',
-        0x17, 'gray',
-        0x11, 'green',
-        0x0B, 'lime',
-        0x0E, 'magenta',
-        0x12, 'navy',
-        0x35, 'orange',
-        0x21, 'pink',
-        0x14, 'purple',
-        0x0A, 'red',
-        0x16, 'silver',
-        0x09, 'white',
-        0x0D, 'yellow',
-    
-    );
-    
-    # Call these subroutines to demonstrate different formatting options
-    intro();
-    fonts();
-    named_colors();
-    standard_colors();
-    numeric_formats();
-    borders();
-    patterns();
-    alignment();
-    misc();
-    
-    # Note: this is required
-    $workbook->close();
-    
-    
-    ######################################################################
-    #
-    # Intro.
-    #
-    sub intro {
-    
-        my $worksheet = $workbook->add_worksheet( 'Introduction' );
-    
-        $worksheet->set_column( 0, 0, 60 );
-    
-        my $format = $workbook->add_format();
-        $format->set_bold();
-        $format->set_size( 14 );
-        $format->set_color( 'blue' );
-        $format->set_align( 'center' );
-    
-        my $format2 = $workbook->add_format();
-        $format2->set_bold();
-        $format2->set_color( 'blue' );
-    
-        my $format3 = $workbook->add_format(
-            color     => 'blue',
-            underline => 1,
-        );
-    
-        $worksheet->write( 2, 0, 'This workbook demonstrates some of', $format );
-        $worksheet->write( 3, 0, 'the formatting options provided by', $format );
-        $worksheet->write( 4, 0, 'the Excel::Writer::XLSX module.',    $format );
-        $worksheet->write( 'A7', 'Sections:', $format2 );
-    
-        $worksheet->write( 'A8', "internal:Fonts!A1", 'Fonts', $format3 );
-    
-        $worksheet->write( 'A9', "internal:'Named colors'!A1",
-            'Named colors', $format3 );
-    
-        $worksheet->write(
-            'A10',
-            "internal:'Standard colors'!A1",
-            'Standard colors', $format3
-        );
-    
-        $worksheet->write(
-            'A11',
-            "internal:'Numeric formats'!A1",
-            'Numeric formats', $format3
-        );
-    
-        $worksheet->write( 'A12', "internal:Borders!A1", 'Borders', $format3 );
-        $worksheet->write( 'A13', "internal:Patterns!A1", 'Patterns', $format3 );
-        $worksheet->write( 'A14', "internal:Alignment!A1", 'Alignment', $format3 );
-        $worksheet->write( 'A15', "internal:Miscellaneous!A1", 'Miscellaneous',
-            $format3 );
-    
-    }
-    
-    
-    ######################################################################
-    #
-    # Demonstrate the named colors.
-    #
-    sub named_colors {
-    
-        my $worksheet = $workbook->add_worksheet( 'Named colors' );
-    
-        $worksheet->set_column( 0, 3, 15 );
-    
-        $worksheet->write( 0, 0, "Index", $heading );
-        $worksheet->write( 0, 1, "Index", $heading );
-        $worksheet->write( 0, 2, "Name",  $heading );
-        $worksheet->write( 0, 3, "Color", $heading );
-    
-        my $i = 1;
-    
-        while ( my ( $index, $color ) = each %colors ) {
-            my $format = $workbook->add_format(
-                bg_color => $color,
-                pattern  => 1,
-                border   => 1
-            );
-    
-            $worksheet->write( $i + 1, 0, $index, $center );
-            $worksheet->write( $i + 1, 1, sprintf( "0x%02X", $index ), $center );
-            $worksheet->write( $i + 1, 2, $color, $center );
-            $worksheet->write( $i + 1, 3, '',     $format );
-            $i++;
-        }
-    }
-    
-    
-    ######################################################################
-    #
-    # Demonstrate the standard Excel colors in the range 8..63.
-    #
-    sub standard_colors {
-    
-        my $worksheet = $workbook->add_worksheet( 'Standard colors' );
-    
-        $worksheet->set_column( 0, 3, 15 );
-    
-        $worksheet->write( 0, 0, "Index", $heading );
-        $worksheet->write( 0, 1, "Index", $heading );
-        $worksheet->write( 0, 2, "Color", $heading );
-        $worksheet->write( 0, 3, "Name",  $heading );
-    
-        for my $i ( 8 .. 63 ) {
-            my $format = $workbook->add_format(
-                bg_color => $i,
-                pattern  => 1,
-                border   => 1
-            );
-    
-            $worksheet->write( ( $i - 7 ), 0, $i, $center );
-            $worksheet->write( ( $i - 7 ), 1, sprintf( "0x%02X", $i ), $center );
-            $worksheet->write( ( $i - 7 ), 2, '', $format );
-    
-            # Add the  color names
-            if ( exists $colors{$i} ) {
-                $worksheet->write( ( $i - 7 ), 3, $colors{$i}, $center );
-    
-            }
-        }
-    }
-    
-    
-    ######################################################################
-    #
-    # Demonstrate the standard numeric formats.
-    #
-    sub numeric_formats {
-    
-        my $worksheet = $workbook->add_worksheet( 'Numeric formats' );
-    
-        $worksheet->set_column( 0, 4, 15 );
-        $worksheet->set_column( 5, 5, 45 );
-    
-        $worksheet->write( 0, 0, "Index",       $heading );
-        $worksheet->write( 0, 1, "Index",       $heading );
-        $worksheet->write( 0, 2, "Unformatted", $heading );
-        $worksheet->write( 0, 3, "Formatted",   $heading );
-        $worksheet->write( 0, 4, "Negative",    $heading );
-        $worksheet->write( 0, 5, "Format",      $heading );
-    
-        #<<<
-        my @formats;
-        push @formats, [ 0x00, 1234.567,   0,         'General' ];
-        push @formats, [ 0x01, 1234.567,   0,         '0' ];
-        push @formats, [ 0x02, 1234.567,   0,         '0.00' ];
-        push @formats, [ 0x03, 1234.567,   0,         '#,##0' ];
-        push @formats, [ 0x04, 1234.567,   0,         '#,##0.00' ];
-        push @formats, [ 0x05, 1234.567,   -1234.567, '($#,##0_);($#,##0)' ];
-        push @formats, [ 0x06, 1234.567,   -1234.567, '($#,##0_);[Red]($#,##0)' ];
-        push @formats, [ 0x07, 1234.567,   -1234.567, '($#,##0.00_);($#,##0.00)' ];
-        push @formats, [ 0x08, 1234.567,   -1234.567, '($#,##0.00_);[Red]($#,##0.00)' ];
-        push @formats, [ 0x09, 0.567,      0,         '0%' ];
-        push @formats, [ 0x0a, 0.567,      0,         '0.00%' ];
-        push @formats, [ 0x0b, 1234.567,   0,         '0.00E+00' ];
-        push @formats, [ 0x0c, 0.75,       0,         '# ?/?' ];
-        push @formats, [ 0x0d, 0.3125,     0,         '# ??/??' ];
-        push @formats, [ 0x0e, 36892.521,  0,         'm/d/yy' ];
-        push @formats, [ 0x0f, 36892.521,  0,         'd-mmm-yy' ];
-        push @formats, [ 0x10, 36892.521,  0,         'd-mmm' ];
-        push @formats, [ 0x11, 36892.521,  0,         'mmm-yy' ];
-        push @formats, [ 0x12, 36892.521,  0,         'h:mm AM/PM' ];
-        push @formats, [ 0x13, 36892.521,  0,         'h:mm:ss AM/PM' ];
-        push @formats, [ 0x14, 36892.521,  0,         'h:mm' ];
-        push @formats, [ 0x15, 36892.521,  0,         'h:mm:ss' ];
-        push @formats, [ 0x16, 36892.521,  0,         'm/d/yy h:mm' ];
-        push @formats, [ 0x25, 1234.567,   -1234.567, '(#,##0_);(#,##0)' ];
-        push @formats, [ 0x26, 1234.567,   -1234.567, '(#,##0_);[Red](#,##0)' ];
-        push @formats, [ 0x27, 1234.567,   -1234.567, '(#,##0.00_);(#,##0.00)' ];
-        push @formats, [ 0x28, 1234.567,   -1234.567, '(#,##0.00_);[Red](#,##0.00)' ];
-        push @formats, [ 0x29, 1234.567,   -1234.567, '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)' ];
-        push @formats, [ 0x2a, 1234.567,   -1234.567, '_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)' ];
-        push @formats, [ 0x2b, 1234.567,   -1234.567, '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)' ];
-        push @formats, [ 0x2c, 1234.567,   -1234.567, '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)' ];
-        push @formats, [ 0x2d, 36892.521,  0,         'mm:ss' ];
-        push @formats, [ 0x2e, 3.0153,     0,         '[h]:mm:ss' ];
-        push @formats, [ 0x2f, 36892.521,  0,         'mm:ss.0' ];
-        push @formats, [ 0x30, 1234.567,   0,         '##0.0E+0' ];
-        push @formats, [ 0x31, 1234.567,   0,         '@' ];
-        #>>>
-    
-        my $i;
-        foreach my $format ( @formats ) {
-            my $style = $workbook->add_format();
-            $style->set_num_format( $format->[0] );
-    
-            $i++;
-            $worksheet->write( $i, 0, $format->[0], $center );
-            $worksheet->write( $i, 1, sprintf( "0x%02X", $format->[0] ), $center );
-            $worksheet->write( $i, 2, $format->[1], $center );
-            $worksheet->write( $i, 3, $format->[1], $style );
-    
-            if ( $format->[2] ) {
-                $worksheet->write( $i, 4, $format->[2], $style );
-            }
-    
-            $worksheet->write_string( $i, 5, $format->[3] );
-        }
-    }
-    
-    
-    ######################################################################
-    #
-    # Demonstrate the font options.
-    #
-    sub fonts {
-    
-        my $worksheet = $workbook->add_worksheet( 'Fonts' );
-    
-        $worksheet->set_column( 0, 0, 30 );
-        $worksheet->set_column( 1, 1, 10 );
-    
-        $worksheet->write( 0, 0, "Font name", $heading );
-        $worksheet->write( 0, 1, "Font size", $heading );
-    
-        my @fonts;
-        push @fonts, [ 10, 'Arial' ];
-        push @fonts, [ 12, 'Arial' ];
-        push @fonts, [ 14, 'Arial' ];
-        push @fonts, [ 12, 'Arial Black' ];
-        push @fonts, [ 12, 'Arial Narrow' ];
-        push @fonts, [ 12, 'Century Schoolbook' ];
-        push @fonts, [ 12, 'Courier' ];
-        push @fonts, [ 12, 'Courier New' ];
-        push @fonts, [ 12, 'Garamond' ];
-        push @fonts, [ 12, 'Impact' ];
-        push @fonts, [ 12, 'Lucida Handwriting' ];
-        push @fonts, [ 12, 'Times New Roman' ];
-        push @fonts, [ 12, 'Symbol' ];
-        push @fonts, [ 12, 'Wingdings' ];
-        push @fonts, [ 12, 'A font that doesn\'t exist' ];
-    
-        my $i;
-        foreach my $font ( @fonts ) {
-            my $format = $workbook->add_format();
-    
-            $format->set_size( $font->[0] );
-            $format->set_font( $font->[1] );
-    
-            $i++;
-            $worksheet->write( $i, 0, $font->[1], $format );
-            $worksheet->write( $i, 1, $font->[0], $format );
-        }
-    
-    }
-    
-    
-    ######################################################################
-    #
-    # Demonstrate the standard Excel border styles.
-    #
-    sub borders {
-    
-        my $worksheet = $workbook->add_worksheet( 'Borders' );
-    
-        $worksheet->set_column( 0, 4, 10 );
-        $worksheet->set_column( 5, 5, 40 );
-    
-        $worksheet->write( 0, 0, "Index",                                $heading );
-        $worksheet->write( 0, 1, "Index",                                $heading );
-        $worksheet->write( 0, 3, "Style",                                $heading );
-        $worksheet->write( 0, 5, "The style is highlighted in red for ", $heading );
-        $worksheet->write( 1, 5, "emphasis, the default color is black.",
-            $heading );
-    
-        for my $i ( 0 .. 13 ) {
-            my $format = $workbook->add_format();
-            $format->set_border( $i );
-            $format->set_border_color( 'red' );
-            $format->set_align( 'center' );
-    
-            $worksheet->write( ( 2 * ( $i + 1 ) ), 0, $i, $center );
-            $worksheet->write( ( 2 * ( $i + 1 ) ),
-                1, sprintf( "0x%02X", $i ), $center );
-    
-            $worksheet->write( ( 2 * ( $i + 1 ) ), 3, "Border", $format );
-        }
-    
-        $worksheet->write( 30, 0, "Diag type",             $heading );
-        $worksheet->write( 30, 1, "Index",                 $heading );
-        $worksheet->write( 30, 3, "Style",                 $heading );
-        $worksheet->write( 30, 5, "Diagonal Boder styles", $heading );
-    
-        for my $i ( 1 .. 3 ) {
-            my $format = $workbook->add_format();
-            $format->set_diag_type( $i );
-            $format->set_diag_border( 1 );
-            $format->set_diag_color( 'red' );
-            $format->set_align( 'center' );
-    
-            $worksheet->write( ( 2 * ( $i + 15 ) ), 0, $i, $center );
-            $worksheet->write( ( 2 * ( $i + 15 ) ),
-                1, sprintf( "0x%02X", $i ), $center );
-    
-            $worksheet->write( ( 2 * ( $i + 15 ) ), 3, "Border", $format );
-        }
-    }
-    
-    
-    ######################################################################
-    #
-    # Demonstrate the standard Excel cell patterns.
-    #
-    sub patterns {
-    
-        my $worksheet = $workbook->add_worksheet( 'Patterns' );
-    
-        $worksheet->set_column( 0, 4, 10 );
-        $worksheet->set_column( 5, 5, 50 );
-    
-        $worksheet->write( 0, 0, "Index",   $heading );
-        $worksheet->write( 0, 1, "Index",   $heading );
-        $worksheet->write( 0, 3, "Pattern", $heading );
-    
-        $worksheet->write( 0, 5, "The background colour has been set to silver.",
-            $heading );
-        $worksheet->write( 1, 5, "The foreground colour has been set to green.",
-            $heading );
-    
-        for my $i ( 0 .. 18 ) {
-            my $format = $workbook->add_format();
-    
-            $format->set_pattern( $i );
-            $format->set_bg_color( 'silver' );
-            $format->set_fg_color( 'green' );
-            $format->set_align( 'center' );
-    
-            $worksheet->write( ( 2 * ( $i + 1 ) ), 0, $i, $center );
-            $worksheet->write( ( 2 * ( $i + 1 ) ),
-                1, sprintf( "0x%02X", $i ), $center );
-    
-            $worksheet->write( ( 2 * ( $i + 1 ) ), 3, "Pattern", $format );
-    
-            if ( $i == 1 ) {
-                $worksheet->write( ( 2 * ( $i + 1 ) ),
-                    5, "This is solid colour, the most useful pattern.", $heading );
-            }
-        }
-    }
-    
-    
-    ######################################################################
-    #
-    # Demonstrate the standard Excel cell alignments.
-    #
-    sub alignment {
-    
-        my $worksheet = $workbook->add_worksheet( 'Alignment' );
-    
-        $worksheet->set_column( 0, 7, 12 );
-        $worksheet->set_row( 0, 40 );
-        $worksheet->set_selection( 7, 0 );
-    
-        my $format01 = $workbook->add_format();
-        my $format02 = $workbook->add_format();
-        my $format03 = $workbook->add_format();
-        my $format04 = $workbook->add_format();
-        my $format05 = $workbook->add_format();
-        my $format06 = $workbook->add_format();
-        my $format07 = $workbook->add_format();
-        my $format08 = $workbook->add_format();
-        my $format09 = $workbook->add_format();
-        my $format10 = $workbook->add_format();
-        my $format11 = $workbook->add_format();
-        my $format12 = $workbook->add_format();
-        my $format13 = $workbook->add_format();
-        my $format14 = $workbook->add_format();
-        my $format15 = $workbook->add_format();
-        my $format16 = $workbook->add_format();
-        my $format17 = $workbook->add_format();
-    
-        $format02->set_align( 'top' );
-        $format03->set_align( 'bottom' );
-        $format04->set_align( 'vcenter' );
-        $format05->set_align( 'vjustify' );
-        $format06->set_text_wrap();
-    
-        $format07->set_align( 'left' );
-        $format08->set_align( 'right' );
-        $format09->set_align( 'center' );
-        $format10->set_align( 'fill' );
-        $format11->set_align( 'justify' );
-        $format12->set_merge();
-    
-        $format13->set_rotation( 45 );
-        $format14->set_rotation( -45 );
-        $format15->set_rotation( 270 );
-    
-        $format16->set_shrink();
-        $format17->set_indent( 1 );
-    
-        $worksheet->write( 0, 0, 'Vertical',   $heading );
-        $worksheet->write( 0, 1, 'top',        $format02 );
-        $worksheet->write( 0, 2, 'bottom',     $format03 );
-        $worksheet->write( 0, 3, 'vcenter',    $format04 );
-        $worksheet->write( 0, 4, 'vjustify',   $format05 );
-        $worksheet->write( 0, 5, "text\nwrap", $format06 );
-    
-        $worksheet->write( 2, 0, 'Horizontal', $heading );
-        $worksheet->write( 2, 1, 'left',       $format07 );
-        $worksheet->write( 2, 2, 'right',      $format08 );
-        $worksheet->write( 2, 3, 'center',     $format09 );
-        $worksheet->write( 2, 4, 'fill',       $format10 );
-        $worksheet->write( 2, 5, 'justify',    $format11 );
-    
-        $worksheet->write( 3, 1, 'merge', $format12 );
-        $worksheet->write( 3, 2, '',      $format12 );
-    
-        $worksheet->write( 3, 3, 'Shrink ' x 3, $format16 );
-        $worksheet->write( 3, 4, 'Indent',      $format17 );
-    
-    
-        $worksheet->write( 5, 0, 'Rotation',   $heading );
-        $worksheet->write( 5, 1, 'Rotate 45',  $format13 );
-        $worksheet->write( 6, 1, 'Rotate -45', $format14 );
-        $worksheet->write( 7, 1, 'Rotate 270', $format15 );
-    }
-    
-    
-    ######################################################################
-    #
-    # Demonstrate other miscellaneous features.
-    #
-    sub misc {
-    
-        my $worksheet = $workbook->add_worksheet( 'Miscellaneous' );
-    
-        $worksheet->set_column( 2, 2, 25 );
-    
-        my $format01 = $workbook->add_format();
-        my $format02 = $workbook->add_format();
-        my $format03 = $workbook->add_format();
-        my $format04 = $workbook->add_format();
-        my $format05 = $workbook->add_format();
-        my $format06 = $workbook->add_format();
-        my $format07 = $workbook->add_format();
-    
-        $format01->set_underline( 0x01 );
-        $format02->set_underline( 0x02 );
-        $format03->set_underline( 0x21 );
-        $format04->set_underline( 0x22 );
-        $format05->set_font_strikeout();
-        $format06->set_font_outline();
-        $format07->set_font_shadow();
-    
-        $worksheet->write( 1,  2, 'Underline  0x01',          $format01 );
-        $worksheet->write( 3,  2, 'Underline  0x02',          $format02 );
-        $worksheet->write( 5,  2, 'Underline  0x21',          $format03 );
-        $worksheet->write( 7,  2, 'Underline  0x22',          $format04 );
-        $worksheet->write( 9,  2, 'Strikeout',                $format05 );
-        $worksheet->write( 11, 2, 'Outline (Macintosh only)', $format06 );
-        $worksheet->write( 13, 2, 'Shadow (Macintosh only)',  $format07 );
-    }
-    
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/formats.pl>
-
-=head2 Example: regions.pl
-
-
-
-An example of how to use the Excel::Writer::XLSX module to write a basic
-Excel workbook with multiple worksheets.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/regions.jpg" width="640" height="420" alt="Output from regions.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # An example of how to use the Excel::Writer::XLSX module to write a basic
-    # Excel workbook with multiple worksheets.
-    #
-    # reverse ('(c)'), March 2001, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    # Create a new Excel workbook
-    my $workbook = Excel::Writer::XLSX->new( 'regions.xlsx' );
-    
-    # Add some worksheets
-    my $north = $workbook->add_worksheet( "North" );
-    my $south = $workbook->add_worksheet( "South" );
-    my $east  = $workbook->add_worksheet( "East" );
-    my $west  = $workbook->add_worksheet( "West" );
-    
-    # Add a Format
-    my $format = $workbook->add_format();
-    $format->set_bold();
-    $format->set_color( 'blue' );
-    
-    # Add a caption to each worksheet
-    foreach my $worksheet ( $workbook->sheets() ) {
-        $worksheet->write( 0, 0, "Sales", $format );
-    }
-    
-    # Write some data
-    $north->write( 0, 1, 200000 );
-    $south->write( 0, 1, 100000 );
-    $east->write( 0, 1, 150000 );
-    $west->write( 0, 1, 100000 );
-    
-    # Set the active worksheet
-    $south->activate();
-    
-    # Set the width of the first column
-    $south->set_column( 0, 0, 20 );
-    
-    # Set the active cell
-    $south->set_selection( 0, 1 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/regions.pl>
-
-=head2 Example: stats.pl
-
-
-
-A simple example of how to use functions with the Excel::Writer::XLSX
-module.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/stats.jpg" width="640" height="420" alt="Output from stats.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # A simple example of how to use functions with the Excel::Writer::XLSX
-    # module.
-    #
-    # reverse ('(c)'), March 2001, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'stats.xlsx' );
-    my $worksheet = $workbook->add_worksheet( 'Test data' );
-    
-    # Set the column width for columns 1
-    $worksheet->set_column( 0, 0, 20 );
-    
-    
-    # Create a format for the headings
-    my $format = $workbook->add_format();
-    $format->set_bold();
-    
-    
-    # Write the sample data
-    $worksheet->write( 0, 0, 'Sample', $format );
-    $worksheet->write( 0, 1, 1 );
-    $worksheet->write( 0, 2, 2 );
-    $worksheet->write( 0, 3, 3 );
-    $worksheet->write( 0, 4, 4 );
-    $worksheet->write( 0, 5, 5 );
-    $worksheet->write( 0, 6, 6 );
-    $worksheet->write( 0, 7, 7 );
-    $worksheet->write( 0, 8, 8 );
-    
-    $worksheet->write( 1, 0, 'Length', $format );
-    $worksheet->write( 1, 1, 25.4 );
-    $worksheet->write( 1, 2, 25.4 );
-    $worksheet->write( 1, 3, 24.8 );
-    $worksheet->write( 1, 4, 25.0 );
-    $worksheet->write( 1, 5, 25.3 );
-    $worksheet->write( 1, 6, 24.9 );
-    $worksheet->write( 1, 7, 25.2 );
-    $worksheet->write( 1, 8, 24.8 );
-    
-    # Write some statistical functions
-    $worksheet->write( 4, 0, 'Count', $format );
-    $worksheet->write( 4, 1, '=COUNT(B1:I1)' );
-    
-    $worksheet->write( 5, 0, 'Sum', $format );
-    $worksheet->write( 5, 1, '=SUM(B2:I2)' );
-    
-    $worksheet->write( 6, 0, 'Average', $format );
-    $worksheet->write( 6, 1, '=AVERAGE(B2:I2)' );
-    
-    $worksheet->write( 7, 0, 'Min', $format );
-    $worksheet->write( 7, 1, '=MIN(B2:I2)' );
-    
-    $worksheet->write( 8, 0, 'Max', $format );
-    $worksheet->write( 8, 1, '=MAX(B2:I2)' );
-    
-    $worksheet->write( 9, 0, 'Standard Deviation', $format );
-    $worksheet->write( 9, 1, '=STDEV(B2:I2)' );
-    
-    $worksheet->write( 10, 0, 'Kurtosis', $format );
-    $worksheet->write( 10, 1, '=KURT(B2:I2)' );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/stats.pl>
-
-=head2 Example: autofilter.pl
-
-
-
-An example of how to create autofilters with Excel::Writer::XLSX.
-
-An autofilter is a way of adding drop down lists to the headers of a 2D range
-of worksheet data. This allows users to filter the data based on
-simple criteria so that some data is shown and some is hidden.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/autofilter.jpg" width="640" height="420" alt="Output from autofilter.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # An example of how to create autofilters with Excel::Writer::XLSX.
-    #
-    # An autofilter is a way of adding drop down lists to the headers of a 2D range
-    # of worksheet data. This allows users to filter the data based on
-    # simple criteria so that some data is shown and some is hidden.
-    #
-    # reverse ('(c)'), September 2007, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook = Excel::Writer::XLSX->new( 'autofilter.xlsx' );
-    
-    my $worksheet1 = $workbook->add_worksheet();
-    my $worksheet2 = $workbook->add_worksheet();
-    my $worksheet3 = $workbook->add_worksheet();
-    my $worksheet4 = $workbook->add_worksheet();
-    my $worksheet5 = $workbook->add_worksheet();
-    my $worksheet6 = $workbook->add_worksheet();
-    
-    my $bold = $workbook->add_format( bold => 1 );
-    
-    
-    # Extract the data embedded at the end of this file.
-    my @headings = split ' ', <DATA>;
-    my @data;
-    push @data, [split] while <DATA>;
-    
-    
-    # Set up several sheets with the same data.
-    for my $worksheet ( $workbook->sheets() ) {
-        $worksheet->set_column( 'A:D', 12 );
-        $worksheet->set_row( 0, 20, $bold );
-        $worksheet->write( 'A1', \@headings );
-    }
-    
-    
-    ###############################################################################
-    #
-    # Example 1. Autofilter without conditions.
-    #
-    
-    $worksheet1->autofilter( 'A1:D51' );
-    $worksheet1->write( 'A2', [ [@data] ] );
-    
-    
-    ###############################################################################
-    #
-    #
-    # Example 2. Autofilter with a filter condition in the first column.
-    #
-    
-    # The range in this example is the same as above but in row-column notation.
-    $worksheet2->autofilter( 0, 0, 50, 3 );
-    
-    # The placeholder "Region" in the filter is ignored and can be any string
-    # that adds clarity to the expression.
-    #
-    $worksheet2->filter_column( 0, 'Region eq East' );
-    
-    #
-    # Hide the rows that don't match the filter criteria.
-    #
-    my $row = 1;
-    
-    for my $row_data ( @data ) {
-        my $region = $row_data->[0];
-    
-        if ( $region eq 'East' ) {
-    
-            # Row is visible.
-        }
-        else {
-    
-            # Hide row.
-            $worksheet2->set_row( $row, undef, undef, 1 );
-        }
-    
-        $worksheet2->write( $row++, 0, $row_data );
-    }
-    
-    
-    ###############################################################################
-    #
-    #
-    # Example 3. Autofilter with a dual filter condition in one of the columns.
-    #
-    
-    $worksheet3->autofilter( 'A1:D51' );
-    
-    $worksheet3->filter_column( 'A', 'x eq East or x eq South' );
-    
-    #
-    # Hide the rows that don't match the filter criteria.
-    #
-    $row = 1;
-    
-    for my $row_data ( @data ) {
-        my $region = $row_data->[0];
-    
-        if ( $region eq 'East' or $region eq 'South' ) {
-    
-            # Row is visible.
-        }
-        else {
-    
-            # Hide row.
-            $worksheet3->set_row( $row, undef, undef, 1 );
-        }
-    
-        $worksheet3->write( $row++, 0, $row_data );
-    }
-    
-    
-    ###############################################################################
-    #
-    #
-    # Example 4. Autofilter with filter conditions in two columns.
-    #
-    
-    $worksheet4->autofilter( 'A1:D51' );
-    
-    $worksheet4->filter_column( 'A', 'x eq East' );
-    $worksheet4->filter_column( 'C', 'x > 3000 and x < 8000' );
-    
-    #
-    # Hide the rows that don't match the filter criteria.
-    #
-    $row = 1;
-    
-    for my $row_data ( @data ) {
-        my $region = $row_data->[0];
-        my $volume = $row_data->[2];
-    
-        if (    $region eq 'East'
-            and $volume > 3000
-            and $volume < 8000 )
-        {
-    
-            # Row is visible.
-        }
-        else {
-    
-            # Hide row.
-            $worksheet4->set_row( $row, undef, undef, 1 );
-        }
-    
-        $worksheet4->write( $row++, 0, $row_data );
-    }
-    
-    
-    ###############################################################################
-    #
-    #
-    # Example 5. Autofilter with filter for blanks.
-    #
-    
-    # Create a blank cell in our test data.
-    $data[5]->[0] = '';
-    
-    
-    $worksheet5->autofilter( 'A1:D51' );
-    $worksheet5->filter_column( 'A', 'x == Blanks' );
-    
-    #
-    # Hide the rows that don't match the filter criteria.
-    #
-    $row = 1;
-    
-    for my $row_data ( @data ) {
-        my $region = $row_data->[0];
-    
-        if ( $region eq '' ) {
-    
-            # Row is visible.
-        }
-        else {
-    
-            # Hide row.
-            $worksheet5->set_row( $row, undef, undef, 1 );
-        }
-    
-        $worksheet5->write( $row++, 0, $row_data );
-    }
-    
-    
-    ###############################################################################
-    #
-    #
-    # Example 6. Autofilter with filter for non-blanks.
-    #
-    
-    
-    $worksheet6->autofilter( 'A1:D51' );
-    $worksheet6->filter_column( 'A', 'x == NonBlanks' );
-    
-    #
-    # Hide the rows that don't match the filter criteria.
-    #
-    $row = 1;
-    
-    for my $row_data ( @data ) {
-        my $region = $row_data->[0];
-    
-        if ( $region ne '' ) {
-    
-            # Row is visible.
-        }
-        else {
-    
-            # Hide row.
-            $worksheet6->set_row( $row, undef, undef, 1 );
-        }
-    
-        $worksheet6->write( $row++, 0, $row_data );
-    }
-    
-    $workbook->close();
-    
-    __DATA__
-    Region    Item      Volume    Month
-    East      Apple     9000      July
-    East      Apple     5000      July
-    South     Orange    9000      September
-    North     Apple     2000      November
-    West      Apple     9000      November
-    South     Pear      7000      October
-    North     Pear      9000      August
-    West      Orange    1000      December
-    West      Grape     1000      November
-    South     Pear      10000     April
-    West      Grape     6000      January
-    South     Orange    3000      May
-    North     Apple     3000      December
-    South     Apple     7000      February
-    West      Grape     1000      December
-    East      Grape     8000      February
-    South     Grape     10000     June
-    West      Pear      7000      December
-    South     Apple     2000      October
-    East      Grape     7000      December
-    North     Grape     6000      April
-    East      Pear      8000      February
-    North     Apple     7000      August
-    North     Orange    7000      July
-    North     Apple     6000      June
-    South     Grape     8000      September
-    West      Apple     3000      October
-    South     Orange    10000     November
-    West      Grape     4000      July
-    North     Orange    5000      August
-    East      Orange    1000      November
-    East      Orange    4000      October
-    North     Grape     5000      August
-    East      Apple     1000      December
-    South     Apple     10000     March
-    East      Grape     7000      October
-    West      Grape     1000      September
-    East      Grape     10000     October
-    South     Orange    8000      March
-    North     Apple     4000      July
-    South     Orange    5000      July
-    West      Apple     4000      June
-    East      Apple     5000      April
-    North     Pear      3000      August
-    East      Grape     9000      November
-    North     Orange    8000      October
-    East      Apple     10000     June
-    South     Pear      1000      December
-    North     Grape     10000     July
-    East      Grape     6000      February
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/autofilter.pl>
-
-=head2 Example: array_formula.pl
-
-
-
-Example of how to use the Excel::Writer::XLSX module to write simple
-array formulas.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/array_formula.jpg" width="640" height="420" alt="Output from array_formula.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # Example of how to use the Excel::Writer::XLSX module to write simple
-    # array formulas.
-    #
-    # reverse ('(c)'), August 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'array_formula.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    # Write some test data.
-    $worksheet->write( 'B1', [ [ 500, 10 ], [ 300, 15 ] ] );
-    $worksheet->write( 'B5', [ [ 1, 2, 3 ], [ 20234, 21003, 10000 ] ] );
-    
-    # Write an array formula that returns a single value
-    $worksheet->write( 'A1', '{=SUM(B1:C1*B2:C2)}' );
-    
-    # Same as above but more verbose.
-    $worksheet->write_array_formula( 'A2:A2', '{=SUM(B1:C1*B2:C2)}' );
-    
-    # Write an array formula that returns a range of values
-    $worksheet->write_array_formula( 'A5:A7', '{=TREND(C5:C7,B5:B7)}' );
-    
-    $workbook->close();
-    
-    __END__
-    
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/array_formula.pl>
-
-=head2 Example: cgi.pl
-
-
-
-Example of how to use the Excel::Writer::XLSX module to send an Excel
-file to a browser in a CGI program.
-
-On Windows the hash-bang line should be something like:
-
-    #!C:\Perl\bin\perl.exe
-
-The "Content-Disposition" line will cause a prompt to be generated to save
-the file. If you want to stream the file to the browser instead, comment out
-that line as shown below.
-
-
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to use the Excel::Writer::XLSX module to send an Excel
-    # file to a browser in a CGI program.
-    #
-    # On Windows the hash-bang line should be something like:
-    #
-    #     #!C:\Perl\bin\perl.exe
-    #
-    # The "Content-Disposition" line will cause a prompt to be generated to save
-    # the file. If you want to stream the file to the browser instead, comment out
-    # that line as shown below.
-    #
-    # reverse ('(c)'), March 2001, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Set the filename and send the content type
-    my $filename = "cgitest.xlsx";
-    
-    print "Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\n";
-    
-    # The Content-Disposition will generate a prompt to save the file. If you want
-    # to stream the file to the browser, comment out the following line.
-    print "Content-Disposition: attachment; filename=$filename\n";
-    print "\n";
-    
-    # Redirect the output to STDOUT. Binmode the filehandle in case it is needed.
-    binmode STDOUT;
-    
-    my $workbook  = Excel::Writer::XLSX->new( \*STDOUT );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    # Set the column width for column 1
-    $worksheet->set_column( 0, 0, 20 );
-    
-    
-    # Create a format
-    my $format = $workbook->add_format();
-    $format->set_bold();
-    $format->set_size( 15 );
-    $format->set_color( 'blue' );
-    
-    
-    # Write to the workbook
-    $worksheet->write( 0, 0, "Hi Excel!", $format );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/cgi.pl>
-
-=head2 Example: chart_area.pl
-
-
-
-A demo of an Area chart in Excel::Writer::XLSX.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_area.jpg" width="640" height="420" alt="Output from chart_area.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of an Area chart in Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), March 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_area.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 40, 40, 50, 30, 25, 50 ],
-        [ 30, 25, 30, 10, 5,  10 ],
-    
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write( 'A2', $data );
-    
-    # Create a new chart object. In this case an embedded chart.
-    my $chart1 = $workbook->add_chart( type => 'area', embedded => 1 );
-    
-    # Configure the first series.
-    $chart1->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart1->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart1->set_title ( name => 'Results of sample analysis' );
-    $chart1->set_x_axis( name => 'Test number' );
-    $chart1->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart1->set_style( 11 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D2', $chart1, 25, 10 );
-    
-    
-    #
-    # Create a stacked chart sub-type
-    #
-    my $chart2 = $workbook->add_chart(
-        type     => 'area',
-        embedded => 1,
-        subtype  => 'stacked'
-    );
-    
-    # Configure the first series.
-    $chart2->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart2->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart2->set_title ( name => 'Stacked Chart' );
-    $chart2->set_x_axis( name => 'Test number' );
-    $chart2->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart2->set_style( 12 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D18', $chart2, 25, 11 );
-    
-    
-    #
-    # Create a percent stacked chart sub-type
-    #
-    my $chart3 = $workbook->add_chart(
-        type     => 'area',
-        embedded => 1,
-        subtype  => 'percent_stacked'
-    );
-    
-    # Configure the first series.
-    $chart3->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart3->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart3->set_title ( name => 'Percent Stacked Chart' );
-    $chart3->set_x_axis( name => 'Test number' );
-    $chart3->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart3->set_style( 13 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D34', $chart3, 25, 11 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_area.pl>
-
-=head2 Example: chart_bar.pl
-
-
-
-A demo of an Bar chart in Excel::Writer::XLSX.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_bar.jpg" width="640" height="420" alt="Output from chart_bar.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of an Bar chart in Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), March 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_bar.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-    
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write( 'A2', $data );
-    
-    # Create a new chart object. In this case an embedded chart.
-    my $chart1 = $workbook->add_chart( type => 'bar', embedded => 1 );
-    
-    # Configure the first series.
-    $chart1->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart1->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart1->set_title ( name => 'Results of sample analysis' );
-    $chart1->set_x_axis( name => 'Test number' );
-    $chart1->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart1->set_style( 11 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D2', $chart1, 25, 10 );
-    
-    
-    #
-    # Create a stacked chart sub-type
-    #
-    my $chart2 = $workbook->add_chart(
-        type     => 'bar',
-        embedded => 1,
-        subtype  => 'stacked'
-    );
-    
-    # Configure the first series.
-    $chart2->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart2->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart2->set_title ( name => 'Stacked Chart' );
-    $chart2->set_x_axis( name => 'Test number' );
-    $chart2->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart2->set_style( 12 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D18', $chart2, 25, 11 );
-    
-    
-    #
-    # Create a percent stacked chart sub-type
-    #
-    my $chart3 = $workbook->add_chart(
-        type     => 'bar',
-        embedded => 1,
-        subtype  => 'percent_stacked'
-    );
-    
-    # Configure the first series.
-    $chart3->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart3->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart3->set_title ( name => 'Percent Stacked Chart' );
-    $chart3->set_x_axis( name => 'Test number' );
-    $chart3->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart3->set_style( 13 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D34', $chart3, 25, 11 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_bar.pl>
-
-=head2 Example: chart_column.pl
-
-
-
-A demo of a Column chart in Excel::Writer::XLSX.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_column.jpg" width="640" height="420" alt="Output from chart_column.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of a Column chart in Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), March 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_column.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-    
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write( 'A2', $data );
-    
-    # Create a new chart object. In this case an embedded chart.
-    my $chart1 = $workbook->add_chart( type => 'column', embedded => 1 );
-    
-    # Configure the first series.
-    $chart1->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart1->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart1->set_title ( name => 'Results of sample analysis' );
-    $chart1->set_x_axis( name => 'Test number' );
-    $chart1->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart1->set_style( 11 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D2', $chart1, 25, 10 );
-    
-    
-    #
-    # Create a stacked chart sub-type
-    #
-    my $chart2 = $workbook->add_chart(
-        type     => 'column',
-        embedded => 1,
-        subtype  => 'stacked'
-    );
-    
-    # Configure the first series.
-    $chart2->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart2->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart2->set_title ( name => 'Stacked Chart' );
-    $chart2->set_x_axis( name => 'Test number' );
-    $chart2->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart2->set_style( 12 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D18', $chart2, 25, 11 );
-    
-    
-    #
-    # Create a percent stacked chart sub-type
-    #
-    my $chart3 = $workbook->add_chart(
-        type     => 'column',
-        embedded => 1,
-        subtype  => 'percent_stacked'
-    );
-    
-    # Configure the first series.
-    $chart3->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart3->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart3->set_title ( name => 'Percent Stacked Chart' );
-    $chart3->set_x_axis( name => 'Test number' );
-    $chart3->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart3->set_style( 13 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D34', $chart3, 25, 11 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_column.pl>
-
-=head2 Example: chart_line.pl
-
-
-
-A demo of a Line chart in Excel::Writer::XLSX.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_line.jpg" width="640" height="420" alt="Output from chart_line.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of a Line chart in Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), March 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_line.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2, 3, 4, 5, 6, 7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-    
-    ];
-    
-    $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.
-    $chart->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # 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 (mm)' );
-    
-    # Set an Excel chart style. Colors with white outline and shadow.
-    $chart->set_style( 10 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D2', $chart, 25, 10 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_line.pl>
-
-=head2 Example: chart_pie.pl
-
-
-
-A demo of a Pie chart in Excel::Writer::XLSX.
-
-The demo also shows how to set segment colours. It is possible to define
-chart colors for most types of Excel::Writer::XLSX charts via the
-add_series() method. However, Pie and Doughtnut charts are a special case
-since each segment is represented as a point so it is necessary to assign
-formatting to each point in the series.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_pie.jpg" width="640" height="420" alt="Output from chart_pie.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of a Pie chart in Excel::Writer::XLSX.
-    #
-    # The demo also shows how to set segment colours. It is possible to define
-    # chart colors for most types of Excel::Writer::XLSX charts via the
-    # add_series() method. However, Pie and Doughtnut charts are a special case
-    # since each segment is represented as a point so it is necessary to assign
-    # formatting to each point in the series.
-    #
-    # reverse ('(c)'), March 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_pie.xlsx' );
-    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 $chart1 = $workbook->add_chart( type => 'pie', embedded => 1 );
-    
-    # Configure the series. Note the use of the array ref to define ranges:
-    # [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    # See below for an alternative syntax.
-    $chart1->add_series(
-        name       => 'Pie sales data',
-        categories => [ 'Sheet1', 1, 3, 0, 0 ],
-        values     => [ 'Sheet1', 1, 3, 1, 1 ],
-    );
-    
-    # Add a title.
-    $chart1->set_title( name => 'Popular Pie Types' );
-    
-    # Set an Excel chart style. Colors with white outline and shadow.
-    $chart1->set_style( 10 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'C2', $chart1, 25, 10 );
-    
-    
-    #
-    # Create a Pie chart with user defined segment colors.
-    #
-    
-    # Create an example Pie chart like above.
-    my $chart2 = $workbook->add_chart( type => 'pie', embedded => 1 );
-    
-    # Configure the series and add user defined segment colours.
-    $chart2->add_series(
-        name       => 'Pie sales data',
-        categories => '=Sheet1!$A$2:$A$4',
-        values     => '=Sheet1!$B$2:$B$4',
-        points     => [
-            { fill => { color => '#5ABA10' } },
-            { fill => { color => '#FE110E' } },
-            { fill => { color => '#CA5C05' } },
-        ],
-    );
-    
-    # Add a title.
-    $chart2->set_title( name => 'Pie Chart with user defined colors' );
-    
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'C18', $chart2, 25, 10 );
-    
-    
-    #
-    # Create a Pie chart with rotation of the segments.
-    #
-    
-    # Create an example Pie chart like above.
-    my $chart3 = $workbook->add_chart( type => 'pie', embedded => 1 );
-    
-    # Configure the series.
-    $chart3->add_series(
-        name       => 'Pie sales data',
-        categories => '=Sheet1!$A$2:$A$4',
-        values     => '=Sheet1!$B$2:$B$4',
-    );
-    
-    # Add a title.
-    $chart3->set_title( name => 'Pie Chart with segment rotation' );
-    
-    # Change the angle/rotation of the first segment.
-    $chart3->set_rotation(90);
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'C34', $chart3, 25, 10 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_pie.pl>
-
-=head2 Example: chart_doughnut.pl
-
-
-
-A demo of a Doughnut chart in Excel::Writer::XLSX.
-
-The demo also shows how to set segment colours. It is possible to define
-chart colors for most types of Excel::Writer::XLSX charts via the
-add_series() method. However, Pie and Doughtnut charts are a special case
-since each segment is represented as a point so it is necessary to assign
-formatting to each point in the series.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_doughnut.jpg" width="640" height="420" alt="Output from chart_doughnut.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of a Doughnut chart in Excel::Writer::XLSX.
-    #
-    # The demo also shows how to set segment colours. It is possible to define
-    # chart colors for most types of Excel::Writer::XLSX charts via the
-    # add_series() method. However, Pie and Doughtnut charts are a special case
-    # since each segment is represented as a point so it is necessary to assign
-    # formatting to each point in the series.
-    #
-    # reverse ('(c)'), March 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_doughnut.xlsx' );
-    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 = [
-        [ 'Glazed', 'Chocolate', 'Cream' ],
-        [ 50,       35,          15      ],
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write( 'A2', $data );
-    
-    # Create a new chart object. In this case an embedded chart.
-    my $chart1 = $workbook->add_chart( type => 'doughnut', embedded => 1 );
-    
-    # Configure the series. Note the use of the array ref to define ranges:
-    # [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    # See below for an alternative syntax.
-    $chart1->add_series(
-        name       => 'Doughnut sales data',
-        categories => [ 'Sheet1', 1, 3, 0, 0 ],
-        values     => [ 'Sheet1', 1, 3, 1, 1 ],
-    );
-    
-    # Add a title.
-    $chart1->set_title( name => 'Popular Doughnut Types' );
-    
-    # Set an Excel chart style. Colors with white outline and shadow.
-    $chart1->set_style( 10 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'C2', $chart1, 25, 10 );
-    
-    
-    #
-    # Create a Doughnut chart with user defined segment colors.
-    #
-    
-    # Create an example Doughnut chart like above.
-    my $chart2 = $workbook->add_chart( type => 'doughnut', embedded => 1 );
-    
-    # Configure the series and add user defined segment colours.
-    $chart2->add_series(
-        name       => 'Doughnut sales data',
-        categories => '=Sheet1!$A$2:$A$4',
-        values     => '=Sheet1!$B$2:$B$4',
-        points     => [
-            { fill => { color => '#FA58D0' } },
-            { fill => { color => '#61210B' } },
-            { fill => { color => '#F5F6CE' } },
-        ],
-    );
-    
-    # Add a title.
-    $chart2->set_title( name => 'Doughnut Chart with user defined colors' );
-    
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'C18', $chart2, 25, 10 );
-    
-    
-    #
-    # Create a Doughnut chart with rotation of the segments.
-    #
-    
-    # Create an example Doughnut chart like above.
-    my $chart3 = $workbook->add_chart( type => 'doughnut', embedded => 1 );
-    
-    # Configure the series.
-    $chart3->add_series(
-        name       => 'Doughnut sales data',
-        categories => '=Sheet1!$A$2:$A$4',
-        values     => '=Sheet1!$B$2:$B$4',
-    );
-    
-    # Add a title.
-    $chart3->set_title( name => 'Doughnut Chart with segment rotation' );
-    
-    # Change the angle/rotation of the first segment.
-    $chart3->set_rotation(90);
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'C34', $chart3, 25, 10 );
-    
-    
-    #
-    # Create a Doughnut chart with user defined hole size.
-    #
-    
-    # Create an example Doughnut chart like above.
-    my $chart4 = $workbook->add_chart( type => 'doughnut', embedded => 1 );
-    
-    # Configure the series.
-    $chart4->add_series(
-        name       => 'Doughnut sales data',
-        categories => '=Sheet1!$A$2:$A$4',
-        values     => '=Sheet1!$B$2:$B$4',
-    );
-    
-    # Add a title.
-    $chart4->set_title( name => 'Doughnut Chart with user defined hole size' );
-    
-    # Change the hole size.
-    $chart4->set_hole_size(33);
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'C50', $chart4, 25, 10 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_doughnut.pl>
-
-=head2 Example: chart_radar.pl
-
-
-
-A demo of an Radar chart in Excel::Writer::XLSX.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_radar.jpg" width="640" height="420" alt="Output from chart_radar.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of an Radar chart in Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), October 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_radar.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-        [ 25, 40, 50, 30, 50, 40 ],
-    
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write( 'A2', $data );
-    
-    # Create a new chart object. In this case an embedded chart.
-    my $chart1 = $workbook->add_chart( type => 'radar', embedded => 1 );
-    
-    # Configure the first series.
-    $chart1->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart1->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart1->set_title ( name => 'Results of sample analysis' );
-    $chart1->set_x_axis( name => 'Test number' );
-    $chart1->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart1->set_style( 11 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D2', $chart1, 25, 10 );
-    
-    
-    #
-    # Create a with_markers chart sub-type
-    #
-    my $chart2 = $workbook->add_chart(
-        type     => 'radar',
-        embedded => 1,
-        subtype  => 'with_markers'
-    );
-    
-    # Configure the first series.
-    $chart2->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart2->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart2->set_title ( name => 'Stacked Chart' );
-    $chart2->set_x_axis( name => 'Test number' );
-    $chart2->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart2->set_style( 12 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D18', $chart2, 25, 11 );
-    
-    
-    #
-    # Create a filled chart sub-type
-    #
-    my $chart3 = $workbook->add_chart(
-        type     => 'radar',
-        embedded => 1,
-        subtype  => 'filled'
-    );
-    
-    # Configure the first series.
-    $chart3->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart3->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart3->set_title ( name => 'Percent Stacked Chart' );
-    $chart3->set_x_axis( name => 'Test number' );
-    $chart3->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart3->set_style( 13 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D34', $chart3, 25, 11 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_radar.pl>
-
-=head2 Example: chart_scatter.pl
-
-
-
-A demo of a Scatter chart in Excel::Writer::XLSX. Other subtypes are
-also supported such as markers_only (the default), straight_with_markers,
-straight, smooth_with_markers and smooth. See the main documentation for
-more details.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_scatter.jpg" width="640" height="420" alt="Output from chart_scatter.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of a Scatter chart in Excel::Writer::XLSX. Other subtypes are
-    # also supported such as markers_only (the default), straight_with_markers,
-    # straight, smooth_with_markers and smooth. See the main documentation for
-    # more details.
-    #
-    # reverse ('(c)'), March 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_scatter.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-    
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write( 'A2', $data );
-    
-    # Create a new chart object. In this case an embedded chart.
-    my $chart1 = $workbook->add_chart( type => 'scatter', embedded => 1 );
-    
-    # Configure the first series.
-    $chart1->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart1->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart1->set_title ( name => 'Results of sample analysis' );
-    $chart1->set_x_axis( name => 'Test number' );
-    $chart1->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart1->set_style( 11 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D2', $chart1, 25, 10 );
-    
-    
-    #
-    # Create a scatter chart sub-type with straight lines and markers.
-    #
-    my $chart2 = $workbook->add_chart(
-        type     => 'scatter',
-        embedded => 1,
-        subtype  => 'straight_with_markers'
-    );
-    
-    # Configure the first series.
-    $chart2->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart2->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart2->set_title ( name => 'Straight line with markers' );
-    $chart2->set_x_axis( name => 'Test number' );
-    $chart2->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart2->set_style( 12 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D18', $chart2, 25, 11 );
-    
-    
-    #
-    # Create a scatter chart sub-type with straight lines and no markers.
-    #
-    my $chart3 = $workbook->add_chart(
-        type     => 'scatter',
-        embedded => 1,
-        subtype  => 'straight'
-    );
-    
-    # Configure the first series.
-    $chart3->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart3->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart3->set_title ( name => 'Straight line' );
-    $chart3->set_x_axis( name => 'Test number' );
-    $chart3->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart3->set_style( 13 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D34', $chart3, 25, 11 );
-    
-    
-    #
-    # Create a scatter chart sub-type with smooth lines and markers.
-    #
-    my $chart4 = $workbook->add_chart(
-        type     => 'scatter',
-        embedded => 1,
-        subtype  => 'smooth_with_markers'
-    );
-    
-    # Configure the first series.
-    $chart4->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart4->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart4->set_title ( name => 'Smooth line with markers' );
-    $chart4->set_x_axis( name => 'Test number' );
-    $chart4->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart4->set_style( 14 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D51', $chart4, 25, 11 );
-    
-    
-    #
-    # Create a scatter chart sub-type with smooth lines and no markers.
-    #
-    my $chart5 = $workbook->add_chart(
-        type     => 'scatter',
-        embedded => 1,
-        subtype  => 'smooth'
-    );
-    
-    # Configure the first series.
-    $chart5->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart5->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart5->set_title ( name => 'Smooth line' );
-    $chart5->set_x_axis( name => 'Test number' );
-    $chart5->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set an Excel chart style. Blue colors with white outline and shadow.
-    $chart5->set_style( 15 );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D66', $chart5, 25, 11 );
-    
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_scatter.pl>
-
-=head2 Example: chart_secondary_axis.pl
-
-
-
-A demo of a Line chart with a secondary axis in Excel::Writer::XLSX.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_secondary_axis.jpg" width="640" height="420" alt="Output from chart_secondary_axis.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of a Line chart with a secondary axis in Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), March 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_secondary_axis.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    my $bold      = $workbook->add_format( bold => 1 );
-    
-    # Add the worksheet data that the charts will refer to.
-    my $headings = [ 'Aliens', 'Humans', ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-    
-    ];
-    
-    
-    $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 a series with a secondary axis
-    $chart->add_series(
-        name    => '=Sheet1!$A$1',
-        values  => '=Sheet1!$A$2:$A$7',
-        y2_axis => 1,
-    );
-    
-    $chart->add_series(
-        name   => '=Sheet1!$B$1',
-        values => '=Sheet1!$B$2:$B$7',
-    );
-    
-    $chart->set_legend( position => 'right' );
-    
-    # Add a chart title and some axis labels.
-    $chart->set_title( name => 'Survey results' );
-    $chart->set_x_axis( name => 'Days', );
-    $chart->set_y_axis( name => 'Population', major_gridlines => { visible => 0 } );
-    $chart->set_y2_axis( name => 'Laser wounds' );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D2', $chart, 25, 10 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_secondary_axis.pl>
-
-=head2 Example: chart_combined.pl
-
-
-
-An example of a Combined chart in Excel::Writer::XLSX.
-
-
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # An example of a Combined chart in Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), March 2015, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_combined.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-    
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write( 'A2', $data );
-    
-    #
-    # In the first example we will create a combined column and line chart.
-    # They will share the same X and Y axes.
-    #
-    
-    # Create a new column chart. This will use this as the primary chart.
-    my $column_chart1 = $workbook->add_chart( type => 'column', embedded => 1 );
-    
-    # Configure the data series for the primary chart.
-    $column_chart1->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Create a new column chart. This will use this as the secondary chart.
-    my $line_chart1 = $workbook->add_chart( type => 'line', embedded => 1 );
-    
-    # Configure the data series for the secondary chart.
-    $line_chart1->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-    );
-    
-    # Combine the charts.
-    $column_chart1->combine( $line_chart1 );
-    
-    # Add a chart title and some axis labels. Note, this is done via the
-    # primary chart.
-    $column_chart1->set_title( name => 'Combined chart - same Y axis' );
-    $column_chart1->set_x_axis( name => 'Test number' );
-    $column_chart1->set_y_axis( name => 'Sample length (mm)' );
-    
-    
-    # Insert the chart into the worksheet
-    $worksheet->insert_chart( 'E2', $column_chart1 );
-    
-    #
-    # In the second example we will create a similar combined column and line
-    # chart except that the secondary chart will have a secondary Y axis.
-    #
-    
-    # Create a new column chart. This will use this as the primary chart.
-    my $column_chart2 = $workbook->add_chart( type => 'column', embedded => 1 );
-    
-    # Configure the data series for the primary chart.
-    $column_chart2->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Create a new column chart. This will use this as the secondary chart.
-    my $line_chart2 = $workbook->add_chart( type => 'line', embedded => 1 );
-    
-    # Configure the data series for the secondary chart. We also set a
-    # secondary Y axis via (y2_axis). This is the only difference between
-    # this and the first example, apart from the axis label below.
-    $line_chart2->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-        y2_axis    => 1,
-    );
-    
-    # Combine the charts.
-    $column_chart2->combine( $line_chart2 );
-    
-    # Add a chart title and some axis labels.
-    $column_chart2->set_title(  name => 'Combine chart - secondary Y axis' );
-    $column_chart2->set_x_axis( name => 'Test number' );
-    $column_chart2->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Note: the y2 properites are on the secondary chart.
-    $line_chart2->set_y2_axis( name => 'Target length (mm)' );
-    
-    
-    # Insert the chart into the worksheet
-    $worksheet->insert_chart( 'E18', $column_chart2 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_combined.pl>
-
-=head2 Example: chart_pareto.pl
-
-
-
-A demo of a Pareto chart in Excel::Writer::XLSX.
-
-
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of a Pareto chart in Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), March 2015, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_pareto.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    # Formats used in the workbook.
-    my $bold           = $workbook->add_format( bold       => 1 );
-    my $percent_format = $workbook->add_format( num_format => '0.0%' );
-    
-    
-    # Widen the columns for visibility.
-    $worksheet->set_column( 'A:A', 15 );
-    $worksheet->set_column( 'B:C', 10 );
-    
-    # Add the worksheet data that the charts will refer to.
-    my $headings = [ 'Reason', 'Number', 'Percentage' ];
-    
-    my $reasons = [
-        'Traffic',   'Child care', 'Public Transport', 'Weather',
-        'Overslept', 'Emergency',
-    ];
-    
-    my $numbers  = [ 60,   40,    20,  15,  10,    5 ];
-    my $percents = [ 0.44, 0.667, 0.8, 0.9, 0.967, 1 ];
-    
-    $worksheet->write_row( 'A1', $headings, $bold );
-    $worksheet->write_col( 'A2', $reasons );
-    $worksheet->write_col( 'B2', $numbers );
-    $worksheet->write_col( 'C2', $percents, $percent_format );
-    
-    
-    # Create a new column chart. This will be the primary chart.
-    my $column_chart = $workbook->add_chart( type => 'column', embedded => 1 );
-    
-    # Add a series.
-    $column_chart->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Add a chart title.
-    $column_chart->set_title( name => 'Reasons for lateness' );
-    
-    # Turn off the chart legend.
-    $column_chart->set_legend( position => 'none' );
-    
-    # Set the title and scale of the Y axes. Note, the secondary axis is set from
-    # the primary chart.
-    $column_chart->set_y_axis(
-        name => 'Respondents (number)',
-        min  => 0,
-        max  => 120
-    );
-    $column_chart->set_y2_axis( max => 1 );
-    
-    # Create a new line chart. This will be the secondary chart.
-    my $line_chart = $workbook->add_chart( type => 'line', embedded => 1 );
-    
-    # Add a series, on the secondary axis.
-    $line_chart->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-        marker     => { type => 'automatic' },
-        y2_axis    => 1,
-    );
-    
-    
-    # Combine the charts.
-    $column_chart->combine( $line_chart );
-    
-    # Insert the chart into the worksheet.
-    $worksheet->insert_chart( 'F2', $column_chart );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_pareto.pl>
-
-=head2 Example: chart_stock.pl
-
-
-
-A demo of a Stock chart in Excel::Writer::XLSX.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_stock.jpg" width="640" height="420" alt="Output from chart_stock.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of a Stock chart in Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), March 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    use Excel::Writer::XLSX;
-    
-    my $workbook    = Excel::Writer::XLSX->new( 'chart_stock.xlsx' );
-    my $worksheet   = $workbook->add_worksheet();
-    my $bold        = $workbook->add_format( bold => 1 );
-    my $date_format = $workbook->add_format( num_format => 'dd/mm/yyyy' );
-    my $chart       = $workbook->add_chart( type => 'stock', embedded => 1 );
-    
-    
-    # Add the worksheet data that the charts will refer to.
-    my $headings = [ 'Date', 'High', 'Low', 'Close' ];
-    my $data = [
-    
-        [ '2007-01-01T', '2007-01-02T', '2007-01-03T', '2007-01-04T', '2007-01-05T' ],
-        [ 27.2,  25.03, 19.05, 20.34, 18.5 ],
-        [ 23.49, 19.55, 15.12, 17.84, 16.34 ],
-        [ 25.45, 23.05, 17.32, 20.45, 17.34 ],
-    
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    
-    for my $row ( 0 .. 4 ) {
-        $worksheet->write_date_time( $row+1, 0, $data->[0]->[$row], $date_format );
-        $worksheet->write( $row+1, 1, $data->[1]->[$row] );
-        $worksheet->write( $row+1, 2, $data->[2]->[$row] );
-        $worksheet->write( $row+1, 3, $data->[3]->[$row] );
-    
-    }
-    
-    $worksheet->set_column( 'A:D', 11 );
-    
-    # Add a series for each of the High-Low-Close columns.
-    $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',
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart->set_title ( name => 'High-Low-Close', );
-    $chart->set_x_axis( name => 'Date', );
-    $chart->set_y_axis( name => 'Share price', );
-    
-    
-    $worksheet->insert_chart( 'E9', $chart );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_stock.pl>
-
-=head2 Example: chart_data_table.pl
-
-
-
-A demo of an Column chart with a data table on the X-axis using
-Excel::Writer::XLSX.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_data_table.jpg" width="640" height="420" alt="Output from chart_data_table.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of an Column chart with a data table on the X-axis using
-    # Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), December 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_data_table.xlsx' );
-    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', 'Batch 1', 'Batch 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-    
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write( 'A2', $data );
-    
-    # Create a column chart with a data table.
-    my $chart1 = $workbook->add_chart( type => 'column', embedded => 1 );
-    
-    # Configure the first series.
-    $chart1->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series. Note alternative use of array ref to define
-    # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
-    $chart1->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart1->set_title( name => 'Chart with Data Table' );
-    $chart1->set_x_axis( name => 'Test number' );
-    $chart1->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set a default data table on the X-Axis.
-    $chart1->set_table();
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D2', $chart1, 25, 10 );
-    
-    
-    #
-    # Create a second chart.
-    #
-    my $chart2 = $workbook->add_chart( type => 'column', embedded => 1 );
-    
-    # Configure the first series.
-    $chart2->add_series(
-        name       => '=Sheet1!$B$1',
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure second series.
-    $chart2->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => [ 'Sheet1', 1, 6, 0, 0 ],
-        values     => [ 'Sheet1', 1, 6, 2, 2 ],
-    );
-    
-    # Add a chart title and some axis labels.
-    $chart2->set_title( name => 'Data Table with legend keys' );
-    $chart2->set_x_axis( name => 'Test number' );
-    $chart2->set_y_axis( name => 'Sample length (mm)' );
-    
-    # Set a data table on the X-Axis with the legend keys showm.
-    $chart2->set_table( show_keys => 1 );
-    
-    # Hide the chart legend since the keys are show on the data table.
-    $chart2->set_legend( position => 'none' );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D18', $chart2, 25, 11 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_data_table.pl>
-
-=head2 Example: chart_data_tools.pl
-
-
-
-A demo of an various Excel chart data tools that are available via
-an Excel::Writer::XLSX chart.
-
-These include, Trendlines, Data Labels, Error Bars, Drop Lines,
-High-Low Lines and Up-Down Bars.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/chart_data_tools.jpg" width="640" height="420" alt="Output from chart_data_tools.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of an various Excel chart data tools that are available via
-    # an Excel::Writer::XLSX chart.
-    #
-    # These include, Trendlines, Data Labels, Error Bars, Drop Lines,
-    # High-Low Lines and Up-Down Bars.
-    #
-    # reverse ('(c)'), December 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_data_tools.xlsx' );
-    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', 'Data 1', 'Data 2' ];
-    my $data = [
-        [ 2,  3,  4,  5,  6,  7 ],
-        [ 10, 40, 50, 20, 10, 50 ],
-        [ 30, 60, 70, 50, 40, 30 ],
-    
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write( 'A2', $data );
-    
-    
-    #######################################################################
-    #
-    # Trendline example.
-    #
-    
-    # Create a Line chart.
-    my $chart1 = $workbook->add_chart( type => 'line', embedded => 1 );
-    
-    # Configure the first series with a polynomial trendline.
-    $chart1->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-        trendline  => {
-            type  => 'polynomial',
-            order => 3,
-        },
-    );
-    
-    # Configure the second series with a moving average trendline.
-    $chart1->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-        trendline  => { type => 'linear' },
-    );
-    
-    # Add a chart title. and some axis labels.
-    $chart1->set_title( name => 'Chart with Trendlines' );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D2', $chart1, 25, 10 );
-    
-    
-    #######################################################################
-    #
-    # Data Labels and Markers example.
-    #
-    
-    # Create a Line chart.
-    my $chart2 = $workbook->add_chart( type => 'line', embedded => 1 );
-    
-    # Configure the first series.
-    $chart2->add_series(
-        categories  => '=Sheet1!$A$2:$A$7',
-        values      => '=Sheet1!$B$2:$B$7',
-        data_labels => { value => 1 },
-        marker      => { type => 'automatic' },
-    );
-    
-    # Configure the second series.
-    $chart2->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-    );
-    
-    # Add a chart title. and some axis labels.
-    $chart2->set_title( name => 'Chart with Data Labels and Markers' );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D18', $chart2, 25, 10 );
-    
-    
-    #######################################################################
-    #
-    # Error Bars example.
-    #
-    
-    # Create a Line chart.
-    my $chart3 = $workbook->add_chart( type => 'line', embedded => 1 );
-    
-    # Configure the first series.
-    $chart3->add_series(
-        categories   => '=Sheet1!$A$2:$A$7',
-        values       => '=Sheet1!$B$2:$B$7',
-        y_error_bars => { type => 'standard_error' },
-    );
-    
-    # Configure the second series.
-    $chart3->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-    );
-    
-    # Add a chart title. and some axis labels.
-    $chart3->set_title( name => 'Chart with Error Bars' );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D34', $chart3, 25, 10 );
-    
-    
-    #######################################################################
-    #
-    # Up-Down Bars example.
-    #
-    
-    # Create a Line chart.
-    my $chart4 = $workbook->add_chart( type => 'line', embedded => 1 );
-    
-    # Add the Up-Down Bars.
-    $chart4->set_up_down_bars();
-    
-    # Configure the first series.
-    $chart4->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure the second series.
-    $chart4->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-    );
-    
-    # Add a chart title. and some axis labels.
-    $chart4->set_title( name => 'Chart with Up-Down Bars' );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D50', $chart4, 25, 10 );
-    
-    
-    #######################################################################
-    #
-    # High-Low Lines example.
-    #
-    
-    # Create a Line chart.
-    my $chart5 = $workbook->add_chart( type => 'line', embedded => 1 );
-    
-    # Add the High-Low lines.
-    $chart5->set_high_low_lines();
-    
-    # Configure the first series.
-    $chart5->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure the second series.
-    $chart5->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-    );
-    
-    # Add a chart title. and some axis labels.
-    $chart5->set_title( name => 'Chart with High-Low Lines' );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D66', $chart5, 25, 10 );
-    
-    
-    #######################################################################
-    #
-    # Drop Lines example.
-    #
-    
-    # Create a Line chart.
-    my $chart6 = $workbook->add_chart( type => 'line', embedded => 1 );
-    
-    # Add Drop Lines.
-    $chart6->set_drop_lines();
-    
-    # Configure the first series.
-    $chart6->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$B$2:$B$7',
-    );
-    
-    # Configure the second series.
-    $chart6->add_series(
-        categories => '=Sheet1!$A$2:$A$7',
-        values     => '=Sheet1!$C$2:$C$7',
-    );
-    
-    # Add a chart title. and some axis labels.
-    $chart6->set_title( name => 'Chart with Drop Lines' );
-    
-    # Insert the chart into the worksheet (with an offset).
-    $worksheet->insert_chart( 'D82', $chart6, 25, 10 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_data_tools.pl>
-
-=head2 Example: chart_clustered.pl
-
-
-
-A demo of a clustered category chart in Excel::Writer::XLSX.
-
-
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A demo of a clustered category chart in Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), March 2015, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'chart_clustered.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    my $bold      = $workbook->add_format( bold => 1 );
-    
-    # Add the worksheet data that the charts will refer to.
-    my $headings = [ 'Types',  'Sub Type',   'Value 1', 'Value 2', 'Value 3' ];
-    my $data = [
-        [ 'Type 1', 'Sub Type A', 5000,      8000,      6000 ],
-        [ '',       'Sub Type B', 2000,      3000,      4000 ],
-        [ '',       'Sub Type C', 250,       1000,      2000 ],
-        [ 'Type 2', 'Sub Type D', 6000,      6000,      6500 ],
-        [ '',       'Sub Type E', 500,       300,       200 ],
-    ];
-    
-    $worksheet->write( 'A1', $headings, $bold );
-    $worksheet->write_col( 'A2', $data );
-    
-    # Create a new chart object. In this case an embedded chart.
-    my $chart = $workbook->add_chart( type => 'column', embedded => 1 );
-    
-    # Configure the series. Note, that the categories are 2D ranges (from column A
-    # to column B). This creates the clusters. The series are shown as formula
-    # strings for clarity but you can also use the array syntax. See the docs.
-    $chart->add_series(
-        name       => '=Sheet1!$C$1',
-        categories => '=Sheet1!$A$2:$B$6',
-        values     => '=Sheet1!$C$2:$C$6',
-    );
-    
-    $chart->add_series(
-        name       => '=Sheet1!$D$1',
-        categories => '=Sheet1!$A$2:$B$6',
-        values     => '=Sheet1!$D$2:$D$6',
-    );
-    
-    $chart->add_series(
-        name       => '=Sheet1!$E$1',
-        categories => '=Sheet1!$A$2:$B$6',
-        values     => '=Sheet1!$E$2:$E$6',
-    );
-    
-    # Set the Excel chart style.
-    $chart->set_style( 37 );
-    
-    # Turn off the legend.
-    $chart->set_legend( position => 'none' );
-    
-    # Insert the chart into the worksheet.
-    $worksheet->insert_chart( 'G3', $chart );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_clustered.pl>
-
-=head2 Example: chart_styles.pl
-
-
-
-An example showing all 48 default chart styles available in Excel 2007
-using Excel::Writer::XLSX.. Note, these styles are not the same as the
-styles available in Excel 2013.
-
-
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # An example showing all 48 default chart styles available in Excel 2007
-    # using Excel::Writer::XLSX.. Note, these styles are not the same as the
-    # styles available in Excel 2013.
-    #
-    # reverse ('(c)'), March 2015, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook = Excel::Writer::XLSX->new( 'chart_styles.xlsx' );
-    
-    # Show the styles for all of these chart types.
-    my @chart_types = ( 'column', 'area', 'line', 'pie' );
-    
-    
-    for my $chart_type ( @chart_types ) {
-    
-        # Add a worksheet for each chart type.
-        my $worksheet = $workbook->add_worksheet( ucfirst( $chart_type ) );
-        $worksheet->set_zoom( 30 );
-        my $style_number = 1;
-    
-        # Create 48 charts, each with a different style.
-        for ( my $row_num = 0 ; $row_num < 90 ; $row_num += 15 ) {
-            for ( my $col_num = 0 ; $col_num < 64 ; $col_num += 8 ) {
-    
-                my $chart = $workbook->add_chart(
-                    type     => $chart_type,
-                    embedded => 1
-                );
-    
-                $chart->add_series( values => '=Data!$A$1:$A$6' );
-                $chart->set_title( name => 'Style ' . $style_number );
-                $chart->set_legend( none => 1 );
-                $chart->set_style( $style_number );
-    
-                $worksheet->insert_chart( $row_num, $col_num, $chart );
-                $style_number++;
-            }
-        }
-    }
-    
-    # Create a worksheet with data for the charts.
-    my $data = [ 10, 40, 50, 20, 10, 50 ];
-    my $data_worksheet = $workbook->add_worksheet( 'Data' );
-    $data_worksheet->write_col( 'A1', $data );
-    $data_worksheet->hide();
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/chart_styles.pl>
-
-=head2 Example: colors.pl
-
-
-
-Demonstrates Excel::Writer::XLSX's named colours and the Excel colour
-palette.
-
-The set_custom_color() Worksheet method can be used to override one of the
-built-in palette values with a more suitable colour. See the main docs.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/colors.jpg" width="640" height="420" alt="Output from colors.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ################################################################################
-    #
-    # Demonstrates Excel::Writer::XLSX's named colours and the Excel colour
-    # palette.
-    #
-    # The set_custom_color() Worksheet method can be used to override one of the
-    # built-in palette values with a more suitable colour. See the main docs.
-    #
-    # reverse ('(c)'), March 2002, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    my $workbook = Excel::Writer::XLSX->new( 'colors.xlsx' );
-    
-    # Some common formats
-    my $center = $workbook->add_format( align => 'center' );
-    my $heading = $workbook->add_format( align => 'center', bold => 1 );
-    
-    
-    ######################################################################
-    #
-    # Demonstrate the named colors.
-    #
-    
-    my %colors = (
-        0x08, 'black',
-        0x0C, 'blue',
-        0x10, 'brown',
-        0x0F, 'cyan',
-        0x17, 'gray',
-        0x11, 'green',
-        0x0B, 'lime',
-        0x0E, 'magenta',
-        0x12, 'navy',
-        0x35, 'orange',
-        0x21, 'pink',
-        0x14, 'purple',
-        0x0A, 'red',
-        0x16, 'silver',
-        0x09, 'white',
-        0x0D, 'yellow',
-    
-    );
-    
-    my $worksheet1 = $workbook->add_worksheet( 'Named colors' );
-    
-    $worksheet1->set_column( 0, 3, 15 );
-    
-    $worksheet1->write( 0, 0, "Index", $heading );
-    $worksheet1->write( 0, 1, "Index", $heading );
-    $worksheet1->write( 0, 2, "Name",  $heading );
-    $worksheet1->write( 0, 3, "Color", $heading );
-    
-    my $i = 1;
-    
-    while ( my ( $index, $color ) = each %colors ) {
-        my $format = $workbook->add_format(
-            fg_color => $color,
-            pattern  => 1,
-            border   => 1
-        );
-    
-        $worksheet1->write( $i + 1, 0, $index, $center );
-        $worksheet1->write( $i + 1, 1, sprintf( "0x%02X", $index ), $center );
-        $worksheet1->write( $i + 1, 2, $color, $center );
-        $worksheet1->write( $i + 1, 3, '',     $format );
-        $i++;
-    }
-    
-    
-    ######################################################################
-    #
-    # Demonstrate the standard Excel colors in the range 8..63.
-    #
-    
-    my $worksheet2 = $workbook->add_worksheet( 'Standard colors' );
-    
-    $worksheet2->set_column( 0, 3, 15 );
-    
-    $worksheet2->write( 0, 0, "Index", $heading );
-    $worksheet2->write( 0, 1, "Index", $heading );
-    $worksheet2->write( 0, 2, "Color", $heading );
-    $worksheet2->write( 0, 3, "Name",  $heading );
-    
-    for my $i ( 8 .. 63 ) {
-        my $format = $workbook->add_format(
-            fg_color => $i,
-            pattern  => 1,
-            border   => 1
-        );
-    
-        $worksheet2->write( ( $i - 7 ), 0, $i, $center );
-        $worksheet2->write( ( $i - 7 ), 1, sprintf( "0x%02X", $i ), $center );
-        $worksheet2->write( ( $i - 7 ), 2, '', $format );
-    
-        # Add the  color names
-        if ( exists $colors{$i} ) {
-            $worksheet2->write( ( $i - 7 ), 3, $colors{$i}, $center );
-    
-        }
-    }
-    
-    
-    ######################################################################
-    #
-    # Demonstrate the Html colors.
-    #
-    
-    
-    
-    %colors = (
-       '#000000',  'black',
-       '#0000FF',  'blue',
-       '#800000',  'brown',
-       '#00FFFF',  'cyan',
-       '#808080',  'gray',
-       '#008000',  'green',
-       '#00FF00',  'lime',
-       '#FF00FF',  'magenta',
-       '#000080',  'navy',
-       '#FF6600',  'orange',
-       '#FF00FF',  'pink',
-       '#800080',  'purple',
-       '#FF0000',  'red',
-       '#C0C0C0',  'silver',
-       '#FFFFFF',  'white',
-       '#FFFF00',  'yellow',
-    );
-    
-    my $worksheet3 = $workbook->add_worksheet( 'Html colors' );
-    
-    $worksheet3->set_column( 0, 3, 15 );
-    
-    $worksheet3->write( 0, 0, "Html", $heading );
-    $worksheet3->write( 0, 1, "Name",  $heading );
-    $worksheet3->write( 0, 2, "Color", $heading );
-    
-    $i = 1;
-    
-    while ( my ( $html_color, $color ) = each %colors ) {
-        my $format = $workbook->add_format(
-            fg_color => $html_color,
-            pattern  => 1,
-            border   => 1
-        );
-    
-        $worksheet3->write( $i + 1, 1, $html_color, $center );
-        $worksheet3->write( $i + 1, 2, $color,      $center );
-        $worksheet3->write( $i + 1, 3, '',          $format );
-        $i++;
-    }
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/colors.pl>
-
-=head2 Example: comments1.pl
-
-
-
-This example demonstrates writing cell comments.
-
-A cell comment is indicated in Excel by a small red triangle in the upper
-right-hand corner of the cell.
-
-For more advanced comment options see comments2.pl.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/comments1.jpg" width="640" height="420" alt="Output from comments1.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # This example demonstrates writing cell comments.
-    #
-    # A cell comment is indicated in Excel by a small red triangle in the upper
-    # right-hand corner of the cell.
-    #
-    # For more advanced comment options see comments2.pl.
-    #
-    # reverse ('(c)'), November 2005, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'comments1.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    $worksheet->write( 'A1', 'Hello' );
-    $worksheet->write_comment( 'A1', 'This is a comment' );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/comments1.pl>
-
-=head2 Example: comments2.pl
-
-
-
-This example demonstrates writing cell comments.
-
-A cell comment is indicated in Excel by a small red triangle in the upper
-right-hand corner of the cell.
-
-Each of the worksheets demonstrates different features of cell comments.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/comments2.jpg" width="640" height="420" alt="Output from comments2.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # This example demonstrates writing cell comments.
-    #
-    # A cell comment is indicated in Excel by a small red triangle in the upper
-    # right-hand corner of the cell.
-    #
-    # Each of the worksheets demonstrates different features of cell comments.
-    #
-    # reverse ('(c)'), November 2005, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook   = Excel::Writer::XLSX->new( 'comments2.xlsx' );
-    my $text_wrap  = $workbook->add_format( text_wrap => 1, valign => 'top' );
-    my $worksheet1 = $workbook->add_worksheet();
-    my $worksheet2 = $workbook->add_worksheet();
-    my $worksheet3 = $workbook->add_worksheet();
-    my $worksheet4 = $workbook->add_worksheet();
-    my $worksheet5 = $workbook->add_worksheet();
-    my $worksheet6 = $workbook->add_worksheet();
-    my $worksheet7 = $workbook->add_worksheet();
-    my $worksheet8 = $workbook->add_worksheet();
-    
-    
-    # Variables that we will use in each example.
-    my $cell_text = '';
-    my $comment   = '';
-    
-    
-    ###############################################################################
-    #
-    # Example 1. Demonstrates a simple cell comments without formatting.
-    #            comments.
-    #
-    
-    # Set up some formatting.
-    $worksheet1->set_column( 'C:C', 25 );
-    $worksheet1->set_row( 2, 50 );
-    $worksheet1->set_row( 5, 50 );
-    
-    
-    # Simple ascii string.
-    $cell_text = 'Hold the mouse over this cell to see the comment.';
-    
-    $comment = 'This is a comment.';
-    
-    $worksheet1->write( 'C3', $cell_text, $text_wrap );
-    $worksheet1->write_comment( 'C3', $comment );
-    
-    $cell_text = 'This is a UTF-8 string.';
-    $comment   = chr 0x263a;
-    
-    $worksheet1->write( 'C6', $cell_text, $text_wrap );
-    $worksheet1->write_comment( 'C6', $comment );
-    
-    
-    
-    ###############################################################################
-    #
-    # Example 2. Demonstrates visible and hidden comments.
-    #
-    
-    # Set up some formatting.
-    $worksheet2->set_column( 'C:C', 25 );
-    $worksheet2->set_row( 2, 50 );
-    $worksheet2->set_row( 5, 50 );
-    
-    
-    $cell_text = 'This cell comment is visible.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet2->write( 'C3', $cell_text, $text_wrap );
-    $worksheet2->write_comment( 'C3', $comment, visible => 1 );
-    
-    
-    $cell_text = "This cell comment isn't visible (the default).";
-    
-    $comment = 'Hello.';
-    
-    $worksheet2->write( 'C6', $cell_text, $text_wrap );
-    $worksheet2->write_comment( 'C6', $comment );
-    
-    
-    ###############################################################################
-    #
-    # Example 3. Demonstrates visible and hidden comments set at the worksheet
-    #            level.
-    #
-    
-    # Set up some formatting.
-    $worksheet3->set_column( 'C:C', 25 );
-    $worksheet3->set_row( 2, 50 );
-    $worksheet3->set_row( 5, 50 );
-    $worksheet3->set_row( 8, 50 );
-    
-    # Make all comments on the worksheet visible.
-    $worksheet3->show_comments();
-    
-    $cell_text = 'This cell comment is visible, explicitly.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet3->write( 'C3', $cell_text, $text_wrap );
-    $worksheet3->write_comment( 'C3', $comment, visible => 1 );
-    
-    
-    $cell_text =
-      'This cell comment is also visible because ' . 'we used show_comments().';
-    
-    $comment = 'Hello.';
-    
-    $worksheet3->write( 'C6', $cell_text, $text_wrap );
-    $worksheet3->write_comment( 'C6', $comment );
-    
-    
-    $cell_text = 'However, we can still override it locally.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet3->write( 'C9', $cell_text, $text_wrap );
-    $worksheet3->write_comment( 'C9', $comment, visible => 0 );
-    
-    
-    ###############################################################################
-    #
-    # Example 4. Demonstrates changes to the comment box dimensions.
-    #
-    
-    # Set up some formatting.
-    $worksheet4->set_column( 'C:C', 25 );
-    $worksheet4->set_row( 2,  50 );
-    $worksheet4->set_row( 5,  50 );
-    $worksheet4->set_row( 8,  50 );
-    $worksheet4->set_row( 15, 50 );
-    
-    $worksheet4->show_comments();
-    
-    $cell_text = 'This cell comment is default size.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet4->write( 'C3', $cell_text, $text_wrap );
-    $worksheet4->write_comment( 'C3', $comment );
-    
-    
-    $cell_text = 'This cell comment is twice as wide.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet4->write( 'C6', $cell_text, $text_wrap );
-    $worksheet4->write_comment( 'C6', $comment, x_scale => 2 );
-    
-    
-    $cell_text = 'This cell comment is twice as high.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet4->write( 'C9', $cell_text, $text_wrap );
-    $worksheet4->write_comment( 'C9', $comment, y_scale => 2 );
-    
-    
-    $cell_text = 'This cell comment is scaled in both directions.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet4->write( 'C16', $cell_text, $text_wrap );
-    $worksheet4->write_comment( 'C16', $comment, x_scale => 1.2, y_scale => 0.8 );
-    
-    
-    $cell_text = 'This cell comment has width and height specified in pixels.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet4->write( 'C19', $cell_text, $text_wrap );
-    $worksheet4->write_comment( 'C19', $comment, width => 200, height => 20 );
-    
-    
-    ###############################################################################
-    #
-    # Example 5. Demonstrates changes to the cell comment position.
-    #
-    
-    $worksheet5->set_column( 'C:C', 25 );
-    $worksheet5->set_row( 2,  50 );
-    $worksheet5->set_row( 5,  50 );
-    $worksheet5->set_row( 8,  50 );
-    $worksheet5->set_row( 11, 50 );
-    
-    $worksheet5->show_comments();
-    
-    $cell_text = 'This cell comment is in the default position.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet5->write( 'C3', $cell_text, $text_wrap );
-    $worksheet5->write_comment( 'C3', $comment );
-    
-    
-    $cell_text = 'This cell comment has been moved to another cell.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet5->write( 'C6', $cell_text, $text_wrap );
-    $worksheet5->write_comment( 'C6', $comment, start_cell => 'E4' );
-    
-    
-    $cell_text = 'This cell comment has been moved to another cell.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet5->write( 'C9', $cell_text, $text_wrap );
-    $worksheet5->write_comment( 'C9', $comment, start_row => 8, start_col => 4 );
-    
-    
-    $cell_text = 'This cell comment has been shifted within its default cell.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet5->write( 'C12', $cell_text, $text_wrap );
-    $worksheet5->write_comment( 'C12', $comment, x_offset => 30, y_offset => 12 );
-    
-    
-    ###############################################################################
-    #
-    # Example 6. Demonstrates changes to the comment background colour.
-    #
-    
-    $worksheet6->set_column( 'C:C', 25 );
-    $worksheet6->set_row( 2, 50 );
-    $worksheet6->set_row( 5, 50 );
-    $worksheet6->set_row( 8, 50 );
-    
-    $worksheet6->show_comments();
-    
-    $cell_text = 'This cell comment has a different colour.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet6->write( 'C3', $cell_text, $text_wrap );
-    $worksheet6->write_comment( 'C3', $comment, color => 'green' );
-    
-    
-    $cell_text = 'This cell comment has the default colour.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet6->write( 'C6', $cell_text, $text_wrap );
-    $worksheet6->write_comment( 'C6', $comment );
-    
-    
-    $cell_text = 'This cell comment has a different colour.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet6->write( 'C9', $cell_text, $text_wrap );
-    $worksheet6->write_comment( 'C9', $comment, color => '#FF6600' );
-    
-    
-    ###############################################################################
-    #
-    # Example 7. Demonstrates how to set the cell comment author.
-    #
-    
-    $worksheet7->set_column( 'C:C', 30 );
-    $worksheet7->set_row( 2,  50 );
-    $worksheet7->set_row( 5,  50 );
-    $worksheet7->set_row( 8,  50 );
-    
-    my $author = '';
-    my $cell   = 'C3';
-    
-    $cell_text = "Move the mouse over this cell and you will see 'Cell commented "
-      . "by $author' (blank) in the status bar at the bottom";
-    
-    $comment = 'Hello.';
-    
-    $worksheet7->write( $cell, $cell_text, $text_wrap );
-    $worksheet7->write_comment( $cell, $comment );
-    
-    
-    $author    = 'Perl';
-    $cell      = 'C6';
-    $cell_text = "Move the mouse over this cell and you will see 'Cell commented "
-      . "by $author' in the status bar at the bottom";
-    
-    $comment = 'Hello.';
-    
-    $worksheet7->write( $cell, $cell_text, $text_wrap );
-    $worksheet7->write_comment( $cell, $comment, author => $author );
-    
-    
-    $author    = chr 0x20AC;
-    $cell      = 'C9';
-    $cell_text = "Move the mouse over this cell and you will see 'Cell commented "
-      . "by $author' in the status bar at the bottom";
-    $comment = 'Hello.';
-    
-    $worksheet7->write( $cell, $cell_text, $text_wrap );
-    $worksheet7->write_comment( $cell, $comment, author => $author );
-    
-    
-    
-    
-    ###############################################################################
-    #
-    # Example 8. Demonstrates the need to explicitly set the row height.
-    #
-    
-    # Set up some formatting.
-    $worksheet8->set_column( 'C:C', 25 );
-    $worksheet8->set_row( 2, 80 );
-    
-    $worksheet8->show_comments();
-    
-    
-    $cell_text =
-        'The height of this row has been adjusted explicitly using '
-      . 'set_row(). The size of the comment box is adjusted '
-      . 'accordingly by Excel::Writer::XLSX.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet8->write( 'C3', $cell_text, $text_wrap );
-    $worksheet8->write_comment( 'C3', $comment );
-    
-    
-    $cell_text =
-        'The height of this row has been adjusted by Excel due to the '
-      . 'text wrap property being set. Unfortunately this means that '
-      . 'the height of the row is unknown to Excel::Writer::XLSX at '
-      . "run time and thus the comment box is stretched as well.\n\n"
-      . 'Use set_row() to specify the row height explicitly to avoid '
-      . 'this problem.';
-    
-    $comment = 'Hello.';
-    
-    $worksheet8->write( 'C6', $cell_text, $text_wrap );
-    $worksheet8->write_comment( 'C6', $comment );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/comments2.pl>
-
-=head2 Example: conditional_format.pl
-
-
-
-Example of how to add conditional formatting to an Excel::Writer::XLSX file.
-
-Conditional formatting allows you to apply a format to a cell or a range of
-cells based on certain criteria.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/conditional_format.jpg" width="640" height="420" alt="Output from conditional_format.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to add conditional formatting to an Excel::Writer::XLSX file.
-    #
-    # Conditional formatting allows you to apply a format to a cell or a range of
-    # cells based on certain criteria.
-    #
-    # reverse ('(c)'), October 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook   = Excel::Writer::XLSX->new( 'conditional_format.xlsx' );
-    my $worksheet1 = $workbook->add_worksheet();
-    my $worksheet2 = $workbook->add_worksheet();
-    my $worksheet3 = $workbook->add_worksheet();
-    my $worksheet4 = $workbook->add_worksheet();
-    my $worksheet5 = $workbook->add_worksheet();
-    my $worksheet6 = $workbook->add_worksheet();
-    my $worksheet7 = $workbook->add_worksheet();
-    my $worksheet8 = $workbook->add_worksheet();
-    my $worksheet9 = $workbook->add_worksheet();
-    
-    
-    # Light red fill with dark red text.
-    my $format1 = $workbook->add_format(
-        bg_color => '#FFC7CE',
-        color    => '#9C0006',
-    
-    );
-    
-    # Green fill with dark green text.
-    my $format2 = $workbook->add_format(
-        bg_color => '#C6EFCE',
-        color    => '#006100',
-    
-    );
-    
-    # Some sample data to run the conditional formatting against.
-    my $data = [
-        [ 34, 72,  38, 30, 75, 48, 75, 66, 84, 86 ],
-        [ 6,  24,  1,  84, 54, 62, 60, 3,  26, 59 ],
-        [ 28, 79,  97, 13, 85, 93, 93, 22, 5,  14 ],
-        [ 27, 71,  40, 17, 18, 79, 90, 93, 29, 47 ],
-        [ 88, 25,  33, 23, 67, 1,  59, 79, 47, 36 ],
-        [ 24, 100, 20, 88, 29, 33, 38, 54, 54, 88 ],
-        [ 6,  57,  88, 28, 10, 26, 37, 7,  41, 48 ],
-        [ 52, 78,  1,  96, 26, 45, 47, 33, 96, 36 ],
-        [ 60, 54,  81, 66, 81, 90, 80, 93, 12, 55 ],
-        [ 70, 5,   46, 14, 71, 19, 66, 36, 41, 21 ],
-    ];
-    
-    
-    ###############################################################################
-    #
-    # Example 1.
-    #
-    my $caption = 'Cells with values >= 50 are in light red. '
-      . 'Values < 50 are in light green.';
-    
-    # Write the data.
-    $worksheet1->write( 'A1', $caption );
-    $worksheet1->write_col( 'B3', $data );
-    
-    # Write a conditional format over a range.
-    $worksheet1->conditional_formatting( 'B3:K12',
-        {
-            type     => 'cell',
-            criteria => '>=',
-            value    => 50,
-            format   => $format1,
-        }
-    );
-    
-    # Write another conditional format over the same range.
-    $worksheet1->conditional_formatting( 'B3:K12',
-        {
-            type     => 'cell',
-            criteria => '<',
-            value    => 50,
-            format   => $format2,
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 2.
-    #
-    $caption = 'Values between 30 and 70 are in light red. '
-      . 'Values outside that range are in light green.';
-    
-    $worksheet2->write( 'A1', $caption );
-    $worksheet2->write_col( 'B3', $data );
-    
-    $worksheet2->conditional_formatting( 'B3:K12',
-        {
-            type     => 'cell',
-            criteria => 'between',
-            minimum  => 30,
-            maximum  => 70,
-            format   => $format1,
-        }
-    );
-    
-    $worksheet2->conditional_formatting( 'B3:K12',
-        {
-            type     => 'cell',
-            criteria => 'not between',
-            minimum  => 30,
-            maximum  => 70,
-            format   => $format2,
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 3.
-    #
-    $caption = 'Duplicate values are in light red. '
-      . 'Unique values are in light green.';
-    
-    $worksheet3->write( 'A1', $caption );
-    $worksheet3->write_col( 'B3', $data );
-    
-    $worksheet3->conditional_formatting( 'B3:K12',
-        {
-            type     => 'duplicate',
-            format   => $format1,
-        }
-    );
-    
-    $worksheet3->conditional_formatting( 'B3:K12',
-        {
-            type     => 'unique',
-            format   => $format2,
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 4.
-    #
-    $caption = 'Above average values are in light red. '
-      . 'Below average values are in light green.';
-    
-    $worksheet4->write( 'A1', $caption );
-    $worksheet4->write_col( 'B3', $data );
-    
-    $worksheet4->conditional_formatting( 'B3:K12',
-        {
-            type     => 'average',
-            criteria => 'above',
-            format   => $format1,
-        }
-    );
-    
-    $worksheet4->conditional_formatting( 'B3:K12',
-        {
-            type     => 'average',
-            criteria => 'below',
-            format   => $format2,
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 5.
-    #
-    $caption = 'Top 10 values are in light red. '
-      . 'Bottom 10 values are in light green.';
-    
-    $worksheet5->write( 'A1', $caption );
-    $worksheet5->write_col( 'B3', $data );
-    
-    $worksheet5->conditional_formatting( 'B3:K12',
-        {
-            type     => 'top',
-            value    => '10',
-            format   => $format1,
-        }
-    );
-    
-    $worksheet5->conditional_formatting( 'B3:K12',
-        {
-            type     => 'bottom',
-            value    => '10',
-            format   => $format2,
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 6.
-    #
-    $caption = 'Cells with values >= 50 are in light red. '
-      . 'Values < 50 are in light green. Non-contiguous ranges.';
-    
-    # Write the data.
-    $worksheet6->write( 'A1', $caption );
-    $worksheet6->write_col( 'B3', $data );
-    
-    # Write a conditional format over a range.
-    $worksheet6->conditional_formatting( 'B3:K6,B9:K12',
-        {
-            type     => 'cell',
-            criteria => '>=',
-            value    => 50,
-            format   => $format1,
-        }
-    );
-    
-    # Write another conditional format over the same range.
-    $worksheet6->conditional_formatting( 'B3:K6,B9:K12',
-        {
-            type     => 'cell',
-            criteria => '<',
-            value    => 50,
-            format   => $format2,
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 7.
-    #
-    $caption = 'Examples of color scales with default and user colors.';
-    
-    $data = [ 1 .. 12 ];
-    
-    $worksheet7->write( 'A1', $caption );
-    
-    $worksheet7->write    ( 'B2', "2 Color Scale" );
-    $worksheet7->write_col( 'B3', $data );
-    
-    $worksheet7->write    ( 'D2', "2 Color Scale + user colors" );
-    $worksheet7->write_col( 'D3', $data );
-    
-    $worksheet7->write    ( 'G2', "3 Color Scale" );
-    $worksheet7->write_col( 'G3', $data );
-    
-    $worksheet7->write    ( 'I2', "3 Color Scale + user colors" );
-    $worksheet7->write_col( 'I3', $data );
-    
-    
-    $worksheet7->conditional_formatting( 'B3:B14',
-        {
-            type => '2_color_scale',
-        }
-    );
-    
-    $worksheet7->conditional_formatting( 'D3:D14',
-        {
-            type => '3_color_scale',
-        }
-    );
-    
-    $worksheet7->conditional_formatting( 'G3:G14',
-        {
-            type      => '2_color_scale',
-            min_color => "#FF0000",
-            max_color => "#00FF00",
-    
-        }
-    );
-    
-    $worksheet7->conditional_formatting( 'I3:I14',
-        {
-            type      => '3_color_scale',
-            min_color => "#C5D9F1",
-            mid_color => "#8DB4E3",
-            max_color => "#538ED5",
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 8.
-    #
-    $caption = 'Examples of data bars.';
-    
-    $data = [ 1 .. 12 ];
-    
-    $worksheet8->write( 'A1', $caption );
-    
-    $worksheet8->write    ( 'B2', "Default data bars" );
-    $worksheet8->write_col( 'B3', $data );
-    
-    $worksheet8->write    ( 'D2', "Bars only" );
-    $worksheet8->write_col( 'D3', $data );
-    
-    $worksheet8->write    ( 'F2', "With user color" );
-    $worksheet8->write_col( 'F3', $data );
-    
-    $worksheet8->write    ( 'H2', "Solid bars" );
-    $worksheet8->write_col( 'H3', $data );
-    
-    $worksheet8->write    ( 'J2', "Right to left" );
-    $worksheet8->write_col( 'J3', $data );
-    
-    $data = [-1, -2, -3, -2, -1, 0, 1, 2, 3, 2, 1, 0];
-    
-    $worksheet8->write    ( 'L2', "Excel 2010 style" );
-    $worksheet8->write_col( 'L3', $data );
-    
-    $worksheet8->write    ( 'N2', "Negative same as positive" );
-    $worksheet8->write_col( 'N3', $data );
-    
-    
-    $worksheet8->conditional_formatting( 'B3:B14',
-        {
-            type      => 'data_bar'
-        }
-    );
-    
-    $worksheet8->conditional_formatting( 'D3:D14',
-        {
-            type     => 'data_bar',
-            bar_only => 1
-        }
-    );
-    
-    $worksheet8->conditional_formatting( 'F3:F14',
-        {
-            type      => 'data_bar',
-            bar_color => '#63C384'
-        }
-    );
-    
-    $worksheet8->conditional_formatting( 'H3:H14',
-        {
-            type      => 'data_bar',
-            bar_solid => 1
-        }
-    );
-    
-    $worksheet8->conditional_formatting( 'J3:J14',
-        {
-            type          => 'data_bar',
-            bar_direction => 'right'
-        }
-    );
-    
-    $worksheet8->conditional_formatting( 'L3:L14',
-        {
-            type          => 'data_bar',
-            data_bar_2010 => 1
-        }
-    );
-    
-    $worksheet8->conditional_formatting( 'N3:N14',
-        {
-            type                           => 'data_bar',
-            bar_negative_color_same        => 1,
-            bar_negative_border_color_same => 1
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 9.
-    #
-    $caption = 'Examples of conditional formats with icon sets.';
-    
-    $data = [
-        [ 1, 2, 3 ],
-        [ 1, 2, 3 ],
-        [ 1, 2, 3 ],
-        [ 1, 2, 3 ],
-        [ 1, 2, 3, 4 ],
-        [ 1, 2, 3, 4, 5 ],
-        [ 1, 2, 3, 4, 5 ],
-    ];
-    
-    $worksheet9->write( 'A1', $caption );
-    $worksheet9->write_col( 'B3', $data );
-    
-    $worksheet9->conditional_formatting( 'B3:D3',
-        {
-            type         => 'icon_set',
-            icon_style   => '3_traffic_lights',
-        }
-    );
-    
-    $worksheet9->conditional_formatting( 'B4:D4',
-        {
-            type         => 'icon_set',
-            icon_style   => '3_traffic_lights',
-            reverse_icons => 1,
-        }
-    );
-    
-    $worksheet9->conditional_formatting( 'B5:D5',
-        {
-            type         => 'icon_set',
-            icon_style   => '3_traffic_lights',
-            icons_only   => 1,
-        }
-    );
-    
-    $worksheet9->conditional_formatting( 'B6:D6',
-        {
-            type         => 'icon_set',
-            icon_style   => '3_arrows',
-        }
-    );
-    
-    $worksheet9->conditional_formatting( 'B7:E8',
-        {
-            type         => 'icon_set',
-            icon_style   => '4_arrows',
-        }
-    );
-    
-    $worksheet9->conditional_formatting( 'B8:F8',
-        {
-            type         => 'icon_set',
-            icon_style   => '5_arrows',
-        }
-    );
-    
-    
-    $worksheet9->conditional_formatting( 'B9:F9',
-        {
-            type         => 'icon_set',
-            icon_style   => '5_ratings',
-        }
-    );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/conditional_format.pl>
-
-=head2 Example: data_validate.pl
-
-
-
-Example of how to add data validation and dropdown lists to an
-Excel::Writer::XLSX file.
-
-Data validation is a feature of Excel which allows you to restrict the data
-that a user enters in a cell and to display help and warning messages. It
-also allows you to restrict input to values in a drop down list.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/data_validate.jpg" width="640" height="420" alt="Output from data_validate.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to add data validation and dropdown lists to an
-    # Excel::Writer::XLSX file.
-    #
-    # Data validation is a feature of Excel which allows you to restrict the data
-    # that a user enters in a cell and to display help and warning messages. It
-    # also allows you to restrict input to values in a drop down list.
-    #
-    # reverse ('(c)'), August 2008, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'data_validate.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    # Add a format for the header cells.
-    my $header_format = $workbook->add_format(
-        border    => 1,
-        bg_color  => '#C6EFCE',
-        bold      => 1,
-        text_wrap => 1,
-        valign    => 'vcenter',
-        indent    => 1,
-    );
-    
-    # Set up layout of the worksheet.
-    $worksheet->set_column( 'A:A', 68 );
-    $worksheet->set_column( 'B:B', 15 );
-    $worksheet->set_column( 'D:D', 15 );
-    $worksheet->set_row( 0, 36 );
-    $worksheet->set_selection( 'B3' );
-    
-    
-    # Write the header cells and some data that will be used in the examples.
-    my $row = 0;
-    my $txt;
-    my $heading1 = 'Some examples of data validation in Excel::Writer::XLSX';
-    my $heading2 = 'Enter values in this column';
-    my $heading3 = 'Sample Data';
-    
-    $worksheet->write( 'A1', $heading1, $header_format );
-    $worksheet->write( 'B1', $heading2, $header_format );
-    $worksheet->write( 'D1', $heading3, $header_format );
-    
-    $worksheet->write( 'D3', [ 'Integers', 1, 10 ] );
-    $worksheet->write( 'D4', [ 'List data', 'open', 'high', 'close' ] );
-    $worksheet->write( 'D5', [ 'Formula', '=AND(F5=50,G5=60)', 50, 60 ] );
-    
-    
-    #
-    # Example 1. Limiting input to an integer in a fixed range.
-    #
-    $txt = 'Enter an integer between 1 and 10';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'integer',
-            criteria => 'between',
-            minimum  => 1,
-            maximum  => 10,
-        }
-    );
-    
-    
-    #
-    # Example 2. Limiting input to an integer outside a fixed range.
-    #
-    $txt = 'Enter an integer that is not between 1 and 10 (using cell references)';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'integer',
-            criteria => 'not between',
-            minimum  => '=E3',
-            maximum  => '=F3',
-        }
-    );
-    
-    
-    #
-    # Example 3. Limiting input to an integer greater than a fixed value.
-    #
-    $txt = 'Enter an integer greater than 0';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'integer',
-            criteria => '>',
-            value    => 0,
-        }
-    );
-    
-    
-    #
-    # Example 4. Limiting input to an integer less than a fixed value.
-    #
-    $txt = 'Enter an integer less than 10';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'integer',
-            criteria => '<',
-            value    => 10,
-        }
-    );
-    
-    
-    #
-    # Example 5. Limiting input to a decimal in a fixed range.
-    #
-    $txt = 'Enter a decimal between 0.1 and 0.5';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'decimal',
-            criteria => 'between',
-            minimum  => 0.1,
-            maximum  => 0.5,
-        }
-    );
-    
-    
-    #
-    # Example 6. Limiting input to a value in a dropdown list.
-    #
-    $txt = 'Select a value from a drop down list';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'list',
-            source   => [ 'open', 'high', 'close' ],
-        }
-    );
-    
-    
-    #
-    # Example 6. Limiting input to a value in a dropdown list.
-    #
-    $txt = 'Select a value from a drop down list (using a cell range)';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'list',
-            source   => '=$E$4:$G$4',
-        }
-    );
-    
-    
-    #
-    # Example 7. Limiting input to a date in a fixed range.
-    #
-    $txt = 'Enter a date between 1/1/2008 and 12/12/2008';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'date',
-            criteria => 'between',
-            minimum  => '2008-01-01T',
-            maximum  => '2008-12-12T',
-        }
-    );
-    
-    
-    #
-    # Example 8. Limiting input to a time in a fixed range.
-    #
-    $txt = 'Enter a time between 6:00 and 12:00';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'time',
-            criteria => 'between',
-            minimum  => 'T06:00',
-            maximum  => 'T12:00',
-        }
-    );
-    
-    
-    #
-    # Example 9. Limiting input to a string greater than a fixed length.
-    #
-    $txt = 'Enter a string longer than 3 characters';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'length',
-            criteria => '>',
-            value    => 3,
-        }
-    );
-    
-    
-    #
-    # Example 10. Limiting input based on a formula.
-    #
-    $txt = 'Enter a value if the following is true "=AND(F5=50,G5=60)"';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate => 'custom',
-            value    => '=AND(F5=50,G5=60)',
-        }
-    );
-    
-    
-    #
-    # Example 11. Displaying and modify data validation messages.
-    #
-    $txt = 'Displays a message when you select the cell';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate      => 'integer',
-            criteria      => 'between',
-            minimum       => 1,
-            maximum       => 100,
-            input_title   => 'Enter an integer:',
-            input_message => 'between 1 and 100',
-        }
-    );
-    
-    
-    #
-    # Example 12. Displaying and modify data validation messages.
-    #
-    $txt = 'Display a custom error message when integer isn\'t between 1 and 100';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate      => 'integer',
-            criteria      => 'between',
-            minimum       => 1,
-            maximum       => 100,
-            input_title   => 'Enter an integer:',
-            input_message => 'between 1 and 100',
-            error_title   => 'Input value is not valid!',
-            error_message => 'It should be an integer between 1 and 100',
-        }
-    );
-    
-    
-    #
-    # Example 13. Displaying and modify data validation messages.
-    #
-    $txt =
-      'Display a custom information message when integer isn\'t between 1 and 100';
-    $row += 2;
-    
-    $worksheet->write( $row, 0, $txt );
-    $worksheet->data_validation(
-        $row, 1,
-        {
-            validate      => 'integer',
-            criteria      => 'between',
-            minimum       => 1,
-            maximum       => 100,
-            input_title   => 'Enter an integer:',
-            input_message => 'between 1 and 100',
-            error_title   => 'Input value is not valid!',
-            error_message => 'It should be an integer between 1 and 100',
-            error_type    => 'information',
-        }
-    );
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/data_validate.pl>
-
-=head2 Example: date_time.pl
-
-
-
-Excel::Writer::XLSX example of writing dates and times using the
-write_date_time() Worksheet method.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/date_time.jpg" width="640" height="420" alt="Output from date_time.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Excel::Writer::XLSX example of writing dates and times using the
-    # write_date_time() Worksheet method.
-    #
-    # reverse ('(c)'), August 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'date_time.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    my $bold      = $workbook->add_format( bold => 1 );
-    
-    
-    # Expand the first columns so that the date is visible.
-    $worksheet->set_column( "A:B", 30 );
-    
-    
-    # Write the column headers
-    $worksheet->write( 'A1', 'Formatted date', $bold );
-    $worksheet->write( 'B1', 'Format',         $bold );
-    
-    
-    # Examples date and time formats. In the output file compare how changing
-    # the format codes change the appearance of the date.
-    #
-    my @date_formats = (
-        'dd/mm/yy',
-        'mm/dd/yy',
-        '',
-        'd mm yy',
-        'dd mm yy',
-        '',
-        'dd m yy',
-        'dd mm yy',
-        'dd mmm yy',
-        'dd mmmm yy',
-        '',
-        'dd mm y',
-        'dd mm yyy',
-        'dd mm yyyy',
-        '',
-        'd mmmm yyyy',
-        '',
-        'dd/mm/yy',
-        'dd/mm/yy hh:mm',
-        'dd/mm/yy hh:mm:ss',
-        'dd/mm/yy hh:mm:ss.000',
-        '',
-        'hh:mm',
-        'hh:mm:ss',
-        'hh:mm:ss.000',
-    );
-    
-    
-    # Write the same date and time using each of the above formats. The empty
-    # string formats create a blank line to make the example clearer.
-    #
-    my $row = 0;
-    for my $date_format ( @date_formats ) {
-        $row++;
-        next if $date_format eq '';
-    
-        # Create a format for the date or time.
-        my $format = $workbook->add_format(
-            num_format => $date_format,
-            align      => 'left'
-        );
-    
-        # Write the same date using different formats.
-        $worksheet->write_date_time( $row, 0, '2004-08-01T12:30:45.123', $format );
-        $worksheet->write( $row, 1, $date_format );
-    }
-    
-    
-    # The following is an example of an invalid date. It is written as a string
-    # instead of a number. This is also Excel's default behaviour.
-    #
-    $row += 2;
-    $worksheet->write_date_time( $row, 0, '2004-13-01T12:30:45.123' );
-    $worksheet->write( $row, 1, 'Invalid date. Written as string.', $bold );
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/date_time.pl>
-
-=head2 Example: defined_name.pl
-
-
-
-Example of how to create defined names in an Excel::Writer::XLSX file.
-
-This method is used to define a user friendly name to represent a value,
-a single cell or a range of cells in a workbook.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/defined_name.jpg" width="640" height="420" alt="Output from defined_name.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # Example of how to create defined names in an Excel::Writer::XLSX file.
-    #
-    # This method is used to define a user friendly name to represent a value,
-    # a single cell or a range of cells in a workbook.
-    #
-    # reverse ('(c)'), September 2008, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook   = Excel::Writer::XLSX->new( 'defined_name.xlsx' );
-    my $worksheet1 = $workbook->add_worksheet();
-    my $worksheet2 = $workbook->add_worksheet();
-    
-    # Define some global/workbook names.
-    $workbook->define_name( 'Exchange_rate', '=0.96' );
-    $workbook->define_name( 'Sales',         '=Sheet1!$G$1:$H$10' );
-    
-    # Define a local/worksheet name.
-    $workbook->define_name( 'Sheet2!Sales', '=Sheet2!$G$1:$G$10' );
-    
-    # Write some text in the file and one of the defined names in a formula.
-    for my $worksheet ( $workbook->sheets() ) {
-        $worksheet->set_column( 'A:A', 45 );
-        $worksheet->write( 'A1', 'This worksheet contains some defined names.' );
-        $worksheet->write( 'A2', 'See Formulas -> Name Manager above.' );
-        $worksheet->write( 'A3', 'Example formula in cell B3 ->' );
-    
-        $worksheet->write( 'B3', '=Exchange_rate' );
-    }
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/defined_name.pl>
-
-=head2 Example: diag_border.pl
-
-
-
-A simple formatting example that demonstrates how to add a diagonal cell
-border with Excel::Writer::XLSX
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/diag_border.jpg" width="640" height="420" alt="Output from diag_border.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ##############################################################################
-    #
-    # A simple formatting example that demonstrates how to add a diagonal cell
-    # border with Excel::Writer::XLSX
-    #
-    # reverse ('(c)'), May 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'diag_border.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    my $format1 = $workbook->add_format( diag_type => 1 );
-    
-    my $format2 = $workbook->add_format( diag_type => 2 );
-    
-    my $format3 = $workbook->add_format( diag_type => 3 );
-    
-    my $format4 = $workbook->add_format(
-        diag_type   => 3,
-        diag_border => 7,
-        diag_color  => 'red',
-    );
-    
-    
-    $worksheet->write( 'B3',  'Text', $format1 );
-    $worksheet->write( 'B6',  'Text', $format2 );
-    $worksheet->write( 'B9',  'Text', $format3 );
-    $worksheet->write( 'B12', 'Text', $format4 );
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/diag_border.pl>
-
-=head2 Example: filehandle.pl
-
-
-
-Example of using Excel::Writer::XLSX to write Excel files to different
-filehandles.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/filehandle.jpg" width="640" height="420" alt="Output from filehandle.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of using Excel::Writer::XLSX to write Excel files to different
-    # filehandles.
-    #
-    # reverse ('(c)'), April 2003, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    use IO::Scalar;
-    
-    
-    ###############################################################################
-    #
-    # Example 1. This demonstrates the standard way of creating an Excel file by
-    # specifying a file name.
-    #
-    
-    my $workbook1  = Excel::Writer::XLSX->new( 'fh_01.xlsx' );
-    my $worksheet1 = $workbook1->add_worksheet();
-    
-    $worksheet1->write( 0, 0, 'Hi Excel 1' );
-    
-    $workbook1->close();
-    
-    
-    ###############################################################################
-    #
-    # Example 2. Write an Excel file to an existing filehandle.
-    #
-    
-    open TEST, '>', 'fh_02.xlsx' or die "Couldn't open file: $!";
-    binmode TEST;   # Always do this regardless of whether the platform requires it.
-    
-    my $workbook2  = Excel::Writer::XLSX->new( \*TEST );
-    my $worksheet2 = $workbook2->add_worksheet();
-    
-    $worksheet2->write( 0, 0, 'Hi Excel 2' );
-    
-    $workbook2->close();
-    
-    ###############################################################################
-    #
-    # Example 3. Write an Excel file to an existing OO style filehandle.
-    #
-    
-    my $fh = FileHandle->new( '> fh_03.xlsx' ) or die "Couldn't open file: $!";
-    
-    binmode( $fh );
-    
-    my $workbook3  = Excel::Writer::XLSX->new( $fh );
-    my $worksheet3 = $workbook3->add_worksheet();
-    
-    $worksheet3->write( 0, 0, 'Hi Excel 3' );
-    
-    $workbook3->close();
-    
-    
-    ###############################################################################
-    #
-    # Example 4. Write an Excel file to a string via IO::Scalar. Please refer to
-    # the IO::Scalar documentation for further details.
-    #
-    
-    my $xlsx_str;
-    
-    tie *XLSX, 'IO::Scalar', \$xlsx_str;
-    
-    my $workbook4  = Excel::Writer::XLSX->new( \*XLSX );
-    my $worksheet4 = $workbook4->add_worksheet();
-    
-    $worksheet4->write( 0, 0, 'Hi Excel 4' );
-    $workbook4->close();    # This is required before we use the scalar
-    
-    
-    # The Excel file is now in $xlsx_str. As a demonstration, print it to a file.
-    open TMP, '>', 'fh_04.xlsx' or die "Couldn't open file: $!";
-    binmode TMP;
-    print TMP $xlsx_str;
-    close TMP;
-    
-    
-    ###############################################################################
-    #
-    # Example 5. Write an Excel file to a string via IO::Scalar's newer interface.
-    # Please refer to the IO::Scalar documentation for further details.
-    #
-    my $xlsx_str2;
-    
-    my $fh5 = IO::Scalar->new( \$xlsx_str2 );
-    
-    my $workbook5  = Excel::Writer::XLSX->new( $fh5 );
-    my $worksheet5 = $workbook5->add_worksheet();
-    
-    $worksheet5->write( 0, 0, 'Hi Excel 5' );
-    $workbook5->close();    # This is required before we use the scalar
-    
-    # The Excel file is now in $xlsx_str. As a demonstration, print it to a file.
-    open TMP, '>', 'fh_05.xlsx' or die "Couldn't open file: $!";
-    binmode TMP;
-    print TMP $xlsx_str2;
-    close TMP;
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/filehandle.pl>
-
-=head2 Example: headers.pl
-
-
-
-This program shows several examples of how to set up headers and
-footers with Excel::Writer::XLSX.
-
-The control characters used in the header/footer strings are:
-
-    Control             Category            Description
-    =======             ========            ===========
-    &L                  Justification       Left
-    &C                                      Center
-    &R                                      Right
-
-    &P                  Information         Page number
-    &N                                      Total number of pages
-    &D                                      Date
-    &T                                      Time
-    &F                                      File name
-    &A                                      Worksheet name
-
-    &fontsize           Font                Font size
-    &"font,style"                           Font name and style
-    &U                                      Single underline
-    &E                                      Double underline
-    &S                                      Strikethrough
-    &X                                      Superscript
-    &Y                                      Subscript
-
-    &[Picture]          Images              Image placeholder
-    &G                                      Same as &[Picture]
-
-    &&                  Miscellaneous       Literal ampersand &
-
-See the main Excel::Writer::XLSX documentation for more information.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/headers.jpg" width="640" height="420" alt="Output from headers.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ######################################################################
-    #
-    # This program shows several examples of how to set up headers and
-    # footers with Excel::Writer::XLSX.
-    #
-    # The control characters used in the header/footer strings are:
-    #
-    #     Control             Category            Description
-    #     =======             ========            ===========
-    #     &L                  Justification       Left
-    #     &C                                      Center
-    #     &R                                      Right
-    #
-    #     &P                  Information         Page number
-    #     &N                                      Total number of pages
-    #     &D                                      Date
-    #     &T                                      Time
-    #     &F                                      File name
-    #     &A                                      Worksheet name
-    #
-    #     &fontsize           Font                Font size
-    #     &"font,style"                           Font name and style
-    #     &U                                      Single underline
-    #     &E                                      Double underline
-    #     &S                                      Strikethrough
-    #     &X                                      Superscript
-    #     &Y                                      Subscript
-    #
-    #     &[Picture]          Images              Image placeholder
-    #     &G                                      Same as &[Picture]
-    #
-    #     &&                  Miscellaneous       Literal ampersand &
-    #
-    # See the main Excel::Writer::XLSX documentation for more information.
-    #
-    # reverse ('(c)'), March 2002, John McNamara, jmcnamara@cpan.org
-    #
-    
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook = Excel::Writer::XLSX->new( 'headers.xlsx' );
-    my $preview  = 'Select Print Preview to see the header and footer';
-    
-    
-    ######################################################################
-    #
-    # A simple example to start
-    #
-    my $worksheet1 = $workbook->add_worksheet( 'Simple' );
-    my $header1    = '&CHere is some centred text.';
-    my $footer1    = '&LHere is some left aligned text.';
-    
-    $worksheet1->set_header( $header1 );
-    $worksheet1->set_footer( $footer1 );
-    
-    $worksheet1->set_column( 'A:A', 50 );
-    $worksheet1->write( 'A1', $preview );
-    
-    
-    ######################################################################
-    #
-    # A simple example to start
-    #
-    my $worksheet2 = $workbook->add_worksheet( 'Image' );
-    my $header2    = '&L&[Picture]';
-    
-    # Adjust the page top margin to allow space for the header image.
-    $worksheet2->set_margin_top(1.75);
-    
-    $worksheet2->set_header( $header2, 0.3, {image_left => 'republic.png'});
-    
-    $worksheet2->set_column( 'A:A', 50 );
-    $worksheet2->write( 'A1', $preview );
-    
-    
-    ######################################################################
-    #
-    # This is an example of some of the header/footer variables.
-    #
-    my $worksheet3 = $workbook->add_worksheet( 'Variables' );
-    my $header3    = '&LPage &P of &N' . '&CFilename: &F' . '&RSheetname: &A';
-    my $footer3    = '&LCurrent date: &D' . '&RCurrent time: &T';
-    
-    $worksheet3->set_header( $header3 );
-    $worksheet3->set_footer( $footer3 );
-    
-    $worksheet3->set_column( 'A:A', 50 );
-    $worksheet3->write( 'A1',  $preview );
-    $worksheet3->write( 'A21', 'Next sheet' );
-    $worksheet3->set_h_pagebreaks( 20 );
-    
-    
-    ######################################################################
-    #
-    # This example shows how to use more than one font
-    #
-    my $worksheet4 = $workbook->add_worksheet( 'Mixed fonts' );
-    my $header4    = q(&C&"Courier New,Bold"Hello &"Arial,Italic"World);
-    my $footer4    = q(&C&"Symbol"e&"Arial" = mc&X2);
-    
-    $worksheet4->set_header( $header4 );
-    $worksheet4->set_footer( $footer4 );
-    
-    $worksheet4->set_column( 'A:A', 50 );
-    $worksheet4->write( 'A1', $preview );
-    
-    
-    ######################################################################
-    #
-    # Example of line wrapping
-    #
-    my $worksheet5 = $workbook->add_worksheet( 'Word wrap' );
-    my $header5    = "&CHeading 1\nHeading 2";
-    
-    $worksheet5->set_header( $header5 );
-    
-    $worksheet5->set_column( 'A:A', 50 );
-    $worksheet5->write( 'A1', $preview );
-    
-    
-    ######################################################################
-    #
-    # Example of inserting a literal ampersand &
-    #
-    my $worksheet6 = $workbook->add_worksheet( 'Ampersand' );
-    my $header6    = '&CCuriouser && Curiouser - Attorneys at Law';
-    
-    $worksheet6->set_header( $header6 );
-    
-    $worksheet6->set_column( 'A:A', 50 );
-    $worksheet6->write( 'A1', $preview );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/headers.pl>
-
-=head2 Example: hide_row_col.pl
-
-
-
-Example of how to hide rows and columns in Excel::Writer::XLSX. In order to
-hide rows without setting each one, (of approximately 1 million rows),
-Excel uses an optimisation to hide all rows that don't have data.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/hide_row_col.jpg" width="640" height="420" alt="Output from hide_row_col.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to hide rows and columns in Excel::Writer::XLSX. In order to
-    # hide rows without setting each one, (of approximately 1 million rows),
-    # Excel uses an optimisation to hide all rows that don't have data.
-    #
-    # reverse ('(c)'), December 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'hide_row_col.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    # Write some data.
-    $worksheet->write( 'D1', 'Some hidden columns.' );
-    $worksheet->write( 'A8', 'Some hidden rows.' );
-    
-    # Hide all rows without data.
-    $worksheet->set_default_row( undef, 1 );
-    
-    # Set emptys row that we do want to display. All other will be hidden.
-    for my $row (1 .. 6) {
-        $worksheet->set_row( $row, 15 );
-    }
-    
-    # Hide a range of columns.
-    $worksheet->set_column( 'G:XFD', undef, undef, 1);
-    
-    $workbook->close();
-    
-    __END__
-    
-    
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/hide_row_col.pl>
-
-=head2 Example: hide_sheet.pl
-
-
-
-Example of how to hide a worksheet with Excel::Writer::XLSX.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/hide_sheet.jpg" width="640" height="420" alt="Output from hide_sheet.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # Example of how to hide a worksheet with Excel::Writer::XLSX.
-    #
-    # reverse ('(c)'), April 2005, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook   = Excel::Writer::XLSX->new( 'hide_sheet.xlsx' );
-    my $worksheet1 = $workbook->add_worksheet();
-    my $worksheet2 = $workbook->add_worksheet();
-    my $worksheet3 = $workbook->add_worksheet();
-    
-    $worksheet1->set_column( 'A:A', 30 );
-    $worksheet2->set_column( 'A:A', 30 );
-    $worksheet3->set_column( 'A:A', 30 );
-    
-    # Sheet2 won't be visible until it is unhidden in Excel.
-    $worksheet2->hide();
-    
-    $worksheet1->write( 0, 0, 'Sheet2 is hidden' );
-    $worksheet2->write( 0, 0, "Now it's my turn to find you." );
-    $worksheet3->write( 0, 0, 'Sheet2 is hidden' );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/hide_sheet.pl>
-
-=head2 Example: hyperlink1.pl
-
-
-
-Example of how to use the Excel::Writer::XLSX module to write hyperlinks
-
-See also hyperlink2.pl for worksheet URL examples.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/hyperlink1.jpg" width="640" height="420" alt="Output from hyperlink1.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to use the Excel::Writer::XLSX module to write hyperlinks
-    #
-    # See also hyperlink2.pl for worksheet URL examples.
-    #
-    # reverse ('(c)'), May 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook = Excel::Writer::XLSX->new( 'hyperlink.xlsx' );
-    
-    
-    my $worksheet = $workbook->add_worksheet( 'Hyperlinks' );
-    
-    # Format the first column
-    $worksheet->set_column( 'A:A', 30 );
-    $worksheet->set_selection( 'B1' );
-    
-    # Add a user defined hyperlink format.
-    my $red_format = $workbook->add_format(
-        color     => 'red',
-        bold      => 1,
-        underline => 1,
-        size      => 12,
-    );
-    
-    # Add an alternate description string to the URL.
-    my $str = 'Perl home.';
-    
-    # Add a "tool tip" to the URL.
-    my $tip = 'Get the latest Perl news here.';
-    
-    
-    # Write some hyperlinks. Unspecified or undefined format paraamters will be
-    # replace with the defuault Excel hyperlink style.
-    $worksheet->write( 'A1', 'http://www.perl.com/' );
-    $worksheet->write( 'A3', 'http://www.perl.com/', undef, $str );
-    $worksheet->write( 'A5', 'http://www.perl.com/', undef, $str, $tip );
-    $worksheet->write( 'A7', 'http://www.perl.com/', $red_format );
-    $worksheet->write( 'A9', 'mailto:jmcnamara@cpan.org', undef, 'Mail me' );
-    
-    # Write a URL that isn't a hyperlink
-    $worksheet->write_string( 'A11', 'http://www.perl.com/' );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/hyperlink1.pl>
-
-=head2 Example: hyperlink2.pl
-
-
-
-Example of how to use the Excel::Writer::XLSX module to write internal and
-external hyperlinks.
-
-If you wish to run this program and follow the hyperlinks you should create
-the following directory structure:
-
-C:\ -- Temp --+-- Europe
-              |
-              \-- Asia
-
-
-See also hyperlink1.pl for web URL examples.
-
-
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to use the Excel::Writer::XLSX module to write internal and
-    # external hyperlinks.
-    #
-    # If you wish to run this program and follow the hyperlinks you should create
-    # the following directory structure:
-    #
-    # C:\ -- Temp --+-- Europe
-    #               |
-    #               \-- Asia
-    #
-    #
-    # See also hyperlink1.pl for web URL examples.
-    #
-    # reverse ('(c)'), February 2002, John McNamara, jmcnamara@cpan.org
-    #
-    
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create three workbooks:
-    #   C:\Temp\Europe\Ireland.xlsx
-    #   C:\Temp\Europe\Italy.xlsx
-    #   C:\Temp\Asia\China.xlsx
-    #
-    
-    my $ireland = Excel::Writer::XLSX->new( 'C:\Temp\Europe\Ireland.xlsx' );
-    
-    my $ire_links      = $ireland->add_worksheet( 'Links' );
-    my $ire_sales      = $ireland->add_worksheet( 'Sales' );
-    my $ire_data       = $ireland->add_worksheet( 'Product Data' );
-    my $ire_url_format = $ireland->get_default_url_format();
-    
-    
-    my $italy = Excel::Writer::XLSX->new( 'C:\Temp\Europe\Italy.xlsx' );
-    
-    my $ita_links      = $italy->add_worksheet( 'Links' );
-    my $ita_sales      = $italy->add_worksheet( 'Sales' );
-    my $ita_data       = $italy->add_worksheet( 'Product Data' );
-    my $ita_url_format = $italy->get_default_url_format();
-    
-    
-    my $china = Excel::Writer::XLSX->new( 'C:\Temp\Asia\China.xlsx' );
-    
-    my $cha_links      = $china->add_worksheet( 'Links' );
-    my $cha_sales      = $china->add_worksheet( 'Sales' );
-    my $cha_data       = $china->add_worksheet( 'Product Data' );
-    my $cha_url_format = $china->get_default_url_format();
-    
-    
-    # Add an alternative format
-    my $format = $ireland->add_format( color => 'green', bold => 1 );
-    $ire_links->set_column( 'A:B', 25 );
-    
-    
-    ###############################################################################
-    #
-    # Examples of internal links
-    #
-    $ire_links->write( 'A1', 'Internal links', $format );
-    
-    # Internal link
-    $ire_links->write_url( 'A2', 'internal:Sales!A2', $ire_url_format );
-    
-    # Internal link to a range
-    $ire_links->write_url( 'A3', 'internal:Sales!A3:D3', $ire_url_format );
-    
-    # Internal link with an alternative string
-    $ire_links->write_url( 'A4', 'internal:Sales!A4', $ire_url_format, 'Link' );
-    
-    # Internal link with an alternative format
-    $ire_links->write_url( 'A5', 'internal:Sales!A5', $format );
-    
-    # Internal link with an alternative string and format
-    $ire_links->write_url( 'A6', 'internal:Sales!A6', $ire_url_format, 'Link' );
-    
-    # Internal link (spaces in worksheet name)
-    $ire_links->write_url( 'A7', q{internal:'Product Data'!A7}, $ire_url_format );
-    
-    
-    ###############################################################################
-    #
-    # Examples of external links
-    #
-    $ire_links->write( 'B1', 'External links', $format );
-    
-    # External link to a local file
-    $ire_links->write_url( 'B2', 'external:Italy.xlsx', $ire_url_format );
-    
-    # External link to a local file with worksheet
-    $ire_links->write_url( 'B3', 'external:Italy.xlsx#Sales!B3', $ire_url_format );
-    
-    # External link to a local file with worksheet and alternative string
-    $ire_links->write_url( 'B4', 'external:Italy.xlsx#Sales!B4', $ire_url_format, 'Link' );
-    
-    # External link to a local file with worksheet and format
-    $ire_links->write_url( 'B5', 'external:Italy.xlsx#Sales!B5', $format );
-    
-    # External link to a remote file, absolute path
-    $ire_links->write_url( 'B6', 'external:C:/Temp/Asia/China.xlsx', $ire_url_format );
-    
-    # External link to a remote file, relative path
-    $ire_links->write_url( 'B7', 'external:../Asia/China.xlsx', $ire_url_format );
-    
-    # External link to a remote file with worksheet
-    $ire_links->write_url( 'B8', 'external:C:/Temp/Asia/China.xlsx#Sales!B8', $ire_url_format );
-    
-    # External link to a remote file with worksheet (with spaces in the name)
-    $ire_links->write_url( 'B9', q{external:C:/Temp/Asia/China.xlsx#'Product Data'!B9}, $ire_url_format );
-    
-    
-    ###############################################################################
-    #
-    # Some utility links to return to the main sheet
-    #
-    $ire_sales->write_url( 'A2', 'internal:Links!A2', $ire_url_format, 'Back' );
-    $ire_sales->write_url( 'A3', 'internal:Links!A3', $ire_url_format, 'Back' );
-    $ire_sales->write_url( 'A4', 'internal:Links!A4', $ire_url_format, 'Back' );
-    $ire_sales->write_url( 'A5', 'internal:Links!A5', $ire_url_format, 'Back' );
-    $ire_sales->write_url( 'A6', 'internal:Links!A6', $ire_url_format, 'Back' );
-    $ire_data->write_url ( 'A7', 'internal:Links!A7', $ire_url_format, 'Back' );
-    
-    $ita_links->write_url( 'A1', 'external:Ireland.xlsx#Links!B2', $ita_url_format, 'Back' );
-    $ita_sales->write_url( 'B3', 'external:Ireland.xlsx#Links!B3', $ita_url_format, 'Back' );
-    $ita_sales->write_url( 'B4', 'external:Ireland.xlsx#Links!B4', $ita_url_format, 'Back' );
-    $ita_sales->write_url( 'B5', 'external:Ireland.xlsx#Links!B5', $ita_url_format, 'Back' );
-    $cha_links->write_url( 'A1', 'external:C:/Temp/Europe/Ireland.xlsx#Links!B6', $cha_url_format, 'Back' );
-    $cha_sales->write_url( 'B8', 'external:C:/Temp/Europe/Ireland.xlsx#Links!B8', $cha_url_format, 'Back' );
-    $cha_data->write_url ( 'B9', 'external:C:/Temp/Europe/Ireland.xlsx#Links!B9', $cha_url_format, 'Back' );
-    
-    $ireland->close();
-    $italy->close();
-    $china->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/hyperlink2.pl>
-
-=head2 Example: indent.pl
-
-
-
-A simple formatting example using Excel::Writer::XLSX.
-
-This program demonstrates the indentation cell format.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/indent.jpg" width="640" height="420" alt="Output from indent.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ##############################################################################
-    #
-    # A simple formatting example using Excel::Writer::XLSX.
-    #
-    # This program demonstrates the indentation cell format.
-    #
-    # reverse ('(c)'), May 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    my $workbook = Excel::Writer::XLSX->new( 'indent.xlsx' );
-    
-    my $worksheet = $workbook->add_worksheet();
-    my $indent1   = $workbook->add_format( indent => 1 );
-    my $indent2   = $workbook->add_format( indent => 2 );
-    
-    $worksheet->set_column( 'A:A', 40 );
-    
-    
-    $worksheet->write( 'A1', "This text is indented 1 level",  $indent1 );
-    $worksheet->write( 'A2', "This text is indented 2 levels", $indent2 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/indent.pl>
-
-=head2 Example: macros.pl
-
-
-
-An example of adding macros to an Excel::Writer::XLSX file using
-a VBA project file extracted from an existing Excel xlsm file.
-
-The C<extract_vba> utility supplied with Excel::Writer::XLSX can be
-used to extract the vbaProject.bin file.
-
-An embedded macro is connected to a form button on the worksheet.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/macros.jpg" width="640" height="420" alt="Output from macros.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # An example of adding macros to an Excel::Writer::XLSX file using
-    # a VBA project file extracted from an existing Excel xlsm file.
-    #
-    # The C<extract_vba> utility supplied with Excel::Writer::XLSX can be
-    # used to extract the vbaProject.bin file.
-    #
-    # An embedded macro is connected to a form button on the worksheet.
-    #
-    # reverse('(c)'), November 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Note the file extension should be .xlsm.
-    my $workbook  = Excel::Writer::XLSX->new( 'macros.xlsm' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    $worksheet->set_column( 'A:A', 30 );
-    
-    # Add the VBA project binary.
-    $workbook->add_vba_project( './vbaProject.bin' );
-    
-    # Show text for the end user.
-    $worksheet->write( 'A3', 'Press the button to say hello.' );
-    
-    # Add a button tied to a macro in the VBA project.
-    $worksheet->insert_button(
-        'B3',
-        {
-            macro   => 'say_hello',
-            caption => 'Press Me',
-            width   => 80,
-            height  => 30
-        }
-    );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/macros.pl>
-
-=head2 Example: merge1.pl
-
-
-
-Simple example of merging cells using the Excel::Writer::XLSX module.
-
-This example merges three cells using the "Centre Across Selection"
-alignment which was the Excel 5 method of achieving a merge. For a more
-modern approach use the merge_range() worksheet method instead.
-See the merge3.pl - merge6.pl programs.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/merge1.jpg" width="640" height="420" alt="Output from merge1.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Simple example of merging cells using the Excel::Writer::XLSX module.
-    #
-    # This example merges three cells using the "Centre Across Selection"
-    # alignment which was the Excel 5 method of achieving a merge. For a more
-    # modern approach use the merge_range() worksheet method instead.
-    # See the merge3.pl - merge6.pl programs.
-    #
-    # reverse ('(c)'), August 2002, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'merge1.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    # Increase the cell size of the merged cells to highlight the formatting.
-    $worksheet->set_column( 'B:D', 20 );
-    $worksheet->set_row( 2, 30 );
-    
-    
-    # Create a merge format
-    my $format = $workbook->add_format( center_across => 1 );
-    
-    
-    # Only one cell should contain text, the others should be blank.
-    $worksheet->write( 2, 1, "Center across selection", $format );
-    $worksheet->write_blank( 2, 2, $format );
-    $worksheet->write_blank( 2, 3, $format );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/merge1.pl>
-
-=head2 Example: merge2.pl
-
-
-
-Simple example of merging cells using the Excel::Writer::XLSX module
-
-This example merges three cells using the "Centre Across Selection"
-alignment which was the Excel 5 method of achieving a merge. For a more
-modern approach use the merge_range() worksheet method instead.
-See the merge3.pl - merge6.pl programs.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/merge2.jpg" width="640" height="420" alt="Output from merge2.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Simple example of merging cells using the Excel::Writer::XLSX module
-    #
-    # This example merges three cells using the "Centre Across Selection"
-    # alignment which was the Excel 5 method of achieving a merge. For a more
-    # modern approach use the merge_range() worksheet method instead.
-    # See the merge3.pl - merge6.pl programs.
-    #
-    # reverse ('(c)'), August 2002, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'merge2.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    # Increase the cell size of the merged cells to highlight the formatting.
-    $worksheet->set_column( 1, 2, 30 );
-    $worksheet->set_row( 2, 40 );
-    
-    
-    # Create a merged format
-    my $format = $workbook->add_format(
-        center_across => 1,
-        bold          => 1,
-        size          => 15,
-        pattern       => 1,
-        border        => 6,
-        color         => 'white',
-        fg_color      => 'green',
-        border_color  => 'yellow',
-        align         => 'vcenter',
-    );
-    
-    
-    # Only one cell should contain text, the others should be blank.
-    $worksheet->write( 2, 1, "Center across selection", $format );
-    $worksheet->write_blank( 2, 2, $format );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/merge2.pl>
-
-=head2 Example: merge3.pl
-
-
-
-Example of how to use Excel::Writer::XLSX to write a hyperlink in a
-merged cell.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/merge3.jpg" width="640" height="420" alt="Output from merge3.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to use Excel::Writer::XLSX to write a hyperlink in a
-    # merged cell.
-    #
-    # reverse ('(c)'), September 2002, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'merge3.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    # Increase the cell size of the merged cells to highlight the formatting.
-    $worksheet->set_row( $_, 30 ) for ( 3, 6, 7 );
-    $worksheet->set_column( 'B:D', 20 );
-    
-    
-    ###############################################################################
-    #
-    # Example: Merge cells containing a hyperlink using merge_range().
-    #
-    my $format = $workbook->add_format(
-        border    => 1,
-        underline => 1,
-        color     => 'blue',
-        align     => 'center',
-        valign    => 'vcenter',
-    );
-    
-    # Merge 3 cells
-    $worksheet->merge_range( 'B4:D4', 'http://www.perl.com', $format );
-    
-    
-    # Merge 3 cells over two rows
-    $worksheet->merge_range( 'B7:D8', 'http://www.perl.com', $format );
-    
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/merge3.pl>
-
-=head2 Example: merge4.pl
-
-
-
-Example of how to use the Excel::Writer::XLSX merge_range() workbook
-method with complex formatting.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/merge4.jpg" width="640" height="420" alt="Output from merge4.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to use the Excel::Writer::XLSX merge_range() workbook
-    # method with complex formatting.
-    #
-    # reverse ('(c)'), September 2002, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'merge4.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    # Increase the cell size of the merged cells to highlight the formatting.
-    $worksheet->set_row( $_, 30 ) for ( 1 .. 11 );
-    $worksheet->set_column( 'B:D', 20 );
-    
-    
-    ###############################################################################
-    #
-    # Example 1: Text centered vertically and horizontally
-    #
-    my $format1 = $workbook->add_format(
-        border => 6,
-        bold   => 1,
-        color  => 'red',
-        valign => 'vcenter',
-        align  => 'center',
-    );
-    
-    
-    $worksheet->merge_range( 'B2:D3', 'Vertical and horizontal', $format1 );
-    
-    
-    ###############################################################################
-    #
-    # Example 2: Text aligned to the top and left
-    #
-    my $format2 = $workbook->add_format(
-        border => 6,
-        bold   => 1,
-        color  => 'red',
-        valign => 'top',
-        align  => 'left',
-    );
-    
-    
-    $worksheet->merge_range( 'B5:D6', 'Aligned to the top and left', $format2 );
-    
-    
-    ###############################################################################
-    #
-    # Example 3:  Text aligned to the bottom and right
-    #
-    my $format3 = $workbook->add_format(
-        border => 6,
-        bold   => 1,
-        color  => 'red',
-        valign => 'bottom',
-        align  => 'right',
-    );
-    
-    
-    $worksheet->merge_range( 'B8:D9', 'Aligned to the bottom and right', $format3 );
-    
-    
-    ###############################################################################
-    #
-    # Example 4:  Text justified (i.e. wrapped) in the cell
-    #
-    my $format4 = $workbook->add_format(
-        border => 6,
-        bold   => 1,
-        color  => 'red',
-        valign => 'top',
-        align  => 'justify',
-    );
-    
-    
-    $worksheet->merge_range( 'B11:D12', 'Justified: ' . 'so on and ' x 18,
-        $format4 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/merge4.pl>
-
-=head2 Example: merge5.pl
-
-
-
-Example of how to use the Excel::Writer::XLSX merge_cells() workbook
-method with complex formatting and rotation.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/merge5.jpg" width="640" height="420" alt="Output from merge5.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to use the Excel::Writer::XLSX merge_cells() workbook
-    # method with complex formatting and rotation.
-    #
-    #
-    # reverse ('(c)'), September 2002, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'merge5.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    # Increase the cell size of the merged cells to highlight the formatting.
-    $worksheet->set_row( $_, 36 ) for ( 3 .. 8 );
-    $worksheet->set_column( $_, $_, 15 ) for ( 1, 3, 5 );
-    
-    
-    ###############################################################################
-    #
-    # Rotation 1, letters run from top to bottom
-    #
-    my $format1 = $workbook->add_format(
-        border   => 6,
-        bold     => 1,
-        color    => 'red',
-        valign   => 'vcentre',
-        align    => 'centre',
-        rotation => 270,
-    );
-    
-    
-    $worksheet->merge_range( 'B4:B9', 'Rotation 270', $format1 );
-    
-    
-    ###############################################################################
-    #
-    # Rotation 2, 90° anticlockwise
-    #
-    my $format2 = $workbook->add_format(
-        border   => 6,
-        bold     => 1,
-        color    => 'red',
-        valign   => 'vcentre',
-        align    => 'centre',
-        rotation => 90,
-    );
-    
-    
-    $worksheet->merge_range( 'D4:D9', 'Rotation 90°', $format2 );
-    
-    
-    ###############################################################################
-    #
-    # Rotation 3, 90° clockwise
-    #
-    my $format3 = $workbook->add_format(
-        border   => 6,
-        bold     => 1,
-        color    => 'red',
-        valign   => 'vcentre',
-        align    => 'centre',
-        rotation => -90,
-    );
-    
-    
-    $worksheet->merge_range( 'F4:F9', 'Rotation -90°', $format3 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/merge5.pl>
-
-=head2 Example: merge6.pl
-
-
-
-Example of how to use the Excel::Writer::XLSX merge_cells() workbook
-method with Unicode strings.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/merge6.jpg" width="640" height="420" alt="Output from merge6.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to use the Excel::Writer::XLSX merge_cells() workbook
-    # method with Unicode strings.
-    #
-    #
-    # reverse ('(c)'), December 2005, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'merge6.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    # Increase the cell size of the merged cells to highlight the formatting.
-    $worksheet->set_row( $_, 36 ) for 2 .. 9;
-    $worksheet->set_column( 'B:D', 25 );
-    
-    
-    # Format for the merged cells.
-    my $format = $workbook->add_format(
-        border => 6,
-        bold   => 1,
-        color  => 'red',
-        size   => 20,
-        valign => 'vcentre',
-        align  => 'left',
-        indent => 1,
-    );
-    
-    
-    ###############################################################################
-    #
-    # Write an Ascii string.
-    #
-    $worksheet->merge_range( 'B3:D4', 'ASCII: A simple string', $format );
-    
-    
-    ###############################################################################
-    #
-    # Write a UTF-8 Unicode string.
-    #
-    my $smiley = chr 0x263a;
-    $worksheet->merge_range( 'B6:D7', "UTF-8: A Unicode smiley $smiley", $format );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/merge6.pl>
-
-=head2 Example: mod_perl1.pl
-
-
-
-Example of how to use the Excel::Writer::XLSX module to send an Excel
-file to a browser using mod_perl 1 and Apache
-
-This module ties *XLSX directly to Apache, and with the correct
-content-disposition/types it will prompt the user to save
-the file, or open it at this location.
-
-This script is a modification of the Excel::Writer::XLSX cgi.pl example.
-
-Change the name of this file to Cgi.pm.
-Change the package location to wherever you locate this package.
-In the example below it is located in the Excel::Writer::XLSX directory.
-
-Your httpd.conf entry for this module, should you choose to use it
-as a stand alone app, should look similar to the following:
-
-    <Location /spreadsheet-test>
-      SetHandler perl-script
-      PerlHandler Excel::Writer::XLSX::Cgi
-      PerlSendHeader On
-    </Location>
-
-The PerlHandler name above and the package name below *have* to match.
-
-    ###############################################################################
-    #
-    # Example of how to use the Excel::Writer::XLSX module to send an Excel
-    # file to a browser using mod_perl 1 and Apache
-    #
-    # This module ties *XLSX directly to Apache, and with the correct
-    # content-disposition/types it will prompt the user to save
-    # the file, or open it at this location.
-    #
-    # This script is a modification of the Excel::Writer::XLSX cgi.pl example.
-    #
-    # Change the name of this file to Cgi.pm.
-    # Change the package location to wherever you locate this package.
-    # In the example below it is located in the Excel::Writer::XLSX directory.
-    #
-    # Your httpd.conf entry for this module, should you choose to use it
-    # as a stand alone app, should look similar to the following:
-    #
-    #     <Location /spreadsheet-test>
-    #       SetHandler perl-script
-    #       PerlHandler Excel::Writer::XLSX::Cgi
-    #       PerlSendHeader On
-    #     </Location>
-    #
-    # The PerlHandler name above and the package name below *have* to match.
-    
-    # Apr 2001, Thomas Sullivan, webmaster@860.org
-    # Feb 2001, John McNamara, jmcnamara@cpan.org
-    
-    package Excel::Writer::XLSX::Cgi;
-    
-    ##########################################
-    # Pragma Definitions
-    ##########################################
-    use strict;
-    
-    ##########################################
-    # Required Modules
-    ##########################################
-    use Apache::Constants qw(:common);
-    use Apache::Request;
-    use Apache::URI;    # This may not be needed
-    use Excel::Writer::XLSX;
-    
-    ##########################################
-    # Main App Body
-    ##########################################
-    sub handler {
-    
-        # New apache object
-        # Should you decide to use it.
-        my $r = Apache::Request->new( shift );
-    
-        # Set the filename and send the content type
-        # This will appear when they save the spreadsheet
-        my $filename = "cgitest.xlsx";
-    
-        ####################################################
-        ## Send the content type headers
-        ####################################################
-        print "Content-disposition: attachment;filename=$filename\n";
-        print "Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\n\n";
-    
-        ####################################################
-        # Tie a filehandle to Apache's STDOUT.
-        # Create a new workbook and add a worksheet.
-        ####################################################
-        tie *XLSX => 'Apache';
-        binmode( *XLSX );
-    
-        my $workbook  = Excel::Writer::XLSX->new( \*XLSX );
-        my $worksheet = $workbook->add_worksheet();
-    
-    
-        # Set the column width for column 1
-        $worksheet->set_column( 0, 0, 20 );
-    
-    
-        # Create a format
-        my $format = $workbook->add_format();
-        $format->set_bold();
-        $format->set_size( 15 );
-        $format->set_color( 'blue' );
-    
-    
-        # Write to the workbook
-        $worksheet->write( 0, 0, "Hi Excel!", $format );
-    
-        # You must close the workbook for Content-disposition
-        $workbook->close();
-    }
-    
-    1;
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/mod_perl1.pl>
-
-=head2 Example: mod_perl2.pl
-
-
-
-Example of how to use the Excel::Writer::XLSX module to send an Excel
-file to a browser using mod_perl 2 and Apache.
-
-This module ties *XLSX directly to Apache, and with the correct
-content-disposition/types it will prompt the user to save
-the file, or open it at this location.
-
-This script is a modification of the Excel::Writer::XLSX cgi.pl example.
-
-Change the name of this file to MP2Test.pm.
-Change the package location to wherever you locate this package.
-In the example below it is located in the Excel::Writer::XLSX directory.
-
-Your httpd.conf entry for this module, should you choose to use it
-as a stand alone app, should look similar to the following:
-
-    PerlModule Apache2::RequestRec
-    PerlModule APR::Table
-    PerlModule Apache2::RequestIO
-
-    <Location /spreadsheet-test>
-       SetHandler perl-script
-       PerlResponseHandler Excel::Writer::XLSX::MP2Test
-    </Location>
-
-The PerlResponseHandler must match the package name below.
-
-    ###############################################################################
-    #
-    # Example of how to use the Excel::Writer::XLSX module to send an Excel
-    # file to a browser using mod_perl 2 and Apache.
-    #
-    # This module ties *XLSX directly to Apache, and with the correct
-    # content-disposition/types it will prompt the user to save
-    # the file, or open it at this location.
-    #
-    # This script is a modification of the Excel::Writer::XLSX cgi.pl example.
-    #
-    # Change the name of this file to MP2Test.pm.
-    # Change the package location to wherever you locate this package.
-    # In the example below it is located in the Excel::Writer::XLSX directory.
-    #
-    # Your httpd.conf entry for this module, should you choose to use it
-    # as a stand alone app, should look similar to the following:
-    #
-    #     PerlModule Apache2::RequestRec
-    #     PerlModule APR::Table
-    #     PerlModule Apache2::RequestIO
-    #
-    #     <Location /spreadsheet-test>
-    #        SetHandler perl-script
-    #        PerlResponseHandler Excel::Writer::XLSX::MP2Test
-    #     </Location>
-    #
-    # The PerlResponseHandler must match the package name below.
-    
-    # Jun 2004, Matisse Enzer, matisse@matisse.net  (mod_perl 2 version)
-    # Apr 2001, Thomas Sullivan, webmaster@860.org
-    # Feb 2001, John McNamara, jmcnamara@cpan.org
-    
-    package Excel::Writer::XLSX::MP2Test;
-    
-    ##########################################
-    # Pragma Definitions
-    ##########################################
-    use strict;
-    
-    ##########################################
-    # Required Modules
-    ##########################################
-    use Apache2::Const -compile => qw( :common );
-    use Excel::Writer::XLSX;
-    
-    ##########################################
-    # Main App Body
-    ##########################################
-    sub handler {
-        my ( $r ) = @_;   # Apache request object is passed to handler in mod_perl 2
-    
-        # Set the filename and send the content type
-        # This will appear when they save the spreadsheet
-        my $filename = "mod_perl2_test.xlsx";
-    
-        ####################################################
-        ## Send the content type headers the mod_perl 2 way
-        ####################################################
-        $r->headers_out->{'Content-Disposition'} = "attachment;filename=$filename";
-        $r->content_type( 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' );
-    
-        ####################################################
-        # Tie a filehandle to Apache's STDOUT.
-        # Create a new workbook and add a worksheet.
-        ####################################################
-        tie *XLSX => $r;  # The mod_perl 2 way. Tie to the Apache::RequestRec object
-        binmode( *XLSX );
-    
-        my $workbook  = Excel::Writer::XLSX->new( \*XLSX );
-        my $worksheet = $workbook->add_worksheet();
-    
-    
-        # Set the column width for column 1
-        $worksheet->set_column( 0, 0, 20 );
-    
-    
-        # Create a format
-        my $format = $workbook->add_format();
-        $format->set_bold();
-        $format->set_size( 15 );
-        $format->set_color( 'blue' );
-    
-    
-        # Write to the workbook
-        $worksheet->write( 0, 0, 'Hi Excel! from ' . $r->hostname, $format );
-    
-        # You must close the workbook for Content-disposition
-        $workbook->close();
-        return Apache2::Const::OK;
-    }
-    
-    1;
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/mod_perl2.pl>
-
-=head2 Example: outline.pl
-
-
-
-Example of how use Excel::Writer::XLSX to generate Excel outlines and
-grouping.
-
-
-Excel allows you to group rows or columns so that they can be hidden or
-displayed with a single mouse click. This feature is referred to as outlines.
-
-Outlines can reduce complex data down to a few salient sub-totals or
-summaries.
-
-This feature is best viewed in Excel but the following is an ASCII
-representation of what a worksheet with three outlines might look like.
-Rows 3-4 and rows 7-8 are grouped at level 2. Rows 2-9 are grouped at
-level 1. The lines at the left hand side are called outline level bars.
-
-
-            ------------------------------------------
-     1 2 3 |   |   A   |   B   |   C   |   D   |  ...
-            ------------------------------------------
-      _    | 1 |   A   |       |       |       |  ...
-     |  _  | 2 |   B   |       |       |       |  ...
-     | |   | 3 |  (C)  |       |       |       |  ...
-     | |   | 4 |  (D)  |       |       |       |  ...
-     | -   | 5 |   E   |       |       |       |  ...
-     |  _  | 6 |   F   |       |       |       |  ...
-     | |   | 7 |  (G)  |       |       |       |  ...
-     | |   | 8 |  (H)  |       |       |       |  ...
-     | -   | 9 |   I   |       |       |       |  ...
-     -     | . |  ...  |  ...  |  ...  |  ...  |  ...
-
-
-Clicking the minus sign on each of the level 2 outlines will collapse and
-hide the data as shown in the next figure. The minus sign changes to a plus
-sign to indicate that the data in the outline is hidden.
-
-            ------------------------------------------
-     1 2 3 |   |   A   |   B   |   C   |   D   |  ...
-            ------------------------------------------
-      _    | 1 |   A   |       |       |       |  ...
-     |     | 2 |   B   |       |       |       |  ...
-     | +   | 5 |   E   |       |       |       |  ...
-     |     | 6 |   F   |       |       |       |  ...
-     | +   | 9 |   I   |       |       |       |  ...
-     -     | . |  ...  |  ...  |  ...  |  ...  |  ...
-
-
-Clicking on the minus sign on the level 1 outline will collapse the remaining
-rows as follows:
-
-            ------------------------------------------
-     1 2 3 |   |   A   |   B   |   C   |   D   |  ...
-            ------------------------------------------
-           | 1 |   A   |       |       |       |  ...
-     +     | . |  ...  |  ...  |  ...  |  ...  |  ...
-
-See the main Excel::Writer::XLSX documentation for more information.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/outline.jpg" width="640" height="420" alt="Output from outline.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how use Excel::Writer::XLSX to generate Excel outlines and
-    # grouping.
-    #
-    #
-    # Excel allows you to group rows or columns so that they can be hidden or
-    # displayed with a single mouse click. This feature is referred to as outlines.
-    #
-    # Outlines can reduce complex data down to a few salient sub-totals or
-    # summaries.
-    #
-    # This feature is best viewed in Excel but the following is an ASCII
-    # representation of what a worksheet with three outlines might look like.
-    # Rows 3-4 and rows 7-8 are grouped at level 2. Rows 2-9 are grouped at
-    # level 1. The lines at the left hand side are called outline level bars.
-    #
-    #
-    #             ------------------------------------------
-    #      1 2 3 |   |   A   |   B   |   C   |   D   |  ...
-    #             ------------------------------------------
-    #       _    | 1 |   A   |       |       |       |  ...
-    #      |  _  | 2 |   B   |       |       |       |  ...
-    #      | |   | 3 |  (C)  |       |       |       |  ...
-    #      | |   | 4 |  (D)  |       |       |       |  ...
-    #      | -   | 5 |   E   |       |       |       |  ...
-    #      |  _  | 6 |   F   |       |       |       |  ...
-    #      | |   | 7 |  (G)  |       |       |       |  ...
-    #      | |   | 8 |  (H)  |       |       |       |  ...
-    #      | -   | 9 |   I   |       |       |       |  ...
-    #      -     | . |  ...  |  ...  |  ...  |  ...  |  ...
-    #
-    #
-    # Clicking the minus sign on each of the level 2 outlines will collapse and
-    # hide the data as shown in the next figure. The minus sign changes to a plus
-    # sign to indicate that the data in the outline is hidden.
-    #
-    #             ------------------------------------------
-    #      1 2 3 |   |   A   |   B   |   C   |   D   |  ...
-    #             ------------------------------------------
-    #       _    | 1 |   A   |       |       |       |  ...
-    #      |     | 2 |   B   |       |       |       |  ...
-    #      | +   | 5 |   E   |       |       |       |  ...
-    #      |     | 6 |   F   |       |       |       |  ...
-    #      | +   | 9 |   I   |       |       |       |  ...
-    #      -     | . |  ...  |  ...  |  ...  |  ...  |  ...
-    #
-    #
-    # Clicking on the minus sign on the level 1 outline will collapse the remaining
-    # rows as follows:
-    #
-    #             ------------------------------------------
-    #      1 2 3 |   |   A   |   B   |   C   |   D   |  ...
-    #             ------------------------------------------
-    #            | 1 |   A   |       |       |       |  ...
-    #      +     | . |  ...  |  ...  |  ...  |  ...  |  ...
-    #
-    # See the main Excel::Writer::XLSX documentation for more information.
-    #
-    # reverse ('(c)'), April 2003, John McNamara, jmcnamara@cpan.org
-    #
-    
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add some worksheets
-    my $workbook   = Excel::Writer::XLSX->new( 'outline.xlsx' );
-    my $worksheet1 = $workbook->add_worksheet( 'Outlined Rows' );
-    my $worksheet2 = $workbook->add_worksheet( 'Collapsed Rows' );
-    my $worksheet3 = $workbook->add_worksheet( 'Outline Columns' );
-    my $worksheet4 = $workbook->add_worksheet( 'Outline levels' );
-    
-    # Add a general format
-    my $bold = $workbook->add_format( bold => 1 );
-    
-    
-    ###############################################################################
-    #
-    # Example 1: Create a worksheet with outlined rows. It also includes SUBTOTAL()
-    # functions so that it looks like the type of automatic outlines that are
-    # generated when you use the Excel Data->SubTotals menu item.
-    #
-    
-    
-    # For outlines the important parameters are $hidden and $level. Rows with the
-    # same $level are grouped together. The group will be collapsed if $hidden is
-    # non-zero. $height and $XF are assigned default values if they are undef.
-    #
-    # The syntax is: set_row($row, $height, $XF, $hidden, $level, $collapsed)
-    #
-    $worksheet1->set_row( 1, undef, undef, 0, 2 );
-    $worksheet1->set_row( 2, undef, undef, 0, 2 );
-    $worksheet1->set_row( 3, undef, undef, 0, 2 );
-    $worksheet1->set_row( 4, undef, undef, 0, 2 );
-    $worksheet1->set_row( 5, undef, undef, 0, 1 );
-    
-    $worksheet1->set_row( 6,  undef, undef, 0, 2 );
-    $worksheet1->set_row( 7,  undef, undef, 0, 2 );
-    $worksheet1->set_row( 8,  undef, undef, 0, 2 );
-    $worksheet1->set_row( 9,  undef, undef, 0, 2 );
-    $worksheet1->set_row( 10, undef, undef, 0, 1 );
-    
-    
-    # Add a column format for clarity
-    $worksheet1->set_column( 'A:A', 20 );
-    
-    # Add the data, labels and formulas
-    $worksheet1->write( 'A1', 'Region', $bold );
-    $worksheet1->write( 'A2', 'North' );
-    $worksheet1->write( 'A3', 'North' );
-    $worksheet1->write( 'A4', 'North' );
-    $worksheet1->write( 'A5', 'North' );
-    $worksheet1->write( 'A6', 'North Total', $bold );
-    
-    $worksheet1->write( 'B1', 'Sales', $bold );
-    $worksheet1->write( 'B2', 1000 );
-    $worksheet1->write( 'B3', 1200 );
-    $worksheet1->write( 'B4', 900 );
-    $worksheet1->write( 'B5', 1200 );
-    $worksheet1->write( 'B6', '=SUBTOTAL(9,B2:B5)', $bold );
-    
-    $worksheet1->write( 'A7',  'South' );
-    $worksheet1->write( 'A8',  'South' );
-    $worksheet1->write( 'A9',  'South' );
-    $worksheet1->write( 'A10', 'South' );
-    $worksheet1->write( 'A11', 'South Total', $bold );
-    
-    $worksheet1->write( 'B7',  400 );
-    $worksheet1->write( 'B8',  600 );
-    $worksheet1->write( 'B9',  500 );
-    $worksheet1->write( 'B10', 600 );
-    $worksheet1->write( 'B11', '=SUBTOTAL(9,B7:B10)', $bold );
-    
-    $worksheet1->write( 'A12', 'Grand Total',         $bold );
-    $worksheet1->write( 'B12', '=SUBTOTAL(9,B2:B10)', $bold );
-    
-    
-    ###############################################################################
-    #
-    # Example 2: Create a worksheet with outlined rows. This is the same as the
-    # previous example except that the rows are collapsed.
-    # Note: We need to indicate the row that contains the collapsed symbol '+'
-    # with the optional parameter, $collapsed.
-    
-    # The group will be collapsed if $hidden is non-zero.
-    # The syntax is: set_row($row, $height, $XF, $hidden, $level, $collapsed)
-    #
-    $worksheet2->set_row( 1, undef, undef, 1, 2 );
-    $worksheet2->set_row( 2, undef, undef, 1, 2 );
-    $worksheet2->set_row( 3, undef, undef, 1, 2 );
-    $worksheet2->set_row( 4, undef, undef, 1, 2 );
-    $worksheet2->set_row( 5, undef, undef, 1, 1 );
-    
-    $worksheet2->set_row( 6,  undef, undef, 1, 2 );
-    $worksheet2->set_row( 7,  undef, undef, 1, 2 );
-    $worksheet2->set_row( 8,  undef, undef, 1, 2 );
-    $worksheet2->set_row( 9,  undef, undef, 1, 2 );
-    $worksheet2->set_row( 10, undef, undef, 1, 1 );
-    $worksheet2->set_row( 11, undef, undef, 0, 0, 1 );
-    
-    
-    # Add a column format for clarity
-    $worksheet2->set_column( 'A:A', 20 );
-    
-    # Add the data, labels and formulas
-    $worksheet2->write( 'A1', 'Region', $bold );
-    $worksheet2->write( 'A2', 'North' );
-    $worksheet2->write( 'A3', 'North' );
-    $worksheet2->write( 'A4', 'North' );
-    $worksheet2->write( 'A5', 'North' );
-    $worksheet2->write( 'A6', 'North Total', $bold );
-    
-    $worksheet2->write( 'B1', 'Sales', $bold );
-    $worksheet2->write( 'B2', 1000 );
-    $worksheet2->write( 'B3', 1200 );
-    $worksheet2->write( 'B4', 900 );
-    $worksheet2->write( 'B5', 1200 );
-    $worksheet2->write( 'B6', '=SUBTOTAL(9,B2:B5)', $bold );
-    
-    $worksheet2->write( 'A7',  'South' );
-    $worksheet2->write( 'A8',  'South' );
-    $worksheet2->write( 'A9',  'South' );
-    $worksheet2->write( 'A10', 'South' );
-    $worksheet2->write( 'A11', 'South Total', $bold );
-    
-    $worksheet2->write( 'B7',  400 );
-    $worksheet2->write( 'B8',  600 );
-    $worksheet2->write( 'B9',  500 );
-    $worksheet2->write( 'B10', 600 );
-    $worksheet2->write( 'B11', '=SUBTOTAL(9,B7:B10)', $bold );
-    
-    $worksheet2->write( 'A12', 'Grand Total',         $bold );
-    $worksheet2->write( 'B12', '=SUBTOTAL(9,B2:B10)', $bold );
-    
-    
-    ###############################################################################
-    #
-    # Example 3: Create a worksheet with outlined columns.
-    #
-    my $data = [
-        [ 'Month', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', ' Total' ],
-        [ 'North', 50,    20,    15,    25,    65,    80,    '=SUM(B2:G2)' ],
-        [ 'South', 10,    20,    30,    50,    50,    50,    '=SUM(B3:G3)' ],
-        [ 'East',  45,    75,    50,    15,    75,    100,   '=SUM(B4:G4)' ],
-        [ 'West',  15,    15,    55,    35,    20,    50,    '=SUM(B5:G5)' ],
-    ];
-    
-    # Add bold format to the first row
-    $worksheet3->set_row( 0, undef, $bold );
-    
-    # Syntax: set_column($col1, $col2, $width, $XF, $hidden, $level, $collapsed)
-    $worksheet3->set_column( 'A:A', 10, $bold );
-    $worksheet3->set_column( 'B:G', 5, undef, 0, 1 );
-    $worksheet3->set_column( 'H:H', 10 );
-    
-    # Write the data and a formula
-    $worksheet3->write_col( 'A1', $data );
-    $worksheet3->write( 'H6', '=SUM(H2:H5)', $bold );
-    
-    
-    ###############################################################################
-    #
-    # Example 4: Show all possible outline levels.
-    #
-    my $levels = [
-        "Level 1", "Level 2", "Level 3", "Level 4", "Level 5", "Level 6",
-        "Level 7", "Level 6", "Level 5", "Level 4", "Level 3", "Level 2",
-        "Level 1"
-    ];
-    
-    
-    $worksheet4->write_col( 'A1', $levels );
-    
-    $worksheet4->set_row( 0,  undef, undef, undef, 1 );
-    $worksheet4->set_row( 1,  undef, undef, undef, 2 );
-    $worksheet4->set_row( 2,  undef, undef, undef, 3 );
-    $worksheet4->set_row( 3,  undef, undef, undef, 4 );
-    $worksheet4->set_row( 4,  undef, undef, undef, 5 );
-    $worksheet4->set_row( 5,  undef, undef, undef, 6 );
-    $worksheet4->set_row( 6,  undef, undef, undef, 7 );
-    $worksheet4->set_row( 7,  undef, undef, undef, 6 );
-    $worksheet4->set_row( 8,  undef, undef, undef, 5 );
-    $worksheet4->set_row( 9,  undef, undef, undef, 4 );
-    $worksheet4->set_row( 10, undef, undef, undef, 3 );
-    $worksheet4->set_row( 11, undef, undef, undef, 2 );
-    $worksheet4->set_row( 12, undef, undef, undef, 1 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/outline.pl>
-
-=head2 Example: outline_collapsed.pl
-
-
-
-Example of how to use Excel::Writer::XLSX to generate Excel outlines and
-grouping.
-
-These examples focus mainly on collapsed outlines. See also the
-outlines.pl example program for more general examples.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/outline_collapsed.jpg" width="640" height="420" alt="Output from outline_collapsed.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to use Excel::Writer::XLSX to generate Excel outlines and
-    # grouping.
-    #
-    # These examples focus mainly on collapsed outlines. See also the
-    # outlines.pl example program for more general examples.
-    #
-    # reverse ('(c)'), March 2008, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add some worksheets
-    my $workbook   = Excel::Writer::XLSX->new( 'outline_collapsed.xlsx' );
-    my $worksheet1 = $workbook->add_worksheet( 'Outlined Rows' );
-    my $worksheet2 = $workbook->add_worksheet( 'Collapsed Rows 1' );
-    my $worksheet3 = $workbook->add_worksheet( 'Collapsed Rows 2' );
-    my $worksheet4 = $workbook->add_worksheet( 'Collapsed Rows 3' );
-    my $worksheet5 = $workbook->add_worksheet( 'Outline Columns' );
-    my $worksheet6 = $workbook->add_worksheet( 'Collapsed Columns' );
-    
-    
-    # Add a general format
-    my $bold = $workbook->add_format( bold => 1 );
-    
-    
-    #
-    # This function will generate the same data and sub-totals on each worksheet.
-    #
-    sub create_sub_totals {
-    
-        my $worksheet = $_[0];
-    
-        # Add a column format for clarity
-        $worksheet->set_column( 'A:A', 20 );
-    
-        # Add the data, labels and formulas
-        $worksheet->write( 'A1', 'Region', $bold );
-        $worksheet->write( 'A2', 'North' );
-        $worksheet->write( 'A3', 'North' );
-        $worksheet->write( 'A4', 'North' );
-        $worksheet->write( 'A5', 'North' );
-        $worksheet->write( 'A6', 'North Total', $bold );
-    
-        $worksheet->write( 'B1', 'Sales', $bold );
-        $worksheet->write( 'B2', 1000 );
-        $worksheet->write( 'B3', 1200 );
-        $worksheet->write( 'B4', 900 );
-        $worksheet->write( 'B5', 1200 );
-        $worksheet->write( 'B6', '=SUBTOTAL(9,B2:B5)', $bold );
-    
-        $worksheet->write( 'A7',  'South' );
-        $worksheet->write( 'A8',  'South' );
-        $worksheet->write( 'A9',  'South' );
-        $worksheet->write( 'A10', 'South' );
-        $worksheet->write( 'A11', 'South Total', $bold );
-    
-        $worksheet->write( 'B7',  400 );
-        $worksheet->write( 'B8',  600 );
-        $worksheet->write( 'B9',  500 );
-        $worksheet->write( 'B10', 600 );
-        $worksheet->write( 'B11', '=SUBTOTAL(9,B7:B10)', $bold );
-    
-        $worksheet->write( 'A12', 'Grand Total',         $bold );
-        $worksheet->write( 'B12', '=SUBTOTAL(9,B2:B10)', $bold );
-    
-    }
-    
-    
-    ###############################################################################
-    #
-    # Example 1: Create a worksheet with outlined rows. It also includes SUBTOTAL()
-    # functions so that it looks like the type of automatic outlines that are
-    # generated when you use the Excel Data->SubTotals menu item.
-    #
-    
-    # The syntax is: set_row($row, $height, $XF, $hidden, $level, $collapsed)
-    $worksheet1->set_row( 1, undef, undef, 0, 2 );
-    $worksheet1->set_row( 2, undef, undef, 0, 2 );
-    $worksheet1->set_row( 3, undef, undef, 0, 2 );
-    $worksheet1->set_row( 4, undef, undef, 0, 2 );
-    $worksheet1->set_row( 5, undef, undef, 0, 1 );
-    
-    $worksheet1->set_row( 6,  undef, undef, 0, 2 );
-    $worksheet1->set_row( 7,  undef, undef, 0, 2 );
-    $worksheet1->set_row( 8,  undef, undef, 0, 2 );
-    $worksheet1->set_row( 9,  undef, undef, 0, 2 );
-    $worksheet1->set_row( 10, undef, undef, 0, 1 );
-    
-    # Write the sub-total data that is common to the row examples.
-    create_sub_totals( $worksheet1 );
-    
-    
-    ###############################################################################
-    #
-    # Example 2: Create a worksheet with collapsed outlined rows.
-    # This is the same as the example 1  except that the all rows are collapsed.
-    # Note: We need to indicate the row that contains the collapsed symbol '+' with
-    # the optional parameter, $collapsed.
-    
-    $worksheet2->set_row( 1, undef, undef, 1, 2 );
-    $worksheet2->set_row( 2, undef, undef, 1, 2 );
-    $worksheet2->set_row( 3, undef, undef, 1, 2 );
-    $worksheet2->set_row( 4, undef, undef, 1, 2 );
-    $worksheet2->set_row( 5, undef, undef, 1, 1 );
-    
-    $worksheet2->set_row( 6,  undef, undef, 1, 2 );
-    $worksheet2->set_row( 7,  undef, undef, 1, 2 );
-    $worksheet2->set_row( 8,  undef, undef, 1, 2 );
-    $worksheet2->set_row( 9,  undef, undef, 1, 2 );
-    $worksheet2->set_row( 10, undef, undef, 1, 1 );
-    
-    $worksheet2->set_row( 11, undef, undef, 0, 0, 1 );
-    
-    # Write the sub-total data that is common to the row examples.
-    create_sub_totals( $worksheet2 );
-    
-    
-    ###############################################################################
-    #
-    # Example 3: Create a worksheet with collapsed outlined rows.
-    # Same as the example 1  except that the two sub-totals are collapsed.
-    
-    $worksheet3->set_row( 1, undef, undef, 1, 2 );
-    $worksheet3->set_row( 2, undef, undef, 1, 2 );
-    $worksheet3->set_row( 3, undef, undef, 1, 2 );
-    $worksheet3->set_row( 4, undef, undef, 1, 2 );
-    $worksheet3->set_row( 5, undef, undef, 0, 1, 1 );
-    
-    $worksheet3->set_row( 6,  undef, undef, 1, 2 );
-    $worksheet3->set_row( 7,  undef, undef, 1, 2 );
-    $worksheet3->set_row( 8,  undef, undef, 1, 2 );
-    $worksheet3->set_row( 9,  undef, undef, 1, 2 );
-    $worksheet3->set_row( 10, undef, undef, 0, 1, 1 );
-    
-    
-    # Write the sub-total data that is common to the row examples.
-    create_sub_totals( $worksheet3 );
-    
-    
-    ###############################################################################
-    #
-    # Example 4: Create a worksheet with outlined rows.
-    # Same as the example 1  except that the two sub-totals are collapsed.
-    
-    $worksheet4->set_row( 1, undef, undef, 1, 2 );
-    $worksheet4->set_row( 2, undef, undef, 1, 2 );
-    $worksheet4->set_row( 3, undef, undef, 1, 2 );
-    $worksheet4->set_row( 4, undef, undef, 1, 2 );
-    $worksheet4->set_row( 5, undef, undef, 1, 1, 1 );
-    
-    $worksheet4->set_row( 6,  undef, undef, 1, 2 );
-    $worksheet4->set_row( 7,  undef, undef, 1, 2 );
-    $worksheet4->set_row( 8,  undef, undef, 1, 2 );
-    $worksheet4->set_row( 9,  undef, undef, 1, 2 );
-    $worksheet4->set_row( 10, undef, undef, 1, 1, 1 );
-    
-    $worksheet4->set_row( 11, undef, undef, 0, 0, 1 );
-    
-    # Write the sub-total data that is common to the row examples.
-    create_sub_totals( $worksheet4 );
-    
-    
-    ###############################################################################
-    #
-    # Example 5: Create a worksheet with outlined columns.
-    #
-    my $data = [
-        [ 'Month', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Total' ],
-        [ 'North', 50,    20,    15,    25,    65,    80,,   '=SUM(B2:G2)' ],
-        [ 'South', 10,    20,    30,    50,    50,    50,,   '=SUM(B3:G3)' ],
-        [ 'East',  45,    75,    50,    15,    75,    100,,  '=SUM(B4:G4)' ],
-        [ 'West',  15,    15,    55,    35,    20,    50,,   '=SUM(B5:G6)' ],
-    ];
-    
-    # Add bold format to the first row
-    $worksheet5->set_row( 0, undef, $bold );
-    
-    # Syntax: set_column($col1, $col2, $width, $XF, $hidden, $level, $collapsed)
-    $worksheet5->set_column( 'A:A', 10, $bold );
-    $worksheet5->set_column( 'B:G', 5, undef, 0, 1 );
-    $worksheet5->set_column( 'H:H', 10 );
-    
-    # Write the data and a formula
-    $worksheet5->write_col( 'A1', $data );
-    $worksheet5->write( 'H6', '=SUM(H2:H5)', $bold );
-    
-    
-    ###############################################################################
-    #
-    # Example 6: Create a worksheet with collapsed outlined columns.
-    # This is the same as the previous example except collapsed columns.
-    
-    # Add bold format to the first row
-    $worksheet6->set_row( 0, undef, $bold );
-    
-    # Syntax: set_column($col1, $col2, $width, $XF, $hidden, $level, $collapsed)
-    $worksheet6->set_column( 'A:A', 10, $bold );
-    $worksheet6->set_column( 'B:G', 5,  undef, 1, 1 );
-    $worksheet6->set_column( 'H:H', 10, undef, 0, 0, 1 );
-    
-    # Write the data and a formula
-    $worksheet6->write_col( 'A1', $data );
-    $worksheet6->write( 'H6', '=SUM(H2:H5)', $bold );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/outline_collapsed.pl>
-
-=head2 Example: panes.pl
-
-
-
-Example of using the Excel::Writer::XLSX module to create worksheet panes.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/panes.jpg" width="640" height="420" alt="Output from panes.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # Example of using the Excel::Writer::XLSX module to create worksheet panes.
-    #
-    # reverse ('(c)'), May 2001, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook = Excel::Writer::XLSX->new( 'panes.xlsx' );
-    
-    my $worksheet1 = $workbook->add_worksheet( 'Panes 1' );
-    my $worksheet2 = $workbook->add_worksheet( 'Panes 2' );
-    my $worksheet3 = $workbook->add_worksheet( 'Panes 3' );
-    my $worksheet4 = $workbook->add_worksheet( 'Panes 4' );
-    
-    # Freeze panes
-    $worksheet1->freeze_panes( 1, 0 );    # 1 row
-    
-    $worksheet2->freeze_panes( 0, 1 );    # 1 column
-    $worksheet3->freeze_panes( 1, 1 );    # 1 row and column
-    
-    # Split panes.
-    # The divisions must be specified in terms of row and column dimensions.
-    # The default row height is 15 and the default column width is 8.43
-    #
-    $worksheet4->split_panes( 15, 8.43 );    # 1 row and column
-    
-    
-    #######################################################################
-    #
-    # Set up some formatting and text to highlight the panes
-    #
-    
-    my $header = $workbook->add_format(
-        align    => 'center',
-        valign   => 'vcenter',
-        fg_color => '#C3FFC0',
-    );
-    
-    my $center = $workbook->add_format( align => 'center' );
-    
-    
-    #######################################################################
-    #
-    # Sheet 1
-    #
-    
-    $worksheet1->set_column( 'A:I', 16 );
-    $worksheet1->set_row( 0, 20 );
-    $worksheet1->set_selection( 'C3' );
-    
-    for my $i ( 0 .. 8 ) {
-        $worksheet1->write( 0, $i, 'Scroll down', $header );
-    }
-    
-    for my $i ( 1 .. 100 ) {
-        for my $j ( 0 .. 8 ) {
-            $worksheet1->write( $i, $j, $i + 1, $center );
-        }
-    }
-    
-    
-    #######################################################################
-    #
-    # Sheet 2
-    #
-    
-    $worksheet2->set_column( 'A:A', 16 );
-    $worksheet2->set_selection( 'C3' );
-    
-    for my $i ( 0 .. 49 ) {
-        $worksheet2->set_row( $i, 15 );
-        $worksheet2->write( $i, 0, 'Scroll right', $header );
-    }
-    
-    for my $i ( 0 .. 49 ) {
-        for my $j ( 1 .. 25 ) {
-            $worksheet2->write( $i, $j, $j, $center );
-        }
-    }
-    
-    
-    #######################################################################
-    #
-    # Sheet 3
-    #
-    
-    $worksheet3->set_column( 'A:Z', 16 );
-    $worksheet3->set_selection( 'C3' );
-    
-    $worksheet3->write( 0, 0, '', $header );
-    
-    for my $i ( 1 .. 25 ) {
-        $worksheet3->write( 0, $i, 'Scroll down', $header );
-    }
-    
-    for my $i ( 1 .. 49 ) {
-        $worksheet3->write( $i, 0, 'Scroll right', $header );
-    }
-    
-    for my $i ( 1 .. 49 ) {
-        for my $j ( 1 .. 25 ) {
-            $worksheet3->write( $i, $j, $j, $center );
-        }
-    }
-    
-    
-    #######################################################################
-    #
-    # Sheet 4
-    #
-    
-    $worksheet4->set_selection( 'C3' );
-    
-    for my $i ( 1 .. 25 ) {
-        $worksheet4->write( 0, $i, 'Scroll', $center );
-    }
-    
-    for my $i ( 1 .. 49 ) {
-        $worksheet4->write( $i, 0, 'Scroll', $center );
-    }
-    
-    for my $i ( 1 .. 49 ) {
-        for my $j ( 1 .. 25 ) {
-            $worksheet4->write( $i, $j, $j, $center );
-        }
-    }
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/panes.pl>
-
-=head2 Example: properties.pl
-
-
-
-An example of adding document properties to a Excel::Writer::XLSX file.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/properties.jpg" width="640" height="420" alt="Output from properties.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # An example of adding document properties to a Excel::Writer::XLSX file.
-    #
-    # reverse ('(c)'), August 2008, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'properties.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    $workbook->set_properties(
-        title    => 'This is an example spreadsheet',
-        subject  => 'With document properties',
-        author   => 'John McNamara',
-        manager  => 'Dr. Heinz Doofenshmirtz',
-        company  => 'of Wolves',
-        category => 'Example spreadsheets',
-        keywords => 'Sample, Example, Properties',
-        comments => 'Created with Perl and Excel::Writer::XLSX',
-        status   => 'Quo',
-    );
-    
-    
-    $worksheet->set_column( 'A:A', 70 );
-    $worksheet->write( 'A1', qq{Select 'Office Button -> Prepare -> Properties' to see the file properties.} );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/properties.pl>
-
-=head2 Example: protection.pl
-
-
-
-Example of cell locking and formula hiding in an Excel worksheet via
-the Excel::Writer::XLSX module.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/protection.jpg" width="640" height="420" alt="Output from protection.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ########################################################################
-    #
-    # Example of cell locking and formula hiding in an Excel worksheet via
-    # the Excel::Writer::XLSX module.
-    #
-    # reverse ('(c)'), August 2001, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'protection.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    # Create some format objects
-    my $unlocked = $workbook->add_format( locked => 0 );
-    my $hidden   = $workbook->add_format( hidden => 1 );
-    
-    # Format the columns
-    $worksheet->set_column( 'A:A', 45 );
-    $worksheet->set_selection( 'B3' );
-    
-    # Protect the worksheet
-    $worksheet->protect();
-    
-    # Examples of cell locking and hiding.
-    $worksheet->write( 'A1', 'Cell B1 is locked. It cannot be edited.' );
-    $worksheet->write_formula( 'B1', '=1+2', undef, 3 );    # Locked by default.
-    
-    $worksheet->write( 'A2', 'Cell B2 is unlocked. It can be edited.' );
-    $worksheet->write_formula( 'B2', '=1+2', $unlocked, 3 );
-    
-    $worksheet->write( 'A3', "Cell B3 is hidden. The formula isn't visible." );
-    $worksheet->write_formula( 'B3', '=1+2', $hidden, 3 );
-    
-    $worksheet->write( 'A5', 'Use Menu->Tools->Protection->Unprotect Sheet' );
-    $worksheet->write( 'A6', 'to remove the worksheet protection.' );
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/protection.pl>
-
-=head2 Example: rich_strings.pl
-
-
-
-An Excel::Writer::XLSX example showing how to use "rich strings", i.e.,
-strings with multiple formatting.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/rich_strings.jpg" width="640" height="420" alt="Output from rich_strings.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # An Excel::Writer::XLSX example showing how to use "rich strings", i.e.,
-    # strings with multiple formatting.
-    #
-    # reverse ('(c)'), February 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'rich_strings.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    $worksheet->set_column( 'A:A', 30 );
-    
-    # Set some formats to use.
-    my $bold   = $workbook->add_format( bold        => 1 );
-    my $italic = $workbook->add_format( italic      => 1 );
-    my $red    = $workbook->add_format( color       => 'red' );
-    my $blue   = $workbook->add_format( color       => 'blue' );
-    my $center = $workbook->add_format( align       => 'center' );
-    my $super  = $workbook->add_format( font_script => 1 );
-    
-    
-    # Write some strings with multiple formats.
-    $worksheet->write_rich_string( 'A1',
-        'This is ', $bold, 'bold', ' and this is ', $italic, 'italic' );
-    
-    $worksheet->write_rich_string( 'A3',
-        'This is ', $red, 'red', ' and this is ', $blue, 'blue' );
-    
-    $worksheet->write_rich_string( 'A5',
-        'Some ', $bold, 'bold text', ' centered', $center );
-    
-    $worksheet->write_rich_string( 'A7',
-        $italic, 'j = k', $super, '(n-1)', $center );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/rich_strings.pl>
-
-=head2 Example: right_to_left.pl
-
-
-
-Example of how to change the default worksheet direction from
-left-to-right to right-to-left as required by some eastern verions
-of Excel.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/right_to_left.jpg" width="640" height="420" alt="Output from right_to_left.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # Example of how to change the default worksheet direction from
-    # left-to-right to right-to-left as required by some eastern verions
-    # of Excel.
-    #
-    # reverse ('(c)'), January 2006, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook   = Excel::Writer::XLSX->new( 'right_to_left.xlsx' );
-    my $worksheet1 = $workbook->add_worksheet();
-    my $worksheet2 = $workbook->add_worksheet();
-    
-    $worksheet2->right_to_left();
-    
-    $worksheet1->write( 0, 0, 'Hello' );    #  A1, B1, C1, ...
-    $worksheet2->write( 0, 0, 'Hello' );    # ..., C1, B1, A1
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/right_to_left.pl>
-
-=head2 Example: sales.pl
-
-
-
-Example of a sales worksheet to demonstrate several different features.
-Also uses functions from the L<Excel::Writer::XLSX::Utility> module.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/sales.jpg" width="640" height="420" alt="Output from sales.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # Example of a sales worksheet to demonstrate several different features.
-    # Also uses functions from the L<Excel::Writer::XLSX::Utility> module.
-    #
-    # reverse ('(c)'), October 2001, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    use Excel::Writer::XLSX::Utility;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'sales.xlsx' );
-    my $worksheet = $workbook->add_worksheet( 'May Sales' );
-    
-    
-    # Set up some formats
-    my %heading = (
-        bold     => 1,
-        pattern  => 1,
-        fg_color => '#C3FFC0',
-        border   => 1,
-        align    => 'center',
-    );
-    
-    my %total = (
-        bold       => 1,
-        top        => 1,
-        num_format => '$#,##0.00'
-    );
-    
-    my $heading      = $workbook->add_format( %heading );
-    my $total_format = $workbook->add_format( %total );
-    my $price_format = $workbook->add_format( num_format => '$#,##0.00' );
-    my $date_format  = $workbook->add_format( num_format => 'mmm d yyy' );
-    
-    
-    # Write the main headings
-    $worksheet->freeze_panes( 1 );    # Freeze the first row
-    $worksheet->write( 'A1', 'Item',     $heading );
-    $worksheet->write( 'B1', 'Quantity', $heading );
-    $worksheet->write( 'C1', 'Price',    $heading );
-    $worksheet->write( 'D1', 'Total',    $heading );
-    $worksheet->write( 'E1', 'Date',     $heading );
-    
-    # Set the column widths
-    $worksheet->set_column( 'A:A', 25 );
-    $worksheet->set_column( 'B:B', 10 );
-    $worksheet->set_column( 'C:E', 16 );
-    
-    
-    # Extract the sales data from the __DATA__ section at the end of the file.
-    # In reality this information would probably come from a database
-    my @sales;
-    
-    foreach my $line ( <DATA> ) {
-        chomp $line;
-        next if $line eq '';
-    
-        # Simple-minded processing of CSV data. Refer to the Text::CSV_XS
-        # and Text::xSV modules for a more complete CSV handling.
-        my @items = split /,/, $line;
-        push @sales, \@items;
-    }
-    
-    
-    # Write out the items from each row
-    my $row = 1;
-    foreach my $sale ( @sales ) {
-    
-        $worksheet->write( $row, 0, @$sale[0] );
-        $worksheet->write( $row, 1, @$sale[1] );
-        $worksheet->write( $row, 2, @$sale[2], $price_format );
-    
-        # Create a formula like '=B2*C2'
-        my $formula =
-          '=' . xl_rowcol_to_cell( $row, 1 ) . "*" . xl_rowcol_to_cell( $row, 2 );
-    
-        $worksheet->write( $row, 3, $formula, $price_format );
-    
-        # Parse the date
-        my $date = xl_decode_date_US( @$sale[3] );
-        $worksheet->write( $row, 4, $date, $date_format );
-        $row++;
-    }
-    
-    # Create a formula to sum the totals, like '=SUM(D2:D6)'
-    my $total = '=SUM(D2:' . xl_rowcol_to_cell( $row - 1, 3 ) . ")";
-    
-    $worksheet->write( $row, 3, $total, $total_format );
-    
-    $workbook->close();
-    
-    __DATA__
-    586 card,20,125.50,5/12/01
-    Flat Screen Monitor,1,1300.00,5/12/01
-    64 MB dimms,45,49.99,5/13/01
-    15 GB HD,12,300.00,5/13/01
-    Speakers (pair),5,15.50,5/14/01
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/sales.pl>
-
-=head2 Example: shape1.pl
-
-
-
-A simple example of how to use the Excel::Writer::XLSX module to
-add shapes to an Excel xlsx file.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/shape1.jpg" width="640" height="420" alt="Output from shape1.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A simple example of how to use the Excel::Writer::XLSX module to
-    # add shapes to an Excel xlsx file.
-    #
-    # reverse ('(c)'), May 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'shape1.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    # Add a circle, with centered text.
-    my $ellipse = $workbook->add_shape(
-        type   => 'ellipse',
-        text   => "Hello\nWorld",
-        width  => 60,
-        height => 60
-    );
-    
-    $worksheet->insert_shape( 'A1', $ellipse, 50, 50 );
-    
-    # Add a plus sign.
-    my $plus = $workbook->add_shape( type => 'plus', width => 20, height => 20 );
-    $worksheet->insert_shape( 'D8', $plus );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/shape1.pl>
-
-=head2 Example: shape2.pl
-
-
-
-A simple example of how to use the Excel::Writer::XLSX module to
-modify shape properties in an Excel xlsx file.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/shape2.jpg" width="640" height="420" alt="Output from shape2.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A simple example of how to use the Excel::Writer::XLSX module to
-    # modify shape properties in an Excel xlsx file.
-    #
-    # reverse ('(c)'), May 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'shape2.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    $worksheet->hide_gridlines( 2 );
-    
-    my $plain = $workbook->add_shape(
-        type   => 'smileyFace',
-        text   => "Plain",
-        width  => 100,
-        height => 100,
-    );
-    
-    my $bbformat = $workbook->add_format(
-        color => 'red',
-        font  => 'Lucida Calligraphy',
-    );
-    
-    $bbformat->set_bold();
-    $bbformat->set_underline();
-    $bbformat->set_italic();
-    
-    my $decor = $workbook->add_shape(
-        type        => 'smileyFace',
-        text        => "Decorated",
-        rotation    => 45,
-        width       => 200,
-        height      => 100,
-        format      => $bbformat,
-        line_type   => 'sysDot',
-        line_weight => 3,
-        fill        => 'FFFF00',
-        line        => '3366FF',
-    );
-    
-    $worksheet->insert_shape( 'A1', $plain, 50,  50 );
-    $worksheet->insert_shape( 'A1', $decor, 250, 50 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/shape2.pl>
-
-=head2 Example: shape3.pl
-
-
-
-A simple example of how to use the Excel::Writer::XLSX module to
-scale shapes in an Excel xlsx file.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/shape3.jpg" width="640" height="420" alt="Output from shape3.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A simple example of how to use the Excel::Writer::XLSX module to
-    # scale shapes in an Excel xlsx file.
-    #
-    # reverse ('(c)'), May 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'shape3.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    my $normal = $workbook->add_shape(
-        name   => 'chip',
-        type   => 'diamond',
-        text   => "Normal",
-        width  => 100,
-        height => 100,
-    );
-    
-    $worksheet->insert_shape( 'A1', $normal, 50, 50 );
-    $normal->set_text( 'Scaled 3w x 2h' );
-    $normal->set_name( 'Hope' );
-    $worksheet->insert_shape( 'A1', $normal, 250, 50, 3, 2 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/shape3.pl>
-
-=head2 Example: shape4.pl
-
-
-
-A simple example of how to use the Excel::Writer::XLSX module to
-demonstrate stenciling in an Excel xlsx file.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/shape4.jpg" width="640" height="420" alt="Output from shape4.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A simple example of how to use the Excel::Writer::XLSX module to
-    # demonstrate stenciling in an Excel xlsx file.
-    #
-    # reverse ('(c)'), May 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'shape4.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    $worksheet->hide_gridlines( 2 );
-    
-    my $type  = 'rect';
-    my $shape = $workbook->add_shape(
-        type   => $type,
-        width  => 90,
-        height => 90,
-    );
-    
-    for my $n ( 1 .. 10 ) {
-    
-        # Change the last 5 rectangles to stars. Previously inserted shapes stay
-        # as rectangles.
-        $type = 'star5' if $n == 6;
-        $shape->set_type( $type );
-        $shape->set_text( "$type $n" );
-        $worksheet->insert_shape( 'A1', $shape, $n * 100, 50 );
-    }
-    
-    
-    my $stencil = $workbook->add_shape(
-        stencil => 1,                    # The default.
-        width   => 90,
-        height  => 90,
-        text    => 'started as a box',
-    );
-    $worksheet->insert_shape( 'A1', $stencil, 100, 150 );
-    
-    $stencil->set_stencil( 0 );
-    $worksheet->insert_shape( 'A1', $stencil, 200, 150 );
-    $worksheet->insert_shape( 'A1', $stencil, 300, 150 );
-    
-    # Ooops!  Changed my mind.  Change the rectangle to an ellipse (circle),
-    # for the last two shapes.
-    $stencil->set_type( 'ellipse' );
-    $stencil->set_text( 'Now its a circle' );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/shape4.pl>
-
-=head2 Example: shape5.pl
-
-
-
-A simple example of how to use the Excel::Writer::XLSX module to
-add shapes (objects and top/bottom connectors) to an Excel xlsx file.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/shape5.jpg" width="640" height="420" alt="Output from shape5.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A simple example of how to use the Excel::Writer::XLSX module to
-    # add shapes (objects and top/bottom connectors) to an Excel xlsx file.
-    #
-    # reverse ('(c)'), May 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'shape5.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    my $s1 = $workbook->add_shape( type => 'ellipse', width => 60, height => 60 );
-    $worksheet->insert_shape( 'A1', $s1, 50, 50 );
-    
-    my $s2 = $workbook->add_shape( type => 'plus', width => 20, height => 20 );
-    $worksheet->insert_shape( 'A1', $s2, 250, 200 );
-    
-    # Create a connector to link the two shapes.
-    my $cxn_shape = $workbook->add_shape( type => 'bentConnector3' );
-    
-    # Link the start of the connector to the right side.
-    $cxn_shape->set_start( $s1->get_id() );
-    $cxn_shape->set_start_index( 4 );  # 4th connection pt, clockwise from top(0).
-    $cxn_shape->set_start_side( 'b' ); # r)ight or b)ottom.
-    
-    # Link the end of the connector to the left side.
-    $cxn_shape->set_end( $s2->get_id() );
-    $cxn_shape->set_end_index( 0 );     # clockwise from top(0).
-    $cxn_shape->set_end_side( 't' );    # t)top.
-    
-    $worksheet->insert_shape( 'A1', $cxn_shape, 0, 0 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/shape5.pl>
-
-=head2 Example: shape6.pl
-
-
-
-A simple example of how to use the Excel::Writer::XLSX module to
-add shapes (objects and right/left connectors) to an Excel xlsx file.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/shape6.jpg" width="640" height="420" alt="Output from shape6.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A simple example of how to use the Excel::Writer::XLSX module to
-    # add shapes (objects and right/left connectors) to an Excel xlsx file.
-    #
-    # reverse ('(c)'), May 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'shape6.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    my $s1 = $workbook->add_shape( type => 'chevron', width => 60, height => 60 );
-    $worksheet->insert_shape( 'A1', $s1, 50, 50 );
-    
-    my $s2 = $workbook->add_shape( type => 'pentagon', width => 20, height => 20 );
-    $worksheet->insert_shape( 'A1', $s2, 250, 200 );
-    
-    # Create a connector to link the two shapes.
-    my $cxn_shape = $workbook->add_shape( type => 'curvedConnector3' );
-    
-    # Link the start of the connector to the right side.
-    $cxn_shape->set_start( $s1->get_id() );
-    $cxn_shape->set_start_index( 2 );    # 2nd connection pt, clockwise from top(0).
-    $cxn_shape->set_start_side( 'r' );   # r)ight or b)ottom.
-    
-    # Link the end of the connector to the left side.
-    $cxn_shape->set_end( $s2->get_id() );
-    $cxn_shape->set_end_index( 4 );      # 4th connection pt, clockwise from top(0).
-    $cxn_shape->set_end_side( 'l' );     # l)eft or t)op.
-    
-    $worksheet->insert_shape( 'A1', $cxn_shape, 0, 0 );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/shape6.pl>
-
-=head2 Example: shape7.pl
-
-
-
-A simple example of how to use the Excel::Writer::XLSX module to
-add shapes and one-to-many connectors to an Excel xlsx file.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/shape7.jpg" width="640" height="420" alt="Output from shape7.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A simple example of how to use the Excel::Writer::XLSX module to
-    # add shapes and one-to-many connectors to an Excel xlsx file.
-    #
-    # reverse ('(c)'), May 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'shape7.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    # Add a circle, with centered text. c is for circle, not center.
-    my $cw = 60;
-    my $ch = 60;
-    my $cx = 210;
-    my $cy = 190;
-    
-    my $ellipse = $workbook->add_shape(
-        type   => 'ellipse',
-        id     => 2,
-        text   => "Hello\nWorld",
-        width  => $cw,
-        height => $ch
-    );
-    $worksheet->insert_shape( 'A1', $ellipse, $cx, $cy );
-    
-    # Add a plus sign at 4 different positions around the circle.
-    my $pw = 20;
-    my $ph = 20;
-    my $px = 120;
-    my $py = 250;
-    my $plus =
-      $workbook->add_shape( type => 'plus', id => 3, width => $pw, height => $ph );
-    my $p1 = $worksheet->insert_shape( 'A1', $plus, 350, 350 );
-    my $p2 = $worksheet->insert_shape( 'A1', $plus, 150, 350 );
-    my $p3 = $worksheet->insert_shape( 'A1', $plus, 350, 150 );
-    $plus->set_adjustments( 35 );    # change shape of plus symbol.
-    my $p4 = $worksheet->insert_shape( 'A1', $plus, 150, 150 );
-    
-    my $cxn_shape = $workbook->add_shape( type => 'bentConnector3', fill => 0 );
-    
-    $cxn_shape->set_start( $ellipse->get_id() );
-    $cxn_shape->set_start_index( 4 );    # 4nd connection pt, clockwise from top(0).
-    $cxn_shape->set_start_side( 'b' );   # r)ight or b)ottom.
-    
-    $cxn_shape->set_end( $p1->get_id() );
-    $cxn_shape->set_end_index( 0 );
-    $cxn_shape->set_end_side( 't' );
-    $worksheet->insert_shape( 'A1', $cxn_shape, 0, 0 );
-    
-    $cxn_shape->set_end( $p2->get_id() );
-    $worksheet->insert_shape( 'A1', $cxn_shape, 0, 0 );
-    
-    $cxn_shape->set_end( $p3->get_id() );
-    $worksheet->insert_shape( 'A1', $cxn_shape, 0, 0 );
-    
-    $cxn_shape->set_end( $p4->get_id() );
-    $cxn_shape->set_adjustments( -50, 45, 120 );
-    $worksheet->insert_shape( 'A1', $cxn_shape, 0, 0 );
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/shape7.pl>
-
-=head2 Example: shape8.pl
-
-
-
-A simple example of how to use the Excel::Writer::XLSX module to
-add shapes and one-to-many connectors to an Excel xlsx file.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/shape8.jpg" width="640" height="420" alt="Output from shape8.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A simple example of how to use the Excel::Writer::XLSX module to
-    # add shapes and one-to-many connectors to an Excel xlsx file.
-    #
-    # reverse ('(c)'), May 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'shape8.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    # Add a circle, with centered text. c is for circle, not center.
-    my $cw = 60;
-    my $ch = 60;
-    my $cx = 210;
-    my $cy = 190;
-    
-    my $ellipse = $workbook->add_shape(
-        type   => 'ellipse',
-        id     => 2,
-        text   => "Hello\nWorld",
-        width  => $cw,
-        height => $ch
-    );
-    $worksheet->insert_shape( 'A1', $ellipse, $cx, $cy );
-    
-    # Add a plus sign at 4 different positions around the circle.
-    my $pw = 20;
-    my $ph = 20;
-    my $px = 120;
-    my $py = 250;
-    my $plus =
-      $workbook->add_shape( type => 'plus', id => 3, width => $pw, height => $ph );
-    my $p1 = $worksheet->insert_shape( 'A1', $plus, 350, 150 );    #  2:00
-    my $p2 = $worksheet->insert_shape( 'A1', $plus, 350, 350 );    #  4:00
-    my $p3 = $worksheet->insert_shape( 'A1', $plus, 150, 350 );    #  8:00
-    my $p4 = $worksheet->insert_shape( 'A1', $plus, 150, 150 );    # 10:00
-    
-    my $cxn_shape = $workbook->add_shape( type => 'bentConnector3', fill => 0 );
-    
-    $cxn_shape->set_start( $ellipse->get_id() );
-    $cxn_shape->set_start_index( 2 );    # 2nd connection pt, clockwise from top(0).
-    $cxn_shape->set_start_side( 'r' );   # r)ight or b)ottom.
-    
-    $cxn_shape->set_end( $p1->get_id() );
-    $cxn_shape->set_end_index( 3 );      # 3rd connection point on plus, right side
-    $cxn_shape->set_end_side( 'l' );
-    $worksheet->insert_shape( 'A1', $cxn_shape, 0, 0 );
-    
-    $cxn_shape->set_end( $p2->get_id() );
-    $worksheet->insert_shape( 'A1', $cxn_shape, 0, 0 );
-    
-    $cxn_shape->set_end( $p3->get_id() );
-    $worksheet->insert_shape( 'A1', $cxn_shape, 0, 0 );
-    
-    $cxn_shape->set_end( $p4->get_id() );
-    $cxn_shape->set_adjustments( -50, 45, 120 );
-    $worksheet->insert_shape( 'A1', $cxn_shape, 0, 0 );
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/shape8.pl>
-
-=head2 Example: shape_all.pl
-
-
-
-A simple example of how to use the Excel::Writer::XLSX module to
-add all shapes (as currently implemented) to an Excel xlsx file.
-
-The list at the end consists of all the shape types defined as
-ST_ShapeType in ECMA-376, Office Open XML File Formats Part 4.
-
-The grouping by worksheet name is for illustration only. It isn't
-part of the ECMA-376 standard.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/shape_all.jpg" width="640" height="420" alt="Output from shape_all.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # A simple example of how to use the Excel::Writer::XLSX module to
-    # add all shapes (as currently implemented) to an Excel xlsx file.
-    #
-    # The list at the end consists of all the shape types defined as
-    # ST_ShapeType in ECMA-376, Office Open XML File Formats Part 4.
-    #
-    # The grouping by worksheet name is for illustration only. It isn't
-    # part of the ECMA-376 standard.
-    #
-    # reverse ('(c)'), May 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook = Excel::Writer::XLSX->new( 'shape_all.xlsx' );
-    
-    my ( $worksheet, $last_sheet, $shape, $r ) = ( 0, '', '', undef, 0 );
-    
-    while ( <DATA> ) {
-        chomp;
-        next unless m/^\w/;    # Skip blank lines and comments.
-    
-        my ( $sheet, $name ) = split( /\t/, $_ );
-        if ( $last_sheet ne $sheet ) {
-            $worksheet = $workbook->add_worksheet( $sheet );
-            $r         = 2;
-        }
-        $last_sheet = $sheet;
-        $shape      = $workbook->add_shape(
-            type   => $name,
-            text   => $name,
-            width  => 90,
-            height => 90
-        );
-    
-        # Connectors can not have labels, so write the connector name in the cell
-        # to the left.
-        $worksheet->write( $r, 0, $name ) if $sheet eq 'Connector';
-        $worksheet->insert_shape( $r, 2, $shape, 0, 0 );
-        $r += 5;
-    }
-    
-    $workbook->close();
-    
-    __END__
-    Action     actionButtonBackPrevious
-    Action     actionButtonBeginning
-    Action     actionButtonBlank
-    Action     actionButtonDocument
-    Action     actionButtonEnd
-    Action     actionButtonForwardNext
-    Action     actionButtonHelp
-    Action     actionButtonHome
-    Action     actionButtonInformation
-    Action     actionButtonMovie
-    Action     actionButtonReturn
-    Action     actionButtonSound
-    Arrow      bentArrow
-    Arrow      bentUpArrow
-    Arrow      circularArrow
-    Arrow      curvedDownArrow
-    Arrow      curvedLeftArrow
-    Arrow      curvedRightArrow
-    Arrow      curvedUpArrow
-    Arrow      downArrow
-    Arrow      leftArrow
-    Arrow      leftCircularArrow
-    Arrow      leftRightArrow
-    Arrow      leftRightCircularArrow
-    Arrow      leftRightUpArrow
-    Arrow      leftUpArrow
-    Arrow      notchedRightArrow
-    Arrow      quadArrow
-    Arrow      rightArrow
-    Arrow      stripedRightArrow
-    Arrow      swooshArrow
-    Arrow      upArrow
-    Arrow      upDownArrow
-    Arrow      uturnArrow
-    Basic      blockArc
-    Basic      can
-    Basic      chevron
-    Basic      cube
-    Basic      decagon
-    Basic      diamond
-    Basic      dodecagon
-    Basic      donut
-    Basic      ellipse
-    Basic      funnel
-    Basic      gear6
-    Basic      gear9
-    Basic      heart
-    Basic      heptagon
-    Basic      hexagon
-    Basic      homePlate
-    Basic      lightningBolt
-    Basic      line
-    Basic      lineInv
-    Basic      moon
-    Basic      nonIsoscelesTrapezoid
-    Basic      noSmoking
-    Basic      octagon
-    Basic      parallelogram
-    Basic      pentagon
-    Basic      pie
-    Basic      pieWedge
-    Basic      plaque
-    Basic      rect
-    Basic      round1Rect
-    Basic      round2DiagRect
-    Basic      round2SameRect
-    Basic      roundRect
-    Basic      rtTriangle
-    Basic      smileyFace
-    Basic      snip1Rect
-    Basic      snip2DiagRect
-    Basic      snip2SameRect
-    Basic      snipRoundRect
-    Basic      star10
-    Basic      star12
-    Basic      star16
-    Basic      star24
-    Basic      star32
-    Basic      star4
-    Basic      star5
-    Basic      star6
-    Basic      star7
-    Basic      star8
-    Basic      sun
-    Basic      teardrop
-    Basic      trapezoid
-    Basic      triangle
-    Callout    accentBorderCallout1
-    Callout    accentBorderCallout2
-    Callout    accentBorderCallout3
-    Callout    accentCallout1
-    Callout    accentCallout2
-    Callout    accentCallout3
-    Callout    borderCallout1
-    Callout    borderCallout2
-    Callout    borderCallout3
-    Callout    callout1
-    Callout    callout2
-    Callout    callout3
-    Callout    cloudCallout
-    Callout    downArrowCallout
-    Callout    leftArrowCallout
-    Callout    leftRightArrowCallout
-    Callout    quadArrowCallout
-    Callout    rightArrowCallout
-    Callout    upArrowCallout
-    Callout    upDownArrowCallout
-    Callout    wedgeEllipseCallout
-    Callout    wedgeRectCallout
-    Callout    wedgeRoundRectCallout
-    Chart      chartPlus
-    Chart      chartStar
-    Chart      chartX
-    Connector  bentConnector2
-    Connector  bentConnector3
-    Connector  bentConnector4
-    Connector  bentConnector5
-    Connector  curvedConnector2
-    Connector  curvedConnector3
-    Connector  curvedConnector4
-    Connector  curvedConnector5
-    Connector  straightConnector1
-    FlowChart  flowChartAlternateProcess
-    FlowChart  flowChartCollate
-    FlowChart  flowChartConnector
-    FlowChart  flowChartDecision
-    FlowChart  flowChartDelay
-    FlowChart  flowChartDisplay
-    FlowChart  flowChartDocument
-    FlowChart  flowChartExtract
-    FlowChart  flowChartInputOutput
-    FlowChart  flowChartInternalStorage
-    FlowChart  flowChartMagneticDisk
-    FlowChart  flowChartMagneticDrum
-    FlowChart  flowChartMagneticTape
-    FlowChart  flowChartManualInput
-    FlowChart  flowChartManualOperation
-    FlowChart  flowChartMerge
-    FlowChart  flowChartMultidocument
-    FlowChart  flowChartOfflineStorage
-    FlowChart  flowChartOffpageConnector
-    FlowChart  flowChartOnlineStorage
-    FlowChart  flowChartOr
-    FlowChart  flowChartPredefinedProcess
-    FlowChart  flowChartPreparation
-    FlowChart  flowChartProcess
-    FlowChart  flowChartPunchedCard
-    FlowChart  flowChartPunchedTape
-    FlowChart  flowChartSort
-    FlowChart  flowChartSummingJunction
-    FlowChart  flowChartTerminator
-    Math       mathDivide
-    Math       mathEqual
-    Math       mathMinus
-    Math       mathMultiply
-    Math       mathNotEqual
-    Math       mathPlus
-    Star_Banner        arc
-    Star_Banner        bevel
-    Star_Banner        bracePair
-    Star_Banner        bracketPair
-    Star_Banner        chord
-    Star_Banner        cloud
-    Star_Banner        corner
-    Star_Banner        diagStripe
-    Star_Banner        doubleWave
-    Star_Banner        ellipseRibbon
-    Star_Banner        ellipseRibbon2
-    Star_Banner        foldedCorner
-    Star_Banner        frame
-    Star_Banner        halfFrame
-    Star_Banner        horizontalScroll
-    Star_Banner        irregularSeal1
-    Star_Banner        irregularSeal2
-    Star_Banner        leftBrace
-    Star_Banner        leftBracket
-    Star_Banner        leftRightRibbon
-    Star_Banner        plus
-    Star_Banner        ribbon
-    Star_Banner        ribbon2
-    Star_Banner        rightBrace
-    Star_Banner        rightBracket
-    Star_Banner        verticalScroll
-    Star_Banner        wave
-    Tabs       cornerTabs
-    Tabs       plaqueTabs
-    Tabs       squareTabs
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/shape_all.pl>
-
-=head2 Example: sparklines1.pl
-
-
-
-Example of how to add sparklines to an Excel::Writer::XLSX file.
-
-Sparklines are small charts that fit in a single cell and are
-used to show trends in data. See sparklines2.pl for examples
-of more complex sparkline formatting.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/sparklines1.jpg" width="640" height="420" alt="Output from sparklines1.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to add sparklines to an Excel::Writer::XLSX file.
-    #
-    # Sparklines are small charts that fit in a single cell and are
-    # used to show trends in data. See sparklines2.pl for examples
-    # of more complex sparkline formatting.
-    #
-    # reverse ('(c)'), November 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'sparklines1.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    # Some sample data to plot.
-    my $data = [
-    
-        [ -2, 2,  3,  -1, 0 ],
-        [ 30, 20, 33, 20, 15 ],
-        [ 1,  -1, -1, 1,  -1 ],
-    
-    ];
-    
-    # Write the sample data to the worksheet.
-    $worksheet->write_col( 'A1', $data );
-    
-    
-    # Add a line sparkline (the default) with markers.
-    $worksheet->add_sparkline(
-        {
-            location => 'F1',
-            range    => 'Sheet1!A1:E1',
-            markers  => 1,
-        }
-    );
-    
-    # Add a column sparkline with non-default style.
-    $worksheet->add_sparkline(
-        {
-            location => 'F2',
-            range    => 'Sheet1!A2:E2',
-            type     => 'column',
-            style    => 12,
-        }
-    );
-    
-    # Add a win/loss sparkline with negative values highlighted.
-    $worksheet->add_sparkline(
-        {
-            location        => 'F3',
-            range           => 'Sheet1!A3:E3',
-            type            => 'win_loss',
-            negative_points => 1,
-        }
-    );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/sparklines1.pl>
-
-=head2 Example: sparklines2.pl
-
-
-
-Example of how to add sparklines to an Excel::Writer::XLSX file.
-
-Sparklines are small charts that fit in a single cell and are
-used to show trends in data. This example shows the majority of
-options that can be applied to sparklines.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/sparklines2.jpg" width="640" height="420" alt="Output from sparklines2.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to add sparklines to an Excel::Writer::XLSX file.
-    #
-    # Sparklines are small charts that fit in a single cell and are
-    # used to show trends in data. This example shows the majority of
-    # options that can be applied to sparklines.
-    #
-    # reverse ('(c)'), November 2011, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook    = Excel::Writer::XLSX->new( 'sparklines2.xlsx' );
-    my $worksheet1  = $workbook->add_worksheet();
-    my $worksheet2  = $workbook->add_worksheet();
-    my $bold        = $workbook->add_format( bold => 1 );
-    my $str;
-    my $row = 1;
-    
-    # Set the columns widths to make the output clearer.
-    $worksheet1->set_column( 'A:A', 14 );
-    $worksheet1->set_column( 'B:B', 50 );
-    $worksheet1->set_zoom( 150 );
-    
-    # Headings.
-    $worksheet1->write( 'A1', 'Sparkline',   $bold );
-    $worksheet1->write( 'B1', 'Description', $bold );
-    
-    
-    ###############################################################################
-    #
-    $str = 'A default "line" sparkline.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A2',
-            range    => 'Sheet2!A1:J1',
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'A default "column" sparkline.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A3',
-            range    => 'Sheet2!A2:J2',
-            type     => 'column',
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'A default "win/loss" sparkline.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A4',
-            range    => 'Sheet2!A3:J3',
-            type     => 'win_loss',
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    $row++;
-    
-    
-    ###############################################################################
-    #
-    $str = 'Line with markers.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A6',
-            range    => 'Sheet2!A1:J1',
-            markers  => 1,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Line with high and low points.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location   => 'A7',
-            range      => 'Sheet2!A1:J1',
-            high_point => 1,
-            low_point  => 1,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Line with first and last point markers.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location    => 'A8',
-            range       => 'Sheet2!A1:J1',
-            first_point => 1,
-            last_point  => 1,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Line with negative point markers.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location        => 'A9',
-            range           => 'Sheet2!A1:J1',
-            negative_points => 1,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Line with axis.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A10',
-            range    => 'Sheet2!A1:J1',
-            axis     => 1,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    $row++;
-    
-    
-    ###############################################################################
-    #
-    $str = 'Column with default style (1).';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A12',
-            range    => 'Sheet2!A2:J2',
-            type     => 'column',
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Column with style 2.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A13',
-            range    => 'Sheet2!A2:J2',
-            type     => 'column',
-            style    => 2,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Column with style 3.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A14',
-            range    => 'Sheet2!A2:J2',
-            type     => 'column',
-            style    => 3,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Column with style 4.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A15',
-            range    => 'Sheet2!A2:J2',
-            type     => 'column',
-            style    => 4,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Column with style 5.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A16',
-            range    => 'Sheet2!A2:J2',
-            type     => 'column',
-            style    => 5,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Column with style 6.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A17',
-            range    => 'Sheet2!A2:J2',
-            type     => 'column',
-            style    => 6,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Column with a user defined colour.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location     => 'A18',
-            range        => 'Sheet2!A2:J2',
-            type         => 'column',
-            series_color => '#E965E0',
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    $row++;
-    
-    
-    ###############################################################################
-    #
-    $str = 'A win/loss sparkline.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A20',
-            range    => 'Sheet2!A3:J3',
-            type     => 'win_loss',
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'A win/loss sparkline with negative points highlighted.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location        => 'A21',
-            range           => 'Sheet2!A3:J3',
-            type            => 'win_loss',
-            negative_points => 1,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    $row++;
-    
-    
-    ###############################################################################
-    #
-    $str = 'A left to right column (the default).';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A23',
-            range    => 'Sheet2!A4:J4',
-            type     => 'column',
-            style    => 20,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'A right to left column.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A24',
-            range    => 'Sheet2!A4:J4',
-            type     => 'column',
-            style    => 20,
-            reverse  => 1,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    ###############################################################################
-    #
-    $str = 'Sparkline and text in one cell.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => 'A25',
-            range    => 'Sheet2!A4:J4',
-            type     => 'column',
-            style    => 20,
-        }
-    );
-    
-    $worksheet1->write( $row,   0, 'Growth' );
-    $worksheet1->write( $row++, 1, $str );
-    $row++;
-    
-    
-    ###############################################################################
-    #
-    $str = 'A grouped sparkline. Changes are applied to all three.';
-    
-    $worksheet1->add_sparkline(
-        {
-            location => [ 'A27',          'A28',          'A29' ],
-            range    => [ 'Sheet2!A5:J5', 'Sheet2!A6:J6', 'Sheet2!A7:J7' ],
-            markers  => 1,
-        }
-    );
-    
-    $worksheet1->write( $row++, 1, $str );
-    
-    
-    
-    
-    ###############################################################################
-    #
-    # Create a second worksheet with data to plot.
-    #
-    
-    $worksheet2->set_column( 'A:J', 11 );
-    
-    my $data = [
-    
-        # Simple line data.
-        [ -2, 2, 3, -1, 0, -2, 3, 2, 1, 0 ],
-    
-        # Simple column data.
-        [ 30, 20, 33, 20, 15, 5, 5, 15, 10, 15 ],
-    
-        # Simple win/loss data.
-        [ 1, 1, -1, -1, 1, -1, 1, 1, 1, -1 ],
-    
-        # Unbalanced histogram.
-        [ 5, 6, 7, 10, 15, 20, 30, 50, 70, 100 ],
-    
-        # Data for the grouped sparkline example.
-        [ -2, 2,  3, -1, 0, -2, 3, 2, 1, 0 ],
-        [ 3,  -1, 0, -2, 3, 2,  1, 0, 2, 1 ],
-        [ 0,  -2, 3, 2,  1, 0,  1, 2, 3, 1 ],
-    
-    
-    ];
-    
-    # Write the sample data to the worksheet.
-    $worksheet2->write_col( 'A1', $data );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/sparklines2.pl>
-
-=head2 Example: stats_ext.pl
-
-
-
-Example of formatting using the Excel::Writer::XLSX module
-
-This is a simple example of how to use functions that reference cells in
-other worksheets within the same workbook.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/stats_ext.jpg" width="640" height="420" alt="Output from stats_ext.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # Example of formatting using the Excel::Writer::XLSX module
-    #
-    # This is a simple example of how to use functions that reference cells in
-    # other worksheets within the same workbook.
-    #
-    # reverse ('(c)'), March 2001, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook   = Excel::Writer::XLSX->new( 'stats_ext.xlsx' );
-    my $worksheet1 = $workbook->add_worksheet( 'Test results' );
-    my $worksheet2 = $workbook->add_worksheet( 'Data' );
-    
-    # Set the column width for columns 1
-    $worksheet1->set_column( 'A:A', 20 );
-    
-    
-    # Create a format for the headings
-    my $heading = $workbook->add_format();
-    $heading->set_bold();
-    
-    # Create a numerical format
-    my $numformat = $workbook->add_format();
-    $numformat->set_num_format( '0.00' );
-    
-    
-    # Write some statistical functions
-    $worksheet1->write( 'A1', 'Count', $heading );
-    $worksheet1->write( 'B1', '=COUNT(Data!B2:B9)' );
-    
-    $worksheet1->write( 'A2', 'Sum', $heading );
-    $worksheet1->write( 'B2', '=SUM(Data!B2:B9)' );
-    
-    $worksheet1->write( 'A3', 'Average', $heading );
-    $worksheet1->write( 'B3', '=AVERAGE(Data!B2:B9)' );
-    
-    $worksheet1->write( 'A4', 'Min', $heading );
-    $worksheet1->write( 'B4', '=MIN(Data!B2:B9)' );
-    
-    $worksheet1->write( 'A5', 'Max', $heading );
-    $worksheet1->write( 'B5', '=MAX(Data!B2:B9)' );
-    
-    $worksheet1->write( 'A6', 'Standard Deviation', $heading );
-    $worksheet1->write( 'B6', '=STDEV(Data!B2:B9)' );
-    
-    $worksheet1->write( 'A7', 'Kurtosis', $heading );
-    $worksheet1->write( 'B7', '=KURT(Data!B2:B9)' );
-    
-    
-    # Write the sample data
-    $worksheet2->write( 'A1', 'Sample', $heading );
-    $worksheet2->write( 'A2', 1 );
-    $worksheet2->write( 'A3', 2 );
-    $worksheet2->write( 'A4', 3 );
-    $worksheet2->write( 'A5', 4 );
-    $worksheet2->write( 'A6', 5 );
-    $worksheet2->write( 'A7', 6 );
-    $worksheet2->write( 'A8', 7 );
-    $worksheet2->write( 'A9', 8 );
-    
-    $worksheet2->write( 'B1', 'Length', $heading );
-    $worksheet2->write( 'B2', 25.4,     $numformat );
-    $worksheet2->write( 'B3', 25.4,     $numformat );
-    $worksheet2->write( 'B4', 24.8,     $numformat );
-    $worksheet2->write( 'B5', 25.0,     $numformat );
-    $worksheet2->write( 'B6', 25.3,     $numformat );
-    $worksheet2->write( 'B7', 24.9,     $numformat );
-    $worksheet2->write( 'B8', 25.2,     $numformat );
-    $worksheet2->write( 'B9', 24.8,     $numformat );
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/stats_ext.pl>
-
-=head2 Example: stocks.pl
-
-
-
-Example of formatting using the Excel::Writer::XLSX module
-
-This example shows how to use a conditional numerical format
-with colours to indicate if a share price has gone up or down.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/stocks.jpg" width="640" height="420" alt="Output from stocks.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # Example of formatting using the Excel::Writer::XLSX module
-    #
-    # This example shows how to use a conditional numerical format
-    # with colours to indicate if a share price has gone up or down.
-    #
-    # reverse ('(c)'), March 2001, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    # Create a new workbook and add a worksheet
-    my $workbook  = Excel::Writer::XLSX->new( 'stocks.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    # Set the column width for columns 1, 2, 3 and 4
-    $worksheet->set_column( 0, 3, 15 );
-    
-    
-    # Create a format for the column headings
-    my $header = $workbook->add_format();
-    $header->set_bold();
-    $header->set_size( 12 );
-    $header->set_color( 'blue' );
-    
-    
-    # Create a format for the stock price
-    my $f_price = $workbook->add_format();
-    $f_price->set_align( 'left' );
-    $f_price->set_num_format( '$0.00' );
-    
-    
-    # Create a format for the stock volume
-    my $f_volume = $workbook->add_format();
-    $f_volume->set_align( 'left' );
-    $f_volume->set_num_format( '#,##0' );
-    
-    
-    # Create a format for the price change. This is an example of a conditional
-    # format. The number is formatted as a percentage. If it is positive it is
-    # formatted in green, if it is negative it is formatted in red and if it is
-    # zero it is formatted as the default font colour (in this case black).
-    # Note: the [Green] format produces an unappealing lime green. Try
-    # [Color 10] instead for a dark green.
-    #
-    my $f_change = $workbook->add_format();
-    $f_change->set_align( 'left' );
-    $f_change->set_num_format( '[Green]0.0%;[Red]-0.0%;0.0%' );
-    
-    
-    # Write out the data
-    $worksheet->write( 0, 0, 'Company', $header );
-    $worksheet->write( 0, 1, 'Price',   $header );
-    $worksheet->write( 0, 2, 'Volume',  $header );
-    $worksheet->write( 0, 3, 'Change',  $header );
-    
-    $worksheet->write( 1, 0, 'Damage Inc.' );
-    $worksheet->write( 1, 1, 30.25, $f_price );       # $30.25
-    $worksheet->write( 1, 2, 1234567, $f_volume );    # 1,234,567
-    $worksheet->write( 1, 3, 0.085, $f_change );      # 8.5% in green
-    
-    $worksheet->write( 2, 0, 'Dump Corp.' );
-    $worksheet->write( 2, 1, 1.56, $f_price );        # $1.56
-    $worksheet->write( 2, 2, 7564, $f_volume );       # 7,564
-    $worksheet->write( 2, 3, -0.015, $f_change );     # -1.5% in red
-    
-    $worksheet->write( 3, 0, 'Rev Ltd.' );
-    $worksheet->write( 3, 1, 0.13, $f_price );        # $0.13
-    $worksheet->write( 3, 2, 321, $f_volume );        # 321
-    $worksheet->write( 3, 3, 0, $f_change );          # 0 in the font color (black)
-    
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/stocks.pl>
-
-=head2 Example: tab_colors.pl
-
-
-
-Example of how to set Excel worksheet tab colours.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/tab_colors.jpg" width="640" height="420" alt="Output from tab_colors.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    #######################################################################
-    #
-    # Example of how to set Excel worksheet tab colours.
-    #
-    # reverse ('(c)'), May 2006, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'tab_colors.xlsx' );
-    
-    my $worksheet1 = $workbook->add_worksheet();
-    my $worksheet2 = $workbook->add_worksheet();
-    my $worksheet3 = $workbook->add_worksheet();
-    my $worksheet4 = $workbook->add_worksheet();
-    
-    # Worksheet1 will have the default tab colour.
-    $worksheet2->set_tab_color( 'red' );
-    $worksheet3->set_tab_color( 'green' );
-    $worksheet4->set_tab_color( '#FF6600'); # Orange
-    
-    $workbook->close();
-    
-    __END__
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/tab_colors.pl>
-
-=head2 Example: tables.pl
-
-
-
-Example of how to add tables to an Excel::Writer::XLSX worksheet.
-
-Tables in Excel are used to group rows and columns of data into a single
-structure that can be referenced in a formula or formatted collectively.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/tables.jpg" width="640" height="420" alt="Output from tables.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ###############################################################################
-    #
-    # Example of how to add tables to an Excel::Writer::XLSX worksheet.
-    #
-    # Tables in Excel are used to group rows and columns of data into a single
-    # structure that can be referenced in a formula or formatted collectively.
-    #
-    # reverse ('(c)'), September 2012, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    my $workbook    = Excel::Writer::XLSX->new( 'tables.xlsx' );
-    my $worksheet1  = $workbook->add_worksheet();
-    my $worksheet2  = $workbook->add_worksheet();
-    my $worksheet3  = $workbook->add_worksheet();
-    my $worksheet4  = $workbook->add_worksheet();
-    my $worksheet5  = $workbook->add_worksheet();
-    my $worksheet6  = $workbook->add_worksheet();
-    my $worksheet7  = $workbook->add_worksheet();
-    my $worksheet8  = $workbook->add_worksheet();
-    my $worksheet9  = $workbook->add_worksheet();
-    my $worksheet10 = $workbook->add_worksheet();
-    my $worksheet11 = $workbook->add_worksheet();
-    my $worksheet12 = $workbook->add_worksheet();
-    
-    my $currency_format = $workbook->add_format( num_format => '$#,##0' );
-    
-    
-    # Some sample data for the table.
-    my $data = [
-        [ 'Apples',  10000, 5000, 8000, 6000 ],
-        [ 'Pears',   2000,  3000, 4000, 5000 ],
-        [ 'Bananas', 6000,  6000, 6500, 6000 ],
-        [ 'Oranges', 500,   300,  200,  700 ],
-    
-    ];
-    
-    
-    ###############################################################################
-    #
-    # Example 1.
-    #
-    my $caption = 'Default table with no data.';
-    
-    # Set the columns widths.
-    $worksheet1->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet1->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet1->add_table( 'B3:F7' );
-    
-    
-    ###############################################################################
-    #
-    # Example 2.
-    #
-    $caption = 'Default table with data.';
-    
-    # Set the columns widths.
-    $worksheet2->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet2->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet2->add_table( 'B3:F7', { data => $data } );
-    
-    
-    ###############################################################################
-    #
-    # Example 3.
-    #
-    $caption = 'Table without default autofilter.';
-    
-    # Set the columns widths.
-    $worksheet3->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet3->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet3->add_table( 'B3:F7', { autofilter => 0 } );
-    
-    # Table data can also be written separately, as an array or individual cells.
-    $worksheet3->write_col( 'B4', $data );
-    
-    
-    ###############################################################################
-    #
-    # Example 4.
-    #
-    $caption = 'Table without default header row.';
-    
-    # Set the columns widths.
-    $worksheet4->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet4->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet4->add_table( 'B4:F7', { header_row => 0 } );
-    
-    # Table data can also be written separately, as an array or individual cells.
-    $worksheet4->write_col( 'B4', $data );
-    
-    
-    ###############################################################################
-    #
-    # Example 5.
-    #
-    $caption = 'Default table with "First Column" and "Last Column" options.';
-    
-    # Set the columns widths.
-    $worksheet5->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet5->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet5->add_table( 'B3:F7', { first_column => 1, last_column => 1 } );
-    
-    # Table data can also be written separately, as an array or individual cells.
-    $worksheet5->write_col( 'B4', $data );
-    
-    
-    ###############################################################################
-    #
-    # Example 6.
-    #
-    $caption = 'Table with banded columns but without default banded rows.';
-    
-    # Set the columns widths.
-    $worksheet6->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet6->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet6->add_table( 'B3:F7', { banded_rows => 0, banded_columns => 1 } );
-    
-    # Table data can also be written separately, as an array or individual cells.
-    $worksheet6->write_col( 'B4', $data );
-    
-    
-    ###############################################################################
-    #
-    # Example 7.
-    #
-    $caption = 'Table with user defined column headers';
-    
-    # Set the columns widths.
-    $worksheet7->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet7->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet7->add_table(
-        'B3:F7',
-        {
-            data    => $data,
-            columns => [
-                { header => 'Product' },
-                { header => 'Quarter 1' },
-                { header => 'Quarter 2' },
-                { header => 'Quarter 3' },
-                { header => 'Quarter 4' },
-            ]
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 8.
-    #
-    $caption = 'Table with user defined column headers';
-    
-    # Set the columns widths.
-    $worksheet8->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet8->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet8->add_table(
-        'B3:G7',
-        {
-            data    => $data,
-            columns => [
-                { header => 'Product' },
-                { header => 'Quarter 1' },
-                { header => 'Quarter 2' },
-                { header => 'Quarter 3' },
-                { header => 'Quarter 4' },
-                {
-                    header  => 'Year',
-                    formula => '=SUM(Table8[@[Quarter 1]:[Quarter 4]])'
-                },
-            ]
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 9.
-    #
-    $caption = 'Table with totals row (but no caption or totals).';
-    
-    # Set the columns widths.
-    $worksheet9->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet9->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet9->add_table(
-        'B3:G8',
-        {
-            data      => $data,
-            total_row => 1,
-            columns   => [
-                { header => 'Product' },
-                { header => 'Quarter 1' },
-                { header => 'Quarter 2' },
-                { header => 'Quarter 3' },
-                { header => 'Quarter 4' },
-                {
-                    header  => 'Year',
-                    formula => '=SUM(Table9[@[Quarter 1]:[Quarter 4]])'
-                },
-            ]
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 10.
-    #
-    $caption = 'Table with totals row with user captions and functions.';
-    
-    # Set the columns widths.
-    $worksheet10->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet10->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet10->add_table(
-        'B3:G8',
-        {
-            data      => $data,
-            total_row => 1,
-            columns   => [
-                { header => 'Product',   total_string   => 'Totals' },
-                { header => 'Quarter 1', total_function => 'sum' },
-                { header => 'Quarter 2', total_function => 'sum' },
-                { header => 'Quarter 3', total_function => 'sum' },
-                { header => 'Quarter 4', total_function => 'sum' },
-                {
-                    header         => 'Year',
-                    formula        => '=SUM(Table10[@[Quarter 1]:[Quarter 4]])',
-                    total_function => 'sum'
-                },
-            ]
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 11.
-    #
-    $caption = 'Table with alternative Excel style.';
-    
-    # Set the columns widths.
-    $worksheet11->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet11->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet11->add_table(
-        'B3:G8',
-        {
-            data      => $data,
-            style     => 'Table Style Light 11',
-            total_row => 1,
-            columns   => [
-                { header => 'Product',   total_string   => 'Totals' },
-                { header => 'Quarter 1', total_function => 'sum' },
-                { header => 'Quarter 2', total_function => 'sum' },
-                { header => 'Quarter 3', total_function => 'sum' },
-                { header => 'Quarter 4', total_function => 'sum' },
-                {
-                    header         => 'Year',
-                    formula        => '=SUM(Table11[@[Quarter 1]:[Quarter 4]])',
-                    total_function => 'sum'
-                },
-            ]
-        }
-    );
-    
-    
-    ###############################################################################
-    #
-    # Example 12.
-    #
-    $caption = 'Table with column formats.';
-    
-    # Set the columns widths.
-    $worksheet12->set_column( 'B:G', 12 );
-    
-    # Write the caption.
-    $worksheet12->write( 'B1', $caption );
-    
-    # Add a table to the worksheet.
-    $worksheet12->add_table(
-        'B3:G8',
-        {
-            data      => $data,
-            total_row => 1,
-            columns   => [
-                { header => 'Product', total_string => 'Totals' },
-                {
-                    header         => 'Quarter 1',
-                    total_function => 'sum',
-                    format         => $currency_format,
-                },
-                {
-                    header         => 'Quarter 2',
-                    total_function => 'sum',
-                    format         => $currency_format,
-                },
-                {
-                    header         => 'Quarter 3',
-                    total_function => 'sum',
-                    format         => $currency_format,
-                },
-                {
-                    header         => 'Quarter 4',
-                    total_function => 'sum',
-                    format         => $currency_format,
-                },
-                {
-                    header         => 'Year',
-                    formula        => '=SUM(Table12[@[Quarter 1]:[Quarter 4]])',
-                    total_function => 'sum',
-                    format         => $currency_format,
-                },
-            ]
-        }
-    );
-    
-    
-    $workbook->close();
-    
-    __END__
-    
-    
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/tables.pl>
-
-=head2 Example: write_handler1.pl
-
-
-
-Example of how to add a user defined data handler to the
-Excel::Writer::XLSX write() method.
-
-The following example shows how to add a handler for a 7 digit ID number.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/write_handler1.jpg" width="640" height="420" alt="Output from write_handler1.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # Example of how to add a user defined data handler to the
-    # Excel::Writer::XLSX write() method.
-    #
-    # The following example shows how to add a handler for a 7 digit ID number.
-    #
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'write_handler1.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    ###############################################################################
-    #
-    # Add a handler for 7 digit id numbers. This is useful when you want a string
-    # such as 0000001 written as a string instead of a number and thus preserve
-    # the leading zeroes.
-    #
-    # Note: you can get the same effect using the keep_leading_zeros() method but
-    # this serves as a simple example.
-    #
-    $worksheet->add_write_handler( qr[^\d{7}$], \&write_my_id );
-    
-    
-    ###############################################################################
-    #
-    # The following function processes the data when a match is found.
-    #
-    sub write_my_id {
-    
-        my $worksheet = shift;
-    
-        return $worksheet->write_string( @_ );
-    }
-    
-    
-    # This format maintains the cell as text even if it is edited.
-    my $id_format = $workbook->add_format( num_format => '@' );
-    
-    
-    # Write some numbers in the user defined format
-    $worksheet->write( 'A1', '0000000', $id_format );
-    $worksheet->write( 'A2', '0000001', $id_format );
-    $worksheet->write( 'A3', '0004000', $id_format );
-    $worksheet->write( 'A4', '1234567', $id_format );
-    
-    # Write some numbers that don't match the defined format
-    $worksheet->write( 'A6', '000000', $id_format );
-    $worksheet->write( 'A7', '000001', $id_format );
-    $worksheet->write( 'A8', '004000', $id_format );
-    $worksheet->write( 'A9', '123456', $id_format );
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/write_handler1.pl>
-
-=head2 Example: write_handler2.pl
-
-
-
-Example of how to add a user defined data handler to the
-Excel::Writer::XLSX write() method.
-
-The following example shows how to add a handler for a 7 digit ID number.
-It adds an additional constraint to the write_handler1.pl in that it only
-filters data that isn't in the third column.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/write_handler2.jpg" width="640" height="420" alt="Output from write_handler2.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # Example of how to add a user defined data handler to the
-    # Excel::Writer::XLSX write() method.
-    #
-    # The following example shows how to add a handler for a 7 digit ID number.
-    # It adds an additional constraint to the write_handler1.pl in that it only
-    # filters data that isn't in the third column.
-    #
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook  = Excel::Writer::XLSX->new( 'write_handler2.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-    
-    
-    ###############################################################################
-    #
-    # Add a handler for 7 digit id numbers. This is useful when you want a string
-    # such as 0000001 written as a string instead of a number and thus preserve
-    # the leading zeroes.
-    #
-    # Note: you can get the same effect using the keep_leading_zeros() method but
-    # this serves as a simple example.
-    #
-    $worksheet->add_write_handler( qr[^\d{7}$], \&write_my_id );
-    
-    
-    ###############################################################################
-    #
-    # The following function processes the data when a match is found. The handler
-    # is set up so that it only filters data if it is in the third column.
-    #
-    sub write_my_id {
-    
-        my $worksheet = shift;
-        my $col       = $_[1];
-    
-        # col is zero based
-        if ( $col != 2 ) {
-            return $worksheet->write_string( @_ );
-        }
-        else {
-    
-            # Reject the match and return control to write()
-            return undef;
-        }
-    
-    }
-    
-    
-    # This format maintains the cell as text even if it is edited.
-    my $id_format = $workbook->add_format( num_format => '@' );
-    
-    
-    # Write some numbers in the user defined format
-    $worksheet->write( 'A1', '0000000', $id_format );
-    $worksheet->write( 'B1', '0000001', $id_format );
-    $worksheet->write( 'C1', '0000002', $id_format );
-    $worksheet->write( 'D1', '0000003', $id_format );
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/write_handler2.pl>
-
-=head2 Example: write_handler3.pl
-
-
-
-Example of how to add a user defined data handler to the
-Excel::Writer::XLSX write() method.
-
-The following example shows how to add a handler for dates in a specific
-format.
-
-See write_handler4.pl for a more rigorous example with error handling.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/write_handler3.jpg" width="640" height="420" alt="Output from write_handler3.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # Example of how to add a user defined data handler to the
-    # Excel::Writer::XLSX write() method.
-    #
-    # The following example shows how to add a handler for dates in a specific
-    # format.
-    #
-    # See write_handler4.pl for a more rigorous example with error handling.
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook    = Excel::Writer::XLSX->new( 'write_handler3.xlsx' );
-    my $worksheet   = $workbook->add_worksheet();
-    my $date_format = $workbook->add_format( num_format => 'dd/mm/yy' );
-    
-    
-    ###############################################################################
-    #
-    # Add a handler to match dates in the following format: d/m/yyyy
-    #
-    # The day and month can be single or double digits.
-    #
-    $worksheet->add_write_handler( qr[^\d{1,2}/\d{1,2}/\d{4}$], \&write_my_date );
-    
-    
-    ###############################################################################
-    #
-    # The following function processes the data when a match is found.
-    # See write_handler4.pl for a more rigorous example with error handling.
-    #
-    sub write_my_date {
-    
-        my $worksheet = shift;
-        my @args      = @_;
-    
-        my $token = $args[2];
-        $token =~ qr[^(\d{1,2})/(\d{1,2})/(\d{4})$];
-    
-        # Change to the date format required by write_date_time().
-        my $date = sprintf "%4d-%02d-%02dT", $3, $2, $1;
-    
-        $args[2] = $date;
-    
-        return $worksheet->write_date_time( @args );
-    }
-    
-    
-    # Write some dates in the user defined format
-    $worksheet->write( 'A1', '22/12/2004', $date_format );
-    $worksheet->write( 'A2', '1/1/1995',   $date_format );
-    $worksheet->write( 'A3', '01/01/1995', $date_format );
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/write_handler3.pl>
-
-=head2 Example: write_handler4.pl
-
-
-
-Example of how to add a user defined data handler to the
-Excel::Writer::XLSX write() method.
-
-The following example shows how to add a handler for dates in a specific
-format.
-
-This is a more rigorous version of write_handler3.pl.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/write_handler4.jpg" width="640" height="420" alt="Output from write_handler4.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl -w
-    
-    ###############################################################################
-    #
-    # Example of how to add a user defined data handler to the
-    # Excel::Writer::XLSX write() method.
-    #
-    # The following example shows how to add a handler for dates in a specific
-    # format.
-    #
-    # This is a more rigorous version of write_handler3.pl.
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook    = Excel::Writer::XLSX->new( 'write_handler4.xlsx' );
-    my $worksheet   = $workbook->add_worksheet();
-    my $date_format = $workbook->add_format( num_format => 'dd/mm/yy' );
-    
-    
-    ###############################################################################
-    #
-    # Add a handler to match dates in the following formats: d/m/yy, d/m/yyyy
-    #
-    # The day and month can be single or double digits and the year can be  2 or 4
-    # digits.
-    #
-    $worksheet->add_write_handler( qr[^\d{1,2}/\d{1,2}/\d{2,4}$], \&write_my_date );
-    
-    
-    ###############################################################################
-    #
-    # The following function processes the data when a match is found.
-    #
-    sub write_my_date {
-    
-        my $worksheet = shift;
-        my @args      = @_;
-    
-        my $token = $args[2];
-    
-        if ( $token =~ qr[^(\d{1,2})/(\d{1,2})/(\d{2,4})$] ) {
-    
-            my $day  = $1;
-            my $mon  = $2;
-            my $year = $3;
-    
-            # Use a window for 2 digit dates. This will keep some ragged Perl
-            # programmer employed in thirty years time. :-)
-            if ( length $year == 2 ) {
-                if ( $year < 50 ) {
-                    $year += 2000;
-                }
-                else {
-                    $year += 1900;
-                }
-            }
-    
-            my $date = sprintf "%4d-%02d-%02dT", $year, $mon, $day;
-    
-            # Convert the ISO ISO8601 style string to an Excel date
-            $date = $worksheet->convert_date_time( $date );
-    
-            if ( defined $date ) {
-    
-                # Date was valid
-                $args[2] = $date;
-                return $worksheet->write_number( @args );
-            }
-            else {
-    
-                # Not a valid date therefore write as a string
-                return $worksheet->write_string( @args );
-            }
-        }
-        else {
-    
-            # Shouldn't happen if the same match is used in the re and sub.
-            return undef;
-        }
-    }
-    
-    
-    # Write some dates in the user defined format
-    $worksheet->write( 'A1', '22/12/2004', $date_format );
-    $worksheet->write( 'A2', '22/12/04',   $date_format );
-    $worksheet->write( 'A3', '2/12/04',    $date_format );
-    $worksheet->write( 'A4', '2/5/04',     $date_format );
-    $worksheet->write( 'A5', '2/5/95',     $date_format );
-    $worksheet->write( 'A6', '2/5/1995',   $date_format );
-    
-    # Some erroneous dates
-    $worksheet->write( 'A8', '2/5/1895',  $date_format ); # Date out of Excel range
-    $worksheet->write( 'A9', '29/2/2003', $date_format ); # Invalid leap day
-    $worksheet->write( 'A10', '50/50/50', $date_format ); # Matches but isn't a date
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/write_handler4.pl>
-
-=head2 Example: write_to_scalar.pl
-
-
-
-An example of writing an Excel::Writer::XLSX file to a perl scalar.
-
-
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # An example of writing an Excel::Writer::XLSX file to a perl scalar.
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    # Use a scalar as a filehandle.
-    open my $fh, '>', \my $str or die "Failed to open filehandle: $!";
-    
-    
-    # Spreadsheet::WriteExce accepts filehandle as well as file names.
-    my $workbook  = Excel::Writer::XLSX->new( $fh );
-    my $worksheet = $workbook->add_worksheet();
-    
-    $worksheet->write( 0, 0, 'Hi Excel!' );
-    
-    $workbook->close();
-    
-    
-    # The Excel file in now in $str. Remember to binmode() the output
-    # filehandle before printing it.
-    open my $out_fh, '>', 'write_to_scalar.xlsx'
-      or die "Failed to open out filehandle: $!";
-    
-    binmode $out_fh;
-    print   $out_fh $str;
-    close   $out_fh;
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/write_to_scalar.pl>
-
-=head2 Example: unicode_2022_jp.pl
-
-
-
-A simple example of converting some Unicode text to an Excel file using
-Excel::Writer::XLSX.
-
-This example generates some Japanese from a file with ISO-2022-JP
-encoded text.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/unicode_2022_jp.jpg" width="640" height="420" alt="Output from unicode_2022_jp.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # A simple example of converting some Unicode text to an Excel file using
-    # Excel::Writer::XLSX.
-    #
-    # This example generates some Japanese from a file with ISO-2022-JP
-    # encoded text.
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'unicode_2022_jp.xlsx' );
-    
-    die "Couldn't create new Excel file: $!.\n" unless defined $workbook;
-    
-    my $worksheet = $workbook->add_worksheet();
-    $worksheet->set_column( 'A:A', 50 );
-    
-    
-    my $file = 'unicode_2022_jp.txt';
-    
-    open FH, '<:encoding(iso-2022-jp)', $file or die "Couldn't open $file: $!\n";
-    
-    my $row = 0;
-    
-    while ( <FH> ) {
-        next if /^#/;    # Ignore the comments in the sample file.
-        chomp;
-        $worksheet->write( $row++, 0, $_ );
-    }
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/unicode_2022_jp.pl>
-
-=head2 Example: unicode_8859_11.pl
-
-
-
-A simple example of converting some Unicode text to an Excel file using
-Excel::Writer::XLSX.
-
-This example generates some Thai from a file with ISO-8859-11 encoded text.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/unicode_8859_11.jpg" width="640" height="420" alt="Output from unicode_8859_11.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # A simple example of converting some Unicode text to an Excel file using
-    # Excel::Writer::XLSX.
-    #
-    # This example generates some Thai from a file with ISO-8859-11 encoded text.
-    #
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'unicode_8859_11.xlsx' );
-    
-    die "Couldn't create new Excel file: $!.\n" unless defined $workbook;
-    
-    my $worksheet = $workbook->add_worksheet();
-    $worksheet->set_column( 'A:A', 50 );
-    
-    
-    my $file = 'unicode_8859_11.txt';
-    
-    open FH, '<:encoding(iso-8859-11)', $file or die "Couldn't open $file: $!\n";
-    
-    my $row = 0;
-    
-    while ( <FH> ) {
-        next if /^#/;    # Ignore the comments in the sample file.
-        chomp;
-        $worksheet->write( $row++, 0, $_ );
-    }
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/unicode_8859_11.pl>
-
-=head2 Example: unicode_8859_7.pl
-
-
-
-A simple example of converting some Unicode text to an Excel file using
-Excel::Writer::XLSX.
-
-This example generates some Greek from a file with ISO-8859-7 encoded text.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/unicode_8859_7.jpg" width="640" height="420" alt="Output from unicode_8859_7.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # A simple example of converting some Unicode text to an Excel file using
-    # Excel::Writer::XLSX.
-    #
-    # This example generates some Greek from a file with ISO-8859-7 encoded text.
-    #
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'unicode_8859_7.xlsx' );
-    
-    die "Couldn't create new Excel file: $!.\n" unless defined $workbook;
-    
-    my $worksheet = $workbook->add_worksheet();
-    $worksheet->set_column( 'A:A', 50 );
-    
-    
-    my $file = 'unicode_8859_7.txt';
-    
-    open FH, '<:encoding(iso-8859-7)', $file or die "Couldn't open $file: $!\n";
-    
-    my $row = 0;
-    
-    while ( <FH> ) {
-        next if /^#/;    # Ignore the comments in the sample file.
-        chomp;
-        $worksheet->write( $row++, 0, $_ );
-    }
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/unicode_8859_7.pl>
-
-=head2 Example: unicode_big5.pl
-
-
-
-A simple example of converting some Unicode text to an Excel file using
-Excel::Writer::XLSX.
-
-This example generates some Chinese from a file with BIG5 encoded text.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/unicode_big5.jpg" width="640" height="420" alt="Output from unicode_big5.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # A simple example of converting some Unicode text to an Excel file using
-    # Excel::Writer::XLSX.
-    #
-    # This example generates some Chinese from a file with BIG5 encoded text.
-    #
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'unicode_big5.xlsx' );
-    
-    die "Couldn't create new Excel file: $!.\n" unless defined $workbook;
-    
-    my $worksheet = $workbook->add_worksheet();
-    $worksheet->set_column( 'A:A', 80 );
-    
-    
-    my $file = 'unicode_big5.txt';
-    
-    open FH, '<:encoding(big5)', $file or die "Couldn't open $file: $!\n";
-    
-    my $row = 0;
-    
-    while ( <FH> ) {
-        next if /^#/;    # Ignore the comments in the sample file.
-        chomp;
-        $worksheet->write( $row++, 0, $_ );
-    }
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/unicode_big5.pl>
-
-=head2 Example: unicode_cp1251.pl
-
-
-
-A simple example of converting some Unicode text to an Excel file using
-Excel::Writer::XLSX.
-
-This example generates some Russian from a file with CP1251 encoded text.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/unicode_cp1251.jpg" width="640" height="420" alt="Output from unicode_cp1251.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # A simple example of converting some Unicode text to an Excel file using
-    # Excel::Writer::XLSX.
-    #
-    # This example generates some Russian from a file with CP1251 encoded text.
-    #
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'unicode_cp1251.xlsx' );
-    
-    die "Couldn't create new Excel file: $!.\n" unless defined $workbook;
-    
-    my $worksheet = $workbook->add_worksheet();
-    $worksheet->set_column( 'A:A', 50 );
-    
-    
-    my $file = 'unicode_cp1251.txt';
-    
-    open FH, '<:encoding(cp1251)', $file or die "Couldn't open $file: $!\n";
-    
-    my $row = 0;
-    
-    while ( <FH> ) {
-        next if /^#/;    # Ignore the comments in the sample file.
-        chomp;
-        $worksheet->write( $row++, 0, $_ );
-    }
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/unicode_cp1251.pl>
-
-=head2 Example: unicode_cp1256.pl
-
-
-
-A simple example of converting some Unicode text to an Excel file using
-Excel::Writer::XLSX.
-
-This example generates some Arabic text from a CP-1256 encoded file.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/unicode_cp1256.jpg" width="640" height="420" alt="Output from unicode_cp1256.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # A simple example of converting some Unicode text to an Excel file using
-    # Excel::Writer::XLSX.
-    #
-    # This example generates some Arabic text from a CP-1256 encoded file.
-    #
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'unicode_cp1256.xlsx' );
-    
-    die "Couldn't create new Excel file: $!.\n" unless defined $workbook;
-    
-    my $worksheet = $workbook->add_worksheet();
-    $worksheet->set_column( 'A:A', 50 );
-    
-    
-    my $file = 'unicode_cp1256.txt';
-    
-    open FH, '<:encoding(cp1256)', $file or die "Couldn't open $file: $!\n";
-    
-    my $row = 0;
-    
-    while ( <FH> ) {
-        next if /^#/;    # Ignore the comments in the sample file.
-        chomp;
-        $worksheet->write( $row++, 0, $_ );
-    }
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/unicode_cp1256.pl>
-
-=head2 Example: unicode_cyrillic.pl
-
-
-
-A simple example of writing some Russian cyrillic text using
-Excel::Writer::XLSX.
-
-
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/unicode_cyrillic.jpg" width="640" height="420" alt="Output from unicode_cyrillic.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # A simple example of writing some Russian cyrillic text using
-    # Excel::Writer::XLSX.
-    #
-    #
-    #
-    #
-    # reverse ('(c)'), March 2005, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    # In this example we generate utf8 strings from character data but in a
-    # real application we would expect them to come from an external source.
-    #
-    
-    
-    # Create a Russian worksheet name in utf8.
-    my $sheet = pack "U*", 0x0421, 0x0442, 0x0440, 0x0430, 0x043D, 0x0438,
-      0x0446, 0x0430;
-    
-    
-    # Create a Russian string.
-    my $str = pack "U*", 0x0417, 0x0434, 0x0440, 0x0430, 0x0432, 0x0441,
-      0x0442, 0x0432, 0x0443, 0x0439, 0x0020, 0x041C,
-      0x0438, 0x0440, 0x0021;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'unicode_cyrillic.xlsx' );
-    
-    die "Couldn't create new Excel file: $!.\n" unless defined $workbook;
-    
-    my $worksheet = $workbook->add_worksheet( $sheet . '1' );
-    
-    $worksheet->set_column( 'A:A', 18 );
-    $worksheet->write( 'A1', $str );
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/unicode_cyrillic.pl>
-
-=head2 Example: unicode_koi8r.pl
-
-
-
-A simple example of converting some Unicode text to an Excel file using
-Excel::Writer::XLSX.
-
-This example generates some Russian from a file with KOI8-R encoded text.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/unicode_koi8r.jpg" width="640" height="420" alt="Output from unicode_koi8r.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # A simple example of converting some Unicode text to an Excel file using
-    # Excel::Writer::XLSX.
-    #
-    # This example generates some Russian from a file with KOI8-R encoded text.
-    #
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'unicode_koi8r.xlsx' );
-    
-    die "Couldn't create new Excel file: $!.\n" unless defined $workbook;
-    
-    my $worksheet = $workbook->add_worksheet();
-    $worksheet->set_column( 'A:A', 50 );
-    
-    
-    my $file = 'unicode_koi8r.txt';
-    
-    open FH, '<:encoding(koi8-r)', $file or die "Couldn't open $file: $!\n";
-    
-    my $row = 0;
-    
-    while ( <FH> ) {
-        next if /^#/;    # Ignore the comments in the sample file.
-        chomp;
-        $worksheet->write( $row++, 0, $_ );
-    }
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/unicode_koi8r.pl>
-
-=head2 Example: unicode_polish_utf8.pl
-
-
-
-A simple example of converting some Unicode text to an Excel file using
-Excel::Writer::XLSX.
-
-This example generates some Polish from a file with UTF8 encoded text.
-
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/unicode_polish_utf8.jpg" width="640" height="420" alt="Output from unicode_polish_utf8.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # A simple example of converting some Unicode text to an Excel file using
-    # Excel::Writer::XLSX.
-    #
-    # This example generates some Polish from a file with UTF8 encoded text.
-    #
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'unicode_polish_utf8.xlsx' );
-    
-    die "Couldn't create new Excel file: $!.\n" unless defined $workbook;
-    
-    my $worksheet = $workbook->add_worksheet();
-    $worksheet->set_column( 'A:A', 50 );
-    
-    
-    my $file = 'unicode_polish_utf8.txt';
-    
-    open FH, '<:encoding(utf8)', $file or die "Couldn't open $file: $!\n";
-    
-    my $row = 0;
-    
-    while ( <FH> ) {
-        next if /^#/;    # Ignore the comments in the sample file.
-        chomp;
-        $worksheet->write( $row++, 0, $_ );
-    }
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/unicode_polish_utf8.pl>
-
-=head2 Example: unicode_shift_jis.pl
-
-
-
-A simple example of converting some Unicode text to an Excel file using
-Excel::Writer::XLSX.
-
-This example generates some Japenese text from a file with Shift-JIS
-encoded text.
-
-
-
-=begin html
-
-<p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/unicode_shift_jis.jpg" width="640" height="420" alt="Output from unicode_shift_jis.pl" /></center></p>
-
-=end html
-
-Source code for this example:
-
-    #!/usr/bin/perl
-    
-    ##############################################################################
-    #
-    # A simple example of converting some Unicode text to an Excel file using
-    # Excel::Writer::XLSX.
-    #
-    # This example generates some Japenese text from a file with Shift-JIS
-    # encoded text.
-    #
-    # reverse ('(c)'), September 2004, John McNamara, jmcnamara@cpan.org
-    #
-    
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-    
-    
-    my $workbook = Excel::Writer::XLSX->new( 'unicode_shift_jis.xlsx' );
-    
-    die "Couldn't create new Excel file: $!.\n" unless defined $workbook;
-    
-    my $worksheet = $workbook->add_worksheet();
-    $worksheet->set_column( 'A:A', 50 );
-    
-    
-    my $file = 'unicode_shift_jis.txt';
-    
-    open FH, '<:encoding(shiftjis)', $file or die "Couldn't open $file: $!\n";
-    
-    my $row = 0;
-    
-    while ( <FH> ) {
-        next if /^#/;    # Ignore the comments in the sample file.
-        chomp;
-        $worksheet->write( $row++, 0, $_ );
-    }
-    
-    $workbook->close();
-    
-    __END__
-    
-
-
-Download this example: L<http://cpansearch.perl.org/src/JMCNAMARA/Excel-Writer-XLSX-0.98/examples/unicode_shift_jis.pl>
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-Contributed examples contain the original author's name.
-
-=head1 COPYRIGHT
-
-Copyright MM-MMXVI, 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
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Format.pm b/tools/lib/perl5/Excel/Writer/XLSX/Format.pm
deleted file mode 100644 (file)
index 076474b..0000000
+++ /dev/null
@@ -1,817 +0,0 @@
-package Excel::Writer::XLSX::Format;
-
-###############################################################################
-#
-# Format - A class for defining Excel formatting.
-#
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-use 5.008002;
-use Exporter;
-use strict;
-use warnings;
-use Carp;
-
-
-our @ISA     = qw(Exporter);
-our $VERSION = '0.98';
-our $AUTOLOAD;
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor
-#
-sub new {
-
-    my $class = shift;
-
-    my $self = {
-        _xf_format_indices  => shift,
-        _dxf_format_indices => shift,
-        _xf_index           => undef,
-        _dxf_index          => undef,
-
-        _num_format        => 0,
-        _num_format_index  => 0,
-        _font_index        => 0,
-        _has_font          => 0,
-        _has_dxf_font      => 0,
-        _font              => 'Calibri',
-        _size              => 11,
-        _bold              => 0,
-        _italic            => 0,
-        _color             => 0x0,
-        _underline         => 0,
-        _font_strikeout    => 0,
-        _font_outline      => 0,
-        _font_shadow       => 0,
-        _font_script       => 0,
-        _font_family       => 2,
-        _font_charset      => 0,
-        _font_scheme       => 'minor',
-        _font_condense     => 0,
-        _font_extend       => 0,
-        _theme             => 0,
-        _hyperlink         => 0,
-        _xf_id             => 0,
-
-        _hidden => 0,
-        _locked => 1,
-
-        _text_h_align  => 0,
-        _text_wrap     => 0,
-        _text_v_align  => 0,
-        _text_justlast => 0,
-        _rotation      => 0,
-
-        _fg_color     => 0x00,
-        _bg_color     => 0x00,
-        _pattern      => 0,
-        _has_fill     => 0,
-        _has_dxf_fill => 0,
-        _fill_index   => 0,
-        _fill_count   => 0,
-
-        _border_index   => 0,
-        _has_border     => 0,
-        _has_dxf_border => 0,
-        _border_count   => 0,
-
-        _bottom       => 0,
-        _bottom_color => 0x0,
-        _diag_border  => 0,
-        _diag_color   => 0x0,
-        _diag_type    => 0,
-        _left         => 0,
-        _left_color   => 0x0,
-        _right        => 0,
-        _right_color  => 0x0,
-        _top          => 0,
-        _top_color    => 0x0,
-
-        _indent        => 0,
-        _shrink        => 0,
-        _merge_range   => 0,
-        _reading_order => 0,
-        _just_distrib  => 0,
-        _color_indexed => 0,
-        _font_only     => 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 Excel::Writer::XLSX::Format object.
-#
-sub copy {
-    my $self  = shift;
-    my $other = $_[0];
-
-
-    return unless defined $other;
-    return unless ( ref( $self ) eq ref( $other ) );
-
-    # Store properties that we don't want over-ridden.
-    my $xf_index           = $self->{_xf_index};
-    my $dxf_index          = $self->{_dxf_index};
-    my $xf_format_indices  = $self->{_xf_format_indices};
-    my $dxf_format_indices = $self->{_dxf_format_indices};
-    my $palette            = $self->{_palette};
-
-    # Copy properties.
-    %$self             = %$other;
-
-    # Restore original properties.
-    $self->{_xf_index}           = $xf_index;
-    $self->{_dxf_index}          = $dxf_index;
-    $self->{_xf_format_indices}  = $xf_format_indices;
-    $self->{_dxf_format_indices} = $dxf_format_indices;
-    $self->{_palette}            = $palette;
-}
-
-
-###############################################################################
-#
-# get_align_properties()
-#
-# Return properties for an Style xf <alignment> sub-element.
-#
-sub get_align_properties {
-
-    my $self = shift;
-
-    my @align;    # Attributes to return
-
-    # Check if any alignment options in the format have been changed.
-    my $changed =
-      (      $self->{_text_h_align} != 0
-          || $self->{_text_v_align} != 0
-          || $self->{_indent} != 0
-          || $self->{_rotation} != 0
-          || $self->{_text_wrap} != 0
-          || $self->{_shrink} != 0
-          || $self->{_reading_order} != 0 ) ? 1 : 0;
-
-    return unless $changed;
-
-
-
-    # Indent is only allowed for horizontal left, right and distributed. If it
-    # is defined for any other alignment or no alignment has been set then
-    # default to left alignment.
-    if (   $self->{_indent}
-        && $self->{_text_h_align} != 1
-        && $self->{_text_h_align} != 3
-        && $self->{_text_h_align} != 7 )
-    {
-        $self->{_text_h_align} = 1;
-    }
-
-    # Check for properties that are mutually exclusive.
-    $self->{_shrink}       = 0 if $self->{_text_wrap};
-    $self->{_shrink}       = 0 if $self->{_text_h_align} == 4;    # Fill
-    $self->{_shrink}       = 0 if $self->{_text_h_align} == 5;    # Justify
-    $self->{_shrink}       = 0 if $self->{_text_h_align} == 7;    # Distributed
-    $self->{_just_distrib} = 0 if $self->{_text_h_align} != 7;    # Distributed
-    $self->{_just_distrib} = 0 if $self->{_indent};
-
-    my $continuous = 'centerContinuous';
-
-    push @align, 'horizontal', 'left'        if $self->{_text_h_align} == 1;
-    push @align, 'horizontal', 'center'      if $self->{_text_h_align} == 2;
-    push @align, 'horizontal', 'right'       if $self->{_text_h_align} == 3;
-    push @align, 'horizontal', 'fill'        if $self->{_text_h_align} == 4;
-    push @align, 'horizontal', 'justify'     if $self->{_text_h_align} == 5;
-    push @align, 'horizontal', $continuous   if $self->{_text_h_align} == 6;
-    push @align, 'horizontal', 'distributed' if $self->{_text_h_align} == 7;
-
-    push @align, 'justifyLastLine', 1 if $self->{_just_distrib};
-
-    # Property 'vertical' => 'bottom' is a default. It sets applyAlignment
-    # without an alignment sub-element.
-    push @align, 'vertical', 'top'         if $self->{_text_v_align} == 1;
-    push @align, 'vertical', 'center'      if $self->{_text_v_align} == 2;
-    push @align, 'vertical', 'justify'     if $self->{_text_v_align} == 4;
-    push @align, 'vertical', 'distributed' if $self->{_text_v_align} == 5;
-
-    push @align, 'indent',       $self->{_indent}   if $self->{_indent};
-    push @align, 'textRotation', $self->{_rotation} if $self->{_rotation};
-
-    push @align, 'wrapText',     1 if $self->{_text_wrap};
-    push @align, 'shrinkToFit',  1 if $self->{_shrink};
-
-    push @align, 'readingOrder', 1 if $self->{_reading_order} == 1;
-    push @align, 'readingOrder', 2 if $self->{_reading_order} == 2;
-
-    return $changed, @align;
-}
-
-
-###############################################################################
-#
-# get_protection_properties()
-#
-# Return properties for an Excel XML <Protection> element.
-#
-sub get_protection_properties {
-
-    my $self = shift;
-
-    my @attribs;
-
-    push @attribs, 'locked', 0 if !$self->{_locked};
-    push @attribs, 'hidden', 1 if $self->{_hidden};
-
-    return @attribs;
-}
-
-
-###############################################################################
-#
-# get_format_key()
-#
-# Returns a unique hash key for the Format object.
-#
-sub get_format_key {
-
-    my $self = shift;
-
-    my $key = join ':',
-      (
-        $self->get_font_key(), $self->get_border_key,
-        $self->get_fill_key(), $self->get_alignment_key(),
-        $self->{_num_format},  $self->{_locked},
-        $self->{_hidden}
-      );
-
-    return $key;
-}
-
-###############################################################################
-#
-# get_font_key()
-#
-# Returns a unique hash key for a font. Used by Workbook.
-#
-sub get_font_key {
-
-    my $self = shift;
-
-    my $key = join ':', (
-        $self->{_bold},
-        $self->{_color},
-        $self->{_font_charset},
-        $self->{_font_family},
-        $self->{_font_outline},
-        $self->{_font_script},
-        $self->{_font_shadow},
-        $self->{_font_strikeout},
-        $self->{_font},
-        $self->{_italic},
-        $self->{_size},
-        $self->{_underline},
-        $self->{_theme},
-
-    );
-
-    return $key;
-}
-
-
-###############################################################################
-#
-# get_border_key()
-#
-# Returns a unique hash key for a border style. Used by Workbook.
-#
-sub get_border_key {
-
-    my $self = shift;
-
-    my $key = join ':', (
-        $self->{_bottom},
-        $self->{_bottom_color},
-        $self->{_diag_border},
-        $self->{_diag_color},
-        $self->{_diag_type},
-        $self->{_left},
-        $self->{_left_color},
-        $self->{_right},
-        $self->{_right_color},
-        $self->{_top},
-        $self->{_top_color},
-
-    );
-
-    return $key;
-}
-
-
-###############################################################################
-#
-# get_fill_key()
-#
-# Returns a unique hash key for a fill style. Used by Workbook.
-#
-sub get_fill_key {
-
-    my $self = shift;
-
-    my $key = join ':', (
-        $self->{_pattern},
-        $self->{_bg_color},
-        $self->{_fg_color},
-
-    );
-
-    return $key;
-}
-
-
-###############################################################################
-#
-# get_alignment_key()
-#
-# Returns a unique hash key for alignment formats.
-#
-sub get_alignment_key {
-
-    my $self = shift;
-
-    my $key = join ':', (
-        $self->{_text_h_align},
-        $self->{_text_v_align},
-        $self->{_indent},
-        $self->{_rotation},
-        $self->{_text_wrap},
-        $self->{_shrink},
-        $self->{_reading_order},
-
-    );
-
-    return $key;
-}
-
-
-###############################################################################
-#
-# get_xf_index()
-#
-# Returns the index used by Worksheet->_XF()
-#
-sub get_xf_index {
-    my $self = shift;
-
-    if ( defined $self->{_xf_index} ) {
-        return $self->{_xf_index};
-    }
-    else {
-        my $key  = $self->get_format_key();
-        my $indices_href = ${ $self->{_xf_format_indices} };
-
-        if ( exists $indices_href->{$key} ) {
-            return $indices_href->{$key};
-        }
-        else {
-            my $index = 1 + scalar keys %$indices_href;
-            $indices_href->{$key} = $index;
-            $self->{_xf_index} = $index;
-            return $index;
-        }
-    }
-}
-
-
-###############################################################################
-#
-# get_dxf_index()
-#
-# Returns the index used by Worksheet->_XF()
-#
-sub get_dxf_index {
-    my $self = shift;
-
-    if ( defined $self->{_dxf_index} ) {
-        return $self->{_dxf_index};
-    }
-    else {
-        my $key  = $self->get_format_key();
-        my $indices_href = ${ $self->{_dxf_format_indices} };
-
-        if ( exists $indices_href->{$key} ) {
-            return $indices_href->{$key};
-        }
-        else {
-            my $index = scalar keys %$indices_href;
-            $indices_href->{$key} = $index;
-            $self->{_dxf_index} = $index;
-            return $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 if undef,
-    return 0x00 unless defined $_[0];
-
-    # Return RGB style colors for processing later.
-    if ( $_[0] =~ m/^#[0-9A-F]{6}$/i ) {
-        return $_[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 0x00 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 0x00 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';              # Legacy.
-    $self->set_text_h_align( 7 ) if $location eq 'distributed';
-    $self->set_text_h_align( 7 ) if $location eq 'equal_space';        # S::PE.
-    $self->set_text_h_align( 7 ) if $location eq 'justify_distributed';
-
-    $self->{_just_distrib} = 1 if $location eq 'justify_distributed';
-
-    $self->set_text_v_align( 1 ) if $location eq 'top';
-    $self->set_text_v_align( 2 ) if $location eq 'vcentre';
-    $self->set_text_v_align( 2 ) if $location eq 'vcenter';
-    $self->set_text_v_align( 3 ) if $location eq 'bottom';
-    $self->set_text_v_align( 4 ) if $location eq 'vjustify';
-    $self->set_text_v_align( 5 ) if $location eq 'vdistributed';
-    $self->set_text_v_align( 5 ) if $location eq 'vequal_space';    # S::PE.
-}
-
-
-###############################################################################
-#
-# set_valign()
-#
-# Set vertical cell alignment. This is required by the set_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()
-#
-#
-sub set_bold {
-
-    my $self = shift;
-    my $bold = defined $_[0] ? $_[0] : 1;
-
-    $self->{_bold} = $bold ? 1 : 0;
-}
-
-
-###############################################################################
-#
-# 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 and $rotation <= 90 ) {
-        $rotation = -$rotation + 90 if $rotation < 0;
-    }
-    else {
-        carp "Rotation $rotation outside range: -90 <= angle <= 90";
-        $rotation = 0;
-    }
-
-    $self->{_rotation} = $rotation;
-}
-
-
-###############################################################################
-#
-# set_hyperlink()
-#
-# Set the properties for the hyperlink style. This isn't a public method. To
-# be fixed when styles are supported.
-#
-sub set_hyperlink {
-
-    my $self      = shift;
-    my $hyperlink = shift;
-
-    $self->{_xf_id} = 1;
-
-    $self->set_underline( 1 );
-    $self->set_theme( 10 );
-    $self->{_hyperlink} = $hyperlink;
-}
-
-
-###############################################################################
-#
-# 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__
-
-
-=head1 NAME
-
-Format - A class for defining Excel formatting.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/App.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/App.pm
deleted file mode 100644 (file)
index 7d69e6c..0000000
+++ /dev/null
@@ -1,454 +0,0 @@
-package Excel::Writer::XLSX::Package::App;
-
-###############################################################################
-#
-# App - A class for writing the Excel XLSX app.xml file.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Package::XMLwriter;
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_part_names}    = [];
-    $self->{_heading_pairs} = [];
-    $self->{_properties}    = {};
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration;
-    $self->_write_properties();
-    $self->_write_application();
-    $self->_write_doc_security();
-    $self->_write_scale_crop();
-    $self->_write_heading_pairs();
-    $self->_write_titles_of_parts();
-    $self->_write_manager();
-    $self->_write_company();
-    $self->_write_links_up_to_date();
-    $self->_write_shared_doc();
-    $self->_write_hyperlink_base();
-    $self->_write_hyperlinks_changed();
-    $self->_write_app_version();
-
-    $self->xml_end_tag( 'Properties' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# _add_part_name()
-#
-# Add the name of a workbook Part such as 'Sheet1' or 'Print_Titles'.
-#
-sub _add_part_name {
-
-    my $self      = shift;
-    my $part_name = shift;
-
-    push @{ $self->{_part_names} }, $part_name;
-}
-
-
-###############################################################################
-#
-# _add_heading_pair()
-#
-# Add the name of a workbook Heading Pair such as 'Worksheets', 'Charts' or
-# 'Named Ranges'.
-#
-sub _add_heading_pair {
-
-    my $self         = shift;
-    my $heading_pair = shift;
-
-    return unless $heading_pair->[1];  # Ignore empty pairs such as chartsheets.
-
-    my @vector = (
-        [ 'lpstr', $heading_pair->[0] ],    # Data name
-        [ 'i4',    $heading_pair->[1] ],    # Data size
-    );
-
-    push @{ $self->{_heading_pairs} }, @vector;
-}
-
-
-###############################################################################
-#
-# _set_properties()
-#
-# Set the document properties.
-#
-sub _set_properties {
-
-    my $self       = shift;
-    my $properties = shift;
-
-    $self->{_properties} = $properties;
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _write_properties()
-#
-# Write the <Properties> element.
-#
-sub _write_properties {
-
-    my $self     = shift;
-    my $schema   = 'http://schemas.openxmlformats.org/officeDocument/2006/';
-    my $xmlns    = $schema . 'extended-properties';
-    my $xmlns_vt = $schema . 'docPropsVTypes';
-
-    my @attributes = (
-        'xmlns'    => $xmlns,
-        'xmlns:vt' => $xmlns_vt,
-    );
-
-    $self->xml_start_tag( 'Properties', @attributes );
-}
-
-###############################################################################
-#
-# _write_application()
-#
-# Write the <Application> element.
-#
-sub _write_application {
-
-    my $self = shift;
-    my $data = 'Microsoft Excel';
-
-    $self->xml_data_element( 'Application', $data );
-}
-
-
-###############################################################################
-#
-# _write_doc_security()
-#
-# Write the <DocSecurity> element.
-#
-sub _write_doc_security {
-
-    my $self = shift;
-    my $data = 0;
-
-    $self->xml_data_element( 'DocSecurity', $data );
-}
-
-
-###############################################################################
-#
-# _write_scale_crop()
-#
-# Write the <ScaleCrop> element.
-#
-sub _write_scale_crop {
-
-    my $self = shift;
-    my $data = 'false';
-
-    $self->xml_data_element( 'ScaleCrop', $data );
-}
-
-
-###############################################################################
-#
-# _write_heading_pairs()
-#
-# Write the <HeadingPairs> element.
-#
-sub _write_heading_pairs {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'HeadingPairs' );
-
-    $self->_write_vt_vector( 'variant', $self->{_heading_pairs} );
-
-    $self->xml_end_tag( 'HeadingPairs' );
-}
-
-
-###############################################################################
-#
-# _write_titles_of_parts()
-#
-# Write the <TitlesOfParts> element.
-#
-sub _write_titles_of_parts {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'TitlesOfParts' );
-
-    my @parts_data;
-
-    for my $part_name ( @{ $self->{_part_names} } ) {
-        push @parts_data, [ 'lpstr', $part_name ];
-    }
-
-    $self->_write_vt_vector( 'lpstr', \@parts_data );
-
-    $self->xml_end_tag( 'TitlesOfParts' );
-}
-
-
-###############################################################################
-#
-# _write_vt_vector()
-#
-# Write the <vt:vector> element.
-#
-sub _write_vt_vector {
-
-    my $self      = shift;
-    my $base_type = shift;
-    my $data      = shift;
-    my $size      = @$data;
-
-    my @attributes = (
-        'size'     => $size,
-        'baseType' => $base_type,
-    );
-
-    $self->xml_start_tag( 'vt:vector', @attributes );
-
-    for my $aref ( @$data ) {
-        $self->xml_start_tag( 'vt:variant' ) if $base_type eq 'variant';
-        $self->_write_vt_data( @$aref );
-        $self->xml_end_tag( 'vt:variant' ) if $base_type eq 'variant';
-    }
-
-    $self->xml_end_tag( 'vt:vector' );
-}
-
-
-##############################################################################
-#
-# _write_vt_data()
-#
-# Write the <vt:*> elements such as <vt:lpstr> and <vt:if>.
-#
-sub _write_vt_data {
-
-    my $self = shift;
-    my $type = shift;
-    my $data = shift;
-
-    $self->xml_data_element( "vt:$type", $data );
-}
-
-
-###############################################################################
-#
-# _write_company()
-#
-# Write the <Company> element.
-#
-sub _write_company {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{company} || '';
-
-    $self->xml_data_element( 'Company', $data );
-}
-
-
-###############################################################################
-#
-# _write_manager()
-#
-# Write the <Manager> element.
-#
-sub _write_manager {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{manager};
-
-    return unless $data;
-
-    $self->xml_data_element( 'Manager', $data );
-}
-
-
-###############################################################################
-#
-# _write_links_up_to_date()
-#
-# Write the <LinksUpToDate> element.
-#
-sub _write_links_up_to_date {
-
-    my $self = shift;
-    my $data = 'false';
-
-    $self->xml_data_element( 'LinksUpToDate', $data );
-}
-
-
-###############################################################################
-#
-# _write_shared_doc()
-#
-# Write the <SharedDoc> element.
-#
-sub _write_shared_doc {
-
-    my $self = shift;
-    my $data = 'false';
-
-    $self->xml_data_element( 'SharedDoc', $data );
-}
-
-
-###############################################################################
-#
-# _write_hyperlink_base()
-#
-# Write the <HyperlinkBase> element.
-#
-sub _write_hyperlink_base {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{hyperlink_base};
-
-    return unless $data;
-
-    $self->xml_data_element( 'HyperlinkBase', $data );
-}
-
-
-###############################################################################
-#
-# _write_hyperlinks_changed()
-#
-# Write the <HyperlinksChanged> element.
-#
-sub _write_hyperlinks_changed {
-
-    my $self = shift;
-    my $data = 'false';
-
-    $self->xml_data_element( 'HyperlinksChanged', $data );
-}
-
-
-###############################################################################
-#
-# _write_app_version()
-#
-# Write the <AppVersion> element.
-#
-sub _write_app_version {
-
-    my $self = shift;
-    my $data = '12.0000';
-
-    $self->xml_data_element( 'AppVersion', $data );
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-App - A class for writing the Excel XLSX app.xml file.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/Comments.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/Comments.pm
deleted file mode 100644 (file)
index f7d9012..0000000
+++ /dev/null
@@ -1,426 +0,0 @@
-package Excel::Writer::XLSX::Package::Comments;
-
-###############################################################################
-#
-# Comments - A class for writing the Excel XLSX Comments files.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Package::XMLwriter;
-use Excel::Writer::XLSX::Utility qw(xl_rowcol_to_cell);
-
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_author_ids} = {};
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self          = shift;
-    my $comments_data = shift;
-
-    $self->xml_declaration;
-
-    # Write the comments element.
-    $self->_write_comments();
-
-    # Write the authors element.
-    $self->_write_authors( $comments_data );
-
-    # Write the commentList element.
-    $self->_write_comment_list( $comments_data );
-
-    $self->xml_end_tag( 'comments' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-##############################################################################
-#
-# _write_comments()
-#
-# Write the <comments> element.
-#
-sub _write_comments {
-
-    my $self  = shift;
-    my $xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
-
-    my @attributes = ( 'xmlns' => $xmlns );
-
-    $self->xml_start_tag( 'comments', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_authors()
-#
-# Write the <authors> element.
-#
-sub _write_authors {
-
-    my $self         = shift;
-    my $comment_data = shift;
-    my $author_count = 0;
-
-    $self->xml_start_tag( 'authors' );
-
-    for my $comment ( @$comment_data ) {
-        my $author = $comment->[3];
-
-        if ( defined $author && !exists $self->{_author_ids}->{$author} ) {
-
-            # Store the author id.
-            $self->{_author_ids}->{$author} = $author_count++;
-
-            # Write the author element.
-            $self->_write_author( $author );
-        }
-    }
-
-    $self->xml_end_tag( 'authors' );
-}
-
-
-##############################################################################
-#
-# _write_author()
-#
-# Write the <author> element.
-#
-sub _write_author {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'author', $data );
-}
-
-
-##############################################################################
-#
-# _write_comment_list()
-#
-# Write the <commentList> element.
-#
-sub _write_comment_list {
-
-    my $self         = shift;
-    my $comment_data = shift;
-
-    $self->xml_start_tag( 'commentList' );
-
-    for my $comment ( @$comment_data ) {
-        my $row    = $comment->[0];
-        my $col    = $comment->[1];
-        my $text   = $comment->[2];
-        my $author = $comment->[3];
-        my $font   = $comment->[7];
-        my $size   = $comment->[8];
-
-        # Look up the author id.
-        my $author_id = undef;
-        $author_id = $self->{_author_ids}->{$author} if defined $author;
-
-        # Write the comment element.
-        $self->_write_comment( $row, $col, $text, $author_id, $font, $size );
-    }
-
-    $self->xml_end_tag( 'commentList' );
-}
-
-
-##############################################################################
-#
-# _write_comment()
-#
-# Write the <comment> element.
-#
-sub _write_comment {
-
-    my $self      = shift;
-    my $row       = shift;
-    my $col       = shift;
-    my $text      = shift;
-    my $author_id = shift;
-    my $ref       = xl_rowcol_to_cell( $row, $col );
-    my $font      = shift;
-    my $size      = shift;
-
-    my @attributes = ( 'ref' => $ref );
-
-    push @attributes, ( 'authorId' => $author_id ) if defined $author_id;
-
-
-    $self->xml_start_tag( 'comment', @attributes );
-
-    # Write the text element.
-    $self->_write_text( $text, [$font, $size] );
-
-
-    $self->xml_end_tag( 'comment' );
-}
-
-
-##############################################################################
-#
-# _write_text()
-#
-# Write the <text> element.
-#
-sub _write_text {
-
-    my $self = shift;
-    my $text = shift;
-    my $fmt  = shift;
-
-    $self->xml_start_tag( 'text' );
-
-    # Write the text r element.
-    $self->_write_text_r( $text, $fmt );
-
-    $self->xml_end_tag( 'text' );
-}
-
-
-##############################################################################
-#
-# _write_text_r()
-#
-# Write the <r> element.
-#
-sub _write_text_r {
-
-    my $self = shift;
-    my $text = shift;
-    my $fmt  = shift;
-
-    $self->xml_start_tag( 'r' );
-
-    # Write the rPr element.
-    $self->_write_r_pr($fmt);
-
-    # Write the text r element.
-    $self->_write_text_t( $text );
-
-    $self->xml_end_tag( 'r' );
-}
-
-
-##############################################################################
-#
-# _write_text_t()
-#
-# Write the text <t> element.
-#
-sub _write_text_t {
-
-    my $self = shift;
-    my $text = shift;
-
-    my @attributes = ();
-
-    if ( $text =~ /^\s/ || $text =~ /\s$/ ) {
-        push @attributes, ( 'xml:space' => 'preserve' );
-    }
-
-    $self->xml_data_element( 't', $text, @attributes );
-}
-
-
-##############################################################################
-#
-# _write_r_pr()
-#
-# Write the <rPr> element.
-#
-sub _write_r_pr {
-
-    my $self = shift;
-    my $fmt  = shift;
-
-    $self->xml_start_tag( 'rPr' );
-
-    # Write the sz element.
-    $self->_write_sz($fmt->[1]);
-
-    # Write the color element.
-    $self->_write_color();
-
-    # Write the rFont element.
-    $self->_write_r_font($fmt->[0]);
-
-    # Write the family element.
-    $self->_write_family();
-
-    $self->xml_end_tag( 'rPr' );
-}
-
-
-##############################################################################
-#
-# _write_sz()
-#
-# Write the <sz> element.
-#
-sub _write_sz {
-
-    my $self = shift;
-    my $val  = shift || 8;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'sz', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_color()
-#
-# Write the <color> element.
-#
-sub _write_color {
-
-    my $self    = shift;
-    my $indexed = 81;
-
-    my @attributes = ( 'indexed' => $indexed );
-
-    $self->xml_empty_tag( 'color', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_r_font()
-#
-# Write the <rFont> element.
-#
-sub _write_r_font {
-
-    my $self = shift;
-    my $val  = shift || 'Tahoma';
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'rFont', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_family()
-#
-# Write the <family> element.
-#
-sub _write_family {
-
-    my $self = shift;
-    my $val  = 2;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'family', @attributes );
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Comments - A class for writing the Excel XLSX Comments files.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/ContentTypes.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/ContentTypes.pm
deleted file mode 100644 (file)
index 166d1ef..0000000
+++ /dev/null
@@ -1,487 +0,0 @@
-package Excel::Writer::XLSX::Package::ContentTypes;
-
-###############################################################################
-#
-# Excel::Writer::XLSX::Package::ContentTypes - A class for writing the Excel
-# XLS [Content_Types] file.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Package::XMLwriter;
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Package data.
-#
-###############################################################################
-
-my $app_package  = 'application/vnd.openxmlformats-package.';
-my $app_document = 'application/vnd.openxmlformats-officedocument.';
-
-our @defaults = (
-    [ 'rels', $app_package . 'relationships+xml' ],
-    [ 'xml',  'application/xml' ],
-);
-
-our @overrides = (
-    [ '/docProps/app.xml',    $app_document . 'extended-properties+xml' ],
-    [ '/docProps/core.xml',   $app_package . 'core-properties+xml' ],
-    [ '/xl/styles.xml',       $app_document . 'spreadsheetml.styles+xml' ],
-    [ '/xl/theme/theme1.xml', $app_document . 'theme+xml' ],
-    [ '/xl/workbook.xml',     $app_document . 'spreadsheetml.sheet.main+xml' ],
-);
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_defaults}  = [@defaults];
-    $self->{_overrides} = [@overrides];
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration;
-    $self->_write_types();
-    $self->_write_defaults();
-    $self->_write_overrides();
-
-    $self->xml_end_tag( 'Types' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# _add_default()
-#
-# Add elements to the ContentTypes defaults.
-#
-sub _add_default {
-
-    my $self         = shift;
-    my $part_name    = shift;
-    my $content_type = shift;
-
-    push @{ $self->{_defaults} }, [ $part_name, $content_type ];
-
-}
-
-
-###############################################################################
-#
-# _add_override()
-#
-# Add elements to the ContentTypes overrides.
-#
-sub _add_override {
-
-    my $self         = shift;
-    my $part_name    = shift;
-    my $content_type = shift;
-
-    push @{ $self->{_overrides} }, [ $part_name, $content_type ];
-
-}
-
-
-###############################################################################
-#
-# _add_worksheet_name()
-#
-# Add the name of a worksheet to the ContentTypes overrides.
-#
-sub _add_worksheet_name {
-
-    my $self           = shift;
-    my $worksheet_name = shift;
-
-    $worksheet_name = "/xl/worksheets/$worksheet_name.xml";
-
-    $self->_add_override( $worksheet_name,
-        $app_document . 'spreadsheetml.worksheet+xml' );
-}
-
-
-###############################################################################
-#
-# _add_chartsheet_name()
-#
-# Add the name of a chartsheet to the ContentTypes overrides.
-#
-sub _add_chartsheet_name {
-
-    my $self            = shift;
-    my $chartsheet_name = shift;
-
-    $chartsheet_name = "/xl/chartsheets/$chartsheet_name.xml";
-
-    $self->_add_override( $chartsheet_name,
-        $app_document . 'spreadsheetml.chartsheet+xml' );
-}
-
-
-###############################################################################
-#
-# _add_chart_name()
-#
-# Add the name of a chart to the ContentTypes overrides.
-#
-sub _add_chart_name {
-
-    my $self       = shift;
-    my $chart_name = shift;
-
-    $chart_name = "/xl/charts/$chart_name.xml";
-
-    $self->_add_override( $chart_name, $app_document . 'drawingml.chart+xml' );
-}
-
-
-###############################################################################
-#
-# _add_drawing_name()
-#
-# Add the name of a drawing to the ContentTypes overrides.
-#
-sub _add_drawing_name {
-
-    my $self         = shift;
-    my $drawing_name = shift;
-
-    $drawing_name = "/xl/drawings/$drawing_name.xml";
-
-    $self->_add_override( $drawing_name, $app_document . 'drawing+xml' );
-}
-
-
-###############################################################################
-#
-# _add_vml_name()
-#
-# Add the name of a VML drawing to the ContentTypes defaults.
-#
-sub _add_vml_name {
-
-    my $self = shift;
-
-    $self->_add_default( 'vml', $app_document . 'vmlDrawing' );
-}
-
-
-###############################################################################
-#
-# _add_comment_name()
-#
-# Add the name of a comment to the ContentTypes overrides.
-#
-sub _add_comment_name {
-
-    my $self         = shift;
-    my $comment_name = shift;
-
-    $comment_name = "/xl/$comment_name.xml";
-
-    $self->_add_override( $comment_name,
-        $app_document . 'spreadsheetml.comments+xml' );
-}
-
-###############################################################################
-#
-# _Add_shared_strings()
-#
-# Add the sharedStrings link to the ContentTypes overrides.
-#
-sub _add_shared_strings {
-
-    my $self = shift;
-
-    $self->_add_override( '/xl/sharedStrings.xml',
-        $app_document . 'spreadsheetml.sharedStrings+xml' );
-}
-
-
-###############################################################################
-#
-# _add_calc_chain()
-#
-# Add the calcChain link to the ContentTypes overrides.
-#
-sub _add_calc_chain {
-
-    my $self = shift;
-
-    $self->_add_override( '/xl/calcChain.xml',
-        $app_document . 'spreadsheetml.calcChain+xml' );
-}
-
-
-###############################################################################
-#
-# _add_image_types()
-#
-# Add the image default types.
-#
-sub _add_image_types {
-
-    my $self  = shift;
-    my %types = @_;
-
-    for my $type ( keys %types ) {
-        $self->_add_default( $type, 'image/' . $type );
-    }
-}
-
-
-###############################################################################
-#
-# _add_table_name()
-#
-# Add the name of a table to the ContentTypes overrides.
-#
-sub _add_table_name {
-
-    my $self       = shift;
-    my $table_name = shift;
-
-    $table_name = "/xl/tables/$table_name.xml";
-
-    $self->_add_override( $table_name,
-        $app_document . 'spreadsheetml.table+xml' );
-}
-
-
-###############################################################################
-#
-# _add_vba_project()
-#
-# Add a vbaProject to the ContentTypes defaults.
-#
-sub _add_vba_project {
-
-    my $self = shift;
-
-    # Change the workbook.xml content-type from xlsx to xlsm.
-    for my $aref ( @{ $self->{_overrides} } ) {
-        if ( $aref->[0] eq '/xl/workbook.xml' ) {
-            $aref->[1] = 'application/vnd.ms-excel.sheet.macroEnabled.main+xml';
-        }
-    }
-
-    $self->_add_default( 'bin', 'application/vnd.ms-office.vbaProject' );
-}
-
-
-###############################################################################
-#
-# _add_custom_properties()
-#
-# Add the custom properties to the ContentTypes overrides.
-#
-sub _add_custom_properties {
-
-    my $self   = shift;
-    my $custom = "/docProps/custom.xml";
-
-    $self->_add_override( $custom, $app_document . 'custom-properties+xml' );
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _write_defaults()
-#
-# Write out all of the <Default> types.
-#
-sub _write_defaults {
-
-    my $self = shift;
-
-    for my $aref ( @{ $self->{_defaults} } ) {
-        #<<<
-        $self->xml_empty_tag(
-            'Default',
-            'Extension',   $aref->[0],
-            'ContentType', $aref->[1] );
-        #>>>
-    }
-}
-
-
-###############################################################################
-#
-# _write_overrides()
-#
-# Write out all of the <Override> types.
-#
-sub _write_overrides {
-
-    my $self = shift;
-
-    for my $aref ( @{ $self->{_overrides} } ) {
-        #<<<
-        $self->xml_empty_tag(
-            'Override',
-            'PartName',    $aref->[0],
-            'ContentType', $aref->[1] );
-        #>>>
-    }
-}
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _write_types()
-#
-# Write the <Types> element.
-#
-sub _write_types {
-
-    my $self  = shift;
-    my $xmlns = 'http://schemas.openxmlformats.org/package/2006/content-types';
-
-    my @attributes = ( 'xmlns' => $xmlns, );
-
-    $self->xml_start_tag( 'Types', @attributes );
-}
-
-###############################################################################
-#
-# _write_default()
-#
-# Write the <Default> element.
-#
-sub _write_default {
-
-    my $self         = shift;
-    my $extension    = shift;
-    my $content_type = shift;
-
-    my @attributes = (
-        'Extension'   => $extension,
-        'ContentType' => $content_type,
-    );
-
-    $self->xml_empty_tag( 'Default', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_override()
-#
-# Write the <Override> element.
-#
-sub _write_override {
-
-    my $self         = shift;
-    my $part_name    = shift;
-    my $content_type = shift;
-    my $writer       = $self;
-
-    my @attributes = (
-        'PartName'    => $part_name,
-        'ContentType' => $content_type,
-    );
-
-    $self->xml_empty_tag( 'Override', @attributes );
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Excel::Writer::XLSX::Package::ContentTypes - A class for writing the Excel XLSX [Content_Types] file.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/Core.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/Core.pm
deleted file mode 100644 (file)
index b6431f3..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-package Excel::Writer::XLSX::Package::Core;
-
-###############################################################################
-#
-# Core - A class for writing the Excel XLSX core.xml file.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Package::XMLwriter;
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_properties} = {};
-    $self->{_createtime}  = [ gmtime() ];
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration;
-    $self->_write_cp_core_properties();
-    $self->_write_dc_title();
-    $self->_write_dc_subject();
-    $self->_write_dc_creator();
-    $self->_write_cp_keywords();
-    $self->_write_dc_description();
-    $self->_write_cp_last_modified_by();
-    $self->_write_dcterms_created();
-    $self->_write_dcterms_modified();
-    $self->_write_cp_category();
-    $self->_write_cp_content_status();
-
-    $self->xml_end_tag( 'cp:coreProperties' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# _set_properties()
-#
-# Set the document properties.
-#
-sub _set_properties {
-
-    my $self       = shift;
-    my $properties = shift;
-
-    $self->{_properties} = $properties;
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _datetime_to_iso8601_date()
-#
-# Convert a gmtime/localtime() date to a ISO 8601 style "2010-01-01T00:00:00Z"
-# date. Excel always treats this as a utc date/time.
-#
-sub _datetime_to_iso8601_date {
-
-    my $self = shift;
-    my $gmtime = shift || $self->{_createtime};
-
-    my ( $seconds, $minutes, $hours, $day, $month, $year ) = @$gmtime;
-
-    $month++;
-    $year += 1900;
-
-    my $date = sprintf "%4d-%02d-%02dT%02d:%02d:%02dZ", $year, $month, $day,
-      $hours, $minutes, $seconds;
-}
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _write_cp_core_properties()
-#
-# Write the <cp:coreProperties> element.
-#
-sub _write_cp_core_properties {
-
-    my $self = shift;
-    my $xmlns_cp =
-      'http://schemas.openxmlformats.org/package/2006/metadata/core-properties';
-    my $xmlns_dc       = 'http://purl.org/dc/elements/1.1/';
-    my $xmlns_dcterms  = 'http://purl.org/dc/terms/';
-    my $xmlns_dcmitype = 'http://purl.org/dc/dcmitype/';
-    my $xmlns_xsi      = 'http://www.w3.org/2001/XMLSchema-instance';
-
-    my @attributes = (
-        'xmlns:cp'       => $xmlns_cp,
-        'xmlns:dc'       => $xmlns_dc,
-        'xmlns:dcterms'  => $xmlns_dcterms,
-        'xmlns:dcmitype' => $xmlns_dcmitype,
-        'xmlns:xsi'      => $xmlns_xsi,
-    );
-
-    $self->xml_start_tag( 'cp:coreProperties', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_dc_creator()
-#
-# Write the <dc:creator> element.
-#
-sub _write_dc_creator {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{author} || '';
-
-    $self->xml_data_element( 'dc:creator', $data );
-}
-
-
-###############################################################################
-#
-# _write_cp_last_modified_by()
-#
-# Write the <cp:lastModifiedBy> element.
-#
-sub _write_cp_last_modified_by {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{author} || '';
-
-    $self->xml_data_element( 'cp:lastModifiedBy', $data );
-}
-
-
-###############################################################################
-#
-# _write_dcterms_created()
-#
-# Write the <dcterms:created> element.
-#
-sub _write_dcterms_created {
-
-    my $self     = shift;
-    my $date     = $self->{_properties}->{created};
-    my $xsi_type = 'dcterms:W3CDTF';
-
-    $date = $self->_datetime_to_iso8601_date( $date );
-
-    my @attributes = ( 'xsi:type' => $xsi_type, );
-
-    $self->xml_data_element( 'dcterms:created', $date, @attributes );
-}
-
-
-###############################################################################
-#
-# _write_dcterms_modified()
-#
-# Write the <dcterms:modified> element.
-#
-sub _write_dcterms_modified {
-
-    my $self     = shift;
-    my $date     = $self->{_properties}->{created};
-    my $xsi_type = 'dcterms:W3CDTF';
-
-    $date = $self->_datetime_to_iso8601_date( $date );
-
-    my @attributes = ( 'xsi:type' => $xsi_type, );
-
-    $self->xml_data_element( 'dcterms:modified', $date, @attributes );
-}
-
-
-##############################################################################
-#
-# _write_dc_title()
-#
-# Write the <dc:title> element.
-#
-sub _write_dc_title {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{title};
-
-    return unless $data;
-
-    $self->xml_data_element( 'dc:title', $data );
-}
-
-
-##############################################################################
-#
-# _write_dc_subject()
-#
-# Write the <dc:subject> element.
-#
-sub _write_dc_subject {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{subject};
-
-    return unless $data;
-
-    $self->xml_data_element( 'dc:subject', $data );
-}
-
-
-##############################################################################
-#
-# _write_cp_keywords()
-#
-# Write the <cp:keywords> element.
-#
-sub _write_cp_keywords {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{keywords};
-
-    return unless $data;
-
-    $self->xml_data_element( 'cp:keywords', $data );
-}
-
-
-##############################################################################
-#
-# _write_dc_description()
-#
-# Write the <dc:description> element.
-#
-sub _write_dc_description {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{comments};
-
-    return unless $data;
-
-    $self->xml_data_element( 'dc:description', $data );
-}
-
-
-##############################################################################
-#
-# _write_cp_category()
-#
-# Write the <cp:category> element.
-#
-sub _write_cp_category {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{category};
-
-    return unless $data;
-
-    $self->xml_data_element( 'cp:category', $data );
-}
-
-
-##############################################################################
-#
-# _write_cp_content_status()
-#
-# Write the <cp:contentStatus> element.
-#
-sub _write_cp_content_status {
-
-    my $self = shift;
-    my $data = $self->{_properties}->{status};
-
-    return unless $data;
-
-    $self->xml_data_element( 'cp:contentStatus', $data );
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Core - A class for writing the Excel XLSX core.xml file.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/Custom.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/Custom.pm
deleted file mode 100644 (file)
index 8489f57..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-package Excel::Writer::XLSX::Package::Custom;
-
-###############################################################################
-#
-# Custom - A class for writing the Excel XLSX custom.xml file for custom
-# workbook properties.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Package::XMLwriter;
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_properties} = [];
-    $self->{_pid}        = 1;
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration;
-
-    $self->_write_properties();
-
-    $self->xml_end_tag( 'Properties' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# _set_properties()
-#
-# Set the document properties.
-#
-sub _set_properties {
-
-    my $self       = shift;
-    my $properties = shift;
-
-    $self->{_properties} = $properties;
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _write_properties()
-#
-# Write the <Properties> element.
-#
-sub _write_properties {
-
-    my $self     = shift;
-    my $schema   = 'http://schemas.openxmlformats.org/officeDocument/2006/';
-    my $xmlns    = $schema . 'custom-properties';
-    my $xmlns_vt = $schema . 'docPropsVTypes';
-
-    my @attributes = (
-        'xmlns'    => $xmlns,
-        'xmlns:vt' => $xmlns_vt,
-    );
-
-    $self->xml_start_tag( 'Properties', @attributes );
-
-    for my $property ( @{ $self->{_properties} } ) {
-
-        # Write the property element.
-        $self->_write_property( $property );
-    }
-}
-
-##############################################################################
-#
-# _write_property()
-#
-# Write the <property> element.
-#
-sub _write_property {
-
-    my $self     = shift;
-    my $property = shift;
-    my $fmtid    = '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}';
-
-    $self->{_pid}++;
-
-    my ( $name, $value, $type ) = @$property;
-
-
-    my @attributes = (
-        'fmtid' => $fmtid,
-        'pid'   => $self->{_pid},
-        'name'  => $name,
-    );
-
-    $self->xml_start_tag( 'property', @attributes );
-
-    if ( $type eq 'date' ) {
-
-        # Write the vt:filetime element.
-        $self->_write_vt_filetime( $value );
-    }
-    elsif ( $type eq 'number' ) {
-
-        # Write the vt:r8 element.
-        $self->_write_vt_r8( $value );
-    }
-    elsif ( $type eq 'number_int' ) {
-
-        # Write the vt:i4 element.
-        $self->_write_vt_i4( $value );
-    }
-    elsif ( $type eq 'bool' ) {
-
-        # Write the vt:bool element.
-        $self->_write_vt_bool( $value );
-    }
-    else {
-
-        # Write the vt:lpwstr element.
-        $self->_write_vt_lpwstr( $value );
-    }
-
-
-    $self->xml_end_tag( 'property' );
-}
-
-
-##############################################################################
-#
-# _write_vt_lpwstr()
-#
-# Write the <vt:lpwstr> element.
-#
-sub _write_vt_lpwstr {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'vt:lpwstr', $data );
-}
-
-
-##############################################################################
-#
-# _write_vt_i4()
-#
-# Write the <vt:i4> element.
-#
-sub _write_vt_i4 {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'vt:i4', $data );
-}
-
-
-##############################################################################
-#
-# _write_vt_r8()
-#
-# Write the <vt:r8> element.
-#
-sub _write_vt_r8 {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'vt:r8', $data );
-}
-
-
-##############################################################################
-#
-# _write_vt_bool()
-#
-# Write the <vt:bool> element.
-#
-sub _write_vt_bool {
-
-    my $self = shift;
-    my $data = shift;
-
-    if ( $data ) {
-        $data = 'true';
-    }
-    else {
-        $data = 'false';
-    }
-
-    $self->xml_data_element( 'vt:bool', $data );
-}
-
-##############################################################################
-#
-# _write_vt_filetime()
-#
-# Write the <vt:filetime> element.
-#
-sub _write_vt_filetime {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'vt:filetime', $data );
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Custom - A class for writing the Excel XLSX custom.xml file.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/Packager.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/Packager.pm
deleted file mode 100644 (file)
index 6d0dca3..0000000
+++ /dev/null
@@ -1,1010 +0,0 @@
-package Excel::Writer::XLSX::Package::Packager;
-
-###############################################################################
-#
-# Packager - A class for creating the Excel XLSX package.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Exporter;
-use Carp;
-use File::Copy;
-use Excel::Writer::XLSX::Package::App;
-use Excel::Writer::XLSX::Package::Comments;
-use Excel::Writer::XLSX::Package::ContentTypes;
-use Excel::Writer::XLSX::Package::Core;
-use Excel::Writer::XLSX::Package::Custom;
-use Excel::Writer::XLSX::Package::Relationships;
-use Excel::Writer::XLSX::Package::SharedStrings;
-use Excel::Writer::XLSX::Package::Styles;
-use Excel::Writer::XLSX::Package::Table;
-use Excel::Writer::XLSX::Package::Theme;
-use Excel::Writer::XLSX::Package::VML;
-
-our @ISA     = qw(Exporter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_package_dir}      = '';
-    $self->{_workbook}         = undef;
-    $self->{_worksheet_count}  = 0;
-    $self->{_chartsheet_count} = 0;
-    $self->{_chart_count}      = 0;
-    $self->{_drawing_count}    = 0;
-    $self->{_table_count}      = 0;
-    $self->{_named_ranges}     = [];
-
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _set_package_dir()
-#
-# Set the XLSX OPC package directory.
-#
-sub _set_package_dir {
-
-    my $self = shift;
-
-    $self->{_package_dir} = shift;
-}
-
-
-###############################################################################
-#
-# _add_workbook()
-#
-# Add the Excel::Writer::XLSX::Workbook object to the package.
-#
-sub _add_workbook {
-
-    my $self        = shift;
-    my $workbook    = shift;
-
-    $self->{_workbook}          = $workbook;
-    $self->{_chart_count}       = scalar @{ $workbook->{_charts} };
-    $self->{_drawing_count}     = scalar @{ $workbook->{_drawings} };
-    $self->{_num_vml_files}     = $workbook->{_num_vml_files};
-    $self->{_num_comment_files} = $workbook->{_num_comment_files};
-    $self->{_named_ranges}      = $workbook->{_named_ranges};
-
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-        if ( $worksheet->{_is_chartsheet} ) {
-            $self->{_chartsheet_count}++;
-        }
-        else {
-            $self->{_worksheet_count}++;
-        }
-    }
-}
-
-
-###############################################################################
-#
-# _create_package()
-#
-# Write the xml files that make up the XLXS OPC package.
-#
-sub _create_package {
-
-    my $self = shift;
-
-    $self->_write_worksheet_files();
-    $self->_write_chartsheet_files();
-    $self->_write_workbook_file();
-    $self->_write_chart_files();
-    $self->_write_drawing_files();
-    $self->_write_vml_files();
-    $self->_write_comment_files();
-    $self->_write_table_files();
-    $self->_write_shared_strings_file();
-    $self->_write_app_file();
-    $self->_write_core_file();
-    $self->_write_custom_file();
-    $self->_write_content_types_file();
-    $self->_write_styles_file();
-    $self->_write_theme_file();
-    $self->_write_root_rels_file();
-    $self->_write_workbook_rels_file();
-    $self->_write_worksheet_rels_files();
-    $self->_write_chartsheet_rels_files();
-    $self->_write_drawing_rels_files();
-    $self->_add_image_files();
-    $self->_add_vba_project();
-}
-
-
-###############################################################################
-#
-# _write_workbook_file()
-#
-# Write the workbook.xml file.
-#
-sub _write_workbook_file {
-
-    my $self     = shift;
-    my $dir      = $self->{_package_dir};
-    my $workbook = $self->{_workbook};
-
-    _mkdir( $dir . '/xl' );
-
-    $workbook->_set_xml_writer( $dir . '/xl/workbook.xml' );
-    $workbook->_assemble_xml_file();
-}
-
-
-###############################################################################
-#
-# _write_worksheet_files()
-#
-# Write the worksheet files.
-#
-sub _write_worksheet_files {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-
-    _mkdir( $dir . '/xl' );
-    _mkdir( $dir . '/xl/worksheets' );
-
-    my $index = 1;
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-        next if $worksheet->{_is_chartsheet};
-
-        $worksheet->_set_xml_writer(
-            $dir . '/xl/worksheets/sheet' . $index++ . '.xml' );
-        $worksheet->_assemble_xml_file();
-
-    }
-}
-
-
-###############################################################################
-#
-# _write_chartsheet_files()
-#
-# Write the chartsheet files.
-#
-sub _write_chartsheet_files {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-
-    my $index = 1;
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-        next unless $worksheet->{_is_chartsheet};
-
-        _mkdir( $dir . '/xl' );
-        _mkdir( $dir . '/xl/chartsheets' );
-
-        $worksheet->_set_xml_writer(
-            $dir . '/xl/chartsheets/sheet' . $index++ . '.xml' );
-        $worksheet->_assemble_xml_file();
-
-    }
-}
-
-
-###############################################################################
-#
-# _write_chart_files()
-#
-# Write the chart files.
-#
-sub _write_chart_files {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-
-    return unless @{ $self->{_workbook}->{_charts} };
-
-    _mkdir( $dir . '/xl' );
-    _mkdir( $dir . '/xl/charts' );
-
-    my $index = 1;
-    for my $chart ( @{ $self->{_workbook}->{_charts} } ) {
-        $chart->_set_xml_writer(
-            $dir . '/xl/charts/chart' . $index++ . '.xml' );
-        $chart->_assemble_xml_file();
-
-    }
-}
-
-
-###############################################################################
-#
-# _write_drawing_files()
-#
-# Write the drawing files.
-#
-sub _write_drawing_files {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-
-    return unless $self->{_drawing_count};
-
-    _mkdir( $dir . '/xl' );
-    _mkdir( $dir . '/xl/drawings' );
-
-    my $index = 1;
-    for my $drawing ( @{ $self->{_workbook}->{_drawings} } ) {
-        $drawing->_set_xml_writer(
-            $dir . '/xl/drawings/drawing' . $index++ . '.xml' );
-        $drawing->_assemble_xml_file();
-    }
-}
-
-
-###############################################################################
-#
-# _write_vml_files()
-#
-# Write the comment VML files.
-#
-sub _write_vml_files {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-
-    my $index = 1;
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-
-        next if !$worksheet->{_has_vml} and !$worksheet->{_has_header_vml};
-
-        _mkdir( $dir . '/xl' );
-        _mkdir( $dir . '/xl/drawings' );
-
-        if ( $worksheet->{_has_vml} ) {
-            my $vml = Excel::Writer::XLSX::Package::VML->new();
-
-            $vml->_set_xml_writer(
-                $dir . '/xl/drawings/vmlDrawing' . $index . '.vml' );
-            $vml->_assemble_xml_file(
-                $worksheet->{_vml_data_id},    $worksheet->{_vml_shape_id},
-                $worksheet->{_comments_array}, $worksheet->{_buttons_array},
-                undef
-            );
-
-            $index++;
-        }
-
-        if ( $worksheet->{_has_header_vml} ) {
-            my $vml = Excel::Writer::XLSX::Package::VML->new();
-
-            $vml->_set_xml_writer(
-                $dir . '/xl/drawings/vmlDrawing' . $index . '.vml' );
-            $vml->_assemble_xml_file(
-                $worksheet->{_vml_header_id},
-                $worksheet->{_vml_header_id} * 1024,
-                undef, undef, $worksheet->{_header_images_array}
-            );
-
-            $self->_write_vml_drawing_rels_file($worksheet, $index);
-
-            $index++;
-        }
-    }
-}
-
-
-###############################################################################
-#
-# _write_comment_files()
-#
-# Write the comment files.
-#
-sub _write_comment_files {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-
-    my $index = 1;
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-        next unless $worksheet->{_has_comments};
-
-        my $comment = Excel::Writer::XLSX::Package::Comments->new();
-
-        _mkdir( $dir . '/xl' );
-        _mkdir( $dir . '/xl/drawings' );
-
-        $comment->_set_xml_writer( $dir . '/xl/comments' . $index++ . '.xml' );
-        $comment->_assemble_xml_file( $worksheet->{_comments_array} );
-    }
-}
-
-
-###############################################################################
-#
-# _write_shared_strings_file()
-#
-# Write the sharedStrings.xml file.
-#
-sub _write_shared_strings_file {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-    my $sst  = Excel::Writer::XLSX::Package::SharedStrings->new();
-
-    my $total    = $self->{_workbook}->{_str_total};
-    my $unique   = $self->{_workbook}->{_str_unique};
-    my $sst_data = $self->{_workbook}->{_str_array};
-
-    return unless $total > 0;
-
-    _mkdir( $dir . '/xl' );
-
-    $sst->_set_string_count( $total );
-    $sst->_set_unique_count( $unique );
-    $sst->_add_strings( $sst_data );
-
-    $sst->_set_xml_writer( $dir . '/xl/sharedStrings.xml' );
-    $sst->_assemble_xml_file();
-}
-
-
-###############################################################################
-#
-# _write_app_file()
-#
-# Write the app.xml file.
-#
-sub _write_app_file {
-
-    my $self       = shift;
-    my $dir        = $self->{_package_dir};
-    my $properties = $self->{_workbook}->{_doc_properties};
-    my $app        = Excel::Writer::XLSX::Package::App->new();
-
-    _mkdir( $dir . '/docProps' );
-
-    # Add the Worksheet heading pairs.
-    $app->_add_heading_pair( [ 'Worksheets', $self->{_worksheet_count} ] );
-
-    # Add the Chartsheet heading pairs.
-    $app->_add_heading_pair( [ 'Charts', $self->{_chartsheet_count} ] );
-
-    # Add the Worksheet parts.
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-        next if $worksheet->{_is_chartsheet};
-        $app->_add_part_name( $worksheet->get_name() );
-    }
-
-    # Add the Chartsheet parts.
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-        next unless $worksheet->{_is_chartsheet};
-        $app->_add_part_name( $worksheet->get_name() );
-    }
-
-    # Add the Named Range heading pairs.
-    if ( my $range_count = scalar @{ $self->{_named_ranges} } ) {
-        $app->_add_heading_pair( [ 'Named Ranges', $range_count ] );
-    }
-
-    # Add the Named Ranges parts.
-    for my $named_range ( @{ $self->{_named_ranges} } ) {
-        $app->_add_part_name( $named_range );
-    }
-
-    $app->_set_properties( $properties );
-
-    $app->_set_xml_writer( $dir . '/docProps/app.xml' );
-    $app->_assemble_xml_file();
-}
-
-
-###############################################################################
-#
-# _write_core_file()
-#
-# Write the core.xml file.
-#
-sub _write_core_file {
-
-    my $self       = shift;
-    my $dir        = $self->{_package_dir};
-    my $properties = $self->{_workbook}->{_doc_properties};
-    my $core       = Excel::Writer::XLSX::Package::Core->new();
-
-    _mkdir( $dir . '/docProps' );
-
-    $core->_set_properties( $properties );
-    $core->_set_xml_writer( $dir . '/docProps/core.xml' );
-    $core->_assemble_xml_file();
-}
-
-
-###############################################################################
-#
-# _write_custom_file()
-#
-# Write the custom.xml file.
-#
-sub _write_custom_file {
-
-    my $self       = shift;
-    my $dir        = $self->{_package_dir};
-    my $properties = $self->{_workbook}->{_custom_properties};
-    my $custom     = Excel::Writer::XLSX::Package::Custom->new();
-
-    return if !@$properties;
-
-    _mkdir( $dir . '/docProps' );
-
-    $custom->_set_properties( $properties );
-    $custom->_set_xml_writer( $dir . '/docProps/custom.xml' );
-    $custom->_assemble_xml_file();
-}
-
-
-###############################################################################
-#
-# _write_content_types_file()
-#
-# Write the ContentTypes.xml file.
-#
-sub _write_content_types_file {
-
-    my $self    = shift;
-    my $dir     = $self->{_package_dir};
-    my $content = Excel::Writer::XLSX::Package::ContentTypes->new();
-
-    $content->_add_image_types( %{ $self->{_workbook}->{_image_types} } );
-
-    my $worksheet_index  = 1;
-    my $chartsheet_index = 1;
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-        if ( $worksheet->{_is_chartsheet} ) {
-            $content->_add_chartsheet_name( 'sheet' . $chartsheet_index++ );
-        }
-        else {
-            $content->_add_worksheet_name( 'sheet' . $worksheet_index++ );
-        }
-    }
-
-    for my $i ( 1 .. $self->{_chart_count} ) {
-        $content->_add_chart_name( 'chart' . $i );
-    }
-
-    for my $i ( 1 .. $self->{_drawing_count} ) {
-        $content->_add_drawing_name( 'drawing' . $i );
-    }
-
-    if ( $self->{_num_vml_files} ) {
-        $content->_add_vml_name();
-    }
-
-    for my $i ( 1 .. $self->{_table_count} ) {
-        $content->_add_table_name( 'table' . $i );
-    }
-
-    for my $i ( 1 .. $self->{_num_comment_files} ) {
-        $content->_add_comment_name( 'comments' . $i );
-    }
-
-    # Add the sharedString rel if there is string data in the workbook.
-    if ( $self->{_workbook}->{_str_total} ) {
-        $content->_add_shared_strings();
-    }
-
-    # Add vbaProject if present.
-    if ( $self->{_workbook}->{_vba_project} ) {
-        $content->_add_vba_project();
-    }
-
-    # Add the custom properties if present.
-    if ( @{ $self->{_workbook}->{_custom_properties} } ) {
-        $content->_add_custom_properties();
-    }
-
-    $content->_set_xml_writer( $dir . '/[Content_Types].xml' );
-    $content->_assemble_xml_file();
-}
-
-
-###############################################################################
-#
-# _write_styles_file()
-#
-# Write the style xml file.
-#
-sub _write_styles_file {
-
-    my $self               = shift;
-    my $dir                = $self->{_package_dir};
-    my $xf_formats         = $self->{_workbook}->{_xf_formats};
-    my $palette            = $self->{_workbook}->{_palette};
-    my $font_count         = $self->{_workbook}->{_font_count};
-    my $num_format_count   = $self->{_workbook}->{_num_format_count};
-    my $border_count       = $self->{_workbook}->{_border_count};
-    my $fill_count         = $self->{_workbook}->{_fill_count};
-    my $custom_colors      = $self->{_workbook}->{_custom_colors};
-    my $dxf_formats        = $self->{_workbook}->{_dxf_formats};
-
-    my $rels = Excel::Writer::XLSX::Package::Styles->new();
-
-    _mkdir( $dir . '/xl' );
-
-    $rels->_set_style_properties(
-        $xf_formats,
-        $palette,
-        $font_count,
-        $num_format_count,
-        $border_count,
-        $fill_count,
-        $custom_colors,
-        $dxf_formats,
-    );
-
-    $rels->_set_xml_writer( $dir . '/xl/styles.xml' );
-    $rels->_assemble_xml_file();
-}
-
-
-###############################################################################
-#
-# _write_theme_file()
-#
-# Write the style xml file.
-#
-sub _write_theme_file {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-    my $rels = Excel::Writer::XLSX::Package::Theme->new();
-
-    _mkdir( $dir . '/xl' );
-    _mkdir( $dir . '/xl/theme' );
-
-    $rels->_set_xml_writer( $dir . '/xl/theme/theme1.xml' );
-    $rels->_assemble_xml_file();
-}
-
-
-###############################################################################
-#
-# _write_table_files()
-#
-# Write the table files.
-#
-sub _write_table_files {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-
-    my $index = 1;
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-        my @table_props = @{ $worksheet->{_tables} };
-
-        next unless @table_props;
-
-        _mkdir( $dir . '/xl' );
-        _mkdir( $dir . '/xl/tables' );
-
-        for my $table_props ( @table_props ) {
-
-            my $table = Excel::Writer::XLSX::Package::Table->new();
-
-            $table->_set_xml_writer(
-                $dir . '/xl/tables/table' . $index++ . '.xml' );
-
-            $table->_set_properties( $table_props );
-
-            $table->_assemble_xml_file();
-
-            $self->{_table_count}++;
-        }
-    }
-}
-
-
-###############################################################################
-#
-# _write_root_rels_file()
-#
-# Write the _rels/.rels xml file.
-#
-sub _write_root_rels_file {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-    my $rels = Excel::Writer::XLSX::Package::Relationships->new();
-
-    _mkdir( $dir . '/_rels' );
-
-    $rels->_add_document_relationship( '/officeDocument', 'xl/workbook.xml' );
-
-    $rels->_add_package_relationship( '/metadata/core-properties',
-        'docProps/core.xml' );
-
-    $rels->_add_document_relationship( '/extended-properties',
-        'docProps/app.xml' );
-
-    if ( @{ $self->{_workbook}->{_custom_properties} } ) {
-        $rels->_add_document_relationship( '/custom-properties',
-            'docProps/custom.xml' );
-    }
-
-    $rels->_set_xml_writer( $dir . '/_rels/.rels' );
-    $rels->_assemble_xml_file();
-}
-
-
-###############################################################################
-#
-# _write_workbook_rels_file()
-#
-# Write the _rels/.rels xml file.
-#
-sub _write_workbook_rels_file {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-    my $rels = Excel::Writer::XLSX::Package::Relationships->new();
-
-    _mkdir( $dir . '/xl' );
-    _mkdir( $dir . '/xl/_rels' );
-
-    my $worksheet_index  = 1;
-    my $chartsheet_index = 1;
-
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-        if ( $worksheet->{_is_chartsheet} ) {
-            $rels->_add_document_relationship( '/chartsheet',
-                'chartsheets/sheet' . $chartsheet_index++ . '.xml' );
-        }
-        else {
-            $rels->_add_document_relationship( '/worksheet',
-                'worksheets/sheet' . $worksheet_index++ . '.xml' );
-        }
-    }
-
-    $rels->_add_document_relationship( '/theme',  'theme/theme1.xml' );
-    $rels->_add_document_relationship( '/styles', 'styles.xml' );
-
-    # Add the sharedString rel if there is string data in the workbook.
-    if ( $self->{_workbook}->{_str_total} ) {
-        $rels->_add_document_relationship( '/sharedStrings',
-            'sharedStrings.xml' );
-    }
-
-    # Add vbaProject if present.
-    if ( $self->{_workbook}->{_vba_project} ) {
-        $rels->_add_ms_package_relationship( '/vbaProject', 'vbaProject.bin' );
-    }
-
-    $rels->_set_xml_writer( $dir . '/xl/_rels/workbook.xml.rels' );
-    $rels->_assemble_xml_file();
-}
-
-
-###############################################################################
-#
-# _write_worksheet_rels_files()
-#
-# Write the worksheet .rels files for worksheets that contain links to external
-# data such as hyperlinks or drawings.
-#
-sub _write_worksheet_rels_files {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-
-    my $index = 0;
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-
-        next if $worksheet->{_is_chartsheet};
-
-        $index++;
-
-        my @external_links = (
-            @{ $worksheet->{_external_hyper_links} },
-            @{ $worksheet->{_external_drawing_links} },
-            @{ $worksheet->{_external_vml_links} },
-            @{ $worksheet->{_external_table_links} },
-            @{ $worksheet->{_external_comment_links} },
-        );
-
-        next unless @external_links;
-
-        # Create the worksheet .rels dirs.
-        _mkdir( $dir . '/xl' );
-        _mkdir( $dir . '/xl/worksheets' );
-        _mkdir( $dir . '/xl/worksheets/_rels' );
-
-        my $rels = Excel::Writer::XLSX::Package::Relationships->new();
-
-        for my $link_data ( @external_links ) {
-            $rels->_add_worksheet_relationship( @$link_data );
-        }
-
-        # Create the .rels file such as /xl/worksheets/_rels/sheet1.xml.rels.
-        $rels->_set_xml_writer(
-            $dir . '/xl/worksheets/_rels/sheet' . $index . '.xml.rels' );
-        $rels->_assemble_xml_file();
-    }
-}
-
-
-###############################################################################
-#
-# _write_chartsheet_rels_files()
-#
-# Write the chartsheet .rels files for links to drawing files.
-#
-sub _write_chartsheet_rels_files {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-
-
-    my $index = 0;
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-
-        next unless $worksheet->{_is_chartsheet};
-
-        $index++;
-
-        my @external_links = @{ $worksheet->{_external_drawing_links} };
-
-        next unless @external_links;
-
-        # Create the chartsheet .rels dir.
-        _mkdir( $dir . '/xl' );
-        _mkdir( $dir . '/xl/chartsheets' );
-        _mkdir( $dir . '/xl/chartsheets/_rels' );
-
-        my $rels = Excel::Writer::XLSX::Package::Relationships->new();
-
-        for my $link_data ( @external_links ) {
-            $rels->_add_worksheet_relationship( @$link_data );
-        }
-
-        # Create the .rels file such as /xl/chartsheets/_rels/sheet1.xml.rels.
-        $rels->_set_xml_writer(
-            $dir . '/xl/chartsheets/_rels/sheet' . $index . '.xml.rels' );
-        $rels->_assemble_xml_file();
-    }
-}
-
-
-###############################################################################
-#
-# _write_drawing_rels_files()
-#
-# Write the drawing .rels files for worksheets that contain charts or drawings.
-#
-sub _write_drawing_rels_files {
-
-    my $self = shift;
-    my $dir  = $self->{_package_dir};
-
-
-    my $index = 0;
-    for my $worksheet ( @{ $self->{_workbook}->{_worksheets} } ) {
-
-        if ( @{ $worksheet->{_drawing_links} } || $worksheet->{_has_shapes} ) {
-            $index++;
-        }
-
-        next unless @{ $worksheet->{_drawing_links} };
-
-        # Create the drawing .rels dir.
-        _mkdir( $dir . '/xl' );
-        _mkdir( $dir . '/xl/drawings' );
-        _mkdir( $dir . '/xl/drawings/_rels' );
-
-        my $rels = Excel::Writer::XLSX::Package::Relationships->new();
-
-        for my $drawing_data ( @{ $worksheet->{_drawing_links} } ) {
-            $rels->_add_document_relationship( @$drawing_data );
-        }
-
-        # Create the .rels file such as /xl/drawings/_rels/sheet1.xml.rels.
-        $rels->_set_xml_writer(
-            $dir . '/xl/drawings/_rels/drawing' . $index . '.xml.rels' );
-        $rels->_assemble_xml_file();
-    }
-}
-
-
-###############################################################################
-#
-# _write_vml_drawing_rels_files()
-#
-# Write the vmlDdrawing .rels files for worksheets with images in header or
-# footers.
-#
-sub _write_vml_drawing_rels_file {
-
-    my $self      = shift;
-    my $worksheet = shift;
-    my $index     = shift;
-    my $dir       = $self->{_package_dir};
-
-
-    # Create the drawing .rels dir.
-    _mkdir( $dir . '/xl' );
-    _mkdir( $dir . '/xl/drawings' );
-    _mkdir( $dir . '/xl/drawings/_rels' );
-
-    my $rels = Excel::Writer::XLSX::Package::Relationships->new();
-
-    for my $drawing_data ( @{ $worksheet->{_vml_drawing_links} } ) {
-        $rels->_add_document_relationship( @$drawing_data );
-    }
-
-    # Create the .rels file such as /xl/drawings/_rels/vmlDrawing1.vml.rels.
-    $rels->_set_xml_writer(
-        $dir . '/xl/drawings/_rels/vmlDrawing' . $index . '.vml.rels' );
-    $rels->_assemble_xml_file();
-
-}
-
-
-###############################################################################
-#
-# _add_image_files()
-#
-# Write the /xl/media/image?.xml files.
-#
-sub _add_image_files {
-
-    my $self     = shift;
-    my $dir      = $self->{_package_dir};
-    my $workbook = $self->{_workbook};
-    my $index    = 1;
-
-    for my $image ( @{ $workbook->{_images} } ) {
-        my $filename  = $image->[0];
-        my $extension = '.' . $image->[1];
-
-        _mkdir( $dir . '/xl' );
-        _mkdir( $dir . '/xl/media' );
-
-        copy( $filename, $dir . '/xl/media/image' . $index++ . $extension );
-    }
-}
-
-
-###############################################################################
-#
-# _add_vba_project()
-#
-# Write the vbaProject.bin file.
-#
-sub _add_vba_project {
-
-    my $self        = shift;
-    my $dir         = $self->{_package_dir};
-    my $vba_project = $self->{_workbook}->{_vba_project};
-
-    return unless $vba_project;
-
-    _mkdir( $dir . '/xl' );
-
-    copy( $vba_project, $dir . '/xl/vbaProject.bin' );
-}
-
-
-###############################################################################
-#
-# _mkdir()
-#
-# Wrapper function for Perl's mkdir to allow error trapping.
-#
-sub _mkdir {
-
-    my $dir = shift;
-
-    return if -e $dir;
-
-    my $ret = mkdir( $dir );
-
-    if ( !$ret ) {
-        croak "Couldn't create sub directory $dir: $!";
-    }
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Packager - A class for creating the Excel XLSX package.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX> to create an Excel XLSX container file.
-
-From Wikipedia: I<The Open Packaging Conventions (OPC) is a container-file technology initially created by Microsoft to store a combination of XML and non-XML files that together form a single entity such as an Open XML Paper Specification (OpenXPS) document>. L<http://en.wikipedia.org/wiki/Open_Packaging_Conventions>.
-
-At its simplest an Excel XLSX file contains the following elements:
-
-     ____ [Content_Types].xml
-    |
-    |____ docProps
-    | |____ app.xml
-    | |____ core.xml
-    |
-    |____ xl
-    | |____ workbook.xml
-    | |____ worksheets
-    | | |____ sheet1.xml
-    | |
-    | |____ styles.xml
-    | |
-    | |____ theme
-    | | |____ theme1.xml
-    | |
-    | |_____rels
-    |   |____ workbook.xml.rels
-    |
-    |_____rels
-      |____ .rels
-
-
-The C<Excel::Writer::XLSX::Package::Packager> class co-ordinates the classes that represent the elements of the package and writes them into the XLSX file.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/Relationships.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/Relationships.pm
deleted file mode 100644 (file)
index a88eb27..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-package Excel::Writer::XLSX::Package::Relationships;
-
-###############################################################################
-#
-# Relationships - A class for writing the Excel XLSX Rels file.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Package::XMLwriter;
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-our $schema_root     = 'http://schemas.openxmlformats.org';
-our $package_schema  = $schema_root . '/package/2006/relationships';
-our $document_schema = $schema_root . '/officeDocument/2006/relationships';
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_rels} = [];
-    $self->{_id}   = 1;
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration;
-    $self->_write_relationships();
-}
-
-
-###############################################################################
-#
-# _add_document_relationship()
-#
-# Add container relationship to XLSX .rels xml files.
-#
-sub _add_document_relationship {
-
-    my $self   = shift;
-    my $type   = shift;
-    my $target = shift;
-
-    $type   = $document_schema . $type;
-
-    push @{ $self->{_rels} }, [ $type, $target ];
-}
-
-
-###############################################################################
-#
-# _add_package_relationship()
-#
-# Add container relationship to XLSX .rels xml files.
-#
-sub _add_package_relationship {
-
-    my $self   = shift;
-    my $type   = shift;
-    my $target = shift;
-
-    $type   = $package_schema . $type;
-
-    push @{ $self->{_rels} }, [ $type, $target ];
-}
-
-
-###############################################################################
-#
-# _add_ms_package_relationship()
-#
-# Add container relationship to XLSX .rels xml files. Uses MS schema.
-#
-sub _add_ms_package_relationship {
-
-    my $self   = shift;
-    my $type   = shift;
-    my $target = shift;
-    my $schema = 'http://schemas.microsoft.com/office/2006/relationships';
-
-    $type   = $schema . $type;
-
-    push @{ $self->{_rels} }, [ $type, $target ];
-}
-
-
-###############################################################################
-#
-# _add_worksheet_relationship()
-#
-# Add worksheet relationship to sheet.rels xml files.
-#
-sub _add_worksheet_relationship {
-
-    my $self        = shift;
-    my $type        = shift;
-    my $target      = shift;
-    my $target_mode = shift;
-
-    $type   = $document_schema . $type;
-
-    push @{ $self->{_rels} }, [ $type, $target, $target_mode ];
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-##############################################################################
-#
-# _write_relationships()
-#
-# Write the <Relationships> element.
-#
-sub _write_relationships {
-
-    my $self = shift;
-
-    my @attributes = ( 'xmlns' => $package_schema, );
-
-    $self->xml_start_tag( 'Relationships', @attributes );
-
-    for my $rel ( @{ $self->{_rels} } ) {
-        $self->_write_relationship( @$rel );
-    }
-
-    $self->xml_end_tag( 'Relationships' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-##############################################################################
-#
-# _write_relationship()
-#
-# Write the <Relationship> element.
-#
-sub _write_relationship {
-
-    my $self        = shift;
-    my $type        = shift;
-    my $target      = shift;
-    my $target_mode = shift;
-
-    my @attributes = (
-        'Id'     => 'rId' . $self->{_id}++,
-        'Type'   => $type,
-        'Target' => $target,
-    );
-
-    push @attributes, ( 'TargetMode' => $target_mode ) if $target_mode;
-
-    $self->xml_empty_tag( 'Relationship', @attributes );
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Relationships - A class for writing the Excel XLSX Rels file.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/SharedStrings.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/SharedStrings.pm
deleted file mode 100644 (file)
index 7877c0c..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-package Excel::Writer::XLSX::Package::SharedStrings;
-
-###############################################################################
-#
-# SharedStrings - A class for writing the Excel XLSX sharedStrings file.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Encode;
-use Excel::Writer::XLSX::Package::XMLwriter;
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_strings}      = [];
-    $self->{_string_count} = 0;
-    $self->{_unique_count} = 0;
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration;
-
-    # Write the sst table.
-    $self->_write_sst( $self->{_string_count}, $self->{_unique_count} );
-
-    # Write the sst strings.
-    $self->_write_sst_strings();
-
-    # Close the sst tag.
-    $self->xml_end_tag( 'sst' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# _set_string_count()
-#
-# Set the total sst string count.
-#
-sub _set_string_count {
-
-    my $self = shift;
-
-    $self->{_string_count} = shift;
-}
-
-
-###############################################################################
-#
-# _set_unique_count()
-#
-# Set the total of unique sst strings.
-#
-sub _set_unique_count {
-
-    my $self = shift;
-
-    $self->{_unique_count} = shift;
-}
-
-
-###############################################################################
-#
-# _add_strings()
-#
-# Add the array ref of strings to be written.
-#
-sub _add_strings {
-
-    my $self = shift;
-
-    $self->{_strings} = shift;
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-##############################################################################
-#
-# _write_sst()
-#
-# Write the <sst> element.
-#
-sub _write_sst {
-
-    my $self         = shift;
-    my $count        = shift;
-    my $unique_count = shift;
-    my $schema       = 'http://schemas.openxmlformats.org';
-    my $xmlns        = $schema . '/spreadsheetml/2006/main';
-
-    my @attributes = (
-        'xmlns'       => $xmlns,
-        'count'       => $count,
-        'uniqueCount' => $unique_count,
-    );
-
-    $self->xml_start_tag( 'sst', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_sst_strings()
-#
-# Write the sst string elements.
-#
-sub _write_sst_strings {
-
-    my $self = shift;
-
-    for my $string ( @{ $self->{_strings} } ) {
-        $self->_write_si( $string );
-    }
-}
-
-
-##############################################################################
-#
-# _write_si()
-#
-# Write the <si> element.
-#
-sub _write_si {
-
-    my $self       = shift;
-    my $string     = shift;
-    my @attributes = ();
-
-    # Excel escapes control characters with _xHHHH_ and also escapes any
-    # literal strings of that type by encoding the leading underscore. So
-    # "\0" -> _x0000_ and "_x0000_" -> _x005F_x0000_.
-    # The following substitutions deal with those cases.
-
-    # Escape the escape.
-    $string =~ s/(_x[0-9a-fA-F]{4}_)/_x005F$1/g;
-
-    # Convert control character to the _xHHHH_ escape.
-    $string =~ s/([\x00-\x08\x0B-\x1F])/sprintf "_x%04X_", ord($1)/eg;
-
-
-    # Add attribute to preserve leading or trailing whitespace.
-    if ( $string =~ /^\s/ || $string =~ /\s$/ ) {
-        push @attributes, ( 'xml:space' => 'preserve' );
-    }
-
-
-    # Write any rich strings without further tags.
-    if ( $string =~ m{^<r>} && $string =~ m{</r>$} ) {
-
-        # Prevent utf8 strings from getting double encoded.
-        $string = decode_utf8( $string );
-
-        $self->xml_rich_si_element( $string );
-    }
-    else {
-        $self->xml_si_element( $string, @attributes );
-    }
-
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-SharedStrings - A class for writing the Excel XLSX sharedStrings.xml file.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/Styles.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/Styles.pm
deleted file mode 100644 (file)
index f22a9b5..0000000
+++ /dev/null
@@ -1,1181 +0,0 @@
-package Excel::Writer::XLSX::Package::Styles;
-
-###############################################################################
-#
-# Styles - A class for writing the Excel XLSX styles file.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Package::XMLwriter;
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_xf_formats}         = undef;
-    $self->{_palette}            = [];
-    $self->{_font_count}         = 0;
-    $self->{_num_format_count}   = 0;
-    $self->{_border_count}       = 0;
-    $self->{_fill_count}         = 0;
-    $self->{_custom_colors}      = [];
-    $self->{_dxf_formats}        = [];
-    $self->{_has_hyperlink}      = 0;
-    $self->{_hyperlink_font_id}  = 0;
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration;
-
-    # Add the style sheet.
-    $self->_write_style_sheet();
-
-    # Write the number formats.
-    $self->_write_num_fmts();
-
-    # Write the fonts.
-    $self->_write_fonts();
-
-    # Write the fills.
-    $self->_write_fills();
-
-    # Write the borders element.
-    $self->_write_borders();
-
-    # Write the cellStyleXfs element.
-    $self->_write_cell_style_xfs();
-
-    # Write the cellXfs element.
-    $self->_write_cell_xfs();
-
-    # Write the cellStyles element.
-    $self->_write_cell_styles();
-
-    # Write the dxfs element.
-    $self->_write_dxfs();
-
-    # Write the tableStyles element.
-    $self->_write_table_styles();
-
-    # Write the colors element.
-    $self->_write_colors();
-
-    # Close the style sheet tag.
-    $self->xml_end_tag( 'styleSheet' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# _set_style_properties()
-#
-# Pass in the Format objects and other properties used to set the styles.
-#
-sub _set_style_properties {
-
-    my $self = shift;
-
-    $self->{_xf_formats}         = shift;
-    $self->{_palette}            = shift;
-    $self->{_font_count}         = shift;
-    $self->{_num_format_count}   = shift;
-    $self->{_border_count}       = shift;
-    $self->{_fill_count}         = shift;
-    $self->{_custom_colors}      = shift;
-    $self->{_dxf_formats}        = shift;
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _get_palette_color()
-#
-# Convert from an Excel internal colour index to a XML style #RRGGBB index
-# based on the default or user defined values in the Workbook palette.
-#
-sub _get_palette_color {
-
-    my $self    = shift;
-    my $index   = shift;
-    my $palette = $self->{_palette};
-
-    # Handle colours in #XXXXXX RGB format.
-    if ( $index =~ m/^#([0-9A-F]{6})$/i ) {
-        return "FF" . uc( $1 );
-    }
-
-    # Adjust the colour index.
-    $index -= 8;
-
-    # Palette is passed in from the Workbook class.
-    my @rgb = @{ $palette->[$index] };
-
-    return sprintf "FF%02X%02X%02X", @rgb[0, 1, 2];
-}
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-##############################################################################
-#
-# _write_style_sheet()
-#
-# Write the <styleSheet> element.
-#
-sub _write_style_sheet {
-
-    my $self  = shift;
-    my $xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
-
-    my @attributes = ( 'xmlns' => $xmlns );
-
-    $self->xml_start_tag( 'styleSheet', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_num_fmts()
-#
-# Write the <numFmts> element.
-#
-sub _write_num_fmts {
-
-    my $self  = shift;
-    my $count = $self->{_num_format_count};
-
-    return unless $count;
-
-    my @attributes = ( 'count' => $count );
-
-    $self->xml_start_tag( 'numFmts', @attributes );
-
-    # Write the numFmts elements.
-    for my $format ( @{ $self->{_xf_formats} } ) {
-
-        # Ignore built-in number formats, i.e., < 164.
-        next unless $format->{_num_format_index} >= 164;
-        $self->_write_num_fmt( $format->{_num_format_index},
-            $format->{_num_format} );
-    }
-
-    $self->xml_end_tag( 'numFmts' );
-}
-
-
-##############################################################################
-#
-# _write_num_fmt()
-#
-# Write the <numFmt> element.
-#
-sub _write_num_fmt {
-
-    my $self        = shift;
-    my $num_fmt_id  = shift;
-    my $format_code = shift;
-
-    my %format_codes = (
-        0  => 'General',
-        1  => '0',
-        2  => '0.00',
-        3  => '#,##0',
-        4  => '#,##0.00',
-        5  => '($#,##0_);($#,##0)',
-        6  => '($#,##0_);[Red]($#,##0)',
-        7  => '($#,##0.00_);($#,##0.00)',
-        8  => '($#,##0.00_);[Red]($#,##0.00)',
-        9  => '0%',
-        10 => '0.00%',
-        11 => '0.00E+00',
-        12 => '# ?/?',
-        13 => '# ??/??',
-        14 => 'm/d/yy',
-        15 => 'd-mmm-yy',
-        16 => 'd-mmm',
-        17 => 'mmm-yy',
-        18 => 'h:mm AM/PM',
-        19 => 'h:mm:ss AM/PM',
-        20 => 'h:mm',
-        21 => 'h:mm:ss',
-        22 => 'm/d/yy h:mm',
-        37 => '(#,##0_);(#,##0)',
-        38 => '(#,##0_);[Red](#,##0)',
-        39 => '(#,##0.00_);(#,##0.00)',
-        40 => '(#,##0.00_);[Red](#,##0.00)',
-        41 => '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
-        42 => '_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)',
-        43 => '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
-        44 => '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)',
-        45 => 'mm:ss',
-        46 => '[h]:mm:ss',
-        47 => 'mm:ss.0',
-        48 => '##0.0E+0',
-        49 => '@',
-    );
-
-    # Set the format code for built-in number formats.
-    if ( $num_fmt_id < 164 ) {
-        if ( exists $format_codes{$num_fmt_id} ) {
-            $format_code = $format_codes{$num_fmt_id};
-        }
-        else {
-            $format_code = 'General';
-        }
-    }
-
-    my @attributes = (
-        'numFmtId'   => $num_fmt_id,
-        'formatCode' => $format_code,
-    );
-
-    $self->xml_empty_tag( 'numFmt', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_fonts()
-#
-# Write the <fonts> element.
-#
-sub _write_fonts {
-
-    my $self  = shift;
-    my $count = $self->{_font_count};
-
-    my @attributes = ( 'count' => $count );
-
-    $self->xml_start_tag( 'fonts', @attributes );
-
-    # Write the font elements for format objects that have them.
-    for my $format ( @{ $self->{_xf_formats} } ) {
-        $self->_write_font( $format ) if $format->{_has_font};
-    }
-
-    $self->xml_end_tag( 'fonts' );
-}
-
-
-##############################################################################
-#
-# _write_font()
-#
-# Write the <font> element.
-#
-sub _write_font {
-
-    my $self       = shift;
-    my $format     = shift;
-    my $dxf_format = shift;
-
-    $self->xml_start_tag( 'font' );
-
-    # The condense and extend elements are mainly used in dxf formats.
-    $self->_write_condense() if $format->{_font_condense};
-    $self->_write_extend()   if $format->{_font_extend};
-
-    $self->xml_empty_tag( 'b' )       if $format->{_bold};
-    $self->xml_empty_tag( 'i' )       if $format->{_italic};
-    $self->xml_empty_tag( 'strike' )  if $format->{_font_strikeout};
-    $self->xml_empty_tag( 'outline' ) if $format->{_font_outline};
-    $self->xml_empty_tag( 'shadow' )  if $format->{_font_shadow};
-
-    # Handle the underline variants.
-    $self->_write_underline( $format->{_underline} ) if $format->{_underline};
-
-    $self->_write_vert_align( 'superscript' ) if $format->{_font_script} == 1;
-    $self->_write_vert_align( 'subscript' )   if $format->{_font_script} == 2;
-
-    if ( !$dxf_format ) {
-        $self->xml_empty_tag( 'sz', 'val', $format->{_size} );
-    }
-
-    my $theme = $format->{_theme};
-
-
-    if ( $theme == -1 ) {
-        # Ignore for excel2003_style.
-    }
-    elsif ( $theme ) {
-        $self->_write_color( 'theme' => $theme );
-    }
-    elsif ( my $index = $format->{_color_indexed} ) {
-        $self->_write_color( 'indexed' => $index );
-    }
-    elsif ( my $color = $format->{_color} ) {
-        $color = $self->_get_palette_color( $color );
-
-        $self->_write_color( 'rgb' => $color );
-    }
-    elsif ( !$dxf_format ) {
-        $self->_write_color( 'theme' => 1 );
-    }
-
-    if ( !$dxf_format ) {
-        $self->xml_empty_tag( 'name',   'val', $format->{_font} );
-
-        if ($format->{_font_family}) {
-            $self->xml_empty_tag( 'family', 'val', $format->{_font_family} );
-        }
-
-        if ($format->{_font_charset}) {
-            $self->xml_empty_tag( 'charset', 'val', $format->{_font_charset} );
-        }
-
-        if ( $format->{_font} eq 'Calibri' && !$format->{_hyperlink} ) {
-            $self->xml_empty_tag(
-
-                'scheme',
-                'val' => $format->{_font_scheme}
-            );
-        }
-
-        if ( $format->{_hyperlink} ) {
-            $self->{_has_hyperlink} = 1;
-
-            if ( !$self->{_hyperlink_font_id} ) {
-                $self->{_hyperlink_font_id} = $format->{_font_index};
-            }
-        }
-    }
-
-    $self->xml_end_tag( 'font' );
-}
-
-
-###############################################################################
-#
-# _write_underline()
-#
-# Write the underline font element.
-#
-sub _write_underline {
-
-    my $self      = shift;
-    my $underline = shift;
-    my @attributes;
-
-    # Handle the underline variants.
-    if ( $underline == 2 ) {
-        @attributes = ( val => 'double' );
-    }
-    elsif ( $underline == 33 ) {
-        @attributes = ( val => 'singleAccounting' );
-    }
-    elsif ( $underline == 34 ) {
-        @attributes = ( val => 'doubleAccounting' );
-    }
-    else {
-        @attributes = ();    # Default to single underline.
-    }
-
-    $self->xml_empty_tag( 'u', @attributes );
-
-}
-
-
-##############################################################################
-#
-# _write_vert_align()
-#
-# Write the <vertAlign> font sub-element.
-#
-sub _write_vert_align {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'vertAlign', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_color()
-#
-# Write the <color> element.
-#
-sub _write_color {
-
-    my $self  = shift;
-    my $name  = shift;
-    my $value = shift;
-
-    my @attributes = ( $name => $value );
-
-    $self->xml_empty_tag( 'color', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_fills()
-#
-# Write the <fills> element.
-#
-sub _write_fills {
-
-    my $self  = shift;
-    my $count = $self->{_fill_count};
-
-    my @attributes = ( 'count' => $count );
-
-    $self->xml_start_tag( 'fills', @attributes );
-
-    # Write the default fill element.
-    $self->_write_default_fill( 'none' );
-    $self->_write_default_fill( 'gray125' );
-
-    # Write the fill elements for format objects that have them.
-    for my $format ( @{ $self->{_xf_formats} } ) {
-        $self->_write_fill( $format ) if $format->{_has_fill};
-    }
-
-    $self->xml_end_tag( 'fills' );
-}
-
-
-##############################################################################
-#
-# _write_default_fill()
-#
-# Write the <fill> element for the default fills.
-#
-sub _write_default_fill {
-
-    my $self         = shift;
-    my $pattern_type = shift;
-
-    $self->xml_start_tag( 'fill' );
-
-    $self->xml_empty_tag( 'patternFill', 'patternType', $pattern_type );
-
-    $self->xml_end_tag( 'fill' );
-}
-
-
-##############################################################################
-#
-# _write_fill()
-#
-# Write the <fill> element.
-#
-sub _write_fill {
-
-    my $self       = shift;
-    my $format     = shift;
-    my $dxf_format = shift;
-    my $pattern    = $format->{_pattern};
-    my $bg_color   = $format->{_bg_color};
-    my $fg_color   = $format->{_fg_color};
-
-    # Colors for dxf formats are handled differently from normal formats since
-    # the normal format reverses the meaning of BG and FG for solid fills.
-    if ( $dxf_format ) {
-        $bg_color = $format->{_dxf_bg_color};
-        $fg_color = $format->{_dxf_fg_color};
-    }
-
-
-    my @patterns = qw(
-      none
-      solid
-      mediumGray
-      darkGray
-      lightGray
-      darkHorizontal
-      darkVertical
-      darkDown
-      darkUp
-      darkGrid
-      darkTrellis
-      lightHorizontal
-      lightVertical
-      lightDown
-      lightUp
-      lightGrid
-      lightTrellis
-      gray125
-      gray0625
-
-    );
-
-
-    $self->xml_start_tag( 'fill' );
-
-    # The "none" pattern is handled differently for dxf formats.
-    if ( $dxf_format && $format->{_pattern} <= 1 ) {
-        $self->xml_start_tag( 'patternFill' );
-    }
-    else {
-        $self->xml_start_tag(
-            'patternFill',
-            'patternType',
-            $patterns[ $format->{_pattern} ]
-
-        );
-    }
-
-    if ( $fg_color ) {
-        $fg_color = $self->_get_palette_color( $fg_color );
-        $self->xml_empty_tag( 'fgColor', 'rgb' => $fg_color );
-    }
-
-    if ( $bg_color ) {
-        $bg_color = $self->_get_palette_color( $bg_color );
-        $self->xml_empty_tag( 'bgColor', 'rgb' => $bg_color );
-    }
-    else {
-        if ( !$dxf_format ) {
-            $self->xml_empty_tag( 'bgColor', 'indexed' => 64 );
-        }
-    }
-
-    $self->xml_end_tag( 'patternFill' );
-    $self->xml_end_tag( 'fill' );
-}
-
-
-##############################################################################
-#
-# _write_borders()
-#
-# Write the <borders> element.
-#
-sub _write_borders {
-
-    my $self  = shift;
-    my $count = $self->{_border_count};
-
-    my @attributes = ( 'count' => $count );
-
-    $self->xml_start_tag( 'borders', @attributes );
-
-    # Write the border elements for format objects that have them.
-    for my $format ( @{ $self->{_xf_formats} } ) {
-        $self->_write_border( $format ) if $format->{_has_border};
-    }
-
-    $self->xml_end_tag( 'borders' );
-}
-
-
-##############################################################################
-#
-# _write_border()
-#
-# Write the <border> element.
-#
-sub _write_border {
-
-    my $self       = shift;
-    my $format     = shift;
-    my $dxf_format = shift;
-    my @attributes = ();
-
-
-    # Diagonal borders add attributes to the <border> element.
-    if ( $format->{_diag_type} == 1 ) {
-        push @attributes, ( diagonalUp => 1 );
-    }
-    elsif ( $format->{_diag_type} == 2 ) {
-        push @attributes, ( diagonalDown => 1 );
-    }
-    elsif ( $format->{_diag_type} == 3 ) {
-        push @attributes, ( diagonalUp   => 1 );
-        push @attributes, ( diagonalDown => 1 );
-    }
-
-    # Ensure that a default diag border is set if the diag type is set.
-    if ( $format->{_diag_type} && !$format->{_diag_border} ) {
-        $format->{_diag_border} = 1;
-    }
-
-    # Write the start border tag.
-    $self->xml_start_tag( 'border', @attributes );
-
-    # Write the <border> sub elements.
-    $self->_write_sub_border(
-        'left',
-        $format->{_left},
-        $format->{_left_color}
-
-    );
-
-    $self->_write_sub_border(
-        'right',
-        $format->{_right},
-        $format->{_right_color}
-
-    );
-
-    $self->_write_sub_border(
-        'top',
-        $format->{_top},
-        $format->{_top_color}
-
-    );
-
-    $self->_write_sub_border(
-        'bottom',
-        $format->{_bottom},
-        $format->{_bottom_color}
-
-    );
-
-    # Condition DXF formats don't allow diagonal borders
-    if ( !$dxf_format ) {
-        $self->_write_sub_border(
-            'diagonal',
-            $format->{_diag_border},
-            $format->{_diag_color}
-
-        );
-    }
-
-    if ( $dxf_format ) {
-        $self->_write_sub_border( 'vertical' );
-        $self->_write_sub_border( 'horizontal' );
-    }
-
-    $self->xml_end_tag( 'border' );
-}
-
-
-##############################################################################
-#
-# _write_sub_border()
-#
-# Write the <border> sub elements such as <right>, <top>, etc.
-#
-sub _write_sub_border {
-
-    my $self  = shift;
-    my $type  = shift;
-    my $style = shift;
-    my $color = shift;
-    my @attributes;
-
-    if ( !$style ) {
-        $self->xml_empty_tag( $type );
-        return;
-    }
-
-    my @border_styles = qw(
-      none
-      thin
-      medium
-      dashed
-      dotted
-      thick
-      double
-      hair
-      mediumDashed
-      dashDot
-      mediumDashDot
-      dashDotDot
-      mediumDashDotDot
-      slantDashDot
-
-    );
-
-
-    push @attributes, ( style => $border_styles[$style] );
-
-    $self->xml_start_tag( $type, @attributes );
-
-    if ( $color ) {
-        $color = $self->_get_palette_color( $color );
-        $self->xml_empty_tag( 'color', 'rgb' => $color );
-    }
-    else {
-        $self->xml_empty_tag( 'color', 'auto' => 1 );
-    }
-
-    $self->xml_end_tag( $type );
-}
-
-
-##############################################################################
-#
-# _write_cell_style_xfs()
-#
-# Write the <cellStyleXfs> element.
-#
-sub _write_cell_style_xfs {
-
-    my $self  = shift;
-    my $count = 1;
-
-    if ( $self->{_has_hyperlink} ) {
-        $count = 2;
-    }
-
-    my @attributes = ( 'count' => $count );
-
-    $self->xml_start_tag( 'cellStyleXfs', @attributes );
-
-    # Write the style_xf element.
-    $self->_write_style_xf( 0, 0 );
-
-    if ( $self->{_has_hyperlink} ) {
-        $self->_write_style_xf( 1, $self->{_hyperlink_font_id} );
-    }
-
-    $self->xml_end_tag( 'cellStyleXfs' );
-}
-
-
-##############################################################################
-#
-# _write_cell_xfs()
-#
-# Write the <cellXfs> element.
-#
-sub _write_cell_xfs {
-
-    my $self    = shift;
-    my @formats = @{ $self->{_xf_formats} };
-
-    # Workaround for when the last format is used for the comment font
-    # and shouldn't be used for cellXfs.
-    my $last_format = $formats[-1];
-
-    if ( $last_format->{_font_only} ) {
-        pop @formats;
-    }
-
-    my $count = scalar @formats;
-    my @attributes = ( 'count' => $count );
-
-    $self->xml_start_tag( 'cellXfs', @attributes );
-
-    # Write the xf elements.
-    for my $format ( @formats ) {
-        $self->_write_xf( $format );
-    }
-
-    $self->xml_end_tag( 'cellXfs' );
-}
-
-
-##############################################################################
-#
-# _write_style_xf()
-#
-# Write the style <xf> element.
-#
-sub _write_style_xf {
-
-    my $self          = shift;
-    my $has_hyperlink = shift;
-    my $font_id       = shift;
-    my $num_fmt_id    = 0;
-    my $fill_id       = 0;
-    my $border_id     = 0;
-
-    my @attributes = (
-        'numFmtId' => $num_fmt_id,
-        'fontId'   => $font_id,
-        'fillId'   => $fill_id,
-        'borderId' => $border_id,
-    );
-
-    if ( $has_hyperlink ) {
-        push @attributes, ( 'applyNumberFormat' => 0 );
-        push @attributes, ( 'applyFill'         => 0 );
-        push @attributes, ( 'applyBorder'       => 0 );
-        push @attributes, ( 'applyAlignment'    => 0 );
-        push @attributes, ( 'applyProtection'   => 0 );
-
-        $self->xml_start_tag( 'xf', @attributes );
-        $self->xml_empty_tag( 'alignment',  ( 'vertical', 'top' ) );
-        $self->xml_empty_tag( 'protection', ( 'locked',   0 ) );
-        $self->xml_end_tag( 'xf' );
-    }
-    else {
-        $self->xml_empty_tag( 'xf', @attributes );
-    }
-}
-
-
-##############################################################################
-#
-# _write_xf()
-#
-# Write the <xf> element.
-#
-sub _write_xf {
-
-    my $self        = shift;
-    my $format      = shift;
-    my $num_fmt_id  = $format->{_num_format_index};
-    my $font_id     = $format->{_font_index};
-    my $fill_id     = $format->{_fill_index};
-    my $border_id   = $format->{_border_index};
-    my $xf_id       = $format->{_xf_id};
-    my $has_align   = 0;
-    my $has_protect = 0;
-
-    my @attributes = (
-        'numFmtId' => $num_fmt_id,
-        'fontId'   => $font_id,
-        'fillId'   => $fill_id,
-        'borderId' => $border_id,
-        'xfId'     => $xf_id,
-    );
-
-
-    if ( $format->{_num_format_index} > 0 ) {
-        push @attributes, ( 'applyNumberFormat' => 1 );
-    }
-
-    # Add applyFont attribute if XF format uses a font element.
-    if ( $format->{_font_index} > 0 && !$format->{_hyperlink} ) {
-        push @attributes, ( 'applyFont' => 1 );
-    }
-
-    # Add applyFill attribute if XF format uses a fill element.
-    if ( $format->{_fill_index} > 0 ) {
-        push @attributes, ( 'applyFill' => 1 );
-    }
-
-    # Add applyBorder attribute if XF format uses a border element.
-    if ( $format->{_border_index} > 0 ) {
-        push @attributes, ( 'applyBorder' => 1 );
-    }
-
-    # Check if XF format has alignment properties set.
-    my ( $apply_align, @align ) = $format->get_align_properties();
-
-    # Check if an alignment sub-element should be written.
-    $has_align = 1 if $apply_align && @align;
-
-    # We can also have applyAlignment without a sub-element.
-    if ( $apply_align || $format->{_hyperlink} ) {
-        push @attributes, ( 'applyAlignment' => 1 );
-    }
-
-    # Check for cell protection properties.
-    my @protection = $format->get_protection_properties();
-
-    if ( @protection || $format->{_hyperlink} ) {
-        push @attributes, ( 'applyProtection' => 1 );
-
-        if ( !$format->{_hyperlink} ) {
-            $has_protect = 1;
-        }
-    }
-
-    # Write XF with sub-elements if required.
-    if ( $has_align || $has_protect ) {
-        $self->xml_start_tag( 'xf', @attributes );
-        $self->xml_empty_tag( 'alignment',  @align )      if $has_align;
-        $self->xml_empty_tag( 'protection', @protection ) if $has_protect;
-        $self->xml_end_tag( 'xf' );
-    }
-    else {
-        $self->xml_empty_tag( 'xf', @attributes );
-    }
-}
-
-
-##############################################################################
-#
-# _write_cell_styles()
-#
-# Write the <cellStyles> element.
-#
-sub _write_cell_styles {
-
-    my $self  = shift;
-    my $count = 1;
-
-    if ( $self->{_has_hyperlink} ) {
-        $count = 2;
-    }
-
-    my @attributes = ( 'count' => $count );
-
-    $self->xml_start_tag( 'cellStyles', @attributes );
-
-    # Write the cellStyle element.
-    if ( $self->{_has_hyperlink} ) {
-        $self->_write_cell_style('Hyperlink', 1, 8);
-    }
-
-    $self->_write_cell_style('Normal', 0, 0);
-
-    $self->xml_end_tag( 'cellStyles' );
-}
-
-
-##############################################################################
-#
-# _write_cell_style()
-#
-# Write the <cellStyle> element.
-#
-sub _write_cell_style {
-
-    my $self       = shift;
-    my $name       = shift;
-    my $xf_id      = shift;
-    my $builtin_id = shift;
-
-    my @attributes = (
-        'name'      => $name,
-        'xfId'      => $xf_id,
-        'builtinId' => $builtin_id,
-    );
-
-    $self->xml_empty_tag( 'cellStyle', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_dxfs()
-#
-# Write the <dxfs> element.
-#
-sub _write_dxfs {
-
-    my $self    = shift;
-    my $formats = $self->{_dxf_formats};
-
-    my $count = scalar @{$formats};
-
-    my @attributes = ( 'count' => $count );
-
-    if ( $count ) {
-        $self->xml_start_tag( 'dxfs', @attributes );
-
-        # Write the font elements for format objects that have them.
-        for my $format ( @{ $self->{_dxf_formats} } ) {
-            $self->xml_start_tag( 'dxf' );
-            $self->_write_font( $format, 1 ) if $format->{_has_dxf_font};
-
-            if ( $format->{_num_format_index} ) {
-                $self->_write_num_fmt( $format->{_num_format_index},
-                    $format->{_num_format} );
-            }
-
-            $self->_write_fill( $format, 1 ) if $format->{_has_dxf_fill};
-            $self->_write_border( $format, 1 ) if $format->{_has_dxf_border};
-            $self->xml_end_tag( 'dxf' );
-        }
-
-        $self->xml_end_tag( 'dxfs' );
-    }
-    else {
-        $self->xml_empty_tag( 'dxfs', @attributes );
-    }
-
-}
-
-
-##############################################################################
-#
-# _write_table_styles()
-#
-# Write the <tableStyles> element.
-#
-sub _write_table_styles {
-
-    my $self                = shift;
-    my $count               = 0;
-    my $default_table_style = 'TableStyleMedium9';
-    my $default_pivot_style = 'PivotStyleLight16';
-
-    my @attributes = (
-        'count'             => $count,
-        'defaultTableStyle' => $default_table_style,
-        'defaultPivotStyle' => $default_pivot_style,
-    );
-
-    $self->xml_empty_tag( 'tableStyles', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_colors()
-#
-# Write the <colors> element.
-#
-sub _write_colors {
-
-    my $self          = shift;
-    my @custom_colors = @{ $self->{_custom_colors} };
-
-    return unless @custom_colors;
-
-    $self->xml_start_tag( 'colors' );
-    $self->_write_mru_colors( @custom_colors );
-    $self->xml_end_tag( 'colors' );
-}
-
-
-##############################################################################
-#
-# _write_mru_colors()
-#
-# Write the <mruColors> element for the most recently used colours.
-#
-sub _write_mru_colors {
-
-    my $self          = shift;
-    my @custom_colors = @_;
-
-    # Limit the mruColors to the last 10.
-    my $count = @custom_colors;
-    if ( $count > 10 ) {
-        splice @custom_colors, 0, ( $count - 10 );
-    }
-
-    $self->xml_start_tag( 'mruColors' );
-
-    # Write the custom colors in reverse order.
-    for my $color ( reverse @custom_colors ) {
-        $self->_write_color( 'rgb' => $color );
-    }
-
-    $self->xml_end_tag( 'mruColors' );
-}
-
-
-##############################################################################
-#
-# _write_condense()
-#
-# Write the <condense> element.
-#
-sub _write_condense {
-
-    my $self = shift;
-    my $val  = 0;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'condense', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_extend()
-#
-# Write the <extend> element.
-#
-sub _write_extend {
-
-    my $self = shift;
-    my $val  = 0;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'extend', @attributes );
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Styles - A class for writing the Excel XLSX styles file.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/Table.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/Table.pm
deleted file mode 100644 (file)
index 13c3ce9..0000000
+++ /dev/null
@@ -1,326 +0,0 @@
-package Excel::Writer::XLSX::Package::Table;
-
-###############################################################################
-#
-# Table - A class for writing the Excel XLSX Table file.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Package::XMLwriter;
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    $self->{_properties} = {};
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration;
-
-    # Write the table element.
-    $self->_write_table();
-
-    # Write the autoFilter element.
-    $self->_write_auto_filter();
-
-    # Write the tableColumns element.
-    $self->_write_table_columns();
-
-    # Write the tableStyleInfo element.
-    $self->_write_table_style_info();
-
-
-    # Close the table tag.
-    $self->xml_end_tag( 'table' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# _set_properties()
-#
-# Set the document properties.
-#
-sub _set_properties {
-
-    my $self       = shift;
-    my $properties = shift;
-
-    $self->{_properties} = $properties;
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-##############################################################################
-#
-# _write_table()
-#
-# Write the <table> element.
-#
-sub _write_table {
-
-    my $self             = shift;
-    my $schema           = 'http://schemas.openxmlformats.org/';
-    my $xmlns            = $schema . 'spreadsheetml/2006/main';
-    my $id               = $self->{_properties}->{_id};
-    my $name             = $self->{_properties}->{_name};
-    my $display_name     = $self->{_properties}->{_name};
-    my $ref              = $self->{_properties}->{_range};
-    my $totals_row_shown = $self->{_properties}->{_totals_row_shown};
-    my $header_row_count = $self->{_properties}->{_header_row_count};
-
-    my @attributes = (
-        'xmlns'       => $xmlns,
-        'id'          => $id,
-        'name'        => $name,
-        'displayName' => $display_name,
-        'ref'         => $ref,
-    );
-
-    push @attributes, ( 'headerRowCount' => 0 ) if !$header_row_count;
-
-    if ( $totals_row_shown ) {
-        push @attributes, ( 'totalsRowCount' => 1 );
-    }
-    else {
-        push @attributes, ( 'totalsRowShown' => 0 );
-    }
-
-
-    $self->xml_start_tag( 'table', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_auto_filter()
-#
-# Write the <autoFilter> element.
-#
-sub _write_auto_filter {
-
-    my $self       = shift;
-    my $autofilter = $self->{_properties}->{_autofilter};
-
-    return unless $autofilter;
-
-    my @attributes = ( 'ref' => $autofilter, );
-
-    $self->xml_empty_tag( 'autoFilter', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_table_columns()
-#
-# Write the <tableColumns> element.
-#
-sub _write_table_columns {
-
-    my $self    = shift;
-    my @columns = @{ $self->{_properties}->{_columns} };
-
-    my $count = scalar @columns;
-
-    my @attributes = ( 'count' => $count, );
-
-    $self->xml_start_tag( 'tableColumns', @attributes );
-
-    for my $col_data ( @columns ) {
-
-        # Write the tableColumn element.
-        $self->_write_table_column( $col_data );
-    }
-
-    $self->xml_end_tag( 'tableColumns' );
-}
-
-
-##############################################################################
-#
-# _write_table_column()
-#
-# Write the <tableColumn> element.
-#
-sub _write_table_column {
-
-    my $self     = shift;
-    my $col_data = shift;
-
-    my @attributes = (
-        'id'   => $col_data->{_id},
-        'name' => $col_data->{_name},
-    );
-
-
-    if ( $col_data->{_total_string} ) {
-        push @attributes, ( totalsRowLabel => $col_data->{_total_string} );
-    }
-    elsif ( $col_data->{_total_function} ) {
-        push @attributes, ( totalsRowFunction => $col_data->{_total_function} );
-    }
-
-
-    if ( defined $col_data->{_format} ) {
-        push @attributes, ( dataDxfId => $col_data->{_format} );
-    }
-
-    if ( $col_data->{_formula} ) {
-        $self->xml_start_tag( 'tableColumn', @attributes );
-
-        # Write the calculatedColumnFormula element.
-        $self->_write_calculated_column_formula( $col_data->{_formula} );
-
-        $self->xml_end_tag( 'tableColumn' );
-    }
-    else {
-        $self->xml_empty_tag( 'tableColumn', @attributes );
-    }
-
-}
-
-
-##############################################################################
-#
-# _write_table_style_info()
-#
-# Write the <tableStyleInfo> element.
-#
-sub _write_table_style_info {
-
-    my $self  = shift;
-    my $props = $self->{_properties};
-
-    my $name                = $props->{_style};
-    my $show_first_column   = $props->{_show_first_col};
-    my $show_last_column    = $props->{_show_last_col};
-    my $show_row_stripes    = $props->{_show_row_stripes};
-    my $show_column_stripes = $props->{_show_col_stripes};
-
-    my @attributes = (
-        'name'              => $name,
-        'showFirstColumn'   => $show_first_column,
-        'showLastColumn'    => $show_last_column,
-        'showRowStripes'    => $show_row_stripes,
-        'showColumnStripes' => $show_column_stripes,
-    );
-
-    $self->xml_empty_tag( 'tableStyleInfo', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_calculated_column_formula()
-#
-# Write the <calculatedColumnFormula> element.
-#
-sub _write_calculated_column_formula {
-
-    my $self    = shift;
-    my $formula = shift;
-
-    $self->xml_data_element( 'calculatedColumnFormula', $formula );
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Table - A class for writing the Excel XLSX Table file.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/Theme.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/Theme.pm
deleted file mode 100644 (file)
index 64e6d08..0000000
+++ /dev/null
@@ -1,502 +0,0 @@
-package Excel::Writer::XLSX::Package::Theme;
-
-###############################################################################
-#
-# Theme - A class for writing the Excel XLSX Theme file.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-
-###############################################################################
-#
-# NOTE: This class doesn't try to create the Excel theme1.xml programmatically.
-#       Instead it just writes a stored default theme. This is mainly to
-#       facilitate easier comparisons during testing. The theme1.xml file
-#       isn't actually required.
-
-use 5.008002;
-use strict;
-use warnings;
-use Exporter;
-use Carp;
-use IO::File;
-use utf8;
-
-our @ISA     = qw(Exporter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-
-    my $self = {};
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->_write_theme_file();
-}
-
-
-###############################################################################
-#
-# _set_xml_writer()
-#
-# Set the filehandle only. This class doesn't use a real XML writer class.
-#
-sub _set_xml_writer {
-
-    my $self     = shift;
-    my $filename = shift;
-
-    my $fh = IO::File->new( $filename, 'w' );
-    croak "Couldn't open file $filename for writing.\n" unless $fh;
-
-    binmode $fh, ':utf8';
-
-    $self->{_fh} = $fh;
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-# Sparklines styles. Used by the Worksheet class.
-our @spark_styles = (
-    {   # 0
-        series   => { _theme => "4", _tint => "-0.499984740745262" },
-        negative => { _theme => "5" },
-        markers  => { _theme => "4", _tint => "-0.499984740745262" },
-        first    => { _theme => "4", _tint => "0.39997558519241921" },
-        last     => { _theme => "4", _tint => "0.39997558519241921" },
-        high     => { _theme => "4" },
-        low      => { _theme => "4" },
-    },
-    {   # 1
-        series   => { _theme => "4", _tint => "-0.499984740745262" },
-        negative => { _theme => "5" },
-        markers  => { _theme => "4", _tint => "-0.499984740745262" },
-        first    => { _theme => "4", _tint => "0.39997558519241921" },
-        last     => { _theme => "4", _tint => "0.39997558519241921" },
-        high     => { _theme => "4" },
-        low      => { _theme => "4" },
-    },
-    {   # 2
-        series   => { _theme => "5", _tint => "-0.499984740745262" },
-        negative => { _theme => "6" },
-        markers  => { _theme => "5", _tint => "-0.499984740745262" },
-        first    => { _theme => "5", _tint => "0.39997558519241921" },
-        last     => { _theme => "5", _tint => "0.39997558519241921" },
-        high     => { _theme => "5" },
-        low      => { _theme => "5" },
-    },
-    {   # 3
-        series   => { _theme => "6", _tint => "-0.499984740745262" },
-        negative => { _theme => "7" },
-        markers  => { _theme => "6", _tint => "-0.499984740745262" },
-        first    => { _theme => "6", _tint => "0.39997558519241921" },
-        last     => { _theme => "6", _tint => "0.39997558519241921" },
-        high     => { _theme => "6" },
-        low      => { _theme => "6" },
-    },
-    {   # 4
-        series   => { _theme => "7", _tint => "-0.499984740745262" },
-        negative => { _theme => "8" },
-        markers  => { _theme => "7", _tint => "-0.499984740745262" },
-        first    => { _theme => "7", _tint => "0.39997558519241921" },
-        last     => { _theme => "7", _tint => "0.39997558519241921" },
-        high     => { _theme => "7" },
-        low      => { _theme => "7" },
-    },
-    {   # 5
-        series   => { _theme => "8", _tint => "-0.499984740745262" },
-        negative => { _theme => "9" },
-        markers  => { _theme => "8", _tint => "-0.499984740745262" },
-        first    => { _theme => "8", _tint => "0.39997558519241921" },
-        last     => { _theme => "8", _tint => "0.39997558519241921" },
-        high     => { _theme => "8" },
-        low      => { _theme => "8" },
-    },
-    {   # 6
-        series   => { _theme => "9", _tint => "-0.499984740745262" },
-        negative => { _theme => "4" },
-        markers  => { _theme => "9", _tint => "-0.499984740745262" },
-        first    => { _theme => "9", _tint => "0.39997558519241921" },
-        last     => { _theme => "9", _tint => "0.39997558519241921" },
-        high     => { _theme => "9" },
-        low      => { _theme => "9" },
-    },
-    {   # 7
-        series   => { _theme => "4", _tint => "-0.249977111117893" },
-        negative => { _theme => "5" },
-        markers  => { _theme => "5", _tint => "-0.249977111117893" },
-        first    => { _theme => "5", _tint => "-0.249977111117893" },
-        last     => { _theme => "5", _tint => "-0.249977111117893" },
-        high     => { _theme => "5", _tint => "-0.249977111117893" },
-        low      => { _theme => "5", _tint => "-0.249977111117893" },
-    },
-    {   # 8
-        series   => { _theme => "5", _tint => "-0.249977111117893" },
-        negative => { _theme => "6" },
-        markers  => { _theme => "6", _tint => "-0.249977111117893" },
-        first    => { _theme => "6", _tint => "-0.249977111117893" },
-        last     => { _theme => "6", _tint => "-0.249977111117893" },
-        high     => { _theme => "6", _tint => "-0.249977111117893" },
-        low      => { _theme => "6", _tint => "-0.249977111117893" },
-    },
-    {   # 9
-        series   => { _theme => "6", _tint => "-0.249977111117893" },
-        negative => { _theme => "7" },
-        markers  => { _theme => "7", _tint => "-0.249977111117893" },
-        first    => { _theme => "7", _tint => "-0.249977111117893" },
-        last     => { _theme => "7", _tint => "-0.249977111117893" },
-        high     => { _theme => "7", _tint => "-0.249977111117893" },
-        low      => { _theme => "7", _tint => "-0.249977111117893" },
-    },
-    {   # 10
-        series   => { _theme => "7", _tint => "-0.249977111117893" },
-        negative => { _theme => "8" },
-        markers  => { _theme => "8", _tint => "-0.249977111117893" },
-        first    => { _theme => "8", _tint => "-0.249977111117893" },
-        last     => { _theme => "8", _tint => "-0.249977111117893" },
-        high     => { _theme => "8", _tint => "-0.249977111117893" },
-        low      => { _theme => "8", _tint => "-0.249977111117893" },
-    },
-    {   # 11
-        series   => { _theme => "8", _tint => "-0.249977111117893" },
-        negative => { _theme => "9" },
-        markers  => { _theme => "9", _tint => "-0.249977111117893" },
-        first    => { _theme => "9", _tint => "-0.249977111117893" },
-        last     => { _theme => "9", _tint => "-0.249977111117893" },
-        high     => { _theme => "9", _tint => "-0.249977111117893" },
-        low      => { _theme => "9", _tint => "-0.249977111117893" },
-    },
-    {   # 12
-        series   => { _theme => "9", _tint => "-0.249977111117893" },
-        negative => { _theme => "4" },
-        markers  => { _theme => "4", _tint => "-0.249977111117893" },
-        first    => { _theme => "4", _tint => "-0.249977111117893" },
-        last     => { _theme => "4", _tint => "-0.249977111117893" },
-        high     => { _theme => "4", _tint => "-0.249977111117893" },
-        low      => { _theme => "4", _tint => "-0.249977111117893" },
-    },
-    {   # 13
-        series   => { _theme => "4" },
-        negative => { _theme => "5" },
-        markers  => { _theme => "4", _tint => "-0.249977111117893" },
-        first    => { _theme => "4", _tint => "-0.249977111117893" },
-        last     => { _theme => "4", _tint => "-0.249977111117893" },
-        high     => { _theme => "4", _tint => "-0.249977111117893" },
-        low      => { _theme => "4", _tint => "-0.249977111117893" },
-    },
-    {   # 14
-        series   => { _theme => "5" },
-        negative => { _theme => "6" },
-        markers  => { _theme => "5", _tint => "-0.249977111117893" },
-        first    => { _theme => "5", _tint => "-0.249977111117893" },
-        last     => { _theme => "5", _tint => "-0.249977111117893" },
-        high     => { _theme => "5", _tint => "-0.249977111117893" },
-        low      => { _theme => "5", _tint => "-0.249977111117893" },
-    },
-    {   # 15
-        series   => { _theme => "6" },
-        negative => { _theme => "7" },
-        markers  => { _theme => "6", _tint => "-0.249977111117893" },
-        first    => { _theme => "6", _tint => "-0.249977111117893" },
-        last     => { _theme => "6", _tint => "-0.249977111117893" },
-        high     => { _theme => "6", _tint => "-0.249977111117893" },
-        low      => { _theme => "6", _tint => "-0.249977111117893" },
-    },
-    {   # 16
-        series   => { _theme => "7" },
-        negative => { _theme => "8" },
-        markers  => { _theme => "7", _tint => "-0.249977111117893" },
-        first    => { _theme => "7", _tint => "-0.249977111117893" },
-        last     => { _theme => "7", _tint => "-0.249977111117893" },
-        high     => { _theme => "7", _tint => "-0.249977111117893" },
-        low      => { _theme => "7", _tint => "-0.249977111117893" },
-    },
-    {   # 17
-        series   => { _theme => "8" },
-        negative => { _theme => "9" },
-        markers  => { _theme => "8", _tint => "-0.249977111117893" },
-        first    => { _theme => "8", _tint => "-0.249977111117893" },
-        last     => { _theme => "8", _tint => "-0.249977111117893" },
-        high     => { _theme => "8", _tint => "-0.249977111117893" },
-        low      => { _theme => "8", _tint => "-0.249977111117893" },
-    },
-    {   # 18
-        series   => { _theme => "9" },
-        negative => { _theme => "4" },
-        markers  => { _theme => "9", _tint => "-0.249977111117893" },
-        first    => { _theme => "9", _tint => "-0.249977111117893" },
-        last     => { _theme => "9", _tint => "-0.249977111117893" },
-        high     => { _theme => "9", _tint => "-0.249977111117893" },
-        low      => { _theme => "9", _tint => "-0.249977111117893" },
-    },
-    {   # 19
-        series   => { _theme => "4", _tint => "0.39997558519241921" },
-        negative => { _theme => "0", _tint => "-0.499984740745262" },
-        markers  => { _theme => "4", _tint => "0.79998168889431442" },
-        first    => { _theme => "4", _tint => "-0.249977111117893" },
-        last     => { _theme => "4", _tint => "-0.249977111117893" },
-        high     => { _theme => "4", _tint => "-0.499984740745262" },
-        low      => { _theme => "4", _tint => "-0.499984740745262" },
-    },
-    {   # 20
-        series   => { _theme => "5", _tint => "0.39997558519241921" },
-        negative => { _theme => "0", _tint => "-0.499984740745262" },
-        markers  => { _theme => "5", _tint => "0.79998168889431442" },
-        first    => { _theme => "5", _tint => "-0.249977111117893" },
-        last     => { _theme => "5", _tint => "-0.249977111117893" },
-        high     => { _theme => "5", _tint => "-0.499984740745262" },
-        low      => { _theme => "5", _tint => "-0.499984740745262" },
-    },
-    {   # 21
-        series   => { _theme => "6", _tint => "0.39997558519241921" },
-        negative => { _theme => "0", _tint => "-0.499984740745262" },
-        markers  => { _theme => "6", _tint => "0.79998168889431442" },
-        first    => { _theme => "6", _tint => "-0.249977111117893" },
-        last     => { _theme => "6", _tint => "-0.249977111117893" },
-        high     => { _theme => "6", _tint => "-0.499984740745262" },
-        low      => { _theme => "6", _tint => "-0.499984740745262" },
-    },
-    {   # 22
-        series   => { _theme => "7", _tint => "0.39997558519241921" },
-        negative => { _theme => "0", _tint => "-0.499984740745262" },
-        markers  => { _theme => "7", _tint => "0.79998168889431442" },
-        first    => { _theme => "7", _tint => "-0.249977111117893" },
-        last     => { _theme => "7", _tint => "-0.249977111117893" },
-        high     => { _theme => "7", _tint => "-0.499984740745262" },
-        low      => { _theme => "7", _tint => "-0.499984740745262" },
-    },
-    {   # 23
-        series   => { _theme => "8", _tint => "0.39997558519241921" },
-        negative => { _theme => "0", _tint => "-0.499984740745262" },
-        markers  => { _theme => "8", _tint => "0.79998168889431442" },
-        first    => { _theme => "8", _tint => "-0.249977111117893" },
-        last     => { _theme => "8", _tint => "-0.249977111117893" },
-        high     => { _theme => "8", _tint => "-0.499984740745262" },
-        low      => { _theme => "8", _tint => "-0.499984740745262" },
-    },
-    {   # 24
-        series   => { _theme => "9", _tint => "0.39997558519241921" },
-        negative => { _theme => "0", _tint => "-0.499984740745262" },
-        markers  => { _theme => "9", _tint => "0.79998168889431442" },
-        first    => { _theme => "9", _tint => "-0.249977111117893" },
-        last     => { _theme => "9", _tint => "-0.249977111117893" },
-        high     => { _theme => "9", _tint => "-0.499984740745262" },
-        low      => { _theme => "9", _tint => "-0.499984740745262" },
-    },
-    {   # 25
-        series   => { _theme => "1", _tint => "0.499984740745262" },
-        negative => { _theme => "1", _tint => "0.249977111117893" },
-        markers  => { _theme => "1", _tint => "0.249977111117893" },
-        first    => { _theme => "1", _tint => "0.249977111117893" },
-        last     => { _theme => "1", _tint => "0.249977111117893" },
-        high     => { _theme => "1", _tint => "0.249977111117893" },
-        low      => { _theme => "1", _tint => "0.249977111117893" },
-    },
-    {   # 26
-        series   => { _theme => "1", _tint => "0.34998626667073579" },
-        negative => { _theme => "0", _tint => "-0.249977111117893" },
-        markers  => { _theme => "0", _tint => "-0.249977111117893" },
-        first    => { _theme => "0", _tint => "-0.249977111117893" },
-        last     => { _theme => "0", _tint => "-0.249977111117893" },
-        high     => { _theme => "0", _tint => "-0.249977111117893" },
-        low      => { _theme => "0", _tint => "-0.249977111117893" },
-    },
-    {   # 27
-        series   => { _rgb => "FF323232" },
-        negative => { _rgb => "FFD00000" },
-        markers  => { _rgb => "FFD00000" },
-        first    => { _rgb => "FFD00000" },
-        last     => { _rgb => "FFD00000" },
-        high     => { _rgb => "FFD00000" },
-        low      => { _rgb => "FFD00000" },
-    },
-    {   # 28
-        series   => { _rgb => "FF000000" },
-        negative => { _rgb => "FF0070C0" },
-        markers  => { _rgb => "FF0070C0" },
-        first    => { _rgb => "FF0070C0" },
-        last     => { _rgb => "FF0070C0" },
-        high     => { _rgb => "FF0070C0" },
-        low      => { _rgb => "FF0070C0" },
-    },
-    {   # 29
-        series   => { _rgb => "FF376092" },
-        negative => { _rgb => "FFD00000" },
-        markers  => { _rgb => "FFD00000" },
-        first    => { _rgb => "FFD00000" },
-        last     => { _rgb => "FFD00000" },
-        high     => { _rgb => "FFD00000" },
-        low      => { _rgb => "FFD00000" },
-    },
-    {   # 30
-        series   => { _rgb => "FF0070C0" },
-        negative => { _rgb => "FF000000" },
-        markers  => { _rgb => "FF000000" },
-        first    => { _rgb => "FF000000" },
-        last     => { _rgb => "FF000000" },
-        high     => { _rgb => "FF000000" },
-        low      => { _rgb => "FF000000" },
-    },
-    {   # 31
-        series   => { _rgb => "FF5F5F5F" },
-        negative => { _rgb => "FFFFB620" },
-        markers  => { _rgb => "FFD70077" },
-        first    => { _rgb => "FF5687C2" },
-        last     => { _rgb => "FF359CEB" },
-        high     => { _rgb => "FF56BE79" },
-        low      => { _rgb => "FFFF5055" },
-    },
-    {   # 32
-        series   => { _rgb => "FF5687C2" },
-        negative => { _rgb => "FFFFB620" },
-        markers  => { _rgb => "FFD70077" },
-        first    => { _rgb => "FF777777" },
-        last     => { _rgb => "FF359CEB" },
-        high     => { _rgb => "FF56BE79" },
-        low      => { _rgb => "FFFF5055" },
-    },
-    {   # 33
-        series   => { _rgb => "FFC6EFCE" },
-        negative => { _rgb => "FFFFC7CE" },
-        markers  => { _rgb => "FF8CADD6" },
-        first    => { _rgb => "FFFFDC47" },
-        last     => { _rgb => "FFFFEB9C" },
-        high     => { _rgb => "FF60D276" },
-        low      => { _rgb => "FFFF5367" },
-    },
-    {   # 34
-        series   => { _rgb => "FF00B050" },
-        negative => { _rgb => "FFFF0000" },
-        markers  => { _rgb => "FF0070C0" },
-        first    => { _rgb => "FFFFC000" },
-        last     => { _rgb => "FFFFC000" },
-        high     => { _rgb => "FF00B050" },
-        low      => { _rgb => "FFFF0000" },
-    },
-    {   # 35
-        series   => { _theme => "3" },
-        negative => { _theme => "9" },
-        markers  => { _theme => "8" },
-        first    => { _theme => "4" },
-        last     => { _theme => "5" },
-        high     => { _theme => "6" },
-        low      => { _theme => "7" },
-    },
-    {   # 36
-        series   => { _theme => "1" },
-        negative => { _theme => "9" },
-        markers  => { _theme => "8" },
-        first    => { _theme => "4" },
-        last     => { _theme => "5" },
-        high     => { _theme => "6" },
-        low      => { _theme => "7" },
-    },
-);
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _write_theme_file()
-#
-# Write a default theme.xml file.
-#
-sub _write_theme_file {
-
-    my $self   = shift;
-    my $theme  = << 'XML_DATA';
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements><a:clrScheme name="Office"><a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1><a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="1F497D"/></a:dk2><a:lt2><a:srgbClr val="EEECE1"/></a:lt2><a:accent1><a:srgbClr val="4F81BD"/></a:accent1><a:accent2><a:srgbClr val="C0504D"/></a:accent2><a:accent3><a:srgbClr val="9BBB59"/></a:accent3><a:accent4><a:srgbClr val="8064A2"/></a:accent4><a:accent5><a:srgbClr val="4BACC6"/></a:accent5><a:accent6><a:srgbClr val="F79646"/></a:accent6><a:hlink><a:srgbClr val="0000FF"/></a:hlink><a:folHlink><a:srgbClr val="800080"/></a:folHlink></a:clrScheme><a:fontScheme name="Office"><a:majorFont><a:latin typeface="Cambria"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="MS Pゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="宋体"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Times New Roman"/><a:font script="Hebr" typeface="Times New Roman"/><a:font script="Thai" typeface="Tahoma"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="MoolBoran"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Times New Roman"/><a:font script="Uigh" typeface="Microsoft Uighur"/></a:majorFont><a:minorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="MS Pゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="宋体"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Arial"/><a:font script="Hebr" typeface="Arial"/><a:font script="Thai" typeface="Tahoma"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="DaunPenh"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Arial"/><a:font script="Uigh" typeface="Microsoft Uighur"/></a:minorFont></a:fontScheme><a:fmtScheme name="Office"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="50000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="35000"><a:schemeClr val="phClr"><a:tint val="37000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="15000"/><a:satMod val="350000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="16200000" scaled="1"/></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:shade val="51000"/><a:satMod val="130000"/></a:schemeClr></a:gs><a:gs pos="80000"><a:schemeClr val="phClr"><a:shade val="93000"/><a:satMod val="130000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="94000"/><a:satMod val="135000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="16200000" scaled="0"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"><a:shade val="95000"/><a:satMod val="105000"/></a:schemeClr></a:solidFill><a:prstDash val="solid"/></a:ln><a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln><a:ln w="38100" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="38000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr></a:outerShdw></a:effectLst><a:scene3d><a:camera prst="orthographicFront"><a:rot lat="0" lon="0" rev="0"/></a:camera><a:lightRig rig="threePt" dir="t"><a:rot lat="0" lon="0" rev="1200000"/></a:lightRig></a:scene3d><a:sp3d><a:bevelT w="63500" h="25400"/></a:sp3d></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="40000"/><a:satMod val="350000"/></a:schemeClr></a:gs><a:gs pos="40000"><a:schemeClr val="phClr"><a:tint val="45000"/><a:shade val="99000"/><a:satMod val="350000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="20000"/><a:satMod val="255000"/></a:schemeClr></a:gs></a:gsLst><a:path path="circle"><a:fillToRect l="50000" t="-80000" r="50000" b="180000"/></a:path></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="80000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="30000"/><a:satMod val="200000"/></a:schemeClr></a:gs></a:gsLst><a:path path="circle"><a:fillToRect l="50000" t="50000" r="50000" b="50000"/></a:path></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults/><a:extraClrSchemeLst/></a:theme>
-XML_DATA
-
-    local $\ = undef; # Protect print from -l on commandline.
-    print { $self->{_fh} } $theme;
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Theme - A class for writing the Excel XLSX Theme file.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/VML.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/VML.pm
deleted file mode 100644 (file)
index ee6b015..0000000
+++ /dev/null
@@ -1,1214 +0,0 @@
-package Excel::Writer::XLSX::Package::VML;
-
-###############################################################################
-#
-# VML - A class for writing the Excel XLSX VML files.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Excel::Writer::XLSX::Package::XMLwriter;
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $fh    = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self               = shift;
-    my $data_id            = shift;
-    my $vml_shape_id       = shift;
-    my $comments_data      = shift;
-    my $buttons_data       = shift;
-    my $header_images_data = shift;
-    my $z_index            = 1;
-
-
-    $self->_write_xml_namespace;
-
-    # Write the o:shapelayout element.
-    $self->_write_shapelayout( $data_id );
-
-    if ( defined $buttons_data && @$buttons_data ) {
-
-        # Write the v:shapetype element.
-        $self->_write_button_shapetype();
-
-        for my $button ( @$buttons_data ) {
-
-            # Write the v:shape element.
-            $self->_write_button_shape( ++$vml_shape_id, $z_index++, $button );
-        }
-    }
-
-    if ( defined $comments_data && @$comments_data ) {
-
-        # Write the v:shapetype element.
-        $self->_write_comment_shapetype();
-
-        for my $comment ( @$comments_data ) {
-
-            # Write the v:shape element.
-            $self->_write_comment_shape( ++$vml_shape_id, $z_index++,
-                $comment );
-        }
-    }
-
-    if ( defined $header_images_data && @$header_images_data ) {
-
-        # Write the v:shapetype element.
-        $self->_write_image_shapetype();
-
-        my $index = 1;
-        for my $image ( @$header_images_data ) {
-
-            # Write the v:shape element.
-            $self->_write_image_shape( ++$vml_shape_id, $index++, $image );
-        }
-    }
-
-
-    $self->xml_end_tag( 'xml' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _pixels_to_points()
-#
-# Convert comment vertices from pixels to points.
-#
-sub _pixels_to_points {
-
-    my $self     = shift;
-    my $vertices = shift;
-
-    my (
-        $col_start, $row_start, $x1,    $y1,
-        $col_end,   $row_end,   $x2,    $y2,
-        $left,      $top,       $width, $height
-    ) = @$vertices;
-
-    for my $pixels ( $left, $top, $width, $height ) {
-        $pixels *= 0.75;
-    }
-
-    return ( $left, $top, $width, $height );
-}
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _write_xml_namespace()
-#
-# Write the <xml> element. This is the root element of VML.
-#
-sub _write_xml_namespace {
-
-    my $self    = shift;
-    my $schema  = 'urn:schemas-microsoft-com:';
-    my $xmlns   = $schema . 'vml';
-    my $xmlns_o = $schema . 'office:office';
-    my $xmlns_x = $schema . 'office:excel';
-
-    my @attributes = (
-        'xmlns:v' => $xmlns,
-        'xmlns:o' => $xmlns_o,
-        'xmlns:x' => $xmlns_x,
-    );
-
-    $self->xml_start_tag( 'xml', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_shapelayout()
-#
-# Write the <o:shapelayout> element.
-#
-sub _write_shapelayout {
-
-    my $self    = shift;
-    my $data_id = shift;
-    my $ext     = 'edit';
-
-    my @attributes = ( 'v:ext' => $ext );
-
-    $self->xml_start_tag( 'o:shapelayout', @attributes );
-
-    # Write the o:idmap element.
-    $self->_write_idmap( $data_id );
-
-    $self->xml_end_tag( 'o:shapelayout' );
-}
-
-
-##############################################################################
-#
-# _write_idmap()
-#
-# Write the <o:idmap> element.
-#
-sub _write_idmap {
-
-    my $self    = shift;
-    my $ext     = 'edit';
-    my $data_id = shift;
-
-    my @attributes = (
-        'v:ext' => $ext,
-        'data'  => $data_id,
-    );
-
-    $self->xml_empty_tag( 'o:idmap', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_comment_shapetype()
-#
-# Write the <v:shapetype> element.
-#
-sub _write_comment_shapetype {
-
-    my $self      = shift;
-    my $id        = '_x0000_t202';
-    my $coordsize = '21600,21600';
-    my $spt       = 202;
-    my $path      = 'm,l,21600r21600,l21600,xe';
-
-    my @attributes = (
-        'id'        => $id,
-        'coordsize' => $coordsize,
-        'o:spt'     => $spt,
-        'path'      => $path,
-    );
-
-    $self->xml_start_tag( 'v:shapetype', @attributes );
-
-    # Write the v:stroke element.
-    $self->_write_stroke();
-
-    # Write the v:path element.
-    $self->_write_comment_path( 't', 'rect' );
-
-    $self->xml_end_tag( 'v:shapetype' );
-}
-
-
-##############################################################################
-#
-# _write_button_shapetype()
-#
-# Write the <v:shapetype> element.
-#
-sub _write_button_shapetype {
-
-    my $self      = shift;
-    my $id        = '_x0000_t201';
-    my $coordsize = '21600,21600';
-    my $spt       = 201;
-    my $path      = 'm,l,21600r21600,l21600,xe';
-
-    my @attributes = (
-        'id'        => $id,
-        'coordsize' => $coordsize,
-        'o:spt'     => $spt,
-        'path'      => $path,
-    );
-
-    $self->xml_start_tag( 'v:shapetype', @attributes );
-
-    # Write the v:stroke element.
-    $self->_write_stroke();
-
-    # Write the v:path element.
-    $self->_write_button_path( 't', 'rect' );
-
-    # Write the o:lock element.
-    $self->_write_shapetype_lock();
-
-    $self->xml_end_tag( 'v:shapetype' );
-}
-
-
-##############################################################################
-#
-# _write_image_shapetype()
-#
-# Write the <v:shapetype> element.
-#
-sub _write_image_shapetype {
-
-    my $self             = shift;
-    my $id               = '_x0000_t75';
-    my $coordsize        = '21600,21600';
-    my $spt              = 75;
-    my $o_preferrelative = 't';
-    my $path             = 'm@4@5l@4@11@9@11@9@5xe';
-    my $filled           = 'f';
-    my $stroked          = 'f';
-
-    my @attributes = (
-        'id'               => $id,
-        'coordsize'        => $coordsize,
-        'o:spt'            => $spt,
-        'o:preferrelative' => $o_preferrelative,
-        'path'             => $path,
-        'filled'           => $filled,
-        'stroked'          => $stroked,
-    );
-
-    $self->xml_start_tag( 'v:shapetype', @attributes );
-
-    # Write the v:stroke element.
-    $self->_write_stroke();
-
-    # Write the v:formulas element.
-    $self->_write_formulas();
-
-    # Write the v:path element.
-    $self->_write_image_path();
-
-    # Write the o:lock element.
-    $self->_write_aspect_ratio_lock();
-
-    $self->xml_end_tag( 'v:shapetype' );
-}
-
-
-##############################################################################
-#
-# _write_stroke()
-#
-# Write the <v:stroke> element.
-#
-sub _write_stroke {
-
-    my $self      = shift;
-    my $joinstyle = 'miter';
-
-    my @attributes = ( 'joinstyle' => $joinstyle );
-
-    $self->xml_empty_tag( 'v:stroke', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_comment_path()
-#
-# Write the <v:path> element.
-#
-sub _write_comment_path {
-
-    my $self            = shift;
-    my $gradientshapeok = shift;
-    my $connecttype     = shift;
-    my @attributes      = ();
-
-    push @attributes, ( 'gradientshapeok' => 't' ) if $gradientshapeok;
-    push @attributes, ( 'o:connecttype' => $connecttype );
-
-    $self->xml_empty_tag( 'v:path', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_button_path()
-#
-# Write the <v:path> element.
-#
-sub _write_button_path {
-
-    my $self        = shift;
-    my $shadowok    = 'f';
-    my $extrusionok = 'f';
-    my $strokeok    = 'f';
-    my $fillok      = 'f';
-    my $connecttype = 'rect';
-
-    my @attributes = (
-        'shadowok'      => $shadowok,
-        'o:extrusionok' => $extrusionok,
-        'strokeok'      => $strokeok,
-        'fillok'        => $fillok,
-        'o:connecttype' => $connecttype,
-    );
-
-    $self->xml_empty_tag( 'v:path', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_image_path()
-#
-# Write the <v:path> element.
-#
-sub _write_image_path {
-
-    my $self            = shift;
-    my $extrusionok     = 'f';
-    my $gradientshapeok = 't';
-    my $connecttype     = 'rect';
-
-    my @attributes = (
-        'o:extrusionok'   => $extrusionok,
-        'gradientshapeok' => $gradientshapeok,
-        'o:connecttype'   => $connecttype,
-    );
-
-    $self->xml_empty_tag( 'v:path', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_shapetype_lock()
-#
-# Write the <o:lock> element.
-#
-sub _write_shapetype_lock {
-
-    my $self      = shift;
-    my $ext       = 'edit';
-    my $shapetype = 't';
-
-    my @attributes = (
-        'v:ext'     => $ext,
-        'shapetype' => $shapetype,
-    );
-
-    $self->xml_empty_tag( 'o:lock', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_rotation_lock()
-#
-# Write the <o:lock> element.
-#
-sub _write_rotation_lock {
-
-    my $self     = shift;
-    my $ext      = 'edit';
-    my $rotation = 't';
-
-    my @attributes = (
-        'v:ext'    => $ext,
-        'rotation' => $rotation,
-    );
-
-    $self->xml_empty_tag( 'o:lock', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_aspect_ratio_lock()
-#
-# Write the <o:lock> element.
-#
-sub _write_aspect_ratio_lock {
-
-    my $self        = shift;
-    my $ext         = 'edit';
-    my $aspectratio = 't';
-
-    my @attributes = (
-        'v:ext'       => $ext,
-        'aspectratio' => $aspectratio,
-    );
-
-    $self->xml_empty_tag( 'o:lock', @attributes );
-}
-
-##############################################################################
-#
-# _write_comment_shape()
-#
-# Write the <v:shape> element.
-#
-sub _write_comment_shape {
-
-    my $self       = shift;
-    my $id         = shift;
-    my $z_index    = shift;
-    my $comment    = shift;
-    my $type       = '#_x0000_t202';
-    my $insetmode  = 'auto';
-    my $visibility = 'hidden';
-
-    # Set the shape index.
-    $id = '_x0000_s' . $id;
-
-    # Get the comment parameters
-    my $row       = $comment->[0];
-    my $col       = $comment->[1];
-    my $string    = $comment->[2];
-    my $author    = $comment->[3];
-    my $visible   = $comment->[4];
-    my $fillcolor = $comment->[5];
-    my $vertices  = $comment->[6];
-
-    my ( $left, $top, $width, $height ) = $self->_pixels_to_points( $vertices );
-
-    # Set the visibility.
-    $visibility = 'visible' if $visible;
-
-    my $style =
-        'position:absolute;'
-      . 'margin-left:'
-      . $left . 'pt;'
-      . 'margin-top:'
-      . $top . 'pt;'
-      . 'width:'
-      . $width . 'pt;'
-      . 'height:'
-      . $height . 'pt;'
-      . 'z-index:'
-      . $z_index . ';'
-      . 'visibility:'
-      . $visibility;
-
-
-    my @attributes = (
-        'id'          => $id,
-        'type'        => $type,
-        'style'       => $style,
-        'fillcolor'   => $fillcolor,
-        'o:insetmode' => $insetmode,
-    );
-
-    $self->xml_start_tag( 'v:shape', @attributes );
-
-    # Write the v:fill element.
-    $self->_write_comment_fill();
-
-    # Write the v:shadow element.
-    $self->_write_shadow();
-
-    # Write the v:path element.
-    $self->_write_comment_path( undef, 'none' );
-
-    # Write the v:textbox element.
-    $self->_write_comment_textbox();
-
-    # Write the x:ClientData element.
-    $self->_write_comment_client_data( $row, $col, $visible, $vertices );
-
-    $self->xml_end_tag( 'v:shape' );
-}
-
-
-##############################################################################
-#
-# _write_button_shape()
-#
-# Write the <v:shape> element.
-#
-sub _write_button_shape {
-
-    my $self       = shift;
-    my $id         = shift;
-    my $z_index    = shift;
-    my $button     = shift;
-    my $type       = '#_x0000_t201';
-
-    # Set the shape index.
-    $id = '_x0000_s' . $id;
-
-    # Get the button parameters
-    my $row       = $button->{_row};
-    my $col       = $button->{_col};
-    my $vertices  = $button->{_vertices};
-
-    my ( $left, $top, $width, $height ) = $self->_pixels_to_points( $vertices );
-
-    my $style =
-        'position:absolute;'
-      . 'margin-left:'
-      . $left . 'pt;'
-      . 'margin-top:'
-      . $top . 'pt;'
-      . 'width:'
-      . $width . 'pt;'
-      . 'height:'
-      . $height . 'pt;'
-      . 'z-index:'
-      . $z_index . ';'
-      . 'mso-wrap-style:tight';
-
-
-    my @attributes = (
-        'id'          => $id,
-        'type'        => $type,
-        'style'       => $style,
-        'o:button'    => 't',
-        'fillcolor'   => 'buttonFace [67]',
-        'strokecolor' => 'windowText [64]',
-        'o:insetmode' => 'auto',
-    );
-
-    $self->xml_start_tag( 'v:shape', @attributes );
-
-    # Write the v:fill element.
-    $self->_write_button_fill();
-
-    # Write the o:lock element.
-    $self->_write_rotation_lock();
-
-    # Write the v:textbox element.
-    $self->_write_button_textbox( $button->{_font} );
-
-    # Write the x:ClientData element.
-    $self->_write_button_client_data( $button );
-
-    $self->xml_end_tag( 'v:shape' );
-}
-
-
-##############################################################################
-#
-# _write_image_shape()
-#
-# Write the <v:shape> element.
-#
-sub _write_image_shape {
-
-    my $self       = shift;
-    my $id         = shift;
-    my $index      = shift;
-    my $image_data = shift;
-    my $type       = '#_x0000_t75';
-
-    # Set the shape index.
-    $id = '_x0000_s' . $id;
-
-    # Get the image parameters
-    my $width    = $image_data->[0];
-    my $height   = $image_data->[1];
-    my $name     = $image_data->[2];
-    my $position = $image_data->[3];
-    my $x_dpi    = $image_data->[4];
-    my $y_dpi    = $image_data->[5];
-
-    # Scale the height/width by the resolution, relative to 72dpi.
-    $width  = $width  * 72 / $x_dpi;
-    $height = $height * 72 / $y_dpi;
-
-    # Excel uses a rounding based around 72 and 96 dpi.
-    $width  = 72/96 * int($width  * 96/72 + 0.25);
-    $height = 72/96 * int($height * 96/72 + 0.25);
-
-    my $style =
-        'position:absolute;'
-      . 'margin-left:0;'
-      . 'margin-top:0;'
-      . 'width:'
-      . $width . 'pt;'
-      . 'height:'
-      . $height . 'pt;'
-      . 'z-index:'
-      . $index;
-
-    my @attributes = (
-        'id'     => $position,
-        'o:spid' => $id,
-        'type'   => $type,
-        'style'  => $style,
-    );
-
-    $self->xml_start_tag( 'v:shape', @attributes );
-
-    # Write the v:imagedata element.
-    $self->_write_imagedata( $index, $name );
-
-    # Write the o:lock element.
-    $self->_write_rotation_lock();
-
-    $self->xml_end_tag( 'v:shape' );
-}
-
-##############################################################################
-#
-# _write_comment_fill()
-#
-# Write the <v:fill> element.
-#
-sub _write_comment_fill {
-
-    my $self    = shift;
-    my $color_2 = '#ffffe1';
-
-    my @attributes = ( 'color2' => $color_2 );
-
-    $self->xml_empty_tag( 'v:fill', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_button_fill()
-#
-# Write the <v:fill> element.
-#
-sub _write_button_fill {
-
-    my $self             = shift;
-    my $color_2          = 'buttonFace [67]';
-    my $detectmouseclick = 't';
-
-    my @attributes = (
-        'color2'             => $color_2,
-        'o:detectmouseclick' => $detectmouseclick,
-    );
-
-    $self->xml_empty_tag( 'v:fill', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_shadow()
-#
-# Write the <v:shadow> element.
-#
-sub _write_shadow {
-
-    my $self     = shift;
-    my $on       = 't';
-    my $color    = 'black';
-    my $obscured = 't';
-
-    my @attributes = (
-        'on'       => $on,
-        'color'    => $color,
-        'obscured' => $obscured,
-    );
-
-    $self->xml_empty_tag( 'v:shadow', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_comment_textbox()
-#
-# Write the <v:textbox> element.
-#
-sub _write_comment_textbox {
-
-    my $self  = shift;
-    my $style = 'mso-direction-alt:auto';
-
-    my @attributes = ( 'style' => $style );
-
-    $self->xml_start_tag( 'v:textbox', @attributes );
-
-    # Write the div element.
-    $self->_write_div( 'left' );
-
-    $self->xml_end_tag( 'v:textbox' );
-}
-
-
-##############################################################################
-#
-# _write_button_textbox()
-#
-# Write the <v:textbox> element.
-#
-sub _write_button_textbox {
-
-    my $self  = shift;
-    my $font  = shift;
-    my $style = 'mso-direction-alt:auto';
-
-    my @attributes = ( 'style' => $style, 'o:singleclick' => 'f' );
-
-    $self->xml_start_tag( 'v:textbox', @attributes );
-
-    # Write the div element.
-    $self->_write_div( 'center', $font );
-
-    $self->xml_end_tag( 'v:textbox' );
-}
-
-
-##############################################################################
-#
-# _write_div()
-#
-# Write the <div> element.
-#
-sub _write_div {
-
-    my $self  = shift;
-    my $align = shift;
-    my $font  = shift;
-    my $style = 'text-align:' . $align;
-
-    my @attributes = ( 'style' => $style );
-
-    $self->xml_start_tag( 'div', @attributes );
-
-
-    if ( $font ) {
-
-        # Write the font element.
-        $self->_write_font( $font );
-    }
-
-    $self->xml_end_tag( 'div' );
-}
-
-##############################################################################
-#
-# _write_font()
-#
-# Write the <font> element.
-#
-sub _write_font {
-
-    my $self    = shift;
-    my $font    = shift;
-    my $caption = $font->{_caption};
-    my $face    = 'Calibri';
-    my $size    = 220;
-    my $color   = '#000000';
-
-    my @attributes = (
-        'face'  => $face,
-        'size'  => $size,
-        'color' => $color,
-    );
-
-    $self->xml_data_element( 'font', $caption, @attributes );
-}
-
-
-##############################################################################
-#
-# _write_comment_client_data()
-#
-# Write the <x:ClientData> element.
-#
-sub _write_comment_client_data {
-
-    my $self        = shift;
-    my $row         = shift;
-    my $col         = shift;
-    my $visible     = shift;
-    my $vertices    = shift;
-    my $object_type = 'Note';
-
-    my @attributes = ( 'ObjectType' => $object_type );
-
-    $self->xml_start_tag( 'x:ClientData', @attributes );
-
-    # Write the x:MoveWithCells element.
-    $self->_write_move_with_cells();
-
-    # Write the x:SizeWithCells element.
-    $self->_write_size_with_cells();
-
-    # Write the x:Anchor element.
-    $self->_write_anchor( $vertices );
-
-    # Write the x:AutoFill element.
-    $self->_write_auto_fill();
-
-    # Write the x:Row element.
-    $self->_write_row( $row );
-
-    # Write the x:Column element.
-    $self->_write_column( $col );
-
-    # Write the x:Visible element.
-    $self->_write_visible() if $visible;
-
-    $self->xml_end_tag( 'x:ClientData' );
-}
-
-
-##############################################################################
-#
-# _write_button_client_data()
-#
-# Write the <x:ClientData> element.
-#
-sub _write_button_client_data {
-
-    my $self      = shift;
-    my $button    = shift;
-    my $row       = $button->{_row};
-    my $col       = $button->{_col};
-    my $macro     = $button->{_macro};
-    my $vertices  = $button->{_vertices};
-
-
-    my $object_type = 'Button';
-
-    my @attributes = ( 'ObjectType' => $object_type );
-
-    $self->xml_start_tag( 'x:ClientData', @attributes );
-
-    # Write the x:Anchor element.
-    $self->_write_anchor( $vertices );
-
-    # Write the x:PrintObject element.
-    $self->_write_print_object();
-
-    # Write the x:AutoFill element.
-    $self->_write_auto_fill();
-
-    # Write the x:FmlaMacro element.
-    $self->_write_fmla_macro( $macro );
-
-    # Write the x:TextHAlign element.
-    $self->_write_text_halign();
-
-    # Write the x:TextVAlign element.
-    $self->_write_text_valign();
-
-    $self->xml_end_tag( 'x:ClientData' );
-}
-
-
-##############################################################################
-#
-# _write_move_with_cells()
-#
-# Write the <x:MoveWithCells> element.
-#
-sub _write_move_with_cells {
-
-    my $self = shift;
-
-    $self->xml_empty_tag( 'x:MoveWithCells' );
-}
-
-
-##############################################################################
-#
-# _write_size_with_cells()
-#
-# Write the <x:SizeWithCells> element.
-#
-sub _write_size_with_cells {
-
-    my $self = shift;
-
-    $self->xml_empty_tag( 'x:SizeWithCells' );
-}
-
-
-##############################################################################
-#
-# _write_visible()
-#
-# Write the <x:Visible> element.
-#
-sub _write_visible {
-
-    my $self = shift;
-
-    $self->xml_empty_tag( 'x:Visible' );
-}
-
-
-##############################################################################
-#
-# _write_anchor()
-#
-# Write the <x:Anchor> element.
-#
-sub _write_anchor {
-
-    my $self     = shift;
-    my $vertices = shift;
-
-    my ( $col_start, $row_start, $x1, $y1, $col_end, $row_end, $x2, $y2 ) =
-      @$vertices;
-
-    my $data = join ", ",
-      ( $col_start, $x1, $row_start, $y1, $col_end, $x2, $row_end, $y2 );
-
-    $self->xml_data_element( 'x:Anchor', $data );
-}
-
-
-##############################################################################
-#
-# _write_auto_fill()
-#
-# Write the <x:AutoFill> element.
-#
-sub _write_auto_fill {
-
-    my $self = shift;
-    my $data = 'False';
-
-    $self->xml_data_element( 'x:AutoFill', $data );
-}
-
-
-##############################################################################
-#
-# _write_row()
-#
-# Write the <x:Row> element.
-#
-sub _write_row {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'x:Row', $data );
-}
-
-
-##############################################################################
-#
-# _write_column()
-#
-# Write the <x:Column> element.
-#
-sub _write_column {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'x:Column', $data );
-}
-
-
-##############################################################################
-#
-# _write_print_object()
-#
-# Write the <x:PrintObject> element.
-#
-sub _write_print_object {
-
-    my $self = shift;
-    my $data = 'False';
-
-    $self->xml_data_element( 'x:PrintObject', $data );
-}
-
-
-##############################################################################
-#
-# _write_text_halign()
-#
-# Write the <x:TextHAlign> element.
-#
-sub _write_text_halign {
-
-    my $self = shift;
-    my $data = 'Center';
-
-    $self->xml_data_element( 'x:TextHAlign', $data );
-}
-
-
-##############################################################################
-#
-# _write_text_valign()
-#
-# Write the <x:TextVAlign> element.
-#
-sub _write_text_valign {
-
-    my $self = shift;
-    my $data = 'Center';
-
-    $self->xml_data_element( 'x:TextVAlign', $data );
-}
-
-
-##############################################################################
-#
-# _write_fmla_macro()
-#
-# Write the <x:FmlaMacro> element.
-#
-sub _write_fmla_macro {
-
-    my $self = shift;
-    my $data = shift;
-
-    $self->xml_data_element( 'x:FmlaMacro', $data );
-}
-
-##############################################################################
-#
-# _write_imagedata()
-#
-# Write the <v:imagedata> element.
-#
-sub _write_imagedata {
-
-    my $self    = shift;
-    my $index   = shift;
-    my $o_title = shift;
-
-    my @attributes = (
-        'o:relid' => 'rId' . $index,
-        'o:title' => $o_title,
-    );
-
-    $self->xml_empty_tag( 'v:imagedata', @attributes );
-}
-
-
-
-##############################################################################
-#
-# _write_formulas()
-#
-# Write the <v:formulas> element.
-#
-sub _write_formulas {
-
-    my $self                 = shift;
-
-    $self->xml_start_tag( 'v:formulas' );
-
-    # Write the v:f elements.
-    $self->_write_f('if lineDrawn pixelLineWidth 0');
-    $self->_write_f('sum @0 1 0');
-    $self->_write_f('sum 0 0 @1');
-    $self->_write_f('prod @2 1 2');
-    $self->_write_f('prod @3 21600 pixelWidth');
-    $self->_write_f('prod @3 21600 pixelHeight');
-    $self->_write_f('sum @0 0 1');
-    $self->_write_f('prod @6 1 2');
-    $self->_write_f('prod @7 21600 pixelWidth');
-    $self->_write_f('sum @8 21600 0');
-    $self->_write_f('prod @7 21600 pixelHeight');
-    $self->_write_f('sum @10 21600 0');
-
-    $self->xml_end_tag( 'v:formulas' );
-}
-
-
-##############################################################################
-#
-# _write_f()
-#
-# Write the <v:f> element.
-#
-sub _write_f {
-
-    my $self = shift;
-    my $eqn  = shift;
-
-    my @attributes = ( 'eqn' => $eqn );
-
-    $self->xml_empty_tag( 'v:f', @attributes );
-}
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-VML - A class for writing the Excel XLSX VML files.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Package/XMLwriter.pm b/tools/lib/perl5/Excel/Writer/XLSX/Package/XMLwriter.pm
deleted file mode 100644 (file)
index 3e578f0..0000000
+++ /dev/null
@@ -1,534 +0,0 @@
-package Excel::Writer::XLSX::Package::XMLwriter;
-
-###############################################################################
-#
-# XMLwriter - A base class for the Excel::Writer::XLSX writer classes.
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Exporter;
-use Carp;
-use IO::File;
-
-our @ISA     = qw(Exporter);
-our $VERSION = '0.98';
-
-#
-# NOTE: this module is a light weight re-implementation of XML::Writer. See
-# the Pod docs below for a full explanation. The methods  are implemented
-# for speed rather than readability since they are used heavily in tight
-# loops by Excel::Writer::XLSX.
-#
-
-# Note "local $\ = undef" protect print statements from -l on commandline.
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-
-    # FH may be undef and set later in _set_xml_writer(), see below.
-    my $fh = shift;
-
-    my $self = { _fh => $fh };
-
-    bless $self, $class;
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _set_xml_writer()
-#
-# Set the XML writer filehandle for the object. This can either be done
-# in the constructor (usually for testing since the file name isn't generally
-# known at that stage) or later via this method.
-#
-sub _set_xml_writer {
-
-    my $self     = shift;
-    my $filename = shift;
-
-    my $fh = IO::File->new( $filename, 'w' );
-    croak "Couldn't open file $filename for writing.\n" unless $fh;
-
-    binmode $fh, ':utf8';
-
-    $self->{_fh} = $fh;
-}
-
-
-###############################################################################
-#
-# xml_declaration()
-#
-# Write the XML declaration.
-#
-sub xml_declaration {
-
-    my $self = shift;
-    local $\ = undef;
-
-    print { $self->{_fh} }
-      qq(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n);
-
-}
-
-
-###############################################################################
-#
-# xml_start_tag()
-#
-# Write an XML start tag with optional attributes.
-#
-sub xml_start_tag {
-
-    my $self = shift;
-    my $tag  = shift;
-
-    while ( @_ ) {
-        my $key   = shift @_;
-        my $value = shift @_;
-        $value = _escape_attributes( $value );
-
-        $tag .= qq( $key="$value");
-    }
-
-    local $\ = undef;
-    print { $self->{_fh} } "<$tag>";
-}
-
-
-###############################################################################
-#
-# xml_start_tag_unencoded()
-#
-# Write an XML start tag with optional, unencoded, attributes.
-# This is a minor speed optimisation for elements that don't need encoding.
-#
-sub xml_start_tag_unencoded {
-
-    my $self = shift;
-    my $tag  = shift;
-
-    while ( @_ ) {
-        my $key   = shift @_;
-        my $value = shift @_;
-
-        $tag .= qq( $key="$value");
-    }
-
-    local $\ = undef;
-    print { $self->{_fh} } "<$tag>";
-}
-
-
-###############################################################################
-#
-# xml_end_tag()
-#
-# Write an XML end tag.
-#
-sub xml_end_tag {
-
-    my $self = shift;
-    my $tag  = shift;
-    local $\ = undef;
-
-    print { $self->{_fh} } "</$tag>";
-}
-
-
-###############################################################################
-#
-# xml_empty_tag()
-#
-# Write an empty XML tag with optional attributes.
-#
-sub xml_empty_tag {
-
-    my $self = shift;
-    my $tag  = shift;
-
-    while ( @_ ) {
-        my $key   = shift @_;
-        my $value = shift @_;
-        $value = _escape_attributes( $value );
-
-        $tag .= qq( $key="$value");
-    }
-
-    local $\ = undef;
-
-    print { $self->{_fh} } "<$tag/>";
-}
-
-
-###############################################################################
-#
-# xml_empty_tag_unencoded()
-#
-# Write an empty XML tag with optional, unencoded, attributes.
-# This is a minor speed optimisation for elements that don't need encoding.
-#
-sub xml_empty_tag_unencoded {
-
-    my $self = shift;
-    my $tag  = shift;
-
-    while ( @_ ) {
-        my $key   = shift @_;
-        my $value = shift @_;
-
-        $tag .= qq( $key="$value");
-    }
-
-    local $\ = undef;
-
-    print { $self->{_fh} } "<$tag/>";
-}
-
-
-###############################################################################
-#
-# xml_data_element()
-#
-# Write an XML element containing data with optional attributes.
-# XML characters in the data are encoded.
-#
-sub xml_data_element {
-
-    my $self    = shift;
-    my $tag     = shift;
-    my $data    = shift;
-    my $end_tag = $tag;
-
-    while ( @_ ) {
-        my $key   = shift @_;
-        my $value = shift @_;
-        $value = _escape_attributes( $value );
-
-        $tag .= qq( $key="$value");
-    }
-
-    $data = _escape_data( $data );
-
-    local $\ = undef;
-    print { $self->{_fh} } "<$tag>$data</$end_tag>";
-}
-
-
-###############################################################################
-#
-# xml_data_element_unencoded()
-#
-# Write an XML unencoded element containing data with optional attributes.
-# This is a minor speed optimisation for elements that don't need encoding.
-#
-sub xml_data_element_unencoded {
-
-    my $self    = shift;
-    my $tag     = shift;
-    my $data    = shift;
-    my $end_tag = $tag;
-
-    while ( @_ ) {
-        my $key   = shift @_;
-        my $value = shift @_;
-
-        $tag .= qq( $key="$value");
-    }
-
-    local $\ = undef;
-    print { $self->{_fh} } "<$tag>$data</$end_tag>";
-}
-
-
-###############################################################################
-#
-# xml_string_element()
-#
-# Optimised tag writer for <c> cell string elements in the inner loop.
-#
-sub xml_string_element {
-
-    my $self  = shift;
-    my $index = shift;
-    my $attr  = '';
-
-    while ( @_ ) {
-        my $key   = shift;
-        my $value = shift;
-        $attr .= qq( $key="$value");
-    }
-
-    local $\ = undef;
-    print { $self->{_fh} } "<c$attr t=\"s\"><v>$index</v></c>";
-}
-
-
-###############################################################################
-#
-# xml_si_element()
-#
-# Optimised tag writer for shared strings <si> elements.
-#
-sub xml_si_element {
-
-    my $self   = shift;
-    my $string = shift;
-    my $attr   = '';
-
-
-    while ( @_ ) {
-        my $key   = shift;
-        my $value = shift;
-        $attr .= qq( $key="$value");
-    }
-
-    $string = _escape_data( $string );
-
-    local $\ = undef;
-    print { $self->{_fh} } "<si><t$attr>$string</t></si>";
-}
-
-
-###############################################################################
-#
-# xml_rich_si_element()
-#
-# Optimised tag writer for shared strings <si> rich string elements.
-#
-sub xml_rich_si_element {
-
-    my $self   = shift;
-    my $string = shift;
-
-
-    local $\ = undef;
-    print { $self->{_fh} } "<si>$string</si>";
-}
-
-
-###############################################################################
-#
-# xml_number_element()
-#
-# Optimised tag writer for <c> cell number elements in the inner loop.
-#
-sub xml_number_element {
-
-    my $self   = shift;
-    my $number = shift;
-    my $attr   = '';
-
-    while ( @_ ) {
-        my $key   = shift;
-        my $value = shift;
-        $attr .= qq( $key="$value");
-    }
-
-    local $\ = undef;
-    print { $self->{_fh} } "<c$attr><v>$number</v></c>";
-}
-
-
-###############################################################################
-#
-# xml_formula_element()
-#
-# Optimised tag writer for <c> cell formula elements in the inner loop.
-#
-sub xml_formula_element {
-
-    my $self    = shift;
-    my $formula = shift;
-    my $result  = shift;
-    my $attr    = '';
-
-    while ( @_ ) {
-        my $key   = shift;
-        my $value = shift;
-        $attr .= qq( $key="$value");
-    }
-
-    $formula = _escape_data( $formula );
-
-    local $\ = undef;
-    print { $self->{_fh} } "<c$attr><f>$formula</f><v>$result</v></c>";
-}
-
-
-###############################################################################
-#
-# xml_inline_string()
-#
-# Optimised tag writer for inlineStr cell elements in the inner loop.
-#
-sub xml_inline_string {
-
-    my $self     = shift;
-    my $string   = shift;
-    my $preserve = shift;
-    my $attr     = '';
-    my $t_attr   = '';
-
-    # Set the <t> attribute to preserve whitespace.
-    $t_attr = ' xml:space="preserve"' if $preserve;
-
-    while ( @_ ) {
-        my $key   = shift;
-        my $value = shift;
-        $attr .= qq( $key="$value");
-    }
-
-    $string = _escape_data( $string );
-
-    local $\ = undef;
-    print { $self->{_fh} }
-      "<c$attr t=\"inlineStr\"><is><t$t_attr>$string</t></is></c>";
-}
-
-
-###############################################################################
-#
-# xml_rich_inline_string()
-#
-# Optimised tag writer for rich inlineStr cell elements in the inner loop.
-#
-sub xml_rich_inline_string {
-
-    my $self   = shift;
-    my $string = shift;
-    my $attr   = '';
-
-    while ( @_ ) {
-        my $key   = shift;
-        my $value = shift;
-        $attr .= qq( $key="$value");
-    }
-
-    local $\ = undef;
-    print { $self->{_fh} } "<c$attr t=\"inlineStr\"><is>$string</is></c>";
-}
-
-
-###############################################################################
-#
-# xml_get_fh()
-#
-# Return the output filehandle.
-#
-sub xml_get_fh {
-
-    my $self = shift;
-
-    return $self->{_fh};
-}
-
-
-###############################################################################
-#
-# _escape_attributes()
-#
-# Escape XML characters in attributes.
-#
-sub _escape_attributes {
-
-    my $str = $_[0];
-
-    return $str if $str !~ m/["&<>\n]/;
-
-    for ( $str ) {
-        s/&/&amp;/g;
-        s/"/&quot;/g;
-        s/</&lt;/g;
-        s/>/&gt;/g;
-        s/\n/&#xA;/g;
-    }
-
-    return $str;
-}
-
-
-###############################################################################
-#
-# _escape_data()
-#
-# Escape XML characters in data sections. Note, this is different from
-# _escape_attributes() in that double quotes are not escaped by Excel.
-#
-sub _escape_data {
-
-    my $str = $_[0];
-
-    return $str if $str !~ m/[&<>]/;
-
-    for ( $str ) {
-        s/&/&amp;/g;
-        s/</&lt;/g;
-        s/>/&gt;/g;
-    }
-
-    return $str;
-}
-
-
-1;
-
-
-__END__
-
-=pod
-
-=head1 NAME
-
-XMLwriter - A base class for the Excel::Writer::XLSX writer classes.
-
-=head1 DESCRIPTION
-
-This module is used by L<Excel::Writer::XLSX> for writing XML documents. It is a light weight re-implementation of L<XML::Writer>.
-
-XMLwriter is approximately twice as fast as L<XML::Writer>. This speed is achieved at the expense of error and correctness checking. In addition not all of the L<XML::Writer> methods are implemented. As such, XMLwriter is not recommended for use outside of Excel::Writer::XLSX.
-
-=head1 SEE ALSO
-
-L<XML::Writer>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
-
-=head1 LICENSE
-
-Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
-
-=head1 DISCLAIMER OF WARRANTY
-
-See the documentation for L<Excel::Writer::XLSX>.
-
-=cut
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Shape.pm b/tools/lib/perl5/Excel/Writer/XLSX/Shape.pm
deleted file mode 100644 (file)
index 5f16d24..0000000
+++ /dev/null
@@ -1,642 +0,0 @@
-package Excel::Writer::XLSX::Shape;
-
-###############################################################################
-#
-# Shape - A class for writing Excel shapes.
-#
-# Used in conjunction with Excel::Writer::XLSX.
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use Exporter;
-
-our @ISA     = qw(Exporter);
-our $VERSION = '0.98';
-our $AUTOLOAD;
-
-###############################################################################
-#
-# new()
-#
-sub new {
-
-    my $class      = shift;
-    my $fh         = shift;
-    my $self       = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-
-    my %properties = @_;
-
-    $self->{_name} = undef;
-    $self->{_type} = 'rect';
-
-    # Is a Connector shape. 1/0 Value is a hash lookup from type.
-    $self->{_connect} = 0;
-
-    # Is a Drawing. Always 0, since a single shape never fills an entire sheet.
-    $self->{_drawing} = 0;
-
-    # OneCell or Absolute: options to move and/or size with cells.
-    $self->{_editAs} = '';
-
-    # Auto-incremented, unless supplied by user.
-    $self->{_id} = 0;
-
-    # Shape text (usually centered on shape geometry).
-    $self->{_text} = 0;
-
-    # Shape stencil mode.  A copy (child) is created when inserted.
-    # The link to parent is broken.
-    $self->{_stencil} = 1;
-
-    # Index to _shapes array when inserted.
-    $self->{_element} = -1;
-
-    # Shape ID of starting connection, if any.
-    $self->{_start} = undef;
-
-    # Shape vertex, starts at 0, numbered clockwise from 12 o'clock.
-    $self->{_start_index} = undef;
-
-    $self->{_end}       = undef;
-    $self->{_end_index} = undef;
-
-    # Number and size of adjustments for shapes (usually connectors).
-    $self->{_adjustments} = [];
-
-    # Start and end sides. t)op, b)ottom, l)eft, or r)ight.
-    $self->{_start_side} = '';
-    $self->{_end_side}   = '';
-
-    # Flip shape Horizontally. eg. arrow left to arrow right.
-    $self->{_flip_h} = 0;
-
-    # Flip shape Vertically. eg. up arrow to down arrow.
-    $self->{_flip_v} = 0;
-
-    # shape rotation (in degrees 0-360).
-    $self->{_rotation} = 0;
-
-    # An alternate way to create a text box, because Excel allows it.
-    # It is just a rectangle with text.
-    $self->{_txBox} = 0;
-
-    # Shape outline colour, or 0 for noFill (default black).
-    $self->{_line} = '000000';
-
-    # Line type: dash, sysDot, dashDot, lgDash, lgDashDot, lgDashDotDot.
-    $self->{_line_type} = '';
-
-    # Line weight (integer).
-    $self->{_line_weight} = 1;
-
-    # Shape fill colour, or 0 for noFill (default noFill).
-    $self->{_fill} = 0;
-
-    # Formatting for shape text, if any.
-    $self->{_format} = {};
-
-    # copy of colour palette table from Workbook.pm.
-    $self->{_palette} = [];
-
-    # Vertical alignment: t, ctr, b.
-    $self->{_valign} = 'ctr';
-
-    # Alignment: l, ctr, r, just
-    $self->{_align} = 'ctr';
-
-    $self->{_x_offset} = 0;
-    $self->{_y_offset} = 0;
-
-    # Scale factors, which also may be set when the shape is inserted.
-    $self->{_scale_x} = 1;
-    $self->{_scale_y} = 1;
-
-    # Default size, which can be modified and/or scaled.
-    $self->{_width}  = 50;
-    $self->{_height} = 50;
-
-    # Initial assignment. May be modified when prepared.
-    $self->{_column_start} = 0;
-    $self->{_row_start}    = 0;
-    $self->{_x1}           = 0;
-    $self->{_y1}           = 0;
-    $self->{_column_end}   = 0;
-    $self->{_row_end}      = 0;
-    $self->{_x2}           = 0;
-    $self->{_y2}           = 0;
-    $self->{_x_abs}        = 0;
-    $self->{_y_abs}        = 0;
-
-    # Override default properties with passed arguments
-    while ( my ( $key, $value ) = each( %properties ) ) {
-
-        # Strip leading "-" from Tk style properties e.g. -color => 'red'.
-        $key =~ s/^-//;
-
-        # Add leading underscore "_" to internal hash keys, if not supplied.
-        $key = "_" . $key unless $key =~ m/^_/;
-
-        $self->{$key} = $value;
-    }
-
-    bless $self, $class;
-    return $self;
-}
-
-
-###############################################################################
-#
-# set_properties( name => 'Shape 1', type => 'rect' )
-#
-# Set shape properties.
-#
-sub set_properties {
-
-    my $self       = shift;
-    my %properties = @_;
-
-    # Update properties with passed arguments.
-    while ( my ( $key, $value ) = each( %properties ) ) {
-
-        # Strip leading "-" from Tk style properties e.g. -color => 'red'.
-        $key =~ s/^-//;
-
-        # Add leading underscore "_" to internal hash keys, if not supplied.
-        $key = "_" . $key unless $key =~ m/^_/;
-
-        if ( !exists $self->{$key} ) {
-            warn "Unknown shape property: $key. Property not set.\n";
-            next;
-        }
-
-        $self->{$key} = $value;
-    }
-}
-
-
-###############################################################################
-#
-# set_adjustment( adj1, adj2, adj3, ... )
-#
-# Set the shape adjustments array (as a reference).
-#
-sub set_adjustments {
-
-    my $self = shift;
-    $self->{_adjustments} = \@_;
-}
-
-
-###############################################################################
-#
-# AUTOLOAD. Deus ex machina.
-#
-# Dynamically create set/get 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_Cy".
-    $AUTOLOAD =~ /.*::(get|set)(\w+)/ or die "Unknown method: $AUTOLOAD\n";
-
-    # Match the function (get or set) and attribute, i.e. "_xxx_yyy".
-    my $gs        = $1;
-    my $attribute = $2;
-
-    # Check that the attribute exists.
-    exists $self->{$attribute} or die "Unknown method: $AUTOLOAD\n";
-
-    # The attribute value
-    my $value;
-
-    # set_property() pattern.
-    # 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
-
-    $value = $_[0];
-    $value = 1 if not defined $value;    # The default value is always 1
-
-    if ( $gs eq 'set' ) {
-        *{$AUTOLOAD} = sub {
-            my $self  = shift;
-            my $value = shift;
-
-            $value = 1 if not defined $value;
-            $self->{$attribute} = $value;
-        };
-
-        $self->{$attribute} = $value;
-    }
-    else {
-        *{$AUTOLOAD} = sub {
-            my $self = shift;
-            return $self->{$attribute};
-        };
-
-        # Let AUTOLOAD return the attribute for the first invocation
-        return $self->{$attribute};
-    }
-}
-
-
-###############################################################################
-#
-# _get_palette_color()
-#
-# Convert from an Excel internal colour index to a XML style #RRGGBB index
-# based on the default or user defined values in the Workbook palette.
-# Note: This version doesn't add an alpha channel.
-#
-sub _get_palette_color {
-
-    my $self    = shift;
-    my $index   = shift;
-    my $palette = $self->{_palette};
-
-    # Adjust the colour index.
-    $index -= 8;
-
-    # Palette is passed in from the Workbook class.
-    my @rgb = @{ $palette->[$index] };
-
-    return sprintf "%02X%02X%02X", @rgb[0, 1, 2];
-}
-
-
-1;
-
-__END__
-
-=head1 NAME
-
-Shape - A class for creating Excel Drawing shapes
-
-=head1 SYNOPSIS
-
-To create a simple Excel file containing shapes using L<Excel::Writer::XLSX>:
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'shape.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    # Add a default rectangle shape.
-    my $rect = $workbook->add_shape();
-
-    # Add an ellipse with centered text.
-    my $ellipse = $workbook->add_shape(
-        type => 'ellipse',
-        text => "Hello\nWorld"
-    );
-
-    # Add a plus shape.
-    my $plus = $workbook->add_shape( type => 'plus');
-
-    # Insert the shapes in the worksheet.
-    $worksheet->insert_shape( 'B3', $rect );
-    $worksheet->insert_shape( 'C3', $ellipse );
-    $worksheet->insert_shape( 'D3', $plus );
-
-
-=head1 DESCRIPTION
-
-The C<Excel::Writer::XLSX::Shape> module is used to create Shape objects for L<Excel::Writer::XLSX>.
-
-A Shape object is created via the Workbook C<add_shape()> method:
-
-    my $shape_rect = $workbook->add_shape( type => 'rect' );
-
-Once the object is created it can be inserted into a worksheet using the C<insert_shape()> method:
-
-    $worksheet->insert_shape('A1', $shape_rect);
-
-A Shape can be inserted multiple times if required.
-
-    $worksheet->insert_shape('A1', $shape_rect);
-    $worksheet->insert_shape('B2', $shape_rect, 20, 30);
-
-
-=head1 METHODS
-
-=head2 add_shape( %properties )
-
-The C<add_shape()> Workbook method specifies the properties of the Shape in hash C<< property => value >> format:
-
-    my $shape = $workbook->add_shape( %properties );
-
-The available properties are shown below.
-
-=head2 insert_shape( $row, $col, $shape, $x, $y, $scale_x, $scale_y )
-
-The C<insert_shape()> Worksheet method sets the location and scale of the shape object within the worksheet.
-
-    # Insert the shape into the worksheet.
-    $worksheet->insert_shape( 'E2', $shape );
-
-Using the cell location and the C<$x> and C<$y> cell offsets it is possible to position a shape anywhere on the canvas of a worksheet.
-
-A more detailed explanation of the C<insert_shape()> method is given in the main L<Excel::Writer::XLSX> documentation.
-
-
-=head1 SHAPE PROPERTIES
-
-Any shape property can be queried or modified by the corresponding get/set method:
-
-    my $ellipse = $workbook->add_shape( %properties );
-    $ellipse->set_type( 'plus' );    # No longer an ellipse!
-    my $type = $ellipse->get_type();  # Find out what it really is.
-
-Multiple shape properties may also be modified in one go by using the C<set_properties()> method:
-
-    $shape->set_properties( type => 'ellipse', text => 'Hello' );
-
-The properties of a shape object that can be defined via C<add_shape()> are shown below.
-
-=head2 name
-
-Defines the name of the shape. This is an optional property and the shape will be given a default name if not supplied. The name is generally only used by Excel Macros to refer to the object.
-
-=head2 type
-
-Defines the type of the object such as C<rect>, C<ellipse> or C<triangle>:
-
-    my $ellipse = $workbook->add_shape( type => 'ellipse' );
-
-The default type is C<rect>.
-
-The full list of available shapes is shown below.
-
-See also the C<shapes_all.pl> program in the C<examples> directory of the distro. It creates an example workbook with all supported shapes labelled with their shape names.
-
-
-=over 4
-
-=item * Basic Shapes
-
-    blockArc              can            chevron       cube          decagon
-    diamond               dodecagon      donut         ellipse       funnel
-    gear6                 gear9          heart         heptagon      hexagon
-    homePlate             lightningBolt  line          lineInv       moon
-    nonIsoscelesTrapezoid noSmoking      octagon       parallelogram pentagon
-    pie                   pieWedge       plaque        rect          round1Rect
-    round2DiagRect        round2SameRect roundRect     rtTriangle    smileyFace
-    snip1Rect             snip2DiagRect  snip2SameRect snipRoundRect star10
-    star12                star16         star24        star32        star4
-    star5                 star6          star7         star8         sun
-    teardrop              trapezoid      triangle
-
-=item * Arrow Shapes
-
-    bentArrow        bentUpArrow       circularArrow     curvedDownArrow
-    curvedLeftArrow  curvedRightArrow  curvedUpArrow     downArrow
-    leftArrow        leftCircularArrow leftRightArrow    leftRightCircularArrow
-    leftRightUpArrow leftUpArrow       notchedRightArrow quadArrow
-    rightArrow       stripedRightArrow swooshArrow       upArrow
-    upDownArrow      uturnArrow
-
-=item * Connector Shapes
-
-    bentConnector2   bentConnector3   bentConnector4
-    bentConnector5   curvedConnector2 curvedConnector3
-    curvedConnector4 curvedConnector5 straightConnector1
-
-=item * Callout Shapes
-
-    accentBorderCallout1  accentBorderCallout2  accentBorderCallout3
-    accentCallout1        accentCallout2        accentCallout3
-    borderCallout1        borderCallout2        borderCallout3
-    callout1              callout2              callout3
-    cloudCallout          downArrowCallout      leftArrowCallout
-    leftRightArrowCallout quadArrowCallout      rightArrowCallout
-    upArrowCallout        upDownArrowCallout    wedgeEllipseCallout
-    wedgeRectCallout      wedgeRoundRectCallout
-
-=item * Flow Chart Shapes
-
-    flowChartAlternateProcess  flowChartCollate        flowChartConnector
-    flowChartDecision          flowChartDelay          flowChartDisplay
-    flowChartDocument          flowChartExtract        flowChartInputOutput
-    flowChartInternalStorage   flowChartMagneticDisk   flowChartMagneticDrum
-    flowChartMagneticTape      flowChartManualInput    flowChartManualOperation
-    flowChartMerge             flowChartMultidocument  flowChartOfflineStorage
-    flowChartOffpageConnector  flowChartOnlineStorage  flowChartOr
-    flowChartPredefinedProcess flowChartPreparation    flowChartProcess
-    flowChartPunchedCard       flowChartPunchedTape    flowChartSort
-    flowChartSummingJunction   flowChartTerminator
-
-=item * Action Shapes
-
-    actionButtonBackPrevious actionButtonBeginning actionButtonBlank
-    actionButtonDocument     actionButtonEnd       actionButtonForwardNext
-    actionButtonHelp         actionButtonHome      actionButtonInformation
-    actionButtonMovie        actionButtonReturn    actionButtonSound
-
-=item * Chart Shapes
-
-Not to be confused with Excel Charts.
-
-    chartPlus chartStar chartX
-
-=item * Math Shapes
-
-    mathDivide mathEqual mathMinus mathMultiply mathNotEqual mathPlus
-
-=item * Stars and Banners
-
-    arc            bevel          bracePair  bracketPair chord
-    cloud          corner         diagStripe doubleWave  ellipseRibbon
-    ellipseRibbon2 foldedCorner   frame      halfFrame   horizontalScroll
-    irregularSeal1 irregularSeal2 leftBrace  leftBracket leftRightRibbon
-    plus           ribbon         ribbon2    rightBrace  rightBracket
-    verticalScroll wave
-
-=item * Tab Shapes
-
-    cornerTabs plaqueTabs squareTabs
-
-=back
-
-=head2 text
-
-This property is used to make the shape act like a text box.
-
-    my $rect = $workbook->add_shape( type => 'rect', text => "Hello\nWorld" );
-
-The text is super-imposed over the shape. The text can be wrapped using the newline character C<\n>.
-
-=head2 id
-
-Identification number for internal identification. This number will be auto-assigned, if not assigned, or if it is a duplicate.
-
-=head2 format
-
-Workbook format for decorating the shape text (font family, size, and decoration).
-
-=head2 start, start_index
-
-Shape indices of the starting point for a connector and the index of the connection. Index numbers are zero-based, start from the top dead centre and are counted clockwise.
-
-Indices are typically created for vertices and centre points of shapes. They are the blue connection points that appear when connection shapes are selected manually in Excel.
-
-=head2 end, end_index
-
-Same as above but for end points and end connections.
-
-
-=head2 start_side, end_side
-
-This is either the letter C<b> or C<r> for the bottom or right side of the shape to be connected to and from.
-
-If the C<start>, C<start_index>, and C<start_side> parameters are defined for a connection shape, the shape will be auto located and linked to the starting and ending shapes respectively. This can be very useful for flow and organisation charts.
-
-=head2 flip_h, flip_v
-
-Set this value to 1, to flip the shape horizontally and/or vertically.
-
-=head2 rotation
-
-Shape rotation, in degrees, from 0 to 360.
-
-=head2 line, fill
-
-Shape colour for the outline and fill. Colours may be specified as a colour index, or in RGB format, i.e. C<AA00FF>.
-
-See C<COLOURS IN EXCEL> in the main documentation for more information.
-
-=head2 line_type
-
-Line type for shape outline. The default is solid. The list of possible values is:
-
-    dash, sysDot, dashDot, lgDash, lgDashDot, lgDashDotDot, solid
-
-=head2 valign, align
-
-Text alignment within the shape.
-
-Vertical alignment can be:
-
-    Setting     Meaning
-    =======     =======
-    t           Top
-    ctr         Centre
-    b           Bottom
-
-Horizontal alignment can be:
-
-    Setting     Meaning
-    =======     =======
-    l           Left
-    r           Right
-    ctr         Centre
-    just        Justified
-
-The default is to centre both horizontally and vertically.
-
-=head2 scale_x, scale_y
-
-Scale factor in x and y dimension, for scaling the shape width and height. The default value is 1.
-
-Scaling may be set on the shape object or via C<insert_shape()>.
-
-=head2 adjustments
-
-Adjustment of shape vertices. Most shapes do not use this. For some shapes, there is a single adjustment to modify the geometry. For instance, the plus shape has one adjustment to control the width of the spokes.
-
-Connectors can have a number of adjustments to control the shape routing. Typically, a connector will have 3 to 5 handles for routing the shape. The adjustment is in percent of the distance from the starting shape to the ending shape, alternating between the x and y dimension. Adjustments may be negative, to route the shape away from the endpoint.
-
-=head2 stencil
-
-Shapes work in stencil mode by default. That is, once a shape is inserted, its connection is separated from its master. The master shape may be modified after an instance is inserted, and only subsequent insertions will show the modifications.
-
-This is helpful for Org charts, where an employee shape may be created once, and then the text of the shape is modified for each employee.
-
-The C<insert_shape()> method returns a reference to the inserted shape (the child).
-
-Stencil mode can be turned off, allowing for shape(s) to be modified after insertion. In this case the C<insert_shape()> method returns a reference to the inserted shape (the master). This is not very useful for inserting multiple shapes, since the x/y coordinates also gets modified.
-
-=head1 TIPS
-
-Use C<< $worksheet->hide_gridlines(2) >> to prepare a blank canvas without gridlines.
-
-Shapes do not need to fit on one page. Excel will split a large drawing into multiple pages if required. Use the page break preview to show page boundaries superimposed on the drawing.
-
-Connected shapes will auto-locate in Excel if you move either the starting shape or the ending shape separately. However, if you select both shapes (lasso or control-click), the connector will move with it, and the shape adjustments will not re-calculate.
-
-=head1 EXAMPLE
-
-    #!/usr/bin/perl
-
-    use strict;
-    use warnings;
-    use Excel::Writer::XLSX;
-
-    my $workbook  = Excel::Writer::XLSX->new( 'shape.xlsx' );
-    my $worksheet = $workbook->add_worksheet();
-
-    # Add a default rectangle shape.
-    my $rect = $workbook->add_shape();
-
-    # Add an ellipse with centered text.
-    my $ellipse = $workbook->add_shape(
-        type => 'ellipse',
-        text => "Hello\nWorld"
-    );
-
-    # Add a plus shape.
-    my $plus = $workbook->add_shape( type => 'plus');
-
-    # Insert the shapes in the worksheet.
-    $worksheet->insert_shape( 'B3', $rect );
-    $worksheet->insert_shape( 'C3', $ellipse );
-    $worksheet->insert_shape( 'D3', $plus );
-
-
-See also the C<shapes_*.pl> program in the C<examples> directory of the distro.
-
-=head1 TODO
-
-=over 4
-
-=item * Add shapes which have custom geometries.
-
-=item * Provide better integration of workbook formats for shapes.
-
-=item * Add further validation of shape properties to prevent creation of workbooks that will not open.
-
-=item * Auto connect shapes that are not anchored to cell A1.
-
-=item * Add automatic shape connection to shape vertices besides the object centre.
-
-=item * Improve automatic shape connection to shapes with concave sides (e.g. chevron).
-
-=back
-
-=head1 AUTHOR
-
-Dave Clarke dclarke@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Utility.pm b/tools/lib/perl5/Excel/Writer/XLSX/Utility.pm
deleted file mode 100644 (file)
index 72dba85..0000000
+++ /dev/null
@@ -1,910 +0,0 @@
-package Excel::Writer::XLSX::Utility;
-
-###############################################################################
-#
-# Utility - Helper functions for Excel::Writer::XLSX.
-#
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use Exporter;
-use warnings;
-use autouse 'Date::Calc'  => qw(Delta_DHMS Decode_Date_EU Decode_Date_US);
-use autouse 'Date::Manip' => qw(ParseDate Date_Init);
-
-our $VERSION = '0.98';
-
-# Row and column functions
-my @rowcol = qw(
-  xl_rowcol_to_cell
-  xl_cell_to_rowcol
-  xl_col_to_name
-  xl_range
-  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
-);
-
-our @ISA         = qw(Exporter);
-our @EXPORT_OK   = ();
-our @EXPORT      = ( @rowcol, @dates, 'quote_sheetname' );
-our %EXPORT_TAGS = (
-    rowcol => \@rowcol,
-    dates  => \@dates
-);
-
-
-###############################################################################
-#
-# xl_rowcol_to_cell($row, $col, $row_absolute, $col_absolute)
-#
-sub xl_rowcol_to_cell {
-
-    my $row     = $_[0] + 1;          # Change from 0-indexed to 1 indexed.
-    my $col     = $_[1];
-    my $row_abs = $_[2] ? '$' : '';
-    my $col_abs = $_[3] ? '$' : '';
-
-
-    my $col_str = xl_col_to_name( $col, $col_abs );
-
-    return $col_str . $row_abs . $row;
-}
-
-
-###############################################################################
-#
-# 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;
-
-    return ( 0, 0, 0, 0 ) unless $cell;
-
-    $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;
-}
-
-
-###############################################################################
-#
-# xl_col_to_name($col, $col_absolute)
-#
-sub xl_col_to_name {
-
-    my $col     = $_[0];
-    my $col_abs = $_[1] ? '$' : '';
-    my $col_str = '';
-
-    # Change from 0-indexed to 1 indexed.
-    $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;
-}
-
-
-###############################################################################
-#
-# xl_range($row_1, $row_2, $col_1, $col_2, $row_abs_1, $row_abs_2, $col_abs_1, $col_abs_2)
-#
-sub xl_range {
-
-    my ( $row_1,     $row_2,     $col_1,     $col_2 )     = @_[ 0 .. 3 ];
-    my ( $row_abs_1, $row_abs_2, $col_abs_1, $col_abs_2 ) = @_[ 4 .. 7 ];
-
-    my $range1 = xl_rowcol_to_cell( $row_1, $col_1, $row_abs_1, $col_abs_1 );
-    my $range2 = xl_rowcol_to_cell( $row_2, $col_2, $row_abs_2, $col_abs_2 );
-
-    return $range1 . ':' . $range2;
-}
-
-
-###############################################################################
-#
-# 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 ) = @_;
-
-    $sheetname = quote_sheetname( $sheetname );
-
-    my $range = xl_range( $row_1, $row_2, $col_1, $col_2, 1, 1, 1, 1 );
-
-    return '=' . $sheetname . '!' . $range
-}
-
-
-###############################################################################
-#
-# quote_sheetname()
-#
-# Sheetnames used in references should be quoted if they contain any spaces,
-# special characters or if they look like something that isn't a sheet name.
-#
-sub quote_sheetname {
-
-    my $sheetname = $_[0];
-
-    # 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 !~ /^'/ ) {
-        # Double quote any single quotes.
-        $sheetname =~ s/'/''/g;
-        $sheetname = q(') . $sheetname . q(');
-    }
-
-    return $sheetname;
-}
-
-
-###############################################################################
-#
-# 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 );
-}
-
-
-###############################################################################
-#
-# 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 );
-}
-
-
-###############################################################################
-#
-# 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 );
-}
-
-
-###############################################################################
-#
-# 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 );
-}
-
-
-###############################################################################
-#
-# 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;
-}
-
-
-###############################################################################
-#
-# xl_parse_time($string)
-#
-sub xl_parse_time {
-
-    my $time = shift;
-
-    if ( $time =~ /(\d+):(\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
-    }
-}
-
-
-###############################################################################
-#
-# xl_parse_date($string)
-#
-sub xl_parse_date {
-
-    my $date = ParseDate( $_[0] );
-
-    # 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 );
-}
-
-
-###############################################################################
-#
-# xl_parse_date_init("variable=value", ...)
-#
-sub xl_parse_date_init {
-
-    Date_Init( @_ );    # How lazy is that.
-}
-
-
-###############################################################################
-#
-# 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+:\d\d:?(\d\d(\.\d+)?)?(\s+)?(am|pm)?)//i ) {
-        $time = xl_parse_time( $1 );
-    }
-
-    # 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;
-}
-
-
-###############################################################################
-#
-# 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+:\d\d:?(\d\d(\.\d+)?)?(\s+)?(am|pm)?)//i ) {
-        $time = xl_parse_time( $1 );
-    }
-
-    # 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;
-}
-
-
-###############################################################################
-#
-# 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;
-}
-
-
-1;
-
-
-__END__
-
-=head1 NAME
-
-Utility - Helper functions for L<Excel::Writer::XLSX>.
-
-=head1 SYNOPSIS
-
-Functions to help with some common tasks when using L<Excel::Writer::XLSX>.
-
-These functions mainly relate to dealing with rows and columns in A1 notation and to handling dates and times.
-
-    use Excel::Writer::XLSX::Utility;                     # Import everything
-
-    ($row, $col)    = xl_cell_to_rowcol( 'C2' );          # (1, 2)
-    $str            = xl_rowcol_to_cell( 1, 2 );          # C2
-    $str            = xl_col_to_name( 702 );              # AAA
-    $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 L<Excel::Writer::XLSX> 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_col_to_name
-    xl_range
-    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 Excel::Writer::XLSX::Utility;                  # Import everything
-    use Excel::Writer::XLSX::Utility qw(xl_date_list); # xl_date_list only
-    use Excel::Writer::XLSX::Utility qw(:rowcol);      # Row/col functions
-    use Excel::Writer::XLSX::Utility qw(:dates);       # Date functions
-
-=head1 ROW AND COLUMN FUNCTIONS
-
-L<Excel::Writer::XLSX> 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 XFD, i.e. 0 to 16,383, rows range from 0 to 1,048,575 in Excel 2007+. 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 makes a difference if the cell is copied. Refer to the Excel documentation for further details. All of the following functions support absolute references.
-
-=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 above for an explanation of absolute cell references.
-
-=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)
-
-=head2 xl_col_to_name($col, $col_absolute)
-
-    Parameters: $col:           Integer
-                $col_absolute:  Boolean (1/0) [optional, default is 0]
-
-    Returns:    A column string name.
-
-
-This function converts a zero based column reference to a string:
-
-    $str = xl_col_to_name(0);      # A
-    $str = xl_col_to_name(1);      # B
-    $str = xl_col_to_name(702);    # AAA
-
-
-The optional parameter C<$col_absolute> can be used to indicate if the column is absolute:
-
-    $str = xl_col_to_name( 0, 0 );    # A
-    $str = xl_col_to_name( 0, 1 );    # $A
-    $str = xl_col_to_name( 1, 1 );    # $B
-
-=head2 xl_range($row_1, $row_2, $col_1, $col_2, $row_abs_1, $row_abs_2, $col_abs_1, $col_abs_2)
-
-    Parameters: $sheetname      String
-                $row_1:         Integer
-                $row_2:         Integer
-                $col_1:         Integer
-                $col_2:         Integer
-                $row_abs_1:     Boolean (1/0) [optional, default is 0]
-                $row_abs_2:     Boolean (1/0) [optional, default is 0]
-                $col_abs_1:     Boolean (1/0) [optional, default is 0]
-                $col_abs_2:     Boolean (1/0) [optional, default is 0]
-
-    Returns:    A worksheet range formula as a string.
-
-This function converts zero based row and column cell references to an A1 style range string:
-
-    my $str = xl_range( 0, 9, 0, 0 );          # A1:A10
-    my $str = xl_range( 1, 8, 2, 2 );          # C2:C9
-    my $str = xl_range( 0, 3, 0, 4 );          # A1:E4
-    my $str = xl_range( 0, 3, 0, 4, 1 );       # A$1:E4
-    my $str = xl_range( 0, 3, 0, 4, 1, 1 );    # A$1:E$4
-
-=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',
-    );
-
-=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
-
-=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
-
-=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
-
-=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
-
-=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.
-
-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 Excel::Writer::XLSX 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
-
-The date handling functions below are supplied for historical reasons. In the current version of the module it is easier to just use the C<write_date_time()> function to write dates or times. See the DATES AND TIME IN EXCEL section of the main L<Excel::Writer::XLSX> documentation for details.
-
-In addition to using the functions below you must install the L<Date::Manip> and L<Date::Calc> modules. See L<REQUIREMENTS> and the individual requirements of each functions.
-
-For a C<DateTime.pm> solution see the L<DateTime::Format::Excel> module.
-
-=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.
-
-=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 );
-
-=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 L<Date::Manip> module. Refer to the C<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 C<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 );
-
-=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 L<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 C<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
-
-=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 L<Date::Calc> module. Refer to the C<Date::Calc> documentation for further information about the date formats that can be parsed. Also note the following from the C<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 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" );
-
-=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 L<Date::Calc> module. Refer to the C<Date::Calc> documentation for further information about the date formats that can be parsed. Also note the following from the C<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( "Sept 12 Saturday 1998" );
-    $date3 = xl_decode_date_US( "4:30 AM Sept 12 Sat 1998" );
-
-=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 L<Excel::Writer::XLSX> documentation.
-
-=head1 REQUIREMENTS
-
-The date and time functions require functions from the L<Date::Manip> and L<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-MMXVIII, 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.
-
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Workbook.pm b/tools/lib/perl5/Excel/Writer/XLSX/Workbook.pm
deleted file mode 100644 (file)
index 80590a0..0000000
+++ /dev/null
@@ -1,2745 +0,0 @@
-package Excel::Writer::XLSX::Workbook;
-
-###############################################################################
-#
-# Workbook - A class for writing Excel Workbooks.
-#
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use IO::File;
-use File::Find;
-use File::Temp qw(tempfile);
-use File::Basename 'fileparse';
-use Archive::Zip;
-use Excel::Writer::XLSX::Worksheet;
-use Excel::Writer::XLSX::Chartsheet;
-use Excel::Writer::XLSX::Format;
-use Excel::Writer::XLSX::Shape;
-use Excel::Writer::XLSX::Chart;
-use Excel::Writer::XLSX::Package::Packager;
-use Excel::Writer::XLSX::Package::XMLwriter;
-use Excel::Writer::XLSX::Utility qw(xl_cell_to_rowcol xl_rowcol_to_cell);
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class = shift;
-    my $self  = Excel::Writer::XLSX::Package::XMLwriter->new();
-
-    $self->{_filename} = $_[0] || '';
-    my $options = $_[1] || {};
-
-    $self->{_tempdir}            = undef;
-    $self->{_date_1904}          = 0;
-    $self->{_activesheet}        = 0;
-    $self->{_firstsheet}         = 0;
-    $self->{_selected}           = 0;
-    $self->{_fileclosed}         = 0;
-    $self->{_filehandle}         = undef;
-    $self->{_internal_fh}        = 0;
-    $self->{_sheet_name}         = 'Sheet';
-    $self->{_chart_name}         = 'Chart';
-    $self->{_sheetname_count}    = 0;
-    $self->{_chartname_count}    = 0;
-    $self->{_worksheets}         = [];
-    $self->{_charts}             = [];
-    $self->{_drawings}           = [];
-    $self->{_sheetnames}         = {};
-    $self->{_formats}            = [];
-    $self->{_xf_formats}         = [];
-    $self->{_xf_format_indices}  = {};
-    $self->{_dxf_formats}        = [];
-    $self->{_dxf_format_indices} = {};
-    $self->{_palette}            = [];
-    $self->{_font_count}         = 0;
-    $self->{_num_format_count}   = 0;
-    $self->{_defined_names}      = [];
-    $self->{_named_ranges}       = [];
-    $self->{_custom_colors}      = [];
-    $self->{_doc_properties}     = {};
-    $self->{_custom_properties}  = [];
-    $self->{_createtime}         = [ gmtime() ];
-    $self->{_num_vml_files}      = 0;
-    $self->{_num_comment_files}  = 0;
-    $self->{_optimization}       = 0;
-    $self->{_x_window}           = 240;
-    $self->{_y_window}           = 15;
-    $self->{_window_width}       = 16095;
-    $self->{_window_height}      = 9660;
-    $self->{_tab_ratio}          = 500;
-    $self->{_excel2003_style}    = 0;
-
-    $self->{_default_format_properties} = {};
-
-    if ( exists $options->{tempdir} ) {
-        $self->{_tempdir} = $options->{tempdir};
-    }
-
-    if ( exists $options->{date_1904} ) {
-        $self->{_date_1904} = $options->{date_1904};
-    }
-
-    if ( exists $options->{optimization} ) {
-        $self->{_optimization} = $options->{optimization};
-    }
-
-    if ( exists $options->{default_format_properties} ) {
-        $self->{_default_format_properties} =
-          $options->{default_format_properties};
-    }
-
-    if ( exists $options->{excel2003_style} ) {
-        $self->{_excel2003_style} = 1;
-    }
-
-    # Structures for the shared strings data.
-    $self->{_str_total}  = 0;
-    $self->{_str_unique} = 0;
-    $self->{_str_table}  = {};
-    $self->{_str_array}  = [];
-
-    # Formula calculation default settings.
-    $self->{_calc_id}      = 124519;
-    $self->{_calc_mode}    = 'auto';
-    $self->{_calc_on_load} = 1;
-
-
-    bless $self, $class;
-
-    # Add the default cell format.
-    if ( $self->{_excel2003_style} ) {
-        $self->add_format( xf_index => 0, font_family => 0 );
-    }
-    else {
-        $self->add_format( xf_index => 0 );
-    }
-
-    # Add a default URL format.
-    $self->{_default_url_format} = $self->add_format( hyperlink => 1 );
-
-    # Check for a filename unless it is an existing filehandle
-    if ( not ref $self->{_filename} and $self->{_filename} eq '' ) {
-        carp 'Filename required by Excel::Writer::XLSX->new()';
-        return undef;
-    }
-
-
-    # If filename is a reference we assume that it is a valid filehandle.
-    if ( ref $self->{_filename} ) {
-
-        $self->{_filehandle}  = $self->{_filename};
-        $self->{_internal_fh} = 0;
-    }
-    elsif ( $self->{_filename} eq '-' ) {
-
-        # Support special filename/filehandle '-' for backward compatibility.
-        binmode STDOUT;
-        $self->{_filehandle}  = \*STDOUT;
-        $self->{_internal_fh} = 0;
-    }
-    else {
-        my $fh = IO::File->new( $self->{_filename}, 'w' );
-
-        return undef unless defined $fh;
-
-        $self->{_filehandle}  = $fh;
-        $self->{_internal_fh} = 1;
-    }
-
-
-    # Set colour palette.
-    $self->set_color_palette();
-
-    return $self;
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    # Prepare format object for passing to Style.pm.
-    $self->_prepare_format_properties();
-
-    $self->xml_declaration;
-
-    # Write the root workbook element.
-    $self->_write_workbook();
-
-    # Write the XLSX file version.
-    $self->_write_file_version();
-
-    # Write the workbook properties.
-    $self->_write_workbook_pr();
-
-    # Write the workbook view properties.
-    $self->_write_book_views();
-
-    # Write the worksheet names and ids.
-    $self->_write_sheets();
-
-    # Write the workbook defined names.
-    $self->_write_defined_names();
-
-    # Write the workbook calculation properties.
-    $self->_write_calc_pr();
-
-    # Write the workbook extension storage.
-    #$self->_write_ext_lst();
-
-    # Close the workbook tag.
-    $self->xml_end_tag( 'workbook' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# close()
-#
-# Calls finalization methods.
-#
-sub close {
-
-    my $self = shift;
-
-    # In case close() is called twice, by user and by DESTROY.
-    return if $self->{_fileclosed};
-
-    # Test filehandle in case new() failed and the user didn't check.
-    return undef if !defined $self->{_filehandle};
-
-    $self->{_fileclosed} = 1;
-    $self->_store_workbook();
-
-    # Return the file close value.
-    if ( $self->{_internal_fh} ) {
-        return $self->{_filehandle}->close();
-    }
-    else {
-        # Return true and let users deal with their own filehandles.
-        return 1;
-    }
-}
-
-
-###############################################################################
-#
-# DESTROY()
-#
-# Close the workbook if it hasn't already been explicitly closed.
-#
-sub DESTROY {
-
-    my $self = shift;
-
-    local ( $@, $!, $^E, $? );
-
-    $self->close() if not $self->{_fileclosed};
-}
-
-
-###############################################################################
-#
-# sheets(slice,...)
-#
-# An accessor for the _worksheets[] array
-#
-# Returns: an optionally sliced list of the worksheet objects in a workbook.
-#
-sub sheets {
-
-    my $self = shift;
-
-    if ( @_ ) {
-
-        # Return a slice of the array
-        return @{ $self->{_worksheets} }[@_];
-    }
-    else {
-
-        # Return the entire list
-        return @{ $self->{_worksheets} };
-    }
-}
-
-
-###############################################################################
-#
-# get_worksheet_by_name(name)
-#
-# Return a worksheet object in the workbook using the sheetname.
-#
-sub get_worksheet_by_name {
-
-    my $self      = shift;
-    my $sheetname = shift;
-
-    return undef if not defined $sheetname;
-
-    return $self->{_sheetnames}->{$sheetname};
-}
-
-
-###############################################################################
-#
-# worksheets()
-#
-# An accessor for the _worksheets[] array.
-# This method is now deprecated. Use the sheets() method instead.
-#
-# Returns: an array reference
-#
-sub worksheets {
-
-    my $self = shift;
-
-    return $self->{_worksheets};
-}
-
-
-###############################################################################
-#
-# add_worksheet($name)
-#
-# Add a new worksheet to the Excel workbook.
-#
-# Returns: reference to a worksheet object
-#
-sub add_worksheet {
-
-    my $self  = shift;
-    my $index = @{ $self->{_worksheets} };
-    my $name  = $self->_check_sheetname( $_[0] );
-    my $fh    = undef;
-
-    # Porters take note, the following scheme of passing references to Workbook
-    # data (in the \$self->{_foo} cases) instead of a reference to the Workbook
-    # itself is a workaround to avoid circular references between Workbook and
-    # Worksheet objects. Feel free to implement this in any way the suits your
-    # language.
-    #
-    my @init_data = (
-        $fh,
-        $name,
-        $index,
-
-        \$self->{_activesheet},
-        \$self->{_firstsheet},
-
-        \$self->{_str_total},
-        \$self->{_str_unique},
-        \$self->{_str_table},
-
-        $self->{_date_1904},
-        $self->{_palette},
-        $self->{_optimization},
-        $self->{_tempdir},
-        $self->{_excel2003_style},
-        $self->{_default_url_format},
-    );
-
-    my $worksheet = Excel::Writer::XLSX::Worksheet->new( @init_data );
-    $self->{_worksheets}->[$index] = $worksheet;
-    $self->{_sheetnames}->{$name} = $worksheet;
-
-    return $worksheet;
-}
-
-
-###############################################################################
-#
-# add_chart( %args )
-#
-# Create a chart for embedding or as a new sheet.
-#
-sub add_chart {
-
-    my $self  = shift;
-    my %arg   = @_;
-    my $name  = '';
-    my $index = @{ $self->{_worksheets} };
-    my $fh    = undef;
-
-    # Type must be specified so we can create the required chart instance.
-    my $type = $arg{type};
-    if ( !defined $type ) {
-        croak "Must define chart type in add_chart()";
-    }
-
-    # Ensure that the chart defaults to non embedded.
-    my $embedded = $arg{embedded} || 0;
-
-    # Check the worksheet name for non-embedded charts.
-    if ( !$embedded ) {
-        $name = $self->_check_sheetname( $arg{name}, 1 );
-    }
-
-
-    my @init_data = (
-
-        $fh,
-        $name,
-        $index,
-
-        \$self->{_activesheet},
-        \$self->{_firstsheet},
-
-        \$self->{_str_total},
-        \$self->{_str_unique},
-        \$self->{_str_table},
-
-        $self->{_date_1904},
-        $self->{_palette},
-        $self->{_optimization},
-    );
-
-
-    my $chart = Excel::Writer::XLSX::Chart->factory( $type, $arg{subtype} );
-
-    # If the chart isn't embedded let the workbook control it.
-    if ( !$embedded ) {
-
-        my $drawing    = Excel::Writer::XLSX::Drawing->new();
-        my $chartsheet = Excel::Writer::XLSX::Chartsheet->new( @init_data );
-
-        $chart->{_palette} = $self->{_palette};
-
-        $chartsheet->{_chart}   = $chart;
-        $chartsheet->{_drawing} = $drawing;
-
-        $self->{_worksheets}->[$index] = $chartsheet;
-        $self->{_sheetnames}->{$name} = $chartsheet;
-
-        push @{ $self->{_charts} }, $chart;
-
-        return $chartsheet;
-    }
-    else {
-
-        # Set the embedded chart name if present.
-        $chart->{_chart_name} = $arg{name} if $arg{name};
-
-        # Set index to 0 so that the activate() and set_first_sheet() methods
-        # point back to the first worksheet if used for embedded charts.
-        $chart->{_index}   = 0;
-        $chart->{_palette} = $self->{_palette};
-        $chart->_set_embedded_config_data();
-        push @{ $self->{_charts} }, $chart;
-
-        return $chart;
-    }
-
-}
-
-
-###############################################################################
-#
-# _check_sheetname( $name )
-#
-# Check for valid worksheet names. We check the length, if it contains any
-# invalid characters and if the name is unique in the workbook.
-#
-sub _check_sheetname {
-
-    my $self         = shift;
-    my $name         = shift || "";
-    my $chart        = shift || 0;
-    my $invalid_char = qr([\[\]:*?/\\]);
-
-    # Increment the Sheet/Chart number used for default sheet names below.
-    if ( $chart ) {
-        $self->{_chartname_count}++;
-    }
-    else {
-        $self->{_sheetname_count}++;
-    }
-
-    # Supply default Sheet/Chart name if none has been defined.
-    if ( $name eq "" ) {
-
-        if ( $chart ) {
-            $name = $self->{_chart_name} . $self->{_chartname_count};
-        }
-        else {
-            $name = $self->{_sheet_name} . $self->{_sheetname_count};
-        }
-    }
-
-    # Check that sheet name is <= 31. Excel limit.
-    croak "Sheetname $name must be <= 31 chars" if length $name > 31;
-
-    # Check that sheetname doesn't contain any invalid characters
-    if ( $name =~ $invalid_char ) {
-        croak 'Invalid character []:*?/\\ in worksheet name: ' . $name;
-    }
-
-    # Check that the worksheet name doesn't already exist since this is a fatal
-    # error in Excel 97. The check must also exclude case insensitive matches.
-    foreach my $worksheet ( @{ $self->{_worksheets} } ) {
-        my $name_a = $name;
-        my $name_b = $worksheet->{_name};
-
-        if ( lc( $name_a ) eq lc( $name_b ) ) {
-            croak "Worksheet name '$name', with case ignored, is already used.";
-        }
-    }
-
-    return $name;
-}
-
-
-###############################################################################
-#
-# add_format(%properties)
-#
-# Add a new format to the Excel workbook.
-#
-sub add_format {
-
-    my $self = shift;
-
-    my @init_data =
-      ( \$self->{_xf_format_indices}, \$self->{_dxf_format_indices} );
-
-    # Change default format style for Excel2003/XLS format.
-    if ( $self->{_excel2003_style} ) {
-        push @init_data, ( font => 'Arial', size => 10, theme => -1 );
-    }
-
-    # Add the default format properties.
-    push @init_data, %{ $self->{_default_format_properties} };
-
-    # Add the user defined properties.
-    push @init_data, @_;
-
-    my $format = Excel::Writer::XLSX::Format->new( @init_data );
-
-    push @{ $self->{_formats} }, $format;    # Store format reference
-
-    return $format;
-}
-
-
-###############################################################################
-#
-# add_shape(%properties)
-#
-# Add a new shape to the Excel workbook.
-#
-sub add_shape {
-
-    my $self  = shift;
-    my $fh    = undef;
-    my $shape = Excel::Writer::XLSX::Shape->new( $fh, @_ );
-
-    $shape->{_palette} = $self->{_palette};
-
-
-    push @{ $self->{_shapes} }, $shape;    # Store shape reference.
-
-    return $shape;
-}
-
-###############################################################################
-#
-# set_1904()
-#
-# Set the date system: 0 = 1900 (the default), 1 = 1904
-#
-sub set_1904 {
-
-    my $self = shift;
-
-    if ( defined( $_[0] ) ) {
-        $self->{_date_1904} = $_[0];
-    }
-    else {
-        $self->{_date_1904} = 1;
-    }
-}
-
-
-###############################################################################
-#
-# get_1904()
-#
-# Return the date system: 0 = 1900, 1 = 1904
-#
-sub get_1904 {
-
-    my $self = shift;
-
-    return $self->{_date_1904};
-}
-
-
-###############################################################################
-#
-# set_custom_color()
-#
-# Change the RGB components of the elements in the colour palette.
-#
-sub set_custom_color {
-
-    my $self = shift;
-
-
-    # Match a HTML #xxyyzz style parameter
-    if ( defined $_[1] and $_[1] =~ /^#(\w\w)(\w\w)(\w\w)/ ) {
-        @_ = ( $_[0], hex $1, hex $2, hex $3 );
-    }
-
-
-    my $index = $_[0] || 0;
-    my $red   = $_[1] || 0;
-    my $green = $_[2] || 0;
-    my $blue  = $_[3] || 0;
-
-    my $aref = $self->{_palette};
-
-    # Check that the colour index is the right range
-    if ( $index < 8 or $index > 64 ) {
-        carp "Color index $index outside range: 8 <= index <= 64";
-        return 0;
-    }
-
-    # Check that the colour components are in the right range
-    if (   ( $red < 0 or $red > 255 )
-        || ( $green < 0 or $green > 255 )
-        || ( $blue < 0  or $blue > 255 ) )
-    {
-        carp "Color component outside range: 0 <= color <= 255";
-        return 0;
-    }
-
-    $index -= 8;    # Adjust colour index (wingless dragonfly)
-
-    # Set the RGB value.
-    my @rgb = ( $red, $green, $blue );
-    $aref->[$index] = [@rgb];
-
-    # Store the custom colors for the style.xml file.
-    push @{ $self->{_custom_colors} }, sprintf "FF%02X%02X%02X", @rgb;
-
-    return $index + 8;
-}
-
-
-###############################################################################
-#
-# set_color_palette()
-#
-# Sets the colour palette to the Excel defaults.
-#
-sub set_color_palette {
-
-    my $self = shift;
-
-    $self->{_palette} = [
-        [ 0x00, 0x00, 0x00, 0x00 ],    # 8
-        [ 0xff, 0xff, 0xff, 0x00 ],    # 9
-        [ 0xff, 0x00, 0x00, 0x00 ],    # 10
-        [ 0x00, 0xff, 0x00, 0x00 ],    # 11
-        [ 0x00, 0x00, 0xff, 0x00 ],    # 12
-        [ 0xff, 0xff, 0x00, 0x00 ],    # 13
-        [ 0xff, 0x00, 0xff, 0x00 ],    # 14
-        [ 0x00, 0xff, 0xff, 0x00 ],    # 15
-        [ 0x80, 0x00, 0x00, 0x00 ],    # 16
-        [ 0x00, 0x80, 0x00, 0x00 ],    # 17
-        [ 0x00, 0x00, 0x80, 0x00 ],    # 18
-        [ 0x80, 0x80, 0x00, 0x00 ],    # 19
-        [ 0x80, 0x00, 0x80, 0x00 ],    # 20
-        [ 0x00, 0x80, 0x80, 0x00 ],    # 21
-        [ 0xc0, 0xc0, 0xc0, 0x00 ],    # 22
-        [ 0x80, 0x80, 0x80, 0x00 ],    # 23
-        [ 0x99, 0x99, 0xff, 0x00 ],    # 24
-        [ 0x99, 0x33, 0x66, 0x00 ],    # 25
-        [ 0xff, 0xff, 0xcc, 0x00 ],    # 26
-        [ 0xcc, 0xff, 0xff, 0x00 ],    # 27
-        [ 0x66, 0x00, 0x66, 0x00 ],    # 28
-        [ 0xff, 0x80, 0x80, 0x00 ],    # 29
-        [ 0x00, 0x66, 0xcc, 0x00 ],    # 30
-        [ 0xcc, 0xcc, 0xff, 0x00 ],    # 31
-        [ 0x00, 0x00, 0x80, 0x00 ],    # 32
-        [ 0xff, 0x00, 0xff, 0x00 ],    # 33
-        [ 0xff, 0xff, 0x00, 0x00 ],    # 34
-        [ 0x00, 0xff, 0xff, 0x00 ],    # 35
-        [ 0x80, 0x00, 0x80, 0x00 ],    # 36
-        [ 0x80, 0x00, 0x00, 0x00 ],    # 37
-        [ 0x00, 0x80, 0x80, 0x00 ],    # 38
-        [ 0x00, 0x00, 0xff, 0x00 ],    # 39
-        [ 0x00, 0xcc, 0xff, 0x00 ],    # 40
-        [ 0xcc, 0xff, 0xff, 0x00 ],    # 41
-        [ 0xcc, 0xff, 0xcc, 0x00 ],    # 42
-        [ 0xff, 0xff, 0x99, 0x00 ],    # 43
-        [ 0x99, 0xcc, 0xff, 0x00 ],    # 44
-        [ 0xff, 0x99, 0xcc, 0x00 ],    # 45
-        [ 0xcc, 0x99, 0xff, 0x00 ],    # 46
-        [ 0xff, 0xcc, 0x99, 0x00 ],    # 47
-        [ 0x33, 0x66, 0xff, 0x00 ],    # 48
-        [ 0x33, 0xcc, 0xcc, 0x00 ],    # 49
-        [ 0x99, 0xcc, 0x00, 0x00 ],    # 50
-        [ 0xff, 0xcc, 0x00, 0x00 ],    # 51
-        [ 0xff, 0x99, 0x00, 0x00 ],    # 52
-        [ 0xff, 0x66, 0x00, 0x00 ],    # 53
-        [ 0x66, 0x66, 0x99, 0x00 ],    # 54
-        [ 0x96, 0x96, 0x96, 0x00 ],    # 55
-        [ 0x00, 0x33, 0x66, 0x00 ],    # 56
-        [ 0x33, 0x99, 0x66, 0x00 ],    # 57
-        [ 0x00, 0x33, 0x00, 0x00 ],    # 58
-        [ 0x33, 0x33, 0x00, 0x00 ],    # 59
-        [ 0x99, 0x33, 0x00, 0x00 ],    # 60
-        [ 0x99, 0x33, 0x66, 0x00 ],    # 61
-        [ 0x33, 0x33, 0x99, 0x00 ],    # 62
-        [ 0x33, 0x33, 0x33, 0x00 ],    # 63
-    ];
-
-    return 0;
-}
-
-
-###############################################################################
-#
-# set_tempdir()
-#
-# Change the default temp directory.
-#
-sub set_tempdir {
-
-    my $self = shift;
-    my $dir  = shift;
-
-    croak "$dir is not a valid directory" if defined $dir and not -d $dir;
-
-    $self->{_tempdir} = $dir;
-
-}
-
-
-###############################################################################
-#
-# define_name()
-#
-# Create a defined name in Excel. We handle global/workbook level names and
-# local/worksheet names.
-#
-sub define_name {
-
-    my $self        = shift;
-    my $name        = shift;
-    my $formula     = shift;
-    my $sheet_index = undef;
-    my $sheetname   = '';
-    my $full_name   = $name;
-
-    # Remove the = sign from the formula if it exists.
-    $formula =~ s/^=//;
-
-    # Local defined names are formatted like "Sheet1!name".
-    if ( $name =~ /^(.*)!(.*)$/ ) {
-        $sheetname   = $1;
-        $name        = $2;
-        $sheet_index = $self->_get_sheet_index( $sheetname );
-    }
-    else {
-        $sheet_index = -1;    # Use -1 to indicate global names.
-    }
-
-    # Warn if the sheet index wasn't found.
-    if ( !defined $sheet_index ) {
-        carp "Unknown sheet name $sheetname in defined_name()";
-        return -1;
-    }
-
-    # Warn if the name contains invalid chars as defined by Excel help.
-    if ( $name !~ m/^[\w\\][\w\\.]*$/ || $name =~ m/^\d/ ) {
-        carp "Invalid character in name '$name' used in defined_name()";
-        return -1;
-    }
-
-    # Warn if the name looks like a cell name.
-    if ( $name =~ m/^[a-zA-Z][a-zA-Z]?[a-dA-D]?[0-9]+$/ ) {
-        carp "Invalid name '$name' looks like a cell name in defined_name()";
-        return -1;
-    }
-
-    # Warn if the name looks like a R1C1.
-    if ( $name =~ m/^[rcRC]$/ || $name =~ m/^[rcRC]\d+[rcRC]\d+$/ ) {
-        carp "Invalid name '$name' like a RC cell ref in defined_name()";
-        return -1;
-    }
-
-    push @{ $self->{_defined_names} }, [ $name, $sheet_index, $formula ];
-}
-
-
-###############################################################################
-#
-# set_size()
-#
-# Set the workbook size.
-#
-sub set_size {
-
-    my $self   = shift;
-    my $width  = shift;
-    my $height = shift;
-
-    if ( !$width ) {
-        $self->{_window_width} = 16095;
-    }
-    else {
-        # Convert to twips at 96 dpi.
-        $self->{_window_width} = int( $width * 1440 / 96 );
-    }
-
-    if ( !$height ) {
-        $self->{_window_height} = 9660;
-    }
-    else {
-        # Convert to twips at 96 dpi.
-        $self->{_window_height} = int( $height * 1440 / 96 );
-    }
-}
-
-
-###############################################################################
-#
-# set_properties()
-#
-# Set the document properties such as Title, Author etc. These are written to
-# property sets in the OLE container.
-#
-sub set_properties {
-
-    my $self  = shift;
-    my %param = @_;
-
-    # Ignore if no args were passed.
-    return -1 unless @_;
-
-    # List of valid input parameters.
-    my %valid = (
-        title          => 1,
-        subject        => 1,
-        author         => 1,
-        keywords       => 1,
-        comments       => 1,
-        last_author    => 1,
-        created        => 1,
-        category       => 1,
-        manager        => 1,
-        company        => 1,
-        status         => 1,
-        hyperlink_base => 1,
-    );
-
-    # Check for valid input parameters.
-    for my $parameter ( keys %param ) {
-        if ( not exists $valid{$parameter} ) {
-            carp "Unknown parameter '$parameter' in set_properties()";
-            return -1;
-        }
-    }
-
-    # Set the creation time unless specified by the user.
-    if ( !exists $param{created} ) {
-        $param{created} = $self->{_createtime};
-    }
-
-
-    $self->{_doc_properties} = \%param;
-}
-
-
-###############################################################################
-#
-# set_custom_property()
-#
-# Set a user defined custom document property.
-#
-sub set_custom_property {
-
-    my $self  = shift;
-    my $name  = shift;
-    my $value = shift;
-    my $type  = shift;
-
-
-    # Valid types.
-    my %valid_type = (
-        'text'       => 1,
-        'date'       => 1,
-        'number'     => 1,
-        'number_int' => 1,
-        'bool'       => 1,
-    );
-
-    if ( !defined $name || !defined $value ) {
-        carp "The name and value parameters must be defined "
-          . "in set_custom_property()";
-
-        return -1;
-    }
-
-    # Determine the type for strings and numbers if it hasn't been specified.
-    if ( !$type ) {
-        if ( $value =~ /^\d+$/ ) {
-            $type = 'number_int';
-        }
-        elsif ( $value =~
-            /^([+-]?)(?=[0-9]|\.[0-9])[0-9]*(\.[0-9]*)?([Ee]([+-]?[0-9]+))?$/ )
-        {
-            $type = 'number';
-        }
-        else {
-            $type = 'text';
-        }
-    }
-
-    # Check for valid validation types.
-    if ( !exists $valid_type{$type} ) {
-        carp "Unknown custom type '$type' in set_custom_property()";
-        return -1;
-    }
-
-    #  Check for strings longer than Excel's limit of 255 chars.
-    if ( $type eq 'text' and length $value > 255 ) {
-        carp "Length of text custom value '$value' exceeds "
-          . "Excel's limit of 255 in set_custom_property()";
-        return -1;
-    }
-    if ( length $value > 255 ) {
-        carp "Length of custom name '$name' exceeds "
-          . "Excel's limit of 255 in set_custom_property()";
-        return -1;
-    }
-
-    push @{ $self->{_custom_properties} }, [ $name, $value, $type ];
-}
-
-
-
-###############################################################################
-#
-# add_vba_project()
-#
-# Add a vbaProject binary to the XLSX file.
-#
-sub add_vba_project {
-
-    my $self        = shift;
-    my $vba_project = shift;
-
-    croak "No vbaProject.bin specified in add_vba_project()"
-      if not $vba_project;
-
-    croak "Couldn't locate $vba_project in add_vba_project(): $!"
-      unless -e $vba_project;
-
-    $self->{_vba_project} = $vba_project;
-}
-
-
-###############################################################################
-#
-# set_vba_name()
-#
-# Set the VBA name for the workbook.
-#
-sub set_vba_name {
-
-    my $self         = shift;
-    my $vba_codemame = shift;
-
-    if ( $vba_codemame ) {
-        $self->{_vba_codename} = $vba_codemame;
-    }
-    else {
-        $self->{_vba_codename} = 'ThisWorkbook';
-    }
-}
-
-
-###############################################################################
-#
-# set_calc_mode()
-#
-# Set the Excel caclcuation mode for the workbook.
-#
-sub set_calc_mode {
-
-    my $self    = shift;
-    my $mode    = shift || 'auto';
-    my $calc_id = shift;
-
-    $self->{_calc_mode} = $mode;
-
-    if ( $mode eq 'manual' ) {
-        $self->{_calc_mode}    = 'manual';
-        $self->{_calc_on_load} = 0;
-    }
-    elsif ( $mode eq 'auto_except_tables' ) {
-        $self->{_calc_mode} = 'autoNoTable';
-    }
-
-    $self->{_calc_id} = $calc_id if defined $calc_id;
-}
-
-
-###############################################################################
-#
-# get_default_url_format()
-#
-# Get the default url format used when a user defined format isn't specified
-# with write_url(). The format is the hyperlink style defined by Excel for the
-# default theme.
-#
-sub get_default_url_format {
-
-    my $self    = shift;
-
-    return $self->{_default_url_format};
-}
-
-
-###############################################################################
-#
-# _store_workbook()
-#
-# Assemble worksheets into a workbook.
-#
-sub _store_workbook {
-
-    my $self     = shift;
-    my $tempdir  = File::Temp->newdir( DIR => $self->{_tempdir} );
-    my $packager = Excel::Writer::XLSX::Package::Packager->new();
-    my $zip      = Archive::Zip->new();
-
-
-    # Add a default worksheet if non have been added.
-    $self->add_worksheet() if not @{ $self->{_worksheets} };
-
-    # Ensure that at least one worksheet has been selected.
-    if ( $self->{_activesheet} == 0 ) {
-        $self->{_worksheets}->[0]->{_selected} = 1;
-        $self->{_worksheets}->[0]->{_hidden}   = 0;
-    }
-
-    # Set the active sheet.
-    for my $sheet ( @{ $self->{_worksheets} } ) {
-        $sheet->{_active} = 1 if $sheet->{_index} == $self->{_activesheet};
-    }
-
-    # Convert the SST strings data structure.
-    $self->_prepare_sst_string_data();
-
-    # Prepare the worksheet VML elements such as comments and buttons.
-    $self->_prepare_vml_objects();
-
-    # Set the defined names for the worksheets such as Print Titles.
-    $self->_prepare_defined_names();
-
-    # Prepare the drawings, charts and images.
-    $self->_prepare_drawings();
-
-    # Add cached data to charts.
-    $self->_add_chart_data();
-
-    # Prepare the worksheet tables.
-    $self->_prepare_tables();
-
-    # Package the workbook.
-    $packager->_add_workbook( $self );
-    $packager->_set_package_dir( $tempdir );
-    $packager->_create_package();
-
-    # Free up the Packager object.
-    $packager = undef;
-
-    # Add the files to the zip archive. Due to issues with Archive::Zip in
-    # taint mode we can't use addTree() so we have to build the file list
-    # with File::Find and pass each one to addFile().
-    my @xlsx_files;
-
-    my $wanted = sub { push @xlsx_files, $File::Find::name if -f };
-
-    File::Find::find(
-        {
-            wanted          => $wanted,
-            untaint         => 1,
-            untaint_pattern => qr|^(.+)$|
-        },
-        $tempdir
-    );
-
-    # Store the xlsx component files with the temp dir name removed.
-    for my $filename ( @xlsx_files ) {
-        my $short_name = $filename;
-        $short_name =~ s{^\Q$tempdir\E/?}{};
-        my $member = $zip->addFile( $filename, $short_name );
-
-        # Set the file member datetime to 1980-01-01 00:00:00 like Excel so
-        # that apps can produce a consistent binary file. Note, we don't use
-        # the Archive::Zip::setLastModFileDateTimeFromUnix() function directly
-        # since it doesn't allow the time 00:00:00 for this date.
-        $member->{'lastModFileDateTime'} = 2162688;
-    }
-
-    if ( $self->{_internal_fh} ) {
-
-        if ( $zip->writeToFileHandle( $self->{_filehandle} ) != 0 ) {
-            carp 'Error writing zip container for xlsx file.';
-        }
-    }
-    else {
-
-        # Archive::Zip needs to rewind a filehandle to write the zip headers.
-        # This won't work for arbitrary user defined filehandles so we use
-        # a temp file based filehandle to create the zip archive and then
-        # stream that to the filehandle.
-        my $tmp_fh = tempfile( DIR => $self->{_tempdir} );
-        my $is_seekable = 1;
-
-        if ( $zip->writeToFileHandle( $tmp_fh, $is_seekable ) != 0 ) {
-            carp 'Error writing zip container for xlsx file.';
-        }
-
-        my $buffer;
-        seek $tmp_fh, 0, 0;
-
-        while ( read( $tmp_fh, $buffer, 4_096 ) ) {
-            local $\ = undef;    # Protect print from -l on commandline.
-            print { $self->{_filehandle} } $buffer;
-        }
-    }
-}
-
-
-###############################################################################
-#
-# _prepare_sst_string_data()
-#
-# Convert the SST string data from a hash to an array.
-#
-sub _prepare_sst_string_data {
-
-    my $self = shift;
-
-    my @strings;
-    $#strings = $self->{_str_unique} - 1;    # Pre-extend array
-
-    while ( my $key = each %{ $self->{_str_table} } ) {
-        $strings[ $self->{_str_table}->{$key} ] = $key;
-    }
-
-    # The SST data could be very large, free some memory (maybe).
-    $self->{_str_table} = undef;
-    $self->{_str_array} = \@strings;
-
-}
-
-
-###############################################################################
-#
-# _prepare_format_properties()
-#
-# Prepare all of the format properties prior to passing them to Styles.pm.
-#
-sub _prepare_format_properties {
-
-    my $self = shift;
-
-    # Separate format objects into XF and DXF formats.
-    $self->_prepare_formats();
-
-    # Set the font index for the format objects.
-    $self->_prepare_fonts();
-
-    # Set the number format index for the format objects.
-    $self->_prepare_num_formats();
-
-    # Set the border index for the format objects.
-    $self->_prepare_borders();
-
-    # Set the fill index for the format objects.
-    $self->_prepare_fills();
-
-
-}
-
-
-###############################################################################
-#
-# _prepare_formats()
-#
-# Iterate through the XF Format objects and separate them into XF and DXF
-# formats.
-#
-sub _prepare_formats {
-
-    my $self = shift;
-
-    for my $format ( @{ $self->{_formats} } ) {
-        my $xf_index  = $format->{_xf_index};
-        my $dxf_index = $format->{_dxf_index};
-
-        if ( defined $xf_index ) {
-            $self->{_xf_formats}->[$xf_index] = $format;
-        }
-
-        if ( defined $dxf_index ) {
-            $self->{_dxf_formats}->[$dxf_index] = $format;
-        }
-    }
-}
-
-
-###############################################################################
-#
-# _set_default_xf_indices()
-#
-# Set the default index for each format. This is mainly used for testing.
-#
-sub _set_default_xf_indices {
-
-    my $self = shift;
-
-    # Delete the default url format.
-    splice @{ $self->{_formats} }, 1, 1;
-
-    for my $format ( @{ $self->{_formats} } ) {
-        $format->get_xf_index();
-    }
-}
-
-
-###############################################################################
-#
-# _prepare_fonts()
-#
-# Iterate through the XF Format objects and give them an index to non-default
-# font elements.
-#
-sub _prepare_fonts {
-
-    my $self = shift;
-
-    my %fonts;
-    my $index = 0;
-
-    for my $format ( @{ $self->{_xf_formats} } ) {
-        my $key = $format->get_font_key();
-
-        if ( exists $fonts{$key} ) {
-
-            # Font has already been used.
-            $format->{_font_index} = $fonts{$key};
-            $format->{_has_font}   = 0;
-        }
-        else {
-
-            # This is a new font.
-            $fonts{$key}           = $index;
-            $format->{_font_index} = $index;
-            $format->{_has_font}   = 1;
-            $index++;
-        }
-    }
-
-    $self->{_font_count} = $index;
-
-    # For the DXF formats we only need to check if the properties have changed.
-    for my $format ( @{ $self->{_dxf_formats} } ) {
-
-        # The only font properties that can change for a DXF format are: color,
-        # bold, italic, underline and strikethrough.
-        if (   $format->{_color}
-            || $format->{_bold}
-            || $format->{_italic}
-            || $format->{_underline}
-            || $format->{_font_strikeout} )
-        {
-            $format->{_has_dxf_font} = 1;
-        }
-    }
-}
-
-
-###############################################################################
-#
-# _prepare_num_formats()
-#
-# Iterate through the XF Format objects and give them an index to non-default
-# number format elements.
-#
-# User defined records start from index 0xA4.
-#
-sub _prepare_num_formats {
-
-    my $self = shift;
-
-    my %num_formats;
-    my $index            = 164;
-    my $num_format_count = 0;
-
-    for my $format ( @{ $self->{_xf_formats} }, @{ $self->{_dxf_formats} } ) {
-        my $num_format = $format->{_num_format};
-
-        # Check if $num_format is an index to a built-in number format.
-        # Also check for a string of zeros, which is a valid number format
-        # string but would evaluate to zero.
-        #
-        if ( $num_format =~ m/^\d+$/ && $num_format !~ m/^0+\d/ ) {
-
-            # Index to a built-in number format.
-            $format->{_num_format_index} = $num_format;
-            next;
-        }
-
-
-        if ( exists( $num_formats{$num_format} ) ) {
-
-            # Number format has already been used.
-            $format->{_num_format_index} = $num_formats{$num_format};
-        }
-        else {
-
-            # Add a new number format.
-            $num_formats{$num_format} = $index;
-            $format->{_num_format_index} = $index;
-            $index++;
-
-            # Only increase font count for XF formats (not for DXF formats).
-            if ( $format->{_xf_index} ) {
-                $num_format_count++;
-            }
-        }
-    }
-
-    $self->{_num_format_count} = $num_format_count;
-}
-
-
-###############################################################################
-#
-# _prepare_borders()
-#
-# Iterate through the XF Format objects and give them an index to non-default
-# border elements.
-#
-sub _prepare_borders {
-
-    my $self = shift;
-
-    my %borders;
-    my $index = 0;
-
-    for my $format ( @{ $self->{_xf_formats} } ) {
-        my $key = $format->get_border_key();
-
-        if ( exists $borders{$key} ) {
-
-            # Border has already been used.
-            $format->{_border_index} = $borders{$key};
-            $format->{_has_border}   = 0;
-        }
-        else {
-
-            # This is a new border.
-            $borders{$key}           = $index;
-            $format->{_border_index} = $index;
-            $format->{_has_border}   = 1;
-            $index++;
-        }
-    }
-
-    $self->{_border_count} = $index;
-
-    # For the DXF formats we only need to check if the properties have changed.
-    for my $format ( @{ $self->{_dxf_formats} } ) {
-        my $key = $format->get_border_key();
-
-        if ( $key =~ m/[^0:]/ ) {
-            $format->{_has_dxf_border} = 1;
-        }
-    }
-
-}
-
-
-###############################################################################
-#
-# _prepare_fills()
-#
-# Iterate through the XF Format objects and give them an index to non-default
-# fill elements.
-#
-# The user defined fill properties start from 2 since there are 2 default
-# fills: patternType="none" and patternType="gray125".
-#
-sub _prepare_fills {
-
-    my $self = shift;
-
-    my %fills;
-    my $index = 2;    # Start from 2. See above.
-
-    # Add the default fills.
-    $fills{'0:0:0'}  = 0;
-    $fills{'17:0:0'} = 1;
-
-
-    # Store the DXF colours separately since them may be reversed below.
-    for my $format ( @{ $self->{_dxf_formats} } ) {
-        if (   $format->{_pattern}
-            || $format->{_bg_color}
-            || $format->{_fg_color} )
-        {
-            $format->{_has_dxf_fill} = 1;
-            $format->{_dxf_bg_color} = $format->{_bg_color};
-            $format->{_dxf_fg_color} = $format->{_fg_color};
-        }
-    }
-
-
-    for my $format ( @{ $self->{_xf_formats} } ) {
-
-        # The following logical statements jointly 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, and
-        # 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 (   $format->{_pattern} == 1
-            && $format->{_bg_color} ne '0'
-            && $format->{_fg_color} ne '0' )
-        {
-            my $tmp = $format->{_fg_color};
-            $format->{_fg_color} = $format->{_bg_color};
-            $format->{_bg_color} = $tmp;
-        }
-
-        if (   $format->{_pattern} <= 1
-            && $format->{_bg_color} ne '0'
-            && $format->{_fg_color} eq '0' )
-        {
-            $format->{_fg_color} = $format->{_bg_color};
-            $format->{_bg_color} = 0;
-            $format->{_pattern}  = 1;
-        }
-
-        if (   $format->{_pattern} <= 1
-            && $format->{_bg_color} eq '0'
-            && $format->{_fg_color} ne '0' )
-        {
-            $format->{_bg_color} = 0;
-            $format->{_pattern}  = 1;
-        }
-
-
-        my $key = $format->get_fill_key();
-
-        if ( exists $fills{$key} ) {
-
-            # Fill has already been used.
-            $format->{_fill_index} = $fills{$key};
-            $format->{_has_fill}   = 0;
-        }
-        else {
-
-            # This is a new fill.
-            $fills{$key}           = $index;
-            $format->{_fill_index} = $index;
-            $format->{_has_fill}   = 1;
-            $index++;
-        }
-    }
-
-    $self->{_fill_count} = $index;
-
-
-}
-
-
-###############################################################################
-#
-# _prepare_defined_names()
-#
-# Iterate through the worksheets and store any defined names in addition to
-# any user defined names. Stores the defined names for the Workbook.xml and
-# the named ranges for App.xml.
-#
-sub _prepare_defined_names {
-
-    my $self = shift;
-
-    my @defined_names = @{ $self->{_defined_names} };
-
-    for my $sheet ( @{ $self->{_worksheets} } ) {
-
-        # Check for Print Area settings.
-        if ( $sheet->{_autofilter} ) {
-
-            my $range  = $sheet->{_autofilter};
-            my $hidden = 1;
-
-            # Store the defined names.
-            push @defined_names,
-              [ '_xlnm._FilterDatabase', $sheet->{_index}, $range, $hidden ];
-
-        }
-
-        # Check for Print Area settings.
-        if ( $sheet->{_print_area} ) {
-
-            my $range = $sheet->{_print_area};
-
-            # Store the defined names.
-            push @defined_names,
-              [ '_xlnm.Print_Area', $sheet->{_index}, $range ];
-        }
-
-        # Check for repeat rows/cols. aka, Print Titles.
-        if ( $sheet->{_repeat_cols} || $sheet->{_repeat_rows} ) {
-            my $range = '';
-
-            if ( $sheet->{_repeat_cols} && $sheet->{_repeat_rows} ) {
-                $range = $sheet->{_repeat_cols} . ',' . $sheet->{_repeat_rows};
-            }
-            else {
-                $range = $sheet->{_repeat_cols} . $sheet->{_repeat_rows};
-            }
-
-            # Store the defined names.
-            push @defined_names,
-              [ '_xlnm.Print_Titles', $sheet->{_index}, $range ];
-        }
-
-    }
-
-    @defined_names          = _sort_defined_names( @defined_names );
-    $self->{_defined_names} = \@defined_names;
-    $self->{_named_ranges}  = _extract_named_ranges( @defined_names );
-}
-
-
-###############################################################################
-#
-# _sort_defined_names()
-#
-# Sort internal and user defined names in the same order as used by Excel.
-# This may not be strictly necessary but unsorted elements caused a lot of
-# issues in the Spreadsheet::WriteExcel binary version. Also makes
-# comparison testing easier.
-#
-sub _sort_defined_names {
-
-    my @names = @_;
-
-    #<<< Perltidy ignore this.
-
-    @names = sort {
-        # Primary sort based on the defined name.
-        _normalise_defined_name( $a->[0] )
-        cmp
-        _normalise_defined_name( $b->[0] )
-
-        ||
-        # Secondary sort based on the sheet name.
-        _normalise_sheet_name( $a->[2] )
-        cmp
-        _normalise_sheet_name( $b->[2] )
-
-    } @names;
-    #>>>
-
-    return @names;
-}
-
-# Used in the above sort routine to normalise the defined names. Removes any
-# leading '_xmln.' from internal names and lowercases the strings.
-sub _normalise_defined_name {
-    my $name = shift;
-
-    $name =~ s/^_xlnm.//;
-    $name = lc $name;
-
-    return $name;
-}
-
-# Used in the above sort routine to normalise the worksheet names for the
-# secondary sort. Removes leading quote and lowercases the strings.
-sub _normalise_sheet_name {
-    my $name = shift;
-
-    $name =~ s/^'//;
-    $name = lc $name;
-
-    return $name;
-}
-
-
-###############################################################################
-#
-# _extract_named_ranges()
-#
-# Extract the named ranges from the sorted list of defined names. These are
-# used in the App.xml file.
-#
-sub _extract_named_ranges {
-
-    my @defined_names = @_;
-    my @named_ranges;
-
-    NAME:
-    for my $defined_name ( @defined_names ) {
-
-        my $name  = $defined_name->[0];
-        my $index = $defined_name->[1];
-        my $range = $defined_name->[2];
-
-        # Skip autoFilter ranges.
-        next NAME if $name eq '_xlnm._FilterDatabase';
-
-        # We are only interested in defined names with ranges.
-        if ( $range =~ /^([^!]+)!/ ) {
-            my $sheet_name = $1;
-
-            # Match Print_Area and Print_Titles xlnm types.
-            if ( $name =~ /^_xlnm\.(.*)$/ ) {
-                my $xlnm_type = $1;
-                $name = $sheet_name . '!' . $xlnm_type;
-            }
-            elsif ( $index != -1 ) {
-                $name = $sheet_name . '!' . $name;
-            }
-
-            push @named_ranges, $name;
-        }
-    }
-
-    return \@named_ranges;
-}
-
-
-###############################################################################
-#
-# _prepare_drawings()
-#
-# Iterate through the worksheets and set up any chart or image drawings.
-#
-sub _prepare_drawings {
-
-    my $self         = shift;
-    my $chart_ref_id = 0;
-    my $image_ref_id = 0;
-    my $drawing_id   = 0;
-
-    for my $sheet ( @{ $self->{_worksheets} } ) {
-
-        my $chart_count = scalar @{ $sheet->{_charts} };
-        my $image_count = scalar @{ $sheet->{_images} };
-        my $shape_count = scalar @{ $sheet->{_shapes} };
-
-        my $header_image_count = scalar @{ $sheet->{_header_images} };
-        my $footer_image_count = scalar @{ $sheet->{_footer_images} };
-        my $has_drawing        = 0;
-
-
-        # Check that some image or drawing needs to be processed.
-        if (   !$chart_count
-            && !$image_count
-            && !$shape_count
-            && !$header_image_count
-            && !$footer_image_count )
-        {
-            next;
-        }
-
-        # Don't increase the drawing_id header/footer images.
-        if ( $chart_count || $image_count || $shape_count ) {
-            $drawing_id++;
-            $has_drawing = 1;
-        }
-
-        # Prepare the worksheet charts.
-        for my $index ( 0 .. $chart_count - 1 ) {
-            $chart_ref_id++;
-            $sheet->_prepare_chart( $index, $chart_ref_id, $drawing_id );
-        }
-
-        # Prepare the worksheet images.
-        for my $index ( 0 .. $image_count - 1 ) {
-
-            my $filename = $sheet->{_images}->[$index]->[2];
-
-            my ( $type, $width, $height, $name, $x_dpi, $y_dpi ) =
-              $self->_get_image_properties( $filename );
-
-            $image_ref_id++;
-
-            $sheet->_prepare_image(
-                $index, $image_ref_id, $drawing_id,
-                $width, $height,       $name,
-                $type,  $x_dpi,        $y_dpi
-            );
-        }
-
-        # Prepare the worksheet shapes.
-        for my $index ( 0 .. $shape_count - 1 ) {
-            $sheet->_prepare_shape( $index, $drawing_id );
-        }
-
-        # Prepare the header images.
-        for my $index ( 0 .. $header_image_count - 1 ) {
-
-            my $filename = $sheet->{_header_images}->[$index]->[0];
-            my $position = $sheet->{_header_images}->[$index]->[1];
-
-            my ( $type, $width, $height, $name, $x_dpi, $y_dpi ) =
-              $self->_get_image_properties( $filename );
-
-            $image_ref_id++;
-
-            $sheet->_prepare_header_image( $image_ref_id, $width, $height,
-                $name, $type, $position, $x_dpi, $y_dpi );
-        }
-
-        # Prepare the footer images.
-        for my $index ( 0 .. $footer_image_count - 1 ) {
-
-            my $filename = $sheet->{_footer_images}->[$index]->[0];
-            my $position = $sheet->{_footer_images}->[$index]->[1];
-
-            my ( $type, $width, $height, $name, $x_dpi, $y_dpi ) =
-              $self->_get_image_properties( $filename );
-
-            $image_ref_id++;
-
-            $sheet->_prepare_header_image( $image_ref_id, $width, $height,
-                $name, $type, $position, $x_dpi, $y_dpi );
-        }
-
-
-        if ( $has_drawing ) {
-            my $drawing = $sheet->{_drawing};
-            push @{ $self->{_drawings} }, $drawing;
-        }
-    }
-
-
-    # Remove charts that were created but not inserted into worksheets.
-    my @chart_data;
-
-    for my $chart ( @{ $self->{_charts} } ) {
-        if ( $chart->{_id} != -1 ) {
-            push @chart_data, $chart;
-        }
-    }
-
-    # Sort the workbook charts references into the order that the were
-    # written from the worksheets above.
-    @chart_data = sort { $a->{_id} <=> $b->{_id} } @chart_data;
-
-    $self->{_charts} = \@chart_data;
-    $self->{_drawing_count} = $drawing_id;
-}
-
-
-###############################################################################
-#
-# _prepare_vml_objects()
-#
-# Iterate through the worksheets and set up the VML objects.
-#
-sub _prepare_vml_objects {
-
-    my $self           = shift;
-    my $comment_id     = 0;
-    my $vml_drawing_id = 0;
-    my $vml_data_id    = 1;
-    my $vml_header_id  = 0;
-    my $vml_shape_id   = 1024;
-    my $vml_files      = 0;
-    my $comment_files  = 0;
-    my $has_button     = 0;
-
-    for my $sheet ( @{ $self->{_worksheets} } ) {
-
-        next if !$sheet->{_has_vml} and !$sheet->{_has_header_vml};
-        $vml_files = 1;
-
-
-        if ( $sheet->{_has_vml} ) {
-
-            $comment_files++ if $sheet->{_has_comments};
-            $comment_id++    if $sheet->{_has_comments};
-            $vml_drawing_id++;
-
-            my $count =
-              $sheet->_prepare_vml_objects( $vml_data_id, $vml_shape_id,
-                $vml_drawing_id, $comment_id );
-
-            # Each VML file should start with a shape id incremented by 1024.
-            $vml_data_id  += 1 * int(    ( 1024 + $count ) / 1024 );
-            $vml_shape_id += 1024 * int( ( 1024 + $count ) / 1024 );
-
-        }
-
-        if ( $sheet->{_has_header_vml} ) {
-            $vml_header_id++;
-            $vml_drawing_id++;
-            $sheet->_prepare_header_vml_objects( $vml_header_id,
-                $vml_drawing_id );
-        }
-
-        # Set the sheet vba_codename if it has a button and the workbook
-        # has a vbaProject binary.
-        if ( $sheet->{_buttons_array} ) {
-            $has_button = 1;
-
-            if ( $self->{_vba_project} && !$sheet->{_vba_codename} ) {
-                $sheet->set_vba_name();
-            }
-        }
-
-    }
-
-    $self->{_num_vml_files}     = $vml_files;
-    $self->{_num_comment_files} = $comment_files;
-
-    # Add a font format for cell comments.
-    if ( $comment_files > 0 ) {
-        my $format = Excel::Writer::XLSX::Format->new(
-            \$self->{_xf_format_indices},
-            \$self->{_dxf_format_indices},
-            font          => 'Tahoma',
-            size          => 8,
-            color_indexed => 81,
-            font_only     => 1,
-        );
-
-        $format->get_xf_index();
-
-        push @{ $self->{_formats} }, $format;
-    }
-
-    # Set the workbook vba_codename if one of the sheets has a button and
-    # the workbook has a vbaProject binary.
-    if ( $has_button && $self->{_vba_project} && !$self->{_vba_codename} ) {
-        $self->set_vba_name();
-    }
-}
-
-
-###############################################################################
-#
-# _prepare_tables()
-#
-# Set the table ids for the worksheet tables.
-#
-sub _prepare_tables {
-
-    my $self     = shift;
-    my $table_id = 0;
-    my $seen     = {};
-
-    for my $sheet ( @{ $self->{_worksheets} } ) {
-
-        my $table_count = scalar @{ $sheet->{_tables} };
-
-        next unless $table_count;
-
-        $sheet->_prepare_tables( $table_id + 1, $seen );
-
-        $table_id += $table_count;
-    }
-}
-
-
-###############################################################################
-#
-# _add_chart_data()
-#
-# Add "cached" data to charts to provide the numCache and strCache data for
-# series and title/axis ranges.
-#
-sub _add_chart_data {
-
-    my $self = shift;
-    my %worksheets;
-    my %seen_ranges;
-    my @charts;
-
-    # Map worksheet names to worksheet objects.
-    for my $worksheet ( @{ $self->{_worksheets} } ) {
-        $worksheets{ $worksheet->{_name} } = $worksheet;
-    }
-
-    # Build an array of the worksheet charts including any combined charts.
-    for my $chart ( @{ $self->{_charts} } ) {
-        push @charts, $chart;
-
-        if ($chart->{_combined}) {
-            push @charts, $chart->{_combined};
-        }
-    }
-
-
-    CHART:
-    for my $chart ( @charts ) {
-
-        RANGE:
-        while ( my ( $range, $id ) = each %{ $chart->{_formula_ids} } ) {
-
-            # Skip if the series has user defined data.
-            if ( defined $chart->{_formula_data}->[$id] ) {
-                if (   !exists $seen_ranges{$range}
-                    || !defined $seen_ranges{$range} )
-                {
-                    my $data = $chart->{_formula_data}->[$id];
-                    $seen_ranges{$range} = $data;
-                }
-                next RANGE;
-            }
-
-            # Check to see if the data is already cached locally.
-            if ( exists $seen_ranges{$range} ) {
-                $chart->{_formula_data}->[$id] = $seen_ranges{$range};
-                next RANGE;
-            }
-
-            # Convert the range formula to a sheet name and cell range.
-            my ( $sheetname, @cells ) = $self->_get_chart_range( $range );
-
-            # Skip if we couldn't parse the formula.
-            next RANGE if !defined $sheetname;
-
-            # Handle non-contiguous ranges: (Sheet1!$A$1:$A$2,Sheet1!$A$4:$A$5).
-            # We don't try to parse the ranges. We just return an empty list.
-            if ( $sheetname =~ m/^\([^,]+,/ ) {
-                $chart->{_formula_data}->[$id] = [];
-                $seen_ranges{$range} = [];
-                next RANGE;
-            }
-
-            # Die if the name is unknown since it indicates a user error in
-            # a chart series formula.
-            if ( !exists $worksheets{$sheetname} ) {
-                die "Unknown worksheet reference '$sheetname' in range "
-                  . "'$range' passed to add_series().\n";
-            }
-
-            # Find the worksheet object based on the sheet name.
-            my $worksheet = $worksheets{$sheetname};
-
-            # Get the data from the worksheet table.
-            my @data = $worksheet->_get_range_data( @cells );
-
-            # Convert shared string indexes to strings.
-            for my $token ( @data ) {
-                if ( ref $token ) {
-                    $token = $self->{_str_array}->[ $token->{sst_id} ];
-
-                    # Ignore rich strings for now. Deparse later if necessary.
-                    if ( $token =~ m{^<r>} && $token =~ m{</r>$} ) {
-                        $token = '';
-                    }
-                }
-            }
-
-            # Add the data to the chart.
-            $chart->{_formula_data}->[$id] = \@data;
-
-            # Store range data locally to avoid lookup if seen again.
-            $seen_ranges{$range} = \@data;
-        }
-    }
-}
-
-
-###############################################################################
-#
-# _get_chart_range()
-#
-# Convert a range formula such as Sheet1!$B$1:$B$5 into a sheet name and cell
-# range such as ( 'Sheet1', 0, 1, 4, 1 ).
-#
-sub _get_chart_range {
-
-    my $self  = shift;
-    my $range = shift;
-    my $cell_1;
-    my $cell_2;
-    my $sheetname;
-    my $cells;
-
-    # Split the range formula into sheetname and cells at the last '!'.
-    my $pos = rindex $range, '!';
-    if ( $pos > 0 ) {
-        $sheetname = substr $range, 0, $pos;
-        $cells = substr $range, $pos + 1;
-    }
-    else {
-        return undef;
-    }
-
-    # Split the cell range into 2 cells or else use single cell for both.
-    if ( $cells =~ ':' ) {
-        ( $cell_1, $cell_2 ) = split /:/, $cells;
-    }
-    else {
-        ( $cell_1, $cell_2 ) = ( $cells, $cells );
-    }
-
-    # Remove leading/trailing apostrophes and convert escaped quotes to single.
-    $sheetname =~ s/^'//g;
-    $sheetname =~ s/'$//g;
-    $sheetname =~ s/''/'/g;
-
-    my ( $row_start, $col_start ) = xl_cell_to_rowcol( $cell_1 );
-    my ( $row_end,   $col_end )   = xl_cell_to_rowcol( $cell_2 );
-
-    # Check that we have a 1D range only.
-    if ( $row_start != $row_end && $col_start != $col_end ) {
-        return undef;
-    }
-
-    return ( $sheetname, $row_start, $col_start, $row_end, $col_end );
-}
-
-
-###############################################################################
-#
-# _store_externs()
-#
-# Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for
-# the NAME records.
-#
-sub _store_externs {
-
-    my $self = shift;
-
-}
-
-
-###############################################################################
-#
-# _store_names()
-#
-# Write the NAME record to define the print area and the repeat rows and cols.
-#
-sub _store_names {
-
-    my $self = shift;
-
-}
-
-
-###############################################################################
-#
-# _quote_sheetname()
-#
-# Sheetnames used in references should be quoted if they contain any spaces,
-# special characters or if the look like something that isn't a sheet name.
-# TODO. We need to handle more special cases.
-#
-sub _quote_sheetname {
-
-    my $self      = shift;
-    my $sheetname = $_[0];
-
-    if ( $sheetname =~ /^Sheet\d+$/ ) {
-        return $sheetname;
-    }
-    else {
-        return qq('$sheetname');
-    }
-}
-
-
-###############################################################################
-#
-# _get_image_properties()
-#
-# Extract information from the image file such as dimension, type, filename,
-# and extension. Also keep track of previously seen images to optimise out
-# any duplicates.
-#
-sub _get_image_properties {
-
-    my $self     = shift;
-    my $filename = shift;
-
-    my $type;
-    my $width;
-    my $height;
-    my $x_dpi = 96;
-    my $y_dpi = 96;
-    my $image_name;
-
-
-    ( $image_name ) = fileparse( $filename );
-
-    # Open the image file and import the data.
-    my $fh = FileHandle->new( $filename );
-    croak "Couldn't import $filename: $!" unless defined $fh;
-    binmode $fh;
-
-    # Slurp the file into a string and do some size calcs.
-    my $data = do { local $/; <$fh> };
-    my $size = length $data;
-
-
-    if ( unpack( 'x A3', $data ) eq 'PNG' ) {
-
-        # Test for PNGs.
-        ( $type, $width, $height, $x_dpi, $y_dpi ) =
-          $self->_process_png( $data, $filename );
-
-        $self->{_image_types}->{png} = 1;
-    }
-    elsif ( unpack( 'n', $data ) == 0xFFD8 ) {
-
-        # Test for JPEG files.
-        ( $type, $width, $height, $x_dpi, $y_dpi ) =
-          $self->_process_jpg( $data, $filename );
-
-        $self->{_image_types}->{jpeg} = 1;
-    }
-    elsif ( unpack( 'A2', $data ) eq 'BM' ) {
-
-        # Test for BMPs.
-        ( $type, $width, $height ) = $self->_process_bmp( $data, $filename );
-
-        $self->{_image_types}->{bmp} = 1;
-    }
-    else {
-        croak "Unsupported image format for file: $filename\n";
-    }
-
-    push @{ $self->{_images} }, [ $filename, $type ];
-
-    # Set a default dpi for images with 0 dpi.
-    $x_dpi = 96 if $x_dpi == 0;
-    $y_dpi = 96 if $y_dpi == 0;
-
-    $fh->close;
-
-    return ( $type, $width, $height, $image_name, $x_dpi, $y_dpi );
-}
-
-
-###############################################################################
-#
-# _process_png()
-#
-# Extract width and height information from a PNG file.
-#
-sub _process_png {
-
-    my $self     = shift;
-    my $data     = $_[0];
-    my $filename = $_[1];
-
-    my $type   = 'png';
-    my $width  = 0;
-    my $height = 0;
-    my $x_dpi  = 96;
-    my $y_dpi  = 96;
-
-    my $offset      = 8;
-    my $data_length = length $data;
-
-    # Search through the image data to read the height and width in the
-    # IHDR element. Also read the DPI in the pHYs element.
-    while ( $offset < $data_length ) {
-
-        my $length = unpack "N",  substr $data, $offset + 0, 4;
-        my $type   = unpack "A4", substr $data, $offset + 4, 4;
-
-        if ( $type eq "IHDR" ) {
-            $width  = unpack "N", substr $data, $offset + 8,  4;
-            $height = unpack "N", substr $data, $offset + 12, 4;
-        }
-
-        if ( $type eq "pHYs" ) {
-            my $x_ppu = unpack "N", substr $data, $offset + 8,  4;
-            my $y_ppu = unpack "N", substr $data, $offset + 12, 4;
-            my $units = unpack "C", substr $data, $offset + 16, 1;
-
-            if ( $units == 1 ) {
-                $x_dpi = $x_ppu * 0.0254;
-                $y_dpi = $y_ppu * 0.0254;
-            }
-        }
-
-        $offset = $offset + $length + 12;
-
-        last if $type eq "IEND";
-    }
-
-    if ( not defined $height ) {
-        croak "$filename: no size data found in png image.\n";
-    }
-
-    return ( $type, $width, $height, $x_dpi, $y_dpi );
-}
-
-
-###############################################################################
-#
-# _process_bmp()
-#
-# Extract width and height information from a BMP file.
-#
-# Most of the checks came from old Spredsheet::WriteExcel code.
-#
-sub _process_bmp {
-
-    my $self     = shift;
-    my $data     = $_[0];
-    my $filename = $_[1];
-    my $type     = 'bmp';
-
-
-    # Check that the file is big enough to be a bitmap.
-    if ( length $data <= 0x36 ) {
-        croak "$filename doesn't contain enough data.";
-    }
-
-
-    # Read the bitmap width and height. Verify the sizes.
-    my ( $width, $height ) = unpack "x18 V2", $data;
-
-    if ( $width > 0xFFFF ) {
-        croak "$filename: largest image width $width supported is 65k.";
-    }
-
-    if ( $height > 0xFFFF ) {
-        croak "$filename: largest image height supported is 65k.";
-    }
-
-    # Read the bitmap planes and bpp data. Verify them.
-    my ( $planes, $bitcount ) = unpack "x26 v2", $data;
-
-    if ( $bitcount != 24 ) {
-        croak "$filename isn't a 24bit true color bitmap.";
-    }
-
-    if ( $planes != 1 ) {
-        croak "$filename: only 1 plane supported in bitmap image.";
-    }
-
-
-    # Read the bitmap compression. Verify compression.
-    my $compression = unpack "x30 V", $data;
-
-    if ( $compression != 0 ) {
-        croak "$filename: compression not supported in bitmap image.";
-    }
-
-    return ( $type, $width, $height );
-}
-
-
-###############################################################################
-#
-# _process_jpg()
-#
-# Extract width and height information from a JPEG file.
-#
-sub _process_jpg {
-
-    my $self     = shift;
-    my $data     = $_[0];
-    my $filename = $_[1];
-    my $type     = 'jpeg';
-    my $x_dpi    = 96;
-    my $y_dpi    = 96;
-    my $width;
-    my $height;
-
-    my $offset      = 2;
-    my $data_length = length $data;
-
-    # Search through the image data to read the JPEG markers.
-    while ( $offset < $data_length ) {
-
-        my $marker = unpack "n", substr $data, $offset + 0, 2;
-        my $length = unpack "n", substr $data, $offset + 2, 2;
-
-        # Read the height and width in the 0xFFCn elements (except C4, C8 and
-        # CC which aren't SOF markers).
-        if (   ( $marker & 0xFFF0 ) == 0xFFC0
-            && $marker != 0xFFC4
-            && $marker != 0xFFCC )
-        {
-            $height = unpack "n", substr $data, $offset + 5, 2;
-            $width  = unpack "n", substr $data, $offset + 7, 2;
-        }
-
-        # Read the DPI in the 0xFFE0 element.
-        if ( $marker == 0xFFE0 ) {
-            my $units     = unpack "C", substr $data, $offset + 11, 1;
-            my $x_density = unpack "n", substr $data, $offset + 12, 2;
-            my $y_density = unpack "n", substr $data, $offset + 14, 2;
-
-            if ( $units == 1 ) {
-                $x_dpi = $x_density;
-                $y_dpi = $y_density;
-            }
-
-            if ( $units == 2 ) {
-                $x_dpi = $x_density * 2.54;
-                $y_dpi = $y_density * 2.54;
-            }
-        }
-
-        $offset = $offset + $length + 2;
-        last if $marker == 0xFFDA;
-    }
-
-    if ( not defined $height ) {
-        croak "$filename: no size data found in jpeg image.\n";
-    }
-
-    return ( $type, $width, $height, $x_dpi, $y_dpi );
-}
-
-
-###############################################################################
-#
-# _get_sheet_index()
-#
-# Convert a sheet name to its index. Return undef otherwise.
-#
-sub _get_sheet_index {
-
-    my $self        = shift;
-    my $sheetname   = shift;
-    my $sheet_index = undef;
-
-    $sheetname =~ s/^'//;
-    $sheetname =~ s/'$//;
-
-    if ( exists $self->{_sheetnames}->{$sheetname} ) {
-        return $self->{_sheetnames}->{$sheetname}->{_index};
-    }
-    else {
-        return undef;
-    }
-}
-
-
-###############################################################################
-#
-# set_optimization()
-#
-# Set the speed/memory optimisation level.
-#
-sub set_optimization {
-
-    my $self = shift;
-    my $level = defined $_[0] ? $_[0] : 1;
-
-    croak "set_optimization() must be called before add_worksheet()"
-      if $self->sheets();
-
-    $self->{_optimization} = $level;
-}
-
-
-###############################################################################
-#
-# Deprecated methods for backwards compatibility.
-#
-###############################################################################
-
-# No longer required by Excel::Writer::XLSX.
-sub compatibility_mode { }
-sub set_codepage       { }
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _write_workbook()
-#
-# Write <workbook> element.
-#
-sub _write_workbook {
-
-    my $self    = shift;
-    my $schema  = 'http://schemas.openxmlformats.org';
-    my $xmlns   = $schema . '/spreadsheetml/2006/main';
-    my $xmlns_r = $schema . '/officeDocument/2006/relationships';
-
-    my @attributes = (
-        'xmlns'   => $xmlns,
-        'xmlns:r' => $xmlns_r,
-    );
-
-    $self->xml_start_tag( 'workbook', @attributes );
-}
-
-
-###############################################################################
-#
-# write_file_version()
-#
-# Write the <fileVersion> element.
-#
-sub _write_file_version {
-
-    my $self          = shift;
-    my $app_name      = 'xl';
-    my $last_edited   = 4;
-    my $lowest_edited = 4;
-    my $rup_build     = 4505;
-
-    my @attributes = (
-        'appName'      => $app_name,
-        'lastEdited'   => $last_edited,
-        'lowestEdited' => $lowest_edited,
-        'rupBuild'     => $rup_build,
-    );
-
-    if ( $self->{_vba_project} ) {
-        push @attributes, codeName => '{37E998C4-C9E5-D4B9-71C8-EB1FF731991C}';
-    }
-
-    $self->xml_empty_tag( 'fileVersion', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_workbook_pr()
-#
-# Write <workbookPr> element.
-#
-sub _write_workbook_pr {
-
-    my $self                   = shift;
-    my $date_1904              = $self->{_date_1904};
-    my $show_ink_annotation    = 0;
-    my $auto_compress_pictures = 0;
-    my $default_theme_version  = 124226;
-    my $codename               = $self->{_vba_codename};
-    my @attributes;
-
-    push @attributes, ( 'codeName' => $codename ) if $codename;
-    push @attributes, ( 'date1904' => 1 )         if $date_1904;
-    push @attributes, ( 'defaultThemeVersion' => $default_theme_version );
-
-    $self->xml_empty_tag( 'workbookPr', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_book_views()
-#
-# Write <bookViews> element.
-#
-sub _write_book_views {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'bookViews' );
-    $self->_write_workbook_view();
-    $self->xml_end_tag( 'bookViews' );
-}
-
-###############################################################################
-#
-# _write_workbook_view()
-#
-# Write <workbookView> element.
-#
-sub _write_workbook_view {
-
-    my $self          = shift;
-    my $x_window      = $self->{_x_window};
-    my $y_window      = $self->{_y_window};
-    my $window_width  = $self->{_window_width};
-    my $window_height = $self->{_window_height};
-    my $tab_ratio     = $self->{_tab_ratio};
-    my $active_tab    = $self->{_activesheet};
-    my $first_sheet   = $self->{_firstsheet};
-
-    my @attributes = (
-        'xWindow'      => $x_window,
-        'yWindow'      => $y_window,
-        'windowWidth'  => $window_width,
-        'windowHeight' => $window_height,
-    );
-
-    # Store the tabRatio attribute when it isn't the default.
-    push @attributes, ( tabRatio => $tab_ratio ) if $tab_ratio != 500;
-
-    # Store the firstSheet attribute when it isn't the default.
-    push @attributes, ( firstSheet => $first_sheet + 1 ) if $first_sheet > 0;
-
-    # Store the activeTab attribute when it isn't the first sheet.
-    push @attributes, ( activeTab => $active_tab ) if $active_tab > 0;
-
-    $self->xml_empty_tag( 'workbookView', @attributes );
-}
-
-###############################################################################
-#
-# _write_sheets()
-#
-# Write <sheets> element.
-#
-sub _write_sheets {
-
-    my $self   = shift;
-    my $id_num = 1;
-
-    $self->xml_start_tag( 'sheets' );
-
-    for my $worksheet ( @{ $self->{_worksheets} } ) {
-        $self->_write_sheet( $worksheet->{_name}, $id_num++,
-            $worksheet->{_hidden} );
-    }
-
-    $self->xml_end_tag( 'sheets' );
-}
-
-
-###############################################################################
-#
-# _write_sheet()
-#
-# Write <sheet> element.
-#
-sub _write_sheet {
-
-    my $self     = shift;
-    my $name     = shift;
-    my $sheet_id = shift;
-    my $hidden   = shift;
-    my $r_id     = 'rId' . $sheet_id;
-
-    my @attributes = (
-        'name'    => $name,
-        'sheetId' => $sheet_id,
-    );
-
-    push @attributes, ( 'state' => 'hidden' ) if $hidden;
-    push @attributes, ( 'r:id' => $r_id );
-
-
-    $self->xml_empty_tag( 'sheet', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_calc_pr()
-#
-# Write <calcPr> element.
-#
-sub _write_calc_pr {
-
-    my $self            = shift;
-    my $calc_id         = $self->{_calc_id};
-    my $concurrent_calc = 0;
-
-    my @attributes = ( calcId => $calc_id );
-
-    if ( $self->{_calc_mode} eq 'manual' ) {
-        push @attributes, 'calcMode'   => 'manual';
-        push @attributes, 'calcOnSave' => 0;
-    }
-    elsif ( $self->{_calc_mode} eq 'autoNoTable' ) {
-        push @attributes, calcMode => 'autoNoTable';
-    }
-
-    if ( $self->{_calc_on_load} ) {
-        push @attributes, 'fullCalcOnLoad' => 1;
-    }
-
-
-    $self->xml_empty_tag( 'calcPr', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_ext_lst()
-#
-# Write <extLst> element.
-#
-sub _write_ext_lst {
-
-    my $self = shift;
-
-    $self->xml_start_tag( 'extLst' );
-    $self->_write_ext();
-    $self->xml_end_tag( 'extLst' );
-}
-
-
-###############################################################################
-#
-# _write_ext()
-#
-# Write <ext> element.
-#
-sub _write_ext {
-
-    my $self     = shift;
-    my $xmlns_mx = 'http://schemas.microsoft.com/office/mac/excel/2008/main';
-    my $uri      = 'http://schemas.microsoft.com/office/mac/excel/2008/main';
-
-    my @attributes = (
-        'xmlns:mx' => $xmlns_mx,
-        'uri'      => $uri,
-    );
-
-    $self->xml_start_tag( 'ext', @attributes );
-    $self->_write_mx_arch_id();
-    $self->xml_end_tag( 'ext' );
-}
-
-###############################################################################
-#
-# _write_mx_arch_id()
-#
-# Write <mx:ArchID> element.
-#
-sub _write_mx_arch_id {
-
-    my $self  = shift;
-    my $Flags = 2;
-
-    my @attributes = ( 'Flags' => $Flags, );
-
-    $self->xml_empty_tag( 'mx:ArchID', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_defined_names()
-#
-# Write the <definedNames> element.
-#
-sub _write_defined_names {
-
-    my $self = shift;
-
-    return unless @{ $self->{_defined_names} };
-
-    $self->xml_start_tag( 'definedNames' );
-
-    for my $aref ( @{ $self->{_defined_names} } ) {
-        $self->_write_defined_name( $aref );
-    }
-
-    $self->xml_end_tag( 'definedNames' );
-}
-
-
-##############################################################################
-#
-# _write_defined_name()
-#
-# Write the <definedName> element.
-#
-sub _write_defined_name {
-
-    my $self = shift;
-    my $data = shift;
-
-    my $name   = $data->[0];
-    my $id     = $data->[1];
-    my $range  = $data->[2];
-    my $hidden = $data->[3];
-
-    my @attributes = ( 'name' => $name );
-
-    push @attributes, ( 'localSheetId' => $id ) if $id != -1;
-    push @attributes, ( 'hidden'       => 1 )   if $hidden;
-
-    $self->xml_data_element( 'definedName', $range, @attributes );
-}
-
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Workbook - A class for writing Excel Workbooks.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
diff --git a/tools/lib/perl5/Excel/Writer/XLSX/Worksheet.pm b/tools/lib/perl5/Excel/Writer/XLSX/Worksheet.pm
deleted file mode 100644 (file)
index 0c61c1f..0000000
+++ /dev/null
@@ -1,10003 +0,0 @@
-package Excel::Writer::XLSX::Worksheet;
-
-###############################################################################
-#
-# Worksheet - A class for writing Excel Worksheets.
-#
-#
-# Used in conjunction with Excel::Writer::XLSX
-#
-# Copyright 2000-2018, John McNamara, jmcnamara@cpan.org
-#
-# Documentation after __END__
-#
-
-# perltidy with the following options: -mbl=2 -pt=0 -nola
-
-use 5.008002;
-use strict;
-use warnings;
-use Carp;
-use File::Temp 'tempfile';
-use List::Util qw(max min);
-use Excel::Writer::XLSX::Format;
-use Excel::Writer::XLSX::Drawing;
-use Excel::Writer::XLSX::Package::XMLwriter;
-use Excel::Writer::XLSX::Utility qw(xl_cell_to_rowcol
-                                    xl_rowcol_to_cell
-                                    xl_col_to_name
-                                    xl_range
-                                    quote_sheetname);
-
-our @ISA     = qw(Excel::Writer::XLSX::Package::XMLwriter);
-our $VERSION = '0.98';
-
-
-###############################################################################
-#
-# Public and private API methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# new()
-#
-# Constructor.
-#
-sub new {
-
-    my $class  = shift;
-    my $fh     = shift;
-    my $self   = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
-    my $rowmax = 1_048_576;
-    my $colmax = 16_384;
-    my $strmax = 32767;
-
-    $self->{_name}               = $_[0];
-    $self->{_index}              = $_[1];
-    $self->{_activesheet}        = $_[2];
-    $self->{_firstsheet}         = $_[3];
-    $self->{_str_total}          = $_[4];
-    $self->{_str_unique}         = $_[5];
-    $self->{_str_table}          = $_[6];
-    $self->{_date_1904}          = $_[7];
-    $self->{_palette}            = $_[8];
-    $self->{_optimization}       = $_[9] || 0;
-    $self->{_tempdir}            = $_[10];
-    $self->{_excel2003_style}    = $_[11];
-    $self->{_default_url_format} = $_[12];
-
-    $self->{_ext_sheets}    = [];
-    $self->{_fileclosed}    = 0;
-    $self->{_excel_version} = 2007;
-
-    $self->{_xls_rowmax} = $rowmax;
-    $self->{_xls_colmax} = $colmax;
-    $self->{_xls_strmax} = $strmax;
-    $self->{_dim_rowmin} = undef;
-    $self->{_dim_rowmax} = undef;
-    $self->{_dim_colmin} = undef;
-    $self->{_dim_colmax} = undef;
-
-    $self->{_colinfo}    = {};
-    $self->{_selections} = [];
-    $self->{_hidden}     = 0;
-    $self->{_active}     = 0;
-    $self->{_tab_color}  = 0;
-
-    $self->{_panes}       = [];
-    $self->{_active_pane} = 3;
-    $self->{_selected}    = 0;
-
-    $self->{_page_setup_changed} = 0;
-    $self->{_paper_size}         = 0;
-    $self->{_orientation}        = 1;
-
-    $self->{_print_options_changed} = 0;
-    $self->{_hcenter}               = 0;
-    $self->{_vcenter}               = 0;
-    $self->{_print_gridlines}       = 0;
-    $self->{_screen_gridlines}      = 1;
-    $self->{_print_headers}         = 0;
-
-    $self->{_header_footer_changed} = 0;
-    $self->{_header}                = '';
-    $self->{_footer}                = '';
-    $self->{_header_footer_aligns}  = 1;
-    $self->{_header_footer_scales}  = 1;
-    $self->{_header_images}         = [];
-    $self->{_footer_images}         = [];
-
-    $self->{_margin_left}   = 0.7;
-    $self->{_margin_right}  = 0.7;
-    $self->{_margin_top}    = 0.75;
-    $self->{_margin_bottom} = 0.75;
-    $self->{_margin_header} = 0.3;
-    $self->{_margin_footer} = 0.3;
-
-    $self->{_repeat_rows} = '';
-    $self->{_repeat_cols} = '';
-    $self->{_print_area}  = '';
-
-    $self->{_page_order}     = 0;
-    $self->{_black_white}    = 0;
-    $self->{_draft_quality}  = 0;
-    $self->{_print_comments} = 0;
-    $self->{_page_start}     = 0;
-
-    $self->{_fit_page}   = 0;
-    $self->{_fit_width}  = 0;
-    $self->{_fit_height} = 0;
-
-    $self->{_hbreaks} = [];
-    $self->{_vbreaks} = [];
-
-    $self->{_protect}  = 0;
-    $self->{_password} = undef;
-
-    $self->{_set_cols} = {};
-    $self->{_set_rows} = {};
-
-    $self->{_zoom}              = 100;
-    $self->{_zoom_scale_normal} = 1;
-    $self->{_print_scale}       = 100;
-    $self->{_right_to_left}     = 0;
-    $self->{_show_zeros}        = 1;
-    $self->{_leading_zeros}     = 0;
-
-    $self->{_outline_row_level} = 0;
-    $self->{_outline_col_level} = 0;
-    $self->{_outline_style}     = 0;
-    $self->{_outline_below}     = 1;
-    $self->{_outline_right}     = 1;
-    $self->{_outline_on}        = 1;
-    $self->{_outline_changed}   = 0;
-
-    $self->{_original_row_height} = 15;
-    $self->{_default_row_height}  = 15;
-    $self->{_default_row_pixels}  = 20;
-    $self->{_default_col_pixels}  = 64;
-    $self->{_default_row_zeroed}  = 0;
-
-    $self->{_names} = {};
-
-    $self->{_write_match} = [];
-
-
-    $self->{_table} = {};
-    $self->{_merge} = [];
-
-    $self->{_has_vml}             = 0;
-    $self->{_has_header_vml}      = 0;
-    $self->{_has_comments}        = 0;
-    $self->{_comments}            = {};
-    $self->{_comments_array}      = [];
-    $self->{_comments_author}     = '';
-    $self->{_comments_visible}    = 0;
-    $self->{_vml_shape_id}        = 1024;
-    $self->{_buttons_array}       = [];
-    $self->{_header_images_array} = [];
-
-    $self->{_autofilter}   = '';
-    $self->{_filter_on}    = 0;
-    $self->{_filter_range} = [];
-    $self->{_filter_cols}  = {};
-
-    $self->{_col_sizes}        = {};
-    $self->{_row_sizes}        = {};
-    $self->{_col_formats}      = {};
-    $self->{_col_size_changed} = 0;
-    $self->{_row_size_changed} = 0;
-
-    $self->{_last_shape_id}          = 1;
-    $self->{_rel_count}              = 0;
-    $self->{_hlink_count}            = 0;
-    $self->{_hlink_refs}             = [];
-    $self->{_external_hyper_links}   = [];
-    $self->{_external_drawing_links} = [];
-    $self->{_external_comment_links} = [];
-    $self->{_external_vml_links}     = [];
-    $self->{_external_table_links}   = [];
-    $self->{_drawing_links}          = [];
-    $self->{_vml_drawing_links}      = [];
-    $self->{_charts}                 = [];
-    $self->{_images}                 = [];
-    $self->{_tables}                 = [];
-    $self->{_sparklines}             = [];
-    $self->{_shapes}                 = [];
-    $self->{_shape_hash}             = {};
-    $self->{_has_shapes}             = 0;
-    $self->{_drawing}                = 0;
-
-    $self->{_horizontal_dpi} = 0;
-    $self->{_vertical_dpi}   = 0;
-
-    $self->{_rstring}      = '';
-    $self->{_previous_row} = 0;
-
-    if ( $self->{_optimization} == 1 ) {
-        my $fh = tempfile( DIR => $self->{_tempdir} );
-        binmode $fh, ':utf8';
-
-        $self->{_cell_data_fh} = $fh;
-        $self->{_fh}           = $fh;
-    }
-
-    $self->{_validations}        = [];
-    $self->{_cond_formats}       = {};
-    $self->{_data_bars_2010}     = [];
-    $self->{_use_data_bars_2010} = 0;
-    $self->{_dxf_priority}       = 1;
-
-    if ( $self->{_excel2003_style} ) {
-        $self->{_original_row_height}  = 12.75;
-        $self->{_default_row_height}   = 12.75;
-        $self->{_default_row_pixels}   = 17;
-        $self->{_margin_left}          = 0.75;
-        $self->{_margin_right}         = 0.75;
-        $self->{_margin_top}           = 1;
-        $self->{_margin_bottom}        = 1;
-        $self->{_margin_header}        = 0.5;
-        $self->{_margin_footer}        = 0.5;
-        $self->{_header_footer_aligns} = 0;
-    }
-
-    bless $self, $class;
-    return $self;
-}
-
-###############################################################################
-#
-# _set_xml_writer()
-#
-# Over-ridden to ensure that write_single_row() is called for the final row
-# when optimisation mode is on.
-#
-sub _set_xml_writer {
-
-    my $self     = shift;
-    my $filename = shift;
-
-    if ( $self->{_optimization} == 1 ) {
-        $self->_write_single_row();
-    }
-
-    $self->SUPER::_set_xml_writer( $filename );
-}
-
-
-###############################################################################
-#
-# _assemble_xml_file()
-#
-# Assemble and write the XML file.
-#
-sub _assemble_xml_file {
-
-    my $self = shift;
-
-    $self->xml_declaration();
-
-    # Write the root worksheet element.
-    $self->_write_worksheet();
-
-    # Write the worksheet properties.
-    $self->_write_sheet_pr();
-
-    # Write the worksheet dimensions.
-    $self->_write_dimension();
-
-    # Write the sheet view properties.
-    $self->_write_sheet_views();
-
-    # Write the sheet format properties.
-    $self->_write_sheet_format_pr();
-
-    # Write the sheet column info.
-    $self->_write_cols();
-
-    # Write the worksheet data such as rows columns and cells.
-    if ( $self->{_optimization} == 0 ) {
-        $self->_write_sheet_data();
-    }
-    else {
-        $self->_write_optimized_sheet_data();
-    }
-
-    # Write the sheetProtection element.
-    $self->_write_sheet_protection();
-
-    # Write the worksheet calculation properties.
-    #$self->_write_sheet_calc_pr();
-
-    # Write the worksheet phonetic properties.
-    if ($self->{_excel2003_style}) {
-        $self->_write_phonetic_pr();
-    }
-
-    # Write the autoFilter element.
-    $self->_write_auto_filter();
-
-    # Write the mergeCells element.
-    $self->_write_merge_cells();
-
-    # Write the conditional formats.
-    $self->_write_conditional_formats();
-
-    # Write the dataValidations element.
-    $self->_write_data_validations();
-
-    # Write the hyperlink element.
-    $self->_write_hyperlinks();
-
-    # Write the printOptions element.
-    $self->_write_print_options();
-
-    # Write the worksheet page_margins.
-    $self->_write_page_margins();
-
-    # Write the worksheet page setup.
-    $self->_write_page_setup();
-
-    # Write the headerFooter element.
-    $self->_write_header_footer();
-
-    # Write the rowBreaks element.
-    $self->_write_row_breaks();
-
-    # Write the colBreaks element.
-    $self->_write_col_breaks();
-
-    # Write the drawing element.
-    $self->_write_drawings();
-
-    # Write the legacyDrawing element.
-    $self->_write_legacy_drawing();
-
-    # Write the legacyDrawingHF element.
-    $self->_write_legacy_drawing_hf();
-
-    # Write the tableParts element.
-    $self->_write_table_parts();
-
-    # Write the extLst elements.
-    $self->_write_ext_list();
-
-    # Close the worksheet tag.
-    $self->xml_end_tag( 'worksheet' );
-
-    # Close the XML writer filehandle.
-    $self->xml_get_fh()->close();
-}
-
-
-###############################################################################
-#
-# _close()
-#
-# Write the worksheet elements.
-#
-sub _close {
-
-    # TODO. Unused. Remove after refactoring.
-    my $self       = shift;
-    my $sheetnames = shift;
-    my $num_sheets = scalar @$sheetnames;
-}
-
-
-###############################################################################
-#
-# get_name().
-#
-# Retrieve the worksheet name.
-#
-sub get_name {
-
-    my $self = shift;
-
-    return $self->{_name};
-}
-
-
-###############################################################################
-#
-# select()
-#
-# Set this worksheet as a selected worksheet, i.e. the worksheet has its tab
-# highlighted.
-#
-sub select {
-
-    my $self = shift;
-
-    $self->{_hidden}   = 0;    # Selected worksheet can't be hidden.
-    $self->{_selected} = 1;
-}
-
-
-###############################################################################
-#
-# activate()
-#
-# Set this worksheet as the active worksheet, i.e. the worksheet that is
-# displayed when the workbook is opened. Also set it as selected.
-#
-sub activate {
-
-    my $self = shift;
-
-    $self->{_hidden}   = 0;    # Active worksheet can't be hidden.
-    $self->{_selected} = 1;
-    ${ $self->{_activesheet} } = $self->{_index};
-}
-
-
-###############################################################################
-#
-# hide()
-#
-# Hide this worksheet.
-#
-sub hide {
-
-    my $self = shift;
-
-    $self->{_hidden} = 1;
-
-    # A hidden worksheet shouldn't be active or selected.
-    $self->{_selected} = 0;
-    ${ $self->{_activesheet} } = 0;
-    ${ $self->{_firstsheet} }  = 0;
-}
-
-
-###############################################################################
-#
-# set_first_sheet()
-#
-# Set this worksheet as the first visible sheet. This is necessary
-# when there are a large number of worksheets and the activated
-# worksheet is not visible on the screen.
-#
-sub set_first_sheet {
-
-    my $self = shift;
-
-    $self->{_hidden} = 0;    # Active worksheet can't be hidden.
-    ${ $self->{_firstsheet} } = $self->{_index};
-}
-
-
-###############################################################################
-#
-# protect( $password )
-#
-# Set the worksheet protection flags to prevent modification of worksheet
-# objects.
-#
-sub protect {
-
-    my $self     = shift;
-    my $password = shift || '';
-    my $options  = shift || {};
-
-    if ( $password ne '' ) {
-        $password = $self->_encode_password( $password );
-    }
-
-    # Default values for objects that can be protected.
-    my %defaults = (
-        sheet                 => 1,
-        content               => 0,
-        objects               => 0,
-        scenarios             => 0,
-        format_cells          => 0,
-        format_columns        => 0,
-        format_rows           => 0,
-        insert_columns        => 0,
-        insert_rows           => 0,
-        insert_hyperlinks     => 0,
-        delete_columns        => 0,
-        delete_rows           => 0,
-        select_locked_cells   => 1,
-        sort                  => 0,
-        autofilter            => 0,
-        pivot_tables          => 0,
-        select_unlocked_cells => 1,
-    );
-
-
-    # Overwrite the defaults with user specified values.
-    for my $key ( keys %{$options} ) {
-
-        if ( exists $defaults{$key} ) {
-            $defaults{$key} = $options->{$key};
-        }
-        else {
-            carp "Unknown protection object: $key\n";
-        }
-    }
-
-    # Set the password after the user defined values.
-    $defaults{password} = $password;
-
-    $self->{_protect} = \%defaults;
-}
-
-
-###############################################################################
-#
-# _encode_password($password)
-#
-# Based on the algorithm provided by Daniel Rentz of OpenOffice.
-#
-sub _encode_password {
-
-    use integer;
-
-    my $self      = shift;
-    my $plaintext = $_[0];
-    my $password;
-    my $count;
-    my @chars;
-    my $i = 0;
-
-    $count = @chars = split //, $plaintext;
-
-    foreach my $char ( @chars ) {
-        my $low_15;
-        my $high_15;
-        $char    = ord( $char ) << ++$i;
-        $low_15  = $char & 0x7fff;
-        $high_15 = $char & 0x7fff << 15;
-        $high_15 = $high_15 >> 15;
-        $char    = $low_15 | $high_15;
-    }
-
-    $password = 0x0000;
-    $password ^= $_ for @chars;
-    $password ^= $count;
-    $password ^= 0xCE4B;
-
-    return sprintf "%X", $password;
-}
-
-
-###############################################################################
-#
-# set_column($firstcol, $lastcol, $width, $format, $hidden, $level)
-#
-# Set the width of a single column or a range of columns.
-# See also: _write_col_info
-#
-sub set_column {
-
-    my $self = shift;
-    my @data = @_;
-    my $cell = $data[0];
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $cell =~ /^\D/ ) {
-        @data = $self->_substitute_cellref( @_ );
-
-        # Returned values $row1 and $row2 aren't required here. Remove them.
-        shift @data;    # $row1
-        splice @data, 1, 1;    # $row2
-    }
-
-    return if @data < 3;       # Ensure at least $firstcol, $lastcol and $width
-    return if not defined $data[0];    # Columns must be defined.
-    return if not defined $data[1];
-
-    # Assume second column is the same as first if 0. Avoids KB918419 bug.
-    $data[1] = $data[0] if $data[1] == 0;
-
-    # Ensure 2nd col is larger than first. Also for KB918419 bug.
-    ( $data[0], $data[1] ) = ( $data[1], $data[0] ) if $data[0] > $data[1];
-
-
-    # Check that cols are valid and store max and min values with default row.
-    # NOTE: The check shouldn't modify the row dimensions and should only modify
-    #       the column dimensions in certain cases.
-    my $ignore_row = 1;
-    my $ignore_col = 1;
-    $ignore_col = 0 if ref $data[3];          # Column has a format.
-    $ignore_col = 0 if $data[2] && $data[4];  # Column has a width but is hidden
-
-    return -2
-      if $self->_check_dimensions( 0, $data[0], $ignore_row, $ignore_col );
-    return -2
-      if $self->_check_dimensions( 0, $data[1], $ignore_row, $ignore_col );
-
-    # Set the limits for the outline levels (0 <= x <= 7).
-    $data[5] = 0 unless defined $data[5];
-    $data[5] = 0 if $data[5] < 0;
-    $data[5] = 7 if $data[5] > 7;
-
-    if ( $data[5] > $self->{_outline_col_level} ) {
-        $self->{_outline_col_level} = $data[5];
-    }
-
-    # Store the column data based on the first column. Padded for sorting.
-    $self->{_colinfo}->{ sprintf "%05d", $data[0] } = [@data];
-
-    # Store the column change to allow optimisations.
-    $self->{_col_size_changed} = 1;
-
-    # Store the col sizes for use when calculating image vertices taking
-    # hidden columns into account. Also store the column formats.
-    my $width = $data[4] ? 0 : $data[2];    # Set width to zero if hidden.
-    my $format = $data[3];
-
-    my ( $firstcol, $lastcol ) = @data;
-
-    foreach my $col ( $firstcol .. $lastcol ) {
-        $self->{_col_sizes}->{$col} = $width;
-        $self->{_col_formats}->{$col} = $format if $format;
-    }
-}
-
-
-###############################################################################
-#
-# set_selection()
-#
-# Set which cell or cells are selected in a worksheet.
-#
-sub set_selection {
-
-    my $self = shift;
-    my $pane;
-    my $active_cell;
-    my $sqref;
-
-    return unless @_;
-
-    # Check for a cell reference in A1 notation and substitute row and column.
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-
-    # There should be either 2 or 4 arguments.
-    if ( @_ == 2 ) {
-
-        # Single cell selection.
-        $active_cell = xl_rowcol_to_cell( $_[0], $_[1] );
-        $sqref = $active_cell;
-    }
-    elsif ( @_ == 4 ) {
-
-        # Range selection.
-        $active_cell = xl_rowcol_to_cell( $_[0], $_[1] );
-
-        my ( $row_first, $col_first, $row_last, $col_last ) = @_;
-
-        # Swap last row/col for first row/col as necessary
-        if ( $row_first > $row_last ) {
-            ( $row_first, $row_last ) = ( $row_last, $row_first );
-        }
-
-        if ( $col_first > $col_last ) {
-            ( $col_first, $col_last ) = ( $col_last, $col_first );
-        }
-
-        # If the first and last cell are the same write a single cell.
-        if ( ( $row_first == $row_last ) && ( $col_first == $col_last ) ) {
-            $sqref = $active_cell;
-        }
-        else {
-            $sqref = xl_range( $row_first, $row_last, $col_first, $col_last );
-        }
-
-    }
-    else {
-
-        # User supplied wrong number or arguments.
-        return;
-    }
-
-    # Selection isn't set for cell A1.
-    return if $sqref eq 'A1';
-
-    $self->{_selections} = [ [ $pane, $active_cell, $sqref ] ];
-}
-
-
-###############################################################################
-#
-# freeze_panes( $row, $col, $top_row, $left_col )
-#
-# Set panes and mark them as frozen.
-#
-sub freeze_panes {
-
-    my $self = shift;
-
-    return unless @_;
-
-    # Check for a cell reference in A1 notation and substitute row and column.
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    my $row      = shift;
-    my $col      = shift || 0;
-    my $top_row  = shift || $row;
-    my $left_col = shift || $col;
-    my $type     = shift || 0;
-
-    $self->{_panes} = [ $row, $col, $top_row, $left_col, $type ];
-}
-
-
-###############################################################################
-#
-# split_panes( $y, $x, $top_row, $left_col )
-#
-# Set panes and mark them as split.
-#
-# Implementers note. The API for this method doesn't map well from the XLS
-# file format and isn't sufficient to describe all cases of split panes.
-# It should probably be something like:
-#
-#     split_panes( $y, $x, $top_row, $left_col, $offset_row, $offset_col )
-#
-# I'll look at changing this if it becomes an issue.
-#
-sub split_panes {
-
-    my $self = shift;
-
-    # Call freeze panes but add the type flag for split panes.
-    $self->freeze_panes( @_[ 0 .. 3 ], 2 );
-}
-
-# Older method name for backwards compatibility.
-*thaw_panes = *split_panes;
-
-
-###############################################################################
-#
-# set_portrait()
-#
-# Set the page orientation as portrait.
-#
-sub set_portrait {
-
-    my $self = shift;
-
-    $self->{_orientation}        = 1;
-    $self->{_page_setup_changed} = 1;
-}
-
-
-###############################################################################
-#
-# set_landscape()
-#
-# Set the page orientation as landscape.
-#
-sub set_landscape {
-
-    my $self = shift;
-
-    $self->{_orientation}        = 0;
-    $self->{_page_setup_changed} = 1;
-}
-
-
-###############################################################################
-#
-# set_page_view()
-#
-# Set the page view mode for Mac Excel.
-#
-sub set_page_view {
-
-    my $self = shift;
-
-    $self->{_page_view} = defined $_[0] ? $_[0] : 1;
-}
-
-
-###############################################################################
-#
-# set_tab_color()
-#
-# Set the colour of the worksheet tab.
-#
-sub set_tab_color {
-
-    my $self  = shift;
-    my $color = &Excel::Writer::XLSX::Format::_get_color( $_[0] );
-
-    $self->{_tab_color} = $color;
-}
-
-
-###############################################################################
-#
-# set_paper()
-#
-# Set the paper type. Ex. 1 = US Letter, 9 = A4
-#
-sub set_paper {
-
-    my $self       = shift;
-    my $paper_size = shift;
-
-    if ( $paper_size ) {
-        $self->{_paper_size}         = $paper_size;
-        $self->{_page_setup_changed} = 1;
-    }
-}
-
-
-###############################################################################
-#
-# set_header()
-#
-# Set the page header caption and optional margin.
-#
-sub set_header {
-
-    my $self    = shift;
-    my $string  = $_[0] || '';
-    my $margin  = $_[1] || 0.3;
-    my $options = $_[2] || {};
-
-
-    # Replace the Excel placeholder &[Picture] with the internal &G.
-    $string =~ s/&\[Picture\]/&G/g;
-
-    if ( length $string >= 255 ) {
-        carp 'Header string must be less than 255 characters';
-        return;
-    }
-
-    if ( defined $options->{align_with_margins} ) {
-        $self->{_header_footer_aligns} = $options->{align_with_margins};
-    }
-
-    if ( defined $options->{scale_with_doc} ) {
-        $self->{_header_footer_scales} = $options->{scale_with_doc};
-    }
-
-    # Reset the array in case the function is called more than once.
-    $self->{_header_images} = [];
-
-    if ( $options->{image_left} ) {
-        push @{ $self->{_header_images} }, [ $options->{image_left}, 'LH' ];
-    }
-
-    if ( $options->{image_center} ) {
-        push @{ $self->{_header_images} }, [ $options->{image_center}, 'CH' ];
-    }
-
-    if ( $options->{image_right} ) {
-        push @{ $self->{_header_images} }, [ $options->{image_right}, 'RH' ];
-    }
-
-    my $placeholder_count = () = $string =~ /&G/g;
-    my $image_count = @{ $self->{_header_images} };
-
-    if ( $image_count != $placeholder_count ) {
-        warn "Number of header images ($image_count) doesn't match placeholder "
-          . "count ($placeholder_count) in string: $string\n";
-        $self->{_header_images} = [];
-        return;
-    }
-
-    if ( $image_count ) {
-        $self->{_has_header_vml} = 1;
-    }
-
-    $self->{_header}                = $string;
-    $self->{_margin_header}         = $margin;
-    $self->{_header_footer_changed} = 1;
-}
-
-
-###############################################################################
-#
-# set_footer()
-#
-# Set the page footer caption and optional margin.
-#
-sub set_footer {
-
-    my $self    = shift;
-    my $string  = $_[0] || '';
-    my $margin  = $_[1] || 0.3;
-    my $options = $_[2] || {};
-
-
-    # Replace the Excel placeholder &[Picture] with the internal &G.
-    $string =~ s/&\[Picture\]/&G/g;
-
-    if ( length $string >= 255 ) {
-        carp 'Footer string must be less than 255 characters';
-        return;
-    }
-
-    if ( defined $options->{align_with_margins} ) {
-        $self->{_header_footer_aligns} = $options->{align_with_margins};
-    }
-
-    if ( defined $options->{scale_with_doc} ) {
-        $self->{_header_footer_scales} = $options->{scale_with_doc};
-    }
-
-    # Reset the array in case the function is called more than once.
-    $self->{_footer_images} = [];
-
-    if ( $options->{image_left} ) {
-        push @{ $self->{_footer_images} }, [ $options->{image_left}, 'LF' ];
-    }
-
-    if ( $options->{image_center} ) {
-        push @{ $self->{_footer_images} }, [ $options->{image_center}, 'CF' ];
-    }
-
-    if ( $options->{image_right} ) {
-        push @{ $self->{_footer_images} }, [ $options->{image_right}, 'RF' ];
-    }
-
-    my $placeholder_count = () = $string =~ /&G/g;
-    my $image_count = @{ $self->{_footer_images} };
-
-    if ( $image_count != $placeholder_count ) {
-        warn "Number of footer images ($image_count) doesn't match placeholder "
-          . "count ($placeholder_count) in string: $string\n";
-        $self->{_footer_images} = [];
-        return;
-    }
-
-    if ( $image_count ) {
-        $self->{_has_header_vml} = 1;
-    }
-
-    $self->{_footer}                = $string;
-    $self->{_margin_footer}         = $margin;
-    $self->{_header_footer_changed} = 1;
-}
-
-
-###############################################################################
-#
-# center_horizontally()
-#
-# Center the page horizontally.
-#
-sub center_horizontally {
-
-    my $self = shift;
-
-    $self->{_print_options_changed} = 1;
-    $self->{_hcenter}               = 1;
-}
-
-
-###############################################################################
-#
-# center_vertically()
-#
-# Center the page horizontally.
-#
-sub center_vertically {
-
-    my $self = shift;
-
-    $self->{_print_options_changed} = 1;
-    $self->{_vcenter}               = 1;
-}
-
-
-###############################################################################
-#
-# set_margins()
-#
-# Set all the page margins to the same value in inches.
-#
-sub set_margins {
-
-    my $self = shift;
-
-    $self->set_margin_left( $_[0] );
-    $self->set_margin_right( $_[0] );
-    $self->set_margin_top( $_[0] );
-    $self->set_margin_bottom( $_[0] );
-}
-
-
-###############################################################################
-#
-# set_margins_LR()
-#
-# Set the left and right margins to the same value in inches.
-#
-sub set_margins_LR {
-
-    my $self = shift;
-
-    $self->set_margin_left( $_[0] );
-    $self->set_margin_right( $_[0] );
-}
-
-
-###############################################################################
-#
-# set_margins_TB()
-#
-# Set the top and bottom margins to the same value in inches.
-#
-sub set_margins_TB {
-
-    my $self = shift;
-
-    $self->set_margin_top( $_[0] );
-    $self->set_margin_bottom( $_[0] );
-}
-
-
-###############################################################################
-#
-# set_margin_left()
-#
-# Set the left margin in inches.
-#
-sub set_margin_left {
-
-    my $self    = shift;
-    my $margin  = shift;
-    my $default = 0.7;
-
-    # Add 0 to ensure the argument is numeric.
-    if   ( defined $margin ) { $margin = 0 + $margin }
-    else                     { $margin = $default }
-
-    $self->{_margin_left} = $margin;
-}
-
-
-###############################################################################
-#
-# set_margin_right()
-#
-# Set the right margin in inches.
-#
-sub set_margin_right {
-
-    my $self    = shift;
-    my $margin  = shift;
-    my $default = 0.7;
-
-    # Add 0 to ensure the argument is numeric.
-    if   ( defined $margin ) { $margin = 0 + $margin }
-    else                     { $margin = $default }
-
-    $self->{_margin_right} = $margin;
-}
-
-
-###############################################################################
-#
-# set_margin_top()
-#
-# Set the top margin in inches.
-#
-sub set_margin_top {
-
-    my $self    = shift;
-    my $margin  = shift;
-    my $default = 0.75;
-
-    # Add 0 to ensure the argument is numeric.
-    if   ( defined $margin ) { $margin = 0 + $margin }
-    else                     { $margin = $default }
-
-    $self->{_margin_top} = $margin;
-}
-
-
-###############################################################################
-#
-# set_margin_bottom()
-#
-# Set the bottom margin in inches.
-#
-sub set_margin_bottom {
-
-
-    my $self    = shift;
-    my $margin  = shift;
-    my $default = 0.75;
-
-    # Add 0 to ensure the argument is numeric.
-    if   ( defined $margin ) { $margin = 0 + $margin }
-    else                     { $margin = $default }
-
-    $self->{_margin_bottom} = $margin;
-}
-
-
-###############################################################################
-#
-# repeat_rows($first_row, $last_row)
-#
-# Set the rows to repeat at the top of each printed page.
-#
-sub repeat_rows {
-
-    my $self = shift;
-
-    my $row_min = $_[0];
-    my $row_max = $_[1] || $_[0];    # Second row is optional
-
-
-    # Convert to 1 based.
-    $row_min++;
-    $row_max++;
-
-    my $area = '$' . $row_min . ':' . '$' . $row_max;
-
-    # Build up the print titles "Sheet1!$1:$2"
-    my $sheetname = quote_sheetname( $self->{_name} );
-    $area = $sheetname . "!" . $area;
-
-    $self->{_repeat_rows} = $area;
-}
-
-
-###############################################################################
-#
-# repeat_columns($first_col, $last_col)
-#
-# Set the columns to repeat at the left hand side of each printed page. This is
-# stored as a <NamedRange> element.
-#
-sub repeat_columns {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-
-        # Returned values $row1 and $row2 aren't required here. Remove them.
-        shift @_;    # $row1
-        splice @_, 1, 1;    # $row2
-    }
-
-    my $col_min = $_[0];
-    my $col_max = $_[1] || $_[0];    # Second col is optional
-
-    # Convert to A notation.
-    $col_min = xl_col_to_name( $_[0], 1 );
-    $col_max = xl_col_to_name( $_[1], 1 );
-
-    my $area = $col_min . ':' . $col_max;
-
-    # Build up the print area range "=Sheet2!C1:C2"
-    my $sheetname = quote_sheetname( $self->{_name} );
-    $area = $sheetname . "!" . $area;
-
-    $self->{_repeat_cols} = $area;
-}
-
-
-###############################################################################
-#
-# print_area($first_row, $first_col, $last_row, $last_col)
-#
-# Set the print area in the current worksheet. This is stored as a <NamedRange>
-# element.
-#
-sub print_area {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    return if @_ != 4;    # Require 4 parameters
-
-    my ( $row1, $col1, $row2, $col2 ) = @_;
-
-    # Ignore max print area since this is the same as no print area for Excel.
-    if (    $row1 == 0
-        and $col1 == 0
-        and $row2 == $self->{_xls_rowmax} - 1
-        and $col2 == $self->{_xls_colmax} - 1 )
-    {
-        return;
-    }
-
-    # Build up the print area range "=Sheet2!R1C1:R2C1"
-    my $area = $self->_convert_name_area( $row1, $col1, $row2, $col2 );
-
-    $self->{_print_area} = $area;
-}
-
-
-###############################################################################
-#
-# autofilter($first_row, $first_col, $last_row, $last_col)
-#
-# Set the autofilter area in the worksheet.
-#
-sub autofilter {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    return if @_ != 4;    # Require 4 parameters
-
-    my ( $row1, $col1, $row2, $col2 ) = @_;
-
-    # Reverse max and min values if necessary.
-    ( $row1, $row2 ) = ( $row2, $row1 ) if $row2 < $row1;
-    ( $col1, $col2 ) = ( $col2, $col1 ) if $col2 < $col1;
-
-    # Build up the print area range "Sheet1!$A$1:$C$13".
-    my $area = $self->_convert_name_area( $row1, $col1, $row2, $col2 );
-    my $ref = xl_range( $row1, $row2, $col1, $col2 );
-
-    $self->{_autofilter}     = $area;
-    $self->{_autofilter_ref} = $ref;
-    $self->{_filter_range}   = [ $col1, $col2 ];
-}
-
-
-###############################################################################
-#
-# filter_column($column, $criteria, ...)
-#
-# Set the column filter criteria.
-#
-sub filter_column {
-
-    my $self       = shift;
-    my $col        = $_[0];
-    my $expression = $_[1];
-
-    croak "Must call autofilter() before filter_column()"
-      unless $self->{_autofilter};
-    croak "Incorrect number of arguments to filter_column()"
-      unless @_ == 2;
-
-
-    # Check for a column reference in A1 notation and substitute.
-    if ( $col =~ /^\D/ ) {
-        my $col_letter = $col;
-
-        # Convert col ref to a cell ref and then to a col number.
-        ( undef, $col ) = $self->_substitute_cellref( $col . '1' );
-
-        croak "Invalid column '$col_letter'" if $col >= $self->{_xls_colmax};
-    }
-
-    my ( $col_first, $col_last ) = @{ $self->{_filter_range} };
-
-    # Reject column if it is outside filter range.
-    if ( $col < $col_first or $col > $col_last ) {
-        croak "Column '$col' outside autofilter() column range "
-          . "($col_first .. $col_last)";
-    }
-
-
-    my @tokens = $self->_extract_filter_tokens( $expression );
-
-    croak "Incorrect number of tokens in expression '$expression'"
-      unless ( @tokens == 3 or @tokens == 7 );
-
-
-    @tokens = $self->_parse_filter_expression( $expression, @tokens );
-
-    # Excel handles single or double custom filters as default filters. We need
-    # to check for them and handle them accordingly.
-    if ( @tokens == 2 && $tokens[0] == 2 ) {
-
-        # Single equality.
-        $self->filter_column_list( $col, $tokens[1] );
-    }
-    elsif (@tokens == 5
-        && $tokens[0] == 2
-        && $tokens[2] == 1
-        && $tokens[3] == 2 )
-    {
-
-        # Double equality with "or" operator.
-        $self->filter_column_list( $col, $tokens[1], $tokens[4] );
-    }
-    else {
-
-        # Non default custom filter.
-        $self->{_filter_cols}->{$col} = [@tokens];
-        $self->{_filter_type}->{$col} = 0;
-
-    }
-
-    $self->{_filter_on} = 1;
-}
-
-
-###############################################################################
-#
-# filter_column_list($column, @matches )
-#
-# Set the column filter criteria in Excel 2007 list style.
-#
-sub filter_column_list {
-
-    my $self   = shift;
-    my $col    = shift;
-    my @tokens = @_;
-
-    croak "Must call autofilter() before filter_column_list()"
-      unless $self->{_autofilter};
-    croak "Incorrect number of arguments to filter_column_list()"
-      unless @tokens;
-
-    # Check for a column reference in A1 notation and substitute.
-    if ( $col =~ /^\D/ ) {
-        my $col_letter = $col;
-
-        # Convert col ref to a cell ref and then to a col number.
-        ( undef, $col ) = $self->_substitute_cellref( $col . '1' );
-
-        croak "Invalid column '$col_letter'" if $col >= $self->{_xls_colmax};
-    }
-
-    my ( $col_first, $col_last ) = @{ $self->{_filter_range} };
-
-    # Reject column if it is outside filter range.
-    if ( $col < $col_first or $col > $col_last ) {
-        croak "Column '$col' outside autofilter() column range "
-          . "($col_first .. $col_last)";
-    }
-
-    $self->{_filter_cols}->{$col} = [@tokens];
-    $self->{_filter_type}->{$col} = 1;           # Default style.
-    $self->{_filter_on}           = 1;
-}
-
-
-###############################################################################
-#
-# _extract_filter_tokens($expression)
-#
-# Extract the tokens from the filter expression. The tokens are mainly non-
-# whitespace groups. The only tricky part is to extract string tokens that
-# contain whitespace and/or quoted double quotes (Excel's escaped quotes).
-#
-# Examples: 'x <  2000'
-#           'x >  2000 and x <  5000'
-#           'x = "foo"'
-#           'x = "foo bar"'
-#           'x = "foo "" bar"'
-#
-sub _extract_filter_tokens {
-
-    my $self       = shift;
-    my $expression = $_[0];
-
-    return unless $expression;
-
-    my @tokens = ( $expression =~ /"(?:[^"]|"")*"|\S+/g );    #"
-
-    # Remove leading and trailing quotes and unescape other quotes
-    for ( @tokens ) {
-        s/^"//;                                               #"
-        s/"$//;                                               #"
-        s/""/"/g;                                             #"
-    }
-
-    return @tokens;
-}
-
-
-###############################################################################
-#
-# _parse_filter_expression(@token)
-#
-# Converts the tokens of a possibly conditional expression into 1 or 2
-# sub expressions for further parsing.
-#
-# Examples:
-#          ('x', '==', 2000) -> exp1
-#          ('x', '>',  2000, 'and', 'x', '<', 5000) -> exp1 and exp2
-#
-sub _parse_filter_expression {
-
-    my $self       = shift;
-    my $expression = shift;
-    my @tokens     = @_;
-
-    # The number of tokens will be either 3 (for 1 expression)
-    # or 7 (for 2  expressions).
-    #
-    if ( @tokens == 7 ) {
-
-        my $conditional = $tokens[3];
-
-        if ( $conditional =~ /^(and|&&)$/ ) {
-            $conditional = 0;
-        }
-        elsif ( $conditional =~ /^(or|\|\|)$/ ) {
-            $conditional = 1;
-        }
-        else {
-            croak "Token '$conditional' is not a valid conditional "
-              . "in filter expression '$expression'";
-        }
-
-        my @expression_1 =
-          $self->_parse_filter_tokens( $expression, @tokens[ 0, 1, 2 ] );
-        my @expression_2 =
-          $self->_parse_filter_tokens( $expression, @tokens[ 4, 5, 6 ] );
-
-        return ( @expression_1, $conditional, @expression_2 );
-    }
-    else {
-        return $self->_parse_filter_tokens( $expression, @tokens );
-    }
-}
-
-
-###############################################################################
-#
-# _parse_filter_tokens(@token)
-#
-# Parse the 3 tokens of a filter expression and return the operator and token.
-#
-sub _parse_filter_tokens {
-
-    my $self       = shift;
-    my $expression = shift;
-    my @tokens     = @_;
-
-    my %operators = (
-        '==' => 2,
-        '='  => 2,
-        '=~' => 2,
-        'eq' => 2,
-
-        '!=' => 5,
-        '!~' => 5,
-        'ne' => 5,
-        '<>' => 5,
-
-        '<'  => 1,
-        '<=' => 3,
-        '>'  => 4,
-        '>=' => 6,
-    );
-
-    my $operator = $operators{ $tokens[1] };
-    my $token    = $tokens[2];
-
-
-    # Special handling of "Top" filter expressions.
-    if ( $tokens[0] =~ /^top|bottom$/i ) {
-
-        my $value = $tokens[1];
-
-        if (   $value =~ /\D/
-            or $value < 1
-            or $value > 500 )
-        {
-            croak "The value '$value' in expression '$expression' "
-              . "must be in the range 1 to 500";
-        }
-
-        $token = lc $token;
-
-        if ( $token ne 'items' and $token ne '%' ) {
-            croak "The type '$token' in expression '$expression' "
-              . "must be either 'items' or '%'";
-        }
-
-        if ( $tokens[0] =~ /^top$/i ) {
-            $operator = 30;
-        }
-        else {
-            $operator = 32;
-        }
-
-        if ( $tokens[2] eq '%' ) {
-            $operator++;
-        }
-
-        $token = $value;
-    }
-
-
-    if ( not $operator and $tokens[0] ) {
-        croak "Token '$tokens[1]' is not a valid operator "
-          . "in filter expression '$expression'";
-    }
-
-
-    # Special handling for Blanks/NonBlanks.
-    if ( $token =~ /^blanks|nonblanks$/i ) {
-
-        # Only allow Equals or NotEqual in this context.
-        if ( $operator != 2 and $operator != 5 ) {
-            croak "The operator '$tokens[1]' in expression '$expression' "
-              . "is not valid in relation to Blanks/NonBlanks'";
-        }
-
-        $token = lc $token;
-
-        # The operator should always be 2 (=) to flag a "simple" equality in
-        # the binary record. Therefore we convert <> to =.
-        if ( $token eq 'blanks' ) {
-            if ( $operator == 5 ) {
-                $token = ' ';
-            }
-        }
-        else {
-            if ( $operator == 5 ) {
-                $operator = 2;
-                $token    = 'blanks';
-            }
-            else {
-                $operator = 5;
-                $token    = ' ';
-            }
-        }
-    }
-
-
-    # if the string token contains an Excel match character then change the
-    # operator type to indicate a non "simple" equality.
-    if ( $operator == 2 and $token =~ /[*?]/ ) {
-        $operator = 22;
-    }
-
-
-    return ( $operator, $token );
-}
-
-
-###############################################################################
-#
-# _convert_name_area($first_row, $first_col, $last_row, $last_col)
-#
-# Convert zero indexed rows and columns to the format required by worksheet
-# named ranges, eg, "Sheet1!$A$1:$C$13".
-#
-sub _convert_name_area {
-
-    my $self = shift;
-
-    my $row_num_1 = $_[0];
-    my $col_num_1 = $_[1];
-    my $row_num_2 = $_[2];
-    my $col_num_2 = $_[3];
-
-    my $range1       = '';
-    my $range2       = '';
-    my $row_col_only = 0;
-    my $area;
-
-    # Convert to A1 notation.
-    my $col_char_1 = xl_col_to_name( $col_num_1, 1 );
-    my $col_char_2 = xl_col_to_name( $col_num_2, 1 );
-    my $row_char_1 = '$' . ( $row_num_1 + 1 );
-    my $row_char_2 = '$' . ( $row_num_2 + 1 );
-
-    # We need to handle some special cases that refer to rows or columns only.
-    if ( $row_num_1 == 0 and $row_num_2 == $self->{_xls_rowmax} - 1 ) {
-        $range1       = $col_char_1;
-        $range2       = $col_char_2;
-        $row_col_only = 1;
-    }
-    elsif ( $col_num_1 == 0 and $col_num_2 == $self->{_xls_colmax} - 1 ) {
-        $range1       = $row_char_1;
-        $range2       = $row_char_2;
-        $row_col_only = 1;
-    }
-    else {
-        $range1 = $col_char_1 . $row_char_1;
-        $range2 = $col_char_2 . $row_char_2;
-    }
-
-    # A repeated range is only written once (if it isn't a special case).
-    if ( $range1 eq $range2 && !$row_col_only ) {
-        $area = $range1;
-    }
-    else {
-        $area = $range1 . ':' . $range2;
-    }
-
-    # Build up the print area range "Sheet1!$A$1:$C$13".
-    my $sheetname = quote_sheetname( $self->{_name} );
-    $area = $sheetname . "!" . $area;
-
-    return $area;
-}
-
-
-###############################################################################
-#
-# hide_gridlines()
-#
-# Set the option to hide gridlines on the screen and the printed page.
-#
-# This was mainly useful for Excel 5 where printed gridlines were on by
-# default.
-#
-sub hide_gridlines {
-
-    my $self = shift;
-    my $option =
-      defined $_[0] ? $_[0] : 1;    # Default to hiding printed gridlines
-
-    if ( $option == 0 ) {
-        $self->{_print_gridlines}       = 1;    # 1 = display, 0 = hide
-        $self->{_screen_gridlines}      = 1;
-        $self->{_print_options_changed} = 1;
-    }
-    elsif ( $option == 1 ) {
-        $self->{_print_gridlines}  = 0;
-        $self->{_screen_gridlines} = 1;
-    }
-    else {
-        $self->{_print_gridlines}  = 0;
-        $self->{_screen_gridlines} = 0;
-    }
-}
-
-
-###############################################################################
-#
-# print_row_col_headers()
-#
-# Set the option to print the row and column headers on the printed page.
-# See also the _store_print_headers() method below.
-#
-sub print_row_col_headers {
-
-    my $self = shift;
-    my $headers = defined $_[0] ? $_[0] : 1;
-
-    if ( $headers ) {
-        $self->{_print_headers}         = 1;
-        $self->{_print_options_changed} = 1;
-    }
-    else {
-        $self->{_print_headers} = 0;
-    }
-}
-
-
-###############################################################################
-#
-# fit_to_pages($width, $height)
-#
-# Store the vertical and horizontal number of pages that will define the
-# maximum area printed.
-#
-sub fit_to_pages {
-
-    my $self = shift;
-
-    $self->{_fit_page}           = 1;
-    $self->{_fit_width}          = defined $_[0] ? $_[0] : 1;
-    $self->{_fit_height}         = defined $_[1] ? $_[1] : 1;
-    $self->{_page_setup_changed} = 1;
-}
-
-
-###############################################################################
-#
-# set_h_pagebreaks(@breaks)
-#
-# Store the horizontal page breaks on a worksheet.
-#
-sub set_h_pagebreaks {
-
-    my $self = shift;
-
-    push @{ $self->{_hbreaks} }, @_;
-}
-
-
-###############################################################################
-#
-# set_v_pagebreaks(@breaks)
-#
-# Store the vertical page breaks on a worksheet.
-#
-sub set_v_pagebreaks {
-
-    my $self = shift;
-
-    push @{ $self->{_vbreaks} }, @_;
-}
-
-
-###############################################################################
-#
-# set_zoom( $scale )
-#
-# Set the worksheet zoom factor.
-#
-sub set_zoom {
-
-    my $self = shift;
-    my $scale = $_[0] || 100;
-
-    # Confine the scale to Excel's range
-    if ( $scale < 10 or $scale > 400 ) {
-        carp "Zoom factor $scale outside range: 10 <= zoom <= 400";
-        $scale = 100;
-    }
-
-    $self->{_zoom} = int $scale;
-}
-
-
-###############################################################################
-#
-# set_print_scale($scale)
-#
-# Set the scale factor for the printed page.
-#
-sub set_print_scale {
-
-    my $self = shift;
-    my $scale = $_[0] || 100;
-
-    # Confine the scale to Excel's range
-    if ( $scale < 10 or $scale > 400 ) {
-        carp "Print scale $scale outside range: 10 <= zoom <= 400";
-        $scale = 100;
-    }
-
-    # Turn off "fit to page" option.
-    $self->{_fit_page} = 0;
-
-    $self->{_print_scale}        = int $scale;
-    $self->{_page_setup_changed} = 1;
-}
-
-
-###############################################################################
-#
-# print_black_and_white()
-#
-# Set the option to print the worksheet in black and white.
-#
-sub print_black_and_white {
-
-    my $self = shift;
-
-    $self->{_black_white} = 1;
-}
-
-
-###############################################################################
-#
-# keep_leading_zeros()
-#
-# Causes the write() method to treat integers with a leading zero as a string.
-# This ensures that any leading zeros such, as in zip codes, are maintained.
-#
-sub keep_leading_zeros {
-
-    my $self = shift;
-
-    if ( defined $_[0] ) {
-        $self->{_leading_zeros} = $_[0];
-    }
-    else {
-        $self->{_leading_zeros} = 1;
-    }
-}
-
-
-###############################################################################
-#
-# show_comments()
-#
-# Make any comments in the worksheet visible.
-#
-sub show_comments {
-
-    my $self = shift;
-
-    $self->{_comments_visible} = defined $_[0] ? $_[0] : 1;
-}
-
-
-###############################################################################
-#
-# set_comments_author()
-#
-# Set the default author of the cell comments.
-#
-sub set_comments_author {
-
-    my $self = shift;
-
-    $self->{_comments_author} = $_[0] if defined $_[0];
-}
-
-
-###############################################################################
-#
-# right_to_left()
-#
-# Display the worksheet right to left for some eastern versions of Excel.
-#
-sub right_to_left {
-
-    my $self = shift;
-
-    $self->{_right_to_left} = defined $_[0] ? $_[0] : 1;
-}
-
-
-###############################################################################
-#
-# hide_zero()
-#
-# Hide cell zero values.
-#
-sub hide_zero {
-
-    my $self = shift;
-
-    $self->{_show_zeros} = defined $_[0] ? not $_[0] : 0;
-}
-
-
-###############################################################################
-#
-# print_across()
-#
-# Set the order in which pages are printed.
-#
-sub print_across {
-
-    my $self = shift;
-    my $page_order = defined $_[0] ? $_[0] : 1;
-
-    if ( $page_order ) {
-        $self->{_page_order}         = 1;
-        $self->{_page_setup_changed} = 1;
-    }
-    else {
-        $self->{_page_order} = 0;
-    }
-}
-
-
-###############################################################################
-#
-# set_start_page()
-#
-# Set the start page number.
-#
-sub set_start_page {
-
-    my $self = shift;
-    return unless defined $_[0];
-
-    $self->{_page_start}   = $_[0];
-}
-
-
-###############################################################################
-#
-# set_first_row_column()
-#
-# Set the topmost and leftmost visible row and column.
-# TODO: Document this when tested fully for interaction with panes.
-#
-sub set_first_row_column {
-
-    my $self = shift;
-
-    my $row = $_[0] || 0;
-    my $col = $_[1] || 0;
-
-    $row = $self->{_xls_rowmax} if $row > $self->{_xls_rowmax};
-    $col = $self->{_xls_colmax} if $col > $self->{_xls_colmax};
-
-    $self->{_first_row} = $row;
-    $self->{_first_col} = $col;
-}
-
-
-###############################################################################
-#
-# add_write_handler($re, $code_ref)
-#
-# Allow the user to add their own matches and handlers to the write() method.
-#
-sub add_write_handler {
-
-    my $self = shift;
-
-    return unless @_ == 2;
-    return unless ref $_[1] eq 'CODE';
-
-    push @{ $self->{_write_match} }, [@_];
-}
-
-
-###############################################################################
-#
-# write($row, $col, $token, $format)
-#
-# Parse $token and call appropriate write method. $row and $column are zero
-# indexed. $format is optional.
-#
-# Returns: return value of called subroutine
-#
-sub write {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    my $token = $_[2];
-
-    # Handle undefs as blanks
-    $token = '' unless defined $token;
-
-
-    # First try user defined matches.
-    for my $aref ( @{ $self->{_write_match} } ) {
-        my $re  = $aref->[0];
-        my $sub = $aref->[1];
-
-        if ( $token =~ /$re/ ) {
-            my $match = &$sub( $self, @_ );
-            return $match if defined $match;
-        }
-    }
-
-
-    # Match an array ref.
-    if ( ref $token eq "ARRAY" ) {
-        return $self->write_row( @_ );
-    }
-
-    # Match integer with leading zero(s)
-    elsif ( $self->{_leading_zeros} and $token =~ /^0\d+$/ ) {
-        return $self->write_string( @_ );
-    }
-
-    # Match number
-    elsif ( $token =~ /^([+-]?)(?=[0-9]|\.[0-9])[0-9]*(\.[0-9]*)?([Ee]([+-]?[0-9]+))?$/ ) {
-        return $self->write_number( @_ );
-    }
-
-    # Match http, https or ftp URL
-    elsif ( $token =~ m|^[fh]tt?ps?://| ) {
-        return $self->write_url( @_ );
-    }
-
-    # Match mailto:
-    elsif ( $token =~ m/^mailto:/ ) {
-        return $self->write_url( @_ );
-    }
-
-    # Match internal or external sheet link
-    elsif ( $token =~ m[^(?:in|ex)ternal:] ) {
-        return $self->write_url( @_ );
-    }
-
-    # Match formula
-    elsif ( $token =~ /^=/ ) {
-        return $self->write_formula( @_ );
-    }
-
-    # Match array formula
-    elsif ( $token =~ /^{=.*}$/ ) {
-        return $self->write_formula( @_ );
-    }
-
-    # Match blank
-    elsif ( $token eq '' ) {
-        splice @_, 2, 1;    # remove the empty string from the parameter list
-        return $self->write_blank( @_ );
-    }
-
-    # Default: match string
-    else {
-        return $self->write_string( @_ );
-    }
-}
-
-
-###############################################################################
-#
-# write_row($row, $col, $array_ref, $format)
-#
-# Write a row of data starting from ($row, $col). Call write_col() if any of
-# the elements of the array ref are in turn array refs. This allows the writing
-# of 1D or 2D arrays of data in one go.
-#
-# Returns: the first encountered error value or zero for no errors
-#
-sub write_row {
-
-    my $self = shift;
-
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    # Catch non array refs passed by user.
-    if ( ref $_[2] ne 'ARRAY' ) {
-        croak "Not an array ref in call to write_row()$!";
-    }
-
-    my $row     = shift;
-    my $col     = shift;
-    my $tokens  = shift;
-    my @options = @_;
-    my $error   = 0;
-    my $ret;
-
-    for my $token ( @$tokens ) {
-
-        # Check for nested arrays
-        if ( ref $token eq "ARRAY" ) {
-            $ret = $self->write_col( $row, $col, $token, @options );
-        }
-        else {
-            $ret = $self->write( $row, $col, $token, @options );
-        }
-
-        # Return only the first error encountered, if any.
-        $error ||= $ret;
-        $col++;
-    }
-
-    return $error;
-}
-
-
-###############################################################################
-#
-# write_col($row, $col, $array_ref, $format)
-#
-# Write a column of data starting from ($row, $col). Call write_row() if any of
-# the elements of the array ref are in turn array refs. This allows the writing
-# of 1D or 2D arrays of data in one go.
-#
-# Returns: the first encountered error value or zero for no errors
-#
-sub write_col {
-
-    my $self = shift;
-
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    # Catch non array refs passed by user.
-    if ( ref $_[2] ne 'ARRAY' ) {
-        croak "Not an array ref in call to write_col()$!";
-    }
-
-    my $row     = shift;
-    my $col     = shift;
-    my $tokens  = shift;
-    my @options = @_;
-    my $error   = 0;
-    my $ret;
-
-    for my $token ( @$tokens ) {
-
-        # write() will deal with any nested arrays
-        $ret = $self->write( $row, $col, $token, @options );
-
-        # Return only the first error encountered, if any.
-        $error ||= $ret;
-        $row++;
-    }
-
-    return $error;
-}
-
-
-###############################################################################
-#
-# write_comment($row, $col, $comment)
-#
-# Write a comment to the specified row and column (zero indexed).
-#
-# Returns  0 : normal termination
-#         -1 : insufficient number of arguments
-#         -2 : row or column out of range
-#
-sub write_comment {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    if ( @_ < 3 ) { return -1 }    # Check the number of args
-
-    my $row = $_[0];
-    my $col = $_[1];
-
-    # Check for pairs of optional arguments, i.e. an odd number of args.
-    croak "Uneven number of additional arguments" unless @_ % 2;
-
-    # Check that row and col are valid and store max and min values
-    return -2 if $self->_check_dimensions( $row, $col );
-
-    $self->{_has_vml}      = 1;
-    $self->{_has_comments} = 1;
-
-    # Process the properties of the cell comment.
-    $self->{_comments}->{$row}->{$col} = [ $self->_comment_params( @_ ) ];
-}
-
-
-###############################################################################
-#
-# write_number($row, $col, $num, $format)
-#
-# Write a double to the specified row and column (zero indexed).
-# An integer can be written as a double. Excel will display an
-# integer. $format is optional.
-#
-# Returns  0 : normal termination
-#         -1 : insufficient number of arguments
-#         -2 : row or column out of range
-#
-sub write_number {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    if ( @_ < 3 ) { return -1 }    # Check the number of args
-
-
-    my $row  = $_[0];              # Zero indexed row
-    my $col  = $_[1];              # Zero indexed column
-    my $num  = $_[2] + 0;
-    my $xf   = $_[3];              # The cell format
-    my $type = 'n';                # The data type
-
-    # Check that row and col are valid and store max and min values
-    return -2 if $self->_check_dimensions( $row, $col );
-
-    # Write previous row if in in-line string optimization mode.
-    if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) {
-        $self->_write_single_row( $row );
-    }
-
-    $self->{_table}->{$row}->{$col} = [ $type, $num, $xf ];
-
-    return 0;
-}
-
-
-###############################################################################
-#
-# write_string ($row, $col, $string, $format)
-#
-# Write a string to the specified row and column (zero indexed).
-# $format is optional.
-# Returns  0 : normal termination
-#         -1 : insufficient number of arguments
-#         -2 : row or column out of range
-#         -3 : long string truncated to 32767 chars
-#
-sub write_string {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    if ( @_ < 3 ) { return -1 }    # Check the number of args
-
-    my $row  = $_[0];              # Zero indexed row
-    my $col  = $_[1];              # Zero indexed column
-    my $str  = $_[2];
-    my $xf   = $_[3];              # The cell format
-    my $type = 's';                # The data type
-    my $index;
-    my $str_error = 0;
-
-    # Check that row and col are valid and store max and min values
-    return -2 if $self->_check_dimensions( $row, $col );
-
-    # Check that the string is < 32767 chars
-    if ( length $str > $self->{_xls_strmax} ) {
-        $str = substr( $str, 0, $self->{_xls_strmax} );
-        $str_error = -3;
-    }
-
-    # Write a shared string or an in-line string based on optimisation level.
-    if ( $self->{_optimization} == 0 ) {
-        $index = $self->_get_shared_string_index( $str );
-    }
-    else {
-        $index = $str;
-    }
-
-    # Write previous row if in in-line string optimization mode.
-    if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) {
-        $self->_write_single_row( $row );
-    }
-
-    $self->{_table}->{$row}->{$col} = [ $type, $index, $xf ];
-
-    return $str_error;
-}
-
-
-###############################################################################
-#
-# write_rich_string( $row, $column, $format, $string, ..., $cell_format )
-#
-# The write_rich_string() method is used to write strings with multiple formats.
-# The method receives string fragments prefixed by format objects. The final
-# format object is used as the cell format.
-#
-# Returns  0 : normal termination.
-#         -1 : insufficient number of arguments.
-#         -2 : row or column out of range.
-#         -3 : long string truncated to 32767 chars.
-#         -4 : 2 consecutive formats used.
-#
-sub write_rich_string {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    if ( @_ < 3 ) { return -1 }    # Check the number of args
-
-    my $row    = shift;            # Zero indexed row.
-    my $col    = shift;            # Zero indexed column.
-    my $str    = '';
-    my $xf     = undef;
-    my $type   = 's';              # The data type.
-    my $length = 0;                # String length.
-    my $index;
-    my $str_error = 0;
-
-    # Check that row and col are valid and store max and min values
-    return -2 if $self->_check_dimensions( $row, $col );
-
-
-    # If the last arg is a format we use it as the cell format.
-    if ( ref $_[-1] ) {
-        $xf = pop @_;
-    }
-
-
-    # Create a temp XML::Writer object and use it to write the rich string
-    # XML to a string.
-    open my $str_fh, '>', \$str or die "Failed to open filehandle: $!";
-    binmode $str_fh, ':utf8';
-
-    my $writer = Excel::Writer::XLSX::Package::XMLwriter->new( $str_fh );
-
-    $self->{_rstring} = $writer;
-
-    # Create a temp format with the default font for unformatted fragments.
-    my $default = Excel::Writer::XLSX::Format->new();
-
-    # Convert the list of $format, $string tokens to pairs of ($format, $string)
-    # except for the first $string fragment which doesn't require a default
-    # formatting run. Use the default for strings without a leading format.
-    my @fragments;
-    my $last = 'format';
-    my $pos  = 0;
-
-    for my $token ( @_ ) {
-        if ( !ref $token ) {
-
-            # Token is a string.
-            if ( $last ne 'format' ) {
-
-                # If previous token wasn't a format add one before the string.
-                push @fragments, ( $default, $token );
-            }
-            else {
-
-                # If previous token was a format just add the string.
-                push @fragments, $token;
-            }
-
-            $length += length $token;    # Keep track of actual string length.
-            $last = 'string';
-        }
-        else {
-
-            # Can't allow 2 formats in a row.
-            if ( $last eq 'format' && $pos > 0 ) {
-                return -4;
-            }
-
-            # Token is a format object. Add it to the fragment list.
-            push @fragments, $token;
-            $last = 'format';
-        }
-
-        $pos++;
-    }
-
-
-    # If the first token is a string start the <r> element.
-    if ( !ref $fragments[0] ) {
-        $self->{_rstring}->xml_start_tag( 'r' );
-    }
-
-    # Write the XML elements for the $format $string fragments.
-    for my $token ( @fragments ) {
-        if ( ref $token ) {
-
-            # Write the font run.
-            $self->{_rstring}->xml_start_tag( 'r' );
-            $self->_write_font( $token );
-        }
-        else {
-
-            # Write the string fragment part, with whitespace handling.
-            my @attributes = ();
-
-            if ( $token =~ /^\s/ || $token =~ /\s$/ ) {
-                push @attributes, ( 'xml:space' => 'preserve' );
-            }
-
-            $self->{_rstring}->xml_data_element( 't', $token, @attributes );
-            $self->{_rstring}->xml_end_tag( 'r' );
-        }
-    }
-
-    # Check that the string is < 32767 chars.
-    if ( $length > $self->{_xls_strmax} ) {
-        return -3;
-    }
-
-
-    # Write a shared string or an in-line string based on optimisation level.
-    if ( $self->{_optimization} == 0 ) {
-        $index = $self->_get_shared_string_index( $str );
-    }
-    else {
-        $index = $str;
-    }
-
-    # Write previous row if in in-line string optimization mode.
-    if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) {
-        $self->_write_single_row( $row );
-    }
-
-    $self->{_table}->{$row}->{$col} = [ $type, $index, $xf ];
-
-    return 0;
-}
-
-
-###############################################################################
-#
-# write_blank($row, $col, $format)
-#
-# Write a blank cell to the specified row and column (zero indexed).
-# A blank cell is used to specify formatting without adding a string
-# or a number.
-#
-# A blank cell without a format serves no purpose. Therefore, we don't write
-# a BLANK record unless a format is specified. This is mainly an optimisation
-# for the write_row() and write_col() methods.
-#
-# Returns  0 : normal termination (including no format)
-#         -1 : insufficient number of arguments
-#         -2 : row or column out of range
-#
-sub write_blank {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    # Check the number of args
-    return -1 if @_ < 2;
-
-    # Don't write a blank cell unless it has a format
-    return 0 if not defined $_[2];
-
-    my $row  = $_[0];    # Zero indexed row
-    my $col  = $_[1];    # Zero indexed column
-    my $xf   = $_[2];    # The cell format
-    my $type = 'b';      # The data type
-
-    # Check that row and col are valid and store max and min values
-    return -2 if $self->_check_dimensions( $row, $col );
-
-    # Write previous row if in in-line string optimization mode.
-    if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) {
-        $self->_write_single_row( $row );
-    }
-
-    $self->{_table}->{$row}->{$col} = [ $type, undef, $xf ];
-
-    return 0;
-}
-
-
-###############################################################################
-#
-# write_formula($row, $col, $formula, $format)
-#
-# Write a formula to the specified row and column (zero indexed).
-#
-# $format is optional.
-#
-# Returns  0 : normal termination
-#         -1 : insufficient number of arguments
-#         -2 : row or column out of range
-#
-sub write_formula {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    if ( @_ < 3 ) { return -1 }    # Check the number of args
-
-    my $row     = $_[0];           # Zero indexed row
-    my $col     = $_[1];           # Zero indexed column
-    my $formula = $_[2];           # The formula text string
-    my $xf      = $_[3];           # The format object.
-    my $value   = $_[4];           # Optional formula value.
-    my $type    = 'f';             # The data type
-
-    # Hand off array formulas.
-    if ( $formula =~ /^{=.*}$/ ) {
-        return $self->write_array_formula( $row, $col, $row, $col, $formula,
-            $xf, $value );
-    }
-
-    # Check that row and col are valid and store max and min values
-    return -2 if $self->_check_dimensions( $row, $col );
-
-    # Remove the = sign if it exists.
-    $formula =~ s/^=//;
-
-    # Write previous row if in in-line string optimization mode.
-    if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) {
-        $self->_write_single_row( $row );
-    }
-
-    $self->{_table}->{$row}->{$col} = [ $type, $formula, $xf, $value ];
-
-    return 0;
-}
-
-
-###############################################################################
-#
-# write_array_formula($row1, $col1, $row2, $col2, $formula, $format)
-#
-# Write an array formula to the specified row and column (zero indexed).
-#
-# $format is optional.
-#
-# Returns  0 : normal termination
-#         -1 : insufficient number of arguments
-#         -2 : row or column out of range
-#
-sub write_array_formula {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    if ( @_ < 5 ) { return -1 }    # Check the number of args
-
-    my $row1    = $_[0];           # First row
-    my $col1    = $_[1];           # First column
-    my $row2    = $_[2];           # Last row
-    my $col2    = $_[3];           # Last column
-    my $formula = $_[4];           # The formula text string
-    my $xf      = $_[5];           # The format object.
-    my $value   = $_[6];           # Optional formula value.
-    my $type    = 'a';             # The data type
-
-    # Swap last row/col with first row/col as necessary
-    ( $row1, $row2 ) = ( $row2, $row1 ) if $row1 > $row2;
-    ( $col1, $col2 ) = ( $col1, $col2 ) if $col1 > $col2;
-
-
-    # Check that row and col are valid and store max and min values
-    return -2 if $self->_check_dimensions( $row2, $col2 );
-
-
-    # Define array range
-    my $range;
-
-    if ( $row1 == $row2 and $col1 == $col2 ) {
-        $range = xl_rowcol_to_cell( $row1, $col1 );
-
-    }
-    else {
-        $range =
-            xl_rowcol_to_cell( $row1, $col1 ) . ':'
-          . xl_rowcol_to_cell( $row2, $col2 );
-    }
-
-    # Remove array formula braces and the leading =.
-    $formula =~ s/^{(.*)}$/$1/;
-    $formula =~ s/^=//;
-
-    # Write previous row if in in-line string optimization mode.
-    my $row = $row1;
-    if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) {
-        $self->_write_single_row( $row );
-    }
-
-    $self->{_table}->{$row1}->{$col1} =
-      [ $type, $formula, $xf, $range, $value ];
-
-
-    # Pad out the rest of the area with formatted zeroes.
-    if ( !$self->{_optimization} ) {
-        for my $row ( $row1 .. $row2 ) {
-            for my $col ( $col1 .. $col2 ) {
-                next if $row == $row1 and $col == $col1;
-                $self->write_number( $row, $col, 0, $xf );
-            }
-        }
-    }
-
-    return 0;
-}
-
-
-###############################################################################
-#
-# write_blank($row, $col, $format)
-#
-# Write a boolean value to the specified row and column (zero indexed).
-#
-# Returns  0 : normal termination (including no format)
-#         -2 : row or column out of range
-#
-sub write_boolean {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    my $row  = $_[0];            # Zero indexed row
-    my $col  = $_[1];            # Zero indexed column
-    my $val  = $_[2] ? 1 : 0;    # Boolean value.
-    my $xf   = $_[3];            # The cell format
-    my $type = 'l';              # The data type
-
-    # Check that row and col are valid and store max and min values
-    return -2 if $self->_check_dimensions( $row, $col );
-
-    # Write previous row if in in-line string optimization mode.
-    if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) {
-        $self->_write_single_row( $row );
-    }
-
-    $self->{_table}->{$row}->{$col} = [ $type, $val, $xf ];
-
-    return 0;
-}
-
-
-###############################################################################
-#
-# outline_settings($visible, $symbols_below, $symbols_right, $auto_style)
-#
-# This method sets the properties for outlining and grouping. The defaults
-# correspond to Excel's defaults.
-#
-sub outline_settings {
-
-    my $self = shift;
-
-    $self->{_outline_on}    = defined $_[0] ? $_[0] : 1;
-    $self->{_outline_below} = defined $_[1] ? $_[1] : 1;
-    $self->{_outline_right} = defined $_[2] ? $_[2] : 1;
-    $self->{_outline_style} = $_[3] || 0;
-
-    $self->{_outline_changed} = 1;
-}
-
-
-###############################################################################
-#
-# Escape urls like Excel.
-#
-sub _escape_url {
-
-    my $url = shift;
-
-    # Don't escape URL if it looks already escaped.
-    return $url if $url =~ /%[0-9a-fA-F]{2}/;
-
-    # Escape the URL escape symbol.
-    $url =~ s/%/%25/g;
-
-    # Escape whitespace in URL.
-    $url =~ s/[\s\x00]/%20/g;
-
-    # Escape other special characters in URL.
-    $url =~ s/(["<>[\]`^{}])/sprintf '%%%x', ord $1/eg;
-
-    return $url;
-}
-
-
-###############################################################################
-#
-# write_url($row, $col, $url, format, $string)
-#
-# Write a hyperlink. This is comprised of two elements: the visible label and
-# the invisible link. The visible label is the same as the link unless an
-# alternative string is specified. The label is written using the
-# write_string() method. Therefore the max characters string limit applies.
-# $string and $format are optional and their order is interchangeable.
-#
-# The hyperlink can be to a http, ftp, mail, internal sheet, or external
-# directory url.
-#
-# Returns  0 : normal termination
-#         -1 : insufficient number of arguments
-#         -2 : row or column out of range
-#         -3 : long string truncated to 32767 chars
-#         -4 : URL longer than 255 characters
-#         -5 : Exceeds limit of 65_530 urls per worksheet
-#
-sub write_url {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    if ( @_ < 3 ) { return -1 }    # Check the number of args
-
-
-    # Reverse the order of $string and $format if necessary, for backward
-    # compatibility. We work on a copy in order to protect the callers
-    # args. We don't use "local @_" in case of perl50005 threads.
-    my @args = @_;
-    if (defined $args[3] and !ref $args[3]) {
-        ( $args[3], $args[4] ) = ( $args[4], $args[3] );
-    }
-
-    my $row       = $args[0];    # Zero indexed row
-    my $col       = $args[1];    # Zero indexed column
-    my $url       = $args[2];    # URL string
-    my $xf        = $args[3];    # Cell format
-    my $str       = $args[4];    # Alternative label
-    my $tip       = $args[5];    # Tool tip
-    my $type      = 'l';         # XML data type
-    my $link_type = 1;
-    my $external  = 0;
-
-    # The displayed string defaults to the url string.
-    $str = $url unless defined $str;
-
-    # Remove the URI scheme from internal links.
-    if ( $url =~ s/^internal:// ) {
-        $str =~ s/^internal://;
-        $link_type = 2;
-    }
-
-    # Remove the URI scheme from external links and change the directory
-    # separator from Unix to Dos.
-    if ( $url =~ s/^external:// ) {
-        $str =~ s/^external://;
-        $url =~ s[/][\\]g;
-        $str =~ s[/][\\]g;
-        $external = 1;
-    }
-
-    # Strip the mailto header.
-    $str =~ s/^mailto://;
-
-    # Check that row and col are valid and store max and min values
-    return -2 if $self->_check_dimensions( $row, $col );
-
-    # Check that the string is < 32767 chars
-    my $str_error = 0;
-    if ( length $str > $self->{_xls_strmax} ) {
-        $str = substr( $str, 0, $self->{_xls_strmax} );
-        $str_error = -3;
-    }
-
-    # Copy string for use in hyperlink elements.
-    my $url_str = $str;
-
-    # External links to URLs and to other Excel workbooks have slightly
-    # different characteristics that we have to account for.
-    if ( $link_type == 1 ) {
-
-        # Split url into the link and optional anchor/location.
-        ( $url, $url_str ) = split /#/, $url, 2;
-
-        $url = _escape_url( $url );
-
-        # Escape the anchor for hyperlink style urls only.
-        if ( $url_str && !$external ) {
-            $url_str = _escape_url( $url_str );
-        }
-
-        # Add the file:/// URI to the url for Windows style "C:/" link and
-        # Network shares.
-        if ( $url =~ m{^\w:} || $url =~ m{^\\\\} ) {
-            $url = 'file:///' . $url;
-        }
-
-        # Convert a ./dir/file.xlsx link to dir/file.xlsx.
-        $url =~ s{^.\\}{};
-    }
-
-    # Excel limits the escaped URL and location/anchor to 255 characters.
-    my $tmp_url_str = $url_str || '';
-
-    if ( length $url > 255 || length $tmp_url_str > 255 ) {
-        carp "Ignoring URL '$url' where link or anchor > 255 characters "
-          . "since it exceeds Excel's limit for URLS. See LIMITATIONS "
-          . "section of the Excel::Writer::XLSX documentation.";
-        return -4;
-    }
-
-    # Check the limit of URLS per worksheet.
-    $self->{_hlink_count}++;
-
-    if ( $self->{_hlink_count} > 65_530 ) {
-        carp "Ignoring URL '$url' since it exceeds Excel's limit of 65,530 "
-          . "URLS per worksheet. See LIMITATIONS section of the "
-          . "Excel::Writer::XLSX documentation.";
-        return -5;
-    }
-
-    # Write previous row if in in-line string optimization mode.
-    if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) {
-        $self->_write_single_row( $row );
-    }
-
-    # Add the default URL format.
-    if ( !defined $xf ) {
-        $xf = $self->{_default_url_format};
-    }
-
-    # Write the hyperlink string.
-    $self->write_string( $row, $col, $str, $xf );
-
-    # Store the hyperlink data in a separate structure.
-    $self->{_hyperlinks}->{$row}->{$col} = {
-        _link_type => $link_type,
-        _url       => $url,
-        _str       => $url_str,
-        _tip       => $tip
-    };
-
-    return $str_error;
-}
-
-
-###############################################################################
-#
-# write_date_time ($row, $col, $string, $format)
-#
-# Write a datetime string in ISO8601 "yyyy-mm-ddThh:mm:ss.ss" format as a
-# number representing an Excel date. $format is optional.
-#
-# Returns  0 : normal termination
-#         -1 : insufficient number of arguments
-#         -2 : row or column out of range
-#         -3 : Invalid date_time, written as string
-#
-sub write_date_time {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    if ( @_ < 3 ) { return -1 }    # Check the number of args
-
-    my $row  = $_[0];              # Zero indexed row
-    my $col  = $_[1];              # Zero indexed column
-    my $str  = $_[2];
-    my $xf   = $_[3];              # The cell format
-    my $type = 'n';                # The data type
-
-
-    # Check that row and col are valid and store max and min values
-    return -2 if $self->_check_dimensions( $row, $col );
-
-    my $str_error = 0;
-    my $date_time = $self->convert_date_time( $str );
-
-    # If the date isn't valid then write it as a string.
-    if ( !defined $date_time ) {
-        return $self->write_string( @_ );
-    }
-
-    # Write previous row if in in-line string optimization mode.
-    if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) {
-        $self->_write_single_row( $row );
-    }
-
-    $self->{_table}->{$row}->{$col} = [ $type, $date_time, $xf ];
-
-    return $str_error;
-}
-
-
-###############################################################################
-#
-# convert_date_time($date_time_string)
-#
-# The function takes a date and time in ISO8601 "yyyy-mm-ddThh:mm:ss.ss" format
-# and converts it to a decimal number representing a valid Excel date.
-#
-# Dates and times in Excel are represented by real numbers. 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.
-#
-# Parameter: Date and time string in one of the following formats:
-#               yyyy-mm-ddThh:mm:ss.ss  # Standard
-#               yyyy-mm-ddT             # Date only
-#                         Thh:mm:ss.ss  # Time only
-#
-# Returns:
-#            A decimal number representing a valid Excel date, or
-#            undef if the date is invalid.
-#
-sub convert_date_time {
-
-    my $self      = shift;
-    my $date_time = $_[0];
-
-    my $days    = 0;    # Number of days since epoch
-    my $seconds = 0;    # Time expressed as fraction of 24h hours in seconds
-
-    my ( $year, $month, $day );
-    my ( $hour, $min,   $sec );
-
-
-    # Strip leading and trailing whitespace.
-    $date_time =~ s/^\s+//;
-    $date_time =~ s/\s+$//;
-
-    # Check for invalid date char.
-    return if $date_time =~ /[^0-9T:\-\.Z]/;
-
-    # Check for "T" after date or before time.
-    return unless $date_time =~ /\dT|T\d/;
-
-    # Strip trailing Z in ISO8601 date.
-    $date_time =~ s/Z$//;
-
-
-    # Split into date and time.
-    my ( $date, $time ) = split /T/, $date_time;
-
-
-    # We allow the time portion of the input DateTime to be optional.
-    if ( $time ne '' ) {
-
-        # Match hh:mm:ss.sss+ where the seconds are optional
-        if ( $time =~ /^(\d\d):(\d\d)(:(\d\d(\.\d+)?))?/ ) {
-            $hour = $1;
-            $min  = $2;
-            $sec  = $4 || 0;
-        }
-        else {
-            return undef;    # Not a valid time format.
-        }
-
-        # Some boundary checks
-        return if $hour >= 24;
-        return if $min >= 60;
-        return if $sec >= 60;
-
-        # Excel expresses seconds as a fraction of the number in 24 hours.
-        $seconds = ( $hour * 60 * 60 + $min * 60 + $sec ) / ( 24 * 60 * 60 );
-    }
-
-
-    # We allow the date portion of the input DateTime to be optional.
-    return $seconds if $date eq '';
-
-
-    # Match date as yyyy-mm-dd.
-    if ( $date =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/ ) {
-        $year  = $1;
-        $month = $2;
-        $day   = $3;
-    }
-    else {
-        return undef;    # Not a valid date format.
-    }
-
-    # Set the epoch as 1900 or 1904. Defaults to 1900.
-    my $date_1904 = $self->{_date_1904};
-
-
-    # Special cases for Excel.
-    if ( not $date_1904 ) {
-        return $seconds      if $date eq '1899-12-31';    # Excel 1900 epoch
-        return $seconds      if $date eq '1900-01-00';    # Excel 1900 epoch
-        return 60 + $seconds if $date eq '1900-02-29';    # Excel false leapday
-    }
-
-
-    # We calculate the date by calculating the number of days since the epoch
-    # and adjust for the number of leap days. We calculate the number of leap
-    # days by normalising the year in relation to the epoch. Thus the year 2000
-    # becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays.
-    #
-    my $epoch  = $date_1904 ? 1904 : 1900;
-    my $offset = $date_1904 ? 4    : 0;
-    my $norm   = 300;
-    my $range  = $year - $epoch;
-
-
-    # Set month days and check for leap year.
-    my @mdays = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
-    my $leap = 0;
-    $leap = 1 if $year % 4 == 0 and $year % 100 or $year % 400 == 0;
-    $mdays[1] = 29 if $leap;
-
-
-    # Some boundary checks
-    return if $year < $epoch or $year > 9999;
-    return if $month < 1     or $month > 12;
-    return if $day < 1       or $day > $mdays[ $month - 1 ];
-
-    # Accumulate the number of days since the epoch.
-    $days = $day;    # Add days for current month
-    $days += $mdays[$_] for 0 .. $month - 2;    # Add days for past months
-    $days += $range * 365;                      # Add days for past years
-    $days += int( ( $range ) / 4 );             # Add leapdays
-    $days -= int( ( $range + $offset ) / 100 ); # Subtract 100 year leapdays
-    $days += int( ( $range + $offset + $norm ) / 400 );  # Add 400 year leapdays
-    $days -= $leap;                                      # Already counted above
-
-
-    # Adjust for Excel erroneously treating 1900 as a leap year.
-    $days++ if $date_1904 == 0 and $days > 59;
-
-    return $days + $seconds;
-}
-
-
-###############################################################################
-#
-# set_row($row, $height, $XF, $hidden, $level, $collapsed)
-#
-# This method is used to set the height and XF format for a row.
-#
-sub set_row {
-
-    my $self      = shift;
-    my $row       = shift;         # Row Number.
-    my $height    = shift;         # Row height.
-    my $xf        = shift;         # Format object.
-    my $hidden    = shift || 0;    # Hidden flag.
-    my $level     = shift || 0;    # Outline level.
-    my $collapsed = shift || 0;    # Collapsed row.
-    my $min_col   = 0;
-
-    return unless defined $row;    # Ensure at least $row is specified.
-
-    # Get the default row height.
-    my $default_height = $self->{_default_row_height};
-
-    # Use min col in _check_dimensions(). Default to 0 if undefined.
-    if ( defined $self->{_dim_colmin} ) {
-        $min_col = $self->{_dim_colmin};
-    }
-
-    # Check that row is valid.
-    return -2 if $self->_check_dimensions( $row, $min_col );
-
-    $height = $default_height if !defined $height;
-
-    # If the height is 0 the row is hidden and the height is the default.
-    if ( $height == 0 ) {
-        $hidden = 1;
-        $height = $default_height;
-    }
-
-    # Set the limits for the outline levels (0 <= x <= 7).
-    $level = 0 if $level < 0;
-    $level = 7 if $level > 7;
-
-    if ( $level > $self->{_outline_row_level} ) {
-        $self->{_outline_row_level} = $level;
-    }
-
-    # Store the row properties.
-    $self->{_set_rows}->{$row} = [ $height, $xf, $hidden, $level, $collapsed ];
-
-    # Store the row change to allow optimisations.
-    $self->{_row_size_changed} = 1;
-
-    if ($hidden) {
-        $height = 0;
-    }
-
-    # Store the row sizes for use when calculating image vertices.
-    $self->{_row_sizes}->{$row} = $height;
-}
-
-
-###############################################################################
-#
-# set_default_row()
-#
-# Set the default row properties
-#
-sub set_default_row {
-
-    my $self        = shift;
-    my $height      = shift || $self->{_original_row_height};
-    my $zero_height = shift || 0;
-
-    if ( $height != $self->{_original_row_height} ) {
-        $self->{_default_row_height} = $height;
-
-        # Store the row change to allow optimisations.
-        $self->{_row_size_changed} = 1;
-    }
-
-    if ( $zero_height ) {
-        $self->{_default_row_zeroed} = 1;
-    }
-}
-
-
-###############################################################################
-#
-# merge_range($first_row, $first_col, $last_row, $last_col, $string, $format)
-#
-# Merge a range of cells. The first cell should contain the data and the others
-# should be blank. All cells should contain the same format.
-#
-sub merge_range {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-    croak "Incorrect number of arguments" if @_ < 6;
-    croak "Fifth parameter must be a format object" unless ref $_[5];
-
-    my $row_first  = shift;
-    my $col_first  = shift;
-    my $row_last   = shift;
-    my $col_last   = shift;
-    my $string     = shift;
-    my $format     = shift;
-    my @extra_args = @_;      # For write_url().
-
-    # Excel doesn't allow a single cell to be merged
-    if ( $row_first == $row_last and $col_first == $col_last ) {
-        croak "Can't merge single cell";
-    }
-
-    # Swap last row/col with first row/col as necessary
-    ( $row_first, $row_last ) = ( $row_last, $row_first )
-      if $row_first > $row_last;
-    ( $col_first, $col_last ) = ( $col_last, $col_first )
-      if $col_first > $col_last;
-
-    # Check that column number is valid and store the max value
-    return if $self->_check_dimensions( $row_last, $col_last );
-
-    # Store the merge range.
-    push @{ $self->{_merge} }, [ $row_first, $col_first, $row_last, $col_last ];
-
-    # Write the first cell
-    $self->write( $row_first, $col_first, $string, $format, @extra_args );
-
-    # Pad out the rest of the area with formatted blank cells.
-    for my $row ( $row_first .. $row_last ) {
-        for my $col ( $col_first .. $col_last ) {
-            next if $row == $row_first and $col == $col_first;
-            $self->write_blank( $row, $col, $format );
-        }
-    }
-}
-
-
-###############################################################################
-#
-# merge_range_type()
-#
-# Same as merge_range() above except the type of write() is specified.
-#
-sub merge_range_type {
-
-    my $self = shift;
-    my $type = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    my $row_first = shift;
-    my $col_first = shift;
-    my $row_last  = shift;
-    my $col_last  = shift;
-    my $format;
-
-    # Get the format. It can be in different positions for the different types.
-    if (   $type eq 'array_formula'
-        || $type eq 'blank'
-        || $type eq 'rich_string' )
-    {
-
-        # The format is the last element.
-        $format = $_[-1];
-    }
-    else {
-
-        # Or else it is after the token.
-        $format = $_[1];
-    }
-
-    # Check that there is a format object.
-    croak "Format object missing or in an incorrect position"
-      unless ref $format;
-
-    # Excel doesn't allow a single cell to be merged
-    if ( $row_first == $row_last and $col_first == $col_last ) {
-        croak "Can't merge single cell";
-    }
-
-    # Swap last row/col with first row/col as necessary
-    ( $row_first, $row_last ) = ( $row_last, $row_first )
-      if $row_first > $row_last;
-    ( $col_first, $col_last ) = ( $col_last, $col_first )
-      if $col_first > $col_last;
-
-    # Check that column number is valid and store the max value
-    return if $self->_check_dimensions( $row_last, $col_last );
-
-    # Store the merge range.
-    push @{ $self->{_merge} }, [ $row_first, $col_first, $row_last, $col_last ];
-
-    # Write the first cell
-    if ( $type eq 'string' ) {
-        $self->write_string( $row_first, $col_first, @_ );
-    }
-    elsif ( $type eq 'number' ) {
-        $self->write_number( $row_first, $col_first, @_ );
-    }
-    elsif ( $type eq 'blank' ) {
-        $self->write_blank( $row_first, $col_first, @_ );
-    }
-    elsif ( $type eq 'date_time' ) {
-        $self->write_date_time( $row_first, $col_first, @_ );
-    }
-    elsif ( $type eq 'rich_string' ) {
-        $self->write_rich_string( $row_first, $col_first, @_ );
-    }
-    elsif ( $type eq 'url' ) {
-        $self->write_url( $row_first, $col_first, @_ );
-    }
-    elsif ( $type eq 'formula' ) {
-        $self->write_formula( $row_first, $col_first, @_ );
-    }
-    elsif ( $type eq 'array_formula' ) {
-        $self->write_formula_array( $row_first, $col_first, @_ );
-    }
-    else {
-        croak "Unknown type '$type'";
-    }
-
-    # Pad out the rest of the area with formatted blank cells.
-    for my $row ( $row_first .. $row_last ) {
-        for my $col ( $col_first .. $col_last ) {
-            next if $row == $row_first and $col == $col_first;
-            $self->write_blank( $row, $col, $format );
-        }
-    }
-}
-
-
-###############################################################################
-#
-# data_validation($row, $col, {...})
-#
-# This method handles the interface to Excel data validation.
-# Somewhat ironically this requires a lot of validation code since the
-# interface is flexible and covers a several types of data validation.
-#
-# We allow data validation to be called on one cell or a range of cells. The
-# hashref contains the validation parameters and must be the last param:
-#    data_validation($row, $col, {...})
-#    data_validation($first_row, $first_col, $last_row, $last_col, {...})
-#
-# Returns  0 : normal termination
-#         -1 : insufficient number of arguments
-#         -2 : row or column out of range
-#         -3 : incorrect parameter.
-#
-sub data_validation {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    # Check for a valid number of args.
-    if ( @_ != 5 && @_ != 3 ) { return -1 }
-
-    # The final hashref contains the validation parameters.
-    my $param = pop;
-
-    # Make the last row/col the same as the first if not defined.
-    my ( $row1, $col1, $row2, $col2 ) = @_;
-    if ( !defined $row2 ) {
-        $row2 = $row1;
-        $col2 = $col1;
-    }
-
-    # Check that row and col are valid without storing the values.
-    return -2 if $self->_check_dimensions( $row1, $col1, 1, 1 );
-    return -2 if $self->_check_dimensions( $row2, $col2, 1, 1 );
-
-
-    # Check that the last parameter is a hash list.
-    if ( ref $param ne 'HASH' ) {
-        carp "Last parameter '$param' in data_validation() must be a hash ref";
-        return -3;
-    }
-
-    # List of valid input parameters.
-    my %valid_parameter = (
-        validate      => 1,
-        criteria      => 1,
-        value         => 1,
-        source        => 1,
-        minimum       => 1,
-        maximum       => 1,
-        ignore_blank  => 1,
-        dropdown      => 1,
-        show_input    => 1,
-        input_title   => 1,
-        input_message => 1,
-        show_error    => 1,
-        error_title   => 1,
-        error_message => 1,
-        error_type    => 1,
-        other_cells   => 1,
-    );
-
-    # Check for valid input parameters.
-    for my $param_key ( keys %$param ) {
-        if ( not exists $valid_parameter{$param_key} ) {
-            carp "Unknown parameter '$param_key' in data_validation()";
-            return -3;
-        }
-    }
-
-    # Map alternative parameter names 'source' or 'minimum' to 'value'.
-    $param->{value} = $param->{source}  if defined $param->{source};
-    $param->{value} = $param->{minimum} if defined $param->{minimum};
-
-    # 'validate' is a required parameter.
-    if ( not exists $param->{validate} ) {
-        carp "Parameter 'validate' is required in data_validation()";
-        return -3;
-    }
-
-
-    # List of  valid validation types.
-    my %valid_type = (
-        'any'          => 'none',
-        'any value'    => 'none',
-        'whole number' => 'whole',
-        'whole'        => 'whole',
-        'integer'      => 'whole',
-        'decimal'      => 'decimal',
-        'list'         => 'list',
-        'date'         => 'date',
-        'time'         => 'time',
-        'text length'  => 'textLength',
-        'length'       => 'textLength',
-        'custom'       => 'custom',
-    );
-
-
-    # Check for valid validation types.
-    if ( not exists $valid_type{ lc( $param->{validate} ) } ) {
-        carp "Unknown validation type '$param->{validate}' for parameter "
-          . "'validate' in data_validation()";
-        return -3;
-    }
-    else {
-        $param->{validate} = $valid_type{ lc( $param->{validate} ) };
-    }
-
-    # No action is required for validation type 'any'
-    # unless there are input messages.
-    if (   $param->{validate} eq 'none'
-        && !defined $param->{input_message}
-        && !defined $param->{input_title} )
-    {
-        return 0;
-    }
-
-    # The any, list and custom validations don't have a criteria
-    # so we use a default of 'between'.
-    if (   $param->{validate} eq 'none'
-        || $param->{validate} eq 'list'
-        || $param->{validate} eq 'custom' )
-    {
-        $param->{criteria} = 'between';
-        $param->{maximum}  = undef;
-    }
-
-    # 'criteria' is a required parameter.
-    if ( not exists $param->{criteria} ) {
-        carp "Parameter 'criteria' is required in data_validation()";
-        return -3;
-    }
-
-
-    # List of valid criteria types.
-    my %criteria_type = (
-        'between'                  => 'between',
-        'not between'              => 'notBetween',
-        'equal to'                 => 'equal',
-        '='                        => 'equal',
-        '=='                       => 'equal',
-        'not equal to'             => 'notEqual',
-        '!='                       => 'notEqual',
-        '<>'                       => 'notEqual',
-        'greater than'             => 'greaterThan',
-        '>'                        => 'greaterThan',
-        'less than'                => 'lessThan',
-        '<'                        => 'lessThan',
-        'greater than or equal to' => 'greaterThanOrEqual',
-        '>='                       => 'greaterThanOrEqual',
-        'less than or equal to'    => 'lessThanOrEqual',
-        '<='                       => 'lessThanOrEqual',
-    );
-
-    # Check for valid criteria types.
-    if ( not exists $criteria_type{ lc( $param->{criteria} ) } ) {
-        carp "Unknown criteria type '$param->{criteria}' for parameter "
-          . "'criteria' in data_validation()";
-        return -3;
-    }
-    else {
-        $param->{criteria} = $criteria_type{ lc( $param->{criteria} ) };
-    }
-
-
-    # 'Between' and 'Not between' criteria require 2 values.
-    if ( $param->{criteria} eq 'between' || $param->{criteria} eq 'notBetween' )
-    {
-        if ( not exists $param->{maximum} ) {
-            carp "Parameter 'maximum' is required in data_validation() "
-              . "when using 'between' or 'not between' criteria";
-            return -3;
-        }
-    }
-    else {
-        $param->{maximum} = undef;
-    }
-
-
-    # List of valid error dialog types.
-    my %error_type = (
-        'stop'        => 0,
-        'warning'     => 1,
-        'information' => 2,
-    );
-
-    # Check for valid error dialog types.
-    if ( not exists $param->{error_type} ) {
-        $param->{error_type} = 0;
-    }
-    elsif ( not exists $error_type{ lc( $param->{error_type} ) } ) {
-        carp "Unknown criteria type '$param->{error_type}' for parameter "
-          . "'error_type' in data_validation()";
-        return -3;
-    }
-    else {
-        $param->{error_type} = $error_type{ lc( $param->{error_type} ) };
-    }
-
-
-    # Convert date/times value if required.
-    if ( $param->{validate} eq 'date' || $param->{validate} eq 'time' ) {
-        if ( $param->{value} =~ /T/ ) {
-            my $date_time = $self->convert_date_time( $param->{value} );
-
-            if ( !defined $date_time ) {
-                carp "Invalid date/time value '$param->{value}' "
-                  . "in data_validation()";
-                return -3;
-            }
-            else {
-                $param->{value} = $date_time;
-            }
-        }
-        if ( defined $param->{maximum} && $param->{maximum} =~ /T/ ) {
-            my $date_time = $self->convert_date_time( $param->{maximum} );
-
-            if ( !defined $date_time ) {
-                carp "Invalid date/time value '$param->{maximum}' "
-                  . "in data_validation()";
-                return -3;
-            }
-            else {
-                $param->{maximum} = $date_time;
-            }
-        }
-    }
-
-    # Check that the input title doesn't exceed the maximum length.
-    if ( $param->{input_title} and length $param->{input_title} > 32 ) {
-        carp "Length of input title '$param->{input_title}'"
-          . " exceeds Excel's limit of 32";
-        return -3;
-    }
-
-    # Check that the error title don't exceed the maximum length.
-    if ( $param->{error_title} and length $param->{error_title} > 32 ) {
-        carp "Length of error title '$param->{error_title}'"
-          . " exceeds Excel's limit of 32";
-        return -3;
-    }
-
-    # Check that the input message don't exceed the maximum length.
-    if ( $param->{input_message} and length $param->{input_message} > 255 ) {
-        carp "Length of input message '$param->{input_message}'"
-          . " exceeds Excel's limit of 255";
-        return -3;
-    }
-
-    # Check that the error message don't exceed the maximum length.
-    if ( $param->{error_message} and length $param->{error_message} > 255 ) {
-        carp "Length of error message '$param->{error_message}'"
-          . " exceeds Excel's limit of 255";
-        return -3;
-    }
-
-    # Check that the input list don't exceed the maximum length.
-    if ( $param->{validate} eq 'list' ) {
-
-        if ( ref $param->{value} eq 'ARRAY' ) {
-
-            my $formula = join ',', @{ $param->{value} };
-            if ( length $formula > 255 ) {
-                carp "Length of list items '$formula' exceeds Excel's "
-                  . "limit of 255, use a formula range instead";
-                return -3;
-            }
-        }
-    }
-
-    # Set some defaults if they haven't been defined by the user.
-    $param->{ignore_blank} = 1 if !defined $param->{ignore_blank};
-    $param->{dropdown}     = 1 if !defined $param->{dropdown};
-    $param->{show_input}   = 1 if !defined $param->{show_input};
-    $param->{show_error}   = 1 if !defined $param->{show_error};
-
-
-    # These are the cells to which the validation is applied.
-    $param->{cells} = [ [ $row1, $col1, $row2, $col2 ] ];
-
-    # A (for now) undocumented parameter to pass additional cell ranges.
-    if ( exists $param->{other_cells} ) {
-
-        push @{ $param->{cells} }, @{ $param->{other_cells} };
-    }
-
-    # Store the validation information until we close the worksheet.
-    push @{ $self->{_validations} }, $param;
-}
-
-
-###############################################################################
-#
-# conditional_formatting($row, $col, {...})
-#
-# This method handles the interface to Excel conditional formatting.
-#
-# We allow the format to be called on one cell or a range of cells. The
-# hashref contains the formatting parameters and must be the last param:
-#    conditional_formatting($row, $col, {...})
-#    conditional_formatting($first_row, $first_col, $last_row, $last_col, {...})
-#
-# Returns  0 : normal termination
-#         -1 : insufficient number of arguments
-#         -2 : row or column out of range
-#         -3 : incorrect parameter.
-#
-sub conditional_formatting {
-
-    my $self       = shift;
-    my $user_range = '';
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-
-        # Check for a user defined multiple range like B3:K6,B8:K11.
-        if ( $_[0] =~ /,/ ) {
-            $user_range = $_[0];
-            $user_range =~ s/^=//;
-            $user_range =~ s/\s*,\s*/ /g;
-            $user_range =~ s/\$//g;
-        }
-
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    # The final hashref contains the validation parameters.
-    my $options = pop;
-
-    # Make the last row/col the same as the first if not defined.
-    my ( $row1, $col1, $row2, $col2 ) = @_;
-    if ( !defined $row2 ) {
-        $row2 = $row1;
-        $col2 = $col1;
-    }
-
-    # Check that row and col are valid without storing the values.
-    return -2 if $self->_check_dimensions( $row1, $col1, 1, 1 );
-    return -2 if $self->_check_dimensions( $row2, $col2, 1, 1 );
-
-
-    # Check that the last parameter is a hash list.
-    if ( ref $options ne 'HASH' ) {
-        carp "Last parameter in conditional_formatting() "
-          . "must be a hash ref";
-        return -3;
-    }
-
-    # Copy the user params.
-    my $param = {%$options};
-
-    # List of valid input parameters.
-    my %valid_parameter = (
-        type                           => 1,
-        format                         => 1,
-        criteria                       => 1,
-        value                          => 1,
-        minimum                        => 1,
-        maximum                        => 1,
-        stop_if_true                   => 1,
-        min_type                       => 1,
-        mid_type                       => 1,
-        max_type                       => 1,
-        min_value                      => 1,
-        mid_value                      => 1,
-        max_value                      => 1,
-        min_color                      => 1,
-        mid_color                      => 1,
-        max_color                      => 1,
-        bar_color                      => 1,
-        bar_negative_color             => 1,
-        bar_negative_color_same        => 1,
-        bar_solid                      => 1,
-        bar_border_color               => 1,
-        bar_negative_border_color      => 1,
-        bar_negative_border_color_same => 1,
-        bar_no_border                  => 1,
-        bar_direction                  => 1,
-        bar_axis_position              => 1,
-        bar_axis_color                 => 1,
-        bar_only                       => 1,
-        icon_style                     => 1,
-        reverse_icons                  => 1,
-        icons_only                     => 1,
-        icons                          => 1,
-        data_bar_2010                  => 1,
-    );
-
-    # Check for valid input parameters.
-    for my $param_key ( keys %$param ) {
-        if ( not exists $valid_parameter{$param_key} ) {
-            carp "Unknown parameter '$param_key' in conditional_formatting()";
-            return -3;
-        }
-    }
-
-    # 'type' is a required parameter.
-    if ( not exists $param->{type} ) {
-        carp "Parameter 'type' is required in conditional_formatting()";
-        return -3;
-    }
-
-    # List of  valid validation types.
-    my %valid_type = (
-        'cell'          => 'cellIs',
-        'date'          => 'date',
-        'time'          => 'time',
-        'average'       => 'aboveAverage',
-        'duplicate'     => 'duplicateValues',
-        'unique'        => 'uniqueValues',
-        'top'           => 'top10',
-        'bottom'        => 'top10',
-        'text'          => 'text',
-        'time_period'   => 'timePeriod',
-        'blanks'        => 'containsBlanks',
-        'no_blanks'     => 'notContainsBlanks',
-        'errors'        => 'containsErrors',
-        'no_errors'     => 'notContainsErrors',
-        '2_color_scale' => '2_color_scale',
-        '3_color_scale' => '3_color_scale',
-        'data_bar'      => 'dataBar',
-        'formula'       => 'expression',
-        'icon_set'      => 'iconSet',
-    );
-
-
-    # Check for valid validation types.
-    if ( not exists $valid_type{ lc( $param->{type} ) } ) {
-        carp "Unknown validation type '$param->{type}' for parameter "
-          . "'type' in conditional_formatting()";
-        return -3;
-    }
-    else {
-        $param->{direction} = 'bottom' if $param->{type} eq 'bottom';
-        $param->{type} = $valid_type{ lc( $param->{type} ) };
-    }
-
-
-    # List of valid criteria types.
-    my %criteria_type = (
-        'between'                  => 'between',
-        'not between'              => 'notBetween',
-        'equal to'                 => 'equal',
-        '='                        => 'equal',
-        '=='                       => 'equal',
-        'not equal to'             => 'notEqual',
-        '!='                       => 'notEqual',
-        '<>'                       => 'notEqual',
-        'greater than'             => 'greaterThan',
-        '>'                        => 'greaterThan',
-        'less than'                => 'lessThan',
-        '<'                        => 'lessThan',
-        'greater than or equal to' => 'greaterThanOrEqual',
-        '>='                       => 'greaterThanOrEqual',
-        'less than or equal to'    => 'lessThanOrEqual',
-        '<='                       => 'lessThanOrEqual',
-        'containing'               => 'containsText',
-        'not containing'           => 'notContains',
-        'begins with'              => 'beginsWith',
-        'ends with'                => 'endsWith',
-        'yesterday'                => 'yesterday',
-        'today'                    => 'today',
-        'last 7 days'              => 'last7Days',
-        'last week'                => 'lastWeek',
-        'this week'                => 'thisWeek',
-        'next week'                => 'nextWeek',
-        'last month'               => 'lastMonth',
-        'this month'               => 'thisMonth',
-        'next month'               => 'nextMonth',
-    );
-
-    # Check for valid criteria types.
-    if ( defined $param->{criteria}
-        && exists $criteria_type{ lc( $param->{criteria} ) } )
-    {
-        $param->{criteria} = $criteria_type{ lc( $param->{criteria} ) };
-    }
-
-    # Convert date/times value if required.
-    if ( $param->{type} eq 'date' || $param->{type} eq 'time' ) {
-        $param->{type} = 'cellIs';
-
-        if ( defined $param->{value} && $param->{value} =~ /T/ ) {
-            my $date_time = $self->convert_date_time( $param->{value} );
-
-            if ( !defined $date_time ) {
-                carp "Invalid date/time value '$param->{value}' "
-                  . "in conditional_formatting()";
-                return -3;
-            }
-            else {
-                $param->{value} = $date_time;
-            }
-        }
-
-        if ( defined $param->{minimum} && $param->{minimum} =~ /T/ ) {
-            my $date_time = $self->convert_date_time( $param->{minimum} );
-
-            if ( !defined $date_time ) {
-                carp "Invalid date/time value '$param->{minimum}' "
-                  . "in conditional_formatting()";
-                return -3;
-            }
-            else {
-                $param->{minimum} = $date_time;
-            }
-        }
-
-        if ( defined $param->{maximum} && $param->{maximum} =~ /T/ ) {
-            my $date_time = $self->convert_date_time( $param->{maximum} );
-
-            if ( !defined $date_time ) {
-                carp "Invalid date/time value '$param->{maximum}' "
-                  . "in conditional_formatting()";
-                return -3;
-            }
-            else {
-                $param->{maximum} = $date_time;
-            }
-        }
-    }
-
-
-    # List of valid icon styles.
-    my %icon_set_styles = (
-        "3_arrows"                => "3Arrows",            # 1
-        "3_flags"                 => "3Flags",             # 2
-        "3_traffic_lights_rimmed" => "3TrafficLights2",    # 3
-        "3_symbols_circled"       => "3Symbols",           # 4
-        "4_arrows"                => "4Arrows",            # 5
-        "4_red_to_black"          => "4RedToBlack",        # 6
-        "4_traffic_lights"        => "4TrafficLights",     # 7
-        "5_arrows_gray"           => "5ArrowsGray",        # 8
-        "5_quarters"              => "5Quarters",          # 9
-        "3_arrows_gray"           => "3ArrowsGray",        # 10
-        "3_traffic_lights"        => "3TrafficLights",     # 11
-        "3_signs"                 => "3Signs",             # 12
-        "3_symbols"               => "3Symbols2",          # 13
-        "4_arrows_gray"           => "4ArrowsGray",        # 14
-        "4_ratings"               => "4Rating",            # 15
-        "5_arrows"                => "5Arrows",            # 16
-        "5_ratings"               => "5Rating",            # 17
-    );
-
-
-    # Set properties for icon sets.
-    if ( $param->{type} eq 'iconSet' ) {
-
-        if ( !defined $param->{icon_style} ) {
-            carp "The 'icon_style' parameter must be specified when "
-              . "'type' == 'icon_set' in conditional_formatting()";
-            return -3;
-        }
-
-        # Check for valid icon styles.
-        if ( not exists $icon_set_styles{ $param->{icon_style} } ) {
-            carp "Unknown icon style '$param->{icon_style}' for parameter "
-              . "'icon_style' in conditional_formatting()";
-            return -3;
-        }
-        else {
-            $param->{icon_style} = $icon_set_styles{ $param->{icon_style} };
-        }
-
-        # Set the number of icons for the icon style.
-        $param->{total_icons} = 3;
-        if ( $param->{icon_style} =~ /^4/ ) {
-            $param->{total_icons} = 4;
-        }
-        elsif ( $param->{icon_style} =~ /^5/ ) {
-            $param->{total_icons} = 5;
-        }
-
-        $param->{icons} =
-          $self->_set_icon_properties( $param->{total_icons}, $param->{icons} );
-    }
-
-
-    # Set the formatting range.
-    my $range      = '';
-    my $start_cell = '';    # Use for formulas.
-
-    # Swap last row/col for first row/col as necessary
-    if ( $row1 > $row2 ) {
-        ( $row1, $row2 ) = ( $row2, $row1 );
-    }
-
-    if ( $col1 > $col2 ) {
-        ( $col1, $col2 ) = ( $col2, $col1 );
-    }
-
-    # If the first and last cell are the same write a single cell.
-    if ( ( $row1 == $row2 ) && ( $col1 == $col2 ) ) {
-        $range = xl_rowcol_to_cell( $row1, $col1 );
-        $start_cell = $range;
-    }
-    else {
-        $range = xl_range( $row1, $row2, $col1, $col2 );
-        $start_cell = xl_rowcol_to_cell( $row1, $col1 );
-    }
-
-    # Override with user defined multiple range if provided.
-    if ( $user_range ) {
-        $range = $user_range;
-    }
-
-    # Get the dxf format index.
-    if ( defined $param->{format} && ref $param->{format} ) {
-        $param->{format} = $param->{format}->get_dxf_index();
-    }
-
-    # Set the priority based on the order of adding.
-    $param->{priority} = $self->{_dxf_priority}++;
-
-    # Check for 2010 style data_bar parameters.
-    if (   $self->{_use_data_bars_2010}
-        || $param->{data_bar_2010}
-        || $param->{bar_solid}
-        || $param->{bar_border_color}
-        || $param->{bar_negative_color}
-        || $param->{bar_negative_color_same}
-        || $param->{bar_negative_border_color}
-        || $param->{bar_negative_border_color_same}
-        || $param->{bar_no_border}
-        || $param->{bar_axis_position}
-        || $param->{bar_axis_color}
-        || $param->{bar_direction} )
-    {
-        $param->{_is_data_bar_2010} = 1;
-    }
-
-    # Special handling of text criteria.
-    if ( $param->{type} eq 'text' ) {
-
-        if ( $param->{criteria} eq 'containsText' ) {
-            $param->{type}    = 'containsText';
-            $param->{formula} = sprintf 'NOT(ISERROR(SEARCH("%s",%s)))',
-              $param->{value}, $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'notContains' ) {
-            $param->{type}    = 'notContainsText';
-            $param->{formula} = sprintf 'ISERROR(SEARCH("%s",%s))',
-              $param->{value}, $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'beginsWith' ) {
-            $param->{type}    = 'beginsWith';
-            $param->{formula} = sprintf 'LEFT(%s,%d)="%s"',
-              $start_cell, length( $param->{value} ), $param->{value};
-        }
-        elsif ( $param->{criteria} eq 'endsWith' ) {
-            $param->{type}    = 'endsWith';
-            $param->{formula} = sprintf 'RIGHT(%s,%d)="%s"',
-              $start_cell, length( $param->{value} ), $param->{value};
-        }
-        else {
-            carp "Invalid text criteria '$param->{criteria}' "
-              . "in conditional_formatting()";
-        }
-    }
-
-    # Special handling of time time_period criteria.
-    if ( $param->{type} eq 'timePeriod' ) {
-
-        if ( $param->{criteria} eq 'yesterday' ) {
-            $param->{formula} = sprintf 'FLOOR(%s,1)=TODAY()-1', $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'today' ) {
-            $param->{formula} = sprintf 'FLOOR(%s,1)=TODAY()', $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'tomorrow' ) {
-            $param->{formula} = sprintf 'FLOOR(%s,1)=TODAY()+1', $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'last7Days' ) {
-            $param->{formula} =
-              sprintf 'AND(TODAY()-FLOOR(%s,1)<=6,FLOOR(%s,1)<=TODAY())',
-              $start_cell, $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'lastWeek' ) {
-            $param->{formula} =
-              sprintf 'AND(TODAY()-ROUNDDOWN(%s,0)>=(WEEKDAY(TODAY())),'
-              . 'TODAY()-ROUNDDOWN(%s,0)<(WEEKDAY(TODAY())+7))',
-              $start_cell, $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'thisWeek' ) {
-            $param->{formula} =
-              sprintf 'AND(TODAY()-ROUNDDOWN(%s,0)<=WEEKDAY(TODAY())-1,'
-              . 'ROUNDDOWN(%s,0)-TODAY()<=7-WEEKDAY(TODAY()))',
-              $start_cell, $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'nextWeek' ) {
-            $param->{formula} =
-              sprintf 'AND(ROUNDDOWN(%s,0)-TODAY()>(7-WEEKDAY(TODAY())),'
-              . 'ROUNDDOWN(%s,0)-TODAY()<(15-WEEKDAY(TODAY())))',
-              $start_cell, $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'lastMonth' ) {
-            $param->{formula} =
-              sprintf
-              'AND(MONTH(%s)=MONTH(TODAY())-1,OR(YEAR(%s)=YEAR(TODAY()),'
-              . 'AND(MONTH(%s)=1,YEAR(A1)=YEAR(TODAY())-1)))',
-              $start_cell, $start_cell, $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'thisMonth' ) {
-            $param->{formula} =
-              sprintf 'AND(MONTH(%s)=MONTH(TODAY()),YEAR(%s)=YEAR(TODAY()))',
-              $start_cell, $start_cell;
-        }
-        elsif ( $param->{criteria} eq 'nextMonth' ) {
-            $param->{formula} =
-              sprintf
-              'AND(MONTH(%s)=MONTH(TODAY())+1,OR(YEAR(%s)=YEAR(TODAY()),'
-              . 'AND(MONTH(%s)=12,YEAR(%s)=YEAR(TODAY())+1)))',
-              $start_cell, $start_cell, $start_cell, $start_cell;
-        }
-        else {
-            carp "Invalid time_period criteria '$param->{criteria}' "
-              . "in conditional_formatting()";
-        }
-    }
-
-
-    # Special handling of blanks/error types.
-    if ( $param->{type} eq 'containsBlanks' ) {
-        $param->{formula} = sprintf 'LEN(TRIM(%s))=0', $start_cell;
-    }
-
-    if ( $param->{type} eq 'notContainsBlanks' ) {
-        $param->{formula} = sprintf 'LEN(TRIM(%s))>0', $start_cell;
-    }
-
-    if ( $param->{type} eq 'containsErrors' ) {
-        $param->{formula} = sprintf 'ISERROR(%s)', $start_cell;
-    }
-
-    if ( $param->{type} eq 'notContainsErrors' ) {
-        $param->{formula} = sprintf 'NOT(ISERROR(%s))', $start_cell;
-    }
-
-
-    # Special handling for 2 color scale.
-    if ( $param->{type} eq '2_color_scale' ) {
-        $param->{type} = 'colorScale';
-
-        # Color scales don't use any additional formatting.
-        $param->{format} = undef;
-
-        # Turn off 3 color parameters.
-        $param->{mid_type}  = undef;
-        $param->{mid_color} = undef;
-
-        $param->{min_type}  ||= 'min';
-        $param->{max_type}  ||= 'max';
-        $param->{min_value} ||= 0;
-        $param->{max_value} ||= 0;
-        $param->{min_color} ||= '#FF7128';
-        $param->{max_color} ||= '#FFEF9C';
-
-        $param->{max_color} = $self->_get_palette_color( $param->{max_color} );
-        $param->{min_color} = $self->_get_palette_color( $param->{min_color} );
-    }
-
-
-    # Special handling for 3 color scale.
-    if ( $param->{type} eq '3_color_scale' ) {
-        $param->{type} = 'colorScale';
-
-        # Color scales don't use any additional formatting.
-        $param->{format} = undef;
-
-        $param->{min_type}  ||= 'min';
-        $param->{mid_type}  ||= 'percentile';
-        $param->{max_type}  ||= 'max';
-        $param->{min_value} ||= 0;
-        $param->{mid_value} = 50 unless defined $param->{mid_value};
-        $param->{max_value} ||= 0;
-        $param->{min_color} ||= '#F8696B';
-        $param->{mid_color} ||= '#FFEB84';
-        $param->{max_color} ||= '#63BE7B';
-
-        $param->{max_color} = $self->_get_palette_color( $param->{max_color} );
-        $param->{mid_color} = $self->_get_palette_color( $param->{mid_color} );
-        $param->{min_color} = $self->_get_palette_color( $param->{min_color} );
-    }
-
-
-    # Special handling for data bar.
-    if ( $param->{type} eq 'dataBar' ) {
-
-        # Excel 2007 data bars don't use any additional formatting.
-        $param->{format} = undef;
-
-        if ( !defined $param->{min_type} ) {
-            $param->{min_type}      = 'min';
-            $param->{_x14_min_type} = 'autoMin';
-        }
-        else {
-            $param->{_x14_min_type} = $param->{min_type};
-        }
-
-        if ( !defined $param->{max_type} ) {
-            $param->{max_type}      = 'max';
-            $param->{_x14_max_type} = 'autoMax';
-        }
-        else {
-            $param->{_x14_max_type} = $param->{max_type};
-        }
-
-        $param->{min_value}                      ||= 0;
-        $param->{max_value}                      ||= 0;
-        $param->{bar_color}                      ||= '#638EC6';
-        $param->{bar_border_color}               ||= $param->{bar_color};
-        $param->{bar_only}                       ||= 0;
-        $param->{bar_no_border}                  ||= 0;
-        $param->{bar_solid}                      ||= 0;
-        $param->{bar_direction}                  ||= '';
-        $param->{bar_negative_color}             ||= '#FF0000';
-        $param->{bar_negative_border_color}      ||= '#FF0000';
-        $param->{bar_negative_color_same}        ||= 0;
-        $param->{bar_negative_border_color_same} ||= 0;
-        $param->{bar_axis_position}              ||= '';
-        $param->{bar_axis_color}                 ||= '#000000';
-
-        $param->{bar_color} =
-          $self->_get_palette_color( $param->{bar_color} );
-
-        $param->{bar_border_color} =
-          $self->_get_palette_color( $param->{bar_border_color} );
-
-        $param->{bar_negative_color} =
-          $self->_get_palette_color( $param->{bar_negative_color} );
-
-        $param->{bar_negative_border_color} =
-          $self->_get_palette_color( $param->{bar_negative_border_color} );
-
-        $param->{bar_axis_color} =
-          $self->_get_palette_color( $param->{bar_axis_color} );
-
-    }
-
-    # Adjust for 2010 style data_bar parameters.
-    if ( $param->{_is_data_bar_2010} ) {
-
-        $self->{_excel_version} = 2010;
-
-        if ( $param->{min_type} eq 'min' && $param->{min_value} == 0 ) {
-            $param->{min_value} = undef;
-        }
-
-        if ( $param->{max_type} eq 'max' && $param->{max_value} == 0 ) {
-            $param->{max_value} = undef;
-        }
-
-        # Store range for Excel 2010 data bars.
-        $param->{_range} = $range;
-    }
-
-    # Strip the leading = from formulas.
-    $param->{min_value} =~ s/^=// if defined $param->{min_value};
-    $param->{mid_value} =~ s/^=// if defined $param->{mid_value};
-    $param->{max_value} =~ s/^=// if defined $param->{max_value};
-
-    # Store the validation information until we close the worksheet.
-    push @{ $self->{_cond_formats}->{$range} }, $param;
-}
-
-
-###############################################################################
-#
-# Set the sub-properites for icons.
-#
-sub _set_icon_properties {
-
-    my $self        = shift;
-    my $total_icons = shift;
-    my $user_props  = shift;
-    my $props       = [];
-
-    # Set the default icon properties.
-    for ( 0 .. $total_icons - 1 ) {
-        push @$props,
-          {
-            criteria => 0,
-            value    => 0,
-            type     => 'percent'
-          };
-    }
-
-    # Set the default icon values based on the number of icons.
-    if ( $total_icons == 3 ) {
-        $props->[0]->{value} = 67;
-        $props->[1]->{value} = 33;
-    }
-
-    if ( $total_icons == 4 ) {
-        $props->[0]->{value} = 75;
-        $props->[1]->{value} = 50;
-        $props->[2]->{value} = 25;
-    }
-
-    if ( $total_icons == 5 ) {
-        $props->[0]->{value} = 80;
-        $props->[1]->{value} = 60;
-        $props->[2]->{value} = 40;
-        $props->[3]->{value} = 20;
-    }
-
-    # Overwrite default properties with user defined properties.
-    if ( defined $user_props ) {
-
-        # Ensure we don't set user properties for lowest icon.
-        my $max_data = @$user_props;
-        if ( $max_data >= $total_icons ) {
-            $max_data = $total_icons -1;
-        }
-
-        for my $i ( 0 .. $max_data - 1 ) {
-
-            # Set the user defined 'value' property.
-            if ( defined $user_props->[$i]->{value} ) {
-                $props->[$i]->{value} = $user_props->[$i]->{value};
-                $props->[$i]->{value} =~ s/^=//;
-            }
-
-            # Set the user defined 'type' property.
-            if ( defined $user_props->[$i]->{type} ) {
-
-                my $type = $user_props->[$i]->{type};
-
-                if (   $type ne 'percent'
-                    && $type ne 'percentile'
-                    && $type ne 'number'
-                    && $type ne 'formula' )
-                {
-                    carp "Unknown icon property type '$props->{type}' for sub-"
-                      . "property 'type' in conditional_formatting()";
-                }
-                else {
-                    $props->[$i]->{type} = $type;
-
-                    if ( $props->[$i]->{type} eq 'number' ) {
-                        $props->[$i]->{type} = 'num';
-                    }
-                }
-            }
-
-            # Set the user defined 'criteria' property.
-            if ( defined $user_props->[$i]->{criteria}
-                && $user_props->[$i]->{criteria} eq '>' )
-            {
-                $props->[$i]->{criteria} = 1;
-            }
-
-        }
-
-    }
-
-    return $props;
-}
-
-
-###############################################################################
-#
-# add_table()
-#
-# Add an Excel table to a worksheet.
-#
-sub add_table {
-
-    my $self       = shift;
-    my $user_range = '';
-    my %table;
-    my @col_formats;
-
-    # We would need to order the write statements very carefully within this
-    # function to support optimisation mode. Disable add_table() when it is
-    # on for now.
-    if ( $self->{_optimization} == 1 ) {
-        carp "add_table() isn't supported when set_optimization() is on";
-        return -1;
-    }
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( @_ && $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    # Check for a valid number of args.
-    if ( @_ < 4 ) {
-        carp "Not enough parameters to add_table()";
-        return -1;
-    }
-
-    my ( $row1, $col1, $row2, $col2 ) = @_;
-
-    # Check that row and col are valid without storing the values.
-    return -2 if $self->_check_dimensions( $row1, $col1, 1, 1 );
-    return -2 if $self->_check_dimensions( $row2, $col2, 1, 1 );
-
-
-    # The final hashref contains the validation parameters.
-    my $param = $_[4] || {};
-
-    # Check that the last parameter is a hash list.
-    if ( ref $param ne 'HASH' ) {
-        carp "Last parameter '$param' in add_table() must be a hash ref";
-        return -3;
-    }
-
-
-    # List of valid input parameters.
-    my %valid_parameter = (
-        autofilter     => 1,
-        banded_columns => 1,
-        banded_rows    => 1,
-        columns        => 1,
-        data           => 1,
-        first_column   => 1,
-        header_row     => 1,
-        last_column    => 1,
-        name           => 1,
-        style          => 1,
-        total_row      => 1,
-    );
-
-    # Check for valid input parameters.
-    for my $param_key ( keys %$param ) {
-        if ( not exists $valid_parameter{$param_key} ) {
-            carp "Unknown parameter '$param_key' in add_table()";
-            return -3;
-        }
-    }
-
-    # Turn on Excel's defaults.
-    $param->{banded_rows} = 1 if !defined $param->{banded_rows};
-    $param->{header_row}  = 1 if !defined $param->{header_row};
-    $param->{autofilter}  = 1 if !defined $param->{autofilter};
-
-    # Set the table options.
-    $table{_show_first_col}   = $param->{first_column}   ? 1 : 0;
-    $table{_show_last_col}    = $param->{last_column}    ? 1 : 0;
-    $table{_show_row_stripes} = $param->{banded_rows}    ? 1 : 0;
-    $table{_show_col_stripes} = $param->{banded_columns} ? 1 : 0;
-    $table{_header_row_count} = $param->{header_row}     ? 1 : 0;
-    $table{_totals_row_shown} = $param->{total_row}      ? 1 : 0;
-
-
-    # Set the table name.
-    if ( defined $param->{name} ) {
-        my $name = $param->{name};
-
-        # Warn if the name contains invalid chars as defined by Excel help.
-        if ( $name !~ m/^[\w\\][\w\\.]*$/ || $name =~ m/^\d/ ) {
-            carp "Invalid character in name '$name' used in add_table()";
-            return -3;
-        }
-
-        # Warn if the name looks like a cell name.
-        if ( $name =~ m/^[a-zA-Z][a-zA-Z]?[a-dA-D]?[0-9]+$/ ) {
-            carp "Invalid name '$name' looks like a cell name in add_table()";
-            return -3;
-        }
-
-        # Warn if the name looks like a R1C1.
-        if ( $name =~ m/^[rcRC]$/ || $name =~ m/^[rcRC]\d+[rcRC]\d+$/ ) {
-            carp "Invalid name '$name' like a RC cell ref in add_table()";
-            return -3;
-        }
-
-        $table{_name} = $param->{name};
-    }
-
-    # Set the table style.
-    if ( defined $param->{style} ) {
-        $table{_style} = $param->{style};
-
-        # Remove whitespace from style name.
-        $table{_style} =~ s/\s//g;
-    }
-    else {
-        $table{_style} = "TableStyleMedium9";
-    }
-
-
-    # Swap last row/col for first row/col as necessary.
-    if ( $row1 > $row2 ) {
-        ( $row1, $row2 ) = ( $row2, $row1 );
-    }
-
-    if ( $col1 > $col2 ) {
-        ( $col1, $col2 ) = ( $col2, $col1 );
-    }
-
-
-    # Set the data range rows (without the header and footer).
-    my $first_data_row = $row1;
-    my $last_data_row  = $row2;
-    $first_data_row++ if $param->{header_row};
-    $last_data_row--  if $param->{total_row};
-
-
-    # Set the table and autofilter ranges.
-    $table{_range}   = xl_range( $row1, $row2,          $col1, $col2 );
-    $table{_a_range} = xl_range( $row1, $last_data_row, $col1, $col2 );
-
-
-    # If the header row if off the default is to turn autofilter off.
-    if ( !$param->{header_row} ) {
-        $param->{autofilter} = 0;
-    }
-
-    # Set the autofilter range.
-    if ( $param->{autofilter} ) {
-        $table{_autofilter} = $table{_a_range};
-    }
-
-    # Add the table columns.
-    my %seen_names;
-    my $col_id = 1;
-    for my $col_num ( $col1 .. $col2 ) {
-
-        # Set up the default column data.
-        my $col_data = {
-            _id             => $col_id,
-            _name           => 'Column' . $col_id,
-            _total_string   => '',
-            _total_function => '',
-            _formula        => '',
-            _format         => undef,
-            _name_format    => undef,
-        };
-
-        # Overwrite the defaults with any use defined values.
-        if ( $param->{columns} ) {
-
-            # Check if there are user defined values for this column.
-            if ( my $user_data = $param->{columns}->[ $col_id - 1 ] ) {
-
-                # Map user defined values to internal values.
-                $col_data->{_name} = $user_data->{header}
-                  if $user_data->{header};
-
-                # Excel requires unique case insensitive header names.
-                my $name = $col_data->{_name};
-                my $key = lc $name;
-                if (exists $seen_names{$key}) {
-                    carp "add_table() contains duplicate name: '$name'";
-                    return -1;
-                }
-                else {
-                    $seen_names{$key} = 1;
-                }
-
-                # Get the header format if defined.
-                $col_data->{_name_format} = $user_data->{header_format};
-
-                # Handle the column formula.
-                if ( $user_data->{formula} ) {
-                    my $formula = $user_data->{formula};
-
-                    # Remove the leading = from formula.
-                    $formula =~ s/^=//;
-
-                    # Covert Excel 2010 "@" ref to 2007 "#This Row".
-                    $formula =~ s/@/[#This Row],/g;
-
-                    $col_data->{_formula} = $formula;
-
-                    for my $row ( $first_data_row .. $last_data_row ) {
-                        $self->write_formula( $row, $col_num, $formula,
-                            $user_data->{format} );
-                    }
-                }
-
-                # Handle the function for the total row.
-                if ( $user_data->{total_function} ) {
-                    my $function = $user_data->{total_function};
-
-                    # Massage the function name.
-                    $function = lc $function;
-                    $function =~ s/_//g;
-                    $function =~ s/\s//g;
-
-                    $function = 'countNums' if $function eq 'countnums';
-                    $function = 'stdDev'    if $function eq 'stddev';
-
-                    $col_data->{_total_function} = $function;
-
-                    my $formula = _table_function_to_formula(
-                        $function,
-                        $col_data->{_name}
-
-                    );
-
-                    my $value = $user_data->{total_value} || 0;
-
-                    $self->write_formula( $row2, $col_num, $formula,
-                        $user_data->{format}, $value );
-
-                }
-                elsif ( $user_data->{total_string} ) {
-
-                    # Total label only (not a function).
-                    my $total_string = $user_data->{total_string};
-                    $col_data->{_total_string} = $total_string;
-
-                    $self->write_string( $row2, $col_num, $total_string,
-                        $user_data->{format} );
-                }
-
-                # Get the dxf format index.
-                if ( defined $user_data->{format} && ref $user_data->{format} )
-                {
-                    $col_data->{_format} =
-                      $user_data->{format}->get_dxf_index();
-                }
-
-                # Store the column format for writing the cell data.
-                # It doesn't matter if it is undefined.
-                $col_formats[ $col_id - 1 ] = $user_data->{format};
-            }
-        }
-
-        # Store the column data.
-        push @{ $table{_columns} }, $col_data;
-
-        # Write the column headers to the worksheet.
-        if ( $param->{header_row} ) {
-            $self->write_string( $row1, $col_num, $col_data->{_name},
-                $col_data->{_name_format} );
-        }
-
-        $col_id++;
-    }    # Table columns.
-
-
-    # Write the cell data if supplied.
-    if ( my $data = $param->{data} ) {
-
-        my $i = 0;    # For indexing the row data.
-        for my $row ( $first_data_row .. $last_data_row ) {
-            my $j = 0;    # For indexing the col data.
-
-            for my $col ( $col1 .. $col2 ) {
-
-                my $token = $data->[$i]->[$j];
-
-                if ( defined $token ) {
-                    $self->write( $row, $col, $token, $col_formats[$j] );
-                }
-
-                $j++;
-            }
-            $i++;
-        }
-    }
-
-
-    # Store the table data.
-    push @{ $self->{_tables} }, \%table;
-
-    return \%table;
-}
-
-
-###############################################################################
-#
-# add_sparkline()
-#
-# Add sparklines to the worksheet.
-#
-sub add_sparkline {
-
-    my $self      = shift;
-    my $param     = shift;
-    my $sparkline = {};
-
-    # Check that the last parameter is a hash list.
-    if ( ref $param ne 'HASH' ) {
-        carp "Parameter list in add_sparkline() must be a hash ref";
-        return -1;
-    }
-
-    # List of valid input parameters.
-    my %valid_parameter = (
-        location        => 1,
-        range           => 1,
-        type            => 1,
-        high_point      => 1,
-        low_point       => 1,
-        negative_points => 1,
-        first_point     => 1,
-        last_point      => 1,
-        markers         => 1,
-        style           => 1,
-        series_color    => 1,
-        negative_color  => 1,
-        markers_color   => 1,
-        first_color     => 1,
-        last_color      => 1,
-        high_color      => 1,
-        low_color       => 1,
-        max             => 1,
-        min             => 1,
-        axis            => 1,
-        reverse         => 1,
-        empty_cells     => 1,
-        show_hidden     => 1,
-        plot_hidden     => 1,
-        date_axis       => 1,
-        weight          => 1,
-    );
-
-    # Check for valid input parameters.
-    for my $param_key ( keys %$param ) {
-        if ( not exists $valid_parameter{$param_key} ) {
-            carp "Unknown parameter '$param_key' in add_sparkline()";
-            return -2;
-        }
-    }
-
-    # 'location' is a required parameter.
-    if ( not exists $param->{location} ) {
-        carp "Parameter 'location' is required in add_sparkline()";
-        return -3;
-    }
-
-    # 'range' is a required parameter.
-    if ( not exists $param->{range} ) {
-        carp "Parameter 'range' is required in add_sparkline()";
-        return -3;
-    }
-
-
-    # Handle the sparkline type.
-    my $type = $param->{type} || 'line';
-
-    if ( $type ne 'line' && $type ne 'column' && $type ne 'win_loss' ) {
-        carp "Parameter 'type' must be 'line', 'column' "
-          . "or 'win_loss' in add_sparkline()";
-        return -4;
-    }
-
-    $type = 'stacked' if $type eq 'win_loss';
-    $sparkline->{_type} = $type;
-
-
-    # We handle single location/range values or array refs of values.
-    if ( ref $param->{location} ) {
-        $sparkline->{_locations} = $param->{location};
-        $sparkline->{_ranges}    = $param->{range};
-    }
-    else {
-        $sparkline->{_locations} = [ $param->{location} ];
-        $sparkline->{_ranges}    = [ $param->{range} ];
-    }
-
-    my $range_count    = @{ $sparkline->{_ranges} };
-    my $location_count = @{ $sparkline->{_locations} };
-
-    # The ranges and locations must match.
-    if ( $range_count != $location_count ) {
-        carp "Must have the same number of location and range "
-          . "parameters in add_sparkline()";
-        return -5;
-    }
-
-    # Store the count.
-    $sparkline->{_count} = @{ $sparkline->{_locations} };
-
-    # Get the worksheet name for the range conversion below.
-    my $sheetname = quote_sheetname( $self->{_name} );
-
-    # Cleanup the input ranges.
-    for my $range ( @{ $sparkline->{_ranges} } ) {
-
-        # Remove the absolute reference $ symbols.
-        $range =~ s{\$}{}g;
-
-        # Remove the = from xl_range_formula(.
-        $range =~ s{^=}{};
-
-        # Convert a simple range into a full Sheet1!A1:D1 range.
-        if ( $range !~ /!/ ) {
-            $range = $sheetname . "!" . $range;
-        }
-    }
-
-    # Cleanup the input locations.
-    for my $location ( @{ $sparkline->{_locations} } ) {
-        $location =~ s{\$}{}g;
-    }
-
-    # Map options.
-    $sparkline->{_high}     = $param->{high_point};
-    $sparkline->{_low}      = $param->{low_point};
-    $sparkline->{_negative} = $param->{negative_points};
-    $sparkline->{_first}    = $param->{first_point};
-    $sparkline->{_last}     = $param->{last_point};
-    $sparkline->{_markers}  = $param->{markers};
-    $sparkline->{_min}      = $param->{min};
-    $sparkline->{_max}      = $param->{max};
-    $sparkline->{_axis}     = $param->{axis};
-    $sparkline->{_reverse}  = $param->{reverse};
-    $sparkline->{_hidden}   = $param->{show_hidden};
-    $sparkline->{_weight}   = $param->{weight};
-
-    # Map empty cells options.
-    my $empty = $param->{empty_cells} || '';
-
-    if ( $empty eq 'zero' ) {
-        $sparkline->{_empty} = 0;
-    }
-    elsif ( $empty eq 'connect' ) {
-        $sparkline->{_empty} = 'span';
-    }
-    else {
-        $sparkline->{_empty} = 'gap';
-    }
-
-
-    # Map the date axis range.
-    my $date_range = $param->{date_axis};
-
-    if ( $date_range && $date_range !~ /!/ ) {
-        $date_range = $sheetname . "!" . $date_range;
-    }
-    $sparkline->{_date_axis} = $date_range;
-
-
-    # Set the sparkline styles.
-    my $style_id = $param->{style} || 0;
-    my $style = $Excel::Writer::XLSX::Package::Theme::spark_styles[$style_id];
-
-    $sparkline->{_series_color}   = $style->{series};
-    $sparkline->{_negative_color} = $style->{negative};
-    $sparkline->{_markers_color}  = $style->{markers};
-    $sparkline->{_first_color}    = $style->{first};
-    $sparkline->{_last_color}     = $style->{last};
-    $sparkline->{_high_color}     = $style->{high};
-    $sparkline->{_low_color}      = $style->{low};
-
-    # Override the style colours with user defined colors.
-    $self->_set_spark_color( $sparkline, $param, 'series_color' );
-    $self->_set_spark_color( $sparkline, $param, 'negative_color' );
-    $self->_set_spark_color( $sparkline, $param, 'markers_color' );
-    $self->_set_spark_color( $sparkline, $param, 'first_color' );
-    $self->_set_spark_color( $sparkline, $param, 'last_color' );
-    $self->_set_spark_color( $sparkline, $param, 'high_color' );
-    $self->_set_spark_color( $sparkline, $param, 'low_color' );
-
-    push @{ $self->{_sparklines} }, $sparkline;
-}
-
-
-###############################################################################
-#
-# insert_button()
-#
-# Insert a button form object into the worksheet.
-#
-sub insert_button {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    # Check the number of args.
-    if ( @_ < 3 ) { return -1 }
-
-    my $button = $self->_button_params( @_ );
-
-    push @{ $self->{_buttons_array} }, $button;
-
-    $self->{_has_vml} = 1;
-}
-
-
-###############################################################################
-#
-# set_vba_name()
-#
-# Set the VBA name for the worksheet.
-#
-sub set_vba_name {
-
-    my $self         = shift;
-    my $vba_codemame = shift;
-
-    if ( $vba_codemame ) {
-        $self->{_vba_codename} = $vba_codemame;
-    }
-    else {
-        $self->{_vba_codename} = $self->{_name};
-    }
-}
-
-
-###############################################################################
-#
-# Internal methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _table_function_to_formula
-#
-# Convert a table total function to a worksheet formula.
-#
-sub _table_function_to_formula {
-
-    my $function = shift;
-    my $col_name = shift;
-    my $formula  = '';
-
-    my %subtotals = (
-        average   => 101,
-        countNums => 102,
-        count     => 103,
-        max       => 104,
-        min       => 105,
-        stdDev    => 107,
-        sum       => 109,
-        var       => 110,
-    );
-
-    if ( exists $subtotals{$function} ) {
-        my $func_num = $subtotals{$function};
-        $formula = qq{SUBTOTAL($func_num,[$col_name])};
-    }
-    else {
-        carp "Unsupported function '$function' in add_table()";
-    }
-
-    return $formula;
-}
-
-
-###############################################################################
-#
-# _set_spark_color()
-#
-# Set the sparkline colour.
-#
-sub _set_spark_color {
-
-    my $self        = shift;
-    my $sparkline   = shift;
-    my $param       = shift;
-    my $user_color  = shift;
-    my $spark_color = '_' . $user_color;
-
-    return unless $param->{$user_color};
-
-    $sparkline->{$spark_color} =
-      { _rgb => $self->_get_palette_color( $param->{$user_color} ) };
-}
-
-
-###############################################################################
-#
-# _get_palette_color()
-#
-# Convert from an Excel internal colour index to a XML style #RRGGBB index
-# based on the default or user defined values in the Workbook palette.
-#
-sub _get_palette_color {
-
-    my $self    = shift;
-    my $index   = shift;
-    my $palette = $self->{_palette};
-
-    # Handle colours in #XXXXXX RGB format.
-    if ( $index =~ m/^#([0-9A-F]{6})$/i ) {
-        return "FF" . uc( $1 );
-    }
-
-    # Adjust the colour index.
-    $index -= 8;
-
-    # Palette is passed in from the Workbook class.
-    my @rgb = @{ $palette->[$index] };
-
-    return sprintf "FF%02X%02X%02X", @rgb[0, 1, 2];
-}
-
-
-###############################################################################
-#
-# _substitute_cellref()
-#
-# Substitute an Excel cell reference in A1 notation for  zero based row and
-# column values in an argument list.
-#
-# Ex: ("A4", "Hello") is converted to (3, 0, "Hello").
-#
-sub _substitute_cellref {
-
-    my $self = shift;
-    my $cell = uc( shift );
-
-    # Convert a column range: 'A:A' or 'B:G'.
-    # A range such as A:A is equivalent to A1:Rowmax, so add rows as required
-    if ( $cell =~ /\$?([A-Z]{1,3}):\$?([A-Z]{1,3})/ ) {
-        my ( $row1, $col1 ) = $self->_cell_to_rowcol( $1 . '1' );
-        my ( $row2, $col2 ) =
-          $self->_cell_to_rowcol( $2 . $self->{_xls_rowmax} );
-        return $row1, $col1, $row2, $col2, @_;
-    }
-
-    # Convert a cell range: 'A1:B7'
-    if ( $cell =~ /\$?([A-Z]{1,3}\$?\d+):\$?([A-Z]{1,3}\$?\d+)/ ) {
-        my ( $row1, $col1 ) = $self->_cell_to_rowcol( $1 );
-        my ( $row2, $col2 ) = $self->_cell_to_rowcol( $2 );
-        return $row1, $col1, $row2, $col2, @_;
-    }
-
-    # Convert a cell reference: 'A1' or 'AD2000'
-    if ( $cell =~ /\$?([A-Z]{1,3}\$?\d+)/ ) {
-        my ( $row1, $col1 ) = $self->_cell_to_rowcol( $1 );
-        return $row1, $col1, @_;
-
-    }
-
-    croak( "Unknown cell reference $cell" );
-}
-
-
-###############################################################################
-#
-# _cell_to_rowcol($cell_ref)
-#
-# Convert an Excel cell reference in A1 notation to a zero based row and column
-# reference; converts C1 to (0, 2).
-#
-# See also: http://www.perlmonks.org/index.pl?node_id=270352
-#
-# Returns: ($row, $col, $row_absolute, $col_absolute)
-#
-#
-sub _cell_to_rowcol {
-
-    my $self = shift;
-
-    my $cell = $_[0];
-    $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--;
-
-    # TODO Check row and column range
-    return $row, $col, $row_abs, $col_abs;
-}
-
-
-###############################################################################
-#
-# _xl_rowcol_to_cell($row, $col)
-#
-# Optimised version of xl_rowcol_to_cell from Utility.pm for the inner loop
-# of _write_cell().
-#
-
-our @col_names = ( 'A' .. 'XFD' );
-
-sub _xl_rowcol_to_cell {
-    return $col_names[ $_[1] ] . ( $_[0] + 1 );
-}
-
-
-###############################################################################
-#
-# _sort_pagebreaks()
-#
-# This is an internal method that is used to filter elements of the array of
-# pagebreaks used in the _store_hbreak() and _store_vbreak() methods. It:
-#   1. Removes duplicate entries from the list.
-#   2. Sorts the list.
-#   3. Removes 0 from the list if present.
-#
-sub _sort_pagebreaks {
-
-    my $self = shift;
-
-    return () unless @_;
-
-    my %hash;
-    my @array;
-
-    @hash{@_} = undef;    # Hash slice to remove duplicates
-    @array = sort { $a <=> $b } keys %hash;    # Numerical sort
-    shift @array if $array[0] == 0;            # Remove zero
-
-    # The Excel 2007 specification says that the maximum number of page breaks
-    # is 1026. However, in practice it is actually 1023.
-    my $max_num_breaks = 1023;
-    splice( @array, $max_num_breaks ) if @array > $max_num_breaks;
-
-    return @array;
-}
-
-
-###############################################################################
-#
-# _check_dimensions($row, $col, $ignore_row, $ignore_col)
-#
-# Check that $row and $col are valid and store max and min values for use in
-# other methods/elements.
-#
-# The $ignore_row/$ignore_col flags is used to indicate that we wish to
-# perform the dimension check without storing the value.
-#
-# The ignore flags are use by set_row() and data_validate.
-#
-sub _check_dimensions {
-
-    my $self       = shift;
-    my $row        = $_[0];
-    my $col        = $_[1];
-    my $ignore_row = $_[2];
-    my $ignore_col = $_[3];
-
-
-    return -2 if not defined $row;
-    return -2 if $row >= $self->{_xls_rowmax};
-
-    return -2 if not defined $col;
-    return -2 if $col >= $self->{_xls_colmax};
-
-    # In optimization mode we don't change dimensions for rows that are
-    # already written.
-    if ( !$ignore_row && !$ignore_col && $self->{_optimization} == 1 ) {
-        return -2 if $row < $self->{_previous_row};
-    }
-
-    if ( !$ignore_row ) {
-
-        if ( not defined $self->{_dim_rowmin} or $row < $self->{_dim_rowmin} ) {
-            $self->{_dim_rowmin} = $row;
-        }
-
-        if ( not defined $self->{_dim_rowmax} or $row > $self->{_dim_rowmax} ) {
-            $self->{_dim_rowmax} = $row;
-        }
-    }
-
-    if ( !$ignore_col ) {
-
-        if ( not defined $self->{_dim_colmin} or $col < $self->{_dim_colmin} ) {
-            $self->{_dim_colmin} = $col;
-        }
-
-        if ( not defined $self->{_dim_colmax} or $col > $self->{_dim_colmax} ) {
-            $self->{_dim_colmax} = $col;
-        }
-    }
-
-    return 0;
-}
-
-
-###############################################################################
-#
-#  _position_object_pixels()
-#
-# Calculate the vertices that define the position of a graphical object within
-# the worksheet in pixels.
-#
-#         +------------+------------+
-#         |     A      |      B     |
-#   +-----+------------+------------+
-#   |     |(x1,y1)     |            |
-#   |  1  |(A1)._______|______      |
-#   |     |    |              |     |
-#   |     |    |              |     |
-#   +-----+----|    Object    |-----+
-#   |     |    |              |     |
-#   |  2  |    |______________.     |
-#   |     |            |        (B2)|
-#   |     |            |     (x2,y2)|
-#   +---- +------------+------------+
-#
-# Example of an object that covers some of the area from cell A1 to cell B2.
-#
-# Based on the width and height of the object we need to calculate 8 vars:
-#
-#     $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
-#
-# We also calculate the absolute x and y position of the top left vertex of
-# the object. This is required for images.
-#
-#    $x_abs, $y_abs
-#
-# The width and height of the cells that the object occupies can be variable
-# and have to be taken into account.
-#
-# The values of $col_start and $row_start are passed in from the calling
-# function. The values of $col_end and $row_end are calculated by subtracting
-# the width and height of the object from the width and height of the
-# underlying cells.
-#
-sub _position_object_pixels {
-
-    my $self = shift;
-
-    my $col_start;    # Col containing upper left corner of object.
-    my $x1;           # Distance to left side of object.
-
-    my $row_start;    # Row containing top left corner of object.
-    my $y1;           # Distance to top of object.
-
-    my $col_end;      # Col containing lower right corner of object.
-    my $x2;           # Distance to right side of object.
-
-    my $row_end;      # Row containing bottom right corner of object.
-    my $y2;           # Distance to bottom of object.
-
-    my $width;        # Width of object frame.
-    my $height;       # Height of object frame.
-
-    my $x_abs = 0;    # Absolute distance to left side of object.
-    my $y_abs = 0;    # Absolute distance to top  side of object.
-
-    ( $col_start, $row_start, $x1, $y1, $width, $height ) = @_;
-
-    # Adjust start column for negative offsets.
-    while ( $x1 < 0 && $col_start > 0) {
-        $x1 += $self->_size_col( $col_start  - 1);
-        $col_start--;
-    }
-
-    # Adjust start row for negative offsets.
-    while ( $y1 < 0 && $row_start > 0) {
-        $y1 += $self->_size_row( $row_start - 1);
-        $row_start--;
-    }
-
-    # Ensure that the image isn't shifted off the page at top left.
-    $x1 = 0 if $x1 < 0;
-    $y1 = 0 if $y1 < 0;
-
-    # Calculate the absolute x offset of the top-left vertex.
-    if ( $self->{_col_size_changed} ) {
-        for my $col_id ( 0 .. $col_start -1 ) {
-            $x_abs += $self->_size_col( $col_id );
-        }
-    }
-    else {
-        # Optimisation for when the column widths haven't changed.
-        $x_abs += $self->{_default_col_pixels} * $col_start;
-    }
-
-    $x_abs += $x1;
-
-    # Calculate the absolute y offset of the top-left vertex.
-    # Store the column change to allow optimisations.
-    if ( $self->{_row_size_changed} ) {
-        for my $row_id ( 0 .. $row_start -1 ) {
-            $y_abs += $self->_size_row( $row_id );
-        }
-    }
-    else {
-        # Optimisation for when the row heights haven't changed.
-        $y_abs += $self->{_default_row_pixels} * $row_start;
-    }
-
-    $y_abs += $y1;
-
-
-    # Adjust start column for offsets that are greater than the col width.
-    while ( $x1 >= $self->_size_col( $col_start ) ) {
-        $x1 -= $self->_size_col( $col_start );
-        $col_start++;
-    }
-
-    # Adjust start row for offsets that are greater than the row height.
-    while ( $y1 >= $self->_size_row( $row_start ) ) {
-        $y1 -= $self->_size_row( $row_start );
-        $row_start++;
-    }
-
-    # Initialise end cell to the same as the start cell.
-    $col_end = $col_start;
-    $row_end = $row_start;
-
-    $width  = $width + $x1;
-    $height = $height + $y1;
-
-
-    # Subtract the underlying cell widths to find the end cell of the object.
-    while ( $width >= $self->_size_col( $col_end ) ) {
-        $width -= $self->_size_col( $col_end );
-        $col_end++;
-    }
-
-
-    # Subtract the underlying cell heights to find the end cell of the object.
-    while ( $height >= $self->_size_row( $row_end ) ) {
-        $height -= $self->_size_row( $row_end );
-        $row_end++;
-    }
-
-    # The end vertices are whatever is left from the width and height.
-    $x2 = $width;
-    $y2 = $height;
-
-    return (
-        $col_start, $row_start, $x1, $y1,
-        $col_end,   $row_end,   $x2, $y2,
-        $x_abs,     $y_abs
-
-    );
-}
-
-
-###############################################################################
-#
-#  _position_object_emus()
-#
-# Calculate the vertices that define the position of a graphical object within
-# the worksheet in EMUs.
-#
-# The vertices are expressed as English Metric Units (EMUs). There are 12,700
-# EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per pixel.
-#
-sub _position_object_emus {
-
-    my $self       = shift;
-
-    my (
-        $col_start, $row_start, $x1, $y1,
-        $col_end,   $row_end,   $x2, $y2,
-        $x_abs,     $y_abs
-
-    ) = $self->_position_object_pixels( @_ );
-
-    # Convert the pixel values to EMUs. See above.
-    $x1    = int( 0.5 + 9_525 * $x1 );
-    $y1    = int( 0.5 + 9_525 * $y1 );
-    $x2    = int( 0.5 + 9_525 * $x2 );
-    $y2    = int( 0.5 + 9_525 * $y2 );
-    $x_abs = int( 0.5 + 9_525 * $x_abs );
-    $y_abs = int( 0.5 + 9_525 * $y_abs );
-
-    return (
-        $col_start, $row_start, $x1, $y1,
-        $col_end,   $row_end,   $x2, $y2,
-        $x_abs,     $y_abs
-
-    );
-}
-
-
-###############################################################################
-#
-#  _position_shape_emus()
-#
-# Calculate the vertices that define the position of a shape object within
-# the worksheet in EMUs.  Save the vertices with the object.
-#
-# The vertices are expressed as English Metric Units (EMUs). There are 12,700
-# EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per pixel.
-#
-sub _position_shape_emus {
-
-    my $self  = shift;
-    my $shape = shift;
-
-    my (
-        $col_start, $row_start, $x1, $y1,    $col_end,
-        $row_end,   $x2,        $y2, $x_abs, $y_abs
-      )
-      = $self->_position_object_pixels(
-        $shape->{_column_start},
-        $shape->{_row_start},
-        $shape->{_x_offset},
-        $shape->{_y_offset},
-        $shape->{_width} * $shape->{_scale_x},
-        $shape->{_height} * $shape->{_scale_y},
-        $shape->{_drawing}
-      );
-
-    # Now that x2/y2 have been calculated with a potentially negative
-    # width/height we use the absolute value and convert to EMUs.
-    $shape->{_width_emu}  = int( abs( $shape->{_width} * 9_525 ) );
-    $shape->{_height_emu} = int( abs( $shape->{_height} * 9_525 ) );
-
-    $shape->{_column_start} = int( $col_start );
-    $shape->{_row_start}    = int( $row_start );
-    $shape->{_column_end}   = int( $col_end );
-    $shape->{_row_end}      = int( $row_end );
-
-    # Convert the pixel values to EMUs. See above.
-    $shape->{_x1}    = int( $x1 * 9_525 );
-    $shape->{_y1}    = int( $y1 * 9_525 );
-    $shape->{_x2}    = int( $x2 * 9_525 );
-    $shape->{_y2}    = int( $y2 * 9_525 );
-    $shape->{_x_abs} = int( $x_abs * 9_525 );
-    $shape->{_y_abs} = int( $y_abs * 9_525 );
-}
-
-###############################################################################
-#
-# _size_col($col)
-#
-# Convert the width of a cell from user's units to pixels. Excel rounds the
-# column width to the nearest pixel. If the width hasn't been set by the user
-# we use the default value. If the column is hidden it has a value of zero.
-#
-sub _size_col {
-
-    my $self = shift;
-    my $col  = shift;
-
-    my $max_digit_width = 7;    # For Calabri 11.
-    my $padding         = 5;
-    my $pixels;
-
-    # Look up the cell value to see if it has been changed.
-    if ( exists $self->{_col_sizes}->{$col}
-        and defined $self->{_col_sizes}->{$col} )
-    {
-        my $width = $self->{_col_sizes}->{$col};
-
-        # Convert to pixels.
-        if ( $width == 0 ) {
-            $pixels = 0;
-        }
-        elsif ( $width < 1 ) {
-            $pixels = int( $width * ( $max_digit_width + $padding ) + 0.5 );
-        }
-        else {
-            $pixels = int( $width * $max_digit_width + 0.5 ) + $padding;
-        }
-    }
-    else {
-        $pixels = $self->{_default_col_pixels};
-    }
-
-    return $pixels;
-}
-
-
-###############################################################################
-#
-# _size_row($row)
-#
-# Convert the height of a cell from user's units to pixels. If the height
-# hasn't been set by the user we use the default value. If the row is hidden
-# it has a value of zero.
-#
-sub _size_row {
-
-    my $self = shift;
-    my $row  = shift;
-    my $pixels;
-
-    # Look up the cell value to see if it has been changed
-    if ( exists $self->{_row_sizes}->{$row} ) {
-        my $height = $self->{_row_sizes}->{$row};
-
-        if ( $height == 0 ) {
-            $pixels = 0;
-        }
-        else {
-            $pixels = int( 4 / 3 * $height );
-        }
-    }
-    else {
-        $pixels = int( 4 / 3 * $self->{_default_row_height} );
-    }
-
-    return $pixels;
-}
-
-
-###############################################################################
-#
-# _get_shared_string_index()
-#
-# Add a string to the shared string table, if it isn't already there, and
-# return the string index.
-#
-sub _get_shared_string_index {
-
-    my $self = shift;
-    my $str  = shift;
-
-    # Add the string to the shared string table.
-    if ( not exists ${ $self->{_str_table} }->{$str} ) {
-        ${ $self->{_str_table} }->{$str} = ${ $self->{_str_unique} }++;
-    }
-
-    ${ $self->{_str_total} }++;
-    my $index = ${ $self->{_str_table} }->{$str};
-
-    return $index;
-}
-
-
-###############################################################################
-#
-# insert_chart( $row, $col, $chart, $x, $y, $x_scale, $y_scale )
-#
-# Insert a chart into a worksheet. The $chart argument should be a Chart
-# object or else it is assumed to be a filename of an external binary file.
-# The latter is for backwards compatibility.
-#
-sub insert_chart {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column.
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    my $row      = $_[0];
-    my $col      = $_[1];
-    my $chart    = $_[2];
-    my $x_offset = $_[3] || 0;
-    my $y_offset = $_[4] || 0;
-    my $x_scale  = $_[5] || 1;
-    my $y_scale  = $_[6] || 1;
-
-    croak "Insufficient arguments in insert_chart()" unless @_ >= 3;
-
-    if ( ref $chart ) {
-
-        # Check for a Chart object.
-        croak "Not a Chart object in insert_chart()"
-          unless $chart->isa( 'Excel::Writer::XLSX::Chart' );
-
-        # Check that the chart is an embedded style chart.
-        croak "Not a embedded style Chart object in insert_chart()"
-          unless $chart->{_embedded};
-
-    }
-
-    # Ensure a chart isn't inserted more than once.
-    if (   $chart->{_already_inserted}
-        || $chart->{_combined} && $chart->{_combined}->{_already_inserted} )
-    {
-        carp "Chart cannot be inserted in a worksheet more than once";
-        return;
-    }
-    else {
-        $chart->{_already_inserted} = 1;
-
-        if ( $chart->{_combined} ) {
-            $chart->{_combined}->{_already_inserted} = 1;
-        }
-    }
-
-    # Use the values set with $chart->set_size(), if any.
-    $x_scale  = $chart->{_x_scale}  if $chart->{_x_scale} != 1;
-    $y_scale  = $chart->{_y_scale}  if $chart->{_y_scale} != 1;
-    $x_offset = $chart->{_x_offset} if $chart->{_x_offset};
-    $y_offset = $chart->{_y_offset} if $chart->{_y_offset};
-
-    push @{ $self->{_charts} },
-      [ $row, $col, $chart, $x_offset, $y_offset, $x_scale, $y_scale ];
-}
-
-
-###############################################################################
-#
-# _prepare_chart()
-#
-# Set up chart/drawings.
-#
-sub _prepare_chart {
-
-    my $self         = shift;
-    my $index        = shift;
-    my $chart_id     = shift;
-    my $drawing_id   = shift;
-    my $drawing_type = 1;
-
-    my ( $row, $col, $chart, $x_offset, $y_offset, $x_scale, $y_scale ) =
-      @{ $self->{_charts}->[$index] };
-
-    $chart->{_id} = $chart_id - 1;
-
-    # Use user specified dimensions, if any.
-    my $width  = $chart->{_width}  if $chart->{_width};
-    my $height = $chart->{_height} if $chart->{_height};
-
-    $width  = int( 0.5 + ( $width  * $x_scale ) );
-    $height = int( 0.5 + ( $height * $y_scale ) );
-
-    my @dimensions =
-      $self->_position_object_emus( $col, $row, $x_offset, $y_offset, $width,
-        $height);
-
-    # Set the chart name for the embedded object if it has been specified.
-    my $name = $chart->{_chart_name};
-
-    # Create a Drawing object to use with worksheet unless one already exists.
-    if ( !$self->{_drawing} ) {
-
-        my $drawing = Excel::Writer::XLSX::Drawing->new();
-        $drawing->_add_drawing_object( $drawing_type, @dimensions, 0, 0,
-            $name );
-        $drawing->{_embedded} = 1;
-
-        $self->{_drawing} = $drawing;
-
-        push @{ $self->{_external_drawing_links} },
-          [ '/drawing', '../drawings/drawing' . $drawing_id . '.xml' ];
-    }
-    else {
-        my $drawing = $self->{_drawing};
-        $drawing->_add_drawing_object( $drawing_type, @dimensions, 0, 0,
-            $name );
-
-    }
-
-    push @{ $self->{_drawing_links} },
-      [ '/chart', '../charts/chart' . $chart_id . '.xml' ];
-}
-
-
-###############################################################################
-#
-# _get_range_data
-#
-# Returns a range of data from the worksheet _table to be used in chart
-# cached data. Strings are returned as SST ids and decoded in the workbook.
-# Return undefs for data that doesn't exist since Excel can chart series
-# with data missing.
-#
-sub _get_range_data {
-
-    my $self = shift;
-
-    return () if $self->{_optimization};
-
-    my @data;
-    my ( $row_start, $col_start, $row_end, $col_end ) = @_;
-
-    # TODO. Check for worksheet limits.
-
-    # Iterate through the table data.
-    for my $row_num ( $row_start .. $row_end ) {
-
-        # Store undef if row doesn't exist.
-        if ( !exists $self->{_table}->{$row_num} ) {
-            push @data, undef;
-            next;
-        }
-
-        for my $col_num ( $col_start .. $col_end ) {
-
-            if ( my $cell = $self->{_table}->{$row_num}->{$col_num} ) {
-
-                my $type  = $cell->[0];
-                my $token = $cell->[1];
-
-
-                if ( $type eq 'n' ) {
-
-                    # Store a number.
-                    push @data, $token;
-                }
-                elsif ( $type eq 's' ) {
-
-                    # Store a string.
-                    if ( $self->{_optimization} == 0 ) {
-                        push @data, { 'sst_id' => $token };
-                    }
-                    else {
-                        push @data, $token;
-                    }
-                }
-                elsif ( $type eq 'f' ) {
-
-                    # Store a formula.
-                    push @data, $cell->[3] || 0;
-                }
-                elsif ( $type eq 'a' ) {
-
-                    # Store an array formula.
-                    push @data, $cell->[4] || 0;
-                }
-                elsif ( $type eq 'b' ) {
-
-                    # Store a empty cell.
-                    push @data, '';
-                }
-            }
-            else {
-
-                # Store undef if col doesn't exist.
-                push @data, undef;
-            }
-        }
-    }
-
-    return @data;
-}
-
-
-###############################################################################
-#
-# insert_image( $row, $col, $filename, $x, $y, $x_scale, $y_scale )
-#
-# Insert an image into the worksheet.
-#
-sub insert_image {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column.
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    my $row      = $_[0];
-    my $col      = $_[1];
-    my $image    = $_[2];
-    my $x_offset = $_[3] || 0;
-    my $y_offset = $_[4] || 0;
-    my $x_scale  = $_[5] || 1;
-    my $y_scale  = $_[6] || 1;
-
-    croak "Insufficient arguments in insert_image()" unless @_ >= 3;
-    croak "Couldn't locate $image: $!" unless -e $image;
-
-    push @{ $self->{_images} },
-      [ $row, $col, $image, $x_offset, $y_offset, $x_scale, $y_scale ];
-}
-
-
-###############################################################################
-#
-# _prepare_image()
-#
-# Set up image/drawings.
-#
-sub _prepare_image {
-
-    my $self         = shift;
-    my $index        = shift;
-    my $image_id     = shift;
-    my $drawing_id   = shift;
-    my $width        = shift;
-    my $height       = shift;
-    my $name         = shift;
-    my $image_type   = shift;
-    my $x_dpi        = shift;
-    my $y_dpi        = shift;
-    my $drawing_type = 2;
-    my $drawing;
-
-    my ( $row, $col, $image, $x_offset, $y_offset, $x_scale, $y_scale ) =
-      @{ $self->{_images}->[$index] };
-
-    $width  *= $x_scale;
-    $height *= $y_scale;
-
-    $width  *= 96 / $x_dpi;
-    $height *= 96 / $y_dpi;
-
-    my @dimensions =
-      $self->_position_object_emus( $col, $row, $x_offset, $y_offset, $width,
-        $height);
-
-    # Convert from pixels to emus.
-    $width  = int( 0.5 + ( $width * 9_525 ) );
-    $height = int( 0.5 + ( $height * 9_525 ) );
-
-    # Create a Drawing object to use with worksheet unless one already exists.
-    if ( !$self->{_drawing} ) {
-
-        $drawing = Excel::Writer::XLSX::Drawing->new();
-        $drawing->{_embedded} = 1;
-
-        $self->{_drawing} = $drawing;
-
-        push @{ $self->{_external_drawing_links} },
-          [ '/drawing', '../drawings/drawing' . $drawing_id . '.xml' ];
-    }
-    else {
-        $drawing = $self->{_drawing};
-    }
-
-    $drawing->_add_drawing_object( $drawing_type, @dimensions, $width, $height,
-        $name );
-
-
-    push @{ $self->{_drawing_links} },
-      [ '/image', '../media/image' . $image_id . '.' . $image_type ];
-}
-
-
-###############################################################################
-#
-# _prepare_header_image()
-#
-# Set up an image without a drawing object for header/footer images.
-#
-sub _prepare_header_image {
-
-    my $self       = shift;
-    my $image_id   = shift;
-    my $width      = shift;
-    my $height     = shift;
-    my $name       = shift;
-    my $image_type = shift;
-    my $position   = shift;
-    my $x_dpi      = shift;
-    my $y_dpi      = shift;
-
-    # Strip the extension from the filename.
-    $name =~ s/\.[^\.]+$//;
-
-    push @{ $self->{_header_images_array} },
-      [ $width, $height, $name, $position, $x_dpi, $y_dpi ];
-
-    push @{ $self->{_vml_drawing_links} },
-      [ '/image', '../media/image' . $image_id . '.' . $image_type ];
-}
-
-
-###############################################################################
-#
-# insert_shape( $row, $col, $shape, $x, $y, $x_scale, $y_scale )
-#
-# Insert a shape into the worksheet.
-#
-sub insert_shape {
-
-    my $self = shift;
-
-    # Check for a cell reference in A1 notation and substitute row and column.
-    if ( $_[0] =~ /^\D/ ) {
-        @_ = $self->_substitute_cellref( @_ );
-    }
-
-    # Check the number of arguments.
-    croak "Insufficient arguments in insert_shape()" unless @_ >= 3;
-
-    my $shape = $_[2];
-
-    # Verify we are being asked to insert a "shape" object.
-    croak "Not a Shape object in insert_shape()"
-      unless $shape->isa( 'Excel::Writer::XLSX::Shape' );
-
-    # Set the shape properties.
-    $shape->{_row_start}    = $_[0];
-    $shape->{_column_start} = $_[1];
-    $shape->{_x_offset}     = $_[3] || 0;
-    $shape->{_y_offset}     = $_[4] || 0;
-
-    # Override shape scale if supplied as an argument.  Otherwise, use the
-    # existing shape scale factors.
-    $shape->{_scale_x} = $_[5] if defined $_[5];
-    $shape->{_scale_y} = $_[6] if defined $_[6];
-
-    # Assign a shape ID.
-    my $needs_id = 1;
-    while ( $needs_id ) {
-        my $id = $shape->{_id} || 0;
-        my $used = exists $self->{_shape_hash}->{$id} ? 1 : 0;
-
-        # Test if shape ID is already used. Otherwise assign a new one.
-        if ( !$used && $id != 0 ) {
-            $needs_id = 0;
-        }
-        else {
-            $shape->{_id} = ++$self->{_last_shape_id};
-        }
-    }
-
-    $shape->{_element} = $#{ $self->{_shapes} } + 1;
-
-    # Allow lookup of entry into shape array by shape ID.
-    $self->{_shape_hash}->{ $shape->{_id} } = $shape->{_element};
-
-    # Create link to Worksheet color palette.
-    $shape->{_palette} = $self->{_palette};
-
-    if ( $shape->{_stencil} ) {
-
-        # Insert a copy of the shape, not a reference so that the shape is
-        # used as a stencil. Previously stamped copies don't get modified
-        # if the stencil is modified.
-        my $insert = { %{$shape} };
-
-       # For connectors change x/y coords based on location of connected shapes.
-        $self->_auto_locate_connectors( $insert );
-
-        # Bless the copy into this class, so AUTOLOADED _get, _set methods
-        #still work on the child.
-        bless $insert, ref $shape;
-
-        push @{ $self->{_shapes} }, $insert;
-        return $insert;
-    }
-    else {
-
-       # For connectors change x/y coords based on location of connected shapes.
-        $self->_auto_locate_connectors( $shape );
-
-        # Insert a link to the shape on the list of shapes. Connection to
-        # the parent shape is maintained
-        push @{ $self->{_shapes} }, $shape;
-        return $shape;
-    }
-}
-
-
-###############################################################################
-#
-# _prepare_shape()
-#
-# Set up drawing shapes
-#
-sub _prepare_shape {
-
-    my $self       = shift;
-    my $index      = shift;
-    my $drawing_id = shift;
-    my $shape      = $self->{_shapes}->[$index];
-    my $drawing;
-    my $drawing_type = 3;
-
-    # Create a Drawing object to use with worksheet unless one already exists.
-    if ( !$self->{_drawing} ) {
-
-        $drawing              = Excel::Writer::XLSX::Drawing->new();
-        $drawing->{_embedded} = 1;
-        $self->{_drawing}     = $drawing;
-
-        push @{ $self->{_external_drawing_links} },
-          [ '/drawing', '../drawings/drawing' . $drawing_id . '.xml' ];
-
-        $self->{_has_shapes} = 1;
-    }
-    else {
-        $drawing = $self->{_drawing};
-    }
-
-    # Validate the he shape against various rules.
-    $self->_validate_shape( $shape, $index );
-
-    $self->_position_shape_emus( $shape );
-
-    my @dimensions = (
-        $shape->{_column_start}, $shape->{_row_start},
-        $shape->{_x1},           $shape->{_y1},
-        $shape->{_column_end},   $shape->{_row_end},
-        $shape->{_x2},           $shape->{_y2},
-        $shape->{_x_abs},        $shape->{_y_abs},
-        $shape->{_width_emu},    $shape->{_height_emu},
-    );
-
-    $drawing->_add_drawing_object( $drawing_type, @dimensions, $shape->{_name},
-        $shape );
-}
-
-
-###############################################################################
-#
-# _auto_locate_connectors()
-#
-# Re-size connector shapes if they are connected to other shapes.
-#
-sub _auto_locate_connectors {
-
-    my $self  = shift;
-    my $shape = shift;
-
-    # Valid connector shapes.
-    my $connector_shapes = {
-        straightConnector => 1,
-        Connector         => 1,
-        bentConnector     => 1,
-        curvedConnector   => 1,
-        line              => 1,
-    };
-
-    my $shape_base = $shape->{_type};
-
-    # Remove the number of segments from end of type.
-    chop $shape_base;
-
-    $shape->{_connect} = $connector_shapes->{$shape_base} ? 1 : 0;
-
-    return unless $shape->{_connect};
-
-    # Both ends have to be connected to size it.
-    return unless ( $shape->{_start} and $shape->{_end} );
-
-    # Both ends need to provide info about where to connect.
-    return unless ( $shape->{_start_side} and $shape->{_end_side} );
-
-    my $sid = $shape->{_start};
-    my $eid = $shape->{_end};
-
-    my $slink_id = $self->{_shape_hash}->{$sid};
-    my ( $sls, $els );
-    if ( defined $slink_id ) {
-        $sls = $self->{_shapes}->[$slink_id];    # Start linked shape.
-    }
-    else {
-        warn "missing start connection for '$shape->{_name}', id=$sid\n";
-        return;
-    }
-
-    my $elink_id = $self->{_shape_hash}->{$eid};
-    if ( defined $elink_id ) {
-        $els = $self->{_shapes}->[$elink_id];    # Start linked shape.
-    }
-    else {
-        warn "missing end connection for '$shape->{_name}', id=$eid\n";
-        return;
-    }
-
-    # Assume shape connections are to the middle of an object, and
-    # not a corner (for now).
-    my $connect_type = $shape->{_start_side} . $shape->{_end_side};
-    my $smidx        = $sls->{_x_offset} + $sls->{_width} / 2;
-    my $emidx        = $els->{_x_offset} + $els->{_width} / 2;
-    my $smidy        = $sls->{_y_offset} + $sls->{_height} / 2;
-    my $emidy        = $els->{_y_offset} + $els->{_height} / 2;
-    my $netx         = abs( $smidx - $emidx );
-    my $nety         = abs( $smidy - $emidy );
-
-    if ( $connect_type eq 'bt' ) {
-        my $sy = $sls->{_y_offset} + $sls->{_height};
-        my $ey = $els->{_y_offset};
-
-        $shape->{_width} = abs( int( $emidx - $smidx ) );
-        $shape->{_x_offset} = int( min( $smidx, $emidx ) );
-        $shape->{_height} =
-          abs(
-            int( $els->{_y_offset} - ( $sls->{_y_offset} + $sls->{_height} ) )
-          );
-        $shape->{_y_offset} = int(
-            min( ( $sls->{_y_offset} + $sls->{_height} ), $els->{_y_offset} ) );
-        $shape->{_flip_h} = ( $smidx < $emidx ) ? 1 : 0;
-        $shape->{_rotation} = 90;
-
-        if ( $sy > $ey ) {
-            $shape->{_flip_v} = 1;
-
-            # Create 3 adjustments for an end shape vertically above a
-            # start shape. Adjustments count from the upper left object.
-            if ( $#{ $shape->{_adjustments} } < 0 ) {
-                $shape->{_adjustments} = [ -10, 50, 110 ];
-            }
-
-            $shape->{_type} = 'bentConnector5';
-        }
-    }
-    elsif ( $connect_type eq 'rl' ) {
-        $shape->{_width} =
-          abs(
-            int( $els->{_x_offset} - ( $sls->{_x_offset} + $sls->{_width} ) ) );
-        $shape->{_height} = abs( int( $emidy - $smidy ) );
-        $shape->{_x_offset} =
-          min( $sls->{_x_offset} + $sls->{_width}, $els->{_x_offset} );
-        $shape->{_y_offset} = min( $smidy, $emidy );
-
-        $shape->{_flip_h} = 1 if ( $smidx < $emidx ) and ( $smidy > $emidy );
-        $shape->{_flip_h} = 1 if ( $smidx > $emidx ) and ( $smidy < $emidy );
-        if ( $smidx > $emidx ) {
-
-            # Create 3 adjustments if end shape is left of start
-            if ( $#{ $shape->{_adjustments} } < 0 ) {
-                $shape->{_adjustments} = [ -10, 50, 110 ];
-            }
-
-            $shape->{_type} = 'bentConnector5';
-        }
-    }
-    else {
-        warn "Connection $connect_type not implemented yet\n";
-    }
-}
-
-
-###############################################################################
-#
-# _validate_shape()
-#
-# Check shape attributes to ensure they are valid.
-#
-sub _validate_shape {
-
-    my $self  = shift;
-    my $shape = shift;
-    my $index = shift;
-
-    if ( !grep ( /^$shape->{_align}$/, qw[l ctr r just] ) ) {
-        croak "Shape $index ($shape->{_type}) alignment ($shape->{align}), "
-          . "not in ('l', 'ctr', 'r', 'just')\n";
-    }
-
-    if ( !grep ( /^$shape->{_valign}$/, qw[t ctr b] ) ) {
-        croak "Shape $index ($shape->{_type}) vertical alignment "
-          . "($shape->{valign}), not ('t', 'ctr', 'b')\n";
-    }
-}
-
-
-###############################################################################
-#
-# _prepare_vml_objects()
-#
-# Turn the HoH that stores the comments into an array for easier handling
-# and set the external links for comments and buttons.
-#
-sub _prepare_vml_objects {
-
-    my $self           = shift;
-    my $vml_data_id    = shift;
-    my $vml_shape_id   = shift;
-    my $vml_drawing_id = shift;
-    my $comment_id     = shift;
-    my @comments;
-
-
-    # We sort the comments by row and column but that isn't strictly required.
-    my @rows = sort { $a <=> $b } keys %{ $self->{_comments} };
-
-    for my $row ( @rows ) {
-        my @cols = sort { $a <=> $b } keys %{ $self->{_comments}->{$row} };
-
-        for my $col ( @cols ) {
-
-            # Set comment visibility if required and not already user defined.
-            if ( $self->{_comments_visible} ) {
-                if ( !defined $self->{_comments}->{$row}->{$col}->[4] ) {
-                    $self->{_comments}->{$row}->{$col}->[4] = 1;
-                }
-            }
-
-            # Set comment author if not already user defined.
-            if ( !defined $self->{_comments}->{$row}->{$col}->[3] ) {
-                $self->{_comments}->{$row}->{$col}->[3] =
-                  $self->{_comments_author};
-            }
-
-            push @comments, $self->{_comments}->{$row}->{$col};
-        }
-    }
-
-    push @{ $self->{_external_vml_links} },
-      [ '/vmlDrawing', '../drawings/vmlDrawing' . $vml_drawing_id . '.vml' ];
-
-    if ( $self->{_has_comments} ) {
-
-        $self->{_comments_array} = \@comments;
-
-        push @{ $self->{_external_comment_links} },
-          [ '/comments', '../comments' . $comment_id . '.xml' ];
-    }
-
-    my $count         = scalar @comments;
-    my $start_data_id = $vml_data_id;
-
-    # The VML o:idmap data id contains a comma separated range when there is
-    # more than one 1024 block of comments, like this: data="1,2".
-    for my $i ( 1 .. int( $count / 1024 ) ) {
-        $vml_data_id = "$vml_data_id," . ( $start_data_id + $i );
-    }
-
-    $self->{_vml_data_id}  = $vml_data_id;
-    $self->{_vml_shape_id} = $vml_shape_id;
-
-    return $count;
-}
-
-
-###############################################################################
-#
-# _prepare_header_vml_objects()
-#
-# Set up external linkage for VML header/footer images.
-#
-sub _prepare_header_vml_objects {
-
-    my $self           = shift;
-    my $vml_header_id  = shift;
-    my $vml_drawing_id = shift;
-
-    $self->{_vml_header_id} = $vml_header_id;
-
-    push @{ $self->{_external_vml_links} },
-      [ '/vmlDrawing', '../drawings/vmlDrawing' . $vml_drawing_id . '.vml' ];
-}
-
-
-###############################################################################
-#
-# _prepare_tables()
-#
-# Set the table ids for the worksheet tables.
-#
-sub _prepare_tables {
-
-    my $self     = shift;
-    my $table_id = shift;
-    my $seen     = shift;
-
-
-    for my $table ( @{ $self->{_tables} } ) {
-
-        $table-> {_id} = $table_id;
-
-        # Set the table name unless defined by the user.
-        if ( !defined $table->{_name} ) {
-
-            # Set a default name.
-            $table->{_name} = 'Table' . $table_id;
-        }
-
-        # Check for duplicate table names.
-        my $name = lc $table->{_name};
-
-        if ( exists $seen->{$name} ) {
-            die "error: invalid duplicate table name '$table->{_name}' found";
-        }
-        else {
-            $seen->{$name} = 1;
-        }
-
-        # Store the link used for the rels file.
-        my $link = [ '/table', '../tables/table' . $table_id . '.xml' ];
-
-        push @{ $self->{_external_table_links} }, $link;
-        $table_id++;
-    }
-}
-
-
-###############################################################################
-#
-# _comment_params()
-#
-# This method handles the additional optional parameters to write_comment() as
-# well as calculating the comment object position and vertices.
-#
-sub _comment_params {
-
-    my $self = shift;
-
-    my $row    = shift;
-    my $col    = shift;
-    my $string = shift;
-
-    my $default_width  = 128;
-    my $default_height = 74;
-
-    my %params = (
-        author     => undef,
-        color      => 81,
-        start_cell => undef,
-        start_col  => undef,
-        start_row  => undef,
-        visible    => undef,
-        width      => $default_width,
-        height     => $default_height,
-        x_offset   => undef,
-        x_scale    => 1,
-        y_offset   => undef,
-        y_scale    => 1,
-        font       => 'Tahoma',
-        font_size  => 8,
-    );
-
-
-    # Overwrite the defaults with any user supplied values. Incorrect or
-    # misspelled parameters are silently ignored.
-    %params = ( %params, @_ );
-
-
-    # Ensure that a width and height have been set.
-    $params{width}  = $default_width  if not $params{width};
-    $params{height} = $default_height if not $params{height};
-
-
-    # Limit the string to the max number of chars.
-    my $max_len = 32767;
-
-    if ( length( $string ) > $max_len ) {
-        $string = substr( $string, 0, $max_len );
-    }
-
-
-    # Set the comment background colour.
-    my $color    = $params{color};
-    my $color_id = &Excel::Writer::XLSX::Format::_get_color( $color );
-
-    if ( $color_id =~ m/^#[0-9A-F]{6}$/i ) {
-        $params{color} = $color_id;
-    }
-    elsif ( $color_id == 0 ) {
-        $params{color} = '#ffffe1';
-    }
-    else {
-        my $palette = $self->{_palette};
-
-        # Get the RGB color from the palette.
-        my @rgb = @{ $palette->[ $color_id - 8 ] };
-        my $rgb_color = sprintf "%02x%02x%02x", @rgb[0, 1, 2];
-
-        # Minor modification to allow comparison testing. Change RGB colors
-        # from long format, ffcc00 to short format fc0 used by VML.
-        $rgb_color =~ s/^([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/$1$2$3/;
-
-        $params{color} = sprintf "#%s [%d]", $rgb_color, $color_id;
-    }
-
-
-    # Convert a cell reference to a row and column.
-    if ( defined $params{start_cell} ) {
-        my ( $row, $col ) = $self->_substitute_cellref( $params{start_cell} );
-        $params{start_row} = $row;
-        $params{start_col} = $col;
-    }
-
-
-    # Set the default start cell and offsets for the comment. These are
-    # generally fixed in relation to the parent cell. However there are
-    # some edge cases for cells at the, er, edges.
-    #
-    my $row_max = $self->{_xls_rowmax};
-    my $col_max = $self->{_xls_colmax};
-
-    if ( not defined $params{start_row} ) {
-
-        if    ( $row == 0 )            { $params{start_row} = 0 }
-        elsif ( $row == $row_max - 3 ) { $params{start_row} = $row_max - 7 }
-        elsif ( $row == $row_max - 2 ) { $params{start_row} = $row_max - 6 }
-        elsif ( $row == $row_max - 1 ) { $params{start_row} = $row_max - 5 }
-        else                           { $params{start_row} = $row - 1 }
-    }
-
-    if ( not defined $params{y_offset} ) {
-
-        if    ( $row == 0 )            { $params{y_offset} = 2 }
-        elsif ( $row == $row_max - 3 ) { $params{y_offset} = 16 }
-        elsif ( $row == $row_max - 2 ) { $params{y_offset} = 16 }
-        elsif ( $row == $row_max - 1 ) { $params{y_offset} = 14 }
-        else                           { $params{y_offset} = 10 }
-    }
-
-    if ( not defined $params{start_col} ) {
-
-        if    ( $col == $col_max - 3 ) { $params{start_col} = $col_max - 6 }
-        elsif ( $col == $col_max - 2 ) { $params{start_col} = $col_max - 5 }
-        elsif ( $col == $col_max - 1 ) { $params{start_col} = $col_max - 4 }
-        else                           { $params{start_col} = $col + 1 }
-    }
-
-    if ( not defined $params{x_offset} ) {
-
-        if    ( $col == $col_max - 3 ) { $params{x_offset} = 49 }
-        elsif ( $col == $col_max - 2 ) { $params{x_offset} = 49 }
-        elsif ( $col == $col_max - 1 ) { $params{x_offset} = 49 }
-        else                           { $params{x_offset} = 15 }
-    }
-
-
-    # Scale the size of the comment box if required.
-    if ( $params{x_scale} ) {
-        $params{width} = $params{width} * $params{x_scale};
-    }
-
-    if ( $params{y_scale} ) {
-        $params{height} = $params{height} * $params{y_scale};
-    }
-
-    # Round the dimensions to the nearest pixel.
-    $params{width}  = int( 0.5 + $params{width} );
-    $params{height} = int( 0.5 + $params{height} );
-
-    # Calculate the positions of comment object.
-    my @vertices = $self->_position_object_pixels(
-        $params{start_col}, $params{start_row}, $params{x_offset},
-        $params{y_offset},  $params{width},     $params{height}
-    );
-
-    # Add the width and height for VML.
-    push @vertices, ( $params{width}, $params{height} );
-
-    return (
-        $row,
-        $col,
-        $string,
-
-        $params{author},
-        $params{visible},
-        $params{color},
-
-        [@vertices],
-
-        $params{font},
-        $params{font_size},
-    );
-}
-
-
-###############################################################################
-#
-# _button_params()
-#
-# This method handles the parameters passed to insert_button() as well as
-# calculating the comment object position and vertices.
-#
-sub _button_params {
-
-    my $self   = shift;
-    my $row    = shift;
-    my $col    = shift;
-    my $params = shift;
-    my $button = { _row => $row, _col => $col };
-
-    my $button_number = 1 + @{ $self->{_buttons_array} };
-
-    # Set the button caption.
-    my $caption = $params->{caption};
-
-    # Set a default caption if none was specified by user.
-    if ( !defined $caption ) {
-        $caption = 'Button ' . $button_number;
-    }
-
-    $button->{_font}->{_caption} = $caption;
-
-
-    # Set the macro name.
-    if ( $params->{macro} ) {
-        $button->{_macro} = '[0]!' . $params->{macro};
-    }
-    else {
-        $button->{_macro} = '[0]!Button' . $button_number . '_Click';
-    }
-
-
-    # Ensure that a width and height have been set.
-    my $default_width  = $self->{_default_col_pixels};
-    my $default_height = $self->{_default_row_pixels};
-    $params->{width}  = $default_width  if !$params->{width};
-    $params->{height} = $default_height if !$params->{height};
-
-    # Set the x/y offsets.
-    $params->{x_offset}  = 0  if !$params->{x_offset};
-    $params->{y_offset}  = 0  if !$params->{y_offset};
-
-    # Scale the size of the comment box if required.
-    if ( $params->{x_scale} ) {
-        $params->{width} = $params->{width} * $params->{x_scale};
-    }
-
-    if ( $params->{y_scale} ) {
-        $params->{height} = $params->{height} * $params->{y_scale};
-    }
-
-    # Round the dimensions to the nearest pixel.
-    $params->{width}  = int( 0.5 + $params->{width} );
-    $params->{height} = int( 0.5 + $params->{height} );
-
-    $params->{start_row} = $row;
-    $params->{start_col} = $col;
-
-    # Calculate the positions of comment object.
-    my @vertices = $self->_position_object_pixels(
-        $params->{start_col}, $params->{start_row}, $params->{x_offset},
-        $params->{y_offset},  $params->{width},     $params->{height}
-    );
-
-    # Add the width and height for VML.
-    push @vertices, ( $params->{width}, $params->{height} );
-
-    $button->{_vertices} = \@vertices;
-
-    return $button;
-}
-
-
-###############################################################################
-#
-# Deprecated methods for backwards compatibility.
-#
-###############################################################################
-
-
-# This method was mainly only required for Excel 5.
-sub write_url_range { }
-
-# Deprecated UTF-16 method required for the Excel 5 format.
-sub write_utf16be_string {
-
-    my $self = shift;
-
-    # Convert A1 notation if present.
-    @_ = $self->_substitute_cellref( @_ ) if $_[0] =~ /^\D/;
-
-    # Check the number of args.
-    return -1 if @_ < 3;
-
-    # Convert UTF16 string to UTF8.
-    require Encode;
-    my $utf8_string = Encode::decode( 'UTF-16BE', $_[2] );
-
-    return $self->write_string( $_[0], $_[1], $utf8_string, $_[3] );
-}
-
-# Deprecated UTF-16 method required for the Excel 5 format.
-sub write_utf16le_string {
-
-    my $self = shift;
-
-    # Convert A1 notation if present.
-    @_ = $self->_substitute_cellref( @_ ) if $_[0] =~ /^\D/;
-
-    # Check the number of args.
-    return -1 if @_ < 3;
-
-    # Convert UTF16 string to UTF8.
-    require Encode;
-    my $utf8_string = Encode::decode( 'UTF-16LE', $_[2] );
-
-    return $self->write_string( $_[0], $_[1], $utf8_string, $_[3] );
-}
-
-# No longer required. Was used to avoid slow formula parsing.
-sub store_formula {
-
-    my $self   = shift;
-    my $string = shift;
-
-    my @tokens = split /(\$?[A-I]?[A-Z]\$?\d+)/, $string;
-
-    return \@tokens;
-}
-
-# No longer required. Was used to avoid slow formula parsing.
-sub repeat_formula {
-
-    my $self = shift;
-
-    # Convert A1 notation if present.
-    @_ = $self->_substitute_cellref( @_ ) if $_[0] =~ /^\D/;
-
-    if ( @_ < 2 ) { return -1 }    # Check the number of args
-
-    my $row         = shift;       # Zero indexed row
-    my $col         = shift;       # Zero indexed column
-    my $formula_ref = shift;       # Array ref with formula tokens
-    my $format      = shift;       # XF format
-    my @pairs       = @_;          # Pattern/replacement pairs
-
-
-    # Enforce an even number of arguments in the pattern/replacement list.
-    croak "Odd number of elements in pattern/replacement list" if @pairs % 2;
-
-    # Check that $formula is an array ref.
-    croak "Not a valid formula" if ref $formula_ref ne 'ARRAY';
-
-    my @tokens = @$formula_ref;
-
-    # Allow the user to specify the result of the formula by appending a
-    # result => $value pair to the end of the arguments.
-    my $value = undef;
-    if ( @pairs && $pairs[-2] eq 'result' ) {
-        $value = pop @pairs;
-        pop @pairs;
-    }
-
-    # Make the substitutions.
-    while ( @pairs ) {
-        my $pattern = shift @pairs;
-        my $replace = shift @pairs;
-
-        foreach my $token ( @tokens ) {
-            last if $token =~ s/$pattern/$replace/;
-        }
-    }
-
-    my $formula = join '', @tokens;
-
-    return $self->write_formula( $row, $col, $formula, $format, $value );
-}
-
-
-###############################################################################
-#
-# XML writing methods.
-#
-###############################################################################
-
-
-###############################################################################
-#
-# _write_worksheet()
-#
-# Write the <worksheet> element. This is the root element of Worksheet.
-#
-sub _write_worksheet {
-
-    my $self                   = shift;
-    my $schema                 = 'http://schemas.openxmlformats.org/';
-    my $xmlns                  = $schema . 'spreadsheetml/2006/main';
-    my $xmlns_r                = $schema . 'officeDocument/2006/relationships';
-    my $xmlns_mc               = $schema . 'markup-compatibility/2006';
-
-    my @attributes = (
-        'xmlns'   => $xmlns,
-        'xmlns:r' => $xmlns_r,
-    );
-
-    if ( $self->{_excel_version} == 2010 ) {
-        push @attributes, ( 'xmlns:mc' => $xmlns_mc );
-
-        push @attributes,
-          (     'xmlns:x14ac' => 'http://schemas.microsoft.com/'
-              . 'office/spreadsheetml/2009/9/ac' );
-
-        push @attributes, ( 'mc:Ignorable' => 'x14ac' );
-
-    }
-
-    $self->xml_start_tag( 'worksheet', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_sheet_pr()
-#
-# Write the <sheetPr> element for Sheet level properties.
-#
-sub _write_sheet_pr {
-
-    my $self       = shift;
-    my @attributes = ();
-
-    if (   !$self->{_fit_page}
-        && !$self->{_filter_on}
-        && !$self->{_tab_color}
-        && !$self->{_outline_changed}
-        && !$self->{_vba_codename} )
-    {
-        return;
-    }
-
-
-    my $codename = $self->{_vba_codename};
-    push @attributes, ( 'codeName'   => $codename ) if $codename;
-    push @attributes, ( 'filterMode' => 1 )         if $self->{_filter_on};
-
-    if (   $self->{_fit_page}
-        || $self->{_tab_color}
-        || $self->{_outline_changed} )
-    {
-        $self->xml_start_tag( 'sheetPr', @attributes );
-        $self->_write_tab_color();
-        $self->_write_outline_pr();
-        $self->_write_page_set_up_pr();
-        $self->xml_end_tag( 'sheetPr' );
-    }
-    else {
-        $self->xml_empty_tag( 'sheetPr', @attributes );
-    }
-}
-
-
-##############################################################################
-#
-# _write_page_set_up_pr()
-#
-# Write the <pageSetUpPr> element.
-#
-sub _write_page_set_up_pr {
-
-    my $self = shift;
-
-    return unless $self->{_fit_page};
-
-    my @attributes = ( 'fitToPage' => 1 );
-
-    $self->xml_empty_tag( 'pageSetUpPr', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_dimension()
-#
-# Write the <dimension> element. This specifies the range of cells in the
-# worksheet. As a special case, empty spreadsheets use 'A1' as a range.
-#
-sub _write_dimension {
-
-    my $self = shift;
-    my $ref;
-
-    if ( !defined $self->{_dim_rowmin} && !defined $self->{_dim_colmin} ) {
-
-        # If the min dims are undefined then no dimensions have been set
-        # and we use the default 'A1'.
-        $ref = 'A1';
-    }
-    elsif ( !defined $self->{_dim_rowmin} && defined $self->{_dim_colmin} ) {
-
-        # If the row dims aren't set but the column dims are then they
-        # have been changed via set_column().
-
-        if ( $self->{_dim_colmin} == $self->{_dim_colmax} ) {
-
-            # The dimensions are a single cell and not a range.
-            $ref = xl_rowcol_to_cell( 0, $self->{_dim_colmin} );
-        }
-        else {
-
-            # The dimensions are a cell range.
-            my $cell_1 = xl_rowcol_to_cell( 0, $self->{_dim_colmin} );
-            my $cell_2 = xl_rowcol_to_cell( 0, $self->{_dim_colmax} );
-
-            $ref = $cell_1 . ':' . $cell_2;
-        }
-
-    }
-    elsif ($self->{_dim_rowmin} == $self->{_dim_rowmax}
-        && $self->{_dim_colmin} == $self->{_dim_colmax} )
-    {
-
-        # The dimensions are a single cell and not a range.
-        $ref = xl_rowcol_to_cell( $self->{_dim_rowmin}, $self->{_dim_colmin} );
-    }
-    else {
-
-        # The dimensions are a cell range.
-        my $cell_1 =
-          xl_rowcol_to_cell( $self->{_dim_rowmin}, $self->{_dim_colmin} );
-        my $cell_2 =
-          xl_rowcol_to_cell( $self->{_dim_rowmax}, $self->{_dim_colmax} );
-
-        $ref = $cell_1 . ':' . $cell_2;
-    }
-
-
-    my @attributes = ( 'ref' => $ref );
-
-    $self->xml_empty_tag( 'dimension', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_sheet_views()
-#
-# Write the <sheetViews> element.
-#
-sub _write_sheet_views {
-
-    my $self = shift;
-
-    my @attributes = ();
-
-    $self->xml_start_tag( 'sheetViews', @attributes );
-    $self->_write_sheet_view();
-    $self->xml_end_tag( 'sheetViews' );
-}
-
-
-###############################################################################
-#
-# _write_sheet_view()
-#
-# Write the <sheetView> element.
-#
-# Sample structure:
-#     <sheetView
-#         showGridLines="0"
-#         showRowColHeaders="0"
-#         showZeros="0"
-#         rightToLeft="1"
-#         tabSelected="1"
-#         showRuler="0"
-#         showOutlineSymbols="0"
-#         view="pageLayout"
-#         zoomScale="121"
-#         zoomScaleNormal="121"
-#         workbookViewId="0"
-#      />
-#
-sub _write_sheet_view {
-
-    my $self             = shift;
-    my $gridlines        = $self->{_screen_gridlines};
-    my $show_zeros       = $self->{_show_zeros};
-    my $right_to_left    = $self->{_right_to_left};
-    my $tab_selected     = $self->{_selected};
-    my $view             = $self->{_page_view};
-    my $zoom             = $self->{_zoom};
-    my $workbook_view_id = 0;
-    my @attributes       = ();
-
-    # Hide screen gridlines if required
-    if ( !$gridlines ) {
-        push @attributes, ( 'showGridLines' => 0 );
-    }
-
-    # Hide zeroes in cells.
-    if ( !$show_zeros ) {
-        push @attributes, ( 'showZeros' => 0 );
-    }
-
-    # Display worksheet right to left for Hebrew, Arabic and others.
-    if ( $right_to_left ) {
-        push @attributes, ( 'rightToLeft' => 1 );
-    }
-
-    # Show that the sheet tab is selected.
-    if ( $tab_selected ) {
-        push @attributes, ( 'tabSelected' => 1 );
-    }
-
-
-    # Turn outlines off. Also required in the outlinePr element.
-    if ( !$self->{_outline_on} ) {
-        push @attributes, ( "showOutlineSymbols" => 0 );
-    }
-
-    # Set the page view/layout mode if required.
-    # TODO. Add pageBreakPreview mode when requested.
-    if ( $view ) {
-        push @attributes, ( 'view' => 'pageLayout' );
-    }
-
-    # Set the zoom level.
-    if ( $zoom != 100 ) {
-        push @attributes, ( 'zoomScale' => $zoom ) unless $view;
-        push @attributes, ( 'zoomScaleNormal' => $zoom )
-          if $self->{_zoom_scale_normal};
-    }
-
-    push @attributes, ( 'workbookViewId' => $workbook_view_id );
-
-    if ( @{ $self->{_panes} } || @{ $self->{_selections} } ) {
-        $self->xml_start_tag( 'sheetView', @attributes );
-        $self->_write_panes();
-        $self->_write_selections();
-        $self->xml_end_tag( 'sheetView' );
-    }
-    else {
-        $self->xml_empty_tag( 'sheetView', @attributes );
-    }
-}
-
-
-###############################################################################
-#
-# _write_selections()
-#
-# Write the <selection> elements.
-#
-sub _write_selections {
-
-    my $self = shift;
-
-    for my $selection ( @{ $self->{_selections} } ) {
-        $self->_write_selection( @$selection );
-    }
-}
-
-
-###############################################################################
-#
-# _write_selection()
-#
-# Write the <selection> element.
-#
-sub _write_selection {
-
-    my $self        = shift;
-    my $pane        = shift;
-    my $active_cell = shift;
-    my $sqref       = shift;
-    my @attributes  = ();
-
-    push @attributes, ( 'pane'       => $pane )        if $pane;
-    push @attributes, ( 'activeCell' => $active_cell ) if $active_cell;
-    push @attributes, ( 'sqref'      => $sqref )       if $sqref;
-
-    $self->xml_empty_tag( 'selection', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_sheet_format_pr()
-#
-# Write the <sheetFormatPr> element.
-#
-sub _write_sheet_format_pr {
-
-    my $self               = shift;
-    my $base_col_width     = 10;
-    my $default_row_height = $self->{_default_row_height};
-    my $row_level          = $self->{_outline_row_level};
-    my $col_level          = $self->{_outline_col_level};
-    my $zero_height        = $self->{_default_row_zeroed};
-
-    my @attributes = ( 'defaultRowHeight' => $default_row_height );
-
-    if ( $self->{_default_row_height} != $self->{_original_row_height} ) {
-        push @attributes, ( 'customHeight' => 1 );
-    }
-
-    if ( $self->{_default_row_zeroed} ) {
-        push @attributes, ( 'zeroHeight' => 1 );
-    }
-
-    push @attributes, ( 'outlineLevelRow' => $row_level ) if $row_level;
-    push @attributes, ( 'outlineLevelCol' => $col_level ) if $col_level;
-
-    if ( $self->{_excel_version} == 2010 ) {
-        push @attributes, ( 'x14ac:dyDescent' => '0.25' );
-    }
-
-    $self->xml_empty_tag( 'sheetFormatPr', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_cols()
-#
-# Write the <cols> element and <col> sub elements.
-#
-sub _write_cols {
-
-    my $self = shift;
-
-    # Exit unless some column have been formatted.
-    return unless %{ $self->{_colinfo} };
-
-    $self->xml_start_tag( 'cols' );
-
-    for my $col ( sort keys %{ $self->{_colinfo} } ) {
-        $self->_write_col_info( @{ $self->{_colinfo}->{$col} } );
-    }
-
-    $self->xml_end_tag( 'cols' );
-}
-
-
-##############################################################################
-#
-# _write_col_info()
-#
-# Write the <col> element.
-#
-sub _write_col_info {
-
-    my $self         = shift;
-    my $min          = $_[0] || 0;    # First formatted column.
-    my $max          = $_[1] || 0;    # Last formatted column.
-    my $width        = $_[2];         # Col width in user units.
-    my $format       = $_[3];         # Format index.
-    my $hidden       = $_[4] || 0;    # Hidden flag.
-    my $level        = $_[5] || 0;    # Outline level.
-    my $collapsed    = $_[6] || 0;    # Outline level.
-    my $custom_width = 1;
-    my $xf_index     = 0;
-
-    # Get the format index.
-    if ( ref( $format ) ) {
-        $xf_index = $format->get_xf_index();
-    }
-
-    # Set the Excel default col width.
-    if ( !defined $width ) {
-        if ( !$hidden ) {
-            $width        = 8.43;
-            $custom_width = 0;
-        }
-        else {
-            $width = 0;
-        }
-    }
-    else {
-
-        # Width is defined but same as default.
-        if ( $width == 8.43 ) {
-            $custom_width = 0;
-        }
-    }
-
-
-    # Convert column width from user units to character width.
-    my $max_digit_width = 7;    # For Calabri 11.
-    my $padding         = 5;
-
-    if ( $width > 0 ) {
-        if ( $width < 1 ) {
-            $width =
-              int( ( int( $width * ($max_digit_width + $padding) + 0.5 ) ) /
-                  $max_digit_width *
-                  256 ) / 256;
-        }
-        else {
-            $width =
-              int( ( int( $width * $max_digit_width + 0.5 ) + $padding ) /
-                  $max_digit_width *
-                  256 ) / 256;
-        }
-    }
-
-    my @attributes = (
-        'min'   => $min + 1,
-        'max'   => $max + 1,
-        'width' => $width,
-    );
-
-    push @attributes, ( 'style'        => $xf_index ) if $xf_index;
-    push @attributes, ( 'hidden'       => 1 )         if $hidden;
-    push @attributes, ( 'customWidth'  => 1 )         if $custom_width;
-    push @attributes, ( 'outlineLevel' => $level )    if $level;
-    push @attributes, ( 'collapsed'    => 1 )         if $collapsed;
-
-
-    $self->xml_empty_tag( 'col', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_sheet_data()
-#
-# Write the <sheetData> element.
-#
-sub _write_sheet_data {
-
-    my $self = shift;
-
-    if ( not defined $self->{_dim_rowmin} ) {
-
-        # If the dimensions aren't defined then there is no data to write.
-        $self->xml_empty_tag( 'sheetData' );
-    }
-    else {
-        $self->xml_start_tag( 'sheetData' );
-        $self->_write_rows();
-        $self->xml_end_tag( 'sheetData' );
-
-    }
-
-}
-
-
-###############################################################################
-#
-# _write_optimized_sheet_data()
-#
-# Write the <sheetData> element when the memory optimisation is on. In which
-# case we read the data stored in the temp file and rewrite it to the XML
-# sheet file.
-#
-sub _write_optimized_sheet_data {
-
-    my $self = shift;
-
-    if ( not defined $self->{_dim_rowmin} ) {
-
-        # If the dimensions aren't defined then there is no data to write.
-        $self->xml_empty_tag( 'sheetData' );
-    }
-    else {
-
-        $self->xml_start_tag( 'sheetData' );
-
-        my $xlsx_fh = $self->xml_get_fh();
-        my $cell_fh = $self->{_cell_data_fh};
-
-        my $buffer;
-
-        # Rewind the temp file.
-        seek $cell_fh, 0, 0;
-
-        while ( read( $cell_fh, $buffer, 4_096 ) ) {
-            local $\ = undef;    # Protect print from -l on commandline.
-            print $xlsx_fh $buffer;
-        }
-
-        $self->xml_end_tag( 'sheetData' );
-    }
-}
-
-
-###############################################################################
-#
-# _write_rows()
-#
-# Write out the worksheet data as a series of rows and cells.
-#
-sub _write_rows {
-
-    my $self = shift;
-
-    $self->_calculate_spans();
-
-    for my $row_num ( $self->{_dim_rowmin} .. $self->{_dim_rowmax} ) {
-
-        # Skip row if it doesn't contain row formatting, cell data or a comment.
-        if (   !$self->{_set_rows}->{$row_num}
-            && !$self->{_table}->{$row_num}
-            && !$self->{_comments}->{$row_num} )
-        {
-            next;
-        }
-
-        my $span_index = int( $row_num / 16 );
-        my $span       = $self->{_row_spans}->[$span_index];
-
-        # Write the cells if the row contains data.
-        if ( my $row_ref = $self->{_table}->{$row_num} ) {
-
-            if ( !$self->{_set_rows}->{$row_num} ) {
-                $self->_write_row( $row_num, $span );
-            }
-            else {
-                $self->_write_row( $row_num, $span,
-                    @{ $self->{_set_rows}->{$row_num} } );
-            }
-
-
-            for my $col_num ( $self->{_dim_colmin} .. $self->{_dim_colmax} ) {
-                if ( my $col_ref = $self->{_table}->{$row_num}->{$col_num} ) {
-                    $self->_write_cell( $row_num, $col_num, $col_ref );
-                }
-            }
-
-            $self->xml_end_tag( 'row' );
-        }
-        elsif ( $self->{_comments}->{$row_num} ) {
-
-            $self->_write_empty_row( $row_num, $span,
-                @{ $self->{_set_rows}->{$row_num} } );
-        }
-        else {
-
-            # Row attributes only.
-            $self->_write_empty_row( $row_num, $span,
-                @{ $self->{_set_rows}->{$row_num} } );
-        }
-    }
-}
-
-
-###############################################################################
-#
-# _write_single_row()
-#
-# Write out the worksheet data as a single row with cells. This method is
-# used when memory optimisation is on. A single row is written and the data
-# table is reset. That way only one row of data is kept in memory at any one
-# time. We don't write span data in the optimised case since it is optional.
-#
-sub _write_single_row {
-
-    my $self        = shift;
-    my $current_row = shift || 0;
-    my $row_num     = $self->{_previous_row};
-
-    # Set the new previous row as the current row.
-    $self->{_previous_row} = $current_row;
-
-    # Skip row if it doesn't contain row formatting, cell data or a comment.
-    if (   !$self->{_set_rows}->{$row_num}
-        && !$self->{_table}->{$row_num}
-        && !$self->{_comments}->{$row_num} )
-    {
-        return;
-    }
-
-    # Write the cells if the row contains data.
-    if ( my $row_ref = $self->{_table}->{$row_num} ) {
-
-        if ( !$self->{_set_rows}->{$row_num} ) {
-            $self->_write_row( $row_num );
-        }
-        else {
-            $self->_write_row( $row_num, undef,
-                @{ $self->{_set_rows}->{$row_num} } );
-        }
-
-        for my $col_num ( $self->{_dim_colmin} .. $self->{_dim_colmax} ) {
-            if ( my $col_ref = $self->{_table}->{$row_num}->{$col_num} ) {
-                $self->_write_cell( $row_num, $col_num, $col_ref );
-            }
-        }
-
-        $self->xml_end_tag( 'row' );
-    }
-    else {
-
-        # Row attributes or comments only.
-        $self->_write_empty_row( $row_num, undef,
-            @{ $self->{_set_rows}->{$row_num} } );
-    }
-
-    # Reset table.
-    $self->{_table} = {};
-
-}
-
-
-###############################################################################
-#
-# _calculate_spans()
-#
-# Calculate the "spans" attribute of the <row> tag. This is an XLSX
-# optimisation and isn't strictly required. However, it makes comparing
-# files easier.
-#
-# The span is the same for each block of 16 rows.
-#
-sub _calculate_spans {
-
-    my $self = shift;
-
-    my @spans;
-    my $span_min;
-    my $span_max;
-
-    for my $row_num ( $self->{_dim_rowmin} .. $self->{_dim_rowmax} ) {
-
-        # Calculate spans for cell data.
-        if ( my $row_ref = $self->{_table}->{$row_num} ) {
-
-            for my $col_num ( $self->{_dim_colmin} .. $self->{_dim_colmax} ) {
-                if ( my $col_ref = $self->{_table}->{$row_num}->{$col_num} ) {
-
-                    if ( !defined $span_min ) {
-                        $span_min = $col_num;
-                        $span_max = $col_num;
-                    }
-                    else {
-                        $span_min = $col_num if $col_num < $span_min;
-                        $span_max = $col_num if $col_num > $span_max;
-                    }
-                }
-            }
-        }
-
-        # Calculate spans for comments.
-        if ( defined $self->{_comments}->{$row_num} ) {
-
-            for my $col_num ( $self->{_dim_colmin} .. $self->{_dim_colmax} ) {
-                if ( defined $self->{_comments}->{$row_num}->{$col_num} ) {
-
-                    if ( !defined $span_min ) {
-                        $span_min = $col_num;
-                        $span_max = $col_num;
-                    }
-                    else {
-                        $span_min = $col_num if $col_num < $span_min;
-                        $span_max = $col_num if $col_num > $span_max;
-                    }
-                }
-            }
-        }
-
-        if ( ( ( $row_num + 1 ) % 16 == 0 )
-            || $row_num == $self->{_dim_rowmax} )
-        {
-            my $span_index = int( $row_num / 16 );
-
-            if ( defined $span_min ) {
-                $span_min++;
-                $span_max++;
-                $spans[$span_index] = "$span_min:$span_max";
-                $span_min = undef;
-            }
-        }
-    }
-
-    $self->{_row_spans} = \@spans;
-}
-
-
-###############################################################################
-#
-# _write_row()
-#
-# Write the <row> element.
-#
-sub _write_row {
-
-    my $self      = shift;
-    my $r         = shift;
-    my $spans     = shift;
-    my $height    = shift;
-    my $format    = shift;
-    my $hidden    = shift || 0;
-    my $level     = shift || 0;
-    my $collapsed = shift || 0;
-    my $empty_row = shift || 0;
-    my $xf_index  = 0;
-
-    $height = $self->{_default_row_height} if !defined $height;
-
-    my @attributes = ( 'r' => $r + 1 );
-
-    # Get the format index.
-    if ( ref( $format ) ) {
-        $xf_index = $format->get_xf_index();
-    }
-
-    push @attributes, ( 'spans'        => $spans )    if defined $spans;
-    push @attributes, ( 's'            => $xf_index ) if $xf_index;
-    push @attributes, ( 'customFormat' => 1 )         if $format;
-
-    if ( $height != $self->{_original_row_height} ) {
-        push @attributes, ( 'ht' => $height );
-    }
-
-    push @attributes, ( 'hidden'       => 1 )         if $hidden;
-
-    if ( $height != $self->{_original_row_height} ) {
-        push @attributes, ( 'customHeight' => 1 );
-    }
-
-    push @attributes, ( 'outlineLevel' => $level )    if $level;
-    push @attributes, ( 'collapsed'    => 1 )         if $collapsed;
-
-    if ( $self->{_excel_version} == 2010 ) {
-        push @attributes, ( 'x14ac:dyDescent' => '0.25' );
-    }
-
-    if ( $empty_row ) {
-        $self->xml_empty_tag_unencoded( 'row', @attributes );
-    }
-    else {
-        $self->xml_start_tag_unencoded( 'row', @attributes );
-    }
-}
-
-
-###############################################################################
-#
-# _write_empty_row()
-#
-# Write and empty <row> element, i.e., attributes only, no cell data.
-#
-sub _write_empty_row {
-
-    my $self = shift;
-
-    # Set the $empty_row parameter.
-    $_[7] = 1;
-
-    $self->_write_row( @_ );
-}
-
-
-###############################################################################
-#
-# _write_cell()
-#
-# Write the <cell> element. This is the innermost loop so efficiency is
-# important where possible. The basic methodology is that the data of every
-# cell type is passed in as follows:
-#
-#      [ $row, $col, $aref]
-#
-# The aref, called $cell below, contains the following structure in all types:
-#
-#     [ $type, $token, $xf, @args ]
-#
-# Where $type:  represents the cell type, such as string, number, formula, etc.
-#       $token: is the actual data for the string, number, formula, etc.
-#       $xf:    is the XF format object.
-#       @args:  additional args relevant to the specific data type.
-#
-sub _write_cell {
-
-    my $self     = shift;
-    my $row      = shift;
-    my $col      = shift;
-    my $cell     = shift;
-    my $type     = $cell->[0];
-    my $token    = $cell->[1];
-    my $xf       = $cell->[2];
-    my $xf_index = 0;
-
-    my %error_codes = (
-        '#DIV/0!' => 1,
-        '#N/A'    => 1,
-        '#NAME?'  => 1,
-        '#NULL!'  => 1,
-        '#NUM!'   => 1,
-        '#REF!'   => 1,
-        '#VALUE!' => 1,
-    );
-
-    my %boolean = ( 'TRUE' => 1, 'FALSE' => 0 );
-
-    # Get the format index.
-    if ( ref( $xf ) ) {
-        $xf_index = $xf->get_xf_index();
-    }
-
-    my $range = _xl_rowcol_to_cell( $row, $col );
-    my @attributes = ( 'r' => $range );
-
-    # Add the cell format index.
-    if ( $xf_index ) {
-        push @attributes, ( 's' => $xf_index );
-    }
-    elsif ( $self->{_set_rows}->{$row} && $self->{_set_rows}->{$row}->[1] ) {
-        my $row_xf = $self->{_set_rows}->{$row}->[1];
-        push @attributes, ( 's' => $row_xf->get_xf_index() );
-    }
-    elsif ( $self->{_col_formats}->{$col} ) {
-        my $col_xf = $self->{_col_formats}->{$col};
-        push @attributes, ( 's' => $col_xf->get_xf_index() );
-    }
-
-
-    # Write the various cell types.
-    if ( $type eq 'n' ) {
-
-        # Write a number.
-        $self->xml_number_element( $token, @attributes );
-    }
-    elsif ( $type eq 's' ) {
-
-        # Write a string.
-        if ( $self->{_optimization} == 0 ) {
-            $self->xml_string_element( $token, @attributes );
-        }
-        else {
-
-            my $string = $token;
-
-            # Escape control characters. See SharedString.pm for details.
-            $string =~ s/(_x[0-9a-fA-F]{4}_)/_x005F$1/g;
-            $string =~ s/([\x00-\x08\x0B-\x1F])/sprintf "_x%04X_", ord($1)/eg;
-
-            # Write any rich strings without further tags.
-            if ( $string =~ m{^<r>} && $string =~ m{</r>$} ) {
-
-                $self->xml_rich_inline_string( $string, @attributes );
-            }
-            else {
-
-                # Add attribute to preserve leading or trailing whitespace.
-                my $preserve = 0;
-                if ( $string =~ /^\s/ || $string =~ /\s$/ ) {
-                    $preserve = 1;
-                }
-
-                $self->xml_inline_string( $string, $preserve, @attributes );
-            }
-        }
-    }
-    elsif ( $type eq 'f' ) {
-
-        # Write a formula.
-        my $value = $cell->[3] || 0;
-
-        # Check if the formula value is a string.
-        if (   $value
-            && $value !~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ )
-        {
-            if ( exists $boolean{$value} ) {
-                push @attributes, ( 't' => 'b' );
-                $value = $boolean{$value};
-            }
-            elsif ( exists $error_codes{$value} ) {
-                push @attributes, ( 't' => 'e' );
-            }
-            else {
-                push @attributes, ( 't' => 'str' );
-                $value = Excel::Writer::XLSX::Package::XMLwriter::_escape_data(
-                    $value );
-            }
-        }
-
-        $self->xml_formula_element( $token, $value, @attributes );
-
-    }
-    elsif ( $type eq 'a' ) {
-
-        # Write an array formula.
-        $self->xml_start_tag( 'c', @attributes );
-        $self->_write_cell_array_formula( $token, $cell->[3] );
-        $self->_write_cell_value( $cell->[4] );
-        $self->xml_end_tag( 'c' );
-    }
-    elsif ( $type eq 'l' ) {
-
-        # Write a boolean value.
-        push @attributes, ( 't' => 'b' );
-
-        $self->xml_start_tag( 'c', @attributes );
-        $self->_write_cell_value( $cell->[1] );
-        $self->xml_end_tag( 'c' );
-    }
-    elsif ( $type eq 'b' ) {
-
-        # Write a empty cell.
-        $self->xml_empty_tag( 'c', @attributes );
-    }
-}
-
-
-###############################################################################
-#
-# _write_cell_value()
-#
-# Write the cell value <v> element.
-#
-sub _write_cell_value {
-
-    my $self = shift;
-    my $value = defined $_[0] ? $_[0] : '';
-
-    $self->xml_data_element( 'v', $value );
-}
-
-
-###############################################################################
-#
-# _write_cell_formula()
-#
-# Write the cell formula <f> element.
-#
-sub _write_cell_formula {
-
-    my $self = shift;
-    my $formula = defined $_[0] ? $_[0] : '';
-
-    $self->xml_data_element( 'f', $formula );
-}
-
-
-###############################################################################
-#
-# _write_cell_array_formula()
-#
-# Write the cell array formula <f> element.
-#
-sub _write_cell_array_formula {
-
-    my $self    = shift;
-    my $formula = shift;
-    my $range   = shift;
-
-    my @attributes = ( 't' => 'array', 'ref' => $range );
-
-    $self->xml_data_element( 'f', $formula, @attributes );
-}
-
-
-##############################################################################
-#
-# _write_sheet_calc_pr()
-#
-# Write the <sheetCalcPr> element for the worksheet calculation properties.
-#
-sub _write_sheet_calc_pr {
-
-    my $self              = shift;
-    my $full_calc_on_load = 1;
-
-    my @attributes = ( 'fullCalcOnLoad' => $full_calc_on_load );
-
-    $self->xml_empty_tag( 'sheetCalcPr', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_phonetic_pr()
-#
-# Write the <phoneticPr> element.
-#
-sub _write_phonetic_pr {
-
-    my $self    = shift;
-    my $font_id = 0;
-    my $type    = 'noConversion';
-
-    my @attributes = (
-        'fontId' => $font_id,
-        'type'   => $type,
-    );
-
-    $self->xml_empty_tag( 'phoneticPr', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_page_margins()
-#
-# Write the <pageMargins> element.
-#
-sub _write_page_margins {
-
-    my $self = shift;
-
-    my @attributes = (
-        'left'   => $self->{_margin_left},
-        'right'  => $self->{_margin_right},
-        'top'    => $self->{_margin_top},
-        'bottom' => $self->{_margin_bottom},
-        'header' => $self->{_margin_header},
-        'footer' => $self->{_margin_footer},
-    );
-
-    $self->xml_empty_tag( 'pageMargins', @attributes );
-}
-
-
-###############################################################################
-#
-# _write_page_setup()
-#
-# Write the <pageSetup> element.
-#
-# The following is an example taken from Excel.
-#
-# <pageSetup
-#     paperSize="9"
-#     scale="110"
-#     fitToWidth="2"
-#     fitToHeight="2"
-#     pageOrder="overThenDown"
-#     orientation="portrait"
-#     blackAndWhite="1"
-#     draft="1"
-#     horizontalDpi="200"
-#     verticalDpi="200"
-#     r:id="rId1"
-# />
-#
-sub _write_page_setup {
-
-    my $self       = shift;
-    my @attributes = ();
-
-    return unless $self->{_page_setup_changed};
-
-    # Set paper size.
-    if ( $self->{_paper_size} ) {
-        push @attributes, ( 'paperSize' => $self->{_paper_size} );
-    }
-
-    # Set the print_scale
-    if ( $self->{_print_scale} != 100 ) {
-        push @attributes, ( 'scale' => $self->{_print_scale} );
-    }
-
-    # Set the "Fit to page" properties.
-    if ( $self->{_fit_page} && $self->{_fit_width} != 1 ) {
-        push @attributes, ( 'fitToWidth' => $self->{_fit_width} );
-    }
-
-    if ( $self->{_fit_page} && $self->{_fit_height} != 1 ) {
-        push @attributes, ( 'fitToHeight' => $self->{_fit_height} );
-    }
-
-    # Set the page print direction.
-    if ( $self->{_page_order} ) {
-        push @attributes, ( 'pageOrder' => "overThenDown" );
-    }
-
-    # Set start page.
-    if ( $self->{_page_start} > 1 ) {
-        push @attributes, ( 'firstPageNumber' => $self->{_page_start} );
-    }
-
-    # Set page orientation.
-    if ( $self->{_orientation} == 0 ) {
-        push @attributes, ( 'orientation' => 'landscape' );
-    }
-    else {
-        push @attributes, ( 'orientation' => 'portrait' );
-    }
-
-    # Set print in black and white option.
-    if ( $self->{_black_white} ) {
-        push @attributes, ( 'blackAndWhite' => 1 );
-    }
-
-    # Set start page.
-    if ( $self->{_page_start} != 0 ) {
-        push @attributes, ( 'useFirstPageNumber' => 1 );
-    }
-
-    # Set the DPI. Mainly only for testing.
-    if ( $self->{_horizontal_dpi} ) {
-        push @attributes, ( 'horizontalDpi' => $self->{_horizontal_dpi} );
-    }
-
-    if ( $self->{_vertical_dpi} ) {
-        push @attributes, ( 'verticalDpi' => $self->{_vertical_dpi} );
-    }
-
-
-    $self->xml_empty_tag( 'pageSetup', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_merge_cells()
-#
-# Write the <mergeCells> element.
-#
-sub _write_merge_cells {
-
-    my $self         = shift;
-    my $merged_cells = $self->{_merge};
-    my $count        = @$merged_cells;
-
-    return unless $count;
-
-    my @attributes = ( 'count' => $count );
-
-    $self->xml_start_tag( 'mergeCells', @attributes );
-
-    for my $merged_range ( @$merged_cells ) {
-
-        # Write the mergeCell element.
-        $self->_write_merge_cell( $merged_range );
-    }
-
-    $self->xml_end_tag( 'mergeCells' );
-}
-
-
-##############################################################################
-#
-# _write_merge_cell()
-#
-# Write the <mergeCell> element.
-#
-sub _write_merge_cell {
-
-    my $self         = shift;
-    my $merged_range = shift;
-    my ( $row_min, $col_min, $row_max, $col_max ) = @$merged_range;
-
-
-    # Convert the merge dimensions to a cell range.
-    my $cell_1 = xl_rowcol_to_cell( $row_min, $col_min );
-    my $cell_2 = xl_rowcol_to_cell( $row_max, $col_max );
-    my $ref    = $cell_1 . ':' . $cell_2;
-
-    my @attributes = ( 'ref' => $ref );
-
-    $self->xml_empty_tag( 'mergeCell', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_print_options()
-#
-# Write the <printOptions> element.
-#
-sub _write_print_options {
-
-    my $self       = shift;
-    my @attributes = ();
-
-    return unless $self->{_print_options_changed};
-
-    # Set horizontal centering.
-    if ( $self->{_hcenter} ) {
-        push @attributes, ( 'horizontalCentered' => 1 );
-    }
-
-    # Set vertical centering.
-    if ( $self->{_vcenter} ) {
-        push @attributes, ( 'verticalCentered' => 1 );
-    }
-
-    # Enable row and column headers.
-    if ( $self->{_print_headers} ) {
-        push @attributes, ( 'headings' => 1 );
-    }
-
-    # Set printed gridlines.
-    if ( $self->{_print_gridlines} ) {
-        push @attributes, ( 'gridLines' => 1 );
-    }
-
-
-    $self->xml_empty_tag( 'printOptions', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_header_footer()
-#
-# Write the <headerFooter> element.
-#
-sub _write_header_footer {
-
-    my $self       = shift;
-    my @attributes = ();
-
-    if ( !$self->{_header_footer_scales} ) {
-        push @attributes, ( 'scaleWithDoc' => 0 );
-    }
-
-    if ( !$self->{_header_footer_aligns} ) {
-        push @attributes, ( 'alignWithMargins' => 0 );
-    }
-
-    if ( $self->{_header_footer_changed} ) {
-        $self->xml_start_tag( 'headerFooter', @attributes );
-        $self->_write_odd_header() if $self->{_header};
-        $self->_write_odd_footer() if $self->{_footer};
-        $self->xml_end_tag( 'headerFooter' );
-    }
-    elsif ( $self->{_excel2003_style} ) {
-        $self->xml_empty_tag( 'headerFooter', @attributes );
-    }
-}
-
-
-##############################################################################
-#
-# _write_odd_header()
-#
-# Write the <oddHeader> element.
-#
-sub _write_odd_header {
-
-    my $self = shift;
-    my $data = $self->{_header};
-
-    $self->xml_data_element( 'oddHeader', $data );
-}
-
-
-##############################################################################
-#
-# _write_odd_footer()
-#
-# Write the <oddFooter> element.
-#
-sub _write_odd_footer {
-
-    my $self = shift;
-    my $data = $self->{_footer};
-
-    $self->xml_data_element( 'oddFooter', $data );
-}
-
-
-##############################################################################
-#
-# _write_row_breaks()
-#
-# Write the <rowBreaks> element.
-#
-sub _write_row_breaks {
-
-    my $self = shift;
-
-    my @page_breaks = $self->_sort_pagebreaks( @{ $self->{_hbreaks} } );
-    my $count       = scalar @page_breaks;
-
-    return unless @page_breaks;
-
-    my @attributes = (
-        'count'            => $count,
-        'manualBreakCount' => $count,
-    );
-
-    $self->xml_start_tag( 'rowBreaks', @attributes );
-
-    for my $row_num ( @page_breaks ) {
-        $self->_write_brk( $row_num, 16383 );
-    }
-
-    $self->xml_end_tag( 'rowBreaks' );
-}
-
-
-##############################################################################
-#
-# _write_col_breaks()
-#
-# Write the <colBreaks> element.
-#
-sub _write_col_breaks {
-
-    my $self = shift;
-
-    my @page_breaks = $self->_sort_pagebreaks( @{ $self->{_vbreaks} } );
-    my $count       = scalar @page_breaks;
-
-    return unless @page_breaks;
-
-    my @attributes = (
-        'count'            => $count,
-        'manualBreakCount' => $count,
-    );
-
-    $self->xml_start_tag( 'colBreaks', @attributes );
-
-    for my $col_num ( @page_breaks ) {
-        $self->_write_brk( $col_num, 1048575 );
-    }
-
-    $self->xml_end_tag( 'colBreaks' );
-}
-
-
-##############################################################################
-#
-# _write_brk()
-#
-# Write the <brk> element.
-#
-sub _write_brk {
-
-    my $self = shift;
-    my $id   = shift;
-    my $max  = shift;
-    my $man  = 1;
-
-    my @attributes = (
-        'id'  => $id,
-        'max' => $max,
-        'man' => $man,
-    );
-
-    $self->xml_empty_tag( 'brk', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_auto_filter()
-#
-# Write the <autoFilter> element.
-#
-sub _write_auto_filter {
-
-    my $self = shift;
-    my $ref  = $self->{_autofilter_ref};
-
-    return unless $ref;
-
-    my @attributes = ( 'ref' => $ref );
-
-    if ( $self->{_filter_on} ) {
-
-        # Autofilter defined active filters.
-        $self->xml_start_tag( 'autoFilter', @attributes );
-
-        $self->_write_autofilters();
-
-        $self->xml_end_tag( 'autoFilter' );
-
-    }
-    else {
-
-        # Autofilter defined without active filters.
-        $self->xml_empty_tag( 'autoFilter', @attributes );
-    }
-
-}
-
-
-###############################################################################
-#
-# _write_autofilters()
-#
-# Function to iterate through the columns that form part of an autofilter
-# range and write the appropriate filters.
-#
-sub _write_autofilters {
-
-    my $self = shift;
-
-    my ( $col1, $col2 ) = @{ $self->{_filter_range} };
-
-    for my $col ( $col1 .. $col2 ) {
-
-        # Skip if column doesn't have an active filter.
-        next unless $self->{_filter_cols}->{$col};
-
-        # Retrieve the filter tokens and write the autofilter records.
-        my @tokens = @{ $self->{_filter_cols}->{$col} };
-        my $type   = $self->{_filter_type}->{$col};
-
-        # Filters are relative to first column in the autofilter.
-        $self->_write_filter_column( $col - $col1, $type, \@tokens );
-    }
-}
-
-
-##############################################################################
-#
-# _write_filter_column()
-#
-# Write the <filterColumn> element.
-#
-sub _write_filter_column {
-
-    my $self    = shift;
-    my $col_id  = shift;
-    my $type    = shift;
-    my $filters = shift;
-
-    my @attributes = ( 'colId' => $col_id );
-
-    $self->xml_start_tag( 'filterColumn', @attributes );
-
-
-    if ( $type == 1 ) {
-
-        # Type == 1 is the new XLSX style filter.
-        $self->_write_filters( @$filters );
-
-    }
-    else {
-
-        # Type == 0 is the classic "custom" filter.
-        $self->_write_custom_filters( @$filters );
-    }
-
-    $self->xml_end_tag( 'filterColumn' );
-}
-
-
-##############################################################################
-#
-# _write_filters()
-#
-# Write the <filters> element.
-#
-sub _write_filters {
-
-    my $self       = shift;
-    my @filters    = @_;
-    my @non_blanks = grep { !/^blanks$/i } @filters;
-    my @attributes = ();
-
-    if ( @filters != @non_blanks ) {
-        @attributes = ( 'blank' => 1 );
-    }
-
-    if ( @filters == 1 && @non_blanks == 0 ) {
-
-        # Special case for blank cells only.
-        $self->xml_empty_tag( 'filters', @attributes );
-    }
-    else {
-
-        # General case.
-        $self->xml_start_tag( 'filters', @attributes );
-
-        for my $filter ( sort @non_blanks ) {
-            $self->_write_filter( $filter );
-        }
-
-        $self->xml_end_tag( 'filters' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_filter()
-#
-# Write the <filter> element.
-#
-sub _write_filter {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->xml_empty_tag( 'filter', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_custom_filters()
-#
-# Write the <customFilters> element.
-#
-sub _write_custom_filters {
-
-    my $self   = shift;
-    my @tokens = @_;
-
-    if ( @tokens == 2 ) {
-
-        # One filter expression only.
-        $self->xml_start_tag( 'customFilters' );
-        $self->_write_custom_filter( @tokens );
-        $self->xml_end_tag( 'customFilters' );
-
-    }
-    else {
-
-        # Two filter expressions.
-
-        my @attributes;
-
-        # Check if the "join" operand is "and" or "or".
-        if ( $tokens[2] == 0 ) {
-            @attributes = ( 'and' => 1 );
-        }
-        else {
-            @attributes = ( 'and' => 0 );
-        }
-
-        # Write the two custom filters.
-        $self->xml_start_tag( 'customFilters', @attributes );
-        $self->_write_custom_filter( $tokens[0], $tokens[1] );
-        $self->_write_custom_filter( $tokens[3], $tokens[4] );
-        $self->xml_end_tag( 'customFilters' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_custom_filter()
-#
-# Write the <customFilter> element.
-#
-sub _write_custom_filter {
-
-    my $self       = shift;
-    my $operator   = shift;
-    my $val        = shift;
-    my @attributes = ();
-
-    my %operators = (
-        1  => 'lessThan',
-        2  => 'equal',
-        3  => 'lessThanOrEqual',
-        4  => 'greaterThan',
-        5  => 'notEqual',
-        6  => 'greaterThanOrEqual',
-        22 => 'equal',
-    );
-
-
-    # Convert the operator from a number to a descriptive string.
-    if ( defined $operators{$operator} ) {
-        $operator = $operators{$operator};
-    }
-    else {
-        croak "Unknown operator = $operator\n";
-    }
-
-    # The 'equal' operator is the default attribute and isn't stored.
-    push @attributes, ( 'operator' => $operator ) unless $operator eq 'equal';
-    push @attributes, ( 'val' => $val );
-
-    $self->xml_empty_tag( 'customFilter', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_hyperlinks()
-#
-# Process any stored hyperlinks in row/col order and write the <hyperlinks>
-# element. The attributes are different for internal and external links.
-#
-sub _write_hyperlinks {
-
-    my $self = shift;
-    my @hlink_refs;
-
-    # Sort the hyperlinks into row order.
-    my @row_nums = sort { $a <=> $b } keys %{ $self->{_hyperlinks} };
-
-    # Exit if there are no hyperlinks to process.
-    return if !@row_nums;
-
-    # Iterate over the rows.
-    for my $row_num ( @row_nums ) {
-
-        # Sort the hyperlinks into column order.
-        my @col_nums = sort { $a <=> $b }
-          keys %{ $self->{_hyperlinks}->{$row_num} };
-
-        # Iterate over the columns.
-        for my $col_num ( @col_nums ) {
-
-            # Get the link data for this cell.
-            my $link      = $self->{_hyperlinks}->{$row_num}->{$col_num};
-            my $link_type = $link->{_link_type};
-
-
-            # If the cell isn't a string then we have to add the url as
-            # the string to display.
-            my $display;
-            if (   $self->{_table}
-                && $self->{_table}->{$row_num}
-                && $self->{_table}->{$row_num}->{$col_num} )
-            {
-                my $cell = $self->{_table}->{$row_num}->{$col_num};
-                $display = $link->{_url} if $cell->[0] ne 's';
-            }
-
-
-            if ( $link_type == 1 ) {
-
-                # External link with rel file relationship.
-                push @hlink_refs,
-                  [
-                    $link_type,    $row_num,
-                    $col_num,      ++$self->{_rel_count},
-                    $link->{_str}, $display,
-                    $link->{_tip}
-                  ];
-
-                # Links for use by the packager.
-                push @{ $self->{_external_hyper_links} },
-                  [ '/hyperlink', $link->{_url}, 'External' ];
-            }
-            else {
-
-                # Internal link with rel file relationship.
-                push @hlink_refs,
-                  [
-                    $link_type,    $row_num,      $col_num,
-                    $link->{_url}, $link->{_str}, $link->{_tip}
-                  ];
-            }
-        }
-    }
-
-    # Write the hyperlink elements.
-    $self->xml_start_tag( 'hyperlinks' );
-
-    for my $aref ( @hlink_refs ) {
-        my ( $type, @args ) = @$aref;
-
-        if ( $type == 1 ) {
-            $self->_write_hyperlink_external( @args );
-        }
-        elsif ( $type == 2 ) {
-            $self->_write_hyperlink_internal( @args );
-        }
-    }
-
-    $self->xml_end_tag( 'hyperlinks' );
-}
-
-
-##############################################################################
-#
-# _write_hyperlink_external()
-#
-# Write the <hyperlink> element for external links.
-#
-sub _write_hyperlink_external {
-
-    my $self     = shift;
-    my $row      = shift;
-    my $col      = shift;
-    my $id       = shift;
-    my $location = shift;
-    my $display  = shift;
-    my $tooltip  = shift;
-
-    my $ref = xl_rowcol_to_cell( $row, $col );
-    my $r_id = 'rId' . $id;
-
-    my @attributes = (
-        'ref'  => $ref,
-        'r:id' => $r_id,
-    );
-
-    push @attributes, ( 'location' => $location ) if defined $location;
-    push @attributes, ( 'display' => $display )   if defined $display;
-    push @attributes, ( 'tooltip'  => $tooltip )  if defined $tooltip;
-
-    $self->xml_empty_tag( 'hyperlink', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_hyperlink_internal()
-#
-# Write the <hyperlink> element for internal links.
-#
-sub _write_hyperlink_internal {
-
-    my $self     = shift;
-    my $row      = shift;
-    my $col      = shift;
-    my $location = shift;
-    my $display  = shift;
-    my $tooltip  = shift;
-
-    my $ref = xl_rowcol_to_cell( $row, $col );
-
-    my @attributes = ( 'ref' => $ref, 'location' => $location );
-
-    push @attributes, ( 'tooltip' => $tooltip ) if defined $tooltip;
-    push @attributes, ( 'display' => $display );
-
-    $self->xml_empty_tag( 'hyperlink', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_panes()
-#
-# Write the frozen or split <pane> elements.
-#
-sub _write_panes {
-
-    my $self  = shift;
-    my @panes = @{ $self->{_panes} };
-
-    return unless @panes;
-
-    if ( $panes[4] == 2 ) {
-        $self->_write_split_panes( @panes );
-    }
-    else {
-        $self->_write_freeze_panes( @panes );
-    }
-}
-
-
-##############################################################################
-#
-# _write_freeze_panes()
-#
-# Write the <pane> element for freeze panes.
-#
-sub _write_freeze_panes {
-
-    my $self = shift;
-    my @attributes;
-
-    my ( $row, $col, $top_row, $left_col, $type ) = @_;
-
-    my $y_split       = $row;
-    my $x_split       = $col;
-    my $top_left_cell = xl_rowcol_to_cell( $top_row, $left_col );
-    my $active_pane;
-    my $state;
-    my $active_cell;
-    my $sqref;
-
-    # Move user cell selection to the panes.
-    if ( @{ $self->{_selections} } ) {
-        ( undef, $active_cell, $sqref ) = @{ $self->{_selections}->[0] };
-        $self->{_selections} = [];
-    }
-
-    # Set the active pane.
-    if ( $row && $col ) {
-        $active_pane = 'bottomRight';
-
-        my $row_cell = xl_rowcol_to_cell( $row, 0 );
-        my $col_cell = xl_rowcol_to_cell( 0,    $col );
-
-        push @{ $self->{_selections} },
-          (
-            [ 'topRight',    $col_cell,    $col_cell ],
-            [ 'bottomLeft',  $row_cell,    $row_cell ],
-            [ 'bottomRight', $active_cell, $sqref ]
-          );
-    }
-    elsif ( $col ) {
-        $active_pane = 'topRight';
-        push @{ $self->{_selections} }, [ 'topRight', $active_cell, $sqref ];
-    }
-    else {
-        $active_pane = 'bottomLeft';
-        push @{ $self->{_selections} }, [ 'bottomLeft', $active_cell, $sqref ];
-    }
-
-    # Set the pane type.
-    if ( $type == 0 ) {
-        $state = 'frozen';
-    }
-    elsif ( $type == 1 ) {
-        $state = 'frozenSplit';
-    }
-    else {
-        $state = 'split';
-    }
-
-
-    push @attributes, ( 'xSplit' => $x_split ) if $x_split;
-    push @attributes, ( 'ySplit' => $y_split ) if $y_split;
-
-    push @attributes, ( 'topLeftCell' => $top_left_cell );
-    push @attributes, ( 'activePane'  => $active_pane );
-    push @attributes, ( 'state'       => $state );
-
-
-    $self->xml_empty_tag( 'pane', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_split_panes()
-#
-# Write the <pane> element for split panes.
-#
-# See also, implementers note for split_panes().
-#
-sub _write_split_panes {
-
-    my $self = shift;
-    my @attributes;
-    my $y_split;
-    my $x_split;
-    my $has_selection = 0;
-    my $active_pane;
-    my $active_cell;
-    my $sqref;
-
-    my ( $row, $col, $top_row, $left_col, $type ) = @_;
-    $y_split = $row;
-    $x_split = $col;
-
-    # Move user cell selection to the panes.
-    if ( @{ $self->{_selections} } ) {
-        ( undef, $active_cell, $sqref ) = @{ $self->{_selections}->[0] };
-        $self->{_selections} = [];
-        $has_selection = 1;
-    }
-
-    # Convert the row and col to 1/20 twip units with padding.
-    $y_split = int( 20 * $y_split + 300 ) if $y_split;
-    $x_split = $self->_calculate_x_split_width( $x_split ) if $x_split;
-
-    # For non-explicit topLeft definitions, estimate the cell offset based
-    # on the pixels dimensions. This is only a workaround and doesn't take
-    # adjusted cell dimensions into account.
-    if ( $top_row == $row && $left_col == $col ) {
-        $top_row  = int( 0.5 + ( $y_split - 300 ) / 20 / 15 );
-        $left_col = int( 0.5 + ( $x_split - 390 ) / 20 / 3 * 4 / 64 );
-    }
-
-    my $top_left_cell = xl_rowcol_to_cell( $top_row, $left_col );
-
-    # If there is no selection set the active cell to the top left cell.
-    if ( !$has_selection ) {
-        $active_cell = $top_left_cell;
-        $sqref       = $top_left_cell;
-    }
-
-    # Set the Cell selections.
-    if ( $row && $col ) {
-        $active_pane = 'bottomRight';
-
-        my $row_cell = xl_rowcol_to_cell( $top_row, 0 );
-        my $col_cell = xl_rowcol_to_cell( 0,        $left_col );
-
-        push @{ $self->{_selections} },
-          (
-            [ 'topRight',    $col_cell,    $col_cell ],
-            [ 'bottomLeft',  $row_cell,    $row_cell ],
-            [ 'bottomRight', $active_cell, $sqref ]
-          );
-    }
-    elsif ( $col ) {
-        $active_pane = 'topRight';
-        push @{ $self->{_selections} }, [ 'topRight', $active_cell, $sqref ];
-    }
-    else {
-        $active_pane = 'bottomLeft';
-        push @{ $self->{_selections} }, [ 'bottomLeft', $active_cell, $sqref ];
-    }
-
-    push @attributes, ( 'xSplit' => $x_split ) if $x_split;
-    push @attributes, ( 'ySplit' => $y_split ) if $y_split;
-    push @attributes, ( 'topLeftCell' => $top_left_cell );
-    push @attributes, ( 'activePane' => $active_pane ) if $has_selection;
-
-    $self->xml_empty_tag( 'pane', @attributes );
-}
-
-
-##############################################################################
-#
-# _calculate_x_split_width()
-#
-# Convert column width from user units to pane split width.
-#
-sub _calculate_x_split_width {
-
-    my $self  = shift;
-    my $width = shift;
-
-    my $max_digit_width = 7;    # For Calabri 11.
-    my $padding         = 5;
-    my $pixels;
-
-    # Convert to pixels.
-    if ( $width < 1 ) {
-        $pixels = int( $width * ( $max_digit_width + $padding ) + 0.5 );
-    }
-    else {
-          $pixels = int( $width * $max_digit_width + 0.5 ) + $padding;
-    }
-
-    # Convert to points.
-    my $points = $pixels * 3 / 4;
-
-    # Convert to twips (twentieths of a point).
-    my $twips = $points * 20;
-
-    # Add offset/padding.
-    $width = $twips + 390;
-
-    return $width;
-}
-
-
-##############################################################################
-#
-# _write_tab_color()
-#
-# Write the <tabColor> element.
-#
-sub _write_tab_color {
-
-    my $self        = shift;
-    my $color_index = $self->{_tab_color};
-
-    return unless $color_index;
-
-    my $rgb = $self->_get_palette_color( $color_index );
-
-    my @attributes = ( 'rgb' => $rgb );
-
-    $self->xml_empty_tag( 'tabColor', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_outline_pr()
-#
-# Write the <outlinePr> element.
-#
-sub _write_outline_pr {
-
-    my $self       = shift;
-    my @attributes = ();
-
-    return unless $self->{_outline_changed};
-
-    push @attributes, ( "applyStyles"        => 1 ) if $self->{_outline_style};
-    push @attributes, ( "summaryBelow"       => 0 ) if !$self->{_outline_below};
-    push @attributes, ( "summaryRight"       => 0 ) if !$self->{_outline_right};
-    push @attributes, ( "showOutlineSymbols" => 0 ) if !$self->{_outline_on};
-
-    $self->xml_empty_tag( 'outlinePr', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_sheet_protection()
-#
-# Write the <sheetProtection> element.
-#
-sub _write_sheet_protection {
-
-    my $self = shift;
-    my @attributes;
-
-    return unless $self->{_protect};
-
-    my %arg = %{ $self->{_protect} };
-
-    push @attributes, ( "password"    => $arg{password} ) if $arg{password};
-    push @attributes, ( "sheet"       => 1 )              if $arg{sheet};
-    push @attributes, ( "content"     => 1 )              if $arg{content};
-    push @attributes, ( "objects"     => 1 )              if !$arg{objects};
-    push @attributes, ( "scenarios"   => 1 )              if !$arg{scenarios};
-    push @attributes, ( "formatCells" => 0 )              if $arg{format_cells};
-    push @attributes, ( "formatColumns"    => 0 ) if $arg{format_columns};
-    push @attributes, ( "formatRows"       => 0 ) if $arg{format_rows};
-    push @attributes, ( "insertColumns"    => 0 ) if $arg{insert_columns};
-    push @attributes, ( "insertRows"       => 0 ) if $arg{insert_rows};
-    push @attributes, ( "insertHyperlinks" => 0 ) if $arg{insert_hyperlinks};
-    push @attributes, ( "deleteColumns"    => 0 ) if $arg{delete_columns};
-    push @attributes, ( "deleteRows"       => 0 ) if $arg{delete_rows};
-
-    push @attributes, ( "selectLockedCells" => 1 )
-      if !$arg{select_locked_cells};
-
-    push @attributes, ( "sort"        => 0 ) if $arg{sort};
-    push @attributes, ( "autoFilter"  => 0 ) if $arg{autofilter};
-    push @attributes, ( "pivotTables" => 0 ) if $arg{pivot_tables};
-
-    push @attributes, ( "selectUnlockedCells" => 1 )
-      if !$arg{select_unlocked_cells};
-
-
-    $self->xml_empty_tag( 'sheetProtection', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_drawings()
-#
-# Write the <drawing> elements.
-#
-sub _write_drawings {
-
-    my $self = shift;
-
-    return unless $self->{_drawing};
-
-    $self->_write_drawing( ++$self->{_rel_count} );
-}
-
-
-##############################################################################
-#
-# _write_drawing()
-#
-# Write the <drawing> element.
-#
-sub _write_drawing {
-
-    my $self = shift;
-    my $id   = shift;
-    my $r_id = 'rId' . $id;
-
-    my @attributes = ( 'r:id' => $r_id );
-
-    $self->xml_empty_tag( 'drawing', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_legacy_drawing()
-#
-# Write the <legacyDrawing> element.
-#
-sub _write_legacy_drawing {
-
-    my $self = shift;
-    my $id;
-
-    return unless $self->{_has_vml};
-
-    # Increment the relationship id for any drawings or comments.
-    $id = ++$self->{_rel_count};
-
-    my @attributes = ( 'r:id' => 'rId' . $id );
-
-    $self->xml_empty_tag( 'legacyDrawing', @attributes );
-}
-
-
-
-##############################################################################
-#
-# _write_legacy_drawing_hf()
-#
-# Write the <legacyDrawingHF> element.
-#
-sub _write_legacy_drawing_hf {
-
-    my $self = shift;
-    my $id;
-
-    return unless $self->{_has_header_vml};
-
-    # Increment the relationship id for any drawings or comments.
-    $id = ++$self->{_rel_count};
-
-    my @attributes = ( 'r:id' => 'rId' . $id );
-
-    $self->xml_empty_tag( 'legacyDrawingHF', @attributes );
-}
-
-
-#
-# Note, the following font methods are, more or less, duplicated from the
-# Excel::Writer::XLSX::Package::Styles class. I will look at implementing
-# this is a cleaner encapsulated mode at a later stage.
-#
-
-
-##############################################################################
-#
-# _write_font()
-#
-# Write the <font> element.
-#
-sub _write_font {
-
-    my $self   = shift;
-    my $format = shift;
-
-    $self->{_rstring}->xml_start_tag( 'rPr' );
-
-    $self->{_rstring}->xml_empty_tag( 'b' )       if $format->{_bold};
-    $self->{_rstring}->xml_empty_tag( 'i' )       if $format->{_italic};
-    $self->{_rstring}->xml_empty_tag( 'strike' )  if $format->{_font_strikeout};
-    $self->{_rstring}->xml_empty_tag( 'outline' ) if $format->{_font_outline};
-    $self->{_rstring}->xml_empty_tag( 'shadow' )  if $format->{_font_shadow};
-
-    # Handle the underline variants.
-    $self->_write_underline( $format->{_underline} ) if $format->{_underline};
-
-    $self->_write_vert_align( 'superscript' ) if $format->{_font_script} == 1;
-    $self->_write_vert_align( 'subscript' )   if $format->{_font_script} == 2;
-
-    $self->{_rstring}->xml_empty_tag( 'sz', 'val', $format->{_size} );
-
-    if ( my $theme = $format->{_theme} ) {
-        $self->_write_rstring_color( 'theme' => $theme );
-    }
-    elsif ( my $color = $format->{_color} ) {
-        $color = $self->_get_palette_color( $color );
-
-        $self->_write_rstring_color( 'rgb' => $color );
-    }
-    else {
-        $self->_write_rstring_color( 'theme' => 1 );
-    }
-
-    $self->{_rstring}->xml_empty_tag( 'rFont', 'val', $format->{_font} );
-    $self->{_rstring}
-      ->xml_empty_tag( 'family', 'val', $format->{_font_family} );
-
-    if ( $format->{_font} eq 'Calibri' && !$format->{_hyperlink} ) {
-        $self->{_rstring}
-          ->xml_empty_tag( 'scheme', 'val', $format->{_font_scheme} );
-    }
-
-    $self->{_rstring}->xml_end_tag( 'rPr' );
-}
-
-
-###############################################################################
-#
-# _write_underline()
-#
-# Write the underline font element.
-#
-sub _write_underline {
-
-    my $self      = shift;
-    my $underline = shift;
-    my @attributes;
-
-    # Handle the underline variants.
-    if ( $underline == 2 ) {
-        @attributes = ( val => 'double' );
-    }
-    elsif ( $underline == 33 ) {
-        @attributes = ( val => 'singleAccounting' );
-    }
-    elsif ( $underline == 34 ) {
-        @attributes = ( val => 'doubleAccounting' );
-    }
-    else {
-        @attributes = ();    # Default to single underline.
-    }
-
-    $self->{_rstring}->xml_empty_tag( 'u', @attributes );
-
-}
-
-
-##############################################################################
-#
-# _write_vert_align()
-#
-# Write the <vertAlign> font sub-element.
-#
-sub _write_vert_align {
-
-    my $self = shift;
-    my $val  = shift;
-
-    my @attributes = ( 'val' => $val );
-
-    $self->{_rstring}->xml_empty_tag( 'vertAlign', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_rstring_color()
-#
-# Write the <color> element.
-#
-sub _write_rstring_color {
-
-    my $self  = shift;
-    my $name  = shift;
-    my $value = shift;
-
-    my @attributes = ( $name => $value );
-
-    $self->{_rstring}->xml_empty_tag( 'color', @attributes );
-}
-
-
-#
-# End font duplication code.
-#
-
-
-##############################################################################
-#
-# _write_data_validations()
-#
-# Write the <dataValidations> element.
-#
-sub _write_data_validations {
-
-    my $self        = shift;
-    my @validations = @{ $self->{_validations} };
-    my $count       = @validations;
-
-    return unless $count;
-
-    my @attributes = ( 'count' => $count );
-
-    $self->xml_start_tag( 'dataValidations', @attributes );
-
-    for my $validation ( @validations ) {
-
-        # Write the dataValidation element.
-        $self->_write_data_validation( $validation );
-    }
-
-    $self->xml_end_tag( 'dataValidations' );
-}
-
-
-##############################################################################
-#
-# _write_data_validation()
-#
-# Write the <dataValidation> element.
-#
-sub _write_data_validation {
-
-    my $self       = shift;
-    my $param      = shift;
-    my $sqref      = '';
-    my @attributes = ();
-
-
-    # Set the cell range(s) for the data validation.
-    for my $cells ( @{ $param->{cells} } ) {
-
-        # Add a space between multiple cell ranges.
-        $sqref .= ' ' if $sqref ne '';
-
-        my ( $row_first, $col_first, $row_last, $col_last ) = @$cells;
-
-        # Swap last row/col for first row/col as necessary
-        if ( $row_first > $row_last ) {
-            ( $row_first, $row_last ) = ( $row_last, $row_first );
-        }
-
-        if ( $col_first > $col_last ) {
-            ( $col_first, $col_last ) = ( $col_last, $col_first );
-        }
-
-        # If the first and last cell are the same write a single cell.
-        if ( ( $row_first == $row_last ) && ( $col_first == $col_last ) ) {
-            $sqref .= xl_rowcol_to_cell( $row_first, $col_first );
-        }
-        else {
-            $sqref .= xl_range( $row_first, $row_last, $col_first, $col_last );
-        }
-    }
-
-
-    if ( $param->{validate} ne 'none' ) {
-
-        push @attributes, ( 'type' => $param->{validate} );
-
-        if ( $param->{criteria} ne 'between' ) {
-            push @attributes, ( 'operator' => $param->{criteria} );
-        }
-
-    }
-
-    if ( $param->{error_type} ) {
-        push @attributes, ( 'errorStyle' => 'warning' )
-          if $param->{error_type} == 1;
-        push @attributes, ( 'errorStyle' => 'information' )
-          if $param->{error_type} == 2;
-    }
-
-    push @attributes, ( 'allowBlank'       => 1 ) if $param->{ignore_blank};
-    push @attributes, ( 'showDropDown'     => 1 ) if !$param->{dropdown};
-    push @attributes, ( 'showInputMessage' => 1 ) if $param->{show_input};
-    push @attributes, ( 'showErrorMessage' => 1 ) if $param->{show_error};
-
-    push @attributes, ( 'errorTitle' => $param->{error_title} )
-      if $param->{error_title};
-
-    push @attributes, ( 'error' => $param->{error_message} )
-      if $param->{error_message};
-
-    push @attributes, ( 'promptTitle' => $param->{input_title} )
-      if $param->{input_title};
-
-    push @attributes, ( 'prompt' => $param->{input_message} )
-      if $param->{input_message};
-
-    push @attributes, ( 'sqref' => $sqref );
-
-    if ( $param->{validate} eq 'none' ) {
-        $self->xml_empty_tag( 'dataValidation', @attributes );
-    }
-    else {
-        $self->xml_start_tag( 'dataValidation', @attributes );
-
-        # Write the formula1 element.
-        $self->_write_formula_1( $param->{value} );
-
-        # Write the formula2 element.
-        $self->_write_formula_2( $param->{maximum} )
-          if defined $param->{maximum};
-
-        $self->xml_end_tag( 'dataValidation' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_formula_1()
-#
-# Write the <formula1> element.
-#
-sub _write_formula_1 {
-
-    my $self    = shift;
-    my $formula = shift;
-
-    # Convert a list array ref into a comma separated string.
-    if ( ref $formula eq 'ARRAY' ) {
-        $formula = join ',', @$formula;
-        $formula = qq("$formula");
-    }
-
-    $formula =~ s/^=//;    # Remove formula symbol.
-
-    $self->xml_data_element( 'formula1', $formula );
-}
-
-
-##############################################################################
-#
-# _write_formula_2()
-#
-# Write the <formula2> element.
-#
-sub _write_formula_2 {
-
-    my $self    = shift;
-    my $formula = shift;
-
-    $formula =~ s/^=//;    # Remove formula symbol.
-
-    $self->xml_data_element( 'formula2', $formula );
-}
-
-
-##############################################################################
-#
-# _write_conditional_formats()
-#
-# Write the Worksheet conditional formats.
-#
-sub _write_conditional_formats {
-
-    my $self   = shift;
-    my @ranges = sort keys %{ $self->{_cond_formats} };
-
-    return unless scalar @ranges;
-
-    for my $range ( @ranges ) {
-        $self->_write_conditional_formatting( $range,
-            $self->{_cond_formats}->{$range} );
-    }
-}
-
-
-##############################################################################
-#
-# _write_conditional_formatting()
-#
-# Write the <conditionalFormatting> element.
-#
-sub _write_conditional_formatting {
-
-    my $self   = shift;
-    my $range  = shift;
-    my $params = shift;
-
-    my @attributes = ( 'sqref' => $range );
-
-    $self->xml_start_tag( 'conditionalFormatting', @attributes );
-
-    for my $param ( @$params ) {
-
-        # Write the cfRule element.
-        $self->_write_cf_rule( $param );
-    }
-
-    $self->xml_end_tag( 'conditionalFormatting' );
-}
-
-##############################################################################
-#
-# _write_cf_rule()
-#
-# Write the <cfRule> element.
-#
-sub _write_cf_rule {
-
-    my $self  = shift;
-    my $param = shift;
-
-    my @attributes = ( 'type' => $param->{type} );
-
-    push @attributes, ( 'dxfId' => $param->{format} )
-      if defined $param->{format};
-
-    push @attributes, ( 'priority' => $param->{priority} );
-
-    push @attributes, ( 'stopIfTrue' => 1 )
-      if $param->{stop_if_true};
-
-    if ( $param->{type} eq 'cellIs' ) {
-        push @attributes, ( 'operator' => $param->{criteria} );
-
-        $self->xml_start_tag( 'cfRule', @attributes );
-
-        if ( defined $param->{minimum} && defined $param->{maximum} ) {
-            $self->_write_formula( $param->{minimum} );
-            $self->_write_formula( $param->{maximum} );
-        }
-        else {
-            $self->_write_formula( $param->{value} );
-        }
-
-        $self->xml_end_tag( 'cfRule' );
-    }
-    elsif ( $param->{type} eq 'aboveAverage' ) {
-        if ( $param->{criteria} =~ /below/ ) {
-            push @attributes, ( 'aboveAverage' => 0 );
-        }
-
-        if ( $param->{criteria} =~ /equal/ ) {
-            push @attributes, ( 'equalAverage' => 1 );
-        }
-
-        if ( $param->{criteria} =~ /([123]) std dev/ ) {
-            push @attributes, ( 'stdDev' => $1 );
-        }
-
-        $self->xml_empty_tag( 'cfRule', @attributes );
-    }
-    elsif ( $param->{type} eq 'top10' ) {
-        if ( defined $param->{criteria} && $param->{criteria} eq '%' ) {
-            push @attributes, ( 'percent' => 1 );
-        }
-
-        if ( $param->{direction} ) {
-            push @attributes, ( 'bottom' => 1 );
-        }
-
-        my $rank = $param->{value} || 10;
-        push @attributes, ( 'rank' => $rank );
-
-        $self->xml_empty_tag( 'cfRule', @attributes );
-    }
-    elsif ( $param->{type} eq 'duplicateValues' ) {
-        $self->xml_empty_tag( 'cfRule', @attributes );
-    }
-    elsif ( $param->{type} eq 'uniqueValues' ) {
-        $self->xml_empty_tag( 'cfRule', @attributes );
-    }
-    elsif ($param->{type} eq 'containsText'
-        || $param->{type} eq 'notContainsText'
-        || $param->{type} eq 'beginsWith'
-        || $param->{type} eq 'endsWith' )
-    {
-        push @attributes, ( 'operator' => $param->{criteria} );
-        push @attributes, ( 'text'     => $param->{value} );
-
-        $self->xml_start_tag( 'cfRule', @attributes );
-        $self->_write_formula( $param->{formula} );
-        $self->xml_end_tag( 'cfRule' );
-    }
-    elsif ( $param->{type} eq 'timePeriod' ) {
-        push @attributes, ( 'timePeriod' => $param->{criteria} );
-
-        $self->xml_start_tag( 'cfRule', @attributes );
-        $self->_write_formula( $param->{formula} );
-        $self->xml_end_tag( 'cfRule' );
-    }
-    elsif ($param->{type} eq 'containsBlanks'
-        || $param->{type} eq 'notContainsBlanks'
-        || $param->{type} eq 'containsErrors'
-        || $param->{type} eq 'notContainsErrors' )
-    {
-        $self->xml_start_tag( 'cfRule', @attributes );
-        $self->_write_formula( $param->{formula} );
-        $self->xml_end_tag( 'cfRule' );
-    }
-    elsif ( $param->{type} eq 'colorScale' ) {
-
-        $self->xml_start_tag( 'cfRule', @attributes );
-        $self->_write_color_scale( $param );
-        $self->xml_end_tag( 'cfRule' );
-    }
-    elsif ( $param->{type} eq 'dataBar' ) {
-
-        $self->xml_start_tag( 'cfRule', @attributes );
-
-        $self->_write_data_bar( $param );
-
-        if ($param->{_is_data_bar_2010}) {
-            $self->_write_data_bar_ext( $param );
-        }
-
-        $self->xml_end_tag( 'cfRule' );
-    }
-    elsif ( $param->{type} eq 'expression' ) {
-
-        $self->xml_start_tag( 'cfRule', @attributes );
-        $self->_write_formula( $param->{criteria} );
-        $self->xml_end_tag( 'cfRule' );
-    }
-    elsif ( $param->{type} eq 'iconSet' ) {
-
-        $self->xml_start_tag( 'cfRule', @attributes );
-        $self->_write_icon_set( $param );
-        $self->xml_end_tag( 'cfRule' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_icon_set()
-#
-# Write the <iconSet> element.
-#
-sub _write_icon_set {
-
-    my $self        = shift;
-    my $param       = shift;
-    my $icon_style  = $param->{icon_style};
-    my $total_icons = $param->{total_icons};
-    my $icons       = $param->{icons};
-    my $i;
-
-    my @attributes = ();
-
-    # Don't set attribute for default style.
-    if ( $icon_style ne '3TrafficLights' ) {
-        @attributes = ( 'iconSet' => $icon_style );
-    }
-
-    if ( exists $param->{'icons_only'} && $param->{'icons_only'} ) {
-        push @attributes, ( 'showValue' => 0 );
-    }
-
-    if ( exists $param->{'reverse_icons'} && $param->{'reverse_icons'} ) {
-        push @attributes, ( 'reverse' => 1 );
-    }
-
-    $self->xml_start_tag( 'iconSet', @attributes );
-
-    # Write the properites for different icon styles.
-    for my $icon ( reverse @{ $param->{icons} } ) {
-        $self->_write_cfvo(
-            $icon->{'type'},
-            $icon->{'value'},
-            $icon->{'criteria'}
-        );
-    }
-
-    $self->xml_end_tag( 'iconSet' );
-}
-
-##############################################################################
-#
-# _write_formula()
-#
-# Write the <formula> element.
-#
-sub _write_formula {
-
-    my $self = shift;
-    my $data = shift;
-
-    # Remove equality from formula.
-    $data =~ s/^=//;
-
-    $self->xml_data_element( 'formula', $data );
-}
-
-
-##############################################################################
-#
-# _write_color_scale()
-#
-# Write the <colorScale> element.
-#
-sub _write_color_scale {
-
-    my $self  = shift;
-    my $param = shift;
-
-    $self->xml_start_tag( 'colorScale' );
-
-    $self->_write_cfvo( $param->{min_type}, $param->{min_value} );
-
-    if ( defined $param->{mid_type} ) {
-        $self->_write_cfvo( $param->{mid_type}, $param->{mid_value} );
-    }
-
-    $self->_write_cfvo( $param->{max_type}, $param->{max_value} );
-
-    $self->_write_color( 'rgb' => $param->{min_color} );
-
-    if ( defined $param->{mid_color} ) {
-        $self->_write_color( 'rgb' => $param->{mid_color} );
-    }
-
-    $self->_write_color( 'rgb' => $param->{max_color} );
-
-    $self->xml_end_tag( 'colorScale' );
-}
-
-
-##############################################################################
-#
-# _write_data_bar()
-#
-# Write the <dataBar> element.
-#
-sub _write_data_bar {
-
-    my $self       = shift;
-    my $data_bar   = shift;
-    my @attributes = ();
-
-    if ( $data_bar->{bar_only} ) {
-        push @attributes, ( 'showValue', 0 );
-    }
-
-    $self->xml_start_tag( 'dataBar', @attributes );
-
-    $self->_write_cfvo( $data_bar->{min_type}, $data_bar->{min_value} );
-    $self->_write_cfvo( $data_bar->{max_type}, $data_bar->{max_value} );
-
-    $self->_write_color( 'rgb' => $data_bar->{bar_color} );
-
-    $self->xml_end_tag( 'dataBar' );
-}
-
-
-##############################################################################
-#
-# _write_data_bar_ext()
-#
-# Write the <extLst> dataBar extension element.
-#
-sub _write_data_bar_ext {
-
-    my $self      = shift;
-    my $param     = shift;
-
-    # Create a pseudo GUID for each unique Excel 2010 data bar.
-    my $worksheet_count = $self->{_index} + 1;
-    my $data_bar_count  = @{ $self->{_data_bars_2010} } + 1;
-
-    my $guid = sprintf "{DA7ABA51-AAAA-BBBB-%04X-%012X}", $worksheet_count,
-      $data_bar_count;
-
-    # Store the 2010 data bar parameters to write the extLst elements.
-    $param->{_guid} = $guid;
-    push @{$self->{_data_bars_2010}}, $param;
-
-    $self->xml_start_tag( 'extLst' );
-    $self->_write_ext('{B025F937-C7B1-47D3-B67F-A62EFF666E3E}');
-
-    $self->xml_data_element( 'x14:id', $guid);
-
-    $self->xml_end_tag( 'ext' );
-    $self->xml_end_tag( 'extLst' );
-}
-
-
-##############################################################################
-#
-# _write_cfvo()
-#
-# Write the <cfvo> element.
-#
-sub _write_cfvo {
-
-    my $self     = shift;
-    my $type     = shift;
-    my $value    = shift;
-    my $criteria = shift;
-
-    my @attributes = ( 'type' => $type );
-
-    if ( defined $value ) {
-        push @attributes, ( 'val', $value );
-    }
-
-    if ( $criteria ) {
-        push @attributes, ( 'gte', 0 );
-    }
-
-    $self->xml_empty_tag( 'cfvo', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_x14_cfvo()
-#
-# Write the <cfvo> element.
-#
-sub _write_x14_cfvo {
-
-    my $self  = shift;
-    my $type  = shift;
-    my $value = shift;
-
-    my @attributes = ( 'type' => $type );
-
-    if (   $type eq 'min'
-        || $type eq 'max'
-        || $type eq 'autoMin'
-        || $type eq 'autoMax' )
-    {
-        $self->xml_empty_tag( 'x14:cfvo', @attributes );
-    }
-    else {
-        $self->xml_start_tag( 'x14:cfvo', @attributes );
-        $self->xml_data_element( 'xm:f', $value );
-        $self->xml_end_tag( 'x14:cfvo' );
-    }
-}
-
-
-##############################################################################
-#
-# _write_color()
-#
-# Write the <color> element.
-#
-sub _write_color {
-
-    my $self  = shift;
-    my $name  = shift;
-    my $value = shift;
-
-    my @attributes = ( $name => $value );
-
-    $self->xml_empty_tag( 'color', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_table_parts()
-#
-# Write the <tableParts> element.
-#
-sub _write_table_parts {
-
-    my $self   = shift;
-    my @tables = @{ $self->{_tables} };
-    my $count  = scalar @tables;
-
-    # Return if worksheet doesn't contain any tables.
-    return unless $count;
-
-    my @attributes = ( 'count' => $count, );
-
-    $self->xml_start_tag( 'tableParts', @attributes );
-
-    for my $table ( @tables ) {
-
-        # Write the tablePart element.
-        $self->_write_table_part( ++$self->{_rel_count} );
-
-    }
-
-    $self->xml_end_tag( 'tableParts' );
-}
-
-
-##############################################################################
-#
-# _write_table_part()
-#
-# Write the <tablePart> element.
-#
-sub _write_table_part {
-
-    my $self = shift;
-    my $id   = shift;
-    my $r_id = 'rId' . $id;
-
-    my @attributes = ( 'r:id' => $r_id, );
-
-    $self->xml_empty_tag( 'tablePart', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_ext_list()
-#
-# Write the <extLst> element for data bars and sparklines.
-#
-sub _write_ext_list {
-
-    my $self            = shift;
-    my $has_data_bars  = scalar @{ $self->{_data_bars_2010} };
-    my $has_sparklines = scalar @{ $self->{_sparklines} };
-
-    if ( !$has_data_bars and !$has_sparklines ) {
-        return;
-    }
-
-    # Write the extLst element.
-    $self->xml_start_tag( 'extLst' );
-
-    if ( $has_data_bars ) {
-        $self->_write_ext_list_data_bars();
-    }
-
-    if ( $has_sparklines ) {
-        $self->_write_ext_list_sparklines();
-    }
-
-    $self->xml_end_tag( 'extLst' );
-}
-
-
-##############################################################################
-#
-# _write_ext_list_data_bars()
-#
-# Write the Excel 2010 data_bar subelements.
-#
-sub _write_ext_list_data_bars {
-
-    my $self      = shift;
-    my @data_bars = @{ $self->{_data_bars_2010} };
-
-    # Write the ext element.
-    $self->_write_ext('{78C0D931-6437-407d-A8EE-F0AAD7539E65}');
-
-
-    $self->xml_start_tag( 'x14:conditionalFormattings' );
-
-    # Write each of the Excel 2010 conditional formatting data bar elements.
-    for my $data_bar (@data_bars) {
-
-        # Write the x14:conditionalFormatting element.
-        $self->_write_conditional_formatting_2010($data_bar);
-    }
-
-    $self->xml_end_tag( 'x14:conditionalFormattings' );
-    $self->xml_end_tag( 'ext' );
-
-
-}
-
-
-##############################################################################
-#
-# _write_conditional_formatting()
-#
-# Write the <x14:conditionalFormatting> element.
-#
-sub _write_conditional_formatting_2010 {
-
-    my $self     = shift;
-    my $data_bar = shift;
-    my $xmlns_xm = 'http://schemas.microsoft.com/office/excel/2006/main';
-
-    my @attributes = ( 'xmlns:xm' => $xmlns_xm );
-
-    $self->xml_start_tag( 'x14:conditionalFormatting', @attributes );
-
-    # Write the '<x14:cfRule element.
-    $self->_write_x14_cf_rule( $data_bar );
-
-    # Write the x14:dataBar element.
-    $self->_write_x14_data_bar( $data_bar );
-
-    # Write the x14 max and min data bars.
-    $self->_write_x14_cfvo( $data_bar->{_x14_min_type},
-        $data_bar->{min_value} );
-
-    $self->_write_x14_cfvo( $data_bar->{_x14_max_type},
-        $data_bar->{max_value} );
-
-    # Write the x14:borderColor element.
-    if ( !$data_bar->{bar_no_border} ) {
-        $self->_write_x14_border_color( $data_bar->{bar_border_color} );
-    }
-
-    # Write the x14:negativeFillColor element.
-    if ( !$data_bar->{bar_negative_color_same} ) {
-        $self->_write_x14_negative_fill_color(
-            $data_bar->{bar_negative_color} );
-    }
-
-    # Write the x14:negativeBorderColor element.
-    if (   !$data_bar->{bar_no_border}
-        && !$data_bar->{bar_negative_border_color_same} )
-    {
-        $self->_write_x14_negative_border_color(
-            $data_bar->{bar_negative_border_color} );
-    }
-
-    # Write the x14:axisColor element.
-    if ( $data_bar->{bar_axis_position} ne 'none') {
-        $self->_write_x14_axis_color($data_bar->{bar_axis_color});
-    }
-
-    # Write closing elements.
-    $self->xml_end_tag( 'x14:dataBar' );
-    $self->xml_end_tag( 'x14:cfRule' );
-
-    # Add the conditional format range.
-    $self->xml_data_element( 'xm:sqref', $data_bar->{_range} );
-
-    $self->xml_end_tag( 'x14:conditionalFormatting' );
-}
-
-
-##############################################################################
-#
-# _write_x14_cf_rule()
-#
-# Write the <'<x14:cfRule> element.
-#
-sub _write_x14_cf_rule {
-
-    my $self     = shift;
-    my $data_bar = shift;
-    my $type     = 'dataBar';
-    my $id       = $data_bar->{_guid};
-
-    my @attributes = (
-        'type' => $type,
-        'id'   => $id,
-    );
-
-    $self->xml_start_tag( 'x14:cfRule', @attributes );
-
-}
-
-
-##############################################################################
-#
-# _write_x14_data_bar()
-#
-# Write the <x14:dataBar> element.
-#
-sub _write_x14_data_bar {
-
-    my $self          = shift;
-    my $data_bar      = shift;
-    my $min_length    = 0;
-    my $max_length    = 100;
-
-    my @attributes = (
-        'minLength' => $min_length,
-        'maxLength' => $max_length,
-    );
-
-    if ( !$data_bar->{bar_no_border} ) {
-        push @attributes, ( 'border', 1 );
-    }
-
-    if ( $data_bar->{bar_solid} ) {
-        push @attributes, ( 'gradient', 0 );
-    }
-
-    if ( $data_bar->{bar_direction} eq 'left' ) {
-        push @attributes, ( 'direction', 'leftToRight' );
-    }
-
-    if ( $data_bar->{bar_direction} eq 'right' ) {
-        push @attributes, ( 'direction', 'rightToLeft' );
-    }
-
-    if ( $data_bar->{bar_negative_color_same} ) {
-        push @attributes, ( 'negativeBarColorSameAsPositive', 1 );
-    }
-
-    if (   !$data_bar->{bar_no_border}
-        && !$data_bar->{bar_negative_border_color_same} )
-    {
-        push @attributes, ( 'negativeBarBorderColorSameAsPositive', 0 );
-    }
-
-    if ( $data_bar->{bar_axis_position} eq 'middle') {
-        push @attributes, ( 'axisPosition', 'middle' );
-    }
-
-    if ( $data_bar->{bar_axis_position} eq 'none') {
-        push @attributes, ( 'axisPosition', 'none' );
-    }
-
-    $self->xml_start_tag( 'x14:dataBar', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_x14_border_color()
-#
-# Write the <x14:borderColor> element.
-#
-sub _write_x14_border_color {
-
-    my $self = shift;
-    my $rgb  = shift;
-
-    my @attributes = ( 'rgb' => $rgb );
-
-    $self->xml_empty_tag( 'x14:borderColor', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_x14_negative_fill_color()
-#
-# Write the <x14:negativeFillColor> element.
-#
-sub _write_x14_negative_fill_color {
-
-    my $self = shift;
-    my $rgb  = shift;
-
-    my @attributes = ( 'rgb' => $rgb );
-
-    $self->xml_empty_tag( 'x14:negativeFillColor', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_x14_negative_border_color()
-#
-# Write the <x14:negativeBorderColor> element.
-#
-sub _write_x14_negative_border_color {
-
-    my $self = shift;
-    my $rgb  = shift;
-
-    my @attributes = ( 'rgb' => $rgb );
-
-    $self->xml_empty_tag( 'x14:negativeBorderColor', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_x14_axis_color()
-#
-# Write the <x14:axisColor> element.
-#
-sub _write_x14_axis_color {
-
-    my $self = shift;
-    my $rgb  = shift;
-
-    my @attributes = ( 'rgb' => $rgb );
-
-    $self->xml_empty_tag( 'x14:axisColor', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_ext_list_sparklines()
-#
-# Write the sparkline subelements.
-#
-sub _write_ext_list_sparklines {
-
-    my $self       = shift;
-    my @sparklines = @{ $self->{_sparklines} };
-    my $count      = scalar @sparklines;
-
-    # Write the ext element.
-    $self->_write_ext('{05C60535-1F16-4fd2-B633-F4F36F0B64E0}');
-
-    # Write the x14:sparklineGroups element.
-    $self->_write_sparkline_groups();
-
-    # Write the sparkline elements.
-    for my $sparkline ( reverse @sparklines ) {
-
-        # Write the x14:sparklineGroup element.
-        $self->_write_sparkline_group( $sparkline );
-
-        # Write the x14:colorSeries element.
-        $self->_write_color_series( $sparkline->{_series_color} );
-
-        # Write the x14:colorNegative element.
-        $self->_write_color_negative( $sparkline->{_negative_color} );
-
-        # Write the x14:colorAxis element.
-        $self->_write_color_axis();
-
-        # Write the x14:colorMarkers element.
-        $self->_write_color_markers( $sparkline->{_markers_color} );
-
-        # Write the x14:colorFirst element.
-        $self->_write_color_first( $sparkline->{_first_color} );
-
-        # Write the x14:colorLast element.
-        $self->_write_color_last( $sparkline->{_last_color} );
-
-        # Write the x14:colorHigh element.
-        $self->_write_color_high( $sparkline->{_high_color} );
-
-        # Write the x14:colorLow element.
-        $self->_write_color_low( $sparkline->{_low_color} );
-
-        if ( $sparkline->{_date_axis} ) {
-            $self->xml_data_element( 'xm:f', $sparkline->{_date_axis} );
-        }
-
-        $self->_write_sparklines( $sparkline );
-
-        $self->xml_end_tag( 'x14:sparklineGroup' );
-    }
-
-
-    $self->xml_end_tag( 'x14:sparklineGroups' );
-    $self->xml_end_tag( 'ext' );
-}
-
-
-##############################################################################
-#
-# _write_sparklines()
-#
-# Write the <x14:sparklines> element and <x14:sparkline> subelements.
-#
-sub _write_sparklines {
-
-    my $self      = shift;
-    my $sparkline = shift;
-
-    # Write the sparkline elements.
-    $self->xml_start_tag( 'x14:sparklines' );
-
-    for my $i ( 0 .. $sparkline->{_count} - 1 ) {
-        my $range    = $sparkline->{_ranges}->[$i];
-        my $location = $sparkline->{_locations}->[$i];
-
-        $self->xml_start_tag( 'x14:sparkline' );
-        $self->xml_data_element( 'xm:f',     $range );
-        $self->xml_data_element( 'xm:sqref', $location );
-        $self->xml_end_tag( 'x14:sparkline' );
-    }
-
-
-    $self->xml_end_tag( 'x14:sparklines' );
-}
-
-
-##############################################################################
-#
-# _write_ext()
-#
-# Write the <ext> element for sparklines.
-#
-sub _write_ext {
-
-    my $self      = shift;
-    my $uri       = shift;
-    my $schema    = 'http://schemas.microsoft.com/office/';
-    my $xmlns_x14 = $schema . 'spreadsheetml/2009/9/main';
-
-    my @attributes = (
-        'xmlns:x14' => $xmlns_x14,
-        'uri'       => $uri,
-    );
-
-    $self->xml_start_tag( 'ext', @attributes );
-}
-
-
-##############################################################################
-#
-# _write_sparkline_groups()
-#
-# Write the <x14:sparklineGroups> element.
-#
-sub _write_sparkline_groups {
-
-    my $self     = shift;
-    my $xmlns_xm = 'http://schemas.microsoft.com/office/excel/2006/main';
-
-    my @attributes = ( 'xmlns:xm' => $xmlns_xm );
-
-    $self->xml_start_tag( 'x14:sparklineGroups', @attributes );
-
-}
-
-
-##############################################################################
-#
-# _write_sparkline_group()
-#
-# Write the <x14:sparklineGroup> element.
-#
-# Example for order.
-#
-# <x14:sparklineGroup
-#     manualMax="0"
-#     manualMin="0"
-#     lineWeight="2.25"
-#     type="column"
-#     dateAxis="1"
-#     displayEmptyCellsAs="span"
-#     markers="1"
-#     high="1"
-#     low="1"
-#     first="1"
-#     last="1"
-#     negative="1"
-#     displayXAxis="1"
-#     displayHidden="1"
-#     minAxisType="custom"
-#     maxAxisType="custom"
-#     rightToLeft="1">
-#
-sub _write_sparkline_group {
-
-    my $self     = shift;
-    my $opts     = shift;
-    my $empty    = $opts->{_empty};
-    my $user_max = 0;
-    my $user_min = 0;
-    my @a;
-
-    if ( defined $opts->{_max} ) {
-
-        if ( $opts->{_max} eq 'group' ) {
-            $opts->{_cust_max} = 'group';
-        }
-        else {
-            push @a, ( 'manualMax' => $opts->{_max} );
-            $opts->{_cust_max} = 'custom';
-        }
-    }
-
-    if ( defined $opts->{_min} ) {
-
-        if ( $opts->{_min} eq 'group' ) {
-            $opts->{_cust_min} = 'group';
-        }
-        else {
-            push @a, ( 'manualMin' => $opts->{_min} );
-            $opts->{_cust_min} = 'custom';
-        }
-    }
-
-
-    # Ignore the default type attribute (line).
-    if ( $opts->{_type} ne 'line' ) {
-        push @a, ( 'type' => $opts->{_type} );
-    }
-
-    push @a, ( 'lineWeight' => $opts->{_weight} ) if $opts->{_weight};
-    push @a, ( 'dateAxis' => 1 ) if $opts->{_date_axis};
-    push @a, ( 'displayEmptyCellsAs' => $empty ) if $empty;
-
-    push @a, ( 'markers'       => 1 )                  if $opts->{_markers};
-    push @a, ( 'high'          => 1 )                  if $opts->{_high};
-    push @a, ( 'low'           => 1 )                  if $opts->{_low};
-    push @a, ( 'first'         => 1 )                  if $opts->{_first};
-    push @a, ( 'last'          => 1 )                  if $opts->{_last};
-    push @a, ( 'negative'      => 1 )                  if $opts->{_negative};
-    push @a, ( 'displayXAxis'  => 1 )                  if $opts->{_axis};
-    push @a, ( 'displayHidden' => 1 )                  if $opts->{_hidden};
-    push @a, ( 'minAxisType'   => $opts->{_cust_min} ) if $opts->{_cust_min};
-    push @a, ( 'maxAxisType'   => $opts->{_cust_max} ) if $opts->{_cust_max};
-    push @a, ( 'rightToLeft'   => 1 )                  if $opts->{_reverse};
-
-    $self->xml_start_tag( 'x14:sparklineGroup', @a );
-}
-
-
-##############################################################################
-#
-# _write_spark_color()
-#
-# Helper function for the sparkline color functions below.
-#
-sub _write_spark_color {
-
-    my $self    = shift;
-    my $element = shift;
-    my $color   = shift;
-    my @attr;
-
-    push @attr, ( 'rgb'   => $color->{_rgb} )   if defined $color->{_rgb};
-    push @attr, ( 'theme' => $color->{_theme} ) if defined $color->{_theme};
-    push @attr, ( 'tint'  => $color->{_tint} )  if defined $color->{_tint};
-
-    $self->xml_empty_tag( $element, @attr );
-}
-
-
-##############################################################################
-#
-# _write_color_series()
-#
-# Write the <x14:colorSeries> element.
-#
-sub _write_color_series {
-
-    my $self = shift;
-
-    $self->_write_spark_color( 'x14:colorSeries', @_ );
-}
-
-
-##############################################################################
-#
-# _write_color_negative()
-#
-# Write the <x14:colorNegative> element.
-#
-sub _write_color_negative {
-
-    my $self = shift;
-
-    $self->_write_spark_color( 'x14:colorNegative', @_ );
-}
-
-
-##############################################################################
-#
-# _write_color_axis()
-#
-# Write the <x14:colorAxis> element.
-#
-sub _write_color_axis {
-
-    my $self = shift;
-
-    $self->_write_spark_color( 'x14:colorAxis', { _rgb => 'FF000000' } );
-}
-
-
-##############################################################################
-#
-# _write_color_markers()
-#
-# Write the <x14:colorMarkers> element.
-#
-sub _write_color_markers {
-
-    my $self = shift;
-
-    $self->_write_spark_color( 'x14:colorMarkers', @_ );
-}
-
-
-##############################################################################
-#
-# _write_color_first()
-#
-# Write the <x14:colorFirst> element.
-#
-sub _write_color_first {
-
-    my $self = shift;
-
-    $self->_write_spark_color( 'x14:colorFirst', @_ );
-}
-
-
-##############################################################################
-#
-# _write_color_last()
-#
-# Write the <x14:colorLast> element.
-#
-sub _write_color_last {
-
-    my $self = shift;
-
-    $self->_write_spark_color( 'x14:colorLast', @_ );
-}
-
-
-##############################################################################
-#
-# _write_color_high()
-#
-# Write the <x14:colorHigh> element.
-#
-sub _write_color_high {
-
-    my $self = shift;
-
-    $self->_write_spark_color( 'x14:colorHigh', @_ );
-}
-
-
-##############################################################################
-#
-# _write_color_low()
-#
-# Write the <x14:colorLow> element.
-#
-sub _write_color_low {
-
-    my $self = shift;
-
-    $self->_write_spark_color( 'x14:colorLow', @_ );
-}
-
-
-1;
-
-
-__END__
-
-
-=head1 NAME
-
-Worksheet - A class for writing Excel Worksheets.
-
-=head1 SYNOPSIS
-
-See the documentation for L<Excel::Writer::XLSX>
-
-=head1 DESCRIPTION
-
-This module is used in conjunction with L<Excel::Writer::XLSX>.
-
-=head1 AUTHOR
-
-John McNamara jmcnamara@cpan.org
-
-=head1 COPYRIGHT
-
-(c) MM-MMXVIII, 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.
diff --git a/tools/notused/DepList.pm b/tools/notused/DepList.pm
new file mode 100644 (file)
index 0000000..dd02f17
--- /dev/null
@@ -0,0 +1,10 @@
+package ListDependencies;
+use strict;
+unshift @INC, sub {
+        local $_ = $_[1];
+        return unless /[[:upper:]]/;
+        s/\.pm$//i;
+        s/[\/:]/::/g;
+        print STDERR $_, $/;
+};
+1;
\ No newline at end of file
diff --git a/tools/notused/SIngleOLDFichesSaveAsPDF.pl b/tools/notused/SIngleOLDFichesSaveAsPDF.pl
new file mode 100644 (file)
index 0000000..a8c7a3b
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+use strict;
+use File::Basename;
+use Getopt::Long;
+use JSON::PP;
+my $druckpfad = "";
+my $druckpfad2 = "";
+my $pdfkopiename = "";
+my $pdfkopiename2 = "";
+my $txttitle = "";
+my $txtsubject = "";
+my $strzusatzpfade = "";
+my $tag = "";
+#my $projectpath = $ENV{"HOME"}."/Desktop/1-Neuer Server/A=Architecture/";
+GetOptions("d|data=s" => \$druckpfad,
+                       "path2|p2=s" => \$druckpfad2,
+                       "file1|f1=s" => \$pdfkopiename,
+                       "file2|f2=s" => \$pdfkopiename2,
+                       "title|t=s" => \$txttitle,
+                       "subject|s=s" => \$txtsubject,
+                       "file2path|pz=s" => \$strzusatzpfade,"tag|g=s" => \$tag);
+if (! -e $druckpfad.'/'.$pdfkopiename){
+       print "FEHLER! Zu kopierende Datei: ".$druckpfad.'/'.$pdfkopiename." existsiert nicht!\n";
+       exit(0);
+}
+
+my $cmd = '"'.dirname($0).'/pdfinfo.pl" -f "'.$druckpfad.'/'.$pdfkopiename.'" -t "'.$txttitle.'" -s "'.$txtsubject.'"';
+my $res = `$cmd`;
+print $pdfkopiename." Meta-Daten eingefügt!\n";
+if ($tag ne ""){
+       &settag($druckpfad.'/'.$pdfkopiename,$tag);
+}
+
+$cmd = 'cp "'.$druckpfad.'/'.$pdfkopiename.'" "'.$druckpfad2.'/'.$pdfkopiename2.'"';
+$res = `$cmd`;
+print $pdfkopiename2.": Kopie erstellt!\n";
+my @zp = split(",",$strzusatzpfade);
+print "Zusätzliche kopien erstellt in:\n";
+foreach my $p (@zp){
+       if (! -d $p){mkdir($p);}
+       my $ptemp = substr($p,length($projectpath));
+       print "$ptemp\n";
+       $cmd = 'cp "'.$druckpfad.'/'.$pdfkopiename.'" "'.$p.'/'.$pdfkopiename2.'"';
+       $res = `$cmd`;
+       if ($tag ne ""){
+               &settag($p.'/'.$pdfkopiename2,$tag);
+       }
+}
+
+sub settag(){
+       my $file = shift;
+       my $ftag = shift;
+       system('xattr -w com.apple.metadata:_kMDItemUserTags \'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><array><string>'.$ftag.'</string></array></plist>\' "'.$file.'"');
+}
+
diff --git a/tools/notused/depencency.pl b/tools/notused/depencency.pl
new file mode 100644 (file)
index 0000000..08378ad
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use 5.010;
+use File::Basename;
+use Data::Dumper;
+use lib ('/Users/kilian/perl5/lib/perl5');
+#use lib ('/Users/kilian/perl5/lib/perl5');
+use MetaCPAN::API;
+my $mcpan = MetaCPAN::API->new;
+my $module   = $mcpan->module( 'Excel::Writer::XLSX' );
+say Dumper $module;
\ No newline at end of file
diff --git a/tools/notused/import_sommaire_data.pl b/tools/notused/import_sommaire_data.pl
new file mode 100644 (file)
index 0000000..0b5c8ba
--- /dev/null
@@ -0,0 +1,118 @@
+#!/usr/bin/perl
+
+use strict;
+use Getopt::Long;
+use File::Basename;
+use utf8;
+use lib (dirname($0).'/lib/perl5');
+
+my $xlsfile = "";
+my $datafile = "";
+my $cellstart = "";
+my $sheet = "";
+GetOptions("file|n=s" => \$xlsfile,"data|t=s" => \$datafile, "cell|c=s" => \$cellstart, "sheet|s=s" => \$sheet);
+if (! -e $datafile){
+       exit(-1);
+}
+
+my ($r,$c)=&xl_cell_to_rowcol(uc($cellstart));
+my $apl = 'tell application "/Applications/Microsoft Excel.app"
+               launch
+               activate object workbook "'.$xlsfile.'" 
+               activate object worksheet "'.$sheet.'"'."\n";
+open(DAT,$datafile);   
+my $dx = <DAT>;
+close(DAT);
+my @data = split(/\r/,$dx);
+foreach my $l (@data){
+       my $xc = $c;
+       
+       my @rdata = split('<!>',$l);
+               $apl .= 'set value of cell "'.&xl_rowcol_to_cell($r,$xc).'" to "'.$rdata[0].'"'."\n";
+               $xc++;
+               $apl .= 'set value of cell "'.&xl_rowcol_to_cell($r,$xc).'" to "'.$rdata[1].'"'."\n";
+               $xc++;
+               $apl .= 'set value of cell "'.&xl_rowcol_to_cell($r,$xc).'" to "'.$rdata[2].'"'."\n";
+               $xc++;
+               $apl .= 'set value of cell "'.&xl_rowcol_to_cell($r,$xc).'" to "'.$rdata[3].'"'."\n";
+        $xc++;
+               $apl .= 'set value of cell "'.&xl_rowcol_to_cell($r,$xc).'" to "'.$rdata[4].'"'."\n";
+        $xc++;
+               $apl .= 'set value of cell "'.&xl_rowcol_to_cell($r,$xc).'" to "'.$rdata[5].'"'."\n";
+        $xc++;
+               $apl .= 'set value of cell "'.&xl_rowcol_to_cell($r,$xc).'" to "'.$rdata[6].'"'."\n";
+        $xc++;
+               $apl .= 'set value of cell "'.&xl_rowcol_to_cell($r,$xc).'" to "'.$rdata[7].'"'."\n";
+               
+       $r++;
+}
+
+
+$apl .= 'end tell'."\n";
+open(SCPT,">".$ENV{HOME}.'/importsommaire.scpt');
+print SCPT $apl;
+close(SCPT);
+my $cmd = 'osascript '.$ENV{HOME}.'/importsommaire.scpt';
+my $st = system($cmd);
+if ($st == 0){
+    unlink($ENV{HOME}.'/importsommaire.scpt');
+}
+
+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;
+}
+
+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;
+}
\ No newline at end of file
diff --git a/tools/notused/readEmailDataFromActiveSheet2.pl b/tools/notused/readEmailDataFromActiveSheet2.pl
new file mode 100644 (file)
index 0000000..519c95d
--- /dev/null
@@ -0,0 +1,91 @@
+#!/usr/bin/perl
+
+use strict;
+use Data::Dumper;
+my $aplscript = 'tell application "/Applications/Microsoft Excel.app
+launch
+set tolist to value of range ("AA16:AA116")
+';
+
+# my $aplscript = 'tell application "/Applications/Microsoft Excel.app"
+# launch
+# set nl to "\n"
+# set bodytext to {}
+# -- emails-addressen
+# set tolist to value of range ("AA16:AA116")
+# -- Betreff
+# set subject to value of range ("BA3")
+# --Body head
+# set bodyhead to value of range ("BA5:BA7")
+# --body foot
+# set bodyfoot to value of range ("BA9")
+# --body files
+# set bodyfiles to value of range ("BA16:BA116")
+# set rw to 20
+#      set fx to 10
+#      set px to 13
+#      set cellp to "BA20"
+#     set cellf to "CB" & fx
+#      set celld to "CB" & px
+#      set curx to value of cell cellp
+#      set fileslist to {}
+#      set pathlist to {}
+#      set bodyfiles to {}
+#      set end of fileslist to value of range("A11") & ".pdf"
+#      set end of pathlist to value of range("A2")
+#      set end of bodyfiles to value of range ("BA3")
+#      repeat until curx is equal to ""
+#              set end of fileslist to value of cell cellf
+#              set end of pathlist to value of cell celld
+#              set end of bodyfiles to curx
+#              set fx to fx + 10
+#              set px to px + 10
+#              set rw to rw + 1
+#              set cellp to "BA" & rw
+#              set cellf to "CB" & fx
+#              set celld to "CB" & px
+#         set curx to value of cell cellp
+#      end repeat
+#      --set end of fileslist to name of active workbook
+#      set xlsfilepath to path of active workbook
+#      --set xlsdocfolder to value of range("A2")
+#      --set xlsdocname to value of range("A11")
+#      return {tolist, nl, subject, nl, bodyhead, nl, bodyfoot, nl, bodyfiles,nl, fileslist,nl, pathlist,nl, xlsfilepath}
+# end tell'."\n";
+
+my $res = `osascript -l JavaScript -e \'$aplscript\'`;
+print $res;
+# $res =~ s/{""},//g;
+# $res =~ s/\ +/ /g;
+# $res =~ s/"}, {"/<!>/g;
+
+# #$res =~ s/[\<\!\>]+/<!>/g;
+#   $res =~ s/", {{"//g;
+#  $res =~ s/", "/<=>/g;
+#   $res =~ s/"}}, "//g;
+#   $res =~ s/{{{"//g;
+#   $res =~ s/"}}}//g;
+#   $res =~ s/", {"//g;
+#   $res =~ s/"}, "//g;
+#   $res =~ s/"}}//g;
+#   $res =~ s/"}//g;
+#   $res =~ s/^<=>//mg;
+#   $res =~ s/<=>$//mg;
+#   $res =~ s/{{ {"//g;
+#  chomp($res);
+# my @test = split(/\n/,$res);
+# my $pp = $test[scalar(@test)-1];
+# my @spl = split("/",$pp);
+# my @nspl = ();
+# for (my $i=1;$i<=6;$i++){
+#     push (@nspl,$spl[$i]);
+# }
+# $test[scalar(@test)-1] = "/".join("/",@nspl)."/";  
+# print join("\n",@test)."\n";
+
+# 
+#  foreach my $f (@test){
+#      my @dx = split("<=>",$f);
+#      print scalar(@dx)."\n";
+#      print Dumper(@dx);
+#  }
diff --git a/tools/solanapmt/img/Microsoft_Excel_Import.svg b/tools/solanapmt/img/Microsoft_Excel_Import.svg
new file mode 100644 (file)
index 0000000..7c50662
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   id="svg4"
+   viewBox="0 0 200.36 194"
+   height="50px"
+   width="50px"
+   y="0px"
+   x="0px"
+   version="1.2">
+  <metadata
+     id="metadata10">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs8" />
+  <path
+     id="path2"
+     d="M154.46533,137.481L154.46533,157.481 187.3549,157.481 187.3549,137.481z M154.46533,111.481L154.46533,131.481 187.3549,131.481 187.3549,111.481z M154.46533,85.481003L154.46533,105.481 187.3549,105.481 187.3549,85.481003z M67.577698,60.258995L57.577702,85.148185 49.133602,62.481582 35.577702,62.481582 49.3563,95.14814 34.023003,127.593 47.3563,127.593 57.3563,104.4811 67.3563,129.81398 82.023003,129.81398 65.133598,94.037147 80.911003,60.258995z M154.46533,59.4813L154.46533,79.4813 187.3549,79.4813 187.3549,59.4813z M154.46533,33.4813L154.46533,53.4813 187.3549,53.4813 187.3549,33.4813z M119.021,22.147997L197.68828,22.147997C199.161,22.147999,200.355,23.341999,200.355,24.814699L200.355,167.481C200.355,168.955,199.161,170.147,197.68828,170.147L119.021,170.147 119.021,157.481 148.35458,157.481 148.35458,137.481 119.021,137.481 119.021,131.481 148.35458,131.481 148.35458,111.481 119.021,111.481 119.021,105.481 148.35458,105.481 148.35458,85.481003 119.021,85.481003 119.021,79.4813 148.35458,79.4813 148.35458,59.4813 119.021,59.4813 119.021,53.4813 148.35458,53.4813 148.35458,33.4813 119.021,33.4813z M115.333,0L115.333,193.99899 0,173.99902 0,19.99996z" />
+  <path
+     style="stroke-width:2.6728024;stroke:#000000;stroke-opacity:1;fill:#ffffff;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1"
+     id="path2-8"
+     d="m 140.34018,2.181317 h 20.16729 V 79.4813 h 16.80917 L 150.42382,132.26266 123.5311,79.4813 h 16.80908 z" />
+</svg>
index 59a7884..7f2008d 100755 (executable)
@@ -5,7 +5,7 @@ use File::Find::Rule;
 use Getopt::Long;
 use File::Basename;
 use FindBin qw($RealBin);
-use lib ('/Users/kilian/Workspace/SolanaPMT/tools/lib/perl5');
+use lib ('/Users/kilian/Workspace/SolanaPMT/tools/lib/perl5');
 use lib ($RealBin.'/lib/perl5');
 use lib (dirname($0).'/lib/perl5');
 use Image::ExifTool;
diff --git a/tools/used/.DS_Store b/tools/used/.DS_Store
new file mode 100644 (file)
index 0000000..530b465
Binary files /dev/null and b/tools/used/.DS_Store differ
diff --git a/tools/used/ExcelDruckenGetData.pl b/tools/used/ExcelDruckenGetData.pl
new file mode 100644 (file)
index 0000000..eb5129c
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+
+use strict;
+use File::Basename;
+use Data::Dumper;
+
+my $aplscript = 'tell application "/Applications/Microsoft Excel.app"
+launch
+set retlist to {}
+set end of retlist to value of range ("A2")
+set end of retlist to value of range ("A11")
+set end of retlist to value of range ("A12")
+set end of retlist to value of range ("A13")
+set end of retlist to value of range ("A14")
+set end of retlist to value of range ("A15")
+set retlist2 to {}
+set end of retlist2 to value of range ("A16:A116")
+return { retlist,"xxx",retlist2 }
+end tell'."\n";
+
+my $res = `osascript -ss -e \'$aplscript\'`;
+chomp($res);
+$res =~ s/{{"//g;
+$res =~ s/"}}//g;
+$res =~ s/"}, "xxx", {/\n/g;
+#$res =~ s/^{{{{"//;
+#$res =~ s/"}}}}$//;
+$res =~ s/"}, {"/<=>/g;
+$res =~ s/", "/<=>/g;
+#$res =~ s/^{"//mg;
+$res =~ s/}}$//;
+#$res =~ s/"}}$//mg;
+#$res =~ s/^{{"//mg;
+&getprintedPDF($res);
+print $res;
+
+sub getprintedPDF(){
+    my $data = shift;
+    my @x = split("<=>",$data);
+    if (-e $ENV{"HOME"}.'/Library/Containers/com.microsoft.Excel/Data/Documents/'.$x[1].'.pdf'){
+        system("mv \"".$ENV{"HOME"}.'/Library/Containers/com.microsoft.Excel/Data/Documents/'.$x[1].'.pdf'."\" \"".$x[0].$x[1].'.pdf'."\"");
+    }
+}
diff --git a/tools/used/ExcelSaveAsPDF.pl b/tools/used/ExcelSaveAsPDF.pl
new file mode 100644 (file)
index 0000000..a2828b2
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+use strict;
+use File::Basename;
+use Getopt::Long;
+use File::Path;
+
+my $druckpfad = "";
+my $druckpfad2 = "";
+my $pdfkopiename = "";
+my $pdfkopiename2 = "";
+my $txttitle = "";
+my $txtsubject = "";
+my $strzusatzpfade = "";
+my $tag = "";
+#my $projectpath = $ENV{"HOME"}."/Desktop/1-Neuer Server/A=Architecture/";
+GetOptions("d|data=s" => \$druckpfad,
+                       "path2|p2=s" => \$druckpfad2,
+                       #"file1|f1=s" => \$pdfkopiename,
+                       "file2|f2=s" => \$pdfkopiename2,
+                       "title|t=s" => \$txttitle,
+                       "subject|s=s" => \$txtsubject,
+                       "file2path|pz=s" => \$strzusatzpfade,"tag|g=s" => \$tag);
+
+if (! -e $druckpfad){
+       &writelog("FEHLER! Zu kopierende Datei: ".$druckpfad." existsiert nicht!");
+       exit(0);
+}
+&writelog("Fiche: zu kopierende Datei: ".$druckpfad);
+my $cmd = '"'.dirname($0).'/pdfinfo.pl" -f "'.$druckpfad.'" -t "'.$txttitle.'" -s "'.$txtsubject.'"';
+my $res = `$cmd`;
+&writelog(basename($druckpfad)." Meta-Daten eingefügt!");
+if ($tag ne ""){
+       &settag($druckpfad,$tag);
+}
+
+my @zp = split(",",$strzusatzpfade);
+#print "Zusätzliche kopien erstellt als ".$pdfkopiename2." in:\n\n";
+foreach my $p (@zp){
+       #print "$p\n-\n";
+       if (! -d $p){mkdir($p);}
+       $cmd = 'cp "'.$druckpfad.'" "'.$p.'/'.$pdfkopiename2.'"';
+       my $st = system($cmd);
+       if ($st > 0){
+               &writelog("Fiche PDF-Kopie: ".$p.'/'.$pdfkopiename2." konnte nicht erstellt werden!");
+       }else {
+               &writelog("Fiche PDF-Kopie: ".$p.'/'.$pdfkopiename2." erstellt!");
+       }
+       if ($tag ne ""){
+               &settag($p.'/'.$pdfkopiename2,$tag);
+       }
+}
+
+sub settag(){
+       my $file = shift;
+       my $ftag = shift;
+       my $cmd = 'xattr -w com.apple.metadata:_kMDItemUserTags \'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><array><string>';
+       $cmd .= $ftag.'</string></array></plist>\' "'.$file.'" &';
+       &writelog("Fiche: Setze Tag auf '".$ftag."' :".$file);
+       my $st = system($cmd);
+       if ($st > 0){
+               &writelog("Fiche: Konnte Tag '".$ftag."' nicht auf ".$file." setzen");
+       }
+}
+
+sub writelog(){
+       my $msg = shift;
+       # 
+       open (LOG,">>".$ENV{HOME}.'/SolanaPMT.log');
+       print LOG localtime().": ".$msg."\n";
+       close(LOG);
+}
+
diff --git a/tools/used/FichesSaveAsPDF.pl b/tools/used/FichesSaveAsPDF.pl
new file mode 100644 (file)
index 0000000..e8bbb6a
--- /dev/null
@@ -0,0 +1,107 @@
+#!/usr/bin/perl
+use strict;
+use File::Basename;
+use Getopt::Long;
+use Data::Dumper;
+my $dateiexportdaten = "";
+my $strzusatzpfade = "";
+GetOptions("d|data=s" => \$dateiexportdaten,
+                  "p|pfade=s" => \$strzusatzpfade);
+                  
+my @zusatzpfade = split(',',$strzusatzpfade);
+
+if (! -e $dateiexportdaten){
+       &writelog("Datei $dateiexportdaten existiert nicht!");
+       exit(0);
+}
+my @data = ();
+open (DATA,$dateiexportdaten);
+while (my $l = <DATA>){
+       chomp($l);
+       $l =~ s/\r//g;
+       $l =~ s/\n//g;
+       my @spdata = split("<!>",$l);
+       my $d->{file} = $spdata[0];
+       $d->{title} = $spdata[1];
+       $d->{subject} = $spdata[2];
+       $d->{newpdfname} = $spdata[3];
+       $d->{newpdfnamecopy} = $spdata[4];
+       $d->{tag} = $spdata[5];
+       $d->{copytopath} = $spdata[6];
+       push (@data,$d); 
+}
+close (DATA);
+#print Dumper(@data);
+foreach my $f (@data){
+       my $rx = rindex($f->{file},"."); 
+       my $suffix = substr($f->{file},$rx);
+       #print $suffix."\n";
+       if ($f->{newpdfname} !~ /$suffix$/){
+                       $f->{newpdfname} = $f->{newpdfname}.$suffix;
+       }
+       if ($f->{newpdfnamecopy} !~ /$suffix$/){
+                       $f->{newpdfnamecopy} = $f->{newpdfnamecopy}.$suffix;
+       }
+       
+       if (-e $f->{file}){
+               if (! -d $f->{copytopath}){
+                       my $r = system("mkdir -p '".$f->{copytopath}."'");
+                       &writelog($r." Nicht existierender Ordner erstellt:".$f->{copytopath});
+               }
+               if ($f->{tag} ne ""){
+                       &settag($f->{file},$f->{tag});
+               }
+               my $r =system('cp "'.$f->{file}.'" "'.$f->{copytopath}.'/'.$f->{newpdfname}.'"');
+               if ($r == 0){
+                       &writelog("Datei kopiert: ".$f->{file}.'=>'.$f->{copytopath}.'/'.$f->{newpdfname});
+               } else {
+                       &writelog($r.": Fehler beim kopieren der Datei: ".$f->{file}.'=>'.$f->{copytopath}.'/'.$f->{newpdfname});
+               }
+
+               if ($f->{tag} ne ""){
+                       
+                       &settag($f->{copytopath}.'/'.$f->{newpdfname},$f->{tag});
+               }
+               my $cmd = '"'.dirname($0).'/pdfinfo.pl" -f "'.$f->{copytopath}.'/'.$f->{newpdfname}.'" -t "'.$f->{title}.'" -s "'.$f->{subject}.'"';
+               my $res = `$cmd`;
+               foreach my $z (@zusatzpfade){
+                       if (! -d $z){
+                               my $r = system("mkdir -p '".$z."'");
+                               &writelog("Nicht existierender Ordner erstellt:".$z);
+                       }
+                       
+                       my $cr = system('cp "'.$f->{copytopath}.'/'.$f->{newpdfname}.'" "'.$z.'/'.$f->{newpdfnamecopy}.'"');
+                       if ($cr == 0){
+                               &writelog("Datei kopiert: ".$f->{file}.'=>'.$z.'/'.$f->{newpdfname});
+                       } else {
+                               &writelog($cr.": Fehler beim kopieren der Datei: ".$f->{file}.'=>'.$z.'/'.$f->{newpdfname});
+                       }
+                       if ($f->{tag} ne ""){
+                               &settag($z.'/'.$f->{newpdfnamecopy},$f->{tag});
+                       }
+               }
+       } else {
+               &writelog($f->{file}." existiert nicht!");
+       }
+}
+
+
+sub settag(){
+       my $file = shift;
+       my $ftag = shift;
+       my $cmd = 'xattr -w com.apple.metadata:_kMDItemUserTags \'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><array><string>';
+       $cmd .= $ftag.'</string></array></plist>\' "'.$file.'" &';
+       &writelog("Setze Tag auf '".$ftag."' :".$file);
+       my $st = system($cmd);
+       if ($st > 0){
+               &writelog("Konnte Tag '".$ftag."' nicht auf ".$file." setzen");
+       }
+}
+
+sub writelog(){
+       my $msg = shift;
+       #print $msg."\n-\n";
+       open (LOG,">>".$ENV{HOME}.'/SolanaPMT.log');
+       print LOG localtime().": ".$msg."\n";
+       close(LOG);
+}
diff --git a/tools/used/Project/cloneproject.pl b/tools/used/Project/cloneproject.pl
new file mode 100644 (file)
index 0000000..7247eb5
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/env perl
+
+use strict;
+use Getopt::Long;
+use File::Basename;
+use lib ('./lib/perl5');
+use File::Copy;
+use File::Path qw(make_path);
+use File::Find::Rule;
+use utf8;
+#use File::Copy::Recursive qw(rcopy);
+
+my $psource = "";
+my $ptarget = "";
+my $strftypes = "";
+
+GetOptions("source|q=s" => \$psource, "target|t=s" => \$ptarget, "ftypes|f=s" => \$strftypes);
+
+if (! -d $psource ){
+ print "No source path!\n";
+ exit(1);
+}
+
+my $srcrename = basename($psource);
+my $trgrename = basename($ptarget);
+
+if (! -d $ptarget){
+ mkdir($ptarget);
+}
+
+#find all directories to clone
+my @subdirs = File::Find::Rule->directory->in( $psource );
+foreach my $d (@subdirs){
+ my $targetpath = $d;
+ $targetpath =~ s/$psource/$ptarget/;
+ $targetpath =~ s/$srcrename/$trgrename/g;
+ make_path($targetpath);
+ #print $d."\n".$targetpath."\n";
+}
+print "Files:\n";
+#find all files to clone
+if ($strftypes ne ""){
+my @files = File::Find::Rule->file()->name( qr/\.($strftypes)$/ )->in($psource);
+foreach my $f (@files){
+ my $targetpath = $f;
+ $targetpath =~ s/$psource/$ptarget/;
+ $targetpath =~ s/$srcrename/$trgrename/g;
+ copy($f,$targetpath);
+ #print $f."\n".$targetpath."\n";
+}
+}
+#sub rename_projectfilesandfolders($){
+#  #sub renamedir(){
+#  my $d = shift;
+#  print $d."\n";
+#  opendir(DD,$d);
+#  #my @files = ();
+#  my @dirs = ();
+#  while (my $l = readdir(DD)){
+#   my $nname = $l;
+#   if ($l =~ /^\./){ next ;}
+#   if ($l =~ /\.app$/){ next ;}
+#   if (-f $d.'/'.$l){ 
+#    my $fname = $cfg->{projektname}.'-'.$l;
+#    #$fname =~ s/:/|/g; 
+#    rename($d.'/'.$l,$d.'/'.$fname); 
+#   }
+#   if (-d $d.'/'.$l){
+#    my $dname = $cfg->{projektname}.'-'.$l;
+#    rename($d.'/'.$l,$d.'/'.$dname);
+#    print "Push: ".$d.'/'.$dname;
+#    push @dirs,$d.'/'.$dname;
+#   }
+#
+# }
+# close(DD);
+# print Dumper(@dirs);
+# foreach my $f (@dirs){
+#   rename_projectfilesandfolders($f);
+# }
+#}
+#
+#sub newproject(){
+#      mkdir($cfg->{targetfolder}.'/'.$cfg->{projektname});
+#      rcopy($cfg->{templatefolder}.'/*',$cfg->{targetfolder}.'/'.$cfg->{projektname}.'/');
+#      rename_projectfilesandfolders($cfg->{targetfolder}.'/'.$cfg->{projektname});
+#      Tkx::tk___messageBox(
+#      -parent => $top,
+#         -icon => "info",
+#         -title => "Neues Projekt",
+#         -message => "Ein neues Projekt ".$cfg->{projektname}." wurde in \n".$cfg->{targetfolder}.'/'.$cfg->{projektname}." erstellt!"
+#      );
+#}
\ No newline at end of file
diff --git a/tools/used/Project/rename_path.pl b/tools/used/Project/rename_path.pl
new file mode 100644 (file)
index 0000000..ef843ec
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+
+use strict;
+use utf8;
+
+my $dir = $ARGV[0];
+#my $appscr = 'osascript -e "set FolderName to quoted form of POSIX path of (choose folder with prompt \"Ordner auswählen\")"';
+#my $dir = `$appscr`;
+#my $dir = $argv[0];
+chomp($dir);
+$dir =~ s/^'//;
+$dir =~ s/\/'$//;
+
+&renamedir($dir);
+
+sub renamedir(){
+ my $d = shift;
+ print $d."\n";
+ opendir(DD,$d);
+ my @sf = ();
+ while (my $l = readdir(DD)){
+  my $nname = $l;
+  if ($l =~ /^\./){ next ;}
+  if (! -d $d.'/'.$l){ next; }
+  if ($l =~ /:/){
+   $nname = $l;
+   $nname =~ s/:/|/g; 
+   rename($d.'/'.$l,$d.'/'.$nname);
+  }
+  push @sf,$d.'/'.$nname;
+  
+ }
+ close(DD);
+ foreach my $f (@sf){
+  &renamedir($f);
+ }
+}
\ No newline at end of file
diff --git a/tools/used/Project/seticon.py b/tools/used/Project/seticon.py
new file mode 100644 (file)
index 0000000..6678155
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/env python
+import Cocoa
+import sys
+Cocoa.NSWorkspace.sharedWorkspace().setIcon_forFile_options_(Cocoa.NSImage.alloc().initWithContentsOfFile_(sys.argv[1].decode('utf-8')), sys.argv[2].decode('utf-8'), 0) or sys.exit("Unable to set file icon")
diff --git a/tools/used/find_sommaire_files.pl b/tools/used/find_sommaire_files.pl
new file mode 100644 (file)
index 0000000..1e64d37
--- /dev/null
@@ -0,0 +1,154 @@
+#!/usr/bin/perl
+
+use strict;
+use File::Find::Rule;
+use Getopt::Long;
+use File::Basename;
+use Data::Dumper;
+use lib (dirname($0).'/lib/perl5');
+use Image::ExifTool;
+use utf8;
+my $ppath = "";
+my $project = "";
+my @filter = ();
+my @excludes = ();
+GetOptions("project|n=s" => \$project,"projectpath|p=s" => \$ppath,"filter|f=s" => \@filter , "exclude|e=s"=> \@excludes);
+open(DD,">/Users/kilian/test.log");
+print DD "project: ".$project."\n";
+print DD "path: ".$ppath."\n";
+print DD "filter: ".Dumper(@filter)."\n";
+print DD "excludes: ".Dumper(@excludes)."\n";
+if (! -d $ppath){
+       print "$ppath does not exist!\n";
+       exit(1);
+}
+my $fil = $filter[0];
+my $outdata = ();
+
+my @files = File::Find::Rule->file()->in( $ppath );
+print DD "Total Files:".scalar(@files)."\n";
+my $cnt = 0;
+print DD "Filters".scalar(@filter)."\n";
+foreach my $f (@files){
+       my $fname = basename($f);
+       #print $cnt." - ".$fname."\n";
+       if ( ($fname =~ /^\./ ) || ($fname =~ /^\~/ ) || ($f =~ /\.app\//) ||
+       ($f =~ /\.app\//) || ($f =~ /\/\./)) { next;}
+       #print $fname."\n";
+       my $bok = 0;
+       if (scalar(@filter) > 0){
+       foreach my $fil (@filter){
+               #print "->$fil<-\n";
+               if (($fname =~ /$fil/) && ($bok == 0)){
+                       #print "$fname OK\n";
+                       my ($y,$m,$d);
+                       if ($fname =~ /\^/){
+                               ($y,$m,$d) = $fname =~ m/.*\^(\d{2,})(\d{2,})(\d{2,}).*/;
+                       } else {
+                               ($y,$m,$d) = $fname =~ m/.*\((\d{2,})(\d{2,})(\d{2,})\).*/;
+                       }
+                       #my $srtdate = $y.$m.$d;
+                       my $senddate = $d.'.'.$m.'.20'.$y;
+                       $outdata->{$cnt}->{senddate} = $senddate; 
+                       $outdata->{$cnt}->{filename} = $fname;
+                       my ($t,$s,$g,$m,$x) = &getfileinfo($f);
+                       $outdata->{$cnt}->{title} = $t;
+                       $outdata->{$cnt}->{description} = $s;
+                       $outdata->{$cnt}->{path} = $f;
+                       $outdata->{$cnt}->{size} = $g;
+                       $outdata->{$cnt}->{moddate} = $m;
+                       $outdata->{$cnt}->{ftype} = $x;
+                       $bok = 1;
+               }
+       }
+       }
+       else {
+               my ($y,$m,$d);
+                       if ($fname =~ /\^/){
+                               ($y,$m,$d) = $fname =~ m/.*\^(\d{2,})(\d{2,})(\d{2,}).*/;
+                       } elsif ($fname =~ /\((\d{2,})(\d{2,})(\d{2,})\)/) {
+                               ($y,$m,$d) = $fname =~ m/.*\((\d{2,})(\d{2,})(\d{2,})\).*/;
+                       } else {
+                               ($y,$m,$d) = $fname =~ m/.*(\d{2,})(\d{2,})(\d{2,}).*/;
+                       }
+                       #my $srtdate = $y.$m.$d;
+                       my $senddate = $d.'.'.$m.'.20'.$y;
+                       $outdata->{$cnt}->{senddate} = $senddate; 
+                       $outdata->{$cnt}->{filename} = $fname;
+                       my ($t,$s,$g,$m,$x) = &getfileinfo($f);
+                       $outdata->{$cnt}->{title} = $t;
+                       $outdata->{$cnt}->{description} = $s;
+                       $outdata->{$cnt}->{path} = $f;
+                       $outdata->{$cnt}->{size} = $g;
+                       $outdata->{$cnt}->{moddate} = $m;
+                       $outdata->{$cnt}->{ftype} = $x;
+       }
+       $cnt++;
+}
+print DD Dumper($outdata)."\n";
+#print keys(%{$outdata})." after filter!\n";
+foreach my $o (keys(%{$outdata})){
+       foreach my $excl (@excludes){
+               if ($outdata->{$o}->{filename} =~ /$excl/){
+                       #print DD "Exclude: ".$outdata->{$o}->{filename}."\n";
+                       delete($outdata->{$o});
+                       next;
+               }
+       }
+}
+foreach my $o (sort {$a <=> $b} keys(%{$outdata})){
+       print $outdata->{$o}->{senddate}."<|>".
+                 $outdata->{$o}->{filename}."<|>".
+                 $outdata->{$o}->{title}."<|>".
+                 $outdata->{$o}->{description}."<|>".
+                 $outdata->{$o}->{path}."<|>".
+                 $outdata->{$o}->{size}."<|>".
+                 $outdata->{$o}->{moddate}."<|>".
+                 $outdata->{$o}->{ftype}."<|>\n";
+}
+
+close(DD);
+sub getfileinfo(){
+       my $file = shift;
+       my $title = "";
+       my $subject = "";
+       my $info = Image::ExifTool::ImageInfo($file);
+       foreach my $k (keys(%{$info})){
+               #print $k."=>".$info->{$k}."\n";
+               if ($k eq "Title"){
+                       $title = $info->{$k};
+               }
+               if ($k eq "Subject"){
+                       $subject = $info->{$k};
+               } 
+               
+       }
+       my @st = stat($file);
+       my $size = $st[7];
+       $size = $size/1024;
+       $size =~ s/\.\d+//g;
+       $size = $size." KB";
+       my $md = &udatetohr($st[9]);
+       my $ftype = &getfiletype($file);
+       return ($title,$subject,$size,$md,$ftype);
+}
+
+sub udatetohr(){
+       my $udate = shift;
+       my @d = localtime($udate);
+       $d[5] = $d[5] + 1900;
+       $d[4] = $d[4] + 1;
+       if ($d[4] < 10){$d[4] = '0'.$d[4];}
+       if ($d[3] < 10){$d[3] = '0'.$d[3];}
+       if ($d[2] < 10){$d[2] = '0'.$d[2];}
+       if ($d[1] < 10){$d[1] = '0'.$d[1];}
+  return ($d[3].'.'.$d[4].'.'.$d[5].' '.$d[2].':'.$d[1]);
+}
+
+sub getfiletype(){
+       my $ft = shift;
+       my $cmd = 'file -b "'.$ft.'"';
+       my $ftype = `$cmd`;
+       chomp($ftype);
+       return $ftype;
+}
diff --git a/tools/used/getopenxlsfiles.pl b/tools/used/getopenxlsfiles.pl
new file mode 100644 (file)
index 0000000..6006fb9
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+
+use strict;
+use Data::Dumper;
+use utf8;
+my $isxlsopen = `ps ax | grep "/Applications/Microsoft Excel.app" | grep -v " grep "`;
+if ($isxlsopen eq ""){
+       print "INFO! excel closed!\n";
+       exit(0);
+}
+my $as = 'tell application "/Applications/Microsoft Excel.app"
+       set tmpfiles to name of every document
+       return tmpfiles
+end tell
+';
+
+my @data = ();
+my $res = `osascript -e '$as'`;
+chomp($res);
+if ($res eq "missing value"){
+       print "INFO! no files open!\n";
+       exit(0);
+}
+my @files = split(", ",$res);
+#print Dumper(@files);
+
+$as = 'tell application "/Applications/Microsoft Excel.app"
+       set tmppath to path of every document
+       return tmppath
+end tell
+';
+$res = `osascript -e '$as'`;
+chomp($res);
+my @paths = split(", ",$res);
+#print Dumper(@paths);
+for (my $f=0;$f<scalar(@files);$f++){
+       my ($project) = $files[$f] =~ m/^([A-Z0-9]+).+/;#substr(,0,index($files[$f],'-'));      
+       
+       my $prpath = substr($paths[$f],0,index($paths[$f],$project)).$project;
+       my $subpath = substr($paths[$f],length($prpath)+1);
+       $prpath =~ s/Library\/Containers\/com.microsoft.Excel\/Data\///;
+       
+       $as = 'tell application \"/Applications/Microsoft Excel.app\"
+       set mySheets to name of every sheet of workbook \"'.$files[$f].'\"
+end tell
+';
+       $res = "";
+       my $cmd = 'osascript -e "'.$as.'"';
+       $res = `$cmd`;
+       chomp($res);
+       my $d = $files[$f]."<=>".$res."<=>".$prpath."<=>".$project."<=>".$paths[$f]."<=>".$subpath;
+       push(@data,$d);
+}
+
+print join("\n",@data);
diff --git a/tools/used/pdfinfo.pl b/tools/used/pdfinfo.pl
new file mode 100644 (file)
index 0000000..c05c5a9
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env perl
+
+use strict;
+use Getopt::Long;
+use File::Basename;
+use lib (dirname($0).'/lib/perl5');
+use Image::ExifTool; 
+my $file = "";
+my $title = "";
+my $subject = "";
+GetOptions("file|f=s" => \$file,"title|t=s" => \$title,"subject|s=s" => \$subject);
+print "$file\n";
+if (! -e $file){
+ print "$file does not exist!\n";
+ exit(1);
+}
+
+#print Dumper($info);
+my $exi = new Image::ExifTool;
+if (($title ne "") or ($subject ne "")){
+ if ($title ne ""){
+   $exi->SetNewValue('Title' => $title);
+ }
+ if ($subject ne ""){
+   $exi->SetNewValue('Subject' => $subject);
+ }
+ my $success = $exi->WriteInfo($file);
+ if ($success == 1){
+  print "$file Info updated!\n";
+ }else {
+  print "Error writing new info to $file\n";
+ }
+}else {
+ my $info = Image::ExifTool::ImageInfo($file);
+ foreach my $k (keys(%{$info})){
+  print $k."=>".$info->{$k}."\n";
+ }
+}
+
diff --git a/tools/used/readEmailDataFromActiveSheet.pl b/tools/used/readEmailDataFromActiveSheet.pl
new file mode 100644 (file)
index 0000000..b3c0430
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+use strict;
+use Data::Dumper;
+
+my $aplscript = 'tell application "/Applications/Microsoft Excel.app"
+launch
+set nl to "\n"
+set bodytext to {}
+-- emails-addressen
+set tolist to value of range ("AA16:AA116")
+-- Betreff
+set subject to value of range ("BA3")
+--Body head
+set bodyhead to value of range ("BA5:BA7")
+--body foot
+set bodyfoot to value of range ("BA9")
+--body files
+set bodyfiles to value of range ("BA16:BA116")
+set rw to 2
+       set fx to 10
+       set px to 13
+       set cellp to "BA20"
+  set cellf to "CB" & fx
+       set celld to "CB" & px
+       set curx to value of cell cellp
+       set fileslist to {}
+       set pathlist to {}
+       set bodyfiles to {}
+       set end of fileslist to value of range("A11") & ".pdf"
+       set end of pathlist to value of range("A2")
+       set end of bodyfiles to value of range ("BA3")
+       repeat until curx is equal to ""
+               set end of fileslist to value of cell cellf
+               set end of pathlist to value of cell celld
+               set end of bodyfiles to curx
+               set fx to fx + 10
+               set px to px + 10
+               set rw to rw + 1
+               set cellp to "BA" & rw
+               set cellf to "CB" & fx
+               set celld to "CB" & px
+        set curx to value of cell cellp
+       end repeat
+       --set end of fileslist to name of active workbook
+       set xlsfilepath to path of active workbook
+       --set xlsdocfolder to value of range("A2")
+       --set xlsdocname to value of range("A11")
+       return {tolist, nl, subject, nl, bodyhead, nl, bodyfoot, nl, bodyfiles,nl, fileslist,nl, pathlist,nl, xlsfilepath}
+end tell'."\n";
+
+my $res = `osascript -ss -e \'$aplscript\'`;
+$res =~ s/{""},//g;
+$res =~ s/\ +/ /g;
+$res =~ s/"}, {"/<!>/g;
+
+#$res =~ s/[\<\!\>]+/<!>/g;
+  $res =~ s/", {{"//g;
+ $res =~ s/", "/<=>/g;
+  $res =~ s/"}}, "//g;
+  $res =~ s/{{{"//g;
+  $res =~ s/"}}}//g;
+  $res =~ s/", {"//g;
+  $res =~ s/"}, "//g;
+  $res =~ s/"}}//g;
+  $res =~ s/"}//g;
+  $res =~ s/^<=>//mg;
+  $res =~ s/<=>$//mg;
+  $res =~ s/{{ {"//g;
+ chomp($res);
+my @test = split(/\n/,$res);
+my $pp = $test[scalar(@test)-1];
+my @spl = split("/",$pp);
+my @nspl = ();
+for (my $i=1;$i<=6;$i++){
+    push (@nspl,$spl[$i]);
+}
+$test[scalar(@test)-1] = "/".join("/",@nspl)."/";  
+print join("\n",@test)."\n";
+
+# 
+#  foreach my $f (@test){
+#      my @dx = split("<=>",$f);
+#      print scalar(@dx)."\n";
+#      print Dumper(@dx);
+#  }
diff --git a/tools/used/sommaire_files_to_excel.pl b/tools/used/sommaire_files_to_excel.pl
new file mode 100644 (file)
index 0000000..59a7884
--- /dev/null
@@ -0,0 +1,148 @@
+#!/usr/bin/perl
+
+#use strict;
+use File::Find::Rule;
+use Getopt::Long;
+use File::Basename;
+use FindBin qw($RealBin);
+use lib ('/Users/kilian/Workspace/SolanaPMT/tools/lib/perl5');
+use lib ($RealBin.'/lib/perl5');
+use lib (dirname($0).'/lib/perl5');
+use Image::ExifTool;
+use utf8;
+use open ':std', ':encoding(UTF-8)';
+use Encode qw (encode decode);
+my $ppath = "";
+my @filter = ();
+my @excludes = ();
+
+GetOptions("projectpath|p=s" => \$ppath);
+if (! -d $ppath){
+       print "$ppath does not exist!\n";
+       exit(1);
+}
+my $fil = $filter[0];
+my $outdata = ();
+my @files = File::Find::Rule->file()->name( qr/\.(xlsx|xls|doc|docx|png|jpg|jpeg|dwg|pla|pln)$/ )->in( $ppath );
+#print "Total Files:".scalar(@files)."\n";
+my $cnt = 0;
+foreach my $f (@files){
+       my $fname = basename($f);
+       #print $cnt." - "$fname."\n";
+       if ( ($fname =~ /^\./ ) || ($fname =~ /^\~/ ) || ($f =~ /\.app\// ) ||
+       ($f =~ /\.app\//) || ($f =~ /\/\./)) { next;}
+       #print $fname."\n";
+       #my $bok = 0;
+       #foreach my $fil (@filter){
+               
+               #if (($fname =~ /$fil/) && ($bok == 0)){
+                       #print "$fname OK\n";
+                       my ($y,$m,$d);
+                       if ($fname =~ /\^/){
+                               ($y,$m,$d) = $fname =~ m/.*\^(\d{2,})(\d{2,})(\d{2,}).*/;
+                       } else {
+                               ($y,$m,$d) = $fname =~ m/.*\((\d{2,})(\d{2,})(\d{2,})\).*/;
+                       }
+                       #my $srtdate = $y.$m.$d;
+                       my $senddate = $d.'.'.$m.'.20'.$y;
+                       $outdata->{$cnt}->{senddate} = $senddate; 
+                       $outdata->{$cnt}->{filename} = $fname;
+                       my ($t,$s,$g,$m,$x) = &getfileinfo($f);
+                       $outdata->{$cnt}->{title} = $t;
+                       $outdata->{$cnt}->{description} = $s;
+                       $outdata->{$cnt}->{path} = $f;
+                       $outdata->{$cnt}->{size} = $g;
+                       $outdata->{$cnt}->{moddate} = $m;
+                       $outdata->{$cnt}->{ftype} = $x;
+                       #$bok = 1;
+               #}
+       #}
+       $cnt++;
+}
+
+
+#my $workbook = Excel::Writer::XLSX->new( $ENV{'HOME'}.'/Desktop/sommaire_all.xlsx' );
+my $row = 0;
+open(SOM,">".$ENV{'HOME'}.'/Desktop/'.basename($ppath).'_sommaire_all.csv');
+#my $worksheet = $workbook->add_worksheet();
+foreach my $o (sort {$a <=> $b} keys(%{$outdata})){
+       print SOM $outdata->{$o}->{senddate}.";";
+       print SOM decode("UTF-8",$outdata->{$o}->{filename}).";";
+       print SOM decode("UTF-8",$outdata->{$o}->{title}).";";
+       print SOM decode("UTF-8",$outdata->{$o}->{description}).";";
+       print SOM decode("UTF-8",$outdata->{$o}->{path}).";";
+       print SOM $outdata->{$o}->{size}.";";
+       print SOM $outdata->{$o}->{moddate}.";";
+       print SOM $outdata->{$o}->{ftype}."\n";
+       # #my $col = 0;
+       # $worksheet->write( $row, $col, $outdata->{$o}->{senddate} );
+       # #$col++;
+       # $worksheet->write( $row, $col, decode("UTF-8",$outdata->{$o}->{filename}) );
+       # #$col++;
+       # $worksheet->write( $row, $col, decode("UTF-8",$outdata->{$o}->{title}) );
+       # $col++;
+       # $worksheet->write( $row, $col, decode("UTF-8",$outdata->{$o}->{description}) );
+       # $col++;
+       # $worksheet->write( $row, $col, decode("UTF-8",$outdata->{$o}->{path}));
+       # $col++;
+       # $worksheet->write( $row, $col, $outdata->{$o}->{size} );
+       # $col++;
+       # $worksheet->write( $row, $col, $outdata->{$o}->{moddate} );
+       # $col++;
+       # $worksheet->write( $row, $col, $outdata->{$o}->{ftype} );
+       # $row++;
+}
+#$workbook->close();
+close(SOM);
+if (-e $ENV{'HOME'}.'/Desktop/'.basename($ppath).'_sommaire_all.csv'){
+       print "Datei: ".$ENV{'HOME'}.'/Desktop/'.basename($ppath).'_sommaire_all.csv wurde erstellt'."\n";
+} else {
+       print "Fehler beim erstellen der Datei!"."\n";
+}
+
+
+
+sub getfileinfo(){
+       my $file = shift;
+       my $title = "";
+       my $subject = "";
+       my $info = Image::ExifTool::ImageInfo($file);
+       foreach my $k (keys(%{$info})){
+               #print $k."=>".$info->{$k}."\n";
+               if ($k eq "Title"){
+                       $title = $info->{$k};
+               }
+               if ($k eq "Subject"){
+                       $subject = $info->{$k};
+               } 
+               
+       }
+       my @st = stat($file);
+       my $size = $st[7];
+       $size = $size/1024;
+       $size =~ s/\.\d+//g;
+       $size = $size." KB";
+       my $md = &udatetohr($st[9]);
+       my $ftype = &getfiletype($file);
+       return ($title,$subject,$size,$md,$ftype);
+}
+
+sub udatetohr(){
+       my $udate = shift;
+       my @d = localtime($udate);
+       $d[5] = $d[5] + 1900;
+       $d[4] = $d[4] + 1;
+       if ($d[4] < 10){$d[4] = '0'.$d[4];}
+       if ($d[3] < 10){$d[3] = '0'.$d[3];}
+       if ($d[2] < 10){$d[2] = '0'.$d[2];}
+       if ($d[1] < 10){$d[1] = '0'.$d[1];}
+  return ($d[3].'.'.$d[4].'.'.$d[5].' '.$d[2].':'.$d[1]);
+}
+
+sub getfiletype(){
+       my $ft = shift;
+       my $cmd = 'file -b "'.$ft.'"';
+       my $ftype = `$cmd`;
+       chomp($ftype);
+       return $ftype;
+}
diff --git a/tools/used/xlsexport_rapport_data.pl b/tools/used/xlsexport_rapport_data.pl
new file mode 100644 (file)
index 0000000..7b1f1d8
--- /dev/null
@@ -0,0 +1,109 @@
+#!/usr/bin/perl
+
+use strict;
+use Getopt::Long;
+use File::Basename;
+use utf8;
+use lib (dirname($0).'/lib/perl5');
+
+my $xlsfile = "";
+my $datafile = "";
+my $cellstart = "";
+my $sheet = "";
+GetOptions("file|n=s" => \$xlsfile,"data|t=s" => \$datafile, "sheet|s=s" => \$sheet);
+
+#my ($r,$c)=&xl_cell_to_rowcol(uc($cellstart));
+#my $apl = 'tell application \"/Applications/Microsoft Excel.app\"
+#              launch
+#              activate object workbook \"'.$xlsfile.'\" 
+#              activate object worksheet \"'.$sheet.'\"'."\n";
+my $apl = 'tell application "/Applications/Microsoft Excel.app"
+       set xval to "x"
+       set rowb to 14
+       set rowe to 19
+       set retlist to {}
+       set retlist2 to {}
+       repeat until xval is equal to ""
+               set xval to value of range ("CA" & rowb)
+               if xval is not equal to "" then
+                       set end of retlist to value of range ("CA" & rowb & ":CA" & rowe)
+               end if
+               set rowb to rowb + 10
+               set rowe to rowe + 10
+       end repeat
+       set end of retlist2 to value of range ("DA10:DA109")
+       
+       return {retlist,retlist2}'."\n";
+$apl .= 'end tell'."\n";
+my $res = `osascript -ss -e  '$apl'`;
+chomp($res);
+$res =~ s/}}, {{/}\n{/g;
+$res =~ s/^{{{{"//;
+$res =~ s/"}}}}$//;
+$res =~ s/"}, {"/<=>/g;
+$res =~ s/", "/<=>/g;
+$res =~ s/^{"//mg;
+$res =~ s/"}$//mg;
+$res =~ s/"}}$//mg;
+$res =~ s/^{{"//mg;
+
+print $res;
+
+#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;
+#}
+#
+#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;
+#}
\ No newline at end of file
diff --git a/tools/used/xlsimport_fiche_data.pl b/tools/used/xlsimport_fiche_data.pl
new file mode 100644 (file)
index 0000000..7847bf3
--- /dev/null
@@ -0,0 +1,134 @@
+#!/usr/bin/perl
+
+use strict;
+use Getopt::Long;
+use File::Basename;
+use utf8;
+use lib (dirname($0).'/lib/perl5');
+
+my $xlsfile = "";
+my $datafile = "";
+my $cellstart = "";
+my $sheet = "";
+GetOptions("file|n=s" => \$xlsfile,"data|t=s" => \$datafile, "sheet|s=s" => \$sheet);
+if (! -e $datafile){
+       exit(-1);
+}
+
+#my ($r,$c)=&xl_cell_to_rowcol(uc($cellstart));
+my $apl = 'tell application "/Applications/Microsoft Excel.app"
+               launch
+               activate object workbook "'.$xlsfile.'" 
+               activate object worksheet "'.$sheet.'"'."\n";
+my @data = ();
+open(DAIMP,$datafile);
+while (my $l = <DAIMP>){
+       chomp($l);
+       push(@data,$l); 
+} 
+close(DAIMP);
+#foreach (my $l = <DAT>){
+#      chomp($l);
+#      push(@data,$l); 
+#}     
+#close(DAT);
+
+my $c = 10;
+foreach my $l (@data){
+       my $xc = $c;
+       
+       my @rdata = split('<!>',$l);
+               
+               $apl .= 'set value of cell "CB'.$xc.'" to "'.$rdata[0].'"'."\n";
+               $xc++;
+               $apl .= 'set value of cell "CB'.$xc.'" to "'.$rdata[1].'"'."\n";
+               $xc++;
+               $apl .= 'set value of cell "CB'.$xc.'" to "'.$rdata[2].'"'."\n";
+               $xc++;
+               $apl .= 'set value of cell "CB'.$xc.'" to "'.$rdata[3].'"'."\n";
+               
+       $c = $c + 10;
+}
+if ($c < 500){
+       $apl .= 'set rw to '.$c."\n";
+       $apl .= 'set cellp to "CB" & rw'."\n";
+       $apl .= 'set curx to value of cell cellp'."\n";
+       $apl .= 'repeat until curx is equal to ""'."\n";
+       $apl .= '       set re to rw + 3'."\n";
+       $apl .= '       set cellp to "CB" & rw & ":CB" & re'."\n";
+       $apl .= '       set value of range cellp to ""'."\n";
+       $apl .= '       set rw to rw + 10'."\n";
+       $apl .= '       set cellp to "CB" & rw'."\n";
+       $apl .= '       set curx to value of cell cellp'."\n";
+       $apl .= 'end repeat'."\n";
+}
+
+
+$apl .= 'end tell'."\n";
+$apl =~ s/'/\'/g;
+
+open (APLT,">".$ENV{HOME}.'/test.scpt');
+print APLT $apl;
+close(APLT);
+my $cmd = 'osascript '.$ENV{HOME}.'/test.scpt';
+my $res = `$cmd`;
+unlink($ENV{HOME}.'/test.scpt') ;
+
+#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;
+#}
+#
+#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;
+#}
\ No newline at end of file