%PDF- %PDF-
Direktori : C:/xampp/perl/vendor/lib/Excel/Writer/XLSX/Package/ |
Current File : C:/xampp/perl/vendor/lib/Excel/Writer/XLSX/Package/Styles.pm |
package Excel::Writer::XLSX::Package::Styles; ############################################################################### # # Styles - A class for writing the Excel XLSX styles file. # # Used in conjunction with Excel::Writer::XLSX # # Copyright 2000-2019, John McNamara, jmcnamara@cpan.org # # Documentation after __END__ # # perltidy with the following options: -mbl=2 -pt=0 -nola use 5.008002; use strict; use warnings; use Carp; use Excel::Writer::XLSX::Package::XMLwriter; our @ISA = qw(Excel::Writer::XLSX::Package::XMLwriter); our $VERSION = '1.03'; ############################################################################### # # Public and private API methods. # ############################################################################### ############################################################################### # # new() # # Constructor. # sub new { my $class = shift; my $fh = shift; my $self = Excel::Writer::XLSX::Package::XMLwriter->new( $fh ); $self->{_xf_formats} = undef; $self->{_palette} = []; $self->{_font_count} = 0; $self->{_num_format_count} = 0; $self->{_border_count} = 0; $self->{_fill_count} = 0; $self->{_custom_colors} = []; $self->{_dxf_formats} = []; $self->{_has_hyperlink} = 0; $self->{_hyperlink_font_id} = 0; bless $self, $class; return $self; } ############################################################################### # # _assemble_xml_file() # # Assemble and write the XML file. # sub _assemble_xml_file { my $self = shift; $self->xml_declaration; # Add the style sheet. $self->_write_style_sheet(); # Write the number formats. $self->_write_num_fmts(); # Write the fonts. $self->_write_fonts(); # Write the fills. $self->_write_fills(); # Write the borders element. $self->_write_borders(); # Write the cellStyleXfs element. $self->_write_cell_style_xfs(); # Write the cellXfs element. $self->_write_cell_xfs(); # Write the cellStyles element. $self->_write_cell_styles(); # Write the dxfs element. $self->_write_dxfs(); # Write the tableStyles element. $self->_write_table_styles(); # Write the colors element. $self->_write_colors(); # Close the style sheet tag. $self->xml_end_tag( 'styleSheet' ); # Close the XML writer filehandle. $self->xml_get_fh()->close(); } ############################################################################### # # _set_style_properties() # # Pass in the Format objects and other properties used to set the styles. # sub _set_style_properties { my $self = shift; $self->{_xf_formats} = shift; $self->{_palette} = shift; $self->{_font_count} = shift; $self->{_num_format_count} = shift; $self->{_border_count} = shift; $self->{_fill_count} = shift; $self->{_custom_colors} = shift; $self->{_dxf_formats} = shift; } ############################################################################### # # Internal methods. # ############################################################################### ############################################################################### # # _get_palette_color() # # Convert from an Excel internal colour index to a XML style #RRGGBB index # based on the default or user defined values in the Workbook palette. # sub _get_palette_color { my $self = shift; my $index = shift; my $palette = $self->{_palette}; # Handle colours in #XXXXXX RGB format. if ( $index =~ m/^#([0-9A-F]{6})$/i ) { return "FF" . uc( $1 ); } # Adjust the colour index. $index -= 8; # Palette is passed in from the Workbook class. my @rgb = @{ $palette->[$index] }; return sprintf "FF%02X%02X%02X", @rgb[0, 1, 2]; } ############################################################################### # # XML writing methods. # ############################################################################### ############################################################################## # # _write_style_sheet() # # Write the <styleSheet> element. # sub _write_style_sheet { my $self = shift; my $xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'; my @attributes = ( 'xmlns' => $xmlns ); $self->xml_start_tag( 'styleSheet', @attributes ); } ############################################################################## # # _write_num_fmts() # # Write the <numFmts> element. # sub _write_num_fmts { my $self = shift; my $count = $self->{_num_format_count}; return unless $count; my @attributes = ( 'count' => $count ); $self->xml_start_tag( 'numFmts', @attributes ); # Write the numFmts elements. for my $format ( @{ $self->{_xf_formats} } ) { # Ignore built-in number formats, i.e., < 164. next unless $format->{_num_format_index} >= 164; $self->_write_num_fmt( $format->{_num_format_index}, $format->{_num_format} ); } $self->xml_end_tag( 'numFmts' ); } ############################################################################## # # _write_num_fmt() # # Write the <numFmt> element. # sub _write_num_fmt { my $self = shift; my $num_fmt_id = shift; my $format_code = shift; my %format_codes = ( 0 => 'General', 1 => '0', 2 => '0.00', 3 => '#,##0', 4 => '#,##0.00', 5 => '($#,##0_);($#,##0)', 6 => '($#,##0_);[Red]($#,##0)', 7 => '($#,##0.00_);($#,##0.00)', 8 => '($#,##0.00_);[Red]($#,##0.00)', 9 => '0%', 10 => '0.00%', 11 => '0.00E+00', 12 => '# ?/?', 13 => '# ??/??', 14 => 'm/d/yy', 15 => 'd-mmm-yy', 16 => 'd-mmm', 17 => 'mmm-yy', 18 => 'h:mm AM/PM', 19 => 'h:mm:ss AM/PM', 20 => 'h:mm', 21 => 'h:mm:ss', 22 => 'm/d/yy h:mm', 37 => '(#,##0_);(#,##0)', 38 => '(#,##0_);[Red](#,##0)', 39 => '(#,##0.00_);(#,##0.00)', 40 => '(#,##0.00_);[Red](#,##0.00)', 41 => '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)', 42 => '_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)', 43 => '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)', 44 => '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)', 45 => 'mm:ss', 46 => '[h]:mm:ss', 47 => 'mm:ss.0', 48 => '##0.0E+0', 49 => '@', ); # Set the format code for built-in number formats. if ( $num_fmt_id < 164 ) { if ( exists $format_codes{$num_fmt_id} ) { $format_code = $format_codes{$num_fmt_id}; } else { $format_code = 'General'; } } my @attributes = ( 'numFmtId' => $num_fmt_id, 'formatCode' => $format_code, ); $self->xml_empty_tag( 'numFmt', @attributes ); } ############################################################################## # # _write_fonts() # # Write the <fonts> element. # sub _write_fonts { my $self = shift; my $count = $self->{_font_count}; my @attributes = ( 'count' => $count ); $self->xml_start_tag( 'fonts', @attributes ); # Write the font elements for format objects that have them. for my $format ( @{ $self->{_xf_formats} } ) { $self->_write_font( $format ) if $format->{_has_font}; } $self->xml_end_tag( 'fonts' ); } ############################################################################## # # _write_font() # # Write the <font> element. # sub _write_font { my $self = shift; my $format = shift; my $dxf_format = shift; $self->xml_start_tag( 'font' ); # The condense and extend elements are mainly used in dxf formats. $self->_write_condense() if $format->{_font_condense}; $self->_write_extend() if $format->{_font_extend}; $self->xml_empty_tag( 'b' ) if $format->{_bold}; $self->xml_empty_tag( 'i' ) if $format->{_italic}; $self->xml_empty_tag( 'strike' ) if $format->{_font_strikeout}; $self->xml_empty_tag( 'outline' ) if $format->{_font_outline}; $self->xml_empty_tag( 'shadow' ) if $format->{_font_shadow}; # Handle the underline variants. $self->_write_underline( $format->{_underline} ) if $format->{_underline}; $self->_write_vert_align( 'superscript' ) if $format->{_font_script} == 1; $self->_write_vert_align( 'subscript' ) if $format->{_font_script} == 2; if ( !$dxf_format ) { $self->xml_empty_tag( 'sz', 'val', $format->{_size} ); } my $theme = $format->{_theme}; if ( $theme == -1 ) { # Ignore for excel2003_style. } elsif ( $theme ) { $self->_write_color( 'theme' => $theme ); } elsif ( my $index = $format->{_color_indexed} ) { $self->_write_color( 'indexed' => $index ); } elsif ( my $color = $format->{_color} ) { $color = $self->_get_palette_color( $color ); $self->_write_color( 'rgb' => $color ); } elsif ( !$dxf_format ) { $self->_write_color( 'theme' => 1 ); } if ( !$dxf_format ) { $self->xml_empty_tag( 'name', 'val', $format->{_font} ); if ($format->{_font_family}) { $self->xml_empty_tag( 'family', 'val', $format->{_font_family} ); } if ($format->{_font_charset}) { $self->xml_empty_tag( 'charset', 'val', $format->{_font_charset} ); } if ( $format->{_font} eq 'Calibri' && !$format->{_hyperlink} ) { $self->xml_empty_tag( 'scheme', 'val' => $format->{_font_scheme} ); } if ( $format->{_hyperlink} ) { $self->{_has_hyperlink} = 1; if ( !$self->{_hyperlink_font_id} ) { $self->{_hyperlink_font_id} = $format->{_font_index}; } } } $self->xml_end_tag( 'font' ); } ############################################################################### # # _write_underline() # # Write the underline font element. # sub _write_underline { my $self = shift; my $underline = shift; my @attributes; # Handle the underline variants. if ( $underline == 2 ) { @attributes = ( val => 'double' ); } elsif ( $underline == 33 ) { @attributes = ( val => 'singleAccounting' ); } elsif ( $underline == 34 ) { @attributes = ( val => 'doubleAccounting' ); } else { @attributes = (); # Default to single underline. } $self->xml_empty_tag( 'u', @attributes ); } ############################################################################## # # _write_vert_align() # # Write the <vertAlign> font sub-element. # sub _write_vert_align { my $self = shift; my $val = shift; my @attributes = ( 'val' => $val ); $self->xml_empty_tag( 'vertAlign', @attributes ); } ############################################################################## # # _write_color() # # Write the <color> element. # sub _write_color { my $self = shift; my $name = shift; my $value = shift; my @attributes = ( $name => $value ); $self->xml_empty_tag( 'color', @attributes ); } ############################################################################## # # _write_fills() # # Write the <fills> element. # sub _write_fills { my $self = shift; my $count = $self->{_fill_count}; my @attributes = ( 'count' => $count ); $self->xml_start_tag( 'fills', @attributes ); # Write the default fill element. $self->_write_default_fill( 'none' ); $self->_write_default_fill( 'gray125' ); # Write the fill elements for format objects that have them. for my $format ( @{ $self->{_xf_formats} } ) { $self->_write_fill( $format ) if $format->{_has_fill}; } $self->xml_end_tag( 'fills' ); } ############################################################################## # # _write_default_fill() # # Write the <fill> element for the default fills. # sub _write_default_fill { my $self = shift; my $pattern_type = shift; $self->xml_start_tag( 'fill' ); $self->xml_empty_tag( 'patternFill', 'patternType', $pattern_type ); $self->xml_end_tag( 'fill' ); } ############################################################################## # # _write_fill() # # Write the <fill> element. # sub _write_fill { my $self = shift; my $format = shift; my $dxf_format = shift; my $pattern = $format->{_pattern}; my $bg_color = $format->{_bg_color}; my $fg_color = $format->{_fg_color}; # Colors for dxf formats are handled differently from normal formats since # the normal format reverses the meaning of BG and FG for solid fills. if ( $dxf_format ) { $bg_color = $format->{_dxf_bg_color}; $fg_color = $format->{_dxf_fg_color}; } my @patterns = qw( none solid mediumGray darkGray lightGray darkHorizontal darkVertical darkDown darkUp darkGrid darkTrellis lightHorizontal lightVertical lightDown lightUp lightGrid lightTrellis gray125 gray0625 ); $self->xml_start_tag( 'fill' ); # The "none" pattern is handled differently for dxf formats. if ( $dxf_format && $format->{_pattern} <= 1 ) { $self->xml_start_tag( 'patternFill' ); } else { $self->xml_start_tag( 'patternFill', 'patternType', $patterns[ $format->{_pattern} ] ); } if ( $fg_color ) { $fg_color = $self->_get_palette_color( $fg_color ); $self->xml_empty_tag( 'fgColor', 'rgb' => $fg_color ); } if ( $bg_color ) { $bg_color = $self->_get_palette_color( $bg_color ); $self->xml_empty_tag( 'bgColor', 'rgb' => $bg_color ); } else { if ( !$dxf_format ) { $self->xml_empty_tag( 'bgColor', 'indexed' => 64 ); } } $self->xml_end_tag( 'patternFill' ); $self->xml_end_tag( 'fill' ); } ############################################################################## # # _write_borders() # # Write the <borders> element. # sub _write_borders { my $self = shift; my $count = $self->{_border_count}; my @attributes = ( 'count' => $count ); $self->xml_start_tag( 'borders', @attributes ); # Write the border elements for format objects that have them. for my $format ( @{ $self->{_xf_formats} } ) { $self->_write_border( $format ) if $format->{_has_border}; } $self->xml_end_tag( 'borders' ); } ############################################################################## # # _write_border() # # Write the <border> element. # sub _write_border { my $self = shift; my $format = shift; my $dxf_format = shift; my @attributes = (); # Diagonal borders add attributes to the <border> element. if ( $format->{_diag_type} == 1 ) { push @attributes, ( diagonalUp => 1 ); } elsif ( $format->{_diag_type} == 2 ) { push @attributes, ( diagonalDown => 1 ); } elsif ( $format->{_diag_type} == 3 ) { push @attributes, ( diagonalUp => 1 ); push @attributes, ( diagonalDown => 1 ); } # Ensure that a default diag border is set if the diag type is set. if ( $format->{_diag_type} && !$format->{_diag_border} ) { $format->{_diag_border} = 1; } # Write the start border tag. $self->xml_start_tag( 'border', @attributes ); # Write the <border> sub elements. $self->_write_sub_border( 'left', $format->{_left}, $format->{_left_color} ); $self->_write_sub_border( 'right', $format->{_right}, $format->{_right_color} ); $self->_write_sub_border( 'top', $format->{_top}, $format->{_top_color} ); $self->_write_sub_border( 'bottom', $format->{_bottom}, $format->{_bottom_color} ); # Condition DXF formats don't allow diagonal borders if ( !$dxf_format ) { $self->_write_sub_border( 'diagonal', $format->{_diag_border}, $format->{_diag_color} ); } if ( $dxf_format ) { $self->_write_sub_border( 'vertical' ); $self->_write_sub_border( 'horizontal' ); } $self->xml_end_tag( 'border' ); } ############################################################################## # # _write_sub_border() # # Write the <border> sub elements such as <right>, <top>, etc. # sub _write_sub_border { my $self = shift; my $type = shift; my $style = shift; my $color = shift; my @attributes; if ( !$style ) { $self->xml_empty_tag( $type ); return; } my @border_styles = qw( none thin medium dashed dotted thick double hair mediumDashed dashDot mediumDashDot dashDotDot mediumDashDotDot slantDashDot ); push @attributes, ( style => $border_styles[$style] ); $self->xml_start_tag( $type, @attributes ); if ( $color ) { $color = $self->_get_palette_color( $color ); $self->xml_empty_tag( 'color', 'rgb' => $color ); } else { $self->xml_empty_tag( 'color', 'auto' => 1 ); } $self->xml_end_tag( $type ); } ############################################################################## # # _write_cell_style_xfs() # # Write the <cellStyleXfs> element. # sub _write_cell_style_xfs { my $self = shift; my $count = 1; if ( $self->{_has_hyperlink} ) { $count = 2; } my @attributes = ( 'count' => $count ); $self->xml_start_tag( 'cellStyleXfs', @attributes ); # Write the style_xf element. $self->_write_style_xf( 0, 0 ); if ( $self->{_has_hyperlink} ) { $self->_write_style_xf( 1, $self->{_hyperlink_font_id} ); } $self->xml_end_tag( 'cellStyleXfs' ); } ############################################################################## # # _write_cell_xfs() # # Write the <cellXfs> element. # sub _write_cell_xfs { my $self = shift; my @formats = @{ $self->{_xf_formats} }; # Workaround for when the last format is used for the comment font # and shouldn't be used for cellXfs. my $last_format = $formats[-1]; if ( $last_format->{_font_only} ) { pop @formats; } my $count = scalar @formats; my @attributes = ( 'count' => $count ); $self->xml_start_tag( 'cellXfs', @attributes ); # Write the xf elements. for my $format ( @formats ) { $self->_write_xf( $format ); } $self->xml_end_tag( 'cellXfs' ); } ############################################################################## # # _write_style_xf() # # Write the style <xf> element. # sub _write_style_xf { my $self = shift; my $has_hyperlink = shift; my $font_id = shift; my $num_fmt_id = 0; my $fill_id = 0; my $border_id = 0; my @attributes = ( 'numFmtId' => $num_fmt_id, 'fontId' => $font_id, 'fillId' => $fill_id, 'borderId' => $border_id, ); if ( $has_hyperlink ) { push @attributes, ( 'applyNumberFormat' => 0 ); push @attributes, ( 'applyFill' => 0 ); push @attributes, ( 'applyBorder' => 0 ); push @attributes, ( 'applyAlignment' => 0 ); push @attributes, ( 'applyProtection' => 0 ); $self->xml_start_tag( 'xf', @attributes ); $self->xml_empty_tag( 'alignment', ( 'vertical', 'top' ) ); $self->xml_empty_tag( 'protection', ( 'locked', 0 ) ); $self->xml_end_tag( 'xf' ); } else { $self->xml_empty_tag( 'xf', @attributes ); } } ############################################################################## # # _write_xf() # # Write the <xf> element. # sub _write_xf { my $self = shift; my $format = shift; my $num_fmt_id = $format->{_num_format_index}; my $font_id = $format->{_font_index}; my $fill_id = $format->{_fill_index}; my $border_id = $format->{_border_index}; my $xf_id = $format->{_xf_id}; my $has_align = 0; my $has_protect = 0; my @attributes = ( 'numFmtId' => $num_fmt_id, 'fontId' => $font_id, 'fillId' => $fill_id, 'borderId' => $border_id, 'xfId' => $xf_id, ); if ( $format->{_num_format_index} > 0 ) { push @attributes, ( 'applyNumberFormat' => 1 ); } # Add applyFont attribute if XF format uses a font element. if ( $format->{_font_index} > 0 && !$format->{_hyperlink} ) { push @attributes, ( 'applyFont' => 1 ); } # Add applyFill attribute if XF format uses a fill element. if ( $format->{_fill_index} > 0 ) { push @attributes, ( 'applyFill' => 1 ); } # Add applyBorder attribute if XF format uses a border element. if ( $format->{_border_index} > 0 ) { push @attributes, ( 'applyBorder' => 1 ); } # Check if XF format has alignment properties set. my ( $apply_align, @align ) = $format->get_align_properties(); # Check if an alignment sub-element should be written. $has_align = 1 if $apply_align && @align; # We can also have applyAlignment without a sub-element. if ( $apply_align || $format->{_hyperlink} ) { push @attributes, ( 'applyAlignment' => 1 ); } # Check for cell protection properties. my @protection = $format->get_protection_properties(); if ( @protection || $format->{_hyperlink} ) { push @attributes, ( 'applyProtection' => 1 ); if ( !$format->{_hyperlink} ) { $has_protect = 1; } } # Write XF with sub-elements if required. if ( $has_align || $has_protect ) { $self->xml_start_tag( 'xf', @attributes ); $self->xml_empty_tag( 'alignment', @align ) if $has_align; $self->xml_empty_tag( 'protection', @protection ) if $has_protect; $self->xml_end_tag( 'xf' ); } else { $self->xml_empty_tag( 'xf', @attributes ); } } ############################################################################## # # _write_cell_styles() # # Write the <cellStyles> element. # sub _write_cell_styles { my $self = shift; my $count = 1; if ( $self->{_has_hyperlink} ) { $count = 2; } my @attributes = ( 'count' => $count ); $self->xml_start_tag( 'cellStyles', @attributes ); # Write the cellStyle element. if ( $self->{_has_hyperlink} ) { $self->_write_cell_style('Hyperlink', 1, 8); } $self->_write_cell_style('Normal', 0, 0); $self->xml_end_tag( 'cellStyles' ); } ############################################################################## # # _write_cell_style() # # Write the <cellStyle> element. # sub _write_cell_style { my $self = shift; my $name = shift; my $xf_id = shift; my $builtin_id = shift; my @attributes = ( 'name' => $name, 'xfId' => $xf_id, 'builtinId' => $builtin_id, ); $self->xml_empty_tag( 'cellStyle', @attributes ); } ############################################################################## # # _write_dxfs() # # Write the <dxfs> element. # sub _write_dxfs { my $self = shift; my $formats = $self->{_dxf_formats}; my $count = scalar @{$formats}; my @attributes = ( 'count' => $count ); if ( $count ) { $self->xml_start_tag( 'dxfs', @attributes ); # Write the font elements for format objects that have them. for my $format ( @{ $self->{_dxf_formats} } ) { $self->xml_start_tag( 'dxf' ); $self->_write_font( $format, 1 ) if $format->{_has_dxf_font}; if ( $format->{_num_format_index} ) { $self->_write_num_fmt( $format->{_num_format_index}, $format->{_num_format} ); } $self->_write_fill( $format, 1 ) if $format->{_has_dxf_fill}; $self->_write_border( $format, 1 ) if $format->{_has_dxf_border}; $self->xml_end_tag( 'dxf' ); } $self->xml_end_tag( 'dxfs' ); } else { $self->xml_empty_tag( 'dxfs', @attributes ); } } ############################################################################## # # _write_table_styles() # # Write the <tableStyles> element. # sub _write_table_styles { my $self = shift; my $count = 0; my $default_table_style = 'TableStyleMedium9'; my $default_pivot_style = 'PivotStyleLight16'; my @attributes = ( 'count' => $count, 'defaultTableStyle' => $default_table_style, 'defaultPivotStyle' => $default_pivot_style, ); $self->xml_empty_tag( 'tableStyles', @attributes ); } ############################################################################## # # _write_colors() # # Write the <colors> element. # sub _write_colors { my $self = shift; my @custom_colors = @{ $self->{_custom_colors} }; return unless @custom_colors; $self->xml_start_tag( 'colors' ); $self->_write_mru_colors( @custom_colors ); $self->xml_end_tag( 'colors' ); } ############################################################################## # # _write_mru_colors() # # Write the <mruColors> element for the most recently used colours. # sub _write_mru_colors { my $self = shift; my @custom_colors = @_; # Limit the mruColors to the last 10. my $count = @custom_colors; if ( $count > 10 ) { splice @custom_colors, 0, ( $count - 10 ); } $self->xml_start_tag( 'mruColors' ); # Write the custom colors in reverse order. for my $color ( reverse @custom_colors ) { $self->_write_color( 'rgb' => $color ); } $self->xml_end_tag( 'mruColors' ); } ############################################################################## # # _write_condense() # # Write the <condense> element. # sub _write_condense { my $self = shift; my $val = 0; my @attributes = ( 'val' => $val ); $self->xml_empty_tag( 'condense', @attributes ); } ############################################################################## # # _write_extend() # # Write the <extend> element. # sub _write_extend { my $self = shift; my $val = 0; my @attributes = ( 'val' => $val ); $self->xml_empty_tag( 'extend', @attributes ); } 1; __END__ =pod =head1 NAME Styles - A class for writing the Excel XLSX styles file. =head1 SYNOPSIS See the documentation for L<Excel::Writer::XLSX>. =head1 DESCRIPTION This module is used in conjunction with L<Excel::Writer::XLSX>. =head1 AUTHOR John McNamara jmcnamara@cpan.org =head1 COPYRIGHT (c) MM-MMXIX, John McNamara. All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself. =head1 LICENSE 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>. =head1 DISCLAIMER OF WARRANTY See the documentation for L<Excel::Writer::XLSX>. =cut