File Coverage

blib/lib/Excel/Writer/XLSX/Package/Styles.pm
Criterion Covered Total %
statement 381 383 99.4
branch 140 144 97.2
condition 31 33 93.9
subroutine 36 36 100.0
pod 0 1 0.0
total 588 597 98.4


line stmt bran cond sub pod time code
1              
2             ###############################################################################
3             #
4             # Styles - A class for writing the Excel XLSX styles file.
5             #
6             # Used in conjunction with Excel::Writer::XLSX
7             #
8             # Copyright 2000-2021, John McNamara, jmcnamara@cpan.org
9             #
10             # Documentation after __END__
11             #
12              
13             # perltidy with the following options: -mbl=2 -pt=0 -nola
14              
15             use 5.008002;
16 1124     1124   15904 use strict;
  1124         3021  
17 1124     1124   4749 use warnings;
  1124         1818  
  1124         17498  
18 1124     1124   4323 use Carp;
  1124         1783  
  1124         30838  
19 1124     1124   4650 use Excel::Writer::XLSX::Package::XMLwriter;
  1124         1777  
  1124         43479  
20 1124     1124   5276  
  1124         1752  
  1124         3172336  
21             our @ISA = qw(Excel::Writer::XLSX::Package::XMLwriter);
22             our $VERSION = '1.09';
23              
24              
25             ###############################################################################
26             #
27             # Public and private API methods.
28             #
29             ###############################################################################
30              
31              
32             ###############################################################################
33             #
34             # new()
35             #
36             # Constructor.
37             #
38              
39             my $class = shift;
40             my $fh = shift;
41 976     976 0 18297 my $self = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
42 976         1736  
43 976         3177 $self->{_xf_formats} = undef;
44             $self->{_palette} = [];
45 976         2430 $self->{_font_count} = 0;
46 976         2873 $self->{_num_format_count} = 0;
47 976         1932 $self->{_border_count} = 0;
48 976         2260 $self->{_fill_count} = 0;
49 976         2178 $self->{_custom_colors} = [];
50 976         1962 $self->{_dxf_formats} = [];
51 976         3115 $self->{_has_hyperlink} = 0;
52 976         2152 $self->{_hyperlink_font_id} = 0;
53 976         2302 $self->{_has_comments} = 0;
54 976         2040  
55 976         1983 bless $self, $class;
56              
57 976         2179 return $self;
58             }
59 976         2365  
60              
61             ###############################################################################
62             #
63             # _assemble_xml_file()
64             #
65             # Assemble and write the XML file.
66             #
67              
68             my $self = shift;
69              
70             $self->xml_declaration;
71 901     901   2103  
72             # Add the style sheet.
73 901         7182 $self->_write_style_sheet();
74              
75             # Write the number formats.
76 901         4579 $self->_write_num_fmts();
77              
78             # Write the fonts.
79 901         3881 $self->_write_fonts();
80              
81             # Write the fills.
82 901         3903 $self->_write_fills();
83              
84             # Write the borders element.
85 901         3796 $self->_write_borders();
86              
87             # Write the cellStyleXfs element.
88 901         4169 $self->_write_cell_style_xfs();
89              
90             # Write the cellXfs element.
91 901         3894 $self->_write_cell_xfs();
92              
93             # Write the cellStyles element.
94 901         4022 $self->_write_cell_styles();
95              
96             # Write the dxfs element.
97 901         3954 $self->_write_dxfs();
98              
99             # Write the tableStyles element.
100 901         4301 $self->_write_table_styles();
101              
102             # Write the colors element.
103 901         3662 $self->_write_colors();
104              
105             # Close the style sheet tag.
106 901         3462 $self->xml_end_tag( 'styleSheet' );
107              
108             # Close the XML writer filehandle.
109 901         3137 $self->xml_get_fh()->close();
110             }
111              
112 901         5479  
113             ###############################################################################
114             #
115             # _set_style_properties()
116             #
117             # Pass in the Format objects and other properties used to set the styles.
118             #
119              
120             my $self = shift;
121              
122             $self->{_xf_formats} = shift;
123             $self->{_palette} = shift;
124 907     907   2151 $self->{_font_count} = shift;
125             $self->{_num_format_count} = shift;
126 907         5230 $self->{_border_count} = shift;
127 907         2374 $self->{_fill_count} = shift;
128 907         2110 $self->{_custom_colors} = shift;
129 907         2032 $self->{_dxf_formats} = shift;
130 907         2217 $self->{_has_comments} = shift;
131 907         1905 }
132 907         2053  
133 907         1879  
134 907         2468 ###############################################################################
135             #
136             # Internal methods.
137             #
138             ###############################################################################
139              
140              
141             ###############################################################################
142             #
143             # _get_palette_color()
144             #
145             # Convert from an Excel internal colour index to a XML style #RRGGBB index
146             # based on the default or user defined values in the Workbook palette.
147             #
148              
149             my $self = shift;
150             my $index = shift;
151             my $palette = $self->{_palette};
152              
153             # Handle colours in #XXXXXX RGB format.
154 74     74   116 if ( $index =~ m/^#([0-9A-F]{6})$/i ) {
155 74         107 return "FF" . uc( $1 );
156 74         106 }
157              
158             # Adjust the colour index.
159 74 100       233 $index -= 8;
160 28         130  
161             # Palette is passed in from the Workbook class.
162             my @rgb = @{ $palette->[$index] };
163              
164 46         69 return sprintf "FF%02X%02X%02X", @rgb[0, 1, 2];
165             }
166              
167 46         57  
  46         100  
