File Coverage

blib/lib/Excel/Writer/XLSX/Package/Styles.pm
Criterion Covered Total %
statement 378 380 99.4
branch 138 142 97.1
condition 24 24 100.0
subroutine 36 36 100.0
pod 0 1 0.0
total 576 583 98.8


line stmt bran cond sub pod time code
1             package Excel::Writer::XLSX::Package::Styles;
2              
3             ###############################################################################
4             #
5             # Styles - A class for writing the Excel XLSX styles file.
6             #
7             # Used in conjunction with Excel::Writer::XLSX
8             #
9             # Copyright 2000-2020, John McNamara, jmcnamara@cpan.org
10             #
11             # Documentation after __END__
12             #
13              
14             # perltidy with the following options: -mbl=2 -pt=0 -nola
15              
16 1081     1081   17152 use 5.008002;
  1081         4679  
17 1081     1081   6483 use strict;
  1081         4398  
  1081         23681  
18 1081     1081   6425 use warnings;
  1081         3395  
  1081         31179  
19 1081     1081   6774 use Carp;
  1081         3531  
  1081         57252  
20 1081     1081   8319 use Excel::Writer::XLSX::Package::XMLwriter;
  1081         9742  
  1081         3533068  
21              
22             our @ISA = qw(Excel::Writer::XLSX::Package::XMLwriter);
23             our $VERSION = '1.07';
24              
25              
26             ###############################################################################
27             #
28             # Public and private API methods.
29             #
30             ###############################################################################
31              
32              
33             ###############################################################################
34             #
35             # new()
36             #
37             # Constructor.
38             #
39             sub new {
40              
41 933     933 0 19776 my $class = shift;
42 933         1928 my $fh = shift;
43 933         3483 my $self = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
44              
45 933         2803 $self->{_xf_formats} = undef;
46 933         2667 $self->{_palette} = [];
47 933         2266 $self->{_font_count} = 0;
48 933         2408 $self->{_num_format_count} = 0;
49 933         2098 $self->{_border_count} = 0;
50 933         2183 $self->{_fill_count} = 0;
51 933         2996 $self->{_custom_colors} = [];
52 933         2490 $self->{_dxf_formats} = [];
53 933         2158 $self->{_has_hyperlink} = 0;
54 933         2085 $self->{_hyperlink_font_id} = 0;
55 933         2032 $self->{_has_comments} = 0;
56              
57 933         2033 bless $self, $class;
58              
59 933         2486 return $self;
60             }
61              
62              
63             ###############################################################################
64             #
65             # _assemble_xml_file()
66             #
67             # Assemble and write the XML file.
68             #
69             sub _assemble_xml_file {
70              
71 858     858   2480 my $self = shift;
72              
73 858         7701 $self->xml_declaration;
74              
75             # Add the style sheet.
76 858         5244 $self->_write_style_sheet();
77              
78             # Write the number formats.
79 858         4178 $self->_write_num_fmts();
80              
81             # Write the fonts.
82 858         4235 $self->_write_fonts();
83              
84             # Write the fills.
85 858         4047 $self->_write_fills();
86              
87             # Write the borders element.
88 858         4566 $self->_write_borders();
89              
90             # Write the cellStyleXfs element.
91 858         4047 $self->_write_cell_style_xfs();
92              
93             # Write the cellXfs element.
94 858         3976 $self->_write_cell_xfs();
95              
96             # Write the cellStyles element.
97 858         4077 $self->_write_cell_styles();
98              
99             # Write the dxfs element.
100 858         3819 $self->_write_dxfs();
101              
102             # Write the tableStyles element.
103 858         4682 $self->_write_table_styles();
104              
105             # Write the colors element.
106 858         3601 $self->_write_colors();
107              
108             # Close the style sheet tag.
109 858         3545 $self->xml_end_tag( 'styleSheet' );
110              
111             # Close the XML writer filehandle.
112 858         5728 $self->xml_get_fh()->close();
113             }
114              
115              
116             ###############################################################################
117             #
118             # _set_style_properties()
119             #
120             # Pass in the Format objects and other properties used to set the styles.
121             #
122             sub _set_style_properties {
123              
124 864     864   2436 my $self = shift;
125              
126 864         5555 $self->{_xf_formats} = shift;
127 864         2435 $self->{_palette} = shift;
128 864         2097 $self->{_font_count} = shift;
129 864         2207 $self->{_num_format_count} = shift;
130 864         2027 $self->{_border_count} = shift;
131 864         1991 $self->{_fill_count} = shift;
132 864         2065 $self->{_custom_colors} = shift;
133 864         2235 $self->{_dxf_formats} = shift;
134 864         2428 $self->{_has_comments} = shift;
135             }
136              
137              
138             ###############################################################################
139             #
140             # Internal methods.
141             #
142             ###############################################################################
143              
144              
145             ###############################################################################
146             #
147             # _get_palette_color()
148             #
149             # Convert from an Excel internal colour index to a XML style #RRGGBB index
150             # based on the default or user defined values in the Workbook palette.
151             #
152             sub _get_palette_color {
153              
154 73     73   131 my $self = shift;
155 73         129 my $index = shift;
156 73         121 my $palette = $self->{_palette};
157              
158             # Handle colours in #XXXXXX RGB format.
159 73 100       272 if ( $index =~ m/^#([0-9A-F]{6})$/i ) {
160 28         119 return "FF" . uc( $1 );
161             }
162              
163             # Adjust the colour index.
164 45         91 $index -= 8;
165              
166             # Palette is passed in from the Workbook class.
167 45         67 my @rgb = @{ $palette->[$index] };
  45         134  
168              
169 45         216 return sprintf "FF%02X%02X%02X", @rgb[0, 1, 2];
170             }
171              
172              
173             ###############################################################################
174             #
175             # XML writing methods.
176             #
177             ###############################################################################
178              
179              
180             ##############################################################################
181             #
182             # _write_style_sheet()
183             #
184             # Write the element.
185             #
186             sub _write_style_sheet {
187              
188 859     859   2283 my $self = shift;
189 859         2177 my $xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
190              
191 859         3221 my @attributes = ( 'xmlns' => $xmlns );
192              
193 859         5886 $self->xml_start_tag( 'styleSheet', @attributes );
194             }
195              
196              
197             ##############################################################################
198             #
199             # _write_num_fmts()
200             #
201             # Write the element.
202             #
203             sub _write_num_fmts {
204              
205 860     860   2064 my $self = shift;
206 860         2267 my $count = $self->{_num_format_count};
207              
208 860 100       3547 return unless $count;
209              
210 10         31 my @attributes = ( 'count' => $count );
211              
212 10         46 $self->xml_start_tag( 'numFmts', @attributes );
213              
214             # Write the numFmts elements.
215 10         21 for my $format ( @{ $self->{_xf_formats} } ) {
  10         33  
216              
217             # Ignore built-in number formats, i.e., < 164.
218 38 100       106 next unless $format->{_num_format_index} >= 164;
219             $self->_write_num_fmt( $format->{_num_format_index},
220 19         66 $format->{_num_format} );
221             }
222              
223 10         54 $self->xml_end_tag( 'numFmts' );
224             }
225              
226              
227             ##############################################################################
228             #
229             # _write_num_fmt()
230             #
231             # Write the element.
232             #
233             sub _write_num_fmt {
234              
235 31     31   67 my $self = shift;
236 31         51 my $num_fmt_id = shift;
237 31         61 my $format_code = shift;
238              
239 31         708 my %format_codes = (
240             0 => 'General',
241             1 => '0',
242             2 => '0.00',
243             3 => '#,##0',
244             4 => '#,##0.00',
245             5 => '($#,##0_);($#,##0)',
246             6 => '($#,##0_);[Red]($#,##0)',
247             7 => '($#,##0.00_);($#,##0.00)',
248             8 => '($#,##0.00_);[Red]($#,##0.00)',
249             9 => '0%',
250             10 => '0.00%',
251             11 => '0.00E+00',
252             12 => '# ?/?',
253             13 => '# ??/??',
254             14 => 'm/d/yy',
255             15 => 'd-mmm-yy',
256             16 => 'd-mmm',
257             17 => 'mmm-yy',
258             18 => 'h:mm AM/PM',
259             19 => 'h:mm:ss AM/PM',
260             20 => 'h:mm',
261             21 => 'h:mm:ss',
262             22 => 'm/d/yy h:mm',
263             37 => '(#,##0_);(#,##0)',
264             38 => '(#,##0_);[Red](#,##0)',
265             39 => '(#,##0.00_);(#,##0.00)',
266             40 => '(#,##0.00_);[Red](#,##0.00)',
267             41 => '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
268             42 => '_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)',
269             43 => '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
270             44 => '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)',
271             45 => 'mm:ss',
272             46 => '[h]:mm:ss',
273             47 => 'mm:ss.0',
274             48 => '##0.0E+0',
275             49 => '@',
276             );
277              
278             # Set the format code for built-in number formats.
279 31 100       93 if ( $num_fmt_id < 164 ) {
280 6 50       26 if ( exists $format_codes{$num_fmt_id} ) {
281 6         15 $format_code = $format_codes{$num_fmt_id};
282             }
283             else {
284 0         0 $format_code = 'General';
285             }
286             }
287              
288 31         85 my @attributes = (
289             'numFmtId' => $num_fmt_id,
290             'formatCode' => $format_code,
291             );
292              
293 31         138 $self->xml_empty_tag( 'numFmt', @attributes );
294             }
295              
296              
297             ##############################################################################
298             #
299             # _write_fonts()
300             #
301             # Write the element.
302             #
303             sub _write_fonts {
304              
305 859     859   2036 my $self = shift;
306 859         2191 my $count = $self->{_font_count};
307              
308 859 100       4217 if ( $self->{_has_comments} ) {
309             # Add an extra font for comments.
310 36         99 $count++;
311             }
312              
313 859         3210 my @attributes = ( 'count' => $count );
314              
315 859         4020 $self->xml_start_tag( 'fonts', @attributes );
316              
317             # Write the font elements for format objects that have them.
318 859         1913 for my $format ( @{ $self->{_xf_formats} } ) {
  859         3314  
319 1118 100       6928 $self->_write_font( $format ) if $format->{_has_font};
320             }
321              
322 859 100       3996 if ( $self->{_has_comments} ) {
323 36         160 $self->_write_comment_font();
324             }
325              
326 859         3626 $self->xml_end_tag( 'fonts' );
327             }
328              
329              
330             ##############################################################################
331             #
332             # _write_font()
333             #
334             # Write the element.
335             #
336             sub _write_font {
337              
338 1024     1024   2605 my $self = shift;
339 1024         2223 my $format = shift;
340 1024         2250 my $dxf_format = shift;
341              
342 1024         4045 $self->xml_start_tag( 'font' );
343              
344             # The condense and extend elements are mainly used in dxf formats.
345 1024 100       4350 $self->_write_condense() if $format->{_font_condense};
346 1024 100       4422 $self->_write_extend() if $format->{_font_extend};
347              
348 1024 100       4444 $self->xml_empty_tag( 'b' ) if $format->{_bold};
349 1024 100       4381 $self->xml_empty_tag( 'i' ) if $format->{_italic};
350 1024 100       4252 $self->xml_empty_tag( 'strike' ) if $format->{_font_strikeout};
351 1024 100       4324 $self->xml_empty_tag( 'outline' ) if $format->{_font_outline};
352 1024 100       3943 $self->xml_empty_tag( 'shadow' ) if $format->{_font_shadow};
353              
354             # Handle the underline variants.
355 1024 100       4078 $self->_write_underline( $format->{_underline} ) if $format->{_underline};
356              
357 1024 100       4116 $self->_write_vert_align( 'superscript' ) if $format->{_font_script} == 1;
358 1024 100       4301 $self->_write_vert_align( 'subscript' ) if $format->{_font_script} == 2;
359              
360 1024 100       4033 if ( !$dxf_format ) {
361 1013         7430 $self->xml_empty_tag( 'sz', 'val', $format->{_size} );
362             }
363              
364 1024         3542 my $theme = $format->{_theme};
365              
366              
367 1024 100       11637 if ( $theme == -1 ) {
    100          
    50          
    100          
    100          
368             # Ignore for excel2003_style.
369             }
370             elsif ( $theme ) {
371 9         28 $self->_write_color( 'theme' => $theme );
372             }
373             elsif ( my $index = $format->{_color_indexed} ) {
374 0         0 $self->_write_color( 'indexed' => $index );
375             }
376             elsif ( my $color = $format->{_color} ) {
377 21         72 $color = $self->_get_palette_color( $color );
378              
379 21         60 $self->_write_color( 'rgb' => $color );
380             }
381             elsif ( !$dxf_format ) {
382 979         5437 $self->_write_color( 'theme' => 1 );
383             }
384              
385 1024 100       4258 if ( !$dxf_format ) {
386 1013         4887 $self->xml_empty_tag( 'name', 'val', $format->{_font} );
387              
388 1013 100       4138 if ($format->{_font_family}) {
389 1004         3932 $self->xml_empty_tag( 'family', 'val', $format->{_font_family} );
390             }
391              
392 1013 100       3991 if ($format->{_font_charset}) {
393 1         2 $self->xml_empty_tag( 'charset', 'val', $format->{_font_charset} );
394             }
395              
396 1013 100 100     9060 if ( $format->{_font} eq 'Calibri' && !$format->{_hyperlink} ) {
397             $self->xml_empty_tag(
398              
399             'scheme',
400             'val' => $format->{_font_scheme}
401 989         4438 );
402             }
403              
404 1013 100       4650 if ( $format->{_hyperlink} ) {
405 9         21 $self->{_has_hyperlink} = 1;
406              
407 9 50       27 if ( !$self->{_hyperlink_font_id} ) {
408 9         18 $self->{_hyperlink_font_id} = $format->{_font_index};
409             }
410             }
411             }
412              
413 1024         6156 $self->xml_end_tag( 'font' );
414             }
415              
416             ##############################################################################
417             #
418             # _write_comment_font()
419             #
420             # Write the element used for comments.
421             #
422             sub _write_comment_font {
423              
424 36     36   98 my $self = shift;
425              
426 36         133 $self->xml_start_tag( 'font' );
427              
428 36         388 $self->xml_empty_tag( 'sz', 'val', 8 );
429 36         144 $self->_write_color( 'indexed' => 81 );
430 36         199 $self->xml_empty_tag( 'name', 'val', 'Tahoma' );
431 36         127 $self->xml_empty_tag( 'family', 'val', 2 );
432              
433 36         138 $self->xml_end_tag( 'font' );
434             }
435              
436             ###############################################################################
437             #
438             # _write_underline()
439             #
440             # Write the underline font element.
441             #
442             sub _write_underline {
443              
444 27     27   61 my $self = shift;
445 27         60 my $underline = shift;
446 27         45 my @attributes;
447              
448             # Handle the underline variants.
449 27 100       162 if ( $underline == 2 ) {
    100          
    100          
450 1         3 @attributes = ( val => 'double' );
451             }
452             elsif ( $underline == 33 ) {
453 1         4 @attributes = ( val => 'singleAccounting' );
454             }
455             elsif ( $underline == 34 ) {
456 1         2 @attributes = ( val => 'doubleAccounting' );
457             }
458             else {
459 24         67 @attributes = (); # Default to single underline.
460             }
461              
462 27         100 $self->xml_empty_tag( 'u', @attributes );
463              
464             }
465              
466              
467             ##############################################################################
468             #
469             # _write_vert_align()
470             #
471             # Write the font sub-element.
472             #
473             sub _write_vert_align {
474              
475 5     5   9 my $self = shift;
476 5         8 my $val = shift;
477              
478 5         12 my @attributes = ( 'val' => $val );
479              
480 5         25 $self->xml_empty_tag( 'vertAlign', @attributes );
481             }
482              
483              
484             ##############################################################################
485             #
486             # _write_color()
487             #
488             # Write the element.
489             #
490             sub _write_color {
491              
492 1070     1070   2847 my $self = shift;
493 1070         2347 my $name = shift;
494 1070         2419 my $value = shift;
495              
496 1070         3442 my @attributes = ( $name => $value );
497              
498 1070         4378 $self->xml_empty_tag( 'color', @attributes );
499             }
500              
501              
502             ##############################################################################
503             #
504             # _write_fills()
505             #
506             # Write the element.
507             #
508             sub _write_fills {
509              
510 859     859   2239 my $self = shift;
511 859         2332 my $count = $self->{_fill_count};
512              
513 859         3021 my @attributes = ( 'count' => $count );
514              
515 859         4068 $self->xml_start_tag( 'fills', @attributes );
516              
517             # Write the default fill element.
518 859         3871 $self->_write_default_fill( 'none' );
519 859         3226 $self->_write_default_fill( 'gray125' );
520              
521             # Write the fill elements for format objects that have them.
522 859         1821 for my $format ( @{ $self->{_xf_formats} } ) {
  859         3075  
523 1117 100       4812 $self->_write_fill( $format ) if $format->{_has_fill};
524             }
525              
526 859         3492 $self->xml_end_tag( 'fills' );
527             }
528              
529              
530             ##############################################################################
531             #
532             # _write_default_fill()
533             #
534             # Write the element for the default fills.
535             #
536             sub _write_default_fill {
537              
538 1719     1719   3505 my $self = shift;
539 1719         3364 my $pattern_type = shift;
540              
541 1719         5938 $self->xml_start_tag( 'fill' );
542              
543 1719         5740 $self->xml_empty_tag( 'patternFill', 'patternType', $pattern_type );
544              
545 1719         4959 $self->xml_end_tag( 'fill' );
546             }
547              
548              
549             ##############################################################################
550             #
551             # _write_fill()
552             #
553             # Write the element.
554             #
555             sub _write_fill {
556              
557 33     33   75 my $self = shift;
558 33         50 my $format = shift;
559 33         148 my $dxf_format = shift;
560 33         75 my $pattern = $format->{_pattern};
561 33         71 my $bg_color = $format->{_bg_color};
562 33         73 my $fg_color = $format->{_fg_color};
563              
564             # Colors for dxf formats are handled differently from normal formats since
565             # the normal format reverses the meaning of BG and FG for solid fills.
566 33 100       198 if ( $dxf_format ) {
567 17         38 $bg_color = $format->{_dxf_bg_color};
568 17         32 $fg_color = $format->{_dxf_fg_color};
569             }
570              
571              
572 33         198 my @patterns = qw(
573             none
574             solid
575             mediumGray
576             darkGray
577             lightGray
578             darkHorizontal
579             darkVertical
580             darkDown
581             darkUp
582             darkGrid
583             darkTrellis
584             lightHorizontal
585             lightVertical
586             lightDown
587             lightUp
588             lightGrid
589             lightTrellis
590             gray125
591             gray0625
592              
593             );
594              
595              
596 33         179 $self->xml_start_tag( 'fill' );
597              
598             # The "none" pattern is handled differently for dxf formats.
599 33 100 100     168 if ( $dxf_format && $format->{_pattern} <= 1 ) {
600 15         61 $self->xml_start_tag( 'patternFill' );
601             }
602             else {
603             $self->xml_start_tag(
604             'patternFill',
605             'patternType',
606 18         52 $patterns[ $format->{_pattern} ]
607              
608             );
609             }
610              
611 33 100       100 if ( $fg_color ) {
612 17         104 $fg_color = $self->_get_palette_color( $fg_color );
613 17         58 $self->xml_empty_tag( 'fgColor', 'rgb' => $fg_color );
614             }
615              
616 33 100       82 if ( $bg_color ) {
617 25         66 $bg_color = $self->_get_palette_color( $bg_color );
618 25         85 $self->xml_empty_tag( 'bgColor', 'rgb' => $bg_color );
619             }
620             else {
621 8 50       21 if ( !$dxf_format ) {
622 8         19 $self->xml_empty_tag( 'bgColor', 'indexed' => 64 );
623             }
624             }
625              
626 33         142 $self->xml_end_tag( 'patternFill' );
627 33         83 $self->xml_end_tag( 'fill' );
628             }
629              
630              
631             ##############################################################################
632             #
633             # _write_borders()
634             #
635             # Write the element.
636             #
637             sub _write_borders {
638              
639 859     859   2166 my $self = shift;
640 859         2342 my $count = $self->{_border_count};
641              
642 859         3009 my @attributes = ( 'count' => $count );
643              
644 859         3995 $self->xml_start_tag( 'borders', @attributes );
645              
646             # Write the border elements for format objects that have them.
647 859         1972 for my $format ( @{ $self->{_xf_formats} } ) {
  859         3000  
648 1118 100       6478 $self->_write_border( $format ) if $format->{_has_border};
649             }
650              
651 859         3703 $self->xml_end_tag( 'borders' );
652             }
653              
654              
655             ##############################################################################
656             #
657             # _write_border()
658             #
659             # Write the element.
660             #
661             sub _write_border {
662              
663 906     906   2395 my $self = shift;
664 906         1987 my $format = shift;
665 906         2048 my $dxf_format = shift;
666 906         2714 my @attributes = ();
667              
668              
669             # Diagonal borders add attributes to the element.
670 906 100       8571 if ( $format->{_diag_type} == 1 ) {
    100          
    100          
671 2         5 push @attributes, ( diagonalUp => 1 );
672             }
673             elsif ( $format->{_diag_type} == 2 ) {
674 2         4 push @attributes, ( diagonalDown => 1 );
675             }
676             elsif ( $format->{_diag_type} == 3 ) {
677 4         63 push @attributes, ( diagonalUp => 1 );
678 4         10 push @attributes, ( diagonalDown => 1 );
679             }
680              
681             # Ensure that a default diag border is set if the diag type is set.
682 906 100 100     5328 if ( $format->{_diag_type} && !$format->{_diag_border} ) {
683 1         2 $format->{_diag_border} = 1;
684             }
685              
686             # Write the start border tag.
687 906         4766 $self->xml_start_tag( 'border', @attributes );
688              
689             # Write the sub elements.
690             $self->_write_sub_border(
691             'left',
692             $format->{_left},
693             $format->{_left_color}
694              
695 906         5336 );
696              
697             $self->_write_sub_border(
698             'right',
699             $format->{_right},
700             $format->{_right_color}
701              
702 906         3803 );
703              
704             $self->_write_sub_border(
705             'top',
706             $format->{_top},
707             $format->{_top_color}
708              
709 906         4061 );
710              
711             $self->_write_sub_border(
712             'bottom',
713             $format->{_bottom},
714             $format->{_bottom_color}
715              
716 906         3688 );
717              
718             # Condition DXF formats don't allow diagonal borders
719 906 100       3472 if ( !$dxf_format ) {
720             $self->_write_sub_border(
721             'diagonal',
722             $format->{_diag_border},
723             $format->{_diag_color}
724              
725 905         3695 );
726             }
727              
728 906 100       3654 if ( $dxf_format ) {
729 1         3 $self->_write_sub_border( 'vertical' );
730 1         2 $self->_write_sub_border( 'horizontal' );
731             }
732              
733 906         3857 $self->xml_end_tag( 'border' );
734             }
735              
736              
737             ##############################################################################
738             #
739             # _write_sub_border()
740             #
741             # Write the sub elements such as , , etc.
742             #
743             sub _write_sub_border {
744              
745 4531     4531   7541 my $self = shift;
746 4531         6851 my $type = shift;
747 4531         6964 my $style = shift;
748 4531         6787 my $color = shift;
749 4531         6089 my @attributes;
750              
751 4531 100       10889 if ( !$style ) {
752 4470         12061 $self->xml_empty_tag( $type );
753 4470         8079 return;
754             }
755              
756 61         149 my @border_styles = qw(
757             none
758             thin
759             medium
760             dashed
761             dotted
762             thick
763             double
764             hair
765             mediumDashed
766             dashDot
767             mediumDashDot
768             dashDotDot
769             mediumDashDotDot
770             slantDashDot
771              
772             );
773              
774              
775 61         107 push @attributes, ( style => $border_styles[$style] );
776              
777 61         142 $self->xml_start_tag( $type, @attributes );
778              
779 61 100       224 if ( $color ) {
780 10         19 $color = $self->_get_palette_color( $color );
781 10         22 $self->xml_empty_tag( 'color', 'rgb' => $color );
782             }
783             else {
784 51         106 $self->xml_empty_tag( 'color', 'auto' => 1 );
785             }
786              
787 61         134 $self->xml_end_tag( $type );
788             }
789              
790              
791             ##############################################################################
792             #
793             # _write_cell_style_xfs()
794             #
795             # Write the element.
796             #
797             sub _write_cell_style_xfs {
798              
799 859     859   2112 my $self = shift;
800 859         2284 my $count = 1;
801              
802 859 100       4013 if ( $self->{_has_hyperlink} ) {
803 8         51 $count = 2;
804             }
805              
806 859         3155 my @attributes = ( 'count' => $count );
807              
808 859         4070 $self->xml_start_tag( 'cellStyleXfs', @attributes );
809              
810             # Write the style_xf element.
811 859         4762 $self->_write_style_xf( 0, 0 );
812              
813 859 100       3413 if ( $self->{_has_hyperlink} ) {
814 8         23 $self->_write_style_xf( 1, $self->{_hyperlink_font_id} );
815             }
816              
817 859         3351 $self->xml_end_tag( 'cellStyleXfs' );
818             }
819              
820              
821             ##############################################################################
822             #
823             # _write_cell_xfs()
824             #
825             # Write the element.
826             #
827             sub _write_cell_xfs {
828              
829 859     859   2125 my $self = shift;
830 859         2047 my @formats = @{ $self->{_xf_formats} };
  859         3246  
831 859         2402 my $count = scalar @formats;
832              
833 859         3021 my @attributes = ( 'count' => $count );
834              
835 859         4257 $self->xml_start_tag( 'cellXfs', @attributes );
836              
837             # Write the xf elements.
838 859         2592 for my $format ( @formats ) {
839 1118         4987 $self->_write_xf( $format );
840             }
841              
842 859         3800 $self->xml_end_tag( 'cellXfs' );
843             }
844              
845              
846             ##############################################################################
847             #
848             # _write_style_xf()
849             #
850             # Write the style element.
851             #
852             sub _write_style_xf {
853              
854 868     868   2446 my $self = shift;
855 868         2140 my $has_hyperlink = shift;
856 868         2005 my $font_id = shift;
857 868         1978 my $num_fmt_id = 0;
858 868         1916 my $fill_id = 0;
859 868         1872 my $border_id = 0;
860              
861 868         4201 my @attributes = (
862             'numFmtId' => $num_fmt_id,
863             'fontId' => $font_id,
864             'fillId' => $fill_id,
865             'borderId' => $border_id,
866             );
867              
868 868 100       3646 if ( $has_hyperlink ) {
869 8         22 push @attributes, ( 'applyNumberFormat' => 0 );
870 8         22 push @attributes, ( 'applyFill' => 0 );
871 8         19 push @attributes, ( 'applyBorder' => 0 );
872 8         17 push @attributes, ( 'applyAlignment' => 0 );
873 8         25 push @attributes, ( 'applyProtection' => 0 );
874              
875 8         30 $self->xml_start_tag( 'xf', @attributes );
876 8         28 $self->xml_empty_tag( 'alignment', ( 'vertical', 'top' ) );
877 8         36 $self->xml_empty_tag( 'protection', ( 'locked', 0 ) );
878 8         24 $self->xml_end_tag( 'xf' );
879             }
880             else {
881 860         3944 $self->xml_empty_tag( 'xf', @attributes );
882             }
883             }
884              
885              
886             ##############################################################################
887             #
888             # _write_xf()
889             #
890             # Write the element.
891             #
892             sub _write_xf {
893              
894 1154     1154   2977 my $self = shift;
895 1154         2446 my $format = shift;
896 1154         2813 my $num_fmt_id = $format->{_num_format_index};
897 1154         2826 my $font_id = $format->{_font_index};
898 1154         2799 my $fill_id = $format->{_fill_index};
899 1154         2518 my $border_id = $format->{_border_index};
900 1154         2870 my $xf_id = $format->{_xf_id};
901 1154         2326 my $has_align = 0;
902 1154         2349 my $has_protect = 0;
903              
904 1154         4973 my @attributes = (
905             'numFmtId' => $num_fmt_id,
906             'fontId' => $font_id,
907             'fillId' => $fill_id,
908             'borderId' => $border_id,
909             'xfId' => $xf_id,
910             );
911              
912              
913 1154 100       4671 if ( $format->{_num_format_index} > 0 ) {
914 48         134 push @attributes, ( 'applyNumberFormat' => 1 );
915             }
916              
917             # Add applyFont attribute if XF format uses a font element.
918 1154 100 100     6427 if ( $format->{_font_index} > 0 && !$format->{_hyperlink} ) {
919 133         362 push @attributes, ( 'applyFont' => 1 );
920             }
921              
922             # Add applyFill attribute if XF format uses a fill element.
923 1154 100       4276 if ( $format->{_fill_index} > 0 ) {
924 21         41 push @attributes, ( 'applyFill' => 1 );
925             }
926              
927             # Add applyBorder attribute if XF format uses a border element.
928 1154 100       4043 if ( $format->{_border_index} > 0 ) {
929 45         67 push @attributes, ( 'applyBorder' => 1 );
930             }
931              
932             # Check if XF format has alignment properties set.
933 1154         6780 my ( $apply_align, @align ) = $format->get_align_properties();
934              
935             # Check if an alignment sub-element should be written.
936 1154 100 100     5525 $has_align = 1 if $apply_align && @align;
937              
938             # We can also have applyAlignment without a sub-element.
939 1154 100 100     7226 if ( $apply_align || $format->{_hyperlink} ) {
940 53         109 push @attributes, ( 'applyAlignment' => 1 );
941             }
942              
943             # Check for cell protection properties.
944 1154         5252 my @protection = $format->get_protection_properties();
945              
946 1154 100 100     7379 if ( @protection || $format->{_hyperlink} ) {
947 18         60 push @attributes, ( 'applyProtection' => 1 );
948              
949 18 100       55 if ( !$format->{_hyperlink} ) {
950 10         17 $has_protect = 1;
951             }
952             }
953              
954             # Write XF with sub-elements if required.
955 1154 100 100     6966 if ( $has_align || $has_protect ) {
956 53         221 $self->xml_start_tag( 'xf', @attributes );
957 53 100       245 $self->xml_empty_tag( 'alignment', @align ) if $has_align;
958 53 100       144 $self->xml_empty_tag( 'protection', @protection ) if $has_protect;
959 53         142 $self->xml_end_tag( 'xf' );
960             }
961             else {
962 1101         4694 $self->xml_empty_tag( 'xf', @attributes );
963             }
964             }
965              
966              
967             ##############################################################################
968             #
969             # _write_cell_styles()
970             #
971             # Write the element.
972             #
973             sub _write_cell_styles {
974              
975 859     859   2185 my $self = shift;
976 859         2014 my $count = 1;
977              
978 859 100       3881 if ( $self->{_has_hyperlink} ) {
979 8         15 $count = 2;
980             }
981              
982 859         3109 my @attributes = ( 'count' => $count );
983              
984 859         4204 $self->xml_start_tag( 'cellStyles', @attributes );
985              
986             # Write the cellStyle element.
987 859 100       4053 if ( $self->{_has_hyperlink} ) {
988 8         32 $self->_write_cell_style('Hyperlink', 1, 8);
989             }
990              
991 859         4864 $self->_write_cell_style('Normal', 0, 0);
992              
993 859         3205 $self->xml_end_tag( 'cellStyles' );
994             }
995              
996              
997             ##############################################################################
998             #
999             # _write_cell_style()
1000             #
1001             # Write the element.
1002             #
1003             sub _write_cell_style {
1004              
1005 868     868   2460 my $self = shift;
1006 868         2443 my $name = shift;
1007 868         2150 my $xf_id = shift;
1008 868         2297 my $builtin_id = shift;
1009              
1010 868         3907 my @attributes = (
1011             'name' => $name,
1012             'xfId' => $xf_id,
1013             'builtinId' => $builtin_id,
1014             );
1015              
1016 868         4353 $self->xml_empty_tag( 'cellStyle', @attributes );
1017             }
1018              
1019              
1020             ##############################################################################
1021             #
1022             # _write_dxfs()
1023             #
1024             # Write the element.
1025             #
1026             sub _write_dxfs {
1027              
1028 859     859   2203 my $self = shift;
1029 859         2983 my $formats = $self->{_dxf_formats};
1030              
1031 859         1999 my $count = scalar @{$formats};
  859         2706  
1032              
1033 859         2873 my @attributes = ( 'count' => $count );
1034              
1035 859 100       3917 if ( $count ) {
1036 23         96 $self->xml_start_tag( 'dxfs', @attributes );
1037              
1038             # Write the font elements for format objects that have them.
1039 23         53 for my $format ( @{ $self->{_dxf_formats} } ) {
  23         81  
1040 33         133 $self->xml_start_tag( 'dxf' );
1041 33 100       134 $self->_write_font( $format, 1 ) if $format->{_has_dxf_font};
1042              
1043 33 100       106 if ( $format->{_num_format_index} ) {
1044             $self->_write_num_fmt( $format->{_num_format_index},
1045 11         48 $format->{_num_format} );
1046             }
1047              
1048 33 100       150 $self->_write_fill( $format, 1 ) if $format->{_has_dxf_fill};
1049 33 100       103 $self->_write_border( $format, 1 ) if $format->{_has_dxf_border};
1050 33         112 $self->xml_end_tag( 'dxf' );
1051             }
1052              
1053 23         91 $self->xml_end_tag( 'dxfs' );
1054             }
1055             else {
1056 836         3716 $self->xml_empty_tag( 'dxfs', @attributes );
1057             }
1058              
1059             }
1060              
1061              
1062             ##############################################################################
1063             #
1064             # _write_table_styles()
1065             #
1066             # Write the element.
1067             #
1068             sub _write_table_styles {
1069              
1070 858     858   2266 my $self = shift;
1071 858         1902 my $count = 0;
1072 858         1995 my $default_table_style = 'TableStyleMedium9';
1073 858         1882 my $default_pivot_style = 'PivotStyleLight16';
1074              
1075 858         3723 my @attributes = (
1076             'count' => $count,
1077             'defaultTableStyle' => $default_table_style,
1078             'defaultPivotStyle' => $default_pivot_style,
1079             );
1080              
1081 858         3396 $self->xml_empty_tag( 'tableStyles', @attributes );
1082             }
1083              
1084              
1085             ##############################################################################
1086             #
1087             # _write_colors()
1088             #
1089             # Write the element.
1090             #
1091             sub _write_colors {
1092              
1093 861     861   1934 my $self = shift;
1094 861         1847 my @custom_colors = @{ $self->{_custom_colors} };
  861         2712  
1095              
1096 861 100       4026 return unless @custom_colors;
1097              
1098 7         28 $self->xml_start_tag( 'colors' );
1099 7         25 $self->_write_mru_colors( @custom_colors );
1100 7         29 $self->xml_end_tag( 'colors' );
1101             }
1102              
1103              
1104             ##############################################################################
1105             #
1106             # _write_mru_colors()
1107             #
1108             # Write the element for the most recently used colours.
1109             #
1110             sub _write_mru_colors {
1111              
1112 9     9   48 my $self = shift;
1113 9         24 my @custom_colors = @_;
1114              
1115             # Limit the mruColors to the last 10.
1116 9         17 my $count = @custom_colors;
1117 9 100       38 if ( $count > 10 ) {
1118 1         2 splice @custom_colors, 0, ( $count - 10 );
1119             }
1120              
1121 9         37 $self->xml_start_tag( 'mruColors' );
1122              
1123             # Write the custom colors in reverse order.
1124 9         24 for my $color ( reverse @custom_colors ) {
1125 25         53 $self->_write_color( 'rgb' => $color );
1126             }
1127              
1128 9         29 $self->xml_end_tag( 'mruColors' );
1129             }
1130              
1131              
1132             ##############################################################################
1133             #
1134             # _write_condense()
1135             #
1136             # Write the element.
1137             #
1138             sub _write_condense {
1139              
1140 7     7   15 my $self = shift;
1141 7         13 my $val = 0;
1142              
1143 7         17 my @attributes = ( 'val' => $val );
1144              
1145 7         29 $self->xml_empty_tag( 'condense', @attributes );
1146             }
1147              
1148              
1149             ##############################################################################
1150             #
1151             # _write_extend()
1152             #
1153             # Write the element.
1154             #
1155             sub _write_extend {
1156              
1157 7     7   15 my $self = shift;
1158 7         19 my $val = 0;
1159              
1160 7         22 my @attributes = ( 'val' => $val );
1161              
1162 7         22 $self->xml_empty_tag( 'extend', @attributes );
1163             }
1164              
1165              
1166             1;
1167              
1168              
1169             __END__