From c7da3873a2fe2cda6fdd04b6e33344e991c30f1c Mon Sep 17 00:00:00 2001 From: John McNamara Date: Sat, 17 Feb 2024 20:00:59 +0000 Subject: [PATCH] wip: add url support to embed_image() --- lib/Excel/Writer/XLSX/Worksheet.pm | 59 +++++++++-------- t/regression/embed_image10.t | 69 ++++++++++++++++++++ t/regression/embed_image11.t | 72 +++++++++++++++++++++ t/regression/xlsx_files/embed_image10.xlsx | Bin 0 -> 9114 bytes t/regression/xlsx_files/embed_image11.xlsx | Bin 0 -> 8610 bytes 5 files changed, 174 insertions(+), 26 deletions(-) create mode 100644 t/regression/embed_image10.t create mode 100644 t/regression/embed_image11.t create mode 100644 t/regression/xlsx_files/embed_image10.xlsx create mode 100644 t/regression/xlsx_files/embed_image11.xlsx diff --git a/lib/Excel/Writer/XLSX/Worksheet.pm b/lib/Excel/Writer/XLSX/Worksheet.pm index efbfe92f..da87e82f 100644 --- a/lib/Excel/Writer/XLSX/Worksheet.pm +++ b/lib/Excel/Writer/XLSX/Worksheet.pm @@ -235,6 +235,7 @@ sub new { $self->{_has_dynamic_functions} = 0; $self->{_has_embedded_images} = 0; $self->{_use_future_functions} = 0; + $self->{_ignore_write_string} = 0; $self->{_rstring} = ''; $self->{_previous_row} = 0; @@ -3437,18 +3438,22 @@ sub write_url { return -5; } - # Write previous row if in in-line string optimization mode. - if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) { - $self->_write_single_row( $row ); - } - # Add the default URL format. if ( !defined $xf ) { $xf = $self->{_default_url_format}; } - # Write the hyperlink string. - $self->write_string( $row, $col, $str, $xf ); + + if ( !$self->{_ignore_write_string} ) { + + # Write previous row if in in-line string optimization mode. + if ( $self->{_optimization} == 1 && $row > $self->{_previous_row} ) { + $self->_write_single_row( $row ); + } + + # Write the hyperlink string. + $self->write_string( $row, $col, $str, $xf ); + } # Store the hyperlink data in a separate structure. $self->{_hyperlinks}->{$row}->{$col} = { @@ -3458,6 +3463,7 @@ sub write_url { _tip => $tip }; + return $str_error; } @@ -6540,15 +6546,10 @@ sub embed_image { @_ = $self->_substitute_cellref( @_ ); } - my $row = $_[0]; - my $col = $_[1]; - my $image = $_[2]; - my $xf = undef; - my $x_offset; - my $y_offset; - my $x_scale; - my $y_scale; - my $anchor; + my $row = $_[0]; + my $col = $_[1]; + my $image = $_[2]; + my $xf = undef; my $url; my $tip; my $description; @@ -6558,29 +6559,36 @@ sub embed_image { return -2 if $self->_check_dimensions( $row, $col ); croak "Insufficient arguments in embed_image()" unless @_ >= 3; - croak "Couldn't locate $image: $!" unless -e $image; + croak "Couldn't locate $image: $!" unless -e $image; if ( ref $_[3] eq 'HASH' ) { + # Newer hashref bashed options. my $options = $_[3]; $xf = $options->{format}; - $x_offset = $options->{x_offset} || 0; - $y_offset = $options->{y_offset} || 0; - $x_scale = $options->{x_scale} || 1; - $y_scale = $options->{y_scale} || 1; - $anchor = $options->{object_position} || 2; $url = $options->{url}; $tip = $options->{tip}; $description = $options->{description}; $decorative = $options->{decorative}; } + # Write the url without writing a string. + if ( $url ) { + if ( !defined $xf ) { + $xf = $self->{_default_url_format}; + } + + $self->{_ignore_write_string} = 1; + $self->write_url( $row, $col, $url, $xf, undef, $tip ); + $self->{_ignore_write_string} = 0; + } - my ( $type, $width, $height, $name, $x_dpi, $y_dpi, $md5 ) = + # Get the image properties, mainly for the type and checksum. + my ( $type, undef, undef, undef, undef, undef, $md5 ) = get_image_properties( $image ); + # Check for duplicate images. my $image_index = ${ $self->{_embedded_images_indexes} }->{$md5}; - if ( !$image_index ) { push @{ ${ $self->{_embedded_images} } }, [ $image, $type, $description, $decorative ]; @@ -6589,9 +6597,8 @@ sub embed_image { ${ $self->{_embedded_images_indexes} }->{$md5} = $image_index; } - # Write the cell placeholder. - $self->{_table}->{$row}->{$col} = [ 'e', "#VALUE!", $xf, $image_index]; + $self->{_table}->{$row}->{$col} = [ 'e', "#VALUE!", $xf, $image_index ]; $self->{_has_embedded_images} = 1; } diff --git a/t/regression/embed_image10.t b/t/regression/embed_image10.t new file mode 100644 index 00000000..99e04f4c --- /dev/null +++ b/t/regression/embed_image10.t @@ -0,0 +1,69 @@ +############################################################################### +# +# 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_image10.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 $worksheet = $workbook->add_worksheet(); + +$worksheet->embed_image( 0, 0, $dir . 'images/red.png', {url => "http://www.cpan.org/"} ); + +$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/embed_image11.t b/t/regression/embed_image11.t new file mode 100644 index 00000000..cc9d8ba9 --- /dev/null +++ b/t/regression/embed_image11.t @@ -0,0 +1,72 @@ +############################################################################### +# +# 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_image11.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 $worksheet = $workbook->add_worksheet(); + +$worksheet->set_column(0, 0, 11); +$worksheet->set_row(0, 72); + +$worksheet->embed_image( 0, 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_image10.xlsx b/t/regression/xlsx_files/embed_image10.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..38ae6342c6316e33c91709e1c168264f7f13ffc2 GIT binary patch literal 9114 zcma)hbzGBu)c&L!q`N~xIz_rcx;qDwqed$YBHfB0t)PU|5NVJc!T3E0! zevhE<^ZNYp-R|x4*>!fG>)a=vcEGVxRg1Qn8x8{%q*{q|S?GC`VeGP0-D1)}v`bzjI8*@fGs>f=0)2 zUp^}$;gwXcs); zr^ok^hwL)9X%|iUg3M#&(@p#$=iYR#w=ypnKY2W30j@3h22Pl>EqP}+2GoI8e>k2R zIAEiHvL{6^&25BH;MU~2DpTf{TucWnR?5OziNO%3TTC3dK`~8G3jB!i%I@a2ropTE zUg+{ynR%VrfQ?F)7ujkxNIPF|E0K2x6BgY`V3Pg#271b5e2muBUh9}Lf*7UJemh44 zp`#~Rl`cf17*WzOV@TYo0ax~skyouwXTD$Vm_*y2M*n)yJ;9MTDHa(;l^^IGp@vlb zQ!7nTCn&6z>rDHhUcNN$FKPN_%GNN+sM@FSSLUfKm`t8Z8GC)?VS9nvt=PwnEWTEvM#L2lH5}(XQX=e zgVvRwN9pd}peJkLU=rpqlp!R}%y-Gr6;`oMXRe7a&C}_;eE@dmpYl1I31JW)*j2$1 zMbMW5Q+G;wDVlihjlp`?p5tZF=`|8;1;!R#MH=L^|#@9u z73yRzX*IT@X4Bze1U4Xd&e|hEdsF8brXf}YqgreLlF~7un{_vW_$WKk3dy{c@?GvX z=nLLZG9^gfI$(Mj~2If*ZDCxpI&DLr4t;F?UwyEZ{rLq zUw2zaVPHTi@Ms_sl{5N?Lw(kDx$F6yImqxK!gT_BoMJvP!3oqr003@YCD0n=4*WZV zP>1$Q2G20^8*f8bFfB!4X(i;h_zQDI=~VGG*MbX1w`cVs1T4WN?i{2c(!Fm{0Em1}W z2(i8|8H_`n95a_J6r_Fb(4Lbil8TiT{(3ZDxbDDAtrOy0;B%!R+?CW=SdZ~zcJzdHg^X) zd2n%FzW$RD=TiU3h^6pnrJWl~aufcnoMgBtqU9FW(-urK^NY7Ix=_@_=D!d9wi%Tm zi4|*)8t1mYYzJNYa5r`}R%@RUi{mvpAu_Z-vcCm-ZtsT6hKUl%fXeGyfc0$2_Us;m za&b>%ydjZdH;P~4VGbA~ruu|Ej<8o9YA?+?@NwmlS4-U9tFlecvaLo7nD0Kj!`g_1fjDZ-Ku2j|sJ8E*rT5`IlU!QD4tH zj8pg@@-MaAH>;;~*qoFjtgGydsE@MO99983wTo%i$D24NV(K#-%gfnQ+G(~IMpU)8 zop-hCq;hP}q?m?IWUF$peY5x1hmJ~BloYgWh~OxDh>9hosbtIdZI2*9qak8OJ*1={b{ z?QMFn3?$~V2%DJcqL)cF(7!b8i}Y1-;4BSt9ejRh4fI1hpmvqMAY9GZXK^A)3Nnqh zaV70iE>l(}=!HE|v+JQW7-0n~OuT+57{HAtW1Tcl@rw||BuxWNmEC$rI3ZqeLP&oR z;&C4c^z`8R@y7EHGhYs1z)kr1t-8l*X{x}Pd3d1z{&0PQRh!JY%K`@(S+n+_q!D6i z&&fExdfu3xk{ZkL&mX*RAwY>f#&m?!0ckh)~nU|k!e%~Ol>&G-j z4<~njh|opSp*xyCT~FM?hY#aiEjC~~r|K)bmgIRTDUIH$10<*_tgDv%MVn4>Q7WEk3@nvkKGk;I5D$CdJ z^;OKvZgdDS6np)775QMck^%2IZwJ>e@^ax_Svk?Cj`#4~BLOFn;SchDW};sd{>n)+ zmtETKpF9q5FEcW(@*8J6nh%MTOIpgI*h+-q z$su&hZxVS|g5~d};=X5E383Uj=iBGaX|hmvHymRpyC8=(J+T52zHc-P5*9-^>)JQ9 z6L{eFfGTSD8M+7|>m6u%h}AmlMddKR?j71d8HttFXS}E20YWmWZb%{*s8;=#gD%@v z22ugj5V&(Hex(~{pr@scrKjaTLH#c+t8=0{Xr3FdV>5MM)SwA9GU!%xk`9s4N9DaC zt!n*+dyjN4lDjOWUY~tiW4wLanG4dDe{|Hie7aObBod@xuPW7*pMuZbrZ=>XdE8*g zRUY9w! zju>rkpY#r}UY9|sPv?!aHn9HcJ>+KyYbNMkvS)sQo~>udTe?)gxa@OB-J&w|>5~F( zDD%a<=Ybq%;HO04lzk(5YXg7!wXJn6ji)s40{V}|qqak^q9|t)#1^cULYH?Weo<-k zNYmi$N}MqQoJI;{008@Etb2I+IsrZYA+li!25g3(-?CGo&ZNT>H{OFyeY8fQN)qxR zE79uk!f-*P%J8OM$U4zrhvGuVw=r+VNk`BmV|arBpWRZQDuZ2ZsNN!DX8PzNf1~oP zn=dMB_?^`PDfdC3TiY$FA5sPAU*>P^L41;ZNmG4W9+kY*1vNFD_9m{Wx>o%edR z-Se=&pC~XI#FDy!z33@~D7O@tNT6?^r)-$0t7oVH zXt$=i$zu}Tv%sfc>B$>}J*E{)(10mfa({n#$bdvr5cs4?%wJr8=b~rn?e^CFbY)Y) z2+2e+rKE6`LU`Yf4Nn;|602Dh`VW{BR<6{ODUWhiL8SF{7k3# z&q$LM7D#W%!;N-E$;+g>!+9`CA^6H-Vb3ncO*{qo-|zXAe|F5$HpzvZ(OXGX35P!= z+~;R~kR2)_|S-MU_J~z2k@k+c^2{z6jS%jz<{tGc2DF%}v@o0EXf0 z9iPLL>#Nu`A%27^s808{s;GMT^Yqmtu|7C<@>dKu`>S6J`cHWarZ?8jM-%toLLZ=g zY3_QFnY84VT(Vw>U?1W|Fbku}tar6`*l<=+?3cS&=XNs^{AduP;k`t^9FR1h7<-e5 zvcEI4Y{y#@YSt{_KMhR|A9qY`4o^=o>%thFHQK&X@h)?M#HUTV3V0A{Q~Vrd;Vp>g zeV{Y&=j*Rx_b+*2!%#JTSe~?LmtDRobm2YDIFVs>4>PQ-%|S@RE%OtBfA=Tr5BI!! zN<*v*m38e$R0?POiYDlTKVqHSUU@leKTuOU7V-22QII3sz}SyhF54oqc6v4yvMXS8Ie=&-wZD=ZQ;&#uU~F zSkG8*rQUk#e0Y!Yuue9Mz6-^g53OOJ? zT*7Y(^vz$<+ZMTH#ZShSTjkTwz!w=KT<;QA3z*4ZYC!-xj_bl$9CXV%4#i0x02W(2 z9y2SdYV)><&%G0-#(2zk!zbBdL#^Tqw6OFZaZ*!Gv4b(qOHwwg8@}I9yYvUL^WcY4 zBEai(D}6yBK91(xiMAmwS%W3u=!_&V@PzevZN;05CJ@-ELUnPXo_)5zmTsFf6wKB1 zaHp$^g{$f4bbyAMVr~)v7kn7hDVt}!h_codeE8`~kSSrju96=$7AK=gS3II~& z;ro22V6~whfjkJ^$JxC(Z%vAk`7wErP~KaFsV|w}x7-;X49SJh_>`opTzLlFPDp7o zY`oROAVNxX$;DAXA$lT#15#OvNJcsg;KAV&Jj8J^W%h^Dg-vrw@r3UbG*PwlRZu*O z`eeuKvD}_!5Qb^!2UPi!fQ9)gX6Yhz83~bWnbX2_r+l#03FBhP7oMziKQoYHX2N~Z zRxWJ9_Q+F_RG~3Q(LS&4_6s5~$l|MP=bOz+dlNYfxi02K)xNs8sm}{8VGAFN_*Chs zP7*0)9$Qs+rD~#^l_of0yS4Q6y3Q8LW|fv~!nk=^6us=|d&^8$nt4>!96MvAh0b9y zc3aLq{7;qVZ7|Kk{ZT14ah@~!Be|c+%$~u#XS<&`dEV;l|AlO@o(zb zLzYyFP{OK0n}z(#vR{x! ze1h=Yd;r|7pW(NUF+B@Q=@iMBZ)|>H^AywCk)az`hZ_YgrjpXx*re!@42d$s>H#tq zP8|mfQ_9eWjV`Sb)bGg@8QrA2bt}V!wD_P*k^fibfD%7MO- zRQqjF#>g=2%2j9EedAjh;N+p3BHwIaPTFAX_VWv$9XI`Z;xFS-F{Qp4+TPT6Jf6t6 zSlG}JUdV^IK;C{UJu6cEycJx@b$?3d(1~Xj=TKah<^zi|Qj|}HZrW^s2il!l{smsW zi*KYgXi|=H4FevBA2X*7KCcgVG}cv#g_buCn>ur)7nM@k?2HW2s0aAa*le~)*|()| z%rJ`u<=cAir{CDBASo~=Ilu9%jkwJITTR6sGVqRY2MGWm|Jg?T?31sKp}#ui%OcSG zr=D4w@?#eLsdTyF=agy;yRa2-eoASZsbs+ws9+c9?9!t0pbt z6U&b>-Cun(x7q2!jY!MF=7d35<$-Z@j`rYXuaD3@enzZ1!?!`}?J||=pIsDD3QRv! z?_s$;I$7UDtpN$1V=<=Cy5-)Ae<~gqNh92AVid|uXP&Y1Xs#sxQDm!-_cQ3aDxrYz zS+sAmsqn5|UA)bnM3QS?`M$--s?8{JUd9@vc;77s^@|UkvC69rE=eDUHHK$c%A<;w zEV}hyWZxT`kE|11r$y#C0t|%Hk`Jea_9rdw_SW}h;f*?%yNwq7QqR%}4E&)s|I_eY zlEaz)N5fZ|GV2H*W4dNhPiT~91Xe>Stl0C7bGbVu5AK%P8-=D9Fcw!|oTteWw_yiwDPF7{+2T?97PjhKr zp;+rp^|?TYWNQX|M+I36q}ZuCA9PW1o=3 zmcZgBkx5izqC!avsgOr+u~TsVCVe5hAN=YWy-)f5oX-iJ4X3E7fiVOU>ZBKYb&R0v z^bMTP+--ypqDXKGDSxN$|C6T$bh<{J*`L$_2Y6JFc<_+t%(f)LR;I@%rESL71tKlj z9cYrqRcL!S`08XX8bzvaLc2oa57HLwdX|*)qF#r$bBE-viWK-kw$ArYw~*wYy>11T z*?f*Bj9WXKrtj&(P3PKEHdNJrW%Q_YgD>w`v13Iei9~N^Vy=!BWV{ph4zn&Nu_!fW zSXkAks!T}bR(revR3g^2dr_;BOw{b!LsofP1JOeklIkLw}1I zrZ_2_Hd;d21Lm3F)5h=&(pXkc>$}0!K`rG>tfvw2vLLQiTU)_h+lXx<5=|bDoPyj< zLbQ8v0gAZT2a{lLgw_sb5Q%cp=UA-#ahC`aE{MV4YLq}kU!6%m|URy_R@Eqzr{=4+a z#j)F63%^xHctEiHcDp}I&}C5kNOS-1N%?CTiXEtMK;*$fnQMf3b5A%)llul>kU!2``S?w*1U__A@3$m){LEc8`f7uSDD4I~D zbXNM*lEuEhy!>({S;#?5XDs3M>SG;5N5>}W{zT6hX)+geB)d@G3D)V z+IJ>{j#_mT>~bil(N;YNPH9AMtGqc)TxY7RmSE$4KQk@G_Waq6Ql3i8y($_oF|M%s z;nw6S_VwK1HL2!S4);qp{9LXU;q#a?(8k`9%ih`24#>ml>SFhgkdD*-N48(X+VWWb zk~D8+VKMi8&lMeE2!pn-6^ZdevB4CpfHXREDvNY9E7=!)@lAo~@f}`nr}gFYj}Tgw*at&dwF!MPuUf+oh|huiy*Ae^7tzn*JN?^(D$xSaSHJer=`l-vF=g8Lk2|3L1j-1N5p`2Zy{A>;CdTuM0I-A)z=wApewY{{CP8oB^%^_rM=a zcyRpdMDXA6uQ#(-@hymd;Qyty{cl#T_gq)4(7->y@8Xx0-|C94&VRFTy@k1IftTzz z3;){C{5MP2TY;;VbjbhJ($B`=zZtk*e6Jdify;nbo%Mf~;{RssdPVzb>@u4d1RGQS bF!pDC`!`@@c(%IS&2HNJ literal 0 HcmV?d00001 diff --git a/t/regression/xlsx_files/embed_image11.xlsx b/t/regression/xlsx_files/embed_image11.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..989ea03b6bf9f9abec7c5ac5429f0120b657eea7 GIT binary patch literal 8610 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 literal 0 HcmV?d00001