blob: 892f49248065101c329d8ff5cb5d32ca56ed32d1 [file] [log] [blame]
yu.dongc33b3072024-08-21 23:14:49 -07001package Spreadsheet::WriteExcel::Chart;
2
3###############################################################################
4#
5# Chart - A writer class for Excel Charts.
6#
7# Used in conjunction with Spreadsheet::WriteExcel.
8#
9# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
10#
11# Documentation after __END__
12#
13
14use Exporter;
15use strict;
16use Carp;
17use FileHandle;
18use Spreadsheet::WriteExcel::Worksheet;
19
20
21use vars qw($VERSION @ISA);
22@ISA = qw(Spreadsheet::WriteExcel::Worksheet);
23
24$VERSION = '2.37';
25
26###############################################################################
27#
28# Formatting information.
29#
30# perltidy with options: -mbl=2 -pt=0 -nola
31#
32# Any camel case Hungarian notation style variable names in the BIFF record
33# writing sub-routines below are for similarity with names used in the Excel
34# documentation. Otherwise lowercase underscore style names are used.
35#
36
37
38###############################################################################
39#
40# The chart class hierarchy is as follows. Chart.pm acts as a factory for the
41# sub-classes.
42#
43#
44# Spreadsheet::WriteExcel::BIFFwriter
45# ^
46# |
47# Spreadsheet::WriteExcel::Worksheet
48# ^
49# |
50# Spreadsheet::WriteExcel::Chart
51# ^
52# |
53# Spreadsheet::WriteExcel::Chart::* (sub-types)
54#
55
56
57###############################################################################
58#
59# factory()
60#
61# Factory method for returning chart objects based on their class type.
62#
63sub factory {
64
65 my $current_class = shift;
66 my $chart_subclass = shift;
67
68 $chart_subclass = ucfirst lc $chart_subclass;
69
70 my $module = "Spreadsheet::WriteExcel::Chart::" . $chart_subclass;
71
72 eval "require $module";
73
74 # TODO. Need to re-raise this error from Workbook::add_chart().
75 die "Chart type '$chart_subclass' not supported in add_chart()\n" if $@;
76
77 return $module->new( @_ );
78}
79
80
81###############################################################################
82#
83# new()
84#
85# Default constructor for sub-classes.
86#
87sub new {
88
89 my $class = shift;
90 my $self = Spreadsheet::WriteExcel::Worksheet->new( @_ );
91
92 $self->{_sheet_type} = 0x0200;
93 $self->{_orientation} = 0x0;
94 $self->{_series} = [];
95 $self->{_embedded} = 0;
96
97 bless $self, $class;
98 $self->_set_default_properties();
99 $self->_set_default_config_data();
100 return $self;
101}
102
103
104###############################################################################
105#
106# Public methods.
107#
108###############################################################################
109
110
111###############################################################################
112#
113# add_series()
114#
115# Add a series and it's properties to a chart.
116#
117sub add_series {
118
119 my $self = shift;
120 my %arg = @_;
121
122 croak "Must specify 'values' in add_series()" if !exists $arg{values};
123
124 # Parse the ranges to validate them and extract salient information.
125 my @value_data = $self->_parse_series_formula( $arg{values} );
126 my @category_data = $self->_parse_series_formula( $arg{categories} );
127 my $name_formula = $self->_parse_series_formula( $arg{name_formula} );
128
129 # Default category count to the same as the value count if not defined.
130 if ( !defined $category_data[1] ) {
131 $category_data[1] = $value_data[1];
132 }
133
134 # Add the parsed data to the user supplied data.
135 %arg = (
136 @_,
137 _values => \@value_data,
138 _categories => \@category_data,
139 _name_formula => $name_formula
140 );
141
142 # Encode the Series name.
143 my ( $name, $encoding ) =
144 $self->_encode_utf16( $arg{name}, $arg{name_encoding} );
145
146 $arg{name} = $name;
147 $arg{name_encoding} = $encoding;
148
149 push @{ $self->{_series} }, \%arg;
150}
151
152
153###############################################################################
154#
155# set_x_axis()
156#
157# Set the properties of the X-axis.
158#
159sub set_x_axis {
160
161 my $self = shift;
162 my %arg = @_;
163
164 my ( $name, $encoding ) =
165 $self->_encode_utf16( $arg{name}, $arg{name_encoding} );
166
167 my $formula = $self->_parse_series_formula( $arg{name_formula} );
168
169 $self->{_x_axis_name} = $name;
170 $self->{_x_axis_encoding} = $encoding;
171 $self->{_x_axis_formula} = $formula;
172}
173
174
175###############################################################################
176#
177# set_y_axis()
178#
179# Set the properties of the Y-axis.
180#
181sub set_y_axis {
182
183 my $self = shift;
184 my %arg = @_;
185
186 my ( $name, $encoding ) =
187 $self->_encode_utf16( $arg{name}, $arg{name_encoding} );
188
189 my $formula = $self->_parse_series_formula( $arg{name_formula} );
190
191 $self->{_y_axis_name} = $name;
192 $self->{_y_axis_encoding} = $encoding;
193 $self->{_y_axis_formula} = $formula;
194}
195
196
197###############################################################################
198#
199# set_title()
200#
201# Set the properties of the chart title.
202#
203sub set_title {
204
205 my $self = shift;
206 my %arg = @_;
207
208 my ( $name, $encoding ) =
209 $self->_encode_utf16( $arg{name}, $arg{name_encoding} );
210
211 my $formula = $self->_parse_series_formula( $arg{name_formula} );
212
213 $self->{_title_name} = $name;
214 $self->{_title_encoding} = $encoding;
215 $self->{_title_formula} = $formula;
216}
217
218
219###############################################################################
220#
221# set_legend()
222#
223# Set the properties of the chart legend.
224#
225sub set_legend {
226
227 my $self = shift;
228 my %arg = @_;
229
230 if ( defined $arg{position} ) {
231 if ( lc $arg{position} eq 'none' ) {
232 $self->{_legend}->{_visible} = 0;
233 }
234 }
235}
236
237
238###############################################################################
239#
240# set_plotarea()
241#
242# Set the properties of the chart plotarea.
243#
244sub set_plotarea {
245
246 my $self = shift;
247 my %arg = @_;
248 return unless keys %arg;
249
250 my $area = $self->{_plotarea};
251
252 # Set the plotarea visibility.
253 if ( defined $arg{visible} ) {
254 $area->{_visible} = $arg{visible};
255 return if !$area->{_visible};
256 }
257
258 # TODO. could move this out of if statement.
259 $area->{_bg_color_index} = 0x08;
260
261 # Set the chart background colour.
262 if ( defined $arg{color} ) {
263 my ( $index, $rgb ) = $self->_get_color_indices( $arg{color} );
264 if ( defined $index ) {
265 $area->{_fg_color_index} = $index;
266 $area->{_fg_color_rgb} = $rgb;
267 $area->{_bg_color_index} = 0x08;
268 $area->{_bg_color_rgb} = 0x000000;
269 }
270 }
271
272 # Set the border line colour.
273 if ( defined $arg{line_color} ) {
274 my ( $index, $rgb ) = $self->_get_color_indices( $arg{line_color} );
275 if ( defined $index ) {
276 $area->{_line_color_index} = $index;
277 $area->{_line_color_rgb} = $rgb;
278 }
279 }
280
281 # Set the border line pattern.
282 if ( defined $arg{line_pattern} ) {
283 my $pattern = $self->_get_line_pattern( $arg{line_pattern} );
284 $area->{_line_pattern} = $pattern;
285 }
286
287 # Set the border line weight.
288 if ( defined $arg{line_weight} ) {
289 my $weight = $self->_get_line_weight( $arg{line_weight} );
290 $area->{_line_weight} = $weight;
291 }
292}
293
294
295###############################################################################
296#
297# set_chartarea()
298#
299# Set the properties of the chart chartarea.
300#
301sub set_chartarea {
302
303 my $self = shift;
304 my %arg = @_;
305 return unless keys %arg;
306
307 my $area = $self->{_chartarea};
308
309 # Embedded automatic line weight has a different default value.
310 $area->{_line_weight} = 0xFFFF if $self->{_embedded};
311
312
313 # Set the chart background colour.
314 if ( defined $arg{color} ) {
315 my ( $index, $rgb ) = $self->_get_color_indices( $arg{color} );
316 if ( defined $index ) {
317 $area->{_fg_color_index} = $index;
318 $area->{_fg_color_rgb} = $rgb;
319 $area->{_bg_color_index} = 0x08;
320 $area->{_bg_color_rgb} = 0x000000;
321 $area->{_area_pattern} = 1;
322 $area->{_area_options} = 0x0000 if $self->{_embedded};
323 $area->{_visible} = 1;
324 }
325 }
326
327 # Set the border line colour.
328 if ( defined $arg{line_color} ) {
329 my ( $index, $rgb ) = $self->_get_color_indices( $arg{line_color} );
330 if ( defined $index ) {
331 $area->{_line_color_index} = $index;
332 $area->{_line_color_rgb} = $rgb;
333 $area->{_line_pattern} = 0x00;
334 $area->{_line_options} = 0x0000;
335 $area->{_visible} = 1;
336 }
337 }
338
339 # Set the border line pattern.
340 if ( defined $arg{line_pattern} ) {
341 my $pattern = $self->_get_line_pattern( $arg{line_pattern} );
342 $area->{_line_pattern} = $pattern;
343 $area->{_line_options} = 0x0000;
344 $area->{_line_color_index} = 0x4F if !defined $arg{line_color};
345 $area->{_visible} = 1;
346 }
347
348 # Set the border line weight.
349 if ( defined $arg{line_weight} ) {
350 my $weight = $self->_get_line_weight( $arg{line_weight} );
351 $area->{_line_weight} = $weight;
352 $area->{_line_options} = 0x0000;
353 $area->{_line_pattern} = 0x00 if !defined $arg{line_pattern};
354 $area->{_line_color_index} = 0x4F if !defined $arg{line_color};
355 $area->{_visible} = 1;
356 }
357}
358
359
360###############################################################################
361#
362# Internal methods. The following section of methods are used for the internal
363# structuring of the Chart object and file format.
364#
365###############################################################################
366
367
368###############################################################################
369#
370# _prepend(), overridden.
371#
372# The parent Worksheet class needs to store some data in memory and some in
373# temporary files for efficiency. The Chart* classes don't need to do this
374# since they are dealing with smaller amounts of data so we override
375# _prepend() to turn it into an _append() method. This allows for a more
376# natural method calling order.
377#
378sub _prepend {
379
380 my $self = shift;
381
382 $self->{_using_tmpfile} = 0;
383
384 return $self->_append( @_ );
385}
386
387
388###############################################################################
389#
390# _close(), overridden.
391#
392# Create and store the Chart data structures.
393#
394sub _close {
395
396 my $self = shift;
397
398 # Ignore any data that has been written so far since it is probably
399 # from unwanted Worksheet method calls.
400 $self->{_data} = '';
401
402 # TODO. Check for charts without a series?
403
404 # Store the chart BOF.
405 $self->_store_bof( 0x0020 );
406
407 # Store the page header
408 $self->_store_header();
409
410 # Store the page footer
411 $self->_store_footer();
412
413 # Store the page horizontal centering
414 $self->_store_hcenter();
415
416 # Store the page vertical centering
417 $self->_store_vcenter();
418
419 # Store the left margin
420 $self->_store_margin_left();
421
422 # Store the right margin
423 $self->_store_margin_right();
424
425 # Store the top margin
426 $self->_store_margin_top();
427
428 # Store the bottom margin
429 $self->_store_margin_bottom();
430
431 # Store the page setup
432 $self->_store_setup();
433
434 # Store the sheet password
435 $self->_store_password();
436
437 # Start of Chart specific records.
438
439 # Store the FBI font records.
440 $self->_store_fbi( @{ $self->{_config}->{_font_numbers} } );
441 $self->_store_fbi( @{ $self->{_config}->{_font_series} } );
442 $self->_store_fbi( @{ $self->{_config}->{_font_title} } );
443 $self->_store_fbi( @{ $self->{_config}->{_font_axes} } );
444
445 # Ignore UNITS record.
446
447 # Store the Chart sub-stream.
448 $self->_store_chart_stream();
449
450 # Append the sheet dimensions
451 $self->_store_dimensions();
452
453 # TODO add SINDEX and NUMBER records.
454
455 if ( !$self->{_embedded} ) {
456 $self->_store_window2();
457 }
458
459 $self->_store_eof();
460}
461
462
463###############################################################################
464#
465# _store_window2(), overridden.
466#
467# Write BIFF record Window2. Note, this overrides the parent Worksheet
468# record because the Chart version of the record is smaller and is used
469# mainly to indicate if the chart tab is selected or not.
470#
471sub _store_window2 {
472
473 use integer; # Avoid << shift bug in Perl 5.6.0 on HP-UX
474
475 my $self = shift;
476
477 my $record = 0x023E; # Record identifier
478 my $length = 0x000A; # Number of bytes to follow
479 my $grbit = 0x0000; # Option flags
480 my $rwTop = 0x0000; # Top visible row
481 my $colLeft = 0x0000; # Leftmost visible column
482 my $rgbHdr = 0x0000; # Row/col heading, grid color
483
484 # The options flags that comprise $grbit
485 my $fDspFmla = 0; # 0 - bit
486 my $fDspGrid = 0; # 1
487 my $fDspRwCol = 0; # 2
488 my $fFrozen = 0; # 3
489 my $fDspZeros = 0; # 4
490 my $fDefaultHdr = 0; # 5
491 my $fArabic = 0; # 6
492 my $fDspGuts = 0; # 7
493 my $fFrozenNoSplit = 0; # 0 - bit
494 my $fSelected = $self->{_selected}; # 1
495 my $fPaged = 0; # 2
496 my $fBreakPreview = 0; # 3
497
498 #<<< Perltidy ignore this.
499 $grbit = $fDspFmla;
500 $grbit |= $fDspGrid << 1;
501 $grbit |= $fDspRwCol << 2;
502 $grbit |= $fFrozen << 3;
503 $grbit |= $fDspZeros << 4;
504 $grbit |= $fDefaultHdr << 5;
505 $grbit |= $fArabic << 6;
506 $grbit |= $fDspGuts << 7;
507 $grbit |= $fFrozenNoSplit << 8;
508 $grbit |= $fSelected << 9;
509 $grbit |= $fPaged << 10;
510 $grbit |= $fBreakPreview << 11;
511 #>>>
512
513 my $header = pack( "vv", $record, $length );
514 my $data = pack( "vvvV", $grbit, $rwTop, $colLeft, $rgbHdr );
515
516 $self->_append( $header, $data );
517}
518
519
520###############################################################################
521#
522# _parse_series_formula()
523#
524# Parse the formula used to define a series. We also extract some range
525# information required for _store_series() and the SERIES record.
526#
527sub _parse_series_formula {
528
529 my $self = shift;
530
531 my $formula = $_[0];
532 my $encoding = 0;
533 my $length = 0;
534 my $count = 0;
535 my @tokens;
536
537 return '' if !defined $formula;
538
539 # Strip the = sign at the beginning of the formula string
540 $formula =~ s(^=)();
541
542 # Parse the formula using the parser in Formula.pm
543 my $parser = $self->{_parser};
544
545 # In order to raise formula errors from the point of view of the calling
546 # program we use an eval block and re-raise the error from here.
547 #
548 eval { @tokens = $parser->parse_formula( $formula ) };
549
550 if ( $@ ) {
551 $@ =~ s/\n$//; # Strip the \n used in the Formula.pm die().
552 croak $@; # Re-raise the error.
553 }
554
555 # Force ranges to be a reference class.
556 s/_ref3d/_ref3dR/ for @tokens;
557 s/_range3d/_range3dR/ for @tokens;
558 s/_name/_nameR/ for @tokens;
559
560 # Parse the tokens into a formula string.
561 $formula = $parser->parse_tokens( @tokens );
562
563 # Return formula for a single cell as used by title and series name.
564 if ( ord $formula == 0x3A ) {
565 return $formula;
566 }
567
568 # Extract the range from the parse formula.
569 if ( ord $formula == 0x3B ) {
570 my ( $ptg, $ext_ref, $row_1, $row_2, $col_1, $col_2 ) = unpack 'Cv5',
571 $formula;
572
573 # TODO. Remove high bit on relative references.
574 $count = $row_2 - $row_1 + 1;
575 }
576
577 return ( $formula, $count );
578}
579
580###############################################################################
581#
582# _encode_utf16()
583#
584# Convert UTF8 strings used in the chart to UTF16.
585#
586sub _encode_utf16 {
587
588 my $self = shift;
589
590 my $string = shift;
591 my $encoding = shift || 0;
592
593 # Exit if the $string isn't defined, i.e., hasn't been set by user.
594 return ( undef, undef ) if !defined $string;
595
596 # Return if encoding is set, i.e., string has been manually encoded.
597 #return ( undef, undef ) if $string == 1;
598
599 # Handle utf8 strings in perl 5.8.
600 if ( $] >= 5.008 ) {
601 require Encode;
602
603 if ( Encode::is_utf8( $string ) ) {
604 $string = Encode::encode( "UTF-16BE", $string );
605 $encoding = 1;
606 }
607 }
608
609 # Chart strings are limited to 255 characters.
610 my $limit = $encoding ? 255 * 2 : 255;
611
612 if ( length $string >= $limit ) {
613
614 # truncate the string and raise a warning.
615 $string = substr $string, 0, $limit;
616 carp 'Chart strings must be less than 256 characters. '
617 . 'String truncated';
618 }
619
620 return ( $string, $encoding );
621}
622
623
624###############################################################################
625#
626# _get_color_indices()
627#
628# Convert the user specified colour index or string to an colour index and
629# RGB colour number.
630#
631sub _get_color_indices {
632
633 my $self = shift;
634 my $color = shift;
635 my $index;
636 my $rgb;
637
638 return ( undef, undef ) if !defined $color;
639
640 my %colors = (
641 aqua => 0x0F,
642 cyan => 0x0F,
643 black => 0x08,
644 blue => 0x0C,
645 brown => 0x10,
646 magenta => 0x0E,
647 fuchsia => 0x0E,
648 gray => 0x17,
649 grey => 0x17,
650 green => 0x11,
651 lime => 0x0B,
652 navy => 0x12,
653 orange => 0x35,
654 pink => 0x21,
655 purple => 0x14,
656 red => 0x0A,
657 silver => 0x16,
658 white => 0x09,
659 yellow => 0x0D,
660 );
661
662
663 # Check for the various supported colour index/name possibilities.
664 if ( exists $colors{$color} ) {
665
666 # Colour matches one of the supported colour names.
667 $index = $colors{$color};
668 }
669 elsif ( $color =~ m/\D/ ) {
670
671 # Return undef if $color is a string but not one of the supported ones.
672 return ( undef, undef );
673 }
674 elsif ( $color < 8 || $color > 63 ) {
675
676 # Return undef if index is out of range.
677 return ( undef, undef );
678 }
679 else {
680
681 # We should have a valid color index in a valid range.
682 $index = $color;
683 }
684
685 $rgb = $self->_get_color_rbg( $index );
686 return ( $index, $rgb );
687}
688
689
690###############################################################################
691#
692# _get_color_rbg()
693#
694# Get the RedGreenBlue number for the colour index from the Workbook palette.
695#
696sub _get_color_rbg {
697
698 my $self = shift;
699 my $index = shift;
700
701 # Adjust colour index from 8-63 (user range) to 0-55 (Excel range).
702 $index -= 8;
703
704 my @red_green_blue = @{ $self->{_palette}->[$index] };
705 return unpack 'V', pack 'C*', @red_green_blue;
706}
707
708
709###############################################################################
710#
711# _get_line_pattern()
712#
713# Get the Excel chart index for line pattern that corresponds to the user
714# defined value.
715#
716sub _get_line_pattern {
717
718 my $self = shift;
719 my $value = lc shift;
720 my $default = 0;
721 my $pattern;
722
723 my %patterns = (
724 0 => 5,
725 1 => 0,
726 2 => 1,
727 3 => 2,
728 4 => 3,
729 5 => 4,
730 6 => 7,
731 7 => 6,
732 8 => 8,
733 'solid' => 0,
734 'dash' => 1,
735 'dot' => 2,
736 'dash-dot' => 3,
737 'dash-dot-dot' => 4,
738 'none' => 5,
739 'dark-gray' => 6,
740 'medium-gray' => 7,
741 'light-gray' => 8,
742 );
743
744 if ( exists $patterns{$value} ) {
745 $pattern = $patterns{$value};
746 }
747 else {
748 $pattern = $default;
749 }
750
751 return $pattern;
752}
753
754
755###############################################################################
756#
757# _get_line_weight()
758#
759# Get the Excel chart index for line weight that corresponds to the user
760# defined value.
761#
762sub _get_line_weight {
763
764 my $self = shift;
765 my $value = lc shift;
766 my $default = 0;
767 my $weight;
768
769 my %weights = (
770 1 => -1,
771 2 => 0,
772 3 => 1,
773 4 => 2,
774 'hairline' => -1,
775 'narrow' => 0,
776 'medium' => 1,
777 'wide' => 2,
778 );
779
780 if ( exists $weights{$value} ) {
781 $weight = $weights{$value};
782 }
783 else {
784 $weight = $default;
785 }
786
787 return $weight;
788}
789
790
791###############################################################################
792#
793# _store_chart_stream()
794#
795# Store the CHART record and it's substreams.
796#
797sub _store_chart_stream {
798
799 my $self = shift;
800
801 $self->_store_chart( @{ $self->{_config}->{_chart} } );
802
803 $self->_store_begin();
804
805 # Ignore SCL record for now.
806 $self->_store_plotgrowth();
807
808 if ( $self->{_chartarea}->{_visible} ) {
809 $self->_store_chartarea_frame_stream();
810 }
811
812 # Store SERIES stream for each series.
813 my $index = 0;
814 for my $series ( @{ $self->{_series} } ) {
815
816 $self->_store_series_stream(
817 _index => $index,
818 _value_formula => $series->{_values}->[0],
819 _value_count => $series->{_values}->[1],
820 _category_count => $series->{_categories}->[1],
821 _category_formula => $series->{_categories}->[0],
822 _name => $series->{name},
823 _name_encoding => $series->{name_encoding},
824 _name_formula => $series->{_name_formula},
825 );
826
827 $index++;
828 }
829
830 $self->_store_shtprops();
831
832 # Write the TEXT streams.
833 for my $font_index ( 5 .. 6 ) {
834 $self->_store_defaulttext();
835 $self->_store_series_text_stream( $font_index );
836 }
837
838 $self->_store_axesused( 1 );
839 $self->_store_axisparent_stream();
840
841 if ( defined $self->{_title_name} || defined $self->{_title_formula} ) {
842 $self->_store_title_text_stream();
843 }
844
845 $self->_store_end();
846
847}
848
849
850###############################################################################
851#
852# _store_series_stream()
853#
854# Write the SERIES chart substream.
855#
856sub _store_series_stream {
857
858 my $self = shift;
859 my %arg = @_;
860
861 my $name_type = $arg{_name_formula} ? 2 : 1;
862 my $value_type = $arg{_value_formula} ? 2 : 0;
863 my $category_type = $arg{_category_formula} ? 2 : 0;
864
865 $self->_store_series( $arg{_value_count}, $arg{_category_count} );
866
867 $self->_store_begin();
868
869 # Store the Series name AI record.
870 $self->_store_ai( 0, $name_type, $arg{_name_formula} );
871 if ( defined $arg{_name} ) {
872 $self->_store_seriestext( $arg{_name}, $arg{_name_encoding} );
873 }
874
875 $self->_store_ai( 1, $value_type, $arg{_value_formula} );
876 $self->_store_ai( 2, $category_type, $arg{_category_formula} );
877 $self->_store_ai( 3, 1, '' );
878
879 $self->_store_dataformat_stream( $arg{_index}, $arg{_index}, 0xFFFF );
880 $self->_store_sertocrt( 0 );
881 $self->_store_end();
882}
883
884
885###############################################################################
886#
887# _store_dataformat_stream()
888#
889# Write the DATAFORMAT chart substream.
890#
891sub _store_dataformat_stream {
892
893 my $self = shift;
894
895 my $series_index = shift;
896
897 $self->_store_dataformat( $series_index, $series_index, 0xFFFF );
898
899 $self->_store_begin();
900 $self->_store_3dbarshape();
901 $self->_store_end();
902}
903
904
905###############################################################################
906#
907# _store_series_text_stream()
908#
909# Write the series TEXT substream.
910#
911sub _store_series_text_stream {
912
913 my $self = shift;
914
915 my $font_index = shift;
916
917 $self->_store_text( @{ $self->{_config}->{_series_text} } );
918
919 $self->_store_begin();
920 $self->_store_pos( @{ $self->{_config}->{_series_text_pos} } );
921 $self->_store_fontx( $font_index );
922 $self->_store_ai( 0, 1, '' );
923 $self->_store_end();
924}
925
926
927###############################################################################
928#
929# _store_x_axis_text_stream()
930#
931# Write the X-axis TEXT substream.
932#
933sub _store_x_axis_text_stream {
934
935 my $self = shift;
936
937 my $formula = $self->{_x_axis_formula};
938 my $ai_type = $formula ? 2 : 1;
939
940 $self->_store_text( @{ $self->{_config}->{_x_axis_text} } );
941
942 $self->_store_begin();
943 $self->_store_pos( @{ $self->{_config}->{_x_axis_text_pos} } );
944 $self->_store_fontx( 8 );
945 $self->_store_ai( 0, $ai_type, $formula );
946
947 if ( defined $self->{_x_axis_name} ) {
948 $self->_store_seriestext( $self->{_x_axis_name},
949 $self->{_x_axis_encoding},
950 );
951 }
952
953 $self->_store_objectlink( 3 );
954 $self->_store_end();
955}
956
957
958###############################################################################
959#
960# _store_y_axis_text_stream()
961#
962# Write the Y-axis TEXT substream.
963#
964sub _store_y_axis_text_stream {
965
966 my $self = shift;
967
968 my $formula = $self->{_y_axis_formula};
969 my $ai_type = $formula ? 2 : 1;
970
971 $self->_store_text( @{ $self->{_config}->{_y_axis_text} } );
972
973 $self->_store_begin();
974 $self->_store_pos( @{ $self->{_config}->{_y_axis_text_pos} } );
975 $self->_store_fontx( 8 );
976 $self->_store_ai( 0, $ai_type, $formula );
977
978 if ( defined $self->{_y_axis_name} ) {
979 $self->_store_seriestext( $self->{_y_axis_name},
980 $self->{_y_axis_encoding},
981 );
982 }
983
984 $self->_store_objectlink( 2 );
985 $self->_store_end();
986}
987
988
989###############################################################################
990#
991# _store_legend_text_stream()
992#
993# Write the legend TEXT substream.
994#
995sub _store_legend_text_stream {
996
997 my $self = shift;
998
999 $self->_store_text( @{ $self->{_config}->{_legend_text} } );
1000
1001 $self->_store_begin();
1002 $self->_store_pos( @{ $self->{_config}->{_legend_text_pos} } );
1003 $self->_store_ai( 0, 1, '' );
1004
1005 $self->_store_end();
1006}
1007
1008
1009###############################################################################
1010#
1011# _store_title_text_stream()
1012#
1013# Write the title TEXT substream.
1014#
1015sub _store_title_text_stream {
1016
1017 my $self = shift;
1018
1019 my $formula = $self->{_title_formula};
1020 my $ai_type = $formula ? 2 : 1;
1021
1022 $self->_store_text( @{ $self->{_config}->{_title_text} } );
1023
1024 $self->_store_begin();
1025 $self->_store_pos( @{ $self->{_config}->{_title_text_pos} } );
1026 $self->_store_fontx( 7 );
1027 $self->_store_ai( 0, $ai_type, $formula );
1028
1029 if ( defined $self->{_title_name} ) {
1030 $self->_store_seriestext( $self->{_title_name},
1031 $self->{_title_encoding},
1032 );
1033 }
1034
1035 $self->_store_objectlink( 1 );
1036 $self->_store_end();
1037}
1038
1039
1040###############################################################################
1041#
1042# _store_axisparent_stream()
1043#
1044# Write the AXISPARENT chart substream.
1045#
1046sub _store_axisparent_stream {
1047
1048 my $self = shift;
1049
1050 $self->_store_axisparent( @{ $self->{_config}->{_axisparent} } );
1051
1052 $self->_store_begin();
1053 $self->_store_pos( @{ $self->{_config}->{_axisparent_pos} } );
1054 $self->_store_axis_category_stream();
1055 $self->_store_axis_values_stream();
1056
1057 if ( defined $self->{_x_axis_name} || defined $self->{_x_axis_formula} ) {
1058 $self->_store_x_axis_text_stream();
1059 }
1060
1061 if ( defined $self->{_y_axis_name} || defined $self->{_y_axis_formula} ) {
1062 $self->_store_y_axis_text_stream();
1063 }
1064
1065 if ( $self->{_plotarea}->{_visible} ) {
1066 $self->_store_plotarea();
1067 $self->_store_plotarea_frame_stream();
1068 }
1069
1070 $self->_store_chartformat_stream();
1071 $self->_store_end();
1072}
1073
1074
1075###############################################################################
1076#
1077# _store_axis_category_stream()
1078#
1079# Write the AXIS chart substream for the chart category.
1080#
1081sub _store_axis_category_stream {
1082
1083 my $self = shift;
1084
1085 $self->_store_axis( 0 );
1086
1087 $self->_store_begin();
1088 $self->_store_catserrange();
1089 $self->_store_axcext();
1090 $self->_store_tick();
1091 $self->_store_end();
1092}
1093
1094
1095###############################################################################
1096#
1097# _store_axis_values_stream()
1098#
1099# Write the AXIS chart substream for the chart values.
1100#
1101sub _store_axis_values_stream {
1102
1103 my $self = shift;
1104
1105 $self->_store_axis( 1 );
1106
1107 $self->_store_begin();
1108 $self->_store_valuerange();
1109 $self->_store_tick();
1110 $self->_store_axislineformat();
1111 $self->_store_lineformat( 0x00000000, 0x0000, 0xFFFF, 0x0009, 0x004D );
1112 $self->_store_end();
1113}
1114
1115
1116###############################################################################
1117#
1118# _store_plotarea_frame_stream()
1119#
1120# Write the FRAME chart substream for the plotarea.
1121#
1122sub _store_plotarea_frame_stream {
1123
1124 my $self = shift;
1125
1126 my $area = $self->{_plotarea};
1127
1128 $self->_store_frame( 0x00, 0x03 );
1129 $self->_store_begin();
1130
1131 $self->_store_lineformat(
1132 $area->{_line_color_rgb}, $area->{_line_pattern},
1133 $area->{_line_weight}, $area->{_line_options},
1134 $area->{_line_color_index}
1135 );
1136
1137 $self->_store_areaformat(
1138 $area->{_fg_color_rgb}, $area->{_bg_color_rgb},
1139 $area->{_area_pattern}, $area->{_area_options},
1140 $area->{_fg_color_index}, $area->{_bg_color_index}
1141 );
1142
1143 $self->_store_end();
1144}
1145
1146
1147###############################################################################
1148#
1149# _store_chartarea_frame_stream()
1150#
1151# Write the FRAME chart substream for the chartarea.
1152#
1153sub _store_chartarea_frame_stream {
1154
1155 my $self = shift;
1156
1157 my $area = $self->{_chartarea};
1158
1159 $self->_store_frame( 0x00, 0x02 );
1160 $self->_store_begin();
1161
1162 $self->_store_lineformat(
1163 $area->{_line_color_rgb}, $area->{_line_pattern},
1164 $area->{_line_weight}, $area->{_line_options},
1165 $area->{_line_color_index}
1166 );
1167
1168 $self->_store_areaformat(
1169 $area->{_fg_color_rgb}, $area->{_bg_color_rgb},
1170 $area->{_area_pattern}, $area->{_area_options},
1171 $area->{_fg_color_index}, $area->{_bg_color_index}
1172 );
1173
1174 $self->_store_end();
1175}
1176
1177###############################################################################
1178#
1179# _store_chartformat_stream()
1180#
1181# Write the CHARTFORMAT chart substream.
1182#
1183sub _store_chartformat_stream {
1184
1185 my $self = shift;
1186
1187 # The _vary_data_color is set by classes that need it, like Pie.
1188 $self->_store_chartformat( $self->{_vary_data_color} );
1189
1190 $self->_store_begin();
1191
1192 # Store the BIFF record that will define the chart type.
1193 $self->_store_chart_type();
1194
1195 # Note, the CHARTFORMATLINK record is only written by Excel.
1196
1197 if ( $self->{_legend}->{_visible} ) {
1198 $self->_store_legend_stream();
1199 }
1200
1201 $self->_store_marker_dataformat_stream();
1202 $self->_store_end();
1203}
1204
1205
1206###############################################################################
1207#
1208# _store_chart_type()
1209#
1210# This is an abstract method that is overridden by the sub-classes to define
1211# the chart types such as Column, Line, Pie, etc.
1212#
1213sub _store_chart_type {
1214
1215}
1216
1217
1218###############################################################################
1219#
1220# _store_marker_dataformat_stream()
1221#
1222# This is an abstract method that is overridden by the sub-classes to define
1223# properties of markers, linetypes, pie formats and other.
1224#
1225sub _store_marker_dataformat_stream {
1226
1227}
1228
1229
1230###############################################################################
1231#
1232# _store_legend_stream()
1233#
1234# Write the LEGEND chart substream.
1235#
1236sub _store_legend_stream {
1237
1238 my $self = shift;
1239
1240 $self->_store_legend( @{ $self->{_config}->{_legend} } );
1241
1242 $self->_store_begin();
1243 $self->_store_pos( @{ $self->{_config}->{_legend_pos} } );
1244 $self->_store_legend_text_stream();
1245 $self->_store_end();
1246}
1247
1248
1249###############################################################################
1250#
1251# BIFF Records.
1252#
1253###############################################################################
1254
1255
1256###############################################################################
1257#
1258# _store_3dbarshape()
1259#
1260# Write the 3DBARSHAPE chart BIFF record.
1261#
1262sub _store_3dbarshape {
1263
1264 my $self = shift;
1265
1266 my $record = 0x105F; # Record identifier.
1267 my $length = 0x0002; # Number of bytes to follow.
1268 my $riser = 0x00; # Shape of base.
1269 my $taper = 0x00; # Column taper type.
1270
1271 my $header = pack 'vv', $record, $length;
1272 my $data = '';
1273 $data .= pack 'C', $riser;
1274 $data .= pack 'C', $taper;
1275
1276 $self->_append( $header, $data );
1277}
1278
1279
1280###############################################################################
1281#
1282# _store_ai()
1283#
1284# Write the AI chart BIFF record.
1285#
1286sub _store_ai {
1287
1288 my $self = shift;
1289
1290 my $record = 0x1051; # Record identifier.
1291 my $length = 0x0008; # Number of bytes to follow.
1292 my $id = $_[0]; # Link index.
1293 my $type = $_[1]; # Reference type.
1294 my $formula = $_[2]; # Pre-parsed formula.
1295 my $format_index = $_[3] || 0; # Num format index.
1296 my $grbit = 0x0000; # Option flags.
1297
1298 my $formula_length = length $formula;
1299
1300 $length += $formula_length;
1301
1302 my $header = pack 'vv', $record, $length;
1303 my $data = '';
1304 $data .= pack 'C', $id;
1305 $data .= pack 'C', $type;
1306 $data .= pack 'v', $grbit;
1307 $data .= pack 'v', $format_index;
1308 $data .= pack 'v', $formula_length;
1309 $data .= $formula;
1310
1311 $self->_append( $header, $data );
1312}
1313
1314
1315###############################################################################
1316#
1317# _store_areaformat()
1318#
1319# Write the AREAFORMAT chart BIFF record. Contains the patterns and colours
1320# of a chart area.
1321#
1322sub _store_areaformat {
1323
1324 my $self = shift;
1325
1326 my $record = 0x100A; # Record identifier.
1327 my $length = 0x0010; # Number of bytes to follow.
1328 my $rgbFore = $_[0]; # Foreground RGB colour.
1329 my $rgbBack = $_[1]; # Background RGB colour.
1330 my $pattern = $_[2]; # Pattern.
1331 my $grbit = $_[3]; # Option flags.
1332 my $indexFore = $_[4]; # Index to Foreground colour.
1333 my $indexBack = $_[5]; # Index to Background colour.
1334
1335 my $header = pack 'vv', $record, $length;
1336 my $data = '';
1337 $data .= pack 'V', $rgbFore;
1338 $data .= pack 'V', $rgbBack;
1339 $data .= pack 'v', $pattern;
1340 $data .= pack 'v', $grbit;
1341 $data .= pack 'v', $indexFore;
1342 $data .= pack 'v', $indexBack;
1343
1344 $self->_append( $header, $data );
1345}
1346
1347
1348###############################################################################
1349#
1350# _store_axcext()
1351#
1352# Write the AXCEXT chart BIFF record.
1353#
1354sub _store_axcext {
1355
1356 my $self = shift;
1357
1358 my $record = 0x1062; # Record identifier.
1359 my $length = 0x0012; # Number of bytes to follow.
1360 my $catMin = 0x0000; # Minimum category on axis.
1361 my $catMax = 0x0000; # Maximum category on axis.
1362 my $catMajor = 0x0001; # Value of major unit.
1363 my $unitMajor = 0x0000; # Units of major unit.
1364 my $catMinor = 0x0001; # Value of minor unit.
1365 my $unitMinor = 0x0000; # Units of minor unit.
1366 my $unitBase = 0x0000; # Base unit of axis.
1367 my $catCrossDate = 0x0000; # Crossing point.
1368 my $grbit = 0x00EF; # Option flags.
1369
1370 my $header = pack 'vv', $record, $length;
1371 my $data = '';
1372 $data .= pack 'v', $catMin;
1373 $data .= pack 'v', $catMax;
1374 $data .= pack 'v', $catMajor;
1375 $data .= pack 'v', $unitMajor;
1376 $data .= pack 'v', $catMinor;
1377 $data .= pack 'v', $unitMinor;
1378 $data .= pack 'v', $unitBase;
1379 $data .= pack 'v', $catCrossDate;
1380 $data .= pack 'v', $grbit;
1381
1382 $self->_append( $header, $data );
1383}
1384
1385
1386###############################################################################
1387#
1388# _store_axesused()
1389#
1390# Write the AXESUSED chart BIFF record.
1391#
1392sub _store_axesused {
1393
1394 my $self = shift;
1395
1396 my $record = 0x1046; # Record identifier.
1397 my $length = 0x0002; # Number of bytes to follow.
1398 my $num_axes = $_[0]; # Number of axes used.
1399
1400 my $header = pack 'vv', $record, $length;
1401 my $data = pack 'v', $num_axes;
1402
1403 $self->_append( $header, $data );
1404}
1405
1406
1407###############################################################################
1408#
1409# _store_axis()
1410#
1411# Write the AXIS chart BIFF record to define the axis type.
1412#
1413sub _store_axis {
1414
1415 my $self = shift;
1416
1417 my $record = 0x101D; # Record identifier.
1418 my $length = 0x0012; # Number of bytes to follow.
1419 my $type = $_[0]; # Axis type.
1420 my $reserved1 = 0x00000000; # Reserved.
1421 my $reserved2 = 0x00000000; # Reserved.
1422 my $reserved3 = 0x00000000; # Reserved.
1423 my $reserved4 = 0x00000000; # Reserved.
1424
1425 my $header = pack 'vv', $record, $length;
1426 my $data = '';
1427 $data .= pack 'v', $type;
1428 $data .= pack 'V', $reserved1;
1429 $data .= pack 'V', $reserved2;
1430 $data .= pack 'V', $reserved3;
1431 $data .= pack 'V', $reserved4;
1432
1433 $self->_append( $header, $data );
1434}
1435
1436
1437###############################################################################
1438#
1439# _store_axislineformat()
1440#
1441# Write the AXISLINEFORMAT chart BIFF record.
1442#
1443sub _store_axislineformat {
1444
1445 my $self = shift;
1446
1447 my $record = 0x1021; # Record identifier.
1448 my $length = 0x0002; # Number of bytes to follow.
1449 my $line_format = 0x0001; # Axis line format.
1450
1451 my $header = pack 'vv', $record, $length;
1452 my $data = pack 'v', $line_format;
1453
1454 $self->_append( $header, $data );
1455}
1456
1457
1458###############################################################################
1459#
1460# _store_axisparent()
1461#
1462# Write the AXISPARENT chart BIFF record.
1463#
1464sub _store_axisparent {
1465
1466 my $self = shift;
1467
1468 my $record = 0x1041; # Record identifier.
1469 my $length = 0x0012; # Number of bytes to follow.
1470 my $iax = $_[0]; # Axis index.
1471 my $x = $_[1]; # X-coord.
1472 my $y = $_[2]; # Y-coord.
1473 my $dx = $_[3]; # Length of x axis.
1474 my $dy = $_[4]; # Length of y axis.
1475
1476 my $header = pack 'vv', $record, $length;
1477 my $data = '';
1478 $data .= pack 'v', $iax;
1479 $data .= pack 'V', $x;
1480 $data .= pack 'V', $y;
1481 $data .= pack 'V', $dx;
1482 $data .= pack 'V', $dy;
1483
1484 $self->_append( $header, $data );
1485}
1486
1487
1488###############################################################################
1489#
1490# _store_begin()
1491#
1492# Write the BEGIN chart BIFF record to indicate the start of a sub stream.
1493#
1494sub _store_begin {
1495
1496 my $self = shift;
1497
1498 my $record = 0x1033; # Record identifier.
1499 my $length = 0x0000; # Number of bytes to follow.
1500
1501 my $header = pack 'vv', $record, $length;
1502
1503 $self->_append( $header );
1504}
1505
1506
1507###############################################################################
1508#
1509# _store_catserrange()
1510#
1511# Write the CATSERRANGE chart BIFF record.
1512#
1513sub _store_catserrange {
1514
1515 my $self = shift;
1516
1517 my $record = 0x1020; # Record identifier.
1518 my $length = 0x0008; # Number of bytes to follow.
1519 my $catCross = 0x0001; # Value/category crossing.
1520 my $catLabel = 0x0001; # Frequency of labels.
1521 my $catMark = 0x0001; # Frequency of ticks.
1522 my $grbit = 0x0001; # Option flags.
1523
1524 my $header = pack 'vv', $record, $length;
1525 my $data = '';
1526 $data .= pack 'v', $catCross;
1527 $data .= pack 'v', $catLabel;
1528 $data .= pack 'v', $catMark;
1529 $data .= pack 'v', $grbit;
1530
1531 $self->_append( $header, $data );
1532}
1533
1534
1535###############################################################################
1536#
1537# _store_chart()
1538#
1539# Write the CHART BIFF record. This indicates the start of the chart sub-stream
1540# and contains dimensions of the chart on the display. Units are in 1/72 inch
1541# and are 2 byte integer with 2 byte fraction.
1542#
1543sub _store_chart {
1544
1545 my $self = shift;
1546
1547 my $record = 0x1002; # Record identifier.
1548 my $length = 0x0010; # Number of bytes to follow.
1549 my $x_pos = $_[0]; # X pos of top left corner.
1550 my $y_pos = $_[1]; # Y pos of top left corner.
1551 my $dx = $_[2]; # X size.
1552 my $dy = $_[3]; # Y size.
1553
1554 my $header = pack 'vv', $record, $length;
1555 my $data = '';
1556 $data .= pack 'V', $x_pos;
1557 $data .= pack 'V', $y_pos;
1558 $data .= pack 'V', $dx;
1559 $data .= pack 'V', $dy;
1560
1561 $self->_append( $header, $data );
1562}
1563
1564
1565###############################################################################
1566#
1567# _store_chartformat()
1568#
1569# Write the CHARTFORMAT chart BIFF record. The parent record for formatting
1570# of a chart group.
1571#
1572sub _store_chartformat {
1573
1574 my $self = shift;
1575
1576 my $record = 0x1014; # Record identifier.
1577 my $length = 0x0014; # Number of bytes to follow.
1578 my $reserved1 = 0x00000000; # Reserved.
1579 my $reserved2 = 0x00000000; # Reserved.
1580 my $reserved3 = 0x00000000; # Reserved.
1581 my $reserved4 = 0x00000000; # Reserved.
1582 my $grbit = $_[0] || 0; # Option flags.
1583 my $icrt = 0x0000; # Drawing order.
1584
1585 my $header = pack 'vv', $record, $length;
1586 my $data = '';
1587 $data .= pack 'V', $reserved1;
1588 $data .= pack 'V', $reserved2;
1589 $data .= pack 'V', $reserved3;
1590 $data .= pack 'V', $reserved4;
1591 $data .= pack 'v', $grbit;
1592 $data .= pack 'v', $icrt;
1593
1594 $self->_append( $header, $data );
1595}
1596
1597
1598###############################################################################
1599#
1600# _store_chartline()
1601#
1602# Write the CHARTLINE chart BIFF record.
1603#
1604sub _store_chartline {
1605
1606 my $self = shift;
1607
1608 my $record = 0x101C; # Record identifier.
1609 my $length = 0x0002; # Number of bytes to follow.
1610 my $type = 0x0001; # Drop/hi-lo line type.
1611
1612 my $header = pack 'vv', $record, $length;
1613 my $data = pack 'v', $type;
1614
1615 $self->_append( $header, $data );
1616}
1617
1618
1619###############################################################################
1620#
1621# _store_charttext()
1622#
1623# Write the TEXT chart BIFF record.
1624#
1625sub _store_charttext {
1626
1627 my $self = shift;
1628
1629 my $record = 0x1025; # Record identifier.
1630 my $length = 0x0020; # Number of bytes to follow.
1631 my $horz_align = 0x02; # Horizontal alignment.
1632 my $vert_align = 0x02; # Vertical alignment.
1633 my $bg_mode = 0x0001; # Background display.
1634 my $text_color_rgb = 0x00000000; # Text RGB colour.
1635 my $text_x = 0xFFFFFF46; # Text x-pos.
1636 my $text_y = 0xFFFFFF06; # Text y-pos.
1637 my $text_dx = 0x00000000; # Width.
1638 my $text_dy = 0x00000000; # Height.
1639 my $grbit1 = 0x00B1; # Options
1640 my $text_color_index = 0x004D; # Auto Colour.
1641 my $grbit2 = 0x0000; # Data label placement.
1642 my $rotation = 0x0000; # Text rotation.
1643
1644 my $header = pack 'vv', $record, $length;
1645 my $data = '';
1646 $data .= pack 'C', $horz_align;
1647 $data .= pack 'C', $vert_align;
1648 $data .= pack 'v', $bg_mode;
1649 $data .= pack 'V', $text_color_rgb;
1650 $data .= pack 'V', $text_x;
1651 $data .= pack 'V', $text_y;
1652 $data .= pack 'V', $text_dx;
1653 $data .= pack 'V', $text_dy;
1654 $data .= pack 'v', $grbit1;
1655 $data .= pack 'v', $text_color_index;
1656 $data .= pack 'v', $grbit2;
1657 $data .= pack 'v', $rotation;
1658
1659 $self->_append( $header, $data );
1660}
1661
1662
1663###############################################################################
1664#
1665# _store_dataformat()
1666#
1667# Write the DATAFORMAT chart BIFF record. This record specifies the series
1668# that the subsequent sub stream refers to.
1669#
1670sub _store_dataformat {
1671
1672 my $self = shift;
1673
1674 my $record = 0x1006; # Record identifier.
1675 my $length = 0x0008; # Number of bytes to follow.
1676 my $series_index = $_[0]; # Series index.
1677 my $series_number = $_[1]; # Series number. (Same as index).
1678 my $point_number = $_[2]; # Point number.
1679 my $grbit = 0x0000; # Format flags.
1680
1681 my $header = pack 'vv', $record, $length;
1682 my $data = '';
1683 $data .= pack 'v', $point_number;
1684 $data .= pack 'v', $series_index;
1685 $data .= pack 'v', $series_number;
1686 $data .= pack 'v', $grbit;
1687
1688 $self->_append( $header, $data );
1689}
1690
1691
1692###############################################################################
1693#
1694# _store_defaulttext()
1695#
1696# Write the DEFAULTTEXT chart BIFF record. Identifier for subsequent TEXT
1697# record.
1698#
1699sub _store_defaulttext {
1700
1701 my $self = shift;
1702
1703 my $record = 0x1024; # Record identifier.
1704 my $length = 0x0002; # Number of bytes to follow.
1705 my $type = 0x0002; # Type.
1706
1707 my $header = pack 'vv', $record, $length;
1708 my $data = pack 'v', $type;
1709
1710 $self->_append( $header, $data );
1711}
1712
1713
1714###############################################################################
1715#
1716# _store_dropbar()
1717#
1718# Write the DROPBAR chart BIFF record.
1719#
1720sub _store_dropbar {
1721
1722 my $self = shift;
1723
1724 my $record = 0x103D; # Record identifier.
1725 my $length = 0x0002; # Number of bytes to follow.
1726 my $percent_gap = 0x0096; # Drop bar width gap (%).
1727
1728 my $header = pack 'vv', $record, $length;
1729 my $data = pack 'v', $percent_gap;
1730
1731 $self->_append( $header, $data );
1732}
1733
1734
1735###############################################################################
1736#
1737# _store_end()
1738#
1739# Write the END chart BIFF record to indicate the end of a sub stream.
1740#
1741sub _store_end {
1742
1743 my $self = shift;
1744
1745 my $record = 0x1034; # Record identifier.
1746 my $length = 0x0000; # Number of bytes to follow.
1747
1748 my $header = pack 'vv', $record, $length;
1749
1750 $self->_append( $header );
1751}
1752
1753
1754###############################################################################
1755#
1756# _store_fbi()
1757#
1758# Write the FBI chart BIFF record. Specifies the font information at the time
1759# it was applied to the chart.
1760#
1761sub _store_fbi {
1762
1763 my $self = shift;
1764
1765 my $record = 0x1060; # Record identifier.
1766 my $length = 0x000A; # Number of bytes to follow.
1767 my $index = $_[0]; # Font index.
1768 my $height = $_[1] * 20; # Default font height in twips.
1769 my $width_basis = $_[2]; # Width basis, in twips.
1770 my $height_basis = $_[3]; # Height basis, in twips.
1771 my $scale_basis = $_[4]; # Scale by chart area or plot area.
1772
1773 my $header = pack 'vv', $record, $length;
1774 my $data = '';
1775 $data .= pack 'v', $width_basis;
1776 $data .= pack 'v', $height_basis;
1777 $data .= pack 'v', $height;
1778 $data .= pack 'v', $scale_basis;
1779 $data .= pack 'v', $index;
1780
1781 $self->_append( $header, $data );
1782}
1783
1784
1785###############################################################################
1786#
1787# _store_fontx()
1788#
1789# Write the FONTX chart BIFF record which contains the index of the FONT
1790# record in the Workbook.
1791#
1792sub _store_fontx {
1793
1794 my $self = shift;
1795
1796 my $record = 0x1026; # Record identifier.
1797 my $length = 0x0002; # Number of bytes to follow.
1798 my $index = $_[0]; # Font index.
1799
1800 my $header = pack 'vv', $record, $length;
1801 my $data = pack 'v', $index;
1802
1803 $self->_append( $header, $data );
1804}
1805
1806
1807###############################################################################
1808#
1809# _store_frame()
1810#
1811# Write the FRAME chart BIFF record.
1812#
1813sub _store_frame {
1814
1815 my $self = shift;
1816
1817 my $record = 0x1032; # Record identifier.
1818 my $length = 0x0004; # Number of bytes to follow.
1819 my $frame_type = $_[0]; # Frame type.
1820 my $grbit = $_[1]; # Option flags.
1821
1822 my $header = pack 'vv', $record, $length;
1823 my $data = '';
1824 $data .= pack 'v', $frame_type;
1825 $data .= pack 'v', $grbit;
1826
1827 $self->_append( $header, $data );
1828}
1829
1830
1831###############################################################################
1832#
1833# _store_legend()
1834#
1835# Write the LEGEND chart BIFF record. The Marcus Horan method.
1836#
1837sub _store_legend {
1838
1839 my $self = shift;
1840
1841 my $record = 0x1015; # Record identifier.
1842 my $length = 0x0014; # Number of bytes to follow.
1843 my $x = $_[0]; # X-position.
1844 my $y = $_[1]; # Y-position.
1845 my $width = $_[2]; # Width.
1846 my $height = $_[3]; # Height.
1847 my $wType = $_[4]; # Type.
1848 my $wSpacing = $_[5]; # Spacing.
1849 my $grbit = $_[6]; # Option flags.
1850
1851 my $header = pack 'vv', $record, $length;
1852 my $data = '';
1853 $data .= pack 'V', $x;
1854 $data .= pack 'V', $y;
1855 $data .= pack 'V', $width;
1856 $data .= pack 'V', $height;
1857 $data .= pack 'C', $wType;
1858 $data .= pack 'C', $wSpacing;
1859 $data .= pack 'v', $grbit;
1860
1861 $self->_append( $header, $data );
1862}
1863
1864
1865###############################################################################
1866#
1867# _store_lineformat()
1868#
1869# Write the LINEFORMAT chart BIFF record.
1870#
1871sub _store_lineformat {
1872
1873 my $self = shift;
1874
1875 my $record = 0x1007; # Record identifier.
1876 my $length = 0x000C; # Number of bytes to follow.
1877 my $rgb = $_[0]; # Line RGB colour.
1878 my $lns = $_[1]; # Line pattern.
1879 my $we = $_[2]; # Line weight.
1880 my $grbit = $_[3]; # Option flags.
1881 my $index = $_[4]; # Index to colour of line.
1882
1883 my $header = pack 'vv', $record, $length;
1884 my $data = '';
1885 $data .= pack 'V', $rgb;
1886 $data .= pack 'v', $lns;
1887 $data .= pack 'v', $we;
1888 $data .= pack 'v', $grbit;
1889 $data .= pack 'v', $index;
1890
1891 $self->_append( $header, $data );
1892}
1893
1894
1895###############################################################################
1896#
1897# _store_markerformat()
1898#
1899# Write the MARKERFORMAT chart BIFF record.
1900#
1901sub _store_markerformat {
1902
1903 my $self = shift;
1904
1905 my $record = 0x1009; # Record identifier.
1906 my $length = 0x0014; # Number of bytes to follow.
1907 my $rgbFore = $_[0]; # Foreground RGB color.
1908 my $rgbBack = $_[1]; # Background RGB color.
1909 my $marker = $_[2]; # Type of marker.
1910 my $grbit = $_[3]; # Format flags.
1911 my $icvFore = $_[4]; # Color index marker border.
1912 my $icvBack = $_[5]; # Color index marker fill.
1913 my $miSize = $_[6]; # Size of line markers.
1914
1915 my $header = pack 'vv', $record, $length;
1916 my $data = '';
1917 $data .= pack 'V', $rgbFore;
1918 $data .= pack 'V', $rgbBack;
1919 $data .= pack 'v', $marker;
1920 $data .= pack 'v', $grbit;
1921 $data .= pack 'v', $icvFore;
1922 $data .= pack 'v', $icvBack;
1923 $data .= pack 'V', $miSize;
1924
1925 $self->_append( $header, $data );
1926}
1927
1928
1929###############################################################################
1930#
1931# _store_objectlink()
1932#
1933# Write the OBJECTLINK chart BIFF record.
1934#
1935sub _store_objectlink {
1936
1937 my $self = shift;
1938
1939 my $record = 0x1027; # Record identifier.
1940 my $length = 0x0006; # Number of bytes to follow.
1941 my $link_type = $_[0]; # Object text link type.
1942 my $link_index1 = 0x0000; # Link index 1.
1943 my $link_index2 = 0x0000; # Link index 2.
1944
1945 my $header = pack 'vv', $record, $length;
1946 my $data = '';
1947 $data .= pack 'v', $link_type;
1948 $data .= pack 'v', $link_index1;
1949 $data .= pack 'v', $link_index2;
1950
1951 $self->_append( $header, $data );
1952}
1953
1954
1955###############################################################################
1956#
1957# _store_pieformat()
1958#
1959# Write the PIEFORMAT chart BIFF record.
1960#
1961sub _store_pieformat {
1962
1963 my $self = shift;
1964
1965 my $record = 0x100B; # Record identifier.
1966 my $length = 0x0002; # Number of bytes to follow.
1967 my $percent = 0x0000; # Distance % from center.
1968
1969 my $header = pack 'vv', $record, $length;
1970 my $data = '';
1971 $data .= pack 'v', $percent;
1972
1973 $self->_append( $header, $data );
1974}
1975
1976
1977###############################################################################
1978#
1979# _store_plotarea()
1980#
1981# Write the PLOTAREA chart BIFF record. This indicates that the subsequent
1982# FRAME record belongs to a plot area.
1983#
1984sub _store_plotarea {
1985
1986 my $self = shift;
1987
1988 my $record = 0x1035; # Record identifier.
1989 my $length = 0x0000; # Number of bytes to follow.
1990
1991 my $header = pack 'vv', $record, $length;
1992
1993 $self->_append( $header );
1994}
1995
1996
1997###############################################################################
1998#
1999# _store_plotgrowth()
2000#
2001# Write the PLOTGROWTH chart BIFF record.
2002#
2003sub _store_plotgrowth {
2004
2005 my $self = shift;
2006
2007 my $record = 0x1064; # Record identifier.
2008 my $length = 0x0008; # Number of bytes to follow.
2009 my $dx_plot = 0x00010000; # Horz growth for font scale.
2010 my $dy_plot = 0x00010000; # Vert growth for font scale.
2011
2012 my $header = pack 'vv', $record, $length;
2013 my $data = '';
2014 $data .= pack 'V', $dx_plot;
2015 $data .= pack 'V', $dy_plot;
2016
2017 $self->_append( $header, $data );
2018}
2019
2020
2021###############################################################################
2022#
2023# _store_pos()
2024#
2025# Write the POS chart BIFF record. Generally not required when using
2026# automatic positioning.
2027#
2028sub _store_pos {
2029
2030 my $self = shift;
2031
2032 my $record = 0x104F; # Record identifier.
2033 my $length = 0x0014; # Number of bytes to follow.
2034 my $mdTopLt = $_[0]; # Top left.
2035 my $mdBotRt = $_[1]; # Bottom right.
2036 my $x1 = $_[2]; # X coordinate.
2037 my $y1 = $_[3]; # Y coordinate.
2038 my $x2 = $_[4]; # Width.
2039 my $y2 = $_[5]; # Height.
2040
2041 my $header = pack 'vv', $record, $length;
2042 my $data = '';
2043 $data .= pack 'v', $mdTopLt;
2044 $data .= pack 'v', $mdBotRt;
2045 $data .= pack 'V', $x1;
2046 $data .= pack 'V', $y1;
2047 $data .= pack 'V', $x2;
2048 $data .= pack 'V', $y2;
2049
2050 $self->_append( $header, $data );
2051}
2052
2053
2054###############################################################################
2055#
2056# _store_serauxtrend()
2057#
2058# Write the SERAUXTREND chart BIFF record.
2059#
2060sub _store_serauxtrend {
2061
2062 my $self = shift;
2063
2064 my $record = 0x104B; # Record identifier.
2065 my $length = 0x001C; # Number of bytes to follow.
2066 my $reg_type = $_[0]; # Regression type.
2067 my $poly_order = $_[1]; # Polynomial order.
2068 my $equation = $_[2]; # Display equation.
2069 my $r_squared = $_[3]; # Display R-squared.
2070 my $intercept; # Forced intercept.
2071 my $forecast; # Forecast forward.
2072 my $backcast; # Forecast backward.
2073
2074 # TODO. When supported, intercept needs to be NAN if not used.
2075 # Also need to reverse doubles.
2076 $intercept = pack 'H*', 'FFFFFFFF0001FFFF';
2077 $forecast = pack 'H*', '0000000000000000';
2078 $backcast = pack 'H*', '0000000000000000';
2079
2080
2081 my $header = pack 'vv', $record, $length;
2082 my $data = '';
2083 $data .= pack 'C', $reg_type;
2084 $data .= pack 'C', $poly_order;
2085 $data .= $intercept;
2086 $data .= pack 'C', $equation;
2087 $data .= pack 'C', $r_squared;
2088 $data .= $forecast;
2089 $data .= $backcast;
2090
2091 $self->_append( $header, $data );
2092}
2093
2094
2095###############################################################################
2096#
2097# _store_series()
2098#
2099# Write the SERIES chart BIFF record.
2100#
2101sub _store_series {
2102
2103 my $self = shift;
2104
2105 my $record = 0x1003; # Record identifier.
2106 my $length = 0x000C; # Number of bytes to follow.
2107 my $category_type = 0x0001; # Type: category.
2108 my $value_type = 0x0001; # Type: value.
2109 my $category_count = $_[0]; # Num of categories.
2110 my $value_count = $_[1]; # Num of values.
2111 my $bubble_type = 0x0001; # Type: bubble.
2112 my $bubble_count = 0x0000; # Num of bubble values.
2113
2114 my $header = pack 'vv', $record, $length;
2115 my $data = '';
2116 $data .= pack 'v', $category_type;
2117 $data .= pack 'v', $value_type;
2118 $data .= pack 'v', $category_count;
2119 $data .= pack 'v', $value_count;
2120 $data .= pack 'v', $bubble_type;
2121 $data .= pack 'v', $bubble_count;
2122
2123 $self->_append( $header, $data );
2124}
2125
2126
2127###############################################################################
2128#
2129# _store_seriestext()
2130#
2131# Write the SERIESTEXT chart BIFF record.
2132#
2133sub _store_seriestext {
2134
2135 my $self = shift;
2136
2137 my $record = 0x100D; # Record identifier.
2138 my $length = 0x0000; # Number of bytes to follow.
2139 my $id = 0x0000; # Text id.
2140 my $str = $_[0]; # Text.
2141 my $encoding = $_[1]; # String encoding.
2142 my $cch = length $str; # String length.
2143
2144 # Character length is num of chars not num of bytes
2145 $cch /= 2 if $encoding;
2146
2147 # Change the UTF-16 name from BE to LE
2148 $str = pack 'n*', unpack 'v*', $str if $encoding;
2149
2150 $length = 4 + length( $str );
2151
2152 my $header = pack 'vv', $record, $length;
2153 my $data = '';
2154 $data .= pack 'v', $id;
2155 $data .= pack 'C', $cch;
2156 $data .= pack 'C', $encoding;
2157
2158 $self->_append( $header, $data, $str );
2159}
2160
2161
2162###############################################################################
2163#
2164# _store_serparent()
2165#
2166# Write the SERPARENT chart BIFF record.
2167#
2168sub _store_serparent {
2169
2170 my $self = shift;
2171
2172 my $record = 0x104A; # Record identifier.
2173 my $length = 0x0002; # Number of bytes to follow.
2174 my $series = $_[0]; # Series parent.
2175
2176 my $header = pack 'vv', $record, $length;
2177 my $data = pack 'v', $series;
2178
2179 $self->_append( $header, $data );
2180}
2181
2182
2183###############################################################################
2184#
2185# _store_sertocrt()
2186#
2187# Write the SERTOCRT chart BIFF record to indicate the chart group index.
2188#
2189sub _store_sertocrt {
2190
2191 my $self = shift;
2192
2193 my $record = 0x1045; # Record identifier.
2194 my $length = 0x0002; # Number of bytes to follow.
2195 my $chartgroup = 0x0000; # Chart group index.
2196
2197 my $header = pack 'vv', $record, $length;
2198 my $data = pack 'v', $chartgroup;
2199
2200 $self->_append( $header, $data );
2201}
2202
2203
2204###############################################################################
2205#
2206# _store_shtprops()
2207#
2208# Write the SHTPROPS chart BIFF record.
2209#
2210sub _store_shtprops {
2211
2212 my $self = shift;
2213
2214 my $record = 0x1044; # Record identifier.
2215 my $length = 0x0004; # Number of bytes to follow.
2216 my $grbit = 0x000E; # Option flags.
2217 my $empty_cells = 0x0000; # Empty cell handling.
2218
2219 $grbit = 0x000A if $self->{_embedded};
2220
2221 my $header = pack 'vv', $record, $length;
2222 my $data = '';
2223 $data .= pack 'v', $grbit;
2224 $data .= pack 'v', $empty_cells;
2225
2226 $self->_append( $header, $data );
2227}
2228
2229
2230###############################################################################
2231#
2232# _store_text()
2233#
2234# Write the TEXT chart BIFF record.
2235#
2236sub _store_text {
2237
2238 my $self = shift;
2239
2240 my $record = 0x1025; # Record identifier.
2241 my $length = 0x0020; # Number of bytes to follow.
2242 my $at = 0x02; # Horizontal alignment.
2243 my $vat = 0x02; # Vertical alignment.
2244 my $wBkgMode = 0x0001; # Background display.
2245 my $rgbText = 0x0000; # Text RGB colour.
2246 my $x = $_[0]; # Text x-pos.
2247 my $y = $_[1]; # Text y-pos.
2248 my $dx = $_[2]; # Width.
2249 my $dy = $_[3]; # Height.
2250 my $grbit1 = $_[4]; # Option flags.
2251 my $icvText = 0x004D; # Auto Colour.
2252 my $grbit2 = $_[5]; # Show legend.
2253 my $rotation = $_[6] || 0x00; # Show value.
2254
2255
2256 my $header = pack 'vv', $record, $length;
2257 my $data = '';
2258 $data .= pack 'C', $at;
2259 $data .= pack 'C', $vat;
2260 $data .= pack 'v', $wBkgMode;
2261 $data .= pack 'V', $rgbText;
2262 $data .= pack 'V', $x;
2263 $data .= pack 'V', $y;
2264 $data .= pack 'V', $dx;
2265 $data .= pack 'V', $dy;
2266 $data .= pack 'v', $grbit1;
2267 $data .= pack 'v', $icvText;
2268 $data .= pack 'v', $grbit2;
2269 $data .= pack 'v', $rotation;
2270
2271 $self->_append( $header, $data );
2272}
2273
2274###############################################################################
2275#
2276# _store_tick()
2277#
2278# Write the TICK chart BIFF record.
2279#
2280sub _store_tick {
2281
2282 my $self = shift;
2283
2284 my $record = 0x101E; # Record identifier.
2285 my $length = 0x001E; # Number of bytes to follow.
2286 my $tktMajor = 0x02; # Type of major tick mark.
2287 my $tktMinor = 0x00; # Type of minor tick mark.
2288 my $tlt = 0x03; # Tick label position.
2289 my $wBkgMode = 0x01; # Background mode.
2290 my $rgb = 0x00000000; # Tick-label RGB colour.
2291 my $reserved1 = 0x00000000; # Reserved.
2292 my $reserved2 = 0x00000000; # Reserved.
2293 my $reserved3 = 0x00000000; # Reserved.
2294 my $reserved4 = 0x00000000; # Reserved.
2295 my $grbit = 0x0023; # Option flags.
2296 my $index = 0x004D; # Colour index.
2297 my $reserved5 = 0x0000; # Reserved.
2298
2299 my $header = pack 'vv', $record, $length;
2300 my $data = '';
2301 $data .= pack 'C', $tktMajor;
2302 $data .= pack 'C', $tktMinor;
2303 $data .= pack 'C', $tlt;
2304 $data .= pack 'C', $wBkgMode;
2305 $data .= pack 'V', $rgb;
2306 $data .= pack 'V', $reserved1;
2307 $data .= pack 'V', $reserved2;
2308 $data .= pack 'V', $reserved3;
2309 $data .= pack 'V', $reserved4;
2310 $data .= pack 'v', $grbit;
2311 $data .= pack 'v', $index;
2312 $data .= pack 'v', $reserved5;
2313
2314 $self->_append( $header, $data );
2315}
2316
2317
2318###############################################################################
2319#
2320# _store_valuerange()
2321#
2322# Write the VALUERANGE chart BIFF record.
2323#
2324sub _store_valuerange {
2325
2326 my $self = shift;
2327
2328 my $record = 0x101F; # Record identifier.
2329 my $length = 0x002A; # Number of bytes to follow.
2330 my $numMin = 0x00000000; # Minimum value on axis.
2331 my $numMax = 0x00000000; # Maximum value on axis.
2332 my $numMajor = 0x00000000; # Value of major increment.
2333 my $numMinor = 0x00000000; # Value of minor increment.
2334 my $numCross = 0x00000000; # Value where category axis crosses.
2335 my $grbit = 0x011F; # Format flags.
2336
2337 # TODO. Reverse doubles when they are handled.
2338
2339 my $header = pack 'vv', $record, $length;
2340 my $data = '';
2341 $data .= pack 'd', $numMin;
2342 $data .= pack 'd', $numMax;
2343 $data .= pack 'd', $numMajor;
2344 $data .= pack 'd', $numMinor;
2345 $data .= pack 'd', $numCross;
2346 $data .= pack 'v', $grbit;
2347
2348 $self->_append( $header, $data );
2349}
2350
2351
2352###############################################################################
2353#
2354# Config data.
2355#
2356###############################################################################
2357
2358
2359###############################################################################
2360#
2361# _set_default_properties()
2362#
2363# Setup the default properties for a chart.
2364#
2365sub _set_default_properties {
2366
2367 my $self = shift;
2368
2369 $self->{_legend} = {
2370 _visible => 1,
2371 _position => 0,
2372 _vertical => 0,
2373 };
2374
2375 $self->{_chartarea} = {
2376 _visible => 0,
2377 _fg_color_index => 0x4E,
2378 _fg_color_rgb => 0xFFFFFF,
2379 _bg_color_index => 0x4D,
2380 _bg_color_rgb => 0x000000,
2381 _area_pattern => 0x0000,
2382 _area_options => 0x0000,
2383 _line_pattern => 0x0005,
2384 _line_weight => 0xFFFF,
2385 _line_color_index => 0x4D,
2386 _line_color_rgb => 0x000000,
2387 _line_options => 0x0008,
2388 };
2389
2390 $self->{_plotarea} = {
2391 _visible => 1,
2392 _fg_color_index => 0x16,
2393 _fg_color_rgb => 0xC0C0C0,
2394 _bg_color_index => 0x4F,
2395 _bg_color_rgb => 0x000000,
2396 _area_pattern => 0x0001,
2397 _area_options => 0x0000,
2398 _line_pattern => 0x0000,
2399 _line_weight => 0x0000,
2400 _line_color_index => 0x17,
2401 _line_color_rgb => 0x808080,
2402 _line_options => 0x0000,
2403 };
2404}
2405
2406
2407###############################################################################
2408#
2409# _set_default_config_data()
2410#
2411# Setup the default configuration data for a chart.
2412#
2413sub _set_default_config_data {
2414
2415 my $self = shift;
2416
2417 #<<< Perltidy ignore this.
2418 $self->{_config} = {
2419 _axisparent => [ 0, 0x00F8, 0x01F5, 0x0E7F, 0x0B36 ],
2420 _axisparent_pos => [ 2, 2, 0x008C, 0x01AA, 0x0EEA, 0x0C52 ],
2421 _chart => [ 0x0000, 0x0000, 0x02DD51E0, 0x01C2B838 ],
2422 _font_numbers => [ 5, 10, 0x38B8, 0x22A1, 0x0000 ],
2423 _font_series => [ 6, 10, 0x38B8, 0x22A1, 0x0001 ],
2424 _font_title => [ 7, 12, 0x38B8, 0x22A1, 0x0000 ],
2425 _font_axes => [ 8, 10, 0x38B8, 0x22A1, 0x0001 ],
2426 _legend => [ 0x05F9, 0x0EE9, 0x047D, 0x9C, 0x00, 0x01, 0x0F ],
2427 _legend_pos => [ 5, 2, 0x05F9, 0x0EE9, 0, 0 ],
2428 _legend_text => [ 0xFFFFFF46, 0xFFFFFF06, 0, 0, 0x00B1, 0x0000 ],
2429 _legend_text_pos => [ 2, 2, 0, 0, 0, 0 ],
2430 _series_text => [ 0xFFFFFF46, 0xFFFFFF06, 0, 0, 0x00B1, 0x1020 ],
2431 _series_text_pos => [ 2, 2, 0, 0, 0, 0 ],
2432 _title_text => [ 0x06E4, 0x0051, 0x01DB, 0x00C4, 0x0081, 0x1030 ],
2433 _title_text_pos => [ 2, 2, 0, 0, 0x73, 0x1D ],
2434 _x_axis_text => [ 0x07E1, 0x0DFC, 0xB2, 0x9C, 0x0081, 0x0000 ],
2435 _x_axis_text_pos => [ 2, 2, 0, 0, 0x2B, 0x17 ],
2436 _y_axis_text => [ 0x002D, 0x06AA, 0x5F, 0x1CC, 0x0281, 0x00, 90 ],
2437 _y_axis_text_pos => [ 2, 2, 0, 0, 0x17, 0x44 ],
2438 }; #>>>
2439
2440
2441}
2442
2443
2444###############################################################################
2445#
2446# _set_embedded_config_data()
2447#
2448# Setup the default configuration data for an embedded chart.
2449#
2450sub _set_embedded_config_data {
2451
2452 my $self = shift;
2453
2454 $self->{_embedded} = 1;
2455
2456 $self->{_chartarea} = {
2457 _visible => 1,
2458 _fg_color_index => 0x4E,
2459 _fg_color_rgb => 0xFFFFFF,
2460 _bg_color_index => 0x4D,
2461 _bg_color_rgb => 0x000000,
2462 _area_pattern => 0x0001,
2463 _area_options => 0x0001,
2464 _line_pattern => 0x0000,
2465 _line_weight => 0x0000,
2466 _line_color_index => 0x4D,
2467 _line_color_rgb => 0x000000,
2468 _line_options => 0x0009,
2469 };
2470
2471
2472 #<<< Perltidy ignore this.
2473 $self->{_config} = {
2474 _axisparent => [ 0, 0x01D8, 0x031D, 0x0D79, 0x07E9 ],
2475 _axisparent_pos => [ 2, 2, 0x010C, 0x0292, 0x0E46, 0x09FD ],
2476 _chart => [ 0x0000, 0x0000, 0x01847FE8, 0x00F47FE8 ],
2477 _font_numbers => [ 5, 10, 0x1DC4, 0x1284, 0x0000 ],
2478 _font_series => [ 6, 10, 0x1DC4, 0x1284, 0x0001 ],
2479 _font_title => [ 7, 12, 0x1DC4, 0x1284, 0x0000 ],
2480 _font_axes => [ 8, 10, 0x1DC4, 0x1284, 0x0001 ],
2481 _legend => [ 0x044E, 0x0E4A, 0x088D, 0x0123, 0x0, 0x1, 0xF ],
2482 _legend_pos => [ 5, 2, 0x044E, 0x0E4A, 0, 0 ],
2483 _legend_text => [ 0xFFFFFFD9, 0xFFFFFFC1, 0, 0, 0x00B1, 0x0000 ],
2484 _legend_text_pos => [ 2, 2, 0, 0, 0, 0 ],
2485 _series_text => [ 0xFFFFFFD9, 0xFFFFFFC1, 0, 0, 0x00B1, 0x1020 ],
2486 _series_text_pos => [ 2, 2, 0, 0, 0, 0 ],
2487 _title_text => [ 0x060F, 0x004C, 0x038A, 0x016F, 0x0081, 0x1030 ],
2488 _title_text_pos => [ 2, 2, 0, 0, 0x73, 0x1D ],
2489 _x_axis_text => [ 0x07EF, 0x0C8F, 0x153, 0x123, 0x81, 0x00 ],
2490 _x_axis_text_pos => [ 2, 2, 0, 0, 0x2B, 0x17 ],
2491 _y_axis_text => [ 0x0057, 0x0564, 0xB5, 0x035D, 0x0281, 0x00, 90 ],
2492 _y_axis_text_pos => [ 2, 2, 0, 0, 0x17, 0x44 ],
2493 }; #>>>
2494}
2495
2496
2497
24981;
2499
2500
2501__END__
2502
2503
2504=head1 NAME
2505
2506Chart - A writer class for Excel Charts.
2507
2508=head1 SYNOPSIS
2509
2510To create a simple Excel file with a chart using Spreadsheet::WriteExcel:
2511
2512 #!/usr/bin/perl -w
2513
2514 use strict;
2515 use Spreadsheet::WriteExcel;
2516
2517 my $workbook = Spreadsheet::WriteExcel->new( 'chart.xls' );
2518 my $worksheet = $workbook->add_worksheet();
2519
2520 my $chart = $workbook->add_chart( type => 'column' );
2521
2522 # Configure the chart.
2523 $chart->add_series(
2524 categories => '=Sheet1!$A$2:$A$7',
2525 values => '=Sheet1!$B$2:$B$7',
2526 );
2527
2528 # Add the worksheet data the chart refers to.
2529 my $data = [
2530 [ 'Category', 2, 3, 4, 5, 6, 7 ],
2531 [ 'Value', 1, 4, 5, 2, 1, 5 ],
2532 ];
2533
2534 $worksheet->write( 'A1', $data );
2535
2536 __END__
2537
2538
2539=head1 DESCRIPTION
2540
2541The C<Chart> module is an abstract base class for modules that implement charts in L<Spreadsheet::WriteExcel>. The information below is applicable to all of the available subclasses.
2542
2543The C<Chart> module isn't used directly, a chart object is created via the Workbook C<add_chart()> method where the chart type is specified:
2544
2545 my $chart = $workbook->add_chart( type => 'column' );
2546
2547Currently the supported chart types are:
2548
2549=over
2550
2551=item * C<area>: Creates an Area (filled line) style chart. See L<Spreadsheet::WriteExcel::Chart::Area>.
2552
2553=item * C<bar>: Creates a Bar style (transposed histogram) chart. See L<Spreadsheet::WriteExcel::Chart::Bar>.
2554
2555=item * C<column>: Creates a column style (histogram) chart. See L<Spreadsheet::WriteExcel::Chart::Column>.
2556
2557=item * C<line>: Creates a Line style chart. See L<Spreadsheet::WriteExcel::Chart::Line>.
2558
2559=item * C<pie>: Creates an Pie style chart. See L<Spreadsheet::WriteExcel::Chart::Pie>.
2560
2561=item * C<scatter>: Creates an Scatter style chart. See L<Spreadsheet::WriteExcel::Chart::Scatter>.
2562
2563=item * C<stock>: Creates an Stock style chart. See L<Spreadsheet::WriteExcel::Chart::Stock>.
2564
2565=back
2566
2567More charts and sub-types will be supported in time. See the L</TODO> section.
2568
2569Methods that are common to all chart types are documented below.
2570
2571=head1 CHART METHODS
2572
2573=head2 add_series()
2574
2575In an Excel chart a "series" is a collection of information such as values, x-axis labels and the name that define which data is plotted. These settings are displayed when you select the C<< Chart -> Source Data... >> menu option.
2576
2577With a Spreadsheet::WriteExcel chart object the C<add_series()> method is used to set the properties for a series:
2578
2579 $chart->add_series(
2580 categories => '=Sheet1!$A$2:$A$10',
2581 values => '=Sheet1!$B$2:$B$10',
2582 name => 'Series name',
2583 name_formula => '=Sheet1!$B$1',
2584 );
2585
2586The properties that can be set are:
2587
2588=over
2589
2590=item * C<values>
2591
2592This is the most important property of a series and must be set for every chart object. It links the chart with the worksheet data that it displays. Note the format that should be used for the formula. See L</Working with Cell Ranges>.
2593
2594=item * C<categories>
2595
2596This sets the chart category labels. The category is more or less the same as the X-axis. In most chart types the C<categories> property is optional and the chart will just assume a sequential series from C<1 .. n>.
2597
2598=item * C<name>
2599
2600Set the name for the series. The name is displayed in the chart legend and in the formula bar. The name property is optional and if it isn't supplied will default to C<Series 1 .. n>.
2601
2602=item * C<name_formula>
2603
2604Optional, can be used to link the name to a worksheet cell. See L</Chart names and links>.
2605
2606=back
2607
2608You can add more than one series to a chart, in fact some chart types such as C<stock> require it. The series numbering and order in the final chart is the same as the order in which that are added.
2609
2610 # Add the first series.
2611 $chart->add_series(
2612 categories => '=Sheet1!$A$2:$A$7',
2613 values => '=Sheet1!$B$2:$B$7',
2614 name => 'Test data series 1',
2615 );
2616
2617 # Add another series. Category is the same but values are different.
2618 $chart->add_series(
2619 categories => '=Sheet1!$A$2:$A$7',
2620 values => '=Sheet1!$C$2:$C$7',
2621 name => 'Test data series 2',
2622 );
2623
2624
2625
2626=head2 set_x_axis()
2627
2628The C<set_x_axis()> method is used to set properties of the X axis.
2629
2630 $chart->set_x_axis( name => 'Sample length (m)' );
2631
2632The properties that can be set are:
2633
2634=over
2635
2636=item * C<name>
2637
2638Set the name (title or caption) for the axis. The name is displayed below the X axis. This property is optional. The default is to have no axis name.
2639
2640=item * C<name_formula>
2641
2642Optional, can be used to link the name to a worksheet cell. See L</Chart names and links>.
2643
2644=back
2645
2646Additional axis properties such as range, divisions and ticks will be made available in later releases. See the L</TODO> section.
2647
2648
2649=head2 set_y_axis()
2650
2651The C<set_y_axis()> method is used to set properties of the Y axis.
2652
2653 $chart->set_y_axis( name => 'Sample weight (kg)' );
2654
2655The properties that can be set are:
2656
2657=over
2658
2659=item * C<name>
2660
2661Set the name (title or caption) for the axis. The name is displayed to the left of the Y axis. This property is optional. The default is to have no axis name.
2662
2663=item * C<name_formula>
2664
2665Optional, can be used to link the name to a worksheet cell. See L</Chart names and links>.
2666
2667=back
2668
2669Additional axis properties such as range, divisions and ticks will be made available in later releases. See the L</TODO> section.
2670
2671=head2 set_title()
2672
2673The C<set_title()> method is used to set properties of the chart title.
2674
2675 $chart->set_title( name => 'Year End Results' );
2676
2677The properties that can be set are:
2678
2679=over
2680
2681=item * C<name>
2682
2683Set the name (title) for the chart. The name is displayed above the chart. This property is optional. The default is to have no chart title.
2684
2685=item * C<name_formula>
2686
2687Optional, can be used to link the name to a worksheet cell. See L</Chart names and links>.
2688
2689=back
2690
2691
2692=head2 set_legend()
2693
2694The C<set_legend()> method is used to set properties of the chart legend.
2695
2696 $chart->set_legend( position => 'none' );
2697
2698The properties that can be set are:
2699
2700=over
2701
2702=item * C<position>
2703
2704Set the position of the chart legend.
2705
2706 $chart->set_legend( position => 'none' );
2707
2708The default legend position is C<bottom>. The currently supported chart positions are:
2709
2710 none
2711 bottom
2712
2713The other legend positions will be added soon.
2714
2715=back
2716
2717
2718=head2 set_chartarea()
2719
2720The C<set_chartarea()> method is used to set the properties of the chart area. In Excel the chart area is the background area behind the chart.
2721
2722The properties that can be set are:
2723
2724=over
2725
2726=item * C<color>
2727
2728Set the colour of the chart area. The Excel default chart area color is 'white', index 9. See L</Chart object colours>.
2729
2730=item * C<line_color>
2731
2732Set the colour of the chart area border line. The Excel default border line colour is 'black', index 9. See L</Chart object colours>.
2733
2734=item * C<line_pattern>
2735
2736Set the pattern of the of the chart area border line. The Excel default pattern is 'none', index 0 for a chart sheet and 'solid', index 1, for an embedded chart. See L</Chart line patterns>.
2737
2738=item * C<line_weight>
2739
2740Set the weight of the of the chart area border line. The Excel default weight is 'narrow', index 2. See L</Chart line weights>.
2741
2742=back
2743
2744Here is an example of setting several properties:
2745
2746 $chart->set_chartarea(
2747 color => 'red',
2748 line_color => 'black',
2749 line_pattern => 2,
2750 line_weight => 3,
2751 );
2752
2753Note, for chart sheets the chart area border is off by default. For embedded charts is is on by default.
2754
2755=head2 set_plotarea()
2756
2757The C<set_plotarea()> method is used to set properties of the plot area of a chart. In Excel the plot area is the area between the axes on which the chart series are plotted.
2758
2759The properties that can be set are:
2760
2761=over
2762
2763=item * C<visible>
2764
2765Set the visibility of the plot area. The default is 1 for visible. Set to 0 to hide the plot area and have the same colour as the background chart area.
2766
2767=item * C<color>
2768
2769Set the colour of the plot area. The Excel default plot area color is 'silver', index 23. See L</Chart object colours>.
2770
2771=item * C<line_color>
2772
2773Set the colour of the plot area border line. The Excel default border line colour is 'gray', index 22. See L</Chart object colours>.
2774
2775=item * C<line_pattern>
2776
2777Set the pattern of the of the plot area border line. The Excel default pattern is 'solid', index 1. See L</Chart line patterns>.
2778
2779=item * C<line_weight>
2780
2781Set the weight of the of the plot area border line. The Excel default weight is 'narrow', index 2. See L</Chart line weights>.
2782
2783=back
2784
2785Here is an example of setting several properties:
2786
2787 $chart->set_plotarea(
2788 color => 'red',
2789 line_color => 'black',
2790 line_pattern => 2,
2791 line_weight => 3,
2792 );
2793
2794
2795
2796=head1 WORKSHEET METHODS
2797
2798In Excel a chart sheet (i.e, a chart that isn't embedded) shares properties with data worksheets such as tab selection, headers, footers, margins and print properties.
2799
2800In Spreadsheet::WriteExcel you can set chart sheet properties using the same methods that are used for Worksheet objects.
2801
2802The following Worksheet methods are also available through a non-embedded Chart object:
2803
2804 get_name()
2805 activate()
2806 select()
2807 hide()
2808 set_first_sheet()
2809 protect()
2810 set_zoom()
2811 set_tab_color()
2812
2813 set_landscape()
2814 set_portrait()
2815 set_paper()
2816 set_margins()
2817 set_header()
2818 set_footer()
2819
2820See L<Spreadsheet::WriteExcel> for a detailed explanation of these methods.
2821
2822=head1 EXAMPLE
2823
2824Here is a complete example that demonstrates some of the available features when creating a chart.
2825
2826 #!/usr/bin/perl -w
2827
2828 use strict;
2829 use Spreadsheet::WriteExcel;
2830
2831 my $workbook = Spreadsheet::WriteExcel->new( 'chart_area.xls' );
2832 my $worksheet = $workbook->add_worksheet();
2833 my $bold = $workbook->add_format( bold => 1 );
2834
2835 # Add the worksheet data that the charts will refer to.
2836 my $headings = [ 'Number', 'Sample 1', 'Sample 2' ];
2837 my $data = [
2838 [ 2, 3, 4, 5, 6, 7 ],
2839 [ 1, 4, 5, 2, 1, 5 ],
2840 [ 3, 6, 7, 5, 4, 3 ],
2841 ];
2842
2843 $worksheet->write( 'A1', $headings, $bold );
2844 $worksheet->write( 'A2', $data );
2845
2846 # Create a new chart object. In this case an embedded chart.
2847 my $chart = $workbook->add_chart( type => 'area', embedded => 1 );
2848
2849 # Configure the first series. (Sample 1)
2850 $chart->add_series(
2851 name => 'Sample 1',
2852 categories => '=Sheet1!$A$2:$A$7',
2853 values => '=Sheet1!$B$2:$B$7',
2854 );
2855
2856 # Configure the second series. (Sample 2)
2857 $chart->add_series(
2858 name => 'Sample 2',
2859 categories => '=Sheet1!$A$2:$A$7',
2860 values => '=Sheet1!$C$2:$C$7',
2861 );
2862
2863 # Add a chart title and some axis labels.
2864 $chart->set_title ( name => 'Results of sample analysis' );
2865 $chart->set_x_axis( name => 'Test number' );
2866 $chart->set_y_axis( name => 'Sample length (cm)' );
2867
2868 # Insert the chart into the worksheet (with an offset).
2869 $worksheet->insert_chart( 'D2', $chart, 25, 10 );
2870
2871 __END__
2872
2873
2874=begin html
2875
2876<p>This will produce a chart that looks like this:</p>
2877
2878<p><center><img src="http://homepage.eircom.net/~jmcnamara/perl/images/area1.jpg" width="527" height="320" alt="Chart example." /></center></p>
2879
2880=end html
2881
2882
2883=head1 Chart object colours
2884
2885Many of the chart objects supported by Spreadsheet::WriteExcl allow the default colours to be changed. Excel provides a palette of 56 colours and in Spreadsheet::WriteExcel these colours are accessed via their palette index in the range 8..63.
2886
2887The most commonly used colours can be accessed by name or index.
2888
2889 black => 8, green => 17, navy => 18,
2890 white => 9, orange => 53, pink => 33,
2891 red => 10, gray => 23, purple => 20,
2892 blue => 12, lime => 11, silver => 22,
2893 yellow => 13, cyan => 15,
2894 brown => 16, magenta => 14,
2895
2896For example the following are equivalent.
2897
2898 $chart->set_plotarea( color => 10 );
2899 $chart->set_plotarea( color => 'red' );
2900
2901The colour palette is shown in C<palette.html> in the C<docs> directory of the distro. An Excel version of the palette can be generated using C<colors.pl> in the C<examples> directory.
2902
2903User defined colours can be set using the C<set_custom_color()> workbook method. This and other aspects of using colours are discussed in the "Colours in Excel" section of the main Spreadsheet::WriteExcel documentation: L<http://search.cpan.org/dist/Spreadsheet-WriteExcel/lib/Spreadsheet/WriteExcel.pm#COLOURS_IN_EXCEL>.
2904
2905=head1 Chart line patterns
2906
2907Chart lines patterns can be set using either an index or a name:
2908
2909 $chart->set_plotarea( weight => 2 );
2910 $chart->set_plotarea( weight => 'dash' );
2911
2912Chart lines have 9 possible patterns are follows:
2913
2914 'none' => 0,
2915 'solid' => 1,
2916 'dash' => 2,
2917 'dot' => 3,
2918 'dash-dot' => 4,
2919 'dash-dot-dot' => 5,
2920 'medium-gray' => 6,
2921 'dark-gray' => 7,
2922 'light-gray' => 8,
2923
2924The patterns 1-8 are shown in order in the drop down dialog boxes in Excel. The default pattern is 'solid', index 1.
2925
2926
2927=head1 Chart line weights
2928
2929Chart lines weights can be set using either an index or a name:
2930
2931 $chart->set_plotarea( weight => 1 );
2932 $chart->set_plotarea( weight => 'hairline' );
2933
2934Chart lines have 4 possible weights are follows:
2935
2936 'hairline' => 1,
2937 'narrow' => 2,
2938 'medium' => 3,
2939 'wide' => 4,
2940
2941The weights 1-4 are shown in order in the drop down dialog boxes in Excel. The default weight is 'narrow', index 2.
2942
2943
2944=head1 Chart names and links
2945
2946The C<add_series())>, C<set_x_axis()>, C<set_y_axis()> and C<set_title()> methods all support a C<name> property. In general these names can be either a static string or a link to a worksheet cell. If you choose to use the C<name_formula> property to specify a link then you should also the C<name> property. This isn't strictly required by Excel but some third party applications expect it to be present.
2947
2948 $chart->set_title(
2949 name => 'Year End Results',
2950 name_formula => '=Sheet1!$C$1',
2951 );
2952
2953These links should be used sparingly since they aren't commonly used in Excel charts.
2954
2955
2956=head1 Chart names and Unicode
2957
2958The C<add_series())>, C<set_x_axis()>, C<set_y_axis()> and C<set_title()> methods all support a C<name> property. These names can be UTF8 strings if you are using perl 5.8+.
2959
2960
2961 # perl 5.8+ example:
2962 my $smiley = "\x{263A}";
2963
2964 $chart->set_title( name => "Best. Results. Ever! $smiley" );
2965
2966For older perls you write Unicode strings as UTF-16BE by adding a C<name_encoding> property:
2967
2968 # perl 5.005 example:
2969 my $utf16be_name = pack 'n', 0x263A;
2970
2971 $chart->set_title(
2972 name => $utf16be_name,
2973 name_encoding => 1,
2974 );
2975
2976This methodology is explained in the "UNICODE IN EXCEL" section of L<Spreadsheet::WriteExcel> but is semi-deprecated. If you are using Unicode the easiest option is to just use UTF8 in perl 5.8+.
2977
2978
2979=head1 Working with Cell Ranges
2980
2981
2982In the section on C<add_series()> it was noted that the series must be defined using a range formula:
2983
2984 $chart->add_series( values => '=Sheet1!$B$2:$B$10' );
2985
2986The worksheet name must be specified (even for embedded charts) and the cell references must be "absolute" references, i.e., they must contain C<$> signs. This is the format that is required by Excel for chart references.
2987
2988Since it isn't very convenient to work with this type of string programmatically the L<Spreadsheet::WriteExcel::Utility> module, which is included with Spreadsheet::WriteExcel, provides a function called C<xl_range_formula()> to convert from zero based row and column cell references to an A1 style formula string.
2989
2990The syntax is:
2991
2992 xl_range_formula($sheetname, $row_1, $row_2, $col_1, $col_2)
2993
2994If you include it in your program, using the standard import syntax, you can use the function as follows:
2995
2996
2997 # Include the Utility module or just the function you need.
2998 use Spreadsheet::WriteExcel::Utility qw( xl_range_formula );
2999 ...
3000
3001 # Then use it as required.
3002 $chart->add_series(
3003 categories => xl_range_formula( 'Sheet1', 1, 9, 0, 0 ),
3004 values => xl_range_formula( 'Sheet1', 1, 9, 1, 1 );,
3005 );
3006
3007 # Which is the same as:
3008 $chart->add_series(
3009 categories => '=Sheet1!$A$2:$A$10',
3010 values => '=Sheet1!$B$2:$B$10',
3011 );
3012
3013See L<Spreadsheet::WriteExcel::Utility> for more details.
3014
3015
3016=head1 TODO
3017
3018Charts in Spreadsheet::WriteExcel are a work in progress. More chart types and features will be added in time. Please be patient. Even a small feature can take a week or more to implement, test and document.
3019
3020Features that are on the TODO list and will be added are:
3021
3022=over
3023
3024=item * Chart sub-types.
3025
3026=item * Colours and formatting options. For now you will have to make do with the default Excel colours and formats.
3027
3028=item * Axis controls, gridlines.
3029
3030=item * 3D charts.
3031
3032=item * Embedded data in charts for third party application support. See Known Issues.
3033
3034=item * Additional chart types such as Bubble and Radar. Send an email if you are interested in other types and they will be added to the queue.
3035
3036=back
3037
3038If you are interested in sponsoring a feature let me know.
3039
3040=head1 KNOWN ISSUES
3041
3042=over
3043
3044=item * Currently charts don't contain embedded data from which the charts can be rendered. Excel and most other third party applications ignore this and read the data via the links that have been specified. However, some applications may complain or not render charts correctly. The preview option in Mac OS X is an known example. This will be fixed in a later release.
3045
3046=item * When there are several charts with titles set in a workbook some of the titles may display at a font size of 10 instead of the default 12 until another chart with the title set is viewed.
3047
3048=item * Stock (and other) charts should have the X-axis dates aligned at an angle for clarity. This will be fixed at a later stage.
3049
3050=back
3051
3052
3053=head1 AUTHOR
3054
3055John McNamara jmcnamara@cpan.org
3056
3057=head1 COPYRIGHT
3058
3059Copyright MM-MMX, John McNamara.
3060
3061All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.
3062