168             ###############################################################################
169 46         214 #
170             # XML writing methods.
171             #
172             ###############################################################################
173              
174              
175             ##############################################################################
176             #
177             # _write_style_sheet()
178             #
179             # Write the <styleSheet> element.
180             #
181              
182             my $self = shift;
183             my $xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
184              
185             my @attributes = ( 'xmlns' => $xmlns );
186              
187             $self->xml_start_tag( 'styleSheet', @attributes );
188 902     902   2050 }
189 902         2118  
190              
191 902         3125 ##############################################################################
192             #
193 902         5020 # _write_num_fmts()
194             #
195             # Write the <numFmts> element.
196             #
197              
198             my $self = shift;
199             my $count = $self->{_num_format_count};
200              
201             return unless $count;
202              
203             my @attributes = ( 'count' => $count );
204              
205 903     903   1921 $self->xml_start_tag( 'numFmts', @attributes );
206 903         2350  
207             # Write the numFmts elements.
208 903 100       3590 for my $format ( @{ $self->{_xf_formats} } ) {
209              
210 10         27 # Ignore built-in number formats, i.e., < 164.
211             next unless $format->{_num_format_index} >= 164;
212 10         46 $self->_write_num_fmt( $format->{_num_format_index},
213             $format->{_num_format} );
214             }
215 10         18  
  10         30  
216             $self->xml_end_tag( 'numFmts' );
217             }
218 38 100       119  
219              
220 19         58 ##############################################################################
221             #
222             # _write_num_fmt()
223 10         50 #
224             # Write the <numFmt> element.
225             #
226              
227             my $self = shift;
228             my $num_fmt_id = shift;
229             my $format_code = shift;
230              
231             my %format_codes = (
232             0 => 'General',
233             1 => '0',
234             2 => '0.00',
235 31     31   57 3 => '#,##0',
236 31         43 4 => '#,##0.00',
237 31         56 5 => '($#,##0_);($#,##0)',
238             6 => '($#,##0_);[Red]($#,##0)',
239 31         639 7 => '($#,##0.00_);($#,##0.00)',
240             8 => '($#,##0.00_);[Red]($#,##0.00)',
241             9 => '0%',
242             10 => '0.00%',
243             11 => '0.00E+00',
244             12 => '# ?/?',
245             13 => '# ??/??',
246             14 => 'm/d/yy',
247             15 => 'd-mmm-yy',
248             16 => 'd-mmm',
249             17 => 'mmm-yy',
250             18 => 'h:mm AM/PM',
251             19 => 'h:mm:ss AM/PM',
252             20 => 'h:mm',
253             21 => 'h:mm:ss',
254             22 => 'm/d/yy h:mm',
255             37 => '(#,##0_);(#,##0)',
256             38 => '(#,##0_);[Red](#,##0)',
257             39 => '(#,##0.00_);(#,##0.00)',
258             40 => '(#,##0.00_);[Red](#,##0.00)',
259             41 => '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
260             42 => '_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)',
261             43 => '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
262             44 => '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)',
263             45 => 'mm:ss',
264             46 => '[h]:mm:ss',
265             47 => 'mm:ss.0',
266             48 => '##0.0E+0',
267             49 => '@',
268             );
269              
270             # Set the format code for built-in number formats.
271             if ( $num_fmt_id < 164 ) {
272             if ( exists $format_codes{$num_fmt_id} ) {
273             $format_code = $format_codes{$num_fmt_id};
274             }
275             else {
276             $format_code = 'General';
277             }
278             }
279 31 100       84  
280 6 50       19 my @attributes = (
281 6         20 'numFmtId' => $num_fmt_id,
282             'formatCode' => $format_code,
283             );
284 0         0  
285             $self->xml_empty_tag( 'numFmt', @attributes );
286             }
287              
288 31         84  
289             ##############################################################################
290             #
291             # _write_fonts()
292             #
293 31         107 # Write the <fonts> element.
294             #
295              
296             my $self = shift;
297             my $count = $self->{_font_count};
298              
299             if ( $self->{_has_comments} ) {
300             # Add an extra font for comments.
301             $count++;
302             }
303              
304             my @attributes = ( 'count' => $count );
305 902     902   1916  
306 902         2228 $self->xml_start_tag( 'fonts', @attributes );
307              
308 902 100       3541 # Write the font elements for format objects that have them.
309             for my $format ( @{ $self->{_xf_formats} } ) {
310 37         88 $self->_write_font( $format ) if $format->{_has_font};
311             }
312              
313 902         3049 if ( $self->{_has_comments} ) {
314             $self->_write_comment_font();
315 902         3659 }
316              
317             $self->xml_end_tag( 'fonts' );
318 902         1902 }
  902         3221  
