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=>..., ...)
17 Imager::Graph provides style information to its base classes. It
18 defines the colors, text display information and fills based on both
19 built-in styles and modifications supplied by the user to the draw()
27 use vars qw($VERSION);
28 use Imager qw(:handy);
33 # the maximum recursion depth in determining a color, fill or number
34 use constant MAX_DEPTH => 10;
36 my $NUM_RE = '(?:[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]\d+?)?)';
40 This is a simple constructor. No parameters required.
50 Returns an error message. Only value if the draw() method returns false.
60 Creates a new image, draws the chart onto that image and returns it.
62 Typically you will need to supply a C<data> parameter in the format
63 required by that particular graph, and if your graph will use any
64 text, a C<font> parameter
66 You can also supply many different parameters which control the way
67 the graph looks. These are supplied as keyword, value pairs, where
68 the value can be a hashref containing sub values.
70 The C<style> parameter will selects a basic color set, and possibly
71 sets other related parameters. See L</"STYLES">.
73 my $img = $graph->draw(data=>\@data,
74 title=>{ text=>"Hello, World!",
78 When referring to a single sub-value this documentation will refer to
79 'title.color' rather than 'the color element of title'.
81 Returns the graph image on success, or false on failure.
87 The currently defined styles are:
93 a light red background with no outlines. Uses primary colors for the
94 data fills. This style is compatible with all versions of Imager.
96 Graphs drawn using this style should save well as a gif, even though
97 some graphs may perform a slight blur.
99 This is the default style.
103 designed for monochrome output, such as most laser printers, this uses
104 hatched fills for the data, and no colors. The returned image is a
105 one channel image (which can be overridden with the C<channels>
108 You can also override the colors used by all components for background
109 or drawing by supplying C<fg> and/or C<bg> parameters. ie. if you
110 supply C<<fg=>'FF0000', channels=>3>> then the hash fills and anything
111 else will be drawn in red. Another use might be to set a transparent
112 background, by supplying C<<bg=>'00000000', channels=>4>>.
114 This style outlines the legend if present and outlines the hashed fills.
116 This and following styles require versions of Imager after 0.38.
120 designed as a "pretty" style this uses linear fountain fills for the
121 background and data fills, and adds a drop shadow.
123 You can override the value used for text and outlines by setting the
128 also designed as a "pretty" style this uses radial fountain fills for
129 the data and a linear blue to green fill for the background.
135 Each graph type has a number of features. These are used to add
136 various items that are displayed in the graph area. Some common
143 adds a box containing boxes filled with the data filess, with
144 the labels provided to the draw method. The legend will only be
145 displayed if both the legend feature is enabled and labels are
150 labels each data fill, usually by including text inside the data fill.
151 If the text does not fit in the fill, they could be displayed in some
152 other form, eg. as callouts in a pie graph. There usually isn't much
153 point in including both labels and a legend.
157 a simple drop shadow is shown behind some of the graph elements.
161 Each graph also has features specific to that graph.
163 =head1 COMMON PARAMETERS
165 When referring to a single sub-value this documentation will refer to
166 'title.color' rather than 'the color element of title'.
168 Normally, except for the font parameter, these are controlled by
169 styles, but these are the style parameters I'd mostly likely expect
176 the Imager font object used to draw text on the chart.
180 the background fill for the graph. Default depends on the style.
184 the base size of the graph image. Default: 256
188 the width of the graph image. Default: 1.5 * size (384)
192 the height of the graph image. Default: size (256)
196 the number of channels in the image. Default: 3 (the 'mono' style
201 the color used for drawing lines, such as outlines or callouts.
202 Default depends on the current style. Set to undef to remove the
203 outline from a style.
207 the text used for a graph title. Default: no title. Note: this is
208 the same as the title=>{ text => ... } field.
214 horizontal alignment of the title in the graph, one of 'left',
215 'center' or 'right'. Default: center
219 vertical alignment of the title, one of 'top', 'center' or 'right'.
220 Default: top. It's probably a bad idea to set this to 'center' unless
221 you have a very short title.
227 This contains basic defaults used in drawing text.
233 the default color used for all text, defaults to the fg color.
237 the base size used for text, also used to scale many graph elements.
246 In most cases you will want to use just the styles, but you may want
247 to exert more control over the way your chart looks. This section
248 describes the options you can use to control the way your chart looks.
250 Hopefully you don't need to read this.
256 The background of the graph.
262 Used to define basic background and foreground colors for the graph.
263 The bg color may be used for the background of the graph, and is used
264 as a default for the background of hatcheed fills. The fg is used as
265 the default for line and text colors.
269 The default font used by the graph. Normally you should supply this
270 if your graph as any text.
274 The default line color.
278 defaults for drawing text. Other textual graph elements will inherit
279 or modify these values.
285 default text color, defaults to the I<fg> color.
289 default text size. Default: 14. This is used to scale many graph
290 elements, including padding and leader sizes. Other text elements
291 will either use or scale this value.
295 default font object. Inherited from I<font>, which should have been
296 supplied by the caller.
302 If you supply a scalar value for this element, it will be stored in
305 Defines the text, font and layout information for the title.
311 The color of the title, inherited from I<text.color>.
315 The font object used for the title, inherited from I<text.font>.
319 size of the title text. Default: double I<text.size>
325 The horizontal and vertical alignment of the title.
331 defines attributes of the graph legend, if present.
341 text attributes for the labels used in the legend.
345 the width and height of the color patch in the legend. Defaults to
346 90% of the legend text size.
350 the minimum gap between patches in pixels. Defaults to 30% of the
355 the color of the border drawn around each patch. Inherited from I<line>.
361 the horizontal and vertical alignment of the legend within the graph.
362 Defaults to 'right' and 'top'.
366 the gap between the legend patches and text and the outside of it's
367 box, or to the legend border, if any.
371 the gap between the border and the outside of the legend's box. This
372 is only used if the I<legend.border> attribute is defined.
376 the background fill for the legend. Default: none
380 the border color of the legend. Default: none (no border is drawn
387 defines attributes for graph callouts, if any are present. eg. if the
388 pie graph cannot fit the label into the pie graph segement it will
389 present it as a callout.
399 the text attributes of the callout label. Inherited from I<text>.
403 the color of the callout lines. Inherited from I<line>
409 the length of the leader on the inside and the outside of the fill,
410 usually at some angle. Both default to the size of the callout text.
414 the length of the horizontal portion of the leader. Default:
419 the gap between the callout leader and the callout text. Defaults to
420 30% of the text callout size.
426 defines attributes for labels drawn into the data areas of a graph.
436 The text attributes of the labels. Inherited from I<text>.
442 the attributes of the graph's drop shadow
448 the fill used for the drop shadow. Default: '404040' (dark gray)
452 the offset of the drop shadow. A convenience value inherited by offx
453 and offy. Default: 40% of I<text.size>.
459 the horizontal and vertical offsets of the drop shadow. Both
460 inherited from I<dropshadow.off>.
464 the filter description passed to Imager's filter method to blur the
465 drop shadow. Default: an 11 element convolution filter.
471 describes the lines drawn around filled data areas, such as the
472 segments of a pie chart.
478 the line color of the outlines, inherited from I<line>.
484 a reference to an array containing fills for each data item.
486 You can mix fill types, ie. using a simple color for the first item, a
487 hatched fill for the second and a fountain fill for the next.
491 =head1 HOW VALUES WORK
493 Internally rather than specifying literal color, fill, or font objects
494 or literal sizes for each element, Imager::Graph uses a number of
495 special values to inherit or modify values taken from other graph
498 =head2 Specifying colors
500 You can specify colors by either supplying an Imager::Color object, by
501 supplying lookup of another color, or by supplying a single value that
502 Imager::Color::new can use as an initializer. The most obvious is
503 just a 6 or 8 digit hex value representing the red, green, blue and
504 optionally alpha channels of the image.
506 You can lookup another color by using the lookup() "function", for
507 example if you give a color as "lookup(fg)" then Imager::Graph will
508 look for the fg element in the current style (or as overridden by
509 you.) This is used internally by Imager::Graph to set up the
510 relationships between the colors of various elements, for example the
511 default style information contains:
518 color=>'lookup(text.color)',
522 So by setting the I<fg> color, you also set the default text color,
523 since each text element uses lookup(text.color) as its value.
525 =head2 Specifying fills
527 Fills can be used for the graph background color, the background color
528 for the legend block and for the fills used for each data element.
530 You can specify a fill as a L<color value|Specifying colors> or as a
531 general fill, see L<Imager::Fill> for details. To use a general fill
532 you need a version of Imager after 0.38.
534 You don't need (or usually want) to call Imager::Fill::new yourself,
535 since the various fill functions will call it for you, and
536 Imager::Graph provides some hooks to make them more useful.
542 with hatched fills, if you don't supply a 'fg' or 'bg' parameter,
543 Imager::Graph will supply the current graph fg and bg colors.
547 with fountain fill, you can supply the xa_ratio, ya_ratio, xb_ratio
548 and yb_ratio parameters, and they will be scaled in the fill area to
549 define the fountain fills xa, ya, xb and yb parameters.
553 As with colors, you can use lookup(name) or lookup(name1.name2) to
554 have one element to inherit the fill of another.
556 =head2 Specifying numbers
558 You can specify various numbers, usually representing the size of
559 something, commonly text, but sometimes the length of a line or the
562 You can use the same lookup mechanism as with colors and fills, but
563 you can also scale values. For example, 'scale(0.5,text.size)' will
564 return half the size of the normal text size.
566 As with colors, this is used internally to scale graph elements based
567 on the base text size. If you change the base text size then other
568 graph elements will scale as well.
570 =head2 Specifying other elements
572 Other elements, such as fonts, or parameters for a filter, can also
573 use the lookup(name) mechanism.
575 =head1 INTERNAL METHODS
577 Only useful if you need to fix bugs, add features or create a new
589 color => 'lookup(fg)',
590 font => 'lookup(font)',
594 color => 'lookup(text.color)',
595 font => 'lookup(text.font)',
598 size => 'scale(text.size,2.0)',
601 color => 'lookup(text.color)',
602 font => 'lookup(text.font)',
603 size => 'lookup(text.size)',
604 patchsize => 'scale(legend.size,0.9)',
605 patchgap => 'scale(legend.patchsize,0.3)',
606 patchborder => 'lookup(line)',
609 padding => 'scale(legend.size,0.3)',
610 outsidepadding => 'scale(legend.padding,0.4)',
613 color => 'lookup(text.color)',
614 font => 'lookup(text.font)',
615 size => 'lookup(text.size)',
616 line => 'lookup(line)',
617 inside => 'lookup(callout.size)',
618 outside => 'lookup(callout.size)',
619 leadlen => 'scale(0.8,callout.size)',
620 gap => 'scale(callout.size,0.3)',
623 font => 'lookup(text.font)',
624 size => 'lookup(text.size)',
625 color => 'lookup(text.color)',
626 hpad => 'lookup(label.pad)',
627 vpad => 'lookup(label.pad)',
628 pad => 'scale(label.size,0.2)',
629 pcformat => sub { sprintf "%s (%.0f%%)", $_[0], $_[1] },
630 pconlyformat => sub { sprintf "%.1f%%", $_[0] },
634 off => 'scale(0.4,text.size)',
635 offx => 'lookup(dropshadow.off)',
636 offy => 'lookup(dropshadow.off)',
637 filter => { type=>'conv',
638 # this needs a fairly heavy blur
639 coef=>[0.1, 0.2, 0.4, 0.6, 0.7, 0.9, 1.2,
640 0.9, 0.7, 0.6, 0.4, 0.2, 0.1 ] },
643 line =>'lookup(line)',
646 width=>'scale(1.5,size)',
647 height=>'lookup(size)',
650 =item _error($message)
652 Sets the error field of the object and returns an empty list or undef,
653 depending on context. Should be used for error handling, since it may
654 provide some user hooks at some point.
659 my ($self, $error) = @_;
661 $self->{_errstr} = $error;
669 Returns the style defaults, such as the relationships between line
670 color and text color.
672 Intended to be over-ridden by base classes to provide graph specific
681 my $def_style = 'primary_red';
689 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
695 patchborder=>'000000'
708 { hatch=>'stipple3' },
709 { hatch=>'stipple2' },
714 features=>{ outline=>1 },
723 { fountain=>'linear',
724 xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
726 segments => Imager::Fountain->simple(positions=>[0, 1],
727 colors=>[ NC('FFC0C0'), NC('FF0000') ]),
729 { fountain=>'linear',
730 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
731 segments => Imager::Fountain->simple(positions=>[0, 1],
732 colors=>[ NC('C0FFC0'), NC('00FF00') ]),
734 { fountain=>'linear',
735 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
736 segments => Imager::Fountain->simple(positions=>[0, 1],
737 colors=>[ NC('C0C0FF'), NC('0000FF') ]),
739 { fountain=>'linear',
740 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
741 segments => Imager::Fountain->simple(positions=>[0, 1],
742 colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
744 { fountain=>'linear',
745 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
746 segments => Imager::Fountain->simple(positions=>[0, 1],
747 colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
749 { fountain=>'linear',
750 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
751 segments => Imager::Fountain->simple(positions=>[0, 1],
752 colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
755 back=>{ fountain=>'linear',
756 xa_ratio=>0, ya_ratio=>0,
757 xb_ratio=>1.0, yb_ratio=>1.0,
758 segments=>Imager::Fountain->simple
760 colors=>[ NC('6060FF'), NC('60FF60') ]) },
763 features=>{ dropshadow=>1 },
769 { fountain=>'radial',
770 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
771 segments => Imager::Fountain->simple(positions=>[0, 1],
772 colors=>[ NC('FF8080'), NC('FF0000') ]),
774 { fountain=>'radial',
775 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
776 segments => Imager::Fountain->simple(positions=>[0, 1],
777 colors=>[ NC('80FF80'), NC('00FF00') ]),
779 { fountain=>'radial',
780 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
781 segments => Imager::Fountain->simple(positions=>[0, 1],
782 colors=>[ NC('808080FF'), NC('0000FF') ]),
784 { fountain=>'radial',
785 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
786 segments => Imager::Fountain->simple(positions=>[0, 1],
787 colors=>[ NC('FFFF80'), NC('FFFF00') ]),
789 { fountain=>'radial',
790 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
791 segments => Imager::Fountain->simple(positions=>[0, 1],
792 colors=>[ NC('80FFFF'), NC('00FFFF') ]),
794 { fountain=>'radial',
795 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
796 segments => Imager::Fountain->simple(positions=>[0, 1],
797 colors=>[ NC('FF80FF'), NC('FF00FF') ]),
800 back=>{ fountain=>'linear',
801 xa_ratio=>0, ya_ratio=>0,
802 xb_ratio=>1.0, yb_ratio=>1.0,
803 segments=>Imager::Fountain->simple
805 colors=>[ NC('6060FF'), NC('60FF60') ]) },
811 =item $self->_style_setup(\%opts)
813 Uses the values from %opts to build a customized hash describing the
814 way the graph should be drawn.
819 my ($self, $opts) = @_;
820 my $style_defs = $self->_style_defs;
822 $style = $styles{$opts->{style}} if $opts->{style};
823 $style ||= $styles{$def_style};
825 my @search_list = ( $style_defs, $style, $opts);
828 my @composite = $self->_composite();
830 @composite{@composite} = @composite;
832 for my $src (@search_list) {
833 for my $key (keys %$src) {
834 if ($composite{$key}) {
835 $work{$key} = {} unless exists $work{$key};
836 if (ref $src->{$key}) {
837 # some keys have sub values, especially text
838 @{$work{$key}}{keys %{$src->{$key}}} = values %{$src->{$key}};
841 # assume it's the text for a title or something
842 $work{$key}{text} = $src->{$key};
846 $work{$key} = $src->{$key};
851 # features are handled specially
852 $work{features} = {};
853 for my $src (@search_list) {
854 if ($src->{features}) {
855 if (ref $src->{features}) {
856 if (ref($src->{features}) =~ /ARRAY/) {
857 # just set those features
858 for my $feature (@{$src->{features}}) {
859 $work{features}{$feature} = 1;
862 elsif (ref($src->{features}) =~ /HASH/) {
863 if ($src->{features}{reset}) {
864 $work{features} = {}; # only the ones the user specifies
866 @{$work{features}}{keys %{$src->{features}}} =
867 values(%{$src->{features}});
871 # just set that single feature
872 $work{features}{$src->{features}} = 1;
877 #print Dumper(\%work);
879 $self->{_style} = \%work;
882 =item $self->_get_thing($name)
884 Retrieve some general 'thing'.
886 Supports the 'lookup(foo)' mechanism.
891 my ($self, $name, @depth) = @_;
895 if ($name =~ /^(\w+)\.(\w+)$/) {
896 $what = $self->{_style}{$1}{$2};
899 $what = $self->{_style}{$name};
906 elsif ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
908 or return $self->_error("too many levels of recursion in lookup(@depth)");
909 return $self->_get_thing($1, @depth);
916 =item $self->_get_number($name)
918 Retrieves a number from the style. The value in the style can be the
919 number, or one of two functions:
923 =item lookup(newname)
925 Recursively looks up I<newname> in the style.
927 =item scale(value1,value2)
929 Each value can be a number or a name. Names are recursively looks up
930 in the style and the product is returned.
937 my ($self, $name, @depth) = @_;
941 if ($name =~ /^(\w+)\.(\w+)$/) {
942 $what = $self->{_style}{$1}{$2};
945 $what = $self->{_style}{$name};
948 return $self->_error("$name is undef (@depth)");
951 if ($what =~ /CODE/) {
952 $what = $what->($self, $name);
956 if ($what =~ /^lookup\(([\w.]+)\)$/) {
958 or return $self->_error("too many levels of recursion in lookup (@depth)");
959 return $self->_get_number($1, @depth);
961 elsif ($what =~ /^scale\(
962 ((?:[a-z][\w.]*)|$NUM_RE)
964 ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
965 my ($left, $right) = ($1, $2);
966 unless ($left =~ /^$NUM_RE$/) {
968 or return $self->_error("too many levels of recursion in scale (@depth)");
969 $left = $self->_get_number($left, @depth);
971 unless ($right =~ /^$NUM_RE$/) {
973 or return $self->_error("too many levels of recursion in scale (@depth)");
974 $right = $self->_get_number($right, @depth);
976 return $left * $right;
984 =item $self->_get_integer($name)
986 Retrieves an integer from the style. This is a simple wrapper around
987 _get_number() that rounds the result to an integer.
992 my ($self, $name, @depth) = @_;
994 my $number = $self->_get_number($name, @depth)
997 return sprintf("%.0f", $number);
1000 =item _get_color($name)
1002 Returns a color object of the given name from the style hash.
1004 Uses Imager::Color->new to translate normal scalars into color objects.
1006 Allows the lookup(name) mechanism.
1011 my ($self, $name, @depth) = @_;
1013 push(@depth, $name);
1015 if ($name =~ /^(\w+)\.(\w+)$/) {
1016 $what = $self->{_style}{$1}{$2};
1019 $what = $self->{_style}{$name};
1023 or return $self->_error("$name was undefined (@depth)");
1025 unless (ref $what) {
1026 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1027 @depth < MAX_DEPTH or
1028 return $self->_error("too many levels of recursion in lookup (@depth)");
1030 return $self->_get_color($1, @depth);
1032 $what = Imager::Color->new($what);
1038 =item _translate_fill($what, $box)
1040 Given the value of a fill, either attempts to convert it into a fill
1041 list (one of C<<color=>$color_value, filled=>1>> or C<<fill=>{ fill
1042 parameters }>>), or to lookup another fill that is referred to with
1043 the 'lookup(name)' mechanism.
1045 This function does the fg and bg initialization for hatched fills, and
1046 translation of *_ratio for fountain fills (using the $box parameter).
1050 sub _translate_fill {
1051 my ($self, $what, $box, @depth) = @_;
1054 if (UNIVERSAL::isa($what, "Imager::Color")) {
1055 return ( color=>Imager::Color->new($what), filled=>1 );
1059 if ($what->{hatch}) {
1062 $work{fg} = $self->_get_color('fg')
1066 $work{bg} = $self->_get_color('bg')
1069 return ( fill=>\%work );
1071 elsif ($what->{fountain}) {
1073 for my $key (qw(xa ya xb yb)) {
1074 if (exists $work{"${key}_ratio"}) {
1076 $work{$key} = $box->[0] + $work{"${key}_ratio"}
1077 * ($box->[2] - $box->[0]);
1080 $work{$key} = $box->[1] + $work{"${key}_ratio"}
1081 * ($box->[3] - $box->[1]);
1085 return ( fill=>\%work );
1088 return ( fill=> $what );
1093 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1094 return $self->_get_fill($1, $box, @depth);
1097 # assumed to be an Imager::Color single value
1098 return ( color=>Imager::Color->new($what), filled=>1 );
1103 =item _data_fill($index, $box)
1105 Retrieves the fill parameters for a data area fill.
1110 my ($self, $index, $box) = @_;
1112 my $fills = $self->{_style}{fills};
1113 return $self->_translate_fill($fills->[$index % @$fills], $box,
1117 =item _get_fill($index, $box)
1119 Retrieves fill parameters for a named fill.
1124 my ($self, $name, $box, @depth) = @_;
1126 push(@depth, $name);
1128 if ($name =~ /^(\w+)\.(\w+)$/) {
1129 $what = $self->{_style}{$1}{$2};
1132 $what = $self->{_style}{$name};
1136 or return $self->_error("no fill $name found");
1138 return $self->_translate_fill($what, $box, @depth);
1143 Builds the image object for the graph and fills it with the background
1151 my ($width, $height) = (256, 256);
1153 $width = $self->_get_number('width');
1154 $height = $self->_get_number('height');
1155 my $channels = $self->{_style}{channels};
1159 my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels);
1161 $img->box($self->_get_fill('back', [ 0, 0, $width-1, $height-1]));
1167 my ($self, $name) = @_;
1171 if ($self->{_style}{$name}) {
1172 %work = %{$self->{_style}{$name}};
1175 %work = %{$self->{_style}{text}};
1178 or return $self->_error("$name has no font parameter");
1180 $work{font} = $self->_get_thing("$name.font")
1181 or return $self->_error("invalid font");
1182 UNIVERSAL::isa($work{font}, "Imager::Font")
1183 or return $self->_error("$name.font is not a font");
1184 if ($work{color} && !ref $work{color}) {
1185 $work{color} = $self->_get_color("$name.color")
1188 $work{size} = $self->_get_number("$name.size");
1189 $work{sizew} = $self->_get_number("$name.sizew")
1196 my ($self, $text, $name) = @_;
1198 my %text_info = $self->_text_style($name);
1200 my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
1207 my ($self, $box, $chart_box, $name) = @_;
1209 my $halign = $self->{_style}{$name}{halign}
1210 or $self->_error("no halign for $name");
1211 my $valign = $self->{_style}{$name}{valign};
1213 if ($halign eq 'right') {
1214 $box->[0] += $chart_box->[2] - $box->[2];
1216 elsif ($halign eq 'left') {
1217 $box->[0] = $chart_box->[0];
1219 elsif ($halign eq 'center' || $halign eq 'centre') {
1220 $box->[0] = ($chart_box->[0] + $chart_box->[2] - $box->[2])/2;
1223 return $self->_error("invalid halign $halign for $name");
1226 if ($valign eq 'top') {
1227 $box->[1] = $chart_box->[1];
1229 elsif ($valign eq 'bottom') {
1230 $box->[1] = $chart_box->[3] - $box->[3];
1232 elsif ($valign eq 'center' || $valign eq 'centre') {
1233 $box->[1] = ($chart_box->[1] + $chart_box->[3] - $box->[3])/2;
1236 return $self->_error("invalid valign $valign for $name");
1238 $box->[2] += $box->[0];
1239 $box->[3] += $box->[1];
1243 my ($self, $chart_box, $object_box) = @_;
1247 if ($object_box->[0] - $chart_box->[0]
1248 < $chart_box->[2] - $object_box->[2]) {
1249 $areax = ($object_box->[2] - $chart_box->[0])
1250 * ($chart_box->[3] - $chart_box->[1]);
1253 $areax = ($chart_box->[2] - $object_box->[0])
1254 * ($chart_box->[3] - $chart_box->[1]);
1257 if ($object_box->[1] - $chart_box->[1]
1258 < $chart_box->[3] - $object_box->[3]) {
1259 $areay = ($object_box->[3] - $chart_box->[1])
1260 * ($chart_box->[2] - $chart_box->[0]);
1263 $areay = ($chart_box->[3] - $object_box->[1])
1264 * ($chart_box->[2] - $chart_box->[0]);
1267 if ($areay < $areax) {
1268 if ($object_box->[1] - $chart_box->[1]
1269 < $chart_box->[3] - $object_box->[3]) {
1270 $chart_box->[1] = $object_box->[3];
1273 $chart_box->[3] = $object_box->[1];
1277 if ($object_box->[0] - $chart_box->[0]
1278 < $chart_box->[2] - $object_box->[2]) {
1279 $chart_box->[0] = $object_box->[2];
1282 $chart_box->[2] = $object_box->[0];
1288 my ($self, $img, $labels, $chart_box) = @_;
1290 defined(my $padding = $self->_get_integer('legend.padding'))
1292 my $patchsize = $self->_get_integer('legend.patchsize')
1294 defined(my $gap = $self->_get_integer('legend.patchgap'))
1296 my $minrowsize = $patchsize + $gap;
1297 my ($width, $height) = (0,0);
1299 for my $label (@$labels) {
1300 my @box = $self->_text_bbox($label, 'legend');
1301 push(@sizes, \@box);
1302 $width = $box[2] if $box[2] > $width;
1303 if ($minrowsize > $box[3]) {
1304 $height += $minrowsize;
1311 $width + $patchsize + $padding * 2 + $gap,
1312 $height + $padding * 2 - $gap);
1313 my $outsidepadding = 0;
1314 if ($self->{_style}{legend}{border}) {
1315 defined($outsidepadding = $self->_get_number('legend.outsidepadding'))
1317 $box[2] += 2 * $outsidepadding;
1318 $box[3] += 2 * $outsidepadding;
1320 $self->_align_box(\@box, $chart_box, 'legend')
1322 if ($self->{_style}{legend}{fill}) {
1323 $img->box(xmin=>$box[0]+$outsidepadding,
1324 ymin=>$box[1]+$outsidepadding,
1325 xmax=>$box[2]-$outsidepadding,
1326 ymax=>$box[3]-$outsidepadding,
1327 $self->_get_fill('legend.fill', \@box));
1329 $box[0] += $outsidepadding;
1330 $box[1] += $outsidepadding;
1331 $box[2] -= $outsidepadding;
1332 $box[3] -= $outsidepadding;
1333 my $ypos = $box[1] + $padding;
1334 my $patchpos = $box[0]+$padding;
1335 my $textpos = $patchpos + $patchsize + $gap;
1336 my %text_info = $self->_text_style('legend')
1339 if ($self->{_style}{legend}{patchborder}) {
1340 $patchborder = $self->_get_color('legend.patchborder')
1344 for my $label (@$labels) {
1345 my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
1346 $patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
1347 my @fill = $self->_data_fill($dataindex, \@patchbox)
1349 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
1350 ymax=>$ypos + $patchsize, @fill);
1351 if ($self->{_style}{legend}{patchborder}) {
1352 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
1353 ymax=>$ypos + $patchsize,
1354 color=>$patchborder);
1356 $img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize,
1359 my $step = $patchsize + $gap;
1360 if ($minrowsize < $sizes[$dataindex][3]) {
1361 $ypos += $sizes[$dataindex][3];
1364 $ypos += $minrowsize;
1368 if ($self->{_style}{legend}{border}) {
1369 my $border_color = $self->_get_color('legend.border')
1371 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
1372 color=>$border_color);
1374 $self->_remove_box($chart_box, \@box);
1379 my ($self, $img, $chart_box) = @_;
1381 my $title = $self->{_style}{title}{text};
1382 my @box = $self->_text_bbox($title, 'title');
1385 $self->_align_box(\@box, $chart_box, 'title');
1386 my %text_info = $self->_text_style('title');
1387 $img->string(%text_info, x=>$box[0], 'y'=>$box[3] + $yoff, text=>$title);
1388 $self->_remove_box($chart_box, \@box);
1393 my ($self, $box) = @_;
1395 if ($box->[2] - $box->[0] > $box->[3] - $box->[1]) {
1396 return $box->[3] - $box->[1];
1399 return $box->[2] - $box->[0];
1405 Returns a list of style fields that are stored as composites, and
1406 should be merged instead of just being replaced.
1411 qw(title legend text label dropshadow outline callout);
1414 sub _filter_region {
1415 my ($self, $img, $left, $top, $right, $bottom, $filter) = @_;
1417 unless (ref $filter) {
1419 $filter = $self->_get_thing($name)
1422 or return $self->_error("no type for filter $name");
1425 $left > 0 or $left = 0;
1426 $top > 0 or $top = 0;
1428 # newer versions of Imager let you work on just part of an image
1429 if ($img->can('masked') && !$self->{_style}{features}{_debugblur}) {
1430 my $masked = $img->masked(left=>$left, top=>$top,
1431 right=>$right, bottom=>$bottom);
1432 $masked->filter(%$filter);
1435 # for older versions of Imager
1436 my $subset = $img->crop(left=>$left, top=>$top,
1437 right=>$right, bottom=>$bottom);
1438 $subset->filter(%$filter);
1439 $img->paste(left=>$left, top=>$top, img=>$subset);
1450 Imager::Graph::Pie(3), Imager(3), perl(1).
1454 Tony Cook <tony@develop-help.com>
1458 Imager::Graph is licensed under the same terms as perl itself.
1462 Addi for producing a cool imaging module. :)