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, ...)
14 $img->write(file => 'image.png');
18 Imager::Graph provides style information to its base classes. It
19 defines the colors, text display information and fills based on both
20 built-in styles and modifications supplied by the user to the draw()
28 use vars qw($VERSION);
29 use Imager qw(:handy);
34 # the maximum recursion depth in determining a color, fill or number
35 use constant MAX_DEPTH => 10;
37 my $NUM_RE = '(?:[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]\d+?)?)';
41 This is a simple constructor. No parameters required.
49 =item set_graph_size($size)
51 Sets the size of the graph (in pixels) within the image. The size of the image defaults to 1.5 * $graph_size.
56 $_[0]->{'custom_style'}->{'size'} = $_[1];
59 =item set_image_width($width)
61 Sets the width of the image in pixels.
66 $_[0]->{'custom_style'}->{'width'} = $_[1];
69 =item set_image_height($height)
71 Sets the height of the image in pixels.
75 sub set_image_height {
76 $_[0]->{'custom_style'}->{'height'} = $_[1];
79 =item add_data_series([8, 6, 7, 5, 3, 0, 9], 'Series Name');
81 Adds a data series to the graph. For L<Imager::Graph::Pie>, only one data series can be added.
88 my $series_name = shift;
90 my $graph_data = $self->{'graph_data'} || [];
92 push @$graph_data, { data => $data_ref, series_name => $series_name };
93 if (defined $series_name) {
94 push @{$self->{'labels'}}, $series_name;
97 $self->{'graph_data'} = $graph_data;
101 sub _get_data_series {
102 my ($self, $opts) = @_;
104 # return the data supplied to draw() if any.
106 # one or multiple series?
107 my $data = $opts->{data};
108 if (@$data && ref $data->[0] && ref $data->[0] =~ /ARRAY/) {
112 return [ { data => $data } ];
116 return $self->{'graph_data'};
119 =item set_labels(['label1', 'label2' ... ])
121 Labels the specific data points. For line/bar graphs, this is the x-axis. For pie graphs, it is the label for the wedges.
126 $_[0]->{'labels'} = $_[1];
130 my ($self, $opts) = @_;
133 and return $opts->{labels};
135 return $_[0]->{'labels'}
138 =item set_title($title)
140 Sets the title of the graph. Requires setting a font.
145 $_[0]->{'custom_style'}->{'title'}->{'text'} = $_[1];
148 =item set_font($font)
150 Sets the font to use for text. Takes an L<Imager::Font> object.
155 $_[0]->{'custom_style'}->{'font'} = $_[1];
158 =item set_style($style_name)
160 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.
165 $_[0]->{'style'} = $_[1];
169 my ($self, $opts) = @_;
172 and return $opts->{style};
174 return $self->{'style'};
179 Returns an error message. Only valid if the draw() method returns false.
189 Creates a new image, draws the chart onto that image and returns it.
191 Optionally, instead of using the api methods to configure your chart,
192 you can supply a C<data> parameter in the format
193 required by that particular graph, and if your graph will use any
194 text, a C<font> parameter
196 You can also supply many different parameters which control the way
197 the graph looks. These are supplied as keyword, value pairs, where
198 the value can be a hashref containing sub values.
200 The C<style> parameter will selects a basic color set, and possibly
201 sets other related parameters. See L</"STYLES">.
203 my $font = Imager::Font->new(file => 'ImUgly.ttf');
204 my $img = $chart->draw(
208 text => "Hello, World!",
214 When referring to a single sub-value this documentation will refer to
215 'title.color' rather than 'the color element of title'.
217 Returns the graph image on success, or false on failure.
223 The currently defined styles are:
229 a light grey background with no outlines. Uses primary colors for the
234 a light red background with no outlines. Uses primary colors for the
237 Graphs drawn using this style should save well as a gif, even though
238 some graphs may perform a slight blur.
240 This was the default style, but the red was too loud.
244 designed for monochrome output, such as most laser printers, this uses
245 hatched fills for the data, and no colors. The returned image is a
246 one channel image (which can be overridden with the C<channels>
249 You can also override the colors used by all components for background
250 or drawing by supplying C<fg> and/or C<bg> parameters. ie. if you
251 supply C<<fg=>'FF0000', channels=>3>> then the hash fills and anything
252 else will be drawn in red. Another use might be to set a transparent
253 background, by supplying C<<bg=>'00000000', channels=>4>>.
255 This style outlines the legend if present and outlines the hashed fills.
259 designed as a "pretty" style this uses linear fountain fills for the
260 background and data fills, and adds a drop shadow.
262 You can override the value used for text and outlines by setting the
265 This is the default style.
269 also designed as a "pretty" style this uses radial fountain fills for
270 the data and a linear blue to green fill for the background.
276 To set or override styles, you can use the following methods:
280 =item set_image_background
284 sub set_image_background {
285 $_[0]->{'custom_style'}->{'back'} = $_[1];
293 $_[0]->{'custom_style'}->{'channels'} = $_[1];
301 $_[0]->{'custom_style'}->{'line'} = $_[1];
304 =item set_title_font_size
308 sub set_title_font_size {
309 $_[0]->{'custom_style'}->{'title'}->{'size'} = $_[1];
312 =item set_title_font_color
316 sub set_title_font_color {
317 $_[0]->{'custom_style'}->{'title'}->{'color'} = $_[1];
320 =item set_title_horizontal_align
324 sub set_title_horizontal_align {
325 $_[0]->{'custom_style'}->{'title'}->{'halign'} = $_[1];
328 =item set_title_vertical_align
332 sub set_title_vertical_align {
333 $_[0]->{'custom_style'}->{'title'}->{'valign'} = $_[1];
336 =item set_text_font_color
340 sub set_text_font_color {
341 $_[0]->{'custom_style'}->{'text'}->{'color'} = $_[1];
344 =item set_text_font_size
348 sub set_text_font_size {
349 $_[0]->{'custom_style'}->{'text'}->{'size'} = $_[1];
352 =item set_graph_background_color
356 sub set_graph_background_color {
357 $_[0]->{'custom_style'}->{'bg'} = $_[1];
360 =item set_graph_foreground_color
364 sub set_graph_foreground_color {
365 $_[0]->{'custom_style'}->{'fg'} = $_[1];
368 =item set_legend_font_color
372 sub set_legend_font_color {
373 $_[0]->{'custom_style'}->{'legend'}->{'color'} = $_[1];
376 =item set_legend_font
380 sub set_legend_font {
381 $_[0]->{'custom_style'}->{'legend'}->{'font'} = $_[1];
384 =item set_legend_font_size
388 sub set_legend_font_size {
389 $_[0]->{'custom_style'}->{'legend'}->{'size'} = $_[1];
392 =item set_legend_patch_size
396 sub set_legend_patch_size {
397 $_[0]->{'custom_style'}->{'legend'}->{'patchsize'} = $_[1];
400 =item set_legend_patch_gap
404 sub set_legend_patch_gap {
405 $_[0]->{'custom_style'}->{'legend'}->{'patchgap'} = $_[1];
408 =item set_legend_horizontal_align
412 sub set_legend_horizontal_align {
413 $_[0]->{'custom_style'}->{'legend'}->{'halign'} = $_[1];
416 =item set_legend_vertical_align
420 sub set_legend_vertical_align {
421 $_[0]->{'custom_style'}->{'legend'}->{'valign'} = $_[1];
424 =item set_legend_padding
428 sub set_legend_padding {
429 $_[0]->{'custom_style'}->{'legend'}->{'padding'} = $_[1];
432 =item set_legend_outside_padding
436 sub set_legend_outside_padding {
437 $_[0]->{'custom_style'}->{'legend'}->{'outsidepadding'} = $_[1];
440 =item set_legend_fill
444 sub set_legend_fill {
445 $_[0]->{'custom_style'}->{'legend'}->{'fill'} = $_[1];
448 =item set_legend_border
452 sub set_legend_border {
453 $_[0]->{'custom_style'}->{'legend'}->{'border'} = $_[1];
456 =item set_legend_orientation
460 sub set_legend_orientation {
461 $_[0]->{'custom_style'}->{'legend'}->{'orientation'} = $_[1];
464 =item set_callout_font_color
468 sub set_callout_font_color {
469 $_[0]->{'custom_style'}->{'callout'}->{'color'} = $_[1];
472 =item set_callout_font
476 sub set_callout_font {
477 $_[0]->{'custom_style'}->{'callout'}->{'font'} = $_[1];
480 =item set_callout_font_size
484 sub set_callout_font_size {
485 $_[0]->{'custom_style'}->{'callout'}->{'size'} = $_[1];
488 =item set_callout_line_color
492 sub set_callout_line_color {
493 $_[0]->{'custom_style'}->{'callout'}->{'line'} = $_[1];
496 =item set_callout_leader_inside_length
500 sub set_callout_leader_inside_length {
501 $_[0]->{'custom_style'}->{'callout'}->{'inside'} = $_[1];
504 =item set_callout_leader_outside_length
508 sub set_callout_leader_outside_length {
509 $_[0]->{'custom_style'}->{'callout'}->{'outside'} = $_[1];
512 =item set_callout_leader_length
516 sub set_callout_leader_length {
517 $_[0]->{'custom_style'}->{'callout'}->{'leadlen'} = $_[1];
520 =item set_callout_gap
524 sub set_callout_gap {
525 $_[0]->{'custom_style'}->{'callout'}->{'gap'} = $_[1];
528 =item set_label_font_color
532 sub set_label_font_color {
533 $_[0]->{'custom_style'}->{'label'}->{'color'} = $_[1];
541 $_[0]->{'custom_style'}->{'label'}->{'font'} = $_[1];
544 =item set_label_font_size
548 sub set_label_font_size {
549 $_[0]->{'custom_style'}->{'label'}->{'size'} = $_[1];
552 =item set_drop_shadow_fill_color
556 sub set_drop_shadow_fill_color {
557 $_[0]->{'custom_style'}->{'dropshadow'}->{'fill'} = $_[1];
560 =item set_drop_shadow_offset
564 sub set_drop_shadow_offset {
565 $_[0]->{'custom_style'}->{'dropshadow'}->{'off'} = $_[1];
568 =item set_drop_shadowXOffset
572 sub set_drop_shadowXOffset {
573 $_[0]->{'custom_style'}->{'dropshadow'}->{'offx'} = $_[1];
576 =item set_drop_shadowYOffset
580 sub set_drop_shadowYOffset {
581 $_[0]->{'custom_style'}->{'dropshadow'}->{'offy'} = $_[1];
584 =item set_drop_shadow_filter
588 sub set_drop_shadow_filter {
589 $_[0]->{'custom_style'}->{'dropshadow'}->{'filter'} = $_[1];
592 =item set_outline_color
596 sub set_outline_color {
597 $_[0]->{'custom_style'}->{'outline'}->{'line'} = $_[1];
600 =item set_data_area_fills
604 sub set_data_area_fills {
605 $_[0]->{'custom_style'}->{'fills'} = $_[1];
608 =item set_data_line_colors
612 sub set_data_line_colors {
613 $_[0]->{'custom_style'}->{'colors'} = $_[1];
620 Each graph type has a number of features. These are used to add
621 various items that are displayed in the graph area. Some common
628 adds a box containing boxes filled with the data filess, with
629 the labels provided to the draw method. The legend will only be
630 displayed if both the legend feature is enabled and labels are
636 $_[0]->{'custom_style'}->{'features'}->{'legend'} = 1;
641 draws a border around the data areas.
646 $_[0]->{'custom_style'}->{'features'}->{'outline'} = 1;
651 labels each data fill, usually by including text inside the data fill.
652 If the text does not fit in the fill, they could be displayed in some
653 other form, eg. as callouts in a pie graph. There usually isn't much
654 point in including both labels and a legend.
659 $_[0]->{'custom_style'}->{'features'}->{'labels'} = 1;
662 =item show_drop_shadow()
664 a simple drop shadow is shown behind some of the graph elements.
668 sub show_drop_shadow {
669 $_[0]->{'custom_style'}->{'features'}->{'dropshadow'} = 1;
672 =item reset_features()
674 Unsets all of the features
679 $_[0]->{'custom_style'}->{'features'} = {};
680 $_[0]->{'custom_style'}->{'features'}->{'reset'} = 1;
685 Additionally, features can be set by passing them into the draw() method:
691 adds a box containing boxes filled with the data filess, with
692 the labels provided to the draw method. The legend will only be
693 displayed if both the legend feature is enabled and labels are
698 labels each data fill, usually by including text inside the data fill.
699 If the text does not fit in the fill, they could be displayed in some
700 other form, eg. as callouts in a pie graph. There usually isn't much
701 point in including both labels and a legend.
705 a simple drop shadow is shown behind some of the graph elements.
709 Each graph also has features specific to that graph.
711 =head1 COMMON PARAMETERS
713 When referring to a single sub-value this documentation will refer to
714 'title.color' rather than 'the color element of title'.
716 Normally, except for the font parameter, these are controlled by
717 styles, but these are the style parameters I'd mostly likely expect
724 the Imager font object used to draw text on the chart.
728 the background fill for the graph. Default depends on the style.
732 the base size of the graph image. Default: 256
736 the width of the graph image. Default: 1.5 * size (384)
740 the height of the graph image. Default: size (256)
744 the number of channels in the image. Default: 3 (the 'mono' style
749 the color used for drawing lines, such as outlines or callouts.
750 Default depends on the current style. Set to undef to remove the
751 outline from a style.
755 the text used for a graph title. Default: no title. Note: this is
756 the same as the title=>{ text => ... } field.
762 horizontal alignment of the title in the graph, one of 'left',
763 'center' or 'right'. Default: center
767 vertical alignment of the title, one of 'top', 'center' or 'right'.
768 Default: top. It's probably a bad idea to set this to 'center' unless
769 you have a very short title.
775 This contains basic defaults used in drawing text.
781 the default color used for all text, defaults to the fg color.
785 the base size used for text, also used to scale many graph elements.
794 In most cases you will want to use just the styles, but you may want
795 to exert more control over the way your chart looks. This section
796 describes the options you can use to control the way your chart looks.
798 Hopefully you don't need to read this.
804 The background of the graph.
810 Used to define basic background and foreground colors for the graph.
811 The bg color may be used for the background of the graph, and is used
812 as a default for the background of hatcheed fills. The fg is used as
813 the default for line and text colors.
817 The default font used by the graph. Normally you should supply this
818 if your graph as any text.
822 The default line color.
826 defaults for drawing text. Other textual graph elements will inherit
827 or modify these values.
833 default text color, defaults to the I<fg> color.
837 default text size. Default: 14. This is used to scale many graph
838 elements, including padding and leader sizes. Other text elements
839 will either use or scale this value.
843 default font object. Inherited from I<font>, which should have been
844 supplied by the caller.
850 If you supply a scalar value for this element, it will be stored in
853 Defines the text, font and layout information for the title.
859 The color of the title, inherited from I<text.color>.
863 The font object used for the title, inherited from I<text.font>.
867 size of the title text. Default: double I<text.size>
873 The horizontal and vertical alignment of the title.
879 defines attributes of the graph legend, if present.
889 text attributes for the labels used in the legend.
893 the width and height of the color patch in the legend. Defaults to
894 90% of the legend text size.
898 the minimum gap between patches in pixels. Defaults to 30% of the
903 the color of the border drawn around each patch. Inherited from I<line>.
909 the horizontal and vertical alignment of the legend within the graph.
910 Defaults to 'right' and 'top'.
914 the gap between the legend patches and text and the outside of it's
915 box, or to the legend border, if any.
919 the gap between the border and the outside of the legend's box. This
920 is only used if the I<legend.border> attribute is defined.
924 the background fill for the legend. Default: none
928 the border color of the legend. Default: none (no border is drawn
933 The orientation of the legend. If this is C<vertical> the the patches
934 and labels are stacked on top of each other. If this is C<horizontal>
935 the patchs and labels are word wrapped across the image. Default:
940 For example to create a horizontal legend with borderless patches,
941 darker than the background, you might do:
943 my $im = $chart->draw
947 patchborder => undef,
948 orientation => 'horizontal',
949 fill => { solid => Imager::Color->new(0, 0, 0, 32), }
955 defines attributes for graph callouts, if any are present. eg. if the
956 pie graph cannot fit the label into the pie graph segement it will
957 present it as a callout.
967 the text attributes of the callout label. Inherited from I<text>.
971 the color of the callout lines. Inherited from I<line>
977 the length of the leader on the inside and the outside of the fill,
978 usually at some angle. Both default to the size of the callout text.
982 the length of the horizontal portion of the leader. Default:
987 the gap between the callout leader and the callout text. Defaults to
988 30% of the text callout size.
994 defines attributes for labels drawn into the data areas of a graph.
1004 The text attributes of the labels. Inherited from I<text>.
1010 the attributes of the graph's drop shadow
1016 the fill used for the drop shadow. Default: '404040' (dark gray)
1020 the offset of the drop shadow. A convenience value inherited by offx
1021 and offy. Default: 40% of I<text.size>.
1027 the horizontal and vertical offsets of the drop shadow. Both
1028 inherited from I<dropshadow.off>.
1032 the filter description passed to Imager's filter method to blur the
1033 drop shadow. Default: an 11 element convolution filter.
1039 describes the lines drawn around filled data areas, such as the
1040 segments of a pie chart.
1046 the line color of the outlines, inherited from I<line>.
1052 a reference to an array containing fills for each data item.
1054 You can mix fill types, ie. using a simple color for the first item, a
1055 hatched fill for the second and a fountain fill for the next.
1059 =head1 HOW VALUES WORK
1061 Internally rather than specifying literal color, fill, or font objects
1062 or literal sizes for each element, Imager::Graph uses a number of
1063 special values to inherit or modify values taken from other graph
1066 =head2 Specifying colors
1068 You can specify colors by either supplying an Imager::Color object, by
1069 supplying lookup of another color, or by supplying a single value that
1070 Imager::Color::new can use as an initializer. The most obvious is
1071 just a 6 or 8 digit hex value representing the red, green, blue and
1072 optionally alpha channels of the image.
1074 You can lookup another color by using the lookup() "function", for
1075 example if you give a color as "lookup(fg)" then Imager::Graph will
1076 look for the fg element in the current style (or as overridden by
1077 you.) This is used internally by Imager::Graph to set up the
1078 relationships between the colors of various elements, for example the
1079 default style information contains:
1082 color=>'lookup(fg)',
1086 color=>'lookup(text.color)',
1090 So by setting the I<fg> color, you also set the default text color,
1091 since each text element uses lookup(text.color) as its value.
1093 =head2 Specifying fills
1095 Fills can be used for the graph background color, the background color
1096 for the legend block and for the fills used for each data element.
1098 You can specify a fill as a L<color value|Specifying colors> or as a
1099 general fill, see L<Imager::Fill> for details.
1101 You don't need (or usually want) to call Imager::Fill::new yourself,
1102 since the various fill functions will call it for you, and
1103 Imager::Graph provides some hooks to make them more useful.
1109 with hatched fills, if you don't supply a 'fg' or 'bg' parameter,
1110 Imager::Graph will supply the current graph fg and bg colors.
1114 with fountain fill, you can supply the xa_ratio, ya_ratio, xb_ratio
1115 and yb_ratio parameters, and they will be scaled in the fill area to
1116 define the fountain fills xa, ya, xb and yb parameters.
1120 As with colors, you can use lookup(name) or lookup(name1.name2) to
1121 have one element to inherit the fill of another.
1123 Imager::Graph defaults the fill combine value to C<'normal'>. This
1124 doesn't apply to simple color fills.
1126 =head2 Specifying numbers
1128 You can specify various numbers, usually representing the size of
1129 something, commonly text, but sometimes the length of a line or the
1132 You can use the same lookup mechanism as with colors and fills, but
1133 you can also scale values. For example, 'scale(0.5,text.size)' will
1134 return half the size of the normal text size.
1136 As with colors, this is used internally to scale graph elements based
1137 on the base text size. If you change the base text size then other
1138 graph elements will scale as well.
1140 =head2 Specifying other elements
1142 Other elements, such as fonts, or parameters for a filter, can also
1143 use the lookup(name) mechanism.
1145 =head1 INTERNAL METHODS
1147 Only useful if you need to fix bugs, add features or create a new
1156 back=> 'lookup(bg)',
1157 line=> 'lookup(fg)',
1160 color => 'lookup(fg)',
1161 font => 'lookup(font)',
1166 color => 'lookup(text.color)',
1167 font => 'lookup(text.font)',
1170 size => 'scale(text.size,2.0)',
1171 aa => 'lookup(text.aa)',
1174 color => 'lookup(text.color)',
1175 font => 'lookup(text.font)',
1176 aa => 'lookup(text.aa)',
1177 size => 'lookup(text.size)',
1178 patchsize => 'scale(legend.size,0.9)',
1179 patchgap => 'scale(legend.patchsize,0.3)',
1180 patchborder => 'lookup(line)',
1183 padding => 'scale(legend.size,0.3)',
1184 outsidepadding => 'scale(legend.padding,0.4)',
1187 color => 'lookup(text.color)',
1188 font => 'lookup(text.font)',
1189 size => 'lookup(text.size)',
1190 line => 'lookup(line)',
1191 inside => 'lookup(callout.size)',
1192 outside => 'lookup(callout.size)',
1193 leadlen => 'scale(0.8,callout.size)',
1194 gap => 'scale(callout.size,0.3)',
1195 aa => 'lookup(text.aa)',
1196 lineaa => 'lookup(lineaa)',
1199 font => 'lookup(text.font)',
1200 size => 'lookup(text.size)',
1201 color => 'lookup(text.color)',
1202 hpad => 'lookup(label.pad)',
1203 vpad => 'lookup(label.pad)',
1204 pad => 'scale(label.size,0.2)',
1205 pcformat => sub { sprintf "%s (%.0f%%)", $_[0], $_[1] },
1206 pconlyformat => sub { sprintf "%.1f%%", $_[0] },
1207 aa => 'lookup(text.aa)',
1208 lineaa => 'lookup(lineaa)',
1211 fill => { solid => Imager::Color->new(0, 0, 0, 96) },
1212 off => 'scale(0.4,text.size)',
1213 offx => 'lookup(dropshadow.off)',
1214 offy => 'lookup(dropshadow.off)',
1215 filter => { type=>'conv',
1216 # this needs a fairly heavy blur
1217 coef=>[0.1, 0.2, 0.4, 0.6, 0.7, 0.9, 1.2,
1218 0.9, 0.7, 0.6, 0.4, 0.2, 0.1 ] },
1221 line =>'lookup(line)',
1222 lineaa => 'lookup(lineaa)',
1225 width=>'scale(1.5,size)',
1226 height=>'lookup(size)',
1228 # yes, the handling of fill and line AA is inconsistent, lack of
1229 # forethought, unfortunately
1233 lineaa => 'lookup(aa)',
1236 =item _error($message)
1238 Sets the error field of the object and returns an empty list or undef,
1239 depending on context. Should be used for error handling, since it may
1240 provide some user hooks at some point.
1242 The intended usage is:
1245 or return $self->_error("error description");
1247 You should almost always return the result of _error() or return
1248 immediately afterwards.
1253 my ($self, $error) = @_;
1255 $self->{_errstr} = $error;
1263 Returns the style defaults, such as the relationships between line
1264 color and text color.
1266 Intended to be over-ridden by base classes to provide graph specific
1275 # Let's make the default something that looks really good, so folks will be interested enough to customize the style.
1276 my $def_style = 'fount_lin';
1284 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1287 negative_bg=>'EEEEEE',
1291 #patchborder=>'000000'
1298 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1301 negative_bg=>'EEEEEE',
1305 patchborder=>'000000'
1312 { hatch=>'slash2' },
1313 { hatch=>'slosh2' },
1314 { hatch=>'vline2' },
1315 { hatch=>'hline2' },
1316 { hatch=>'cross2' },
1318 { hatch=>'stipple3' },
1319 { hatch=>'stipple2' },
1324 negative_bg=>'EEEEEE',
1325 features=>{ outline=>1 },
1335 { fountain=>'linear',
1336 xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
1337 segments => Imager::Fountain->simple(positions=>[0, 1],
1338 colors=>[ NC('FFC0C0'), NC('FF0000') ]),
1340 { fountain=>'linear',
1341 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1342 segments => Imager::Fountain->simple(positions=>[0, 1],
1343 colors=>[ NC('C0FFC0'), NC('00FF00') ]),
1345 { fountain=>'linear',
1346 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1347 segments => Imager::Fountain->simple(positions=>[0, 1],
1348 colors=>[ NC('C0C0FF'), NC('0000FF') ]),
1350 { fountain=>'linear',
1351 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1352 segments => Imager::Fountain->simple(positions=>[0, 1],
1353 colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
1355 { fountain=>'linear',
1356 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1357 segments => Imager::Fountain->simple(positions=>[0, 1],
1358 colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
1360 { fountain=>'linear',
1361 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1362 segments => Imager::Fountain->simple(positions=>[0, 1],
1363 colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
1367 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1370 { shape => 'circle', radius => 4 },
1371 { shape => 'square', radius => 4 },
1372 { shape => 'diamond', radius => 4 },
1373 { shape => 'triangle', radius => 4 },
1374 { shape => 'x', radius => 4 },
1375 { shape => 'plus', radius => 4 },
1377 back=>{ fountain=>'linear',
1378 xa_ratio=>0, ya_ratio=>0,
1379 xb_ratio=>1.0, yb_ratio=>1.0,
1380 segments=>Imager::Fountain->simple
1381 ( positions=>[0, 1],
1382 colors=>[ NC('6060FF'), NC('60FF60') ]) },
1384 negative_bg=>'EEEEEE',
1386 features=>{ dropshadow=>1 },
1392 { fountain=>'radial',
1393 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1394 segments => Imager::Fountain->simple(positions=>[0, 1],
1395 colors=>[ NC('FF8080'), NC('FF0000') ]),
1397 { fountain=>'radial',
1398 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1399 segments => Imager::Fountain->simple(positions=>[0, 1],
1400 colors=>[ NC('80FF80'), NC('00FF00') ]),
1402 { fountain=>'radial',
1403 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1404 segments => Imager::Fountain->simple(positions=>[0, 1],
1405 colors=>[ NC('808080FF'), NC('0000FF') ]),
1407 { fountain=>'radial',
1408 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1409 segments => Imager::Fountain->simple(positions=>[0, 1],
1410 colors=>[ NC('FFFF80'), NC('FFFF00') ]),
1412 { fountain=>'radial',
1413 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1414 segments => Imager::Fountain->simple(positions=>[0, 1],
1415 colors=>[ NC('80FFFF'), NC('00FFFF') ]),
1417 { fountain=>'radial',
1418 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1419 segments => Imager::Fountain->simple(positions=>[0, 1],
1420 colors=>[ NC('FF80FF'), NC('FF00FF') ]),
1424 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1426 back=>{ fountain=>'linear',
1427 xa_ratio=>0, ya_ratio=>0,
1428 xb_ratio=>1.0, yb_ratio=>1.0,
1429 segments=>Imager::Fountain->simple
1430 ( positions=>[0, 1],
1431 colors=>[ NC('6060FF'), NC('60FF60') ]) },
1433 negative_bg=>'EEEEEE',
1438 $styles{'ocean'} = {
1441 fountain =>'linear',
1442 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1443 segments => Imager::Fountain->simple(
1445 colors=>[ NC('EFEDCF'), NC('E6E2AF') ]),
1448 fountain =>'linear',
1449 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1450 segments => Imager::Fountain->simple(
1452 colors=>[ NC('DCD7AB'), NC('A7A37E') ]),
1455 fountain =>'linear',
1456 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1457 segments => Imager::Fountain->simple(
1459 colors=>[ NC('B2E5D4'), NC('80B4A2') ]),
1462 fountain =>'linear',
1463 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1464 segments => Imager::Fountain->simple(
1466 colors=>[ NC('7aaab9'), NC('046380') ]),
1469 fountain =>'linear',
1470 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1471 segments => Imager::Fountain->simple(
1473 colors=>[ NC('c3b8e9'), NC('877EA7') ]),
1476 fountain =>'linear',
1477 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1478 segments => Imager::Fountain->simple(
1480 colors=>[ NC('A3DF9A'), NC('67A35E') ]),
1483 fountain =>'linear',
1484 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1485 segments => Imager::Fountain->simple(
1487 colors=>[ NC('E19C98'), NC('B4726F') ]),
1491 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1494 negative_bg=>'EEEEEE',
1496 features=>{ dropshadow=>1 },
1500 $styles{'ocean_flat'} = {
1503 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1506 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1509 negative_bg=>'EEEEEE',
1511 features=>{ dropshadow=>1 },
1516 =item $self->_style_setup(\%opts)
1518 Uses the values from %opts to build a customized hash describing the
1519 way the graph should be drawn.
1524 my ($self, $opts) = @_;
1525 my $style_defs = $self->_style_defs;
1528 my $pre_def_style = $self->_get_style($opts);
1529 my $api_style = $self->{'custom_style'} || {};
1530 $style = $styles{$pre_def_style} if $pre_def_style;
1532 $style ||= $styles{$def_style};
1534 my @search_list = ( $style_defs, $style, $api_style, $opts);
1537 my @composite = $self->_composite();
1539 @composite{@composite} = @composite;
1541 for my $src (@search_list) {
1542 for my $key (keys %$src) {
1543 if ($composite{$key}) {
1544 $work{$key} = {} unless exists $work{$key};
1545 if (ref $src->{$key}) {
1546 # some keys have sub values, especially text
1547 @{$work{$key}}{keys %{$src->{$key}}} = values %{$src->{$key}};
1550 # assume it's the text for a title or something
1551 $work{$key}{text} = $src->{$key};
1555 $work{$key} = $src->{$key}
1556 if defined $src->{$key}; # $opts with pmichauds new accessor handling
1561 # features are handled specially
1562 $work{features} = {};
1563 for my $src (@search_list) {
1564 if ($src->{features}) {
1565 if (ref $src->{features}) {
1566 if (ref($src->{features}) =~ /ARRAY/) {
1567 # just set those features
1568 for my $feature (@{$src->{features}}) {
1569 $work{features}{$feature} = 1;
1572 elsif (ref($src->{features}) =~ /HASH/) {
1573 if ($src->{features}{reset}) {
1574 $work{features} = {}; # only the ones the user specifies
1576 @{$work{features}}{keys %{$src->{features}}} =
1577 values(%{$src->{features}});
1581 # just set that single feature
1582 $work{features}{$src->{features}} = 1;
1587 #print Dumper(\%work);
1589 $self->{_style} = \%work;
1592 =item $self->_get_thing($name)
1594 Retrieve some general 'thing'.
1596 Supports the 'lookup(foo)' mechanism.
1598 Returns an empty list on failure.
1603 my ($self, $name, @depth) = @_;
1605 push(@depth, $name);
1607 if ($name =~ /^(\w+)\.(\w+)$/) {
1608 $what = $self->{_style}{$1}{$2};
1611 $what = $self->{_style}{$name};
1618 elsif ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1620 or return $self->_error("too many levels of recursion in lookup(@depth)");
1621 return $self->_get_thing($1, @depth);
1628 =item $self->_get_number($name)
1630 Retrieves a number from the style. The value in the style can be the
1631 number, or one of two functions:
1635 =item lookup(newname)
1637 Recursively looks up I<newname> in the style.
1639 =item scale(value1,value2)
1641 Each value can be a number or a name. Names are recursively looked up
1642 in the style and the product is returned.
1649 my ($self, $name, @depth) = @_;
1651 push(@depth, $name);
1653 if ($name =~ /^(\w+)\.(\w+)$/) {
1654 $what = $self->{_style}{$1}{$2};
1657 $what = $self->{_style}{$name};
1660 return $self->_error("$name is undef (@depth)");
1663 if ($what =~ /CODE/) {
1664 $what = $what->($self, $name);
1668 if ($what =~ /^lookup\(([\w.]+)\)$/) {
1670 or return $self->_error("too many levels of recursion in lookup (@depth)");
1671 return $self->_get_number($1, @depth);
1673 elsif ($what =~ /^scale\(
1674 ((?:[a-z][\w.]*)|$NUM_RE)
1676 ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
1677 my ($left, $right) = ($1, $2);
1678 unless ($left =~ /^$NUM_RE$/) {
1680 or return $self->_error("too many levels of recursion in scale (@depth)");
1681 $left = $self->_get_number($left, @depth);
1683 unless ($right =~ /^$NUM_RE$/) {
1685 or return $self->_error("too many levels of recursion in scale (@depth)");
1686 $right = $self->_get_number($right, @depth);
1688 return $left * $right;
1696 =item $self->_get_integer($name)
1698 Retrieves an integer from the style. This is a simple wrapper around
1699 _get_number() that rounds the result to an integer.
1701 Returns an empty list on failure.
1706 my ($self, $name, @depth) = @_;
1708 my $number = $self->_get_number($name, @depth)
1711 return sprintf("%.0f", $number);
1714 =item _get_color($name)
1716 Returns a color object of the given name from the style hash.
1718 Uses Imager::Color->new to translate normal scalars into color objects.
1720 Allows the lookup(name) mechanism.
1722 Returns an empty list on failure.
1727 my ($self, $name, @depth) = @_;
1729 push(@depth, $name);
1731 if ($name =~ /^(\w+)\.(\w+)$/) {
1732 $what = $self->{_style}{$1}{$2};
1735 $what = $self->{_style}{$name};
1739 or return $self->_error("$name was undefined (@depth)");
1741 unless (ref $what) {
1742 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1743 @depth < MAX_DEPTH or
1744 return $self->_error("too many levels of recursion in lookup (@depth)");
1746 return $self->_get_color($1, @depth);
1748 $what = Imager::Color->new($what);
1754 =item _translate_fill($what, $box)
1756 Given the value of a fill, either attempts to convert it into a fill
1757 list (one of C<<color=>$color_value, filled=>1>> or C<<fill=>{ fill
1758 parameters }>>), or to lookup another fill that is referred to with
1759 the 'lookup(name)' mechanism.
1761 This function does the fg and bg initialization for hatched fills, and
1762 translation of *_ratio for fountain fills (using the $box parameter).
1764 Returns an empty list on failure.
1768 sub _translate_fill {
1769 my ($self, $what, $box, @depth) = @_;
1772 if (UNIVERSAL::isa($what, "Imager::Color")) {
1773 return ( color=>Imager::Color->new($what), filled=>1 );
1777 # default to normal combine mode
1778 my %work = ( combine => 'normal', %$what );
1779 if ($what->{hatch}) {
1781 $work{fg} = $self->_get_color('fg')
1785 $work{bg} = $self->_get_color('bg')
1788 return ( fill=>\%work );
1790 elsif ($what->{fountain}) {
1791 for my $key (qw(xa ya xb yb)) {
1792 if (exists $work{"${key}_ratio"}) {
1794 $work{$key} = $box->[0] + $work{"${key}_ratio"}
1795 * ($box->[2] - $box->[0]);
1798 $work{$key} = $box->[1] + $work{"${key}_ratio"}
1799 * ($box->[3] - $box->[1]);
1803 return ( fill=>\%work );
1806 return ( fill=> \%work );
1811 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1812 return $self->_get_fill($1, $box, @depth);
1815 # assumed to be an Imager::Color single value
1816 return ( color=>Imager::Color->new($what), filled=>1 );
1821 =item _data_fill($index, $box)
1823 Retrieves the fill parameters for a data area fill.
1828 my ($self, $index, $box) = @_;
1830 my $fills = $self->{_style}{fills};
1831 return $self->_translate_fill($fills->[$index % @$fills], $box,
1836 my ($self, $index) = @_;
1838 my $colors = $self->{'_style'}{'colors'} || [];
1839 my $fills = $self->{'_style'}{'fills'} || [];
1841 # Try to just use a fill, so non-fountain styles don't need
1842 # to have a duplicated set of fills and colors
1843 my $fill = $fills->[$index % @$fills];
1849 return $colors->[$index % @$colors] || '000000';
1854 =item _get_fill($index, $box)
1856 Retrieves fill parameters for a named fill.
1861 my ($self, $name, $box, @depth) = @_;
1863 push(@depth, $name);
1865 if ($name =~ /^(\w+)\.(\w+)$/) {
1866 $what = $self->{_style}{$1}{$2};
1869 $what = $self->{_style}{$name};
1873 or return $self->_error("no fill $name found");
1875 return $self->_translate_fill($what, $box, @depth);
1880 Builds the image object for the graph and fills it with the background
1888 my $width = $self->_get_number('width') || 256;
1889 my $height = $self->_get_number('height') || 256;
1890 my $channels = $self->{_style}{channels};
1894 my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels);
1896 $img->box($self->_get_fill('back', [ 0, 0, $width-1, $height-1]));
1904 if (!$self->{'_image'}) {
1905 $self->{'_image'} = $self->_make_img();
1907 return $self->{'_image'};
1910 =item _text_style($name)
1912 Returns parameters suitable for calls to Imager::Font's bounding_box()
1913 and draw() methods intended for use in defining text styles.
1915 Returns an empty list on failure.
1917 Returns the following attributes: font, color, size, aa, sizew
1923 my ($self, $name) = @_;
1927 if ($self->{_style}{$name}) {
1928 %work = %{$self->{_style}{$name}};
1931 %work = %{$self->{_style}{text}};
1934 or return $self->_error("$name has no font parameter");
1936 $work{font} = $self->_get_thing("$name.font")
1937 or return $self->_error("No $name.font defined, either set $name.font or font to a font");
1938 UNIVERSAL::isa($work{font}, "Imager::Font")
1939 or return $self->_error("$name.font is not a font");
1940 if ($work{color} && !ref $work{color}) {
1941 $work{color} = $self->_get_color("$name.color")
1944 $work{size} = $self->_get_number("$name.size");
1945 $work{sizew} = $self->_get_number("$name.sizew")
1947 $work{aa} = $self->_get_number("$name.aa");
1952 =item _text_bbox($text, $name)
1954 Returns a bounding box for the specified $text as styled by $name.
1956 Returns an empty list on failure.
1961 my ($self, $text, $name) = @_;
1963 my %text_info = $self->_text_style($name)
1966 my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
1972 =item _line_style($name)
1974 Return parameters suitable for calls to Imager's line(), polyline(),
1977 For now this returns only color and aa parameters, but future releases
1978 of Imager may support extra parameters.
1983 my ($self, $name) = @_;
1986 $line{color} = $self->_get_color("$name.line")
1988 $line{aa} = $self->_get_number("$name.lineaa");
1989 defined $line{aa} or $line{aa} = $self->_get_number("aa");
1995 my ($self, $box, $chart_box, $name) = @_;
1997 my $halign = $self->{_style}{$name}{halign}
1998 or $self->_error("no halign for $name");
1999 my $valign = $self->{_style}{$name}{valign};
2001 if ($halign eq 'right') {
2002 $box->[0] += $chart_box->[2] - $box->[2];
2004 elsif ($halign eq 'left') {
2005 $box->[0] = $chart_box->[0];
2007 elsif ($halign eq 'center' || $halign eq 'centre') {
2008 $box->[0] = ($chart_box->[0] + $chart_box->[2] - $box->[2])/2;
2011 return $self->_error("invalid halign $halign for $name");
2014 if ($valign eq 'top') {
2015 $box->[1] = $chart_box->[1];
2017 elsif ($valign eq 'bottom') {
2018 $box->[1] = $chart_box->[3] - $box->[3];
2020 elsif ($valign eq 'center' || $valign eq 'centre') {
2021 $box->[1] = ($chart_box->[1] + $chart_box->[3] - $box->[3])/2;
2024 return $self->_error("invalid valign $valign for $name");
2026 $box->[2] += $box->[0];
2027 $box->[3] += $box->[1];
2031 my ($self, $chart_box, $object_box) = @_;
2035 if ($object_box->[0] - $chart_box->[0]
2036 < $chart_box->[2] - $object_box->[2]) {
2037 $areax = ($object_box->[2] - $chart_box->[0])
2038 * ($chart_box->[3] - $chart_box->[1]);
2041 $areax = ($chart_box->[2] - $object_box->[0])
2042 * ($chart_box->[3] - $chart_box->[1]);
2045 if ($object_box->[1] - $chart_box->[1]
2046 < $chart_box->[3] - $object_box->[3]) {
2047 $areay = ($object_box->[3] - $chart_box->[1])
2048 * ($chart_box->[2] - $chart_box->[0]);
2051 $areay = ($chart_box->[3] - $object_box->[1])
2052 * ($chart_box->[2] - $chart_box->[0]);
2055 if ($areay < $areax) {
2056 if ($object_box->[1] - $chart_box->[1]
2057 < $chart_box->[3] - $object_box->[3]) {
2058 $chart_box->[1] = $object_box->[3];
2061 $chart_box->[3] = $object_box->[1];
2065 if ($object_box->[0] - $chart_box->[0]
2066 < $chart_box->[2] - $object_box->[2]) {
2067 $chart_box->[0] = $object_box->[2];
2070 $chart_box->[2] = $object_box->[0];
2076 my ($self, $img, $labels, $chart_box) = @_;
2078 my $orient = $self->_get_thing('legend.orientation');
2079 defined $orient or $orient = 'vertical';
2081 if ($orient eq 'vertical') {
2082 return $self->_draw_legend_vertical($img, $labels, $chart_box);
2084 elsif ($orient eq 'horizontal') {
2085 return $self->_draw_legend_horizontal($img, $labels, $chart_box);
2088 return $self->_error("Unknown legend.orientation $orient");
2092 sub _draw_legend_horizontal {
2093 my ($self, $img, $labels, $chart_box) = @_;
2095 defined(my $padding = $self->_get_integer('legend.padding'))
2097 my $patchsize = $self->_get_integer('legend.patchsize')
2099 defined(my $gap = $self->_get_integer('legend.patchgap'))
2102 my $minrowsize = $patchsize + $gap;
2103 my ($width, $height) = (0,0);
2104 my $row_height = $minrowsize;
2108 for my $label (@$labels) {
2109 my @text_box = $self->_text_bbox($label, 'legend')
2111 push(@sizes, \@text_box);
2112 my $entry_width = $patchsize + $gap + $text_box[2];
2114 # never re-wrap the first entry
2115 push @offsets, [ 0, $height ];
2118 if ($pos + $gap + $entry_width > $chart_box->[2]) {
2120 $height += $row_height;
2122 push @offsets, [ $pos, $height ];
2124 my $entry_right = $pos + $entry_width;
2125 $pos += $gap + $entry_width;
2126 $entry_right > $width and $width = $entry_right;
2127 if ($text_box[3] > $row_height) {
2128 $row_height = $text_box[3];
2131 $height += $row_height;
2132 my @box = ( 0, 0, $width + $padding * 2, $height + $padding * 2 );
2133 my $outsidepadding = 0;
2134 if ($self->{_style}{legend}{border}) {
2135 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
2137 $box[2] += 2 * $outsidepadding;
2138 $box[3] += 2 * $outsidepadding;
2140 $self->_align_box(\@box, $chart_box, 'legend')
2142 if ($self->{_style}{legend}{fill}) {
2143 $img->box(xmin=>$box[0]+$outsidepadding,
2144 ymin=>$box[1]+$outsidepadding,
2145 xmax=>$box[2]-$outsidepadding,
2146 ymax=>$box[3]-$outsidepadding,
2147 $self->_get_fill('legend.fill', \@box));
2149 $box[0] += $outsidepadding;
2150 $box[1] += $outsidepadding;
2151 $box[2] -= $outsidepadding;
2152 $box[3] -= $outsidepadding;
2153 my %text_info = $self->_text_style('legend')
2156 if ($self->{_style}{legend}{patchborder}) {
2157 $patchborder = $self->_get_color('legend.patchborder')
2162 for my $label (@$labels) {
2163 my ($left, $top) = @{$offsets[$dataindex]};
2164 $left += $box[0] + $padding;
2165 $top += $box[1] + $padding;
2166 my $textpos = $left + $patchsize + $gap;
2167 my @patchbox = ( $left, $top,
2168 $left + $patchsize, $top + $patchsize );
2169 my @fill = $self->_data_fill($dataindex, \@patchbox)
2171 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
2172 ymax=>$top + $patchsize, @fill);
2173 if ($self->{_style}{legend}{patchborder}) {
2174 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
2175 ymax=>$top + $patchsize,
2176 color=>$patchborder);
2178 $img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize,
2183 if ($self->{_style}{legend}{border}) {
2184 my $border_color = $self->_get_color('legend.border')
2186 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
2187 color=>$border_color);
2189 $self->_remove_box($chart_box, \@box);
2193 sub _draw_legend_vertical {
2194 my ($self, $img, $labels, $chart_box) = @_;
2196 defined(my $padding = $self->_get_integer('legend.padding'))
2198 my $patchsize = $self->_get_integer('legend.patchsize')
2200 defined(my $gap = $self->_get_integer('legend.patchgap'))
2202 my $minrowsize = $patchsize + $gap;
2203 my ($width, $height) = (0,0);
2205 for my $label (@$labels) {
2206 my @box = $self->_text_bbox($label, 'legend')
2208 push(@sizes, \@box);
2209 $width = $box[2] if $box[2] > $width;
2210 if ($minrowsize > $box[3]) {
2211 $height += $minrowsize;
2218 $width + $patchsize + $padding * 2 + $gap,
2219 $height + $padding * 2 - $gap);
2220 my $outsidepadding = 0;
2221 if ($self->{_style}{legend}{border}) {
2222 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
2224 $box[2] += 2 * $outsidepadding;
2225 $box[3] += 2 * $outsidepadding;
2227 $self->_align_box(\@box, $chart_box, 'legend')
2229 if ($self->{_style}{legend}{fill}) {
2230 $img->box(xmin=>$box[0]+$outsidepadding,
2231 ymin=>$box[1]+$outsidepadding,
2232 xmax=>$box[2]-$outsidepadding,
2233 ymax=>$box[3]-$outsidepadding,
2234 $self->_get_fill('legend.fill', \@box));
2236 $box[0] += $outsidepadding;
2237 $box[1] += $outsidepadding;
2238 $box[2] -= $outsidepadding;
2239 $box[3] -= $outsidepadding;
2240 my $ypos = $box[1] + $padding;
2241 my $patchpos = $box[0]+$padding;
2242 my $textpos = $patchpos + $patchsize + $gap;
2243 my %text_info = $self->_text_style('legend')
2246 if ($self->{_style}{legend}{patchborder}) {
2247 $patchborder = $self->_get_color('legend.patchborder')
2251 for my $label (@$labels) {
2252 my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
2253 $patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
2256 if ($self->_draw_flat_legend()) {
2257 @fill = (color => $self->_data_color($dataindex), filled => 1);
2260 @fill = $self->_data_fill($dataindex, \@patchbox)
2263 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
2264 ymax=>$ypos + $patchsize, @fill);
2265 if ($self->{_style}{legend}{patchborder}) {
2266 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
2267 ymax=>$ypos + $patchsize,
2268 color=>$patchborder);
2270 $img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize,
2273 my $step = $patchsize + $gap;
2274 if ($minrowsize < $sizes[$dataindex][3]) {
2275 $ypos += $sizes[$dataindex][3];
2278 $ypos += $minrowsize;
2282 if ($self->{_style}{legend}{border}) {
2283 my $border_color = $self->_get_color('legend.border')
2285 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
2286 color=>$border_color);
2288 $self->_remove_box($chart_box, \@box);
2293 my ($self, $img, $chart_box) = @_;
2295 my $title = $self->{_style}{title}{text};
2296 my @box = $self->_text_bbox($title, 'title')
2300 $self->_align_box(\@box, $chart_box, 'title');
2301 my %text_info = $self->_text_style('title')
2303 $img->string(%text_info, x=>$box[0], 'y'=>$box[3] + $yoff, text=>$title);
2304 $self->_remove_box($chart_box, \@box);
2309 my ($self, $box) = @_;
2311 if ($box->[2] - $box->[0] > $box->[3] - $box->[1]) {
2312 return $box->[3] - $box->[1];
2315 return $box->[2] - $box->[0];
2319 sub _draw_flat_legend {
2325 Returns a list of style fields that are stored as composites, and
2326 should be merged instead of just being replaced.
2331 qw(title legend text label dropshadow outline callout);
2334 sub _filter_region {
2335 my ($self, $img, $left, $top, $right, $bottom, $filter) = @_;
2337 unless (ref $filter) {
2339 $filter = $self->_get_thing($name)
2342 or return $self->_error("no type for filter $name");
2345 $left > 0 or $left = 0;
2346 $top > 0 or $top = 0;
2348 # newer versions of Imager let you work on just part of an image
2349 if ($img->can('masked') && !$self->{_style}{features}{_debugblur}) {
2350 my $masked = $img->masked(left=>$left, top=>$top,
2351 right=>$right, bottom=>$bottom);
2352 $masked->filter(%$filter);
2355 # for older versions of Imager
2356 my $subset = $img->crop(left=>$left, top=>$top,
2357 right=>$right, bottom=>$bottom);
2358 $subset->filter(%$filter);
2359 $img->paste(left=>$left, top=>$top, img=>$subset);
2370 Imager::Graph::Pie(3), Imager(3), perl(1).
2374 Tony Cook <tony@develop-help.com>
2378 Imager::Graph is licensed under the same terms as perl itself.
2382 Addi for producing a cool imaging module. :)