File Coverage

blib/lib/Excel/Writer/XLSX/Package/Styles.pm
Criterion Covered Total %
statement 369 370 99.7
branch 137 140 97.8
condition 24 24 100.0
subroutine 35 35 100.0
pod 0 1 0.0
total 565 570 99.1


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-2019, 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 1040     1040   17544 use 5.008002;
  1040         6244  
17 1040     1040   6766 use strict;
  1040         3496  
  1040         22333  
18 1040     1040   6147 use warnings;
  1040         3414  
  1040         24326  
19 1040     1040   7640 use Carp;
  1040         3485  
  1040         58119  
20 1040     1040   6967 use Excel::Writer::XLSX::Package::XMLwriter;
  1040         4979  
  1040         3587687  
21              
22             our @ISA = qw(Excel::Writer::XLSX::Package::XMLwriter);
23             our $VERSION = '1.03';
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 893     893 0 20520 my $class = shift;
42 893         2009 my $fh = shift;
43 893         3653 my $self = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
44              
45 893         3056 $self->{_xf_formats} = undef;
46 893         2799 $self->{_palette} = [];
47 893         2377 $self->{_font_count} = 0;
48 893         2228 $self->{_num_format_count} = 0;
49 893         2255 $self->{_border_count} = 0;
50 893         2183 $self->{_fill_count} = 0;
51 893         2970 $self->{_custom_colors} = [];
52 893         2375 $self->{_dxf_formats} = [];
53 893         2169 $self->{_has_hyperlink} = 0;
54 893         2227 $self->{_hyperlink_font_id} = 0;
55              
56 893         2141 bless $self, $class;
57              
58 893         2489 return $self;
59             }
60              
61              
62             ###############################################################################
63             #
64             # _assemble_xml_file()
65             #
66             # Assemble and write the XML file.
67             #
68             sub _assemble_xml_file {
69              
70 818     818   2517 my $self = shift;
71              
72 818         7730 $self->xml_declaration;
73              
74             # Add the style sheet.
75 818         5100 $self->_write_style_sheet();
76              
77             # Write the number formats.
78 818         4725 $self->_write_num_fmts();
79              
80             # Write the fonts.
81 818         4397 $self->_write_fonts();
82              
83             # Write the fills.
84 818         4639 $self->_write_fills();
85              
86             # Write the borders element.
87 818         4682 $self->_write_borders();
88              
89             # Write the cellStyleXfs element.
90 818         4618 $self->_write_cell_style_xfs();
91              
92             # Write the cellXfs element.
93 818         4916 $self->_write_cell_xfs();
94              
95             # Write the cellStyles element.
96 818         4618 $self->_write_cell_styles();
97              
98             # Write the dxfs element.
99 818         5028 $self->_write_dxfs();
100              
101             # Write the tableStyles element.
102 818         4676 $self->_write_table_styles();
103              
104             # Write the colors element.
105 818         4064 $self->_write_colors();
106              
107             # Close the style sheet tag.
108 818         3832 $self->xml_end_tag( 'styleSheet' );
109              
110             # Close the XML writer filehandle.
111 818         5948 $self->xml_get_fh()->close();
112             }
113              
114              
115             ###############################################################################
116             #
117             # _set_style_properties()
118             #
119             # Pass in the Format objects and other properties used to set the styles.
120             #
121             sub _set_style_properties {
122              
123 824     824   2461 my $self = shift;
124              
125 824         5694 $self->{_xf_formats} = shift;
126 824         2473 $self->{_palette} = shift;
127 824         2137 $self->{_font_count} = shift;
128 824         2130 $self->{_num_format_count} = shift;
129 824         2148 $self->{_border_count} = shift;
130 824         2029 $self->{_fill_count} = shift;
131 824         2068 $self->{_custom_colors} = shift;
132 824         2389 $self->{_dxf_formats} = shift;
133             }
134              
135              
136             ###############################################################################
137             #
138             # Internal methods.
139             #
140             ###############################################################################
141              
142              
143             ###############################################################################
144             #
145             # _get_palette_color()
146             #
147             # Convert from an Excel internal colour index to a XML style #RRGGBB index
148             # based on the default or user defined values in the Workbook palette.
149             #
150             sub _get_palette_color {
151              
152 73     73   200 my $self = shift;
153 73         158 my $index = shift;
154 73         138 my $palette = $self->{_palette};
155              
156             # Handle colours in #XXXXXX RGB format.
157 73 100       271 if ( $index =~ m/^#([0-9A-F]{6})$/i ) {
158 28         152 return "FF" . uc( $1 );
159             }
160              
161             # Adjust the colour index.
162 45         146 $index -= 8;
163              
164             # Palette is passed in from the Workbook class.
165 45         89 my @rgb = @{ $palette->[$index] };
  45         122  
166              
167 45         237 return sprintf "FF%02X%02X%02X", @rgb[0, 1, 2];
168             }
169              
170              
171             ###############################################################################
172             #
173             # XML writing methods.
174             #
175             ###############################################################################
176              
177              
178             ##############################################################################
179             #
180             # _write_style_sheet()
181             #
182             # Write the element.
183             #
184             sub _write_style_sheet {
185              
186 819     819   2321 my $self = shift;
187 819         2266 my $xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
188              
189 819         3283 my @attributes = ( 'xmlns' => $xmlns );
190              
191 819         5462 $self->xml_start_tag( 'styleSheet', @attributes );
192             }
193              
194              
195             ##############################################################################
196             #
197             # _write_num_fmts()
198             #
199             # Write the element.
200             #
201             sub _write_num_fmts {
202              
203 820     820   2244 my $self = shift;
204 820         2347 my $count = $self->{_num_format_count};
205              
206 820 100       3742 return unless $count;
207              
208 10         41 my @attributes = ( 'count' => $count );
209              
210 10         47 $self->xml_start_tag( 'numFmts', @attributes );
211              
212             # Write the numFmts elements.
213 10         26 for my $format ( @{ $self->{_xf_formats} } ) {
  10         41  
214              
215             # Ignore built-in number formats, i.e., < 164.
216 38 100       125 next unless $format->{_num_format_index} >= 164;
217             $self->_write_num_fmt( $format->{_num_format_index},
218 19         69 $format->{_num_format} );
219             }
220              
221 10         90 $self->xml_end_tag( 'numFmts' );
222             }
223              
224              
225             ##############################################################################
226             #
227             # _write_num_fmt()
228             #
229             # Write the element.
230             #
231             sub _write_num_fmt {
232              
233 31     31   77 my $self = shift;
234 31         66 my $num_fmt_id = shift;
235 31         69 my $format_code = shift;
236              
237 31         732 my %format_codes = (
238             0 => 'General',
239             1 => '0',
240             2 => '0.00',
241             3 => '#,##0',
242             4 => '#,##0.00',
243             5 => '($#,##0_);($#,##0)',
244             6 => '($#,##0_);[Red]($#,##0)',
245             7 => '($#,##0.00_);($#,##0.00)',
246             8 => '($#,##0.00_);[Red]($#,##0.00)',
247             9 => '0%',
248             10 => '0.00%',
249             11 => '0.00E+00',
250             12 => '# ?/?',
251             13 => '# ??/??',
252             14 => 'm/d/yy',
253             15 => 'd-mmm-yy',
254             16 => 'd-mmm',
255             17 => 'mmm-yy',
256             18 => 'h:mm AM/PM',
257             19 => 'h:mm:ss AM/PM',
258             20 => 'h:mm',
259             21 => 'h:mm:ss',
260             22 => 'm/d/yy h:mm',
261             37 => '(#,##0_);(#,##0)',
262             38 => '(#,##0_);[Red](#,##0)',
263             39 => '(#,##0.00_);(#,##0.00)',
264             40 => '(#,##0.00_);[Red](#,##0.00)',
265             41 => '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
266             42 => '_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)',
267             43 => '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
268             44 => '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)',
269             45 => 'mm:ss',
270             46 => '[h]:mm:ss',
271             47 => 'mm:ss.0',
272             48 => '##0.0E+0',
273             49 => '@',
274             );
275              
276             # Set the format code for built-in number formats.
277 31 100       116 if ( $num_fmt_id < 164 ) {
278 6 50       19 if ( exists $format_codes{$num_fmt_id} ) {
279 6         15 $format_code = $format_codes{$num_fmt_id};
280             }
281             else {
282 0         0 $format_code = 'General';
283             }
284             }
285              
286 31         102 my @attributes = (
287             'numFmtId' => $num_fmt_id,
288             'formatCode' => $format_code,
289             );
290              
291 31         139 $self->xml_empty_tag( 'numFmt', @attributes );
292             }
293              
294              
295             ##############################################################################
296             #
297             # _write_fonts()
298             #
299             # Write the element.
300             #
301             sub _write_fonts {
302              
303 819     819   2140 my $self = shift;
304 819         2863 my $count = $self->{_font_count};
305              
306 819         3113 my @attributes = ( 'count' => $count );
307              
308 819         4105 $self->xml_start_tag( 'fonts', @attributes );
309              
310             # Write the font elements for format objects that have them.
311 819         2305 for my $format ( @{ $self->{_xf_formats} } ) {
  819         3934  
312 1109 100       6819 $self->_write_font( $format ) if $format->{_has_font};
313             }
314              
315 819         4043 $self->xml_end_tag( 'fonts' );
316             }
317              
318              
319             ##############################################################################
320             #
321             # _write_font()
322             #
323             # Write the element.
324             #
325             sub _write_font {
326              
327 1015     1015   2842 my $self = shift;
328 1015         2194 my $format = shift;
329 1015         2419 my $dxf_format = shift;
330              
331 1015         4221 $self->xml_start_tag( 'font' );
332              
333             # The condense and extend elements are mainly used in dxf formats.
334 1015 100       4753 $self->_write_condense() if $format->{_font_condense};
335 1015 100       4529 $self->_write_extend() if $format->{_font_extend};
336              
337 1015 100       4311 $self->xml_empty_tag( 'b' ) if $format->{_bold};
338 1015 100       4683 $self->xml_empty_tag( 'i' ) if $format->{_italic};
339 1015 100       4694 $self->xml_empty_tag( 'strike' ) if $format->{_font_strikeout};
340 1015 100       4149 $self->xml_empty_tag( 'outline' ) if $format->{_font_outline};
341 1015 100       4236 $self->xml_empty_tag( 'shadow' ) if $format->{_font_shadow};
342              
343             # Handle the underline variants.
344 1015 100       4174 $self->_write_underline( $format->{_underline} ) if $format->{_underline};
345              
346 1015 100       4448 $self->_write_vert_align( 'superscript' ) if $format->{_font_script} == 1;
347 1015 100       4336 $self->_write_vert_align( 'subscript' ) if $format->{_font_script} == 2;
348              
349 1015 100       3979 if ( !$dxf_format ) {
350 1004         7333 $self->xml_empty_tag( 'sz', 'val', $format->{_size} );
351             }
352              
353 1015         4131 my $theme = $format->{_theme};
354              
355              
356 1015 100       11724 if ( $theme == -1 ) {
    100          
    100          
    100          
    100          
357             # Ignore for excel2003_style.
358             }
359             elsif ( $theme ) {
360 9         30 $self->_write_color( 'theme' => $theme );
361             }
362             elsif ( my $index = $format->{_color_indexed} ) {
363 32         110 $self->_write_color( 'indexed' => $index );
364             }
365             elsif ( my $color = $format->{_color} ) {
366 21         81 $color = $self->_get_palette_color( $color );
367              
368 21         98 $self->_write_color( 'rgb' => $color );
369             }
370             elsif ( !$dxf_format ) {
371 938         5185 $self->_write_color( 'theme' => 1 );
372             }
373              
374 1015 100       5110 if ( !$dxf_format ) {
375 1004         5063 $self->xml_empty_tag( 'name', 'val', $format->{_font} );
376              
377 1004 100       4619 if ($format->{_font_family}) {
378 995         4489 $self->xml_empty_tag( 'family', 'val', $format->{_font_family} );
379             }
380              
381 1004 100       4846 if ($format->{_font_charset}) {
382 1         3 $self->xml_empty_tag( 'charset', 'val', $format->{_font_charset} );
383             }
384              
385 1004 100 100     9192 if ( $format->{_font} eq 'Calibri' && !$format->{_hyperlink} ) {
386             $self->xml_empty_tag(
387              
388             'scheme',
389             'val' => $format->{_font_scheme}
390 948         4410 );
391             }
392              
393 1004 100       5041 if ( $format->{_hyperlink} ) {
394 9         24 $self->{_has_hyperlink} = 1;
395              
396 9 50       40 if ( !$self->{_hyperlink_font_id} ) {
397 9         19 $self->{_hyperlink_font_id} = $format->{_font_index};
398             }
399             }
400             }
401              
402 1015         6340 $self->xml_end_tag( 'font' );
403             }
404              
405              
406             ###############################################################################
407             #
408             # _write_underline()
409             #
410             # Write the underline font element.
411             #
412             sub _write_underline {
413              
414 27     27   62 my $self = shift;
415 27         56 my $underline = shift;
416 27         48 my @attributes;
417              
418             # Handle the underline variants.
419 27 100       175 if ( $underline == 2 ) {
    100          
    100          
420 1         5 @attributes = ( val => 'double' );
421             }
422             elsif ( $underline == 33 ) {
423 1         4 @attributes = ( val => 'singleAccounting' );
424             }
425             elsif ( $underline == 34 ) {
426 1         4 @attributes = ( val => 'doubleAccounting' );
427             }
428             else {
429 24         60 @attributes = (); # Default to single underline.
430             }
431              
432 27         105 $self->xml_empty_tag( 'u', @attributes );
433              
434             }
435              
436              
437             ##############################################################################
438             #
439             # _write_vert_align()
440             #
441             # Write the font sub-element.
442             #
443             sub _write_vert_align {
444              
445 5     5   9 my $self = shift;
446 5         11 my $val = shift;
447              
448 5         22 my @attributes = ( 'val' => $val );
449              
450 5         21 $self->xml_empty_tag( 'vertAlign', @attributes );
451             }
452              
453              
454             ##############################################################################
455             #
456             # _write_color()
457             #
458             # Write the element.
459             #
460             sub _write_color {
461              
462 1025     1025   3055 my $self = shift;
463 1025         2537 my $name = shift;
464 1025         2751 my $value = shift;
465              
466 1025         3749 my @attributes = ( $name => $value );
467              
468 1025         4376 $self->xml_empty_tag( 'color', @attributes );
469             }
470              
471              
472             ##############################################################################
473             #
474             # _write_fills()
475             #
476             # Write the element.
477             #
478             sub _write_fills {
479              
480 819     819   2361 my $self = shift;
481 819         2515 my $count = $self->{_fill_count};
482              
483 819         3367 my @attributes = ( 'count' => $count );
484              
485 819         4196 $self->xml_start_tag( 'fills', @attributes );
486              
487             # Write the default fill element.
488 819         5082 $self->_write_default_fill( 'none' );
489 819         3809 $self->_write_default_fill( 'gray125' );
490              
491             # Write the fill elements for format objects that have them.
492 819         2029 for my $format ( @{ $self->{_xf_formats} } ) {
  819         3540  
493 1108 100       4921 $self->_write_fill( $format ) if $format->{_has_fill};
494             }
495              
496 819         3553 $self->xml_end_tag( 'fills' );
497             }
498              
499              
500             ##############################################################################
501             #
502             # _write_default_fill()
503             #
504             # Write the element for the default fills.
505             #
506             sub _write_default_fill {
507              
508 1639     1639   3694 my $self = shift;
509 1639         3429 my $pattern_type = shift;
510              
511 1639         5881 $self->xml_start_tag( 'fill' );
512              
513 1639         6595 $self->xml_empty_tag( 'patternFill', 'patternType', $pattern_type );
514              
515 1639         5292 $self->xml_end_tag( 'fill' );
516             }
517              
518              
519             ##############################################################################
520             #
521             # _write_fill()
522             #
523             # Write the element.
524             #
525             sub _write_fill {
526              
527 33     33   77 my $self = shift;
528 33         59 my $format = shift;
529 33         73 my $dxf_format = shift;
530 33         81 my $pattern = $format->{_pattern};
531 33         85 my $bg_color = $format->{_bg_color};
532 33         63 my $fg_color = $format->{_fg_color};
533              
534             # Colors for dxf formats are handled differently from normal formats since
535             # the normal format reverses the meaning of BG and FG for solid fills.
536 33 100       99 if ( $dxf_format ) {
537 17         47 $bg_color = $format->{_dxf_bg_color};
538 17         45 $fg_color = $format->{_dxf_fg_color};
539             }
540              
541              
542 33         267 my @patterns = qw(
543             none
544             solid
545             mediumGray
546             darkGray
547             lightGray
548             darkHorizontal
549             darkVertical
550             darkDown
551             darkUp
552             darkGrid
553             darkTrellis
554             lightHorizontal
555             lightVertical
556             lightDown
557             lightUp
558             lightGrid
559             lightTrellis
560             gray125
561             gray0625
562              
563             );
564              
565              
566 33         119 $self->xml_start_tag( 'fill' );
567              
568             # The "none" pattern is handled differently for dxf formats.
569 33 100 100     193 if ( $dxf_format && $format->{_pattern} <= 1 ) {
570 15         83 $self->xml_start_tag( 'patternFill' );
571             }
572             else {
573             $self->xml_start_tag(
574             'patternFill',
575             'patternType',
576 18         66 $patterns[ $format->{_pattern} ]
577              
578             );
579             }
580              
581 33 100       209 if ( $fg_color ) {
582 17         59 $fg_color = $self->_get_palette_color( $fg_color );
583 17         64 $self->xml_empty_tag( 'fgColor', 'rgb' => $fg_color );
584             }
585              
586 33 100       113 if ( $bg_color ) {
587 25         89 $bg_color = $self->_get_palette_color( $bg_color );
588 25         124 $self->xml_empty_tag( 'bgColor', 'rgb' => $bg_color );
589             }
590             else {
591 8 50       22 if ( !$dxf_format ) {
592 8         30 $self->xml_empty_tag( 'bgColor', 'indexed' => 64 );
593             }
594             }
595              
596 33         186 $self->xml_end_tag( 'patternFill' );
597 33         106 $self->xml_end_tag( 'fill' );
598             }
599              
600              
601             ##############################################################################
602             #
603             # _write_borders()
604             #
605             # Write the element.
606             #
607             sub _write_borders {
608              
609 819     819   2292 my $self = shift;
610 819         2475 my $count = $self->{_border_count};
611              
612 819         3122 my @attributes = ( 'count' => $count );
613              
614 819         3819 $self->xml_start_tag( 'borders', @attributes );
615              
616             # Write the border elements for format objects that have them.
617 819         2331 for my $format ( @{ $self->{_xf_formats} } ) {
  819         3566  
618 1109 100       6475 $self->_write_border( $format ) if $format->{_has_border};
619             }
620              
621 819         3740 $self->xml_end_tag( 'borders' );
622             }
623              
624              
625             ##############################################################################
626             #
627             # _write_border()
628             #
629             # Write the element.
630             #
631             sub _write_border {
632              
633 866     866   2502 my $self = shift;
634 866         2104 my $format = shift;
635 866         2155 my $dxf_format = shift;
636 866         2545 my @attributes = ();
637              
638              
639             # Diagonal borders add attributes to the element.
640 866 100       8337 if ( $format->{_diag_type} == 1 ) {
    100          
    100          
641 2         4 push @attributes, ( diagonalUp => 1 );
642             }
643             elsif ( $format->{_diag_type} == 2 ) {
644 2         5 push @attributes, ( diagonalDown => 1 );
645             }
646             elsif ( $format->{_diag_type} == 3 ) {
647 4         81 push @attributes, ( diagonalUp => 1 );
648 4         12 push @attributes, ( diagonalDown => 1 );
649             }
650              
651             # Ensure that a default diag border is set if the diag type is set.
652 866 100 100     5121 if ( $format->{_diag_type} && !$format->{_diag_border} ) {
653 1         2 $format->{_diag_border} = 1;
654             }
655              
656             # Write the start border tag.
657 866         4863 $self->xml_start_tag( 'border', @attributes );
658              
659             # Write the sub elements.
660             $self->_write_sub_border(
661             'left',
662             $format->{_left},
663             $format->{_left_color}
664              
665 866         5923 );
666              
667             $self->_write_sub_border(
668             'right',
669             $format->{_right},
670             $format->{_right_color}
671              
672 866         4232 );
673              
674             $self->_write_sub_border(
675             'top',
676             $format->{_top},
677             $format->{_top_color}
678              
679 866         4486 );
680              
681             $self->_write_sub_border(
682             'bottom',
683             $format->{_bottom},
684             $format->{_bottom_color}
685              
686 866         4473 );
687              
688             # Condition DXF formats don't allow diagonal borders
689 866 100       3721 if ( !$dxf_format ) {
690             $self->_write_sub_border(
691             'diagonal',
692             $format->{_diag_border},
693             $format->{_diag_color}
694              
695 865         3569 );
696             }
697              
698 866 100       4072 if ( $dxf_format ) {
699 1         4 $self->_write_sub_border( 'vertical' );
700 1         3 $self->_write_sub_border( 'horizontal' );
701             }
702              
703 866         4054 $self->xml_end_tag( 'border' );
704             }
705              
706              
707             ##############################################################################
708             #
709             # _write_sub_border()
710             #
711             # Write the sub elements such as , , etc.
712             #
713             sub _write_sub_border {
714              
715 4331     4331   8009 my $self = shift;
716 4331         7171 my $type = shift;
717 4331         7390 my $style = shift;
718 4331         7143 my $color = shift;
719 4331         6707 my @attributes;
720              
721 4331 100       9869 if ( !$style ) {
722 4270         12608 $self->xml_empty_tag( $type );
723 4270         8751 return;
724             }
725              
726 61         174 my @border_styles = qw(
727             none
728             thin
729             medium
730             dashed
731             dotted
732             thick
733             double
734             hair
735             mediumDashed
736             dashDot
737             mediumDashDot
738             dashDotDot
739             mediumDashDotDot
740             slantDashDot
741              
742             );
743              
744              
745 61         106 push @attributes, ( style => $border_styles[$style] );
746              
747 61         338 $self->xml_start_tag( $type, @attributes );
748              
749 61 100       203 if ( $color ) {
750 10         24 $color = $self->_get_palette_color( $color );
751 10         27 $self->xml_empty_tag( 'color', 'rgb' => $color );
752             }
753             else {
754 51         125 $self->xml_empty_tag( 'color', 'auto' => 1 );
755             }
756              
757 61         160 $self->xml_end_tag( $type );
758             }
759              
760              
761             ##############################################################################
762             #
763             # _write_cell_style_xfs()
764             #
765             # Write the element.
766             #
767             sub _write_cell_style_xfs {
768              
769 819     819   2467 my $self = shift;
770 819         2194 my $count = 1;
771              
772 819 100       4030 if ( $self->{_has_hyperlink} ) {
773 8         22 $count = 2;
774             }
775              
776 819         3297 my @attributes = ( 'count' => $count );
777              
778 819         4147 $self->xml_start_tag( 'cellStyleXfs', @attributes );
779              
780             # Write the style_xf element.
781 819         5501 $self->_write_style_xf( 0, 0 );
782              
783 819 100       3870 if ( $self->{_has_hyperlink} ) {
784 8         27 $self->_write_style_xf( 1, $self->{_hyperlink_font_id} );
785             }
786              
787 819         3919 $self->xml_end_tag( 'cellStyleXfs' );
788             }
789              
790              
791             ##############################################################################
792             #
793             # _write_cell_xfs()
794             #
795             # Write the element.
796             #
797             sub _write_cell_xfs {
798              
799 819     819   2370 my $self = shift;
800 819         2076 my @formats = @{ $self->{_xf_formats} };
  819         3157  
801              
802             # Workaround for when the last format is used for the comment font
803             # and shouldn't be used for cellXfs.
804 819         2433 my $last_format = $formats[-1];
805              
806 819 100       3803 if ( $last_format->{_font_only} ) {
807 32         92 pop @formats;
808             }
809              
810 819         2210 my $count = scalar @formats;
811 819         2884 my @attributes = ( 'count' => $count );
812              
813 819         4366 $self->xml_start_tag( 'cellXfs', @attributes );
814              
815             # Write the xf elements.
816 819         3110 for my $format ( @formats ) {
817 1077         4878 $self->_write_xf( $format );
818             }
819              
820 819         3573 $self->xml_end_tag( 'cellXfs' );
821             }
822              
823              
824             ##############################################################################
825             #
826             # _write_style_xf()
827             #
828             # Write the style element.
829             #
830             sub _write_style_xf {
831              
832 828     828   2561 my $self = shift;
833 828         2189 my $has_hyperlink = shift;
834 828         2136 my $font_id = shift;
835 828         2123 my $num_fmt_id = 0;
836 828         2111 my $fill_id = 0;
837 828         2064 my $border_id = 0;
838              
839 828         4253 my @attributes = (
840             'numFmtId' => $num_fmt_id,
841             'fontId' => $font_id,
842             'fillId' => $fill_id,
843             'borderId' => $border_id,
844             );
845              
846 828 100       3551 if ( $has_hyperlink ) {
847 8         23 push @attributes, ( 'applyNumberFormat' => 0 );
848 8         22 push @attributes, ( 'applyFill' => 0 );
849 8         20 push @attributes, ( 'applyBorder' => 0 );
850 8         18 push @attributes, ( 'applyAlignment' => 0 );
851 8         19 push @attributes, ( 'applyProtection' => 0 );
852              
853 8         38 $self->xml_start_tag( 'xf', @attributes );
854 8         30 $self->xml_empty_tag( 'alignment', ( 'vertical', 'top' ) );
855 8         41 $self->xml_empty_tag( 'protection', ( 'locked', 0 ) );
856 8         34 $self->xml_end_tag( 'xf' );
857             }
858             else {
859 820         4078 $self->xml_empty_tag( 'xf', @attributes );
860             }
861             }
862              
863              
864             ##############################################################################
865             #
866             # _write_xf()
867             #
868             # Write the element.
869             #
870             sub _write_xf {
871              
872 1113     1113   3114 my $self = shift;
873 1113         2553 my $format = shift;
874 1113         3041 my $num_fmt_id = $format->{_num_format_index};
875 1113         2871 my $font_id = $format->{_font_index};
876 1113         2756 my $fill_id = $format->{_fill_index};
877 1113         2849 my $border_id = $format->{_border_index};
878 1113         2722 my $xf_id = $format->{_xf_id};
879 1113         2497 my $has_align = 0;
880 1113         2550 my $has_protect = 0;
881              
882 1113         5217 my @attributes = (
883             'numFmtId' => $num_fmt_id,
884             'fontId' => $font_id,
885             'fillId' => $fill_id,
886             'borderId' => $border_id,
887             'xfId' => $xf_id,
888             );
889              
890              
891 1113 100       4624 if ( $format->{_num_format_index} > 0 ) {
892 48         142 push @attributes, ( 'applyNumberFormat' => 1 );
893             }
894              
895             # Add applyFont attribute if XF format uses a font element.
896 1113 100 100     6037 if ( $format->{_font_index} > 0 && !$format->{_hyperlink} ) {
897 132         402 push @attributes, ( 'applyFont' => 1 );
898             }
899              
900             # Add applyFill attribute if XF format uses a fill element.
901 1113 100       4376 if ( $format->{_fill_index} > 0 ) {
902 21         44 push @attributes, ( 'applyFill' => 1 );
903             }
904              
905             # Add applyBorder attribute if XF format uses a border element.
906 1113 100       4477 if ( $format->{_border_index} > 0 ) {
907 45         92 push @attributes, ( 'applyBorder' => 1 );
908             }
909              
910             # Check if XF format has alignment properties set.
911 1113         6008 my ( $apply_align, @align ) = $format->get_align_properties();
912              
913             # Check if an alignment sub-element should be written.
914 1113 100 100     5639 $has_align = 1 if $apply_align && @align;
915              
916             # We can also have applyAlignment without a sub-element.
917 1113 100 100     7884 if ( $apply_align || $format->{_hyperlink} ) {
918 53         141 push @attributes, ( 'applyAlignment' => 1 );
919             }
920              
921             # Check for cell protection properties.
922 1113         5297 my @protection = $format->get_protection_properties();
923              
924 1113 100 100     7800 if ( @protection || $format->{_hyperlink} ) {
925 18         46 push @attributes, ( 'applyProtection' => 1 );
926              
927 18 100       75 if ( !$format->{_hyperlink} ) {
928 10         18 $has_protect = 1;
929             }
930             }
931              
932             # Write XF with sub-elements if required.
933 1113 100 100     6843 if ( $has_align || $has_protect ) {
934 53         223 $self->xml_start_tag( 'xf', @attributes );
935 53 100       275 $self->xml_empty_tag( 'alignment', @align ) if $has_align;
936 53 100       167 $self->xml_empty_tag( 'protection', @protection ) if $has_protect;
937 53         249 $self->xml_end_tag( 'xf' );
938             }
939             else {
940 1060         5173 $self->xml_empty_tag( 'xf', @attributes );
941             }
942             }
943              
944              
945             ##############################################################################
946             #
947             # _write_cell_styles()
948             #
949             # Write the element.
950             #
951             sub _write_cell_styles {
952              
953 819     819   2603 my $self = shift;
954 819         2086 my $count = 1;
955              
956 819 100       3848 if ( $self->{_has_hyperlink} ) {
957 8         20 $count = 2;
958             }
959              
960 819         3461 my @attributes = ( 'count' => $count );
961              
962 819         4163 $self->xml_start_tag( 'cellStyles', @attributes );
963              
964             # Write the cellStyle element.
965 819 100       4513 if ( $self->{_has_hyperlink} ) {
966 8         53 $self->_write_cell_style('Hyperlink', 1, 8);
967             }
968              
969 819         5226 $self->_write_cell_style('Normal', 0, 0);
970              
971 819         3736 $self->xml_end_tag( 'cellStyles' );
972             }
973              
974              
975             ##############################################################################
976             #
977             # _write_cell_style()
978             #
979             # Write the element.
980             #
981             sub _write_cell_style {
982              
983 828     828   2533 my $self = shift;
984 828         2382 my $name = shift;
985 828         2534 my $xf_id = shift;
986 828         2132 my $builtin_id = shift;
987              
988 828         4342 my @attributes = (
989             'name' => $name,
990             'xfId' => $xf_id,
991             'builtinId' => $builtin_id,
992             );
993              
994 828         3975 $self->xml_empty_tag( 'cellStyle', @attributes );
995             }
996              
997              
998             ##############################################################################
999             #
1000             # _write_dxfs()
1001             #
1002             # Write the element.
1003             #
1004             sub _write_dxfs {
1005              
1006 819     819   2718 my $self = shift;
1007 819         2488 my $formats = $self->{_dxf_formats};
1008              
1009 819         2174 my $count = scalar @{$formats};
  819         3332  
1010              
1011 819         3077 my @attributes = ( 'count' => $count );
1012              
1013 819 100       4138 if ( $count ) {
1014 23         100 $self->xml_start_tag( 'dxfs', @attributes );
1015              
1016             # Write the font elements for format objects that have them.
1017 23         51 for my $format ( @{ $self->{_dxf_formats} } ) {
  23         127  
1018 33         130 $self->xml_start_tag( 'dxf' );
1019 33 100       148 $self->_write_font( $format, 1 ) if $format->{_has_dxf_font};
1020              
1021 33 100       129 if ( $format->{_num_format_index} ) {
1022             $self->_write_num_fmt( $format->{_num_format_index},
1023 11         35 $format->{_num_format} );
1024             }
1025              
1026 33 100       196 $self->_write_fill( $format, 1 ) if $format->{_has_dxf_fill};
1027 33 100       135 $self->_write_border( $format, 1 ) if $format->{_has_dxf_border};
1028 33         112 $self->xml_end_tag( 'dxf' );
1029             }
1030              
1031 23         91 $self->xml_end_tag( 'dxfs' );
1032             }
1033             else {
1034 796         3770 $self->xml_empty_tag( 'dxfs', @attributes );
1035             }
1036              
1037             }
1038              
1039              
1040             ##############################################################################
1041             #
1042             # _write_table_styles()
1043             #
1044             # Write the element.
1045             #
1046             sub _write_table_styles {
1047              
1048 818     818   2458 my $self = shift;
1049 818         2794 my $count = 0;
1050 818         2210 my $default_table_style = 'TableStyleMedium9';
1051 818         2079 my $default_pivot_style = 'PivotStyleLight16';
1052              
1053 818         3751 my @attributes = (
1054             'count' => $count,
1055             'defaultTableStyle' => $default_table_style,
1056             'defaultPivotStyle' => $default_pivot_style,
1057             );
1058              
1059 818         3279 $self->xml_empty_tag( 'tableStyles', @attributes );
1060             }
1061              
1062              
1063             ##############################################################################
1064             #
1065             # _write_colors()
1066             #
1067             # Write the element.
1068             #
1069             sub _write_colors {
1070              
1071 821     821   2008 my $self = shift;
1072 821         1906 my @custom_colors = @{ $self->{_custom_colors} };
  821         2861  
1073              
1074 821 100       3906 return unless @custom_colors;
1075              
1076 7         31 $self->xml_start_tag( 'colors' );
1077 7         34 $self->_write_mru_colors( @custom_colors );
1078 7         33 $self->xml_end_tag( 'colors' );
1079             }
1080              
1081              
1082             ##############################################################################
1083             #
1084             # _write_mru_colors()
1085             #
1086             # Write the element for the most recently used colours.
1087             #
1088             sub _write_mru_colors {
1089              
1090 9     9   35 my $self = shift;
1091 9         27 my @custom_colors = @_;
1092              
1093             # Limit the mruColors to the last 10.
1094 9         26 my $count = @custom_colors;
1095 9 100       30 if ( $count > 10 ) {
1096 1         3 splice @custom_colors, 0, ( $count - 10 );
1097             }
1098              
1099 9         45 $self->xml_start_tag( 'mruColors' );
1100              
1101             # Write the custom colors in reverse order.
1102 9         32 for my $color ( reverse @custom_colors ) {
1103 25         52 $self->_write_color( 'rgb' => $color );
1104             }
1105              
1106 9         33 $self->xml_end_tag( 'mruColors' );
1107             }
1108              
1109              
1110             ##############################################################################
1111             #
1112             # _write_condense()
1113             #
1114             # Write the element.
1115             #
1116             sub _write_condense {
1117              
1118 7     7   16 my $self = shift;
1119 7         14 my $val = 0;
1120              
1121 7         30 my @attributes = ( 'val' => $val );
1122              
1123 7         25 $self->xml_empty_tag( 'condense', @attributes );
1124             }
1125              
1126              
1127             ##############################################################################
1128             #
1129             # _write_extend()
1130             #
1131             # Write the element.
1132             #
1133             sub _write_extend {
1134              
1135 7     7   15 my $self = shift;
1136 7         22 my $val = 0;
1137              
1138 7         27 my @attributes = ( 'val' => $val );
1139              
1140 7         21 $self->xml_empty_tag( 'extend', @attributes );
1141             }
1142              
1143              
1144             1;
1145              
1146              
1147             __END__