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 ] },
1220 # controls the outline of graph elements representing data, eg. pie
1221 # slices, bars or columns
1223 line =>'lookup(line)',
1224 lineaa => 'lookup(lineaa)',
1226 # controls the outline and background of the data area of the chart
1229 fill => "lookup(bg)",
1230 outline => "lookup(fg)",
1233 width=>'scale(1.5,size)',
1234 height=>'lookup(size)',
1236 # yes, the handling of fill and line AA is inconsistent, lack of
1237 # forethought, unfortunately
1241 lineaa => 'lookup(aa)',
1244 =item _error($message)
1246 Sets the error field of the object and returns an empty list or undef,
1247 depending on context. Should be used for error handling, since it may
1248 provide some user hooks at some point.
1250 The intended usage is:
1253 or return $self->_error("error description");
1255 You should almost always return the result of _error() or return
1256 immediately afterwards.
1261 my ($self, $error) = @_;
1263 $self->{_errstr} = $error;
1271 Returns the style defaults, such as the relationships between line
1272 color and text color.
1274 Intended to be over-ridden by base classes to provide graph specific
1283 # Let's make the default something that looks really good, so folks will be interested enough to customize the style.
1284 my $def_style = 'fount_lin';
1292 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1295 negative_bg=>'EEEEEE',
1299 #patchborder=>'000000'
1306 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1309 negative_bg=>'EEEEEE',
1313 patchborder=>'000000'
1320 { hatch=>'slash2' },
1321 { hatch=>'slosh2' },
1322 { hatch=>'vline2' },
1323 { hatch=>'hline2' },
1324 { hatch=>'cross2' },
1326 { hatch=>'stipple3' },
1327 { hatch=>'stipple2' },
1332 negative_bg=>'EEEEEE',
1333 features=>{ outline=>1 },
1343 { fountain=>'linear',
1344 xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
1345 segments => Imager::Fountain->simple(positions=>[0, 1],
1346 colors=>[ NC('FFC0C0'), NC('FF0000') ]),
1348 { fountain=>'linear',
1349 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1350 segments => Imager::Fountain->simple(positions=>[0, 1],
1351 colors=>[ NC('C0FFC0'), NC('00FF00') ]),
1353 { fountain=>'linear',
1354 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1355 segments => Imager::Fountain->simple(positions=>[0, 1],
1356 colors=>[ NC('C0C0FF'), NC('0000FF') ]),
1358 { fountain=>'linear',
1359 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1360 segments => Imager::Fountain->simple(positions=>[0, 1],
1361 colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
1363 { fountain=>'linear',
1364 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1365 segments => Imager::Fountain->simple(positions=>[0, 1],
1366 colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
1368 { fountain=>'linear',
1369 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1370 segments => Imager::Fountain->simple(positions=>[0, 1],
1371 colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
1375 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1378 { shape => 'circle', radius => 4 },
1379 { shape => 'square', radius => 4 },
1380 { shape => 'diamond', radius => 4 },
1381 { shape => 'triangle', radius => 4 },
1382 { shape => 'x', radius => 4 },
1383 { shape => 'plus', radius => 4 },
1385 back=>{ fountain=>'linear',
1386 xa_ratio=>0, ya_ratio=>0,
1387 xb_ratio=>1.0, yb_ratio=>1.0,
1388 segments=>Imager::Fountain->simple
1389 ( positions=>[0, 1],
1390 colors=>[ NC('6060FF'), NC('60FF60') ]) },
1392 negative_bg=>'EEEEEE',
1394 features=>{ dropshadow=>1 },
1400 { fountain=>'radial',
1401 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1402 segments => Imager::Fountain->simple(positions=>[0, 1],
1403 colors=>[ NC('FF8080'), NC('FF0000') ]),
1405 { fountain=>'radial',
1406 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1407 segments => Imager::Fountain->simple(positions=>[0, 1],
1408 colors=>[ NC('80FF80'), NC('00FF00') ]),
1410 { fountain=>'radial',
1411 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1412 segments => Imager::Fountain->simple(positions=>[0, 1],
1413 colors=>[ NC('808080FF'), NC('0000FF') ]),
1415 { fountain=>'radial',
1416 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1417 segments => Imager::Fountain->simple(positions=>[0, 1],
1418 colors=>[ NC('FFFF80'), NC('FFFF00') ]),
1420 { fountain=>'radial',
1421 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1422 segments => Imager::Fountain->simple(positions=>[0, 1],
1423 colors=>[ NC('80FFFF'), NC('00FFFF') ]),
1425 { fountain=>'radial',
1426 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1427 segments => Imager::Fountain->simple(positions=>[0, 1],
1428 colors=>[ NC('FF80FF'), NC('FF00FF') ]),
1432 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1434 back=>{ fountain=>'linear',
1435 xa_ratio=>0, ya_ratio=>0,
1436 xb_ratio=>1.0, yb_ratio=>1.0,
1437 segments=>Imager::Fountain->simple
1438 ( positions=>[0, 1],
1439 colors=>[ NC('6060FF'), NC('60FF60') ]) },
1441 negative_bg=>'EEEEEE',
1446 $styles{'ocean'} = {
1449 fountain =>'linear',
1450 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1451 segments => Imager::Fountain->simple(
1453 colors=>[ NC('EFEDCF'), NC('E6E2AF') ]),
1456 fountain =>'linear',
1457 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1458 segments => Imager::Fountain->simple(
1460 colors=>[ NC('DCD7AB'), NC('A7A37E') ]),
1463 fountain =>'linear',
1464 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1465 segments => Imager::Fountain->simple(
1467 colors=>[ NC('B2E5D4'), NC('80B4A2') ]),
1470 fountain =>'linear',
1471 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1472 segments => Imager::Fountain->simple(
1474 colors=>[ NC('7aaab9'), NC('046380') ]),
1477 fountain =>'linear',
1478 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1479 segments => Imager::Fountain->simple(
1481 colors=>[ NC('c3b8e9'), NC('877EA7') ]),
1484 fountain =>'linear',
1485 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1486 segments => Imager::Fountain->simple(
1488 colors=>[ NC('A3DF9A'), NC('67A35E') ]),
1491 fountain =>'linear',
1492 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1493 segments => Imager::Fountain->simple(
1495 colors=>[ NC('E19C98'), NC('B4726F') ]),
1499 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1502 negative_bg=>'EEEEEE',
1504 features=>{ dropshadow=>1 },
1508 $styles{'ocean_flat'} = {
1511 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1514 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1517 negative_bg=>'EEEEEE',
1519 features=>{ dropshadow=>1 },
1524 =item $self->_style_setup(\%opts)
1526 Uses the values from %opts to build a customized hash describing the
1527 way the graph should be drawn.
1532 my ($self, $opts) = @_;
1533 my $style_defs = $self->_style_defs;
1536 my $pre_def_style = $self->_get_style($opts);
1537 my $api_style = $self->{'custom_style'} || {};
1538 $style = $styles{$pre_def_style} if $pre_def_style;
1540 $style ||= $styles{$def_style};
1542 my @search_list = ( $style_defs, $style, $api_style, $opts);
1545 my @composite = $self->_composite();
1547 @composite{@composite} = @composite;
1549 for my $src (@search_list) {
1550 for my $key (keys %$src) {
1551 if ($composite{$key}) {
1552 $work{$key} = {} unless exists $work{$key};
1553 if (ref $src->{$key}) {
1554 # some keys have sub values, especially text
1555 @{$work{$key}}{keys %{$src->{$key}}} = values %{$src->{$key}};
1558 # assume it's the text for a title or something
1559 $work{$key}{text} = $src->{$key};
1563 $work{$key} = $src->{$key}
1564 if defined $src->{$key}; # $opts with pmichauds new accessor handling
1569 # features are handled specially
1571 $work{features} = \%features;
1572 for my $src (@search_list) {
1573 if ($src->{features}) {
1574 if (ref $src->{features}) {
1575 if (ref($src->{features}) =~ /ARRAY/) {
1576 # just set those features
1577 for my $feature (@{$src->{features}}) {
1578 if ($feature =~ /^no(.+)$/) {
1579 delete $features{$1};
1582 $features{$feature} = 1;
1586 elsif (ref($src->{features}) =~ /HASH/) {
1587 if ($src->{features}{reset}) {
1588 $work{features} = {}; # only the ones the user specifies
1590 @{$work{features}}{keys %{$src->{features}}} =
1591 values(%{$src->{features}});
1595 # just set that single feature
1596 if ($src->{features} =~ /^no(.+)$/) {
1597 delete $features{$1};
1600 $features{$src->{features}} = 1;
1606 $self->{_style} = \%work;
1609 =item $self->_get_thing($name)
1611 Retrieve some general 'thing'.
1613 Supports the 'lookup(foo)' mechanism.
1615 Returns an empty list on failure.
1620 my ($self, $name, @depth) = @_;
1622 push(@depth, $name);
1624 if ($name =~ /^(\w+)\.(\w+)$/) {
1625 $what = $self->{_style}{$1}{$2};
1628 $what = $self->{_style}{$name};
1635 elsif ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1637 or return $self->_error("too many levels of recursion in lookup(@depth)");
1638 return $self->_get_thing($1, @depth);
1645 =item $self->_get_number($name)
1647 Retrieves a number from the style. The value in the style can be the
1648 number, or one of two functions:
1652 =item lookup(newname)
1654 Recursively looks up I<newname> in the style.
1656 =item scale(value1,value2)
1658 Each value can be a number or a name. Names are recursively looked up
1659 in the style and the product is returned.
1666 my ($self, $name, @depth) = @_;
1668 push(@depth, $name);
1670 if ($name =~ /^(\w+)\.(\w+)$/) {
1671 $what = $self->{_style}{$1}{$2};
1674 $what = $self->{_style}{$name};
1677 return $self->_error("$name is undef (@depth)");
1680 if ($what =~ /CODE/) {
1681 $what = $what->($self, $name);
1685 if ($what =~ /^lookup\(([\w.]+)\)$/) {
1687 or return $self->_error("too many levels of recursion in lookup (@depth)");
1688 return $self->_get_number($1, @depth);
1690 elsif ($what =~ /^scale\(
1691 ((?:[a-z][\w.]*)|$NUM_RE)
1693 ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
1694 my ($left, $right) = ($1, $2);
1695 unless ($left =~ /^$NUM_RE$/) {
1697 or return $self->_error("too many levels of recursion in scale (@depth)");
1698 $left = $self->_get_number($left, @depth);
1700 unless ($right =~ /^$NUM_RE$/) {
1702 or return $self->_error("too many levels of recursion in scale (@depth)");
1703 $right = $self->_get_number($right, @depth);
1705 return $left * $right;
1713 =item $self->_get_integer($name)
1715 Retrieves an integer from the style. This is a simple wrapper around
1716 _get_number() that rounds the result to an integer.
1718 Returns an empty list on failure.
1723 my ($self, $name, @depth) = @_;
1725 my $number = $self->_get_number($name, @depth)
1728 return sprintf("%.0f", $number);
1731 =item _get_color($name)
1733 Returns a color object of the given name from the style hash.
1735 Uses Imager::Color->new to translate normal scalars into color objects.
1737 Allows the lookup(name) mechanism.
1739 Returns an empty list on failure.
1744 my ($self, $name, @depth) = @_;
1746 push(@depth, $name);
1748 if ($name =~ /^(\w+)\.(\w+)$/) {
1749 $what = $self->{_style}{$1}{$2};
1752 $what = $self->{_style}{$name};
1756 or return $self->_error("$name was undefined (@depth)");
1758 unless (ref $what) {
1759 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1760 @depth < MAX_DEPTH or
1761 return $self->_error("too many levels of recursion in lookup (@depth)");
1763 return $self->_get_color($1, @depth);
1765 $what = Imager::Color->new($what);
1771 =item _translate_fill($what, $box)
1773 Given the value of a fill, either attempts to convert it into a fill
1774 list (one of C<<color=>$color_value, filled=>1>> or C<<fill=>{ fill
1775 parameters }>>), or to lookup another fill that is referred to with
1776 the 'lookup(name)' mechanism.
1778 This function does the fg and bg initialization for hatched fills, and
1779 translation of *_ratio for fountain fills (using the $box parameter).
1781 Returns an empty list on failure.
1785 sub _translate_fill {
1786 my ($self, $what, $box, @depth) = @_;
1789 if (UNIVERSAL::isa($what, "Imager::Color")) {
1790 return ( color=>Imager::Color->new($what), filled=>1 );
1794 # default to normal combine mode
1795 my %work = ( combine => 'normal', %$what );
1796 if ($what->{hatch}) {
1798 $work{fg} = $self->_get_color('fg')
1802 $work{bg} = $self->_get_color('bg')
1805 return ( fill=>\%work );
1807 elsif ($what->{fountain}) {
1808 for my $key (qw(xa ya xb yb)) {
1809 if (exists $work{"${key}_ratio"}) {
1811 $work{$key} = $box->[0] + $work{"${key}_ratio"}
1812 * ($box->[2] - $box->[0]);
1815 $work{$key} = $box->[1] + $work{"${key}_ratio"}
1816 * ($box->[3] - $box->[1]);
1820 return ( fill=>\%work );
1823 return ( fill=> \%work );
1828 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1829 return $self->_get_fill($1, $box, @depth);
1832 # assumed to be an Imager::Color single value
1833 return ( color=>Imager::Color->new($what), filled=>1 );
1838 =item _data_fill($index, $box)
1840 Retrieves the fill parameters for a data area fill.
1845 my ($self, $index, $box) = @_;
1847 my $fills = $self->{_style}{fills};
1848 return $self->_translate_fill($fills->[$index % @$fills], $box,
1853 my ($self, $index) = @_;
1855 my $colors = $self->{'_style'}{'colors'} || [];
1856 my $fills = $self->{'_style'}{'fills'} || [];
1858 # Try to just use a fill, so non-fountain styles don't need
1859 # to have a duplicated set of fills and colors
1860 my $fill = $fills->[$index % @$fills];
1866 return $colors->[$index % @$colors] || '000000';
1871 =item _get_fill($name, $box)
1873 Retrieves fill parameters for a named fill.
1878 my ($self, $name, $box, @depth) = @_;
1880 push(@depth, $name);
1882 if ($name =~ /^(\w+)\.(\w+)$/) {
1883 $what = $self->{_style}{$1}{$2};
1886 $what = $self->{_style}{$name};
1890 or return $self->_error("no fill $name found");
1892 return $self->_translate_fill($what, $box, @depth);
1895 =item _get_line($name)
1897 Return color (and possibly other) parameters for drawing a line with
1903 my ($self, $name, @depth) = @_;
1905 push (@depth, $name);
1907 if ($name =~ /^(\w+)\.(\w+)$/) {
1908 $what = $self->{_style}{$1}{$2};
1911 $what = $self->{_style}{$name};
1915 or return $self->_error("no line style $name found");
1918 if (eval { $what->isa("Imager::Color") }) {
1921 if (ref $what eq "HASH") {
1922 # allow each kep to be looked up
1925 if ($work{color} =~ /^lookup\((.*)\)$/) {
1926 $work{color} = $self->_get_color($1, @depth);
1928 for my $key (keys %work) {
1929 $key eq "color" and next;
1931 if ($work{$key} =~ /^lookup\((.*)\)$/) {
1932 $work{$key} = $self->_get_thing($1);
1938 return ( color => Imager::Color->new(@$what) );
1941 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1943 or return $self->_error("too many levels of recursion in lookup (@depth)");
1944 return $self->_get_line($1, @depth);
1947 # presumably a text color
1948 my $color = Imager::Color->new($what)
1949 or return $self->_error("Could not translate $what as a color: ".Imager->errstr);
1951 return ( color => $color );
1958 Builds the image object for the graph and fills it with the background
1966 my $width = $self->_get_number('width') || 256;
1967 my $height = $self->_get_number('height') || 256;
1968 my $channels = $self->{_style}{channels};
1972 my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels)
1973 or return $self->_error("Error creating image: " . Imager->errstr);
1975 $img->box($self->_get_fill('back', [ 0, 0, $width-1, $height-1]));
1977 $self->{_image} = $img;
1985 return $self->{'_image'};
1988 =item _text_style($name)
1990 Returns parameters suitable for calls to Imager::Font's bounding_box()
1991 and draw() methods intended for use in defining text styles.
1993 Returns an empty list on failure.
1995 Returns the following attributes: font, color, size, aa, sizew
2001 my ($self, $name) = @_;
2005 if ($self->{_style}{$name}) {
2006 %work = %{$self->{_style}{$name}};
2009 %work = %{$self->{_style}{text}};
2012 or return $self->_error("$name has no font parameter");
2014 $work{font} = $self->_get_thing("$name.font")
2015 or return $self->_error("No $name.font defined, either set $name.font or font to a font");
2016 UNIVERSAL::isa($work{font}, "Imager::Font")
2017 or return $self->_error("$name.font is not a font");
2018 if ($work{color} && !ref $work{color}) {
2019 $work{color} = $self->_get_color("$name.color")
2022 $work{size} = $self->_get_number("$name.size");
2023 $work{sizew} = $self->_get_number("$name.sizew")
2025 $work{aa} = $self->_get_number("$name.aa");
2030 =item _text_bbox($text, $name)
2032 Returns a bounding box for the specified $text as styled by $name.
2034 Returns an empty list on failure.
2039 my ($self, $text, $name) = @_;
2041 my %text_info = $self->_text_style($name)
2044 my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
2050 =item _line_style($name)
2052 Return parameters suitable for calls to Imager's line(), polyline(),
2055 For now this returns only color and aa parameters, but future releases
2056 of Imager may support extra parameters.
2061 my ($self, $name) = @_;
2064 $line{color} = $self->_get_color("$name.line")
2066 $line{aa} = $self->_get_number("$name.lineaa");
2067 defined $line{aa} or $line{aa} = $self->_get_number("aa");
2073 my ($self, $box, $chart_box, $name) = @_;
2075 my $halign = $self->{_style}{$name}{halign}
2076 or $self->_error("no halign for $name");
2077 my $valign = $self->{_style}{$name}{valign};
2079 if ($halign eq 'right') {
2080 $box->[0] += $chart_box->[2] - $box->[2];
2082 elsif ($halign eq 'left') {
2083 $box->[0] = $chart_box->[0];
2085 elsif ($halign eq 'center' || $halign eq 'centre') {
2086 $box->[0] = ($chart_box->[0] + $chart_box->[2] - $box->[2])/2;
2089 return $self->_error("invalid halign $halign for $name");
2092 if ($valign eq 'top') {
2093 $box->[1] = $chart_box->[1];
2095 elsif ($valign eq 'bottom') {
2096 $box->[1] = $chart_box->[3] - $box->[3];
2098 elsif ($valign eq 'center' || $valign eq 'centre') {
2099 $box->[1] = ($chart_box->[1] + $chart_box->[3] - $box->[3])/2;
2102 return $self->_error("invalid valign $valign for $name");
2104 $box->[2] += $box->[0];
2105 $box->[3] += $box->[1];
2109 my ($self, $chart_box, $object_box) = @_;
2113 if ($object_box->[0] - $chart_box->[0]
2114 < $chart_box->[2] - $object_box->[2]) {
2115 $areax = ($object_box->[2] - $chart_box->[0])
2116 * ($chart_box->[3] - $chart_box->[1]);
2119 $areax = ($chart_box->[2] - $object_box->[0])
2120 * ($chart_box->[3] - $chart_box->[1]);
2123 if ($object_box->[1] - $chart_box->[1]
2124 < $chart_box->[3] - $object_box->[3]) {
2125 $areay = ($object_box->[3] - $chart_box->[1])
2126 * ($chart_box->[2] - $chart_box->[0]);
2129 $areay = ($chart_box->[3] - $object_box->[1])
2130 * ($chart_box->[2] - $chart_box->[0]);
2133 if ($areay < $areax) {
2134 if ($object_box->[1] - $chart_box->[1]
2135 < $chart_box->[3] - $object_box->[3]) {
2136 $chart_box->[1] = $object_box->[3];
2139 $chart_box->[3] = $object_box->[1];
2143 if ($object_box->[0] - $chart_box->[0]
2144 < $chart_box->[2] - $object_box->[2]) {
2145 $chart_box->[0] = $object_box->[2];
2148 $chart_box->[2] = $object_box->[0];
2154 my ($self, $img, $labels, $chart_box) = @_;
2156 my $orient = $self->_get_thing('legend.orientation');
2157 defined $orient or $orient = 'vertical';
2159 if ($orient eq 'vertical') {
2160 return $self->_draw_legend_vertical($img, $labels, $chart_box);
2162 elsif ($orient eq 'horizontal') {
2163 return $self->_draw_legend_horizontal($img, $labels, $chart_box);
2166 return $self->_error("Unknown legend.orientation $orient");
2170 sub _draw_legend_horizontal {
2171 my ($self, $img, $labels, $chart_box) = @_;
2173 defined(my $padding = $self->_get_integer('legend.padding'))
2175 my $patchsize = $self->_get_integer('legend.patchsize')
2177 defined(my $gap = $self->_get_integer('legend.patchgap'))
2180 my $minrowsize = $patchsize + $gap;
2181 my ($width, $height) = (0,0);
2182 my $row_height = $minrowsize;
2186 for my $label (@$labels) {
2187 my @text_box = $self->_text_bbox($label, 'legend')
2189 push(@sizes, \@text_box);
2190 my $entry_width = $patchsize + $gap + $text_box[2];
2192 # never re-wrap the first entry
2193 push @offsets, [ 0, $height ];
2196 if ($pos + $gap + $entry_width > $chart_box->[2]) {
2198 $height += $row_height;
2200 push @offsets, [ $pos, $height ];
2202 my $entry_right = $pos + $entry_width;
2203 $pos += $gap + $entry_width;
2204 $entry_right > $width and $width = $entry_right;
2205 if ($text_box[3] > $row_height) {
2206 $row_height = $text_box[3];
2209 $height += $row_height;
2210 my @box = ( 0, 0, $width + $padding * 2, $height + $padding * 2 );
2211 my $outsidepadding = 0;
2212 if ($self->{_style}{legend}{border}) {
2213 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
2215 $box[2] += 2 * $outsidepadding;
2216 $box[3] += 2 * $outsidepadding;
2218 $self->_align_box(\@box, $chart_box, 'legend')
2220 if ($self->{_style}{legend}{fill}) {
2221 $img->box(xmin=>$box[0]+$outsidepadding,
2222 ymin=>$box[1]+$outsidepadding,
2223 xmax=>$box[2]-$outsidepadding,
2224 ymax=>$box[3]-$outsidepadding,
2225 $self->_get_fill('legend.fill', \@box));
2227 $box[0] += $outsidepadding;
2228 $box[1] += $outsidepadding;
2229 $box[2] -= $outsidepadding;
2230 $box[3] -= $outsidepadding;
2231 my %text_info = $self->_text_style('legend')
2234 if ($self->{_style}{legend}{patchborder}) {
2235 $patchborder = $self->_get_color('legend.patchborder')
2240 for my $label (@$labels) {
2241 my ($left, $top) = @{$offsets[$dataindex]};
2242 $left += $box[0] + $padding;
2243 $top += $box[1] + $padding;
2244 my $textpos = $left + $patchsize + $gap;
2245 my @patchbox = ( $left, $top,
2246 $left + $patchsize, $top + $patchsize );
2247 my @fill = $self->_data_fill($dataindex, \@patchbox)
2249 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
2250 ymax=>$top + $patchsize, @fill);
2251 if ($self->{_style}{legend}{patchborder}) {
2252 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
2253 ymax=>$top + $patchsize,
2254 color=>$patchborder);
2256 $img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize,
2261 if ($self->{_style}{legend}{border}) {
2262 my $border_color = $self->_get_color('legend.border')
2264 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
2265 color=>$border_color);
2267 $self->_remove_box($chart_box, \@box);
2271 sub _draw_legend_vertical {
2272 my ($self, $img, $labels, $chart_box) = @_;
2274 defined(my $padding = $self->_get_integer('legend.padding'))
2276 my $patchsize = $self->_get_integer('legend.patchsize')
2278 defined(my $gap = $self->_get_integer('legend.patchgap'))
2280 my $minrowsize = $patchsize + $gap;
2281 my ($width, $height) = (0,0);
2283 for my $label (@$labels) {
2284 my @box = $self->_text_bbox($label, 'legend')
2286 push(@sizes, \@box);
2287 $width = $box[2] if $box[2] > $width;
2288 if ($minrowsize > $box[3]) {
2289 $height += $minrowsize;
2296 $width + $patchsize + $padding * 2 + $gap,
2297 $height + $padding * 2 - $gap);
2298 my $outsidepadding = 0;
2299 if ($self->{_style}{legend}{border}) {
2300 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
2302 $box[2] += 2 * $outsidepadding;
2303 $box[3] += 2 * $outsidepadding;
2305 $self->_align_box(\@box, $chart_box, 'legend')
2307 if ($self->{_style}{legend}{fill}) {
2308 $img->box(xmin=>$box[0]+$outsidepadding,
2309 ymin=>$box[1]+$outsidepadding,
2310 xmax=>$box[2]-$outsidepadding,
2311 ymax=>$box[3]-$outsidepadding,
2312 $self->_get_fill('legend.fill', \@box));
2314 $box[0] += $outsidepadding;
2315 $box[1] += $outsidepadding;
2316 $box[2] -= $outsidepadding;
2317 $box[3] -= $outsidepadding;
2318 my $ypos = $box[1] + $padding;
2319 my $patchpos = $box[0]+$padding;
2320 my $textpos = $patchpos + $patchsize + $gap;
2321 my %text_info = $self->_text_style('legend')
2324 if ($self->{_style}{legend}{patchborder}) {
2325 $patchborder = $self->_get_color('legend.patchborder')
2329 for my $label (@$labels) {
2330 my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
2331 $patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
2334 if ($self->_draw_flat_legend()) {
2335 @fill = (color => $self->_data_color($dataindex), filled => 1);
2338 @fill = $self->_data_fill($dataindex, \@patchbox)
2341 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
2342 ymax=>$ypos + $patchsize, @fill);
2343 if ($self->{_style}{legend}{patchborder}) {
2344 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
2345 ymax=>$ypos + $patchsize,
2346 color=>$patchborder);
2348 $img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize,
2351 my $step = $patchsize + $gap;
2352 if ($minrowsize < $sizes[$dataindex][3]) {
2353 $ypos += $sizes[$dataindex][3];
2356 $ypos += $minrowsize;
2360 if ($self->{_style}{legend}{border}) {
2361 my $border_color = $self->_get_color('legend.border')
2363 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
2364 color=>$border_color);
2366 $self->_remove_box($chart_box, \@box);
2371 my ($self, $img, $chart_box) = @_;
2373 my $title = $self->{_style}{title}{text};
2374 my @box = $self->_text_bbox($title, 'title')
2378 $self->_align_box(\@box, $chart_box, 'title');
2379 my %text_info = $self->_text_style('title')
2381 $img->string(%text_info, x=>$box[0], 'y'=>$box[3] + $yoff, text=>$title);
2382 $self->_remove_box($chart_box, \@box);
2387 my ($self, $box) = @_;
2389 if ($box->[2] - $box->[0] > $box->[3] - $box->[1]) {
2390 return $box->[3] - $box->[1];
2393 return $box->[2] - $box->[0];
2397 sub _draw_flat_legend {
2403 Returns a list of style fields that are stored as composites, and
2404 should be merged instead of just being replaced.
2409 qw(title legend text label dropshadow outline callout graph);
2412 sub _filter_region {
2413 my ($self, $img, $left, $top, $right, $bottom, $filter) = @_;
2415 unless (ref $filter) {
2417 $filter = $self->_get_thing($name)
2420 or return $self->_error("no type for filter $name");
2423 $left > 0 or $left = 0;
2424 $top > 0 or $top = 0;
2426 my $masked = $img->masked(left=>$left, top=>$top,
2427 right=>$right, bottom=>$bottom);
2428 $masked->filter(%$filter);
2431 =item _line(x1 => $x1, y1 => $y1, ..., style => $style)
2433 Wrapper for line drawing, implements styles Imager doesn't.
2435 Currently styles are limited to horizontal and vertical lines.
2440 my ($self, %opts) = @_;
2442 my $img = delete $opts{img}
2443 or die "No img supplied to _line()";
2444 my $style = delete $opts{style} || "solid";
2446 if ($style eq "solid" || ($opts{x1} != $opts{x2} && $opts{y1} != $opts{y2})) {
2447 return $img->line(%opts);
2449 elsif ($style eq 'dashed' || $style eq 'dotted') {
2450 my ($x1, $y1, $x2, $y2) = delete @opts{qw/x1 y1 x2 y2/};
2451 # the line is vertical or horizontal, so swapping doesn't hurt
2452 $x1 > $x2 and ($x1, $x2) = ($x2, $x1);
2453 $y1 > $y2 and ($y1, $y2) = ($y2, $y1);
2454 my ($stepx, $stepy) = ( 0, 0 );
2455 my $step_size = $style eq "dashed" ? 8 : 2;
2456 my ($counter, $count_end);
2458 $stepy = $step_size;
2459 ($counter, $count_end) = ($y1, $y2);
2462 $stepx = $step_size;
2463 ($counter, $count_end) = ($x1, $x2);
2465 my ($x, $y) = ($x1, $y1);
2466 while ($counter < $count_end) {
2467 if ($style eq "dotted") {
2468 $img->setpixel(x => $x, y => $y, color => $opts{color});
2471 my $xe = $stepx ? $x + $stepx / 2 - 1 : $x;
2472 $xe > $x2 and $xe = $x2;
2473 my $ye = $stepy ? $y + $stepy / 2 - 1 : $y;
2474 $ye > $y2 and $ye = $y2;
2475 $img->line(x1 => $x, y1 => $y, x2 => $xe, y2 => $ye, %opts);
2477 $counter += $step_size;
2485 $self->_error("Unknown line style $style");
2490 =item _box(xmin ..., style => $style)
2492 A wrapper for drawing styled box outlines.
2497 my ($self, %opts) = @_;
2499 my $style = delete $opts{style} || "solid";
2500 my $img = delete $opts{img}
2501 or die "No img supplied to _box";
2503 if ($style eq "solid") {
2504 return $img->box(%opts);
2507 my $box = delete $opts{box};
2508 # replicate Imager's defaults
2509 my %work_opts = ( xmin => 0, ymin => 0, xmax => $img->getwidth() - 1, ymax => $img->getheight() -1, %opts, style => $style, img => $img );
2510 my ($xmin, $ymin, $xmax, $ymax) = delete @work_opts{qw/xmin ymin xmax ymax/};
2512 ($xmin, $ymin, $xmax, $ymax) = @$box;
2514 $xmin > $xmax and ($xmin, $xmax) = ($xmax, $xmin);
2515 $ymin > $ymax and ($ymin, $ymax) = ($ymax, $ymin);
2517 if ($xmax - $xmin > 1) {
2518 $self->_line(x1 => $xmin+1, y1 => $ymin, x2 => $xmax-1, y2 => $ymin, %work_opts);
2519 $self->_line(x1 => $xmin+1, y1 => $ymax, x2 => $xmax-1, y2 => $ymax, %work_opts);
2521 $self->_line(x1 => $xmin, y1 => $ymin, x2 => $xmin, y2 => $ymax, %work_opts);
2522 return $self->_line(x1 => $xmin, y1 => $ymin, x2 => $xmin, y2 => $ymax, %work_opts);
2533 Imager::Graph::Pie(3), Imager(3), perl(1).
2537 Tony Cook <tony@develop-help.com>
2541 Imager::Graph is licensed under the same terms as perl itself.
2545 Addi for producing a cool imaging module. :)