319 1171 100       6538  
320              
321             ##############################################################################
322 902 100       4228 #
323 37         126 # _write_font()
324             #
325             # Write the <font> element.
326 902         3249 #
327              
328             my $self = shift;
329             my $format = shift;
330             my $dxf_format = shift;
331              
332             $self->xml_start_tag( 'font' );
333              
334             # The condense and extend elements are mainly used in dxf formats.
335             $self->_write_condense() if $format->{_font_condense};
336             $self->_write_extend() if $format->{_font_extend};
337              
338 1069     1069   2422 $self->xml_empty_tag( 'b' ) if $format->{_bold};
339 1069         2072 $self->xml_empty_tag( 'i' ) if $format->{_italic};
340 1069         2060 $self->xml_empty_tag( 'strike' ) if $format->{_font_strikeout};
341             $self->xml_empty_tag( 'outline' ) if $format->{_font_outline};
342 1069         4119 $self->xml_empty_tag( 'shadow' ) if $format->{_font_shadow};
343              
344             # Handle the underline variants.
345 1069 100       4057 $self->_write_underline( $format->{_underline} ) if $format->{_underline};
346 1069 100       3841  
347             $self->_write_vert_align( 'superscript' ) if $format->{_font_script} == 1;
348 1069 100       4216 $self->_write_vert_align( 'subscript' ) if $format->{_font_script} == 2;
349 1069 100       4597  
350 1069 100       4212 if ( !$dxf_format ) {
351 1069 100       3901 $self->xml_empty_tag( 'sz', 'val', $format->{_size} );
352 1069 100       3860 }
353              
354             my $theme = $format->{_theme};
355 1069 100       4128  
356              
357 1069 100       4233 if ( $theme == -1 ) {
358 1069 100       3965 # Ignore for excel2003_style.
359             }
360 1069 100       3688 elsif ( $theme ) {
361 1058         6226 $self->_write_color( 'theme' => $theme );
362             }
363             elsif ( my $index = $format->{_color_indexed} ) {
364 1069         2860 $self->_write_color( 'indexed' => $index );
365             }
366             elsif ( my $color = $format->{_color} ) {
367 1069 100       10598 $color = $self->_get_palette_color( $color );
    100          
    50          
    100          
    100          
368              
369             $self->_write_color( 'rgb' => $color );
370             }
371 9         38 elsif ( !$dxf_format ) {
372             $self->_write_color( 'theme' => 1 );
373             }
374 0         0  
375             if ( !$dxf_format ) {
376             $self->xml_empty_tag( 'name', 'val', $format->{_font} );
377 21         82  
378             if ($format->{_font_family}) {
379 21         73 $self->xml_empty_tag( 'family', 'val', $format->{_font_family} );
380             }
381              
382 1024         4767 if ($format->{_font_charset}) {
383             $self->xml_empty_tag( 'charset', 'val', $format->{_font_charset} );
384             }
385 1069 100       4360  
386 1058         5243 if ( $format->{_font} eq 'Calibri' && !$format->{_hyperlink} ) {
387             $self->xml_empty_tag(
388 1058 100       4013  
389 1049         3911 'scheme',
390             'val' => $format->{_font_scheme}
391             );
392 1058 100       4253 }
393 1         2  
394             if ( $format->{_hyperlink} ) {
395             $self->{_has_hyperlink} = 1;
396 1058 100 100     8368  
397             if ( !$self->{_hyperlink_font_id} ) {
398             $self->{_hyperlink_font_id} = $format->{_font_index};
399             }
400             }
401 1034         4073 }
402              
403             $self->xml_end_tag( 'font' );
404 1058 100       4152 }
405 9         16  
406             ##############################################################################
407 9 50       24 #
408 9         13 # _write_comment_font()
409             #
410             # Write the <font> element used for comments.
411             #
412              
413 1069         5855 my $self = shift;
414              
415             $self->xml_start_tag( 'font' );
416              
417             $self->xml_empty_tag( 'sz', 'val', 8 );
418             $self->_write_color( 'indexed' => 81 );
419             $self->xml_empty_tag( 'name', 'val', 'Tahoma' );
420             $self->xml_empty_tag( 'family', 'val', 2 );
421              
422             $self->xml_end_tag( 'font' );
423             }
424 37     37   90  
425             ###############################################################################
426 37         184 #
427             # _write_underline()
428 37         142 #
429 37         146 # Write the underline font element.
430 37         135 #
431 37         122  
432             my $self = shift;
433 37         103 my $underline = shift;
434             my @attributes;
435              
436             # Handle the underline variants.
437             if ( $underline == 2 ) {
438             @attributes = ( val => 'double' );
439             }
440             elsif ( $underline == 33 ) {
441             @attributes = ( val => 'singleAccounting' );
442             }
443             elsif ( $underline == 34 ) {
444 27     27   46 @attributes = ( val => 'doubleAccounting' );
445 27         42 }
446 27         132 else {
447             @attributes = (); # Default to single underline.
448             }
449 27 100       137  
    100          
    100          
