File Coverage

blib/lib/GD/SVG.pm
Criterion Covered Total %
statement 96 530 18.1
branch 3 116 2.5
condition 4 120 3.3
subroutine 28 115 24.3
pod n/a
total 131 881 14.8


line stmt bran cond sub pod time code
1             package GD::SVG;
2              
3 1     1   23373 use strict;
  1         1  
  1         46  
4 1     1   5 use Carp 'croak','carp','confess';
  1         2  
  1         90  
5 1     1   889 use SVG;
  1         17717  
  1         8  
6             #use warnings;
7 1     1   922 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);
  1         2  
  1         111  
8             require Exporter;
9              
10             $VERSION = '0.33';
11             # $Id: SVG.pm,v 1.16 2009/05/10 14:07:17 todd Exp $
12              
13             # Conditional support for side-by-side raster generation. Off for now.
14             # Methods that support this are commented out multiple times (ie ######)
15 1     1   6 use constant USEGD => 0;
  1         2  
  1         94  
16             if (USEGD) {
17             eval "use GD";
18             }
19              
20             # A global debug flag which can be overriden by new()
21 1     1   5 use constant DEBUG => 0;
  1         2  
  1         104  
22              
23             @ISA = qw(Exporter);
24             %EXPORT_TAGS = ('cmp' => [qw(GD_CMP_IMAGE
25             GD_CMP_NUM_COLORS
26             GD_CMP_COLOR
27             GD_CMP_SIZE_X
28             GD_CMP_SIZE_Y
29             GD_CMP_TRANSPARENT
30             GD_CMP_BACKGROUND
31             GD_CMP_INTERLACE
32             GD_CMP_TRUECOLOR
33             )
34             ]
35             );
36              
37             @EXPORT = qw(
38             gdStyled
39             gdBrushed
40             gdTransparent
41             gdTinyFont
42             gdSmallFont
43             gdMediumBoldFont
44             gdLargeFont
45             gdGiantFont
46             gdDashSize
47             gdMaxColors
48             gdStyledBrushed
49             gdTiled
50             gdChord
51             gdEdged
52             gdNoFill
53             gdArc
54             gdPie
55             );
56              
57             # Not yet implemented
58             #@EXPORT_OK = qw (
59             # GD_CMP_IMAGE
60             # GD_CMP_NUM_COLORS
61             # GD_CMP_COLOR
62             # GD_CMP_SIZE_X
63             # GD_CMP_SIZE_Y
64             # GD_CMP_TRANSPARENT
65             # GD_CMP_BACKGROUND
66             # GD_CMP_INTERLACE
67             # GD_CMP_TRUECOLOR
68             # );
69              
70              
71             # GD does not allow dynamic creation of fonts. These default values
72             # are approximate sizes for the various fonts based on an extensive
73             # afternoon of cross-comparison ;)
74 1     1   4 use constant DEFAULT_FONT => 'Helvetica';
  1         1  
  1         32  
75 1     1   4 use constant TINY_HEIGHT => 8;
  1         2  
  1         36  
76 1     1   4 use constant TINY_WIDTH => 5;
  1         2  
  1         41  
77 1     1   4 use constant TINY_WEIGHT => 'normal';
  1         1  
  1         30  
78 1     1   4 use constant SMALL_HEIGHT => 11; # originally 12
  1         2  
  1         32  
79 1     1   5 use constant SMALL_WIDTH => 6;
  1         1  
  1         39  
80 1     1   4 use constant SMALL_WEIGHT => 'normal';
  1         1  
  1         58  
81 1     1   20 use constant MEDIUM_BOLD_HEIGHT => 13;
  1         3  
  1         39  
82 1     1   4 use constant MEDIUM_BOLD_WIDTH => 7;
  1         1  
  1         55  
83 1     1   5 use constant MEDIUM_BOLD_WEIGHT => 'bold';
  1         2  
  1         45  
84 1     1   21 use constant LARGE_HEIGHT => 16;
  1         1  
  1         65  
85 1     1   7 use constant LARGE_WIDTH => 8;
  1         1  
  1         35  
86 1     1   4 use constant LARGE_WEIGHT => 'normal';
  1         2  
  1         47  
87 1     1   5 use constant GIANT_HEIGHT => 15;
  1         2  
  1         45  
88 1     1   4 use constant GIANT_WIDTH => 8;
  1         2  
  1         38  
89 1     1   5 use constant GIANT_WEIGHT => 'bold';
  1         1  
  1         41  
90              
91             # TEXT_KLUDGE controls the number of pixels to bump text on the
92             # Y-axis in order to more closely match GD output.
93 1     1   4 use constant TEXT_KLUDGE => '2';
  1         2  
  1         698  
94              
95             #########################
96             # END CONSTANTS - No user serviceable options below this point
97             #########################
98              
99             # Trap GD methods that are not yet implemented in SVG.pm
100             sub AUTOLOAD {
101 0     0   0 my $self = shift;
102 0 0 0     0 warn "GD method $AUTOLOAD is not implemented in GD::SVG" if ref $self && $self->{debug} > 0;
103             }
104              
105             ##################################################
106             # Exported methods that belong in Main namespace #
107             ##################################################
108              
109             # In GD, the gdStyled method allows one to draw with a styled line
110             # Here we will simply return the format of the line along with a flag
111             # so that appropriate subroutines can deal with it.
112              
113             # Similarly, the gdTransparent method lets users introduce gaps in
114             # lines. I'll handle it similarly to gdStyled...
115             # This might just be as simple as setting the color to the background color.
116             # (This, of course, will not work for styled lines).
117 0     0   0 sub gdStyled { return 'gdStyled'; }
118 0     0   0 sub gdBrushed { return 'gdBrushed'; }
119 0     0   0 sub gdTransparent { return 'gdTransparent'; }
120              
121 0     0   0 sub gdStyledBrush { _error('gdStyledBrush'); }
122 0     0   0 sub gdTiled { _error('gdTiled'); }
123 0     0   0 sub gdDashSize { _error('gdDashSize'); }
124 0     0   0 sub gdMaxColors { _error('gdMaxColors'); }
125              
126             # Bitwise operations for filledArcs
127 0     0   0 sub gdArc { return 0; }
128 0     0   0 sub gdPie { return 0; }
129 0     0   0 sub gdChord { return 1; }
130 0     0   0 sub gdEdged { return 4; }
131 0     0   0 sub gdNoFill { return 2; }
132              
133 0     0   0 sub gdAntiAliased { _error('gdAntiAliased'); }
134 0     0   0 sub setAntiAliased { shift->_error('setAntiAliased'); }
135 0     0   0 sub setAntiAliasedDontBlend { shift->_error('setAntiAliasedDontBlend'); }
136              
137             ################################
138             # Font Factories and Utilities
139             ################################
140             sub gdTinyFont {
141 0     0   0 my $this = bless {},'GD::SVG::Font';
142 0         0 $this->{font} = DEFAULT_FONT;
143 0         0 $this->{height} = TINY_HEIGHT;
144 0         0 $this->{width} = TINY_WIDTH;
145 0         0 $this->{weight} = TINY_WEIGHT;
146 0         0 return $this;
147             }
148              
149             sub gdSmallFont {
150 0     0   0 my $this = bless {},'GD::SVG::Font';
151 0         0 $this->{font} = DEFAULT_FONT;
152 0         0 $this->{height} = SMALL_HEIGHT;
153 0         0 $this->{width} = SMALL_WIDTH;
154 0         0 $this->{weight} = SMALL_WEIGHT;
155 0         0 return $this;
156             }
157              
158             sub gdMediumBoldFont {
159 0     0   0 my $this = bless {},'GD::SVG::Font';
160 0         0 $this->{font} = DEFAULT_FONT;
161 0         0 $this->{height} = MEDIUM_BOLD_HEIGHT;
162 0         0 $this->{width} = MEDIUM_BOLD_WIDTH;
163 0         0 $this->{weight} = MEDIUM_BOLD_WEIGHT;
164 0         0 return $this;
165             }
166              
167             sub gdLargeFont {
168 0     0   0 my $this = bless {},'GD::SVG::Font';
169 0         0 $this->{font} = DEFAULT_FONT;
170 0         0 $this->{height} = LARGE_HEIGHT;
171 0         0 $this->{width} = LARGE_WIDTH;
172 0         0 $this->{weight} = LARGE_WEIGHT;
173 0         0 return $this;
174             }
175              
176             sub gdGiantFont {
177 0     0   0 my $this = bless {},'GD::SVG::Font';
178 0         0 $this->{font} = DEFAULT_FONT;
179 0         0 $this->{height} = GIANT_HEIGHT;
180 0         0 $this->{width} = GIANT_WIDTH;
181 0         0 $this->{weight} = GIANT_WEIGHT;
182 0         0 return $this;
183             }
184              
185             # Don't break stuff!
186             # The can() method is not supported in GD::SVG
187             # sub can { return 0; }
188              
189              
190             package GD::SVG::Image;
191 1     1   6 use Carp 'croak','carp','confess';
  1         7  
  1         5524  
