+ my $count = @{ $data[0] };
+ $chart_options{'bar_spacing'} =
+ $count > 30 ? 1
+ : $count > 20 ? 2
+ : $count > 10 ? 3
+ : 5
+ ;
+ if ( my $code = $report->LabelValueCode( $columns{'Functions'}[0] ) ) {
+ my %info = %{ $report->ColumnInfo( $columns{'Functions'}[0] ) };
+ $chart_options{'values_format'} = $chart_options{'y_number_format'} = sub {
+ return $code->($report, %info, VALUE => shift );
+ };
+ }
+ $report->GotoFirstItem;
+
+ # normalize min/max values to graph boundaries
+ {
+ my $integer = 1;
+ $integer = 0 for grep $_ ne int $_, $min_value, $max_value;
+
+ $max_value *= $max_value > 0 ? 1.1 : 0.9
+ if $max_value;
+ $min_value *= $min_value > 0 ? 0.9 : 1.1
+ if $min_value;
+
+ if ($integer) {
+ $max_value = int($max_value + ($max_value > 0? 1 : 0) );
+ $min_value = int($min_value + ($min_value < 0? -1 : 0) );
+
+ my $span = abs($max_value - $min_value);
+ $max_value += 5 - ($span % 5);
+ }
+ $chart_options{'y_label_skip'} = 2;
+ $chart_options{'y_tick_number'} = 10;
+ }
+ my $text_size = sub {
+ my ($size, $text) = (@_);
+ my $font_handle = GD::Text::Align->new(
+ $chart->get('graph'), valign => 'top', 'halign' => 'center',
+ );
+ $font_handle->set_font($font, $size);
+ $font_handle->set_text($text);
+ return $font_handle;
+ };
+
+ my $fitter = sub {
+ my %args = @_;
+
+ foreach my $font_size ( @{$args{'sizes'}} ) {
+ my $line_height = $text_size->($font_size, 'Q')->get('height');
+
+ my $keyset_height = $line_height;
+ if ( ref $args{data}->[0] ) {
+ $keyset_height = $text_size->($font_size, join "\n", ('Q')x scalar @{ $args{data}->[0] })
+ ->get('height');
+ }
+
+ my $status = 1;
+ foreach my $e ( @{ $args{data} } ) {
+ $status = $args{'cb'}->(
+ element => $e,
+ size => $font_size,
+ line_height => $line_height,
+ keyset_height => $keyset_height,
+ );
+ last unless $status;
+ }
+ next unless $status;
+
+ return $font_size;
+ }
+ return 0;
+ };
+
+ # try to fit in labels on X axis values, aka key
+ {
+ # we have several labels layouts:
+ # 1) horizontal, one line per label
+ # 2) horizontal, multi-line - doesn't work, GD::Chart bug
+ # 3) vertical, one line
+ # 4) vertical, multi-line
+ my %can = (
+ 'horizontal, one line' => 1,
+ 'vertical, one line' => 1,
+ 'vertical, multi line' => @{$data[0][0]} > 1,
+ );
+
+ my $x_space_for_label = $Width*0.8/($count+1.5);
+ my $y_space_for_label = $Height*0.4;
+
+ my $found_solution = $fitter->(
+ sizes => [12,11,10],
+ data => $data[0],
+ cb => sub {
+ my %args = @_;
+
+ # if horizontal space doesn't allow us to fit one vertical line,
+ # then we need smaller font
+ return 0 if $args{'line_height'} > $x_space_for_label;
+
+ my $width = $text_size->( $args{'size'}, join ' - ', @{ $args{'element'} } )
+ ->get('width');
+
+ if ( $width > $x_space_for_label ) {
+ $can{'horizontal, one line'} = 0;
+ }
+ if ( $width > $y_space_for_label ) {
+ $can{'vertical, one line'} = 0;
+ }
+ if ( $args{'keyset_height'} >= $x_space_for_label ) {
+ $can{'vertical, multi line'} = 0;
+ }
+ if ( $can{'vertical, multi line'} ) {
+ my $width = $text_size->( $args{'size'}, join "\n", @{ $args{'element'} } )
+ ->get('width');
+ if ( $width > $y_space_for_label ) {
+ $can{'vertical, multi line'} = 0;
+ }
+ }
+ return 0 unless grep $_, values %can;
+ return 1;
+ },
+ );
+ if ( $found_solution ) {
+ $chart_options{'x_axis_font'} = [$font, $found_solution];
+
+ if ( $can{'horizontal, one line'} ) {
+ $chart_options{'x_labels_vertical'} = 0;
+ $_ = join ' - ', @$_ foreach @{$data[0]};
+ }
+ elsif ( $can{'vertical, multi line'} ) {
+ $chart_options{'x_labels_vertical'} = 1;
+ $_ = join "\n", @$_ foreach @{$data[0]};
+ }
+ else {
+ $chart_options{'x_labels_vertical'} = 1;
+ $_ = join " - ", @$_ foreach @{$data[0]};
+ }
+ }
+ else {
+ my $font_handle = $text_size->(10, 'Q');
+ my $line_height = $font_handle->get('height');
+ if ( $line_height > $x_space_for_label ) {
+ $Width *= $line_height/$x_space_for_label;
+ $Width = int( $Width+1 );
+ }
+
+ $_ = join " - ", @$_ foreach @{$data[0]};
+
+ my $max_text_width = 0;
+ foreach (@{$data[0]}) {
+ $font_handle->set_text($_);
+ my $width = $font_handle->get('width');
+ $max_text_width = $width if $width > $max_text_width;
+ }
+ if ( $max_text_width > $Height*0.4 ) {
+ $Height = int($max_text_width / 0.4 + 1);
+ }
+
+ $chart_options{'x_labels_vertical'} = 1;
+ $chart_options{'x_axis_font'} = [$font, 10];
+ }
+ }
+
+ # use the same size for y axis labels
+ {
+ $chart_options{'y_axis_font'} = $chart_options{'x_axis_font'};
+ }
+
+ # try to fit in values above bars
+ {
+ # 0.8 is guess, labels for ticks on Y axis can be wider
+ # 1.5 for paddings around bars that GD::Graph adds
+ my $x_space_for_label = $Width*0.8/($count*(@data - 1)+1.5);
+
+ my %can = (
+ 'horizontal, one line' => 1,
+ 'vertical, one line' => 1,
+ );
+
+ my %seen;
+ my $found_solution = $fitter->(
+ sizes => [ grep $_ <= $chart_options{'x_axis_font'}[1], 12, 11, 10, 9 ],
+ data => [ map {@$_} @data[1..(@data-1)] ],
+ cb => sub {
+ my %args = @_;
+
+ # if horizontal space doesn't allow us to fit one vertical line,
+ # then we need smaller font
+ return 0 if $args{'line_height'} > $x_space_for_label;
+
+ my $value = $args{'element'};
+ $value = $chart_options{'values_format'}->($value)
+ if $chart_options{'values_format'};
+ return 1 if $seen{$value}++;
+
+ my $width = $text_size->( $args{'size'}, $value )->get('width');
+ if ( $width > $x_space_for_label ) {
+ $can{'horizontal, one line'} = 0;
+ }
+ my $y_space_for_label;
+ if ($max_value == $min_value) {
+ $y_space_for_label = 0;
+ }
+ else {
+ $y_space_for_label = $Height * 0.6
+ *( 1 - ($args{'element'}-$min_value)/($max_value-$min_value) );
+ }
+ if ( $width > $y_space_for_label ) {
+ $can{'vertical, one line'} = 0;
+ }
+ return 0 unless grep $_, values %can;
+ return 1;
+ },
+ );
+ $chart_options{'show_values'} = 1;
+ $chart_options{'hide_overlapping_values'} = 1;
+ if ( $found_solution ) {
+ $chart_options{'values_font'} = [ $font, $found_solution ],
+ $chart_options{'values_space'} = 2;
+ $chart_options{'values_vertical'} =
+ $can{'horizontal, one line'} ? 0 : 1;
+ } else {
+ $chart_options{'values_font'} = [ $font, 9 ],
+ $chart_options{'values_space'} = 1;
+ $chart_options{'values_vertical'} = 1;
+ }
+ }
+
+ %chart_options = (
+ %chart_options,
+ x_label => join( ' - ', map $report->Label( $_ ), @{ $columns{'Groups'} } ),