=head1 SYNOPSIS
- use Imager::Graph::SubClass;
- my $chart = Imager::Graph::SubClass->new;
+ use Imager::Graph::Sub_class;
+ my $chart = Imager::Graph::Sub_class->new;
my $img = $chart->draw(data=> \@data, ...)
or die $chart->error;
+ $img->write(file => 'image.png');
=head1 DESCRIPTION
=cut
use strict;
-use warnings;
use vars qw($VERSION);
use Imager qw(:handy);
use Imager::Fountain;
-$VERSION = '0.06';
+$VERSION = '0.07';
# the maximum recursion depth in determining a color, fill or number
use constant MAX_DEPTH => 10;
bless {}, $_[0];
}
+=item set_graph_size($size)
+
+Sets the size of the graph (in pixels) within the image. The size of the image defaults to 1.5 * $graph_size.
+
+=cut
+
+sub set_graph_size {
+ $_[0]->{'custom_style'}->{'size'} = $_[1];
+}
+
+=item set_image_width($width)
+
+Sets the width of the image in pixels.
+
+=cut
+
+sub set_image_width {
+ $_[0]->{'custom_style'}->{'width'} = $_[1];
+}
+
+=item set_image_height($height)
+
+Sets the height of the image in pixels.
+
+=cut
+
+sub set_image_height {
+ $_[0]->{'custom_style'}->{'height'} = $_[1];
+}
+
+=item add_data_series([8, 6, 7, 5, 3, 0, 9], 'Series Name');
+
+Adds a data series to the graph. For L<Imager::Graph::Pie>, only one data series can be added.
+
+=cut
+
+sub add_data_series {
+ my $self = shift;
+ my $data_ref = shift;
+ my $series_name = shift;
+
+ my $graph_data = $self->{'graph_data'} || [];
+
+ push @$graph_data, { data => $data_ref, series_name => $series_name };
+ if (defined $series_name) {
+ push @{$self->{'labels'}}, $series_name;
+ }
+
+ $self->{'graph_data'} = $graph_data;
+ return;
+}
+
+sub _get_data_series {
+ my ($self, $opts) = @_;
+
+ # return the data supplied to draw() if any.
+ if ($opts->{data}) {
+ # one or multiple series?
+ my $data = $opts->{data};
+ if (@$data && ref $data->[0] && ref $data->[0] =~ /ARRAY/) {
+ return $data;
+ }
+ else {
+ return [ { data => $data } ];
+ }
+ }
+
+ return $self->{'graph_data'};
+}
+
+=item set_labels(['label1', 'label2' ... ])
+
+Labels the specific data points. For line/bar graphs, this is the x-axis. For pie graphs, it is the label for the wedges.
+
+=cut
+
+sub set_labels {
+ $_[0]->{'labels'} = $_[1];
+}
+
+sub _get_labels {
+ my ($self, $opts) = @_;
+
+ $opts->{labels}
+ and return $opts->{labels};
+
+ return $_[0]->{'labels'}
+}
+
+=item set_title($title)
+
+Sets the title of the graph. Requires setting a font.
+
+=cut
+
+sub set_title {
+ $_[0]->{'custom_style'}->{'title'}->{'text'} = $_[1];
+}
+
+=item set_font($font)
+
+Sets the font to use for text. Takes an L<Imager::Font> object.
+
+=cut
+
+sub set_font {
+ $_[0]->{'custom_style'}->{'font'} = $_[1];
+}
+
+=item set_style($style_name)
+
+Sets the style to be used for the graph. Imager::Graph comes with several pre-defined styles: fount_lin (default), fount_rad, mono, primary_red, and primary.
+
+=cut
+
+sub set_style {
+ $_[0]->{'style'} = $_[1];
+}
+
+sub _get_style {
+ my ($self, $opts) = @_;
+
+ $opts->{style}
+ and return $opts->{style};
+
+ return $self->{'style'};
+}
+
=item error
-Returns an error message. Only value if the draw() method returns false.
+Returns an error message. Only valid if the draw() method returns false.
=cut
Creates a new image, draws the chart onto that image and returns it.
-Typically you will need to supply a C<data> parameter in the format
+Optionally, instead of using the api methods to configure your chart,
+you can supply a C<data> parameter in the format
required by that particular graph, and if your graph will use any
text, a C<font> parameter
=back
+=head1 Style API
+
+To set or override styles, you can use the following methods:
+
+=over 4
+
+=item set_image_background
+
+=cut
+
+sub set_image_background {
+ $_[0]->{'custom_style'}->{'back'} = $_[1];
+}
+
+=item set_channels
+
+=cut
+
+sub set_channels {
+ $_[0]->{'custom_style'}->{'channels'} = $_[1];
+}
+
+=item set_line_color
+
+=cut
+
+sub set_line_color {
+ $_[0]->{'custom_style'}->{'line'} = $_[1];
+}
+
+=item set_title_font_size
+
+=cut
+
+sub set_title_font_size {
+ $_[0]->{'custom_style'}->{'title'}->{'size'} = $_[1];
+}
+
+=item set_title_font_color
+
+=cut
+
+sub set_title_font_color {
+ $_[0]->{'custom_style'}->{'title'}->{'color'} = $_[1];
+}
+
+=item set_title_horizontal_align
+
+=cut
+
+sub set_title_horizontal_align {
+ $_[0]->{'custom_style'}->{'title'}->{'halign'} = $_[1];
+}
+
+=item set_title_vertical_align
+
+=cut
+
+sub set_title_vertical_align {
+ $_[0]->{'custom_style'}->{'title'}->{'valign'} = $_[1];
+}
+
+=item set_text_font_color
+
+=cut
+
+sub set_text_font_color {
+ $_[0]->{'custom_style'}->{'text'}->{'color'} = $_[1];
+}
+
+=item set_text_font_size
+
+=cut
+
+sub set_text_font_size {
+ $_[0]->{'custom_style'}->{'text'}->{'size'} = $_[1];
+}
+
+=item set_graph_background_color
+
+=cut
+
+sub set_graph_background_color {
+ $_[0]->{'custom_style'}->{'bg'} = $_[1];
+}
+
+=item set_graph_foreground_color
+
+=cut
+
+sub set_graph_foreground_color {
+ $_[0]->{'custom_style'}->{'fg'} = $_[1];
+}
+
+=item set_legend_font_color
+
+=cut
+
+sub set_legend_font_color {
+ $_[0]->{'custom_style'}->{'legend'}->{'color'} = $_[1];
+}
+
+=item set_legend_font
+
+=cut
+
+sub set_legend_font {
+ $_[0]->{'custom_style'}->{'legend'}->{'font'} = $_[1];
+}
+
+=item set_legend_font_size
+
+=cut
+
+sub set_legend_font_size {
+ $_[0]->{'custom_style'}->{'legend'}->{'size'} = $_[1];
+}
+
+=item set_legend_patch_size
+
+=cut
+
+sub set_legend_patch_size {
+ $_[0]->{'custom_style'}->{'legend'}->{'patchsize'} = $_[1];
+}
+
+=item set_legend_patch_gap
+
+=cut
+
+sub set_legend_patch_gap {
+ $_[0]->{'custom_style'}->{'legend'}->{'patchgap'} = $_[1];
+}
+
+=item set_legend_horizontal_align
+
+=cut
+
+sub set_legend_horizontal_align {
+ $_[0]->{'custom_style'}->{'legend'}->{'halign'} = $_[1];
+}
+
+=item set_legend_vertical_align
+
+=cut
+
+sub set_legend_vertical_align {
+ $_[0]->{'custom_style'}->{'legend'}->{'valign'} = $_[1];
+}
+
+=item set_legend_padding
+
+=cut
+
+sub set_legend_padding {
+ $_[0]->{'custom_style'}->{'legend'}->{'padding'} = $_[1];
+}
+
+=item set_legend_outside_padding
+
+=cut
+
+sub set_legend_outside_padding {
+ $_[0]->{'custom_style'}->{'legend'}->{'outsidepadding'} = $_[1];
+}
+
+=item set_legend_fill
+
+=cut
+
+sub set_legend_fill {
+ $_[0]->{'custom_style'}->{'legend'}->{'fill'} = $_[1];
+}
+
+=item set_legend_border
+
+=cut
+
+sub set_legend_border {
+ $_[0]->{'custom_style'}->{'legend'}->{'border'} = $_[1];
+}
+
+=item set_legend_orientation
+
+=cut
+
+sub set_legend_orientation {
+ $_[0]->{'custom_style'}->{'legend'}->{'orientation'} = $_[1];
+}
+
+=item set_callout_font_color
+
+=cut
+
+sub set_callout_font_color {
+ $_[0]->{'custom_style'}->{'callout'}->{'color'} = $_[1];
+}
+
+=item set_callout_font
+
+=cut
+
+sub set_callout_font {
+ $_[0]->{'custom_style'}->{'callout'}->{'font'} = $_[1];
+}
+
+=item set_callout_font_size
+
+=cut
+
+sub set_callout_font_size {
+ $_[0]->{'custom_style'}->{'callout'}->{'size'} = $_[1];
+}
+
+=item set_callout_line_color
+
+=cut
+
+sub set_callout_line_color {
+ $_[0]->{'custom_style'}->{'callout'}->{'line'} = $_[1];
+}
+
+=item set_callout_leader_inside_length
+
+=cut
+
+sub set_callout_leader_inside_length {
+ $_[0]->{'custom_style'}->{'callout'}->{'inside'} = $_[1];
+}
+
+=item set_callout_leader_outside_length
+
+=cut
+
+sub set_callout_leader_outside_length {
+ $_[0]->{'custom_style'}->{'callout'}->{'outside'} = $_[1];
+}
+
+=item set_callout_leader_length
+
+=cut
+
+sub set_callout_leader_length {
+ $_[0]->{'custom_style'}->{'callout'}->{'leadlen'} = $_[1];
+}
+
+=item set_callout_gap
+
+=cut
+
+sub set_callout_gap {
+ $_[0]->{'custom_style'}->{'callout'}->{'gap'} = $_[1];
+}
+
+=item set_label_font_color
+
+=cut
+
+sub set_label_font_color {
+ $_[0]->{'custom_style'}->{'label'}->{'color'} = $_[1];
+}
+
+=item set_label_font
+
+=cut
+
+sub set_label_font {
+ $_[0]->{'custom_style'}->{'label'}->{'font'} = $_[1];
+}
+
+=item set_label_font_size
+
+=cut
+
+sub set_label_font_size {
+ $_[0]->{'custom_style'}->{'label'}->{'size'} = $_[1];
+}
+
+=item set_drop_shadow_fill_color
+
+=cut
+
+sub set_drop_shadow_fill_color {
+ $_[0]->{'custom_style'}->{'dropshadow'}->{'fill'} = $_[1];
+}
+
+=item set_drop_shadow_offset
+
+=cut
+
+sub set_drop_shadow_offset {
+ $_[0]->{'custom_style'}->{'dropshadow'}->{'off'} = $_[1];
+}
+
+=item set_drop_shadowXOffset
+
+=cut
+
+sub set_drop_shadowXOffset {
+ $_[0]->{'custom_style'}->{'dropshadow'}->{'offx'} = $_[1];
+}
+
+=item set_drop_shadowYOffset
+
+=cut
+
+sub set_drop_shadowYOffset {
+ $_[0]->{'custom_style'}->{'dropshadow'}->{'offy'} = $_[1];
+}
+
+=item set_drop_shadow_filter
+
+=cut
+
+sub set_drop_shadow_filter {
+ $_[0]->{'custom_style'}->{'dropshadow'}->{'filter'} = $_[1];
+}
+
+=item set_outline_color
+
+=cut
+
+sub set_outline_color {
+ $_[0]->{'custom_style'}->{'outline'}->{'line'} = $_[1];
+}
+
+=item set_data_area_fills
+
+=cut
+
+sub set_data_area_fills {
+ $_[0]->{'custom_style'}->{'fills'} = $_[1];
+}
+
+=item set_data_line_colors
+
+=cut
+
+sub set_data_line_colors {
+ $_[0]->{'custom_style'}->{'colors'} = $_[1];
+}
+
+=back
+
=head1 FEATURES
Each graph type has a number of features. These are used to add
various items that are displayed in the graph area. Some common
-features are:
+methods are:
+
+=over
+
+=item show_legend()
+
+adds a box containing boxes filled with the data filess, with
+the labels provided to the draw method. The legend will only be
+displayed if both the legend feature is enabled and labels are
+supplied.
+
+=cut
+
+sub show_legend {
+ $_[0]->{'custom_style'}->{'features'}->{'legend'} = 1;
+}
+
+=item show_outline()
+
+draws a border around the data areas.
+
+=cut
+
+sub show_outline {
+ $_[0]->{'custom_style'}->{'features'}->{'outline'} = 1;
+}
+
+=item show_labels()
+
+labels each data fill, usually by including text inside the data fill.
+If the text does not fit in the fill, they could be displayed in some
+other form, eg. as callouts in a pie graph. There usually isn't much
+point in including both labels and a legend.
+
+=cut
+
+sub show_labels {
+ $_[0]->{'custom_style'}->{'features'}->{'labels'} = 1;
+}
+
+=item show_drop_shadow()
+
+a simple drop shadow is shown behind some of the graph elements.
+
+=cut
+
+sub show_drop_shadow {
+ $_[0]->{'custom_style'}->{'features'}->{'dropshadow'} = 1;
+}
+
+=item reset_features()
+
+Unsets all of the features
+
+=cut
+
+sub reset_features {
+ $_[0]->{'custom_style'}->{'features'} = {};
+ $_[0]->{'custom_style'}->{'features'}->{'reset'} = 1;
+}
+
+=back
+
+Additionally, features can be set by passing them into the draw() method:
=over
default style information contains:
text=>{
- color=>'lookup(fg)',
+ color=>'lookup(fg)',
...
},
legend =>{
- color=>'lookup(text.color)',
+ color=>'lookup(text.color)',
...
},
(
back=> 'lookup(bg)',
line=> 'lookup(fg)',
+ aa => 1,
text=>{
- color => 'lookup(fg)',
+ color => 'lookup(fg)',
font => 'lookup(font)',
- size => 14,
- },
+ size => 14,
+ aa => 'lookup(aa)',
+ },
title=>{
- color => 'lookup(text.color)',
+ color => 'lookup(text.color)',
font => 'lookup(text.font)',
- halign => 'center',
- valign => 'top',
- size => 'scale(text.size,2.0)',
- },
+ halign => 'center',
+ valign => 'top',
+ size => 'scale(text.size,2.0)',
+ aa => 'lookup(text.aa)',
+ },
legend =>{
- color => 'lookup(text.color)',
+ color => 'lookup(text.color)',
font => 'lookup(text.font)',
- size => 'lookup(text.size)',
- patchsize => 'scale(legend.size,0.9)',
- patchgap => 'scale(legend.patchsize,0.3)',
- patchborder => 'lookup(line)',
- halign => 'right',
- valign => 'top',
- padding => 'scale(legend.size,0.3)',
- outsidepadding => 'scale(legend.padding,0.4)',
- },
+ aa => 'lookup(text.aa)',
+ size => 'lookup(text.size)',
+ patchsize => 'scale(legend.size,0.9)',
+ patchgap => 'scale(legend.patchsize,0.3)',
+ patchborder => 'lookup(line)',
+ halign => 'right',
+ valign => 'top',
+ padding => 'scale(legend.size,0.3)',
+ outsidepadding => 'scale(legend.padding,0.4)',
+ },
callout => {
- color => 'lookup(text.color)',
+ color => 'lookup(text.color)',
font => 'lookup(text.font)',
- size => 'lookup(text.size)',
- line => 'lookup(line)',
- inside => 'lookup(callout.size)',
- outside => 'lookup(callout.size)',
- leadlen => 'scale(0.8,callout.size)',
- gap => 'scale(callout.size,0.3)',
- },
+ size => 'lookup(text.size)',
+ line => 'lookup(line)',
+ inside => 'lookup(callout.size)',
+ outside => 'lookup(callout.size)',
+ leadlen => 'scale(0.8,callout.size)',
+ gap => 'scale(callout.size,0.3)',
+ aa => 'lookup(text.aa)',
+ lineaa => 'lookup(lineaa)',
+ },
label => {
font => 'lookup(text.font)',
- size => 'lookup(text.size)',
- color => 'lookup(text.color)',
+ size => 'lookup(text.size)',
+ color => 'lookup(text.color)',
hpad => 'lookup(label.pad)',
vpad => 'lookup(label.pad)',
pad => 'scale(label.size,0.2)',
pcformat => sub { sprintf "%s (%.0f%%)", $_[0], $_[1] },
pconlyformat => sub { sprintf "%.1f%%", $_[0] },
- },
+ aa => 'lookup(text.aa)',
+ lineaa => 'lookup(lineaa)',
+ },
dropshadow => {
fill => { solid => Imager::Color->new(0, 0, 0, 96) },
off => 'scale(0.4,text.size)',
},
outline => {
line =>'lookup(line)',
+ lineaa => 'lookup(lineaa)',
},
size=>256,
width=>'scale(1.5,size)',
height=>'lookup(size)',
+
+ # yes, the handling of fill and line AA is inconsistent, lack of
+ # forethought, unfortunately
+ fill => {
+ aa => 'lookup(aa)',
+ },
+ lineaa => 'lookup(aa)',
);
=item _error($message)
depending on context. Should be used for error handling, since it may
provide some user hooks at some point.
+The intended usage is:
+
+ some action
+ or return $self->_error("error description");
+
+You should almost always return the result of _error() or return
+immediately afterwards.
+
=cut
sub _error {
qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
],
fg=>'000000',
+ negative_bg=>'EEEEEE',
bg=>'E0E0E0',
legend=>
{
qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
],
fg=>'000000',
+ negative_bg=>'EEEEEE',
bg=>'C08080',
legend=>
{
channels=>1,
bg=>'FFFFFF',
fg=>'000000',
+ negative_bg=>'EEEEEE',
features=>{ outline=>1 },
pie =>{
blur=>undef,
},
+ aa => 0,
},
fount_lin =>
{
{ fountain=>'linear',
xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('FFC0C0'), NC('FF0000') ]),
+ colors=>[ NC('FFC0C0'), NC('FF0000') ]),
},
{ fountain=>'linear',
xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('C0FFC0'), NC('00FF00') ]),
+ colors=>[ NC('C0FFC0'), NC('00FF00') ]),
},
{ fountain=>'linear',
xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('C0C0FF'), NC('0000FF') ]),
+ colors=>[ NC('C0C0FF'), NC('0000FF') ]),
},
{ fountain=>'linear',
xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
+ colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
},
{ fountain=>'linear',
xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
+ colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
},
{ fountain=>'linear',
xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
+ colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
},
],
+ colors => [
+ qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
+ ],
+ line_markers =>[
+ { shape => 'circle', radius => 4 },
+ { shape => 'square', radius => 4 },
+ { shape => 'diamond', radius => 4 },
+ { shape => 'triangle', radius => 4 },
+ { shape => 'x', radius => 4 },
+ { shape => 'plus', radius => 4 },
+ ],
back=>{ fountain=>'linear',
xa_ratio=>0, ya_ratio=>0,
xb_ratio=>1.0, yb_ratio=>1.0,
( positions=>[0, 1],
colors=>[ NC('6060FF'), NC('60FF60') ]) },
fg=>'000000',
+ negative_bg=>'EEEEEE',
bg=>'FFFFFF',
features=>{ dropshadow=>1 },
},
{ fountain=>'radial',
xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('FF8080'), NC('FF0000') ]),
+ colors=>[ NC('FF8080'), NC('FF0000') ]),
},
{ fountain=>'radial',
xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('80FF80'), NC('00FF00') ]),
+ colors=>[ NC('80FF80'), NC('00FF00') ]),
},
{ fountain=>'radial',
xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('808080FF'), NC('0000FF') ]),
+ colors=>[ NC('808080FF'), NC('0000FF') ]),
},
{ fountain=>'radial',
xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('FFFF80'), NC('FFFF00') ]),
+ colors=>[ NC('FFFF80'), NC('FFFF00') ]),
},
{ fountain=>'radial',
xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('80FFFF'), NC('00FFFF') ]),
+ colors=>[ NC('80FFFF'), NC('00FFFF') ]),
},
{ fountain=>'radial',
xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
segments => Imager::Fountain->simple(positions=>[0, 1],
- colors=>[ NC('FF80FF'), NC('FF00FF') ]),
+ colors=>[ NC('FF80FF'), NC('FF00FF') ]),
},
],
+ colors => [
+ qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
+ ],
back=>{ fountain=>'linear',
xa_ratio=>0, ya_ratio=>0,
xb_ratio=>1.0, yb_ratio=>1.0,
( positions=>[0, 1],
colors=>[ NC('6060FF'), NC('60FF60') ]) },
fg=>'000000',
+ negative_bg=>'EEEEEE',
bg=>'FFFFFF',
}
);
+$styles{'ocean'} = {
+ fills => [
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('EFEDCF'), NC('E6E2AF') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('DCD7AB'), NC('A7A37E') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('B2E5D4'), NC('80B4A2') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('7aaab9'), NC('046380') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('c3b8e9'), NC('877EA7') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('A3DF9A'), NC('67A35E') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('E19C98'), NC('B4726F') ]),
+ },
+ ],
+ colors => [
+ qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
+ ],
+ fg=>'000000',
+ negative_bg=>'EEEEEE',
+ bg=>'FFFFFF',
+ features=>{ dropshadow=>1 },
+
+};
+
+$styles{'ocean_flat'} = {
+ fills=>
+ [
+ qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
+ ],
+ colors => [
+ qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
+ ],
+ fg=>'000000',
+ negative_bg=>'EEEEEE',
+ bg=>'FFFFFF',
+ features=>{ dropshadow=>1 },
+
+};
+
+
=item $self->_style_setup(\%opts)
Uses the values from %opts to build a customized hash describing the
my ($self, $opts) = @_;
my $style_defs = $self->_style_defs;
my $style;
- $style = $styles{$opts->{style}} if $opts->{style};
+
+ my $pre_def_style = $self->_get_style($opts);
+ my $api_style = $self->{'custom_style'} || {};
+ $style = $styles{$pre_def_style} if $pre_def_style;
+
$style ||= $styles{$def_style};
- my @search_list = ( $style_defs, $style, $opts);
+ my @search_list = ( $style_defs, $style, $api_style, $opts);
my %work;
my @composite = $self->_composite();
}
}
else {
- $work{$key} = $src->{$key};
+ $work{$key} = $src->{$key}
+ if defined $src->{$key}; # $opts with pmichauds new accessor handling
}
}
}
Supports the 'lookup(foo)' mechanism.
+Returns an empty list on failure.
+
=cut
sub _get_thing {
else {
if ($what =~ /^lookup\(([\w.]+)\)$/) {
@depth < MAX_DEPTH
- or return $self->_error("too many levels of recursion in lookup (@depth)");
+ or return $self->_error("too many levels of recursion in lookup (@depth)");
return $self->_get_number($1, @depth);
}
elsif ($what =~ /^scale\(
- ((?:[a-z][\w.]*)|$NUM_RE)
+ ((?:[a-z][\w.]*)|$NUM_RE)
,
- ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
+ ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
my ($left, $right) = ($1, $2);
unless ($left =~ /^$NUM_RE$/) {
- @depth < MAX_DEPTH
- or return $self->_error("too many levels of recursion in scale (@depth)");
- $left = $self->_get_number($left, @depth);
+ @depth < MAX_DEPTH
+ or return $self->_error("too many levels of recursion in scale (@depth)");
+ $left = $self->_get_number($left, @depth);
}
unless ($right =~ /^$NUM_RE$/) {
- @depth < MAX_DEPTH
- or return $self->_error("too many levels of recursion in scale (@depth)");
- $right = $self->_get_number($right, @depth);
+ @depth < MAX_DEPTH
+ or return $self->_error("too many levels of recursion in scale (@depth)");
+ $right = $self->_get_number($right, @depth);
}
return $left * $right;
}
Retrieves an integer from the style. This is a simple wrapper around
_get_number() that rounds the result to an integer.
+Returns an empty list on failure.
+
=cut
sub _get_integer {
Allows the lookup(name) mechanism.
+Returns an empty list on failure.
+
=cut
sub _get_color {
unless (ref $what) {
if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
@depth < MAX_DEPTH or
- return $self->_error("too many levels of recursion in lookup (@depth)");
+ return $self->_error("too many levels of recursion in lookup (@depth)");
return $self->_get_color($1, @depth);
}
This function does the fg and bg initialization for hatched fills, and
translation of *_ratio for fountain fills (using the $box parameter).
+Returns an empty list on failure.
+
=cut
sub _translate_fill {
# default to normal combine mode
my %work = ( combine => 'normal', %$what );
if ($what->{hatch}) {
- if (!$work{fg}) {
- $work{fg} = $self->_get_color('fg')
- or return;
- }
- if (!$work{bg}) {
- $work{bg} = $self->_get_color('bg')
- or return;
- }
- return ( fill=>\%work );
+ if (!$work{fg}) {
+ $work{fg} = $self->_get_color('fg')
+ or return;
+ }
+ if (!$work{bg}) {
+ $work{bg} = $self->_get_color('bg')
+ or return;
+ }
+ return ( fill=>\%work );
}
elsif ($what->{fountain}) {
- for my $key (qw(xa ya xb yb)) {
- if (exists $work{"${key}_ratio"}) {
- if ($key =~ /^x/) {
- $work{$key} = $box->[0] + $work{"${key}_ratio"}
- * ($box->[2] - $box->[0]);
- }
- else {
- $work{$key} = $box->[1] + $work{"${key}_ratio"}
- * ($box->[3] - $box->[1]);
- }
- }
- }
- return ( fill=>\%work );
+ for my $key (qw(xa ya xb yb)) {
+ if (exists $work{"${key}_ratio"}) {
+ if ($key =~ /^x/) {
+ $work{$key} = $box->[0] + $work{"${key}_ratio"}
+ * ($box->[2] - $box->[0]);
+ }
+ else {
+ $work{$key} = $box->[1] + $work{"${key}_ratio"}
+ * ($box->[3] - $box->[1]);
+ }
+ }
+ }
+ return ( fill=>\%work );
}
else {
- return ( fill=> \%work );
+ return ( fill=> \%work );
}
}
}
"data.$index");
}
+sub _data_color {
+ my ($self, $index) = @_;
+
+ my $colors = $self->{'_style'}{'colors'} || [];
+ my $fills = $self->{'_style'}{'fills'} || [];
+
+ # Try to just use a fill, so non-fountain styles don't need
+ # to have a duplicated set of fills and colors
+ my $fill = $fills->[$index % @$fills];
+ if (!ref $fill) {
+ return $fill;
+ }
+
+ if (@$colors) {
+ return $colors->[$index % @$colors] || '000000';
+ }
+ return '000000';
+}
+
=item _get_fill($index, $box)
Retrieves fill parameters for a named fill.
$img;
}
+sub _get_image {
+ my $self = shift;
+
+ if (!$self->{'_image'}) {
+ $self->{'_image'} = $self->_make_img();
+ }
+ return $self->{'_image'};
+}
+
+=item _text_style($name)
+
+Returns parameters suitable for calls to Imager::Font's bounding_box()
+and draw() methods intended for use in defining text styles.
+
+Returns an empty list on failure.
+
+Returns the following attributes: font, color, size, aa, sizew
+(optionally)
+
+=cut
+
sub _text_style {
my ($self, $name) = @_;
$work{size} = $self->_get_number("$name.size");
$work{sizew} = $self->_get_number("$name.sizew")
if $work{sizew};
+ $work{aa} = $self->_get_number("$name.aa");
%work;
}
+=item _text_bbox($text, $name)
+
+Returns a bounding box for the specified $text as styled by $name.
+
+Returns an empty list on failure.
+
+=cut
+
sub _text_bbox {
my ($self, $text, $name) = @_;
or return;
my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
- canon=>1);
+ canon=>1);
return @bbox[0..3];
}
+=item _line_style($name)
+
+Return parameters suitable for calls to Imager's line(), polyline(),
+and box() methods.
+
+For now this returns only color and aa parameters, but future releases
+of Imager may support extra parameters.
+
+=cut
+
+sub _line_style {
+ my ($self, $name) = @_;
+
+ my %line;
+ $line{color} = $self->_get_color("$name.line")
+ or return;
+ $line{aa} = $self->_get_number("$name.lineaa");
+ defined $line{aa} or $line{aa} = $self->_get_number("aa");
+
+ return %line;
+}
+
sub _align_box {
my ($self, $box, $chart_box, $name) = @_;
if ($areay < $areax) {
if ($object_box->[1] - $chart_box->[1]
- < $chart_box->[3] - $object_box->[3]) {
+ < $chart_box->[3] - $object_box->[3]) {
$chart_box->[1] = $object_box->[3];
}
else {
}
else {
if ($object_box->[0] - $chart_box->[0]
- < $chart_box->[2] - $object_box->[2]) {
+ < $chart_box->[2] - $object_box->[2]) {
$chart_box->[0] = $object_box->[2];
}
else {
}
else {
if ($pos + $gap + $entry_width > $chart_box->[2]) {
- $pos = 0;
- $height += $row_height;
+ $pos = 0;
+ $height += $row_height;
}
push @offsets, [ $pos, $height ];
}
ymin=>$box[1]+$outsidepadding,
xmax=>$box[2]-$outsidepadding,
ymax=>$box[3]-$outsidepadding,
- $self->_get_fill('legend.fill', \@box));
+ $self->_get_fill('legend.fill', \@box));
}
$box[0] += $outsidepadding;
$box[1] += $outsidepadding;
my @fill = $self->_data_fill($dataindex, \@patchbox)
or return;
$img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
- ymax=>$top + $patchsize, @fill);
+ ymax=>$top + $patchsize, @fill);
if ($self->{_style}{legend}{patchborder}) {
$img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
- ymax=>$top + $patchsize,
- color=>$patchborder);
+ ymax=>$top + $patchsize,
+ color=>$patchborder);
}
$img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize,
text=>$label);
my $border_color = $self->_get_color('legend.border')
or return;
$img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
- color=>$border_color);
+ color=>$border_color);
}
$self->_remove_box($chart_box, \@box);
1;
}
}
my @box = (0, 0,
- $width + $patchsize + $padding * 2 + $gap,
- $height + $padding * 2 - $gap);
+ $width + $patchsize + $padding * 2 + $gap,
+ $height + $padding * 2 - $gap);
my $outsidepadding = 0;
if ($self->{_style}{legend}{border}) {
defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
ymin=>$box[1]+$outsidepadding,
xmax=>$box[2]-$outsidepadding,
ymax=>$box[3]-$outsidepadding,
- $self->_get_fill('legend.fill', \@box));
+ $self->_get_fill('legend.fill', \@box));
}
$box[0] += $outsidepadding;
$box[1] += $outsidepadding;
for my $label (@$labels) {
my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
$patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
- my @fill = $self->_data_fill($dataindex, \@patchbox)
- or return;
+
+ my @fill;
+ if ($self->_draw_flat_legend()) {
+ @fill = (color => $self->_data_color($dataindex), filled => 1);
+ }
+ else {
+ @fill = $self->_data_fill($dataindex, \@patchbox)
+ or return;
+ }
$img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
- ymax=>$ypos + $patchsize, @fill);
+ ymax=>$ypos + $patchsize, @fill);
if ($self->{_style}{legend}{patchborder}) {
$img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
- ymax=>$ypos + $patchsize,
- color=>$patchborder);
+ ymax=>$ypos + $patchsize,
+ color=>$patchborder);
}
$img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize,
text=>$label);
my $border_color = $self->_get_color('legend.border')
or return;
$img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
- color=>$border_color);
+ color=>$border_color);
}
$self->_remove_box($chart_box, \@box);
1;
}
}
+sub _draw_flat_legend {
+ return 0;
+}
+
=item _composite()
Returns a list of style fields that are stored as composites, and