documentation working commit
[imager-graph.git] / Graph.pm
CommitLineData
35574351 1package Imager::Graph;
54ada35d 2require 5.005;
35574351
TC
3
4=head1 NAME
5
6Imager::Graph - Perl extension for producing Graphs using the Imager library.
7
8=head1 SYNOPSIS
9
2eac77fc 10 use Imager::Graph::Sub_class;
11 my $chart = Imager::Graph::Sub_class->new;
81453d28 12 my $img = $chart->draw(data=> \@data, ...)
35574351 13 or die $chart->error;
1509eee7 14 $img->write(file => 'image.png');
35574351
TC
15
16=head1 DESCRIPTION
17
18Imager::Graph provides style information to its base classes. It
19defines the colors, text display information and fills based on both
20built-in styles and modifications supplied by the user to the draw()
21method.
22
35574351
TC
23=over
24
25=cut
26
27use strict;
28use vars qw($VERSION);
29use Imager qw(:handy);
bb0de914 30use Imager::Fountain;
35574351 31
aac2cfef 32$VERSION = '0.07';
35574351 33
35574351
TC
34# the maximum recursion depth in determining a color, fill or number
35use constant MAX_DEPTH => 10;
36
37my $NUM_RE = '(?:[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]\d+?)?)';
38
39=item new
40
41This is a simple constructor. No parameters required.
42
43=cut
44
45sub new {
46 bless {}, $_[0];
47}
48
2eac77fc 49=item set_graph_size($size)
dfd889da 50
51Sets the size of the graph (in pixels) within the image. The size of the image defaults to 1.5 * $graph_size.
52
53=cut
54
2eac77fc 55sub set_graph_size {
56 $_[0]->{'custom_style'}->{'size'} = $_[1];
dfd889da 57}
58
2eac77fc 59=item set_image_width($width)
dfd889da 60
61Sets the width of the image in pixels.
62
63=cut
64
2eac77fc 65sub set_image_width {
66 $_[0]->{'custom_style'}->{'width'} = $_[1];
dfd889da 67}
68
2eac77fc 69=item set_image_height($height)
dfd889da 70
71Sets the height of the image in pixels.
72
73=cut
74
2eac77fc 75sub set_image_height {
76 $_[0]->{'custom_style'}->{'height'} = $_[1];
dfd889da 77}
78
2eac77fc 79=item add_data_series([8, 6, 7, 5, 3, 0, 9], 'Series Name');
dfd889da 80
81Adds a data series to the graph. For L<Imager::Graph::Pie>, only one data series can be added.
82
83=cut
84
2eac77fc 85sub add_data_series {
dfd889da 86 my $self = shift;
87 my $data_ref = shift;
88 my $series_name = shift;
89
90 my $graph_data = $self->{'graph_data'} || [];
91
92 push @$graph_data, { data => $data_ref, series_name => $series_name };
1509eee7 93 if (defined $series_name) {
94 push @{$self->{'labels'}}, $series_name;
95 }
dfd889da 96
97 $self->{'graph_data'} = $graph_data;
98 return;
99}
100
2eac77fc 101sub _get_data_series {
a17e870a
TC
102 my ($self, $opts) = @_;
103
104 # return the data supplied to draw() if any.
105 if ($opts->{data}) {
106 # one or multiple series?
107 my $data = $opts->{data};
108 if (@$data && ref $data->[0] && ref $data->[0] =~ /ARRAY/) {
109 return $data;
110 }
111 else {
112 return [ { data => $data } ];
113 }
114 }
115
116 return $self->{'graph_data'};
dfd889da 117}
118
2eac77fc 119=item set_labels(['label1', 'label2' ... ])
dfd889da 120
121Labels the specific data points. For line/bar graphs, this is the x-axis. For pie graphs, it is the label for the wedges.
122
123=cut
124
2eac77fc 125sub set_labels {
dfd889da 126 $_[0]->{'labels'} = $_[1];
127}
128
2eac77fc 129sub _get_labels {
a17e870a
TC
130 my ($self, $opts) = @_;
131
132 $opts->{labels}
133 and return $opts->{labels};
134
dfd889da 135 return $_[0]->{'labels'}
136}
137
2eac77fc 138=item set_title($title)
dfd889da 139
140Sets the title of the graph. Requires setting a font.
141
142=cut
143
2eac77fc 144sub set_title {
145 $_[0]->{'custom_style'}->{'title'}->{'text'} = $_[1];
dfd889da 146}
147
2eac77fc 148=item set_font($font)
dfd889da 149
150Sets the font to use for text. Takes an L<Imager::Font> object.
151
152=cut
153
2eac77fc 154sub set_font {
155 $_[0]->{'custom_style'}->{'font'} = $_[1];
dfd889da 156}
157
2eac77fc 158=item set_style($style_name)
dfd889da 159
160Sets the style to be used for the graph. Imager::Graph comes with several pre-defined styles: fount_lin (default), fount_rad, mono, primary_red, and primary.
161
162=cut
163
2eac77fc 164sub set_style {
dfd889da 165 $_[0]->{'style'} = $_[1];
166}
167
2eac77fc 168sub _get_style {
a17e870a
TC
169 my ($self, $opts) = @_;
170
171 $opts->{style}
172 and return $opts->{style};
173
174 return $self->{'style'};
dfd889da 175}
176
35574351
TC
177=item error
178
a22672ea 179Returns an error message. Only valid if the draw() method returns false.
35574351
TC
180
181=cut
182
183sub error {
184 $_[0]->{_errstr};
185}
186
187=item draw
188
189Creates a new image, draws the chart onto that image and returns it.
190
2eac77fc 191Optionally, instead of using the api methods to configure your chart,
192you can supply a C<data> parameter in the format
35574351
TC
193required by that particular graph, and if your graph will use any
194text, a C<font> parameter
195
196You can also supply many different parameters which control the way
197the graph looks. These are supplied as keyword, value pairs, where
198the value can be a hashref containing sub values.
199
200The C<style> parameter will selects a basic color set, and possibly
201sets other related parameters. See L</"STYLES">.
202
aaaa03e1 203 my $font = Imager::Font->new(file => 'ImUgly.ttf');
81453d28 204 my $img = $chart->draw(
205 data => \@data,
206 font => $font,
207 title => {
208 text => "Hello, World!",
209 size => 36,
210 color => 'FF0000'
211 }
212 );
35574351
TC
213
214When referring to a single sub-value this documentation will refer to
215'title.color' rather than 'the color element of title'.
216
217Returns the graph image on success, or false on failure.
218
219=back
220
221=head1 STYLES
222
223The currently defined styles are:
224
225=over
226
7b94e723
TC
227=item primary
228
229a light grey background with no outlines. Uses primary colors for the
230data fills.
231
35574351
TC
232=item primary_red
233
234a light red background with no outlines. Uses primary colors for the
7b94e723 235data fills.
35574351
TC
236
237Graphs drawn using this style should save well as a gif, even though
238some graphs may perform a slight blur.
239
7b94e723 240This was the default style, but the red was too loud.
35574351
TC
241
242=item mono
243
244designed for monochrome output, such as most laser printers, this uses
245hatched fills for the data, and no colors. The returned image is a
246one channel image (which can be overridden with the C<channels>
247parameter.)
248
249You can also override the colors used by all components for background
250or drawing by supplying C<fg> and/or C<bg> parameters. ie. if you
251supply C<<fg=>'FF0000', channels=>3>> then the hash fills and anything
252else will be drawn in red. Another use might be to set a transparent
253background, by supplying C<<bg=>'00000000', channels=>4>>.
254
255This style outlines the legend if present and outlines the hashed fills.
256
35574351
TC
257=item fount_lin
258
259designed as a "pretty" style this uses linear fountain fills for the
260background and data fills, and adds a drop shadow.
261
262You can override the value used for text and outlines by setting the
263C<fg> parameter.
264
81453d28 265This is the default style.
266
35574351
TC
267=item fount_rad
268
269also designed as a "pretty" style this uses radial fountain fills for
270the data and a linear blue to green fill for the background.
271
272=back
273
2eac77fc 274=head1 Style API
275
276To set or override styles, you can use the following methods:
277
278=over 4
279
280=item set_image_background
281
282=cut
283
284sub set_image_background {
285 $_[0]->{'custom_style'}->{'back'} = $_[1];
286}
287
288=item set_channels
289
290=cut
291
292sub set_channels {
293 $_[0]->{'custom_style'}->{'channels'} = $_[1];
294}
295
296=item set_line_color
297
298=cut
299
300sub set_line_color {
301 $_[0]->{'custom_style'}->{'line'} = $_[1];
302}
303
304=item set_title_font_size
305
306=cut
307
308sub set_title_font_size {
309 $_[0]->{'custom_style'}->{'title'}->{'size'} = $_[1];
310}
311
312=item set_title_font_color
313
314=cut
315
316sub set_title_font_color {
317 $_[0]->{'custom_style'}->{'title'}->{'color'} = $_[1];
318}
319
320=item set_title_horizontal_align
321
322=cut
323
324sub set_title_horizontal_align {
325 $_[0]->{'custom_style'}->{'title'}->{'halign'} = $_[1];
326}
327
328=item set_title_vertical_align
329
330=cut
331
332sub set_title_vertical_align {
333 $_[0]->{'custom_style'}->{'title'}->{'valign'} = $_[1];
334}
335
336=item set_text_font_color
337
338=cut
339
340sub set_text_font_color {
341 $_[0]->{'custom_style'}->{'text'}->{'color'} = $_[1];
342}
343
344=item set_text_font_size
345
346=cut
347
348sub set_text_font_size {
349 $_[0]->{'custom_style'}->{'text'}->{'size'} = $_[1];
350}
351
352=item set_graph_background_color
353
354=cut
355
356sub set_graph_background_color {
357 $_[0]->{'custom_style'}->{'bg'} = $_[1];
358}
359
360=item set_graph_foreground_color
361
362=cut
363
364sub set_graph_foreground_color {
365 $_[0]->{'custom_style'}->{'fg'} = $_[1];
366}
367
368=item set_legend_font_color
369
370=cut
371
372sub set_legend_font_color {
373 $_[0]->{'custom_style'}->{'legend'}->{'color'} = $_[1];
374}
375
376=item set_legend_font
377
378=cut
379
380sub set_legend_font {
381 $_[0]->{'custom_style'}->{'legend'}->{'font'} = $_[1];
382}
383
384=item set_legend_font_size
385
386=cut
387
388sub set_legend_font_size {
389 $_[0]->{'custom_style'}->{'legend'}->{'size'} = $_[1];
390}
391
392=item set_legend_patch_size
393
394=cut
395
396sub set_legend_patch_size {
397 $_[0]->{'custom_style'}->{'legend'}->{'patchsize'} = $_[1];
398}
399
400=item set_legend_patch_gap
401
402=cut
403
404sub set_legend_patch_gap {
405 $_[0]->{'custom_style'}->{'legend'}->{'patchgap'} = $_[1];
406}
407
408=item set_legend_horizontal_align
409
410=cut
411
412sub set_legend_horizontal_align {
413 $_[0]->{'custom_style'}->{'legend'}->{'halign'} = $_[1];
414}
415
416=item set_legend_vertical_align
417
418=cut
419
420sub set_legend_vertical_align {
421 $_[0]->{'custom_style'}->{'legend'}->{'valign'} = $_[1];
422}
423
424=item set_legend_padding
425
426=cut
427
428sub set_legend_padding {
429 $_[0]->{'custom_style'}->{'legend'}->{'padding'} = $_[1];
430}
431
432=item set_legend_outside_padding
433
434=cut
435
436sub set_legend_outside_padding {
437 $_[0]->{'custom_style'}->{'legend'}->{'outsidepadding'} = $_[1];
438}
439
440=item set_legend_fill
441
442=cut
443
444sub set_legend_fill {
445 $_[0]->{'custom_style'}->{'legend'}->{'fill'} = $_[1];
446}
447
448=item set_legend_border
449
450=cut
451
452sub set_legend_border {
453 $_[0]->{'custom_style'}->{'legend'}->{'border'} = $_[1];
454}
455
456=item set_legend_orientation
457
458=cut
459
460sub set_legend_orientation {
461 $_[0]->{'custom_style'}->{'legend'}->{'orientation'} = $_[1];
462}
463
464=item set_callout_font_color
465
466=cut
467
468sub set_callout_font_color {
469 $_[0]->{'custom_style'}->{'callout'}->{'color'} = $_[1];
470}
471
472=item set_callout_font
473
474=cut
475
476sub set_callout_font {
477 $_[0]->{'custom_style'}->{'callout'}->{'font'} = $_[1];
478}
479
480=item set_callout_font_size
481
482=cut
483
484sub set_callout_font_size {
485 $_[0]->{'custom_style'}->{'callout'}->{'size'} = $_[1];
486}
487
488=item set_callout_line_color
489
490=cut
491
492sub set_callout_line_color {
493 $_[0]->{'custom_style'}->{'callout'}->{'line'} = $_[1];
494}
495
496=item set_callout_leader_inside_length
497
498=cut
499
500sub set_callout_leader_inside_length {
501 $_[0]->{'custom_style'}->{'callout'}->{'inside'} = $_[1];
502}
503
504=item set_callout_leader_outside_length
505
506=cut
507
508sub set_callout_leader_outside_length {
509 $_[0]->{'custom_style'}->{'callout'}->{'outside'} = $_[1];
510}
511
512=item set_callout_leader_length
513
514=cut
515
516sub set_callout_leader_length {
517 $_[0]->{'custom_style'}->{'callout'}->{'leadlen'} = $_[1];
518}
519
520=item set_callout_gap
521
522=cut
523
524sub set_callout_gap {
525 $_[0]->{'custom_style'}->{'callout'}->{'gap'} = $_[1];
526}
527
528=item set_label_font_color
529
530=cut
531
532sub set_label_font_color {
533 $_[0]->{'custom_style'}->{'label'}->{'color'} = $_[1];
534}
535
536=item set_label_font
537
538=cut
539
540sub set_label_font {
541 $_[0]->{'custom_style'}->{'label'}->{'font'} = $_[1];
542}
543
544=item set_label_font_size
545
546=cut
547
548sub set_label_font_size {
549 $_[0]->{'custom_style'}->{'label'}->{'size'} = $_[1];
550}
551
552=item set_drop_shadow_fill_color
553
554=cut
555
556sub set_drop_shadow_fill_color {
557 $_[0]->{'custom_style'}->{'dropshadow'}->{'fill'} = $_[1];
558}
559
560=item set_drop_shadow_offset
561
562=cut
563
564sub set_drop_shadow_offset {
565 $_[0]->{'custom_style'}->{'dropshadow'}->{'off'} = $_[1];
566}
567
568=item set_drop_shadowXOffset
569
570=cut
571
572sub set_drop_shadowXOffset {
573 $_[0]->{'custom_style'}->{'dropshadow'}->{'offx'} = $_[1];
574}
575
576=item set_drop_shadowYOffset
577
578=cut
579
580sub set_drop_shadowYOffset {
581 $_[0]->{'custom_style'}->{'dropshadow'}->{'offy'} = $_[1];
582}
583
584=item set_drop_shadow_filter
585
586=cut
587
588sub set_drop_shadow_filter {
589 $_[0]->{'custom_style'}->{'dropshadow'}->{'filter'} = $_[1];
590}
591
592=item set_outline_color
593
594=cut
595
596sub set_outline_color {
597 $_[0]->{'custom_style'}->{'outline'}->{'line'} = $_[1];
598}
599
600=item set_data_area_fills
601
602=cut
603
604sub set_data_area_fills {
605 $_[0]->{'custom_style'}->{'fills'} = $_[1];
606}
607
608=item set_data_line_colors
609
610=cut
611
612sub set_data_line_colors {
613 $_[0]->{'custom_style'}->{'colors'} = $_[1];
614}
615
616=back
617
35574351
TC
618=head1 FEATURES
619
620Each graph type has a number of features. These are used to add
5057a68f
TC
621various items that are displayed in the graph area.
622
623Features can be controlled by calling methods on the graph object, or
624by passing a C<features> parameter to draw().
625
626Some common features are:
2eac77fc 627
628=over
629
630=item show_legend()
631
5057a68f
TC
632Feature: legend
633X<legend><features, legend>
634
635adds a box containing boxes filled with the data fills, with
2eac77fc 636the labels provided to the draw method. The legend will only be
637displayed if both the legend feature is enabled and labels are
638supplied.
639
640=cut
641
642sub show_legend {
643 $_[0]->{'custom_style'}->{'features'}->{'legend'} = 1;
644}
645
646=item show_outline()
647
5057a68f
TC
648Feature: outline
649X<outline>X<features, outline>
650
651If enabled, draw a border around the elements representing data in the
652graph, eg. around each pie segments on a pie chart, around each bar on
653a bar chart.
2eac77fc 654
655=cut
656
657sub show_outline {
658 $_[0]->{'custom_style'}->{'features'}->{'outline'} = 1;
659}
660
661=item show_labels()
662
5057a68f
TC
663Feature: labels
664X<labels>X<features, labels>
665
2eac77fc 666labels each data fill, usually by including text inside the data fill.
667If the text does not fit in the fill, they could be displayed in some
5057a68f
TC
668other form, eg. as callouts in a pie graph.
669
670For pie charts there isn't much point in enabling both the C<legend>
671and C<labels> features.
672
673For other charts, the labels label the independent variable, while the
674legend describes the color used to plot the dependent variables.
2eac77fc 675
676=cut
677
678sub show_labels {
679 $_[0]->{'custom_style'}->{'features'}->{'labels'} = 1;
680}
681
682=item show_drop_shadow()
683
5057a68f
TC
684Feature: dropshadow
685X<dropshadow>X<features, dropshadow>
686
2eac77fc 687a simple drop shadow is shown behind some of the graph elements.
688
689=cut
690
691sub show_drop_shadow {
692 $_[0]->{'custom_style'}->{'features'}->{'dropshadow'} = 1;
693}
694
695=item reset_features()
696
5057a68f
TC
697Unsets all of the features.
698
699Note: this disables all features, even those enabled by default for a
700style. They can then be enabled by calling feature methods or by
701supplying a C<feature> parameter to the draw() method.
2eac77fc 702
703=cut
704
705sub reset_features {
706 $_[0]->{'custom_style'}->{'features'} = {};
707 $_[0]->{'custom_style'}->{'features'}->{'reset'} = 1;
708}
709
710=back
711
5057a68f
TC
712Additionally, features can be set by passing them into the draw()
713method, named as above:
35574351
TC
714
715=over
716
5057a68f 717=item *
35574351 718
5057a68f
TC
719if supplied as an array reference, then any element C<no>I<featurename> will
720disable that feature, while an element I<featurename> will enable it.
35574351 721
5057a68f 722=item *
35574351 723
5057a68f
TC
724if supplied as a scalar, it is treated as if it were a reference to
725an array containing only that scalar.
35574351 726
5057a68f 727=item *
35574351 728
5057a68f
TC
729if supplied as a hash reference, then a C<reset> key with a true value
730will avoid inheriting any default features, a key I<feature> with a
731false value will disable that feature and a key I<feature> with a true
732value will enable that feature.
35574351
TC
733
734=back
735
736Each graph also has features specific to that graph.
737
738=head1 COMMON PARAMETERS
739
740When referring to a single sub-value this documentation will refer to
741'title.color' rather than 'the color element of title'.
742
743Normally, except for the font parameter, these are controlled by
744styles, but these are the style parameters I'd mostly likely expect
745you want to use:
746
747=over
748
749=item font
750
751the Imager font object used to draw text on the chart.
752
753=item back
754
755the background fill for the graph. Default depends on the style.
756
757=item size
758
759the base size of the graph image. Default: 256
760
761=item width
762
763the width of the graph image. Default: 1.5 * size (384)
764
765=item height
766
767the height of the graph image. Default: size (256)
768
769=item channels
770
771the number of channels in the image. Default: 3 (the 'mono' style
772sets this to 1).
773
774=item line
775
776the color used for drawing lines, such as outlines or callouts.
777Default depends on the current style. Set to undef to remove the
778outline from a style.
779
780=item title
781
782the text used for a graph title. Default: no title. Note: this is
783the same as the title=>{ text => ... } field.
784
785=over
786
787=item halign
788
789horizontal alignment of the title in the graph, one of 'left',
790'center' or 'right'. Default: center
791
792=item valign
793
794vertical alignment of the title, one of 'top', 'center' or 'right'.
795Default: top. It's probably a bad idea to set this to 'center' unless
796you have a very short title.
797
798=back
799
800=item text
801
802This contains basic defaults used in drawing text.
803
804=over
805
806=item color
807
808the default color used for all text, defaults to the fg color.
809
810=item size
811
812the base size used for text, also used to scale many graph elements.
813Default: 14.
814
815=back
816
817=back
818
819=head1 BEYOND STYLES
820
821In most cases you will want to use just the styles, but you may want
822to exert more control over the way your chart looks. This section
823describes the options you can use to control the way your chart looks.
824
825Hopefully you don't need to read this.
826
827=over
828
829=item back
830
831The background of the graph.
832
833=item bg
834
835=item fg
836
837Used to define basic background and foreground colors for the graph.
838The bg color may be used for the background of the graph, and is used
5057a68f 839as a default for the background of hatched fills. The fg is used as
35574351
TC
840the default for line and text colors.
841
842=item font
843
844The default font used by the graph. Normally you should supply this
845if your graph as any text.
846
847=item line
848
849The default line color.
850
851=item text
852
853defaults for drawing text. Other textual graph elements will inherit
854or modify these values.
855
856=over
857
858=item color
859
860default text color, defaults to the I<fg> color.
861
862=item size
863
864default text size. Default: 14. This is used to scale many graph
865elements, including padding and leader sizes. Other text elements
866will either use or scale this value.
867
868=item font
869
870default font object. Inherited from I<font>, which should have been
871supplied by the caller.
872
873=back
874
875=item title
876
877If you supply a scalar value for this element, it will be stored in
878the I<text> field.
879
880Defines the text, font and layout information for the title.
881
882=over
883
884=item color
885
886The color of the title, inherited from I<text.color>.
887
888=item font
889
890The font object used for the title, inherited from I<text.font>.
891
892=item size
893
894size of the title text. Default: double I<text.size>
895
896=item halign
897
898=item valign
899
900The horizontal and vertical alignment of the title.
901
902=back
903
904=item legend
905
906defines attributes of the graph legend, if present.
907
908=over
909
910=item color
911
912=item font
913
914=item size
915
916text attributes for the labels used in the legend.
917
918=item patchsize
919
920the width and height of the color patch in the legend. Defaults to
92190% of the legend text size.
922
923=item patchgap
924
925the minimum gap between patches in pixels. Defaults to 30% of the
926patchsize.
927
928=item patchborder
929
930the color of the border drawn around each patch. Inherited from I<line>.
931
932=item halign
933
934=item valign
935
936the horizontal and vertical alignment of the legend within the graph.
937Defaults to 'right' and 'top'.
938
939=item padding
940
5057a68f 941the gap between the legend patches and text and the outside of its
35574351
TC
942box, or to the legend border, if any.
943
944=item outsidepadding
945
946the gap between the border and the outside of the legend's box. This
947is only used if the I<legend.border> attribute is defined.
948
949=item fill
950
951the background fill for the legend. Default: none
952
953=item border
954
955the border color of the legend. Default: none (no border is drawn
956around the legend.)
957
33a928b7
TC
958=item orientation
959
960The orientation of the legend. If this is C<vertical> the the patches
961and labels are stacked on top of each other. If this is C<horizontal>
962the patchs and labels are word wrapped across the image. Default:
963vertical.
964
35574351
TC
965=back
966
33a928b7
TC
967For example to create a horizontal legend with borderless patches,
968darker than the background, you might do:
969
970 my $im = $chart->draw
971 (...,
972 legend =>
973 {
974 patchborder => undef,
975 orientation => 'horizontal',
976 fill => { solid => Imager::Color->new(0, 0, 0, 32), }
977 },
978 ...);
979
35574351
TC
980=item callout
981
982defines attributes for graph callouts, if any are present. eg. if the
983pie graph cannot fit the label into the pie graph segement it will
984present it as a callout.
985
986=over
987
988=item color
989
990=item font
991
992=item size
993
994the text attributes of the callout label. Inherited from I<text>.
995
996=item line
997
998the color of the callout lines. Inherited from I<line>
999
1000=item inside
1001
1002=item outside
1003
1004the length of the leader on the inside and the outside of the fill,
1005usually at some angle. Both default to the size of the callout text.
1006
1007=item leadlen
1008
1009the length of the horizontal portion of the leader. Default:
1010I<callout.size>.
1011
1012=item gap
1013
1014the gap between the callout leader and the callout text. Defaults to
101530% of the text callout size.
1016
1017=back
1018
1019=item label
1020
1021defines attributes for labels drawn into the data areas of a graph.
1022
1023=over
1024
1025=item color
1026
1027=item font
1028
1029=item size
1030
1031The text attributes of the labels. Inherited from I<text>.
1032
1033=back
1034
1035=item dropshadow
1036
1037the attributes of the graph's drop shadow
1038
1039=over
1040
1041=item fill
1042
1043the fill used for the drop shadow. Default: '404040' (dark gray)
1044
1045=item off
1046
1047the offset of the drop shadow. A convenience value inherited by offx
1048and offy. Default: 40% of I<text.size>.
1049
1050=item offx
1051
1052=item offy
1053
1054the horizontal and vertical offsets of the drop shadow. Both
1055inherited from I<dropshadow.off>.
1056
1057=item filter
1058
1059the filter description passed to Imager's filter method to blur the
1060drop shadow. Default: an 11 element convolution filter.
1061
1062=back
1063
1064=item outline
1065
1066describes the lines drawn around filled data areas, such as the
1067segments of a pie chart.
1068
1069=over
1070
1071=item line
1072
1073the line color of the outlines, inherited from I<line>.
1074
1075=back
1076
1077=item fills
1078
1079a reference to an array containing fills for each data item.
1080
1081You can mix fill types, ie. using a simple color for the first item, a
1082hatched fill for the second and a fountain fill for the next.
1083
1084=back
1085
1086=head1 HOW VALUES WORK
1087
1088Internally rather than specifying literal color, fill, or font objects
1089or literal sizes for each element, Imager::Graph uses a number of
1090special values to inherit or modify values taken from other graph
1091element names.
1092
1093=head2 Specifying colors
1094
1095You can specify colors by either supplying an Imager::Color object, by
1096supplying lookup of another color, or by supplying a single value that
1097Imager::Color::new can use as an initializer. The most obvious is
1098just a 6 or 8 digit hex value representing the red, green, blue and
1099optionally alpha channels of the image.
1100
1101You can lookup another color by using the lookup() "function", for
1102example if you give a color as "lookup(fg)" then Imager::Graph will
1103look for the fg element in the current style (or as overridden by
1104you.) This is used internally by Imager::Graph to set up the
1105relationships between the colors of various elements, for example the
1106default style information contains:
1107
1108 text=>{
d7fd5863 1109 color=>'lookup(fg)',
35574351
TC
1110 ...
1111 },
1112 legend =>{
d7fd5863 1113 color=>'lookup(text.color)',
35574351
TC
1114 ...
1115 },
1116
1117So by setting the I<fg> color, you also set the default text color,
1118since each text element uses lookup(text.color) as its value.
1119
1120=head2 Specifying fills
1121
1122Fills can be used for the graph background color, the background color
1123for the legend block and for the fills used for each data element.
1124
1125You can specify a fill as a L<color value|Specifying colors> or as a
33a928b7 1126general fill, see L<Imager::Fill> for details.
35574351
TC
1127
1128You don't need (or usually want) to call Imager::Fill::new yourself,
1129since the various fill functions will call it for you, and
1130Imager::Graph provides some hooks to make them more useful.
1131
1132=over
1133
1134=item *
1135
1136with hatched fills, if you don't supply a 'fg' or 'bg' parameter,
1137Imager::Graph will supply the current graph fg and bg colors.
1138
1139=item *
1140
1141with fountain fill, you can supply the xa_ratio, ya_ratio, xb_ratio
1142and yb_ratio parameters, and they will be scaled in the fill area to
1143define the fountain fills xa, ya, xb and yb parameters.
1144
1145=back
1146
1147As with colors, you can use lookup(name) or lookup(name1.name2) to
1148have one element to inherit the fill of another.
1149
33a928b7
TC
1150Imager::Graph defaults the fill combine value to C<'normal'>. This
1151doesn't apply to simple color fills.
1152
35574351
TC
1153=head2 Specifying numbers
1154
1155You can specify various numbers, usually representing the size of
1156something, commonly text, but sometimes the length of a line or the
1157size of a gap.
1158
1159You can use the same lookup mechanism as with colors and fills, but
1160you can also scale values. For example, 'scale(0.5,text.size)' will
1161return half the size of the normal text size.
1162
1163As with colors, this is used internally to scale graph elements based
1164on the base text size. If you change the base text size then other
1165graph elements will scale as well.
1166
1167=head2 Specifying other elements
1168
1169Other elements, such as fonts, or parameters for a filter, can also
1170use the lookup(name) mechanism.
1171
1172=head1 INTERNAL METHODS
1173
1174Only useful if you need to fix bugs, add features or create a new
1175graph class.
1176
1177=over
1178
1179=cut
1180
1181my %style_defs =
1182 (
1183 back=> 'lookup(bg)',
1184 line=> 'lookup(fg)',
49a35584 1185 aa => 1,
35574351 1186 text=>{
d7fd5863 1187 color => 'lookup(fg)',
35574351 1188 font => 'lookup(font)',
d7fd5863 1189 size => 14,
49a35584 1190 aa => 'lookup(aa)',
d7fd5863 1191 },
35574351 1192 title=>{
d7fd5863 1193 color => 'lookup(text.color)',
35574351 1194 font => 'lookup(text.font)',
d7fd5863
TC
1195 halign => 'center',
1196 valign => 'top',
1197 size => 'scale(text.size,2.0)',
49a35584 1198 aa => 'lookup(text.aa)',
d7fd5863 1199 },
35574351 1200 legend =>{
d7fd5863 1201 color => 'lookup(text.color)',
35574351 1202 font => 'lookup(text.font)',
49a35584 1203 aa => 'lookup(text.aa)',
d7fd5863
TC
1204 size => 'lookup(text.size)',
1205 patchsize => 'scale(legend.size,0.9)',
1206 patchgap => 'scale(legend.patchsize,0.3)',
1207 patchborder => 'lookup(line)',
1208 halign => 'right',
1209 valign => 'top',
1210 padding => 'scale(legend.size,0.3)',
1211 outsidepadding => 'scale(legend.padding,0.4)',
1212 },
35574351 1213 callout => {
d7fd5863 1214 color => 'lookup(text.color)',
35574351 1215 font => 'lookup(text.font)',
d7fd5863
TC
1216 size => 'lookup(text.size)',
1217 line => 'lookup(line)',
1218 inside => 'lookup(callout.size)',
1219 outside => 'lookup(callout.size)',
1220 leadlen => 'scale(0.8,callout.size)',
1221 gap => 'scale(callout.size,0.3)',
49a35584
TC
1222 aa => 'lookup(text.aa)',
1223 lineaa => 'lookup(lineaa)',
d7fd5863 1224 },
35574351
TC
1225 label => {
1226 font => 'lookup(text.font)',
d7fd5863
TC
1227 size => 'lookup(text.size)',
1228 color => 'lookup(text.color)',
35574351
TC
1229 hpad => 'lookup(label.pad)',
1230 vpad => 'lookup(label.pad)',
1231 pad => 'scale(label.size,0.2)',
1232 pcformat => sub { sprintf "%s (%.0f%%)", $_[0], $_[1] },
1233 pconlyformat => sub { sprintf "%.1f%%", $_[0] },
49a35584
TC
1234 aa => 'lookup(text.aa)',
1235 lineaa => 'lookup(lineaa)',
d7fd5863 1236 },
35574351 1237 dropshadow => {
320f5a49 1238 fill => { solid => Imager::Color->new(0, 0, 0, 96) },
35574351
TC
1239 off => 'scale(0.4,text.size)',
1240 offx => 'lookup(dropshadow.off)',
1241 offy => 'lookup(dropshadow.off)',
1242 filter => { type=>'conv',
1243 # this needs a fairly heavy blur
1244 coef=>[0.1, 0.2, 0.4, 0.6, 0.7, 0.9, 1.2,
1245 0.9, 0.7, 0.6, 0.4, 0.2, 0.1 ] },
1246 },
1a36ef75
TC
1247 # controls the outline of graph elements representing data, eg. pie
1248 # slices, bars or columns
35574351
TC
1249 outline => {
1250 line =>'lookup(line)',
49a35584 1251 lineaa => 'lookup(lineaa)',
35574351 1252 },
1a36ef75
TC
1253 # controls the outline and background of the data area of the chart
1254 graph =>
1255 {
1256 fill => "lookup(bg)",
1257 outline => "lookup(fg)",
1258 },
35574351
TC
1259 size=>256,
1260 width=>'scale(1.5,size)',
1261 height=>'lookup(size)',
49a35584
TC
1262
1263 # yes, the handling of fill and line AA is inconsistent, lack of
1264 # forethought, unfortunately
1265 fill => {
1266 aa => 'lookup(aa)',
1267 },
1268 lineaa => 'lookup(aa)',
35574351
TC
1269 );
1270
1271=item _error($message)
1272
1273Sets the error field of the object and returns an empty list or undef,
1274depending on context. Should be used for error handling, since it may
1275provide some user hooks at some point.
1276
f68db40f
TC
1277The intended usage is:
1278
1279 some action
1280 or return $self->_error("error description");
1281
1282You should almost always return the result of _error() or return
1283immediately afterwards.
1284
35574351
TC
1285=cut
1286
1287sub _error {
1288 my ($self, $error) = @_;
1289
1290 $self->{_errstr} = $error;
1291
1292 return;
1293}
1294
1295
1296=item _style_defs()
1297
1298Returns the style defaults, such as the relationships between line
1299color and text color.
1300
1301Intended to be over-ridden by base classes to provide graph specific
1302defaults.
1303
1304=cut
1305
1306sub _style_defs {
1307 \%style_defs;
1308}
1309
81453d28 1310# Let's make the default something that looks really good, so folks will be interested enough to customize the style.
1311my $def_style = 'fount_lin';
35574351
TC
1312
1313my %styles =
1314 (
7b94e723
TC
1315 primary =>
1316 {
1317 fills=>
1318 [
1319 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1320 ],
1321 fg=>'000000',
2eac77fc 1322 negative_bg=>'EEEEEE',
7b94e723
TC
1323 bg=>'E0E0E0',
1324 legend=>
1325 {
1326 #patchborder=>'000000'
1327 },
1328 },
35574351
TC
1329 primary_red =>
1330 {
1331 fills=>
1332 [
1333 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1334 ],
1335 fg=>'000000',
2eac77fc 1336 negative_bg=>'EEEEEE',
35574351
TC
1337 bg=>'C08080',
1338 legend=>
1339 {
1340 patchborder=>'000000'
1341 },
1342 },
1343 mono =>
1344 {
1345 fills=>
1346 [
1347 { hatch=>'slash2' },
1348 { hatch=>'slosh2' },
1349 { hatch=>'vline2' },
1350 { hatch=>'hline2' },
1351 { hatch=>'cross2' },
1352 { hatch=>'grid2' },
1353 { hatch=>'stipple3' },
1354 { hatch=>'stipple2' },
1355 ],
1356 channels=>1,
1357 bg=>'FFFFFF',
1358 fg=>'000000',
2eac77fc 1359 negative_bg=>'EEEEEE',
35574351
TC
1360 features=>{ outline=>1 },
1361 pie =>{
1362 blur=>undef,
1363 },
49a35584 1364 aa => 0,
35574351 1365 },
bb0de914 1366 fount_lin =>
35574351
TC
1367 {
1368 fills=>
1369 [
1370 { fountain=>'linear',
1371 xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
35574351 1372 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1373 colors=>[ NC('FFC0C0'), NC('FF0000') ]),
35574351
TC
1374 },
1375 { fountain=>'linear',
1376 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1377 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1378 colors=>[ NC('C0FFC0'), NC('00FF00') ]),
35574351
TC
1379 },
1380 { fountain=>'linear',
1381 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1382 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1383 colors=>[ NC('C0C0FF'), NC('0000FF') ]),
35574351
TC
1384 },
1385 { fountain=>'linear',
1386 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1387 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1388 colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
35574351
TC
1389 },
1390 { fountain=>'linear',
1391 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1392 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1393 colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
35574351
TC
1394 },
1395 { fountain=>'linear',
1396 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1397 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1398 colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
35574351
TC
1399 },
1400 ],
dfd889da 1401 colors => [
1402 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1403 ],
8e140388
TC
1404 line_markers =>[
1405 { shape => 'circle', radius => 4 },
1406 { shape => 'square', radius => 4 },
1407 { shape => 'diamond', radius => 4 },
1408 { shape => 'triangle', radius => 4 },
1409 { shape => 'x', radius => 4 },
1410 { shape => 'plus', radius => 4 },
1411 ],
35574351
TC
1412 back=>{ fountain=>'linear',
1413 xa_ratio=>0, ya_ratio=>0,
1414 xb_ratio=>1.0, yb_ratio=>1.0,
1415 segments=>Imager::Fountain->simple
1416 ( positions=>[0, 1],
1417 colors=>[ NC('6060FF'), NC('60FF60') ]) },
1418 fg=>'000000',
2eac77fc 1419 negative_bg=>'EEEEEE',
35574351
TC
1420 bg=>'FFFFFF',
1421 features=>{ dropshadow=>1 },
bb0de914
TC
1422 },
1423 fount_rad =>
1424 {
35574351
TC
1425 fills=>
1426 [
1427 { fountain=>'radial',
1428 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1429 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1430 colors=>[ NC('FF8080'), NC('FF0000') ]),
35574351
TC
1431 },
1432 { fountain=>'radial',
1433 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1434 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1435 colors=>[ NC('80FF80'), NC('00FF00') ]),
35574351
TC
1436 },
1437 { fountain=>'radial',
1438 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1439 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1440 colors=>[ NC('808080FF'), NC('0000FF') ]),
35574351
TC
1441 },
1442 { fountain=>'radial',
1443 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1444 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1445 colors=>[ NC('FFFF80'), NC('FFFF00') ]),
35574351
TC
1446 },
1447 { fountain=>'radial',
1448 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1449 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1450 colors=>[ NC('80FFFF'), NC('00FFFF') ]),
35574351
TC
1451 },
1452 { fountain=>'radial',
1453 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1454 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1455 colors=>[ NC('FF80FF'), NC('FF00FF') ]),
35574351
TC
1456 },
1457 ],
dfd889da 1458 colors => [
1459 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1460 ],
35574351
TC
1461 back=>{ fountain=>'linear',
1462 xa_ratio=>0, ya_ratio=>0,
1463 xb_ratio=>1.0, yb_ratio=>1.0,
1464 segments=>Imager::Fountain->simple
1465 ( positions=>[0, 1],
1466 colors=>[ NC('6060FF'), NC('60FF60') ]) },
1467 fg=>'000000',
2eac77fc 1468 negative_bg=>'EEEEEE',
35574351 1469 bg=>'FFFFFF',
bb0de914
TC
1470 }
1471 );
35574351 1472
2eac77fc 1473$styles{'ocean'} = {
1474 fills => [
1475 {
1476 fountain =>'linear',
1477 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1478 segments => Imager::Fountain->simple(
1479 positions=>[0, 1],
7422650e 1480 colors=>[ NC('EFEDCF'), NC('E6E2AF') ]),
2eac77fc 1481 },
1482 {
1483 fountain =>'linear',
1484 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1485 segments => Imager::Fountain->simple(
1486 positions=>[0, 1],
7422650e 1487 colors=>[ NC('DCD7AB'), NC('A7A37E') ]),
2eac77fc 1488 },
1489 {
1490 fountain =>'linear',
1491 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1492 segments => Imager::Fountain->simple(
1493 positions=>[0, 1],
7422650e 1494 colors=>[ NC('B2E5D4'), NC('80B4A2') ]),
2eac77fc 1495 },
1496 {
1497 fountain =>'linear',
1498 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1499 segments => Imager::Fountain->simple(
1500 positions=>[0, 1],
7422650e 1501 colors=>[ NC('7aaab9'), NC('046380') ]),
2eac77fc 1502 },
1503 {
1504 fountain =>'linear',
1505 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1506 segments => Imager::Fountain->simple(
1507 positions=>[0, 1],
7422650e 1508 colors=>[ NC('c3b8e9'), NC('877EA7') ]),
2eac77fc 1509 },
1510 {
1511 fountain =>'linear',
1512 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1513 segments => Imager::Fountain->simple(
1514 positions=>[0, 1],
7422650e 1515 colors=>[ NC('A3DF9A'), NC('67A35E') ]),
2eac77fc 1516 },
1517 {
1518 fountain =>'linear',
1519 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1520 segments => Imager::Fountain->simple(
1521 positions=>[0, 1],
7422650e 1522 colors=>[ NC('E19C98'), NC('B4726F') ]),
2eac77fc 1523 },
1524 ],
1525 colors => [
1526 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1527 ],
1528 fg=>'000000',
1529 negative_bg=>'EEEEEE',
1530 bg=>'FFFFFF',
1531 features=>{ dropshadow=>1 },
1532
1533};
1534
7422650e 1535$styles{'ocean_flat'} = {
1536 fills=>
1537 [
1538 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1539 ],
1540 colors => [
1541 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1542 ],
1543 fg=>'000000',
1544 negative_bg=>'EEEEEE',
1545 bg=>'FFFFFF',
1546 features=>{ dropshadow=>1 },
1547
1548};
1549
1550
35574351
TC
1551=item $self->_style_setup(\%opts)
1552
5057a68f
TC
1553Uses the values from %opts, the custom style set by methods, the style
1554set by the style parameter or the set_style() method and the built in
1555chart defaults to build a working style.
1556
1557The working style features member is also populated with the active
1558features for the chart.
1559
1560The working style is stored in the C<_style> member of $self.
35574351
TC
1561
1562=cut
1563
1564sub _style_setup {
1565 my ($self, $opts) = @_;
1566 my $style_defs = $self->_style_defs;
1567 my $style;
dfd889da 1568
2eac77fc 1569 my $pre_def_style = $self->_get_style($opts);
1570 my $api_style = $self->{'custom_style'} || {};
dfd889da 1571 $style = $styles{$pre_def_style} if $pre_def_style;
1572
35574351
TC
1573 $style ||= $styles{$def_style};
1574
2eac77fc 1575 my @search_list = ( $style_defs, $style, $api_style, $opts);
35574351
TC
1576 my %work;
1577
1578 my @composite = $self->_composite();
1579 my %composite;
1580 @composite{@composite} = @composite;
1581
1582 for my $src (@search_list) {
1583 for my $key (keys %$src) {
1584 if ($composite{$key}) {
1585 $work{$key} = {} unless exists $work{$key};
1586 if (ref $src->{$key}) {
1587 # some keys have sub values, especially text
1588 @{$work{$key}}{keys %{$src->{$key}}} = values %{$src->{$key}};
1589 }
1590 else {
1591 # assume it's the text for a title or something
1592 $work{$key}{text} = $src->{$key};
1593 }
1594 }
1595 else {
a17e870a
TC
1596 $work{$key} = $src->{$key}
1597 if defined $src->{$key}; # $opts with pmichauds new accessor handling
35574351
TC
1598 }
1599 }
1600 }
1601
1602 # features are handled specially
1a36ef75
TC
1603 my %features;
1604 $work{features} = \%features;
35574351
TC
1605 for my $src (@search_list) {
1606 if ($src->{features}) {
1607 if (ref $src->{features}) {
1608 if (ref($src->{features}) =~ /ARRAY/) {
1609 # just set those features
1610 for my $feature (@{$src->{features}}) {
1a36ef75
TC
1611 if ($feature =~ /^no(.+)$/) {
1612 delete $features{$1};
1613 }
1614 else {
1615 $features{$feature} = 1;
1616 }
35574351
TC
1617 }
1618 }
1619 elsif (ref($src->{features}) =~ /HASH/) {
1620 if ($src->{features}{reset}) {
1621 $work{features} = {}; # only the ones the user specifies
1622 }
1623 @{$work{features}}{keys %{$src->{features}}} =
1624 values(%{$src->{features}});
1625 }
1626 }
1627 else {
1628 # just set that single feature
56b495c0
TC
1629 if ($src->{features} =~ /^no(.+)$/) {
1630 delete $features{$1};
1631 }
1632 else {
1633 $features{$src->{features}} = 1;
1634 }
35574351
TC
1635 }
1636 }
1637 }
35574351
TC
1638
1639 $self->{_style} = \%work;
1640}
1641
1642=item $self->_get_thing($name)
1643
1644Retrieve some general 'thing'.
1645
1646Supports the 'lookup(foo)' mechanism.
1647
f68db40f
TC
1648Returns an empty list on failure.
1649
35574351
TC
1650=cut
1651
1652sub _get_thing {
1653 my ($self, $name, @depth) = @_;
1654
1655 push(@depth, $name);
1656 my $what;
1657 if ($name =~ /^(\w+)\.(\w+)$/) {
1658 $what = $self->{_style}{$1}{$2};
1659 }
1660 else {
1661 $what = $self->{_style}{$name};
1662 }
1663 defined $what or
1664 return;
1665 if (ref $what) {
1666 return $what;
1667 }
1668 elsif ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1669 @depth < MAX_DEPTH
1670 or return $self->_error("too many levels of recursion in lookup(@depth)");
1671 return $self->_get_thing($1, @depth);
1672 }
1673 else {
1674 return $what;
1675 }
1676}
1677
1678=item $self->_get_number($name)
1679
1680Retrieves a number from the style. The value in the style can be the
1681number, or one of two functions:
1682
1683=over
1684
1685=item lookup(newname)
1686
1687Recursively looks up I<newname> in the style.
1688
1689=item scale(value1,value2)
1690
33a928b7 1691Each value can be a number or a name. Names are recursively looked up
35574351
TC
1692in the style and the product is returned.
1693
1694=back
1695
1696=cut
bb0de914 1697
35574351
TC
1698sub _get_number {
1699 my ($self, $name, @depth) = @_;
1700
1701 push(@depth, $name);
1702 my $what;
1703 if ($name =~ /^(\w+)\.(\w+)$/) {
1704 $what = $self->{_style}{$1}{$2};
1705 }
1706 else {
1707 $what = $self->{_style}{$name};
1708 }
1709 defined $what or
1710 return $self->_error("$name is undef (@depth)");
1711
1712 if (ref $what) {
1713 if ($what =~ /CODE/) {
1714 $what = $what->($self, $name);
1715 }
1716 }
1717 else {
1718 if ($what =~ /^lookup\(([\w.]+)\)$/) {
1719 @depth < MAX_DEPTH
d7fd5863 1720 or return $self->_error("too many levels of recursion in lookup (@depth)");
35574351
TC
1721 return $self->_get_number($1, @depth);
1722 }
1723 elsif ($what =~ /^scale\(
d7fd5863 1724 ((?:[a-z][\w.]*)|$NUM_RE)
35574351 1725 ,
d7fd5863 1726 ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
35574351
TC
1727 my ($left, $right) = ($1, $2);
1728 unless ($left =~ /^$NUM_RE$/) {
d7fd5863
TC
1729 @depth < MAX_DEPTH
1730 or return $self->_error("too many levels of recursion in scale (@depth)");
1731 $left = $self->_get_number($left, @depth);
35574351
TC
1732 }
1733 unless ($right =~ /^$NUM_RE$/) {
d7fd5863
TC
1734 @depth < MAX_DEPTH
1735 or return $self->_error("too many levels of recursion in scale (@depth)");
1736 $right = $self->_get_number($right, @depth);
35574351
TC
1737 }
1738 return $left * $right;
1739 }
1740 else {
1741 return $what+0;
1742 }
1743 }
1744}
1745
379c5b02
TC
1746=item $self->_get_integer($name)
1747
1748Retrieves an integer from the style. This is a simple wrapper around
1749_get_number() that rounds the result to an integer.
1750
f68db40f
TC
1751Returns an empty list on failure.
1752
379c5b02
TC
1753=cut
1754
1755sub _get_integer {
1756 my ($self, $name, @depth) = @_;
1757
1758 my $number = $self->_get_number($name, @depth)
1759 or return;
1760
1761 return sprintf("%.0f", $number);
1762}
1763
35574351
TC
1764=item _get_color($name)
1765
1766Returns a color object of the given name from the style hash.
1767
1768Uses Imager::Color->new to translate normal scalars into color objects.
1769
1770Allows the lookup(name) mechanism.
1771
f68db40f
TC
1772Returns an empty list on failure.
1773
35574351
TC
1774=cut
1775
1776sub _get_color {
1777 my ($self, $name, @depth) = @_;
1778
1779 push(@depth, $name);
1780 my $what;
1781 if ($name =~ /^(\w+)\.(\w+)$/) {
1782 $what = $self->{_style}{$1}{$2};
1783 }
1784 else {
1785 $what = $self->{_style}{$name};
1786 }
1787
1788 defined($what)
1789 or return $self->_error("$name was undefined (@depth)");
1790
1791 unless (ref $what) {
1792 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1793 @depth < MAX_DEPTH or
d7fd5863 1794 return $self->_error("too many levels of recursion in lookup (@depth)");
35574351
TC
1795
1796 return $self->_get_color($1, @depth);
1797 }
1798 $what = Imager::Color->new($what);
1799 }
1800
1801 $what;
1802}
1803
1804=item _translate_fill($what, $box)
1805
1806Given the value of a fill, either attempts to convert it into a fill
1807list (one of C<<color=>$color_value, filled=>1>> or C<<fill=>{ fill
1808parameters }>>), or to lookup another fill that is referred to with
1809the 'lookup(name)' mechanism.
1810
1811This function does the fg and bg initialization for hatched fills, and
1812translation of *_ratio for fountain fills (using the $box parameter).
1813
f68db40f
TC
1814Returns an empty list on failure.
1815
35574351
TC
1816=cut
1817
1818sub _translate_fill {
1819 my ($self, $what, $box, @depth) = @_;
1820
1821 if (ref $what) {
1822 if (UNIVERSAL::isa($what, "Imager::Color")) {
1823 return ( color=>Imager::Color->new($what), filled=>1 );
1824 }
1825 else {
1826 # a general fill
33a928b7
TC
1827 # default to normal combine mode
1828 my %work = ( combine => 'normal', %$what );
35574351 1829 if ($what->{hatch}) {
d7fd5863
TC
1830 if (!$work{fg}) {
1831 $work{fg} = $self->_get_color('fg')
1832 or return;
1833 }
1834 if (!$work{bg}) {
1835 $work{bg} = $self->_get_color('bg')
1836 or return;
1837 }
1838 return ( fill=>\%work );
35574351
TC
1839 }
1840 elsif ($what->{fountain}) {
d7fd5863
TC
1841 for my $key (qw(xa ya xb yb)) {
1842 if (exists $work{"${key}_ratio"}) {
1843 if ($key =~ /^x/) {
1844 $work{$key} = $box->[0] + $work{"${key}_ratio"}
1845 * ($box->[2] - $box->[0]);
1846 }
1847 else {
1848 $work{$key} = $box->[1] + $work{"${key}_ratio"}
1849 * ($box->[3] - $box->[1]);
1850 }
1851 }
1852 }
1853 return ( fill=>\%work );
35574351
TC
1854 }
1855 else {
d7fd5863 1856 return ( fill=> \%work );
35574351
TC
1857 }
1858 }
1859 }
1860 else {
1861 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1862 return $self->_get_fill($1, $box, @depth);
1863 }
1864 else {
1865 # assumed to be an Imager::Color single value
1866 return ( color=>Imager::Color->new($what), filled=>1 );
1867 }
1868 }
1869}
1870
1871=item _data_fill($index, $box)
1872
1873Retrieves the fill parameters for a data area fill.
1874
1875=cut
1876
1877sub _data_fill {
1878 my ($self, $index, $box) = @_;
1879
1880 my $fills = $self->{_style}{fills};
1881 return $self->_translate_fill($fills->[$index % @$fills], $box,
1882 "data.$index");
1883}
1884
dfd889da 1885sub _data_color {
1886 my ($self, $index) = @_;
1887
1888 my $colors = $self->{'_style'}{'colors'} || [];
1889 my $fills = $self->{'_style'}{'fills'} || [];
1890
1891 # Try to just use a fill, so non-fountain styles don't need
1892 # to have a duplicated set of fills and colors
1893 my $fill = $fills->[$index % @$fills];
1894 if (!ref $fill) {
1895 return $fill;
1896 }
1897
1898 if (@$colors) {
1899 return $colors->[$index % @$colors] || '000000';
1900 }
1901 return '000000';
1902}
1903
56b495c0 1904=item _get_fill($name, $box)
35574351
TC
1905
1906Retrieves fill parameters for a named fill.
1907
1908=cut
1909
1910sub _get_fill {
1911 my ($self, $name, $box, @depth) = @_;
1912
1913 push(@depth, $name);
1914 my $what;
1915 if ($name =~ /^(\w+)\.(\w+)$/) {
1916 $what = $self->{_style}{$1}{$2};
1917 }
1918 else {
1919 $what = $self->{_style}{$name};
1920 }
1921
1922 defined($what)
1923 or return $self->_error("no fill $name found");
1924
1925 return $self->_translate_fill($what, $box, @depth);
1926}
1927
56b495c0
TC
1928=item _get_line($name)
1929
1930Return color (and possibly other) parameters for drawing a line with
1931the _line() method.
1932
1933=cut
1934
1935sub _get_line {
1936 my ($self, $name, @depth) = @_;
1937
1938 push (@depth, $name);
1939 my $what;
1940 if ($name =~ /^(\w+)\.(\w+)$/) {
1941 $what = $self->{_style}{$1}{$2};
1942 }
1943 else {
1944 $what = $self->{_style}{$name};
1945 }
1946
1947 defined($what)
1948 or return $self->_error("no line style $name found");
1949
1950 if (ref $what) {
1951 if (eval { $what->isa("Imager::Color") }) {
1952 return $what;
1953 }
1954 if (ref $what eq "HASH") {
1955 # allow each kep to be looked up
1956 my %work = %$what;
1957
1958 if ($work{color} =~ /^lookup\((.*)\)$/) {
1959 $work{color} = $self->_get_color($1, @depth);
1960 }
1961 for my $key (keys %work) {
1962 $key eq "color" and next;
1963
1964 if ($work{$key} =~ /^lookup\((.*)\)$/) {
1965 $work{$key} = $self->_get_thing($1);
1966 }
1967 }
1968
1969 return %work;
1970 }
1971 return ( color => Imager::Color->new(@$what) );
1972 }
1973 else {
1974 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1975 @depth < MAX_DEPTH
1976 or return $self->_error("too many levels of recursion in lookup (@depth)");
1977 return $self->_get_line($1, @depth);
1978 }
1979 else {
1980 # presumably a text color
1981 my $color = Imager::Color->new($what)
1982 or return $self->_error("Could not translate $what as a color: ".Imager->errstr);
1983
1984 return ( color => $color );
1985 }
1986 }
1987}
1988
35574351
TC
1989=item _make_img()
1990
1991Builds the image object for the graph and fills it with the background
1992fill.
1993
1994=cut
1995
1996sub _make_img {
1997 my ($self) = @_;
35574351 1998
81453d28 1999 my $width = $self->_get_number('width') || 256;
2000 my $height = $self->_get_number('height') || 256;
35574351
TC
2001 my $channels = $self->{_style}{channels};
2002
2003 $channels ||= 3;
2004
56b495c0
TC
2005 my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels)
2006 or return $self->_error("Error creating image: " . Imager->errstr);
35574351
TC
2007
2008 $img->box($self->_get_fill('back', [ 0, 0, $width-1, $height-1]));
2009
56b495c0
TC
2010 $self->{_image} = $img;
2011
2012 return $img;
35574351
TC
2013}
2014
2eac77fc 2015sub _get_image {
2016 my $self = shift;
2017
2eac77fc 2018 return $self->{'_image'};
2019}
2020
f68db40f
TC
2021=item _text_style($name)
2022
2023Returns parameters suitable for calls to Imager::Font's bounding_box()
2024and draw() methods intended for use in defining text styles.
2025
2026Returns an empty list on failure.
2027
49a35584
TC
2028Returns the following attributes: font, color, size, aa, sizew
2029(optionally)
2030
f68db40f
TC
2031=cut
2032
35574351
TC
2033sub _text_style {
2034 my ($self, $name) = @_;
2035
2036 my %work;
2037
2038 if ($self->{_style}{$name}) {
2039 %work = %{$self->{_style}{$name}};
2040 }
2041 else {
2042 %work = %{$self->{_style}{text}};
2043 }
2044 $work{font}
5d622bb8 2045 or return $self->_error("$name has no font parameter");
35574351
TC
2046
2047 $work{font} = $self->_get_thing("$name.font")
5d622bb8 2048 or return $self->_error("No $name.font defined, either set $name.font or font to a font");
35574351
TC
2049 UNIVERSAL::isa($work{font}, "Imager::Font")
2050 or return $self->_error("$name.font is not a font");
2051 if ($work{color} && !ref $work{color}) {
2052 $work{color} = $self->_get_color("$name.color")
2053 or return;
2054 }
2055 $work{size} = $self->_get_number("$name.size");
2056 $work{sizew} = $self->_get_number("$name.sizew")
2057 if $work{sizew};
49a35584 2058 $work{aa} = $self->_get_number("$name.aa");
35574351
TC
2059
2060 %work;
2061}
2062
f68db40f
TC
2063=item _text_bbox($text, $name)
2064
2065Returns a bounding box for the specified $text as styled by $name.
2066
2067Returns an empty list on failure.
2068
2069=cut
2070
35574351
TC
2071sub _text_bbox {
2072 my ($self, $text, $name) = @_;
2073
5d622bb8
TC
2074 my %text_info = $self->_text_style($name)
2075 or return;
35574351
TC
2076
2077 my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
d7fd5863 2078 canon=>1);
35574351
TC
2079
2080 return @bbox[0..3];
2081}
2082
49a35584
TC
2083=item _line_style($name)
2084
2085Return parameters suitable for calls to Imager's line(), polyline(),
2086and box() methods.
2087
2088For now this returns only color and aa parameters, but future releases
2089of Imager may support extra parameters.
2090
2091=cut
2092
2093sub _line_style {
2094 my ($self, $name) = @_;
2095
2096 my %line;
2097 $line{color} = $self->_get_color("$name.line")
2098 or return;
2099 $line{aa} = $self->_get_number("$name.lineaa");
2100 defined $line{aa} or $line{aa} = $self->_get_number("aa");
2101
2102 return %line;
2103}
2104
35574351
TC
2105sub _align_box {
2106 my ($self, $box, $chart_box, $name) = @_;
2107
2108 my $halign = $self->{_style}{$name}{halign}
2109 or $self->_error("no halign for $name");
2110 my $valign = $self->{_style}{$name}{valign};
2111
2112 if ($halign eq 'right') {
2113 $box->[0] += $chart_box->[2] - $box->[2];
2114 }
2115 elsif ($halign eq 'left') {
2116 $box->[0] = $chart_box->[0];
2117 }
2118 elsif ($halign eq 'center' || $halign eq 'centre') {
2119 $box->[0] = ($chart_box->[0] + $chart_box->[2] - $box->[2])/2;
2120 }
2121 else {
2122 return $self->_error("invalid halign $halign for $name");
2123 }
2124
2125 if ($valign eq 'top') {
2126 $box->[1] = $chart_box->[1];
2127 }
2128 elsif ($valign eq 'bottom') {
2129 $box->[1] = $chart_box->[3] - $box->[3];
2130 }
2131 elsif ($valign eq 'center' || $valign eq 'centre') {
2132 $box->[1] = ($chart_box->[1] + $chart_box->[3] - $box->[3])/2;
2133 }
2134 else {
2135 return $self->_error("invalid valign $valign for $name");
2136 }
2137 $box->[2] += $box->[0];
2138 $box->[3] += $box->[1];
2139}
2140
2141sub _remove_box {
2142 my ($self, $chart_box, $object_box) = @_;
2143
2144 my $areax;
2145 my $areay;
2146 if ($object_box->[0] - $chart_box->[0]
2147 < $chart_box->[2] - $object_box->[2]) {
2148 $areax = ($object_box->[2] - $chart_box->[0])
2149 * ($chart_box->[3] - $chart_box->[1]);
2150 }
2151 else {
2152 $areax = ($chart_box->[2] - $object_box->[0])
2153 * ($chart_box->[3] - $chart_box->[1]);
2154 }
2155
2156 if ($object_box->[1] - $chart_box->[1]
2157 < $chart_box->[3] - $object_box->[3]) {
2158 $areay = ($object_box->[3] - $chart_box->[1])
2159 * ($chart_box->[2] - $chart_box->[0]);
2160 }
2161 else {
2162 $areay = ($chart_box->[3] - $object_box->[1])
2163 * ($chart_box->[2] - $chart_box->[0]);
2164 }
2165
2166 if ($areay < $areax) {
2167 if ($object_box->[1] - $chart_box->[1]
d7fd5863 2168 < $chart_box->[3] - $object_box->[3]) {
35574351
TC
2169 $chart_box->[1] = $object_box->[3];
2170 }
2171 else {
2172 $chart_box->[3] = $object_box->[1];
2173 }
2174 }
2175 else {
2176 if ($object_box->[0] - $chart_box->[0]
d7fd5863 2177 < $chart_box->[2] - $object_box->[2]) {
35574351
TC
2178 $chart_box->[0] = $object_box->[2];
2179 }
2180 else {
2181 $chart_box->[2] = $object_box->[0];
2182 }
2183 }
2184}
2185
2186sub _draw_legend {
2187 my ($self, $img, $labels, $chart_box) = @_;
2188
33a928b7
TC
2189 my $orient = $self->_get_thing('legend.orientation');
2190 defined $orient or $orient = 'vertical';
2191
2192 if ($orient eq 'vertical') {
2193 return $self->_draw_legend_vertical($img, $labels, $chart_box);
2194 }
2195 elsif ($orient eq 'horizontal') {
2196 return $self->_draw_legend_horizontal($img, $labels, $chart_box);
2197 }
2198 else {
2199 return $self->_error("Unknown legend.orientation $orient");
2200 }
2201}
2202
2203sub _draw_legend_horizontal {
2204 my ($self, $img, $labels, $chart_box) = @_;
2205
2206 defined(my $padding = $self->_get_integer('legend.padding'))
2207 or return;
2208 my $patchsize = $self->_get_integer('legend.patchsize')
2209 or return;
2210 defined(my $gap = $self->_get_integer('legend.patchgap'))
2211 or return;
2212
2213 my $minrowsize = $patchsize + $gap;
2214 my ($width, $height) = (0,0);
2215 my $row_height = $minrowsize;
2216 my $pos = 0;
2217 my @sizes;
2218 my @offsets;
2219 for my $label (@$labels) {
5d622bb8
TC
2220 my @text_box = $self->_text_bbox($label, 'legend')
2221 or return;
33a928b7
TC
2222 push(@sizes, \@text_box);
2223 my $entry_width = $patchsize + $gap + $text_box[2];
2224 if ($pos == 0) {
2225 # never re-wrap the first entry
2226 push @offsets, [ 0, $height ];
2227 }
2228 else {
2229 if ($pos + $gap + $entry_width > $chart_box->[2]) {
d7fd5863
TC
2230 $pos = 0;
2231 $height += $row_height;
33a928b7
TC
2232 }
2233 push @offsets, [ $pos, $height ];
2234 }
2235 my $entry_right = $pos + $entry_width;
2236 $pos += $gap + $entry_width;
2237 $entry_right > $width and $width = $entry_right;
2238 if ($text_box[3] > $row_height) {
2239 $row_height = $text_box[3];
2240 }
2241 }
2242 $height += $row_height;
2243 my @box = ( 0, 0, $width + $padding * 2, $height + $padding * 2 );
2244 my $outsidepadding = 0;
2245 if ($self->{_style}{legend}{border}) {
2246 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
2247 or return;
2248 $box[2] += 2 * $outsidepadding;
2249 $box[3] += 2 * $outsidepadding;
2250 }
2251 $self->_align_box(\@box, $chart_box, 'legend')
2252 or return;
2253 if ($self->{_style}{legend}{fill}) {
2254 $img->box(xmin=>$box[0]+$outsidepadding,
2255 ymin=>$box[1]+$outsidepadding,
2256 xmax=>$box[2]-$outsidepadding,
2257 ymax=>$box[3]-$outsidepadding,
d7fd5863 2258 $self->_get_fill('legend.fill', \@box));
33a928b7
TC
2259 }
2260 $box[0] += $outsidepadding;
2261 $box[1] += $outsidepadding;
2262 $box[2] -= $outsidepadding;
2263 $box[3] -= $outsidepadding;
2264 my %text_info = $self->_text_style('legend')
2265 or return;
2266 my $patchborder;
2267 if ($self->{_style}{legend}{patchborder}) {
2268 $patchborder = $self->_get_color('legend.patchborder')
2269 or return;
2270 }
2271
2272 my $dataindex = 0;
2273 for my $label (@$labels) {
2274 my ($left, $top) = @{$offsets[$dataindex]};
2275 $left += $box[0] + $padding;
2276 $top += $box[1] + $padding;
2277 my $textpos = $left + $patchsize + $gap;
2278 my @patchbox = ( $left, $top,
2279 $left + $patchsize, $top + $patchsize );
2280 my @fill = $self->_data_fill($dataindex, \@patchbox)
2281 or return;
2282 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
d7fd5863 2283 ymax=>$top + $patchsize, @fill);
33a928b7
TC
2284 if ($self->{_style}{legend}{patchborder}) {
2285 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
d7fd5863
TC
2286 ymax=>$top + $patchsize,
2287 color=>$patchborder);
33a928b7
TC
2288 }
2289 $img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize,
2290 text=>$label);
2291
2292 ++$dataindex;
2293 }
2294 if ($self->{_style}{legend}{border}) {
2295 my $border_color = $self->_get_color('legend.border')
2296 or return;
2297 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
d7fd5863 2298 color=>$border_color);
33a928b7
TC
2299 }
2300 $self->_remove_box($chart_box, \@box);
2301 1;
2302}
2303
2304sub _draw_legend_vertical {
2305 my ($self, $img, $labels, $chart_box) = @_;
2306
379c5b02 2307 defined(my $padding = $self->_get_integer('legend.padding'))
35574351 2308 or return;
379c5b02 2309 my $patchsize = $self->_get_integer('legend.patchsize')
35574351 2310 or return;
379c5b02 2311 defined(my $gap = $self->_get_integer('legend.patchgap'))
35574351
TC
2312 or return;
2313 my $minrowsize = $patchsize + $gap;
2314 my ($width, $height) = (0,0);
2315 my @sizes;
2316 for my $label (@$labels) {
5d622bb8
TC
2317 my @box = $self->_text_bbox($label, 'legend')
2318 or return;
35574351
TC
2319 push(@sizes, \@box);
2320 $width = $box[2] if $box[2] > $width;
2321 if ($minrowsize > $box[3]) {
2322 $height += $minrowsize;
2323 }
2324 else {
2325 $height += $box[3];
2326 }
2327 }
2328 my @box = (0, 0,
d7fd5863
TC
2329 $width + $patchsize + $padding * 2 + $gap,
2330 $height + $padding * 2 - $gap);
35574351
TC
2331 my $outsidepadding = 0;
2332 if ($self->{_style}{legend}{border}) {
33a928b7 2333 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
35574351
TC
2334 or return;
2335 $box[2] += 2 * $outsidepadding;
2336 $box[3] += 2 * $outsidepadding;
2337 }
2338 $self->_align_box(\@box, $chart_box, 'legend')
2339 or return;
2340 if ($self->{_style}{legend}{fill}) {
2341 $img->box(xmin=>$box[0]+$outsidepadding,
2342 ymin=>$box[1]+$outsidepadding,
2343 xmax=>$box[2]-$outsidepadding,
2344 ymax=>$box[3]-$outsidepadding,
d7fd5863 2345 $self->_get_fill('legend.fill', \@box));
35574351
TC
2346 }
2347 $box[0] += $outsidepadding;
2348 $box[1] += $outsidepadding;
2349 $box[2] -= $outsidepadding;
2350 $box[3] -= $outsidepadding;
2351 my $ypos = $box[1] + $padding;
2352 my $patchpos = $box[0]+$padding;
2353 my $textpos = $patchpos + $patchsize + $gap;
2354 my %text_info = $self->_text_style('legend')
2355 or return;
2356 my $patchborder;
2357 if ($self->{_style}{legend}{patchborder}) {
2358 $patchborder = $self->_get_color('legend.patchborder')
2359 or return;
2360 }
2361 my $dataindex = 0;
2362 for my $label (@$labels) {
2363 my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
2364 $patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
2eac77fc 2365
2366 my @fill;
2367 if ($self->_draw_flat_legend()) {
2368 @fill = (color => $self->_data_color($dataindex), filled => 1);
2369 }
2370 else {
2371 @fill = $self->_data_fill($dataindex, \@patchbox)
2372 or return;
2373 }
35574351 2374 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
d7fd5863 2375 ymax=>$ypos + $patchsize, @fill);
35574351
TC
2376 if ($self->{_style}{legend}{patchborder}) {
2377 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
d7fd5863
TC
2378 ymax=>$ypos + $patchsize,
2379 color=>$patchborder);
35574351
TC
2380 }
2381 $img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize,
2382 text=>$label);
2383
2384 my $step = $patchsize + $gap;
2385 if ($minrowsize < $sizes[$dataindex][3]) {
2386 $ypos += $sizes[$dataindex][3];
2387 }
2388 else {
2389 $ypos += $minrowsize;
2390 }
2391 ++$dataindex;
2392 }
2393 if ($self->{_style}{legend}{border}) {
2394 my $border_color = $self->_get_color('legend.border')
2395 or return;
2396 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
d7fd5863 2397 color=>$border_color);
35574351
TC
2398 }
2399 $self->_remove_box($chart_box, \@box);
2400 1;
2401}
2402
2403sub _draw_title {
2404 my ($self, $img, $chart_box) = @_;
2405
2406 my $title = $self->{_style}{title}{text};
5d622bb8
TC
2407 my @box = $self->_text_bbox($title, 'title')
2408 or return;
35574351
TC
2409 my $yoff = $box[1];
2410 @box[0,1] = (0,0);
2411 $self->_align_box(\@box, $chart_box, 'title');
5d622bb8
TC
2412 my %text_info = $self->_text_style('title')
2413 or return;
35574351
TC
2414 $img->string(%text_info, x=>$box[0], 'y'=>$box[3] + $yoff, text=>$title);
2415 $self->_remove_box($chart_box, \@box);
2416 1;
2417}
2418
2419sub _small_extent {
2420 my ($self, $box) = @_;
2421
2422 if ($box->[2] - $box->[0] > $box->[3] - $box->[1]) {
2423 return $box->[3] - $box->[1];
2424 }
2425 else {
2426 return $box->[2] - $box->[0];
2427 }
2428}
2429
2eac77fc 2430sub _draw_flat_legend {
2431 return 0;
2432}
2433
35574351
TC
2434=item _composite()
2435
2436Returns a list of style fields that are stored as composites, and
2437should be merged instead of just being replaced.
2438
2439=cut
2440
2441sub _composite {
56b495c0 2442 qw(title legend text label dropshadow outline callout graph);
35574351
TC
2443}
2444
2445sub _filter_region {
2446 my ($self, $img, $left, $top, $right, $bottom, $filter) = @_;
2447
2448 unless (ref $filter) {
2449 my $name = $filter;
2450 $filter = $self->_get_thing($name)
2451 or return;
2452 $filter->{type}
2453 or return $self->_error("no type for filter $name");
2454 }
2455
2456 $left > 0 or $left = 0;
2457 $top > 0 or $top = 0;
2458
ba203e3a
TC
2459 my $masked = $img->masked(left=>$left, top=>$top,
2460 right=>$right, bottom=>$bottom);
2461 $masked->filter(%$filter);
35574351
TC
2462}
2463
56b495c0
TC
2464=item _line(x1 => $x1, y1 => $y1, ..., style => $style)
2465
2466Wrapper for line drawing, implements styles Imager doesn't.
2467
2468Currently styles are limited to horizontal and vertical lines.
2469
2470=cut
2471
2472sub _line {
2473 my ($self, %opts) = @_;
2474
2475 my $img = delete $opts{img}
2476 or die "No img supplied to _line()";
2477 my $style = delete $opts{style} || "solid";
2478
2479 if ($style eq "solid" || ($opts{x1} != $opts{x2} && $opts{y1} != $opts{y2})) {
2480 return $img->line(%opts);
2481 }
2482 elsif ($style eq 'dashed' || $style eq 'dotted') {
2483 my ($x1, $y1, $x2, $y2) = delete @opts{qw/x1 y1 x2 y2/};
2484 # the line is vertical or horizontal, so swapping doesn't hurt
2485 $x1 > $x2 and ($x1, $x2) = ($x2, $x1);
2486 $y1 > $y2 and ($y1, $y2) = ($y2, $y1);
2487 my ($stepx, $stepy) = ( 0, 0 );
2488 my $step_size = $style eq "dashed" ? 8 : 2;
2489 my ($counter, $count_end);
2490 if ($x1 == $x2) {
2491 $stepy = $step_size;
2492 ($counter, $count_end) = ($y1, $y2);
2493 }
2494 else {
2495 $stepx = $step_size;
2496 ($counter, $count_end) = ($x1, $x2);
2497 }
2498 my ($x, $y) = ($x1, $y1);
2499 while ($counter < $count_end) {
2500 if ($style eq "dotted") {
2501 $img->setpixel(x => $x, y => $y, color => $opts{color});
2502 }
2503 else {
2504 my $xe = $stepx ? $x + $stepx / 2 - 1 : $x;
2505 $xe > $x2 and $xe = $x2;
2506 my $ye = $stepy ? $y + $stepy / 2 - 1 : $y;
2507 $ye > $y2 and $ye = $y2;
2508 $img->line(x1 => $x, y1 => $y, x2 => $xe, y2 => $ye, %opts);
2509 }
2510 $counter += $step_size;
2511 $x += $stepx;
2512 $y += $stepy;
2513 }
2514
2515 return 1;
2516 }
2517 else {
2518 $self->_error("Unknown line style $style");
2519 return;
2520 }
2521}
2522
2523=item _box(xmin ..., style => $style)
2524
2525A wrapper for drawing styled box outlines.
2526
2527=cut
2528
2529sub _box {
2530 my ($self, %opts) = @_;
2531
2532 my $style = delete $opts{style} || "solid";
2533 my $img = delete $opts{img}
2534 or die "No img supplied to _box";
2535
2536 if ($style eq "solid") {
2537 return $img->box(%opts);
2538 }
2539 else {
2540 my $box = delete $opts{box};
2541 # replicate Imager's defaults
2542 my %work_opts = ( xmin => 0, ymin => 0, xmax => $img->getwidth() - 1, ymax => $img->getheight() -1, %opts, style => $style, img => $img );
2543 my ($xmin, $ymin, $xmax, $ymax) = delete @work_opts{qw/xmin ymin xmax ymax/};
2544 if ($box) {
2545 ($xmin, $ymin, $xmax, $ymax) = @$box;
2546 }
2547 $xmin > $xmax and ($xmin, $xmax) = ($xmax, $xmin);
2548 $ymin > $ymax and ($ymin, $ymax) = ($ymax, $ymin);
2549
2550 if ($xmax - $xmin > 1) {
2551 $self->_line(x1 => $xmin+1, y1 => $ymin, x2 => $xmax-1, y2 => $ymin, %work_opts);
2552 $self->_line(x1 => $xmin+1, y1 => $ymax, x2 => $xmax-1, y2 => $ymax, %work_opts);
2553 }
2554 $self->_line(x1 => $xmin, y1 => $ymin, x2 => $xmin, y2 => $ymax, %work_opts);
86f73dc2 2555 return $self->_line(x1 => $xmax, y1 => $ymin, x2 => $xmax, y2 => $ymax, %work_opts);
56b495c0
TC
2556 }
2557}
2558
5057a68f
TC
2559=item _feature_enabled($feature_name)
2560
2561Check if the given feature is enabled in the work style.
2562
2563=cut
2564
86f73dc2
TC
2565sub _feature_enabled {
2566 my ($self, $name) = @_;
2567
2568 return $self->{_style}{features}{$name};
2569}
2570
35574351
TC
25711;
2572__END__
2573
2574=back
2575
2576=head1 SEE ALSO
2577
2578Imager::Graph::Pie(3), Imager(3), perl(1).
2579
2580=head1 AUTHOR
2581
2582Tony Cook <tony@develop-help.com>
2583
54ada35d
TC
2584=head1 LICENSE
2585
2586Imager::Graph is licensed under the same terms as perl itself.
2587
35574351
TC
2588=head1 BLAME
2589
2590Addi for producing a cool imaging module. :)
2591
2592=cut