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