450 1         4 $self->xml_empty_tag( 'u', @attributes );
451              
452             }
453 1         2  
454              
455             ##############################################################################
456 1         2 #
457             # _write_vert_align()
458             #
459 24         61 # Write the <vertAlign> font sub-element.
460             #
461              
462 27         83 my $self = shift;
463             my $val = shift;
464              
465             my @attributes = ( 'val' => $val );
466              
467             $self->xml_empty_tag( 'vertAlign', @attributes );
468             }
469              
470              
471             ##############################################################################
472             #
473             # _write_color()
474             #
475 5     5   12 # Write the <color> element.
476 5         6 #
477              
478 5         11 my $self = shift;
479             my $name = shift;
480 5         14 my $value = shift;
481              
482             my @attributes = ( $name => $value );
483              
484             $self->xml_empty_tag( 'color', @attributes );
485             }
486              
487              
488             ##############################################################################
489             #
490             # _write_fills()
491             #
492 1116     1116   2596 # Write the <fills> element.
493 1116         2224 #
494 1116         1980  
495             my $self = shift;
496 1116         3352 my $count = $self->{_fill_count};
497              
498 1116         4101 my @attributes = ( 'count' => $count );
499              
500             $self->xml_start_tag( 'fills', @attributes );
501              
502             # Write the default fill element.
503             $self->_write_default_fill( 'none' );
504             $self->_write_default_fill( 'gray125' );
505              
506             # Write the fill elements for format objects that have them.
507             for my $format ( @{ $self->{_xf_formats} } ) {
508             $self->_write_fill( $format ) if $format->{_has_fill};
509             }
510 902     902   1979  
511 902         2215 $self->xml_end_tag( 'fills' );
512             }
513 902         2846  
514              
515 902         3733 ##############################################################################
516             #
517             # _write_default_fill()
518 902         3578 #
519 902         3019 # Write the <fill> element for the default fills.
520             #
521              
522 902         1659 my $self = shift;
  902         2935  
