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.
690 The intended usage is:
693 or return $self->_error("error description");
695 You should almost always return the result of _error() or return
696 immediately afterwards.
701 my ($self, $error) = @_;
703 $self->{_errstr} = $error;
711 Returns the style defaults, such as the relationships between line
712 color and text color.
714 Intended to be over-ridden by base classes to provide graph specific
723 # Let's make the default something that looks really good, so folks will be interested enough to customize the style.
724 my $def_style = 'fount_lin';
732 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
738 #patchborder=>'000000'
745 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
751 patchborder=>'000000'
764 { hatch=>'stipple3' },
765 { hatch=>'stipple2' },
770 features=>{ outline=>1 },
779 { fountain=>'linear',
780 xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
781 segments => Imager::Fountain->simple(positions=>[0, 1],
782 colors=>[ NC('FFC0C0'), NC('FF0000') ]),
784 { fountain=>'linear',
785 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
786 segments => Imager::Fountain->simple(positions=>[0, 1],
787 colors=>[ NC('C0FFC0'), NC('00FF00') ]),
789 { fountain=>'linear',
790 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
791 segments => Imager::Fountain->simple(positions=>[0, 1],
792 colors=>[ NC('C0C0FF'), NC('0000FF') ]),
794 { fountain=>'linear',
795 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
796 segments => Imager::Fountain->simple(positions=>[0, 1],
797 colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
799 { fountain=>'linear',
800 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
801 segments => Imager::Fountain->simple(positions=>[0, 1],
802 colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
804 { fountain=>'linear',
805 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
806 segments => Imager::Fountain->simple(positions=>[0, 1],
807 colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
810 back=>{ fountain=>'linear',
811 xa_ratio=>0, ya_ratio=>0,
812 xb_ratio=>1.0, yb_ratio=>1.0,
813 segments=>Imager::Fountain->simple
815 colors=>[ NC('6060FF'), NC('60FF60') ]) },
818 features=>{ dropshadow=>1 },
824 { fountain=>'radial',
825 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
826 segments => Imager::Fountain->simple(positions=>[0, 1],
827 colors=>[ NC('FF8080'), NC('FF0000') ]),
829 { fountain=>'radial',
830 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
831 segments => Imager::Fountain->simple(positions=>[0, 1],
832 colors=>[ NC('80FF80'), NC('00FF00') ]),
834 { fountain=>'radial',
835 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
836 segments => Imager::Fountain->simple(positions=>[0, 1],
837 colors=>[ NC('808080FF'), NC('0000FF') ]),
839 { fountain=>'radial',
840 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
841 segments => Imager::Fountain->simple(positions=>[0, 1],
842 colors=>[ NC('FFFF80'), NC('FFFF00') ]),
844 { fountain=>'radial',
845 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
846 segments => Imager::Fountain->simple(positions=>[0, 1],
847 colors=>[ NC('80FFFF'), NC('00FFFF') ]),
849 { fountain=>'radial',
850 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
851 segments => Imager::Fountain->simple(positions=>[0, 1],
852 colors=>[ NC('FF80FF'), NC('FF00FF') ]),
855 back=>{ fountain=>'linear',
856 xa_ratio=>0, ya_ratio=>0,
857 xb_ratio=>1.0, yb_ratio=>1.0,
858 segments=>Imager::Fountain->simple
860 colors=>[ NC('6060FF'), NC('60FF60') ]) },
866 =item $self->_style_setup(\%opts)
868 Uses the values from %opts to build a customized hash describing the
869 way the graph should be drawn.
874 my ($self, $opts) = @_;
875 my $style_defs = $self->_style_defs;
877 $style = $styles{$opts->{style}} if $opts->{style};
878 $style ||= $styles{$def_style};
880 my @search_list = ( $style_defs, $style, $opts);
883 my @composite = $self->_composite();
885 @composite{@composite} = @composite;
887 for my $src (@search_list) {
888 for my $key (keys %$src) {
889 if ($composite{$key}) {
890 $work{$key} = {} unless exists $work{$key};
891 if (ref $src->{$key}) {
892 # some keys have sub values, especially text
893 @{$work{$key}}{keys %{$src->{$key}}} = values %{$src->{$key}};
896 # assume it's the text for a title or something
897 $work{$key}{text} = $src->{$key};
901 $work{$key} = $src->{$key};
906 # features are handled specially
907 $work{features} = {};
908 for my $src (@search_list) {
909 if ($src->{features}) {
910 if (ref $src->{features}) {
911 if (ref($src->{features}) =~ /ARRAY/) {
912 # just set those features
913 for my $feature (@{$src->{features}}) {
914 $work{features}{$feature} = 1;
917 elsif (ref($src->{features}) =~ /HASH/) {
918 if ($src->{features}{reset}) {
919 $work{features} = {}; # only the ones the user specifies
921 @{$work{features}}{keys %{$src->{features}}} =
922 values(%{$src->{features}});
926 # just set that single feature
927 $work{features}{$src->{features}} = 1;
932 #print Dumper(\%work);
934 $self->{_style} = \%work;
937 =item $self->_get_thing($name)
939 Retrieve some general 'thing'.
941 Supports the 'lookup(foo)' mechanism.
943 Returns an empty list on failure.
948 my ($self, $name, @depth) = @_;
952 if ($name =~ /^(\w+)\.(\w+)$/) {
953 $what = $self->{_style}{$1}{$2};
956 $what = $self->{_style}{$name};
963 elsif ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
965 or return $self->_error("too many levels of recursion in lookup(@depth)");
966 return $self->_get_thing($1, @depth);
973 =item $self->_get_number($name)
975 Retrieves a number from the style. The value in the style can be the
976 number, or one of two functions:
980 =item lookup(newname)
982 Recursively looks up I<newname> in the style.
984 =item scale(value1,value2)
986 Each value can be a number or a name. Names are recursively looked up
987 in the style and the product is returned.
994 my ($self, $name, @depth) = @_;
998 if ($name =~ /^(\w+)\.(\w+)$/) {
999 $what = $self->{_style}{$1}{$2};
1002 $what = $self->{_style}{$name};
1005 return $self->_error("$name is undef (@depth)");
1008 if ($what =~ /CODE/) {
1009 $what = $what->($self, $name);
1013 if ($what =~ /^lookup\(([\w.]+)\)$/) {
1015 or return $self->_error("too many levels of recursion in lookup (@depth)");
1016 return $self->_get_number($1, @depth);
1018 elsif ($what =~ /^scale\(
1019 ((?:[a-z][\w.]*)|$NUM_RE)
1021 ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
1022 my ($left, $right) = ($1, $2);
1023 unless ($left =~ /^$NUM_RE$/) {
1025 or return $self->_error("too many levels of recursion in scale (@depth)");
1026 $left = $self->_get_number($left, @depth);
1028 unless ($right =~ /^$NUM_RE$/) {
1030 or return $self->_error("too many levels of recursion in scale (@depth)");
1031 $right = $self->_get_number($right, @depth);
1033 return $left * $right;
1041 =item $self->_get_integer($name)
1043 Retrieves an integer from the style. This is a simple wrapper around
1044 _get_number() that rounds the result to an integer.
1046 Returns an empty list on failure.
1051 my ($self, $name, @depth) = @_;
1053 my $number = $self->_get_number($name, @depth)
1056 return sprintf("%.0f", $number);
1059 =item _get_color($name)
1061 Returns a color object of the given name from the style hash.
1063 Uses Imager::Color->new to translate normal scalars into color objects.
1065 Allows the lookup(name) mechanism.
1067 Returns an empty list on failure.
1072 my ($self, $name, @depth) = @_;
1074 push(@depth, $name);
1076 if ($name =~ /^(\w+)\.(\w+)$/) {
1077 $what = $self->{_style}{$1}{$2};
1080 $what = $self->{_style}{$name};
1084 or return $self->_error("$name was undefined (@depth)");
1086 unless (ref $what) {
1087 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1088 @depth < MAX_DEPTH or
1089 return $self->_error("too many levels of recursion in lookup (@depth)");
1091 return $self->_get_color($1, @depth);
1093 $what = Imager::Color->new($what);
1099 =item _translate_fill($what, $box)
1101 Given the value of a fill, either attempts to convert it into a fill
1102 list (one of C<<color=>$color_value, filled=>1>> or C<<fill=>{ fill
1103 parameters }>>), or to lookup another fill that is referred to with
1104 the 'lookup(name)' mechanism.
1106 This function does the fg and bg initialization for hatched fills, and
1107 translation of *_ratio for fountain fills (using the $box parameter).
1109 Returns an empty list on failure.
1113 sub _translate_fill {
1114 my ($self, $what, $box, @depth) = @_;
1117 if (UNIVERSAL::isa($what, "Imager::Color")) {
1118 return ( color=>Imager::Color->new($what), filled=>1 );
1122 # default to normal combine mode
1123 my %work = ( combine => 'normal', %$what );
1124 if ($what->{hatch}) {
1126 $work{fg} = $self->_get_color('fg')
1130 $work{bg} = $self->_get_color('bg')
1133 return ( fill=>\%work );
1135 elsif ($what->{fountain}) {
1136 for my $key (qw(xa ya xb yb)) {
1137 if (exists $work{"${key}_ratio"}) {
1139 $work{$key} = $box->[0] + $work{"${key}_ratio"}
1140 * ($box->[2] - $box->[0]);
1143 $work{$key} = $box->[1] + $work{"${key}_ratio"}
1144 * ($box->[3] - $box->[1]);
1148 return ( fill=>\%work );
1151 return ( fill=> \%work );
1156 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1157 return $self->_get_fill($1, $box, @depth);
1160 # assumed to be an Imager::Color single value
1161 return ( color=>Imager::Color->new($what), filled=>1 );
1166 =item _data_fill($index, $box)
1168 Retrieves the fill parameters for a data area fill.
1173 my ($self, $index, $box) = @_;
1175 my $fills = $self->{_style}{fills};
1176 return $self->_translate_fill($fills->[$index % @$fills], $box,
1180 =item _get_fill($index, $box)
1182 Retrieves fill parameters for a named fill.
1187 my ($self, $name, $box, @depth) = @_;
1189 push(@depth, $name);
1191 if ($name =~ /^(\w+)\.(\w+)$/) {
1192 $what = $self->{_style}{$1}{$2};
1195 $what = $self->{_style}{$name};
1199 or return $self->_error("no fill $name found");
1201 return $self->_translate_fill($what, $box, @depth);
1206 Builds the image object for the graph and fills it with the background
1214 my $width = $self->_get_number('width') || 256;
1215 my $height = $self->_get_number('height') || 256;
1216 my $channels = $self->{_style}{channels};
1220 my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels);
1222 $img->box($self->_get_fill('back', [ 0, 0, $width-1, $height-1]));
1227 =item _text_style($name)
1229 Returns parameters suitable for calls to Imager::Font's bounding_box()
1230 and draw() methods intended for use in defining text styles.
1232 Returns an empty list on failure.
1237 my ($self, $name) = @_;
1241 if ($self->{_style}{$name}) {
1242 %work = %{$self->{_style}{$name}};
1245 %work = %{$self->{_style}{text}};
1248 or return $self->_error("$name has no font parameter");
1250 $work{font} = $self->_get_thing("$name.font")
1251 or return $self->_error("No $name.font defined, either set $name.font or font to a font");
1252 UNIVERSAL::isa($work{font}, "Imager::Font")
1253 or return $self->_error("$name.font is not a font");
1254 if ($work{color} && !ref $work{color}) {
1255 $work{color} = $self->_get_color("$name.color")
1258 $work{size} = $self->_get_number("$name.size");
1259 $work{sizew} = $self->_get_number("$name.sizew")
1265 =item _text_bbox($text, $name)
1267 Returns a bounding box for the specified $text as styled by $name.
1269 Returns an empty list on failure.
1274 my ($self, $text, $name) = @_;
1276 my %text_info = $self->_text_style($name)
1279 my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
1286 my ($self, $box, $chart_box, $name) = @_;
1288 my $halign = $self->{_style}{$name}{halign}
1289 or $self->_error("no halign for $name");
1290 my $valign = $self->{_style}{$name}{valign};
1292 if ($halign eq 'right') {
1293 $box->[0] += $chart_box->[2] - $box->[2];
1295 elsif ($halign eq 'left') {
1296 $box->[0] = $chart_box->[0];
1298 elsif ($halign eq 'center' || $halign eq 'centre') {
1299 $box->[0] = ($chart_box->[0] + $chart_box->[2] - $box->[2])/2;
1302 return $self->_error("invalid halign $halign for $name");
1305 if ($valign eq 'top') {
1306 $box->[1] = $chart_box->[1];
1308 elsif ($valign eq 'bottom') {
1309 $box->[1] = $chart_box->[3] - $box->[3];
1311 elsif ($valign eq 'center' || $valign eq 'centre') {
1312 $box->[1] = ($chart_box->[1] + $chart_box->[3] - $box->[3])/2;
1315 return $self->_error("invalid valign $valign for $name");
1317 $box->[2] += $box->[0];
1318 $box->[3] += $box->[1];
1322 my ($self, $chart_box, $object_box) = @_;
1326 if ($object_box->[0] - $chart_box->[0]
1327 < $chart_box->[2] - $object_box->[2]) {
1328 $areax = ($object_box->[2] - $chart_box->[0])
1329 * ($chart_box->[3] - $chart_box->[1]);
1332 $areax = ($chart_box->[2] - $object_box->[0])
1333 * ($chart_box->[3] - $chart_box->[1]);
1336 if ($object_box->[1] - $chart_box->[1]
1337 < $chart_box->[3] - $object_box->[3]) {
1338 $areay = ($object_box->[3] - $chart_box->[1])
1339 * ($chart_box->[2] - $chart_box->[0]);
1342 $areay = ($chart_box->[3] - $object_box->[1])
1343 * ($chart_box->[2] - $chart_box->[0]);
1346 if ($areay < $areax) {
1347 if ($object_box->[1] - $chart_box->[1]
1348 < $chart_box->[3] - $object_box->[3]) {
1349 $chart_box->[1] = $object_box->[3];
1352 $chart_box->[3] = $object_box->[1];
1356 if ($object_box->[0] - $chart_box->[0]
1357 < $chart_box->[2] - $object_box->[2]) {
1358 $chart_box->[0] = $object_box->[2];
1361 $chart_box->[2] = $object_box->[0];
1367 my ($self, $img, $labels, $chart_box) = @_;
1369 my $orient = $self->_get_thing('legend.orientation');
1370 defined $orient or $orient = 'vertical';
1372 if ($orient eq 'vertical') {
1373 return $self->_draw_legend_vertical($img, $labels, $chart_box);
1375 elsif ($orient eq 'horizontal') {
1376 return $self->_draw_legend_horizontal($img, $labels, $chart_box);
1379 return $self->_error("Unknown legend.orientation $orient");
1383 sub _draw_legend_horizontal {
1384 my ($self, $img, $labels, $chart_box) = @_;
1386 defined(my $padding = $self->_get_integer('legend.padding'))
1388 my $patchsize = $self->_get_integer('legend.patchsize')
1390 defined(my $gap = $self->_get_integer('legend.patchgap'))
1393 my $minrowsize = $patchsize + $gap;
1394 my ($width, $height) = (0,0);
1395 my $row_height = $minrowsize;
1399 for my $label (@$labels) {
1400 my @text_box = $self->_text_bbox($label, 'legend')
1402 push(@sizes, \@text_box);
1403 my $entry_width = $patchsize + $gap + $text_box[2];
1405 # never re-wrap the first entry
1406 push @offsets, [ 0, $height ];
1409 if ($pos + $gap + $entry_width > $chart_box->[2]) {
1411 $height += $row_height;
1413 push @offsets, [ $pos, $height ];
1415 my $entry_right = $pos + $entry_width;
1416 $pos += $gap + $entry_width;
1417 $entry_right > $width and $width = $entry_right;
1418 if ($text_box[3] > $row_height) {
1419 $row_height = $text_box[3];
1422 $height += $row_height;
1423 my @box = ( 0, 0, $width + $padding * 2, $height + $padding * 2 );
1424 my $outsidepadding = 0;
1425 if ($self->{_style}{legend}{border}) {
1426 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
1428 $box[2] += 2 * $outsidepadding;
1429 $box[3] += 2 * $outsidepadding;
1431 $self->_align_box(\@box, $chart_box, 'legend')
1433 if ($self->{_style}{legend}{fill}) {
1434 $img->box(xmin=>$box[0]+$outsidepadding,
1435 ymin=>$box[1]+$outsidepadding,
1436 xmax=>$box[2]-$outsidepadding,
1437 ymax=>$box[3]-$outsidepadding,
1438 $self->_get_fill('legend.fill', \@box));
1440 $box[0] += $outsidepadding;
1441 $box[1] += $outsidepadding;
1442 $box[2] -= $outsidepadding;
1443 $box[3] -= $outsidepadding;
1444 my %text_info = $self->_text_style('legend')
1447 if ($self->{_style}{legend}{patchborder}) {
1448 $patchborder = $self->_get_color('legend.patchborder')
1453 for my $label (@$labels) {
1454 my ($left, $top) = @{$offsets[$dataindex]};
1455 $left += $box[0] + $padding;
1456 $top += $box[1] + $padding;
1457 my $textpos = $left + $patchsize + $gap;
1458 my @patchbox = ( $left, $top,
1459 $left + $patchsize, $top + $patchsize );
1460 my @fill = $self->_data_fill($dataindex, \@patchbox)
1462 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
1463 ymax=>$top + $patchsize, @fill);
1464 if ($self->{_style}{legend}{patchborder}) {
1465 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
1466 ymax=>$top + $patchsize,
1467 color=>$patchborder);
1469 $img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize,
1474 if ($self->{_style}{legend}{border}) {
1475 my $border_color = $self->_get_color('legend.border')
1477 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
1478 color=>$border_color);
1480 $self->_remove_box($chart_box, \@box);
1484 sub _draw_legend_vertical {
1485 my ($self, $img, $labels, $chart_box) = @_;
1487 defined(my $padding = $self->_get_integer('legend.padding'))
1489 my $patchsize = $self->_get_integer('legend.patchsize')
1491 defined(my $gap = $self->_get_integer('legend.patchgap'))
1493 my $minrowsize = $patchsize + $gap;
1494 my ($width, $height) = (0,0);
1496 for my $label (@$labels) {
1497 my @box = $self->_text_bbox($label, 'legend')
1499 push(@sizes, \@box);
1500 $width = $box[2] if $box[2] > $width;
1501 if ($minrowsize > $box[3]) {
1502 $height += $minrowsize;
1509 $width + $patchsize + $padding * 2 + $gap,
1510 $height + $padding * 2 - $gap);
1511 my $outsidepadding = 0;
1512 if ($self->{_style}{legend}{border}) {
1513 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
1515 $box[2] += 2 * $outsidepadding;
1516 $box[3] += 2 * $outsidepadding;
1518 $self->_align_box(\@box, $chart_box, 'legend')
1520 if ($self->{_style}{legend}{fill}) {
1521 $img->box(xmin=>$box[0]+$outsidepadding,
1522 ymin=>$box[1]+$outsidepadding,
1523 xmax=>$box[2]-$outsidepadding,
1524 ymax=>$box[3]-$outsidepadding,
1525 $self->_get_fill('legend.fill', \@box));
1527 $box[0] += $outsidepadding;
1528 $box[1] += $outsidepadding;
1529 $box[2] -= $outsidepadding;
1530 $box[3] -= $outsidepadding;
1531 my $ypos = $box[1] + $padding;
1532 my $patchpos = $box[0]+$padding;
1533 my $textpos = $patchpos + $patchsize + $gap;
1534 my %text_info = $self->_text_style('legend')
1537 if ($self->{_style}{legend}{patchborder}) {
1538 $patchborder = $self->_get_color('legend.patchborder')
1542 for my $label (@$labels) {
1543 my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
1544 $patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
1545 my @fill = $self->_data_fill($dataindex, \@patchbox)
1547 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
1548 ymax=>$ypos + $patchsize, @fill);
1549 if ($self->{_style}{legend}{patchborder}) {
1550 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
1551 ymax=>$ypos + $patchsize,
1552 color=>$patchborder);
1554 $img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize,
1557 my $step = $patchsize + $gap;
1558 if ($minrowsize < $sizes[$dataindex][3]) {
1559 $ypos += $sizes[$dataindex][3];
1562 $ypos += $minrowsize;
1566 if ($self->{_style}{legend}{border}) {
1567 my $border_color = $self->_get_color('legend.border')
1569 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
1570 color=>$border_color);
1572 $self->_remove_box($chart_box, \@box);
1577 my ($self, $img, $chart_box) = @_;
1579 my $title = $self->{_style}{title}{text};
1580 my @box = $self->_text_bbox($title, 'title')
1584 $self->_align_box(\@box, $chart_box, 'title');
1585 my %text_info = $self->_text_style('title')
1587 $img->string(%text_info, x=>$box[0], 'y'=>$box[3] + $yoff, text=>$title);
1588 $self->_remove_box($chart_box, \@box);
1593 my ($self, $box) = @_;
1595 if ($box->[2] - $box->[0] > $box->[3] - $box->[1]) {
1596 return $box->[3] - $box->[1];
1599 return $box->[2] - $box->[0];
1605 Returns a list of style fields that are stored as composites, and
1606 should be merged instead of just being replaced.
1611 qw(title legend text label dropshadow outline callout);
1614 sub _filter_region {
1615 my ($self, $img, $left, $top, $right, $bottom, $filter) = @_;
1617 unless (ref $filter) {
1619 $filter = $self->_get_thing($name)
1622 or return $self->_error("no type for filter $name");
1625 $left > 0 or $left = 0;
1626 $top > 0 or $top = 0;
1628 # newer versions of Imager let you work on just part of an image
1629 if ($img->can('masked') && !$self->{_style}{features}{_debugblur}) {
1630 my $masked = $img->masked(left=>$left, top=>$top,
1631 right=>$right, bottom=>$bottom);
1632 $masked->filter(%$filter);
1635 # for older versions of Imager
1636 my $subset = $img->crop(left=>$left, top=>$top,
1637 right=>$right, bottom=>$bottom);
1638 $subset->filter(%$filter);
1639 $img->paste(left=>$left, top=>$top, img=>$subset);
1650 Imager::Graph::Pie(3), Imager(3), perl(1).
1654 Tony Cook <tony@develop-help.com>
1658 Imager::Graph is licensed under the same terms as perl itself.
1662 Addi for producing a cool imaging module. :)