Skip to content

Commit

Permalink
worksheet: fix repeat_row() issue with quoted sheet names
Browse files Browse the repository at this point in the history
Add a fix for an issue where a worksheet name needed quoting when
used with a repeat_row()/Print_Titles defined name.
  • Loading branch information
jmcnamara committed Jun 30, 2024
1 parent dbf86ef commit 16923b8
Show file tree
Hide file tree
Showing 10 changed files with 629 additions and 4 deletions.
82 changes: 78 additions & 4 deletions lib/Excel/Writer/XLSX/Utility.pm
Original file line number Diff line number Diff line change
Expand Up @@ -218,23 +218,97 @@ sub xl_range_formula {
#
# Sheetnames used in references should be quoted if they contain any spaces,
# special characters or if they look like something that isn't a sheet name.
# The rules are shown inline below.
#
sub quote_sheetname {

my $sheetname = $_[0];
my $sheetname = shift;
my $uc_sheetname = uc( $sheetname );
my $needs_quoting = 0;
my $row_max = 1_048_576;
my $col_max = 16_384;

# Use Excel's conventions and quote the sheet name if it contains any
# non-word character or if it isn't already quoted.
if ( $sheetname =~ /\W/ && $sheetname !~ /^'/ ) {
# Don't quote sheetname if it is already quoted by the user.
if ( $sheetname !~ /^'/ ) {


# Rule 1. Sheet names that contain anything other than \w and "."
# characters must be quoted.
if ( $sheetname =~ /[^\w\.\p{Emoticons}]/ ) {
$needs_quoting = 1;
}

# Rule 2. Sheet names that start with a digit or "." must be quoted.
elsif ( $sheetname =~ /^[\d\.\p{Emoticons}]/ ) {
$needs_quoting = 1;
}

# Rule 3. Sheet names must not be a valid A1 style cell reference.
# Valid means that the row and column values are within Excel limits.
elsif ( $uc_sheetname =~ /^([A-Z]{1,3}\d+)$/ ) {
my ( $row, $col ) = xl_cell_to_rowcol( $1 );

if ( $row >= 0 && $row < $row_max && $col >= 0 && $col < $col_max )
{
$needs_quoting = 1;
}
}

# Rule 4. Sheet names must not *start* with a valid RC style cell
# reference. Valid means that the row and column values are within
# Excel limits.

# Rule 4a. Check for some single R/C references.
elsif ($uc_sheetname eq "R"
|| $uc_sheetname eq "C"
|| $uc_sheetname eq "RC" )
{
$needs_quoting = 1;

}

# Rule 4b. Check for C1 or RC1 style references. References without
# trailing characters (like C12345) are caught by Rule 3.
elsif ( $uc_sheetname =~ /^R?C(\d+)/ ) {
my $col = $1;
if ( $col > 0 && $col <= $col_max ) {
$needs_quoting = 1;
}
}

# Rule 4c. Check for R1C1 style references where both the number
# ranges are optional. Note that only 1 of the number ranges is
# required to be valid.
elsif ( $uc_sheetname =~ /^R(\d+)?C(\d+)?/ ) {
if ( defined $1 ) {
my $row = $1;
if ( $row > 0 && $row <= $row_max ) {
$needs_quoting = 1;
}
}

if ( defined $2 ) {
my $col = $1;
if ( $col > 0 && $col <= $col_max ) {
$needs_quoting = 1;
}
}
}
}


if ( $needs_quoting ) {
# Double quote any single quotes.
$sheetname =~ s/'/''/g;
$sheetname = q(') . $sheetname . q(');
}


return $sheetname;
}



###############################################################################
#
# xl_inc_row($string)
Expand Down
91 changes: 91 additions & 0 deletions t/regression/quote_name08.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
###############################################################################
#
# Tests the output of Excel::Writer::XLSX against Excel generated files.
#
# Copyright 2000-2024, John McNamara, [email protected]
#
# SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
#

use lib 't/lib';
use TestFunctions qw(_compare_xlsx_files _is_deep_diff);
use strict;
use warnings;

use Test::More tests => 1;

###############################################################################
#
# Tests setup.
#
my $filename = 'quote_name08.xlsx';
my $dir = 't/regression/';
my $got_filename = $dir . "ewx_$filename";
my $exp_filename = $dir . 'xlsx_files/' . $filename;

my $ignore_members = [];

my $ignore_elements = {};


###############################################################################
#
# Test the creation of a simple Excel::Writer::XLSX file.
#
use Excel::Writer::XLSX;

my $workbook = Excel::Writer::XLSX->new( $got_filename );
my $worksheet = $workbook->add_worksheet('1Sheet');
my $chart = $workbook->add_chart( type => 'column', embedded => 1 );

# For testing, copy the randomly generated axis ids in the target xlsx file.
$chart->{_axis_ids} = [ 55487104, 84573184 ];

my $data = [
[ 1, 2, 3, 4, 5 ],
[ 2, 4, 6, 8, 10 ],
[ 3, 6, 9, 12, 15 ],

];

$worksheet->write( 'A1', $data );
$worksheet->repeat_rows( 0, 1 );
$worksheet->set_portrait();
$worksheet->{_vertical_dpi} = 200;

$chart->add_series( values => [ '1Sheet', 0, 4, 0, 0 ] );
$chart->add_series( values => [ '1Sheet', 0, 4, 1, 1 ] );
$chart->add_series( values => [ '1Sheet', 0, 4, 2, 2 ] );

$worksheet->insert_chart( 'E9', $chart );

$workbook->close();


###############################################################################
#
# Compare the generated and existing Excel files.
#

my ( $got, $expected, $caption ) = _compare_xlsx_files(

$got_filename,
$exp_filename,
$ignore_members,
$ignore_elements,
);

_is_deep_diff( $got, $expected, $caption );



###############################################################################
#
# Cleanup.
#
unlink $got_filename;

__END__
91 changes: 91 additions & 0 deletions t/regression/quote_name09.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
###############################################################################
#
# Tests the output of Excel::Writer::XLSX against Excel generated files.
#
# Copyright 2000-2024, John McNamara, [email protected]
#
# SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
#

use lib 't/lib';
use TestFunctions qw(_compare_xlsx_files _is_deep_diff);
use strict;
use warnings;

use Test::More tests => 1;

###############################################################################
#
# Tests setup.
#
my $filename = 'quote_name09.xlsx';
my $dir = 't/regression/';
my $got_filename = $dir . "ewx_$filename";
my $exp_filename = $dir . 'xlsx_files/' . $filename;

my $ignore_members = [];

my $ignore_elements = {};


###############################################################################
#
# Test the creation of a simple Excel::Writer::XLSX file.
#
use Excel::Writer::XLSX;

my $workbook = Excel::Writer::XLSX->new( $got_filename );
my $worksheet = $workbook->add_worksheet('Sheet_1');
my $chart = $workbook->add_chart( type => 'column', embedded => 1 );

# For testing, copy the randomly generated axis ids in the target xlsx file.
$chart->{_axis_ids} = [ 54437760, 59195776 ];

my $data = [
[ 1, 2, 3, 4, 5 ],
[ 2, 4, 6, 8, 10 ],
[ 3, 6, 9, 12, 15 ],

];

$worksheet->write( 'A1', $data );
$worksheet->repeat_rows( 0, 1 );
$worksheet->set_portrait();
$worksheet->{_vertical_dpi} = 200;

$chart->add_series( values => [ 'Sheet_1', 0, 4, 0, 0 ] );
$chart->add_series( values => [ 'Sheet_1', 0, 4, 1, 1 ] );
$chart->add_series( values => [ 'Sheet_1', 0, 4, 2, 2 ] );

$worksheet->insert_chart( 'E9', $chart );

$workbook->close();


###############################################################################
#
# Compare the generated and existing Excel files.
#

my ( $got, $expected, $caption ) = _compare_xlsx_files(

$got_filename,
$exp_filename,
$ignore_members,
$ignore_elements,
);

_is_deep_diff( $got, $expected, $caption );



###############################################################################
#
# Cleanup.
#
unlink $got_filename;

__END__
91 changes: 91 additions & 0 deletions t/regression/quote_name10.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
###############################################################################
#
# Tests the output of Excel::Writer::XLSX against Excel generated files.
#
# Copyright 2000-2024, John McNamara, [email protected]
#
# SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
#

use lib 't/lib';
use TestFunctions qw(_compare_xlsx_files _is_deep_diff);
use strict;
use warnings;

use Test::More tests => 1;

###############################################################################
#
# Tests setup.
#
my $filename = 'quote_name10.xlsx';
my $dir = 't/regression/';
my $got_filename = $dir . "ewx_$filename";
my $exp_filename = $dir . 'xlsx_files/' . $filename;

my $ignore_members = [];

my $ignore_elements = {};


###############################################################################
#
# Test the creation of a simple Excel::Writer::XLSX file.
#
use Excel::Writer::XLSX;

my $workbook = Excel::Writer::XLSX->new( $got_filename );
my $worksheet = $workbook->add_worksheet('Sh.eet.1');
my $chart = $workbook->add_chart( type => 'column', embedded => 1 );

# For testing, copy the randomly generated axis ids in the target xlsx file.
$chart->{_axis_ids} = [ 46905600, 46796800 ];

my $data = [
[ 1, 2, 3, 4, 5 ],
[ 2, 4, 6, 8, 10 ],
[ 3, 6, 9, 12, 15 ],

];

$worksheet->write( 'A1', $data );
$worksheet->repeat_rows( 0, 1 );
$worksheet->set_portrait();
$worksheet->{_vertical_dpi} = 200;

$chart->add_series( values => [ 'Sh.eet.1', 0, 4, 0, 0 ] );
$chart->add_series( values => [ 'Sh.eet.1', 0, 4, 1, 1 ] );
$chart->add_series( values => [ 'Sh.eet.1', 0, 4, 2, 2 ] );

$worksheet->insert_chart( 'E9', $chart );

$workbook->close();


###############################################################################
#
# Compare the generated and existing Excel files.
#

my ( $got, $expected, $caption ) = _compare_xlsx_files(

$got_filename,
$exp_filename,
$ignore_members,
$ignore_elements,
);

_is_deep_diff( $got, $expected, $caption );



###############################################################################
#
# Cleanup.
#
unlink $got_filename;

__END__
Loading

0 comments on commit 16923b8

Please sign in to comment.