523 1170 100       4519 my $pattern_type = shift;
524              
525             $self->xml_start_tag( 'fill' );
526 902         3563  
527             $self->xml_empty_tag( 'patternFill', 'patternType', $pattern_type );
528              
529             $self->xml_end_tag( 'fill' );
530             }
531              
532              
533             ##############################################################################
534             #
535             # _write_fill()
536             #
537             # Write the <fill> element.
538 1806     1806   3134 #
539 1806         3341  
540             my $self = shift;
541 1806         5022 my $format = shift;
542             my $dxf_format = shift;
543 1806         5370 my $pattern = $format->{_pattern};
544             my $bg_color = $format->{_bg_color};
545 1806         4530 my $fg_color = $format->{_fg_color};
546              
547             # Colors for dxf formats are handled differently from normal formats since
548             # the normal format reverses the meaning of BG and FG for solid fills.
549             if ( $dxf_format ) {
550             $bg_color = $format->{_dxf_bg_color};
551             $fg_color = $format->{_dxf_fg_color};
552             }
553              
554              
555             my @patterns = qw(
556             none
557 35     35   63 solid
558 35         60 mediumGray
559 35         49 darkGray
560 35         67 lightGray
561 35         59 darkHorizontal
562 35         64 darkVertical
563             darkDown
564             darkUp
565             darkGrid
566 35 100       76 darkTrellis
567 17         33 lightHorizontal
568 17         44 lightVertical
569             lightDown
570             lightUp
571             lightGrid
572 35         182 lightTrellis
573             gray125
574             gray0625
575              
576             );
577              
578             # Special handling for pattern only case.
579             if ( !$fg_color && !$bg_color && $format->{_pattern} ) {
580             $self->_write_default_fill( $patterns[ $format->{_pattern} ] );
581             return;
582             }
583              
584             $self->xml_start_tag( 'fill' );
585              
586             # The "none" pattern is handled differently for dxf formats.
587             if ( $dxf_format && $format->{_pattern} <= 1 ) {
588             $self->xml_start_tag( 'patternFill' );
589             }
590             else {
591             $self->xml_start_tag(
592             'patternFill',
593             'patternType',
594             $patterns[ $format->{_pattern} ]
595              
596 35 50 100     144 );
      66        
597 1         4 }
598 1         3  
599             if ( $fg_color ) {
600             $fg_color = $self->_get_palette_color( $fg_color );
601 34         93 $self->xml_empty_tag( 'fgColor', 'rgb' => $fg_color );
602             }
603              
604 34 100 100     175 if ( $bg_color ) {
605 15         42 $bg_color = $self->_get_palette_color( $bg_color );
606             $self->xml_empty_tag( 'bgColor', 'rgb' => $bg_color );
607             }
608             else {
609             if ( !$dxf_format && $format->{_pattern} <= 1) {
610             $self->xml_empty_tag( 'bgColor', 'indexed' => 64 );
611 19         50 }
612             }
613              
614             $self->xml_end_tag( 'patternFill' );
615             $self->xml_end_tag( 'fill' );
616 34 100       83 }
617 18         45  
618 18         55  
619             ##############################################################################
620             #
621 34 100       91 # _write_borders()
622 25         71 #
623 25         69 # Write the <borders> element.
624             #
625              
626 9 100 66     42 my $self = shift;
627 8         22 my $count = $self->{_border_count};
628              
629             my @attributes = ( 'count' => $count );
630              
631 34         117 $self->xml_start_tag( 'borders', @attributes );
632 34         80  
633             # Write the border elements for format objects that have them.
634             for my $format ( @{ $self->{_xf_formats} } ) {
635             $self->_write_border( $format ) if $format->{_has_border};
636             }
637              
638             $self->xml_end_tag( 'borders' );
639             }
640              
641              
642             ##############################################################################
643             #
644 902     902   2016 # _write_border()
645 902         2374 #
646             # Write the <border> element.
647 902         2784 #
648              
649 902         3879 my $self = shift;
650             my $format = shift;
651             my $dxf_format = shift;
652 902         1904 my @attributes = ();
  902         2903  
653 1171 100       6409  
654              
655             # Diagonal borders add attributes to the <border> element.
656 902         3176 if ( $format->{_diag_type} == 1 ) {
657             push @attributes, ( diagonalUp => 1 );
658             }
659             elsif ( $format->{_diag_type} == 2 ) {
660             push @attributes, ( diagonalDown => 1 );
661             }
662             elsif ( $format->{_diag_type} == 3 ) {
663             push @attributes, ( diagonalUp => 1 );
664             push @attributes, ( diagonalDown => 1 );
665             }
666              
667             # Ensure that a default diag border is set if the diag type is set.
668 949     949   2330 if ( $format->{_diag_type} && !$format->{_diag_border} ) {
669 949         2025 $format->{_diag_border} = 1;
670 949         1951 }
671 949         2395  
672             # Write the start border tag.
673             $self->xml_start_tag( 'border', @attributes );
674              
675 949 100       7577 # Write the <border> sub elements.
    100          
    100          
