File Coverage

blib/lib/Excel/Writer/XLSX/Drawing.pm
Criterion Covered Total %
statement 482 484 99.5
branch 68 76 89.4
condition 8 12 66.6
subroutine 54 54 100.0
pod 0 1 0.0
total 612 627 97.6


line stmt bran cond sub pod time code
1             package Excel::Writer::XLSX::Drawing;
2              
3             ###############################################################################
4             #
5             # Drawing - A class for writing the Excel XLSX drawing.xml file.
6             #
7             # Used in conjunction with Excel::Writer::XLSX
8             #
9             # Copyright 2000-2019, John McNamara, jmcnamara@cpan.org
10             #
11             # Documentation after __END__
12             #
13              
14             # perltidy with the following options: -mbl=2 -pt=0 -nola
15              
16 1041     1041   18505 use 5.008002;
  1041         3743  
17 1041     1041   5753 use strict;
  1041         2216  
  1041         20905  
18 1041     1041   4995 use warnings;
  1041         2063  
  1041         24076  
19 1041     1041   5263 use Carp;
  1041         2246  
  1041         59673  
20 1041     1041   508142 use Excel::Writer::XLSX::Package::XMLwriter;
  1041         3332  
  1041         57485  
21 1041     1041   7734 use Excel::Writer::XLSX::Worksheet;
  1041         2262  
  1041         4255834  
