Skip to content

Commit a5cb6d7

Browse files
committed
worksheet: add embed_image()
Add support for embedded worksheet images from newer versions of Excel.
1 parent a703853 commit a5cb6d7

34 files changed

+2354
-311
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ TAGS
1212
*#
1313
~*xlsx
1414
*.bak
15+
.vscode
1516

lib/Excel/Writer/XLSX/Package/ContentTypes.pm

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,38 @@ sub _add_metadata {
350350
}
351351

352352

353+
###############################################################################
354+
#
355+
# _add_richvalue()
356+
#
357+
# Add the RichValue files to the ContentTypes overrides.
358+
#
359+
sub _add_richvalue {
360+
361+
my $self = shift;
362+
363+
$self->_add_override(
364+
'/xl/richData/rdRichValueTypes.xml',
365+
'application/vnd.ms-excel.rdrichvaluetypes+xml'
366+
);
367+
368+
$self->_add_override(
369+
'/xl/richData/rdrichvalue.xml',
370+
'application/vnd.ms-excel.rdrichvalue+xml'
371+
);
372+
373+
$self->_add_override(
374+
'/xl/richData/rdrichvaluestructure.xml',
375+
'application/vnd.ms-excel.rdrichvaluestructure+xml'
376+
);
377+
378+
$self->_add_override(
379+
'/xl/richData/richValueRel.xml',
380+
'application/vnd.ms-excel.richvaluerel+xml'
381+
);
382+
}
383+
384+
353385
###############################################################################
354386
#
355387
# Internal methods.

lib/Excel/Writer/XLSX/Package/Metadata.pm