676 2         5 $self->_write_sub_border(
677             'left',
678             $format->{_left},
679 2         3 $format->{_left_color}
680              
681             );
682 4         8  
683 4         5 $self->_write_sub_border(
684             'right',
685             $format->{_right},
686             $format->{_right_color}
687 949 100 100     4610  
688 1         2 );
689              
690             $self->_write_sub_border(
691             'top',
692 949         3918 $format->{_top},
693             $format->{_top_color}
694              
695             );
696              
697             $self->_write_sub_border(
698             'bottom',
699             $format->{_bottom},
700 949         4891 $format->{_bottom_color}
701              
702             );
703              
704             # Condition DXF formats don't allow diagonal borders
705             if ( !$dxf_format ) {
706             $self->_write_sub_border(
707 949         3868 'diagonal',
708             $format->{_diag_border},
709             $format->{_diag_color}
710              
711             );
712             }
713              
714 949         3776 if ( $dxf_format ) {
715             $self->_write_sub_border( 'vertical' );
716             $self->_write_sub_border( 'horizontal' );
717             }
718              
719             $self->xml_end_tag( 'border' );
720             }
721 949         4037  
722              
723             ##############################################################################
724 949 100       3475 #
725             # _write_sub_border()
726             #
727             # Write the <border> sub elements such as <right>, <top>, etc.
728             #
729              
730 948         3917 my $self = shift;
731             my $type = shift;
732             my $style = shift;
733 949 100       3673 my $color = shift;
734 1         3 my @attributes;
735 1         3  
736             if ( !$style ) {
737             $self->xml_empty_tag( $type );
738 949         3531 return;
739             }
740              
741             my @border_styles = qw(
742             none
743             thin
744             medium
745             dashed
746             dotted
747             thick
748             double
749             hair
750 4746     4746   6715 mediumDashed
751 4746         6341 dashDot
752 4746         6390 mediumDashDot
753 4746         6109 dashDotDot
754 4746         5989 mediumDashDotDot
755             slantDashDot
756 4746 100       8642  
757 4685         11209 );
758 4685         7762  
759              
760             push @attributes, ( style => $border_styles[$style] );
761 61         141  
762             $self->xml_start_tag( $type, @attributes );
763              
764             if ( $color ) {
765             $color = $self->_get_palette_color( $color );
766             $self->xml_empty_tag( 'color', 'rgb' => $color );
767             }
768             else {
769             $self->xml_empty_tag( 'color', 'auto' => 1 );
770             }
771              
772             $self->xml_end_tag( $type );
773             }
774              
775              
776             ##############################################################################
777             #
778             # _write_cell_style_xfs()
779             #
780 61         83 # Write the <cellStyleXfs> element.
781             #
782 61         119  
783             my $self = shift;
784 61 100       95 my $count = 1;
785 10         14  
786 10         22 if ( $self->{_has_hyperlink} ) {
787             $count = 2;
788             }
789 51         85  
790             my @attributes = ( 'count' => $count );
791              
792 61         111 $self->xml_start_tag( 'cellStyleXfs', @attributes );
793              
794             # Write the style_xf element.
795             $self->_write_style_xf( 0, 0 );
796              
797             if ( $self->{_has_hyperlink} ) {
798             $self->_write_style_xf( 1, $self->{_hyperlink_font_id} );
799             }
800              
801             $self->xml_end_tag( 'cellStyleXfs' );
802             }
803              
804 902     902   2164  
805 902         2005 ##############################################################################
806             #
807 902 100       3623 # _write_cell_xfs()
808 8         12 #
809             # Write the <cellXfs> element.
810             #
811 902         2964  
812             my $self = shift;
813 902         3603 my @formats = @{ $self->{_xf_formats} };
814             my $count = scalar @formats;
815              
816 902         4048 my @attributes = ( 'count' => $count );
817              
818 902 100       3636 $self->xml_start_tag( 'cellXfs', @attributes );
819 8         17  
820             # Write the xf elements.
821             for my $format ( @formats ) {
822 902         3312 $self->_write_xf( $format );
823             }
824              
825             $self->xml_end_tag( 'cellXfs' );
826             }
827              
828              
829             ##############################################################################
830             #
831             # _write_style_xf()
832             #
833             # Write the style <xf> element.
834 902     902   2252 #
835 902         2002  
  902         3151  
