File Coverage

blib/lib/OpenOffice/OOCBuilder.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package OpenOffice::OOCBuilder;
2              
3             # Copyright 2004, 2007 Stefan Loones
4             # More info can be found at http://www.maygill.com/oobuilder
5             #
6             # This library is free software; you can redistribute it and/or
7             # modify it under the same terms as Perl itself.
8              
9 2     2   5249 use 5.008; # lower versions not tested
  2         6  
  2         67  
10 2     2   9 use strict;
  2         4  
  2         53  
11 2     2   10 use warnings;
  2         3  
  2         65  
12 2     2   9 no warnings 'uninitialized'; # don't want this, because we use strict
  2         4  
  2         60  
13 2     2   591 use OpenOffice::OOBuilder;
  0            
  0            
14             our (@ISA);
15             @ISA=qw(OpenOffice::OOBuilder);
16              
17             my $VERSION=sprintf("%d.%02d", q$Revision: 0.9 $ =~ /(\d+)\.(\d+)/);
18              
19             my ($MAXC, $MAXR, $MAXSHEETS, @TYPES);
20             $MAXC=256; # is column IV
21             $MAXR=32000;
22             $MAXSHEETS=64;
23             # - possible types ($TYPES[0] is default type)
24             @TYPES=('standard', 'text', 'float', 'formula');
25              
26             # TODO push & pop cell locations (incl sheetnb) - to make formulas easier to construct
27             # create tags for cell locations
28             # cell-format ? (seems with numeric styles, not possible in cell directly)
29              
30             # - Object constructor
31             #
32             sub new {
33             my ($class, $self);
34             $class=shift;
35             $self=$class->SUPER::new('sxc');
36              
37             # - active data
38             $self->{actsheet}=1;
39             $self->{act}{1}{c}=1; # {act}{sheetnb}{c}=
40             $self->{act}{1}{r}=1;
41              
42             # - general data (parameters)
43             $self->{cpars}{sheets}=1;
44             $self->{cpars}{autoc}=0;
45             $self->{cpars}{autor}=0;
46              
47             # - data
48             $self->{cdata} = undef; # {cdata}{sheetnb}{}{}
49             $self->{sheetname}= undef; # {sheetname}{sheetnb}=name
50             $self->{cstyle} = undef; # {cstyle}{sheetnb}{}{}
51             $self->{colwidth} = undef; # {colwidth}{sheetnb}{c}
52             $self->{rowheight}= undef; # {rowheight}{sheetnb}{r}
53              
54             # - defaults (specific ooc - see other defaults in parent class oooBuilder.pm)
55             $self->{defcolwidth} = '0.8925inch';
56             # ** $self->{defrowheight} = '0.8925inch';
57              
58             return $self;
59             } # - - End new (Object constructor)
60              
61              
62             sub add_sheet {
63             my ($self);
64             $self=shift;
65             if ($self->{cpars}{sheets}<$MAXSHEETS) {
66             ++$self->{cpars}{sheets};
67             }
68             1;
69             }
70              
71             sub goto_sheet {
72             my ($self, $sheet)=@_;
73             if ($sheet > $self->{cpars}{sheets}) {
74             $self->{actsheet}=$self->{cpars}{sheets};
75             } elsif ($sheet < 1) {
76             $self->{actsheet}=1;
77             } else {
78             $self->{actsheet}=$sheet;
79             }
80             1;
81             }
82              
83             sub set_sheet_name {
84             my ($self, $name, $sheet)=@_;
85             # TODO process name: check valid characters and length ?!
86             if ($name) {
87             $sheet=$self->{actsheet} if (! $sheet);
88             if ($sheet>0 && $sheet <=$self->{cpars}{sheets}) {
89             $self->{sheetname}{$sheet}=$name;
90             }
91             }
92             1;
93             }
94              
95             sub set_colwidth {
96             my ($self, $c, $width)=@_;
97             $c=$self->_check_column ($c);
98             # TODO do we need to check $width ?
99             $self->{colwidth}{$self->{actsheet}}{$c}=$width;
100             1;
101             }
102              
103             sub set_rowheight {
104             my ($self, $r, $height)=@_;
105             $r=$self->_check_row ($r);
106             $self->{rowheight}{$self->{actsheet}}{$r}=$height;
107             1;
108             }
109              
110             sub goto_xy {
111             my ($self, $c, $r)=@_;
112             $c=$self->_check_column ($c);
113             $self->{act}{$self->{actsheet}}{c}=$c;
114             $r=$self->_check_row ($r);
115             $self->{act}{$self->{actsheet}}{r}=$r;
116             1;
117             }
118              
119             sub goto_cell {
120             my ($self, $cell)=@_;
121             $cell=uc($cell);
122             $cell=~ s/^([A-Z]+)([0-9]+)/$1$2/;
123             $self->goto_xy ($1, $2);
124             1;
125             }
126              
127             sub get_column {
128             my $self=shift;
129             return $self->_convert_column ($self->{act}{$self->{actsheet}}{c});
130             }
131              
132             sub get_x {
133             my $self=shift;
134             return $self->{act}{$self->{actsheet}}{c};
135             }
136              
137             sub get_row {
138             my $self=shift;
139             return $self->{act}{$self->{actsheet}}{r};
140             }
141              
142             sub get_y {
143             my $self=shift;
144             return $self->{act}{$self->{actsheet}}{r};
145             }
146              
147             sub get_xy {
148             my $self=shift;
149             return ($self->{act}{$self->{actsheet}}{c}, $self->{act}{$self->{actsheet}}{r});
150             }
151              
152             sub get_cell_id {
153             my $self=shift;
154             my $cell=$self->_convert_column ($self->{act}{$self->{actsheet}}{c});
155             return $cell . $self->{act}{$self->{actsheet}}{r};
156             }
157              
158             # - PublicMethod: set_data : set_data in active sheet/cell, with active style
159             # API: set_data ($data, $type, $format)
160             # $type && $format can be ommitted
161             #
162             sub set_data {
163             my ($self, $data, $type, $format)=@_;
164             return $self->set_data_sheet_xy($self->{actsheet},
165             $self->{act}{$self->{actsheet}}{c},
166             $self->{act}{$self->{actsheet}}{r},
167             $data, $type, $format);
168             }
169              
170             sub set_data_xy {
171             my ($self, $c, $r, $data, $type, $format)=@_;
172             return $self->set_data_sheet_xy($self->{actsheet}, $c, $r, $data, $type, $format);
173             }
174              
175             sub set_data_sheet_xy {
176             my ($self, $sheet, $c, $r, $data, $type, $format)=@_;
177              
178             # - check sheet
179             if ($sheet != $self->{actsheet}) {
180             $self->goto_sheet ($sheet);
181             $sheet=$self->{actsheet};
182             }
183              
184             # - check cell
185             if ($c ne $self->{act}{$sheet}{c} || $r != $self->{act}{$sheet}{r}) {
186             $self->goto_xy ($c, $r);
187             $c=$self->{act}{$sheet}{c};
188             $r=$self->{act}{$sheet}{r};
189             }
190              
191             # - check type
192             my ($ok);
193             if ($type) {
194             $type=lc($type);
195             foreach (@TYPES) {
196             if ($type eq $_) {
197             $ok=1;
198             last;
199             }
200             }
201             }
202             $type=$TYPES[0] if (! $ok); # take $TYPES[0] as default type
203              
204             # - check format
205             # TODO
206              
207             # - check data
208             $data=$self->encode_data ($data) if ($data);
209              
210             # - store (ATTENTION $r before $c because of the way we need to generate xml)
211             $self->{cdata}{$sheet}{$r}{$c}{type}=$type;
212             $self->{cdata}{$sheet}{$r}{$c}{format}=$format if ($format);
213             $self->{cdata}{$sheet}{$r}{$c}{data}=$data;
214             $self->{cdata}{$sheet}{$r}{$c}{style}=$self->{actstyle};
215             $self->cell_update if ($self->{cpars}{autoc} || $self->{cpars}{autor});
216             1;
217             } # - - End set_data_sheet_xy
218              
219             sub set_auto_xy {
220             my ($self, $c, $r)=@_;
221             $self->{cpars}{autoc}=$c;
222             $self->{cpars}{autor}=$r;
223             1;
224             }
225              
226             sub get_auto_x {
227             my $self=shift;
228             return $self->{cpars}{autoc};
229             }
230              
231             sub get_auto_y {
232             my $self=shift;
233             return $self->{cpars}{autor};
234             }
235              
236             sub cell_update {
237             my $self=shift;
238             if ($self->{cpars}{autoc}) {
239             if ($self->{cpars}{autoc}>0) {
240             $self->move_cell('right',$self->{cpars}{autoc});
241             } else {
242             $self->move_cell('left',abs($self->{cpars}{autoc}));
243             }
244             }
245             if ($self->{cpars}{autor}) {
246             if ($self->{cpars}{autor}>0) {
247             $self->move_cell('down',$self->{cpars}{autor});
248             } else {
249             $self->move_cell('up',abs($self->{cpars}{autor}));
250             }
251             }
252             1;
253             }
254              
255             sub move_cell {
256             my ($self, $direction, $number)=@_;
257             $number=1 if (! $number);
258             $direction=lc($direction);
259             if ($direction eq 'left') {
260             $self->{act}{$self->{actsheet}}{c}-=$number;
261             } elsif ($direction eq 'right') {
262             $self->{act}{$self->{actsheet}}{c}+=$number;
263             } elsif ($direction eq 'down') {
264             $self->{act}{$self->{actsheet}}{r}+=$number;
265             } elsif ($direction eq 'up') {
266             $self->{act}{$self->{actsheet}}{r}-=$number;
267             } else {
268             # TODO direction unknown
269              
270             }
271             $self->_cell_check;
272             1;
273             }
274              
275             # - generate ooc specific, then call parent to complete generation
276             sub generate {
277             my ($self, $tgtfile)=@_;
278              
279             my ($subGetMaxRange);
280             $subGetMaxRange=sub {
281             my ($hr, $max, @keys);
282             $hr=shift;
283             @keys=sort {$a <=> $b} (keys(%$hr));
284             return (pop(@keys));
285             };
286              
287             # - Build content.xml
288             $self->{contentxml}=q{
289            
290            
291            
292             };
293              
294             # Styles will be done later, because they depend on the content
295              
296             # TODO $self->{rowheight}{$self->{actsheet}}{c}=$height; still to implement
297              
298              
299             # Beginning of document content
300             my ($content);
301             $content=q{};
302              
303             my ($sheet, $sheetname, $c, $columns, $r, $rows, $type, $format, $data);
304             my ($style, $stylexml);
305             my (%cellstyleids, $cellmaxid, %cellstylexml);
306             my (%colstyleids, $colmaxid, %colstylexml, $colwidth);
307             my ($colid, $prevcolid, $width, $t);
308             my (%rowstyleids, $rowmaxid, %rowstylexml);
309             $cellmaxid=0;
310             $colmaxid=$rowmaxid=1;
311             $colstyleids{$self->{defcolwidth}}='co1';
312              
313             for (1 .. $self->{cpars}{sheets}) {
314             $sheet=$_;
315             if ($self->{sheetname}{$sheet}) {
316             $sheetname=$self->{sheetname}{$sheet};
317             } else {
318             $sheetname="Sheet$sheet";
319             }
320             $content.=qq{};
321             foreach $c (sort {$a <=> $b} keys(%{$self->{colwidth}{$sheet}})) {
322             $width=$self->{colwidth}{$sheet}{$c};
323             if (! $colstyleids{$width} && $width) {
324             ++$colmaxid;
325             $colstyleids{$width}=qq{co$colmaxid};
326             }
327             }
328             if ($self->{colwidth}{$sheet}{1}) {
329             $prevcolid=$colstyleids{$self->{colwidth}{$sheet}{1}};
330             } else {
331             $prevcolid='co1';
332             }
333             $t=1;
334             for ($c=2;$c<=256;++$c) {
335             if ($self->{colwidth}{$sheet}{$c}) {
336             $colid=$colstyleids{$self->{colwidth}{$sheet}{$c}};
337             } else {
338             $colid='co1';
339             }
340             if ($colid eq $prevcolid) {
341             ++$t;
342             } else {
343             if ($t>1) {
344             $content.=qq{};
345             $t=1;
346             } else {
347             $content.=qq{};
348             }
349             $prevcolid=$colid
350             }
351             }
352             if ($t>1) {
353             $content.=qq{};
354             } else {
355             $content.=qq{};
356             }
357             $rows=&$subGetMaxRange ($self->{cdata}{$sheet});
358             for (1 .. $rows) {
359             $r=$_;
360             # TODO row style ?
361             $content.=q{};
362             $columns=&$subGetMaxRange ($self->{cdata}{$sheet}{$r});
363             for (1 .. $columns) {
364             $c=$_;
365             $type=$self->{cdata}{$sheet}{$r}{$c}{type};
366             $format=$self->{cdata}{$sheet}{$r}{$c}{format};
367             $data=$self->{cdata}{$sheet}{$r}{$c}{data};
368             $style=$self->{cdata}{$sheet}{$r}{$c}{style};
369             if ($style eq $self->{defstyle} || ! $style) {
370             $stylexml='';
371             } else {
372             if (! exists($cellstyleids{$style})) {
373             ++$cellmaxid;
374             $cellstyleids{$style}=qq{ce$cellmaxid};
375             $cellstylexml{$cellstyleids{$style}}=qq{ table:style-name="$cellstyleids{$style}"};
376             }
377             $stylexml=$cellstylexml{$cellstyleids{$style}};
378             }
379             if ($type eq 'standard' || $type eq 'text') {
380             $content.=qq{$data};
381             } elsif ($type eq 'float') {
382             $content.=
383             qq{
384             $data};
385             } elsif ($type eq 'formula') {
386             $content.=
387             qq{
388             };
389             } elsif ($type eq 'others') {
390             # TODO
391             } else {
392             $content.=q{};
393             }
394             }
395             $content.=q{};
396             }
397             $content.=q{};
398             }
399              
400             # - Process used fonts and used cell styles
401             my ($bold, $italic, $underline, $align, $txtcolor, $bgcolor, $font, $size);
402             my ($defbold, $defitalic, $defunderline, $defalign, $deftxtcolor, $defbgcolor);
403             my ($deffont, $defsize, %usedfonts, $xml, %stylexml);
404             ($defbold, $defitalic, $defunderline, $defalign, $deftxtcolor, $defbgcolor,
405             $deffont, $defsize)=split(/#/, $self->{defstyle});
406             foreach $style (keys(%cellstyleids)) {
407             ($bold, $italic, $underline, $align, $txtcolor, $bgcolor, $font, $size)=
408             split(/#/, $style);
409             $xml=
410             qq{
411            
412             if ($bgcolor ne $defbgcolor) {
413             $xml.=qq{ fo:background-color="#$bgcolor"};
414             }
415             if ($align ne $defalign) {
416             $align='end' if ($align eq 'right');
417             $xml.=qq{ fo:text-align="$align" style:text-align-source="fix" fo:margin-left="0inch"};
418             }
419             if ($txtcolor ne $deftxtcolor) {
420             $xml.=qq{ fo:color="#$txtcolor"};
421             }
422             if ($font ne $deffont) {
423             $usedfonts{$font}=1;
424             $xml.=qq{ style:font-name="$font"};
425             }
426             if ($size ne $defsize) {
427             $xml.=q{ fo:font-size="} . $size . q{pt"}
428             }
429             if ($italic ne $defitalic) {
430             if ($italic) {
431             $xml.=q{ fo:font-style="italic"};
432             } else {
433             $xml.=q{ fo:font-style="normal"};
434             }
435             }
436             if ($underline ne $defunderline) {
437             if ($underline) {
438             $xml.=q{ style:text-underline="single" style:text-underline-color="font-color"};
439             } else {
440             $xml.=q{ style:text-underline="normal"};
441             }
442             }
443             if ($bold ne $defbold) {
444             if ($bold) {
445             $xml.=q{ fo:font-weight="bold"};
446             } else {
447             $xml.=q{ fo:font-weight="normal"};
448             }
449             }
450             $xml.=q{/>};
451             $stylexml{$cellstyleids{$style}}=$xml;
452             }
453              
454             # - Fonts
455             $usedfonts{$deffont}=1;
456             $self->{contentxml}.=q{};
457             foreach $font (sort(keys(%usedfonts))) {
458             $self->{contentxml}.=$self->{availfonts}{$font};
459             }
460             $self->{contentxml}.=q{};
461              
462             # - col styles
463             $self->{contentxml}.=qq{};
464             foreach $width (keys(%colstyleids)) {
465             $colstylexml{$colstyleids{$width}}=
466             qq{
467             };
468             }
469             foreach $colid (sort(keys(%colstylexml))) {
470             $self->{contentxml}.=$colstylexml{$colid};
471             }
472              
473             # TODO look at row styles ?
474             # qq{
475             #
476             #
477             #
478             # };
479              
480             # - cell styles
481             foreach $style (sort(keys(%stylexml))) {
482             $self->{contentxml}.=$stylexml{$style};
483             }
484             $self->{contentxml}.=qq{$content};
485              
486             $self->SUPER::generate ($tgtfile);
487             1;
488             }
489              
490             # - * - PrivateMethods
491              
492             sub _check_column {
493             my ($self, $c)=@_;
494             if ($c =~ /[A-Za-z]/) {
495             # - convert to number
496             my (@char, $char, $multi, $newx);
497             $c=~ s/[^A-Za-z]//g; # we don't want anything else when using letters
498             @char=split(//,uc($c));
499             $multi=1;
500             $newx=0;
501             while (@char) {
502             $char=pop(@char);
503             $newx+=$multi*(ord($char)-64);
504             $multi*=26;
505             }
506             $c=$newx;
507             }
508             $c=1 if ($c<1);
509             $c=$MAXC if ($c>$MAXC);
510             return $c;
511             }
512              
513             sub _convert_column {
514             my ($self, $col)=@_;
515             my $cell;
516             while ($col>26) {
517             my $div=int($col/26);
518             $cell.=chr($div+64);
519             $col-=$div*26;
520             }
521             $cell.=chr($col+64) if ($col>0);
522             return $cell;
523             }
524              
525             sub _check_row {
526             my ($self, $r)=@_;
527             $r=1 if ($r<1);
528             $r=$MAXR if ($r>$MAXR);
529             return $r;
530             }
531              
532             sub _cell_check {
533             my ($self);
534             $self=shift;
535              
536             $self->{actsheet}=1 if ($self->{actsheet}<1);
537             $self->{actsheet}=$MAXSHEETS if ($self->{actsheet}>$MAXSHEETS);
538             my $sheet=$self->{actsheet}; # only for readability
539             $self->{act}{$sheet}{c}=1 if ($self->{act}{$sheet}{c}<1);
540             $self->{act}{$sheet}{r}=1 if ($self->{act}{$sheet}{r}<1);
541             $self->{act}{$sheet}{c}=$MAXC if ($self->{act}{$sheet}{c}>$MAXC);
542             $self->{act}{$sheet}{r}=$MAXR if ($self->{act}{$sheet}{r}>$MAXR);
543             1;
544             }
545              
546             1;
547              
548             __END__