File Coverage

blib/lib/Excel/Writer/XLSX/Shape.pm
Criterion Covered Total %
statement 109 111 98.2
branch 12 18 66.6
condition n/a
subroutine 13 13 100.0
pod 0 3 0.0
total 134 145 92.4


line stmt bran cond sub pod time code
1              
2             ###############################################################################
3             #
4             # Shape - A class for writing Excel shapes.
5             #
6             # Used in conjunction with Excel::Writer::XLSX.
7             #
8             # Copyright 2000-2021, John McNamara, jmcnamara@cpan.org
9             #
10             # Documentation after __END__
11             #
12              
13             # perltidy with the following options: -mbl=2 -pt=0 -nola
14              
15             use 5.008002;
16 1124     1124   16828 use strict;
  1124         3085  
17 1124     1124   5604 use warnings;
  1124         2669  
  1124         18530  
18 1124     1124   5783 use Carp;
  1124         2889  
  1124         21056  
19 1124     1124   5366 use Exporter;
  1124         3551  
  1124         54319  
20 1124     1124   6838  
  1124         1761  
  1124         535555  
21             our @ISA = qw(Exporter);
22             our $VERSION = '1.09';
23             our $AUTOLOAD;
24              
25             ###############################################################################
26             #
27             # new()
28             #
29              
30             my $class = shift;
31             my $fh = shift;
32 28     28 0 911 my $self = Excel::Writer::XLSX::Package::XMLwriter->new( $fh );
33 28         45  
34 28         103 my %properties = @_;
35              
36 28         107 $self->{_name} = undef;
37             $self->{_type} = 'rect';
38 28         81  
39 28         70 # Is a Connector shape. 1/0 Value is a hash lookup from type.
40             $self->{_connect} = 0;
41              
42 28         54 # Is a Drawing. Always 0, since a single shape never fills an entire sheet.
43             $self->{_drawing} = 0;
44              
45 28         50 # OneCell or Absolute: options to move and/or size with cells.
46             $self->{_editAs} = '';
47              
48 28         52 # Auto-incremented, unless supplied by user.
49             $self->{_id} = 0;
50              
51 28         58 # Shape text (usually centered on shape geometry).
52             $self->{_text} = 0;
53              
54 28         74 # Shape stencil mode. A copy (child) is created when inserted.
55             # The link to parent is broken.
56             $self->{_stencil} = 1;
57              
58 28         62 # Index to _shapes array when inserted.
59             $self->{_element} = -1;
60              
61 28         45 # Shape ID of starting connection, if any.
62             $self->{_start} = undef;
63              
64 28         58 # Shape vertex, starts at 0, numbered clockwise from 12 o'clock.
65             $self->{_start_index} = undef;
66              
67 28         51 $self->{_end} = undef;
68             $self->{_end_index} = undef;
69 28         50  
70 28         59 # Number and size of adjustments for shapes (usually connectors).
71             $self->{_adjustments} = [];
72              
73 28         57 # Start and end sides. t)op, b)ottom, l)eft, or r)ight.
74             $self->{_start_side} = '';
75             $self->{_end_side} = '';
76 28         100  
77 28         60 # Flip shape Horizontally. eg. arrow left to arrow right.
78             $self->{_flip_h} = 0;
79              
80 28         55 # Flip shape Vertically. eg. up arrow to down arrow.
81             $self->{_flip_v} = 0;
82              
83 28         52 # shape rotation (in degrees 0-360).
84             $self->{_rotation} = 0;
85              
86 28         43 # An alternate way to create a text box, because Excel allows it.
87             # It is just a rectangle with text.
88             $self->{_txBox} = 0;
89              
90 28         51 # Shape outline colour, or 0 for noFill (default black).
91             $self->{_line} = '000000';
92              
93 28         68 # Line type: dash, sysDot, dashDot, lgDash, lgDashDot, lgDashDotDot.
94             $self->{_line_type} = '';
95              
96 28         53 # Line weight (integer).
97             $self->{_line_weight} = 1;
98              
99 28         57 # Shape fill colour, or 0 for noFill (default noFill).
100             $self->{_fill} = 0;
101              
102 28         63 # Formatting for shape text, if any.
103             $self->{_format} = {};
104              
105 28         65 # copy of colour palette table from Workbook.pm.
106             $self->{_palette} = [];
107              
108 28         53 # Vertical alignment: t, ctr, b.
109             $self->{_valign} = 'ctr';
110              
111 28         58 # Alignment: l, ctr, r, just
112             $self->{_align} = 'ctr';
113              
114 28         56 $self->{_x_offset} = 0;
115             $self->{_y_offset} = 0;
116 28         62  
117 28         49 # Scale factors, which also may be set when the shape is inserted.
118             $self->{_scale_x} = 1;
119             $self->{_scale_y} = 1;
120 28         102  
121 28         55 # Default size, which can be modified and/or scaled.
122             $self->{_width} = 50;
123             $self->{_height} = 50;
124 28         81  
125 28         49 # Initial assignment. May be modified when prepared.
126             $self->{_column_start} = 0;
127             $self->{_row_start} = 0;
128 28         49 $self->{_x1} = 0;
129 28         48 $self->{_y1} = 0;
130 28         53 $self->{_column_end} = 0;
131 28         55 $self->{_row_end} = 0;
132 28         52 $self->{_x2} = 0;
133 28         59 $self->{_y2} = 0;
134 28         48 $self->{_x_abs} = 0;
135 28         55 $self->{_y_abs} = 0;
136 28         47  
137 28         49 # Override default properties with passed arguments
138             while ( my ( $key, $value ) = each( %properties ) ) {
139              
140 28         129 # Strip leading "-" from Tk style properties e.g. -color => 'red'.
141             $key =~ s/^-//;
142              
143 53         88 # Add leading underscore "_" to internal hash keys, if not supplied.
144             $key = "_" . $key unless $key =~ m/^_/;
145              
146 53 50       197 $self->{$key} = $value;
147             }
148 53         197  
149             bless $self, $class;
150             return $self;
151 28         55 }
152 28         89  
153              
154             ###############################################################################
155             #
156             # set_properties( name => 'Shape 1', type => 'rect' )
157             #
158             # Set shape properties.
159             #
160              
161             my $self = shift;
162             my %properties = @_;
163              
164 1     1 0 5 # Update properties with passed arguments.
165 1         3 while ( my ( $key, $value ) = each( %properties ) ) {
166              
167             # Strip leading "-" from Tk style properties e.g. -color => 'red'.
168 1         6 $key =~ s/^-//;
169              
170             # Add leading underscore "_" to internal hash keys, if not supplied.
171 2         4 $key = "_" . $key unless $key =~ m/^_/;
172              
173             if ( !exists $self->{$key} ) {
174 2 50       8 warn "Unknown shape property: $key. Property not set.\n";
175             next;
176 2 50       5 }
177 0         0  
178 0         0 $self->{$key} = $value;
179             }
180             }
181 2         9  
182              
183             ###############################################################################
184             #
185             # set_adjustment( adj1, adj2, adj3, ... )
186             #
187             # Set the shape adjustments array (as a reference).
188             #
189              
190             my $self = shift;
191             $self->{_adjustments} = \@_;
192             }
193              
194 4     4 0 13  
195 4         13 ###############################################################################
196             #
197             # AUTOLOAD. Deus ex machina.
198             #
199             # Dynamically create set/get methods that aren't already defined.
200             #
201              
202             my $self = shift;
203              
204             # Ignore calls to DESTROY.
205             return if $AUTOLOAD =~ /::DESTROY$/;
206              
207 127     127   11707 # Check for a valid method names, i.e. "set_xxx_Cy".
208             $AUTOLOAD =~ /.*::(get|set)(\w+)/ or die "Unknown method: $AUTOLOAD\n";
209              
210 127 100       2591 # Match the function (get or set) and attribute, i.e. "_xxx_yyy".
211             my $gs = $1;
212             my $attribute = $2;
213 54 50       234  
214             # Check that the attribute exists.
215             exists $self->{$attribute} or die "Unknown method: $AUTOLOAD\n";
216 54         95  
217 54         87 # The attribute value
218             my $value;
219              
220 54 50       121 # set_property() pattern.
221             # When a method is AUTOLOADED we store a new anonymous
222             # sub in the appropriate slot in the symbol table. The speeds up subsequent
223 54         61 # calls to the same method.
224             #
225             no strict 'refs'; # To allow symbol table hackery
226              
227             $value = $_[0];
228             $value = 1 if not defined $value; # The default value is always 1
229              
230 1124     1124   6728 if ( $gs eq 'set' ) {
  1124         3984  
  1124         197485  
231             *{$AUTOLOAD} = sub {
232 54         69 my $self = shift;
233 54 100       97 my $value = shift;
234              
235 54 100       110 $value = 1 if not defined $value;
236 48         127 $self->{$attribute} = $value;
237 17     17   37 };
238 17         20  
239             $self->{$attribute} = $value;
240 17 50       30 }
241 17         34 else {
242 48         138 *{$AUTOLOAD} = sub {
243             my $self = shift;
244 48         168 return $self->{$attribute};
245             };
246              
247 6         28 # Let AUTOLOAD return the attribute for the first invocation
248 20     20   51 return $self->{$attribute};
249 20         71 }
250 6         27 }
251              
252              
253 6         64 ###############################################################################
254             #
255             # _get_palette_color()
256             #
257             # Convert from an Excel internal colour index to a XML style #RRGGBB index
258             # based on the default or user defined values in the Workbook palette.
259             # Note: This version doesn't add an alpha channel.
260             #
261              
262             my $self = shift;
263             my $index = shift;
264             my $palette = $self->{_palette};
265              
266             # Adjust the colour index.
267             $index -= 8;
268 14     14   21  
269 14         19 # Palette is passed in from the Workbook class.
270 14         21 my @rgb = @{ $palette->[$index] };
271              
272             return sprintf "%02X%02X%02X", @rgb[0, 1, 2];
273 14         21 }
274              
275              
276 14         19 1;
  14         32  