192              
193             # There must be a better way to trap these errors
194             sub _error {
195 0     0   0 my ($self,$method) = @_;
196 0 0       0 warn "GD method $method is not implemented in GD::SVG" if ($self->{debug} > 0);
197             }
198              
199              
200             #########################
201             # GD Constants
202             #########################
203             # Kludge - use precalculated values of cos(theta) and sin(theta)
204             # so that I do no have to examine quadrants
205              
206             my @cosT = (qw/1024 1023 1023 1022 1021 1020 1018 1016 1014 1011 1008
207             1005 1001 997 993 989 984 979 973 968 962 955 949 942 935 928 920 912
208             904 895 886 877 868 858 848 838 828 817 806 795 784 772 760 748 736
209             724 711 698 685 671 658 644 630 616 601 587 572 557 542 527 512 496
210             480 464 448 432 416 400 383 366 350 333 316 299 282 265 247 230 212
211             195 177 160 142 124 107 89 71 53 35 17 0 -17 -35 -53 -71 -89 -107 -124
212             -142 -160 -177 -195 -212 -230 -247 -265 -282 -299 -316 -333 -350 -366
213             -383 -400 -416 -432 -448 -464 -480 -496 -512 -527 -542 -557 -572 -587
214             -601 -616 -630 -644 -658 -671 -685 -698 -711 -724 -736 -748 -760 -772
215             -784 -795 -806 -817 -828 -838 -848 -858 -868 -877 -886 -895 -904 -912
216             -920 -928 -935 -942 -949 -955 -962 -968 -973 -979 -984 -989 -993 -997
217             -1001 -1005 -1008 -1011 -1014 -1016 -1018 -1020 -1021 -1022 -1023
218             -1023 -1024 -1023 -1023 -1022 -1021 -1020 -1018 -1016 -1014 -1011
219             -1008 -1005 -1001 -997 -993 -989 -984 -979 -973 -968 -962 -955 -949
220             -942 -935 -928 -920 -912 -904 -895 -886 -877 -868 -858 -848 -838 -828
221             -817 -806 -795 -784 -772 -760 -748 -736 -724 -711 -698 -685 -671 -658
222             -644 -630 -616 -601 -587 -572 -557 -542 -527 -512 -496 -480 -464 -448
223             -432 -416 -400 -383 -366 -350 -333 -316 -299 -282 -265 -247 -230 -212
224             -195 -177 -160 -142 -124 -107 -89 -71 -53 -35 -17 0 17 35 53 71 89 107
225             124 142 160 177 195 212 230 247 265 282 299 316 333 350 366 383 400
226             416 432 448 464 480 496 512 527 542 557 572 587 601 616 630 644 658
227             671 685 698 711 724 736 748 760 772 784 795 806 817 828 838 848 858
228             868 877 886 895 904 912 920 928 935 942 949 955 962 968 973 979 984
229             989 993 997 1001 1005 1008 1011 1014 1016 1018 1020 1021 1022 1023
230             1023/);
231              
232             my @sinT = (qw/0 17 35 53 71 89 107 124 142 160 177 195 212 230 247
233             265 282 299 316 333 350 366 383 400 416 432 448 464 480 496 512 527
234             542 557 572 587 601 616 630 644 658 671 685 698 711 724 736 748 760
235             772 784 795 806 817 828 838 848 858 868 877 886 895 904 912 920 928
236             935 942 949 955 962 968 973 979 984 989 993 997 1001 1005 1008 1011
237             1014 1016 1018 1020 1021 1022 1023 1023 1024 1023 1023 1022 1021 1020
238             1018 1016 1014 1011 1008 1005 1001 997 993 989 984 979 973 968 962 955
239             949 942 935 928 920 912 904 895 886 877 868 858 848 838 828 817 806
240             795 784 772 760 748 736 724 711 698 685 671 658 644 630 616 601 587
241             572 557 542 527 512 496 480 464 448 432 416 400 383 366 350 333 316
242             299 282 265 247 230 212 195 177 160 142 124 107 89 71 53 35 17 0 -17
243             -35 -53 -71 -89 -107 -124 -142 -160 -177 -195 -212 -230 -247 -265 -282
244             -299 -316 -333 -350 -366 -383 -400 -416 -432 -448 -464 -480 -496 -512
245             -527 -542 -557 -572 -587 -601 -616 -630 -644 -658 -671 -685 -698 -711
246             -724 -736 -748 -760 -772 -784 -795 -806 -817 -828 -838 -848 -858 -868
247             -877 -886 -895 -904 -912 -920 -928 -935 -942 -949 -955 -962 -968 -973
248             -979 -984 -989 -993 -997 -1001 -1005 -1008 -1011 -1014 -1016 -1018
249             -1020 -1021 -1022 -1023 -1023 -1024 -1023 -1023 -1022 -1021 -1020
250             -1018 -1016 -1014 -1011 -1008 -1005 -1001 -997 -993 -989 -984 -979
251             -973 -968 -962 -955 -949 -942 -935 -928 -920 -912 -904 -895 -886 -877
252             -868 -858 -848 -838 -828 -817 -806 -795 -784 -772 -760 -748 -736 -724
253             -711 -698 -685 -671 -658 -644 -630 -616 -601 -587 -572 -557 -542 -527
254             -512 -496 -480 -464 -448 -432 -416 -400 -383 -366 -350 -333 -316 -299
255             -282 -265 -247 -230 -212 -195 -177 -160 -142 -124 -107 -89 -71 -53 -35
256             -17 /);
257              
258              
259             #############################
260             # GD::SVG::Image methods
261             #############################
262             sub new {
263 1     1   14 my ($self,$width,$height,$debug) = @_;
264 1         3 my $this = bless {},$self;
265 1         11 my $img = SVG->new(width=>$width,height=>$height);
266 1         361 $this->{img} = [$img];
267 1         2 $this->{width} = $width;
268 1         3 $this->{height} = $height;
269              
270             # Let's create an internal representation of the image in GD
271             # so that I can easily use some of GD's methods
272             ###GD###$this->{gd} = GD::Image->new($width,$height);
273              
274             # Let's just assume that we always want the foreground color to be
275             # black This, for the most part, works for Bio::Graphics. This
276             # certainly needs to be fixed...
277 1         5 $this->{foreground} = $this->colorAllocate(0,0,0);
278 1 50       6 $this->{debug} = ($debug) ? $debug : GD::SVG::DEBUG;
279 1         3 return $this;
280             }
281              
282             sub img {
283 0     0   0 my $this = shift;
284 0         0 return $this->{img}[0];
285             }
286              
287             sub currentGroup {
288 0     0   0 my $this = shift;
289 0 0       0 $this->{currentGroup} = shift if @_;
290 0   0     0 return $this->{currentGroup} || $this->{img}[-1];
291 0         0 return $this->{img}[-1];
292             }
293              
294             sub closeAllGroups {
295 0     0   0 my $this = shift;
296 0         0 while (@{$this->{img}}>1) {
  0         0  
297 0         0 pop @{$this->{img}};
  0         0  
298             }
299             }
300              
301              
302             #############################
303             # Image Data Output Methods #
304             #############################
305             sub svg {
306 0     0   0 my $self = shift;
307 0         0 $self->closeAllGroups;
308 0         0 my $img = $self->img;
309 0         0 $img->xmlify(-pubid => "-//W3C//DTD SVG 1.0//EN",
310             -inline => 1);
311             }
312              
313             ###GD###sub png {
314             ###GD### my ($self,$compression) = @_;
315             ###GD### return $self->{gd}->png($compression);
316             ###GD###}
317              
318             ###GD###sub jpeg {
319             ###GD### my ($self,$quality) = @_;
320             ###GD### return $self->{gd}->jpeg($quality);
321             ###GD###}
322              
323             #############################
324             # Color management routines #
325             #############################
326             # As with GD, colorAllocate returns integers...
327             # This could easily rely on GD itself to generate the indices
328             sub colorAllocate {
329 2     2   8 my ($self,$r,$g,$b,$alpha) = @_;
330 2   50     10 $r ||= 0;
331 2   50     9 $g ||= 0;
332 2   50     7 $b ||= 0;
333 2   50     9 $alpha ||= 0;
334              
335             ###GD###my $newindex = $self->{gd}->colorAllocate($r,$g,$b);
336              
337             # Cannot use the numberof keys to generate index
338             # colorDeallocate removes keys.
339             # Instead use the colors_added array.
340 2 100       6 my $new_index = (defined $self->{colors_added}) ? scalar @{$self->{colors_added}} : 0;
  1         2  
341 2         9 $self->{colors}->{$new_index} = [$r,$g,$b,$alpha];
342              
343             # Keep a list of colors in the order that they are added
344             # This is used as a kludge for setBrush
345 2         3 push (@{$self->{colors_added}},$new_index);
  2         5  
346 2         5 return $new_index;
347             }
348              
349             sub colorAllocateAlpha {
350 0     0     my $self = shift;
351             ###GD###$self->{gd}->colorAllocateAlpha($r,$g,$b,$alpha);
352 0           $self->colorAllocate(@_);
353             }
354              
355             sub colorDeallocate {
356 0     0     my ($self,$index) = @_;
357 0           my $colors = %{$self->{colors}};
  0            
358 0           delete $colors->{$index};
359             ###GD###$self->{gd}->colorDeallocate($index);
360             }
361              
362             # workaround for bad GD
363             sub colorClosest {
364 0     0     my ($self,@c) = @_;
365             ###GD###my $index = $self->{gd}->colorClosest(@c);
366              
367             # Let's just return the color for now.
368             # Terrible kludge.
369 0           my $index = $self->colorAllocate(@c);
370 0           return $index;
371             # my ($self,$gd,@c) = @_;
372             # return $self->{closestcache}{"@c"} if exists $self->{closestcache}{"@c"};
373             # return $self->{closestcache}{"@c"} = $gd->colorClosest(@c) if $GD::VERSION < 2.04;
374             # my ($value,$index);
375             # for (keys %COLORS) {
376             # my ($r,$g,$b) = @{$COLORS{$_}};
377             # my $dist = ($r-$c[0])**2 + ($g-$c[1])**2 + ($b-$c[2])**2;
378             # ($value,$index) = ($dist,$_) if !defined($value) || $dist < $value;
379             # }
380             # return $self->{closestcache}{"@c"} = $self->{translations}{$index};
381             }
382              
383 0     0     sub colorClosestHWB { shift->_error('colorClosestHWB'); }
384              
385             sub colorExact {
386 0     0     my ($self,$r,$g,$b) = @_;
387             ###GD###my $index = $self->{gd}->colorExact($r,$g,$b);
388            
389             # Let's just allocate the color instead of looking it up
390 0           my $index = $self->colorAllocate($r,$g,$b);
391 0 0         if ($index) {
392 0           return $index;
393             } else {
394 0           return ('-1');
395             }
396             }
397              
398             sub colorResolve {
399 0     0     my ($self,$r,$g,$b) = @_;
400             ###GD###my $index = $self->{gd}->colorResolve($r,$g,$b);
401 0           my $index = $self->colorAllocate($r,$g,$b);
402 0           return $index;
403             }
404              
405             sub colorsTotal {
406 0     0     my $self = shift;
407             ###GD###return $self->{gd}->colorsTotal;
408 0           return scalar keys %{$self->{colors}};
  0            
409             }
410              
411              
412             sub getPixel {
413 0     0     my ($self,$x,$y) = @_;
414             # Internal GD - probably unnecessary in this context...
415             # Will contstruct appropriate return value later
416             ###GD### $self->{gd}->getPixel($x,$y);
417            
418             # I don't have any cogent way to fetch the value of an asigned pixel
419             # without calculating all positions and loading into memory.
420            
421             # For these purposes, I could maybe just look it up... From a hash
422             # table or something - Keep track of all assigned pixels and their
423             # color. Ugh. Compute intensive.
424 0           return (1);
425             }
426              
427             # Given the color index, return its rgb triplet
428             sub rgb {
429 0     0     my ($self,$index) = @_;
430 0           my ($r,$g,$b) = @{$self->{colors}->{$index}};
  0            
431 0           return ($r,$g,$b);
432             }
433              
434 0     0     sub transparent { shift->_error('transparent'); }
435              
436              
437             #######################
438             # Special Colors
439             #######################
440             # Kludgy preliminary support for gdBrushed This is based on
441             # Bio::Graphics implementation of set_pen which in essence just
442             # controls line color and thickness... We will assume that the last
443             # color added is intended to be the foreground color.
444             sub setBrush {
445 0     0     my ($self,$pen) = @_;
446             ###GD###$self->{gd}->setBrush($pen);
447 0           my ($width,$height) = $pen->getBounds();
448 0           my $last_color = $pen->{colors_added}->[-1];
449 0           my ($r,$g,$b) = $self->rgb($last_color);
450 0           $self->{gdBrushed}->{color} = $self->colorAllocate($r,$g,$b);
451 0           $self->{gdBrushed}->{thickness} = $width;
452             }
453              
454             # There is no direct translation of gdStyled. In gd, this is used to
455             # set the style for the line using the settings of the current brush.
456             # Drawing with the new style is then used by passing the gdStyled as a
457             # color.
458             sub setStyle {
459 0     0     my ($self,@colors) = @_;
460             ###GD###$self->{gd}->setStyle(@colors);
461 0           $self->{gdStyled}->{color} = [ @colors ];
462 0           return;
463             }
464              
465             # Lines in GD are 1 pixel in diameter by default.
466             # setThickness allows line thickness to be changed.
467             # This should be retained until it's changed again
468             # Each method should check the thickness of the line...
469             sub setThickness {
470 0     0     my ($self,$thickness) = @_;
471             ###GD### $self->{gd}->setThickness($thickness);
472 0           $self->{line_thickness} = $thickness;
473             # WRONG!
474             # $self->{prev_line_thickness} = (!defined $self->{prev_line_thickness}) ? $thickness : undef;
475             }
476              
477              
478             ########################
479             # Grouping subroutines #
480             ########################
481             sub startGroup {
482 0     0     my $this = shift;
483 0           my $id = shift;
484 0           my $style = shift;
485              
486 0           my @args;
487 0 0         push @args,(id => $id) if defined $id;
488 0 0         push @args,(style => $style) if defined $style;
489              
490 0           my $group = $this->currentGroup->group(@args);
491 0           push @{$this->{img}},$group;
  0            
492 0           return $group;
493             }
494             sub endGroup {
495 0     0     my $this = shift;
496 0           my $group = shift;
497              
498 0 0         if ($group) {
  0 0          
499 0           my @imgs = grep {$_ ne $group} @{$this->{img}};
  0            
  0            
500 0           $this->{img} = \@imgs;
501             }
502             elsif (@{$this->{img}}>1) {
503 0           pop @{$this->{img}};
  0            
504             }
505 0           delete $this->{currentGroup};
506             }
507             sub newGroup {
508 0     0     my $this = shift;
509 0           my $group = $this->startGroup(@_);
510 0 0         eval "require GD::Group" unless GD::Group->can('new');
511 0           return GD::Group->new($this,$group);
512             }
513              
514             #######################
515             # Drawing subroutines #
516             #######################
517             sub setPixel {
518 0     0     my ($self,$x1,$y1,$color_index) = @_;
519             ###GD### $self->{gd}->setPixel($x1,$y1,$color_index);
520 0           my ($img,$id,$thickness,$dasharray) = $self->_prep($x1,$y1);
521 0           my $color = $self->_get_color($color_index);
522 0           my $result =
523             $img->circle(cx=>$x1,cy=>$y1,r=>'0.03',
524             id=>$id,
525             style=>{
526             'stroke'=>$color,
527             'fill' =>$color,
528             'fill-opacity'=>'1.0'
529             }
530             );
531 0           return $result;
532             }
533              
534             sub line {
535 0     0     my ($self,$x1,$y1,$x2,$y2,$color_index) = @_;
536             # Are we trying to draw with a styled line (ie gdStyled, gdBrushed?)
537             # If so, we need to deconstruct the values for line thickness,
538             # foreground color, and dash spacing
539 0 0 0       if ($color_index eq 'gdStyled' || $color_index eq 'gdBrushed') {
540 0           my $fg = $self->_distill_gdSpecial($color_index);
541 0           $self->line($x1,$y1,$x2,$y2,$fg);
542             } else {
543             ###GD### $self->{gd}->line($x1,$y1,$x2,$y2,$color_index);
544 0           my ($img,$id) = $self->_prep($x1,$y1);
545 0           my $style = $self->_build_style($id,$color_index,$color_index);
546            
547             # Suggested patch by Jettero to fix lines
548             # that don't go to the ends of their length.
549             # This could possibly be relocated to _build_style
550             # but I'm unsure of the ramifications on other features.
551 0           $style->{'stroke-linecap'} = 'square';
552 0           my $result = $img->line(x1=>$x1,y1=>$y1,
553             x2=>$x2,y2=>$y2,
554             id=>$id,
555             style => $style,
556             );
557 0           $self->_reset();
558 0           return $result;
559             }
560             }
561              
562 0     0     sub dashedLine { shift->_error('dashedLine'); }
563              
564             # The fill parameter is used internally as a simplification...
565             sub rectangle {
566 0     0     my ($self,$x1,$y1,$x2,$y2,$color_index,$fill) = @_;
567 0 0 0       if ($color_index eq 'gdStyled' || $color_index eq 'gdBrushed') {
568 0           my $fg = $self->_distill_gdSpecial($color_index);
569 0           $self->rectangle($x1,$y1,$x2,$y2,$fg,$fill);
570             } else {
571             ###GD###$self->{gd}->rectangle($x1,$y1,$x2,$y2,$color_index);
572 0           my ($img,$id) = $self->_prep($x1,$y1);
573 0           my $style = $self->_build_style($id,$color_index,$fill);
574              
575             # flip coordinates if they are "backwards"
576 0 0         ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
577 0 0         ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
578 0           my $result =
579             $img->rectangle(x=>$x1,y=>$y1,
580             width =>$x2-$x1,
581             height =>$y2-$y1,
582             id =>$id,
583             style => $style,
584             );
585 0           $self->_reset();
586 0           return $result;
587             }
588             }
589              
590             # This should just call the rectangle method passing it a flag.
591             # I will need to fix the glyph that bypasses this option...
592             sub filledRectangle {
593 0     0     my ($self,$x1,$y1,$x2,$y2,$color) = @_;
594             # Call the rectangle method passing the fill color
595 0           $self->rectangle($x1,$y1,$x2,$y2,$color,$color);
596             }
597              
598             sub polygon {
599 0     0     my ($self,$poly,$color,$fill) = @_;
600 0           $self->_polygon($poly,$color,$fill,1);
601             }
602              
603             sub polyline {
604 0     0     my ($self,$poly,$color,$fill) = @_;
605 0           $self->_polygon($poly,$color,$fill,0);
606             }
607              
608             sub polydraw {
609 0     0     my $self = shift; # the GD::Image
610 0           my $p = shift; # the GD::Polyline or GD::Polygon
611 0           my $c = shift; # the color
612 0 0         return $self->polyline($p, $c) if $p->isa('GD::Polyline');
613 0           return $self->polygon($p, $c);
614             }
615              
616             sub _polygon {
617 0     0     my ($self,$poly,$color_index,$fill,$close) = @_;
618 0 0         my $shape = $close ? 'polygon' : 'polyline';
619 0 0 0       if ($color_index eq 'gdStyled' || $color_index eq 'gdBrushed') {
620 0           my $fg = $self->_distill_gdSpecial($color_index);
621 0           $self->$shape($poly,$fg);
622             } else {
623             ###GD###$self->{gd}->polygon($poly,$color);
624             # Create seperate x and y arrays of vertices
625 0           my (@xpoints,@ypoints);
626 0 0         if ($poly->can('_fetch_vertices')) {
627 0           @xpoints = $poly->_fetch_vertices('x');
628 0           @ypoints = $poly->_fetch_vertices('y');
629             } else {
630 0           my @points = $poly->vertices;
631 0           @xpoints = map { $_->[0] } @points;
  0            
632 0           @ypoints = map { $_->[1] } @points;
  0            
633             }
634 0           my ($img,$id) = $self->_prep($xpoints[0],$ypoints[0]);
635 0           my $points = $img->get_path(
636             x=>\@xpoints, y=>\@ypoints,
637             -type=>$shape,
638             );
639 0           my $style = $self->_build_style($id,$color_index,$fill);
640 0           my $result =
641             $img->$shape(
642             %$points,
643             id=>$id,
644             style => $style,
645             );
646 0           $self->_reset();
647 0           return $result;
648             }
649             }
650              
651             # Passing the stroke doesn't really work as expected...
652             sub filledPolygon {
653 0     0     my ($self,$poly,$color) = @_;
654 0           my $result = $self->polygon($poly,$color,$color);
655 0           return $result;
656             }
657              
658             sub ellipse {
659 0     0     my ($self,$x1,$y1,$width,$height,$color_index,$fill) = @_;
660 0 0 0       if ($color_index eq 'gdStyled' || $color_index eq 'gdBrushed') {
661 0           my $fg = $self->_distill_gdSpecial($color_index);
662 0           $self->ellipse($x1,$y1,$width,$height,$fg);
663             } else {
664             ###GD### $self->{gd}->ellipse($x1,$y1,$width,$height,$color_index);
665              
666 0           my ($img,$id) = $self->_prep($x1,$y1);
667             # GD uses width and height - SVG uses radii...
668 0           $width = $width / 2;
669 0           $height = $height / 2;
670 0           my $style = $self->_build_style($id,$color_index,$fill);
671 0           my $result =
672             $img->ellipse(
673             cx=>$x1, cy=>$y1,
674             rx=>$width, ry=>$height,
675             id=>$id,
676             style => $style,
677             );
678 0           $self->_reset();
679 0           return $result;
680             }
681             }
682              
683             sub filledEllipse {
684 0     0     my ($self,$x1,$y1,$width,$height,$color) = @_;
685 0           my $result = $self->ellipse($x1,$y1,$width,$height,$color,$color);
686 0           return $result;
687             }
688              
689             # GD uses the arc() and filledArc() methods in two capacities
690             # 1. to create closed ellipses, where start and end are 0 and 360
691             # 2. to create honest-to-god open arcs
692             # The arc method is no longer being used to draw filledArcs.
693             # All the fill-specific code within is no deprecated.
694             sub arc {
695 0     0     my ($self,$cx,$cy,$width,$height,$start,$end,$color_index,$fill) = @_;
696 0 0 0       if ($color_index eq 'gdStyled' || $color_index eq 'gdBrushed') {
697 0           my $fg = $self->_distill_gdSpecial($color_index);
698 0           $self->arc($cx,$cy,$width,$height,$start,$end,$fg);
699             } else {
700             ###GD### $self->{gd}->arc($x,$y,$width,$height,$start,$end,$color);
701             # Are we just trying to draw a closed arc (an ellipse)?
702 0           my $result;
703 0 0 0       if ($start == 0 && $end == 360 || $end == 360 && $start == 0) {
      0        
      0        
704 0           $result = $self->ellipse($cx,$cy,$width,$height,$color_index,$fill);
705             } else {
706 0           my ($img,$id) = $self->_prep($cy,$cx);
707              
708             # Taking a stab at drawing elliptical arcs
709 0           my ($start,$end,$large,$sweep,$a,$b) = _calculate_arc_params($start,$end,$width,$height);
710 0           my ($startx,$starty) = _calculate_point_coords($cx,$cy,$width,$height,$start);
711 0           my ($endx,$endy) = _calculate_point_coords($cx,$cy,$width,$height,$end);
712              
713             # M = move to (origin of the curve)
714             # my $rotation = abs $start - $end;
715 0           my $style = $self->_build_style($id,$color_index,$fill);
716 0           $result =
717             $img->path('d'=>"M$startx,$starty " .
718             "A$a,$b 0 $large,$sweep $endx,$endy",
719             style => $style,
720             );
721             }
722 0           $self->_reset();
723 0           return $result;
724             }
725             }
726              
727             # Return the x and y positions of start and stop of arcs.
728             sub _calculate_point_coords {
729 0     0     my ($cx,$cy,$width,$height,$angle) = @_;
730 0           my $x = ( $cosT[$angle % 360] * $width) / (2 * 1024) + $cx;
731 0           my $y = ( $sinT[$angle % 360] * $height) / (2 * 1024) + $cy;
732 0           return ($x,$y);
733             }
734              
735             sub _calculate_arc_params {
736 0     0     my ($start,$end,$width,$height) = @_;
737              
738             # GD uses diameters, SVG uses radii
739 0           my $a = $width / 2;
740 0           my $b = $height / 2;
741            
742 0           while ($start < 0 ) { $start += 360; }
  0            
743 0           while ($end < 0 ) { $end += 360; }
  0            
744 0           while ($end < $start ) { $end += 360; }
  0            
745              
746 0 0         my $large = (abs $start - $end > 180) ? 1 : 0;
747             # my $sweep = ($start > $end) ? 0 : 1; # directionality of the arc, + CW, - CCW
748 0           my $sweep = 1; # Always CW with GD
749 0           return ($start,$end,$large,$sweep,$a,$b);
750             }
751              
752             sub filledArc {
753 0     0     my ($self,$cx,$cy,$width,$height,$start,$end,$color_index,$fill_style) = @_;
754 0 0 0       if ($color_index eq 'gdStyled' || $color_index eq 'gdBrushed') {
755 0           my $fg = $self->_distill_gdSpecial($color_index);
756 0           $self->filledArc($cx,$cy,$width,$height,$start,$end,$fg);
757             } else {
758             ###GD### $self->{gd}->arc($x,$y,$width,$height,$start,$end,$color_index);
759 0           my $result;
760              
761             # distill the special colors, if provided...
762             my $fill_color;
763             # Set it to gdArc, the default value to avoid undef errors in comparisons
764 0   0       $fill_style ||= 0;
765 0 0 0       if ($fill_style == 2 || $fill_style == 4 || $fill_style == 6) {
      0        
766 0           $fill_color = 'none';
767             } else {
768 0           $fill_color = $self->_get_color($color_index);
769             }
770              
771             # Are we just trying to draw a closed filled arc (an ellipse)?
772 0 0 0       if (($start == 0 && $end == 360) || ($start == 360 && $end == 0)) {
    0 0        
      0        
      0        
      0        
773 0           $result = $self->ellipse($cx,$cy,$width,$height,$color_index,$fill_color);
774             }
775              
776             # are we trying to draw a pie?
777             elsif ($end - $start > 180 && ($fill_style == 0 || $fill_style == 4)) {
778 0           $self->filledArc($cx,$cy,$width,$height,$start,$start+180,$color_index,$fill_style);
779             # $self->filledArc($cx,$cy,$width,$height,$start+180,$end,$color_index,$fill_style);
780 0           $result = $self->filledArc($cx,$cy,$width,$height,$start+180,$end,$color_index,$fill_style);
781             }
782              
783             else {
784 0           my ($img,$id) = $self->_prep($cy,$cx);
785              
786 0           my ($start,$end,$large,$sweep,$a,$b) = _calculate_arc_params($start,$end,$width,$height);
787 0           my ($startx,$starty) = _calculate_point_coords($cx,$cy,$width,$height,$start);
788 0           my ($endx,$endy) = _calculate_point_coords($cx,$cy,$width,$height,$end);
789              
790             # Evaluate the various fill styles
791             # gdEdged connects the center to the start and end
792 0 0 0       if ($fill_style == 4 || $fill_style == 6) {
793 0           $self->line($cx,$cy,$startx,$starty,$color_index);
794 0           $self->line($cx,$cy,$endx,$endy,$color_index);
795             }
796              
797             # gdNoFill outlines portions of the arc
798             # noFill or gdArc|gdNoFill
799 0 0 0       if ($fill_style == 2 || $fill_style == 6) {
800 0           $result = $self->arc($cx,$cy,$width,$height,$start,$end,$color_index);
801 0           return $result;
802             }
803              
804             # gdChord|gdNofFill
805 0 0         if ($fill_style == 3) {
806 0           $result = $self->line($startx,$starty,$endx,$endy,$color_index);
807 0           return $result;
808             }
809              
810             # Create the actual filled portion of the arc
811             # This is the default behavior for gdArc and if no style is passed.
812 0 0 0       if ($fill_style == 0 || $fill_style == 4) {
813             # M = move to (origin of the curve)
814             # my $rotation = abs $start - $end;
815 0           my $style = $self->_build_style($id,$color_index,$fill_color);
816 0           $result =
817             $img->path('d'=>"M$startx,$starty " .
818             "A$a,$b 0 $large,$sweep $endx,$endy",
819             style => $style,
820             );
821             }
822              
823             # If we are filling, draw a filled triangle to complete.
824             # This is also the same as using gdChord by itself
825 0           my $poly = GD::SVG::Polygon->new();
826 0           $poly->addPt($cx,$cy);
827 0           $poly->addPt($startx,$starty);
828 0           $poly->addPt($endx,$endy);
829 0           $self->filledPolygon($poly,$color_index);
830             }
831              
832 0           $self->_reset();
833 0           return $result;
834             }
835             }
836              
837             # Flood fill that stops at first pixel of a different color.
838 0     0     sub fill { shift->_error('fill'); }
839 0     0     sub fillToBorder { shift->_error('fillToBorder'); }
840              
841             ##################################################
842             # Image Copying Methods
843             ##################################################
844              
845             # Taking a stab at implementing the copy() methods
846             # Should be relatively easy to implement clone() from this
847             sub copy {
848 0     0     my $self = shift;
849 0           my ($source,$dstx,$dsty,$srcx,$srcy,$width,$height) = @_;
850              
851             # special case -- if we have been asked to copy a
852             # GD::Image into us, then we embed an image with the
853             # data:url
854 0 0 0       if ($source->isa('GD::Image') || $source->isa('GD::Simple')) {
855 0           return $self->_copy_image(@_);
856             }
857              
858 0           my $topx = $srcx;
859 0           my $topy = $srcy;
860 0           my $bottomx = $srcx + $width; # arithmetic right here?
861 0           my $bottomy = $srcy + $height;
862              
863             # Fetch all elements of the source image
864 0           my @elements = $source->img->getElements;
865 0           foreach my $element (@elements) {
866 0           my $att = $element->getAttributes();
867             # Points|rectangles|text, circles|ellipses, lines
868 0   0       my $x = $att->{x} || $att->{cx} || $att->{x1};
869 0   0       my $y = $att->{y} || $att->{cy} || $att->{y1};
870              
871             # Use the first point for polygons
872 0 0 0       unless ($x && $y) {
873 0           my @points = split(/\s/,$att->{points});
874 0 0         if (@points) {
875 0           ($x,$y) = split(',',$points[0]);
876             }
877             }
878              
879             # Paths
880 0 0 0       unless ($x && $y) {
881 0           my @d = split(/\s/,$att->{d});
882 0 0         if (@d) {
883 0           ($x,$y) = split(',',$d[0]);
884 0           $x =~ s/^M//; # Remove the style directive
885             }
886             }
887              
888             # Are the starting coords within the bounds of the desired rectangle?
889             # We will simplistically assume that the entire glyph fits inside
890             # the rectangle which may not be true.
891 0 0 0       if (($x >= $topx && $y >= $topy) &&
      0        
      0        
892             ($x <= $bottomx && $y <= $bottomy)) {
893 0           my $type = $element->getType;
894             # warn "$type $x $y $bottomx $bottomy $topx $topy";
895              
896             # Transform the coordinates as necessary,
897             # calculating the offsets relative to the
898             # original bounding rectangle in the source image
899              
900             # Text or rectangles
901 0 0 0       if ($type eq 'text' || $type eq 'rect') {
    0 0        
    0          
    0          
    0          
902 0           my ($newx,$newy) = _transform_coords($topx,$topy,$x,$y,$dstx,$dsty);
903 0           $element->setAttribute('x',$newx);
904 0           $element->setAttribute('y',$newy);
905             # Circles or ellipses
906             } elsif ($type eq 'circle' || $type eq 'ellipse') {
907 0           my ($newx,$newy) = _transform_coords($topx,$topy,$x,$y,$dstx,$dsty);
908 0           $element->setAttribute('cx',$newx);
909 0           $element->setAttribute('cy',$newy);
910             # Lines
911             } elsif ($type eq 'line') {
912 0           my ($newx1,$newy1) = _transform_coords($topx,$topy,$x,$y,$dstx,$dsty);
913 0           my ($newx2,$newy2) = _transform_coords($topx,$topy,$att->{x2},$element->{y2},$dstx,$dsty);
914 0           $element->setAttribute('x1',$newx1);
915 0           $element->setAttribute('y1',$newy1);
916 0           $element->setAttribute('x2',$newx2);
917 0           $element->setAttribute('y2',$newy2);
918             # Polygons
919             } elsif ($type eq 'polygon') {
920 0           my @points = split(/\s/,$att->{points});
921 0           my @transformed;
922 0           foreach (@points) {
923 0           ($x,$y) = split(',',$_);
924 0           my ($newx,$newy) = _transform_coords($topx,$topy,$x,$y,$dstx,$dsty);
925 0           push (@transformed,"$newx,$newy");
926             }
927 0           my $transformed = join(" ",@transformed);
928 0           $element->setAttribute('points',$transformed);
929             # Paths
930             } elsif ($type eq 'path') {
931            
932             }
933              
934             # Create new elements for the destination image
935             # via the generic SVG::Element::tag method
936 0           my %attributes = $element->getAttributes;
937 0           $self->img->tag($type,%attributes);
938             }
939             }
940             }
941              
942             # Used internally by the copy method
943             # Transform coordinates of a given point with reference
944             # to a bounding rectangle
945             sub _transform_coords {
946 0     0     my ($refx,$refy,$x,$y,$dstx,$dsty) = @_;
947 0           my $xoffset = $x - $refx;
948 0           my $yoffset = $y - $refy;
949 0           my $newx = $dstx + $xoffset;
950 0           my $newy = $dsty + $yoffset;
951 0           return ($newx,$newy);
952             }
953              
954             sub _copy_image {
955 0     0     my $self = shift;
956 0           my ($source,$dstx,$dsty,$srcx,$srcy,$width,$height) = @_;
957              
958 0 0         eval "use MIME::Base64; 1"
959             or croak "The MIME::Base64 module is required to copy a GD::Image into a GD::SVG: $@";
960              
961 0           my $subimage = GD::Image->new($width,$height); # will be loaded
962 0 0         $subimage->copy($source->isa('GD::Simple') ? $source->gd : $source,
963             0,0,
964             $srcx,$srcy,
965             $width,$height);
966              
967 0           my $data = encode_base64($subimage->png);
968 0           my ($img,$id) = $self->_prep($dstx,$dsty);
969 0           my $result =
970             $img->image('x' => $dstx,
971             'y' => $dsty,
972             width => $width,
973             height => $height,
974             id => $id,
975             'xlink:href' => "data:image/png;base64,$data");
976 0           $self->_reset;
977 0           return $result;
978             }
979              
980              
981              
982              
983             ##################################################
984             # Image Transformation Methods
985             ##################################################
986              
987             # None implemented
988              
989             ##################################################
990             # Character And String Drawing
991             ##################################################
992             sub string {
993 0     0     my ($self,$font_obj,$x,$y,$text,$color_index) = @_;
994 0           my $img = $self->currentGroup;
995 0           my $id = $self->_create_id($x,$y);
996 0           my $formatting = $font_obj->formatting();
997 0           my $color = $self->_get_color($color_index);
998 0           my $result =
999             $img->text(
1000             id=>$id,
1001             x=>$x,
1002             y=>$y + $font_obj->{height} - GD::SVG::TEXT_KLUDGE,
1003             %$formatting,
1004             fill => $color,
1005             )->cdata($text);
1006 0           return $result;
1007             }
1008              
1009             sub stringUp {
1010 0     0     my ($self,$font_obj,$x,$y,$text,$color_index) = @_;
1011 0           my $img = $self->currentGroup;
1012 0           my $id = $self->_create_id($x,$y);
1013 0           my $formatting = $font_obj->formatting();
1014 0           my $color = $self->_get_color($color_index);
1015 0           $x += $font_obj->height;
1016 0           my $result =
1017             $img->text(
1018             id=>$id,
1019             %$formatting,
1020             'transform' => "translate($x,$y) rotate(-90)",
1021             fill => $color,
1022             )->cdata($text);
1023             }
1024              
1025             sub char {
1026 0     0     my ($self,@rest) = @_;
1027 0           $self->string(@rest);
1028             }
1029              
1030             sub charUp {
1031 0     0     my ($self,@rest) = @_;
1032 0           $self->stringUp(@rest);
1033             }
1034              
1035             # Replicating the TrueType handling
1036             #sub GD::Image::stringFT { shift->_error('stringFT'); }
1037              
1038             sub stringFT {
1039 0     0     return;
1040             }
1041              
1042             # not implemented
1043             sub useFontConfig {
1044 0     0     return 0;
1045             }
1046              
1047              
1048             ##################################################
1049             # Alpha Channels
1050             ##################################################
1051 0     0     sub alphaBlending { shift->_error('alphaBlending'); }
1052 0     0     sub saveAlpha { shift->_error('saveAlpha'); }
1053              
1054             ##################################################
1055             # Miscellaneous Image Methods
1056             ##################################################
1057 0     0     sub interlaced { shift->_error('inerlaced'); }
1058              
1059             sub getBounds {
1060 0     0     my $self = shift;
1061 0           my $width = $self->{width};
1062 0           my $height = $self->{height};
1063 0           return($width,$height);
1064             }
1065              
1066 0     0     sub isTrueColor { shift->_error('isTrueColor'); }
1067 0     0     sub compare { shift->_error('compare'); }
1068 0     0     sub clip { shift->_error('clip'); }
1069 0     0     sub boundsSafe { shift->_error('boundsSafe'); }
1070              
1071             ##########################################
1072             # Internal routines for meshing with SVG #
1073             ##########################################
1074             # Fetch out typical params used for drawing.
1075             package GD::SVG::Image;
1076 1     1   16 use Carp 'confess';
  1         2  
  1         981  