Lines changed: 183 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ sub new {
4444
my $fh = shift;
4545
my $self = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
4646

47+
$self->{_has_dynamic_functions} = 0;
48+
$self->{_has_embedded_images} = 0;
49+
$self->{_num_embedded_images} = 0;
50+
4751
bless $self, $class;
4852

4953
return $self;
@@ -60,6 +64,10 @@ sub _assemble_xml_file {
6064

6165
my $self = shift;
6266

67+
if ( $self->{_num_embedded_images} > 0 ) {
68+
$self->{_has_embedded_images} = 1;
69+
}
70+
6371
$self->xml_declaration;
6472

6573
# Write the metadata element.
@@ -69,10 +77,12 @@ sub _assemble_xml_file {
6977
$self->_write_metadata_types();
7078

7179
# Write the futureMetadata element.
72-
$self->_write_future_metadata();
80+
$self->_write_cell_future_metadata() if $self->{_has_dynamic_functions};
81+
$self->_write_value_future_metadata() if $self->{_has_embedded_images};
7382

74-
# Write the cellMetadata element.
75-
$self->_write_cell_metadata();
83+
# Write the valueMetadata element.
84+
$self->_write_cell_metadata() if $self->{_has_dynamic_functions};
85+
$self->_write_value_metadata() if $self->{_has_embedded_images};
7686

7787
$self->xml_end_tag( 'metadata' );
7888

@@ -96,16 +106,30 @@ sub _assemble_xml_file {
96106
#
97107
sub _write_metadata {
98108

99-
my $self = shift;
100-
my $xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
101-
my $xmlns_xda =
102-
'http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray';
109+
my $self = shift;
103110

104111
my @attributes = (
105-
'xmlns' => $xmlns,
106-
'xmlns:xda' => $xmlns_xda,
112+
'xmlns' =>
113+
'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
107114
);
108115

116+
117+
if ( $self->{_has_embedded_images} ) {
118+
push @attributes,
119+
(
120+
'xmlns:xlrd' =>
121+
'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata'
122+
);
123+
}
124+
125+
if ( $self->{_has_dynamic_functions} ) {
126+
push @attributes,
127+
(
128+
'xmlns:xda' =>
129+
'http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray'
130+
);
131+
}
132+
109133
$self->xml_start_tag( 'metadata', @attributes );
110134
}
111135

@@ -119,25 +143,27 @@ sub _write_metadata {
119143
sub _write_metadata_types {
120144

121145
my $self = shift;
146+
my $count = $self->{_has_dynamic_functions} + $self->{_has_embedded_images};
122147

123-
my @attributes = ( 'count' => 1 );
148+
my @attributes = ( 'count' => $count );
124149

125150
$self->xml_start_tag( 'metadataTypes', @attributes );
126151

127152
# Write the metadataType element.
128-
$self->_write_metadata_type();
153+
$self->_write_cell_metadata_type() if $self->{_has_dynamic_functions};
154+
$self->_write_value_metadata_type() if $self->{_has_embedded_images};
129155

130156
$self->xml_end_tag( 'metadataTypes' );
131157
}
132158

133159

134160
##############################################################################
135161
#
136-
# _write_metadata_type()
162+
# _write_cell_metadata_type()
137163
#
138164
# Write the <metadataType> element.
139165
#
140-
sub _write_metadata_type {
166+
sub _write_cell_metadata_type {
141167

142168
my $self = shift;
143169

@@ -163,11 +189,40 @@ sub _write_metadata_type {
163189

164190
##############################################################################
165191
#
166-
# _write_future_metadata()
192+
# _write_value_metadata_type()
193+
#
194+
# Write the <metadataType> element.
195+
#
196+
sub _write_value_metadata_type {
197+
198+
my $self = shift;
199+
200+
my @attributes = (
201+
'name' => 'XLRICHVALUE',
202+
'minSupportedVersion' => 120000,
203+
'copy' => 1,
204+
'pasteAll' => 1,
205+
'pasteValues' => 1,
206+
'merge' => 1,
207+
'splitFirst' => 1,
208+
'rowColShift' => 1,
209+
'clearFormats' => 1,
210+
'clearComments' => 1,
211+
'assign' => 1,
212+
'coerce' => 1,
213+
);
214+
215+
$self->xml_empty_tag( 'metadataType', @attributes );
216+
}
217+
218+
219+
##############################################################################
220+
#
221+
# _write_cell_future_metadata()
167222
#
168223
# Write the <futureMetadata> element.
169224
#
170-
sub _write_future_metadata {
225+
sub _write_cell_future_metadata {
171226

172227
my $self = shift;
173228

@@ -181,7 +236,7 @@ sub _write_future_metadata {
181236
$self->xml_start_tag( 'extLst' );
182237

183238
# Write the ext element.
184-
$self->_write_ext();
239+
$self->_write_cell_ext();
185240

186241
$self->xml_end_tag( 'ext' );
187242
$self->xml_end_tag( 'extLst' );
@@ -193,13 +248,48 @@ sub _write_future_metadata {
193248

194249
##############################################################################
195250
#
196-
# _write_ext()
251+
# _write_value_future_metadata()
252+
#
253+
# Write the <futureMetadata> element.
254+
#
255+
sub _write_value_future_metadata {
256+
257+
my $self = shift;
258+
my $num_images = $self->{_num_embedded_images};
259+
260+
my @attributes = (
261+
'name' => 'XLRICHVALUE',
262+
'count' => $num_images,
263+
);
264+
265+
$self->xml_start_tag( 'futureMetadata', @attributes );
266+
267+
for my $i ( 0 .. $num_images - 1 ) {
268+
$self->xml_start_tag( 'bk' );
269+
$self->xml_start_tag( 'extLst' );
270+
271+
# Write the ext element.
272+
$self->_write_value_ext( $i );
273+
274+
$self->xml_end_tag( 'ext' );
275+
$self->xml_end_tag( 'extLst' );
276+
$self->xml_end_tag( 'bk' );
277+
}
278+
279+
$self->xml_end_tag( 'futureMetadata' );
280+
}
281+
282+
283+
##############################################################################
284+
#
285+
# _write_cell_ext()
197286
#
198287
# Write the <ext> element.
199288
#
200-
sub _write_ext {
289+
sub _write_cell_ext {
201290

202291
my $self = shift;
292+
my $uri = shift;
203293

204294
my @attributes = ( 'uri' => '{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}' );
205295

@@ -210,6 +300,25 @@ sub _write_ext {
210300
}
211301

212302

303+
##############################################################################
304+
#
305+
# _write_value_ext()
306+
#
307+
# Write the <ext> element.
308+
#
309+
sub _write_value_ext {
310+
311+
my $self = shift;
312+
my $num = shift;
313+
314+
my @attributes = ( 'uri' => '{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}' );
315+
316+
$self->xml_start_tag( 'ext', @attributes );
317+
318+
# Write the <xlrd:rvb> element.
319+
$self->_write_xlrd_rvb( $num );
320+
}
321+
213322
##############################################################################
214323
#
215324
# _write_xda_dynamic_array_properties()
@@ -246,13 +355,45 @@ sub _write_cell_metadata {
246355
$self->xml_start_tag( 'bk' );
247356

248357
# Write the rc element.
249-
$self->_write_rc();
358+
$self->_write_rc(1, 0);
250359

251360
$self->xml_end_tag( 'bk' );
252361
$self->xml_end_tag( 'cellMetadata' );
253362
}
254363

255364

365+
##############################################################################
366+
#
367+
# _write_value_metadata()
368+
#
369+
# Write the <valueMetadata> element.
370+
#
371+
sub _write_value_metadata {
372+
373+
my $self = shift;
374+
my $count = $self->{_num_embedded_images};
375+
my $type = 1;
376+
377+
if ($self->{_has_dynamic_functions}) {
378+
$type = 2;
379+
}
380+
my @attributes = ( 'count' => $count, );
381+
382+
$self->xml_start_tag( 'valueMetadata', @attributes );
383+
384+
for my $i ( 0 .. $count - 1 ) {
385+
$self->xml_start_tag( 'bk' );
386+
387+
# Write the rc element.
388+
$self->_write_rc( $type, $i );
389+
390+
$self->xml_end_tag( 'bk' );
391+
}
392+
393+
$self->xml_end_tag( 'valueMetadata' );
394+
}
395+
396+
256397
##############################################################################
257398
#
258399
# _write_rc()
@@ -261,17 +402,36 @@ sub _write_cell_metadata {
261402
#
262403
sub _write_rc {
263404

264-
my $self = shift;
405+
my $self = shift;
406+
my $type = shift;
407+
my $value = shift;
265408

266409
my @attributes = (
267-
't' => 1,
268-
'v' => 0,
410+
't' => $type,
411+
'v' => $value,
269412
);
270413

271414
$self->xml_empty_tag( 'rc', @attributes );
272415
}
273416

274417

418+
##############################################################################
419+
#
420+
# _write_xlrd_rvb()
421+
#
422+
# Write the <xlrd:rvb> element.
423+
#
424+
sub _write_xlrd_rvb {
425+
426+
my $self = shift;
427+
my $value = shift;
428+
429+
my @attributes = ( 'i' => $value, );
430+
431+
$self->xml_empty_tag( 'xlrd:rvb', @attributes );
432+
}
433+
434+
275435
1;
276436

277437

@@ -297,6 +457,7 @@ John McNamara [email protected]
297457
298458
=head1 COPYRIGHT
299459
460+
300461
(c) MM-MMXXIII, John McNamara.
301462
302463
All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.

0 commit comments

Comments
 (0)