277              
278 14         77  
279             =head1 NAME
280              
281             Shape - A class for creating Excel Drawing shapes
282              
283             =head1 SYNOPSIS
284              
285             To create a simple Excel file containing shapes using L<Excel::Writer::XLSX>:
286              
287             #!/usr/bin/perl
288              
289             use strict;
290             use warnings;
291             use Excel::Writer::XLSX;
292              
293             my $workbook = Excel::Writer::XLSX->new( 'shape.xlsx' );
294             my $worksheet = $workbook->add_worksheet();
295              
296             # Add a default rectangle shape.
297             my $rect = $workbook->add_shape();
298              
299             # Add an ellipse with centered text.
300             my $ellipse = $workbook->add_shape(
301             type => 'ellipse',
302             text => "Hello\nWorld"
303             );
304              
305             # Add a plus shape.
306             my $plus = $workbook->add_shape( type => 'plus');
307              
308             # Insert the shapes in the worksheet.
309             $worksheet->insert_shape( 'B3', $rect );
310             $worksheet->insert_shape( 'C3', $ellipse );
311             $worksheet->insert_shape( 'D3', $plus );
312              
313              
314             =head1 DESCRIPTION
315              
316             The C<Excel::Writer::XLSX::Shape> module is used to create Shape objects for L<Excel::Writer::XLSX>.
317              
318             A Shape object is created via the Workbook C<add_shape()> method:
319              
320             my $shape_rect = $workbook->add_shape( type => 'rect' );
321              
322             Once the object is created it can be inserted into a worksheet using the C<insert_shape()> method:
323              
324             $worksheet->insert_shape('A1', $shape_rect);
325              
326             A Shape can be inserted multiple times if required.
327              
328             $worksheet->insert_shape('A1', $shape_rect);
329             $worksheet->insert_shape('B2', $shape_rect, 20, 30);
330              
331              
332             =head1 METHODS
333              
334             =head2 add_shape( %properties )
335              
336             The C<add_shape()> Workbook method specifies the properties of the Shape in hash C<< property => value >> format:
337              
338             my $shape = $workbook->add_shape( %properties );
339              
340             The available properties are shown below.
341              
342             =head2 insert_shape( $row, $col, $shape, $x, $y, $scale_x, $scale_y )
343              
344             The C<insert_shape()> Worksheet method sets the location and scale of the shape object within the worksheet.
345              
346             # Insert the shape into the worksheet.
347             $worksheet->insert_shape( 'E2', $shape );
348              
349             Using the cell location and the C<$x> and C<$y> cell offsets it is possible to position a shape anywhere on the canvas of a worksheet.
350              
351             A more detailed explanation of the C<insert_shape()> method is given in the main L<Excel::Writer::XLSX> documentation.
352              
353              
354             =head1 SHAPE PROPERTIES
355              
356             Any shape property can be queried or modified by the corresponding get/set method:
357              
358             my $ellipse = $workbook->add_shape( %properties );
359             $ellipse->set_type( 'plus' ); # No longer an ellipse!
360             my $type = $ellipse->get_type(); # Find out what it really is.
361              
362             Multiple shape properties may also be modified in one go by using the C<set_properties()> method:
363              
364             $shape->set_properties( type => 'ellipse', text => 'Hello' );
365              
366             The properties of a shape object that can be defined via C<add_shape()> are shown below.
367              
368             =head2 name
369              
370             Defines the name of the shape. This is an optional property and the shape will be given a default name if not supplied. The name is generally only used by Excel Macros to refer to the object.
371              
372             =head2 type
373              
374             Defines the type of the object such as C<rect>, C<ellipse> or C<triangle>:
375              
376             my $ellipse = $workbook->add_shape( type => 'ellipse' );
377              
378             The default type is C<rect>.
379              
380             The full list of available shapes is shown below.
381              
382             See also the C<shapes_all.pl> program in the C<examples> directory of the distro. It creates an example workbook with all supported shapes labelled with their shape names.
383              
384              
385             =over 4
386              
387             =item * Basic Shapes
388              
389             blockArc can chevron cube decagon
390             diamond dodecagon donut ellipse funnel
391             gear6 gear9 heart heptagon hexagon
392             homePlate lightningBolt line lineInv moon
393             nonIsoscelesTrapezoid noSmoking octagon parallelogram pentagon
394             pie pieWedge plaque rect round1Rect
395             round2DiagRect round2SameRect roundRect rtTriangle smileyFace
396             snip1Rect snip2DiagRect snip2SameRect snipRoundRect star10
397             star12 star16 star24 star32 star4
398             star5 star6 star7 star8 sun
399             teardrop trapezoid triangle
400              
401             =item * Arrow Shapes
402              
403             bentArrow bentUpArrow circularArrow curvedDownArrow
404             curvedLeftArrow curvedRightArrow curvedUpArrow downArrow
405             leftArrow leftCircularArrow leftRightArrow leftRightCircularArrow
406             leftRightUpArrow leftUpArrow notchedRightArrow quadArrow
407             rightArrow stripedRightArrow swooshArrow upArrow
408             upDownArrow uturnArrow
409              
410             =item * Connector Shapes
411              
412             bentConnector2 bentConnector3 bentConnector4
413             bentConnector5 curvedConnector2 curvedConnector3
414             curvedConnector4 curvedConnector5 straightConnector1
415              
416             =item * Callout Shapes
417              
418             accentBorderCallout1 accentBorderCallout2 accentBorderCallout3
419             accentCallout1 accentCallout2 accentCallout3
420             borderCallout1 borderCallout2 borderCallout3
421             callout1 callout2 callout3
422             cloudCallout downArrowCallout leftArrowCallout
423             leftRightArrowCallout quadArrowCallout rightArrowCallout
424             upArrowCallout upDownArrowCallout wedgeEllipseCallout
425             wedgeRectCallout wedgeRoundRectCallout
426              
427             =item * Flow Chart Shapes
428              
429             flowChartAlternateProcess flowChartCollate flowChartConnector
430             flowChartDecision flowChartDelay flowChartDisplay
431             flowChartDocument flowChartExtract flowChartInputOutput
432             flowChartInternalStorage flowChartMagneticDisk flowChartMagneticDrum
433             flowChartMagneticTape flowChartManualInput flowChartManualOperation
434             flowChartMerge flowChartMultidocument flowChartOfflineStorage
435             flowChartOffpageConnector flowChartOnlineStorage flowChartOr
436             flowChartPredefinedProcess flowChartPreparation flowChartProcess
437             flowChartPunchedCard flowChartPunchedTape flowChartSort
438             flowChartSummingJunction flowChartTerminator
439              
440             =item * Action Shapes
441              
442             actionButtonBackPrevious actionButtonBeginning actionButtonBlank
443             actionButtonDocument actionButtonEnd actionButtonForwardNext
444             actionButtonHelp actionButtonHome actionButtonInformation
445             actionButtonMovie actionButtonReturn actionButtonSound
446              
447             =item * Chart Shapes
448              
449             Not to be confused with Excel Charts.
450              
451             chartPlus chartStar chartX
452              
453             =item * Math Shapes
454              
455             mathDivide mathEqual mathMinus mathMultiply mathNotEqual mathPlus
456              
457             =item * Stars and Banners
458              
459             arc bevel bracePair bracketPair chord
460             cloud corner diagStripe doubleWave ellipseRibbon
461             ellipseRibbon2 foldedCorner frame halfFrame horizontalScroll
462             irregularSeal1 irregularSeal2 leftBrace leftBracket leftRightRibbon
463             plus ribbon ribbon2 rightBrace rightBracket
464             verticalScroll wave
465              
466             =item * Tab Shapes
467              
468             cornerTabs plaqueTabs squareTabs
469              
470             =back
471              
472             =head2 text
473              
474             This property is used to make the shape act like a text box.
475              
476             my $rect = $workbook->add_shape( type => 'rect', text => "Hello\nWorld" );
477              
478             The text is super-imposed over the shape. The text can be wrapped using the newline character C<\n>.
479              
480             =head2 id
481              
482             Identification number for internal identification. This number will be auto-assigned, if not assigned, or if it is a duplicate.
483              
484             =head2 format
485              
486             Workbook format for decorating the shape text (font family, size, and decoration).
487              
488             =head2 start, start_index
489              
490             Shape indices of the starting point for a connector and the index of the connection. Index numbers are zero-based, start from the top dead centre and are counted clockwise.
491              
492             Indices are typically created for vertices and centre points of shapes. They are the blue connection points that appear when connection shapes are selected manually in Excel.
493              
494             =head2 end, end_index
495              
496             Same as above but for end points and end connections.
497              
498              
499             =head2 start_side, end_side
500              
501             This is either the letter C<b> or C<r> for the bottom or right side of the shape to be connected to and from.
502              
503             If the C<start>, C<start_index>, and C<start_side> parameters are defined for a connection shape, the shape will be auto located and linked to the starting and ending shapes respectively. This can be very useful for flow and organisation charts.
504              
505             =head2 flip_h, flip_v
506              
507             Set this value to 1, to flip the shape horizontally and/or vertically.
508              
509             =head2 rotation
510              
511             Shape rotation, in degrees, from 0 to 360.
512              
513             =head2 line, fill
514              
515             Shape colour for the outline and fill. Colours may be specified as a colour index, or in RGB format, i.e. C<AA00FF>.
516              
517             See C<COLOURS IN EXCEL> in the main documentation for more information.
518              
519             =head2 line_type
520              
521             Line type for shape outline. The default is solid. The list of possible values is:
522              
523             dash, sysDot, dashDot, lgDash, lgDashDot, lgDashDotDot, solid
524              
525             =head2 valign, align
526              
527             Text alignment within the shape.
528              
529             Vertical alignment can be:
530              
531             Setting Meaning
532             ======= =======
533             t Top
534             ctr Centre
535             b Bottom
536              
537             Horizontal alignment can be:
538              
539             Setting Meaning
540             ======= =======
541             l Left
542             r Right
543             ctr Centre
544             just Justified
545              
546             The default is to centre both horizontally and vertically.
547              
548             =head2 scale_x, scale_y
549              
550             Scale factor in x and y dimension, for scaling the shape width and height. The default value is 1.
551              
552             Scaling may be set on the shape object or via C<insert_shape()>.
553              
554             =head2 adjustments
555              
556             Adjustment of shape vertices. Most shapes do not use this. For some shapes, there is a single adjustment to modify the geometry. For instance, the plus shape has one adjustment to control the width of the spokes.
557              
558             Connectors can have a number of adjustments to control the shape routing. Typically, a connector will have 3 to 5 handles for routing the shape. The adjustment is in percent of the distance from the starting shape to the ending shape, alternating between the x and y dimension. Adjustments may be negative, to route the shape away from the endpoint.
559              
560             =head2 stencil
561              
562             Shapes work in stencil mode by default. That is, once a shape is inserted, its connection is separated from its master. The master shape may be modified after an instance is inserted, and only subsequent insertions will show the modifications.
563              
564             This is helpful for Org charts, where an employee shape may be created once, and then the text of the shape is modified for each employee.
565              
566             The C<insert_shape()> method returns a reference to the inserted shape (the child).
567              
568             Stencil mode can be turned off, allowing for shape(s) to be modified after insertion. In this case the C<insert_shape()> method returns a reference to the inserted shape (the master). This is not very useful for inserting multiple shapes, since the x/y coordinates also gets modified.
569              
570             =head1 TIPS
571              
572             Use C<< $worksheet->hide_gridlines(2) >> to prepare a blank canvas without gridlines.
573              
574             Shapes do not need to fit on one page. Excel will split a large drawing into multiple pages if required. Use the page break preview to show page boundaries superimposed on the drawing.
575              
576             Connected shapes will auto-locate in Excel if you move either the starting shape or the ending shape separately. However, if you select both shapes (lasso or control-click), the connector will move with it, and the shape adjustments will not re-calculate.
577              
578             =head1 EXAMPLE
579              
580             #!/usr/bin/perl
581              
582             use strict;
583             use warnings;
584             use Excel::Writer::XLSX;
585              
586             my $workbook = Excel::Writer::XLSX->new( 'shape.xlsx' );
587             my $worksheet = $workbook->add_worksheet();
588              
589             # Add a default rectangle shape.
590             my $rect = $workbook->add_shape();
591              
592             # Add an ellipse with centered text.
593             my $ellipse = $workbook->add_shape(
594             type => 'ellipse',
595             text => "Hello\nWorld"
596             );
597              
598             # Add a plus shape.
599             my $plus = $workbook->add_shape( type => 'plus');
600              
601             # Insert the shapes in the worksheet.
602             $worksheet->insert_shape( 'B3', $rect );
603             $worksheet->insert_shape( 'C3', $ellipse );
604             $worksheet->insert_shape( 'D3', $plus );
605              
606              
607             See also the C<shapes_*.pl> program in the C<examples> directory of the distro.
608              
609             =head1 TODO
610              
611             =over 4
612              
613             =item * Add shapes which have custom geometries.
614              
615             =item * Provide better integration of workbook formats for shapes.
616              
617             =item * Add further validation of shape properties to prevent creation of workbooks that will not open.
618              
619             =item * Auto connect shapes that are not anchored to cell A1.
620              
621             =item * Add automatic shape connection to shape vertices besides the object centre.
622              
623             =item * Improve automatic shape connection to shapes with concave sides (e.g. chevron).
624              
625             =back
626              
627             =head1 AUTHOR
628              
629             Dave Clarke dclarke@cpan.org
630              
631             =head1 COPYRIGHT
632              
633             (c) MM-MMXXI, John McNamara.
634              
635             All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.