line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Graphics::Primitive::Driver::CairoPango; |
2
|
1
|
|
|
1
|
|
24775
|
use Moose; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
use Moose::Util::TypeConstraints; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
use Cairo; |
6
|
|
|
|
|
|
|
use Carp; |
7
|
|
|
|
|
|
|
use Geometry::Primitive::Point; |
8
|
|
|
|
|
|
|
use Geometry::Primitive::Rectangle; |
9
|
|
|
|
|
|
|
use Graphics::Primitive::Driver::CairoPango::TextLayout; |
10
|
|
|
|
|
|
|
use Pango; |
11
|
|
|
|
|
|
|
use IO::File; |
12
|
|
|
|
|
|
|
use Math::Trig ':pi'; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
extends 'Graphics::Primitive::Driver::Cairo'; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# with 'Graphics::Primitive::Driver'; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:GPHAT'; |
19
|
|
|
|
|
|
|
our $VERSION = '0.63'; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
enum 'Graphics::Primitive::Driver::CairoPango::AntialiasModes' => [ |
22
|
|
|
|
|
|
|
qw(default none gray subpixel) |
23
|
|
|
|
|
|
|
]; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
enum 'Graphics::Primitive::Driver::CairoPango::Format' => [ |
26
|
|
|
|
|
|
|
qw(PDF PS PNG SVG pdf ps png svg) |
27
|
|
|
|
|
|
|
]; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub _draw_textbox { |
30
|
|
|
|
|
|
|
my ($self, $comp) = @_; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
return unless(defined($comp->text)); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my $context = $self->cairo; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
$context->save; |
37
|
|
|
|
|
|
|
$self->_draw_component($comp); |
38
|
|
|
|
|
|
|
$context->restore; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
my $bbox = $comp->inside_bounding_box; |
41
|
|
|
|
|
|
|
my $width = $bbox->width; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
$context->set_source_rgba($comp->color->as_array_with_alpha); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my $origin = $bbox->origin; |
46
|
|
|
|
|
|
|
my $x = $origin->x; |
47
|
|
|
|
|
|
|
my $y = $origin->y; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
if(defined($comp->lines)) { |
50
|
|
|
|
|
|
|
my $start = $comp->lines->{start}; |
51
|
|
|
|
|
|
|
my $count = $comp->lines->{count}; |
52
|
|
|
|
|
|
|
my $endline = $start + $count; |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
my $layout = $comp->layout->_layout; |
55
|
|
|
|
|
|
|
my $iter = $layout->get_iter; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
my $startpos = 0; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
my $line_index = 0; |
60
|
|
|
|
|
|
|
while($line_index < $endline) { |
61
|
|
|
|
|
|
|
if($line_index < $start) { |
62
|
|
|
|
|
|
|
$iter->next_line; |
63
|
|
|
|
|
|
|
$line_index++; |
64
|
|
|
|
|
|
|
next; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
my $line = $iter->get_line_readonly; |
68
|
|
|
|
|
|
|
my ($ink, $log) = $iter->get_line_extents; |
69
|
|
|
|
|
|
|
my $baseline = $iter->get_baseline; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
if($start == $line_index) { |
72
|
|
|
|
|
|
|
$startpos = $log->{y} / 1024; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
$context->move_to($x + ($log->{x} / 1024), $baseline / 1024 - $startpos + $y); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Pango::Cairo::show_layout_line($context, $line); |
78
|
|
|
|
|
|
|
$line_index++; |
79
|
|
|
|
|
|
|
$iter->next_line; |
80
|
|
|
|
|
|
|
}; |
81
|
|
|
|
|
|
|
} else { |
82
|
|
|
|
|
|
|
my $layout = $comp->layout->_layout; |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
my $angle = $comp->angle; |
85
|
|
|
|
|
|
|
if($angle) { |
86
|
|
|
|
|
|
|
my $tw2 = $comp->width / 2; |
87
|
|
|
|
|
|
|
my $th2 = $comp->height / 2; |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
$context->translate($tw2, $th2); |
90
|
|
|
|
|
|
|
$context->rotate($angle); |
91
|
|
|
|
|
|
|
$context->translate(-$tw2, -$th2); |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# Get the un-rotated extents so we can position it |
94
|
|
|
|
|
|
|
my ($ink, $log) = $layout->get_pixel_extents; |
95
|
|
|
|
|
|
|
$context->move_to( |
96
|
|
|
|
|
|
|
$tw2 - $log->{width} / 2, |
97
|
|
|
|
|
|
|
$th2 - $log->{height} / 2 |
98
|
|
|
|
|
|
|
); |
99
|
|
|
|
|
|
|
} else { |
100
|
|
|
|
|
|
|
my ($ink, $log) = $layout->get_pixel_extents; |
101
|
|
|
|
|
|
|
if($comp->vertical_alignment eq 'bottom') { |
102
|
|
|
|
|
|
|
$y = $bbox->height - $log->{height} / 2; |
103
|
|
|
|
|
|
|
} elsif($comp->vertical_alignment eq 'center') { |
104
|
|
|
|
|
|
|
$y = $bbox->height / 2 - $log->{height} / 2; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
$context->move_to($x, $y); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
Pango::Cairo::update_layout($context, $layout); |
109
|
|
|
|
|
|
|
Pango::Cairo::show_layout($context, $layout); |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub get_textbox_layout { |
114
|
|
|
|
|
|
|
my ($self, $comp) = @_; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
my $tl = Graphics::Primitive::Driver::CairoPango::TextLayout->new( |
117
|
|
|
|
|
|
|
component => $comp, |
118
|
|
|
|
|
|
|
); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
unless(defined($comp->text)) { |
121
|
|
|
|
|
|
|
$tl->height(0); |
122
|
|
|
|
|
|
|
return $tl; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
my $context = $self->cairo; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
my $font = $comp->font; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
my $fontmap = Pango::Cairo::FontMap->get_default; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
my $desc = Pango::FontDescription->new; |
132
|
|
|
|
|
|
|
$desc->set_family($font->family); |
133
|
|
|
|
|
|
|
$desc->set_variant($font->variant); |
134
|
|
|
|
|
|
|
$desc->set_style($font->slant); |
135
|
|
|
|
|
|
|
$desc->set_weight($font->weight); |
136
|
|
|
|
|
|
|
$desc->set_size(Pango::units_from_double($font->size)); |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
my $layout = Pango::Cairo::create_layout($context); |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
$layout->set_font_description($desc); |
141
|
|
|
|
|
|
|
$layout->set_markup($comp->text); |
142
|
|
|
|
|
|
|
$layout->set_indent(Pango::units_from_double($comp->indent)); |
143
|
|
|
|
|
|
|
$layout->set_alignment($comp->horizontal_alignment); |
144
|
|
|
|
|
|
|
$layout->set_justify($comp->justify); |
145
|
|
|
|
|
|
|
if(defined($comp->wrap_mode)) { |
146
|
|
|
|
|
|
|
$layout->set_wrap($comp->wrap_mode); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
if(defined($comp->ellipsize_mode)) { |
149
|
|
|
|
|
|
|
$layout->set_ellipsize($comp->ellipsize_mode); |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
if(defined($comp->line_height)) { |
153
|
|
|
|
|
|
|
$layout->set_spacing(Pango::units_from_double($comp->line_height - $comp->font->size)); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
my $pcontext = $layout->get_context; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
$fontmap->set_resolution(72); |
159
|
|
|
|
|
|
|
my $options = Cairo::FontOptions->create; |
160
|
|
|
|
|
|
|
$options->set_antialias($font->antialias_mode); |
161
|
|
|
|
|
|
|
$options->set_subpixel_order($font->subpixel_order); |
162
|
|
|
|
|
|
|
$options->set_hint_style($font->hint_style); |
163
|
|
|
|
|
|
|
$options->set_hint_metrics($font->hint_metrics); |
164
|
|
|
|
|
|
|
Pango::Cairo::Context::set_font_options($pcontext, $options); |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
if(defined($comp->direction)) { |
167
|
|
|
|
|
|
|
$pcontext->set_base_dir($comp->direction); |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
my $width = $comp->width ? $comp->inside_width : $comp->minimum_inside_width; |
171
|
|
|
|
|
|
|
$width = -1 if(!defined($width) || ($width == 0)); |
172
|
|
|
|
|
|
|
$layout->set_width(Pango::units_from_double($width)); |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
if($comp->height) { |
175
|
|
|
|
|
|
|
# $layout->set_height(Pango::units_from_double($comp->height)); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
if($comp->angle) { |
179
|
|
|
|
|
|
|
$context->save; |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
my $tw2 = $comp->width / 2; |
182
|
|
|
|
|
|
|
my $th2 = $comp->height / 2; |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
$context->translate($tw2, $th2); |
185
|
|
|
|
|
|
|
$context->rotate($comp->angle); |
186
|
|
|
|
|
|
|
$context->translate(-$tw2, -$th2); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
Pango::Cairo::update_context($context, $pcontext); |
189
|
|
|
|
|
|
|
Pango::Cairo::update_layout($context, $layout); |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
my ($rw, $rh) = $self->_get_bounding_box($context, $layout); |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
$tl->width($rw); |
194
|
|
|
|
|
|
|
$tl->height($rh); |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$context->restore; |
197
|
|
|
|
|
|
|
} else { |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
Pango::Cairo::update_context($context, $pcontext); |
200
|
|
|
|
|
|
|
Pango::Cairo::update_layout($context, $layout); |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
my ($ink, $log) = $layout->get_pixel_extents; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
$tl->width($log->{width}); |
205
|
|
|
|
|
|
|
$tl->height($log->{height}); |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
$tl->_layout($layout); |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
return $tl; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub _get_bounding_box { |
214
|
|
|
|
|
|
|
my ($self, $context, $layout) = @_; |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
my ($lw, $lh) = Pango::Layout::get_size($layout); |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
my $matrix = $context->get_matrix; |
219
|
|
|
|
|
|
|
my @corners = ([0,0], [$lw/1024,0], [$lw/1024,$lh/1024], [0,$lh/1024]); |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# Transform each of the four corners, the find the maximum X and Y |
222
|
|
|
|
|
|
|
# coordinates to create a bounding box |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
my @points; |
225
|
|
|
|
|
|
|
foreach my $pt (@corners) { |
226
|
|
|
|
|
|
|
my ($x, $y) = $matrix->transform_point($pt->[0], $pt->[1]); |
227
|
|
|
|
|
|
|
push(@points, [ $x, $y ]); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
my $maxX = $points[0]->[0]; |
231
|
|
|
|
|
|
|
my $maxY = $points[0]->[1]; |
232
|
|
|
|
|
|
|
my $minX = $points[0]->[0]; |
233
|
|
|
|
|
|
|
my $minY = $points[0]->[1]; |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
foreach my $pt (@points) { |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
if($pt->[0] > $maxX) { |
238
|
|
|
|
|
|
|
$maxX = $pt->[0]; |
239
|
|
|
|
|
|
|
} elsif($pt->[0] < $minX) { |
240
|
|
|
|
|
|
|
$minX = $pt->[0]; |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
if($pt->[1] > $maxY) { |
244
|
|
|
|
|
|
|
$maxY = $pt->[1]; |
245
|
|
|
|
|
|
|
} elsif($pt->[1] < $minY) { |
246
|
|
|
|
|
|
|
$minY = $pt->[1]; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
my $bw = $maxX - $minX; |
251
|
|
|
|
|
|
|
my $bh = $maxY - $minY; |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
return ($bw, $bh); |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
no Moose; |
257
|
|
|
|
|
|
|
1; |
258
|
|
|
|
|
|
|
__END__ |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
=head1 NAME |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
Graphics::Primitive::Driver::CairoPango - Cairo/Pango backend for Graphics::Primitive |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=head1 SYNOPSIS |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
use Graphics::Pritive::Component; |
267
|
|
|
|
|
|
|
use Graphics::Pritive::Component; |
268
|
|
|
|
|
|
|
use Graphics::Primitive::Driver::CairoPango; |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
my $driver = Graphics::Primitive::Driver::CairoPango->new; |
271
|
|
|
|
|
|
|
my $container = Graphics::Primitive::Container->new( |
272
|
|
|
|
|
|
|
width => $form->sheet_width, |
273
|
|
|
|
|
|
|
height => $form->sheet_height |
274
|
|
|
|
|
|
|
); |
275
|
|
|
|
|
|
|
$container->border->width(1); |
276
|
|
|
|
|
|
|
$container->border->color($black); |
277
|
|
|
|
|
|
|
$container->padding( |
278
|
|
|
|
|
|
|
Graphics::Primitive::Insets->new(top => 5, bottom => 5, left => 5, right => 5) |
279
|
|
|
|
|
|
|
); |
280
|
|
|
|
|
|
|
my $comp = Graphics::Primitive::Component->new; |
281
|
|
|
|
|
|
|
$comp->background_color($black); |
282
|
|
|
|
|
|
|
$container->add_component($comp, 'c'); |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
my $lm = Layout::Manager::Compass->new; |
285
|
|
|
|
|
|
|
$lm->do_layout($container); |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
my $driver = Graphics::Primitive::Driver::CairoPango->new( |
288
|
|
|
|
|
|
|
format => 'PDF' |
289
|
|
|
|
|
|
|
); |
290
|
|
|
|
|
|
|
$driver->draw($container); |
291
|
|
|
|
|
|
|
$driver->write('/Users/gphat/foo.pdf'); |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head1 DESCRIPTION |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
This module draws Graphics::Primitive objects using Cairo and Pango. This |
296
|
|
|
|
|
|
|
is a separate distribution due to the Pango requirement. The Pango specific |
297
|
|
|
|
|
|
|
bits will be rolled into the normal Cairo driver at some point. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head1 IMPLEMENTATION DETAILS |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=over 4 |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=item B<Borders> |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
Borders are drawn clockwise starting with the top one. Since cairo can't do |
306
|
|
|
|
|
|
|
line-joins on different colored lines, each border overlaps those before it. |
307
|
|
|
|
|
|
|
This is not the way I'd like it to work, but i'm opting to fix this later. |
308
|
|
|
|
|
|
|
Consider yourself warned. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=back |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=head1 METHODS |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=head2 Constructor |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=over 4 |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=item I<new> |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
Creates a new Graphics::Primitive::Driver::CairoPango object. Requires a format. |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
my $driver = Graphics::Primitive::Driver::CairoPango->new(format => 'PDF'); |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=back |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=head2 Instance Methods |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=over 4 |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=item I<antialias_mode> |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
Set/Get the antialias mode of this driver. Options are default, none, gray and |
333
|
|
|
|
|
|
|
subpixel. |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=item I<cairo> |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
This driver's Cairo::Context object |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=item I<data> |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
Get the data in a scalar for this driver. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=item I<draw> |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
Draws the specified component. Container's components are drawn recursively. |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=item I<format> |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
Get the format for this driver. |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=item I<get_textbox_layout ($font, $textbox)> |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
Returns this driver's implementation of a |
354
|
|
|
|
|
|
|
L<TextLayout|Graphics::Primitive::Driver::TextLayout>. |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
=item I<reset> |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
Reset the driver. |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=item I<surface> |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
Get/Set the surface on which this driver is operating. |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=item I<write> |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
Write this driver's data to the specified file. |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
=back |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=head1 AUTHOR |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
Cory Watson, C<< <gphat@cpan.org> >> |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
Infinity Interactive, L<http://www.iinteractive.com> |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=head1 BUGS |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
Please report any bugs or feature requests to C<bug-geometry-primitive at rt.cpan.org>, or through |
379
|
|
|
|
|
|
|
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Geometry-Primitive>. I will be notified, and then you'll |
380
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
Copyright 2008 by Infinity Interactive, Inc. |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
L<http://www.iinteractive.com> |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
389
|
|
|
|
|
|
|
under the same terms as Perl itself. |