From 71824ef0401ba62c3b3e739e91b6da48c053cc9b Mon Sep 17 00:00:00 2001 From: John McNamara Date: Sat, 24 Feb 2024 15:37:25 +0000 Subject: [PATCH] worksheet: fix multi-sheet embed_image() --- examples/embedded_images.pl | 33 ++++++++ lib/Excel/Writer/XLSX.pm | 38 ++++++--- lib/Excel/Writer/XLSX/Worksheet.pm | 5 +- t/regression/embed_image13.t | 85 +++++++++++++++++++++ t/regression/xlsx_files/embed_image13.xlsx | Bin 0 -> 9475 bytes 5 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 examples/embedded_images.pl create mode 100644 t/regression/embed_image13.t create mode 100644 t/regression/xlsx_files/embed_image13.xlsx diff --git a/examples/embedded_images.pl b/examples/embedded_images.pl new file mode 100644 index 00000000..de40792f --- /dev/null +++ b/examples/embedded_images.pl @@ -0,0 +1,33 @@ +####################################################################### +# +# An example of embedding images into a worksheet cells using the the +# Excel::Writer::XLSX module. This is equivalent to Excel's "Place in cell" +# image insert. +# +# Copyright 2000-2023, John McNamara, jmcnamara@cpan.org +# +# SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later +# + +use strict; +use Excel::Writer::XLSX; + +# Create a new workbook called simple.xls and add a worksheet +my $workbook = Excel::Writer::XLSX->new( 'embedded_images.xlsx' ); +my $worksheet = $workbook->add_worksheet(); + + +# Widen the first column to make the caption clearer. +$worksheet->set_column( 0, 0, 30 ); +$worksheet->write( 0, 0, "Embed images that scale to cell size" ); + +# Embed an images in cells of different widths/heights. +$worksheet->set_column( 1, 1, 14 ); + +$worksheet->set_row( 1, 60 ); +$worksheet->embed_image( 1, 1, "republic.png" ); + +$worksheet->set_row( 3, 120 ); +$worksheet->embed_image( 3, 1, "republic.png" ); + +$workbook->close(); diff --git a/lib/Excel/Writer/XLSX.pm b/lib/Excel/Writer/XLSX.pm index 18af38a8..f677cdd5 100644 --- a/lib/Excel/Writer/XLSX.pm +++ b/lib/Excel/Writer/XLSX.pm @@ -1785,7 +1785,8 @@ BMP images must be 24 bit, true colour, bitmaps. In general it is best to avoid =head2 embed_image( $row, $col, $filename, { %options } ) -This method can be used to embed an image into a worksheet. The image can be in PNG, JPEG, GIF or BMP format. +This method can be used to embed an image into a worksheet. The image can be +in PNG, JPEG, GIF or BMP format. $worksheet1->embed_image( 'A1', 'perl.bmp' ); $worksheet2->embed_image( 'A1', '../images/perl.bmp' ); @@ -1797,10 +1798,13 @@ of the image is preserved but the size of the image is adjusted to fit the largest possible width or height depending on the cell dimensions. This is the equivalent of Excel's menu option to insert an image using the -option to "Place in Cell". See C for the equivalent method to -"Place over Cells". +option to "Place in Cell" which is only available in Excel 365 versions from +2023 onwards. For older versions of Excel a ``#VALUE!`` error is displayed. -The optional C hash/hashref parameter can be used to set various options for the image. The defaults are: +See C for the equivalent method to "Place over Cells". + +The optional C hash/hashref parameter can be used to set various +options for the image. The defaults are: %options = ( cell_format => format, @@ -1810,14 +1814,16 @@ The optional C hash/hashref parameter can be used to set various option decorative => 0, ); -The C parameters can be an standard Format to set the formatting of the cell behind the image. +The C parameters can be an standard Format to set the formatting +of the cell behind the image. The C option can be use to used to add a hyperlink to an image: $worksheet->insert_image( 'A1', 'logo.png', { url => 'https://github.com/jmcnamara' } ); -The supported url formats are the same as those supported by the C method and the same rules/limits apply. +The supported url formats are the same as those supported by the +C method and the same rules/limits apply. The C option can be use to used to add a mouseover tip to the hyperlink: @@ -1828,18 +1834,30 @@ The C option can be use to used to add a mouseover tip to the hyperlink: } ); -The C parameter can be used to specify a description or "alt text" string for the image. In general this would be used to provide a text description of the image to help accessibility. It is an optional parameter and defaults to the filename of the image. It can be used as follows: +The C parameter can be used to specify a description or "alt +text" string for the image. In general this would be used to provide a text +description of the image to help accessibility. It is an optional parameter +and defaults to the filename of the image. It can be used as follows: $worksheet->insert_image( 'E9', 'logo.png', {description => "This is some alternative text"} ); -The optional C parameter is also used to help accessibility. It is used to mark the image as decorative, and thus uninformative, for automated screen readers. As in Excel, if this parameter is in use the C field isn't written. It is used as follows: +The optional C parameter is also used to help accessibility. It is +used to mark the image as decorative, and thus uninformative, for automated +screen readers. As in Excel, if this parameter is in use the C +field isn't written. It is used as follows: $worksheet->insert_image( 'E9', 'logo.png', {decorative => 1} ); -Note: you must call C or C before C if you wish to change the default dimensions of any of the rows or columns that the image occupies. The height of a row can also change if you use a font that is larger than the default. This in turn will affect the scaling of your image. To avoid this you should explicitly set the height of the row using C if it contains a font size that will change the row height. +Note: you must call C or C before C +if you wish to change the default dimensions of any of the rows or columns +that the image occupies. The height of a row can also change if you use a font +that is larger than the default. This in turn will affect the scaling of your +image. To avoid this you should explicitly set the height of the row using +C if it contains a font size that will change the row height. -BMP images must be 24 bit, true colour, bitmaps. In general it is best to avoid BMP images since they aren't compressed. +BMP images must be 24 bit, true colour, bitmaps. In general it is best to +avoid BMP images since they aren't compressed. diff --git a/lib/Excel/Writer/XLSX/Worksheet.pm b/lib/Excel/Writer/XLSX/Worksheet.pm index dd5bd839..1b835add 100644 --- a/lib/Excel/Writer/XLSX/Worksheet.pm +++ b/lib/Excel/Writer/XLSX/Worksheet.pm @@ -6588,13 +6588,14 @@ sub embed_image { get_image_properties( $image ); # Check for duplicate images. - my $image_index = ${ $self->{_embedded_images_indexes} }->{$md5}; + my $image_index = ${ $self->{_embedded_image_indexes} }->{$md5}; + if ( !$image_index ) { push @{ ${ $self->{_embedded_images} } }, [ $image, $type, $description, $decorative ]; $image_index = scalar @{ ${ $self->{_embedded_images} } }; - ${ $self->{_embedded_images_indexes} }->{$md5} = $image_index; + ${ $self->{_embedded_image_indexes} }->{$md5} = $image_index; } # Write the cell placeholder. diff --git a/t/regression/embed_image13.t b/t/regression/embed_image13.t new file mode 100644 index 00000000..b25a99db --- /dev/null +++ b/t/regression/embed_image13.t @@ -0,0 +1,85 @@ +############################################################################### +# +# Tests the output of Excel::Writer::XLSX against Excel generated files. +# +# Copyright 2000-2023, John McNamara, jmcnamara@cpan.org +# +# 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 = 'embed_image13.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 image(s). +# +use Excel::Writer::XLSX; + +my $workbook = Excel::Writer::XLSX->new( $got_filename ); + +my $worksheet1 = $workbook->add_worksheet(); + +$worksheet1->embed_image( 0, 0, $dir . 'images/red.png' ); +$worksheet1->embed_image( 2, 0, $dir . 'images/blue.png' ); +$worksheet1->embed_image( 4, 0, $dir . 'images/yellow.png' ); + +my $worksheet2 = $workbook->add_worksheet(); + +$worksheet2->embed_image( 0, 0, $dir . 'images/yellow.png' ); +$worksheet2->embed_image( 2, 0, $dir . 'images/red.png' ); +$worksheet2->embed_image( 4, 0, $dir . 'images/blue.png' ); + +my $worksheet3 = $workbook->add_worksheet(); + +$worksheet3->embed_image( 0, 0, $dir . 'images/blue.png' ); +$worksheet3->embed_image( 2, 0, $dir . 'images/yellow.png' ); +$worksheet3->embed_image( 4, 0, $dir . 'images/red.png' ); + + +$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__ + + + diff --git a/t/regression/xlsx_files/embed_image13.xlsx b/t/regression/xlsx_files/embed_image13.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..038f97473df0000e9fb6ce26a082a84b0607562a GIT binary patch literal 9475 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