6 Imager::Graph - Perl extension for producing Graphs using the Imager library.
10 use Imager::Graph::SubClass;
11 my $chart = Imager::Graph::SubClass->new;
12 my $img = $chart->draw(data=> \@data, ...)
17 Imager::Graph provides style information to its base classes. It
18 defines the colors, text display information and fills based on both
19 built-in styles and modifications supplied by the user to the draw()
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.
51 Returns an error message. Only value if the draw() method returns false.
61 Creates a new image, draws the chart onto that image and returns it.
63 Typically you will need to supply a C<data> parameter in the format
64 required by that particular graph, and if your graph will use any
65 text, a C<font> parameter
67 You can also supply many different parameters which control the way
68 the graph looks. These are supplied as keyword, value pairs, where
69 the value can be a hashref containing sub values.
71 The C<style> parameter will selects a basic color set, and possibly
72 sets other related parameters. See L</"STYLES">.
74 my $font = Imager::Font->new(file => 'ImUgly.ttf');
75 my $img = $chart->draw(
79 text => "Hello, World!",
85 When referring to a single sub-value this documentation will refer to
86 'title.color' rather than 'the color element of title'.
88 Returns the graph image on success, or false on failure.
94 The currently defined styles are:
100 a light grey background with no outlines. Uses primary colors for the
105 a light red background with no outlines. Uses primary colors for the
108 Graphs drawn using this style should save well as a gif, even though
109 some graphs may perform a slight blur.
111 This was the default style, but the red was too loud.
115 designed for monochrome output, such as most laser printers, this uses
116 hatched fills for the data, and no colors. The returned image is a
117 one channel image (which can be overridden with the C<channels>
120 You can also override the colors used by all components for background
121 or drawing by supplying C<fg> and/or C<bg> parameters. ie. if you
122 supply C<<fg=>'FF0000', channels=>3>> then the hash fills and anything
123 else will be drawn in red. Another use might be to set a transparent
124 background, by supplying C<<bg=>'00000000', channels=>4>>.
126 This style outlines the legend if present and outlines the hashed fills.
130 designed as a "pretty" style this uses linear fountain fills for the
131 background and data fills, and adds a drop shadow.
133 You can override the value used for text and outlines by setting the
136 This is the default style.
140 also designed as a "pretty" style this uses radial fountain fills for
141 the data and a linear blue to green fill for the background.
147 Each graph type has a number of features. These are used to add
148 various items that are displayed in the graph area. Some common
155 adds a box containing boxes filled with the data filess, with
156 the labels provided to the draw method. The legend will only be
157 displayed if both the legend feature is enabled and labels are
162 labels each data fill, usually by including text inside the data fill.
163 If the text does not fit in the fill, they could be displayed in some
164 other form, eg. as callouts in a pie graph. There usually isn't much
165 point in including both labels and a legend.
169 a simple drop shadow is shown behind some of the graph elements.
173 Each graph also has features specific to that graph.
175 =head1 COMMON PARAMETERS
177 When referring to a single sub-value this documentation will refer to
178 'title.color' rather than 'the color element of title'.
180 Normally, except for the font parameter, these are controlled by
181 styles, but these are the style parameters I'd mostly likely expect
188 the Imager font object used to draw text on the chart.
192 the background fill for the graph. Default depends on the style.
196 the base size of the graph image. Default: 256
200 the width of the graph image. Default: 1.5 * size (384)
204 the height of the graph image. Default: size (256)
208 the number of channels in the image. Default: 3 (the 'mono' style
213 the color used for drawing lines, such as outlines or callouts.
214 Default depends on the current style. Set to undef to remove the
215 outline from a style.
219 the text used for a graph title. Default: no title. Note: this is
220 the same as the title=>{ text => ... } field.
226 horizontal alignment of the title in the graph, one of 'left',
227 'center' or 'right'. Default: center
231 vertical alignment of the title, one of 'top', 'center' or 'right'.
232 Default: top. It's probably a bad idea to set this to 'center' unless
233 you have a very short title.
239 This contains basic defaults used in drawing text.
245 the default color used for all text, defaults to the fg color.
249 the base size used for text, also used to scale many graph elements.
258 In most cases you will want to use just the styles, but you may want
259 to exert more control over the way your chart looks. This section
260 describes the options you can use to control the way your chart looks.
262 Hopefully you don't need to read this.
268 The background of the graph.
274 Used to define basic background and foreground colors for the graph.
275 The bg color may be used for the background of the graph, and is used
276 as a default for the background of hatcheed fills. The fg is used as
277 the default for line and text colors.
281 The default font used by the graph. Normally you should supply this
282 if your graph as any text.
286 The default line color.
290 defaults for drawing text. Other textual graph elements will inherit
291 or modify these values.
297 default text color, defaults to the I<fg> color.
301 default text size. Default: 14. This is used to scale many graph
302 elements, including padding and leader sizes. Other text elements
303 will either use or scale this value.
307 default font object. Inherited from I<font>, which should have been
308 supplied by the caller.
314 If you supply a scalar value for this element, it will be stored in
317 Defines the text, font and layout information for the title.
323 The color of the title, inherited from I<text.color>.
327 The font object used for the title, inherited from I<text.font>.
331 size of the title text. Default: double I<text.size>
337 The horizontal and vertical alignment of the title.
343 defines attributes of the graph legend, if present.
353 text attributes for the labels used in the legend.
357 the width and height of the color patch in the legend. Defaults to
358 90% of the legend text size.
362 the minimum gap between patches in pixels. Defaults to 30% of the
367 the color of the border drawn around each patch. Inherited from I<line>.
373 the horizontal and vertical alignment of the legend within the graph.
374 Defaults to 'right' and 'top'.
378 the gap between the legend patches and text and the outside of it's
379 box, or to the legend border, if any.
383 the gap between the border and the outside of the legend's box. This
384 is only used if the I<legend.border> attribute is defined.
388 the background fill for the legend. Default: none
392 the border color of the legend. Default: none (no border is drawn
397 The orientation of the legend. If this is C<vertical> the the patches
398 and labels are stacked on top of each other. If this is C<horizontal>
399 the patchs and labels are word wrapped across the image. Default:
404 For example to create a horizontal legend with borderless patches,
405 darker than the background, you might do:
407 my $im = $chart->draw
411 patchborder => undef,
412 orientation => 'horizontal',
413 fill => { solid => Imager::Color->new(0, 0, 0, 32), }
419 defines attributes for graph callouts, if any are present. eg. if the
420 pie graph cannot fit the label into the pie graph segement it will
421 present it as a callout.
431 the text attributes of the callout label. Inherited from I<text>.
435 the color of the callout lines. Inherited from I<line>
441 the length of the leader on the inside and the outside of the fill,
442 usually at some angle. Both default to the size of the callout text.
446 the length of the horizontal portion of the leader. Default:
451 the gap between the callout leader and the callout text. Defaults to
452 30% of the text callout size.
458 defines attributes for labels drawn into the data areas of a graph.
468 The text attributes of the labels. Inherited from I<text>.
474 the attributes of the graph's drop shadow
480 the fill used for the drop shadow. Default: '404040' (dark gray)
484 the offset of the drop shadow. A convenience value inherited by offx
485 and offy. Default: 40% of I<text.size>.
491 the horizontal and vertical offsets of the drop shadow. Both
492 inherited from I<dropshadow.off>.
496 the filter description passed to Imager's filter method to blur the
497 drop shadow. Default: an 11 element convolution filter.
503 describes the lines drawn around filled data areas, such as the
504 segments of a pie chart.
510 the line color of the outlines, inherited from I<line>.
516 a reference to an array containing fills for each data item.
518 You can mix fill types, ie. using a simple color for the first item, a
519 hatched fill for the second and a fountain fill for the next.
523 =head1 HOW VALUES WORK
525 Internally rather than specifying literal color, fill, or font objects
526 or literal sizes for each element, Imager::Graph uses a number of
527 special values to inherit or modify values taken from other graph
530 =head2 Specifying colors
532 You can specify colors by either supplying an Imager::Color object, by
533 supplying lookup of another color, or by supplying a single value that
534 Imager::Color::new can use as an initializer. The most obvious is
535 just a 6 or 8 digit hex value representing the red, green, blue and
536 optionally alpha channels of the image.
538 You can lookup another color by using the lookup() "function", for
539 example if you give a color as "lookup(fg)" then Imager::Graph will
540 look for the fg element in the current style (or as overridden by
541 you.) This is used internally by Imager::Graph to set up the
542 relationships between the colors of various elements, for example the
543 default style information contains:
550 color=>'lookup(text.color)',
554 So by setting the I<fg> color, you also set the default text color,
555 since each text element uses lookup(text.color) as its value.
557 =head2 Specifying fills
559 Fills can be used for the graph background color, the background color
560 for the legend block and for the fills used for each data element.
562 You can specify a fill as a L<color value|Specifying colors> or as a
563 general fill, see L<Imager::Fill> for details.
565 You don't need (or usually want) to call Imager::Fill::new yourself,
566 since the various fill functions will call it for you, and
567 Imager::Graph provides some hooks to make them more useful.
573 with hatched fills, if you don't supply a 'fg' or 'bg' parameter,
574 Imager::Graph will supply the current graph fg and bg colors.
578 with fountain fill, you can supply the xa_ratio, ya_ratio, xb_ratio
579 and yb_ratio parameters, and they will be scaled in the fill area to
580 define the fountain fills xa, ya, xb and yb parameters.
584 As with colors, you can use lookup(name) or lookup(name1.name2) to
585 have one element to inherit the fill of another.
587 Imager::Graph defaults the fill combine value to C<'normal'>. This
588 doesn't apply to simple color fills.
590 =head2 Specifying numbers
592 You can specify various numbers, usually representing the size of
593 something, commonly text, but sometimes the length of a line or the
596 You can use the same lookup mechanism as with colors and fills, but
597 you can also scale values. For example, 'scale(0.5,text.size)' will
598 return half the size of the normal text size.
600 As with colors, this is used internally to scale graph elements based
601 on the base text size. If you change the base text size then other
602 graph elements will scale as well.
604 =head2 Specifying other elements
606 Other elements, such as fonts, or parameters for a filter, can also
607 use the lookup(name) mechanism.
609 =head1 INTERNAL METHODS
611 Only useful if you need to fix bugs, add features or create a new
623 color => 'lookup(fg)',
624 font => 'lookup(font)',
628 color => 'lookup(text.color)',
629 font => 'lookup(text.font)',
632 size => 'scale(text.size,2.0)',
635 color => 'lookup(text.color)',
636 font => 'lookup(text.font)',
637 size => 'lookup(text.size)',
638 patchsize => 'scale(legend.size,0.9)',
639 patchgap => 'scale(legend.patchsize,0.3)',
640 patchborder => 'lookup(line)',
643 padding => 'scale(legend.size,0.3)',
644 outsidepadding => 'scale(legend.padding,0.4)',
647 color => 'lookup(text.color)',
648 font => 'lookup(text.font)',
649 size => 'lookup(text.size)',
650 line => 'lookup(line)',
651 inside => 'lookup(callout.size)',
652 outside => 'lookup(callout.size)',
653 leadlen => 'scale(0.8,callout.size)',
654 gap => 'scale(callout.size,0.3)',
657 font => 'lookup(text.font)',
658 size => 'lookup(text.size)',
659 color => 'lookup(text.color)',
660 hpad => 'lookup(label.pad)',
661 vpad => 'lookup(label.pad)',
662 pad => 'scale(label.size,0.2)',
663 pcformat => sub { sprintf "%s (%.0f%%)", $_[0], $_[1] },
664 pconlyformat => sub { sprintf "%.1f%%", $_[0] },
667 fill => { solid => Imager::Color->new(0, 0, 0, 96) },
668 off => 'scale(0.4,text.size)',
669 offx => 'lookup(dropshadow.off)',
670 offy => 'lookup(dropshadow.off)',
671 filter => { type=>'conv',
672 # this needs a fairly heavy blur
673 coef=>[0.1, 0.2, 0.4, 0.6, 0.7, 0.9, 1.2,
674 0.9, 0.7, 0.6, 0.4, 0.2, 0.1 ] },
677 line =>'lookup(line)',
680 width=>'scale(1.5,size)',
681 height=>'lookup(size)',
684 =item _error($message)
686 Sets the error field of the object and returns an empty list or undef,
687 depending on context. Should be used for error handling, since it may
688 provide some user hooks at some point.
693 my ($self, $error) = @_;
695 $self->{_errstr} = $error;
703 Returns the style defaults, such as the relationships between line
704 color and text color.
706 Intended to be over-ridden by base classes to provide graph specific
715 # Let's make the default something that looks really good, so folks will be interested enough to customize the style.
716 my $def_style = 'fount_lin';
724 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
730 #patchborder=>'000000'
737 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
743 patchborder=>'000000'
756 { hatch=>'stipple3' },
757 { hatch=>'stipple2' },
762 features=>{ outline=>1 },
771 { fountain=>'linear',
772 xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
773 segments => Imager::Fountain->simple(positions=>[0, 1],
774 colors=>[ NC('FFC0C0'), NC('FF0000') ]),
776 { fountain=>'linear',
777 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
778 segments => Imager::Fountain->simple(positions=>[0, 1],
779 colors=>[ NC('C0FFC0'), NC('00FF00') ]),
781 { fountain=>'linear',
782 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
783 segments => Imager::Fountain->simple(positions=>[0, 1],
784 colors=>[ NC('C0C0FF'), NC('0000FF') ]),
786 { fountain=>'linear',
787 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
788 segments => Imager::Fountain->simple(positions=>[0, 1],
789 colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
791 { fountain=>'linear',
792 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
793 segments => Imager::Fountain->simple(positions=>[0, 1],
794 colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
796 { fountain=>'linear',
797 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
798 segments => Imager::Fountain->simple(positions=>[0, 1],
799 colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
802 back=>{ fountain=>'linear',
803 xa_ratio=>0, ya_ratio=>0,
804 xb_ratio=>1.0, yb_ratio=>1.0,
805 segments=>Imager::Fountain->simple
807 colors=>[ NC('6060FF'), NC('60FF60') ]) },
810 features=>{ dropshadow=>1 },
816 { fountain=>'radial',
817 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
818 segments => Imager::Fountain->simple(positions=>[0, 1],
819 colors=>[ NC('FF8080'), NC('FF0000') ]),
821 { fountain=>'radial',
822 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
823 segments => Imager::Fountain->simple(positions=>[0, 1],
824 colors=>[ NC('80FF80'), NC('00FF00') ]),
826 { fountain=>'radial',
827 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
828 segments => Imager::Fountain->simple(positions=>[0, 1],
829 colors=>[ NC('808080FF'), NC('0000FF') ]),
831 { fountain=>'radial',
832 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
833 segments => Imager::Fountain->simple(positions=>[0, 1],
834 colors=>[ NC('FFFF80'), NC('FFFF00') ]),
836 { fountain=>'radial',
837 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
838 segments => Imager::Fountain->simple(positions=>[0, 1],
839 colors=>[ NC('80FFFF'), NC('00FFFF') ]),
841 { fountain=>'radial',
842 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
843 segments => Imager::Fountain->simple(positions=>[0, 1],
844 colors=>[ NC('FF80FF'), NC('FF00FF') ]),
847 back=>{ fountain=>'linear',
848 xa_ratio=>0, ya_ratio=>0,
849 xb_ratio=>1.0, yb_ratio=>1.0,
850 segments=>Imager::Fountain->simple
852 colors=>[ NC('6060FF'), NC('60FF60') ]) },
858 =item $self->_style_setup(\%opts)
860 Uses the values from %opts to build a customized hash describing the
861 way the graph should be drawn.
866 my ($self, $opts) = @_;
867 my $style_defs = $self->_style_defs;
869 $style = $styles{$opts->{style}} if $opts->{style};
870 $style ||= $styles{$def_style};
872 my @search_list = ( $style_defs, $style, $opts);
875 my @composite = $self->_composite();
877 @composite{@composite} = @composite;
879 for my $src (@search_list) {
880 for my $key (keys %$src) {
881 if ($composite{$key}) {
882 $work{$key} = {} unless exists $work{$key};
883 if (ref $src->{$key}) {
884 # some keys have sub values, especially text
885 @{$work{$key}}{keys %{$src->{$key}}} = values %{$src->{$key}};
888 # assume it's the text for a title or something
889 $work{$key}{text} = $src->{$key};
893 $work{$key} = $src->{$key};
898 # features are handled specially
899 $work{features} = {};
900 for my $src (@search_list) {
901 if ($src->{features}) {
902 if (ref $src->{features}) {
903 if (ref($src->{features}) =~ /ARRAY/) {
904 # just set those features
905 for my $feature (@{$src->{features}}) {
906 $work{features}{$feature} = 1;
909 elsif (ref($src->{features}) =~ /HASH/) {
910 if ($src->{features}{reset}) {
911 $work{features} = {}; # only the ones the user specifies
913 @{$work{features}}{keys %{$src->{features}}} =
914 values(%{$src->{features}});
918 # just set that single feature
919 $work{features}{$src->{features}} = 1;
924 #print Dumper(\%work);
926 $self->{_style} = \%work;
929 =item $self->_get_thing($name)
931 Retrieve some general 'thing'.
933 Supports the 'lookup(foo)' mechanism.
938 my ($self, $name, @depth) = @_;
942 if ($name =~ /^(\w+)\.(\w+)$/) {
943 $what = $self->{_style}{$1}{$2};
946 $what = $self->{_style}{$name};
953 elsif ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
955 or return $self->_error("too many levels of recursion in lookup(@depth)");
956 return $self->_get_thing($1, @depth);
963 =item $self->_get_number($name)
965 Retrieves a number from the style. The value in the style can be the
966 number, or one of two functions:
970 =item lookup(newname)
972 Recursively looks up I<newname> in the style.
974 =item scale(value1,value2)
976 Each value can be a number or a name. Names are recursively looked up
977 in the style and the product is returned.
984 my ($self, $name, @depth) = @_;
988 if ($name =~ /^(\w+)\.(\w+)$/) {
989 $what = $self->{_style}{$1}{$2};
992 $what = $self->{_style}{$name};
995 return $self->_error("$name is undef (@depth)");
998 if ($what =~ /CODE/) {
999 $what = $what->($self, $name);
1003 if ($what =~ /^lookup\(([\w.]+)\)$/) {
1005 or return $self->_error("too many levels of recursion in lookup (@depth)");
1006 return $self->_get_number($1, @depth);
1008 elsif ($what =~ /^scale\(
1009 ((?:[a-z][\w.]*)|$NUM_RE)
1011 ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
1012 my ($left, $right) = ($1, $2);
1013 unless ($left =~ /^$NUM_RE$/) {
1015 or return $self->_error("too many levels of recursion in scale (@depth)");
1016 $left = $self->_get_number($left, @depth);
1018 unless ($right =~ /^$NUM_RE$/) {
1020 or return $self->_error("too many levels of recursion in scale (@depth)");
1021 $right = $self->_get_number($right, @depth);
1023 return $left * $right;
1031 =item $self->_get_integer($name)
1033 Retrieves an integer from the style. This is a simple wrapper around
1034 _get_number() that rounds the result to an integer.
1039 my ($self, $name, @depth) = @_;
1041 my $number = $self->_get_number($name, @depth)
1044 return sprintf("%.0f", $number);
1047 =item _get_color($name)
1049 Returns a color object of the given name from the style hash.
1051 Uses Imager::Color->new to translate normal scalars into color objects.
1053 Allows the lookup(name) mechanism.
1058 my ($self, $name, @depth) = @_;
1060 push(@depth, $name);
1062 if ($name =~ /^(\w+)\.(\w+)$/) {
1063 $what = $self->{_style}{$1}{$2};
1066 $what = $self->{_style}{$name};
1070 or return $self->_error("$name was undefined (@depth)");
1072 unless (ref $what) {
1073 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1074 @depth < MAX_DEPTH or
1075 return $self->_error("too many levels of recursion in lookup (@depth)");
1077 return $self->_get_color($1, @depth);
1079 $what = Imager::Color->new($what);
1085 =item _translate_fill($what, $box)
1087 Given the value of a fill, either attempts to convert it into a fill
1088 list (one of C<<color=>$color_value, filled=>1>> or C<<fill=>{ fill
1089 parameters }>>), or to lookup another fill that is referred to with
1090 the 'lookup(name)' mechanism.
1092 This function does the fg and bg initialization for hatched fills, and
1093 translation of *_ratio for fountain fills (using the $box parameter).
1097 sub _translate_fill {
1098 my ($self, $what, $box, @depth) = @_;
1101 if (UNIVERSAL::isa($what, "Imager::Color")) {
1102 return ( color=>Imager::Color->new($what), filled=>1 );
1106 # default to normal combine mode
1107 my %work = ( combine => 'normal', %$what );
1108 if ($what->{hatch}) {
1110 $work{fg} = $self->_get_color('fg')
1114 $work{bg} = $self->_get_color('bg')
1117 return ( fill=>\%work );
1119 elsif ($what->{fountain}) {
1120 for my $key (qw(xa ya xb yb)) {
1121 if (exists $work{"${key}_ratio"}) {
1123 $work{$key} = $box->[0] + $work{"${key}_ratio"}
1124 * ($box->[2] - $box->[0]);
1127 $work{$key} = $box->[1] + $work{"${key}_ratio"}
1128 * ($box->[3] - $box->[1]);
1132 return ( fill=>\%work );
1135 return ( fill=> \%work );
1140 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1141 return $self->_get_fill($1, $box, @depth);
1144 # assumed to be an Imager::Color single value
1145 return ( color=>Imager::Color->new($what), filled=>1 );
1150 =item _data_fill($index, $box)
1152 Retrieves the fill parameters for a data area fill.
1157 my ($self, $index, $box) = @_;
1159 my $fills = $self->{_style}{fills};
1160 return $self->_translate_fill($fills->[$index % @$fills], $box,
1164 =item _get_fill($index, $box)
1166 Retrieves fill parameters for a named fill.
1171 my ($self, $name, $box, @depth) = @_;
1173 push(@depth, $name);
1175 if ($name =~ /^(\w+)\.(\w+)$/) {
1176 $what = $self->{_style}{$1}{$2};
1179 $what = $self->{_style}{$name};
1183 or return $self->_error("no fill $name found");
1185 return $self->_translate_fill($what, $box, @depth);
1190 Builds the image object for the graph and fills it with the background
1198 my $width = $self->_get_number('width') || 256;
1199 my $height = $self->_get_number('height') || 256;
1200 my $channels = $self->{_style}{channels};
1204 my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels);
1206 $img->box($self->_get_fill('back', [ 0, 0, $width-1, $height-1]));
1212 my ($self, $name) = @_;
1216 if ($self->{_style}{$name}) {
1217 %work = %{$self->{_style}{$name}};
1220 %work = %{$self->{_style}{text}};
1223 or return $self->_error("$name has no font parameter");
1225 $work{font} = $self->_get_thing("$name.font")
1226 or return $self->_error("invalid font");
1227 UNIVERSAL::isa($work{font}, "Imager::Font")
1228 or return $self->_error("$name.font is not a font");
1229 if ($work{color} && !ref $work{color}) {
1230 $work{color} = $self->_get_color("$name.color")
1233 $work{size} = $self->_get_number("$name.size");
1234 $work{sizew} = $self->_get_number("$name.sizew")
1241 my ($self, $text, $name) = @_;
1243 my %text_info = $self->_text_style($name);
1245 if (!defined $text_info{font}) {
1246 die "No font or invalid font specified, and we're trying to draw text.\n";
1248 my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
1255 my ($self, $box, $chart_box, $name) = @_;
1257 my $halign = $self->{_style}{$name}{halign}
1258 or $self->_error("no halign for $name");
1259 my $valign = $self->{_style}{$name}{valign};
1261 if ($halign eq 'right') {
1262 $box->[0] += $chart_box->[2] - $box->[2];
1264 elsif ($halign eq 'left') {
1265 $box->[0] = $chart_box->[0];
1267 elsif ($halign eq 'center' || $halign eq 'centre') {
1268 $box->[0] = ($chart_box->[0] + $chart_box->[2] - $box->[2])/2;
1271 return $self->_error("invalid halign $halign for $name");
1274 if ($valign eq 'top') {
1275 $box->[1] = $chart_box->[1];
1277 elsif ($valign eq 'bottom') {
1278 $box->[1] = $chart_box->[3] - $box->[3];
1280 elsif ($valign eq 'center' || $valign eq 'centre') {
1281 $box->[1] = ($chart_box->[1] + $chart_box->[3] - $box->[3])/2;
1284 return $self->_error("invalid valign $valign for $name");
1286 $box->[2] += $box->[0];
1287 $box->[3] += $box->[1];
1291 my ($self, $chart_box, $object_box) = @_;
1295 if ($object_box->[0] - $chart_box->[0]
1296 < $chart_box->[2] - $object_box->[2]) {
1297 $areax = ($object_box->[2] - $chart_box->[0])
1298 * ($chart_box->[3] - $chart_box->[1]);
1301 $areax = ($chart_box->[2] - $object_box->[0])
1302 * ($chart_box->[3] - $chart_box->[1]);
1305 if ($object_box->[1] - $chart_box->[1]
1306 < $chart_box->[3] - $object_box->[3]) {
1307 $areay = ($object_box->[3] - $chart_box->[1])
1308 * ($chart_box->[2] - $chart_box->[0]);
1311 $areay = ($chart_box->[3] - $object_box->[1])
1312 * ($chart_box->[2] - $chart_box->[0]);
1315 if ($areay < $areax) {
1316 if ($object_box->[1] - $chart_box->[1]
1317 < $chart_box->[3] - $object_box->[3]) {
1318 $chart_box->[1] = $object_box->[3];
1321 $chart_box->[3] = $object_box->[1];
1325 if ($object_box->[0] - $chart_box->[0]
1326 < $chart_box->[2] - $object_box->[2]) {
1327 $chart_box->[0] = $object_box->[2];
1330 $chart_box->[2] = $object_box->[0];
1336 my ($self, $img, $labels, $chart_box) = @_;
1338 my $orient = $self->_get_thing('legend.orientation');
1339 defined $orient or $orient = 'vertical';
1341 if ($orient eq 'vertical') {
1342 return $self->_draw_legend_vertical($img, $labels, $chart_box);
1344 elsif ($orient eq 'horizontal') {
1345 return $self->_draw_legend_horizontal($img, $labels, $chart_box);
1348 return $self->_error("Unknown legend.orientation $orient");
1352 sub _draw_legend_horizontal {
1353 my ($self, $img, $labels, $chart_box) = @_;
1355 defined(my $padding = $self->_get_integer('legend.padding'))
1357 my $patchsize = $self->_get_integer('legend.patchsize')
1359 defined(my $gap = $self->_get_integer('legend.patchgap'))
1362 my $minrowsize = $patchsize + $gap;
1363 my ($width, $height) = (0,0);
1364 my $row_height = $minrowsize;
1368 for my $label (@$labels) {
1369 my @text_box = $self->_text_bbox($label, 'legend');
1370 push(@sizes, \@text_box);
1371 my $entry_width = $patchsize + $gap + $text_box[2];
1373 # never re-wrap the first entry
1374 push @offsets, [ 0, $height ];
1377 if ($pos + $gap + $entry_width > $chart_box->[2]) {
1379 $height += $row_height;
1381 push @offsets, [ $pos, $height ];
1383 my $entry_right = $pos + $entry_width;
1384 $pos += $gap + $entry_width;
1385 $entry_right > $width and $width = $entry_right;
1386 if ($text_box[3] > $row_height) {
1387 $row_height = $text_box[3];
1390 $height += $row_height;
1391 my @box = ( 0, 0, $width + $padding * 2, $height + $padding * 2 );
1392 my $outsidepadding = 0;
1393 if ($self->{_style}{legend}{border}) {
1394 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
1396 $box[2] += 2 * $outsidepadding;
1397 $box[3] += 2 * $outsidepadding;
1399 $self->_align_box(\@box, $chart_box, 'legend')
1401 if ($self->{_style}{legend}{fill}) {
1402 $img->box(xmin=>$box[0]+$outsidepadding,
1403 ymin=>$box[1]+$outsidepadding,
1404 xmax=>$box[2]-$outsidepadding,
1405 ymax=>$box[3]-$outsidepadding,
1406 $self->_get_fill('legend.fill', \@box));
1408 $box[0] += $outsidepadding;
1409 $box[1] += $outsidepadding;
1410 $box[2] -= $outsidepadding;
1411 $box[3] -= $outsidepadding;
1412 my %text_info = $self->_text_style('legend')
1415 if ($self->{_style}{legend}{patchborder}) {
1416 $patchborder = $self->_get_color('legend.patchborder')
1421 for my $label (@$labels) {
1422 my ($left, $top) = @{$offsets[$dataindex]};
1423 $left += $box[0] + $padding;
1424 $top += $box[1] + $padding;
1425 my $textpos = $left + $patchsize + $gap;
1426 my @patchbox = ( $left, $top,
1427 $left + $patchsize, $top + $patchsize );
1428 my @fill = $self->_data_fill($dataindex, \@patchbox)
1430 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
1431 ymax=>$top + $patchsize, @fill);
1432 if ($self->{_style}{legend}{patchborder}) {
1433 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
1434 ymax=>$top + $patchsize,
1435 color=>$patchborder);
1437 $img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize,
1442 if ($self->{_style}{legend}{border}) {
1443 my $border_color = $self->_get_color('legend.border')
1445 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
1446 color=>$border_color);
1448 $self->_remove_box($chart_box, \@box);
1452 sub _draw_legend_vertical {
1453 my ($self, $img, $labels, $chart_box) = @_;
1455 defined(my $padding = $self->_get_integer('legend.padding'))
1457 my $patchsize = $self->_get_integer('legend.patchsize')
1459 defined(my $gap = $self->_get_integer('legend.patchgap'))
1461 my $minrowsize = $patchsize + $gap;
1462 my ($width, $height) = (0,0);
1464 for my $label (@$labels) {
1465 my @box = $self->_text_bbox($label, 'legend');
1466 push(@sizes, \@box);
1467 $width = $box[2] if $box[2] > $width;
1468 if ($minrowsize > $box[3]) {
1469 $height += $minrowsize;
1476 $width + $patchsize + $padding * 2 + $gap,
1477 $height + $padding * 2 - $gap);
1478 my $outsidepadding = 0;
1479 if ($self->{_style}{legend}{border}) {
1480 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
1482 $box[2] += 2 * $outsidepadding;
1483 $box[3] += 2 * $outsidepadding;
1485 $self->_align_box(\@box, $chart_box, 'legend')
1487 if ($self->{_style}{legend}{fill}) {
1488 $img->box(xmin=>$box[0]+$outsidepadding,
1489 ymin=>$box[1]+$outsidepadding,
1490 xmax=>$box[2]-$outsidepadding,
1491 ymax=>$box[3]-$outsidepadding,
1492 $self->_get_fill('legend.fill', \@box));
1494 $box[0] += $outsidepadding;
1495 $box[1] += $outsidepadding;
1496 $box[2] -= $outsidepadding;
1497 $box[3] -= $outsidepadding;
1498 my $ypos = $box[1] + $padding;
1499 my $patchpos = $box[0]+$padding;
1500 my $textpos = $patchpos + $patchsize + $gap;
1501 my %text_info = $self->_text_style('legend')
1504 if ($self->{_style}{legend}{patchborder}) {
1505 $patchborder = $self->_get_color('legend.patchborder')
1509 for my $label (@$labels) {
1510 my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
1511 $patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
1512 my @fill = $self->_data_fill($dataindex, \@patchbox)
1514 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
1515 ymax=>$ypos + $patchsize, @fill);
1516 if ($self->{_style}{legend}{patchborder}) {
1517 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
1518 ymax=>$ypos + $patchsize,
1519 color=>$patchborder);
1521 $img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize,
1524 my $step = $patchsize + $gap;
1525 if ($minrowsize < $sizes[$dataindex][3]) {
1526 $ypos += $sizes[$dataindex][3];
1529 $ypos += $minrowsize;
1533 if ($self->{_style}{legend}{border}) {
1534 my $border_color = $self->_get_color('legend.border')
1536 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
1537 color=>$border_color);
1539 $self->_remove_box($chart_box, \@box);
1544 my ($self, $img, $chart_box) = @_;
1546 my $title = $self->{_style}{title}{text};
1547 my @box = $self->_text_bbox($title, 'title');
1550 $self->_align_box(\@box, $chart_box, 'title');
1551 my %text_info = $self->_text_style('title');
1552 $img->string(%text_info, x=>$box[0], 'y'=>$box[3] + $yoff, text=>$title);
1553 $self->_remove_box($chart_box, \@box);
1558 my ($self, $box) = @_;
1560 if ($box->[2] - $box->[0] > $box->[3] - $box->[1]) {
1561 return $box->[3] - $box->[1];
1564 return $box->[2] - $box->[0];
1570 Returns a list of style fields that are stored as composites, and
1571 should be merged instead of just being replaced.
1576 qw(title legend text label dropshadow outline callout);
1579 sub _filter_region {
1580 my ($self, $img, $left, $top, $right, $bottom, $filter) = @_;
1582 unless (ref $filter) {
1584 $filter = $self->_get_thing($name)
1587 or return $self->_error("no type for filter $name");
1590 $left > 0 or $left = 0;
1591 $top > 0 or $top = 0;
1593 # newer versions of Imager let you work on just part of an image
1594 if ($img->can('masked') && !$self->{_style}{features}{_debugblur}) {
1595 my $masked = $img->masked(left=>$left, top=>$top,
1596 right=>$right, bottom=>$bottom);
1597 $masked->filter(%$filter);
1600 # for older versions of Imager
1601 my $subset = $img->crop(left=>$left, top=>$top,
1602 right=>$right, bottom=>$bottom);
1603 $subset->filter(%$filter);
1604 $img->paste(left=>$left, top=>$top, img=>$subset);
1615 Imager::Graph::Pie(3), Imager(3), perl(1).
1619 Tony Cook <tony@develop-help.com>
1623 Imager::Graph is licensed under the same terms as perl itself.
1627 Addi for producing a cool imaging module. :)