File Coverage

blib/lib/PDF/Report.pm
Criterion Covered Total %
statement 9 359 2.5
branch 0 90 0.0
condition 0 44 0.0
subroutine 3 42 7.1
pod 37 39 94.8
total 49 574 8.5


line stmt bran cond sub pod time code
1             package PDF::Report;
2 1     1   21836 use strict;
  1         2  
  1         54  
3              
4             =head1 NAME
5              
6             PDF::Report - A wrapper written for PDF::API2
7              
8             =head1 SYNOPSIS
9              
10             use PDF::Report;
11              
12             my $pdf = new PDF::Report(%opts);
13              
14             =head1 DESCRIPTION
15              
16             This is a wrapper for Alfred Reibenschuh's PDF::API2
17             Defines methods to create PDF reports
18              
19             =head1 VERSION
20              
21             1.36
22              
23             =cut
24              
25             our $VERSION = "1.36";
26              
27 1     1   6 use strict;
  1         2  
  1         26  
28 1     1   1032 use PDF::API2;
  1         398670  
  1         3899  
29              
30             ### GLOBAL SECTION ############################################################
31             # Sane defaults
32             my %DEFAULTS;
33             $DEFAULTS{PageSize}='letter';
34             $DEFAULTS{PageOrientation}='Portrait';
35             $DEFAULTS{Compression}=1;
36             $DEFAULTS{PdfVersion}=3;
37             $DEFAULTS{marginX}=30;
38             $DEFAULTS{marginY}=30;
39             $DEFAULTS{font}="Helvetica";
40             $DEFAULTS{size}=12;
41              
42             # Document info
43             my @parameterlist=qw(
44             PageSize
45             PageWidth
46             PageHeight
47             PageOrientation
48             Compression
49             PdfVersion
50             );
51             ### END GLOBALS ###############################################################
52              
53             ### GLOBAL SUBS ###############################################################
54              
55             =head1 METHODS
56              
57             =head2 new
58              
59             my $pdf = new PDF::Report(%opts);
60              
61             Creates a new pdf report object.
62             If no %opts are specified the module
63             will use the factory defaults.
64              
65             B
66              
67             my $pdf = new PDF::Report(PageSize => "letter",
68             PageOrientation => "Landscape");
69              
70             my $pdf = new PDF::Report(File => $file);
71              
72             %opts:
73              
74             PageSize - '4A', '2A', 'A0', 'A1', 'A2',
75             'A3', 'A4', 'A5', 'A6', '4B',
76             '2B', 'B0', 'B1', 'B2', 'B3',
77             'B4', 'B5', 'B6', 'LETTER',
78             'BROADSHEET', 'LEDGER', 'TABLOID',
79             'LEGAL', 'EXECUTIVE', '36X36'
80              
81             PageOrientation - 'Portrait', 'Landscape'
82              
83             =cut
84              
85             # Create a new PDF document
86             sub new {
87 0     0 1   my $class = shift;
88 0           my %defaults = @_;
89              
90 0           foreach my $dflt (@parameterlist) {
91 0 0         if (defined($defaults{$dflt})) {
92 0           $DEFAULTS{$dflt} = $defaults{$dflt}; # Overridden from user
93             }
94             }
95              
96 0           my $pageWidth;
97             my $pageHeight;
98 0           my $x1;
99 0           my $y1;
100 0 0         if ( ref $DEFAULTS{PageSize} eq "ARRAY" ) {
101 0           ($pageWidth, $pageHeight) = @{$DEFAULTS{PageSize}};
  0            
102             }
103             else {
104             # Set the width and height of the page
105 0           ($x1, $y1, $pageWidth, $pageHeight) =
106             PDF::API2::Util::page_size($DEFAULTS{PageSize});
107             }
108              
109             # Swap w and h if landscape
110 0 0         if (lc($DEFAULTS{PageOrientation})=~/landscape/) {
111 0           my $tempW = $pageWidth;
112 0           $pageWidth = $pageHeight;
113 0           $pageHeight = $tempW;
114 0           $tempW = undef;
115             }
116              
117 0           my $MARGINX = $DEFAULTS{marginX};
118 0           my $MARGINY = $DEFAULTS{marginY};
119 0           my ( $day, $month, $year )= ( localtime( time ) )[3..5];
120 0           my $DATE=sprintf "%02d/%02d/%04d", ++$month, $day, 1900 + $year;
121              
122             # May not need alot of these, will review later
123 0           my $self= { #pdf => PDF::API2->new(),
124             hPos => undef,
125             vPos => undef,
126             size => 12, # Default
127             font => undef, # the font object
128             PageWidth => $pageWidth,
129             PageHeight => $pageHeight,
130             Xmargin => $MARGINX,
131             Ymargin => $MARGINY,
132             BodyWidth => $pageWidth - $MARGINX * 2,
133             BodyHeight => $pageHeight - $MARGINY * 2,
134             page => undef, # the current page object
135             page_nbr => 1,
136             align => 'left',
137             linewidth => 1,
138             linespacing => 0,
139             FtrFontName => 'Helvetica-Bold',
140             FtrFontSize => 11,
141             MARGIN_DEBUG => 0,
142             PDF_API2_VERSION => $PDF::API2::VERSION,
143             INFO => {
144             Creator => "None",
145             Producer => "None",
146             CreationDate => $DATE,
147             Title => "Untitled",
148             Subject => "None",
149             Author => "Auto-generated",
150             },
151              
152             ########################################################
153             # Cache for font object caching -- used by setFont() ###
154             ########################################################
155             __font_cache => {},
156             DATE => $DATE,
157             };
158              
159 0 0 0       if (defined $defaults{File} && length($defaults{File})) {
160 0 0         $self->{pdf} = PDF::API2->open($defaults{File})
161             or die "$defaults{File} not found: $!\n";
162             } else {
163 0           $self->{pdf} = PDF::API2->new();
164             }
165              
166             # Default fonts
167 0           $self->{font} = $self->{pdf}->corefont('Helvetica'); # Default font object
168             #$self->{font}->encode('latin1');
169              
170             # Set the users options
171 0           foreach my $key (keys %defaults) {
172 0           $self->{$key}=$defaults{$key};
173             }
174              
175 0           bless $self, $class;
176              
177 0           return $self;
178             }
179              
180             =head2 newpage
181              
182             $pdf->newpage($nopage);
183              
184             Creates a new blank page. Pass $nopage = 1 to toggle page numbering.
185              
186             =cut
187              
188             sub newpage {
189 0     0 1   my $self = shift;
190 0           my $no_page_number = shift;
191              
192             # make a new page
193 0           $self->{page} = $self->{pdf}->page;
194 0           $self->{page}->mediabox($self->{PageWidth}, $self->{PageHeight});
195              
196             # Handle the page numbering if this page is to be numbered
197 0           my $total = $self->pages;
198 0           push(@{$self->{no_page_num}}, $no_page_number);
  0            
199              
200 0           $self->{page_nbr}++;
201 0           return(0);
202             }
203              
204             =head2 openpage
205              
206             $pdf->openpage($index);
207              
208             If no index is specified, this will open the last page of the document.
209              
210             =cut
211              
212              
213             sub openpage {
214 0     0 1   my $self = shift;
215 0           my $index = shift;
216 0           my $totpgs = $self->{pdf}->pages;
217              
218 0 0 0       $index = $totpgs if (!defined $index or
      0        
219             $index =~ /[^\d]/ or
220             $index > $totpgs);
221              
222 0           $self->{page} = $self->{pdf}->openpage($index);
223             }
224              
225             =head2 importpage
226              
227             Import page from another PDF document, see PDF::API2
228              
229             =cut
230              
231             sub importpage {
232 0     0 1   my $self = shift;
233 0           my $sourcepdf = shift;
234 0           my $sourceindex = shift;
235 0           my $targetindex = shift; # can be a page object
236              
237             # my $source = $self->{pdf}->open($sourcepdf);
238              
239 0           $self->{page} = $self->{pdf}->importpage($sourcepdf, $sourceindex,
240             $targetindex);
241             }
242              
243             =head2 clonepage
244              
245             Clone page within document, see PDF::API2
246              
247             =cut
248              
249              
250             sub clonepage {
251 0     0 1   my $self = shift;
252 0           my $sourceindex = shift;
253 0           my $targetindex = shift;
254              
255 0           $self->{page} = $self->{pdf}->clonepage($sourceindex, $targetindex);
256              
257             }
258              
259             =head2 getPageDimensions
260              
261             ($pagewidth, $pageheight) = $pdf->getPageDimensions();
262              
263             Returns the width and height of the page according to what page size chosen
264             in "new".
265              
266             =cut
267              
268             sub getPageDimensions {
269 0     0 1   my $self = shift;
270              
271 0           return($self->{PageWidth}, $self->{PageHeight});
272             }
273              
274             =head2 addRawText
275              
276             $pdf->addRawText($text, $x, $y, $color, $underline, $indent, $rotate);
277              
278             Add $text at position $x, $y with $color, $underline, $indent and/or $rotate.
279              
280             =cut
281              
282             # This positions string $text at $x, $y
283             sub addRawText {
284 0     0 1   my ( $self, $text, $x, $y, $color, $underline, $indent, $rotate ) = @_;
285              
286 0 0 0       $color = undef if defined $color && !length($color);
287 0 0 0       $underline = undef if defined $underline && !length($underline);
288 0 0 0       $indent = undef if defined $indent && !length($indent);
289              
290 0           my $txt = $self->{page}->text;
291             # $txt->font($self->{font}, $self->{size});
292             # $txt->transform_rel(-translate => [$x, $y], -rotate => $rotate);
293             # $txt->text($text, -color=>[$color], -underline=>$underline,
294             # -indent=>$indent);
295              
296 0           $txt->textlabel($x, $y, $self->{font}, $self->{size}, $text,
297             -rotate => $rotate,
298             -color => $color, -underline=>$underline, -indent=>$indent);
299              
300             }
301              
302             =pod
303              
304             PDF::API2 Removes all space between every word in the string you pass
305             and then rejoins each word with one space. If you want to use a string with
306             more than one space between words for formatting purposes, you can either use
307             the hack below or change PDF::API2 (that's what I did ;). The code below may
308             or may not work according to what font you are using. I used 2 \xA0 per space
309             because that worked for the Helvetica font I was using.
310              
311             B
312              
313             sub replaceSpace {
314             my $text = shift;
315             my $nbsp = "\xA0";
316             my $new = '';
317             my @words = split(/ /, $text);
318             foreach my $word (@words) {
319             if (length($word)) {
320             $new.=$word . ' ';
321             } else {
322             $new.=$nbsp . $nbsp;
323             }
324             }
325             chop($new);
326             return $new;
327             }
328              
329             =head2 setAddTextPos
330              
331             $pdf->setAddTextPos($hPos, $vPos);
332              
333             Set the position on the page. Used by the addText function.
334              
335             =cut
336              
337             sub setAddTextPos {
338 0     0 1   my ($self, $hPos, $vPos) = @_;
339 0           $self->{hPos}=$hPos;
340 0           $self->{vPos}=$vPos;
341             }
342              
343             =head2 getAddTextPos
344              
345             ($hPos, $vPos) = $pdf->getAddTextPos();
346              
347             Return the (x, y) value of the text position.
348              
349             =cut
350              
351             sub getAddTextPos {
352 0     0 1   my ($self) = @_;
353 0           return($self->{hPos}, $self->{vPos});
354             }
355              
356             =head2 setAlign
357              
358             $pdf->setAlign($align);
359              
360             Set the justification of the text. Used by the addText function.
361              
362             =cut
363              
364             sub setAlign {
365 0     0 1   my $self = shift;
366 0           my $align = lc(shift);
367              
368 0 0         if ($align=~m/^left$|^right$|^center$/) {
369 0           $self->{align}=$align;
370 0           $self->{hPos}=undef; # Clear addText()'s tracking of hPos
371             }
372             }
373              
374             =head2 getAlign
375              
376             $align = $pdf->getAlign();
377              
378             Returns the text justification.
379              
380             =cut
381              
382             sub getAlign {
383 0     0 1   my $self= shift @_;
384 0           return($self->{align});
385             }
386              
387             =head2 wrapText
388              
389             $newtext = $pdf->wrapText($text, $width);
390              
391             This is a helper function called by addText, which can be called by itself.
392             wrapText() wraps $text within $width.
393              
394             =cut
395              
396             sub wrapText {
397 0     0 1   my $self = shift;
398 0           my $text = shift;
399 0           my $width = shift;
400              
401 0 0         $text = '' if !length($text);
402              
403 0 0         return $text if ($text =~ /\n/); # We don't wrap text with carriage returns
404 0 0         return $text unless defined $width; # If no width was specified, return text
405              
406 0           my $txt = $self->{page}->text;
407 0           $txt->font($self->{font}, $self->{size});
408              
409 0           my $ThisTextWidth=$txt->advancewidth($text);
410 0 0         return $text if ( $ThisTextWidth <= $width);
411              
412 0           my $widSpace = $txt->advancewidth('t'); # 't' closest width to a space
413              
414 0           my $currentWidth = 0;
415 0           my $newText = "";
416 0           foreach ( split / /, $text ) {
417 0           my $strWidth = $txt->advancewidth($_);
418 0 0         if ( ( $currentWidth + $strWidth ) > $width ) {
419 0           $currentWidth = $strWidth + $widSpace;
420 0           $newText .= "\n$_ ";
421             } else {
422 0           $currentWidth += $strWidth + $widSpace;
423 0           $newText .= "$_ ";
424             }
425             }
426              
427 0           return $newText;
428             }
429              
430             =head2 addText
431              
432             $pdf->addText($text, $hPos, $textWidth, $textHeight);
433              
434             Takes $text and prints it to the current page at $hPos. You may just want
435             to pass this function $text if the text is "pre-wrapped" and setAddTextPos
436             has been called previously. Pass a $hPos to change the position the text
437             will be printed on the page. Pass a $textWidth and addText will wrap the
438             text for you. $textHeight controls the row height.
439              
440             =cut
441              
442             sub addText {
443 0     0 1   my ( $self, $text, $hPos, $textWidth, $textHeight )= @_;
444              
445 0           my $txt = $self->{page}->text;
446 0           $txt->font($self->{font}, $self->{size});
447              
448             # Push the margin on for align=left (need to work on align=right)
449 0 0 0       if ( ($hPos=~/^[0-9]+([.][0-9]+)?$/) && ($self->{align}=~ /^left$/i) ) {
450 0           $self->{hPos}=$hPos + $self->{Xmargin};
451             }
452              
453             # Establish a proper $self->{hPos} if we don't have one already
454 0 0         if ($self->{hPos} !~ /^[0-9]+([.][0-9]+)?$/) {
455 0 0         if ($self->{align}=~ /^left$/i) {
    0          
    0          
456 0           $self->{hPos} = $self->{Xmargin};
457             } elsif ($self->{align}=~ /^right$/i) {
458 0           $self->{hPos} = $self->{PageWidth} - $self->{Xmargin};
459             } elsif ($self->{align}=~ /^center$/i) {
460 0           $self->{hPos} = int($self->{PageWidth} / 2);
461             }
462             }
463              
464             # If the user did not give us a $textWidth, use the distance
465             # from $hPos to the right margin as the $textWidth for align=left,
466             # use the distance from $hPos back to the left margin for align=right
467 0 0 0       if ( ($textWidth !~ /^[0-9]+$/) && ($self->{align}=~ /^left$/i) ) {
    0 0        
    0 0        
468 0           $textWidth = $self->{BodyWidth} - $self->{hPos} + $self->{Xmargin};
469             } elsif ( ($textWidth !~ /^[0-9]+$/) && ($self->{align}=~ /^right$/i) ) {
470 0           $textWidth = $self->{hPos} + $self->{Xmargin};
471             } elsif ( ($textWidth !~ /^[0-9]+$/) && ($self->{align}=~ /^center$/i) ) {
472 0           my $textWidthL=$self->{BodyWidth} - $self->{hPos} + $self->{Xmargin};
473 0           my $textWidthR=$self->{hPos} + $self->{Xmargin};
474 0           $textWidth = $textWidthL;
475 0 0         if ($textWidthR < $textWidth) { $textWidth = $textWidthR; }
  0            
476 0           $textWidth = $textWidth * 2;
477             }
478              
479             # If $self->{vPos} is not set calculate it (on first text add)
480 0 0 0       if ( (!defined $self->{vPos} ) || ($self->{vPos} == 0) ) {
481 0           $self->{vPos} = $self->{PageHeight} - $self->{Ymargin} - $self->{size};
482             }
483              
484             # If the text has no carrige returns we may need to wrap it for the user
485 0 0         if ( $text !~ /\n/ ) {
486 0           $text = $self->wrapText($text, $textWidth);
487             }
488              
489 0 0         if ( $text !~ /\n/ ) {
490             # Determine the width of this text
491 0           my $thistextWidth = $txt->advancewidth($text);
492              
493             # If align ne 'left' (the default) then we need to recalc the xPos
494             # for this call to addRawText() -- needs attention
495 0           my $xPos=$self->{hPos};
496 0 0         if ($self->{align}=~ /^right$/i) {
    0          
497 0           $xPos=$self->{hPos} - $thistextWidth;
498             } elsif ($self->{align}=~ /^center$/i) {
499 0           $xPos=$self->{hPos} - $thistextWidth / 2;
500             }
501 0           $self->addRawText($text,$xPos,$self->{vPos});
502              
503 0 0         $thistextWidth = -1 * $thistextWidth if ($self->{align}=~ /^right$/i);
504 0 0         $thistextWidth = -1 * $thistextWidth / 2 if ($self->{align}=~ /^center$/i);
505 0           $self->{hPos} += $thistextWidth;
506             } else {
507 0           $text=~ s/\n/\0\n/g; # This copes w/strings of only "\n"
508 0           my @lines= split /\n/, $text;
509 0           foreach ( @lines ) {
510 0           $text= $_;
511 0           $text=~ s/\0//;
512 0 0         if (length( $text )) {
513 0           $self->addRawText($text, $self->{hPos}, $self->{vPos});
514             }
515 0 0         if (($self->{vPos} - $self->{size}) < $self->{Ymargin}) {
516 0           $self->{vPos} = $self->{PageHeight} - $self->{Ymargin} - $self->{size};
517 0           $self->newpage;
518             } else {
519 0 0         $textHeight = $self->{size} unless $textHeight;
520 0           $self->{vPos} -= $self->{size} - $self->{linespacing};
521             }
522             }
523             }
524             }
525              
526             =head2 addParagraph
527              
528             $pdf->addParagraph($text, $hPos, $vPos, $width, $height, $indent, $lead);
529              
530             Add $text at ($hPos, $vPos) within $width and $height, with $indent.
531             $indent is the number of spaces at the beginning of the first line.
532              
533             =cut
534              
535             sub addParagraph {
536 0     0 1   my ( $self, $text, $hPos, $vPos, $width, $height, $indent, $lead, $align ) = @_;
537              
538 0   0       $align ||= 'justified';
539 0           my $txt = $self->{page}->text;
540 0           $txt->font($self->{font}, $self->{size});
541              
542             # $txt->paragraph($text, -x => $hPos, -y => $vPos, -w => $width,
543             # -h => $height, -flindent => $indent, -lead => $lead, -rel => 1);
544              
545             # 0.40.x
546 0           $txt->lead($lead); # Line spacing
547 0           $txt->translate($hPos,$vPos);
548 0           $txt->paragraph($text, $width, $height, -align => $align);
549              
550 0           ($self->{hPos},$self->{vPos}) = $txt->textpos;
551             }
552              
553             # Backwards compatibility for that pesky typo
554             sub addParagragh {
555 0     0 0   my ( $self, $text, $hPos, $vPos, $width, $height, $indent, $lead ) = @_;
556              
557 0           $self->addParagraph($text, $hPos, $vPos, $width, $height, $indent, $lead);
558             }
559              
560             =head2 centerString
561              
562             $pdf->centerString($a, $b, $yPos, $text);
563              
564             Centers $text between points $a and $b at position $yPos. Be careful how much
565             text you try to jam between those points, this function shrinks the text till
566             it fits!
567              
568             =cut
569              
570             sub centerString {
571 0     0 1   my $self = shift;
572 0           my $PointBegin = shift;
573 0           my $PointEnd = shift;
574 0           my $YPos = shift;
575 0           my $String = shift;
576              
577 0           my $OldTextSize = $self->getSize;
578 0           my $TextSize = $OldTextSize;
579              
580 0           my $Area = $PointEnd - $PointBegin;
581              
582 0           my $StringWidth;
583 0           while (($StringWidth = $self->getStringWidth($String)) > $Area) {
584 0           $self->setSize(--$TextSize); ### DECREASE THE FONTSIZE TO MAKE IT FIT
585             }
586              
587 0           my $Offset = ($Area - $StringWidth) / 2;
588 0           $self->addRawText("$String",$PointBegin+$Offset,$YPos);
589 0           $self->setSize($OldTextSize);
590             }
591              
592             =head2 setRowHeight
593              
594             =cut
595              
596             sub setRowHeight {
597 0     0 1   my $self = shift;
598 0           my $size = shift; # the fontsize
599              
600 0           return (int($size * 1.20));
601             }
602              
603             =head2 getStringWidth
604              
605             $pdf->getStringWidth($String);
606              
607             Returns the width of $String according to the current font and fontsize being
608             used.
609              
610             =cut
611              
612             # replaces silly $pdf->{pdf}->calcTextWidth calls
613             sub getStringWidth {
614 0     0 1   my $self = shift;
615 0           my $String = shift;
616              
617 0           my $txt = $self->{page}->text;
618 0           $txt->font($self->{font}, $self->{size});
619 0           return $txt->advancewidth($String);
620             }
621              
622             =head2 addImg
623              
624             $pdf->addImg($file, $x, $y);
625              
626             Add image $file to the current page at position ($x, $y).
627              
628             =cut
629              
630             sub addImg {
631 0     0 1   my ( $self, $file, $x, $y ) = @_;
632              
633 0           $self->addImgScaled($file, $x, $y, 1);
634             }
635              
636             =head2 addImgScaled
637              
638             $pdf->addImgScaled($file, $x, $y, $scale);
639              
640             Add image $file to the current page at position ($x, $y) scaled to $scale.
641              
642             =cut
643              
644             sub addImgScaled {
645 0     0 1   my ( $self, $file, $x, $y, $scale ) = @_;
646              
647 0           my %type = (jpeg => "jpeg",
648             jpg => "jpeg",
649             tif => "tiff",
650             tiff => "tiff",
651             pnm => "pnm",
652             gif => "gif",
653             png => "png",
654             );
655              
656 0           $file =~ /\.(\w+)$/;
657 0           my $ext = lc($1);
658              
659 0           my $sub = "image_$type{$ext}";
660 0           my $img = $self->{pdf}->$sub($file);
661 0           my $gfx = $self->{page}->gfx;
662              
663 0           $gfx->image($img, $x, $y, $scale);
664             }
665              
666             =head2 setGfxLineWidth
667              
668             $pdf->setGfxLineWidth($width);
669              
670             Set the line width drawn on the page.
671              
672             =cut
673              
674             sub setGfxLineWidth {
675 0     0 1   my ( $self, $width ) = @_;
676              
677 0           $self->{linewidth} = $width;
678             }
679              
680             =head2 getGfxLineWidth
681              
682             $width = $pdf->getGfxLineWidth();
683              
684             Returns the current line width.
685              
686             =cut
687              
688             sub getGfxLineWidth {
689 0     0 1   my $self = shift;
690              
691 0           return $self->{linewidth};
692             }
693              
694             =head2 drawLine
695              
696             $pdf->drawLine($x1, $y1, $x2, $y2);
697              
698             Draw a line on the current page starting at ($x1, $y1) and ending
699             at ($x2, $y2).
700              
701             =cut
702              
703             sub drawLine {
704 0     0 1   my ( $self, $x1, $y1, $x2, $y2 ) = @_;
705              
706 0           my $gfx = $self->{page}->gfx;
707 0           $gfx->move($x1, $y1);
708 0           $gfx->linewidth($self->{linewidth});
709 0           $gfx->line($x2, $y2);
710 0           $gfx->stroke;
711             }
712              
713             =head2 drawRect
714              
715             $pdf->drawRect($x1, $y1, $x2, $y2);
716              
717             Draw a rectangle on the current page. Top left corner is represented by
718             ($x1, $y1) and the bottom right corner is ($x2, $y2).
719              
720             =cut
721              
722             sub drawRect {
723 0     0 1   my ( $self, $x1, $y1, $x2, $y2 ) = @_;
724              
725 0           my $gfx = $self->{page}->gfx;
726 0           $gfx->linewidth($self->{linewidth});
727 0           $gfx->rectxy($x1, $y1, $x2, $y2);
728 0           $gfx->stroke;
729             }
730              
731             =head2 shadeRect
732              
733             $pdf->shadeRect($x1, $y1, $x2, $y2, $color);
734              
735             Shade a rectangle with $color. Top left corner is ($x1, $y1) and the bottom
736             right corner is ($x2, $y2).
737              
738             =over 4
739              
740             =item B
741              
742             aliceblue, antiquewhite, aqua, aquamarine, azure,
743             beige, bisque, black, blanchedalmond, blue,
744             blueviolet, brown, burlywood, cadetblue, chartreuse,
745             chocolate, coral, cornflowerblue, cornsilk, crimson,
746             cyan, darkblue, darkcyan, darkgoldenrod, darkgray,
747             darkgreen, darkgrey, darkkhaki, darkmagenta,
748             darkolivegreen, darkorange, darkorchid, darkred,
749             darksalmon, darkseagreen, darkslateblue, darkslategray,
750             darkslategrey, darkturquoise, darkviolet, deeppink,
751             deepskyblue, dimgray, dimgrey, dodgerblue, firebrick,
752             floralwhite, forestgreen, fuchsia, gainsboro, ghostwhite,
753             gold, goldenrod, gray, grey, green, greenyellow,
754             honeydew, hotpink, indianred, indigo, ivory, khaki,
755             lavender, lavenderblush, lawngreen, lemonchiffon,
756             lightblue, lightcoral, lightcyan, lightgoldenrodyellow,
757             lightgray, lightgreen, lightgrey, lightpink, lightsalmon,
758             lightseagreen, lightskyblue, lightslategray,
759             lightslategrey, lightsteelblue, lightyellow, lime,
760             limegreen, linen, magenta, maroon, mediumaquamarine,
761             mediumblue, mediumorchid, mediumpurple, mediumseagreen,
762             mediumslateblue, mediumspringgreen, mediumturquoise,
763             mediumvioletred, midnightblue, mintcream, mistyrose,
764             moccasin, navajowhite, navy, oldlace, olive, olivedrab,
765             orange, orangered, orchid, palegoldenrod, palegreen,
766             paleturquoise, palevioletred, papayawhip, peachpuff,
767             peru, pink, plum, powderblue, purple, red, rosybrown,
768             royalblue, saddlebrown, salmon, sandybrown, seagreen,
769             seashell, sienna, silver, skyblue, slateblue, slategray,
770             slategrey, snow, springgreen, steelblue, tan, teal,
771             thistle, tomato, turquoise, violet, wheat, white,
772             whitesmoke, yellow, yellowgreen
773              
774             or the rgb-hex-notation:
775              
776             #rgb, #rrggbb, #rrrgggbbb and #rrrrggggbbbb
777              
778             or the cmyk-hex-notation:
779              
780             %cmyk, %ccmmyykk, %cccmmmyyykkk and %ccccmmmmyyyykkkk
781              
782             and additionally the hsv-hex-notation:
783              
784             !hsv, !hhssvv, !hhhsssvvv and !hhhhssssvvvv
785              
786             =back
787              
788             =cut
789              
790             sub shadeRect {
791 0     0 1   my ( $self, $x1, $y1, $x2, $y2, $color ) = @_;
792              
793 0           my $gfx = $self->{page}->gfx;
794              
795 0           $gfx->fillcolor($color);
796 0           $gfx->rectxy($x1, $y1, $x2, $y2);
797 0           $gfx->fill;
798             }
799              
800             =head2 drawPieGraph
801              
802             $pdf->drawPieGraph($x, $y, $size, $rData, $rLabels);
803              
804             Method to create a piegraph using a reference to an array of values.
805             It also takes a reference to an array for labels for each data value. A
806             legend with all the colors and labels will appear if $rLabels is passed. $x and
807             $y are the coordinates for the center of the pie and $size is the radius.
808              
809             =cut
810              
811             sub drawPieGraph {
812 0     0 1   my $self = shift;
813 0           my $x = shift;
814 0           my $y = shift;
815 0           my $size = shift;
816 0           my $rData = shift;
817 0           my $rLabels = shift;
818              
819 0           my $circ = 360;
820 0           my $a = 0;
821 0           my $b = 0;
822 0           my @colors = &getcolors();
823 0           my $lastclr = $#colors;
824 0           my $gfx = $self->{page}->gfx;
825              
826             # Set up the colors we'll use
827 0           my @clr;
828 0           foreach my $elem ( 0 .. $#$rData ) {
829             # push(@clr, $colors[int(rand($#colors))]);
830 0           push(@clr, $colors[$elem]);
831             }
832              
833             # Add up the numbers
834 0           my $total;
835 0           foreach my $elem ( 0 .. $#$rData ) {
836 0           $total+=$rData->[$elem];
837             }
838             # Get the percentages
839 0           my @perc;
840 0           foreach my $elem ( 0 .. $#$rData ) {
841 0           $perc[$elem] = $rData->[$elem] / $total;
842             }
843              
844             # Draw a pie
845 0           my $cnt = 0;
846 0           foreach my $pct (@perc) {
847 0           $b+=$circ * $pct;
848 0 0         $b = $circ if $b > $circ;
849 0           $gfx->fillcolor($clr[$cnt++]);
850 0           $gfx->pie($x, $y, $size, $size, $a, $b);
851 0           $gfx->fill;
852 0           $a = $b;
853             }
854              
855             # Do we print labels?
856 0 0         if (scalar(@{ $rLabels })) {
  0            
857 0           my $oldfont = $self->getFont();
858 0           my $oldsize = $self->getSize();
859 0           my $fontsize = 12;
860 0           $self->setFont("Times-roman");
861 0           $self->setSize($fontsize);
862 0           my $colorblocksize = 10;
863 0           my $maxsize = 0;
864 0           for (0 .. $#$rLabels) {
865 0 0         $maxsize = $self->getStringWidth($rLabels->[$_])
866             if $self->getStringWidth($rLabels->[$_]) > $maxsize;
867             }
868 0           my $top = $y + ((($#perc + 1) * $fontsize) / 2);
869 0           my $left = $x + $size + 8;
870 0           $self->drawRect($left, $top,
871             $x + $size + 8 + $colorblocksize + $maxsize + 3,
872             $y - ((($#perc + 1) * $fontsize) / 2));
873 0           my $pos = $top - 1;
874 0           $cnt = 0;
875 0           foreach my $nbr (0 .. $#perc) {
876 0           $self->shadeRect($left+1, $pos, $left+1+$colorblocksize,
877             $pos-$colorblocksize, $clr[$cnt++]);
878 0           $self->addRawText($rLabels->[$nbr], $left+$colorblocksize+3,
879             $pos-$colorblocksize);
880 0           $pos-=$fontsize;
881             }
882             }
883             }
884              
885             =head2 getcolors
886              
887             Returns list of available colours
888              
889             =cut
890              
891             sub getcolors {
892 0     0 1   my @cols=qw(
893             red yellow blue green aqua bisque black
894             blueviolet brown burlywood cadetblue chartreuse
895             chocolate coral cornflowerblue cornsilk crimson
896             cyan darkblue darkcyan darkgoldenrod darkgray
897             darkgreen darkgrey darkkhaki darkmagenta
898             darkolivegreen darkorange darkorchid darkred
899             darksalmon darkseagreen darkslateblue darkslategray
900             darkslategrey darkturquoise darkviolet deeppink
901             deepskyblue dimgrey dodgerblue firebrick
902             floralwhite forestgreen fuchsia gainsboro ghostwhite
903             gold goldenrod gray greenyellow
904             honeydew hotpink indianred indigo ivory khaki
905             lavender lavenderblush lawngreen lemonchiffon
906             lightblue lightcoral lightcyan lightgoldenrodyellow
907             lightgray lightgreen lightgrey lightpink lightsalmon
908             lightseagreen lightskyblue lightslategray
909             lightslategrey lightsteelblue lightyellow lime
910             limegreen linen magenta maroon mediumaquamarine
911             mediumblue mediumorchid mediumpurple mediumseagreen
912             mediumslateblue mediumspringgreen mediumturquoise
913             mediumvioletred midnightblue mintcream mistyrose
914             moccasin navajowhite navy oldlace olivedrab
915             orange orangered orchid palegoldenrod palegreen
916             paleturquoise palevioletred papayawhip peachpuff
917             peru pink plum powderblue purple rosybrown
918             royalblue saddlebrown salmon sandybrown seagreen
919             seashell sienna silver skyblue slateblue slategray
920             slategrey snow springgreen steelblue tan teal
921             thistle tomato turquoise violet wheat white
922             whitesmoke yellowgreen);
923              
924 0           return @cols;
925             }
926              
927             =head2 drawBarcode
928              
929             $pdf->drawBarcode($x, $y, $scale, $frame, $type, $code, $extn, $umzn,
930             $lmzn, $zone, $quzn, $spcr, $ofwt, $fnsz, $text);
931              
932             This is really not that complicated, trust me! ;) I am pretty unfamiliar with
933             barcode lingo and types so if I get any of this wrong, lemme know!
934             This is a very flexible way to draw a barcode on your PDF document.
935             $x and $y represent the center of the barcode's position on the document.
936             $scale is the size of the entire barcode 1 being 1:1, which is all you'll
937             need most likely. $type is the type of barcode which can be codabar, 2of5int,
938             3of9, code128, or ean13. $code is the alpha-numeric code which the barcode
939             will represent. $extn is the
940             extension to the $code, where applicable. $umzn is the upper mending zone and
941             $lmzn is the lower mending zone. $zone is the the zone or height of the bars.
942             $quzn is the quiet zone or the space between the frame and the barcode. $spcr
943             is what to put between each number/character in the text. $ofwt is the
944             overflow width. $fnsz is the fontsize used for the text. $text is optional
945             text beneathe the barcode.
946              
947             =cut
948              
949             sub drawBarcode {
950 0     0 1   my $self = shift;
951 0           my $x = shift; # x center of barcode image
952 0           my $y = shift; # y center of barcode image
953 0           my $scale = shift; # scale of barcode image
954 0           my $frame = shift; # width of the frame around the quiet zone
955             # my $font = shift;
956 0           my $type = shift; # type of barcode
957 0           my $code = shift; # the code
958 0           my $extn = shift; # code extension
959 0           my $umzn = shift; # upper mending zone
960 0           my $lmzn = shift; # lower mending zone
961 0           my $zone = shift; # height of the bars
962 0           my $quzn = shift; # zone between barcode and frame
963 0           my $spcr = shift; # space between numbers
964 0           my $ofwt = shift; # overflow
965 0           my $fnsz = shift; # fontsize
966 0           my $text = shift; # alt text
967              
968 0           my $page = $self->{page};
969 0           my $gfx = $page->gfx;
970              
971 0           my $bSub = "xo_$type";
972 0           my $bar = $self->{pdf}->$bSub(
973             -font => $self->{font},
974             -type => $type,
975             -code => $code,
976             -quzn => $quzn,
977             -umzn => $umzn,
978             -lmzn => $lmzn,
979             -zone => $zone,
980             -quzn => $quzn,
981             -spcr => $spcr,
982             -ofwt => $ofwt,
983             -fnsz => $fnsz,
984             -text => $text
985             );
986              
987             # $gfx->barcode($bar, $x, $y, $scale, $frame);
988 0           $gfx->save;
989 0           $gfx->linecap(0);
990 0           $gfx->transform( -translate => [$x, $y]);
991 0           $gfx->fillcolor('#ffffff');
992 0           $gfx->linewidth(0.1);
993 0           $gfx->fill;
994 0           $gfx->formimage($bar,0,0,$scale);
995 0           $gfx->restore;
996             }
997              
998             =head2 setFont
999              
1000             $pdf->setFont($font);
1001              
1002             Creates a new font object of type $font to be used in the page.
1003              
1004             =cut
1005              
1006             sub setFont {
1007 0     0 1   my ( $self, $font, $size )= @_;
1008              
1009 0 0         if (exists $self->{__font_cache}->{$font}) {
1010 0           $self->{font} = $self->{__font_cache}->{$font};
1011             }
1012             else {
1013 0           $self->{font} = $self->{pdf}->corefont($font);
1014 0           $self->{__font_cache}->{$font} = $self->{font};
1015             }
1016              
1017 0           $self->{fontname} = $font;
1018             }
1019              
1020             =head2 getFont
1021              
1022             $fontname = $pdf->getFont();
1023              
1024             Returns the font name currently being used.
1025              
1026             =cut
1027              
1028             sub getFont {
1029 0     0 1   my $self = shift;
1030              
1031 0           return $self->{fontname};
1032             }
1033              
1034             =head2 setSize
1035              
1036             $pdf->setSize($size);
1037              
1038             Sets the fontsize to $size. Called before setFont().
1039              
1040             =cut
1041              
1042             sub setSize {
1043 0     0 1   my ( $self, $size ) = @_;
1044              
1045 0           $self->{size} = $size;
1046             }
1047              
1048             =head2 getSize
1049              
1050             $fontsize = $pdf->getSize();
1051              
1052             Returns the font size currently being used.
1053              
1054             =cut
1055              
1056             sub getSize {
1057 0     0 1   my $self = shift;
1058              
1059 0           return $self->{size};
1060             }
1061              
1062             =head2 pages
1063              
1064             $pages = $pdf->pages();
1065              
1066             The number of pages in the document.
1067              
1068             =cut
1069              
1070             sub pages {
1071 0     0 1   my $self = shift;
1072              
1073 0           return $self->{pdf}->pages;
1074             }
1075              
1076             =head2 setInfo
1077              
1078             $pdf->setInfo(%infohash);
1079              
1080             Sets the info structure of the document. Valid keys for %infohash:
1081             Creator, Producer, CreationDate, Title, Subject, Author, etc.
1082              
1083             =cut
1084              
1085             sub setInfo {
1086 0     0 1   my ($self, %info) = @_;
1087              
1088             # Over-ride or define %INFO values
1089 0           foreach my $key (keys %{$self->{INFO}}) {
  0            
1090 0 0 0       next unless (exists($info{$key}) && defined($info{$key}));
1091 0 0 0       if (length($info{$key}) and ($info{$key} ne ${$self->{INFO}}{$key})) {
  0            
1092 0           ${$self->{INFO}}{$key} = $info{$key};
  0            
1093             }
1094             }
1095 0           my @orig_keys = keys(%{$self->{INFO}});
  0            
1096 0           foreach my $key (keys %info) {
1097 0 0         if (! grep /$key/, @orig_keys) {
1098 0           ${$self->{INFO}}{$key} = $info{$key};
  0            
1099             }
1100             }
1101             }
1102              
1103             =head2 getInfo
1104              
1105             %infohash = $pdf->getInfo();
1106              
1107             Gets meta-data from the info structure of the document.
1108             Valid keys for %infohash: Creator, Producer, CreationDate,
1109             Title, Subject, Author, etc.
1110              
1111             =cut
1112              
1113             sub getInfo {
1114 0     0 1   my $self = shift;
1115              
1116 0           my %info = $self->{pdf}->info();
1117 0           return %info;
1118             }
1119              
1120             =head2 saveAs
1121              
1122             Saves the document to a file.
1123              
1124             # Save the document as "file.pdf"
1125             my $fileName = "file.pdf";
1126             $pdf->saveAs($fileName);
1127              
1128             =cut
1129              
1130             sub saveAs {
1131 0     0 1   my $self = shift;
1132 0           my $fileName = shift;
1133              
1134 0           $self->{pdf}->info(%{$self->{INFO}});
  0            
1135 0           $self->{pdf}->saveas($fileName);
1136 0           $self->{pdf}->end();
1137             }
1138              
1139             =head2 Finish
1140              
1141             Returns the PDF document as text. Pass your own custom routine to do things
1142             on the footer of the page. Pass 'roman' for Roman Numeral page numbering.
1143              
1144             # Hand the document to the web browser
1145             print "Content-type: application/pdf\n\n";
1146             print $pdf->Finish();
1147              
1148             =cut
1149              
1150             sub Finish {
1151 0     0 1   my $self = shift;
1152 0           my $callback = shift;
1153              
1154 0           my $total = $self->{page_nbr} - 1;
1155              
1156             # Call the callback if one was given to us
1157 0 0 0       if (ref($callback) eq 'CODE') {
    0          
1158 0           &$callback($self, $total);
1159             # This will print a footer if no $callback is passed for backwards
1160             # compatibility
1161             } elsif (defined $callback && $callback !~ /none/i) {
1162 0           &gen_page_footer($self, $total, $callback);
1163             }
1164              
1165 0           $self->{pdf}->info(%{$self->{INFO}});
  0            
1166 0           my $out = $self->{pdf}->stringify;
1167              
1168 0           return $out;
1169             }
1170              
1171             =head2 getPDFAPI2Object
1172              
1173             Object method returns underlying PDF::API2 object
1174              
1175             =cut
1176              
1177             sub getPDFAPI2Object {
1178 0     0 1   my $self = shift;
1179 0           return $self->{pdf};
1180             }
1181              
1182             ### PRIVATE SUBS ##############################################################
1183              
1184             sub gen_page_footer {
1185 0     0 0   my $self = shift;
1186 0           my $total = shift;
1187 0           my $type = shift;
1188              
1189 0           for (my $i = 1; $i <= $total; $i++) {
1190 0 0         next if ( $self->{no_page_num}->[$i - 1] );
1191 0           my $page = $self->{pdf}->openpage($i);
1192 0           my $txtobj = $page->text;
1193 0           my $txt;
1194             my $font;
1195 0 0         if ($type eq 'roman') {
1196 0           require Text::Roman;
1197 0           $font = $self->{pdf}->corefont("Times-roman");
1198 0           $txt = Text::Roman::int2roman($i). " of " .
1199             Text::Roman::int2roman($total);
1200             } else {
1201 0           $font = $self->{pdf}->corefont("Helvetica");
1202 0           $txt = "Page $i of $total";
1203             }
1204 0           my $size = 10;
1205 0           $txtobj->font($font, $size);
1206 0           $txtobj->translate($self->{Xmargin}, 8);
1207 0           $txtobj->text($txt);
1208 0           $size = $self->getStringWidth($self->{DATE});
1209 0           $txtobj->translate($self->{PageWidth} - $self->{Xmargin} - $size, 8);
1210 0           $txtobj->text($self->{DATE});
1211             }
1212             }
1213              
1214             =head1 AUTHOR EMERITUS
1215              
1216             Andrew Orr
1217              
1218             =head1 MAINTAINER
1219              
1220             Aaron TEEJAY Trevena
1221              
1222             =head1 BUGS
1223              
1224             Please report any bugs or feature requests to C, or through
1225             the web interface at L. I will be notified, and then you'll
1226             automatically be notified of progress on your bug as I make changes.
1227              
1228             =head1 SUPPORT
1229              
1230             You can find documentation for this module with the perldoc command.
1231              
1232             perldoc PDF::Report
1233              
1234              
1235             You can also look for information at:
1236              
1237             =over 4
1238              
1239             =item * RT: CPAN's request tracker (report bugs here)
1240              
1241             L
1242              
1243             =item * AnnoCPAN: Annotated CPAN documentation
1244              
1245             L
1246              
1247             =item * CPAN Ratings
1248              
1249             L
1250              
1251             =item * METACPAN
1252              
1253             L
1254              
1255             =item * GITHUB
1256              
1257             L
1258              
1259             =back
1260              
1261             =head1 SEE ALSO
1262              
1263             =over 4
1264              
1265             =item PDF::API2
1266              
1267             =back
1268              
1269             =head1 LICENSE AND COPYRIGHT
1270              
1271             Copyright 2008-2010 Andy Orr
1272              
1273             Copyright 2013 Aaron Trevena
1274              
1275             This program is free software; you can redistribute it and/or modify it
1276             under the terms of either: the GNU General Public License as published
1277             by the Free Software Foundation; or the Artistic License.
1278              
1279             See L for more information.
1280              
1281             =cut