1077              
1078             sub _prep {
1079 0     0     my ($self,@params) = @_;
1080 0           my $img = $self->currentGroup;
1081 0           my $id = $self->_create_id(@params);
1082             # my $thickness = $self->_get_thickness() || 1;
1083             # return ($img,$id,$thickness,undef);
1084 0           return ($img,$id,undef,undef);
1085             }
1086              
1087             # Pass in a ordered list to create a hash ref of style parameters
1088             # ORDER: $id,$color_index,$fill_color,$stroke_opacity);
1089             sub _build_style {
1090 0     0     my ($self,$id,$color,$fill,$stroke_opacity) = @_;
1091 0   0       my $thickness = $self->_get_thickness() || 1;
1092              
1093 0 0         my $fill_opacity = ($fill) ? '1.0' : 0;
1094 0 0         $fill = defined $fill ? $self->_get_color($fill) : 'none';
1095 0 0         if ((my $color_opacity = $self->_get_opacity($color)) > 0) {
1096 0           $stroke_opacity = (127-$color_opacity)/127;
1097             } else {
1098 0   0       $stroke_opacity ||= '1.0';
1099             }
1100 0           my %style = ('stroke' => $self->_get_color($color),
1101             'stroke-opacity' => $stroke_opacity,
1102             'stroke-width' => $thickness,
1103             'fill' => $fill,
1104             'fill-opacity' => $stroke_opacity,
1105             );
1106 0           my $dasharray = $self->{dasharray};
1107 0 0         if ($self->{dasharray}) {
1108 0           $style{'stroke-dasharray'} = @{$self->{dasharray}};
  0            
1109 0           $style{fill} = 'none';
1110             }
1111 0           return \%style;
1112             }
1113              
1114             # From a color index, return a stringified rgb triplet for SVG
1115             sub _get_color {
1116 0     0     my ($self,$index) = @_;
1117 0 0         confess "somebody gave me a bum index!" unless length $index > 0;
1118 0 0         return ($index) if ($index =~ /rgb/); # Already allocated.
1119 0 0         return ($index) if ($index eq 'none'); # Generate by callbacks using none for fill
1120 0           my ($r,$g,$b,$a) = @{$self->{colors}->{$index}};
  0            
1121 0           my $color = "rgb($r,$g,$b)";
1122 0           return $color;
1123             }
1124              
1125             sub _get_opacity {
1126 0     0     my ($self,$index) = @_;
1127 0 0         confess "somebody gave me a bum index!" unless length $index > 0;
1128 0 0         return ($index) if ($index =~ /rgb/); # Already allocated.
1129 0 0         return ($index) if ($index eq 'none'); # Generate by callbacks using none for fill
1130 0           my ($r,$g,$b,$a) = @{$self->{colors}->{$index}};
  0            
1131 0           return $a;
1132             }
1133              
1134             sub _create_id {
1135 0     0     my ($self,$x,$y) = @_;
1136 0           $self->{id_count}++;
1137 0           return (join('-',$self->{id_count},$x,$y));
1138             }
1139              
1140             # Break apart the internal representation of gdBrushed
1141             # setting the line thickness and returning the foreground color
1142             sub _distill_gdSpecial {
1143 0     0     my ($self,$type) = @_;
1144             # Save the previous line thickness so I can restore after drawing...
1145 0   0       $self->{prev_line_thickness} = $self->_get_thickness() || 1;
1146 0           my $thickness = $self->{$type}->{thickness};
1147 0   0       $thickness ||= 1;
1148 0           my $color;
1149 0 0         if ($type eq 'gdStyled') {
1150             # Calculate the size in pixels of each dash
1151             # The first color only will be used starting with the first
1152             # dash; remaining dashes will become gaps
1153 0           my @colors = @{$self->{$type}->{color}};
  0            
1154 0           my ($prev,@dashes,$dash_length);
1155 0           foreach (@colors) {
1156 0 0 0       if (!$prev) {
    0 0        
    0          
1157 0           $dash_length = 1;
1158             # Numeric comparisons work for normal colors
1159             # but fail for named special colors like gdTransparent
1160             } elsif ($prev && $prev eq $_) {
1161 0           $dash_length++;
1162             } elsif ($prev && $prev ne $_) {
1163             # } elsif ($prev && $prev == $_) {
1164             # $dash_length++;
1165             # } elsif ($prev && $prev != $_) {
1166 0           push (@{$self->{dasharray}},$dash_length);
  0            
1167 0           $dash_length = 1;
1168             }
1169 0           $prev = $_;
1170             }
1171 0           push (@{$self->{dasharray}},$dash_length);
  0            
1172 0           $color = $colors[0];
1173             } else {
1174 0           $color = $self->{$type}->{color};
1175             }
1176            
1177 0           $self->setThickness($thickness);
1178 0           return $color;
1179             }
1180              
1181              
1182             # Reset presistent drawing settings between uses of stylized brushes
1183             sub _reset {
1184 0     0     my $self = shift;
1185 0   0       $self->{line_thickness} = $self->{prev_line_thickness} || $self->{line_thickness};
1186 0           $self->{prev_line_thickness} = undef;
1187 0           delete $self->{dasharray};
1188             }
1189              
1190             # SVG needs some self-awareness so that post-drawing operations can
1191             # occur. This is accomplished by tracking all of the pixels that have
1192             # been filled in thus far.
1193             sub _save {
1194 0     0     my ($self) = @_;
1195             # my $path = $img->get_path(x=>[$x1,$x2],y=>[$y1,$y2],-type=>'polyline',-closed=>1);
1196             # foreach (keys %$path) {
1197             # print STDERR $_,"\t",$path->{$_},"\n";
1198             # }
1199             # push (@{$self->{pixels_filled}},$path);
1200             }
1201              
1202             # Value-access methods
1203             # Get the thickness of the line (if it has been set)
1204 0     0     sub _get_thickness { return shift->{line_thickness} }
1205              
1206             # return the internal GD object
1207 0     0     sub _gd { return shift->{gd} }
1208              
1209             ##################################################
1210             # GD::SVG::Polygon
1211             ##################################################
1212             package GD::SVG::Polygon;
1213 1     1   698 use GD::Polygon;
  0         0  
  0         0  
