package PDF::CreateSimple;

use Error qw(:try);
use strict;
use warnings;
use PDF::API2;

use 5.008002;

our $VERSION = '1.1.0.0';

use constant PI => 4 * atan2(1, 1);

sub new{
    my ($class,$filePath,$templateFile,$firstPageFormat) = @_;
    my $self = {};
    $self = bless($self, $class);

    my $PDF;
    $self->{'_page_images'} = [[]];
    if ($templateFile){
        $PDF = PDF::API2->open($templateFile);  
    }
    else{
        $PDF = PDF::API2->new(-file => $filePath);
        my $page = $PDF->page;
        $page->mediabox($firstPageFormat) if $firstPageFormat;
        $self->_setCurPage(1);
        
    }

    $self->_setMWCache({});
    $self->_setSWCache({});
    $self->_setFPCache({});
    $self->_setPDF($PDF);
    $self->setFilePath($filePath); # for retrieval when we'll want its content
    $self->resetTransformations;
        
    return $self;   
}

# INTERNAL FUNCTION, do not use outside scope
sub _setPDF{
    my ($self,$value) = @_;
    $self->{_PDF} = $value;   
    
}

sub getPDF{
    my $self = shift;
    return $self->{_PDF};   
}


sub setFilePath{
    my ($self,$value) = @_;
    $self->{_file_path} = $value;   
}

sub getFilePath{
    my $self = shift;
    return $self->{_file_path};
}

sub _setMWCache{
    my ($self,$value) = @_;
    $self->{_mw_cache} = $value;
}

sub _getMWCache{
    my $self = shift;
    return $self->{_mw_cache};
}

sub _setSWCache{
    my ($self,$value) = @_;
    $self->{_sw_cache} = $value;
}

sub _getSWCache{
    my $self = shift;
    return $self->{_sw_cache};
}

sub _setFPCache{
    my ($self,$value) = @_;
    $self->{_fp_cache} = $value;
}

sub _getFPCache{
    my $self = shift;
    return $self->{_fp_cache};
}

sub addNewPage{
    my ($self,$format,$pageNo) = @_;  
    $pageNo = 0 unless $pageNo;
    my $PDF = $self->getPDF();

    my $pageImages = $self->{'_page_images'};

    if ($pageNo == 0){
        push(@$pageImages,[]);
    }
    elsif($pageNo == 1){
        unshift(@$pageImages,[]);
    }
    else{
        my @afterList = splice(@$pageImages,($pageNo-1));
        push(@$pageImages, []);
        push(@$pageImages,@afterList);
    }
    
    throw Error::Simple('Page Number is invalid')
        if $pageNo > $PDF->pages ||
           $pageNo < 0;
    
    my $page = $PDF->page($pageNo);
    $page->mediabox($format) if $format;
    
    $self->_setCurPage($pageNo);
    
    return $pageNo;
}

# INTERNAL FUNCTION, do not use outside scope
sub _getPage{
    my ($self,$pageNo) = @_;
    return $self->getPDF->openpage($pageNo);
    
}

#INTERNAL FUNCTION, do not use outside scope
sub _setCurPage{
    my ($self,$value) = @_;
    $self->{_cur_page} = $value;   
}


#INTERNAL FUNCTION, do not use outside scope
sub _getCurPage{
    my $self = shift;
    return $self->{_cur_page};   
}

sub changePage{
    my ($self,$pageNo) = @_;   
    $pageNo = 0 unless $pageNo;
    my $PDF = $self->getPDF;
    
    throw Error::Simple('Page Number is invalid')
        if $pageNo > $PDF->pages ||
           $pageNo < 0;
    
    $self->_setCurPage($pageNo);
}

#INTERNAL FUNCTION, do not use outside scope
sub _setFonts{
    my ($self,$value) = @_;   
    $self->{_fonts} = $value;
}

#INTERNAL FUNCTION, do not use outside scope
sub _getFonts{
    my $self = shift;
    return $self->{_fonts};   
}

#INTERNAL FUNCTION, do not use outside scope
sub _getFont{
    my ($self,$value) = @_;
    my $fonts = $self->_getFonts;    
    my $font;
    
    if ($fonts->{$value}){ # if the font object already exist, then we have no need to recreate it.
        $font = $fonts->{$value};
    }
    else{   
        my $PDF = $self->getPDF;
        $font = $PDF->corefont($value);
        $fonts->{$value} = $font;
    }
    
    return $font;
}

sub setRotation{
    my ($self,$rotation) = @_;   

    $self->{_rotation} = $rotation;
}

#INTERNAL FUNCTION, do not use outside scope
sub _getRotation{
    my $self = shift;
    return $self->{_rotation};   
}

sub setSkew{
    my ($self,$x,$y) = @_;
    
    $self->{_skew} = [$x,$y];
}

#INTERNAL FUNCTION, do not use outside scope
sub _getSkew{
    my $self = shift;
    
    return $self->{_skew};   
}

sub resetTransformations{
    my ($self) = @_;    
    
    $self->setRotation(0);
    $self->setSkew(0,0);
    
}

