File Coverage

blib/lib/Image/OrgChart.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Image::OrgChart;
2            
3 2     2   14943 use strict;
  2         5  
  2         70  
4 2     2   3046 use GD;
  0            
  0            
5             use vars qw($VERSION $DEBUG);
6             require Exporter;
7            
8            
9             $VERSION = '0.20';
10            
11             sub new {
12             my ($pkg,@args) = @_;
13             my %args;
14             if ((scalar @args % 2) == 0) {
15             %args = @args;
16             }
17            
18             ## defaults
19             my $self = {
20             min_width => 0,
21             min_height => 0,
22             box_color => [0,0,0],
23             box_fill_color => [120,120,120],
24             connect_color => [0,0,0],
25             text_color => [0,0,0],
26             bg_color => [255,255,255],
27             shadow_color => [50,50,50],
28             shadow => 0,
29             arrow_heads => 0,
30             fill_boxes => 0,
31             h_spacing => 15,
32             v_spacing => 5,
33             path_seperator => '/',
34             font => 'gdTinyFont',
35             font_height => 10, ## ?
36             font_width => 5, ## ?
37             indent => undef,
38             _data => {},
39             _track => {
40             longest_name => 0,
41             shortest_name => 100,
42             deapest_path => 0,
43             most_keys => 0,
44             total_boxes => 1,
45             },
46             _image_info => {
47             height => 0,
48             width => 0,
49             },
50             color => {
51             ## used by allocateColor
52             },
53             data_type => ( GD::Image->can('gif') ? 'gif' : 'png'),
54             };
55            
56             ## from new() args
57             for (keys %args) {
58             $self->{$_} = $args{$_};
59             }
60            
61             return bless($self,$pkg);
62             }
63            
64             sub data_type {
65             return shift->{data_type};
66             }
67            
68             sub add {
69             my ($self,$path) = @_;
70             $path =~ s/^$self->{path_seperator}//;
71             my @arr_path = split(/$self->{path_seperator}/,$path);
72             warn("PATH($path) - ",join(',',@arr_path),"\n") if $DEBUG;
73             my $curr = '$self->{_data}';
74             my $depth = 0;
75             foreach my $limb (@arr_path) {
76             $curr .= "{'$limb'}";
77             if (length($limb) > $self->{_track}{longest_name}) {
78             $self->{_track}{longest_name} = length($limb);
79             } elsif (length($limb) < $self->{_track}{shortest_name}) {
80             $self->{_track}{shortest_name} = length($limb);
81             }
82             }
83             warn("CREATING: $curr\n") if $DEBUG;
84             eval("$curr = {} unless (exists $curr)");
85             die $@ if $@;
86             }
87            
88             sub set_hashref {
89             my ($self,$href) = @_;
90             $self->{_data} = $href;
91             }
92            
93             sub add_hashref {
94             my ($self,$href) = @_;
95             foreach my $nkey (keys %{ $href }) {
96             if ($self->{$nkey}) {
97             &add_hashref($self->{$nkey},$href->{$nkey});
98             } else {
99             $self->{$nkey} = $href->{$nkey};
100             if (length($nkey) > $self->{_track}{longest_name}) {
101             $self->{_track}{longest_name} = length($nkey);
102             } elsif (length($nkey) > $self->{_track}{shortest_name}) {
103             $self->{_track}{shortest_name} = length($nkey);
104             }
105             }
106             }
107             }
108            
109             sub alloc_collors {
110             my ($self,$image) = @_;
111             $self->{color}{box_color} = $image->colorAllocate($self->{box_color}[0],$self->{box_color}[1],$self->{box_color}[2]);
112             $self->{color}{box_fill_color} = $image->colorAllocate($self->{box_fill_color}[0],$self->{box_fill_color}[1],$self->{box_fill_color}[2]);
113             $self->{color}{connect_color} = $image->colorAllocate($self->{connect_color}[0],$self->{connect_color}[1],$self->{connect_color}[2]);
114             $self->{color}{text_color} = $image->colorAllocate($self->{text_color}[0],$self->{text_color}[1],$self->{text_color}[2]);
115             $self->{color}{bg_color} = $image->colorAllocate($self->{bg_color}[0],$self->{bg_color}[1],$self->{bg_color}[2]);
116             }
117            
118             sub alloc_fonts {
119             my $self = shift;
120            
121             no strict 'refs';
122             my $fnt = &{$self->{font}}();
123             use strict 'refs';
124             $self->{font_width} = $fnt->width;
125             $self->{font_height} = $fnt->height;
126             $self->{indent} ||= 5;
127             $self->{indent} = $self->{font_width}*$self->{indent};
128             warn "GD::Font H/W ($self->{font}) : $self->{font_height}/$self->{font_width}\n" if $DEBUG;
129             }
130            
131             sub draw_boxes {
132             my ($self,$image) = @_;
133             my ($ULx,$ULy) = (5,5); ## start with some padding
134             $Image::OrgChart::S::CurrentY = $ULy;
135             $Image::OrgChart::S::BaseX = $ULx;
136             &_draw_one_row_box($self,$self->{_data},$image,$ULx,$ULy);
137             }
138            
139            
140             sub _draw_one_row_box {
141             my ($self,$href,$image,$indentX,$indentY) = @_;
142             my $ULx = $indentX;
143             my $indent = $self->{indent};
144             my $creap = $self->{_tracked}{box_height} + $self->{v_spacing};
145             foreach my $person (sort keys %{ $href }) {
146             my $ULy = $Image::OrgChart::S::CurrentY;
147            
148             # CONNECTER (if we are a child)
149             if ($Image::OrgChart::S::ParentX && ($ULx > $Image::OrgChart::S::BaseX) ) {
150             ## connect
151             $self->_con_boxes($image,[$Image::OrgChart::S::ParentX,$Image::OrgChart::S::ParentY-$creap],[$ULx,$ULy]);
152             }
153            
154             # RECTANGLE
155             if ($self->{shadow}) {
156             $image->filledRectangle($ULx+3,$ULy+3,$ULx+3+$self->{_tracked}{box_width},$ULy+3+$self->{_tracked}{box_height},$self->{color}{shadow_color});
157             $image->filledRectangle($ULx,$ULy,$ULx+$self->{_tracked}{box_width},$ULy+$self->{_tracked}{box_height},$self->{color}{bg_color});
158             $image->rectangle($ULx,$ULy,$ULx+$self->{_tracked}{box_width},$ULy+$self->{_tracked}{box_height},$self->{color}{box_color});
159             if ($self->{fill_boxes}) {
160             $image->fill($ULx+1,$ULy+1,$self->{color}{box_fill_color});
161             }
162             } else {
163             $image->rectangle($ULx,$ULy,$ULx+$self->{_tracked}{box_width},$ULy+$self->{_tracked}{box_height},$self->{color}{box_color});
164             if ($self->{fill_boxes}) {
165             $image->fill($ULx+1,$ULy+1,$self->{color}{box_fill_color});
166             }
167             }
168            
169             # STRING
170             no strict 'refs';
171             my $fnt = &{$self->{font}}();
172             use strict 'refs';
173             $image->string($fnt,$ULx+2,$ULy+2,$person,$self->{color}{text_color});
174            
175             # TRANSLATE
176             $Image::OrgChart::S::CurrentY += $creap;
177            
178             # REPORTS
179             my $report_cnt = scalar keys %{ $href->{$person} };
180             if ($report_cnt > 0) {
181             $Image::OrgChart::S::ParentX = $ULx;
182             $Image::OrgChart::S::ParentY = $Image::OrgChart::S::CurrentY;
183             $self->_draw_one_row_box($href->{$person},$image,$ULx+$indent,$Image::OrgChart::S::CurrentY);
184             $Image::OrgChart::S::ParentX -= $indent;
185             $Image::OrgChart::S::ParentY -= $creap;
186             }
187             }
188             }
189            
190             sub _con_boxes {
191             my ($self,$image,$from,$to) = @_;
192            
193             $to->[1] += ( $self->{_tracked}{box_height} / 2 );
194             my $vert_x = ( $from->[0] + ($self->{indent}/2));
195             my $v_start_y = ( $from->[1] + $self->{_tracked}{box_height} );
196            
197             $self->_draw_line($image,[$vert_x,$v_start_y],[$vert_x,$to->[1]]); # vertical
198             $self->_draw_line($image,[$vert_x,$to->[1]],[$to->[0],$to->[1]]);
199             }
200            
201             sub _draw_line {
202             my ($self,$image,$from,$to) = @_;
203             $image->line($from->[0],$from->[1],$to->[0],$to->[1],$self->{color}{connect_color});
204             if ($self->{arrow_heads}) {
205             ## i only currently do Horizontal and Vertical lines, which makes
206             ## directional calculations much easier
207             if ($from->[0] == $to->[0]) {
208             ## vert line
209             if ($to->[1] > $from->[1]) {
210             ## face up
211             ###### not yet needed
212             } else {
213             ## face down
214             ###### not yet needed
215             }
216             } else {
217             ## horiz line
218             if ($to->[0] > $from->[0]) {
219             ## face right
220             $image->line($to->[0],$to->[1],$to->[0]-4,$to->[1]-2,$self->{color}{connect_color});
221             $image->line($to->[0],$to->[1],$to->[0]-4,$to->[1]+2,$self->{color}{connect_color});
222             $image->line($to->[0]-4,$to->[1]-2,$to->[0]-4,$to->[1]+2,$self->{color}{connect_color});
223             } else {
224             ## face left
225             ###### currently unused
226             $image->line($to->[0],$to->[1],$to->[0]+2,$to->[1]+4,$self->{color}{connect_color});
227             $image->line($to->[0],$to->[1],$to->[0]-2,$to->[1]+4,$self->{color}{connect_color});
228             $image->line($to->[0]+2,$to->[1]+4,$to->[0]-2,$to->[1]+4,$self->{color}{connect_color});
229             }
230             }
231             }
232             }
233            
234             sub draw {
235             my $self = shift;
236             my $gd = $self->gd();
237            
238             my $dt = $self->{data_type};
239             return $gd->$dt();
240             }
241            
242             *as_image = *draw;
243            
244             sub gd {
245             my $self = shift;
246            
247             ## new image
248             $self->alloc_fonts();
249             $self->_calc_depth();
250             $self->calc_image_info();
251             my $height = ( $self->{min_height} > $self->{_image_info}{height} ? $self->{min_height} : $self->{_image_info}{height} );
252             my $width = ( $self->{min_width} > $self->{_image_info}{width} ? $self->{min_width} : $self->{_image_info}{width} );
253             #printf("HxW = %dx%d\n",$height,$width);
254             my $image = new GD::Image($width,$height);
255             $self->alloc_collors($image);
256             $image->fill(0,0,$self->{color}{bg_color});
257             $self->draw_boxes($image);
258            
259             return $image;
260             }
261            
262             sub _calc_depth {
263             my $self = shift;
264             $Image::OrgChart::S::total = $self->{_track}{deapest_path};
265             $Image::OrgChart::S::Kcount = $self->{_track}{most_keys};
266             $Image::OrgChart::S::Lname = $self->{_track}{longest_name};
267             $Image::OrgChart::S::Sname = $self->{_track}{shortest_name};
268             $Image::OrgChart::S::TBox = $self->{_track}{total_boxes};
269             &_re_f_depth($self->{_data});
270             $self->{_track}{most_keys} = $Image::OrgChart::S::Kcount;
271             $self->{_track}{deapest_path} = $Image::OrgChart::S::total;
272             $self->{_track}{longest_name} = $Image::OrgChart::S::Lname;
273             $self->{_track}{shortest_name} = $Image::OrgChart::S::Sname;
274             $self->{_track}{total_boxes} = $Image::OrgChart::S::TBox ;
275             undef($Image::OrgChart::S::total);
276             undef($Image::OrgChart::S::Kcount);
277             undef($Image::OrgChart::S::Lname);
278             undef($Image::OrgChart::S::Sname);
279             undef($Image::OrgChart::S::TBox);
280             }
281            
282             sub _re_f_depth {
283             my $href = shift;
284             my $indent = shift;
285             $indent ||= 0;
286             if ( $indent > $Image::OrgChart::S::total ) {
287             $Image::OrgChart::S::total = $indent;
288             }
289             foreach my $key (keys %$href) {
290             $Image::OrgChart::S::TBox++;
291             if (length($key) > $Image::OrgChart::S::Lname) {
292             $Image::OrgChart::S::Lname = length($key);
293             } elsif (length($key) < $Image::OrgChart::S::Sname) {
294             $Image::OrgChart::S::Sname = length($key);
295             }
296             my $value = $href->{$key};
297             if (ref($value) eq 'HASH') {
298             &_re_f_depth($value, $indent + 1);
299             $Image::OrgChart::S::Kcount = ( (scalar keys %$href > $Image::OrgChart::S::Kcount) ? scalar keys %$href : $Image::OrgChart::S::Kcount );
300             }
301             }
302             }
303            
304             sub calc_image_info {
305             my $self = shift;
306             $self->{_tracked}{box_width} = ( ( $self->{_track}{longest_name} * $self->{font_width} ) + 2);
307             $self->{_tracked}{box_height} = ( $self->{font_height} + 2 );
308             $self->{_image_info}{height} = ( ($self->{v_spacing} + $self->{_tracked}{box_height}) * $self->{_track}{total_boxes});
309             $self->{_image_info}{width} = ( ($self->{indent}+$self->{h_spacing}) * $self->{_track}{deapest_path})+$self->{_tracked}{box_width};
310             }
311            
312             sub mid_point {
313             my ($x1,$y1,$x2,$y2) = @_;
314             my $X = (((_max($x1,$x2) - _min($x1,$x2))/2) + _min($x1,$x2));
315             my $Y = (((_max($y1,$y2) - _min($y1,$y2))/2) + _min($y1,$y2));
316             return [$X,$Y];
317             }
318            
319             sub _min {
320             my ($a,$b) = @_;
321             return ( $a > $b ? $b : $a);
322             }
323            
324             sub _max {
325             my ($a,$b) = @_;
326             return ( $a < $b ? $b : $a);
327             }
328            
329             1;
330             __END__