836 902         3315 my $self = shift;
837             my $has_hyperlink = shift;
838 902         2605 my $font_id = shift;
839             my $num_fmt_id = 0;
840 902         3954 my $fill_id = 0;
841             my $border_id = 0;
842              
843 902         2546 my @attributes = (
844 1171         4487 'numFmtId' => $num_fmt_id,
845             'fontId' => $font_id,
846             'fillId' => $fill_id,
847 902         3282 'borderId' => $border_id,
848             );
849              
850             if ( $has_hyperlink ) {
851             push @attributes, ( 'applyNumberFormat' => 0 );
852             push @attributes, ( 'applyFill' => 0 );
853             push @attributes, ( 'applyBorder' => 0 );
854             push @attributes, ( 'applyAlignment' => 0 );
855             push @attributes, ( 'applyProtection' => 0 );
856              
857             $self->xml_start_tag( 'xf', @attributes );
858             $self->xml_empty_tag( 'alignment', ( 'vertical', 'top' ) );
859 911     911   2327 $self->xml_empty_tag( 'protection', ( 'locked', 0 ) );
860 911         1874 $self->xml_end_tag( 'xf' );
861 911         1832 }
862 911         1995 else {
863 911         1989 $self->xml_empty_tag( 'xf', @attributes );
864 911         2052 }
865             }
866 911         4033  
867              
868             ##############################################################################
869             #
870             # _write_xf()
871             #
872             # Write the <xf> element.
873 911 100       3692 #
874 8         23  
875 8         23 my $self = shift;
876 8         16 my $format = shift;
877 8         14 my $num_fmt_id = $format->{_num_format_index};
878 8         14 my $font_id = $format->{_font_index};
879             my $fill_id = $format->{_fill_index};
880 8         25 my $border_id = $format->{_border_index};
881 8         26 my $xf_id = $format->{_xf_id};
882 8         19 my $has_align = 0;
883 8         29 my $has_protect = 0;
884              
885             my @attributes = (
886 903         3696 'numFmtId' => $num_fmt_id,
887             'fontId' => $font_id,
888             'fillId' => $fill_id,
889             'borderId' => $border_id,
890             'xfId' => $xf_id,
891             );
892              
893              
894             if ( $format->{_num_format_index} > 0 ) {
895             push @attributes, ( 'applyNumberFormat' => 1 );
896             }
897              
898             # Add applyFont attribute if XF format uses a font element.
899 1207     1207   2811 if ( $format->{_font_index} > 0 && !$format->{_hyperlink} ) {
900 1207         2930 push @attributes, ( 'applyFont' => 1 );
901 1207         2846 }
902 1207         2538  
903 1207         2518 # Add applyFill attribute if XF format uses a fill element.
904 1207         2308 if ( $format->{_fill_index} > 0 ) {
905 1207         2316 push @attributes, ( 'applyFill' => 1 );
906 1207         2004 }
907 1207         2222  
908             # Add applyBorder attribute if XF format uses a border element.
909 1207         4496 if ( $format->{_border_index} > 0 ) {
910             push @attributes, ( 'applyBorder' => 1 );
911             }
912              
913             # Check if XF format has alignment properties set.
914             my ( $apply_align, @align ) = $format->get_align_properties();
915              
916             # Check if an alignment sub-element should be written.
917             $has_align = 1 if $apply_align && @align;
918 1207 100       4431  
919 48         119 # We can also have applyAlignment without a sub-element.
920             if ( $apply_align || $format->{_hyperlink} ) {
921             push @attributes, ( 'applyAlignment' => 1 );
922             }
923 1207 100 100     5909  
924 135         394 # Check for cell protection properties.
925             my @protection = $format->get_protection_properties();
926              
927             if ( @protection || $format->{_hyperlink} ) {
928 1207 100       4022 push @attributes, ( 'applyProtection' => 1 );
929 23         43  
930             if ( !$format->{_hyperlink} ) {
931             $has_protect = 1;
932             }
933 1207 100       4181 }
934 45         80  
935             # Write XF with sub-elements if required.
936             if ( $has_align || $has_protect ) {
937             $self->xml_start_tag( 'xf', @attributes );
938 1207         5626 $self->xml_empty_tag( 'alignment', @align ) if $has_align;
939             $self->xml_empty_tag( 'protection', @protection ) if $has_protect;
940             $self->xml_end_tag( 'xf' );
941 1207 100 100     4914 }
942             else {
943             $self->xml_empty_tag( 'xf', @attributes );
944 1207 100 100     6625 }
945 53         164 }
946              
947              
948             ##############################################################################
949 1207         4806 #
950             # _write_cell_styles()
951 1207 100 100     6903 #
952 24         61 # Write the <cellStyles> element.
953             #
954 24 100       82  
955 16         24 my $self = shift;
956             my $count = 1;
957              
958             if ( $self->{_has_hyperlink} ) {
959             $count = 2;
960 1207 100 100     6228 }
961 59         240  
962 59 100       207 my @attributes = ( 'count' => $count );
963 59 100       172  
964 59         158 $self->xml_start_tag( 'cellStyles', @attributes );
965              
966             # Write the cellStyle element.
967 1148         4193 if ( $self->{_has_hyperlink} ) {
968             $self->_write_cell_style('Hyperlink', 1, 8);
969             }
970              
971             $self->_write_cell_style('Normal', 0, 0);
972              
973             $self->xml_end_tag( 'cellStyles' );
974             }
975              
976              
977             ##############################################################################
978             #
979             # _write_cell_style()
980 902     902   2009 #
981 902         1805 # Write the <cellStyle> element.
982             #
983 902 100       3473  
984 8         36 my $self = shift;
985             my $name = shift;
986             my $xf_id = shift;
987 902         2914 my $builtin_id = shift;
988              
989 902         3839 my @attributes = (
990             'name' => $name,
991             'xfId' => $xf_id,
992 902 100       3610 'builtinId' => $builtin_id,
993 8         22 );
994              
995             $self->xml_empty_tag( 'cellStyle', @attributes );
996 902         4346 }
997              
998 902         2672  
999             ##############################################################################
1000             #
1001             # _write_dxfs()
1002             #
1003             # Write the <dxfs> element.
1004             #
1005              
1006             my $self = shift;
1007             my $formats = $self->{_dxf_formats};
1008              
1009             my $count = scalar @{$formats};
1010 911     911   2049  
1011 911         1942 my @attributes = ( 'count' => $count );
1012 911         1798  
1013 911         1832 if ( $count ) {
1014             $self->xml_start_tag( 'dxfs', @attributes );
1015 911         3383  
1016             # Write the font elements for format objects that have them.
1017             for my $format ( @{ $self->{_dxf_formats} } ) {
1018             $self->xml_start_tag( 'dxf' );
1019             $self->_write_font( $format, 1 ) if $format->{_has_dxf_font};
1020              
1021 911         3216 if ( $format->{_num_format_index} ) {
1022             $self->_write_num_fmt( $format->{_num_format_index},
1023             $format->{_num_format} );
1024             }
1025              
1026             $self->_write_fill( $format, 1 ) if $format->{_has_dxf_fill};
1027             $self->_write_border( $format, 1 ) if $format->{_has_dxf_border};
1028             $self->xml_end_tag( 'dxf' );
1029             }
1030              
1031             $self->xml_end_tag( 'dxfs' );
1032             }
1033 902     902   2023 else {
1034 902         2155 $self->xml_empty_tag( 'dxfs', @attributes );
1035             }
1036 902         1808  
  902         2110  
