--- /dev/null
+Revision history for Perl module Excel::Template
+
+0.12 Thu Apr 08 07:30:00 2004
+ - Fixed bug regarding empty arrays as loop variables
+
+0.11 Wed Mar 17 16:00:00 2004
+ - Fixed bug introduced in 0.10 (Loops were not case-insensitive)
+
+0.10 Wed Mar 17 16:00:00 2004
+ - Parameters are now case-insensitive
+
+0.09 Mon Feb 02 16:00:00 2004
+ - Fixed bug with multiple worksheets
+
+0.08 Fri Jan 30 14:00:00 2004
+ - Added Base to the params for XML::Parser to allow for entity includes
+
+0.07 Fri Jan 23 08:30:00 2004
+ - Fixed the MANIFEST to account for missing files
+
+0.06 Mon Jan 20 11:00:00 2004
+ - Added formulas (no back-references yet)
+ - Improved POD a little
+
+0.05 Wed Jan 16 12:30:00 2004
+ - Fixed a bug in formats
+
+0.04 Wed Jan 16 12:00:00 2004
+ - Added BIG_FILES as an option, which will use
+ Spreadsheet::WriteExcel::Big as the renderer (yet unimplemented)
+ - Changed the output() method to use a tied IO::Scalar (which is
+ now a requirement.
+ - Firmed up the infrastructure in Excel::Template::Format
+ - Added the following tags
+ - FORMAT
+ - HIDDEN
+ - LOCKED
+ - OUTLINE
+ - SHADOW
+ - STRIKEOUT
+
+0.03 Wed Dec 03 20:30:00 2003
+ - Added XML::Parser as a required pre-requisite module
+ - Added Italic format
+ - Removed $VERSION from Excel::Template::Base (Unneeded)
+ - No documentation or testing changes
+
+0.02 Sun Nov 30 17:00:00 2003
+ - documentation improvements
+ - No actual functional changes
+
+0.01 Tue Nov 18 14:23:42 2003
+ - original version; created by ExtUtils::ModuleMaker 0.32
+
+
--- /dev/null
+Terms of Perl itself
+
+a) the GNU General Public License as published by the Free
+ Software Foundation; either version 1, or (at your option) any
+ later version, or
+b) the "Artistic License"
+
+---------------------------------------------------------------------------
+
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"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
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. 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 PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(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 PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+
+---------------------------------------------------------------------------
+
+The Artistic License
+
+Preamble
+
+The intent of this document is to state the conditions under which a Package
+may be copied, such that the Copyright Holder maintains some semblance of
+artistic control over the development of the package, while giving the users of the
+package the right to use and distribute the Package in a more-or-less customary
+fashion, plus the right to make reasonable modifications.
+
+Definitions:
+
+- "Package" refers to the collection of files distributed by the Copyright
+ Holder, and derivatives of that collection of files created through textual
+ modification.
+- "Standard Version" refers to such a Package if it has not been modified,
+ or has been modified in accordance with the wishes of the Copyright
+ Holder.
+- "Copyright Holder" is whoever is named in the copyright or copyrights for
+ the package.
+- "You" is you, if you're thinking about copying or distributing this Package.
+- "Reasonable copying fee" is whatever you can justify on the basis of
+ media cost, duplication charges, time of people involved, and so on. (You
+ will not be required to justify it to the Copyright Holder, but only to the
+ computing community at large as a market that must bear the fee.)
+- "Freely Available" means that no fee is charged for the item itself, though
+ there may be fees involved in handling the item. It also means that
+ recipients of the item may redistribute it under the same conditions they
+ received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you duplicate
+all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications derived from
+the Public Domain or from the Copyright Holder. A Package modified in such a
+way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and when
+you changed that file, and provided that you do at least ONE of the following:
+
+ a) place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said modifications
+ to Usenet or an equivalent medium, or placing the modifications on
+ a major archive site such as ftp.uu.net, or by allowing the
+ Copyright Holder to include your modifications in the Standard
+ Version of the Package.
+
+ b) use the modified Package only within your corporation or
+ organization.
+
+ c) rename any non-standard executables so the names do not
+ conflict with standard executables, which must also be provided,
+ and provide a separate manual page for each non-standard
+ executable that clearly documents how it differs from the Standard
+ Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or executable
+form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library
+ files, together with instructions (in the manual page or equivalent)
+ on where to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of
+ the Package with your modifications.
+
+ c) accompany any non-standard executables with their
+ corresponding Standard Version executables, giving the
+ non-standard executables non-standard names, and clearly
+ documenting the differences in manual pages (or equivalent),
+ together with instructions on where to get the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this Package.
+You may charge any fee you choose for support of this Package. You may not
+charge a fee for this Package itself. However, you may distribute this Package in
+aggregate with other (possibly commercial) programs as part of a larger
+(possibly commercial) software distribution provided that you do not advertise
+this Package as a product of your own.
+
+6. The scripts and library files supplied as input to or produced as output from
+the programs of this Package do not automatically fall under the copyright of this
+Package, but belong to whomever generated them, and may be sold
+commercially, and may be aggregated with this Package.
+
+7. C or perl subroutines supplied by you and linked into this Package shall not
+be considered part of this Package.
+
+8. Aggregation of this Package with a commercial distribution is always permitted
+provided that the use of this Package is embedded; that is, when no overt attempt
+is made to make this Package's interfaces visible to the end user of the
+commercial distribution. Such use shall not be construed as a distribution of
+this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The End
+
+
--- /dev/null
+MANIFEST
+LICENSE
+README
+Todo
+Changes
+lib/Excel/Template.pm
+lib/Excel/Template/Base.pm
+lib/Excel/Template/Container.pm
+lib/Excel/Template/Context.pm
+lib/Excel/Template/Element.pm
+lib/Excel/Template/Factory.pm
+lib/Excel/Template/Format.pm
+lib/Excel/Template/Iterator.pm
+lib/Excel/Template/TextObject.pm
+lib/Excel/Template/Container/Bold.pm
+lib/Excel/Template/Container/Conditional.pm
+lib/Excel/Template/Container/Format.pm
+lib/Excel/Template/Container/Hidden.pm
+lib/Excel/Template/Container/Italic.pm
+lib/Excel/Template/Container/Loop.pm
+lib/Excel/Template/Container/Locked.pm
+lib/Excel/Template/Container/Outline.pm
+lib/Excel/Template/Container/Row.pm
+lib/Excel/Template/Container/Scope.pm
+lib/Excel/Template/Container/Strikeout.pm
+lib/Excel/Template/Container/Shadow.pm
+lib/Excel/Template/Container/Workbook.pm
+lib/Excel/Template/Container/Worksheet.pm
+lib/Excel/Template/Element/Cell.pm
+lib/Excel/Template/Element/Formula.pm
+lib/Excel/Template/Element/Var.pm
+t/001_load.t
+Makefile.PL
--- /dev/null
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+ NAME => 'Excel::Template',
+ VERSION_FROM => 'lib/Excel/Template.pm', # finds $VERSION
+ AUTHOR => 'Rob Kinyon (rkinyon@columbus.rr.com',
+ ABSTRACT => 'Excel::Template',
+ PREREQ_PM => {
+ 'Test::Simple' => 0.44,
+ 'Spreadsheet::WriteExcel' => 0.42,
+ 'XML::Parser' => 0.01,
+ 'IO::Scalar' => 0.01,
+ 'IO::File' => 0.01,
+ },
+);
--- /dev/null
+Excel::Template is a layout system to use the data structure from HTML::Template
+and create a Microsoft Excel file. A few caveats:
+1) All limitations stated in Spreadsheet::WriteExcel are in force, as that is
+ the module used for rendering.
+2) The output() method is not extremely well-tested.
+3) Not all the methods have been tested in all situations. I fully expect bugs
+ to come back and have to be fixed.
+
+Barring all of that, and a slight lack of features, it's a good module. Enjoy!
+
+Rob
--- /dev/null
+TODO list for Perl module Excel::Template
+
+- Add more formatting options, such as italic
+- Add fonts
+- Add numeric/string formats
+- Figure out how to add support for formulas, especially in <LOOP>s
+- Figure out if and how to add support for charts
+- Anything else people suggest
+
--- /dev/null
+package Excel::Template;
+
+use strict;
+
+BEGIN {
+ use Excel::Template::Base;
+ use vars qw ($VERSION @ISA);
+
+ $VERSION = '0.12';
+ @ISA = qw( Excel::Template::Base );
+}
+
+use File::Basename;
+use XML::Parser;
+use IO::File;
+use IO::Scalar;
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->parse_xml($self->{FILENAME})
+ if defined $self->{FILENAME};
+
+ my @renderer_classes = ( 'Spreadsheet::WriteExcel' );
+ if (exists $self->{BIG_FILE} && $self->{BIG_FILE})
+ {
+ unshift @renderer_classes, 'Spreadsheet::WriteExcel::Big';
+ }
+
+ $self->{RENDERER} = undef;
+ foreach my $class (@renderer_classes)
+ {
+ (my $filename = $class) =~ s!::!/!g;
+ eval {
+ require "$filename.pm";
+ $class->import;
+ };
+ if ($@) {
+ warn "Could not find or compile '$class'\n";
+ } else {
+ $self->{RENDERER} = $class;
+ last;
+ }
+ }
+
+ defined $self->{RENDERER} ||
+ die "Could not find a renderer class. Tried:\n\t" .
+ join("\n\t", @renderer_classes) .
+ "\n";
+
+ return $self;
+}
+
+sub param
+{
+ my $self = shift;
+
+ # Allow an arbitrary number of hashrefs, so long as they're the first things # into param(). Put each one onto the end, de-referenced.
+ push @_, %{shift @_} while UNIVERSAL::isa($_[0], 'HASH');
+
+ (@_ % 2)
+ && die __PACKAGE__, "->param() : Odd number of parameters to param()\n";
+
+ my %params = @_;
+ $params{uc $_} = delete $params{$_} for keys %params;
+ @{$self->{PARAM_MAP}}{keys %params} = @params{keys %params};
+
+ return 1;
+}
+
+sub write_file
+{
+ my $self = shift;
+ my ($filename) = @_;
+
+ my $xls = $self->{RENDERER}->new($filename)
+ || die "Cannot create XLS in '$filename': $!\n";
+
+ $self->_prepare_output($xls);
+
+ $xls->close;
+
+ return 1;
+}
+
+sub output
+{
+ my $self = shift;
+
+ my $output;
+ tie *XLS, 'IO::Scalar', \$output;
+
+ $self->write_file(\*XLS);
+
+ return $output;
+}
+
+sub parse
+{
+ my $self = shift;
+
+ $self->parse_xml(@_);
+}
+
+sub parse_xml
+{
+ my $self = shift;
+ my ($fname) = @_;
+
+ my ($filename, $dirname) = fileparse($fname);
+
+ my @stack;
+ my $parser = XML::Parser->new(
+ Base => $dirname,
+ Handlers => {
+ Start => sub {
+ shift;
+
+ my $name = uc shift;
+
+ my $node = Excel::Template::Factory->create_node($name, @_);
+ die "'$name' (@_) didn't make a node!\n" unless defined $node;
+
+ if ($name eq 'WORKBOOK')
+ {
+ push @{$self->{WORKBOOKS}}, $node;
+ }
+ elsif ($name eq 'VAR')
+ {
+ return unless @stack;
+
+ if (exists $stack[-1]{TXTOBJ} &&
+ $stack[-1]{TXTOBJ}->isa('TEXTOBJECT'))
+ {
+ push @{$stack[-1]{TXTOBJ}{STACK}}, $node;
+ }
+
+ }
+ else
+ {
+ push @{$stack[-1]{ELEMENTS}}, $node
+ if @stack;
+ }
+ push @stack, $node;
+ },
+ Char => sub {
+ shift;
+ return unless @stack;
+
+ my $parent = $stack[-1];
+
+ if (
+ exists $parent->{TXTOBJ}
+ &&
+ $parent->{TXTOBJ}->isa('TEXTOBJECT')
+ ) {
+ push @{$parent->{TXTOBJ}{STACK}}, @_;
+ }
+ },
+ End => sub {
+ shift;
+ return unless @stack;
+
+ pop @stack if $stack[-1]->isa(uc $_[0]);
+ },
+ },
+ );
+
+ {
+ my $fh = IO::File->new($fname)
+ || die "Cannot open '$fname' for reading: $!\n";
+
+ $parser->parse(do { local $/ = undef; <$fh> });
+
+ $fh->close;
+ }
+
+ return 1;
+}
+
+sub _prepare_output
+{
+ my $self = shift;
+ my ($xls) = @_;
+
+ my $context = Excel::Template::Factory->create(
+ 'CONTEXT',
+
+ XLS => $xls,
+ PARAM_MAP => [ $self->{PARAM_MAP} ],
+ );
+
+ $_->render($context) for @{$self->{WORKBOOKS}};
+
+ return 1;
+}
+
+sub register { shift; Excel::Template::Factory::register(@_) }
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template - Excel::Template
+
+=head1 SYNOPSIS
+
+First, make a template. This is an XML file, describing the layout of the
+spreadsheet.
+
+For example, test.xml:
+
+ <workbook>
+ <worksheet name="tester">
+ <cell text="$HOME"/>
+ <cell text="$PATH"/>
+ </worksheet>
+ </workbook>
+
+Now, create a small program to use it:
+
+ #!/usr/bin/perl -w
+ use Excel::Template
+
+ # Create the Excel template
+ my $template = Excel::Template->new(
+ filename => 'test.xml',
+ );
+
+ # Add a few parameters
+ $template->param(
+ HOME => $ENV{HOME},
+ PATH => $ENV{PATH},
+ );
+
+ $template->write_file('test.xls');
+
+If everything worked, then you should have a spreadsheet in your work directory
+that looks something like:
+
+ A B C
+ +----------------+----------------+----------------
+ 1 | /home/me | /bin:/usr/bin |
+ +----------------+----------------+----------------
+ 2 | | |
+ +----------------+----------------+----------------
+ 3 | | |
+
+=head1 DESCRIPTION
+
+This is a module used for templating Excel files. Its genesis came from the
+need to use the same datastructure as HTML::Template, but provide Excel files
+instead. The existing modules don't do the trick, as they require replication
+of logic that's already been done within HTML::Template.
+
+Currently, only a small subset of the planned features are supported. This is
+meant to be a test of the waters, to see what features people actually want.
+
+=head1 MOTIVATION
+
+I do a lot of Perl/CGI for reporting purposes. In nearly every place I've been,
+I've been asked for HTML, PDF, and Excel. HTML::Template provides the first, and
+PDF::Template does the second pretty well. But, generating Excel was the
+sticking point. I already had the data structure for the other templating
+modules, but I just didn't have an easy mechanism to get that data structure
+into an XLS file.
+
+=head1 USAGE
+
+=head2 new()
+
+This creates a Excel::Template object. If passed a filename parameter, it will
+parse the template in the given file. (You can also use the parse() method,
+described below.)
+
+=head2 param()
+
+This method is exactly like HTML::Template's param() method. Although I will
+be adding more to this section later, please see HTML::Template's description
+for info right now.
+
+=head2 parse() / parse_xml()
+
+This method actually parses the template file. It can either be called
+separately or through the new() call. It will die() if it runs into a situation
+it cannot handle.
+
+=head2 write_file()
+
+Create the Excel file and write it to the specified filename, if possible. (This
+is when the actual merging of the template and the parameters occurs.)
+
+=head2 output()
+
+It will act just like HTML::Template's output() method, returning the resultant
+file as a stream, usually for output to the web. (This is when the actual
+merging of the template and the parameters occurs.)
+
+=head1 SUPPORTED NODES
+
+This is just a list of nodes. See the other classes in this distro for more
+details on specific parameters and the like.
+
+Every node can set the ROW and COL parameters. These are the actual ROW/COL
+values that the next CELL tag will write into.
+
+=over 4
+
+=item * WORKBOOK
+
+=item * WORKSHEET
+
+=item * IF
+
+=item * LOOP
+
+=item * ROW
+
+=item * CELL
+
+=item * FORMULA
+
+=item * BOLD
+
+=item * ITALIC
+
+=back 4
+
+=head1 BUGS
+
+None, that I know of.
+
+=head1 SUPPORT
+
+This is currently beta-quality software. The featureset is extremely limited,
+but I expect to be adding on to it very soon.
+
+=head1 AUTHOR
+
+ Rob Kinyon
+ rkinyon@columbus.rr.com
+
+=head1 COPYRIGHT
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=head1 SEE ALSO
+
+perl(1), HTML::Template, Spreadsheet::WriteExcel.
+
+=cut
--- /dev/null
+package Excel::Template::Base;
+
+use strict;
+
+BEGIN {
+}
+
+use Excel::Template::Factory;
+
+sub new
+{
+ my $class = shift;
+
+ push @_, %{shift @_} while UNIVERSAL::isa($_[0], 'HASH');
+ (@_ % 2)
+ and die "$class->new() called with odd number of option parameters\n";
+
+ my %x = @_;
+
+ # Do not use a hashref-slice here because of the uppercase'ing
+ my $self = {};
+ $self->{uc $_} = $x{$_} for keys %x;
+
+ bless $self, $class;
+}
+
+sub isa { Excel::Template::Factory::isa(@_) }
+
+sub calculate { ($_[1])->get(@_[0,2]) }
+#{
+# my $self = shift;
+# my ($context, $attr) = @_;
+#
+# return $context->get($self, $attr);
+#}
+
+sub enter_scope { ($_[1])->enter_scope($_[0]) }
+#{
+# my $self = shift;
+# my ($context) = @_;
+#
+# return $context->enter_scope($self);
+#}
+
+sub exit_scope { ($_[1])->exit_scope(@_[0, 2]) }
+#{
+# my $self = shift;
+# my ($context, $no_delta) = @_;
+#
+# return $context->exit_scope($self, $no_delta);
+#}
+
+sub deltas
+{
+# my $self = shift;
+# my ($context) = @_;
+
+ return {};
+}
+
+sub resolve
+{
+# my $self = shift;
+# my ($context) = @_;
+
+ '';
+}
+
+sub render
+{
+# my $self = shift;
+# my ($context) = @_;
+
+ 1;
+}
+
+1;
+__END__
--- /dev/null
+package Excel::Template::Container;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Base);
+
+ use Excel::Template::Base;
+}
+
+# Containers are objects that can contain arbitrary elements, such as
+# PageDefs or Loops.
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{ELEMENTS} = [] unless UNIVERSAL::isa($self->{ELEMENTS}, 'ARRAY');
+
+ return $self;
+}
+
+sub _do_page
+{
+ my $self = shift;
+ my ($method, $context) = @_;
+
+ for my $e (@{$self->{ELEMENTS}})
+ {
+ $e->enter_scope($context);
+ $e->$method($context);
+ $e->exit_scope($context, 1);
+ }
+
+ return 1;
+}
+
+sub begin_page { _do_page 'begin_page', @_ }
+sub end_page { _do_page 'end_page', @_ }
+
+sub iterate_over_children
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $continue = 1;
+
+ for my $e (
+ @{$self->{ELEMENTS}})
+ {
+ $e->enter_scope($context);
+
+ my $rc = $e->render($context);
+ $continue = $rc if $continue;
+
+ $e->exit_scope($context);
+ }
+
+ return $continue;
+}
+
+sub render { $_[0]->iterate_over_children($_[1]) }
+#{
+# my $self = shift;
+# my ($context) = @_;
+#
+# return $self->iterate_over_children($context);
+#}
+
+sub max_of
+{
+ my $self = shift;
+ my ($context, $attr) = @_;
+
+ my $max = $context->get($self, $attr);
+
+ ELEMENT:
+ foreach my $e (@{$self->{ELEMENTS}})
+ {
+ $e->enter_scope($context);
+
+ my $v = $e->isa('CONTAINER')
+ ? $e->max_of($context, $attr)
+ : $e->calculate($context, $attr);
+
+ $max = $v if $max < $v;
+
+ $e->exit_scope($context, 1);
+ }
+
+ return $max;
+}
+
+sub total_of
+{
+ my $self = shift;
+ my ($context, $attr) = @_;
+
+ my $total = 0;
+
+ ELEMENT:
+ foreach my $e (@{$self->{ELEMENTS}})
+ {
+ $e->enter_scope($context);
+
+ $total += $e->isa('CONTAINER')
+ ? $e->total_of($context, $attr)
+ : $e->calculate($context, $attr);
+
+ $e->exit_scope($context, 1);
+ }
+
+ return $total;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Container::Bold;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{BOLD} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Bold - Excel::Template::Container::Bold
+
+=head1 PURPOSE
+
+To format all children in bold
+
+=head1 NODE NAME
+
+BOLD
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <bold>
+ ... Children here
+ </bold>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a bold format. All other formatting will remain the same and the
+"bold"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Conditional;
+
+#GGG Convert <conditional> to be a special case of <switch>?
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+my %isOp = (
+ '=' => '==',
+ (map { $_ => $_ } ( '>', '<', '==', '!=', '>=', '<=' )),
+ (map { $_ => $_ } ( 'gt', 'lt', 'eq', 'ne', 'ge', 'le' )),
+);
+
+sub conditional_passes
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $name = $context->get($self, 'NAME');
+ return 0 unless $name =~ /\S/;
+
+ my $val = $context->param($name);
+ $val = @{$val} while UNIVERSAL::isa($val, 'ARRAY');
+ $val = ${$val} while UNIVERSAL::isa($val, 'SCALAR');
+
+ my $value = $context->get($self, 'VALUE');
+ if (defined $value)
+ {
+ my $op = $context->get($self, 'OP');
+ $op = defined $op && exists $isOp{$op}
+ ? $isOp{$op}
+ : '==';
+
+ # Force numerical context on both values;
+ $value *= 1;
+ $val *= 1;
+
+ my $res;
+ for ($op)
+ {
+ /^>$/ && do { $res = ($val > $value); last };
+ /^<$/ && do { $res = ($val < $value); last };
+ /^==$/ && do { $res = ($val == $value); last };
+ /^!=$/ && do { $res = ($val != $value); last };
+ /^>=$/ && do { $res = ($val >= $value); last };
+ /^<=$/ && do { $res = ($val <= $value); last };
+ /^gt$/ && do { $res = ($val gt $value); last };
+ /^lt$/ && do { $res = ($val lt $value); last };
+ /^eq$/ && do { $res = ($val eq $value); last };
+ /^ne$/ && do { $res = ($val ne $value); last };
+ /^ge$/ && do { $res = ($val ge $value); last };
+ /^le$/ && do { $res = ($val le $value); last };
+
+ die "Unknown operator in conditional resolve", $/;
+ }
+
+ return 0 unless $res;
+ }
+ elsif (my $is = uc $context->get($self, 'IS'))
+ {
+ my $istrue = $val && 1;
+ if ($is eq 'TRUE')
+ {
+ return 0 unless $istrue;
+ }
+ else
+ {
+ warn "Conditional 'is' value was [$is], defaulting to 'FALSE'" . $/
+ if $is ne 'FALSE';
+
+ return 0 if $istrue;
+ }
+ }
+
+ return 1;
+}
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ return 1 unless $self->conditional_passes($context);
+
+ return $self->iterate_over_children($context);
+}
+
+sub max_of
+{
+ my $self = shift;
+ my ($context, $attr) = @_;
+
+ return 0 unless $self->conditional_passes($context);
+
+ return $self->SUPER::max_of($context, $attr);
+}
+
+sub total_of
+{
+ my $self = shift;
+ my ($context, $attr) = @_;
+
+ return 0 unless $self->conditional_passes($context);
+
+ return $self->SUPER::total_of($context, $attr);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Conditional - Excel::Template::Container::Conditional
+
+=head1 PURPOSE
+
+To provide conditional execution of children nodes
+
+=head1 NODE NAME
+
+IF
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the parameter to be testing. It is resolved like any other
+parameter.
+
+=item * VALUE
+
+If VALUE is set, then a comparison operation is done. The value of NAME is
+compared to VALUE using the value of OP.
+
+=item * OP
+
+If VALUE is set, then this is checked. If it isn't present, then '==' (numeric
+equality) is assumed. OP must be one of the numeric comparison operators or the
+string comparison operators. All 6 of each kind is supported.
+
+=item * IS
+
+If VALUE is not set, then IS is checked. IS is allowed to be either "TRUE" or
+"FALSE". The boolean value of NAME is checked against IS.
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <if name="__ODD__" is="false">
+ ... Children here
+ </if>
+
+In the above example, the children will be executed if the value of __ODD__
+(which is set by the LOOP node) is false. So, for all even iterations.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+LOOP
+
+=cut
--- /dev/null
+package Excel::Template::Container::Format;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container );
+
+ use Excel::Template::Container;
+}
+
+use Excel::Template::Format;
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $old_format = $context->active_format;
+ my $format = Excel::Template::Format->copy(
+ $context, $old_format,
+
+ %{$self},
+ );
+ $context->active_format($format);
+
+ my $child_success = $self->iterate_over_children($context);
+
+ $context->active_format($old_format);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Format - Excel::Template::Container::Format
+
+=head1 PURPOSE
+
+To format all children according to the parameters
+
+=head1 NODE NAME
+
+FORMAT
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * bold
+
+This will set bold to on or off, depending on the boolean value.
+
+=item * hidden
+
+This will set whether the cell is hidden to on or off, depending on the boolean
+value. (q.v. BOLD tag)
+
+=item * italic
+
+This will set italic to on or off, depending on the boolean value. (q.v. ITALIC
+tag)
+
+=item * font_outline
+
+This will set font_outline to on or off, depending on the boolean value. (q.v.
+OUTLINE tag)
+
+=item * font_shadow
+
+This will set font_shadow to on or off, depending on the boolean value. (q.v.
+SHADOW tag)
+
+=item * font_strikeout
+
+This will set font_strikeout to on or off, depending on the boolean value. (q.v.
+STRIKEOUT tag)
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <format bold="1">
+ ... Children here
+ </format>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a bold format. All other formatting will remain the same and the
+"bold"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+BOLD, HIDDEN, ITALIC, OUTLINE, SHADOW, STRIKEOUT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Hidden;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{HIDDEN} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Hidden - Excel::Template::Container::Hidden
+
+=head1 PURPOSE
+
+To format all children in hidden
+
+=head1 NODE NAME
+
+HIDDEN
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <hidden>
+ ... Children here
+ </hidden>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a hidden format. All other formatting will remain the same and the
+"hidden"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Italic;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{ITALIC} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Italic - Excel::Template::Container::Italic
+
+=head1 PURPOSE
+
+To format all children in italic
+
+=head1 NODE NAME
+
+ITALIC
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <italic>
+ ... Children here
+ </italic>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a italic format. All other formatting will remain the same and the
+"italic"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Locked;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{LOCKED} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Locked - Excel::Template::Container::Locked
+
+=head1 PURPOSE
+
+To format all children in locked
+
+=head1 NODE NAME
+
+LOCKED
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <locked>
+ ... Children here
+ </locked>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a locked format. All other formatting will remain the same and the
+"locked"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Loop;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ if (exists $self->{MAXITERS} && $self->{MAXITERS} < 1)
+ {
+ die "<loop> MAXITERS must be greater than or equal to 1", $/;
+ }
+ else
+ {
+ $self->{MAXITERS} = 0;
+ }
+
+ return $self;
+}
+
+sub make_iterator
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ return Excel::Template::Factory->create('ITERATOR',
+ NAME => $context->get($self, 'NAME'),
+ MAXITERS => $context->get($self, 'MAXITERS'),
+ CONTEXT => $context,
+ );
+}
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ unless ($self->{ITERATOR} && $self->{ITERATOR}->more_params)
+ {
+ $self->{ITERATOR} = $self->make_iterator($context);
+ }
+ my $iterator = $self->{ITERATOR};
+
+ $iterator->enter_scope;
+
+ while ($iterator->can_continue)
+ {
+ $iterator->next;
+
+ unless ($self->iterate_over_children($context))
+ {
+ $iterator->back_up;
+ last;
+ }
+ }
+
+ $iterator->exit_scope;
+
+ return 0 if $iterator->more_params;
+
+ return 1;
+}
+
+sub total_of
+{
+ my $self = shift;
+ my ($context, $attr) = @_;
+
+ my $iterator = $self->make_iterator($context);
+
+ my $total = 0;
+
+ $iterator->enter_scope;
+ while ($iterator->can_continue)
+ {
+ $iterator->next;
+ $total += $self->SUPER::total_of($context, $attr);
+ }
+ $iterator->exit_scope;
+
+ return $total;
+}
+
+sub max_of
+{
+ my $self = shift;
+ my ($context, $attr) = @_;
+
+ my $iterator = $self->make_iterator($context);
+
+ my $max = $context->get($self, $attr);
+
+ $iterator->enter_scope;
+ while ($iterator->can_continue)
+ {
+ $iterator->next;
+ my $v = $self->SUPER::max_of($context, $attr);
+
+ $max = $v if $max < $v;
+ }
+ $iterator->exit_scope;
+
+ return $max;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Loop
+
+=head1 PURPOSE
+
+To provide looping
+
+=head1 NODE NAME
+
+LOOP
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the loop. It's used to identify within the parameter set
+what variables to expose to the children nodes each iteration.
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <loop name="LOOPY">
+ ... Children here ...
+ </loop>
+
+In the above example, the children nodes would have access to the LOOPY array
+of hashes as parameters. Each iteration through the array would expose a
+different hash of parameters to the children.
+
+These loops work just like HTML::Template's loops. (I promise I'll give more
+info here!)
+
+There is one difference - I prefer using Perl-like scoping, so accessing of
+variables outside the LOOP scope from within is perfectly acceptable. You can
+also hide outside variables with inner values, if you desire, just like Perl.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Container::Outline;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{FONT_OUTLINE} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Outline - Excel::Template::Container::Outline
+
+=head1 PURPOSE
+
+To format all children in outline
+
+=head1 NODE NAME
+
+OUTLINE
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <outline>
+ ... Children here
+ </outline>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a outline format. All other formatting will remain the same and the
+"outline"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Row;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ $context->{COL} = 0;
+
+ return $self->SUPER::render($context);
+}
+
+sub deltas
+{
+ return {
+ ROW => +1,
+ };
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Row - Excel::Template::Container::Row
+
+=head1 PURPOSE
+
+To provide a row context for CELL tags
+
+=head1 NODE NAME
+
+ROW
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+Each ROW tag will consume one row of the workbook. When the ROW tag starts, it
+will set the COL value to 0.
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <row>
+ ... Children here
+ </row>
+
+Generally, you will have CELL and/or FORMULA tags within a ROW.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+CELL, FORMULA
+
+=cut
--- /dev/null
+package Excel::Template::Container::Scope;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+# This is used as a placeholder for scoping values across any number
+# of children. It does nothing on its own.
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Scope
+
+=head1 PURPOSE
+
+To provide scoping of parameters for children
+
+=head1 NODE NAME
+
+SCOPE
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <scope param1="value1" param2="value2">
+ ... Children here ...
+ </scope>
+
+In the above example, the children would all have access to the parameters
+param1 and param2. This is useful if you have a section of your template that
+all has the same set of parameter values, but don't have a common parent.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Container::Shadow;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{FONT_SHADOW} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Shadow - Excel::Template::Container::Shadow
+
+=head1 PURPOSE
+
+To format all children in shadow
+
+=head1 NODE NAME
+
+SHADOW
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <shadow>
+ ... Children here
+ </shadow>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a shadow format. All other formatting will remain the same and the
+"shadow"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Strikeout;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{FONT_STRIKEOUT} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Strikeout - Excel::Template::Container::Strikeout
+
+=head1 PURPOSE
+
+To format all children in bold
+
+=head1 NODE NAME
+
+STRIKEOUT
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <bold>
+ ... Children here
+ </bold>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a bold format. All other formatting will remain the same and the
+"bold"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Workbook;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container );
+
+ use Excel::Template::Container;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Workbook - Excel::Template::Container::Workbook
+
+=head1 PURPOSE
+
+The root node
+
+=head1 NODE NAME
+
+WORKBOOK
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+Currently, none. There will be attributes added here, regarding how the
+workbook as a whole will behave.
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <workbook>
+ ... Children here
+ </workbook>
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+Nothing
+
+=cut
--- /dev/null
+package Excel::Template::Container::Worksheet;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ $context->new_worksheet($self->{NAME});
+
+ return $self->SUPER::render($context);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Worksheet - Excel::Template::Container::Worksheet
+
+=head1 PURPOSE
+
+To provide a new worksheet.
+
+=head1 NODE NAME
+
+WORKSHEET
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the worksheet to be added.
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <worksheet name="My Taxes">
+ ... Children here
+ </worksheet>
+
+In the above example, the children will be executed in the context of the
+"My Taxes" worksheet.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+ROW, CELL, FORMULA
+
+=cut
--- /dev/null
+package Excel::Template::Context;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Base);
+
+ use Excel::Template::Base;
+}
+
+use Excel::Template::Format;
+
+# This is a helper object. It is not instantiated by the user, nor does it
+# represent an XML object. Rather, every container will use this object to
+# maintain the context for its children.
+
+my %isAbsolute = map { $_ => 1 } qw(
+ ROW
+ COL
+);
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{ACTIVE_WORKSHEET} = undef;
+ $self->{ACTIVE_FORMAT} = Excel::Template::Format->blank_format($self);
+
+ UNIVERSAL::isa($self->{$_}, 'ARRAY') || ($self->{$_} = [])
+ for qw( STACK PARAM_MAP NAME_MAP );
+
+ $self->{$_} = 0 for keys %isAbsolute;
+
+ return $self;
+}
+
+sub _find_param_in_map
+{
+ my $self = shift;
+ my ($map, $param, $depth) = @_;
+ $param = uc $param;
+ $depth ||= 0;
+
+ my $val = undef;
+ my $found = 0;
+
+ for my $map (reverse @{$self->{$map}})
+ {
+ next unless exists $map->{$param};
+ $depth--, next if $depth;
+
+ $found = 1;
+ $val = $map->{$param};
+ last;
+ }
+
+ die "Parameter '$param' not found\n"
+ if !$found && $self->{DIE_ON_NO_PARAM};
+
+ return $val;
+}
+
+sub param
+{
+ my $self = shift;
+ $self->_find_param_in_map(
+ 'PARAM_MAP',
+ @_,
+ );
+}
+
+sub named_param
+{
+ my $self = shift;
+ $self->_find_param_in_map(
+ 'NAME_MAP',
+ @_,
+ );
+}
+
+sub resolve
+{
+ my $self = shift;
+ my ($obj, $key, $depth) = @_;
+ $key = uc $key;
+ $depth ||= 0;
+
+ my $obj_val = $obj->{$key};
+
+ $obj_val = $self->param($1)
+ if $obj_val =~ /^\$(\S+)$/o;
+
+#GGG Remove this once NAME_MAP is working
+# $obj_val = $self->named_param($1)
+# if $obj_val =~ /^\\(\S+)$/o;
+
+#GGG Does this adequately test values to make sure they're legal??
+ # A value is defined as:
+ # 1) An optional operator (+, -, *, or /)
+ # 2) A decimal number
+
+#GGG Convert this to use //x
+ my ($op, $val) = $obj_val =~ m!^\s*([\+\*\/\-])?\s*([\d.]*\d)\s*$!oi;
+
+ # Unless it's a relative value, we have what we came for.
+ return $obj_val unless $op;
+
+ my $prev_val = $isAbsolute{$key}
+ ? $self->{$key}
+ : $self->get($obj, $key, $depth + 1);
+
+ return $obj_val unless defined $prev_val;
+ return $prev_val unless defined $obj_val;
+
+ # Prevent divide-by-zero issues.
+ return $val if $op eq '/' and $val == 0;
+
+ my $new_val;
+ for ($op)
+ {
+ /^\+$/ && do { $new_val = ($prev_val + $val); last; };
+ /^\-$/ && do { $new_val = ($prev_val - $val); last; };
+ /^\*$/ && do { $new_val = ($prev_val * $val); last; };
+ /^\/$/ && do { $new_val = ($prev_val / $val); last; };
+
+ die "Unknown operator '$op' in arithmetic resolve\n";
+ }
+
+ return $new_val if defined $new_val;
+ return;
+}
+
+sub enter_scope
+{
+ my $self = shift;
+ my ($obj) = @_;
+
+ push @{$self->{STACK}}, $obj;
+
+ for my $key (keys %isAbsolute)
+ {
+ next unless exists $obj->{$key};
+ $self->{$key} = $self->resolve($obj, $key);
+ }
+
+ return 1;
+}
+
+sub exit_scope
+{
+ my $self = shift;
+ my ($obj, $no_delta) = @_;
+
+ unless ($no_delta)
+ {
+ my $deltas = $obj->deltas($self);
+ $self->{$_} += $deltas->{$_} for keys %$deltas;
+ }
+
+ pop @{$self->{STACK}};
+
+ return 1;
+}
+
+sub get
+{
+ my $self = shift;
+ my ($dummy, $key, $depth) = @_;
+ $depth ||= 0;
+ $key = uc $key;
+
+ return unless @{$self->{STACK}};
+
+ my $obj = $self->{STACK}[-1];
+
+ return $self->{$key} if $isAbsolute{$key};
+
+ my $val = undef;
+ my $this_depth = $depth;
+ foreach my $e (reverse @{$self->{STACK}})
+ {
+ next unless exists $e->{$key};
+ next if $this_depth-- > 0;
+
+ $val = $self->resolve($e, $key, $depth);
+ last;
+ }
+
+ $val = $self->{$key} unless defined $val;
+ return $val unless defined $val;
+
+ return $self->param($1, $depth) if $val =~ /^\$(\S+)$/o;
+
+ return $val;
+}
+
+sub active_format
+{
+ my $self = shift;
+
+ $self->{ACTIVE_FORMAT} = $_[0]
+ if @_;
+
+ $self->{ACTIVE_FORMAT};
+}
+
+sub new_worksheet
+{
+ my $self = shift;
+ my ($name) = @_;
+
+ $self->{ROW} = $self->{COL} = 0;
+
+ $self->active_worksheet(
+ $self->{XLS}->add_worksheet(
+ $name || '',
+ ),
+ );
+}
+
+sub active_worksheet
+{
+ my $self = shift;
+
+ $self->{ACTIVE_WORKSHEET} = $_[0]
+ if @_;
+
+ $self->{ACTIVE_WORKSHEET};
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Context
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Element;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Base);
+
+ use Excel::Template::Base;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Element::Cell;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Element);
+
+ use Excel::Template::Element;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{TXTOBJ} = Excel::Template::Factory->create('TEXTOBJECT');
+
+ return $self;
+}
+
+sub get_text
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $txt = $context->get($self, 'TEXT');
+ if (defined $txt)
+ {
+ my $txt_obj = Excel::Template::Factory->create('TEXTOBJECT');
+ push @{$txt_obj->{STACK}}, $txt;
+ $txt = $txt_obj->resolve($context);
+ }
+ elsif ($self->{TXTOBJ})
+ {
+ $txt = $self->{TXTOBJ}->resolve($context)
+ }
+ else
+ {
+# $txt = Unicode::String::utf8('');
+ $txt = '';
+ }
+
+ return $txt;
+}
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ $context->active_worksheet->write(
+ (map { $context->get($self, $_) } qw(ROW COL)),
+ $self->get_text($context),
+ $context->active_format,
+ );
+
+ return 1;
+}
+
+sub deltas
+{
+ return {
+ COL => +1,
+ };
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Cell - Excel::Template::Element::Cell
+
+=head1 PURPOSE
+
+To actually write stuff to the worksheet
+
+=head1 NODE NAME
+
+CELL
+
+=head1 INHERITANCE
+
+Excel::Template::Element
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * TEXT
+
+This is the text to write to the cell. This can either be text or a parameter
+with a dollar-sign in front of the parameter name.
+
+=item * COL
+
+Optionally, you can specify which column you want this cell to be in. It can be
+either a number (zero-based) or an offset. See Excel::Template for more info on
+offset-based numbering.
+
+=back 4
+
+There will be more parameters added, as features are added.
+
+=head1 CHILDREN
+
+Excel::Template::Element::Formula
+
+=head1 EFFECTS
+
+This will consume one column on the current row.
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <cell text="Some Text Here"/>
+ <cell>Some other text here</cell>
+
+ <cell text="$Param2"/>
+ <cell>Some <var name="Param"> text here</cell>
+
+In the above example, four cells are written out. The first two have text hard-
+coded. The second two have variables. The third and fourth items have another
+thing that should be noted. If you have text where you want a variable in the
+middle, you have to use the latter form. Variables within parameters are the
+entire parameter's value.
+
+Please see Spreadsheet::WriteExcel for what constitutes a legal formula.
+
+=head1 BACK-REFERENCES
+
+Currently, you can only use a hard-coded formula. The next release will add the
+capability to have a formula reference other nodes in the template dynamically.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+ROW, VAR, FORMULA
+
+=cut
--- /dev/null
+package Excel::Template::Element::Formula;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Element::Cell);
+
+ use Excel::Template::Element::Cell;
+}
+
+sub get_text
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $text = $self->SUPER::get_text($context);
+
+# At this point, we must do back-reference dereferencing
+
+ return $text;
+}
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ $context->active_worksheet->write_formula(
+ (map { $context->get($self, $_) } qw(ROW COL)),
+ $self->get_text($context),
+ );
+
+ return 1;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Formula - Excel::Template::Element::Formula
+
+=head1 PURPOSE
+
+To write formulas to the worksheet
+
+=head1 NODE NAME
+
+FORMULA
+
+=head1 INHERITANCE
+
+Excel::Template::Element::Cell
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * TEXT
+
+This is the formula to write to the cell. This can either be text or a parameter
+with a dollar-sign in front of the parameter name.
+
+=item * COL
+
+Optionally, you can specify which column you want this cell to be in. It can be
+either a number (zero-based) or an offset. See Excel::Template for more info on
+offset-based numbering.
+
+=back 4
+
+There will be more parameters added, as features are added.
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+This will consume one column on the current row.
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <formula text="=(1 + 2)"/>
+ <formula>=SUM(A1:A5)</formula>
+
+ <formula text="$Param2"/>
+ <formula>=(A1 + <var name="Param">)</formula>
+
+In the above example, four formulas are written out. The first two have the
+formula hard-coded. The second two have variables. The third and fourth items
+have another thing that should be noted. If you have a formula where you want a
+variable in the middle, you have to use the latter form. Variables within
+parameters are the entire parameter's value.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+ROW, VAR, CELL
+
+=cut
--- /dev/null
+package Excel::Template::Element::Var;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Element);
+
+ use Excel::Template::Element;
+}
+
+sub resolve { ($_[1])->param($_[1]->resolve($_[0], 'NAME')) }
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Var
+
+=head1 PURPOSE
+
+To provide parameter substitution.
+
+=head1 NODE NAME
+
+VAR
+
+=head1 INHERITANCE
+
+Excel::Template::Element
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the parameter to substitute here.
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+This will only be used within CELL tags.
+
+=head1 USAGE
+
+This is used exactly like HTML::Template's TMPL_VAR. There is one exception -
+since you can have variable names inside the parameters, you can do something
+like:
+
+ <loop name="LOOPY">
+ <var name="$SomeParam"/>
+ </loop>
+
+Where the actual name to be substituted is, itself, a parameter.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+CELL
+
+=cut
--- /dev/null
+package Excel::Template::Factory;
+
+use strict;
+
+BEGIN {
+ use vars qw(%Manifest %isBuildable);
+}
+
+%Manifest = (
+
+# These are the instantiable nodes
+ 'IF' => 'Excel::Template::Container::Conditional',
+ 'LOOP' => 'Excel::Template::Container::Loop',
+ 'ROW' => 'Excel::Template::Container::Row',
+ 'SCOPE' => 'Excel::Template::Container::Scope',
+ 'WORKBOOK' => 'Excel::Template::Container::Workbook',
+ 'WORKSHEET' => 'Excel::Template::Container::Worksheet',
+
+ 'CELL' => 'Excel::Template::Element::Cell',
+ 'FORMULA' => 'Excel::Template::Element::Formula',
+ 'VAR' => 'Excel::Template::Element::Var',
+
+ 'FORMAT' => 'Excel::Template::Container::Format',
+
+# These are all the Format short-cut objects
+ 'BOLD' => 'Excel::Template::Container::Bold',
+ 'HIDDEN' => 'Excel::Template::Container::Hidden',
+ 'ITALIC' => 'Excel::Template::Container::Italic',
+ 'LOCKED' => 'Excel::Template::Container::Locked',
+ 'OUTLINE' => 'Excel::Template::Container::Outline',
+ 'SHADOW' => 'Excel::Template::Container::Shadow',
+ 'STRIKEOUT' => 'Excel::Template::Container::Strikeout',
+
+# These are the helper objects
+
+ 'CONTEXT' => 'Excel::Template::Context',
+ 'ITERATOR' => 'Excel::Template::Iterator',
+ 'TEXTOBJECT' => 'Excel::Template::TextObject',
+
+ 'CONTAINER' => 'Excel::Template::Container',
+ 'ELEMENT' => 'Excel::Template::Element',
+
+ 'BASE' => 'Excel::Template::Base',
+);
+
+%isBuildable = map { $_ => 1 } qw(
+ BOLD
+ CELL
+ FORMAT
+ FORMULA
+ IF
+ ITALIC
+ OUTLINE
+ LOOP
+ ROW
+ SHADOW
+ STRIKEOUT
+ VAR
+ WORKBOOK
+ WORKSHEET
+);
+
+sub register
+{
+ my %params = @_;
+
+ my @param_names = qw(name class isa);
+ for (@param_names)
+ {
+ unless ($params{$_})
+ {
+ warn "$_ was not supplied to register()\n";
+ return 0;
+ }
+ }
+
+ my $name = uc $params{name};
+ if (exists $Manifest{$name})
+ {
+ warn "$params{name} already exists in the manifest.\n";
+ return 0;
+ }
+
+ my $isa = uc $params{isa};
+ unless (exists $Manifest{$isa})
+ {
+ warn "$params{isa} does not exist in the manifest.\n";
+ return 0;
+ }
+
+ $Manifest{$name} = $params{class};
+ $isBuildable{$name} = 1;
+
+ {
+ no strict 'refs';
+ unshift @{"$params{class}::ISA"}, $Manifest{$isa};
+ }
+
+ return 1;
+}
+
+sub create
+{
+ my $class = shift;
+ my $name = uc shift;
+
+ return unless exists $Manifest{$name};
+
+ (my $filename = $Manifest{$name}) =~ s!::!/!g;
+
+ eval {
+ require "$filename.pm";
+ }; if ($@) {
+ die "Cannot find or compile PM file for '$name' ($filename)\n";
+ }
+
+ return $Manifest{$name}->new(@_);
+}
+
+sub create_node
+{
+ my $class = shift;
+ my $name = uc shift;
+
+ return unless exists $isBuildable{$name};
+
+ return $class->create($name, @_);
+}
+
+sub isa
+{
+ return unless @_ >= 2;
+ exists $Manifest{uc $_[1]}
+ ? UNIVERSAL::isa($_[0], $Manifest{uc $_[1]})
+ : UNIVERSAL::isa(@_)
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Factory
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Format;
+
+use strict;
+
+# This is the format repository. Spreadsheet::WriteExcel does not cache the
+# known formats. So, it is very possible to continually add the same format
+# over and over until you run out of RAM or addressability in the XLS file. In
+# real life, less than 10-20 formats are used, and they're re-used in various
+# places in the file. This provides a way of keeping track of already-allocated
+# formats and making new formats based on old ones.
+
+{
+ # %_Parameters is a hash with the key being the format name and the value
+ # being the index/length of the format in the bit-vector.
+ my %_Formats = (
+ bold => [ 0, 1 ],
+ italic => [ 1, 1 ],
+ locked => [ 2, 1 ],
+ hidden => [ 3, 1 ],
+ font_outline => [ 4, 1 ],
+ font_shadow => [ 5, 1 ],
+ font_strikeout => [ 6, 1 ],
+ );
+
+ sub _params_to_vec
+ {
+ my %params = @_;
+ $params{lc $_} = delete $params{$_} for keys %params;
+
+ my $vec = '';
+ vec( $vec, $_Formats{$_}[0], $_Formats{$_}[1] ) = ($params{$_} && 1)
+ for grep { exists $_Formats{$_} }
+ map { lc } keys %params;
+
+ $vec;
+ }
+
+ sub _vec_to_params
+ {
+ my ($vec) = @_;
+
+ my %params;
+ while (my ($k, $v) = each %_Formats)
+ {
+ next unless vec( $vec, $v->[0], $v->[1] );
+ $params{$k} = 1;
+ }
+
+ %params;
+ }
+}
+
+{
+ my %_Formats;
+
+ sub _assign {
+ $_Formats{$_[0]} = $_[1] unless exists $_Formats{$_[0]};
+ $_Formats{$_[1]} = $_[0] unless exists $_Formats{$_[1]};
+ }
+
+ sub _retrieve_vec { ref($_[0]) ? ($_Formats{$_[0]}) : ($_[0]); }
+ sub _retrieve_format { ref($_[0]) ? ($_[0]) : ($_Formats{$_[0]}); }
+}
+
+sub blank_format
+{
+ shift;
+ my ($context) = @_;
+
+ my $blank_vec = _params_to_vec();
+
+ my $format = _retrieve_format($blank_vec);
+ return $format if $format;
+
+ $format = $context->{XLS}->add_format;
+ _assign($blank_vec, $format);
+ $format;
+}
+
+sub copy
+{
+ shift;
+ my ($context, $old_format, %properties) = @_;
+
+ defined(my $vec = _retrieve_vec($old_format))
+ || die "Internal Error: Cannot find vector for format '$old_format'!\n";
+
+ my $new_vec = _params_to_vec(%properties);
+
+ $new_vec |= $vec;
+
+ my $format = _retrieve_format($new_vec);
+ return $format if $format;
+
+ $format = $context->{XLS}->add_format(_vec_to_params($new_vec));
+ _assign($new_vec, $format);
+ $format;
+}
+
+1;
+__END__
+
+Category Description Property Method Name Implemented
+-------- ----------- -------- ----------- -----------
+Font Font type font set_font()
+ Font size size set_size()
+ Font color color set_color()
+ Bold bold set_bold() YES
+ Italic italic set_italic() YES
+ Underline underline set_underline()
+ Strikeout font_strikeout set_font_strikeout() YES
+ Super/Subscript font_script set_font_script()
+ Outline font_outline set_font_outline() YES
+ Shadow font_shadow set_font_shadow() YES
+
+Number Numeric format num_format set_num_format()
+
+Protection Lock cells locked set_locked() YES
+ Hide formulas hidden set_hidden() YES
+
+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()
+ Merge merge set_merge()
+
+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()
--- /dev/null
+package Excel::Template::Iterator;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Base);
+
+ use Excel::Template::Base;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ unless (Excel::Template::Factory::isa($self->{CONTEXT}, 'CONTEXT'))
+ {
+ die "Internal Error: No context object passed to ", __PACKAGE__, $/;
+ }
+
+ $self->{MAXITERS} ||= 0;
+
+ # This is the index we will work on NEXT, in whatever direction the
+ # iterator is going.
+ $self->{INDEX} = -1;
+
+ # This is a short-circuit parameter to let the iterator function in a
+ # null state.
+ $self->{NO_PARAMS} = 0;
+ unless ($self->{NAME} =~ /\w/)
+ {
+ $self->{NO_PARAMS} = 1;
+
+ warn "INTERNAL ERROR: 'NAME' was blank was blank when passed to ", __PACKAGE__, $/;
+
+ return $self;
+ }
+
+ # Cache the reference to the appropriate data.
+ $self->{DATA} = $self->{CONTEXT}->param($self->{NAME});
+
+ unless (UNIVERSAL::isa($self->{DATA}, 'ARRAY'))
+ {
+ $self->{NO_PARAMS} = 1;
+ warn "'$self->{NAME}' does not have a list of parameters", $/;
+
+ return $self;
+ }
+
+ unless (@{$self->{DATA}})
+ {
+ $self->{NO_PARAMS} = 1;
+ }
+
+ $self->{MAX_INDEX} = $#{$self->{DATA}};
+
+ return $self;
+}
+
+sub enter_scope
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ for my $x ($self->{DATA}[$self->{INDEX}])
+ {
+ $x->{uc $_} = delete $x->{$_} for keys %$x;
+ }
+
+ push @{$self->{CONTEXT}{PARAM_MAP}}, $self->{DATA}[$self->{INDEX}];
+
+ return 1;
+}
+
+sub exit_scope
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ # There has to be the base parameter map and at least the one that
+ # Iterator::enter_scope() added on top.
+ @{$self->{CONTEXT}{PARAM_MAP}} > 1 ||
+ die "Internal Error: ", __PACKAGE__, "'s internal param_map off!", $/;
+
+ pop @{$self->{CONTEXT}{PARAM_MAP}};
+
+ return 1;
+}
+
+sub can_continue
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ return 1 if $self->more_params;
+
+ return 0;
+}
+
+sub more_params
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ return 1 if $self->{MAX_INDEX} > $self->{INDEX};
+
+ return 0;
+}
+
+# Call this method BEFORE incrementing the index to the next value.
+sub _do_globals
+{
+ my $self = shift;
+
+ my $data = $self->{DATA}[$self->{INDEX}];
+
+ # Perl's arrays are 0-indexed. Thus, the first element is at index "0".
+ # This means that odd-numbered elements are at even indices, and vice-versa.
+ # This also means that MAX (the number of elements in the array) can never
+ # be the value of an index. It is NOT the last index in the array.
+
+ $data->{'__FIRST__'} ||= ($self->{INDEX} == 0);
+ $data->{'__INNER__'} ||= (0 < $self->{INDEX} && $self->{INDEX} < $self->{MAX_INDEX});
+ $data->{'__LAST__'} ||= ($self->{INDEX} == $self->{MAX_INDEX});
+ $data->{'__ODD__'} ||= !($self->{INDEX} % 2);
+
+ return 1;
+}
+
+sub next
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ return 0 unless $self->more_params;
+
+ $self->exit_scope;
+
+ $self->{INDEX}++;
+
+ $self->_do_globals;
+
+ $self->enter_scope;
+
+ return 1;
+}
+
+sub back_up
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ $self->exit_scope;
+
+ $self->{INDEX}--;
+
+ $self->_do_globals;
+
+ $self->enter_scope;
+
+ return 1;
+}
+
+sub reset
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ $self->{INDEX} = -1;
+
+ return 1;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Iterator
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::TextObject;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Base);
+
+ use Excel::Template::Base;
+
+# use Unicode::String;
+}
+
+# This is a helper object. It is not instantiated by the user,
+# nor does it represent an XML object. Rather, certain elements,
+# such as <textbox>, can use this object to do text with variable
+# substitutions.
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{STACK} = [] unless UNIVERSAL::isa($self->{STACK}, 'ARRAY');
+
+ return $self;
+}
+
+sub resolve
+{
+ my $self = shift;
+ my ($context) = @_;
+
+# my $t = Unicode::String::utf8('');
+ my $t = '';
+
+ for my $tok (@{$self->{STACK}})
+ {
+ my $val = $tok;
+ $val = $val->resolve($context)
+ if Excel::Template::Factory::isa($val, 'VAR');
+
+# $t .= Unicode::String::utf8("$val");
+ $t .= $val;
+ }
+
+ return $t;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::TextObject
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+# -*- perl -*-
+
+# t/001_load.t - check module loading and create testing directory
+
+use Test::More tests => 2;
+
+BEGIN { use_ok( 'Excel::Template' ); }
+
+my $object = Excel::Template->new ();
+isa_ok ($object, 'Excel::Template');
+
+