File Coverage

blib/lib/SVG/Calendar.pm
Criterion Covered Total %
statement 238 270 88.1
branch 61 108 56.4
condition 32 70 45.7
subroutine 23 23 100.0
pod 8 8 100.0
total 362 479 75.5


line stmt bran cond sub pod time code
1             package SVG::Calendar;
2              
3             # Created on: 2006-04-22 10:36:43
4             # Create by: ivan
5             # $Id$
6             # # $Revision$, $HeadURL$, $Date$
7             # # $Revision$, $Source$, $Date$
8              
9 4     4   329833 use strict;
  4         13  
  4         119  
10 4     4   23 use warnings;
  4         5  
  4         92  
11 4     4   1650 use version;
  4         7492  
  4         21  
12 4     4   305 use Carp;
  4         8  
  4         232  
13 4     4   26 use Scalar::Util qw/blessed/;
  4         9  
  4         173  
14 4     4   2409 use Data::Dumper qw/Dumper/;
  4         24100  
  4         297  
15 4     4   1705 use Clone qw/clone/;
  4         9698  
  4         250  
16 4     4   1989 use Math::Trig;
  4         52539  
  4         617  
17 4     4   2447 use DateTime::Format::Strptime qw/strptime strftime/;
  4         2157279  
  4         23  
18 4     4   2607 use Template;
  4         75427  
  4         148  
19 4     4   34 use File::ShareDir qw/dist_dir/;
  4         9  
  4         179  
20 4     4   2040 use Readonly;
  4         15307  
  4         226  
21 4     4   6594 use Image::ExifTool qw/ImageInfo/;
  4         214432  
  4         431  
22 4     4   2339 use English '-no_match_vars';
  4         10312  
  4         29  
23 4     4   1479 use base qw/Exporter/;
  4         8  
  4         14827  