22              
23             our @ISA = qw(Excel::Writer::XLSX::Package::XMLwriter);
24             our $VERSION = '1.03';
25              
26              
27             ###############################################################################
28             #
29             # Public and private API methods.
30             #
31             ###############################################################################
32              
33              
34             ###############################################################################
35             #
36             # new()
37             #
38             # Constructor.
39             #
40             sub new {
41              
42 512     512 0 19990 my $class = shift;
43 512         1353 my $fh = shift;
44 512         2673 my $self = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
45              
46 512         1871 $self->{_drawings} = [];
47 512         1442 $self->{_embedded} = 0;
48 512         1489 $self->{_orientation} = 0;
49              
50 512         1263 bless $self, $class;
51              
52 512         1706 return $self;
53             }
54              
55              
56             ###############################################################################
57             #
58             # _assemble_xml_file()
59             #
60             # Assemble and write the XML file.
61             #
62             sub _assemble_xml_file {
63              
64 474     474   1385 my $self = shift;
65              
66 474         4652 $self->xml_declaration;
67              
68             # Write the xdr:wsDr element.
69 474         3083 $self->_write_drawing_workspace();
70              
71 474 100       2281 if ( $self->{_embedded} ) {
72              
73 454         1162 my $index = 0;
74 454         1066 for my $drawing_object ( @{ $self->{_drawings} } ) {
  454         1695  
75             # Write the xdr:twoCellAnchor element.
76 523         2344 $self->_write_two_cell_anchor( ++$index, $drawing_object );
77             }
78             }
79             else {
80 20         49 my $index = 0;
81              
82             # Write the xdr:absoluteAnchor element.
83 20         79 $self->_write_absolute_anchor( ++$index );
84             }
85              
86 474         2341 $self->xml_end_tag( 'xdr:wsDr' );
87              
88             # Close the XML writer filehandle.
89 474         3194 $self->xml_get_fh()->close();
90             }
91              
92              
93             ###############################################################################
94             #
95             # _add_drawing_object()
96             #
97             # Add a chart, image or shape sub object to the drawing.
98             #
99             sub _add_drawing_object {
100              
101 527     527   1567 my $self = shift;
102              
103 527         5055 my $drawing_object = {
104             _type => undef,
105             _dimensions => [],
106             _width => 0,
107             _height => 0,
108             _description => undef,
109             _shape => undef,
110             _anchor => undef,
111             _rel_index => 0,
112             _url_rel_index => 0,
113             _tip => undef
114             };
115              
116 527         1341 push @{ $self->{_drawings} }, $drawing_object;
  527         1611  
117              
118 527         1600 return $drawing_object;
119             }
120              
121              
122             ###############################################################################
123             #
124             # Internal methods.
125             #
126             ###############################################################################
127              
128              
129             ###############################################################################
130             #
131             # XML writing methods.
132             #
133             ###############################################################################
134              
135              
136             ##############################################################################
137             #
138             # _write_drawing_workspace()
139             #
140             # Write the element.
141             #
142             sub _write_drawing_workspace {
143              
144 474     474   1198 my $self = shift;
145 474         1184 my $schema = 'http://schemas.openxmlformats.org/drawingml/';
146 474         1792 my $xmlns_xdr = $schema . '2006/spreadsheetDrawing';
147 474         1361 my $xmlns_a = $schema . '2006/main';
148              
149 474         1922 my @attributes = (
150             'xmlns:xdr' => $xmlns_xdr,
151             'xmlns:a' => $xmlns_a,
152             );
153              
154 474         2932 $self->xml_start_tag( 'xdr:wsDr', @attributes );
155             }
156              
157              
158             ##############################################################################
159             #
160             # _write_two_cell_anchor()
161             #
162             # Write the element.
163             #
164             sub _write_two_cell_anchor {
165              
166 523     523   1284 my $self = shift;
167 523         1117 my $index = shift;
168 523         1189 my $drawing_object = shift;
169              
170 523         1364 my $type = $drawing_object->{_type};
171 523         1232 my $dimensions = $drawing_object->{_dimensions};
172 523         1384 my $col_from = $dimensions->[0];
173 523         1192 my $row_from = $dimensions->[1];
174 523         1172 my $col_from_offset = $dimensions->[2];
175 523         1260 my $row_from_offset = $dimensions->[3];
176 523         1238 my $col_to = $dimensions->[4];
177 523         1200 my $row_to = $dimensions->[5];
178 523         1149 my $col_to_offset = $dimensions->[6];
179 523         1157 my $row_to_offset = $dimensions->[7];
180 523         1167 my $col_absolute = $dimensions->[8];
181 523         1081 my $row_absolute = $dimensions->[9];
182 523         1220 my $width = $drawing_object->{_width};
183 523         1235 my $height = $drawing_object->{_height};
184 523         1372 my $description = $drawing_object->{_description};
185 523         1328 my $shape = $drawing_object->{_shape};
186 523         1123 my $anchor = $drawing_object->{_anchor};
187 523         1174 my $rel_index = $drawing_object->{_rel_index};
188 523         1092 my $url_rel_index = $drawing_object->{_url_rel_index};
189 523         1042 my $tip = $drawing_object->{_tip};
190              
191 523         1351 my @attributes = ();
192              
193             # Add attribute for images.
194 523 100       2940 if ( $anchor == 2 ) {
    100          
195 101         302 push @attributes, ( editAs => 'oneCell' );
196             }
197             elsif ( $anchor == 3 ) {
198 1         3 push @attributes, ( editAs => 'absolute' );
199             }
200              
201             # Add editAs attribute for shapes.
202 523 50       2078 push @attributes, ( editAs => $shape->{_editAs} ) if $shape->{_editAs};
203              
204 523         2509 $self->xml_start_tag( 'xdr:twoCellAnchor', @attributes );
205              
206             # Write the xdr:from element.
207 523         3017 $self->_write_from(
208             $col_from,
209             $row_from,
210             $col_from_offset,
211             $row_from_offset,
212              
213             );
214              
215             # Write the xdr:from element.
216 523         2619 $self->_write_to(
217             $col_to,
218             $row_to,
219             $col_to_offset,
220             $row_to_offset,
221              
222             );
223              
224 523 100       2757 if ( $type == 1 ) {
    100          
225              
226             # Graphic frame.
227              
228             # Write the xdr:graphicFrame element for charts.
229 375         1953 $self->_write_graphic_frame( $index, $rel_index, $description );
230             }
231             elsif ( $type == 2 ) {
232              
233             # Write the xdr:pic element.
234 104         564 $self->_write_pic(
235             $index, $rel_index, $col_absolute,
236             $row_absolute, $width, $height,
237             $description, $url_rel_index, $tip
238             );
239             }
240             else {
241              
242             # Write the xdr:sp element for shapes.
243 44         158 $self->_write_sp( $index, $col_absolute, $row_absolute, $width, $height,
244             $shape );
245             }
246              
247             # Write the xdr:clientData element.
248 523         2416 $self->_write_client_data();
249              
250 523         1860 $self->xml_end_tag( 'xdr:twoCellAnchor' );
251             }
252              
253              
254             ##############################################################################
255             #
256             # _write_absolute_anchor()
257             #
258             # Write the element.
259             #
260             sub _write_absolute_anchor {
261              
262 20     20   53 my $self = shift;
263 20         40 my $index = shift;
264              
265 20         72 $self->xml_start_tag( 'xdr:absoluteAnchor' );
266              
267             # Different co-ordinates for horizonatal (= 0) and vertical (= 1).
268 20 100       99 if ( $self->{_orientation} == 0 ) {
269              
270             # Write the xdr:pos element.
271 19         95 $self->_write_pos( 0, 0 );
272              
273             # Write the xdr:ext element.
274 19         84 $self->_write_ext( 9308969, 6078325 );
275              
276             }
277             else {
278              
279             # Write the xdr:pos element.
280 1         5 $self->_write_pos( 0, -47625 );
281              
282             # Write the xdr:ext element.
283 1         4 $self->_write_ext( 6162675, 6124575 );
284              
285             }
286              
287              
288             # Write the xdr:graphicFrame element.
289 20         118 $self->_write_graphic_frame( $index, $index );
290              
291             # Write the xdr:clientData element.
292 20         90 $self->_write_client_data();
293              
294 20         67 $self->xml_end_tag( 'xdr:absoluteAnchor' );
295             }
296              
297              
298             ##############################################################################
299             #
300             # _write_from()
301             #
302             # Write the element.
303             #
304             sub _write_from {
305              
306 523     523   1428 my $self = shift;
307 523         1259 my $col = shift;
308 523         1240 my $row = shift;
309 523         1191 my $col_offset = shift;
310 523         1108 my $row_offset = shift;
311              
312 523         2187 $self->xml_start_tag( 'xdr:from' );
313              
314             # Write the xdr:col element.
315 523         2755 $self->_write_col( $col );
316              
317             # Write the xdr:colOff element.
318 523         2474 $self->_write_col_off( $col_offset );
319              
320             # Write the xdr:row element.
321 523         2477 $self->_write_row( $row );
322              
323             # Write the xdr:rowOff element.
324 523         2455 $self->_write_row_off( $row_offset );
325              
326 523         3004 $self->xml_end_tag( 'xdr:from' );
327             }
328              
329              
330             ##############################################################################
331             #
332             # _write_to()
333             #
334             # Write the element.
335             #
336             sub _write_to {
337              
338 523     523   1356 my $self = shift;
339 523         1233 my $col = shift;
340 523         1644 my $row = shift;
341 523         1244 my $col_offset = shift;
342 523         1220 my $row_offset = shift;
343              
344 523         2238 $self->xml_start_tag( 'xdr:to' );
345              
346             # Write the xdr:col element.
347 523         2036 $self->_write_col( $col );
348              
349             # Write the xdr:colOff element.
350 523         1982 $self->_write_col_off( $col_offset );
351              
352             # Write the xdr:row element.
353 523         2039 $self->_write_row( $row );
354              
355             # Write the xdr:rowOff element.
356 523         1975 $self->_write_row_off( $row_offset );
357              
358 523         2022 $self->xml_end_tag( 'xdr:to' );
359             }
360              
361              
362             ##############################################################################
363             #
364             # _write_col()
365             #
366             # Write the element.
367             #
368             sub _write_col {
369              
370 1047     1047   2289 my $self = shift;
371 1047         2116 my $data = shift;
372              
373 1047         4478 $self->xml_data_element( 'xdr:col', $data );
374             }
375              
376              
377             ##############################################################################
378             #
379             # _write_col_off()
380             #
381             # Write the element.
382             #
383             sub _write_col_off {
384              
385 1047     1047   2152 my $self = shift;
386 1047         1840 my $data = shift;
387              
388 1047         3053 $self->xml_data_element( 'xdr:colOff', $data );
389             }
390              
391              
392             ##############################################################################
393             #
394             # _write_row()
395             #
396             # Write the element.
397             #
398             sub _write_row {
399              
400 1047     1047   2143 my $self = shift;
401 1047         1889 my $data = shift;
402              
403 1047         2909 $self->xml_data_element( 'xdr:row', $data );
404             }
405              
406              
407             ##############################################################################
408             #
409             # _write_row_off()
410             #
411             # Write the element.
412             #
413             sub _write_row_off {
414              
415 1047     1047   2070 my $self = shift;
416 1047         1983 my $data = shift;
417              
418 1047         3058 $self->xml_data_element( 'xdr:rowOff', $data );
419             }
420              
421              
422             ##############################################################################
423             #
424             # _write_pos()
425             #
426             # Write the element.
427             #
428             sub _write_pos {
429              
430 21     21   60 my $self = shift;
431 21         44 my $x = shift;
432 21         40 my $y = shift;
433              
434 21         77 my @attributes = (
435             'x' => $x,
436             'y' => $y,
437             );
438              
439 21         134 $self->xml_empty_tag( 'xdr:pos', @attributes );
440             }
441              
442              
443             ##############################################################################
444             #
445             # _write_ext()
446             #
447             # Write the element.
448             #
449             sub _write_ext {
450              
451 21     21   58 my $self = shift;
452 21         42 my $cx = shift;
453 21         95 my $cy = shift;
454              
455 21         97 my @attributes = (
456             'cx' => $cx,
457             'cy' => $cy,
458             );
459              
460 21         78 $self->xml_empty_tag( 'xdr:ext', @attributes );
461             }
462              
463              
464             ##############################################################################
465             #
466             # _write_graphic_frame()
467             #
468             # Write the element.
469             #
470             sub _write_graphic_frame {
471              
472 395     395   1015 my $self = shift;
473 395         973 my $index = shift;
474 395         921 my $rel_index = shift;
475 395         964 my $name = shift;
476 395         965 my $macro = '';
477              
478 395         1489 my @attributes = ( 'macro' => $macro );
479              
480 395         2023 $self->xml_start_tag( 'xdr:graphicFrame', @attributes );
481              
482             # Write the xdr:nvGraphicFramePr element.
483 395         2148 $self->_write_nv_graphic_frame_pr( $index, $name );
484              
485             # Write the xdr:xfrm element.
486 395         2121 $self->_write_xfrm();
487              
488             # Write the a:graphic element.
489 395         2050 $self->_write_atag_graphic( $rel_index );
490              
491 395         1390 $self->xml_end_tag( 'xdr:graphicFrame' );
492             }
493              
494              
495             ##############################################################################
496             #
497             # _write_nv_graphic_frame_pr()
498             #
499             # Write the element.
500             #
501             sub _write_nv_graphic_frame_pr {
502              
503 395     395   1114 my $self = shift;
504 395         938 my $index = shift;
505 395         1002 my $name = shift;
506              
507 395 100       1705 if ( !$name ) {
508 390         1453 $name = 'Chart ' . $index;
509             }
510              
511 395         1757 $self->xml_start_tag( 'xdr:nvGraphicFramePr' );
512              
513             # Write the xdr:cNvPr element.
514 395         2210 $self->_write_c_nv_pr( $index + 1, $name );
515              
516             # Write the xdr:cNvGraphicFramePr element.
517 395         2000 $self->_write_c_nv_graphic_frame_pr();
518              
519 395         1539 $self->xml_end_tag( 'xdr:nvGraphicFramePr' );
520             }
521              
522              
523             ##############################################################################
524             #
525             # _write_c_nv_pr()
526             #
527             # Write the element.
528             #
529             sub _write_c_nv_pr {
530              
531 545     545   1524 my $self = shift;
532 545         1300 my $index = shift;
533 545         1425 my $name = shift;
534 545         1506 my $description = shift;
535 545         1282 my $url_rel_index = shift;
536 545         1208 my $tip = shift;
537              
538 545         2117 my @attributes = (
539             'id' => $index,
540             'name' => $name,
541             );
542              
543             # Add description attribute for images.
544 545 100       2240 if ( defined $description ) {
545 104         309 push @attributes, ( descr => $description );
546             }
547              
548 545 100       2220 if ($url_rel_index) {
549 21         115 $self->xml_start_tag( 'xdr:cNvPr', @attributes );
550              
551             # Write the a:hlinkClick element.
552 21         97 $self->_write_a_hlink_click($url_rel_index, $tip);
553              
554 21         76 $self->xml_end_tag( 'xdr:cNvPr');
555             }
556             else {
557 524         4038 $self->xml_empty_tag( 'xdr:cNvPr', @attributes );
558             }
559              
560             }
561              
562              
563             ##############################################################################
564             #
565             # _write_a_hlink_click()
566             #
567             # Write the element.
568             #
569             sub _write_a_hlink_click {
570              
571 21     21   51 my $self = shift;
572 21         59 my $index = shift;
573 21         38 my $tip = shift;
574 21         58 my $schema = 'http://schemas.openxmlformats.org/officeDocument/';
575 21         70 my $xmlns_r = $schema . '2006/relationships';
576 21         61 my $r_id = 'rId' . $index;
577              
578 21         80 my @attributes = (
579             'xmlns:r' => $xmlns_r,
580             'r:id' => $r_id,
581             );
582              
583 21 100       74 push( @attributes, ( 'tooltip' => $tip ) ) if $tip;
584              
585 21         140 $self->xml_empty_tag('a:hlinkClick', @attributes );
586             }
587              
588              
589             ##############################################################################
590             #
591             # _write_c_nv_graphic_frame_pr()
592             #
593             # Write the element.
594             #
595             sub _write_c_nv_graphic_frame_pr {
596              
597 397     397   1016 my $self = shift;
598              
599 397 100       1668 if ( $self->{_embedded} ) {
600 376         1675 $self->xml_empty_tag( 'xdr:cNvGraphicFramePr' );
601             }
602             else {
603 21         91 $self->xml_start_tag( 'xdr:cNvGraphicFramePr' );
604              
605             # Write the a:graphicFrameLocks element.
606 21         121 $self->_write_a_graphic_frame_locks();
607              
608 21         119 $self->xml_end_tag( 'xdr:cNvGraphicFramePr' );
609             }
610             }
611              
612              
613             ##############################################################################
614             #
615             # _write_a_graphic_frame_locks()
616             #
617             # Write the element.
618             #
619             sub _write_a_graphic_frame_locks {
620              
621 22     22   58 my $self = shift;
622 22         49 my $no_grp = 1;
623              
624 22         66 my @attributes = ( 'noGrp' => $no_grp );
625              
626 22         89 $self->xml_empty_tag( 'a:graphicFrameLocks', @attributes );
627             }
628              
629              
630             ##############################################################################
631             #
632             # _write_xfrm()
633             #
634             # Write the element.
635             #
636             sub _write_xfrm {
637              
638 395     395   1091 my $self = shift;
639              
640 395         1844 $self->xml_start_tag( 'xdr:xfrm' );
641              
642             # Write the xfrmOffset element.
643 395         1908 $self->_write_xfrm_offset();
644              
645             # Write the xfrmOffset element.
646 395         2029 $self->_write_xfrm_extension();
647              
648 395         1701 $self->xml_end_tag( 'xdr:xfrm' );
649             }
650              
651              
652             ##############################################################################
653             #
654             # _write_xfrm_offset()
655             #
656             # Write the xfrm sub-element.
657             #
658             sub _write_xfrm_offset {
659              
660 396     396   1060 my $self = shift;
661 396         1048 my $x = 0;
662 396         929 my $y = 0;
663              
664 396         1654 my @attributes = (
665             'x' => $x,
666             'y' => $y,
667             );
668              
669 396         1775 $self->xml_empty_tag( 'a:off', @attributes );
670             }
671              
672              
673             ##############################################################################
674             #
675             # _write_xfrm_extension()
676             #
677             # Write the xfrm sub-element.
678             #
679             sub _write_xfrm_extension {
680              
681 396     396   940 my $self = shift;
682 396         877 my $x = 0;
683 396         894 my $y = 0;
684              
685 396         1505 my @attributes = (
686             'cx' => $x,
687             'cy' => $y,
688             );
689              
690 396         1658 $self->xml_empty_tag( 'a:ext', @attributes );
691             }
692              
693              
694             ##############################################################################
695             #
696             # _write_atag_graphic()
697             #
698             # Write the element.
699             #
700             sub _write_atag_graphic {
701              
702 395     395   1026 my $self = shift;
703 395         938 my $index = shift;
704              
705 395         1598 $self->xml_start_tag( 'a:graphic' );
706              
707             # Write the a:graphicData element.
708 395         1883 $self->_write_atag_graphic_data( $index );
709              
710 395         1482 $self->xml_end_tag( 'a:graphic' );
711             }
712              
713              
714             ##############################################################################
715             #
716             # _write_atag_graphic_data()
717             #
718             # Write the element.
719             #
720             sub _write_atag_graphic_data {
721              
722 395     395   1090 my $self = shift;
723 395         942 my $index = shift;
724 395         925 my $uri = 'http://schemas.openxmlformats.org/drawingml/2006/chart';
725              
726 395         1288 my @attributes = ( 'uri' => $uri, );
727              
728 395         1901 $self->xml_start_tag( 'a:graphicData', @attributes );
729              
730             # Write the c:chart element.
731 395         2625 $self->_write_c_chart( 'rId' . $index );
732              
733 395         1559 $self->xml_end_tag( 'a:graphicData' );
734             }
735              
736              
737             ##############################################################################
738             #
739             # _write_c_chart()
740             #
741             # Write the element.
742             #
743             sub _write_c_chart {
744              
745 396     396   963 my $self = shift;
746 396         1164 my $r_id = shift;
747 396         963 my $schema = 'http://schemas.openxmlformats.org/';
748 396         1302 my $xmlns_c = $schema . 'drawingml/2006/chart';
749 396         1337 my $xmlns_r = $schema . 'officeDocument/2006/relationships';
750              
751              
752 396         1747 my @attributes = (
753             'xmlns:c' => $xmlns_c,
754             'xmlns:r' => $xmlns_r,
755             'r:id' => $r_id,
756             );
757              
758 396         1648 $self->xml_empty_tag( 'c:chart', @attributes );
759             }
760              
761              
762             ##############################################################################
763             #
764             # _write_client_data()
765             #
766             # Write the element.
767             #
768             sub _write_client_data {
769              
770 543     543   1312 my $self = shift;
771              
772 543         1920 $self->xml_empty_tag( 'xdr:clientData' );
773             }
774              
775              
776             ##############################################################################
777             #
778             # _write_sp()
779             #
780             # Write the element.
781             #
782             sub _write_sp {
783              
784 44     44   83 my $self = shift;
785 44         76 my $index = shift;
786 44         74 my $col_absolute = shift;
787 44         68 my $row_absolute = shift;
788 44         110 my $width = shift;
789 44         87 my $height = shift;
790 44         76 my $shape = shift;
791              
792 44 100       137 if ( $shape->{_connect} ) {
793 11         29 my @attributes = ( macro => '' );
794 11         39 $self->xml_start_tag( 'xdr:cxnSp', @attributes );
795              
796             # Write the xdr:nvCxnSpPr element.
797 11         44 $self->_write_nv_cxn_sp_pr( $index, $shape );
798              
799             # Write the xdr:spPr element.
800 11         40 $self->_write_xdr_sp_pr( $index, $col_absolute, $row_absolute, $width,
801             $height, $shape );
802              
803 11         29 $self->xml_end_tag( 'xdr:cxnSp' );
804             }
805             else {
806              
807             # Add attribute for shapes.
808 33         114 my @attributes = ( macro => '', textlink => '' );
809 33         164 $self->xml_start_tag( 'xdr:sp', @attributes );
810              
811             # Write the xdr:nvSpPr element.
812 33         150 $self->_write_nv_sp_pr( $index, $shape );
813              
814             # Write the xdr:spPr element.
815 33         159 $self->_write_xdr_sp_pr( $index, $col_absolute, $row_absolute, $width,
816             $height, $shape );
817              
818             # Write the xdr:txBody element.
819 33 100       107 if ( $shape->{_text} ) {
820 16         48 $self->_write_txBody( $col_absolute, $row_absolute, $width, $height,
821             $shape );
822             }
823              
824 33         95 $self->xml_end_tag( 'xdr:sp' );
825             }
826             }
827             ##############################################################################
828             #
829             # _write_nv_cxn_sp_pr()
830             #
831             # Write the element.
832             #
833             sub _write_nv_cxn_sp_pr {
834              
835 12     12   39 my $self = shift;
836 12         22 my $index = shift;
837 12         23 my $shape = shift;
838              
839 12         39 $self->xml_start_tag( 'xdr:nvCxnSpPr' );
840              
841             $shape->{_name} = join( ' ', $shape->{_type}, $index )
842 12 100       72 unless defined $shape->{_name};
843 12         46 $self->_write_c_nv_pr( $shape->{_id}, $shape->{_name} );
844              
845 12         43 $self->xml_start_tag( 'xdr:cNvCxnSpPr' );
846              
847 12         34 my @attributes = ( noChangeShapeType => '1' );
848 12         40 $self->xml_empty_tag( 'a:cxnSpLocks', @attributes );
849              
850 12 100       45 if ( $shape->{_start} ) {
851             @attributes =
852 11         50 ( 'id' => $shape->{_start}, 'idx' => $shape->{_start_index} );
853 11         33 $self->xml_empty_tag( 'a:stCxn', @attributes );
854             }
855              
856 12 100       39 if ( $shape->{_end} ) {
857 11         40 @attributes = ( 'id' => $shape->{_end}, 'idx' => $shape->{_end_index} );
858 11         31 $self->xml_empty_tag( 'a:endCxn', @attributes );
859             }
860 12         42 $self->xml_end_tag( 'xdr:cNvCxnSpPr' );
861 12         34 $self->xml_end_tag( 'xdr:nvCxnSpPr' );
862             }
863              
864              
865             ##############################################################################
866             #
867             # _write_nv_sp_pr()
868             #
869             # Write the element.
870             #
871             sub _write_nv_sp_pr {
872              
873 33     33   71 my $self = shift;
874 33         59 my $index = shift;
875 33         73 my $shape = shift;
876              
877 33         88 my @attributes = ();
878              
879 33         113 $self->xml_start_tag( 'xdr:nvSpPr' );
880              
881 33         110 my $shape_name = $shape->{_type} . ' ' . $index;
882              
883 33         133 $self->_write_c_nv_pr( $shape->{_id}, $shape_name );
884              
885 33 50       102 @attributes = ( 'txBox' => 1 ) if $shape->{_txBox};
886              
887 33         116 $self->xml_start_tag( 'xdr:cNvSpPr', @attributes );
888              
889 33         83 @attributes = ( noChangeArrowheads => '1' );
890              
891 33         112 $self->xml_empty_tag( 'a:spLocks', @attributes );
892              
893 33         117 $self->xml_end_tag( 'xdr:cNvSpPr' );
894 33         92 $self->xml_end_tag( 'xdr:nvSpPr' );
895             }
896              
897              
898             ##############################################################################
899             #
900             # _write_pic()
901             #
902             # Write the element.
903             #
904             sub _write_pic {
905              
906 104     104   240 my $self = shift;
907 104         228 my $index = shift;
908 104         198 my $rel_index = shift;
909 104         219 my $col_absolute = shift;
910 104         206 my $row_absolute = shift;
911 104         227 my $width = shift;
912 104         201 my $height = shift;
913 104         195 my $description = shift;
914 104         170 my $url_rel_index = shift;
915 104         224 my $tip = shift;
916              
917 104         451 $self->xml_start_tag( 'xdr:pic' );
918              
919             # Write the xdr:nvPicPr element.
920 104         489 $self->_write_nv_pic_pr( $index, $rel_index, $description, $url_rel_index,
921             $tip );
922              
923             # Write the xdr:blipFill element.
924 104         624 $self->_write_blip_fill( $rel_index );
925              
926             # Pictures are rectangle shapes by default.
927 104         404 my $shape = { _type => 'rect' };
928              
929             # Write the xdr:spPr element.
930 104         494 $self->_write_sp_pr( $col_absolute, $row_absolute, $width, $height,
931             $shape );
932              
933 104         317 $self->xml_end_tag( 'xdr:pic' );
934             }
935              
936              
937             ##############################################################################
938             #
939             # _write_nv_pic_pr()
940             #
941             # Write the element.
942             #
943             sub _write_nv_pic_pr {
944              
945 104     104   231 my $self = shift;
946 104         210 my $index = shift;
947 104         230 my $rel_index = shift;
948 104         209 my $description = shift;
949 104         249 my $url_rel_index = shift;
950 104         210 my $tip = shift;
951              
952 104         408 $self->xml_start_tag( 'xdr:nvPicPr' );
953              
954             # Write the xdr:cNvPr element.
955 104         817 $self->_write_c_nv_pr( $index + 1, 'Picture ' . $index,
956             $description, $url_rel_index, $tip );
957              
958             # Write the xdr:cNvPicPr element.
959 104         425 $self->_write_c_nv_pic_pr();
960              
961 104         486 $self->xml_end_tag( 'xdr:nvPicPr' );
962             }
963              
964              
965             ##############################################################################
966             #
967             # _write_c_nv_pic_pr()
968             #
969             # Write the element.
970             #
971             sub _write_c_nv_pic_pr {
972              
973 104     104   276 my $self = shift;
974              
975 104         394 $self->xml_start_tag( 'xdr:cNvPicPr' );
976              
977             # Write the a:picLocks element.
978 104         439 $self->_write_a_pic_locks();
979              
980 104         388 $self->xml_end_tag( 'xdr:cNvPicPr' );
981             }
982              
983              
984             ##############################################################################
985             #
986             # _write_a_pic_locks()
987             #
988             # Write the element.
989             #
990             sub _write_a_pic_locks {
991              
992 104     104   245 my $self = shift;
993 104         222 my $no_change_aspect = 1;
994              
995 104         448 my @attributes = ( 'noChangeAspect' => $no_change_aspect );
996              
997 104         393 $self->xml_empty_tag( 'a:picLocks', @attributes );
998             }
999              
1000              
1001             ##############################################################################
1002             #
1003             # _write_blip_fill()
1004             #
1005             # Write the element.
1006             #
1007             sub _write_blip_fill {
1008              
1009 104     104   236 my $self = shift;
1010 104         229 my $index = shift;
1011              
1012 104         404 $self->xml_start_tag( 'xdr:blipFill' );
1013              
1014             # Write the a:blip element.
1015 104         607 $self->_write_a_blip( $index );
1016              
1017             # Write the a:stretch element.
1018 104         437 $self->_write_a_stretch();
1019              
1020 104         339 $self->xml_end_tag( 'xdr:blipFill' );
1021             }
1022              
1023              
1024             ##############################################################################
1025             #
1026             # _write_a_blip()
1027             #
1028             # Write the element.
1029             #
1030             sub _write_a_blip {
1031              
1032 104     104   381 my $self = shift;
1033 104         272 my $index = shift;
1034 104         695 my $schema = 'http://schemas.openxmlformats.org/officeDocument/';
1035 104         365 my $xmlns_r = $schema . '2006/relationships';
1036 104         342 my $r_embed = 'rId' . $index;
1037              
1038 104         434 my @attributes = (
1039             'xmlns:r' => $xmlns_r,
1040             'r:embed' => $r_embed,
1041             );
1042              
1043 104         401 $self->xml_empty_tag( 'a:blip', @attributes );
1044             }
1045              
1046              
1047             ##############################################################################
1048             #
1049             # _write_a_stretch()
1050             #
1051             # Write the element.
1052             #
1053             sub _write_a_stretch {
1054              
1055 104     104   218 my $self = shift;
1056              
1057 104         373 $self->xml_start_tag( 'a:stretch' );
1058              
1059             # Write the a:fillRect element.
1060 104         387 $self->_write_a_fill_rect();
1061              
1062 104         331 $self->xml_end_tag( 'a:stretch' );
1063             }
1064              
1065              
1066             ##############################################################################
1067             #
1068             # _write_a_fill_rect()
1069             #
1070             # Write the element.
1071             #
1072             sub _write_a_fill_rect {
1073              
1074 104     104   232 my $self = shift;
1075              
1076 104         362 $self->xml_empty_tag( 'a:fillRect' );
1077             }
1078              
1079              
1080             ##############################################################################
1081             #
1082             # _write_sp_pr()
1083             #
1084             # Write the element, for charts.
1085             #
1086             sub _write_sp_pr {
1087              
1088 104     104   387 my $self = shift;
1089 104         227 my $col_absolute = shift;
1090 104         221 my $row_absolute = shift;
1091 104         227 my $width = shift;
1092 104         257 my $height = shift;
1093 104   50     505 my $shape = shift || {};
1094              
1095 104         431 $self->xml_start_tag( 'xdr:spPr' );
1096              
1097             # Write the a:xfrm element.
1098 104         628 $self->_write_a_xfrm( $col_absolute, $row_absolute, $width, $height );
1099              
1100             # Write the a:prstGeom element.
1101 104         404 $self->_write_a_prst_geom( $shape );
1102              
1103 104         350 $self->xml_end_tag( 'xdr:spPr' );
1104             }
1105              
1106              
1107             ##############################################################################
1108             #
1109             # _write_xdr_sp_pr()
1110             #
1111             # Write the element for shapes.
1112             #
1113             sub _write_xdr_sp_pr {
1114              
1115 44     44   85 my $self = shift;
1116 44         71 my $index = shift;
1117 44         71 my $col_absolute = shift;
1118 44         74 my $row_absolute = shift;
1119 44         73 my $width = shift;
1120 44         82 my $height = shift;
1121 44         83 my $shape = shift;
1122              
1123 44         141 my @attributes = ( 'bwMode' => 'auto' );
1124              
1125 44         136 $self->xml_start_tag( 'xdr:spPr', @attributes );
1126              
1127             # Write the a:xfrm element.
1128 44         158 $self->_write_a_xfrm( $col_absolute, $row_absolute, $width, $height,
1129             $shape );
1130              
1131             # Write the a:prstGeom element.
1132 44         146 $self->_write_a_prst_geom( $shape );
1133              
1134 44         92 my $fill = $shape->{_fill};
1135              
1136 44 50       152 if ( length $fill > 1 ) {
1137              
1138             # Write the a:solidFill element.
1139 0         0 $self->_write_a_solid_fill( $fill );
1140             }
1141             else {
1142 44         146 $self->xml_empty_tag( 'a:noFill' );
1143             }
1144              
1145             # Write the a:ln element.
1146 44         194 $self->_write_a_ln( $shape );
1147              
1148 44         130 $self->xml_end_tag( 'xdr:spPr' );
1149             }
1150              
1151             ##############################################################################
1152             #
1153             # _write_a_xfrm()
1154             #
1155             # Write the element.
1156             #
1157             sub _write_a_xfrm {
1158              
1159 149     149   324 my $self = shift;
1160 149         281 my $col_absolute = shift;
1161 149         306 my $row_absolute = shift;
1162 149         281 my $width = shift;
1163 149         335 my $height = shift;
1164 149   100     832 my $shape = shift || {};
1165 149         383 my @attributes = ();
1166              
1167 149   100     836 my $rotation = $shape->{_rotation} || 0;
1168 149         374 $rotation *= 60000;
1169              
1170 149 100       503 push( @attributes, ( 'rot' => $rotation ) ) if $rotation;
1171 149 100       496 push( @attributes, ( 'flipH' => 1 ) ) if $shape->{_flip_h};
1172 149 100       477 push( @attributes, ( 'flipV' => 1 ) ) if $shape->{_flip_v};
1173              
1174 149         657 $self->xml_start_tag( 'a:xfrm', @attributes );
1175              
1176             # Write the a:off element.
1177 149         616 $self->_write_a_off( $col_absolute, $row_absolute );
1178              
1179             # Write the a:ext element.
1180 149         680 $self->_write_a_ext( $width, $height );
1181              
1182 149         525 $self->xml_end_tag( 'a:xfrm' );
1183             }
1184              
1185              
1186             ##############################################################################
1187             #
1188             # _write_a_off()
1189             #
1190             # Write the element.
1191             #
1192             sub _write_a_off {
1193              
1194 149     149   469 my $self = shift;
1195 149         298 my $x = shift;
1196 149         273 my $y = shift;
1197              
1198 149         465 my @attributes = (
1199             'x' => $x,
1200             'y' => $y,
1201             );
1202              
1203 149         548 $self->xml_empty_tag( 'a:off', @attributes );
1204             }
1205              
1206              
1207             ##############################################################################
1208             #
1209             # _write_a_ext()
1210             #
1211             # Write the element.
1212             #
1213             sub _write_a_ext {
1214              
1215 149     149   343 my $self = shift;
1216 149         279 my $cx = shift;
1217 149         298 my $cy = shift;
1218              
1219 149         485 my @attributes = (
1220             'cx' => $cx,
1221             'cy' => $cy,
1222             );
1223              
1224 149         502 $self->xml_empty_tag( 'a:ext', @attributes );
1225             }
1226              
1227              
1228             ##############################################################################
1229             #
1230             # _write_a_prst_geom()
1231             #
1232             # Write the element.
1233             #
1234             sub _write_a_prst_geom {
1235              
1236 148     148   353 my $self = shift;
1237 148   50     485 my $shape = shift || {};
1238              
1239 148         384 my @attributes = ();
1240              
1241 148 50       1206 @attributes = ( 'prst' => $shape->{_type} ) if $shape->{_type};
1242              
1243 148         594 $self->xml_start_tag( 'a:prstGeom', @attributes );
1244              
1245             # Write the a:avLst element.
1246 148         909 $self->_write_a_av_lst( $shape );
1247              
1248 148         607 $self->xml_end_tag( 'a:prstGeom' );
1249             }
1250              
1251              
1252             ##############################################################################
1253             #
1254             # _write_a_av_lst()
1255             #
1256             # Write the element.
1257             #
1258             sub _write_a_av_lst {
1259              
1260 149     149   385 my $self = shift;
1261 149   50     480 my $shape = shift || {};
1262 149         346 my $adjustments = [];
1263              
1264 149 100       579 if ( defined $shape->{_adjustments} ) {
1265 45         101 $adjustments = $shape->{_adjustments};
1266             }
1267              
1268 149 100       518 if ( @$adjustments ) {
1269 6         27 $self->xml_start_tag( 'a:avLst' );
1270              
1271 6         13 my $i = 0;
1272 6         25 foreach my $adj ( @{$adjustments} ) {
  6         18  
1273 16         24 $i++;
1274              
1275             # Only connectors have multiple adjustments.
1276 16 100       38 my $suffix = $shape->{_connect} ? $i : '';
1277              
1278             # Scale Adjustments: 100,000 = 100%.
1279 16         32 my $adj_int = int( $adj * 1000 );
1280              
1281 16         49 my @attributes =
1282             ( name => 'adj' . $suffix, fmla => "val $adj_int" );
1283              
1284 16         40 $self->xml_empty_tag( 'a:gd', @attributes );
1285             }
1286 6         22 $self->xml_end_tag( 'a:avLst' );
1287             }
1288             else {
1289 143         487 $self->xml_empty_tag( 'a:avLst' );
1290             }
1291             }
1292              
1293              
1294             ##############################################################################
1295             #
1296             # _write_a_solid_fill()
1297             #
1298             # Write the element.
1299             #
1300             sub _write_a_solid_fill {
1301              
1302 61     61   107 my $self = shift;
1303 61         106 my $rgb = shift;
1304              
1305 61 50       152 $rgb = '000000' unless defined $rgb;
1306              
1307 61         154 my @attributes = ( 'val' => $rgb );
1308              
1309 61         173 $self->xml_start_tag( 'a:solidFill' );
1310              
1311 61         216 $self->xml_empty_tag( 'a:srgbClr', @attributes );
1312              
1313 61         165 $self->xml_end_tag( 'a:solidFill' );
1314             }
1315              
1316              
1317             ##############################################################################
1318             #
1319             # _write_a_ln()
1320             #
1321             # Write the element.
1322             #
1323             sub _write_a_ln {
1324              
1325 45     45   82 my $self = shift;
1326 45   50     123 my $shape = shift || {};
1327              
1328 45         89 my $weight = $shape->{_line_weight};
1329              
1330 45         136 my @attributes = ( 'w' => $weight * 9525 );
1331              
1332 45         171 $self->xml_start_tag( 'a:ln', @attributes );
1333              
1334 45         112 my $line = $shape->{_line};
1335              
1336 45 50       126 if ( length $line > 1 ) {
1337              
1338             # Write the a:solidFill element.
1339 45         124 $self->_write_a_solid_fill( $line );
1340             }
1341             else {
1342 0         0 $self->xml_empty_tag( 'a:noFill' );
1343             }
1344              
1345 45 100       156 if ( $shape->{_line_type} ) {
1346              
1347 1         3 @attributes = ( 'val' => $shape->{_line_type} );
1348 1         3 $self->xml_empty_tag( 'a:prstDash', @attributes );
1349             }
1350              
1351 45 100       116 if ( $shape->{_connect} ) {
1352 11         27 $self->xml_empty_tag( 'a:round' );
1353             }
1354             else {
1355 34         94 @attributes = ( 'lim' => 800000 );
1356 34         99 $self->xml_empty_tag( 'a:miter', @attributes );
1357             }
1358              
1359 45         201 $self->xml_empty_tag( 'a:headEnd' );
1360 45         139 $self->xml_empty_tag( 'a:tailEnd' );
1361              
1362 45         120 $self->xml_end_tag( 'a:ln' );
1363             }
1364              
1365              
1366             ##############################################################################
1367             #
1368             # _write_txBody
1369             #
1370             # Write the element.
1371             #
1372             sub _write_txBody {
1373              
1374 16     16   48 my $self = shift;
1375 16         36 my $col_absolute = shift;
1376 16         31 my $row_absolute = shift;
1377 16         26 my $width = shift;
1378 16         28 my $height = shift;
1379 16         26 my $shape = shift;
1380              
1381             my @attributes = (
1382             vertOverflow => "clip",
1383             wrap => "square",
1384             lIns => "27432",
1385             tIns => "22860",
1386             rIns => "27432",
1387             bIns => "22860",
1388             anchor => $shape->{_valign},
1389 16         86 upright => "1",
1390             );
1391              
1392 16         66 $self->xml_start_tag( 'xdr:txBody' );
1393 16         202 $self->xml_empty_tag( 'a:bodyPr', @attributes );
1394 16         55 $self->xml_empty_tag( 'a:lstStyle' );
1395              
1396 16         52 $self->xml_start_tag( 'a:p' );
1397              
1398 16         205 my $rotation = $shape->{_format}->{_rotation};
1399 16 100       45 $rotation = 0 unless defined $rotation;
1400 16         30 $rotation *= 60000;
1401              
1402 16         55 @attributes = ( algn => $shape->{_align}, rtl => $rotation );
1403 16         52 $self->xml_start_tag( 'a:pPr', @attributes );
1404              
1405 16         43 @attributes = ( sz => "1000" );
1406 16         53 $self->xml_empty_tag( 'a:defRPr', @attributes );
1407              
1408 16         52 $self->xml_end_tag( 'a:pPr' );
1409 16         162 $self->xml_start_tag( 'a:r' );
1410              
1411 16         39 my $size = $shape->{_format}->{_size};
1412 16 100       42 $size = 8 unless defined $size;
1413 16         148 $size *= 100;
1414              
1415 16         34 my $bold = $shape->{_format}->{_bold};
1416 16 100       40 $bold = 0 unless defined $bold;
1417              
1418 16         31 my $italic = $shape->{_format}->{_italic};
1419 16 100       181 $italic = 0 unless defined $italic;
1420              
1421 16         47 my $underline = $shape->{_format}->{_underline};
1422 16 50       52 $underline = $underline ? 'sng' : 'none';
1423              
1424 16         31 my $strike = $shape->{_format}->{_font_strikeout};
1425 16 50       35 $strike = $strike ? 'Strike' : 'noStrike';
1426              
1427 16         61 @attributes = (
1428             lang => "en-US",
1429             sz => $size,
1430             b => $bold,
1431             i => $italic,
1432             u => $underline,
1433             strike => $strike,
1434             baseline => 0,
1435             );
1436              
1437 16         64 $self->xml_start_tag( 'a:rPr', @attributes );
1438              
1439 16         38 my $color = $shape->{_format}->{_color};
1440 16 100       41 if ( defined $color ) {
1441 14         48 $color = $shape->_get_palette_color( $color );
1442 14         37 $color =~ s/^FF//; # Remove leading FF from rgb for shape color.
1443             }
1444             else {
1445 2         7 $color = '000000';
1446             }
1447              
1448 16         48 $self->_write_a_solid_fill( $color );
1449              
1450 16         39 my $font = $shape->{_format}->{_font};
1451 16 100       45 $font = 'Calibri' unless defined $font;
1452 16         46 @attributes = ( typeface => $font );
1453 16         53 $self->xml_empty_tag( 'a:latin', @attributes );
1454              
1455 16         52 $self->xml_empty_tag( 'a:cs', @attributes );
1456              
1457 16         52 $self->xml_end_tag( 'a:rPr' );
1458              
1459 16         195 $self->xml_data_element( 'a:t', $shape->{_text} );
1460              
1461 16         53 $self->xml_end_tag( 'a:r' );
1462 16         54 $self->xml_end_tag( 'a:p' );
1463 16         44 $self->xml_end_tag( 'xdr:txBody' );
1464              
1465             }
1466              
1467              
1468             1;
1469             __END__