package PDF::API2::Util;
our $VERSION = '2.025'; # VERSION
no warnings qw[ recursion uninitialized ];
BEGIN {
use Encode qw(:all);
use vars qw(
@ISA
@EXPORT
@EXPORT_OK
%colors
$key_var
$key_var2
%u2n
%n2u
$pua
%PaperSizes
);
use Math::Trig;
use List::Util qw(min max);
use PDF::API2::Basic::PDF::Utils;
use PDF::API2::Basic::PDF::Filter;
use PDF::API2::Resource::Colors;
use PDF::API2::Resource::Glyphs;
use PDF::API2::Resource::PaperSizes;
use POSIX qw( HUGE_VAL floor );
use Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(
pdfkey
float floats floats5 intg intgs
mMin mMax
HSVtoRGB RGBtoHSV HSLtoRGB RGBtoHSL RGBtoLUM
namecolor namecolor_cmyk namecolor_lab optInvColor defineColor
dofilter unfilter
nameByUni uniByName initNameTable defineName
page_size
getPaperSizes
);
@EXPORT_OK = qw(
pdfkey
digest digestx digest16 digest32
float floats floats5 intg intgs
mMin mMax
cRGB cRGB8 RGBasCMYK
HSVtoRGB RGBtoHSV HSLtoRGB RGBtoHSL RGBtoLUM
namecolor namecolor_cmyk namecolor_lab optInvColor defineColor
dofilter unfilter
nameByUni uniByName initNameTable defineName
page_size
);
%colors = PDF::API2::Resource::Colors->get_colors();
%PaperSizes = PDF::API2::Resource::PaperSizes->get_paper_sizes();
no warnings qw[ recursion uninitialized ];
$key_var='CBA';
$key_var2=0;
$pua=0xE000;
%u2n = %{$PDF::API2::Resource::Glyphs::u2n};
%n2u = %{$PDF::API2::Resource::Glyphs::n2u};
}
sub pdfkey {
return($PDF::API2::Util::key_var++);
}
sub digestx {
my $len=shift @_;
my $mask=$len-1;
my $ddata=join('',@_);
my $mdkey='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789gT';
my $xdata="0" x $len;
my $off=0;
my $set;
foreach $set (0..(length($ddata)<<1)) {
$off+=vec($ddata,$set,4);
$off+=vec($xdata,($set & $mask),8);
vec($xdata,($set & ($mask<<1 |1)),4)=vec($mdkey,($off & 0x7f),4);
}
# foreach $set (0..$mask) {
# vec($xdata,$set,8)=(vec($xdata,$set,8) & 0x7f) | 0x40;
# }
# $off=0;
# foreach $set (0..$mask) {
# $off+=vec($xdata,$set,8);
# vec($xdata,$set,8)=vec($mdkey,($off & 0x3f),8);
# }
return($xdata);
}
sub digest {
return(digestx(32,@_));
}
sub digest16 {
return(digestx(16,@_));
}
sub digest32 {
return(digestx(32,@_));
}
sub xlog10 {
my $n = shift;
if($n) {
return log(abs($n))/log(10);
} else { return 0; }
}
sub float {
my $f=shift @_;
my $mxd=shift @_||4;
$f=0 if(abs($f)<0.0000000000000001);
my $ad=floor(xlog10($f)-$mxd);
if(abs($f-int($f)) < (10**(-$mxd))) {
# just in case we have an integer
return sprintf('%i',$f);
} elsif($ad>0){
my $value = sprintf('%f',$f);
# Remove trailing zeros
$value =~ s/(\.\d*?)0+$/$1/;
$value =~ s/\.$//;
return $value;
} else {
my $value = sprintf('%.*f', abs($ad), $f);
# Remove trailing zeros
$value =~ s/(\.\d*?)0+$/$1/;
$value =~ s/\.$//;
return $value;
}
}
sub floats { return map { float($_); } @_; }
sub floats5 { return map { float($_,5); } @_; }
sub intg {
my $f=shift @_;
return sprintf('%i',$f);
}
sub intgs { return map { intg($_); } @_; }
sub mMin {
my $n=HUGE_VAL;
map { $n=($n>$_) ? $_ : $n } @_;
return($n);
}
sub mMax {
my $n=-(HUGE_VAL);
map { $n=($n<$_) ? $_ : $n } @_;
return($n);
}
sub cRGB {
my @cmy=(map { 1-$_ } @_);
my $k=mMin(@cmy);
return((map { $_-$k } @cmy),$k);
}
sub cRGB8 {
return cRGB(map { $_/255 } @_);
}
sub RGBtoLUM {
my ($r,$g,$b)=@_;
return($r*0.299+$g*0.587+$b*0.114);
}
sub RGBasCMYK {
my @rgb=@_;
my @cmy=(map { 1-$_ } @rgb);
my $k=mMin(@cmy)*0.44;
return((map { $_-$k } @cmy),$k);
}
sub HSVtoRGB {
my ($h,$s,$v)=@_;
my ($r,$g,$b,$i,$f,$p,$q,$t);
if( $s == 0 ) {
## achromatic (grey)
return ($v,$v,$v);
}
$h %= 360;
$h /= 60; ## sector 0 to 5
$i = POSIX::floor( $h );
$f = $h - $i; ## factorial part of h
$p = $v * ( 1 - $s );
$q = $v * ( 1 - $s * $f );
$t = $v * ( 1 - $s * ( 1 - $f ) );
if($i<1) {
$r = $v;
$g = $t;
$b = $p;
} elsif($i<2){
$r = $q;
$g = $v;
$b = $p;
} elsif($i<3){
$r = $p;
$g = $v;
$b = $t;
} elsif($i<4){
$r = $p;
$g = $q;
$b = $v;
} elsif($i<5){
$r = $t;
$g = $p;
$b = $v;
} else {
$r = $v;
$g = $p;
$b = $q;
}
return ($r,$g,$b);
}
sub _HSVtoRGB { # test
my ($h,$s,$v)=@_;
my ($r,$g,$b,$i,$f,$p,$q,$t);
if( $s == 0 ) {
## achromatic (grey)
return ($v,$v,$v);
}
$h %= 360;
$r = 2*cos(deg2rad($h));
$g = 2*cos(deg2rad($h+120));
$b = 2*cos(deg2rad($h+240));
$p = max($r,$g,$b);
$q = min($r,$g,$b);
($p,$q) = map { ($_<0 ? 0 : ($_>1 ? 1 : $_)) } ($p,$q);
$f = $p - $q;
#if($p>=$v) {
# ($r,$g,$b) = map { $_*$v/$p } ($r,$g,$b);
#} else {
# ($r,$g,$b) = map { $_*$p/$v } ($r,$g,$b);
#}
#
#if($f>=$s) {
# ($r,$g,$b) = map { (($_-$q/2)*$f/$s)+$q/2 } ($r,$g,$b);
#} else {
# ($r,$g,$b) = map { (($_-$q/2)*$s/$f)+$q/2 } ($r,$g,$b);
#}
($r,$g,$b) = map { ($_<0 ? 0 : ($_>1 ? 1 : $_)) } ($r,$g,$b);
return ($r,$g,$b);
}
sub RGBquant ($$$) {
my($q1,$q2,$h)=@_;
while($h<0){$h+=360;}
$h%=360;
if ($h<60) {
return($q1+(($q2-$q1)*$h/60));
} elsif ($h<180) {
return($q2);
} elsif ($h<240) {
return($q1+(($q2-$q1)*(240-$h)/60));
} else {
return($q1);
}
}
sub RGBtoHSV {
my ($r,$g,$b)=@_;
my ($h,$s,$v,$min,$max,$delta);
$min= mMin($r,$g,$b);
$max= mMax($r,$g,$b);
$v = $max;
$delta = $max - $min;
if( $delta > 0.000000001 ) {
$s = $delta / $max;
} else {
$s = 0;
$h = 0;
return($h,$s,$v);
}
if( $r == $max ) {
$h = ( $g - $b ) / $delta;
} elsif( $g == $max ) {
$h = 2 + ( $b - $r ) / $delta;
} else {
$h = 4 + ( $r - $g ) / $delta;
}
$h *= 60;
if( $h < 0 ) {$h += 360;}
return($h,$s,$v);
}
sub RGBtoHSL {
my ($r,$g,$b)=@_;
my ($h,$s,$v,$l,$min,$max,$delta);
$min= mMin($r,$g,$b);
$max= mMax($r,$g,$b);
($h,$s,$v)=RGBtoHSV($r,$g,$b);
$l=($max+$min)/2.0;
$delta = $max - $min;
if($delta<0.00000000001){
return(0,0,$l);
} else {
if($l<=0.5){
$s=$delta/($max+$min);
} else {
$s=$delta/(2-$max-$min);
}
}
return($h,$s,$l);
}
sub HSLtoRGB {
my($h,$s,$l,$r,$g,$b,$p1,$p2)=@_;
if($l<=0.5){
$p2=$l*(1+$s);
} else {
$p2=$l+$s-($l*$s);
}
$p1=2*$l-$p2;
if($s<0.0000000000001){
$r=$l; $g=$l; $b=$l;
} else {
$r=RGBquant($p1,$p2,$h+120);
$g=RGBquant($p1,$p2,$h);
$b=RGBquant($p1,$p2,$h-120);
}
return($r,$g,$b);
}
sub optInvColor {
my ($r,$g,$b) = @_;
my $ab = (0.2*$r) + (0.7*$g) + (0.1*$b);
if($ab > 0.45) {
return(0,0,0);
} else {
return(1,1,1);
}
}
sub defineColor {
my ($name,$mx,$r,$g,$b)=@_;
$colors{$name}||=[ map {$_/$mx} ($r,$g,$b) ];
return($colors{$name});
}
sub rgbHexValues {
my $name=lc(shift @_);
my ($r,$g,$b);
if(length($name)<5) { # zb. #fa4, #cf0
$r=hex(substr($name,1,1))/0xf;
$g=hex(substr($name,2,1))/0xf;
$b=hex(substr($name,3,1))/0xf;
} elsif(length($name)<8) { # zb. #ffaa44, #ccff00
$r=hex(substr($name,1,2))/0xff;
$g=hex(substr($name,3,2))/0xff;
$b=hex(substr($name,5,2))/0xff;
} elsif(length($name)<11) { # zb. #fffaaa444, #cccfff000
$r=hex(substr($name,1,3))/0xfff;
$g=hex(substr($name,4,3))/0xfff;
$b=hex(substr($name,7,3))/0xfff;
} else { # zb. #ffffaaaa4444, #ccccffff0000
$r=hex(substr($name,1,4))/0xffff;
$g=hex(substr($name,5,4))/0xffff;
$b=hex(substr($name,9,4))/0xffff;
}
return($r,$g,$b);
}
sub cmykHexValues {
my $name=lc(shift @_);
my ($c,$m,$y,$k);
if(length($name)<6) { # zb. %cmyk
$c=hex(substr($name,1,1))/0xf;
$m=hex(substr($name,2,1))/0xf;
$y=hex(substr($name,3,1))/0xf;
$k=hex(substr($name,4,1))/0xf;
} elsif(length($name)<10) { # zb. %ccmmyykk
$c=hex(substr($name,1,2))/0xff;
$m=hex(substr($name,3,2))/0xff;
$y=hex(substr($name,5,2))/0xff;
$k=hex(substr($name,7,2))/0xff;
} elsif(length($name)<14) { # zb. %cccmmmyyykkk
$c=hex(substr($name,1,3))/0xfff;
$m=hex(substr($name,4,3))/0xfff;
$y=hex(substr($name,7,3))/0xfff;
$k=hex(substr($name,10,3))/0xfff;
} else { # zb. %ccccmmmmyyyykkkk
$c=hex(substr($name,1,4))/0xffff;
$m=hex(substr($name,5,4))/0xffff;
$y=hex(substr($name,9,4))/0xffff;
$k=hex(substr($name,13,4))/0xffff;
}
return($c,$m,$y,$k);
}
sub hsvHexValues {
my $name=lc(shift @_);
my ($h,$s,$v);
if(length($name)<5) {
$h=360*hex(substr($name,1,1))/0x10;
$s=hex(substr($name,2,1))/0xf;
$v=hex(substr($name,3,1))/0xf;
} elsif(length($name)<8) {
$h=360*hex(substr($name,1,2))/0x100;
$s=hex(substr($name,3,2))/0xff;
$v=hex(substr($name,5,2))/0xff;
} elsif(length($name)<11) {
$h=360*hex(substr($name,1,3))/0x1000;
$s=hex(substr($name,4,3))/0xfff;
$v=hex(substr($name,7,3))/0xfff;
} else {
$h=360*hex(substr($name,1,4))/0x10000;
$s=hex(substr($name,5,4))/0xffff;
$v=hex(substr($name,9,4))/0xffff;
}
return($h,$s,$v);
}
sub labHexValues {
my $name=lc(shift @_);
my ($l,$a,$b);
if(length($name)<5) {
$l=100*hex(substr($name,1,1))/0xf;
$a=(200*hex(substr($name,2,1))/0xf)-100;
$b=(200*hex(substr($name,3,1))/0xf)-100;
} elsif(length($name)<8) {
$l=100*hex(substr($name,1,2))/0xff;
$a=(200*hex(substr($name,3,2))/0xff)-100;
$b=(200*hex(substr($name,5,2))/0xff)-100;
} elsif(length($name)<11) {
$l=100*hex(substr($name,1,3))/0xfff;
$a=(200*hex(substr($name,4,3))/0xfff)-100;
$b=(200*hex(substr($name,7,3))/0xfff)-100;
} else {
$l=100*hex(substr($name,1,4))/0xffff;
$a=(200*hex(substr($name,5,4))/0xffff)-100;
$b=(200*hex(substr($name,9,4))/0xffff)-100;
}
return($l,$a,$b);
}
sub namecolor {
my $name=shift @_;
unless(ref $name) {
$name=lc($name);
$name=~s/[^\#!%\&\$a-z0-9]//go;
}
if($name=~/^[a-z]/) { # name spec.
return(namecolor($colors{$name}));
} elsif($name=~/^#/) { # rgb spec.
return(floats5(rgbHexValues($name)));
} elsif($name=~/^%/) { # cmyk spec.
return(floats5(cmykHexValues($name)));
} elsif($name=~/^!/) { # hsv spec.
return(floats5(HSVtoRGB(hsvHexValues($name))));
} elsif($name=~/^&/) { # hsl spec.
return(floats5(HSLtoRGB(hsvHexValues($name))));
} else { # or it is a ref ?
return(floats5(@{$name || [0.5,0.5,0.5]}));
}
}
sub namecolor_cmyk {
my $name=shift @_;
unless(ref $name) {
$name=lc($name);
$name=~s/[^\#!%\&\$a-z0-9]//go;
}
if($name=~/^[a-z]/) { # name spec.
return(namecolor_cmyk($colors{$name}));
} elsif($name=~/^#/) { # rgb spec.
return(floats5(RGBasCMYK(rgbHexValues($name))));
} elsif($name=~/^%/) { # cmyk spec.
return(floats5(cmykHexValues($name)));
} elsif($name=~/^!/) { # hsv spec.
return(floats5(RGBasCMYK(HSVtoRGB(hsvHexValues($name)))));
} elsif($name=~/^&/) { # hsl spec.
return(floats5(RGBasCMYK(HSLtoRGB(hsvHexValues($name)))));
} else { # or it is a ref ?
return(floats5(RGBasCMYK(@{$name || [0.5,0.5,0.5]})));
}
}
sub namecolor_lab {
my $name=shift @_;
unless(ref $name) {
$name=lc($name);
$name=~s/[^\#!%\&\$a-z0-9]//go;
}
if($name=~/^[a-z]/) { # name spec.
return(namecolor_lab($colors{$name}));
} elsif($name=~/^\$/) { # lab spec.
return(floats5(labHexValues($name)));
} elsif($name=~/^#/) { # rgb spec.
my ($h,$s,$v)=RGBtoHSV(rgbHexValues($name));
my $a=cos(deg2rad $h)*$s*100;
my $b=sin(deg2rad $h)*$s*100;
my $l=100*$v;
return(floats5($l,$a,$b));
} elsif($name=~/^!/) { # hsv spec.
# fake conversion
my ($h,$s,$v)=hsvHexValues($name);
my $a=cos(deg2rad $h)*$s*100;
my $b=sin(deg2rad $h)*$s*100;
my $l=100*$v;
return(floats5($l,$a,$b));
} elsif($name=~/^&/) { # hsl spec.
my ($h,$s,$v)=hsvHexValues($name);
my $a=cos(deg2rad $h)*$s*100;
my $b=sin(deg2rad $h)*$s*100;
($h,$s,$v)=RGBtoHSV(HSLtoRGB($h,$s,$v));
my $l=100*$v;
return(floats5($l,$a,$b));
} else { # or it is a ref ?
my ($h,$s,$v)=RGBtoHSV(@{$name || [0.5,0.5,0.5]});
my $a=cos(deg2rad $h)*$s*100;
my $b=sin(deg2rad $h)*$s*100;
my $l=100*$v;
return(floats5($l,$a,$b));
}
}
sub unfilter
{
my ($filter,$stream)=@_;
if(defined $filter)
{
# we need to fix filter because it MAY be
# an array BUT IT COULD BE only a name
if(ref($filter)!~/Array$/)
{
$filter = PDFArray($filter);
}
my @filts;
my ($hasflate) = -1;
my ($temp, $i, $temp1);
@filts=(map { ("PDF::API2::Basic::PDF::Filter::".($_->val))->new } $filter->elementsof);
foreach my $f (@filts)
{
$stream = $f->infilt($stream, 1);
}
}
return($stream);
}
sub dofilter {
my ($filter,$stream)=@_;
if((defined $filter) ) {
# we need to fix filter because it MAY be
# an array BUT IT COULD BE only a name
if(ref($filter)!~/Array$/) {
$filter = PDFArray($filter);
}
my @filts;
my ($hasflate) = -1;
my ($temp, $i, $temp1);
@filts=(map { ("PDF::API2::Basic::PDF::Filter::".($_->val))->new } $filter->elementsof);
foreach my $f (@filts) {
$stream = $f->outfilt($stream, 1);
}
}
return($stream);
}
sub nameByUni {
my ($e)=@_;
return($u2n{$e} || sprintf('uni%04X',$e));
}
sub uniByName {
my ($e)=@_;
if($e=~/^uni([0-9A-F]{4})$/) {
return(hex($1));
}
return($n2u{$e} || undef);
}
sub initNameTable {
%u2n = %{$PDF::API2::Resource::Glyphs::u2n};
%n2u = %{$PDF::API2::Resource::Glyphs::n2u};
$pua = 0xE000;
1;
}
sub defineName {
my $name=shift @_;
return($n2u{$name}) if(defined $n2u{$name});
while(defined $u2n{$pua}) { $pua++; }
$u2n{$pua}=$name;
$n2u{$name}=$pua;
return($pua);
}
sub page_size {
my ($x1, $y1, $x2, $y2) = @_;
# full bbox
if (defined $x2) {
return ($x1, $y1, $x2, $y2);
}
# half bbox
elsif (defined $y1) {
return (0, 0, $x1, $y1);
}
# textual spec.
elsif (defined $PaperSizes{lc $x1}) {
return (0, 0, @{$PaperSizes{lc $x1}});
}
# single quadratic
elsif ($x1 =~ /^[\d\.]+$/) {
return(0, 0, $x1, $x1);
}
# pdf default.
else {
return (0, 0, 612, 792);
}
}
sub getPaperSizes {
my %sizes = ();
foreach my $type (keys %PaperSizes) {
$sizes{$type} = [@{$PaperSizes{$type}}];
}
return %sizes;
}
1;
__END__
=head1 NAME
PDF::API2::Util - utility package for often use methods across the package.
=head1 PREDEFINED PAPERSIZES
=over 4
=item %sizes = getPaperSizes();
Will retrieve the registered paper sizes of PDF::API2.
print Dumper(\%sizes);
$VAR1={
'4a' => [ 4760 , 6716 ],
'2a' => [ 3368 , 4760 ],
'a0' => [ 2380 , 3368 ],
'a1' => [ 1684 , 2380 ],
'a2' => [ 1190 , 1684 ],
'a3' => [ 842 , 1190 ],
'a4' => [ 595 , 842 ],
'a5' => [ 421 , 595 ],
'a6' => [ 297 , 421 ],
'4b' => [ 5656 , 8000 ],
'2b' => [ 4000 , 5656 ],
'b0' => [ 2828 , 4000 ],
'b1' => [ 2000 , 2828 ],
'b2' => [ 1414 , 2000 ],
'b3' => [ 1000 , 1414 ],
'b4' => [ 707 , 1000 ],
'b5' => [ 500 , 707 ],
'b6' => [ 353 , 500 ],
'letter' => [ 612 , 792 ],
'broadsheet' => [ 1296 , 1584 ],
'ledger' => [ 1224 , 792 ],
'tabloid' => [ 792 , 1224 ],
'legal' => [ 612 , 1008 ],
'executive' => [ 522 , 756 ],
'36x36' => [ 2592 , 2592 ],
};
=back
=head1 PREDEFINED COLORS
See the source of L<PDF::API2::Resource::Colors> for a complete list.
B<Please Note:> This is an amalgamation of the X11, SGML and (X)HTML
specification sets.
=head1 PREDEFINED GLYPH-NAMES
See the file C<uniglyph.txt> for a complete list.
B<Please Note:> You may notice that apart from the 'AGL/WGL4', names
from the XML, (X)HTML and SGML specification sets have been included
to enable interoperability towards PDF.
=cut