24              
25             our $VERSION = version->new('0.3.14');
26             our @EXPORT_OK = qw//;
27              
28             Readonly my $MARGIN_RATIO => 0.04;
29             Readonly my $DAY_COLS => 8;
30             Readonly my $ROUNDING_FACTOR => 0.5;
31             Readonly my $TEXT_OFFSET_Y => 0.1;
32             Readonly my $TEXT_OFFSET_X => 0.15;
33             Readonly my $TEXT_WIDTH_RATIO => 0.1;
34             Readonly my $TEXT_HEIGHT_RATIO => 0.145;
35             Readonly my $MOON_SCALE_WIDTH => 0.3;
36             Readonly my $MOON_SCALE_HEIGHT => 0.8;
37             Readonly my $HEADING_WIDTH_SCALE => 0.8;
38             Readonly my $HEADING_HEIGHT_SCALE => 0.45;
39             Readonly my $HEADING_DOW_WIDTH_SCALE => 2;
40             Readonly my $HEADING_DOW_HEIGHT_SCALE => 0.4;
41             Readonly my $HEADING_WOY_WIDTH_SCALE => 4;
42             Readonly my $HEADING_WOY_HEIGHT_SCALE => 0.9;
43             Readonly my $MAX_WEEK_ROW => 5;
44             Readonly my $MAX_DAYS => 42;
45             Readonly my $INTERVAL_ONE_DAY => DateTime::Duration->new(days => 1);
46             Readonly my $INTERVAL_ONE_WEEK => DateTime::Duration->new(days => 7);
47             Readonly my $INTERVAL_ONE_MONTH => DateTime::Duration->new(months => 1);
48             Readonly my $INTERVAL_ELEVEN_MONTHS => DateTime::Duration->new(months => 11);
49             Readonly my $FULL_MOON => 100;
50             Readonly my $MOON_RADIAL_STEP => 1.34;
51             Readonly my $MOON_AT_NIGHT => DateTime::Duration->new(hours => 20);
52             Readonly my $FULL_CIRCLE_DEGREES => 360;
53             Readonly my $ONE_WEEK => 7;
54              
55             sub new {
56              
57 3     3 1 37825 my ( $class, %param ) = @_;
58 3         29 my $self = clone \%param;
59              
60 3         9 bless $self, $class;
61              
62 3         12 $self->init();
63              
64 3         12 return $self;
65             }
66              
67             sub init {
68 3     3 1 6 my $self = shift;
69 3         11 my %size = $self->get_page();
70 3         15 my %temp = ( page => \%size, xu => $self->{page}{width_unit}, yu => $self->{page}{height_unit}, );
71 3         8 my $height = $self->{page}{height};
72 3         8 my $width = $self->{page}{width};
73 3         7 my $xu = $self->{page}{width_unit};
74 3         8 my $yu = $self->{page}{height_unit};
75 3   33     28 my $xmargin = $self->{page}{margin} || $self->{page}{width} * $MARGIN_RATIO;
76 3   33     46 my $ymargin = $self->{page}{margin} || $self->{page}{height} * $MARGIN_RATIO;
77 3         20 $self->{page}{x_margin} = $xmargin;
78 3         8 $self->{page}{y_margin} = $ymargin;
79 3   50     17 $self->{moon}{xoffset} ||= 0;
80 3   50     16 $self->{moon}{yoffset} ||= 0;
81 3   50     17 $self->{calendar_height} ||= '0.5';
82 3         9 $self->{calendar_height} =~ s/%//exms;
83 3 50       14 if ( $self->{calendar_height} > 1 ) {
84             # assume that the height is a percentage value and divide by 100
85 0         0 $self->{calendar_height} /= 100;
86             }
87 3         6 $self->{classes} = {};
88              
89             # cal bounding box (bb)
90             $temp{bb} = {
91             x => $xmargin,
92             y => ( $height * ( 1 - $self->{calendar_height} ) + $ymargin ),
93 3         23 height => ( $height * $self->{calendar_height} - $ymargin * 2 ),
94             width => ( $width - $xmargin * 2 ),
95             };
96              
97 3         9 my $rows = $MAX_WEEK_ROW + 1;
98 3         21 my $row_height = $temp{bb}{height} / ( $rows + $ROUNDING_FACTOR ) * ( 0.5 + $self->{calendar_height} );
99 3         23 my $row_margin_height = $row_height / ( $rows * 2 );
100 3         7 my $cols = $DAY_COLS;
101 3         18 my $col_width = $temp{bb}{width} / ( $cols + $ROUNDING_FACTOR );
102 3         17 my $col_margin_width = $col_width / ( $cols * 2 );
103              
104             # setup the day boxes row by row
105 3         10 for my $i ( 2 .. $rows ) {
106 15         235 my $row_y = $temp{bb}{y} + $row_margin_height * ( 2 * $i - 1 ) + $row_height * ( $i - 1 );
107              
108             # setup the individual days
109 15         31 for my $j ( 2 .. $cols ) {
110 105         2005 my $x = ( $temp{bb}{x} + $col_margin_width * ( 2 * $j - 1 ) + $col_width * ( $j - 1 ) ) - $col_width / 2;
111 105         151 my $y = $row_y - $row_height / 2;
112 105         236 $temp{cal}[ $i - 1 ][ $j - 1 ] = {
113             x => $x,
114             y => $y,
115             height => $row_height,
116             width => $col_width,
117             text => {
118             x => $x + $col_margin_width * $TEXT_OFFSET_X,
119             y => $y + $row_height * $TEXT_OFFSET_X,
120             length => $col_width * $TEXT_WIDTH_RATIO,
121             class => 'mday ',
122             style => 'font-size: ' . ( $row_height * $TEXT_HEIGHT_RATIO ),
123             },
124             };
125             }
126             }
127              
128             # set up the week day headings
129 3         52 my $count = 1;
130 3         8 for my $day (qw/Mon Tue Wed Thu Fri Sat Sun/) {
131 21         49 my $x = $temp{bb}{x} + $col_margin_width * ( 2 * $count + 1 ) + $col_width * ( $count - 1 ) + $col_width / 2;
132 21         38 my $y = $temp{bb}{y} + $row_margin_height;
133             $temp{cal}[0][$count] = {
134             x => $x,
135             y => $y,
136             height => $row_height * $self->{calendar_height},
137 21         58 width => $col_width,
138             text => {
139             text => $day,
140             x => $x + $col_width / $HEADING_DOW_WIDTH_SCALE,
141             y => $y + $row_height * $HEADING_DOW_HEIGHT_SCALE,
142             length => $col_width * $HEADING_WIDTH_SCALE,
143             adjust => 'spacing', #AndGlyphs',
144             class => 'day ' . lc $day,
145             style => 'font-size: ' . ( $row_height * $HEADING_HEIGHT_SCALE ),
146             },
147             };
148 21         463 $count++;
149             }
150              
151             # set up the week of the year column
152 3         8 $count = 1;
153 3         20 for my $week ( 1 .. $MAX_WEEK_ROW ) {
154 15         38 my $x = $temp{bb}{x} + $col_margin_width;
155 15         34 my $y = $temp{bb}{y} + $row_margin_height * ( 2 * $count + 1 ) + $row_height * ( $count - 1 ) + $row_height / 2;
156 15         35 $temp{cal}[$count][0] = {
157             x => $x,
158             y => $y,
159             height => $row_height,
160             width => $col_width / 2,
161             text => {
162             text => $week,
163             x => $x + $col_width / $HEADING_WOY_WIDTH_SCALE,
164             y => $y + $row_height * $HEADING_WOY_HEIGHT_SCALE,
165             length => $col_width * $HEADING_WIDTH_SCALE,
166             adjust => 'spacing', #AndGlyphs',
167             class => 'week',
168             style => 'font-size: ' . ( $row_height * $HEADING_HEIGHT_SCALE ),
169             },
170             };
171 15         278 $count++;
172             }
173              
174             # get the month display stuff
175             $temp{month} = {
176             x => $temp{bb}{x} + $col_margin_width * 2,
177 3         31 y => $temp{bb}{y} - $ymargin/2,
178             style => 'font-size: ' . ($row_height),
179             };
180              
181             # set up the year display
182             $temp{year} = {
183             x => $temp{bb}{x} + $temp{bb}{width},
184 3         26 y => $temp{bb}{y} - $ymargin/2,
185             style => 'text-align: end; text-anchor: end; font-size: ' . $row_height,
186             };
187              
188 3         9 $self->{template} = \%temp;
189              
190 3         11 return;
191             }
192              
193             sub get_page {
194              
195 20     20 1 5417 my $self = shift;
196 20 100       102 my $page = ref $self->{page} ? $self->{page}{page} : $self->{page};
197 20         79 my %size = ( width => '210.00mm', height => '297.00mm' );
198              
199 20 100       66 if ($page) {
200 2 0       16 %size =
    0          
    50          
    100          
    50          
    50          
    50          
201             $page eq 'A0' ? ( width => '840.00mm', height => '1188.00mm' )
202             : $page eq 'A1' ? ( width => '594.00mm', height => '840.00mm' )
203             : $page eq 'A2' ? ( width => '420.00mm', height => '594.00mm' )
204             : $page eq 'A3' ? ( width => '297.00mm', height => '420.00mm' )
205             : $page eq 'A4' ? ( width => '210.00mm', height => '297.00mm' )
206             : $page eq 'A5' ? ( width => '148.50mm', height => '210.00mm' )
207             : $page eq 'A6' ? ( width => '105.00mm', height => '148.50mm' )
208             : croak "Unknown page type '$page'!\n";
209             }
210              
211 20 50 66     113 if ( ref $self->{page} && $self->{page}{width} ) {
212 15         36 $size{width} = $self->{page}{width};
213             }
214 20 50 66     88 if ( ref $self->{page} && $self->{page}{height} ) {
215 15         37 $size{height} = $self->{page}{height};
216             }
217              
218             # Get the values to internal variables
219 20         220 my ( $width, $width_unit ) = $size{width} =~ /\A(.+?)(px|pt|mm|cm|m|in)?\Z/xms;
220 20         99 $width *= 1.0;
221 20 50       62 croak "Unable to get a width from $self->{page} or $self->{width}" if !$width;
222 20   100     81 $width_unit ||= 'px';
223              
224 20         117 my ( $height, $height_unit ) = $size{height} =~ /\A(.+?)(px|pt|mm|cm|m|in)?\Z/xms;
225 20         52 $height *= 1.0;
226 20 50       48 croak "Unable to get a height from $self->{page} or $self->{height}" if !$height;
227 20   100     85 $height_unit ||= 'px';
228              
229             # store the internal variables
230 20 100       58 if ( !ref $self->{page} ) {
231 5         13 $self->{page} = {};
232             }
233 20         46 $self->{page}{width} = $width;
234 20         38 $self->{page}{width_unit} = $width_unit;
235 20         45 $self->{page}{height} = $height;
236 20         37 $self->{page}{height_unit} = $height_unit;
237              
238             return (
239 20         122 width => $width,
240             width_unit => $width_unit,
241             height => $height,
242             height_unit => $height_unit,
243             );
244             }
245              
246             sub output_year {
247              
248 1     1 1 5 my ( $self, @params ) = @_;
249 1         3 my $file = pop @params;
250 1         3 my ( $start, $end ) = @params;
251              
252 1 50       3 return if !$start;
253              
254 1 50       3 if ($end) {
255 0         0 $start = strptime('%F', "$start-01");
256 0         0 $end = strptime('%F', "$end-01");
257             }
258             else {
259 1         8 $start = strptime('%F', "$start-01-01");
260 1         2046 $end = $start + $INTERVAL_ELEVEN_MONTHS;
261             }
262              
263 1         974 my @files;
264 1         5 while ( $start <= $end ) {
265 12         12744 my $month = $start->strftime('%Y-%m');
266 12         711 push @files, "$file-$month.svg";
267 12         55 $self->output_month( $month, "$file-$month.svg" );
268 12         139 $start += $INTERVAL_ONE_MONTH;
269             }
270              
271 1         1100 return @files;
272             }
273              
274             sub output_month {
275              
276 13     13 1 36 my ( $self, $month, $file, ) = @_;
277              
278             # add the month specific details to a clone of the general settings
279 13         7974 my $templ = clone $self->{template};
280 13         296 my %size = $self->get_page();
281 13         37 $self->{full_moon} = 0;
282              
283 13 50 33     93 carp "Month '$month' is not the correct format (YYYY-MM) " if !$month || $month !~ /\A\d{4}-\d{2}\Z/xms;
284              
285 13         93 my $date = strptime('%F', "$month-01");
286 13         27069 $templ->{year}{text} = $date->year();
287 13         114 $templ->{month}{text} = $date->month_name();
288 13         193 my $month_day = $date - $INTERVAL_ONE_WEEK;
289 13         14467 my $row = 1;
290 13         26 my $wrap = 0;
291              
292             # make sure that we start on a monday
293 13         51 while ( $month_day->wday() != 2 ) {
294 35         22617 $month_day += $INTERVAL_ONE_DAY;
295             }
296              
297             DAY:
298 13         9112 for my $count ( 1 .. $MAX_DAYS ) {
299              
300             # get the day of the week (of the first day of the month)
301 466         1563 my $wday = $month_day->wday();
302 466 100       2181 $wday = $wday == 1 ? $ONE_WEEK : $wday - 1;
303 466         2208 my $r = $templ->{cal}[$row][$wday]{width} / $DAY_COLS;
304 466 50       3042 if ( $self->{moon}{radius} ) {
305 0         0 $r *= $self->{moon}{radius};
306             }
307              
308 466         1100 $templ->{cal}[$row][$wday]{text}{text} = $month_day->mday();
309 466 100       2857 $templ->{cal}[$row][$wday]{current} = $date->month() == $month_day->month() ? 1 : 2;
310 466 100       3986 if ( $date->month() == $month_day->month() ) {
311 397         3399 $templ->{cal}[$row][$wday]{text}{class} .= 'current_month';
312             }
313              
314 466 50 33     2392 if ( $self->{moon} && $self->{moon}{display} ) {
315              
316             # get the phase info at 8:00pm
317 0         0 my $moon_date = $month_day + $MOON_AT_NIGHT;
318 0         0 my $phase = $self->get_moon_phase($moon_date);
319             $templ->{cal}[$row][$wday]{moon} = $self->moon(
320             phase => $phase,
321             id => 'moon_' . $month_day->strftime('%Y-%m-%d'),
322             x => $templ->{cal}[$row][$wday]{x} + $r + $templ->{cal}[$row][$wday]{width} * $MOON_SCALE_WIDTH + $self->{moon}{xoffset},
323             y => $templ->{cal}[$row][$wday]{y} - $r + $templ->{cal}[$row][$wday]{height} * $MOON_SCALE_HEIGHT + $self->{moon}{yoffset},
324 0         0 r => $r,
325             );
326             }
327              
328 466 100       1096 if ( $wday == $ONE_WEEK ) {
329 65         316 $row++;
330             }
331 466 100       2396 if ( $row > $MAX_WEEK_ROW ) {
332 13         78 $row = 1;
333 13         22 $wrap = 1;
334             }
335 466         2511 $month_day += $INTERVAL_ONE_DAY;
336              
337             # stop if we leave the current month.
338 466 100 100     416770 last DAY if $wrap && $date->month() != $month_day->month();
339             }
340              
341             # process the image if present
342 13 0 0     174 if ( $self->{image} && ( $self->{image}{src} || $self->{image}{$month} ) ) {
      33        
343 0   0     0 my $image = $self->{image}{$month} || $self->{image}{src};
344 0         0 $templ->{image}{src} = $image;
345              
346 0         0 $templ->{image}{x} = $self->{page}{x_margin};
347 0         0 $templ->{image}{y} = $self->{page}{y_margin};
348              
349 0 0       0 if ( -f $image ) {
350 0         0 my $info = ImageInfo($image);
351 0 0 0     0 if ( $info->{ImageHeight} && $info->{ImageWidth} ) {
352 0         0 $templ->{image}{x} = $self->{page}{x_margin};
353 0         0 $templ->{image}{y} = $self->{page}{y_margin};
354 0         0 $templ->{image}{width} = $self->{page}{width} - 2 * $self->{page}{x_margin};
355 0         0 $templ->{image}{height} = $self->{page}{height} * (1 - $self->{calendar_height}) - $self->{page}{y_margin} * 2;
356              
357 0         0 my $image_scale = $info->{ImageHeight} / $info->{ImageWidth};
358 0         0 my $page_scale = $templ->{image}{height} / $templ->{image}{width};
359              
360 0 0       0 if ($image_scale < $page_scale) {
361 0         0 $templ->{image}{y} -= ( $templ->{image}{height} - ( $templ->{image}{height} * $page_scale / $image_scale ) ) / 2;
362 0         0 $templ->{image}{height} *= $image_scale / $page_scale;
363             }
364             else {
365 0         0 $templ->{image}{x} += ( $templ->{image}{width} - ( $templ->{image}{width} * $page_scale / $image_scale ) ) / 2;
366 0         0 $templ->{image}{width} *= $page_scale / $image_scale;
367             }
368             }
369             else {
370 0         0 die "The image $image doesn't apear to have a height or width\n";
371             }
372             }
373             }
374              
375 13         64 return $self->output( $file, $templ );
376             }
377              
378             sub output {
379              
380 14     14 1 829 my ( $self, $file, $template ) = @_;
381              
382 14         25 my $fh;
383 14         49 my %option = ( EVAL_PERL => 1 );
384 14   33     71 $option{INCLUDE_PATH} = $self->{INCLUDE_PATH} || dist_dir('SVG-Calendar');
385 14 50       54 if ( $self->{path} ) {
386 0         0 $option{INCLUDE_PATH} .= ':' . $self->{path};
387             }
388              
389 14   66     70 my $tmpl = $self->{tt} || Template->new(%option);
390              
391 14         22686 my $text;
392 14 50       42 print Dumper($template) if $self->{verbose};
393              
394 14 50       76 $tmpl->process( 'calendar.svg', $template, \$text )
395             or croak( $tmpl->error );
396              
397 14 100       1955 if ($file) {
398 12 50       37 if ( $file eq q/-/ ) {
399 0 0       0 print $text or carp "Could not write to STDOUT: $OS_ERROR\n";
400             }
401             else {
402 12 50       1482 open $fh, q/>/, $file or croak "Cannot write SVG to file '$file': $!\n";
403              
404 12 50       43 print {$fh} $text or carp "Could not write to file '$file': $OS_ERROR\n";
  12         866  
405              
406 12 50       374 close $fh or carp "There was an issue closing file '$file': $OS_ERROR\n";
407              
408 12 50 33     252 if ( -f $file && $self->{inkscape} ) {
409 0 0       0 if ( $self->{inkscape}{pdf} ) {
410             # get inkscape to convert svg to PDF
411             }
412 0 0       0 if ( $self->{inkscape}{print} ) {
413             # get inkscape to print out the document
414             }
415             }
416             }
417             }
418              
419 14         45 $self->{tt} = $tmpl;
420 14         1019 return $text;
421             }
422              
423             sub moon {
424 31     31 1 284 my ( $self, %params ) = @_;
425              
426 31         68 my $phase = $params{phase};
427 31         71 my $id = $params{id};
428 31   33     90 my $x = $params{x} || $FULL_MOON;
429 31   33     76 my $y = $params{y} || $FULL_MOON;
430 31   33     77 my $r = $params{r} || $FULL_MOON;
431 31         49 my $class = q//;
432              
433             # approx error of less than one lunar day
434 31         43 my $error = 2 * pi / 56; ## no critic
435              
436 31         66 my $moon = { id => $id };
437              
438             # moon phases 0 == new moon 3 == last quarter
439              
440 31         65 my ( $sx, $sy ) = ( $x, $y );
441 31         63 my ( $ex, $ey ) = ( $x, $y + 2 * $r );
442              
443 31 100 66     279 if ( $phase < $error || 2 * pi - $error < $phase ) {
    100 100        
    100          
    50          
444 1         11 $class = ' new-moon';
445             }
446             elsif ( pi - $error < $phase && $phase < pi + $error ) {
447              
448             # approx full moon
449 1 50       7 my $moon_type = $self->{full_moon}++ ? 'blue-moon' : 'full-moon';
450             $moon->{highlight} = {
451 1         9 type => 'circle',
452             id => $id,
453             class => $moon_type,
454             cx => $x,
455             cy => ( $sy + $ey ) / 2,
456             r => $r,
457             };
458             }
459             elsif ( $phase < pi ) {
460              
461             # moon waxing partial
462 16         56 my $d = "M $sx\t$sy C ";
463 16         468 $d .= ( $sx + $r * $MOON_RADIAL_STEP ) . q/ / . $sy . q/,/;
464 16         278 $d .= ( $sx + $r * $MOON_RADIAL_STEP ) . q/ / . $ey;
465 16         121 $d .= ",$ex\t$ey C ";
466 16         37 $d .= ( $ex - $r * $MOON_RADIAL_STEP * ( -cos($phase) ) ) . q/ / . ( $ey + $r / 2 * ( -sin($phase) ) ) . q/,/;
467 16         210 $d .= ( $ex - $r * $MOON_RADIAL_STEP * ( -cos($phase) ) ) . q/ / . ( $sy - $r / 2 * ( -sin($phase) ) );
468 16         209 $d .= ", $sx\t$sy Z";
469             $moon->{highlight} = {
470 16         77 type => 'path',
471             id => $id,
472             d => $d,
473             };
474             }
475             elsif ( $phase > pi ) {
476              
477             # moon waning partial
478 13         44 my $d = "M $sx\t$sy C ";
479 13         81 $d .= ( $sx - $r * $MOON_RADIAL_STEP ) . q/ / . $sy . q/,/;
480 13         152 $d .= ( $sx - $r * $MOON_RADIAL_STEP ) . q/ / . $ey;
481 13         105 $d .= ",$ex\t$ey C ";
482 13         32 $d .= ( $ex + $r * $MOON_RADIAL_STEP * ( -cos($phase) ) ) . q/ / . ( $ey - $r / 2 * ( -sin($phase) ) ) . q/,/;
483 13         148 $d .= ( $ex + $r * $MOON_RADIAL_STEP * ( -cos($phase) ) ) . q/ / . ( $sy + $r / 2 * ( -sin($phase) ) );
484 13         123 $d .= ", $sx\t$sy Z";
485             $moon->{highlight} = {
486 13         54 type => 'path',
487             id => $id,
488             d => $d,
489             };
490             }
491              
492             $moon->{border} = {
493 31         224 id => "moon_border_$id",
494             class => "outline$class",
495             cx => $x,
496             cy => ( $sy + $ey ) / 2,
497             r => $r,
498             };
499              
500 31         172 return $moon;
501             }
502              
503             sub get_moon_phase {
504              
505 93     93 1 33036 my ( $self, $date ) = @_;
506              
507 93 50 33     389 if ( !blessed $date || !$date->isa('DateTime') ) {
508 93         386 $date = strptime('%F %T', "$date 20:00:00");
509             }
510              
511 93 50       179302 if ( !$date ) {
512 0         0 carp 'Unable to create a date!';
513             }
514              
515             # check if we have a way to calculate the phase of the moon
516 93 100       649 if ( !$self->{moon_phase} ) {
517 1         5 my @packages = qw/Astro::Coord::ECI::Moon Astro::MoonPhase/;
518              
519             PACKAGE:
520 1         3 for my $package (@packages) {
521 1         3 my $package_file = $package;
522 1         6 $package_file =~ s{::}{/}gxms;
523              
524 1         3 eval{ require $package_file . '.pm' }; ## no critic
  1         10  
525 1 50       4 if ( !$EVAL_ERROR ) {
526 1         4 $self->{moon_phase} = $package;
527 1         27 last PACKAGE;
528             }
529             }
530              
531             # croak if there is no way to calculate the phase of the moon
532 1 50       5 if ( !$self->{moon_phase} ) {
533 0         0 die "Cannot find any packages installed to calculate the moon phase\nTry installing one of:\ncpan "
534             . join( "\ncpan ", @packages ) . "\n";
535             }
536             }
537              
538 93         143 my $phase;
539 93 100       321 if ( $self->{moon_phase} eq 'Astro::Coord::ECI::Moon' ) {
    50          
540              
541             # phase in radians
542 62         194 $phase = Astro::Coord::ECI::Moon->phase( $date->epoch() );
543             }
544             elsif ( $self->{moon_phase} eq 'Astro::MoonPhase' ) {
545              
546             # phase in fraction of circle
547 31         135 ($phase) = Astro::MoonPhase::phase( $date->epoch() );
548 31         5960 $phase *= 2 * pi;
549             }
550              
551 93         144877 return $phase;
552             }
553              
554             1;
555              
556             __DATA__
557              
558             =head1 NAME
559              
560             SVG::Calendar - Creates calendars in SVG format which can be printed
561              
562             =head1 VERSION
563              
564             This documentation refers to SVG::Calendar version 0.3.14.
565              
566             =head1 SYNOPSIS
567              
568             use SVG::Calendar;
569              
570             # Brief but working code example(s) here showing the most common usage(s)
571             # This section will be as far as many users bother reading, so make it as
572             # educational and exemplary as possible.
573              
574             # Create a new (basic) SVG::Calendar object for producing A4 calendars
575             my $svg = SVG::Calendar->new( page => 'A4' );
576              
577             # print to standard out the calendar for June 2006
578             print $svg->output_month( '2006-06' );
579              
580             # create a calendar for the year 2007 with filenames
581             # my-calendar-2015-01.svg
582             # ...
583             # my-calendar-2015-12.svg
584             $svg->output_year( '2007', 'my-calendar' );
585              
586             =head1 DESCRIPTION
587              
588             This module generates an SVG image for one or more months for a calendar.
589              
590             =head1 SUBROUTINES/METHODS
591              
592             =head3 C<new ( %args )>
593              
594             Arg: C<page> - hash ref - description
595              
596             Arg: C<moon> - hash ref - description
597              
598             Arg: C<image> - hash ref - description
599              
600             Arg: C<path> - string - Directory containing alternate svg template version
601              
602             Arg: C<inkscape> - hash ref - Use inkscape to convert the SVG to a PDF or to
603             print out the generated SVG calendar.
604              
605             Return: SVG::Calendar - A new SVG::Calendar object
606              
607             Description: Creates and sets up a new SVG::Calendar object
608              
609             =head3 C<init ( )>
610              
611             Initialises the calendar object
612              
613             =head3 C<get_page ( )>
614              
615             Return: hash - contains the page height and width and the units used
616              
617             Description: Gets the dimensions of the page based on the parameters
618             supplied at creation time
619              
620             =head3 C<output_year ( ($start, $end | $year), $file )>
621              
622             Param: C<$start> - string ('YYYY-MM') - description
623              
624             Param: C<$end> - string ('YYYY-MM') - description
625              
626             Param: C<$year> - int (year) - description
627              
628             Param: C<$file> - string - The base name for the SVG files calendars for each
629             year
630              
631             Return: list - A list of the files created
632              
633             Description: Creates the SVG calendar files for each month of the year (or for
634             each month from start and end)
635              
636             eg $svg->output_year( 2006, 'flowers' );
637              
638             Will result in the following files created
639              
640             flowers-2006-01.svg
641             flowers-2006-02.svg
642             ..
643             flowers-2006-11.svg
644             flowers-2006-12.svg
645              
646             =head3 C<output_month ( $month, $file, )>
647              
648             Param: C<$month> - string (detail) - The month that the calendar page should
649             display (format YYYY-MM)
650              
651             Param: C<$file> - string (detail) - The file to save the output to if defined.
652             if $file eq '-' prints to STDOUT
653              
654             Return: string - The SVG text to display the calendar page
655              
656             Description: Outputs a particular months calendar...
657              
658             (Adds the week of the year and the
659              
660             =head3 C<output ( $file )>
661              
662             Param: C<$file> - string (detail) - The file name to print the SVG file to (if undefined it will print nothing)
663              
664             Return: scalar - The SVG text.
665              
666             Description:
667              
668             <path
669             style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.25000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
670             d="M 264.88031,225.97672 C 518.24408,341.14207 267.18361,490.85702 267.18361,490.85702 L 264.88031,225.97672 z "
671             id="path1460"
672             sodipodi:nodetypes="ccc" />
673             <path
674             style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.25000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
675             d="M 628.80282,189.12380 C 854.52691,299.68254 847.54045,393.30582 626.49951,477.03718 C 579.56639,494.81567 769.30455,334.23215 628.80282,189.12380 z "
676             id="path1464"
677             sodipodi:nodetypes="csc" />
678             <path
679             style="fill: green; fill-opacity: 0.25; stroke: black;"
680             d="M 0 0 C 133.3333 8, 133.3333 192, 0 200 C -133.333 192 -133.3333 8 Z"
681             M 0 0 C 133.3333 8 133.3333 192, 0 200 C -133.333 192 -133.3333 8 Z
682             id="test" />
683             <circle
684             style="fill: none; stroke: red; stroke-opacity: 0.5"
685             cx="0"
686             cy="100"
687             r="100"
688             id="circle" />
689              
690             =head3 C<moon ( %params )>
691              
692             Param: C<phase> - float - 0 <= $phase < 2 * pi, represents the phase of the moon
693              
694             Param: C<id> - string - The id that the moon SVG part should use
695              
696             Param: C<x> - float - The X coordinate of the left hand side of the moon to be drawn
697              
698             Param: C<y> - float - The Y coordinate of the top side of the moon to be drawn
699              
700             Param: C<r> - float - The Radius of the the moon to be drawn
701              
702             Return: SVG part - The SVG to display the moon in the phase passed in
703              
704             Description: From the phase information this function calculates the details
705             of the curve to represent the phase of the moon and puts it on the diagram
706             based on the x, y and r parameters.
707              
708             =head3 C<get_moon_phase ( $date )>
709              
710             Param: C<$date> - date (DateTime object or string to convert to one) - The
711             date that the moon phase is desired
712              
713             Return: float - The phase of the moon from 0 (new moon) via 2 (full moon) to
714             < 4 (next new moon)
715              
716             Description: This method calculates the phase of the moon (it will what ever
717             it can find to calculate the phase)
718              
719             =head1 DIAGNOSTICS
720              
721             =head1 CONFIGURATION AND ENVIRONMENT
722              
723             =head1 DEPENDENCIES
724              
725             =head1 INCOMPATIBILITIES
726              
727             =head1 BUGS AND LIMITATIONS
728              
729             There are no known bugs in this module.
730              
731             Please report problems to Ivan Wills (ivan.wills@gmail.com).
732              
733             Patches are welcome.
734              
735             =head1 AUTHOR
736              
737             Ivan Wills - (ivan.wills@gmail.com)
738             <Author name(s)> (<contact address>)
739              
740             =head1 LICENSE AND COPYRIGHT
741              
742             Copyright (c) 2006-2009 Ivan Wills (14 Mullion Close, Hornsby Heights, NSW Australia 2077)
743             All rights reserved.
744              
745              
746             This module is free software; you can redistribute it and/or modify it under
747             the same terms as Perl itself. See L<perlartistic>. This program is
748             distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
749             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
750             PARTICULAR PURPOSE.
751              
752             =cut