Skip to content

Commit 7c04c90

Browse files
committed
chart: add position option for custom data labels
Add the option to position custom data labels in the same way that the data labels can be positioned for the entire series.
1 parent b042634 commit 7c04c90

File tree

7 files changed

+347
-26
lines changed

7 files changed

+347
-26
lines changed

lib/Excel/Writer/XLSX/Chart.pm

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,6 +1884,24 @@ sub _get_labels_properties {
18841884
$property{font} = $self->_convert_font_args( $property{font} );
18851885
}
18861886

1887+
1888+
# Map user defined label positions to Excel positions.
1889+
if ( my $position = $property{position} ) {
1890+
1891+
if ( exists $self->{_label_positions}->{$position} ) {
1892+
if ( $position eq $self->{_label_position_default} ) {
1893+
$property{position} = undef;
1894+
}
1895+
else {
1896+
$property{position} = $self->{_label_positions}->{$position};
1897+
}
1898+
}
1899+
else {
1900+
carp "Unsupported label position '$position' for this chart type";
1901+
return undef;
1902+
}
1903+
}
1904+
18871905
# Set the line properties for the data labels.
18881906
my $line = $self->_get_line_properties( $property{line} );
18891907

@@ -5929,38 +5947,54 @@ sub _write_custom_labels {
59295947
$index++;
59305948
next if !defined $label;
59315949

5950+
my $use_custom_formatting = 1;
5951+
59325952
$self->xml_start_tag( 'c:dLbl' );
59335953

59345954
# Write the c:idx element.
59355955
$self->_write_idx( $index - 1 );
59365956

59375957
if ( defined $label->{delete} && $label->{delete} ) {
5958+
5959+
# Delete/hide label.
59385960
$self->_write_delete( 1 );
59395961
}
5940-
elsif ( defined $label->{formula} ) {
5941-
$self->_write_custom_label_formula( $label );
5962+
elsif (defined $label->{formula}
5963+
|| defined $label->{value}
5964+
|| $label->{position} )
5965+
{
59425966

5943-
if ( $parent->{position} ) {
5944-
$self->_write_d_lbl_pos( $parent->{position} );
5967+
# Write the c:layout element.
5968+
$self->_write_layout();
5969+
5970+
if ( defined $label->{formula} ) {
5971+
$self->_write_custom_label_formula( $label );
59455972
}
5973+
elsif ( defined $label->{value} ) {
5974+
$self->_write_custom_label_str( $label );
59465975

5947-
$self->_write_show_val() if $parent->{value};
5948-
$self->_write_show_cat_name() if $parent->{category};
5949-
$self->_write_show_ser_name() if $parent->{series_name};
5950-
}
5951-
elsif ( defined $label->{value} ) {
5952-
$self->_write_custom_label_str( $label );
5976+
# String values use spPr formatting.
5977+
$use_custom_formatting = 0;
5978+
}
59535979

5954-
if ( $parent->{position} ) {
5955-
$self->_write_d_lbl_pos( $parent->{position} );
5980+
5981+
if ( $use_custom_formatting ) {
5982+
$self->_write_custom_label_format( $label );
5983+
}
5984+
5985+
5986+
if ( my $position = $label->{position} || $parent->{position} ) {
5987+
$self->_write_d_lbl_pos( $position );
59565988
}
59575989

59585990
$self->_write_show_val() if $parent->{value};
59595991
$self->_write_show_cat_name() if $parent->{category};
59605992
$self->_write_show_ser_name() if $parent->{series_name};
5993+
5994+
59615995
}
59625996
else {
5963-
$self->_write_custom_label_format_only( $label );
5997+
$self->_write_custom_label_format( $label );
59645998
}
59655999

59666000
$self->xml_end_tag( 'c:dLbl' );
@@ -5970,6 +6004,7 @@ sub _write_custom_labels {
59706004

59716005
##############################################################################
59726006
#
6007+
59736008
# _write_custom_label_str()
59746009
#
59756010
# Write parts of the <c:dLbl> element for strings.
@@ -5983,9 +6018,6 @@ sub _write_custom_label_str {
59836018
my $is_y_axis = 0;
59846019
my $has_formatting = _has_fill_formatting($label);
59856020

5986-
# Write the c:layout element.
5987-
$self->_write_layout();
5988-
59896021
$self->xml_start_tag( 'c:tx' );
59906022

59916023
# Write the c:rich element.
@@ -6017,28 +6049,21 @@ sub _write_custom_label_formula {
60176049
$data = $self->{_formula_data}->[$data_id];
60186050
}
60196051

6020-
# Write the c:layout element.
6021-
$self->_write_layout();
6022-
60236052
$self->xml_start_tag( 'c:tx' );
60246053

60256054
# Write the c:strRef element.
60266055
$self->_write_str_ref( $formula, $data, 'str' );
60276056

60286057
$self->xml_end_tag( 'c:tx' );
6029-
6030-
# Write the data label formating, if any.
6031-
$self->_write_custom_label_format_only($label);
60326058
}
60336059

60346060
##############################################################################
60356061
#
6036-
# _write_custom_label_format_only()
6062+
# _write_custom_label_format()
60376063
#
6038-
# Write parts of the <c:dLbl> element for labels where only the formatting has
6039-
# changed.
6064+
# Write the formatting and font elements for the custom labels.
60406065
#
6041-
sub _write_custom_label_format_only {
6066+
sub _write_custom_label_format {
60426067

60436068
my $self = shift;
60446069
my $label = shift;
@@ -8484,6 +8509,7 @@ The property elements of the C<custom> lists should be dicts with the following
84848509
pattern
84858510
gradient
84868511
delete
8512+
position
84878513
84888514
The C<value> property should be a string, number or formula string that refers to a cell from which the value will be taken:
84898515
@@ -8539,6 +8565,11 @@ the maximum and the minimum:
85398565
undef,
85408566
];
85418567
8568+
The C<position> property is used to position the custom data such as "center"
8569+
or "left" relative to the data point. See the explanation for the C<position>
8570+
property of series data labels above.
8571+
8572+
85428573
85438574
=head2 Points
85448575

t/regression/chart_data_labels52.t

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
###############################################################################
2+
#
3+
# Tests the output of Excel::Writer::XLSX against Excel generated files.
4+
#
5+
# Copyright 2000-2024, John McNamara, jmcnamara@cpan.org
6+
#
7+
# SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
8+
#
9+
10+
use lib 't/lib';
11+
use TestFunctions qw(_compare_xlsx_files _is_deep_diff);
12+
use strict;
13+
use warnings;
14+
15+
use Test::More tests => 1;
16+
17+
###############################################################################
18+
#
19+
# Tests setup.
20+
#
21+
my $filename = 'chart_data_labels52.xlsx';
22+
my $dir = 't/regression/';
23+
my $got_filename = $dir . "ewx_$filename";
24+
my $exp_filename = $dir . 'xlsx_files/' . $filename;
25+
26+
my $ignore_members = [];
27+
28+
my $ignore_elements = {};
29+
30+
31+
###############################################################################
32+
#
33+
# Test the creation of a simple Excel::Writer::XLSX file.
34+
#
35+
use Excel::Writer::XLSX;
36+
37+
my $workbook = Excel::Writer::XLSX->new( $got_filename );
38+
my $worksheet = $workbook->add_worksheet();
39+
my $chart = $workbook->add_chart( type => 'column', embedded => 1 );
40+
41+
# For testing, copy the randomly generated axis ids in the target xlsx file.
42+
$chart->{_axis_ids} = [ 80508800, 88002560 ];
43+
44+
my $data = [
45+
[ 1, 2, 3, 4, 5 ],
46+
[ 2, 4, 6, 8, 10 ],
47+
[ 3, 6, 9, 12, 15 ],
48+
49+
];
50+
51+
$worksheet->write( 'A1', $data );
52+
53+
$chart->add_series(
54+
values => '=Sheet1!$A$1:$A$5',
55+
data_labels => { value => 1, custom => [{position => 'center'}] }
56+
);
57+
58+
$chart->add_series( values => '=Sheet1!$B$1:$B$5');
59+
$chart->add_series( values => '=Sheet1!$C$1:$C$5' );
60+
61+
$worksheet->insert_chart( 'E9', $chart );
62+
63+
$workbook->close();
64+
65+
66+
###############################################################################
67+
#
68+
# Compare the generated and existing Excel files.
69+
#
70+
71+
my ( $got, $expected, $caption ) = _compare_xlsx_files(
72+
73+
$got_filename,
74+
$exp_filename,
75+
$ignore_members,
76+
$ignore_elements,
77+
);
78+
79+
_is_deep_diff( $got, $expected, $caption );
80+
81+
82+
###############################################################################
83+
#
84+
# Cleanup.
85+
#
86+
unlink $got_filename;
87+
88+
__END__

t/regression/chart_data_labels53.t

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
###############################################################################
2+
#
3+
# Tests the output of Excel::Writer::XLSX against Excel generated files.
4+
#
5+
# Copyright 2000-2024, John McNamara, jmcnamara@cpan.org
6+
#
7+
# SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
8+
#
9+
10+
use lib 't/lib';
11+
use TestFunctions qw(_compare_xlsx_files _is_deep_diff);
12+
use strict;
13+
use warnings;
14+
15+
use Test::More tests => 1;
16+
17+
###############################################################################
18+
#
19+
# Tests setup.
20+
#
21+
my $filename = 'chart_data_labels53.xlsx';
22+
my $dir = 't/regression/';
23+
my $got_filename = $dir . "ewx_$filename";
24+
my $exp_filename = $dir . 'xlsx_files/' . $filename;
25+
26+
my $ignore_members = [];
27+
28+
my $ignore_elements = {};
29+
30+
31+
###############################################################################
32+
#
33+
# Test the creation of a simple Excel::Writer::XLSX file.
34+
#
35+
use Excel::Writer::XLSX;
36+
37+
my $workbook = Excel::Writer::XLSX->new( $got_filename );
38+
my $worksheet = $workbook->add_worksheet();
39+
my $chart = $workbook->add_chart( type => 'column', embedded => 1 );
40+
41+
# For testing, copy the randomly generated axis ids in the target xlsx file.
42+
$chart->{_axis_ids} = [ 100378496, 100380032 ];
43+
44+
my $data = [
45+
[ 1, 2, 3, 4, 5 ],
46+
[ 2, 4, 6, 8, 10 ],
47+
[ 3, 6, 9, 12, 15 ],
48+
49+
];
50+
51+
$worksheet->write( 'A1', $data );
52+
53+
$chart->add_series(
54+
values => '=Sheet1!$A$1:$A$5',
55+
data_labels => {
56+
value => 1,
57+
position => 'outside_end',
58+
custom => [ { value => 31 } ]
59+
},
60+
);
61+
62+
$chart->add_series(
63+
values => '=Sheet1!$B$1:$B$5',
64+
data_labels => {
65+
value => 1,
66+
position => 'inside_base',
67+
custom => [ { value => 32, position => 'center' } ]
68+
},
69+
);
70+
71+
$chart->add_series( values => '=Sheet1!$C$1:$C$5' );
72+
73+
$worksheet->insert_chart( 'E9', $chart );
74+
75+
$workbook->close();
76+
77+
78+
###############################################################################
79+
#
80+
# Compare the generated and existing Excel files.
81+
#
82+
83+
my ( $got, $expected, $caption ) = _compare_xlsx_files(
84+
85+
$got_filename,
86+
$exp_filename,
87+
$ignore_members,
88+
$ignore_elements,
89+
);
90+
91+
_is_deep_diff( $got, $expected, $caption );
92+
93+
94+
###############################################################################
95+
#
96+
# Cleanup.
97+
#
98+
unlink $got_filename;
99+
100+
__END__

0 commit comments

Comments
 (0)