1214             use vars qw(@ISA);
1215             @ISA = 'GD::Polygon';
1216              
1217             sub _error {
1218             my ($self,$method) = @_;
1219             GD::SVG::Image->_error($method);
1220             }
1221              
1222             sub DESTROY { }
1223              
1224             # Generic Font package for accessing height and width information
1225             # and for formatting strings
1226             package GD::SVG::Font;
1227              
1228             use vars qw/@ISA/;
1229             @ISA = qw(GD::SVG);
1230              
1231             # Return guestimated values on the font height and width
1232             sub width { return shift->{width}; }
1233             sub height { return shift->{height}; }
1234             sub font { return shift->{font}; }
1235             sub weight { return shift->{weight}; }
1236             sub nchars { shift->_error('nchars')} # NOT SUPPORTED!!
1237              
1238             # Build the formatting hash for each font...
1239             sub formatting {
1240             my $self = shift;
1241             my $size = $self->height;
1242             my $font = $self->font;
1243             my $weight = $self->weight;
1244             my %format = ('font-size' => $size,
1245             'font' => $font,
1246             # 'writing-mode' => 'tb',
1247             );
1248             $format{'font-weight'} = $weight if ($weight);
1249             return \%format;
1250             }
1251              
1252             sub Tiny { return GD::SVG::gdTinyFont; }
1253             sub Small { return GD::SVG::gdSmallFont; }
1254             sub MediumBold { return GD::SVG::gdMediumBoldFont; }
1255             sub Large { return GD::SVG::gdLargeFont; }
1256             sub Giant { return GD::SVG::gdGiantFont; }
1257              
1258             sub _error {
1259             my ($self,$method) = @_;
1260             GD::SVG::Image->_error($method);
1261             }
1262              
1263             sub DESTROY { }
1264              
1265             1;
1266              
1267             =pod
1268              
1269             =head1 NAME
1270              
1271             GD::SVG - Seamlessly enable SVG output from scripts written using GD
1272              
1273             =head1 SYNOPSIS
1274              
1275             # use GD;
1276             use GD::SVG;
1277              
1278             # my $img = GD::Image->new();
1279             my $img = GD::SVG::Image->new();
1280              
1281             # $img->png();
1282             $img->svg();
1283              
1284             =head1 DESCRIPTION
1285              
1286             GD::SVG painlessly enables scripts that utilize GD to export scalable
1287             vector graphics (SVG). It accomplishes this task by wrapping SVG.pm
1288             with GD-styled method calls. To enable this functionality, one need
1289             only change the "use GD" call to "use GD::SVG" (and initial "new"
1290             method calls).
1291              
1292             =head1 EXPORTS
1293              
1294             GD::SVG exports the same methods as GD itself, overriding those
1295             methods.
1296              
1297             =head1 USAGE
1298              
1299             In order to generate SVG output from your script using GD::SVG, you
1300             will need to first
1301              
1302             # use GD;
1303             use GD::SVG;
1304              
1305             After that, each call to the package classes that GD implements should
1306             be changed to GD::SVG. Thus:
1307              
1308             GD::Image becomes GD::SVG::Image
1309             GD::Font becomes GD::SVG::Font
1310              
1311             =head1 DYNAMICALLY SELECTING SVG OUTPUT
1312              
1313             If you would like your script to be able to dynamically select either
1314             PNG or JPEG output (via GD) or SVG output (via GD::SVG), you should
1315             place your "use" statement within an eval. In the example below, each
1316             of the available classes is created at the top of the script for
1317             convenience, as well as the image output type.
1318              
1319             my $package = shift;
1320             eval "use $package";
1321             my $image_pkg = $package . '::Image';
1322             my $font_pkg = $package . '::Font';
1323              
1324             # Creating new images thus becomes
1325             my $image = $image_pkg->new($width,$height);
1326              
1327             # Establish the image output type
1328             my $image_type;
1329             if ($package = 'GD::SVG') {
1330             $image_type = 'svg';
1331             } else {
1332             $image_type = 'png';
1333             }
1334              
1335             Finally, you should change all GD::Image and GD::Font references to
1336             $image_pkg-> and $font_pkg->, respectively.
1337              
1338             GD::Image->new() becomes $image_pkg->new()
1339             GD::Font->Large() becomes $font_pkg->Large()
1340              
1341             The GD::Polygon and GD::Polyline classes work with GD::SVG without
1342             modification.
1343              
1344             If you make heavy use of GD's exported methods, it may also be
1345             necessary to add () to the endo of method names to avoide bareword
1346             compilation errors. That's the price you pay for using exported
1347             functions!
1348              
1349             =head1 IMPORTANT NOTES
1350              
1351             GD::SVG does not directly generate SVG, but instead relies upon
1352             SVG.pm. It is not intended to supplant SVG.pm. Furthermore, since
1353             GD::SVG is, in essence an API to an API, it may not be suitable for
1354             applications where speed is of the essence. In these cases, GD::SVG
1355             may provide a short-term solution while scripts are re-written to
1356             enable more direct output of SVG.
1357              
1358             Many of the GD::SVG methods accept additional parameters (which are in
1359             turn reflected in the SVG.pm API) that are not supported in GD. Look
1360             through the remainder of this document for options on specific In
1361             addition, several functions have yet to be mapped to SVG.pm
1362             calls. Please see the section below regarding regarding GD functions
1363             that are missing or altered in GD::SVG.
1364              
1365             A similar module (SVG::GD) implements a similar wrapper around
1366             GD. Please see the section at the bottom of this document that
1367             compares GD::SVG to SVG::GD.
1368              
1369             =head1 PREREQUISITES
1370              
1371             GD::SVG requires the Ronan Oger's SVG.pm module, Lincoln Stein's GD.pm
1372             module, libgd and its dependencies.
1373              
1374             =head1 GENERAL DIFFICULTIES IN TRANSLATING GD TO SVG
1375              
1376             These are the primary weaknesses of GD::SVG.
1377              
1378             =over 4
1379              
1380             =item SVG requires unique identifiers for each element
1381              
1382             Each element in an SVG image requires a unique identifier. In general,
1383             GD::SVG handles this by automatically generating unique random
1384             numbers. In addition to the typical parameters for GD methods,
1385             GD::SVG methods allow a user to pass an optional id parameter for
1386             naming the object.
1387              
1388             =item Direct calls to the GD package will fail
1389              
1390             You must change direct calls to the classes that GD invokes:
1391             GD::Image->new() should be changed to GD::SVG::Image->new()
1392              
1393             See the documentation above for how to dynamically switch between
1394             packages.
1395              
1396             =item raster fill() and fillToBorder() not supported
1397              
1398             As SVG documents are not inherently aware of their canvas, the flood
1399             fill methods are not currently supported.
1400              
1401             =item getPixel() not supported.
1402              
1403             Although setPixel() works as expected, its counterpart getPixel() is
1404             not supported. I plan to support this method in a future release.
1405              
1406             =item No support for generation of images from filehandles or raw data
1407              
1408             GD::SVG works only with scripts that generate images directly in the
1409             code using the GD->new(height,width) approach. newFrom() methods are
1410             not currently supported.
1411              
1412             =item Tiled fills are not supported
1413              
1414             Any functions passed gdTiled objects will die.
1415              
1416             =item Styled and Brushed lines only partially implemented
1417              
1418             Calls to the gdStyled and gdBrushed functions via a
1419             rather humorous kludge (and simplification). Depending on the
1420             complexity of the brush, they may behave from slightly differently to
1421             radically differently from their behavior under GD. You have been
1422             warned. See the documentation sections for the methods that set these
1423             options (setStyle(), setBrush(), and setTransparent()).
1424              
1425             =back
1426              
1427             See below for a full list of methods that have not yet been
1428             implemented.
1429              
1430             =head1 WHEN THINGS GO WRONG
1431              
1432             GD is a complicated module. Translating GD methods into those
1433             required to draw in SVG are not always direct. You may or may not get
1434             the output you expect. In general, some tweaking of image parameters
1435             (like text height and width) may be necessary.
1436              
1437             If your script doesn't work as expected, first check the list of
1438             methods that GD::SVG provides. Due to differences in the nature of
1439             SVG images, not all GD methods have been implemented in GD::SVG.
1440              
1441             If your image doesn't look as expected, try tweaking specific aspects
1442             of image generation. In particular, check for instances where you
1443             calculate dimensions of items on the fly like font->height. In SVG,
1444             the values of fonts are defined explicitly.
1445              
1446             =head1 GD FUNCTIONS MISSING FROM GD::SVG
1447              
1448             The following GD functions have not yet been incorporated into
1449             GD::SVG. If you attempt to use one of these functions (and you have
1450             enabled debug warnings via the new() method), GD::SVG will print a
1451             warning to STDERR.
1452              
1453             Creating image objects:
1454             GD::Image->newPalette([$width,$height])
1455             GD::Image->newTrueColor([$width,$height])
1456             GD::Image->newFromPng($file, [$truecolor])
1457             GD::Image->newFromPngData($data, [$truecolor])
1458             GD::Image->newFromJpeg($file, [$truecolor])
1459             GD::Image->newFromJpegData($data, [$truecolor])
1460             GD::Image->newFromXbm($file)
1461             GD::Image->newFromWMP($file)
1462             GD::Image->newFromGd($file)
1463             GD::Image->newFromGdData($data)
1464             GD::Image->newFromGd2($file)
1465             GD::Image->newFromGd2Data($data)
1466             GD::Image->newFromGd2Part($file,srcX,srcY,width,height)
1467             GD::Image->newFromXpm($filename)
1468              
1469             Image methods:
1470             $gddata = $image->gd
1471             $gd2data = $image->gd2
1472             $wbmpdata = $image->wbmp([$foreground])
1473              
1474             Color control methods:
1475             $image->colorAllocateAlpha()
1476             $image->colorClosest()
1477             $image->colorClosestHWB()
1478             $image->getPixel()
1479             $image->transparent()
1480              
1481             Special Colors:
1482             $image->setBrush() (semi-supported, with kludge)
1483             $image->setStyle() (semi-supported, with kludge)
1484             gdTiled
1485             $image->setAntialiased()
1486             gdAntiAliased()
1487             $image->setAntiAliasedDontBlend()
1488              
1489             Drawing methods:
1490             $image->dashedLine()
1491             $image->fill()
1492             $image->fillToBorder()
1493              
1494             Image copying methods
1495             None of the image copying methods are yet supported
1496              
1497             Image transformation methods
1498             None of the image transformation methods are yet supported
1499              
1500             Character and string drawing methods
1501             $image->stringUp() - incompletely supported - broken
1502             $image->charUp()
1503             $image->stringFT()
1504              
1505             Alpha Channels
1506             $image->alphaBlending()
1507             $image->saveAlpha()
1508              
1509             Miscellaneous image methods
1510             $image->isTrueColor()
1511             $image->compare($image2)
1512             $image->clip()
1513             $image->boundsSafe()
1514              
1515             GD::Polyline
1516             Supported without modifications
1517              
1518             Font methods:
1519             $font->nchars()
1520             $font->offset()
1521              
1522             =head1 GROUPING FUNCTIONS GD::SVG
1523              
1524             GD::SVG supports three additional methods that provides the ability to
1525             recursively group objects:
1526              
1527             =over 4
1528              
1529             =item $this->startGroup([$id,\%style]), $this->endGroup()
1530              
1531             These methods start and end a group in a procedural manner. Once a
1532             group is started, all further drawing will be appended to the group
1533             until endGroup() is invoked. You may optionally pass a string ID and
1534             an SVG styles hash to startGroup.
1535              
1536             =item $group = $this->newGroup([$id,\%style])
1537              
1538             This method returns a GD::Group object, which has all the behaviors of
1539             a GD::SVG object except that it draws within the current group. You
1540             can invoke this object's drawing methods to draw into a group. The
1541             group is closed once the object goes out of scope. While the object is
1542             open, invoking drawing methods on the parent GD::SVG object will also
1543             draw into the group until it goes out of scope.
1544              
1545             Here is an example of using grouping in the procedural way:
1546              
1547             use GD::SVG;
1548             my $img = GD::SVG::Image->new(500,500);
1549             my $white = $img->colorAllocate(255,255,255);
1550             my $black = $img->colorAllocate(0,0,0);
1551             my $blue = $img->colorAllocate(0,0,255);
1552             my $red = $img->colorAllocate(255,0,0);
1553              
1554             $img->startGroup('circle in square');
1555             $img->rectangle(100,100,400,400,$blue);
1556              
1557             $img->startGroup('circle and boundary');
1558             $img->filledEllipse(250,250,200,200,$red);
1559             $img->ellipse(250,250,200,200,$black);
1560              
1561             $img->endGroup;
1562             $img->endGroup;
1563            
1564             print $img->svg;
1565              
1566             Here is an example of using grouping with the GD::Group object:
1567              
1568             ...
1569              
1570             my $g1 = $img->newGroup('circle in square');
1571             $g1->rectangle(100,100,400,400,$blue);
1572              
1573             my $g2 = $g1->startGroup('circle and boundary');
1574             $g2->filledEllipse(250,250,200,200,$red);
1575             $g2->ellipse(250,250,200,200,$black);
1576              
1577             print $img->svg;
1578              
1579             Finally, here is a fully worked example of using the GD::Simple module
1580             to make the syntax cleaner:
1581              
1582             #!/usr/bin/perl
1583            
1584             use strict;
1585             use GD::Simple;
1586              
1587             GD::Simple->class('GD::SVG');
1588              
1589             my $img = GD::Simple->new(500,500);
1590             $img->bgcolor('white');
1591             $img->fgcolor('blue');
1592              
1593             my $g1 = $img->newGroup('circle in square');
1594             $g1->rectangle(100,100,400,400);
1595             $g1->moveTo(250,250);
1596              
1597             my $g2 = $g1->newGroup('circle and boundary');
1598             $g2->fgcolor('black');
1599             $g2->bgcolor('red');
1600             $g2->ellipse(200,200);
1601              
1602             print $img->svg;
1603              
1604             =back
1605              
1606             =head1 GD VERSUS GD::SVG METHODS
1607              
1608             All GD::SVG methods mimic the naming and interface of GD methods. As
1609             such, maintenance of GD::SVG follows the development of both GD and
1610             SVG. Much of the original GD documentation is replicated here for ease
1611             of use. Subtle differences in the implementation of these methods
1612             between GD and GD::SVG are discussed below. In particular, the return
1613             value for some GD::SVG methods differs from its GD counterpart.
1614              
1615             =head1 OBJECT CONSTRUCTORS: CREATING IMAGES
1616              
1617             GD::SVG currently only supports the creation of image objects via its
1618             new constructor. This is in contrast to GD proper which supports the
1619             creation of images from previous images, filehandles, filenames, and
1620             data.
1621              
1622             =over 4
1623              
1624             =item $image = GD::SVG::Image->new($height,$width,$debug);
1625              
1626             Create a blank GD::SVG image object of the specified dimensions in
1627             pixels. In turn, this method will create a new SVG object and store it
1628             internally. You can turn on debugging with the GD::SVG specific $debug
1629             parameter. This should be boolean true and will cause non-implemented
1630             methods to print a warning on their status to STDERR.
1631              
1632             =back
1633              
1634             =head1 GD::SVG::Image METHODS
1635              
1636             Once a GD::Image object is created, you can draw with it, copy it, and
1637             merge two images. When you are finished manipulating the object, you
1638             can convert it into a standard image file format to output or save to
1639             a file.
1640              
1641             =head2 Image Data Output Methods
1642              
1643             GD::SVG implements a single output method, svg()!
1644              
1645             =over 4
1646              
1647             =item $svg = $image->svg();
1648              
1649             This returns the image in SVG format. You may then print it, pipe it
1650             to an image viewer, or write it to a file handle. For example,
1651              
1652             $svg_data = $image->svg();
1653             open (DISPLAY,"| display -") || die;
1654             binmode DISPLAY;
1655             print DISPLAY $svg_data;
1656             close DISPLAY;
1657              
1658             if you'd like to return an inline version of the image (instead of a
1659             full document version complete with the DTD), pass the svg() method the
1660             'inline' flag:
1661              
1662             $svg_data = $image->svg(-inline=>'true');
1663              
1664             Calling the other standard GD image output methods (eg
1665             jpeg,gd,gd2,png) on a GD::SVG::Image object will cause your script to
1666             exit with a warning.
1667              
1668             =back
1669              
1670             =head2 Color Control
1671              
1672             These methods allow you to control and manipulate the color table of a
1673             GD::SVG image. In contrast to GD which uses color indices, GD::SVG
1674             passes stringified RGB triplets as colors. GD::SVG, however, maintains
1675             an internal hash structure of colors and colored indices in order to
1676             map GD functions that manipulate the color table. This typically
1677             requires behind-the-scenes translation of these stringified RGB
1678             triplets into a color index.
1679              
1680             =over 4
1681              
1682             =item $stringified_color = $image->colorAllocate(RED,GREEN,BLUE)
1683              
1684             Unlike GD, colors need not be allocated in advance in SVG. Unlike GD
1685             which returns a color index, colorAllocate returns a formatted string
1686             compatible with SVG. Simultaneously, it creates and stores internally
1687             a GD compatible color index for use with GD's color manipulation
1688             methods.
1689              
1690             returns: "rgb(RED,GREEN,BLUE)"
1691              
1692             =item $index = $image->colorAllocateAlpha()
1693              
1694             NOT IMPLEMENTED
1695              
1696             =item $image->colorDeallocate($index)
1697              
1698             Provided with a color index, remove it from the color table.
1699              
1700             =item $index = $image->colorClosest(red,green,blue)
1701              
1702             This returns the index of the color closest in the color table to the
1703             red green and blue components specified. This method is inherited
1704             directly from GD.
1705              
1706             Example: $apricot = $myImage->colorClosest(255,200,180);
1707              
1708             NOT IMPLEMENTED
1709              
1710             =item $index = $image->colorClosestHWB(red,green,blue)
1711              
1712             NOT IMPLEMENTED
1713              
1714             =item $index = $image->colorExact(red,green,blue)
1715              
1716             Retrieve the color index of an rgb triplet (or -1 if it has yet to be
1717             allocated).
1718              
1719             NOT IMPLEMENTED
1720              
1721             =item $index = $image->colorResolve(red,green,blue)
1722              
1723             NOT IMPLEMENTED
1724              
1725             =item $colors_total = $image->colorsTotal()
1726              
1727             Retrieve the total number of colors indexed in the image.
1728              
1729             =item $index = $image->getPixel(x,y)
1730              
1731             NOT IMPLEMENTED
1732              
1733             =item ($red,$green,$blue) = $image->rgb($index)
1734              
1735             Provided with a color index, return the RGB triplet. In GD::SVG,
1736             color indexes are replaced with actual RGB triplets in the form
1737             "rgb($r,$g,$b)".
1738              
1739             =item $image->transparent($colorIndex);
1740              
1741             Control the transparency of individual colors.
1742              
1743             NOT IMPLEMENTED
1744              
1745             =back
1746              
1747             =head2 Special Colors
1748              
1749             GD implements a number of special colors that can be used to achieve
1750             special effects. They are constants defined in the GD:: namespace,
1751             but automatically exported into your namespace when the GD module is
1752             loaded. GD::SVG offers limited support for these methods.
1753              
1754             =over 4
1755              
1756             =item $image->setBrush($brush) (KLUDGE ALERT)
1757              
1758             =item gdBrushed
1759              
1760             In GD, one can draw lines and shapes using a brush pattern. Brushes
1761             are just images that you can create and manipulate in the usual way.
1762             When you draw with them, their contents are used for the color and
1763             shape of the lines.
1764              
1765             To make a brushed line, you must create or load the brush first, then
1766             assign it to the image using setBrush(). You can then draw in that
1767             with that brush using the gdBrushed special color. It's often useful
1768             to set the background of the brush to transparent so that the
1769             non-colored parts don't overwrite other parts of your image.
1770              
1771             # Via GD, this is how one would set a Brush
1772             $diagonal_brush = new GD::Image(5,5);
1773             $white = $diagonal_brush->colorAllocate(255,255,255);
1774             $black = $diagonal_brush->colorAllocate(0,0,0);
1775             $diagonal_brush->transparent($white);
1776             $diagonal_brush->line(0,4,4,0,$black); # NE diagonal
1777              
1778             GD::SVG offers limited support for setBrush (and the corresponding
1779             gdBrushed methods) - currently only in the shapes of squares.
1780             Internally, GD::SVG extracts the longest dimension of the image using
1781             the getBounds() method. Next, it extracts the second color set,
1782             assuming that to be the foreground color. It then re-calls the
1783             original drawing method with these new values in place of the
1784             gdBrushed. See the private _distill_gdSpecial method for the internal
1785             details of this operation.
1786              
1787             =item $image->setThickness($thickness)
1788              
1789             Lines drawn with line(), rectangle(), arc(), and so forth are 1 pixel
1790             thick by default. Call setThickness() to change the line drawing
1791             width.
1792              
1793             =item $image->setStyle(@colors)
1794              
1795             setStyle() and gdStyled() are partially supported in GD::SVG. GD::SVG
1796             determines the alternating pattern of dashes, treating the first
1797             unique color encountered in the array as on, the second as off and so
1798             on. The first color in the array is then used to draw the actual line.
1799              
1800             =item gdTiled
1801              
1802             NOT IMPLEMENTED
1803              
1804             =item gdStyled()
1805              
1806             The GD special color gdStyled is partially implemented in
1807             GD::SVG. Only the first color will be used to generate the dashed
1808             pattern specified in setStyle(). See setStyle() for additional
1809             information.
1810              
1811             =item $image->setAntiAliased($color)
1812              
1813             NOT IMPLEMENTED
1814              
1815             =item gdAntiAliased
1816              
1817             NOT IMPLEMENTED
1818              
1819             =item $image->setAntiAliasedDontBlend($color,[$flag])
1820              
1821             NOT IMPLEMENTED
1822              
1823             =back
1824              
1825             =head2 Drawing Commands
1826              
1827             =over 4
1828              
1829             =item $image->setPixel($x,$y,$color)
1830              
1831             Set the corresponding pixel to the given color. GD::SVG implements
1832             this by drawing a single dot in the specified color at that position.
1833              
1834             =item $image->line(x1,y1,x2,y2,color);
1835              
1836             Draw a line between the two coordinate points with the specified
1837             color. Passing an optional id will set the id of that SVG
1838             element. GD::SVG also supports drawing with the special brushes -
1839             gdStyled and gdBrushed - although these special styles are difficult
1840             to replicate precisley in GD::SVG.
1841              
1842             =item $image->dashedLine($x1,$y1,$x2,$y2,$color);
1843              
1844             NOT IMPLEMENTED
1845              
1846             =item $image->rectangle($x1,$y1,$x2,$y2,$color);
1847              
1848             This draws a rectangle with the specified color. (x1,y1) and (x2,y2)
1849             are the upper left and lower right corners respectively. You may also
1850             draw with the special colors gdBrushed and gdStyled.
1851              
1852             =item $image->filledRectangle($x1,$y1,$x2,$y2,$color);
1853              
1854             filledRectangle is a GD specific method with no direct equivalent in
1855             SVG. GD::SVG translates this method into an SVG appropriate method by
1856             passing the filled color parameter as a named 'filled' parameter to
1857             SVG. Drawing with the special colors is also permitted. See the
1858             documentation for the line() method for additional details.
1859              
1860             GD call:
1861             $img->filledRectangle($x1,$y1,$x2,$y2,$color);
1862            
1863             SVG call:
1864             $img->rectangle(x=> $x1,y=> $y1,
1865             width => $x2-$x1,
1866             height => $y2-$y1,
1867             fill => $color
1868              
1869             =item $image->polygon($polygon,$color);
1870              
1871             This draws a polygon with the specified color. The polygon must be
1872             created first (see "Polygons" below). The polygon must have at least
1873             three vertices. If the last vertex doesn't close the polygon, the
1874             method will close it for you. Both real color indexes and the special
1875             colors gdBrushed, gdStyled and gdStyledBrushed can be specified. See
1876             the documentation for the line() method for additional details.
1877              
1878             $poly = new GD::Polygon;
1879             $poly->addPt(50,0);
1880             $poly->addPt(99,99);
1881             $poly->addPt(0,99);
1882             $image->polygon($poly,$blue);
1883              
1884             =item $image->filledPolygon($polygon,$color);
1885              
1886             This draws a polygon filled with the specified color. Drawing with
1887             the special colors is also permitted. See the documentation for the
1888             line() method for additional details.
1889              
1890             # make a polygon
1891             $poly = new GD::Polygon;
1892             $poly->addPt(50,0);
1893             $poly->addPt(99,99);
1894             $poly->addPt(0,99);
1895              
1896             # draw the polygon, filling it with a color
1897             $image->filledPolygon($poly,$peachpuff);
1898              
1899             =item $image->filledPolygon($polygon,$color);
1900              
1901             This draws a polygon filled with the specified color. Drawing with
1902             the special colors is also permitted. See the documentation for the
1903             line() method for additional details.
1904              
1905             # make a polygon
1906             $poly = new GD::Polygon;
1907             $poly->addPt(50,0);
1908             $poly->addPt(99,99);
1909             $poly->addPt(0,99);
1910              
1911             # draw the polygon, filling it with a color
1912             $image->filledPolygon($poly,$peachpuff);
1913              
1914             =item $image->polyline(polyline,color)
1915              
1916             $image->polyline($polyline,$black)
1917              
1918             This draws a polyline with the specified color.
1919             Both real color indexes and the special
1920             colors gdBrushed, gdStyled and gdStyledBrushed can be specified.
1921              
1922             Neither the polyline() method or the polygon() method are very picky:
1923             you can call either method with either a GD::Polygon or a
1924             GD::Polyline. The I determines if the shape is "closed" or
1925             "open" as drawn, I the object type.
1926              
1927             =item $image-Epolydraw(polything,color)
1928              
1929             $image->polydraw($poly,$black)
1930              
1931             This method draws the polything as expected (polygons are closed,
1932             polylines are open) by simply checking the object type and calling
1933             either $image->polygon() or $image->polyline().
1934              
1935             =item $image->ellipse($cx,$cy,$width,$height,$color)
1936              
1937             =item $image->filledEllipse($cx,$cy,$width,$height,$color)
1938              
1939             These methods() draw ellipses. ($cx,$cy) is the center of the arc, and
1940             ($width,$height) specify the ellipse width and height, respectively.
1941             filledEllipse() is like ellipse() except that the former produces
1942             filled versions of the ellipse. Drawing with the special colors is
1943             also permitted. See the documentation for the line() method for
1944             additional details.
1945              
1946             =item $image->arc($cy,$cy,$width,$height,$start,$end,$color);
1947              
1948             This draws arcs and ellipses. (cx,cy) are the center of the arc, and
1949             (width,height) specify the width and height, respectively. The
1950             portion of the ellipse covered by the arc are controlled by start and
1951             end, both of which are given in degrees from 0 to 360. Zero is at the
1952             top of the ellipse, and angles increase clockwise. To specify a
1953             complete ellipse, use 0 and 360 as the starting and ending angles. To
1954             draw a circle, use the same value for width and height.
1955              
1956             Internally, arc() calls the ellipse() method of SVG.pm. Drawing with
1957             the special colors is also permitted. See the documentation for the
1958             line() method for additional details.
1959              
1960             Currently, true arcs are NOT supported, only those where the start and
1961             end equal 0 and 360 respectively resulting in a closed arc.
1962              
1963             =item $image->filledArc($cx,$cy,$width,$height,$start,$end,$color
1964             [,$arc_style])
1965              
1966             This method is like arc() except that it colors in the pie wedge with
1967             the selected color. $arc_style is optional. If present it is a
1968             bitwise OR of the following constants:
1969              
1970             gdArc connect start & end points of arc with a rounded edge
1971             gdChord connect start & end points of arc with a straight line
1972             gdPie synonym for gdChord
1973             gdNoFill outline the arc or chord
1974             gdEdged connect beginning and ending of the arc to the center
1975              
1976             gdArc and gdChord are mutally exclusive. gdChord just connects the
1977             starting and ending angles with a straight line, while gdArc pro-
1978             duces a rounded edge. gdPie is a synonym for gdArc. gdNoFill indi-
1979             cates that the arc or chord should be outlined, not filled. gdEdged,
1980             used together with gdNoFill, indicates that the beginning and ending
1981             angles should be connected to the center; this is a good way to
1982             outline (rather than fill) a "pie slice."
1983              
1984             Using these special styles, you can easily draw bordered ellipses and
1985             circles.
1986              
1987             # Create the filled shape:
1988             $image->filledArc($x,$y,$width,$height,0,360,$fill);
1989             # Now border it.
1990             $image->filledArc($x,$y,$width,$height,0,360,$color,gdNoFill);
1991              
1992             =item $image->fill();
1993              
1994             NOT IMPLEMENTED
1995              
1996             =item $image->fillToBorder()
1997              
1998             NOT IMPLEMENTED
1999              
2000             =back
2001              
2002             =head2 Image Copying Methods
2003              
2004             The basic copy() command is implemented in GD::SVG. You can copy one
2005             GD::SVG into another GD::SVG, or copy a GD::Image or GD::Simple object
2006             into a GD::SVG, thereby embedding a pixmap image into the SVG image.
2007              
2008             All other image copying methods are unsupported, and if your script
2009             calls one of the following methods, your script will die remorsefully
2010             with a warning. With sufficient demand, I might try to implement some
2011             of these methods. For now, I think that they are beyond the intent of
2012             GD::SVG.
2013              
2014             $image->clone()
2015             $image->copyMerge()
2016             $image->copyMergeGray()
2017             $image->copyResized()
2018             $image->copyResampled()
2019             $image->trueColorToPalette()
2020              
2021             =head2 Image Transfomation Commands
2022              
2023             None of the image transformation commands are implemented in GD::SVG.
2024             If your script calls one of the following methods, your script will
2025             die remorsefully with a warning. With sufficient demand, I might try
2026             to implement some of these methods. For now, I think that they are
2027             beyond the intent of GD::SVG.
2028              
2029             $image = $sourceImage->copyRotate90()
2030             $image = $sourceImage->copyRotate180()
2031             $image = $sourceImage->copyRotate270()
2032             $image = $sourceImage->copyFlipHorizontal()
2033             $image = $sourceImage->copyFlipVertical()
2034             $image = $sourceImage->copyTranspose()
2035             $image = $sourceImage->copyReverseTranspose()
2036             $image->rotate180()
2037             $image->flipHorizontal()
2038             $image->flipVertical()
2039              
2040             =head2 Character And String Drawing
2041              
2042             GD allows you to draw characters and strings, either in normal
2043             horizon- tal orientation or rotated 90 degrees. In GD, these routines
2044             use a GD::Font object. Internally, GD::SVG mimics the behavior of GD
2045             with respect to fonts in a very similar manner, using instead a
2046             GD::SVG::Font object described in more detail below.
2047              
2048             GD's font handling abilities are not as flexible as SVG and it does
2049             not allow the dynamic creation of fonts, instead exporting five
2050             available fonts as global variables: gdGiantFont, gdLargeFont,
2051             gdMediumBoldFont, gdSmallFont and gdTinyFont. GD::SVG also exports
2052             these same global variables but establishes them in a different manner
2053             using constant variables to establish the font family, font height and
2054             width of these global fonts. These values were chosen to match as
2055             closely as possible GD's output. If unsatisfactory, adjust the
2056             constants at the top of this file. In all subroutines below, GD::SVG
2057             passes a generic GD::SVG::Font object in place of the exported font
2058             variables.
2059              
2060             =over 4
2061              
2062             =item $image->string($font,$x,$y,$string,$color)
2063              
2064             This method draws a string starting at position (x,y) in the speci-
2065             fied font and color. Your choices of fonts are gdSmallFont,
2066             gdMediumBoldFont, gdTinyFont, gdLargeFont and gdGiantFont.
2067              
2068             $myImage->string(gdSmallFont,2,10,"Peachy Keen",$peach);
2069              
2070             =item $image->stringUp($font,$x,$y,$string,$color)
2071              
2072             Same as the previous example, except that it draws the text rotated
2073             counter-clockwise 90 degrees.
2074              
2075             =item $image->char($font,$x,$y,$char,$color)
2076              
2077             =item $image->charUp($font,$x,$y,$char,$color)
2078              
2079             These methods draw single characters at position (x,y) in the spec-
2080             ified font and color. They're carry-overs from the C interface, where
2081             there is a distinction between characters and strings. Perl is
2082             insensible to such subtle distinctions. Neither is SVG, which simply
2083             calls the string() method internally.
2084              
2085             =item @bounds = $image->stringFT($fgcolor,$font-
2086             name,$ptsize,$angle,$x,$y,$string)
2087              
2088             =item @bounds = $image->stringFT($fgcolor,$font-
2089             name,$ptsize,$angle,$x,$y,$string,\%options)
2090              
2091             In GD, these methods use TrueType to draw a scaled, antialiased
2092             strings using the TrueType font of your choice. GD::SVG can handle
2093             this directly generating by calling the string() method internally.
2094              
2095             The arguments are as follows:
2096              
2097             fgcolor Color index to draw the string in
2098             fontname An absolute path to the TrueType (.ttf) font file
2099             ptsize The desired point size (may be fractional)
2100             angle The rotation angle, in radians
2101             x,y X and Y coordinates to start drawing the string
2102             string The string itself
2103              
2104             GD::SVG attempts to extract the name of the font from the pathname
2105             supplied in the fontname argument. If it fails, Helvetica will be used
2106             instead.
2107              
2108             If successful, the method returns an eight-element list giving the
2109             boundaries of the rendered string:
2110              
2111             @bounds[0,1] Lower left corner (x,y)
2112             @bounds[2,3] Lower right corner (x,y)
2113             @bounds[4,5] Upper right corner (x,y)
2114             @bounds[6,7] Upper left corner (x,y)
2115              
2116             This from the GD documentation (not yet implemented in GD::SVG):
2117              
2118             An optional 8th argument allows you to pass a hashref of options to
2119             stringFT(). Two hashkeys are recognized: linespacing, if present,
2120             controls the spacing between lines of text. charmap, if present, sets
2121             the character map to use.
2122              
2123             The value of linespacing is supposed to be a multiple of the char-
2124             acter height, so setting linespacing to 2.0 will result in double-
2125             spaced lines of text. However the current version of libgd (2.0.12)
2126             does not do this. Instead the linespacing seems to be double what is
2127             provided in this argument. So use a spacing of 0.5 to get separation
2128             of exactly one line of text. In practice, a spacing of 0.6 seems to
2129             give nice results. Another thing to watch out for is that successive
2130             lines of text should be separated by the "\r\n" characters, not just
2131             "\n".
2132              
2133             The value of charmap is one of "Unicode", "Shift_JIS" and "Big5". The
2134             interaction between Perl, Unicode and libgd is not clear to me, and
2135             you should experiment a bit if you want to use this feature.
2136              
2137             $gd->stringFT($black,'/dosc/windows/Fonts/pala.ttf',40,0,20,90,
2138             "hi there\r\nbye now",
2139             {linespacing=>0.6,
2140             charmap => 'Unicode',
2141             });
2142              
2143             For backward compatibility with older versions of the FreeType
2144             library, the alias stringTTF() is also recognized. Also be aware that
2145             relative font paths are not recognized due to problems in the libgd
2146             library.
2147              
2148             =item $hasfontconfig = $image-EuseFontConfig($flag)
2149              
2150             Call useFontConfig() with a value of 1 in order to enable support for
2151             fontconfig font patterns (see stringFT). Regardless of the value of
2152             $flag, this method will return a true value if the fontconfig library
2153             is present, or false otherwise.
2154              
2155             NOT IMPLEMENTED
2156              
2157             =back
2158              
2159             =head2 Alpha Channels
2160              
2161             =over 4
2162              
2163             =item $image->alphaBlending($blending)
2164              
2165             NOT IMPLEMENTED
2166              
2167             =item $image->saveAlpha($saveAlpha)
2168              
2169             NOT IMPLEMENTED
2170              
2171             =back
2172              
2173             =head2 Miscellaneous Image Methods
2174              
2175             =over 4
2176              
2177             =item $image->interlaced([$flag])
2178              
2179             NOT IMPLEMENTED
2180              
2181             =item ($width,$height) = $image->getBounds()
2182              
2183             getBounds() returns the height and width of the image.
2184              
2185             =item $is_truecolor = $image->isTrueColor()
2186              
2187             NOT IMPLEMENTED
2188              
2189             =item $flag = $image1->compare($image2)
2190              
2191             NOT IMPLEMENTED
2192              
2193             =item $image->clip($x1,$y1,$x2,$y2)
2194             ($x1,$y1,$x2,$y2) = $image->clip
2195              
2196             NOT IMPLEMENTED
2197              
2198             =item $flag = $image->boundsSafe($x,$y)
2199              
2200             NOT IMPLEMENTED
2201              
2202             =back
2203              
2204             =head1 GD::SVG::Polygon METHODS
2205              
2206             SVG is much more adept at creating polygons than GD. That said, GD
2207             does provide some rudimentary support for polygons but must be created
2208             as seperate objects point by point.
2209              
2210             =over 4
2211              
2212             =item $poly = GD::SVG::Polygon->new
2213              
2214             Create an empty polygon with no vertices.
2215              
2216             $poly = new GD::SVG::Polygon;
2217              
2218             =item $poly->addPt($x,$y)
2219              
2220             Add point (x,y) to the polygon.
2221              
2222             $poly->addPt(0,0);
2223             $poly->addPt(0,50);
2224             $poly->addPt(25,25);
2225              
2226             =item ($x,$y) = $poly->getPt($index)
2227              
2228             Retrieve the point at the specified vertex.
2229              
2230             ($x,$y) = $poly->getPt(2);
2231              
2232             =item $poly->setPt($index,$x,$y)
2233              
2234             Change the value of an already existing vertex. It is an error to set
2235             a vertex that isn't already defined.
2236              
2237             $poly->setPt(2,100,100);
2238              
2239             =item ($x,$y) = $poly->deletePt($index)
2240              
2241             Delete the specified vertex, returning its value.
2242              
2243             ($x,$y) = $poly->deletePt(1);
2244              
2245             =item $poly->toPt($dx,$dy)
2246              
2247             Draw from current vertex to a new vertex, using relative (dx,dy)
2248             coordinates. If this is the first point, act like addPt().
2249              
2250             $poly->addPt(0,0);
2251             $poly->toPt(0,50);
2252             $poly->toPt(25,-25);
2253              
2254             NOT IMPLEMENTED
2255              
2256             =item $vertex_count = $poly->length()
2257              
2258             Return the number of vertices in the polygon.
2259              
2260             =item @vertices = $poly->vertices()
2261              
2262             Return a list of all the verticies in the polygon object. Each mem-
2263             ber of the list is a reference to an (x,y) array.
2264              
2265             @vertices = $poly->vertices;
2266             foreach $v (@vertices)
2267             print join(",",@$v),"\n";
2268             }
2269              
2270             =item @rect = $poly->bounds()
2271              
2272             Return the smallest rectangle that completely encloses the polygon.
2273             The return value is an array containing the (left,top,right,bottom) of
2274             the rectangle.
2275              
2276             ($left,$top,$right,$bottom) = $poly->bounds;
2277              
2278             =item $poly->offset($dx,$dy)
2279              
2280             Offset all the vertices of the polygon by the specified horizontal
2281             (dh) and vertical (dy) amounts. Positive numbers move the polygon
2282             down and to the right. Returns the number of vertices affected.
2283              
2284             $poly->offset(10,30);
2285              
2286             =item $poly->map($srcL,$srcT,$srcR,$srcB,$destL,$dstT,$dstR,$dstB)
2287              
2288             Map the polygon from a source rectangle to an equivalent position in a
2289             destination rectangle, moving it and resizing it as necessary. See
2290             polys.pl for an example of how this works. Both the source and
2291             destination rectangles are given in (left,top,right,bottom) coordi-
2292             nates. For convenience, you can use the polygon's own bounding box as
2293             the source rectangle.
2294              
2295             # Make the polygon really tall
2296             $poly->map($poly->bounds,0,0,50,200);
2297              
2298             NOT IMPLEMENTED
2299              
2300             =item $poly->scale($sx,$sy)
2301              
2302             Scale each vertex of the polygon by the X and Y factors indicated by
2303             sx and sy. For example scale(2,2) will make the polygon twice as
2304             large. For best results, move the center of the polygon to position
2305             (0,0) before you scale, then move it back to its previous position.
2306              
2307             NOT IMPLEMENTED
2308              
2309             =item $poly->transform($sx,$rx,$sy,$ry,$tx,$ty)
2310              
2311             Run each vertex of the polygon through a transformation matrix, where
2312             sx and sy are the X and Y scaling factors, rx and ry are the X and Y
2313             rotation factors, and tx and ty are X and Y offsets. See the Adobe
2314             PostScript Reference, page 154 for a full explanation, or experiment.
2315              
2316             NOT IMPLEMENTED
2317              
2318             =back
2319              
2320             =head2 GD::Polyline
2321              
2322             Please see GD::Polyline for information on creating open polygons and
2323             splines.
2324              
2325             =head1 GD::SVG::Font METHODS
2326              
2327             NOTE: The object-oriented implementation to font utilites is not yet
2328             supported.
2329              
2330             The libgd library (used by the Perl GD library) has built-in support
2331             for about half a dozen fonts, which were converted from public-domain
2332             X Windows fonts. For more fonts, compile libgd with TrueType support
2333             and use the stringFT() call.
2334              
2335             GD::SVG replicates the internal fonts of GD by hardcoding fonts which
2336             resemble the design and point size of the original. Each of these
2337             fonts is available both as an imported global (e.g. gdSmallFont) and
2338             as a package method (e.g. GD::Font->Small).
2339              
2340             =over 4
2341              
2342             =item gdTinyFont
2343              
2344             =item GD::Font->Tiny
2345              
2346             This is a tiny, almost unreadable font, 5x8 pixels wide.
2347              
2348             =item gdSmallFont
2349              
2350             =item GD::Font->Small
2351              
2352             This is the basic small font, "borrowed" from a well known public
2353             domain 6x12 font.
2354              
2355             =item gdMediumBoldFont
2356              
2357             =item GD::Font->MediumBold
2358              
2359             This is a bold font intermediate in size between the small and large
2360             fonts, borrowed from a public domain 7x13 font;
2361              
2362             =item gdLargeFont
2363              
2364             =item GD::Font->Large
2365              
2366             This is the basic large font, "borrowed" from a well known public
2367             domain 8x16 font.
2368              
2369             =item gdGiantFont
2370              
2371             =item GD::Font->Giant
2372              
2373             This is a 9x15 bold font converted by Jan Pazdziora from a sans serif
2374             X11 font.
2375              
2376             =item $font->nchars
2377              
2378             This returns the number of characters in the font.
2379              
2380             print "The large font contains ",gdLargeFont->nchars," characters\n";
2381              
2382             NOT IMPLEMENTED
2383              
2384             =item $font->offset()
2385              
2386             This returns the ASCII value of the first character in the font
2387              
2388             =item $width = $font->width
2389              
2390             =item $height = $font->height
2391              
2392             These return the width and height of the font.
2393              
2394             ($w,$h) = (gdLargeFont->width,gdLargeFont->height);
2395              
2396             =back
2397              
2398             =head1 REAL WORLD EXAMPLES
2399              
2400             =over 4
2401              
2402             =item BioPerl
2403              
2404             The Bio::Graphics package of the BioPerl project makes use of GD::SVG
2405             to export SVG graphics.
2406              
2407             http://www.bioperl.org/
2408              
2409             =item Generic Genome Browser
2410              
2411             The Generic Genome Browser (GBrowse) utilizes Bio::Graphics and
2412             enables SVG dumping of genomics views. You can see a real-world
2413             example of SVG output from GBrowse at WormBase:
2414              
2415             http://www.wormbase.org/cgi-bin/gbrowse/
2416              
2417             Further information about the Generic Genome Browser is available at
2418             the Generic Model Organism Project home page:
2419              
2420             http://www.gmod.org/
2421              
2422             =item toddot
2423              
2424             I've also prepared a number of comparative images at my website
2425             (shameless plug, hehe):
2426              
2427             http://www.toddot.net/projects/GD-SVG/
2428              
2429             =back
2430              
2431             =head1 INTERNAL METHODS
2432              
2433             The following internal methods are private and documented only for
2434             those wishing to extend the GD::SVG interface.
2435              
2436             =over 4
2437              
2438             =item _distill_gdSpecial()
2439              
2440             When a drawing method is passed a stylized brush via gdBrushed, the
2441             internal _distill_gdSpecial() method attempts to make sense of this by
2442             setting line thickness and foreground color. Since stylized brushes
2443             are GD::SVG::Image objects, it does this by fetching the width of the
2444             image using the getBounds method. This width is then used to
2445             setThickness. The last color set by colorAllocate is then used for
2446             the foreground color.
2447              
2448             In setting line thickness, GD::SVG temporarily overrides any
2449             previously set line thickness. In GD, setThickness is persistent
2450             through uses of stylized brushes. To accomodate this behavior,
2451             _distill_gdSpecial() temporarily stores the previous line_thickness in
2452             the $self->{previous_line_thickness} flag.
2453              
2454             =item _reset()
2455              
2456             The _reset() method is used to restore persistent drawing settings
2457             between uses of stylized brushes. Currently, this involves
2458              
2459             - restoring line thickness
2460              
2461             =back
2462              
2463             =head1 IMPORTANT NOTE! GD::SVG / SVG::GD
2464              
2465             A second module (SVG::GD), written by Ronan Oger also provides similar
2466             functionality as this module. Ronan and I are concurrently developing
2467             these modules with an eye towards integrating them in the future. In
2468             principle, the primary difference is that GD::SVG aims to generate SVG
2469             and SVG only. That is, it:
2470              
2471             1. Does not store an internal representation of the GD image
2472              
2473             2. Does not enable JPG, PNG, OR SVG output from a single pass
2474             through data
2475              
2476             3. Only occasioanally uses inherited methods from GD
2477              
2478             Instead GD::SVG depends on the user to choose which output format they
2479             would like in advance, "use"ing the appropriate module for that
2480             output. As described at the start of this document, module selection
2481             between GD and GD::SVG can be made dynamically using eval statements
2482             and variables for the differnet classes that GD and GD::SVG create.
2483              
2484             There is a second reason for not maintaining a double representation
2485             of the data in GD and SVG format: SVG documents can quickly become
2486             very large, especially with large datasets. In cases where scripts are
2487             primarily generating png images in a server environment and would only
2488             occasionally need to export SVG, gernerating an SVG image in parallel
2489             would result in an unacceptable performance hit.
2490              
2491             Thus GD::SVG aims to be a plugin for existing configurations that
2492             depend on GD but would like to take advantage of SVG output.
2493              
2494             SVG::GD, on the other hand, aims to tie in the raster-editing ability
2495             of GD with the power of SVG output. In part, it aims to do this by
2496             inheriting many methods from GD directly and bringing them into the
2497             functional space of GD. This makes SVG::GD easier to set up initially
2498             (simply by adding the "use SVG::GD" below the "use GD" statement of
2499             your script. GD::SVG sacrfices this initial ease-of-setup for more
2500             targeted applications.
2501              
2502             =head1 ACKNOWLEDGEMENTS
2503              
2504             Lincoln Stein, my postdoctoral mentor, author of GD.pm, and all around
2505             Perl stud. Ronan Oger, author of SVG.pm conceptualized and implemented
2506             another wrapper around GD at about the exact same time as this module.
2507             He also provided helpful discussions on implementing GD functions into
2508             SVG. Oliver Drechsel and Marc Lohse provided patches to actually
2509             make the stringUP method functional.
2510              
2511             =head1 AUTHOR
2512              
2513             Todd Harris, PhD Eharris@cshl.orgE
2514              
2515             =head1 COPYRIGHT AND LICENSE
2516              
2517             Copyright @ 2003-2005 Todd Harris and the Cold Spring Harbor Laboratory
2518              
2519             This library is free software; you can redistribute it and/or modify
2520             it under the same terms as Perl itself.
2521              
2522             =head1 SEE ALSO
2523              
2524             L,
2525             L,
2526             L,
2527             L
2528              
2529             =cut