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