6 Imager::Graph - Perl extension for producing Graphs using the Imager library.
10 use Imager::Graph::Sub_class;
11 my $chart = Imager::Graph::Sub_class->new;
12 my $img = $chart->draw(data=> \@data, ...)
17 Imager::Graph provides style information to its base classes. It
18 defines the colors, text display information and fills based on both
19 built-in styles and modifications supplied by the user to the draw()
27 use vars qw($VERSION);
28 use Imager qw(:handy);
33 # the maximum recursion depth in determining a color, fill or number
34 use constant MAX_DEPTH => 10;
36 my $NUM_RE = '(?:[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]\d+?)?)';
40 This is a simple constructor. No parameters required.
48 =item set_graph_size($size)
50 Sets the size of the graph (in pixels) within the image. The size of the image defaults to 1.5 * $graph_size.
55 $_[0]->{'custom_style'}->{'size'} = $_[1];
58 =item set_image_width($width)
60 Sets the width of the image in pixels.
65 $_[0]->{'custom_style'}->{'width'} = $_[1];
68 =item set_image_height($height)
70 Sets the height of the image in pixels.
74 sub set_image_height {
75 $_[0]->{'custom_style'}->{'height'} = $_[1];
78 =item add_data_series([8, 6, 7, 5, 3, 0, 9], 'Series Name');
80 Adds a data series to the graph. For L<Imager::Graph::Pie>, only one data series can be added.
87 my $series_name = shift;
89 my $graph_data = $self->{'graph_data'} || [];
91 push @$graph_data, { data => $data_ref, series_name => $series_name };
93 $self->{'graph_data'} = $graph_data;
97 sub _get_data_series {
98 my ($self, $opts) = @_;
100 # return the data supplied to draw() if any.
102 # one or multiple series?
103 my $data = $opts->{data};
104 if (@$data && ref $data->[0] && ref $data->[0] =~ /ARRAY/) {
108 return [ { data => $data } ];
112 return $self->{'graph_data'};
115 =item set_labels(['label1', 'label2' ... ])
117 Labels the specific data points. For line/bar graphs, this is the x-axis. For pie graphs, it is the label for the wedges.
122 $_[0]->{'labels'} = $_[1];
126 my ($self, $opts) = @_;
129 and return $opts->{labels};
131 return $_[0]->{'labels'}
134 =item set_title($title)
136 Sets the title of the graph. Requires setting a font.
141 $_[0]->{'custom_style'}->{'title'}->{'text'} = $_[1];
144 =item set_font($font)
146 Sets the font to use for text. Takes an L<Imager::Font> object.
151 $_[0]->{'custom_style'}->{'font'} = $_[1];
154 =item set_style($style_name)
156 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.
161 $_[0]->{'style'} = $_[1];
165 my ($self, $opts) = @_;
168 and return $opts->{style};
170 return $self->{'style'};
175 Returns an error message. Only valid if the draw() method returns false.
185 Creates a new image, draws the chart onto that image and returns it.
187 Optionally, instead of using the api methods to configure your chart,
188 you can supply a C<data> parameter in the format
189 required by that particular graph, and if your graph will use any
190 text, a C<font> parameter
192 You can also supply many different parameters which control the way
193 the graph looks. These are supplied as keyword, value pairs, where
194 the value can be a hashref containing sub values.
196 The C<style> parameter will selects a basic color set, and possibly
197 sets other related parameters. See L</"STYLES">.
199 my $font = Imager::Font->new(file => 'ImUgly.ttf');
200 my $img = $chart->draw(
204 text => "Hello, World!",
210 When referring to a single sub-value this documentation will refer to
211 'title.color' rather than 'the color element of title'.
213 Returns the graph image on success, or false on failure.
219 The currently defined styles are:
225 a light grey background with no outlines. Uses primary colors for the
230 a light red background with no outlines. Uses primary colors for the
233 Graphs drawn using this style should save well as a gif, even though
234 some graphs may perform a slight blur.
236 This was the default style, but the red was too loud.
240 designed for monochrome output, such as most laser printers, this uses
241 hatched fills for the data, and no colors. The returned image is a
242 one channel image (which can be overridden with the C<channels>
245 You can also override the colors used by all components for background
246 or drawing by supplying C<fg> and/or C<bg> parameters. ie. if you
247 supply C<<fg=>'FF0000', channels=>3>> then the hash fills and anything
248 else will be drawn in red. Another use might be to set a transparent
249 background, by supplying C<<bg=>'00000000', channels=>4>>.
251 This style outlines the legend if present and outlines the hashed fills.
255 designed as a "pretty" style this uses linear fountain fills for the
256 background and data fills, and adds a drop shadow.
258 You can override the value used for text and outlines by setting the
261 This is the default style.
265 also designed as a "pretty" style this uses radial fountain fills for
266 the data and a linear blue to green fill for the background.
272 To set or override styles, you can use the following methods:
276 =item set_image_background
280 sub set_image_background {
281 $_[0]->{'custom_style'}->{'back'} = $_[1];
289 $_[0]->{'custom_style'}->{'channels'} = $_[1];
297 $_[0]->{'custom_style'}->{'line'} = $_[1];
300 =item set_title_font_size
304 sub set_title_font_size {
305 $_[0]->{'custom_style'}->{'title'}->{'size'} = $_[1];
308 =item set_title_font_color
312 sub set_title_font_color {
313 $_[0]->{'custom_style'}->{'title'}->{'color'} = $_[1];
316 =item set_title_horizontal_align
320 sub set_title_horizontal_align {
321 $_[0]->{'custom_style'}->{'title'}->{'halign'} = $_[1];
324 =item set_title_vertical_align
328 sub set_title_vertical_align {
329 $_[0]->{'custom_style'}->{'title'}->{'valign'} = $_[1];
332 =item set_text_font_color
336 sub set_text_font_color {
337 $_[0]->{'custom_style'}->{'text'}->{'color'} = $_[1];
340 =item set_text_font_size
344 sub set_text_font_size {
345 $_[0]->{'custom_style'}->{'text'}->{'size'} = $_[1];
348 =item set_graph_background_color
352 sub set_graph_background_color {
353 $_[0]->{'custom_style'}->{'bg'} = $_[1];
356 =item set_graph_foreground_color
360 sub set_graph_foreground_color {
361 $_[0]->{'custom_style'}->{'fg'} = $_[1];
364 =item set_legend_font_color
368 sub set_legend_font_color {
369 $_[0]->{'custom_style'}->{'legend'}->{'color'} = $_[1];
372 =item set_legend_font
376 sub set_legend_font {
377 $_[0]->{'custom_style'}->{'legend'}->{'font'} = $_[1];
380 =item set_legend_font_size
384 sub set_legend_font_size {
385 $_[0]->{'custom_style'}->{'legend'}->{'size'} = $_[1];
388 =item set_legend_patch_size
392 sub set_legend_patch_size {
393 $_[0]->{'custom_style'}->{'legend'}->{'patchsize'} = $_[1];
396 =item set_legend_patch_gap
400 sub set_legend_patch_gap {
401 $_[0]->{'custom_style'}->{'legend'}->{'patchgap'} = $_[1];
404 =item set_legend_horizontal_align
408 sub set_legend_horizontal_align {
409 $_[0]->{'custom_style'}->{'legend'}->{'halign'} = $_[1];
412 =item set_legend_vertical_align
416 sub set_legend_vertical_align {
417 $_[0]->{'custom_style'}->{'legend'}->{'valign'} = $_[1];
420 =item set_legend_padding
424 sub set_legend_padding {
425 $_[0]->{'custom_style'}->{'legend'}->{'padding'} = $_[1];
428 =item set_legend_outside_padding
432 sub set_legend_outside_padding {
433 $_[0]->{'custom_style'}->{'legend'}->{'outsidepadding'} = $_[1];
436 =item set_legend_fill
440 sub set_legend_fill {
441 $_[0]->{'custom_style'}->{'legend'}->{'fill'} = $_[1];
444 =item set_legend_border
448 sub set_legend_border {
449 $_[0]->{'custom_style'}->{'legend'}->{'border'} = $_[1];
452 =item set_legend_orientation
456 sub set_legend_orientation {
457 $_[0]->{'custom_style'}->{'legend'}->{'orientation'} = $_[1];
460 =item set_callout_font_color
464 sub set_callout_font_color {
465 $_[0]->{'custom_style'}->{'callout'}->{'color'} = $_[1];
468 =item set_callout_font
472 sub set_callout_font {
473 $_[0]->{'custom_style'}->{'callout'}->{'font'} = $_[1];
476 =item set_callout_font_size
480 sub set_callout_font_size {
481 $_[0]->{'custom_style'}->{'callout'}->{'size'} = $_[1];
484 =item set_callout_line_color
488 sub set_callout_line_color {
489 $_[0]->{'custom_style'}->{'callout'}->{'line'} = $_[1];
492 =item set_callout_leader_inside_length
496 sub set_callout_leader_inside_length {
497 $_[0]->{'custom_style'}->{'callout'}->{'inside'} = $_[1];
500 =item set_callout_leader_outside_length
504 sub set_callout_leader_outside_length {
505 $_[0]->{'custom_style'}->{'callout'}->{'outside'} = $_[1];
508 =item set_callout_leader_length
512 sub set_callout_leader_length {
513 $_[0]->{'custom_style'}->{'callout'}->{'leadlen'} = $_[1];
516 =item set_callout_gap
520 sub set_callout_gap {
521 $_[0]->{'custom_style'}->{'callout'}->{'gap'} = $_[1];
524 =item set_label_font_color
528 sub set_label_font_color {
529 $_[0]->{'custom_style'}->{'label'}->{'color'} = $_[1];
537 $_[0]->{'custom_style'}->{'label'}->{'font'} = $_[1];
540 =item set_label_font_size
544 sub set_label_font_size {
545 $_[0]->{'custom_style'}->{'label'}->{'size'} = $_[1];
548 =item set_drop_shadow_fill_color
552 sub set_drop_shadow_fill_color {
553 $_[0]->{'custom_style'}->{'dropshadow'}->{'fill'} = $_[1];
556 =item set_drop_shadow_offset
560 sub set_drop_shadow_offset {
561 $_[0]->{'custom_style'}->{'dropshadow'}->{'off'} = $_[1];
564 =item set_drop_shadowXOffset
568 sub set_drop_shadowXOffset {
569 $_[0]->{'custom_style'}->{'dropshadow'}->{'offx'} = $_[1];
572 =item set_drop_shadowYOffset
576 sub set_drop_shadowYOffset {
577 $_[0]->{'custom_style'}->{'dropshadow'}->{'offy'} = $_[1];
580 =item set_drop_shadow_filter
584 sub set_drop_shadow_filter {
585 $_[0]->{'custom_style'}->{'dropshadow'}->{'filter'} = $_[1];
588 =item set_outline_color
592 sub set_outline_color {
593 $_[0]->{'custom_style'}->{'outline'}->{'line'} = $_[1];
596 =item set_data_area_fills
600 sub set_data_area_fills {
601 $_[0]->{'custom_style'}->{'fills'} = $_[1];
604 =item set_data_line_colors
608 sub set_data_line_colors {
609 $_[0]->{'custom_style'}->{'colors'} = $_[1];
616 Each graph type has a number of features. These are used to add
617 various items that are displayed in the graph area. Some common
624 adds a box containing boxes filled with the data filess, with
625 the labels provided to the draw method. The legend will only be
626 displayed if both the legend feature is enabled and labels are
632 $_[0]->{'custom_style'}->{'features'}->{'legend'} = 1;
637 draws a border around the data areas.
642 $_[0]->{'custom_style'}->{'features'}->{'outline'} = 1;
647 labels each data fill, usually by including text inside the data fill.
648 If the text does not fit in the fill, they could be displayed in some
649 other form, eg. as callouts in a pie graph. There usually isn't much
650 point in including both labels and a legend.
655 $_[0]->{'custom_style'}->{'features'}->{'labels'} = 1;
658 =item show_drop_shadow()
660 a simple drop shadow is shown behind some of the graph elements.
664 sub show_drop_shadow {
665 $_[0]->{'custom_style'}->{'features'}->{'dropshadow'} = 1;
668 =item reset_features()
670 Unsets all of the features
675 $_[0]->{'custom_style'}->{'features'} = {};
676 $_[0]->{'custom_style'}->{'features'}->{'reset'} = 1;
681 Additionally, features can be set by passing them into the draw() method:
687 adds a box containing boxes filled with the data filess, with
688 the labels provided to the draw method. The legend will only be
689 displayed if both the legend feature is enabled and labels are
694 labels each data fill, usually by including text inside the data fill.
695 If the text does not fit in the fill, they could be displayed in some
696 other form, eg. as callouts in a pie graph. There usually isn't much
697 point in including both labels and a legend.
701 a simple drop shadow is shown behind some of the graph elements.
705 Each graph also has features specific to that graph.
707 =head1 COMMON PARAMETERS
709 When referring to a single sub-value this documentation will refer to
710 'title.color' rather than 'the color element of title'.
712 Normally, except for the font parameter, these are controlled by
713 styles, but these are the style parameters I'd mostly likely expect
720 the Imager font object used to draw text on the chart.
724 the background fill for the graph. Default depends on the style.
728 the base size of the graph image. Default: 256
732 the width of the graph image. Default: 1.5 * size (384)
736 the height of the graph image. Default: size (256)
740 the number of channels in the image. Default: 3 (the 'mono' style
745 the color used for drawing lines, such as outlines or callouts.
746 Default depends on the current style. Set to undef to remove the
747 outline from a style.
751 the text used for a graph title. Default: no title. Note: this is
752 the same as the title=>{ text => ... } field.
758 horizontal alignment of the title in the graph, one of 'left',
759 'center' or 'right'. Default: center
763 vertical alignment of the title, one of 'top', 'center' or 'right'.
764 Default: top. It's probably a bad idea to set this to 'center' unless
765 you have a very short title.
771 This contains basic defaults used in drawing text.
777 the default color used for all text, defaults to the fg color.
781 the base size used for text, also used to scale many graph elements.
790 In most cases you will want to use just the styles, but you may want
791 to exert more control over the way your chart looks. This section
792 describes the options you can use to control the way your chart looks.
794 Hopefully you don't need to read this.
800 The background of the graph.
806 Used to define basic background and foreground colors for the graph.
807 The bg color may be used for the background of the graph, and is used
808 as a default for the background of hatcheed fills. The fg is used as
809 the default for line and text colors.
813 The default font used by the graph. Normally you should supply this
814 if your graph as any text.
818 The default line color.
822 defaults for drawing text. Other textual graph elements will inherit
823 or modify these values.
829 default text color, defaults to the I<fg> color.
833 default text size. Default: 14. This is used to scale many graph
834 elements, including padding and leader sizes. Other text elements
835 will either use or scale this value.
839 default font object. Inherited from I<font>, which should have been
840 supplied by the caller.
846 If you supply a scalar value for this element, it will be stored in
849 Defines the text, font and layout information for the title.
855 The color of the title, inherited from I<text.color>.
859 The font object used for the title, inherited from I<text.font>.
863 size of the title text. Default: double I<text.size>
869 The horizontal and vertical alignment of the title.
875 defines attributes of the graph legend, if present.
885 text attributes for the labels used in the legend.
889 the width and height of the color patch in the legend. Defaults to
890 90% of the legend text size.
894 the minimum gap between patches in pixels. Defaults to 30% of the
899 the color of the border drawn around each patch. Inherited from I<line>.
905 the horizontal and vertical alignment of the legend within the graph.
906 Defaults to 'right' and 'top'.
910 the gap between the legend patches and text and the outside of it's
911 box, or to the legend border, if any.
915 the gap between the border and the outside of the legend's box. This
916 is only used if the I<legend.border> attribute is defined.
920 the background fill for the legend. Default: none
924 the border color of the legend. Default: none (no border is drawn
929 The orientation of the legend. If this is C<vertical> the the patches
930 and labels are stacked on top of each other. If this is C<horizontal>
931 the patchs and labels are word wrapped across the image. Default:
936 For example to create a horizontal legend with borderless patches,
937 darker than the background, you might do:
939 my $im = $chart->draw
943 patchborder => undef,
944 orientation => 'horizontal',
945 fill => { solid => Imager::Color->new(0, 0, 0, 32), }
951 defines attributes for graph callouts, if any are present. eg. if the
952 pie graph cannot fit the label into the pie graph segement it will
953 present it as a callout.
963 the text attributes of the callout label. Inherited from I<text>.
967 the color of the callout lines. Inherited from I<line>
973 the length of the leader on the inside and the outside of the fill,
974 usually at some angle. Both default to the size of the callout text.
978 the length of the horizontal portion of the leader. Default:
983 the gap between the callout leader and the callout text. Defaults to
984 30% of the text callout size.
990 defines attributes for labels drawn into the data areas of a graph.
1000 The text attributes of the labels. Inherited from I<text>.
1006 the attributes of the graph's drop shadow
1012 the fill used for the drop shadow. Default: '404040' (dark gray)
1016 the offset of the drop shadow. A convenience value inherited by offx
1017 and offy. Default: 40% of I<text.size>.
1023 the horizontal and vertical offsets of the drop shadow. Both
1024 inherited from I<dropshadow.off>.
1028 the filter description passed to Imager's filter method to blur the
1029 drop shadow. Default: an 11 element convolution filter.
1035 describes the lines drawn around filled data areas, such as the
1036 segments of a pie chart.
1042 the line color of the outlines, inherited from I<line>.
1048 a reference to an array containing fills for each data item.
1050 You can mix fill types, ie. using a simple color for the first item, a
1051 hatched fill for the second and a fountain fill for the next.
1055 =head1 HOW VALUES WORK
1057 Internally rather than specifying literal color, fill, or font objects
1058 or literal sizes for each element, Imager::Graph uses a number of
1059 special values to inherit or modify values taken from other graph
1062 =head2 Specifying colors
1064 You can specify colors by either supplying an Imager::Color object, by
1065 supplying lookup of another color, or by supplying a single value that
1066 Imager::Color::new can use as an initializer. The most obvious is
1067 just a 6 or 8 digit hex value representing the red, green, blue and
1068 optionally alpha channels of the image.
1070 You can lookup another color by using the lookup() "function", for
1071 example if you give a color as "lookup(fg)" then Imager::Graph will
1072 look for the fg element in the current style (or as overridden by
1073 you.) This is used internally by Imager::Graph to set up the
1074 relationships between the colors of various elements, for example the
1075 default style information contains:
1078 color=>'lookup(fg)',
1082 color=>'lookup(text.color)',
1086 So by setting the I<fg> color, you also set the default text color,
1087 since each text element uses lookup(text.color) as its value.
1089 =head2 Specifying fills
1091 Fills can be used for the graph background color, the background color
1092 for the legend block and for the fills used for each data element.
1094 You can specify a fill as a L<color value|Specifying colors> or as a
1095 general fill, see L<Imager::Fill> for details.
1097 You don't need (or usually want) to call Imager::Fill::new yourself,
1098 since the various fill functions will call it for you, and
1099 Imager::Graph provides some hooks to make them more useful.
1105 with hatched fills, if you don't supply a 'fg' or 'bg' parameter,
1106 Imager::Graph will supply the current graph fg and bg colors.
1110 with fountain fill, you can supply the xa_ratio, ya_ratio, xb_ratio
1111 and yb_ratio parameters, and they will be scaled in the fill area to
1112 define the fountain fills xa, ya, xb and yb parameters.
1116 As with colors, you can use lookup(name) or lookup(name1.name2) to
1117 have one element to inherit the fill of another.
1119 Imager::Graph defaults the fill combine value to C<'normal'>. This
1120 doesn't apply to simple color fills.
1122 =head2 Specifying numbers
1124 You can specify various numbers, usually representing the size of
1125 something, commonly text, but sometimes the length of a line or the
1128 You can use the same lookup mechanism as with colors and fills, but
1129 you can also scale values. For example, 'scale(0.5,text.size)' will
1130 return half the size of the normal text size.
1132 As with colors, this is used internally to scale graph elements based
1133 on the base text size. If you change the base text size then other
1134 graph elements will scale as well.
1136 =head2 Specifying other elements
1138 Other elements, such as fonts, or parameters for a filter, can also
1139 use the lookup(name) mechanism.
1141 =head1 INTERNAL METHODS
1143 Only useful if you need to fix bugs, add features or create a new
1152 back=> 'lookup(bg)',
1153 line=> 'lookup(fg)',
1156 color => 'lookup(fg)',
1157 font => 'lookup(font)',
1162 color => 'lookup(text.color)',
1163 font => 'lookup(text.font)',
1166 size => 'scale(text.size,2.0)',
1167 aa => 'lookup(text.aa)',
1170 color => 'lookup(text.color)',
1171 font => 'lookup(text.font)',
1172 aa => 'lookup(text.aa)',
1173 size => 'lookup(text.size)',
1174 patchsize => 'scale(legend.size,0.9)',
1175 patchgap => 'scale(legend.patchsize,0.3)',
1176 patchborder => 'lookup(line)',
1179 padding => 'scale(legend.size,0.3)',
1180 outsidepadding => 'scale(legend.padding,0.4)',
1183 color => 'lookup(text.color)',
1184 font => 'lookup(text.font)',
1185 size => 'lookup(text.size)',
1186 line => 'lookup(line)',
1187 inside => 'lookup(callout.size)',
1188 outside => 'lookup(callout.size)',
1189 leadlen => 'scale(0.8,callout.size)',
1190 gap => 'scale(callout.size,0.3)',
1191 aa => 'lookup(text.aa)',
1192 lineaa => 'lookup(lineaa)',
1195 font => 'lookup(text.font)',
1196 size => 'lookup(text.size)',
1197 color => 'lookup(text.color)',
1198 hpad => 'lookup(label.pad)',
1199 vpad => 'lookup(label.pad)',
1200 pad => 'scale(label.size,0.2)',
1201 pcformat => sub { sprintf "%s (%.0f%%)", $_[0], $_[1] },
1202 pconlyformat => sub { sprintf "%.1f%%", $_[0] },
1203 aa => 'lookup(text.aa)',
1204 lineaa => 'lookup(lineaa)',
1207 fill => { solid => Imager::Color->new(0, 0, 0, 96) },
1208 off => 'scale(0.4,text.size)',
1209 offx => 'lookup(dropshadow.off)',
1210 offy => 'lookup(dropshadow.off)',
1211 filter => { type=>'conv',
1212 # this needs a fairly heavy blur
1213 coef=>[0.1, 0.2, 0.4, 0.6, 0.7, 0.9, 1.2,
1214 0.9, 0.7, 0.6, 0.4, 0.2, 0.1 ] },
1217 line =>'lookup(line)',
1218 lineaa => 'lookup(lineaa)',
1221 width=>'scale(1.5,size)',
1222 height=>'lookup(size)',
1224 # yes, the handling of fill and line AA is inconsistent, lack of
1225 # forethought, unfortunately
1229 lineaa => 'lookup(aa)',
1232 =item _error($message)
1234 Sets the error field of the object and returns an empty list or undef,
1235 depending on context. Should be used for error handling, since it may
1236 provide some user hooks at some point.
1238 The intended usage is:
1241 or return $self->_error("error description");
1243 You should almost always return the result of _error() or return
1244 immediately afterwards.
1249 my ($self, $error) = @_;
1251 $self->{_errstr} = $error;
1259 Returns the style defaults, such as the relationships between line
1260 color and text color.
1262 Intended to be over-ridden by base classes to provide graph specific
1271 # Let's make the default something that looks really good, so folks will be interested enough to customize the style.
1272 my $def_style = 'fount_lin';
1280 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1283 negative_bg=>'EEEEEE',
1287 #patchborder=>'000000'
1294 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1297 negative_bg=>'EEEEEE',
1301 patchborder=>'000000'
1308 { hatch=>'slash2' },
1309 { hatch=>'slosh2' },
1310 { hatch=>'vline2' },
1311 { hatch=>'hline2' },
1312 { hatch=>'cross2' },
1314 { hatch=>'stipple3' },
1315 { hatch=>'stipple2' },
1320 negative_bg=>'EEEEEE',
1321 features=>{ outline=>1 },
1331 { fountain=>'linear',
1332 xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
1333 segments => Imager::Fountain->simple(positions=>[0, 1],
1334 colors=>[ NC('FFC0C0'), NC('FF0000') ]),
1336 { fountain=>'linear',
1337 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1338 segments => Imager::Fountain->simple(positions=>[0, 1],
1339 colors=>[ NC('C0FFC0'), NC('00FF00') ]),
1341 { fountain=>'linear',
1342 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1343 segments => Imager::Fountain->simple(positions=>[0, 1],
1344 colors=>[ NC('C0C0FF'), NC('0000FF') ]),
1346 { fountain=>'linear',
1347 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1348 segments => Imager::Fountain->simple(positions=>[0, 1],
1349 colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
1351 { fountain=>'linear',
1352 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1353 segments => Imager::Fountain->simple(positions=>[0, 1],
1354 colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
1356 { fountain=>'linear',
1357 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1358 segments => Imager::Fountain->simple(positions=>[0, 1],
1359 colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
1363 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1366 { shape => 'circle', radius => 4 },
1367 { shape => 'square', radius => 4 },
1368 { shape => 'diamond', radius => 4 },
1369 { shape => 'triangle', radius => 4 },
1370 { shape => 'x', radius => 4 },
1371 { shape => 'plus', radius => 4 },
1373 back=>{ fountain=>'linear',
1374 xa_ratio=>0, ya_ratio=>0,
1375 xb_ratio=>1.0, yb_ratio=>1.0,
1376 segments=>Imager::Fountain->simple
1377 ( positions=>[0, 1],
1378 colors=>[ NC('6060FF'), NC('60FF60') ]) },
1380 negative_bg=>'EEEEEE',
1382 features=>{ dropshadow=>1 },
1388 { fountain=>'radial',
1389 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1390 segments => Imager::Fountain->simple(positions=>[0, 1],
1391 colors=>[ NC('FF8080'), NC('FF0000') ]),
1393 { fountain=>'radial',
1394 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1395 segments => Imager::Fountain->simple(positions=>[0, 1],
1396 colors=>[ NC('80FF80'), NC('00FF00') ]),
1398 { fountain=>'radial',
1399 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1400 segments => Imager::Fountain->simple(positions=>[0, 1],
1401 colors=>[ NC('808080FF'), NC('0000FF') ]),
1403 { fountain=>'radial',
1404 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1405 segments => Imager::Fountain->simple(positions=>[0, 1],
1406 colors=>[ NC('FFFF80'), NC('FFFF00') ]),
1408 { fountain=>'radial',
1409 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1410 segments => Imager::Fountain->simple(positions=>[0, 1],
1411 colors=>[ NC('80FFFF'), NC('00FFFF') ]),
1413 { fountain=>'radial',
1414 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1415 segments => Imager::Fountain->simple(positions=>[0, 1],
1416 colors=>[ NC('FF80FF'), NC('FF00FF') ]),
1420 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1422 back=>{ fountain=>'linear',
1423 xa_ratio=>0, ya_ratio=>0,
1424 xb_ratio=>1.0, yb_ratio=>1.0,
1425 segments=>Imager::Fountain->simple
1426 ( positions=>[0, 1],
1427 colors=>[ NC('6060FF'), NC('60FF60') ]) },
1429 negative_bg=>'EEEEEE',
1434 $styles{'ocean'} = {
1437 fountain =>'linear',
1438 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1439 segments => Imager::Fountain->simple(
1441 colors=>[ NC('EFEDCF'), NC('E6E2AF') ]),
1444 fountain =>'linear',
1445 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1446 segments => Imager::Fountain->simple(
1448 colors=>[ NC('DCD7AB'), NC('A7A37E') ]),
1451 fountain =>'linear',
1452 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1453 segments => Imager::Fountain->simple(
1455 colors=>[ NC('B2E5D4'), NC('80B4A2') ]),
1458 fountain =>'linear',
1459 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1460 segments => Imager::Fountain->simple(
1462 colors=>[ NC('7aaab9'), NC('046380') ]),
1465 fountain =>'linear',
1466 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1467 segments => Imager::Fountain->simple(
1469 colors=>[ NC('c3b8e9'), NC('877EA7') ]),
1472 fountain =>'linear',
1473 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1474 segments => Imager::Fountain->simple(
1476 colors=>[ NC('A3DF9A'), NC('67A35E') ]),
1479 fountain =>'linear',
1480 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1481 segments => Imager::Fountain->simple(
1483 colors=>[ NC('E19C98'), NC('B4726F') ]),
1487 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1490 negative_bg=>'EEEEEE',
1492 features=>{ dropshadow=>1 },
1496 $styles{'ocean_flat'} = {
1499 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1502 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1505 negative_bg=>'EEEEEE',
1507 features=>{ dropshadow=>1 },
1512 =item $self->_style_setup(\%opts)
1514 Uses the values from %opts to build a customized hash describing the
1515 way the graph should be drawn.
1520 my ($self, $opts) = @_;
1521 my $style_defs = $self->_style_defs;
1524 my $pre_def_style = $self->_get_style($opts);
1525 my $api_style = $self->{'custom_style'} || {};
1526 $style = $styles{$pre_def_style} if $pre_def_style;
1528 $style ||= $styles{$def_style};
1530 my @search_list = ( $style_defs, $style, $api_style, $opts);
1533 my @composite = $self->_composite();
1535 @composite{@composite} = @composite;
1537 for my $src (@search_list) {
1538 for my $key (keys %$src) {
1539 if ($composite{$key}) {
1540 $work{$key} = {} unless exists $work{$key};
1541 if (ref $src->{$key}) {
1542 # some keys have sub values, especially text
1543 @{$work{$key}}{keys %{$src->{$key}}} = values %{$src->{$key}};
1546 # assume it's the text for a title or something
1547 $work{$key}{text} = $src->{$key};
1551 $work{$key} = $src->{$key}
1552 if defined $src->{$key}; # $opts with pmichauds new accessor handling
1557 # features are handled specially
1558 $work{features} = {};
1559 for my $src (@search_list) {
1560 if ($src->{features}) {
1561 if (ref $src->{features}) {
1562 if (ref($src->{features}) =~ /ARRAY/) {
1563 # just set those features
1564 for my $feature (@{$src->{features}}) {
1565 $work{features}{$feature} = 1;
1568 elsif (ref($src->{features}) =~ /HASH/) {
1569 if ($src->{features}{reset}) {
1570 $work{features} = {}; # only the ones the user specifies
1572 @{$work{features}}{keys %{$src->{features}}} =
1573 values(%{$src->{features}});
1577 # just set that single feature
1578 $work{features}{$src->{features}} = 1;
1583 #print Dumper(\%work);
1585 $self->{_style} = \%work;
1588 =item $self->_get_thing($name)
1590 Retrieve some general 'thing'.
1592 Supports the 'lookup(foo)' mechanism.
1594 Returns an empty list on failure.
1599 my ($self, $name, @depth) = @_;
1601 push(@depth, $name);
1603 if ($name =~ /^(\w+)\.(\w+)$/) {
1604 $what = $self->{_style}{$1}{$2};
1607 $what = $self->{_style}{$name};
1614 elsif ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1616 or return $self->_error("too many levels of recursion in lookup(@depth)");
1617 return $self->_get_thing($1, @depth);
1624 =item $self->_get_number($name)
1626 Retrieves a number from the style. The value in the style can be the
1627 number, or one of two functions:
1631 =item lookup(newname)
1633 Recursively looks up I<newname> in the style.
1635 =item scale(value1,value2)
1637 Each value can be a number or a name. Names are recursively looked up
1638 in the style and the product is returned.
1645 my ($self, $name, @depth) = @_;
1647 push(@depth, $name);
1649 if ($name =~ /^(\w+)\.(\w+)$/) {
1650 $what = $self->{_style}{$1}{$2};
1653 $what = $self->{_style}{$name};
1656 return $self->_error("$name is undef (@depth)");
1659 if ($what =~ /CODE/) {
1660 $what = $what->($self, $name);
1664 if ($what =~ /^lookup\(([\w.]+)\)$/) {
1666 or return $self->_error("too many levels of recursion in lookup (@depth)");
1667 return $self->_get_number($1, @depth);
1669 elsif ($what =~ /^scale\(
1670 ((?:[a-z][\w.]*)|$NUM_RE)
1672 ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
1673 my ($left, $right) = ($1, $2);
1674 unless ($left =~ /^$NUM_RE$/) {
1676 or return $self->_error("too many levels of recursion in scale (@depth)");
1677 $left = $self->_get_number($left, @depth);
1679 unless ($right =~ /^$NUM_RE$/) {
1681 or return $self->_error("too many levels of recursion in scale (@depth)");
1682 $right = $self->_get_number($right, @depth);
1684 return $left * $right;
1692 =item $self->_get_integer($name)
1694 Retrieves an integer from the style. This is a simple wrapper around
1695 _get_number() that rounds the result to an integer.
1697 Returns an empty list on failure.
1702 my ($self, $name, @depth) = @_;
1704 my $number = $self->_get_number($name, @depth)
1707 return sprintf("%.0f", $number);
1710 =item _get_color($name)
1712 Returns a color object of the given name from the style hash.
1714 Uses Imager::Color->new to translate normal scalars into color objects.
1716 Allows the lookup(name) mechanism.
1718 Returns an empty list on failure.
1723 my ($self, $name, @depth) = @_;
1725 push(@depth, $name);
1727 if ($name =~ /^(\w+)\.(\w+)$/) {
1728 $what = $self->{_style}{$1}{$2};
1731 $what = $self->{_style}{$name};
1735 or return $self->_error("$name was undefined (@depth)");
1737 unless (ref $what) {
1738 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1739 @depth < MAX_DEPTH or
1740 return $self->_error("too many levels of recursion in lookup (@depth)");
1742 return $self->_get_color($1, @depth);
1744 $what = Imager::Color->new($what);
1750 =item _translate_fill($what, $box)
1752 Given the value of a fill, either attempts to convert it into a fill
1753 list (one of C<<color=>$color_value, filled=>1>> or C<<fill=>{ fill
1754 parameters }>>), or to lookup another fill that is referred to with
1755 the 'lookup(name)' mechanism.
1757 This function does the fg and bg initialization for hatched fills, and
1758 translation of *_ratio for fountain fills (using the $box parameter).
1760 Returns an empty list on failure.
1764 sub _translate_fill {
1765 my ($self, $what, $box, @depth) = @_;
1768 if (UNIVERSAL::isa($what, "Imager::Color")) {
1769 return ( color=>Imager::Color->new($what), filled=>1 );
1773 # default to normal combine mode
1774 my %work = ( combine => 'normal', %$what );
1775 if ($what->{hatch}) {
1777 $work{fg} = $self->_get_color('fg')
1781 $work{bg} = $self->_get_color('bg')
1784 return ( fill=>\%work );
1786 elsif ($what->{fountain}) {
1787 for my $key (qw(xa ya xb yb)) {
1788 if (exists $work{"${key}_ratio"}) {
1790 $work{$key} = $box->[0] + $work{"${key}_ratio"}
1791 * ($box->[2] - $box->[0]);
1794 $work{$key} = $box->[1] + $work{"${key}_ratio"}
1795 * ($box->[3] - $box->[1]);
1799 return ( fill=>\%work );
1802 return ( fill=> \%work );
1807 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1808 return $self->_get_fill($1, $box, @depth);
1811 # assumed to be an Imager::Color single value
1812 return ( color=>Imager::Color->new($what), filled=>1 );
1817 =item _data_fill($index, $box)
1819 Retrieves the fill parameters for a data area fill.
1824 my ($self, $index, $box) = @_;
1826 my $fills = $self->{_style}{fills};
1827 return $self->_translate_fill($fills->[$index % @$fills], $box,
1832 my ($self, $index) = @_;
1834 my $colors = $self->{'_style'}{'colors'} || [];
1835 my $fills = $self->{'_style'}{'fills'} || [];
1837 # Try to just use a fill, so non-fountain styles don't need
1838 # to have a duplicated set of fills and colors
1839 my $fill = $fills->[$index % @$fills];
1845 return $colors->[$index % @$colors] || '000000';
1850 =item _get_fill($index, $box)
1852 Retrieves fill parameters for a named fill.
1857 my ($self, $name, $box, @depth) = @_;
1859 push(@depth, $name);
1861 if ($name =~ /^(\w+)\.(\w+)$/) {
1862 $what = $self->{_style}{$1}{$2};
1865 $what = $self->{_style}{$name};
1869 or return $self->_error("no fill $name found");
1871 return $self->_translate_fill($what, $box, @depth);
1876 Builds the image object for the graph and fills it with the background
1884 my $width = $self->_get_number('width') || 256;
1885 my $height = $self->_get_number('height') || 256;
1886 my $channels = $self->{_style}{channels};
1890 my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels);
1892 $img->box($self->_get_fill('back', [ 0, 0, $width-1, $height-1]));
1900 if (!$self->{'_image'}) {
1901 $self->{'_image'} = $self->_make_img();
1903 return $self->{'_image'};
1906 =item _text_style($name)
1908 Returns parameters suitable for calls to Imager::Font's bounding_box()
1909 and draw() methods intended for use in defining text styles.
1911 Returns an empty list on failure.
1913 Returns the following attributes: font, color, size, aa, sizew
1919 my ($self, $name) = @_;
1923 if ($self->{_style}{$name}) {
1924 %work = %{$self->{_style}{$name}};
1927 %work = %{$self->{_style}{text}};
1930 or return $self->_error("$name has no font parameter");
1932 $work{font} = $self->_get_thing("$name.font")
1933 or return $self->_error("No $name.font defined, either set $name.font or font to a font");
1934 UNIVERSAL::isa($work{font}, "Imager::Font")
1935 or return $self->_error("$name.font is not a font");
1936 if ($work{color} && !ref $work{color}) {
1937 $work{color} = $self->_get_color("$name.color")
1940 $work{size} = $self->_get_number("$name.size");
1941 $work{sizew} = $self->_get_number("$name.sizew")
1943 $work{aa} = $self->_get_number("$name.aa");
1948 =item _text_bbox($text, $name)
1950 Returns a bounding box for the specified $text as styled by $name.
1952 Returns an empty list on failure.
1957 my ($self, $text, $name) = @_;
1959 my %text_info = $self->_text_style($name)
1962 my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
1968 =item _line_style($name)
1970 Return parameters suitable for calls to Imager's line(), polyline(),
1973 For now this returns only color and aa parameters, but future releases
1974 of Imager may support extra parameters.
1979 my ($self, $name) = @_;
1982 $line{color} = $self->_get_color("$name.line")
1984 $line{aa} = $self->_get_number("$name.lineaa");
1985 defined $line{aa} or $line{aa} = $self->_get_number("aa");
1991 my ($self, $box, $chart_box, $name) = @_;
1993 my $halign = $self->{_style}{$name}{halign}
1994 or $self->_error("no halign for $name");
1995 my $valign = $self->{_style}{$name}{valign};
1997 if ($halign eq 'right') {
1998 $box->[0] += $chart_box->[2] - $box->[2];
2000 elsif ($halign eq 'left') {
2001 $box->[0] = $chart_box->[0];
2003 elsif ($halign eq 'center' || $halign eq 'centre') {
2004 $box->[0] = ($chart_box->[0] + $chart_box->[2] - $box->[2])/2;
2007 return $self->_error("invalid halign $halign for $name");
2010 if ($valign eq 'top') {
2011 $box->[1] = $chart_box->[1];
2013 elsif ($valign eq 'bottom') {
2014 $box->[1] = $chart_box->[3] - $box->[3];
2016 elsif ($valign eq 'center' || $valign eq 'centre') {
2017 $box->[1] = ($chart_box->[1] + $chart_box->[3] - $box->[3])/2;
2020 return $self->_error("invalid valign $valign for $name");
2022 $box->[2] += $box->[0];
2023 $box->[3] += $box->[1];
2027 my ($self, $chart_box, $object_box) = @_;
2031 if ($object_box->[0] - $chart_box->[0]
2032 < $chart_box->[2] - $object_box->[2]) {
2033 $areax = ($object_box->[2] - $chart_box->[0])
2034 * ($chart_box->[3] - $chart_box->[1]);
2037 $areax = ($chart_box->[2] - $object_box->[0])
2038 * ($chart_box->[3] - $chart_box->[1]);
2041 if ($object_box->[1] - $chart_box->[1]
2042 < $chart_box->[3] - $object_box->[3]) {
2043 $areay = ($object_box->[3] - $chart_box->[1])
2044 * ($chart_box->[2] - $chart_box->[0]);
2047 $areay = ($chart_box->[3] - $object_box->[1])
2048 * ($chart_box->[2] - $chart_box->[0]);
2051 if ($areay < $areax) {
2052 if ($object_box->[1] - $chart_box->[1]
2053 < $chart_box->[3] - $object_box->[3]) {
2054 $chart_box->[1] = $object_box->[3];
2057 $chart_box->[3] = $object_box->[1];
2061 if ($object_box->[0] - $chart_box->[0]
2062 < $chart_box->[2] - $object_box->[2]) {
2063 $chart_box->[0] = $object_box->[2];
2066 $chart_box->[2] = $object_box->[0];
2072 my ($self, $img, $labels, $chart_box) = @_;
2074 my $orient = $self->_get_thing('legend.orientation');
2075 defined $orient or $orient = 'vertical';
2077 if ($orient eq 'vertical') {
2078 return $self->_draw_legend_vertical($img, $labels, $chart_box);
2080 elsif ($orient eq 'horizontal') {
2081 return $self->_draw_legend_horizontal($img, $labels, $chart_box);
2084 return $self->_error("Unknown legend.orientation $orient");
2088 sub _draw_legend_horizontal {
2089 my ($self, $img, $labels, $chart_box) = @_;
2091 defined(my $padding = $self->_get_integer('legend.padding'))
2093 my $patchsize = $self->_get_integer('legend.patchsize')
2095 defined(my $gap = $self->_get_integer('legend.patchgap'))
2098 my $minrowsize = $patchsize + $gap;
2099 my ($width, $height) = (0,0);
2100 my $row_height = $minrowsize;
2104 for my $label (@$labels) {
2105 my @text_box = $self->_text_bbox($label, 'legend')
2107 push(@sizes, \@text_box);
2108 my $entry_width = $patchsize + $gap + $text_box[2];
2110 # never re-wrap the first entry
2111 push @offsets, [ 0, $height ];
2114 if ($pos + $gap + $entry_width > $chart_box->[2]) {
2116 $height += $row_height;
2118 push @offsets, [ $pos, $height ];
2120 my $entry_right = $pos + $entry_width;
2121 $pos += $gap + $entry_width;
2122 $entry_right > $width and $width = $entry_right;
2123 if ($text_box[3] > $row_height) {
2124 $row_height = $text_box[3];
2127 $height += $row_height;
2128 my @box = ( 0, 0, $width + $padding * 2, $height + $padding * 2 );
2129 my $outsidepadding = 0;
2130 if ($self->{_style}{legend}{border}) {
2131 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
2133 $box[2] += 2 * $outsidepadding;
2134 $box[3] += 2 * $outsidepadding;
2136 $self->_align_box(\@box, $chart_box, 'legend')
2138 if ($self->{_style}{legend}{fill}) {
2139 $img->box(xmin=>$box[0]+$outsidepadding,
2140 ymin=>$box[1]+$outsidepadding,
2141 xmax=>$box[2]-$outsidepadding,
2142 ymax=>$box[3]-$outsidepadding,
2143 $self->_get_fill('legend.fill', \@box));
2145 $box[0] += $outsidepadding;
2146 $box[1] += $outsidepadding;
2147 $box[2] -= $outsidepadding;
2148 $box[3] -= $outsidepadding;
2149 my %text_info = $self->_text_style('legend')
2152 if ($self->{_style}{legend}{patchborder}) {
2153 $patchborder = $self->_get_color('legend.patchborder')
2158 for my $label (@$labels) {
2159 my ($left, $top) = @{$offsets[$dataindex]};
2160 $left += $box[0] + $padding;
2161 $top += $box[1] + $padding;
2162 my $textpos = $left + $patchsize + $gap;
2163 my @patchbox = ( $left, $top,
2164 $left + $patchsize, $top + $patchsize );
2165 my @fill = $self->_data_fill($dataindex, \@patchbox)
2167 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
2168 ymax=>$top + $patchsize, @fill);
2169 if ($self->{_style}{legend}{patchborder}) {
2170 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
2171 ymax=>$top + $patchsize,
2172 color=>$patchborder);
2174 $img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize,
2179 if ($self->{_style}{legend}{border}) {
2180 my $border_color = $self->_get_color('legend.border')
2182 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
2183 color=>$border_color);
2185 $self->_remove_box($chart_box, \@box);
2189 sub _draw_legend_vertical {
2190 my ($self, $img, $labels, $chart_box) = @_;
2192 defined(my $padding = $self->_get_integer('legend.padding'))
2194 my $patchsize = $self->_get_integer('legend.patchsize')
2196 defined(my $gap = $self->_get_integer('legend.patchgap'))
2198 my $minrowsize = $patchsize + $gap;
2199 my ($width, $height) = (0,0);
2201 for my $label (@$labels) {
2202 my @box = $self->_text_bbox($label, 'legend')
2204 push(@sizes, \@box);
2205 $width = $box[2] if $box[2] > $width;
2206 if ($minrowsize > $box[3]) {
2207 $height += $minrowsize;
2214 $width + $patchsize + $padding * 2 + $gap,
2215 $height + $padding * 2 - $gap);
2216 my $outsidepadding = 0;
2217 if ($self->{_style}{legend}{border}) {
2218 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
2220 $box[2] += 2 * $outsidepadding;
2221 $box[3] += 2 * $outsidepadding;
2223 $self->_align_box(\@box, $chart_box, 'legend')
2225 if ($self->{_style}{legend}{fill}) {
2226 $img->box(xmin=>$box[0]+$outsidepadding,
2227 ymin=>$box[1]+$outsidepadding,
2228 xmax=>$box[2]-$outsidepadding,
2229 ymax=>$box[3]-$outsidepadding,
2230 $self->_get_fill('legend.fill', \@box));
2232 $box[0] += $outsidepadding;
2233 $box[1] += $outsidepadding;
2234 $box[2] -= $outsidepadding;
2235 $box[3] -= $outsidepadding;
2236 my $ypos = $box[1] + $padding;
2237 my $patchpos = $box[0]+$padding;
2238 my $textpos = $patchpos + $patchsize + $gap;
2239 my %text_info = $self->_text_style('legend')
2242 if ($self->{_style}{legend}{patchborder}) {
2243 $patchborder = $self->_get_color('legend.patchborder')
2247 for my $label (@$labels) {
2248 my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
2249 $patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
2252 if ($self->_draw_flat_legend()) {
2253 @fill = (color => $self->_data_color($dataindex), filled => 1);
2256 @fill = $self->_data_fill($dataindex, \@patchbox)
2259 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
2260 ymax=>$ypos + $patchsize, @fill);
2261 if ($self->{_style}{legend}{patchborder}) {
2262 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
2263 ymax=>$ypos + $patchsize,
2264 color=>$patchborder);
2266 $img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize,
2269 my $step = $patchsize + $gap;
2270 if ($minrowsize < $sizes[$dataindex][3]) {
2271 $ypos += $sizes[$dataindex][3];
2274 $ypos += $minrowsize;
2278 if ($self->{_style}{legend}{border}) {
2279 my $border_color = $self->_get_color('legend.border')
2281 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
2282 color=>$border_color);
2284 $self->_remove_box($chart_box, \@box);
2289 my ($self, $img, $chart_box) = @_;
2291 my $title = $self->{_style}{title}{text};
2292 my @box = $self->_text_bbox($title, 'title')
2296 $self->_align_box(\@box, $chart_box, 'title');
2297 my %text_info = $self->_text_style('title')
2299 $img->string(%text_info, x=>$box[0], 'y'=>$box[3] + $yoff, text=>$title);
2300 $self->_remove_box($chart_box, \@box);
2305 my ($self, $box) = @_;
2307 if ($box->[2] - $box->[0] > $box->[3] - $box->[1]) {
2308 return $box->[3] - $box->[1];
2311 return $box->[2] - $box->[0];
2315 sub _draw_flat_legend {
2321 Returns a list of style fields that are stored as composites, and
2322 should be merged instead of just being replaced.
2327 qw(title legend text label dropshadow outline callout);
2330 sub _filter_region {
2331 my ($self, $img, $left, $top, $right, $bottom, $filter) = @_;
2333 unless (ref $filter) {
2335 $filter = $self->_get_thing($name)
2338 or return $self->_error("no type for filter $name");
2341 $left > 0 or $left = 0;
2342 $top > 0 or $top = 0;
2344 # newer versions of Imager let you work on just part of an image
2345 if ($img->can('masked') && !$self->{_style}{features}{_debugblur}) {
2346 my $masked = $img->masked(left=>$left, top=>$top,
2347 right=>$right, bottom=>$bottom);
2348 $masked->filter(%$filter);
2351 # for older versions of Imager
2352 my $subset = $img->crop(left=>$left, top=>$top,
2353 right=>$right, bottom=>$bottom);
2354 $subset->filter(%$filter);
2355 $img->paste(left=>$left, top=>$top, img=>$subset);
2366 Imager::Graph::Pie(3), Imager(3), perl(1).
2370 Tony Cook <tony@develop-help.com>
2374 Imager::Graph is licensed under the same terms as perl itself.
2378 Addi for producing a cool imaging module. :)