Added filehandles to be parseable
[p5sagit/Excel-Template.git] / lib / Excel / Template.pm
CommitLineData
d0eafc11 1package Excel::Template;
2
3use strict;
4
5BEGIN {
6 use Excel::Template::Base;
7 use vars qw ($VERSION @ISA);
8
a084e413 9 $VERSION = '0.22';
d0eafc11 10 @ISA = qw( Excel::Template::Base );
11}
12
13use File::Basename;
14use XML::Parser;
d0eafc11 15use IO::Scalar;
16
6dd4c89d 17use constant RENDER_NML => 'normal';
18use constant RENDER_BIG => 'big';
19use constant RENDER_XML => 'xml';
20
21my %renderers = (
22 RENDER_NML, 'Spreadsheet::WriteExcel',
23 RENDER_BIG, 'Spreadsheet::WriteExcel::Big',
24 RENDER_XML, 'Spreadsheet::WriteExcelXML',
25);
26
d0eafc11 27sub new
28{
29 my $class = shift;
30 my $self = $class->SUPER::new(@_);
31
a084e413 32 $self->{FILE} = $self->{FILENAME}
33 if !defined $self->{FILE} && defined $self->{FILENAME};
34
35 $self->parse_xml($self->{FILE})
36 if defined $self->{FILE};
d0eafc11 37
38 my @renderer_classes = ( 'Spreadsheet::WriteExcel' );
6dd4c89d 39
40 if (exists $self->{RENDERER} && $self->{RENDERER})
d0eafc11 41 {
6dd4c89d 42 if (exists $renderers{ lc $self->{RENDERER} })
43 {
44 unshift @renderer_classes, $renderers{ lc $self->{RENDERER} };
45 }
46 elsif ($^W)
47 {
48 warn "'$self->{RENDERER}' is not recognized\n";
49 }
d0eafc11 50 }
6dd4c89d 51 elsif (exists $self->{BIG_FILE} && $self->{BIG_FILE})
9d172425 52 {
6dd4c89d 53 warn "Use of BIG_FILE is deprecated.\n";
54 unshift @renderer_classes, 'Spreadsheet::WriteExcel::Big';
9d172425 55 }
56
d0eafc11 57 $self->{RENDERER} = undef;
58 foreach my $class (@renderer_classes)
59 {
60 (my $filename = $class) =~ s!::!/!g;
61 eval {
62 require "$filename.pm";
63 $class->import;
64 };
65 if ($@) {
dee1f239 66 warn "Could not find or compile '$class'\n" if $^W;
d0eafc11 67 } else {
68 $self->{RENDERER} = $class;
69 last;
70 }
71 }
72
73 defined $self->{RENDERER} ||
74 die "Could not find a renderer class. Tried:\n\t" .
75 join("\n\t", @renderer_classes) .
76 "\n";
77
8c63e224 78 $self->{USE_UNICODE} = ~~0
79 if $] >= 5.008;
80
d0eafc11 81 return $self;
82}
83
84sub param
85{
86 my $self = shift;
87
88 # Allow an arbitrary number of hashrefs, so long as they're the first things # into param(). Put each one onto the end, de-referenced.
89 push @_, %{shift @_} while UNIVERSAL::isa($_[0], 'HASH');
90
91 (@_ % 2)
92 && die __PACKAGE__, "->param() : Odd number of parameters to param()\n";
93
94 my %params = @_;
95 $params{uc $_} = delete $params{$_} for keys %params;
96 @{$self->{PARAM_MAP}}{keys %params} = @params{keys %params};
97
8c63e224 98 return ~~1;
d0eafc11 99}
100
101sub write_file
102{
103 my $self = shift;
104 my ($filename) = @_;
105
106 my $xls = $self->{RENDERER}->new($filename)
107 || die "Cannot create XLS in '$filename': $!\n";
108
109 $self->_prepare_output($xls);
110
111 $xls->close;
112
8c63e224 113 return ~~1;
d0eafc11 114}
115
116sub output
117{
118 my $self = shift;
119
120 my $output;
121 tie *XLS, 'IO::Scalar', \$output;
122
123 $self->write_file(\*XLS);
124
125 return $output;
126}
127
d0eafc11 128sub parse_xml
129{
130 my $self = shift;
a084e413 131 my ($file) = @_;
d0eafc11 132
d0eafc11 133 my @stack;
a084e413 134 my @parms = (
d0eafc11 135 Handlers => {
136 Start => sub {
137 shift;
138
139 my $name = uc shift;
140
6dd4c89d 141 my $node = Excel::Template::Factory->_create_node($name, @_);
d0eafc11 142 die "'$name' (@_) didn't make a node!\n" unless defined $node;
143
37513eae 144 if ( $node->isa( 'WORKBOOK' ) )
d0eafc11 145 {
146 push @{$self->{WORKBOOKS}}, $node;
147 }
37513eae 148 elsif ( $node->is_embedded )
d0eafc11 149 {
150 return unless @stack;
151
152 if (exists $stack[-1]{TXTOBJ} &&
153 $stack[-1]{TXTOBJ}->isa('TEXTOBJECT'))
154 {
155 push @{$stack[-1]{TXTOBJ}{STACK}}, $node;
156 }
157
158 }
159 else
160 {
161 push @{$stack[-1]{ELEMENTS}}, $node
162 if @stack;
163 }
164 push @stack, $node;
165 },
166 Char => sub {
167 shift;
168 return unless @stack;
169
170 my $parent = $stack[-1];
171
172 if (
173 exists $parent->{TXTOBJ}
174 &&
175 $parent->{TXTOBJ}->isa('TEXTOBJECT')
176 ) {
177 push @{$parent->{TXTOBJ}{STACK}}, @_;
178 }
179 },
180 End => sub {
181 shift;
182 return unless @stack;
183
184 pop @stack if $stack[-1]->isa(uc $_[0]);
185 },
186 },
187 );
188
a084e413 189 if ( ref $file )
190 {
191 *INFILE = $file;
192 }
193 else
d0eafc11 194 {
a084e413 195 my ($filename, $dirname) = fileparse($file);
196
197 push @parms, Base => $dirname;
198
199 open( INFILE, "<$file" )
200 || die "Cannot open '$file' for reading: $!\n";
201
202 }
d0eafc11 203
a084e413 204 my $parser = XML::Parser->new( @parms );
205 $parser->parse(do { local $/ = undef; <INFILE> });
d0eafc11 206
a084e413 207 unless ( ref $file )
208 {
b6bc5a5d 209 close INFILE;
d0eafc11 210 }
211
8ca5e726 212 return ~~1;
d0eafc11 213}
b6bc5a5d 214*parse = \&parse_xml;
d0eafc11 215
216sub _prepare_output
217{
218 my $self = shift;
219 my ($xls) = @_;
220
6dd4c89d 221 my $context = Excel::Template::Factory->_create(
d0eafc11 222 'CONTEXT',
223
224 XLS => $xls,
225 PARAM_MAP => [ $self->{PARAM_MAP} ],
8c63e224 226 UNICODE => $self->{UNICODE},
d0eafc11 227 );
228
a084e413 229# print "@{$self->{WORKBOOKS}}\n";
d0eafc11 230 $_->render($context) for @{$self->{WORKBOOKS}};
231
8ca5e726 232 return ~~1;
d0eafc11 233}
234
235sub register { shift; Excel::Template::Factory::register(@_) }
236
2371;
238__END__
239
240=head1 NAME
241
242Excel::Template - Excel::Template
243
244=head1 SYNOPSIS
245
246First, make a template. This is an XML file, describing the layout of the
247spreadsheet.
248
249For example, test.xml:
250
251 <workbook>
252 <worksheet name="tester">
253 <cell text="$HOME"/>
254 <cell text="$PATH"/>
255 </worksheet>
256 </workbook>
257
258Now, create a small program to use it:
259
260 #!/usr/bin/perl -w
9d172425 261 use Excel::Template;
d0eafc11 262
263 # Create the Excel template
264 my $template = Excel::Template->new(
265 filename => 'test.xml',
266 );
267
268 # Add a few parameters
269 $template->param(
270 HOME => $ENV{HOME},
271 PATH => $ENV{PATH},
272 );
273
274 $template->write_file('test.xls');
275
276If everything worked, then you should have a spreadsheet in your work directory
277that looks something like:
278
279 A B C
280 +----------------+----------------+----------------
281 1 | /home/me | /bin:/usr/bin |
282 +----------------+----------------+----------------
283 2 | | |
284 +----------------+----------------+----------------
285 3 | | |
286
287=head1 DESCRIPTION
288
289This is a module used for templating Excel files. Its genesis came from the
290need to use the same datastructure as HTML::Template, but provide Excel files
291instead. The existing modules don't do the trick, as they require replication
292of logic that's already been done within HTML::Template.
293
d0eafc11 294=head1 MOTIVATION
295
296I do a lot of Perl/CGI for reporting purposes. In nearly every place I've been,
297I've been asked for HTML, PDF, and Excel. HTML::Template provides the first, and
298PDF::Template does the second pretty well. But, generating Excel was the
299sticking point. I already had the data structure for the other templating
300modules, but I just didn't have an easy mechanism to get that data structure
301into an XLS file.
302
303=head1 USAGE
304
305=head2 new()
306
a084e413 307This creates a Excel::Template object.
d0eafc11 308
6dd4c89d 309=head3 Parameters
310
311=over 4
312
a084e413 313=item * FILE / FILENAME
314
315Excel::Template will parse the template in the given file or filehandle automatically. (You can also use the parse() method, described below.)
316
317If you want to use the __DATA__ section, you can do so by passing
318
319 FILE => \*DATA
320
6dd4c89d 321=item * RENDERER
322
323The default rendering engine is Spreadsheet::WriteExcel. You may, if you choose, change that to another choice. The legal values are:
324
325=over 4
326
327=item * Excel::Template->RENDER_NML
328
329This is the default of Spreadsheet::WriteExcel.
8c63e224 330
6dd4c89d 331=item * Excel::Template->RENDER_BIG
332
333This attempts to load Spreadsheet::WriteExcel::Big.
334
335=item * Excel::Template->RENDER_XML
336
337This attempts to load Spreadsheet::WriteExcelXML.
338
339=back
340
341=item * USE_UNICODE
342
343This will use L<Unicode::String> to represent strings instead of Perl's internal string handling. You must already have L<Unicode::String> installed on your system.
8c63e224 344
345The USE_UNICODE parameter will be ignored if you are using Perl 5.8 or higher as
346Perl's internal string handling is unicode-aware.
8ca5e726 347
9d172425 348NOTE: Certain older versions of L<OLE::Storage_Lite> and mod_perl clash for some
349reason. Upgrading to the latest version of L<OLE::Storage_Lite> should fix the
350problem.
8ca5e726 351
6dd4c89d 352=back
353
354=head3 Deprecated
355
356=over 4
357
358=item * BIG_FILE
359
360Instead, use RENDERER => Excel::Template->RENDER_BIG
361
362=back
363
d0eafc11 364=head2 param()
365
8ca5e726 366This method is exactly like L<HTML::Template>'s param() method.
d0eafc11 367
368=head2 parse() / parse_xml()
369
a084e413 370This 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.
371
372If a filename is passed in (vs. a filehandle), the directory name will be passed in to XML::Parser as the I<Base> parameter. This will allow for XML directives to work as expected.
d0eafc11 373
374=head2 write_file()
375
376Create the Excel file and write it to the specified filename, if possible. (This
377is when the actual merging of the template and the parameters occurs.)
378
379=head2 output()
380
381It will act just like HTML::Template's output() method, returning the resultant
382file as a stream, usually for output to the web. (This is when the actual
383merging of the template and the parameters occurs.)
384
6dd4c89d 385=head2 register()
386
387This allows you to register a class as handling a node. q.v. L<Excel::Template::Factory> for more info.
388
d0eafc11 389=head1 SUPPORTED NODES
390
8ca5e726 391This is a partial list of nodes. See the other classes in this distro for more
d0eafc11 392details on specific parameters and the like.
393
394Every node can set the ROW and COL parameters. These are the actual ROW/COL
8ca5e726 395values that the next CELL-type tag will write into.
d0eafc11 396
397=over 4
398
8ca5e726 399=item * L<WORKBOOK|Excel::Template::Container::Workbook>
400
401This is the node representing the workbook. It is the parent for all other
402nodes.
403
404=item * L<WORKSHEET|Excel::Template::Container::Worksheet>
405
406This is the node representing a given worksheet.
407
408=item * L<IF|Excel::Template::Container::Conditional>
409
410This node represents a conditional expression. Its children may or may not be
411rendered. It behaves just like L<HTML::Template>'s TMPL_IF.
412
413=item * L<LOOP|Excel::Template::Container::Loop>
414
415This node represents a loop. It behaves just like L<HTML::Template>'s TMPL_LOOP.
416
417=item * L<ROW|Excel::Template::Container::Row>
418
419This node represents a row of data. This is the A in A1.
420
421=item * L<FORMAT|Excel::Template::Container::Format>
422
423This node varies the format for its children. All formatting options supported
424in L<Spreadsheet::WriteExcel> are supported here. There are also a number of
425formatting shortcuts, such as L<BOLD|Excel::Template::Container::Bold> and
426L<ITALIC|Excel::Template::Container::Italic>.
427
428=item * L<BACKREF|Excel::Template::Element::Backref>
429
430This refers back to a cell previously named.
d0eafc11 431
8ca5e726 432=item * L<CELL|Excel::Template::Element::Cell>
d0eafc11 433
8ca5e726 434This is the actual cell in a spreadsheet.
d0eafc11 435
8ca5e726 436=item * L<FORMULA|Excel::Template::Element::Formula>
d0eafc11 437
8ca5e726 438This is a formula in a spreadsheet.
d0eafc11 439
8ca5e726 440=item * L<RANGE|Excel::Template::Element::Range>
d0eafc11 441
8ca5e726 442This is a BACKREF for a number of identically-named cells.
d0eafc11 443
8ca5e726 444=item * L<VAR|Excel::Template::Element::Var>
d0eafc11 445
8ca5e726 446This is a variable. It is generally used when the 'text' attribute isn't
447sufficient.
d0eafc11 448
6dd4c89d 449=back
d0eafc11 450
451=head1 BUGS
452
453None, that I know of.
454
455=head1 SUPPORT
456
8ca5e726 457This is production quality software, used in several production web
458applications.
d0eafc11 459
460=head1 AUTHOR
461
8ca5e726 462 Rob Kinyon (rob.kinyon@gmail.com)
a8441e01 463
464=head1 CONTRIBUTORS
465
c11fa570 466There is a mailing list at http://groups.google.com/group/ExcelTemplate or exceltemplate@googlegroups.com
d0eafc11 467
c11fa570 468=head2 Robert Graff
37513eae 469
470=over 4
471
472=item * Finishing formats
473
474=item * Fixing several bugs in worksheet naming
475
6dd4c89d 476=back
37513eae 477
c11fa570 478=head1 TEST COVERAGE
479
480I used Devel::Cover to test the coverage of my tests. Every release, I intend to improve these numbers.
481
482Excel::Template is also part of the CPAN Kwalitee initiative, being one of the top 100 non-core modules downloaded from CPAN. If you wish to help out, please feel free to contribute tests, patches, and/or suggestions.
483
484---------------------------- ------ ------ ------ ------ ------ ------ ------
485File stmt branch cond sub pod time total
486---------------------------- ------ ------ ------ ------ ------ ------ ------
a084e413 487blib/lib/Excel/Template.pm 90.4 62.5 58.8 90.5 100.0 30.2 82.0
488...ib/Excel/Template/Base.pm 83.3 50.0 66.7 75.0 88.9 8.2 80.0
c11fa570 489...cel/Template/Container.pm 46.3 20.0 33.3 58.3 85.7 4.6 47.7
a084e413 490...emplate/Container/Bold.pm 100.0 n/a n/a 100.0 0.0 0.5 95.0
491.../Container/Conditional.pm 58.5 52.3 66.7 75.0 66.7 0.8 58.4
492...plate/Container/Format.pm 100.0 n/a n/a 100.0 0.0 0.7 96.6
c11fa570 493...plate/Container/Hidden.pm 100.0 n/a n/a 100.0 0.0 0.2 95.0
a084e413 494...plate/Container/Italic.pm 100.0 n/a n/a 100.0 0.0 0.1 95.0
c11fa570 495...plate/Container/Locked.pm 100.0 n/a n/a 100.0 0.0 0.1 95.0
496...emplate/Container/Loop.pm 55.6 40.0 50.0 77.8 75.0 0.5 56.6
497...late/Container/Outline.pm 71.4 n/a n/a 80.0 0.0 0.0 70.0
498...Template/Container/Row.pm 100.0 75.0 n/a 100.0 50.0 0.3 93.8
499...mplate/Container/Scope.pm 100.0 n/a n/a 100.0 n/a 0.1 100.0
500...plate/Container/Shadow.pm 100.0 n/a n/a 100.0 0.0 0.1 95.0
501...te/Container/Strikeout.pm 100.0 n/a n/a 100.0 0.0 0.1 95.0
a084e413 502...ate/Container/Workbook.pm 100.0 n/a n/a 100.0 n/a 1.0 100.0
c11fa570 503...te/Container/Worksheet.pm 94.1 50.0 n/a 100.0 0.0 0.9 88.0
a084e413 504...Excel/Template/Context.pm 83.1 53.4 54.2 95.0 92.9 19.1 75.2
505...Excel/Template/Element.pm 100.0 n/a n/a 100.0 n/a 0.4 100.0
c11fa570 506...mplate/Element/Backref.pm 100.0 50.0 33.3 100.0 0.0 0.1 87.1
507.../Template/Element/Cell.pm 95.8 65.0 80.0 100.0 66.7 3.6 86.9
508...mplate/Element/Formula.pm 100.0 n/a n/a 100.0 0.0 0.3 94.1
509...Template/Element/Range.pm 100.0 66.7 n/a 100.0 66.7 0.2 93.3
a084e413 510...l/Template/Element/Var.pm 100.0 n/a n/a 100.0 0.0 0.1 94.1
511...Excel/Template/Factory.pm 57.1 34.6 n/a 88.9 100.0 14.3 55.2
512.../Excel/Template/Format.pm 98.3 81.2 33.3 100.0 100.0 8.9 93.2
513...xcel/Template/Iterator.pm 85.2 70.6 70.6 84.6 87.5 1.9 80.4
514...el/Template/TextObject.pm 92.9 62.5 33.3 100.0 50.0 2.7 83.0
515Total 83.1 56.6 58.3 91.1 98.7 100.0 78.8
c11fa570 516---------------------------- ------ ------ ------ ------ ------ ------ ------
517
d0eafc11 518=head1 COPYRIGHT
519
520This program is free software; you can redistribute
521it and/or modify it under the same terms as Perl itself.
522
523The full text of the license can be found in the
524LICENSE file included with this module.
525
526=head1 SEE ALSO
527
528perl(1), HTML::Template, Spreadsheet::WriteExcel.
529
530=cut