1037             }
1038 902         2689  
1039              
1040 902 100       3428 ##############################################################################
1041 23         78 #
1042             # _write_table_styles()
1043             #
1044 23         35 # Write the <tableStyles> element.
  23         86  
1045 33         119 #
1046 33 100       117  
1047             my $self = shift;
1048 33 100       111 my $count = 0;
1049             my $default_table_style = 'TableStyleMedium9';
1050 11         43 my $default_pivot_style = 'PivotStyleLight16';
1051              
1052             my @attributes = (
1053 33 100       204 'count' => $count,
1054 33 100       101 'defaultTableStyle' => $default_table_style,
1055 33         87 'defaultPivotStyle' => $default_pivot_style,
1056             );
1057              
1058 23         77 $self->xml_empty_tag( 'tableStyles', @attributes );
1059             }
1060              
1061 879         3242  
1062             ##############################################################################
1063             #
1064             # _write_colors()
1065             #
1066             # Write the <colors> element.
1067             #
1068              
1069             my $self = shift;
1070             my @custom_colors = @{ $self->{_custom_colors} };
1071              
1072             return unless @custom_colors;
1073              
1074             $self->xml_start_tag( 'colors' );
1075 901     901   2024 $self->_write_mru_colors( @custom_colors );
1076 901         1766 $self->xml_end_tag( 'colors' );
1077 901         1998 }
1078 901         1806  
1079              
1080 901         3495 ##############################################################################
1081             #
1082             # _write_mru_colors()
1083             #
1084             # Write the <mruColors> element for the most recently used colours.
1085             #
1086 901         3312  
1087             my $self = shift;
1088             my @custom_colors = @_;
1089              
1090             # Limit the mruColors to the last 10.
1091             my $count = @custom_colors;
1092             if ( $count > 10 ) {
1093             splice @custom_colors, 0, ( $count - 10 );
1094             }
1095              
1096             $self->xml_start_tag( 'mruColors' );
1097              
1098 904     904   1804 # Write the custom colors in reverse order.
1099 904         1691 for my $color ( reverse @custom_colors ) {
  904         2594  
1100             $self->_write_color( 'rgb' => $color );
1101 904 100       3456 }
1102              
1103 7         25 $self->xml_end_tag( 'mruColors' );
1104 7         25 }
1105 7         18  
1106              
1107             ##############################################################################
1108             #
1109             # _write_condense()
1110             #
1111             # Write the <condense> element.
1112             #
1113              
1114             my $self = shift;
1115             my $val = 0;
1116              
1117 9     9   28 my @attributes = ( 'val' => $val );
1118 9         18  
1119             $self->xml_empty_tag( 'condense', @attributes );
1120             }
1121 9         16  
1122 9 100       26  
1123 1         3 ##############################################################################
1124             #
1125             # _write_extend()
1126 9         29 #
1127             # Write the <extend> element.
1128             #
1129 9         21  
1130 25         57 my $self = shift;
1131             my $val = 0;
1132              
1133 9         30 my @attributes = ( 'val' => $val );
1134              
1135             $self->xml_empty_tag( 'extend', @attributes );
1136             }
1137              
1138              
1139             1;
1140              
1141              
1142              
1143             =pod
1144              
1145 7     7   11 =head1 NAME
1146 7         13  
1147             Styles - A class for writing the Excel XLSX styles file.
1148 7         15  
1149             =head1 SYNOPSIS
1150 7         17  
1151             See the documentation for L<Excel::Writer::XLSX>.
1152              
1153             =head1 DESCRIPTION
1154              
1155             This module is used in conjunction with L<Excel::Writer::XLSX>.
1156              
1157             =head1 AUTHOR
1158              
1159             John McNamara jmcnamara@cpan.org
1160              
1161             =head1 COPYRIGHT
1162 7     7   12  
1163 7         12 (c) MM-MMXXI, John McNamara.
1164              
1165 7         14 All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.
1166              
1167 7         16 =head1 LICENSE
1168              
1169             Either the Perl Artistic Licence L<http://dev.perl.org/licenses/artistic.html> or the GPL L<http://www.opensource.org/licenses/gpl-license.php>.
1170              
1171             =head1 DISCLAIMER OF WARRANTY
1172              
1173             See the documentation for L<Excel::Writer::XLSX>.
1174              
1175             =cut