File Coverage

blib/lib/SimpleXlsx.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package SimpleXlsx;
2              
3 1     1   31303 use strict;
  1         2  
  1         45  
4 1     1   7 use warnings;
  1         2  
  1         40  
5 1     1   1224 use Archive::Zip qw( :ERROR_CODES );
  1         220857  
  1         928  
6 1     1   505 use XML::Simple;
  0            
  0            
7             use File::Basename;
8             use Data::Dumper;
9              
10             require Exporter;
11              
12             our @ISA = qw(Exporter);
13              
14             # Items to export into callers namespace by default. Note: do not export
15             # names by default without a very good reason. Use EXPORT_OK instead.
16             # Do not simply export all your public functions/methods/constants.
17              
18             our @EXPORT_OK = ( 'parse' );
19              
20             our $VERSION = '0.05';
21              
22             our $zip;
23              
24             # Preloaded methods go here.
25              
26             sub new {
27             my $package = shift;
28            
29             $zip = Archive::Zip->new();
30             return bless({}, $package);
31             }
32              
33             sub getValues {
34             my(@zStrings) = $zip->membersMatching('^xl/sharedStrings');
35            
36             if ($#zStrings > 0) {
37             warn "Error: Multiple shared strings are not [yet] supported\n";
38             }
39            
40             my($xml) = new XML::Simple;
41             my($sstrings) = $zStrings[0];
42             $sstrings = $sstrings->contents();
43             my($tstrings) = $xml->XMLin($sstrings);
44            
45             my(@strings);
46             for my $idx (0 .. $#{$tstrings->{'si'}}) {
47             push @strings, $tstrings->{'si'}->[$idx]->{'t'};
48             }
49            
50             return @strings;
51             }
52              
53             sub getWorksheets {
54             return $zip->membersMatching('^xl/worksheets');
55             }
56              
57             sub getStyles {
58             my(@zStyles) = $zip->membersMatching('^xl/styles');
59             my($data) = $zStyles[0]->contents();
60            
61             my($xml) = new XML::Simple;
62             $data = $xml->XMLin($data);
63            
64             my(@cellFormats);
65             my(%fonts);
66             my(%borders);
67            
68             my($xcellFormats) = $data->{'cellXfs'}->{'xf'};
69             my($xfonts) = $data->{'fonts'}->{'font'};
70             my($xborders) = $data->{'borders'}->{'border'};
71            
72             my($idx) = 0;
73             if (ref($xfonts)) {
74             for my $ind (0 .. $#{$xfonts}) {
75             $fonts{$idx} = {
76             'Name' => $xfonts->[$ind]->{'name'}->{'val'},
77             'Size' => $xfonts->[$ind]->{'sz'}->{'val'},
78             'Bold' => defined $xfonts->[$ind]->{'b'} ? '1' : '0'
79             };
80            
81             $idx++;
82             }
83             }
84            
85             $idx = 0;
86             for my $ind (0 .. $#{$xborders}) {
87             $borders{$idx} = {
88             'Left' => {
89             'Color' => defined $xborders->[$ind]->{'left'}->{'color'}->{'indexed'} ? $xborders->[$ind]->{'left'}->{'color'}->{'indexed'} : '',
90             'Style' => defined $xborders->[$ind]->{'left'}->{'style'} ? $xborders->[$ind]->{'left'}->{'style'} : ''
91             },
92             'Right' => {
93             'Color' => defined $xborders->[$ind]->{'right'}->{'color'}->{'indexed'} ? $xborders->[$ind]->{'right'}->{'color'}->{'indexed'} : '',
94             'Style' => defined $xborders->[$ind]->{'right'}->{'style'} ? $xborders->[$ind]->{'right'}->{'style'} : ''
95             },
96             'Top' => {
97             'Color' => defined $xborders->[$ind]->{'top'}->{'color'}->{'indexed'} ? $xborders->[$ind]->{'top'}->{'color'}->{'indexed'} : '',
98             'Style' => defined $xborders->[$ind]->{'top'}->{'style'} ? $xborders->[$ind]->{'left'}->{'top'} : ''
99             },
100             'Bottom' => {
101             'Color' => defined $xborders->[$ind]->{'bottom'}->{'color'}->{'indexed'} ? $xborders->[$ind]->{'bottom'}->{'color'}->{'indexed'} : '',
102             'Style' => defined $xborders->[$ind]->{'bottom'}->{'style'} ? $xborders->[$ind]->{'bottom'}->{'style'} : ''
103             },
104             'Diagonal' => {
105             'Color' => defined $xborders->[$ind]->{'diagonal'}->{'color'}->{'indexed'} ? $xborders->[$ind]->{'diagonal'}->{'color'}->{'indexed'} : '',
106             'Style' => defined $xborders->[$ind]->{'diagonal'}->{'style'} ? $xborders->[$ind]->{'diagonal'}->{'style'} : ''
107             }
108             };
109            
110             $idx++;
111             }
112            
113             $idx = 0;
114             for my $ind (0 .. $#{$xcellFormats}) {
115             my($bix) = $xcellFormats->[$ind]->{'borderId'};
116             push @cellFormats, {
117             'fillId' => $xcellFormats->[$ind]->{'fillId'},
118             'Font' => $fonts{$xcellFormats->[$ind]->{'fontId'}},
119             'xfId' => $xcellFormats->[$ind]->{'xfId'},
120             'numFmtId' => $xcellFormats->[$ind]->{'numFmtId'},
121             'Border' => $borders{$bix}
122             };
123            
124             $idx++;
125             }
126            
127             return \@cellFormats;
128             }
129              
130             sub parse {
131             my($self, $file) = @_;
132            
133             my($ret) = $zip->read($file);
134             unless ($ret == AZ_OK) {
135             warn "Unable to read file \"$file\" ($!)\n";
136             return undef;
137             }
138            
139             # For now we are only interested in worksheets and the shared strings
140             my(@zWorksheets) = $self->getWorksheets();
141             my(@strings) = $self->getValues();
142             my($styles) = $self->getStyles();
143             my(%worksheets);
144             my(@sheetNames);
145            
146             $worksheets{'Worksheets'} = [];
147            
148             $worksheets{'Total Worksheets'} = ($#zWorksheets + 1);
149             for my $file (@zWorksheets) {
150             my(%worksheet);
151             my($contents) = $file->contents();
152             my($name) = basename($file->fileName());
153             $name =~ s/\.xml$//;
154            
155             my($xml) = new XML::Simple;
156             my($data) = $xml->XMLin($contents);
157            
158             my($sData) = $data->{'sheetData'}->{'row'};
159             my($sMerge) = $data->{'mergeCells'}->{'mergeCell'};
160            
161             my(%merge);
162             for my $mc (@{$sMerge}) {
163             my($from, $to) = split(':', $mc->{'ref'});
164            
165             $from =~ /([a-zA-Z]+)([0-9]+)/;
166             my($col1, $row1) = ($1, $2);
167            
168             $to =~ /([a-zA-Z]+)([0-9]+)/;
169             my($col2, $row2) = ($1, $2);
170            
171             $merge{$row1} = {
172             'From' => { 'Row' => $row1, 'Column' => $col1 },
173             'To' => { 'Row' => $row2, 'Column' => $col2 }
174             };
175             }
176            
177             my(@tcol);
178             for my $col (0 .. $#{$sData->[0]->{'c'}}) {
179             push @tcol, $sData->[0]->{'c'}->[$col]->{'r'};
180             }
181             $worksheet{'Columns'} = \@tcol;
182            
183             my(@trow);
184             my(%tdata);
185             for my $row (0 .. $#{$sData}) {
186             my($cols) = $sData->[$row]->{'c'};
187            
188             my(@rdata);
189             my(@sdata);
190             for my $col (0 .. $#{$cols}) {
191             if (!defined $cols->[$col]->{'v'}) {
192             push @rdata, '';
193             }
194             else {
195             if (defined $cols->[$col]->{'t'}) {
196             push @rdata, ($cols->[$col]->{'t'} eq 's' ? $strings[$cols->[$col]->{'v'}] : $cols->[$col]->{'v'});
197             }
198             }
199              
200             if (defined $styles->[$cols->[$col]->{'s'}]) {
201             push @sdata, $styles->[$cols->[$col]->{'s'}];
202             }
203             }
204              
205             if (defined $sData->[$row]->{'r'}) {
206             push @trow, $sData->[$row]->{'r'};
207             $tdata{$sData->[$row]->{'r'}}{'Data'} = \@rdata;
208             $tdata{$sData->[$row]->{'r'}}{'Style'} = \@sdata;
209             }
210             }
211            
212             $worksheet{'Rows'} = \@trow;
213             $worksheet{'Data'} = \%tdata;
214             $worksheet{'Merge'} = \%merge;
215            
216             $worksheets{$name} = \%worksheet;
217            
218             push @sheetNames, $name;
219             }
220            
221             $worksheets{'Worksheets'} = \@sheetNames;
222            
223             return \%worksheets;
224             }
225              
226             1;
227             __END__