Skip to content

Commit

Permalink
Allow longer hyperlinks when anchors.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmcnamara committed Oct 4, 2015
1 parent 4a4401c commit 72fd2df
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 44 deletions.
23 changes: 13 additions & 10 deletions lib/Excel/Writer/XLSX.pm
Expand Up @@ -1254,6 +1254,8 @@ Finally, you can avoid most of these quoting problems by using forward slashes.
Note: Excel::Writer::XLSX will escape the following characters in URLs as required by Excel: C<< \s " < > \ [ ] ` ^ { } >> unless the URL already contains C<%xx> style escapes. In which case it is assumed that the URL was escaped correctly by the user and will by passed directly to Excel.
Excel limits hyperlink links and anchor/locations to 255 characters each.
See also, the note about L</Cell notation>.
Expand Down Expand Up @@ -6416,16 +6418,17 @@ different features and options of the module. See L<Excel::Writer::XLSX::Example
The following limits are imposed by Excel 2007+:
Description Limit
----------------------------------- ------
Maximum number of chars in a string 32,767
Maximum number of columns 16,384
Maximum number of rows 1,048,576
Maximum chars in a sheet name 31
Maximum chars in a header/footer 254
Maximum characters in hyperlink 255
Maximum number of unique hyperlinks* 65,530
Description Limit
-------------------------------------- ------
Maximum number of chars in a string 32,767
Maximum number of columns 16,384
Maximum number of rows 1,048,576
Maximum chars in a sheet name 31
Maximum chars in a header/footer 254
Maximum characters in hyperlink url 255
Maximum characters in hyperlink anchor 255
Maximum number of unique hyperlinks* 65,530
* Per worksheet. Excel allows a greater number of non-unique hyperlinks if they are contiguous and can be grouped into a single range. This will be supported in a later version of Excel::Writer::XLSX if possible.
Expand Down
52 changes: 18 additions & 34 deletions lib/Excel/Writer/XLSX/Worksheet.pm
Expand Up @@ -2665,22 +2665,19 @@ sub write_url {
my $type = 'l'; # XML data type
my $link_type = 1;
# The displayed string defaults to the url string.
$str = $url unless defined $str;
# Remove the URI scheme from internal links.
if ( $url =~ s/^internal:// ) {
$str =~ s/^internal://;
$link_type = 2;
}
# Remove the URI scheme from external links.
# Remove the URI scheme from external links and change the directory
# separator from Unix to Dos.
if ( $url =~ s/^external:// ) {
$link_type = 3;
}
# The displayed string defaults to the url string.
$str = $url unless defined $str;
# For external links change the directory separator from Unix to Dos.
if ( $link_type == 3 ) {
$str =~ s/^external://;
$url =~ s[/][\\]g;
$str =~ s[/][\\]g;
}
Expand All @@ -2703,7 +2700,7 @@ sub write_url {
# External links to URLs and to other Excel workbooks have slightly
# different characteristics that we have to account for.
if ( $link_type == 1 || $link_type == 3) {
if ( $link_type == 1 ) {
# Escape URL unless it looks already escaped.
if ( $url !~ /%[0-9a-fA-F]{2}/ ) {
Expand All @@ -2718,39 +2715,26 @@ sub write_url {
$url =~ s/(["<>[\]`^{}])/sprintf '%%%x', ord $1/eg;
}
# Ordinary URL style external links don't have a "location" string.
$url_str = undef;
}
if ( $link_type == 3 ) {
# External Workbook links need to be modified into the right format.
# The URL will look something like 'c:\temp\file.xlsx#Sheet!A1'.
# We need the part to the left of the # as the URL and the part to
# the right as the "location" string (if it exists).
# Split url into the link and optional anchor/location.
( $url, $url_str ) = split /#/, $url;
# Add the file:/// URI to the $url if non-local.
if (
$url =~ m{[:]} # Windows style "C:/" link.
|| $url =~ m{^\\\\} # Network share.
)
{
# Add the file:/// URI to the url for Windows style "C:/" link and
# Network shares.
if ( $url =~ m{^\w:} || $url =~ m{^\\\\} ) {
$url = 'file:///' . $url;
}
# Convert a ./dir/file.xlsx link to dir/file.xlsx.
$url =~ s{^.\\}{};
# Treat as a default external link now that the data has been modified.
$link_type = 1;
}
# Excel limits escaped URL to 255 characters.
if ( length $url > 255 ) {
carp "Ignoring URL '$url' > 255 characters since it exceeds Excel's "
. "limit for URLS. See LIMITATIONS section of the "
. "Excel::Writer::XLSX documentation.";
# Excel limits the escaped URL and location/anchor to 255 characters.
my $tmp_url_str = $url_str || '';
if ( length $url > 255 || length $tmp_url_str > 255) {
carp "Ignoring URL '$url' where link or anchor > 255 characters "
. "since it exceeds Excel's limit for URLS. See LIMITATIONS "
. "section of the Excel::Writer::XLSX documentation.";
return -4;
}
Expand Down
65 changes: 65 additions & 0 deletions t/regression/hyperlink23.t
@@ -0,0 +1,65 @@
###############################################################################
#
# Tests the output of Excel::Writer::XLSX against Excel generated files.
#
# reverse ('(c)'), January 2015, John McNamara, jmcnamara@cpan.org
#

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 = 'hyperlink23.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 with hyperlinks.
#
use Excel::Writer::XLSX;

my $workbook = Excel::Writer::XLSX->new( $got_filename );
my $worksheet = $workbook->add_worksheet();

$worksheet->write_url( 'A1', 'https://en.wikipedia.org/wiki/Microsoft_Excel#Data_storage_and_communication', 'Display text' );

$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__
65 changes: 65 additions & 0 deletions t/regression/hyperlink24.t
@@ -0,0 +1,65 @@
###############################################################################
#
# Tests the output of Excel::Writer::XLSX against Excel generated files.
#
# reverse ('(c)'), January 2015, John McNamara, jmcnamara@cpan.org
#

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 = 'hyperlink24.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 with hyperlinks.
#
use Excel::Writer::XLSX;

my $workbook = Excel::Writer::XLSX->new( $got_filename );
my $worksheet = $workbook->add_worksheet();

$worksheet->write_url( 'A1', 'http://www.example.com/some_long_url_that_is_255_characters_long_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_z#some_long_location_that_is_255_characters_long_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_abcdefgh_z' );

$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__
Binary file added t/regression/xlsx_files/hyperlink23.xlsx
Binary file not shown.
Binary file added t/regression/xlsx_files/hyperlink24.xlsx
Binary file not shown.

0 comments on commit 72fd2df

Please sign in to comment.