sub getContent{
    my $self = shift;  
    my $isBinary = shift; 
    my $fileContent = '';
    my $FH ;

    open (FH,$self->getFilePath());
    binmode(FH) if $isBinary;
    
    while(<FH>){               # while we still have something to read
        $fileContent .= $_;   # write it into a variable
    }
    
    close FH;
    
    return $fileContent;
}

sub closeFile{
    my $self = shift;
    my $PDF = $self->getPDF;
    my $filePath = $self->getFilePath;
    
    $self->_drawImages;
    
    $PDF->saveas($filePath);
    $PDF->end;
        
}

#INTERNAL FUNCTION, do not use outside scope
sub _applyTransformation{
    my ($self,$gfx,$x,$y,$rotation,$skew) = @_;   
 
    $rotation = $self->_getRotation unless defined($rotation);
    $skew = $self->_getSkew unless defined($skew);

    $gfx->move(0,0);
    
    $gfx->translate($x,$y);
    $gfx->rotate($rotation) if $rotation;
    $gfx->skew($skew->[0],$skew->[1]) if $skew;
       
}

#INTERNAL FUNCTION, do not use outside scope
sub _resetTransformation{
    my ($self,$gfx,$x,$y,$rotation,$skew) = @_;  

    $rotation = $self->_getRotation unless defined($rotation);
    $skew = $self->_getSkew unless defined($skew);
       
    $gfx->skew(-($skew->[0]),-($skew->[1])) if $skew;    
    $gfx->rotate(-$rotation) if $rotation;
    $gfx->translate(-$x,-$y);   

}

