| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#========================================================================== |
|
2
|
|
|
|
|
|
|
# Copyright (c) 1995-2000 Martien Verbruggen |
|
3
|
|
|
|
|
|
|
#-------------------------------------------------------------------------- |
|
4
|
|
|
|
|
|
|
# |
|
5
|
|
|
|
|
|
|
# Name: |
|
6
|
|
|
|
|
|
|
# GD::Graph.pm |
|
7
|
|
|
|
|
|
|
# |
|
8
|
|
|
|
|
|
|
# Description: |
|
9
|
|
|
|
|
|
|
# Module to create graphs from a data set drawing on a GD::Image |
|
10
|
|
|
|
|
|
|
# object |
|
11
|
|
|
|
|
|
|
# |
|
12
|
|
|
|
|
|
|
# Package of a number of graph types: |
|
13
|
|
|
|
|
|
|
# GD::Graph::bars |
|
14
|
|
|
|
|
|
|
# GD::Graph::hbars |
|
15
|
|
|
|
|
|
|
# GD::Graph::lines |
|
16
|
|
|
|
|
|
|
# GD::Graph::points |
|
17
|
|
|
|
|
|
|
# GD::Graph::linespoints |
|
18
|
|
|
|
|
|
|
# GD::Graph::area |
|
19
|
|
|
|
|
|
|
# GD::Graph::pie |
|
20
|
|
|
|
|
|
|
# GD::Graph::mixed |
|
21
|
|
|
|
|
|
|
# |
|
22
|
|
|
|
|
|
|
# $Id: Graph.pm,v 1.55 2007/04/26 04:12:47 ben Exp $ |
|
23
|
|
|
|
|
|
|
# |
|
24
|
|
|
|
|
|
|
#========================================================================== |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# |
|
27
|
|
|
|
|
|
|
# GD::Graph |
|
28
|
|
|
|
|
|
|
# |
|
29
|
|
|
|
|
|
|
# Parent class containing data all graphs have in common. |
|
30
|
|
|
|
|
|
|
# |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
package GD::Graph; |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
($GD::Graph::prog_version) = '$Revision: 1.55 $' =~ /\s([\d.]+)/; |
|
35
|
|
|
|
|
|
|
$GD::Graph::VERSION = '1.51'; |
|
36
|
|
|
|
|
|
|
|
|
37
|
3
|
|
|
3
|
|
6859
|
use strict; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
74
|
|
|
38
|
3
|
|
|
3
|
|
3063
|
use GD; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
use GD::Text::Align; |
|
40
|
|
|
|
|
|
|
use GD::Graph::Data; |
|
41
|
|
|
|
|
|
|
use GD::Graph::Error; |
|
42
|
|
|
|
|
|
|
use Carp; |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
@GD::Graph::ISA = qw(GD::Graph::Error); |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# Some tools and utils |
|
47
|
|
|
|
|
|
|
use GD::Graph::colour qw(:colours); |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
my %GDsize = ( |
|
50
|
|
|
|
|
|
|
'x' => 400, |
|
51
|
|
|
|
|
|
|
'y' => 300 |
|
52
|
|
|
|
|
|
|
); |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
my %Defaults = ( |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# Set the top, bottom, left and right margin for the chart. These |
|
57
|
|
|
|
|
|
|
# margins will be left empty. |
|
58
|
|
|
|
|
|
|
t_margin => 0, |
|
59
|
|
|
|
|
|
|
b_margin => 0, |
|
60
|
|
|
|
|
|
|
l_margin => 0, |
|
61
|
|
|
|
|
|
|
r_margin => 0, |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# Set the factor with which to resize the logo in the chart (need to |
|
64
|
|
|
|
|
|
|
# automatically compute something nice for this, really), set the |
|
65
|
|
|
|
|
|
|
# default logo file name, and set the logo position (UR, BR, UL, BL) |
|
66
|
|
|
|
|
|
|
logo => undef, |
|
67
|
|
|
|
|
|
|
logo_resize => 1.0, |
|
68
|
|
|
|
|
|
|
logo_position => 'LR', |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# Do we want a transparent background? |
|
71
|
|
|
|
|
|
|
transparent => 1, |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Do we want interlacing? |
|
74
|
|
|
|
|
|
|
interlaced => 1, |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# Set the background colour, the default foreground colour (used |
|
77
|
|
|
|
|
|
|
# for axes etc), the textcolour, the colour for labels, the colour |
|
78
|
|
|
|
|
|
|
# for numbers on the axes, the colour for accents (extra lines, tick |
|
79
|
|
|
|
|
|
|
# marks, etc..) |
|
80
|
|
|
|
|
|
|
bgclr => 'white', # background colour |
|
81
|
|
|
|
|
|
|
fgclr => 'dblue', # Axes and grid |
|
82
|
|
|
|
|
|
|
boxclr => undef, # Fill colour for box axes, default: not used |
|
83
|
|
|
|
|
|
|
accentclr => 'gray', # bar, area and pie outlines. |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
labelclr => 'dblue', # labels on axes |
|
86
|
|
|
|
|
|
|
axislabelclr => 'dblue', # values on axes |
|
87
|
|
|
|
|
|
|
legendclr => 'dblue', # Text for the legend |
|
88
|
|
|
|
|
|
|
textclr => 'dblue', # All text, apart from the following 2 |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
valuesclr => 'dblue', # values printed above the points |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# data set colours |
|
93
|
|
|
|
|
|
|
dclrs => [qw(lred lgreen lblue lyellow lpurple cyan lorange)], |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# number of pixels to use as text spacing |
|
96
|
|
|
|
|
|
|
text_space => 4, |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# These have undefined values, but are here so that the set method |
|
99
|
|
|
|
|
|
|
# knows about them: |
|
100
|
|
|
|
|
|
|
title => undef, |
|
101
|
|
|
|
|
|
|
); |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub _has_default { |
|
104
|
|
|
|
|
|
|
my $self = shift; |
|
105
|
|
|
|
|
|
|
my $attr = shift || return; |
|
106
|
|
|
|
|
|
|
exists $Defaults{$attr} |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# |
|
110
|
|
|
|
|
|
|
# PUBLIC methods, documented in pod. |
|
111
|
|
|
|
|
|
|
# |
|
112
|
|
|
|
|
|
|
sub new # ( width, height ) optional; |
|
113
|
|
|
|
|
|
|
{ |
|
114
|
|
|
|
|
|
|
my $type = shift; |
|
115
|
|
|
|
|
|
|
my $self = {}; |
|
116
|
|
|
|
|
|
|
bless $self, $type; |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
if (@_) |
|
119
|
|
|
|
|
|
|
{ |
|
120
|
|
|
|
|
|
|
# If there are any parameters, they should be the size |
|
121
|
|
|
|
|
|
|
return GD::Graph->_set_error( |
|
122
|
|
|
|
|
|
|
"Usage: GD::Graph::::new(width, height)") unless @_ >= 2; |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
$self->{width} = shift; |
|
125
|
|
|
|
|
|
|
$self->{height} = shift; |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
else |
|
128
|
|
|
|
|
|
|
{ |
|
129
|
|
|
|
|
|
|
# There were obviously no parameters, so use defaults |
|
130
|
|
|
|
|
|
|
$self->{width} = $GDsize{'x'}; |
|
131
|
|
|
|
|
|
|
$self->{height} = $GDsize{'y'}; |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# Initialise all relevant parameters to defaults |
|
135
|
|
|
|
|
|
|
# These are defined in the subclasses. See there. |
|
136
|
|
|
|
|
|
|
$self->initialise() or return; |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
return $self; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub get |
|
142
|
|
|
|
|
|
|
{ |
|
143
|
|
|
|
|
|
|
my $self = shift; |
|
144
|
|
|
|
|
|
|
my @wanted = map $self->{$_}, @_; |
|
145
|
|
|
|
|
|
|
wantarray ? @wanted : $wanted[0]; |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
sub set |
|
149
|
|
|
|
|
|
|
{ |
|
150
|
|
|
|
|
|
|
my $self = shift; |
|
151
|
|
|
|
|
|
|
my %args = @_; |
|
152
|
|
|
|
|
|
|
my $w = 0; |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
foreach (keys %args) |
|
155
|
|
|
|
|
|
|
{ |
|
156
|
|
|
|
|
|
|
# Enforce read-only attributes. |
|
157
|
|
|
|
|
|
|
/^width$/ || /^height$/ and do |
|
158
|
|
|
|
|
|
|
{ |
|
159
|
|
|
|
|
|
|
$self->_set_warning("Read-only attribute '$_' not set"); |
|
160
|
|
|
|
|
|
|
$w++; |
|
161
|
|
|
|
|
|
|
next; |
|
162
|
|
|
|
|
|
|
}; |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
$self->{$_} = $args{$_}, next if $self->_has_default($_); |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
$w++; |
|
167
|
|
|
|
|
|
|
$self->_set_warning("No attribute '$_'"); |
|
168
|
|
|
|
|
|
|
} |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
return $w ? undef : "No problems"; |
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# Generic routine to instantiate GD::Text::Align objects for text |
|
174
|
|
|
|
|
|
|
# attributes |
|
175
|
|
|
|
|
|
|
sub _set_font |
|
176
|
|
|
|
|
|
|
{ |
|
177
|
|
|
|
|
|
|
my $self = shift; |
|
178
|
|
|
|
|
|
|
my $name = shift; |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
if (! exists $self->{$name}) |
|
181
|
|
|
|
|
|
|
{ |
|
182
|
|
|
|
|
|
|
$self->{$name} = GD::Text::Align->new($self->{graph}, |
|
183
|
|
|
|
|
|
|
valign => 'top', |
|
184
|
|
|
|
|
|
|
halign => 'center', |
|
185
|
|
|
|
|
|
|
) or return $self->_set_error("Couldn't set font"); |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
$self->{$name}->set_font(@_); |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
sub set_title_font # (fontname, size) |
|
192
|
|
|
|
|
|
|
{ |
|
193
|
|
|
|
|
|
|
my $self = shift; |
|
194
|
|
|
|
|
|
|
$self->_set_font('gdta_title', @_); |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub set_text_clr # (colour name) |
|
198
|
|
|
|
|
|
|
{ |
|
199
|
|
|
|
|
|
|
my $self = shift; |
|
200
|
|
|
|
|
|
|
my $clr = shift; |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
$self->set( |
|
203
|
|
|
|
|
|
|
textclr => $clr, |
|
204
|
|
|
|
|
|
|
labelclr => $clr, |
|
205
|
|
|
|
|
|
|
axislabelclr => $clr, |
|
206
|
|
|
|
|
|
|
valuesclr => $clr, |
|
207
|
|
|
|
|
|
|
); |
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub plot |
|
211
|
|
|
|
|
|
|
{ |
|
212
|
|
|
|
|
|
|
# ABSTRACT |
|
213
|
|
|
|
|
|
|
my $self = shift; |
|
214
|
|
|
|
|
|
|
$self->die_abstract("sub plot missing,"); |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# Set defaults that apply to all graph/chart types. |
|
218
|
|
|
|
|
|
|
# This is called by the default initialise methods |
|
219
|
|
|
|
|
|
|
# from the objects further down the tree. |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub initialise |
|
222
|
|
|
|
|
|
|
{ |
|
223
|
|
|
|
|
|
|
my $self = shift; |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
foreach (keys %Defaults) |
|
226
|
|
|
|
|
|
|
{ |
|
227
|
|
|
|
|
|
|
$self->set($_ => $Defaults{$_}); |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
$self->open_graph() or return; |
|
231
|
|
|
|
|
|
|
$self->set_title_font(GD::Font->Large) or return; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# Check the integrity of the submitted data |
|
236
|
|
|
|
|
|
|
# |
|
237
|
|
|
|
|
|
|
# Checks are done to assure that every input array |
|
238
|
|
|
|
|
|
|
# has the same number of data points, it sets the variables |
|
239
|
|
|
|
|
|
|
# that store the number of sets and the number of points |
|
240
|
|
|
|
|
|
|
# per set, and kills the process if there are no datapoints |
|
241
|
|
|
|
|
|
|
# in the sets, or if there are no data sets. |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub check_data # \@data |
|
244
|
|
|
|
|
|
|
{ |
|
245
|
|
|
|
|
|
|
my $self = shift; |
|
246
|
|
|
|
|
|
|
my $data = shift; |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
$self->{_data} = GD::Graph::Data->new($data) |
|
249
|
|
|
|
|
|
|
or return $self->_set_error(GD::Graph::Data->error); |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
$self->{_data}->make_strict; |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
$self->{_data}->num_sets > 0 && $self->{_data}->num_points > 0 |
|
254
|
|
|
|
|
|
|
or return $self->_set_error('No data sets or points'); |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
if ($self->{show_values}) |
|
257
|
|
|
|
|
|
|
{ |
|
258
|
|
|
|
|
|
|
# If this isn't a GD::Graph::Data compatible structure, then |
|
259
|
|
|
|
|
|
|
# we'll just use the data structure. |
|
260
|
|
|
|
|
|
|
# |
|
261
|
|
|
|
|
|
|
# XXX We should probably check a few more things here, e.g. |
|
262
|
|
|
|
|
|
|
# similarity between _data and show_values. |
|
263
|
|
|
|
|
|
|
# |
|
264
|
|
|
|
|
|
|
my $ref = ref($self->{show_values}); |
|
265
|
|
|
|
|
|
|
if (! $ref || ($ref ne 'GD::Graph::Data' && $ref ne 'ARRAY')) |
|
266
|
|
|
|
|
|
|
{ |
|
267
|
|
|
|
|
|
|
$self->{show_values} = $self->{_data} |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
elsif ($ref eq 'ARRAY') |
|
270
|
|
|
|
|
|
|
{ |
|
271
|
|
|
|
|
|
|
$self->{show_values} = |
|
272
|
|
|
|
|
|
|
GD::Graph::Data->new($self->{show_values}) |
|
273
|
|
|
|
|
|
|
or return $self->_set_error(GD::Graph::Data->error); |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
return $self; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# Open the graph output canvas by creating a new GD object. |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub open_graph |
|
283
|
|
|
|
|
|
|
{ |
|
284
|
|
|
|
|
|
|
my $self = shift; |
|
285
|
|
|
|
|
|
|
return $self->{graph} if exists $self->{graph}; |
|
286
|
|
|
|
|
|
|
$self->{graph} = 2.0 <= $GD::VERSION |
|
287
|
|
|
|
|
|
|
? GD::Image->newPalette($self->{width}, $self->{height}) |
|
288
|
|
|
|
|
|
|
: GD::Image->new($self->{width}, $self->{height}); |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
} |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# Initialise the graph output canvas, setting colours (and getting back |
|
293
|
|
|
|
|
|
|
# index numbers for them) setting the graph to transparent, and |
|
294
|
|
|
|
|
|
|
# interlaced, putting a logo (if defined) on there. |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub init_graph |
|
297
|
|
|
|
|
|
|
{ |
|
298
|
|
|
|
|
|
|
my $self = shift; |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
$self->{bgci} = $self->set_clr(_rgb($self->{bgclr})); |
|
301
|
|
|
|
|
|
|
$self->{fgci} = $self->set_clr(_rgb($self->{fgclr})); |
|
302
|
|
|
|
|
|
|
$self->{tci} = $self->set_clr(_rgb($self->{textclr})); |
|
303
|
|
|
|
|
|
|
$self->{lci} = $self->set_clr(_rgb($self->{labelclr})); |
|
304
|
|
|
|
|
|
|
$self->{alci} = $self->set_clr(_rgb($self->{axislabelclr})); |
|
305
|
|
|
|
|
|
|
$self->{acci} = $self->set_clr(_rgb($self->{accentclr})); |
|
306
|
|
|
|
|
|
|
$self->{valuesci} = $self->set_clr(_rgb($self->{valuesclr})); |
|
307
|
|
|
|
|
|
|
$self->{legendci} = $self->set_clr(_rgb($self->{legendclr})); |
|
308
|
|
|
|
|
|
|
$self->{boxci} = $self->set_clr(_rgb($self->{boxclr})) |
|
309
|
|
|
|
|
|
|
if $self->{boxclr}; |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
$self->{graph}->transparent($self->{bgci}) if $self->{transparent}; |
|
312
|
|
|
|
|
|
|
$self->{graph}->interlaced( $self->{interlaced} || undef ); # required by GD.pm |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# XXX yuck. This doesn't belong here.. or does it? |
|
315
|
|
|
|
|
|
|
$self->put_logo(); |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
return $self; |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub _read_logo_file |
|
321
|
|
|
|
|
|
|
{ |
|
322
|
|
|
|
|
|
|
my $self = shift; |
|
323
|
|
|
|
|
|
|
my $glogo; |
|
324
|
|
|
|
|
|
|
local (*LOGO); |
|
325
|
|
|
|
|
|
|
my $logo_path = $self->{logo}; |
|
326
|
|
|
|
|
|
|
open(LOGO, $logo_path) |
|
327
|
|
|
|
|
|
|
or do { carp "Unable to open logo file '$logo_path': $!";return}; |
|
328
|
|
|
|
|
|
|
binmode(LOGO); |
|
329
|
|
|
|
|
|
|
# if the file has an extension, use that importer |
|
330
|
|
|
|
|
|
|
my $gdimport; |
|
331
|
|
|
|
|
|
|
my @tried; |
|
332
|
|
|
|
|
|
|
# possibly forward-compatible: just try whatever file extension |
|
333
|
|
|
|
|
|
|
if ( $logo_path =~ /\.(\w+)$/i) { |
|
334
|
|
|
|
|
|
|
my $fmt = lc $1; |
|
335
|
|
|
|
|
|
|
$fmt = "jpeg" if 'jpg' eq $fmt; |
|
336
|
|
|
|
|
|
|
push @tried, uc $fmt; |
|
337
|
|
|
|
|
|
|
if ($gdimport = GD::Image->can("newFrom\u$fmt")) { |
|
338
|
|
|
|
|
|
|
if ('xpm' ne $fmt) { $glogo = GD::Image->$gdimport(\*LOGO) } |
|
339
|
|
|
|
|
|
|
else { $glogo = GD::Image->$gdimport($logo_path) } # quirky special case |
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
} |
|
342
|
|
|
|
|
|
|
# if that didn't work, try using magic numbers |
|
343
|
|
|
|
|
|
|
if (!$glogo) { |
|
344
|
|
|
|
|
|
|
my $logodata; |
|
345
|
|
|
|
|
|
|
read LOGO,$logodata, -s LOGO; |
|
346
|
|
|
|
|
|
|
my %magic = ( |
|
347
|
|
|
|
|
|
|
pack("H8",'ffd8ffe0') => "jpeg", |
|
348
|
|
|
|
|
|
|
'GIF8' => "gif", |
|
349
|
|
|
|
|
|
|
'.PNG' => "png", |
|
350
|
|
|
|
|
|
|
'/* X'=> "xpm", # technically '/* XPM */', but I'm hashing, here |
|
351
|
|
|
|
|
|
|
); |
|
352
|
|
|
|
|
|
|
if (my $match = $magic{ substr $logodata, 0, 4 }) { |
|
353
|
|
|
|
|
|
|
push @tried, $match; |
|
354
|
|
|
|
|
|
|
my $matchmethod = "newFrom\u$match"; |
|
355
|
|
|
|
|
|
|
if ($gdimport = GD::Image->can($matchmethod . "Data")) { |
|
356
|
|
|
|
|
|
|
$glogo = GD::Image->$gdimport($logodata); |
|
357
|
|
|
|
|
|
|
} elsif ($gdimport = GD::Image->can($matchmethod)) { |
|
358
|
|
|
|
|
|
|
if ('xpm' eq $match) { |
|
359
|
|
|
|
|
|
|
$glogo = GD::Image->$gdimport($logo_path); |
|
360
|
|
|
|
|
|
|
} else { |
|
361
|
|
|
|
|
|
|
seek LOGO,0,0; |
|
362
|
|
|
|
|
|
|
$glogo = GD::Image->$gdimport(\*LOGO); |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
} |
|
365
|
|
|
|
|
|
|
# should this actually be "if (!$glogo), rather than an else? |
|
366
|
|
|
|
|
|
|
} else { # Hail Mary, full of Grace! Blessed art thou among women... |
|
367
|
|
|
|
|
|
|
push @tried, 'libgd best-guess'; |
|
368
|
|
|
|
|
|
|
$glogo = GD::Image->new($logodata); |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
close LOGO or croak "Unable to close logo file '$logo_path': $!"; |
|
372
|
|
|
|
|
|
|
# XXX change to use warnings::enabled when we break 5.005 compatibility |
|
373
|
|
|
|
|
|
|
carp "Problems reading $logo_path (tried: @tried)" unless $glogo; |
|
374
|
|
|
|
|
|
|
return $glogo; |
|
375
|
|
|
|
|
|
|
} |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
# read in the logo, and paste it on the graph canvas |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
sub put_logo |
|
380
|
|
|
|
|
|
|
{ |
|
381
|
|
|
|
|
|
|
my $self = shift; |
|
382
|
|
|
|
|
|
|
return unless defined $self->{logo}; |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
my $glogo = $self->_read_logo_file() or return; |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
my ($x, $y); |
|
387
|
|
|
|
|
|
|
my $r = $self->{logo_resize}; |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
my $r_margin = (defined $self->{r_margin_abs}) ? |
|
390
|
|
|
|
|
|
|
$self->{r_margin_abs} : $self->{r_margin}; |
|
391
|
|
|
|
|
|
|
my $b_margin = (defined $self->{b_margin_abs}) ? |
|
392
|
|
|
|
|
|
|
$self->{b_margin_abs} : $self->{b_margin}; |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
my ($w, $h) = $glogo->getBounds; |
|
395
|
|
|
|
|
|
|
LOGO: for ($self->{logo_position}) { |
|
396
|
|
|
|
|
|
|
/UL/i and do { |
|
397
|
|
|
|
|
|
|
$x = $self->{l_margin}; |
|
398
|
|
|
|
|
|
|
$y = $self->{t_margin}; |
|
399
|
|
|
|
|
|
|
last LOGO; |
|
400
|
|
|
|
|
|
|
}; |
|
401
|
|
|
|
|
|
|
/UR/i and do { |
|
402
|
|
|
|
|
|
|
$x = $self->{width} - $r_margin - $w * $r; |
|
403
|
|
|
|
|
|
|
$y = $self->{t_margin}; |
|
404
|
|
|
|
|
|
|
last LOGO; |
|
405
|
|
|
|
|
|
|
}; |
|
406
|
|
|
|
|
|
|
/LL/i and do { |
|
407
|
|
|
|
|
|
|
$x = $self->{l_margin}; |
|
408
|
|
|
|
|
|
|
$y = $self->{height} - $b_margin - $h * $r; |
|
409
|
|
|
|
|
|
|
last LOGO; |
|
410
|
|
|
|
|
|
|
}; |
|
411
|
|
|
|
|
|
|
# default "LR" |
|
412
|
|
|
|
|
|
|
$x = $self->{width} - $r_margin - $r * $w; |
|
413
|
|
|
|
|
|
|
$y = $self->{height} - $b_margin - $r * $h; |
|
414
|
|
|
|
|
|
|
last LOGO; |
|
415
|
|
|
|
|
|
|
} |
|
416
|
|
|
|
|
|
|
$self->{graph}->copyResized($glogo, |
|
417
|
|
|
|
|
|
|
$x, $y, 0, 0, $r * $w, $r * $h, $w, $h); |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
# Set a colour to work with on the canvas, by rgb value. |
|
421
|
|
|
|
|
|
|
# Return the colour index in the palette |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub set_clr # GD::Image, r, g, b |
|
424
|
|
|
|
|
|
|
{ |
|
425
|
|
|
|
|
|
|
my $self = shift; |
|
426
|
|
|
|
|
|
|
return unless @_; |
|
427
|
|
|
|
|
|
|
my $gd = $self->{graph}; |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
# All of this could potentially be done by using colorResolve |
|
430
|
|
|
|
|
|
|
# The problem is that colorResolve doesn't return an error |
|
431
|
|
|
|
|
|
|
# condition (-1) if it can't allocate a color. Instead it always |
|
432
|
|
|
|
|
|
|
# returns 0. |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
# Check if this colour already exists on the canvas |
|
435
|
|
|
|
|
|
|
my $i = $gd->colorExact(@_); |
|
436
|
|
|
|
|
|
|
# if not, allocate a new one, and return its index |
|
437
|
|
|
|
|
|
|
$i = $gd->colorAllocate(@_) if $i < 0; |
|
438
|
|
|
|
|
|
|
# if this fails, we should use colorClosest. |
|
439
|
|
|
|
|
|
|
$i = $gd->colorClosest(@_) if $i < 0; |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
# TODO Deal with antialiasing here? |
|
442
|
|
|
|
|
|
|
if (0 && $self->can("setAntiAliased")) |
|
443
|
|
|
|
|
|
|
{ |
|
444
|
|
|
|
|
|
|
$self->setAntiAliased($i); |
|
445
|
|
|
|
|
|
|
eval "$i = gdAntiAliased"; |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
return $i; |
|
449
|
|
|
|
|
|
|
} |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
# Set a temporary colour that can be used with fillToBorder |
|
452
|
|
|
|
|
|
|
sub _set_tmp_clr |
|
453
|
|
|
|
|
|
|
{ |
|
454
|
|
|
|
|
|
|
my $self = shift; |
|
455
|
|
|
|
|
|
|
# XXX Error checks! |
|
456
|
|
|
|
|
|
|
$self->{graph}->colorAllocate(0,0,0); |
|
457
|
|
|
|
|
|
|
} |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# Remove the temporary colour |
|
460
|
|
|
|
|
|
|
sub _rm_tmp_clr |
|
461
|
|
|
|
|
|
|
{ |
|
462
|
|
|
|
|
|
|
my $self = shift; |
|
463
|
|
|
|
|
|
|
return unless @_; |
|
464
|
|
|
|
|
|
|
# XXX Error checks? |
|
465
|
|
|
|
|
|
|
$self->{graph}->colorDeallocate(shift); |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# Set a colour, disregarding wether or not it already exists. This may |
|
469
|
|
|
|
|
|
|
# be necessary where one wants the same colour to have a different |
|
470
|
|
|
|
|
|
|
# index, as in pie slices of the same color as the edge. |
|
471
|
|
|
|
|
|
|
# Note that this could be cleaned up after needed, but we won't do that. |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
sub set_clr_uniq # GD::Image, r, g, b |
|
474
|
|
|
|
|
|
|
{ |
|
475
|
|
|
|
|
|
|
my $self = shift; |
|
476
|
|
|
|
|
|
|
return unless @_; |
|
477
|
|
|
|
|
|
|
$self->{graph}->colorAllocate(@_); |
|
478
|
|
|
|
|
|
|
} |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# Return an array of rgb values for a colour number |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
sub pick_data_clr # number |
|
483
|
|
|
|
|
|
|
{ |
|
484
|
|
|
|
|
|
|
my $self = shift; |
|
485
|
|
|
|
|
|
|
_rgb($self->{dclrs}[$_[0] % @{$self->{dclrs}} - 1]); |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# contrib "Bremford, Mike" |
|
489
|
|
|
|
|
|
|
sub pick_border_clr # number |
|
490
|
|
|
|
|
|
|
{ |
|
491
|
|
|
|
|
|
|
my $self = shift; |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
ref $self->{borderclrs} ? |
|
494
|
|
|
|
|
|
|
_rgb($self->{borderclrs}[$_[0] % @{$self->{borderclrs}} - 1]) : |
|
495
|
|
|
|
|
|
|
_rgb($self->{accentclr}); |
|
496
|
|
|
|
|
|
|
} |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
sub gd |
|
499
|
|
|
|
|
|
|
{ |
|
500
|
|
|
|
|
|
|
my $self = shift; |
|
501
|
|
|
|
|
|
|
return $self->{graph}; |
|
502
|
|
|
|
|
|
|
} |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
sub export_format |
|
505
|
|
|
|
|
|
|
{ |
|
506
|
|
|
|
|
|
|
my $proto = shift; |
|
507
|
|
|
|
|
|
|
my @f = grep { GD::Image->can($_) && |
|
508
|
|
|
|
|
|
|
do { |
|
509
|
|
|
|
|
|
|
my $g = GD::Image->new(5,5); |
|
510
|
|
|
|
|
|
|
$g->colorAllocate(0,0,0); |
|
511
|
|
|
|
|
|
|
$g->$_() |
|
512
|
|
|
|
|
|
|
}; |
|
513
|
|
|
|
|
|
|
} qw(gif png jpeg xbm xpm gd gd2); |
|
514
|
|
|
|
|
|
|
wantarray ? @f : $f[0]; |
|
515
|
|
|
|
|
|
|
} |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
# The following method is undocumented, and will not be supported as |
|
518
|
|
|
|
|
|
|
# part of the interface. There isn't really much reason to do so. |
|
519
|
|
|
|
|
|
|
sub import_format |
|
520
|
|
|
|
|
|
|
{ |
|
521
|
|
|
|
|
|
|
my $proto = shift; |
|
522
|
|
|
|
|
|
|
# xpm now included despite bugginess--should document the problem, though |
|
523
|
|
|
|
|
|
|
my @f = grep { GD::Image->can("newFrom\u$_") } |
|
524
|
|
|
|
|
|
|
qw(gif png jpeg xbm xpm gd gd2); |
|
525
|
|
|
|
|
|
|
wantarray ? @f : $f[0]; |
|
526
|
|
|
|
|
|
|
} |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
sub can_do_ttf |
|
529
|
|
|
|
|
|
|
{ |
|
530
|
|
|
|
|
|
|
my $proto = shift; |
|
531
|
|
|
|
|
|
|
return GD::Text->can_do_ttf; |
|
532
|
|
|
|
|
|
|
} |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
# DEBUGGING |
|
535
|
|
|
|
|
|
|
# data_dump obsolete now, use Data::Dumper |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
sub die_abstract |
|
538
|
|
|
|
|
|
|
{ |
|
539
|
|
|
|
|
|
|
my $self = shift; |
|
540
|
|
|
|
|
|
|
my $msg = shift; |
|
541
|
|
|
|
|
|
|
# ABSTRACT |
|
542
|
|
|
|
|
|
|
confess |
|
543
|
|
|
|
|
|
|
"Subclass (" . |
|
544
|
|
|
|
|
|
|
ref($self) . |
|
545
|
|
|
|
|
|
|
") not implemented correctly: " . |
|
546
|
|
|
|
|
|
|
(defined($msg) ? $msg : "unknown error"); |
|
547
|
|
|
|
|
|
|
} |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
"Just another true value"; |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
__END__ |