0.17 Sat Nov 05 23:30:00 2004
[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
dba6a68e 9 $VERSION = '0.17';
d0eafc11 10 @ISA = qw( Excel::Template::Base );
11}
12
13use File::Basename;
14use XML::Parser;
d0eafc11 15use IO::Scalar;
16
17sub new
18{
19 my $class = shift;
20 my $self = $class->SUPER::new(@_);
21
22 $self->parse_xml($self->{FILENAME})
23 if defined $self->{FILENAME};
24
25 my @renderer_classes = ( 'Spreadsheet::WriteExcel' );
26 if (exists $self->{BIG_FILE} && $self->{BIG_FILE})
27 {
28 unshift @renderer_classes, 'Spreadsheet::WriteExcel::Big';
29 }
30
31 $self->{RENDERER} = undef;
32 foreach my $class (@renderer_classes)
33 {
34 (my $filename = $class) =~ s!::!/!g;
35 eval {
36 require "$filename.pm";
37 $class->import;
38 };
39 if ($@) {
40 warn "Could not find or compile '$class'\n";
41 } else {
42 $self->{RENDERER} = $class;
43 last;
44 }
45 }
46
47 defined $self->{RENDERER} ||
48 die "Could not find a renderer class. Tried:\n\t" .
49 join("\n\t", @renderer_classes) .
50 "\n";
51
52 return $self;
53}
54
55sub param
56{
57 my $self = shift;
58
59 # Allow an arbitrary number of hashrefs, so long as they're the first things # into param(). Put each one onto the end, de-referenced.
60 push @_, %{shift @_} while UNIVERSAL::isa($_[0], 'HASH');
61
62 (@_ % 2)
63 && die __PACKAGE__, "->param() : Odd number of parameters to param()\n";
64
65 my %params = @_;
66 $params{uc $_} = delete $params{$_} for keys %params;
67 @{$self->{PARAM_MAP}}{keys %params} = @params{keys %params};
68
b6bc5a5d 69 return !!1;
d0eafc11 70}
71
72sub write_file
73{
74 my $self = shift;
75 my ($filename) = @_;
76
77 my $xls = $self->{RENDERER}->new($filename)
78 || die "Cannot create XLS in '$filename': $!\n";
79
80 $self->_prepare_output($xls);
81
82 $xls->close;
83
b6bc5a5d 84 return !!1;
d0eafc11 85}
86
87sub output
88{
89 my $self = shift;
90
91 my $output;
92 tie *XLS, 'IO::Scalar', \$output;
93
94 $self->write_file(\*XLS);
95
96 return $output;
97}
98
d0eafc11 99sub parse_xml
100{
101 my $self = shift;
102 my ($fname) = @_;
103
104 my ($filename, $dirname) = fileparse($fname);
105
106 my @stack;
107 my $parser = XML::Parser->new(
108 Base => $dirname,
109 Handlers => {
110 Start => sub {
111 shift;
112
113 my $name = uc shift;
114
115 my $node = Excel::Template::Factory->create_node($name, @_);
116 die "'$name' (@_) didn't make a node!\n" unless defined $node;
117
37513eae 118 if ( $node->isa( 'WORKBOOK' ) )
d0eafc11 119 {
120 push @{$self->{WORKBOOKS}}, $node;
121 }
37513eae 122 elsif ( $node->is_embedded )
d0eafc11 123 {
124 return unless @stack;
125
126 if (exists $stack[-1]{TXTOBJ} &&
127 $stack[-1]{TXTOBJ}->isa('TEXTOBJECT'))
128 {
129 push @{$stack[-1]{TXTOBJ}{STACK}}, $node;
130 }
131
132 }
133 else
134 {
135 push @{$stack[-1]{ELEMENTS}}, $node
136 if @stack;
137 }
138 push @stack, $node;
139 },
140 Char => sub {
141 shift;
142 return unless @stack;
143
144 my $parent = $stack[-1];
145
146 if (
147 exists $parent->{TXTOBJ}
148 &&
149 $parent->{TXTOBJ}->isa('TEXTOBJECT')
150 ) {
151 push @{$parent->{TXTOBJ}{STACK}}, @_;
152 }
153 },
154 End => sub {
155 shift;
156 return unless @stack;
157
158 pop @stack if $stack[-1]->isa(uc $_[0]);
159 },
160 },
161 );
162
163 {
b6bc5a5d 164 open( INFILE, "<$fname" )
d0eafc11 165 || die "Cannot open '$fname' for reading: $!\n";
166
b6bc5a5d 167 $parser->parse(do { local $/ = undef; <INFILE> });
d0eafc11 168
b6bc5a5d 169 close INFILE;
d0eafc11 170 }
171
b6bc5a5d 172 return !!1;
d0eafc11 173}
b6bc5a5d 174*parse = \&parse_xml;
d0eafc11 175
176sub _prepare_output
177{
178 my $self = shift;
179 my ($xls) = @_;
180
181 my $context = Excel::Template::Factory->create(
182 'CONTEXT',
183
184 XLS => $xls,
185 PARAM_MAP => [ $self->{PARAM_MAP} ],
186 );
187
188 $_->render($context) for @{$self->{WORKBOOKS}};
189
b6bc5a5d 190 return !!1;
d0eafc11 191}
192
193sub register { shift; Excel::Template::Factory::register(@_) }
194
1951;
196__END__
197
198=head1 NAME
199
200Excel::Template - Excel::Template
201
202=head1 SYNOPSIS
203
204First, make a template. This is an XML file, describing the layout of the
205spreadsheet.
206
207For example, test.xml:
208
209 <workbook>
210 <worksheet name="tester">
211 <cell text="$HOME"/>
212 <cell text="$PATH"/>
213 </worksheet>
214 </workbook>
215
216Now, create a small program to use it:
217
218 #!/usr/bin/perl -w
219 use Excel::Template
220
221 # Create the Excel template
222 my $template = Excel::Template->new(
223 filename => 'test.xml',
224 );
225
226 # Add a few parameters
227 $template->param(
228 HOME => $ENV{HOME},
229 PATH => $ENV{PATH},
230 );
231
232 $template->write_file('test.xls');
233
234If everything worked, then you should have a spreadsheet in your work directory
235that looks something like:
236
237 A B C
238 +----------------+----------------+----------------
239 1 | /home/me | /bin:/usr/bin |
240 +----------------+----------------+----------------
241 2 | | |
242 +----------------+----------------+----------------
243 3 | | |
244
245=head1 DESCRIPTION
246
247This is a module used for templating Excel files. Its genesis came from the
248need to use the same datastructure as HTML::Template, but provide Excel files
249instead. The existing modules don't do the trick, as they require replication
250of logic that's already been done within HTML::Template.
251
252Currently, only a small subset of the planned features are supported. This is
253meant to be a test of the waters, to see what features people actually want.
254
255=head1 MOTIVATION
256
257I do a lot of Perl/CGI for reporting purposes. In nearly every place I've been,
258I've been asked for HTML, PDF, and Excel. HTML::Template provides the first, and
259PDF::Template does the second pretty well. But, generating Excel was the
260sticking point. I already had the data structure for the other templating
261modules, but I just didn't have an easy mechanism to get that data structure
262into an XLS file.
263
264=head1 USAGE
265
266=head2 new()
267
268This creates a Excel::Template object. If passed a filename parameter, it will
269parse the template in the given file. (You can also use the parse() method,
270described below.)
271
272=head2 param()
273
274This method is exactly like HTML::Template's param() method. Although I will
275be adding more to this section later, please see HTML::Template's description
276for info right now.
277
278=head2 parse() / parse_xml()
279
280This method actually parses the template file. It can either be called
281separately or through the new() call. It will die() if it runs into a situation
282it cannot handle.
283
284=head2 write_file()
285
286Create the Excel file and write it to the specified filename, if possible. (This
287is when the actual merging of the template and the parameters occurs.)
288
289=head2 output()
290
291It will act just like HTML::Template's output() method, returning the resultant
292file as a stream, usually for output to the web. (This is when the actual
293merging of the template and the parameters occurs.)
294
295=head1 SUPPORTED NODES
296
297This is just a list of nodes. See the other classes in this distro for more
298details on specific parameters and the like.
299
300Every node can set the ROW and COL parameters. These are the actual ROW/COL
301values that the next CELL tag will write into.
302
303=over 4
304
305=item * WORKBOOK
306
307=item * WORKSHEET
308
309=item * IF
310
311=item * LOOP
312
313=item * ROW
314
315=item * CELL
316
317=item * FORMULA
318
319=item * BOLD
320
321=item * ITALIC
322
323=back 4
324
325=head1 BUGS
326
327None, that I know of.
328
329=head1 SUPPORT
330
331This is currently beta-quality software. The featureset is extremely limited,
332but I expect to be adding on to it very soon.
333
334=head1 AUTHOR
335
336 Rob Kinyon
a8441e01 337 rob.kinyon@gmail.com
338
339=head1 CONTRIBUTORS
340
341There is a mailing list at http://groups-beta.google.com/group/ExcelTemplate
d0eafc11 342
37513eae 343Robert Graff -
344
345=over 4
346
347=item * Finishing formats
348
349=item * Fixing several bugs in worksheet naming
350
351=back 4
352
d0eafc11 353=head1 COPYRIGHT
354
355This program is free software; you can redistribute
356it and/or modify it under the same terms as Perl itself.
357
358The full text of the license can be found in the
359LICENSE file included with this module.
360
361=head1 SEE ALSO
362
363perl(1), HTML::Template, Spreadsheet::WriteExcel.
364
365=cut