sub drawLine{ 
    my ($self,$x1,$y1,$x2,$y2,$lineWidth,$strokecolor,$pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo; 
    
    my $page = $self->_getPage($pageNo);
    my $gfx = $page->gfx;
    
    $self->_applyTransformation($gfx,$x1,$y1);
    
    $gfx->strokecolor($strokecolor);
    $gfx->linewidth($lineWidth);
    $gfx->line($x2 - $x1,$y2 - $y1);
    $gfx->stroke;    
    
    $self->_resetTransformation($gfx,$x1,$y1);
}

sub drawDashedLine
{
    my ($self, $x1, $y1, $x2, $y2, $lineWidth, $on, $off, $strokecolor, $pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo;

    my $page = $self->_getPage($pageNo);
    my $gfx = $page->gfx;

    $self->_applyTransformation($gfx, $x1, $y1);
    $gfx->strokecolor($strokecolor);
    $gfx->linewidth($lineWidth);

    my $linelength = sqrt(($x2-$x1)**2 + ($y2-$y1)**2);
    my $dashes = ($linelength-$on)/($on+$off);
    my $remainder = $dashes - int($dashes);
    if ($remainder > 0.5) {
        $dashes = int($dashes)+1;
    } else {
        $dashes = int($dashes);
    }
    return unless $dashes;

    my $xon = ($x2 - $x1) / ($dashes + 1 + $dashes * $off / $on );
    my $xonoff = $xon + $xon/$on*$off;

    my $yon = ($y2 - $y1) / ($dashes + 1 + $dashes * $off / $on );
    my $yonoff = $yon + $yon/$on*$off;

    for my $i (0..($dashes-1)) {
        $gfx->move($xonoff * $i, $yonoff * $i);
        $gfx->line($xonoff * $i + $xon, $yonoff * $i + $yon);
    }
    $gfx->stroke;

    $self->_resetTransformation($gfx, $x1, $y1);
}

sub drawCurve{ 
    my ($self,$x1,$y1,$x2,$y2,$fx,$fy,$lineWidth,$strokecolor,$pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo; 
    
    my $page = $self->_getPage($pageNo);
    my $gfx = $page->gfx;
    
    $self->_applyTransformation($gfx,$x1,$y1);
    
    $gfx->strokecolor($strokecolor);
    $gfx->linewidth($lineWidth);
    
    $gfx->curve(0,0,$fx-$x1,$fy-$y1,$x2-$x1,$y2-$y1);
    $gfx->stroke;    
    
    $self->_resetTransformation($gfx,$x1,$y1);
}

sub drawGridFromPoints{
    my ($self,$xs,$ys,$lineWidth,$strokecolor,$pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo; 
    
    my @xs = sort {$a <=> $b} @$xs;
    my @ys = sort {$a <=> $b} @$ys;
    
    my $page = $self->_getPage($pageNo);
    my $gfx = $page->gfx;
    
    $gfx->strokecolor($strokecolor);
    $gfx->linewidth($lineWidth);
    my ($minX,$maxX,$minY,$maxY) = ($xs[0],$xs[-1],$ys[0],$ys[-1]);
    
    for (my $i = 0; $i < @xs; $i++){
        $gfx->move($xs[$i],$minY);
        $gfx->vline($maxY);
    }
    
    for (my $j = 0; $j < @ys; $j++){
        $gfx->move($minX,$ys[$j]);
        $gfx->hline($maxX);    
    }
    
    $gfx->stroke; 
}

sub drawGridFromIntervals{
    my ($self,$x1,$y1,$x2,$y2,$xInt,$yInt,$lineWidth,$strokecolor,$pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo; 
            
    my $page = $self->_getPage($pageNo);
    my $gfx = $page->gfx;
    
    $gfx->strokecolor($strokecolor);
    $gfx->linewidth($lineWidth);
        
    for (my $i = $x1; $i <= $x2; $i+= $xInt){
        $gfx->move($i,$y1);
        $gfx->vline($y2);
    }
    
    for (my $j = $y1; $j <= $y2 ; $j+= $yInt){
        $gfx->move($x1,$j);
        $gfx->hline($x2);    
    }
    
    $gfx->stroke; 
}

sub drawRectangle{
    my ($self,$x1,$y1,$x2,$y2,$lineWidth,$strokecolor,$fillcolor,$pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo; 
    
    my $page = $self->_getPage($pageNo);
    my $gfx = $page->gfx;
    
    $self->_applyTransformation($gfx,($x1+$x2)/2,($y1+$y2)/2);
    
    $gfx->strokecolor($strokecolor);
    $gfx->linewidth($lineWidth);
    $gfx->fillcolor($fillcolor) if $fillcolor;
    
    $gfx->rectxy(-(($x2-$x1)/2),-(($y2-$y1)/2),($x2-$x1)/2,($y2-$y1)/2);
    
    if ($fillcolor){
        $gfx->fillstroke;
    }
    else{
        $gfx->stroke;
    }

    $self->_resetTransformation($gfx,($x1+$x2)/2,($y1+$y2)/2);
}

sub drawSquare{
    my ($self,$x,$y,$size,$lineWidth,$strokecolor,$fillcolor,$pageNo) = @_;
    $self->drawRectangle($x,$y,$x+$size,$y+$size,$lineWidth,$strokecolor,$fillcolor,$pageNo);  
}

sub drawCircle{
    my ($self,$x,$y,$r,$lineWidth,$strokecolor,$fillcolor,$pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo; 
    
    my $page = $self->_getPage($pageNo);
    my $gfx = $page->gfx;
    
    $self->_applyTransformation($gfx,$x,$y);
    
    $gfx->strokecolor($strokecolor);
    $gfx->linewidth($lineWidth);
    $gfx->fillcolor($fillcolor) if $fillcolor;
    $gfx->circle(0,0,$r);
    
    if ($fillcolor){
        $gfx->fillstroke;    
    }
    else{
        $gfx->stroke;
    }    
    
    $self->_resetTransformation($gfx,$x,$y);
}

sub drawArc{
    my ($self,$x,$y,$a,$b,$alpha,$beta,$lineWidth,$strokecolor,$fillcolor,$pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo; 
    
    my $page = $self->_getPage($pageNo);
    my $gfx = $page->gfx;
    
    $self->_applyTransformation($gfx,$x,$y);
    
    $gfx->strokecolor($strokecolor);
    $gfx->linewidth($lineWidth);
    $gfx->fillcolor($fillcolor) if $fillcolor;
    $gfx->arc(0,0,$a,$b,$alpha,$beta,1);
    
    if ($fillcolor){
        $gfx->fillstroke;    
    }
    else{
        $gfx->stroke;
    }        
    
    $self->_resetTransformation($gfx,$x,$y);
}

sub drawEllipse{
    my ($self,$x,$y,$r1,$r2,$lineWidth,$strokecolor,$fillcolor,$pageNo) = @_;
    $self->drawArc($x,$y,$r1,$r2,0,360,$lineWidth,$strokecolor,$fillcolor,$pageNo);   
}

sub drawPie{
    my ($self,$x,$y,$a,$b,$alpha,$beta,$lineWidth,$strokecolor,$fillcolor,$pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo; 
    
    my $page = $self->_getPage($pageNo);
    my $gfx = $page->gfx;
    
    $self->_applyTransformation($gfx,$x,$y);
    
    $gfx->strokecolor($strokecolor);
    $gfx->linewidth($lineWidth);
    $gfx->fillcolor($fillcolor) if $fillcolor;
    $gfx->pie(0,0,$a,$b,$alpha,$beta);
    
    if ($fillcolor){
        $gfx->fillstroke;    
    }
    else{
        $gfx->stroke;
    }        
    
    $self->_resetTransformation($gfx,$x,$y);
}

# sub drawPieChart{
#     my ($self,$x,$y,$r1,$r2,$values,$showString,$showValue,$pageNo) = @_;
#     $pageNo = $self->_getCurPage unless $pageNo; 
#     my $sum = 0;
#     
#     foreach my $value(@$values){
#         $sum += $value->{value};
#     }
#   
#     my $curValue = 0;
#     foreach my $value(@$values){
#         my $ratio = ($value->{value} / $sum) * 360;
#         
#         $self->drawPie($x,$y,$r1,$r2,$curValue,$curValue + $ratio,1,$value->{color},$value->{color},$pageNo);    
#         $curValue += $ratio;
#     }
#     
#     if ($showString || $showValue){
#         my $totalRad = 2 * PI();
#         my $rotation = $self->_getRotation();
#         $rotation = 0  unless $rotation;
#         my $page = $self->_getPage($pageNo);
#         my $gfx = $page->gfx;
#         $self->_applyTransformation($gfx,$x,$y);
#         
#         my $curRad = 0;
#         $gfx->fillcolor('black');
#         my $font = $self->_getFont('Times-Roman');
#         $gfx->font($font,15);
#         foreach my $value(@$values){ 
#             my $ratio = ($value->{value} / $sum) * $totalRad;
#             my $newX = ($r1 * (cos(($ratio/2) + $curRad))) * 1.07;
#             my $newY = ($r2 * (sin(($ratio/2) + $curRad))) * 1.07;
#             
#             my $align = 0;
#             $align = 2 if (($ratio/2) + $curRad) >= ($totalRad / 4) && 
#                           (($ratio/2) + $curRad) <= (3 * ($totalRad / 4));
#             my $string = $value->{string} if $showString && !$showValue;
#             $string = $value->{value} if !$showString && $showValue;
#             $string = $value->{string} . '(' . $value->{value}  . ')' if $showString && $showValue;
# # we use this instead of drawText so that we can rotate the text the right way.
#             $gfx->move($newX,$newY);       
#             $gfx->text($string)        if $align == 0;
#             $gfx->text_center($string) if $align == 1;
#             $gfx->text_right($string)  if $align == 2;           
#  
#             $curRad += $ratio;
#         }     
#         $self->_resetTransformation($gfx,$x,$y);
#     }   
# }

sub drawPolygon{
    my ($self,$points,$lineWidth,$strokecolor,$fillcolor,$pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo; 
    
    my $page = $self->_getPage($pageNo);
    my $gfx = $page->gfx;
    
    $self->_applyTransformation($gfx,$points->[0],$points->[1]);
    my $nPoints = $self->_reformatPoints($points);
        
    $gfx->strokecolor($strokecolor);
    $gfx->linewidth($lineWidth);
    $gfx->fillcolor($fillcolor) if $fillcolor;
    
    $gfx->poly((@$nPoints,$nPoints->[0],$nPoints->[1])); # that way we know for sure it will close
    
    if ($fillcolor){
        $gfx->fillstroke;    
    }
    else{
        $gfx->stroke;
    }    
    
    $self->_resetTransformation($gfx,$points->[0],$points->[1]);
}        

#INTERNAL FUNCTION, do not use outside scope
sub _reformatPoints{
    my ($self,$points) = @_;
    my @nPoints;
    
    for (my $i = 0; $i < @$points; $i++){
        if ($i % 2){
            push(@nPoints, $points->[$i] -$points->[1]);
        }
        else{
            push(@nPoints, $points->[$i] -$points->[0]);
        }    
    }
    
    return \@nPoints;
}

#INTERNAL FUNCTION, do not use outside scope
sub _drawImages{
    my $self = shift;
    
    my $pages = $self->{'_page_images'};
    for (my $i = 0; $i < @$pages;$i++){
        my $page = $pages->[$i];
        $self->_drawImageForPage($page,$i);
    }
    
}

#INTERNAL FUNCTION, do not use outside scope
sub _drawImageForPage{
    my ($self,$pageImages,$pageNo) = @_;

    $pageNo++; # because 0 is the first and not the last.

    my $page = $self->_getPage($pageNo);
    my $gfx  = $page->gfx;

    foreach my $image(@$pageImages){
        my $x        = $image->{'x'};
        my $y        = $image->{'y'};
        my $scale    = $image->{'scale'};
        my $skew     = $image->{'skew'};
        my $rotation = $image->{'rotation'};
        my $h        = $image->{'h'};
        my $mw       = $image->{'mw'};
        my $mh       = $image->{'mh'};
        my $obj      = $image->{'obj'};
        
        
        $self->_applyTransformation($gfx,$x + $mw,$y + $mh,$rotation,$skew);
    
        unless ($h){
            $gfx->image($obj, -$mw,-$mh,$scale);
        }
        else{
            my $w = $scale;
            $gfx->image($obj, -$mw,-$mh,$w,$h);
        }
    
        $self->_resetTransformation($gfx,$x + $mw,$y + $mh,$rotation,$skew);
        
    }

}

sub drawImage{
    my ($self,$imagePath,$x,$y,$scale,$h,$pageNo) = @_;
    
    $pageNo = $self->_getCurPage unless $pageNo;
    
    my $pageImages = $self->{'_page_images'}->[$pageNo-1];
    my $page = $self->_getPage($pageNo);
    my $gfx  = $page->gfx;
       
    $imagePath =~ /^(.+)\.(\w+)$/;
    my ($name,$ext) = ($1,$2);
    my $imgObj = $self->_getImageObject($imagePath,$ext);
    
    my ($mw,$mh) = $self->_getCenterPointForImage($imgObj,$scale,$h);

    push(@$pageImages,{'x'          => $x,
                       'y'          => $y,
                       'skew'       => $self->_getSkew,
                       'rotation'   => $self->_getRotation,
                       'scale'      => $scale,
                       'h'          => $h,
                       'mw'         => $mw,
                       'mh'         => $mh,
                       'obj'        => $imgObj});

}

#INTERNAL FUNCTION, do not use outside scope
sub _getImageObject{
    my ($self,$imagePath,$ext) = @_;
    
    $ext = '' unless defined($ext);
    my $PDF = $self->getPDF;
    if ($ext && $ext eq 'tiff'){
        return $PDF->image_tiff($imagePath);
    }
    elsif ($ext && $ext eq 'jpg' || $ext eq 'jpeg'){
        return $PDF->image_jpeg($imagePath);
    }
    elsif ($ext && $ext eq 'png'){
        return $PDF->image_png($imagePath);
    }
    elsif ($ext && $ext eq 'pnm'){
        return $PDF->image_pnm($imagePath);
    }
    elsif ($ext && $ext eq 'gif'){
        return $PDF->image_gif($imagePath);
    }
    else
    {
        return $PDF->image_gd($imagePath, -lossless => 1) ;
    }
}

#INTERNAL FUNCTION, do not use outside scope
sub _getCenterPointForImage{
    my ($self,$imgObj,$scale,$h) = @_;
    
    if ($scale && $h){
        return ($scale/2,$h/2);
    }
    else{
        $scale = 1 unless $scale;
        return (($imgObj->width * $scale)/2, ($imgObj->height * $scale)/2);
    }    
}

sub drawText{
    my ($self,$text,$font,$fontSize,$x,$y,$color,$align,$pageNo) = @_;
    $pageNo = $self->_getCurPage unless $pageNo; 
    $align = 0 unless $align;
        
    my $fontObj = $self->_getFont($font);
    my $page    = $self->_getPage($pageNo);
    my $gfx     = $page->gfx;
    
#     $self->_applyTransformation($gfx,$x,$y);
#     $gfx->fillcolor($color);
#     $gfx->font($fontObj,$fontSize);
    my $rotation = $self->_getRotation || 0 ;
    if ($align == 1){
#         $gfx->text_center($text); # centered
        return $gfx->textlabel($x,$y,$fontObj,$fontSize,$text,-color=>$color,-center=>1, -rotate=> $rotation);
    }
    elsif($align == 2){
#         $gfx->text_right($text);  #right aligned
        return $gfx->textlabel($x,$y,$fontObj,$fontSize,$text,-color=>$color,-right=>1, -rotate=> $rotation);
    }
    else{
#         $gfx->text($text); # left aligned
        return $gfx->textlabel($x,$y,$fontObj,$fontSize,$text,-color=>$color, -rotate=> $rotation);
    }
#     $self->_resetTransformation($gfx,$x,$y);
 
}

sub drawTextOrWidth
{
    my ($self,$drawing,$text,$font,$size,@more) = @_;
    if ($drawing) {
        return $self->drawText($text,$font,$size,@more);
    } else {
        return $self->getWidth($text,$font,$size);
    }
}

sub drawTextOnlyIfFit{
    my ($self,$text,$font,$fontSize,$x,$y,$color,$align,$pageNo);
    $pageNo = $self->_getCurPage unless $pageNo; 
    my $page    = $self->_getPage($pageNo);
    
    my $width = $self->getWidth($text,$font,$fontSize);
    my ($llx,$lly,$urx,$ury) = $page->get_mediabox;
    
    return $self->drawText($font,$fontSize,$x,$y,$color,$align,$pageNo)
        if $urx >= ($width + $x);
    return 0; # could'n draw it because it's would get out of the page.
    
}

sub getFontWidth
{
    my ($self,$text,$font,$size) = @_;
    $size ||= 1;
    my $fontObj = $self->_getFont($font);
    return map { $_ * $size } $fontObj->width_array($text)
        if wantarray;
    return $fontObj->width($text) * $size;
}

sub getFontMaxWidth
{
    my ($self,$font,$size) = @_;
    $size ||= 1;
    my $mwCache = $self->_getMWCache;
    return $mwCache->{$font}*$size if $mwCache->{$font};
    
    my $fontObj = $self->_getFont($font);
    $mwCache->{$font} = $fontObj->maxwidth();
    $self->_setMWCache($mwCache);
    
    return ($mwCache->{$font})*$size;
}


sub getFontSpaceWidth
{
    my ($self,$font,$size) = @_;
    $size ||= 1;
    my $swCache = $self->_getSWCache;
    return $swCache->{$font}*$size if $swCache->{$font};
    
    my $fontObj = $self->_getFont($font);
    $swCache->{$font} = $fontObj->width(' ');
    $self->_setSWCache($swCache);
    
    return ($swCache->{$font})*$size;
}

sub getFontPosition
{
    my ($self,$font,$size) = @_;
    $size ||= 1;
    my $fpCache = $self->_getFPCache;
    return $fpCache->{$font}*$size if $fpCache->{$font};
    
    my $fontObj = $self->_getFont($font);
    my $a = $fontObj->ascender;
    my $d = $fontObj->descender;
    my $totalheight = $a - $d;
    my $pos = $a/$totalheight;
    $fpCache->{$font} = $pos;
    $self->_setFPCache($fpCache);
    
    return $pos*$size;
}

sub importPage{
    my ($self,$sourcepdfPath,$sourceindex,$pageNo) = @_;

    my $PDF = $self->getPDF;

    throw Error::Simple('Page Number is invalid')
        if $pageNo > $PDF->pages ||
           $pageNo < 0;

    my $sourcePDF = PDF::API2->open($sourcepdfPath);
    throw Error::Simple('Wrong source page number') if $sourceindex > $sourcePDF->pages ||
                                                       $sourceindex < 0;

    $PDF->importpage($sourcePDF, $sourceindex, $pageNo);
### fix by Tim White
    push (@{$self->{'_page_images'}}, []);
###
    $self->_setCurPage($pageNo);

    $sourcePDF->end;
}

'yet another true value';

__END__

## David Brunelle 2005

=head1 NAME

PDF::CreateSimple

=head1 DESCRIPTION

This module let you create simple PDF files using a set of eary to use function. You can either create a file from scratch or start using an already existing file. 

=head1 SYNOPSIS

    use PDF::CreateSimple;
    
    my $pdfFile = PDF::CreateSimple->new($pdfPath);
    
    #-----------------------------------------------------------------------------
    # Add a new page                 
    #-----------------------------------------------------------------------------    
    
    $pdfFile->addNewPage;
    
    #-----------------------------------------------------------------------------
    # Write Text                   
    #-----------------------------------------------------------------------------    
    
    $pdfFile->drawText('See what it can do!','Verdana',12,400,200,'cyan');
    
    #-----------------------------------------------------------------------------
    # Create Straight and Curved Lines                     
    #-----------------------------------------------------------------------------    
    
    $pdfFile->drawLine(0,0,50,50,1,'black');
    $pdfFile->drawCurve(0,0,50,50,100,50,1,'olive');
    
    #-----------------------------------------------------------------------------
    # Create Hollow Geometric Figures                     
    #-----------------------------------------------------------------------------
    
    $pdfFile->drawRectangle(0,0,50,50,2,'blue');
    $pdfFile->drawPolygon([10,15,20,25,25,30,50,100,50,200],2,'orange');
    
    #-----------------------------------------------------------------------------
    # Create Filled Geometric Figures                     
    #-----------------------------------------------------------------------------    

    $pdfFile->drawCircle(100,100,30,3,'black','green');
    $pdfFile->drawSquare(150,100,30,1,'olive','red');
    
    #-----------------------------------------------------------------------------
    # Import Images (png,pnm,tiff,gif,jpg) and other PDF Files
    #-----------------------------------------------------------------------------     
    
    $pdfFile->drawImage('some_img.jpg',150,150,1.75);
    $pdfFile->drawImage('some_other_img.jpg',350,150,200,200);
    $pdfFile->importPage('source_pdf');
            
    #-----------------------------------------------------------------------------
    # Save And close                   
    #-----------------------------------------------------------------------------    
    
    $pdfFile->closeFile;

=head1 METHODS

=head3 Note on recurring function parameters:

lineWidth(integer) : Specefy the thickness of the lines defining the figures
strokecolor : See color for more info 
fillcolor : Optional, when defined, the object will be filled using that color. If undef, the form will be hollow
pageno : optional, when not defined, it will used the currently selected page.
    
=head2 Text

=head3 drawText(string,font,fontsize,x,y,color,align,pageNo)

Draw the text 'string' at (x,y) using the font 'font' of size fontSize and color 'color'.
If align is undef or 0, the text will be left aligned at (x,y)
If align is set to 1, the text will be centered at (x,y)
If align is set to 2, the text will be right aligned at (x,y)
calling 'drawText' in scalar context will return the width of the printed Text.

=head3 drawTextOrWidth($drawing,$text,$font,$size,@more)

If drawing is set to 1, return the width of 'text' given the 'font' and the 'size'.
If drawing is set to 0, draw the text 'string' at (x,y) using the font 'font' of size fontSize and color 'color'.
If align is undef or 0, the text will be left aligned at (x,y)
If align is set to 1, the text will be centered at (x,y)
If align is set to 2, the text will be right aligned at (x,y)

=head3 drawTextOnlyIfFit($text,$font,$fontSize,$x,$y,$color,$align,$pageNo)

Draw the text 'string' at (x,y) using the font 'font' of size fontSize and color 'color' only if the text will fit in the page. Otherwise, it will draw nothing. 
If align is undef or 0, the text will be left aligned at (x,y)
If align is set to 1, the text will be centered at (x,y)
If align is set to 2, the text will be right aligned at (x,y)
calling 'drawTextOnlyIfFit' in scalar context will return the width of the printed Text or 0 if it can't display it.

=head2 Fonts Properties

=head3 getFontWidth($text,$font,$size)

Return the width of the 'text' given the 'font' and 'size' 

=head3 getFontMaxWidth($font,$size)

Return the maximum width for the given 'font' and 'size'. The maximum width is the width of the largest possible caracter of the font set. 

=head3 getFontSpaceWidth($font,$size)

Return the width of the whitespace caracter for the given 'font' and 'size'

=head3 getFontPosition($font,$size)

Return the relative y position for the given 'font' and 'size'

=head2 Lines and Curves

=head3 drawLine(x1,x2,y1,y2,lineWidth,strokecolor,pageNo)

Draw a line at the specified coordinates from (x1,y1) to (x2,y2)

=head3 drawDashedLine($x1, $y1, $x2, $y2, $lineWidth, $on, $off, $strokecolor, $pageNo)

Draw a dashed line at the specified coodinates from (x1,y1) to (x2,y2). $on specifies the length of the 'on'(drawn) portions while $off the length of the 'off'(not drawn) portions.

=head3 drawCurve(x1,y1,x2,y2,fx,fy,linewidth,strokecolor,pageNo)

Draw a curve at the specified coordinates from (x1,y1) to (x2,y2) with a curve streching to reach the flex point(fx,fy)

=head2 Simple Geometric Forms

=head3 drawRectangle(x1,y1,x2,y2,lineWidth,strokecolor,fillcolor,pageNo)

Draw a rectangle using (x1,y1) and (x2,y2) as corner point

=head3 drawSquare(x,y,size,lineWidth,strokecolor,fillcolor,pageNo)

Draw a square using (x,y) as one of the corner and size as width and height

=head3 drawCircle(x,y,r,lineWidth,strokecolor,fillcolor,pageNo)

Draw a circle of center point(x,y) and radius r

=head3 drawEllipse(x,y,r1,r2,lineWidth,strokecolor,fillcolor,pageNo)

Draw an ellipse of center point(x,y), horizontal radius r1 and vertical radius r2

=head3 drawArc(x,y,r1,r2,alpha,beta,lineWidth,strokecolor,fillcolor,pageNo)

Draw an arc of center point(x,y), horizontal radius r1 , versitcal radius r2 and going from degree alpha to beta

=head3 drawPie(x,y,r1,r2,alpha,beta,lineWidth,strokecolor,fillcolor,pageNo)

Draw a pie of center point (x,y), horizontal radius r1, vertical redius r2 and going from degree alpha to beta. Unlike an Arc, drawPie will close the form to make a pie slice shape form.

=head2 Complex Figures

=head3 drawPolygon(points,lineWidth,strokecolor,fillcolor,pageNo)

Draw a polygon using the specified points. Points format is (x1,y1,x2,y2,x3,y3,...,xn,yn) 

=head3 drawGridFromPoints(xs,ys,lineWidth,strokecolor,pageNo)

draw a grid using two list of points for vertical line(xs) and horizontal lines(ys)

=head3 drawGridFromIntervals(x1,y1,x2,y2,xInt,yInt,lineWidth,stokecolor,pageNo)

draw a grid starting at (x1,y1) and ending at (x2,y2) that have a vertical line at every 'xInt' and an horizontal line at every 'yInt'

=head3 drawPieChart(x,y,r1,r2,values,showString,showValue,pageNo)

Draw a pie chart at center point (x,y), horizontal radius r1 and vertical radius r2. Each item in values is a slice of the pie. The format of the values list is a hash 
    string    : the caption to display, 
    value     : the value of the item (used to calculate the size of the slice)
    color     : the color of the pie slice
If showString is set to true, the caption will be shown near the slice. 
If showValue is set to true, the value will be shown near the slice. 
If both showString and showValue are set to true, the label that will appear near the slice will be string(value)

=head2 Images and Pdf Pages

=head3 drawImage(path,x,y,scale,undef,pageNo)

Import an image found in path and draw it with left,bottom corner point at (x,y). 
If scale is defined, but not h, the image size will be multiplied by 'scale' (ex: 0.5 would print it at half its size)
If both scale and h is defined, then the image will be resized to width of 'scale' and height of 'h'
Currently supported image format are png,pnm,jpeg,gif and tiff

=head3 importPage(sourcePath,sourcePageIndex,pageNo)

Import a page from another pdf file into this one. The source page will be 'sourcePageIndex' and will be inserted before the 'pageNo'th page (ex: if pageNo = 2, it will insert between the first and second page)
If pageNo is set to 0 or undef, the page will be inserted at the end.
The current page will also be set to the newly created page.

=head2 Cannonical Transformations

=head3 setRotation(angle)

Set the rotation angle to 'angle' degree (not radian). All future objects (except an inported pdf page) will be rotated counter-clockwise by that angle until it is changed. To remove the rotation, either set it to 0 or call resetTransformations. 
setRotation is not additive (ex : setRotation(15); setRotation(25) will set it to 25, not 40)

=head3 setSkew(x,y)

Set the skewness to (x,y). All future objects (except an inported pdf page), will be skewed until it is changed. To remove skewness, either set it to (0,0) or call resetTransformations
setSkew is not additive (ex : setSkew(1,1); setSkew(2,3) will set it to (2,3), not (3,4))

=head3 resetTransformations

Reset both rotation and skewness to none.

=head2 File Browsing

=head3 addNewPage(format,pageNo)

Add a new page before the 'pageNo'th page (ex: addNewPage(1) will add at the beginnig, while addNewPage(3) will add between the second and third page)
If pageNo is set to 0 or undef, it will add at the end.
The following format are supported : '4A', '2A', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', '4B', '2B', 'B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'LETTER', 'BROADSHEET', 'LEDGER', 'TABLOID', 'LEGAL', 'EXECUTIVE', and '36X36'. leaving it undefined will use the default 8 1/2* 11 format

=head3 changePage(pageNo)

Change the current page to 'pageNo'. If set to 0 or undef, will go at the last page.

=head2 Others Functions
    
=head3 Constructor: new($outPath,$inFile,$firstPageFormat)

Create the PDF::CreateSimple object. The file will be put at outPath. If inpath is defined, the new pdf will be a copy of inPath at start. If not defined, it will be a one page blank pdf file. $firstPageFormat define the format of the first page.
The following format are supported : '4A', '2A', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', '4B', '2B', 'B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'LETTER', 'BROADSHEET', 'LEDGER', 'TABLOID', 'LEGAL', 'EXECUTIVE', and '36X36'. leaving it undefined will use the default 8 1/2* 11 format

=head3 getPDF

Return the PDF::API2 object used by the package. Do not use this unless you really know what you are doing.

=head3 setFilePath(path)

Change the file location of the pdf file to path. 

=head3 getFilePath

Return the location of the pdf file

=head3 getContent(isBinary)

Return the content of the file. If isBinary is set to true, the content will be in binary format. Otherwise is ASCII format.

=head2 FONTS
    
The following fonts are supported by PDF::CreateSimple

    "Adobe Font"
        Courier
        Courier-Bold
        Courier-BoldOblique
        Courier-Oblique
        Helvetica
        Helvetica-Bold
        Helvetica-BoldOblique
        Helvetica-Oblique
        Symbol
        Times-Bold
        Times-BoldItalic
        Times-Italic
        Times-Roman
        ZapfDingbats
    "Windows Fonts"
        Georgia
        Georgia,Bold
        Georgia,BoldItalic
        Georgia,Italic
        Verdana
        Verdana,Bold
        Verdana,BoldItalic
        Verdana,Italic
        Webdings
        Wingdings
  
=head2 COLORS
 
The following colors are available in text format : 

    aliceblue, antiquewhite, aqua, aquamarine, azure, beige, bisque, black, blanchedalmond,
    blue, blueviolet, brown, burlywood, cadetblue, chartreuse, chocolate, coral, cornflowerblue,
    cornsilk, crimson, cyan, darkblue, darkcyan, darkgoldenrod, darkgray, darkgreen, darkgrey,
    darkkhaki, darkmagenta, darkolivegreen, darkorange, darkorchid, darkred, darksalmon,
    darkseagreen, darkslateblue, darkslategray, darkslategrey, darkturquoise, darkviolet,
    deeppink, deepskyblue, dimgray, dimgrey, dodgerblue, firebrick, floralwhite, forestgreen,
    fuchsia, gainsboro, ghostwhite, gold, goldenrod, gray, grey, green, greenyellow, honeydew,
    hotpink, indianred, indigo, ivory, khaki, lavender, lavenderblush, lawngreen, lemonchiffon,
    lightblue, lightcoral, lightcyan, lightgoldenrodyellow, lightgray, lightgreen, lightgrey,
    lightpink, lightsalmon, lightseagreen, lightskyblue, lightslategray, lightslategrey,
    lightsteelblue, lightyellow, lime, limegreen, linen, magenta, maroon, mediumaquamarine,
    mediumblue, mediumorchid, mediumpurple, mediumseagreen, mediumslateblue, mediumspringgreen,
    mediumturquoise, mediumvioletred, midnightblue, mintcream, mistyrose, moccasin, navajowhite,
    navy, oldlace, olive, olivedrab, orange, orangered, orchid, palegoldenrod, palegreen,
    paleturquoise, palevioletred, papayawhip, peachpuff, peru, pink, plum, powderblue, purple,
    red, rosybrown, royalblue, saddlebrown, salmon, sandybrown, seagreen, seashell, sienna,
    silver, skyblue, slateblue, slategray, slategrey, snow, springgreen, steelblue, tan, teal,
    thistle, tomato, turquoise, violet, wheat, white, whitesmoke, yellow, yellowgreen
    
You can also define a color using the "#RRGGBB" notation

=head2 Other

This module is still in development phase, so comments and suggestions about new stuff to added or corrections are welcomed.
    
=head1 BUGS

None known yet   

=head1 ACKNOWLEDGEMENTS

First of all, I would like to thank alfred reibenschuh, the author of PDF::API2.
In no particular order: Philippe Cot?, author of Devel::Monitor and Julien Gervais-Bird, my boss.
 
=head1 AUTHOR
 
David Brunelle E<lt> david.brunelle@usherbrooke.ca E<gt>
G?nome Qu?bec E<lt> http://www.genomequebec.com E<gt>

=cut
