Linux ip-172-26-7-228 5.4.0-1103-aws #111~18.04.1-Ubuntu SMP Tue May 23 20:04:10 UTC 2023 x86_64
Your IP : 3.149.24.49
Current Path : /usr/bin/X11/ |
| Current File : //usr/bin/X11/gropdf |
#!/usr/bin/perl -w
#
# gropdf : PDF post processor for groff
#
# Copyright (C) 2011-2014 Free Software Foundation, Inc.
# Written by Deri James <deri@chuzzlewit.demon.co.uk>
#
# This file is part of groff.
#
# groff is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# groff is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
use strict;
use Getopt::Long qw(:config bundling);
use Compress::Zlib;
my %cfg;
$cfg{GROFF_VERSION}='1.22.3';
$cfg{GROFF_FONT_PATH}='/usr/share/groff/site-font:/usr/share/groff/1.22.3/font:/usr/lib/font';
$cfg{RT_SEP}=':';
binmode(STDOUT);
my @obj; # Array of PDF objects
my $objct=0; # Count of Objects
my $fct=0; # Output count
my %fnt; # Used fonts
my $lct=0; # Input Line Count
my $src_name='';
my %env; # Current environment
my %fontlst; # Fonts Loaded
my $rot=0; # Portrait
my %desc; # Contents of DESC
my %download; # Contents of downlopad file
my $pages; # Pointer to /Pages object
my $devnm='devpdf';
my $cpage; # Pointer to current pages
my $cpageno=0; # Object no of current page
my $cat; # Pointer to catalogue
my $dests; # Pointer to Dests
my @mediabox=(0,0,595,842);
my @defaultmb=(0,0,595,842);
my $stream=''; # Current Text/Graphics stream
my $cftsz=10; # Current font sz
my $cft; # Current Font
my $lwidth=1; # current linewidth
my $linecap=1;
my $linejoin=1;
my $textcol=''; # Current groff text
my $fillcol=''; # Current groff fill
my $curfill=''; # Current PDF fill
my $strkcol='';
my $curstrk='';
my @lin=(); # Array holding current line of text
my @ahead=(); # Buffer used to hol the next line
my $mode='g'; # Graphic (g) or Text (t) mode;
my $xpos=0; # Current X position
my $ypos=0; # Current Y position
my $tmxpos=0;
my $kernadjust=0;
my $curkern=0;
my $widtbl; # Pointer to width table for current font size
my $origwidtbl; # Pointer to width table
my $krntbl; # Pointer to kern table
my $matrix="1 0 0 1";
my $whtsz; # Current width of a space
my $poschg=0; # V/H pending
my $fontchg=0; # font change pending
my $tnum=2; # flatness of B-Spline curve
my $tden=3; # flatness of B-Spline curve
my $linewidth=40;
my $w_flg=0;
my $nomove=0;
my $pendmv=0;
my $gotT=0;
my $suppress=0; # Suppress processing?
my %incfil; # Included Files
my @outlev=([0,undef,0,0]); # Structure pdfmark /OUT entries
my $curoutlev=\@outlev;
my $curoutlevno=0; # Growth point for @curoutlev
my $Foundry='';
my $xrev=0; # Reverse x direction of font
my $matrixchg=0;
my $wt=-1;
my $thislev=1;
my $mark=undef;
my $suspendmark=undef;
my $n_flg=1;
my $pginsert=-1; # Growth point for kids array
my %pgnames; # 'names' of pages for switchtopage
my @outlines=(); # State of Bookmark Outlines at end of each page
my $custompaper=0; # Has there been an X papersize
my $textenccmap=''; # CMap for groff text.enc encoding
my %ppsz=( 'ledger'=>[1224,792],
'legal'=>[612,1008],
'letter'=>[612,792],
'a0'=>[2384,3370],
'a1'=>[1684,2384],
'a2'=>[1191,1684],
'a3'=>[842,1191],
'a4'=>[595,842],
'a5'=>[420,595],
'a6'=>[297,420],
'a7'=>[210,297],
'a8'=>[148,210],
'a9'=>[105,148],
'a10'=>[73,105],
'isob0'=>[2835,4008],
'isob1'=>[2004,2835],
'isob2'=>[1417,2004],
'isob3'=>[1001,1417],
'isob4'=>[709,1001],
'isob5'=>[499,709],
'isob6'=>[354,499],
'c0'=>[2599,3677],
'c1'=>[1837,2599],
'c2'=>[1298,1837],
'c3'=>[918,1298],
'c4'=>[649,918],
'c5'=>[459,649],
'c6'=>[323,459] );
my $ucmap=<<'EOF';
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (Adobe)
/Ordering (UCS)
/Supplement 0
>> def
/CMapName /Adobe-Identity-UCS def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
2 beginbfrange
<008b> <008f> [<00660066> <00660069> <0066006c> <006600660069> <00660066006C>]
<00ad> <00ad> <002d>
endbfrange
endcmap
CMapName currentdict /CMap defineresource pop
end
end
EOF
my $fd;
my $frot;
my $fpsz;
my $embedall=0;
my $debug=0;
my $version=0;
my $stats=0;
my $unicodemap;
#Load_Config();
GetOptions("F=s" => \$fd, 'l' => \$frot, 'p=s' => \$fpsz, 'd!' => \$debug, 'v' => \$version, 'e' => \$embedall, 'y=s' => \$Foundry, 's' => \$stats, 'u:s' => \$unicodemap);
if ($version)
{
print "GNU gropdf (groff) version $cfg{GROFF_VERSION}\n";
exit;
}
if (defined($unicodemap))
{
if ($unicodemap eq '')
{
$ucmap='';
}
elsif (-r $unicodemap)
{
local $/;
open(F,"<$unicodemap") or die "gropdf: Failed to open '$unicodemap'";
($ucmap)=(<F>);
close(F);
}
else
{
Msg(0,"Failed to find '$unicodemap' - ignoring");
}
}
# Search for 'font directory': paths in -f opt, shell var GROFF_FONT_PATH, default paths
my $fontdir=$cfg{GROFF_FONT_PATH};
$fontdir=$ENV{GROFF_FONT_PATH}.$cfg{RT_SEP}.$fontdir if exists($ENV{GROFF_FONT_PATH});
$fontdir=$fd.$cfg{RT_SEP}.$fontdir if defined($fd);
$rot=90 if $frot;
$matrix="0 1 -1 0" if $frot;
LoadDownload();
LoadDesc();
my $unitwidth=$desc{unitwidth};
my $papersz=$desc{papersize};
$papersz=lc($fpsz) if $fpsz;
$env{FontHT}=0;
$env{FontSlant}=0;
MakeMatrix();
if (substr($papersz,0,1) eq '/' and -r $papersz)
{
if (open(P,"<$papersz"))
{
while (<P>)
{
chomp;
s/# .*//;
next if $_ eq '';
$papersz=$_;
last
}
close(P);
}
}
if ($papersz=~m/([\d.]+)([cipP]),([\d.]+)([cipP])/)
{
@defaultmb=@mediabox=(0,0,ToPoints($3,$4),ToPoints($1,$2));
}
elsif (exists($ppsz{$papersz}))
{
@defaultmb=@mediabox=(0,0,$ppsz{$papersz}->[0],$ppsz{$papersz}->[1]);
}
my (@dt)=gmtime($ENV{SOURCE_DATE_EPOCH} || time);
my $dt=PDFDate(\@dt);
my %info=('Creator' => "(groff version $cfg{GROFF_VERSION})",
'Producer' => "(gropdf version $cfg{GROFF_VERSION})",
'ModDate' => "($dt)",
'CreationDate' => "($dt)");
while (<>)
{
chomp;
s/\r$//;
$lct++;
do # The ahead buffer behaves like 'ungetc'
{{
if (scalar(@ahead))
{
$_=shift(@ahead);
}
my $cmd=substr($_,0,1);
next if $cmd eq '#'; # just a comment
my $lin=substr($_,1);
while ($cmd eq 'w')
{
$cmd=substr($lin,0,1);
$lin=substr($lin,1);
$w_flg=1 if $gotT;
}
$lin=~s/^\s+//;
# $lin=~s/\s#.*?$//; # remove comment
$stream.="\% $_\n" if $debug;
do_x($lin),next if ($cmd eq 'x');
next if $suppress;
do_p($lin),next if ($cmd eq 'p');
do_f($lin),next if ($cmd eq 'f');
do_s($lin),next if ($cmd eq 's');
do_m($lin),next if ($cmd eq 'm');
do_D($lin),next if ($cmd eq 'D');
do_V($lin),next if ($cmd eq 'V');
do_v($lin),next if ($cmd eq 'v');
do_t($lin),next if ($cmd eq 't');
do_u($lin),next if ($cmd eq 'u');
do_C($lin),next if ($cmd eq 'C');
do_c($lin),next if ($cmd eq 'c');
do_N($lin),next if ($cmd eq 'N');
do_h($lin),next if ($cmd eq 'h');
do_H($lin),next if ($cmd eq 'H');
do_n($lin),next if ($cmd eq 'n');
my $tmp=scalar(@ahead);
}} until scalar(@ahead) == 0;
}
if ($cpageno > 0)
{
$cpage->{MediaBox}=\@mediabox if $custompaper;
PutObj($cpageno);
OutStream($cpageno+1);
}
PutOutlines(\@outlev);
PutObj(1);
my $info=BuildObj(++$objct,\%info);
PutObj($objct);
foreach my $fontno (sort keys %fontlst)
{
my $o=$fontlst{$fontno}->{FNT};
my $p=GetObj($fontlst{$fontno}->{OBJ});
if (exists($p->{LastChar}) and $p->{LastChar} > 255)
{
$p->{LastChar} = 255;
splice(@{$o->{GNO}},256);
splice(@{$o->{WID}},256);
}
}
foreach my $o (3..$objct)
{
PutObj($o) if (!exists($obj[$o]->{XREF}));
}
#my $encrypt=BuildObj(++$objct,{'Filter' => '/Standard', 'V' => 1, 'R' => 2, 'P' => 252});
#PutObj($objct);
PutObj(2);
my $xrefct=$fct;
$objct+=1;
print "xref\n0 $objct\n0000000000 65535 f \n";
foreach my $xr (@obj)
{
next if !defined($xr);
printf("%010d 00000 n \n",$xr->{XREF});
}
print "trailer\n<<\n/Info $info\n/Root 1 0 R\n/Size $objct\n>>\nstartxref\n$fct\n\%\%EOF\n";
print "\% Pages=$pages->{Count}\n" if $stats;
sub MakeMatrix
{
my $fontxrev=shift||0;
my @mat=($frot)?(0,1,-1,0):(1,0,0,1);
if (!$frot)
{
if ($env{FontHT} != 0)
{
$mat[3]=sprintf('%.3f',$env{FontHT}/$cftsz);
}
if ($env{FontSlant} != 0)
{
my $slant=$env{FontSlant};
$slant*=$env{FontHT}/$cftsz if $env{FontHT} != 0;
my $ang=rad($slant);
$mat[2]=sprintf('%.3f',sin($ang)/cos($ang));
}
if ($fontxrev)
{
$mat[0]=-$mat[0];
}
}
$matrix=join(' ',@mat);
$matrixchg=1;
}
sub PutOutlines
{
my $o=shift;
my $outlines;
if ($#{$o} > 0)
{
# We've got Outlines to deal with
my $openct=$curoutlev->[0]->[2];
while ($thislev-- > 1)
{
my $nxtoutlev=$curoutlev->[0]->[1];
$nxtoutlev->[0]->[2]+=$openct if $curoutlev->[0]->[3]==1;
$openct=0 if $nxtoutlev->[0]->[3]==-1;
$curoutlev=$nxtoutlev;
}
$cat->{Outlines}=BuildObj(++$objct,{'Count' => abs($o->[0]->[0])+$o->[0]->[2]});
$outlines=$obj[$objct]->{DATA};
}
else
{
return;
}
SetOutObj($o);
$outlines->{First}=$o->[1]->[2];
$outlines->{Last}=$o->[$#{$o}]->[2];
LinkOutObj($o,$cat->{Outlines});
}
sub SetOutObj
{
my $o=shift;
for my $j (1..$#{$o})
{
my $ono=BuildObj(++$objct,$o->[$j]->[0]);
$o->[$j]->[2]=$ono;
SetOutObj($o->[$j]->[1]) if $#{$o->[$j]->[1]} > -1;
}
}
sub LinkOutObj
{
my $o=shift;
my $parent=shift;
for my $j (1..$#{$o})
{
my $op=GetObj($o->[$j]->[2]);
$op->{Next}=$o->[$j+1]->[2] if ($j < $#{$o});
$op->{Prev}=$o->[$j-1]->[2] if ($j > 1);
$op->{Parent}=$parent;
if ($#{$o->[$j]->[1]} > -1)
{
$op->{Count}=$o->[$j]->[1]->[0]->[2]*$o->[$j]->[1]->[0]->[3];# if exists($op->{Count}) and $op->{Count} > 0;
$op->{First}=$o->[$j]->[1]->[1]->[2];
$op->{Last}=$o->[$j]->[1]->[$#{$o->[$j]->[1]}]->[2];
LinkOutObj($o->[$j]->[1],$o->[$j]->[2]);
}
}
}
sub GetObj
{
my $ono=shift;
($ono)=split(' ',$ono);
return($obj[$ono]->{DATA});
}
sub PDFDate
{
my $dt=shift;
return(sprintf("D:%04d%02d%02d%02d%02d%02d+00'00'",$dt->[5]+1900,$dt->[4]+1,$dt->[3],$dt->[2],$dt->[1],$dt->[0]));
}
sub ToPoints
{
my $num=shift;
my $unit=shift;
if ($unit eq 'i')
{
return($num*72);
}
elsif ($unit eq 'c')
{
return int($num*72/2.54);
}
elsif ($unit eq 'm') # millimetres
{
return int($num*72/25.4);
}
elsif ($unit eq 'p')
{
return($num);
}
elsif ($unit eq 'P')
{
return($num*6);
}
elsif ($unit eq 'z')
{
return($num/$unitwidth);
}
else
{
Msg(1,"Unknown scaling factor '$unit'");
}
}
sub Load_Config
{
open(CFG,"<gropdf_config") or die "Can't open config file: $!";
while (<CFG>)
{
chomp;
my ($key,$val)=split(/ ?= ?/);
$cfg{$key}=$val;
}
close(CFG);
}
sub LoadDownload
{
my $f;
OpenFile(\$f,$fontdir,"download");
Msg(1,"Failed to open 'download'") if !defined($f);
while (<$f>)
{
chomp;
s/#.*$//;
next if $_ eq '';
my ($foundry,$name,$file)=split(/\t+/);
if (substr($file,0,1) eq '*')
{
next if !$embedall;
$file=substr($file,1);
}
$download{"$foundry $name"}=$file;
}
close($f);
}
sub OpenFile
{
my $f=shift;
my $dirs=shift;
my $fnm=shift;
if (substr($fnm,0,1) eq '/' or substr($fnm,1,1) eq ':') # dos
{
return if -r "$fnm" and open($$f,"<$fnm");
}
my (@dirs)=split($cfg{RT_SEP},$dirs);
foreach my $dir (@dirs)
{
last if -r "$dir/$devnm/$fnm" and open($$f,"<$dir/$devnm/$fnm");
}
}
sub LoadDesc
{
my $f;
OpenFile(\$f,$fontdir,"DESC");
Msg(1,"Failed to open 'DESC'") if !defined($f);
while (<$f>)
{
chomp;
s/#.*$//;
next if $_ eq '';
my ($name,$prms)=split(' ',$_,2);
$desc{lc($name)}=$prms;
}
close($f);
}
sub rad { $_[0]*3.14159/180 }
my $InPicRotate=0;
sub do_x
{
my $l=shift;
my ($xcmd,@xprm)=split(' ',$l);
$xcmd=substr($xcmd,0,1);
if ($xcmd eq 'T')
{
Msg(0,"Expecting a pdf pipe (got $xprm[0])") if $xprm[0] ne substr($devnm,3);
}
elsif ($xcmd eq 'f') # Register Font
{
$xprm[1]="${Foundry}-$xprm[1]" if $Foundry ne '';
LoadFont($xprm[0],$xprm[1]);
}
elsif ($xcmd eq 'F') # Source File (for errors)
{
$env{SourceFile}=$xprm[0];
}
elsif ($xcmd eq 'H') # FontHT
{
$xprm[0]/=$unitwidth;
$xprm[0]=0 if $xprm[0] == $cftsz;
$env{FontHT}=$xprm[0];
MakeMatrix();
}
elsif ($xcmd eq 'S') # FontSlant
{
$env{FontSlant}=$xprm[0];
MakeMatrix();
}
elsif ($xcmd eq 'i') # Initialise
{
if ($objct == 0)
{
$objct++;
@defaultmb=@mediabox;
BuildObj($objct,{'Pages' => BuildObj($objct+1,
{'Kids' => [],
'Count' => 0,
'Type' => '/Pages',
'Rotate' => $rot,
'MediaBox' => \@defaultmb,
'Resources' =>
{'Font' => {},
'ProcSet' => ['/PDF', '/Text', '/ImageB', '/ImageC', '/ImageI']}
}
),
'Type' => '/Catalog'});
$cat=$obj[$objct]->{DATA};
$objct++;
$pages=$obj[2]->{DATA};
Put("%PDF-1.4\n\x25\xe2\xe3\xcf\xd3\n");
}
}
elsif ($xcmd eq 'X')
{
# There could be extended args
do
{{
LoadAhead(1);
if (substr($ahead[0],0,1) eq '+')
{
$l.="\n".substr($ahead[0],1);
shift(@ahead);
}
}} until $#ahead==0;
($xcmd,@xprm)=split(' ',$l);
$xcmd=substr($xcmd,0,1);
if ($xprm[0]=~m/^(.+:)(.+)/)
{
splice(@xprm,1,0,$2);
$xprm[0]=$1;
}
my $par=join(' ',@xprm[1..$#xprm]);
if ($xprm[0] eq 'ps:')
{
if ($xprm[1] eq 'invis')
{
$suppress=1;
}
elsif ($xprm[1] eq 'endinvis')
{
$suppress=0;
}
elsif ($par=~m/exec gsave currentpoint 2 copy translate (.+) rotate neg exch neg exch translate/)
{
# This is added by gpic to rotate a single object
my $theta=-rad($1);
IsGraphic();
my ($curangle,$hyp)=RtoP($xpos,GraphY($ypos));
my ($x,$y)=PtoR($theta+$curangle,$hyp);
$stream.="q\n".sprintf("%.3f %.3f %.3f %.3f %.3f %.3f cm",cos($theta),sin($theta),-sin($theta),cos($theta),$xpos-$x,GraphY($ypos)-$y)."\n";
$InPicRotate=1;
}
elsif ($par=~m/exec grestore/ and $InPicRotate)
{
IsGraphic();
$stream.="Q\n";
$InPicRotate=0;
}
elsif ($par=~m/exec (\d) setlinejoin/)
{
IsGraphic();
$linejoin=$1;
$stream.="$linejoin j\n";
}
elsif ($par=~m/exec (\d) setlinecap/)
{
IsGraphic();
$linecap=$1;
$stream.="$linecap J\n";
}
elsif ($par=~m/\[(.+) pdfmark/)
{
my $pdfmark=$1;
$pdfmark=~s((\d{4,6}) u)(sprintf("%.1f",$1/$desc{sizescale}))eg;
$pdfmark=~s(\\\[u00(..)\])(chr(hex($1)))eg;
if ($pdfmark=~m/(.+) \/DOCINFO/)
{
my @xwds=split(' ',"<< $1 >>");
my $docinfo=ParsePDFValue(\@xwds);
foreach my $k (sort keys %{$docinfo})
{
$info{$k}=$docinfo->{$k} if $k ne 'Producer';
}
}
elsif ($pdfmark=~m/(.+) \/DOCVIEW/)
{
my @xwds=split(' ',"<< $1 >>");
my $docview=ParsePDFValue(\@xwds);
foreach my $k (sort keys %{$docview})
{
$cat->{$k}=$docview->{$k} if !exists($cat->{$k});
}
}
elsif ($pdfmark=~m/(.+) \/DEST/)
{
my @xwds=split(' ',"<< $1 >>");
my $dest=ParsePDFValue(\@xwds);
foreach my $v (@{$dest->{View}})
{
$v=GraphY(abs($v)) if substr($v,0,1) eq '-';
}
unshift(@{$dest->{View}},"$cpageno 0 R");
if (!defined($dests))
{
$cat->{Dests}=BuildObj(++$objct,{});
$dests=$obj[$objct]->{DATA};
}
my $k=substr($dest->{Dest},1);
$dests->{$k}=$dest->{View};
}
elsif ($pdfmark=~m/(.+) \/ANN/)
{
my $l=$1;
$l=~s/Color/C/;
$l=~s/Action/A/;
$l=~s/Title/T/;
$l=~s'/Subtype /URI'/S /URI';
my @xwds=split(' ',"<< $l >>");
my $annotno=BuildObj(++$objct,ParsePDFValue(\@xwds));
my $annot=$obj[$objct];
$annot->{DATA}->{Type}='/Annot';
FixRect($annot->{DATA}->{Rect}); # Y origin to ll
FixPDFColour($annot->{DATA});
push(@{$cpage->{Annots}},$annotno);
}
elsif ($pdfmark=~m/(.+) \/OUT/)
{
my @xwds=split(' ',"<< $1 >>");
my $out=ParsePDFValue(\@xwds);
my $this=[$out,[]];
if (exists($out->{Level}))
{
my $lev=abs($out->{Level});
my $levsgn=sgn($out->{Level});
delete($out->{Level});
if ($lev > $thislev)
{
my $thisoutlev=$curoutlev->[$#{$curoutlev}]->[1];
$thisoutlev->[0]=[0,$curoutlev,0,$levsgn];
$curoutlev=$thisoutlev;
$curoutlevno=$#{$curoutlev};
$thislev++;
}
elsif ($lev < $thislev)
{
my $openct=$curoutlev->[0]->[2];
while ($thislev > $lev)
{
my $nxtoutlev=$curoutlev->[0]->[1];
$nxtoutlev->[0]->[2]+=$openct if $curoutlev->[0]->[3]==1;
$openct=0 if $nxtoutlev->[0]->[3]==-1;
$curoutlev=$nxtoutlev;
$thislev--;
}
$curoutlevno=$#{$curoutlev};
}
# push(@{$curoutlev},$this);
splice(@{$curoutlev},++$curoutlevno,0,$this);
$curoutlev->[0]->[2]++;
}
else
{
# This code supports old pdfmark.tmac, unused by pdf.tmac
while ($curoutlev->[0]->[0] == 0 and defined($curoutlev->[0]->[1]))
{
$curoutlev=$curoutlev->[0]->[1];
}
$curoutlev->[0]->[0]--;
$curoutlev->[0]->[2]++;
push(@{$curoutlev},$this);
if (exists($out->{Count}) and $out->{Count} != 0)
{
push(@{$this->[1]},[abs($out->{Count}),$curoutlev,0,sgn($out->{Count})]);
$curoutlev=$this->[1];
if ($out->{Count} > 0)
{
my $p=$curoutlev;
while (defined($p))
{
$p->[0]->[2]+=$out->{Count};
$p=$p->[0]->[1];
}
}
}
}
}
}
}
elsif (lc($xprm[0]) eq 'pdf:')
{
if (lc($xprm[1]) eq 'import')
{
my $fil=$xprm[2];
my $llx=$xprm[3];
my $lly=$xprm[4];
my $urx=$xprm[5];
my $ury=$xprm[6];
my $wid=$xprm[7];
my $hgt=$xprm[8]||-1;
my $mat=[1,0,0,1,0,0];
if (!exists($incfil{$fil}))
{
if ($fil=~m/\.pdf$/)
{
$incfil{$fil}=LoadPDF($fil,$mat,$wid,$hgt,"import");
}
elsif ($fil=~m/\.swf$/)
{
my $xscale=$wid/($urx-$llx+1);
my $yscale=($hgt<=0)?$xscale:($hgt/($ury-$lly+1));
$hgt=($ury-$lly+1)*$yscale;
if ($rot)
{
$mat->[3]=$xscale;
$mat->[0]=$yscale;
}
else
{
$mat->[0]=$xscale;
$mat->[3]=$yscale;
}
$incfil{$fil}=LoadSWF($fil,[$llx,$lly,$urx,$ury],$mat);
}
else
{
Msg(0,"Unknown filetype '$fil'");
return undef;
}
}
if (defined($incfil{$fil}))
{
IsGraphic();
if ($fil=~m/\.pdf$/)
{
my $bbox=$incfil{$fil}->[1];
my $xscale=$wid/($bbox->[2]-$bbox->[0]+1);
my $yscale=($hgt<=0)?$xscale:($hgt/($bbox->[3]-$bbox->[1]+1));
$stream.="q $xscale 0 0 $yscale ".PutXY($xpos,$ypos)." cm";
$stream.=" 0 1 -1 0 0 0 cm" if $rot;
$stream.=" /$incfil{$fil}->[0] Do Q\n";
}
elsif ($fil=~m/\.swf$/)
{
$stream.=PutXY($xpos,$ypos)." m /$incfil{$fil} Do\n";
}
}
}
elsif (lc($xprm[1]) eq 'pdfpic')
{
my $fil=$xprm[2];
my $flag=uc($xprm[3])||'-L';
my $wid=GetPoints($xprm[4])||-1;
my $hgt=GetPoints($xprm[5]||-1);
my $ll=GetPoints($xprm[6]||0);
my $mat=[1,0,0,1,0,0];
if (!exists($incfil{$fil}))
{
$incfil{$fil}=LoadPDF($fil,$mat,$wid,$hgt,"pdfpic");
}
if (defined($incfil{$fil}))
{
IsGraphic();
my $bbox=$incfil{$fil}->[1];
$wid=($bbox->[2]-$bbox->[0]) if $wid <= 0;
my $xscale=$wid/($bbox->[2]-$bbox->[0]);
my $yscale=($hgt<=0)?$xscale:($hgt/($bbox->[3]-$bbox->[1]));
$xscale=($wid<=0)?$yscale:$xscale;
$xscale=$yscale if $yscale < $xscale;
$yscale=$xscale if $xscale < $yscale;
$wid=($bbox->[2]-$bbox->[0])*$xscale;
$hgt=($bbox->[3]-$bbox->[1])*$yscale;
if ($flag eq '-C' and $ll > $wid)
{
$xpos=int(($ll-$wid)/2);
}
elsif ($flag eq '-R' and $ll > $wid)
{
$xpos=$ll-$wid;
}
$ypos+=$hgt;
$stream.="q $xscale 0 0 $yscale ".PutXY($xpos,$ypos)." cm";
$stream.=" 0 1 -1 0 0 0 cm" if $rot;
$stream.=" /$incfil{$fil}->[0] Do Q\n";
}
}
elsif (lc($xprm[1]) eq 'xrev')
{
$xrev=!$xrev;
}
elsif (lc($xprm[1]) eq 'markstart')
{
$mark={'rst' => ($xprm[2]+$xprm[4])/$unitwidth, 'rsb' => ($xprm[3]-$xprm[4])/$unitwidth, 'xpos' => $xpos-($xprm[4]/$unitwidth),
'ypos' => $ypos, 'lead' => $xprm[4]/$unitwidth, 'pdfmark' => join(' ',@xprm[5..$#xprm])};
}
elsif (lc($xprm[1]) eq 'markend')
{
PutHotSpot($xpos) if defined($mark);
$mark=undef;
}
elsif (lc($xprm[1]) eq 'marksuspend')
{
$suspendmark=$mark;
$mark=undef;
}
elsif (lc($xprm[1]) eq 'markrestart')
{
$mark=$suspendmark;
$suspendmark=undef;
}
elsif (lc($xprm[1]) eq 'pagename')
{
if ($pginsert > -1)
{
$pgnames{$xprm[2]}=$pages->{Kids}->[$pginsert];
}
else
{
$pgnames{$xprm[2]}='top';
}
}
elsif (lc($xprm[1]) eq 'switchtopage')
{
my $ba=$xprm[2];
my $want=$xprm[3];
if ($pginsert > -1)
{
if (!defined($want) or $want eq '')
{
# no before/after
$want=$ba;
$ba='before';
}
if (!defined($ba) or $ba eq '' or $want eq 'bottom')
{
$pginsert=$#{$pages->{Kids}};
}
elsif ($want eq 'top')
{
$pginsert=-1;
}
else
{
if (exists($pgnames{$want}))
{
my $ref=$pgnames{$want};
if ($ref eq 'top')
{
$pginsert=-1;
}
else
{
FIND: while (1)
{
foreach my $j (0..$#{$pages->{Kids}})
{
if ($ref eq $pages->{Kids}->[$j])
{
if ($ba eq 'before')
{
$pginsert=$j-1;
last FIND;
}
elsif ($ba eq 'after')
{
$pginsert=$j;
last FIND;
}
else
{
Msg(0,"Parameter must be top|bottom|before|after not '$ba'");
last FIND;
}
}
}
Msg(0,"Can't find page ref '$ref'");
last FIND
}
}
}
else
{
Msg(0,"Can't find page named '$want'");
}
}
if ($pginsert < 0)
{
($curoutlev,$curoutlevno,$thislev)=(\@outlev,0,1);
}
else
{
($curoutlev,$curoutlevno,$thislev)=(@{$outlines[$pginsert]});
}
}
}
}
elsif (lc(substr($xprm[0],0,9)) eq 'papersize')
{
my ($px,$py)=split(',',substr($xprm[0],10));
$px=GetPoints($px);
$py=GetPoints($py);
@mediabox=(0,0,$px,$py);
my @mb=@mediabox;
$matrixchg=1;
$custompaper=1;
$cpage->{MediaBox}=\@mb;
}
}
}
sub FixPDFColour
{
my $o=shift;
my $a=$o->{C};
my @r=();
my $c=$a->[0];
if ($#{$a}==3)
{
if ($c > 1)
{
foreach my $j (0..2)
{
push(@r,sprintf("%1.3f",$a->[$j]/0xffff));
}
$o->{C}=\@r;
}
}
elsif (substr($c,0,1) eq '#')
{
if (length($c) == 7)
{
foreach my $j (0..2)
{
push(@r,sprintf("%1.3f",hex(substr($c,$j*2+1,2))/0xff));
}
$o->{C}=\@r;
}
elsif (length($c) == 14)
{
foreach my $j (0..2)
{
push(@r,sprintf("%1.3f",hex(substr($c,$j*4+2,4))/0xffff));
}
$o->{C}=\@r;
}
}
}
sub PutHotSpot
{
my $endx=shift;
my $l=$mark->{pdfmark};
$l=~s/Color/C/;
$l=~s/Action/A/;
$l=~s'/Subtype /URI'/S /URI';
$l=~s(\\\[u00(..)\])(chr(hex($1)))eg;
my @xwds=split(' ',"<< $l >>");
my $annotno=BuildObj(++$objct,ParsePDFValue(\@xwds));
my $annot=$obj[$objct];
$annot->{DATA}->{Type}='/Annot';
$annot->{DATA}->{Rect}=[$mark->{xpos},$mark->{ypos}-$mark->{rsb},$endx+$mark->{lead},$mark->{ypos}-$mark->{rst}];
FixPDFColour($annot->{DATA});
FixRect($annot->{DATA}->{Rect}); # Y origin to ll
push(@{$cpage->{Annots}},$annotno);
}
sub sgn
{
return(1) if $_[0] > 0;
return(-1) if $_[0] < 0;
return(0);
}
sub FixRect
{
my $rect=shift;
return if !defined($rect);
$rect->[1]=GraphY($rect->[1]);
$rect->[3]=GraphY($rect->[3]);
}
sub GetPoints
{
my $val=shift;
$val=ToPoints($1,$2) if ($val=~m/(-?[\d.]+)([cipnz])/);
return $val;
}
# Although the PDF reference mentions XObject/Form as a way of incorporating an external PDF page into
# the current PDF, it seems not to work with any current PDF reader (although I am told (by Leonard Rosenthol,
# who helped author the PDF ISO standard) that Acroread 9 does support it, empiorical observation shows otherwise!!).
# So... do it the hard way - full PDF parser and merge required objects!!!
# sub BuildRef
# {
# my $fil=shift;
# my $bbox=shift;
# my $mat=shift;
# my $wid=($bbox->[2]-$bbox->[0])*$mat->[0];
# my $hgt=($bbox->[3]-$bbox->[1])*$mat->[3];
#
# if (!open(PDF,"<$fil"))
# {
# Msg(0,"Failed to open '$fil'");
# return(undef);
# }
#
# my (@f)=(<PDF>);
#
# close(PDF);
#
# $objct++;
# my $xonm="XO$objct";
#
# $pages->{'Resources'}->{'XObject'}->{$xonm}=BuildObj($objct,{'Type' => '/XObject',
# 'Subtype' => '/Form',
# 'BBox' => $bbox,
# 'Matrix' => $mat,
# 'Resources' => $pages->{'Resources'},
# 'Ref' => {'Page' => '1',
# 'F' => BuildObj($objct+1,{'Type' => '/Filespec',
# 'F' => "($fil)",
# 'EF' => {'F' => BuildObj($objct+2,{'Type' => '/EmbeddedFile'})}
# })
# }
# });
#
# $obj[$objct]->{STREAM}="q 1 0 0 1 0 0 cm
# q BT
# 1 0 0 1 0 0 Tm
# .5 g .5 G
# /F5 20 Tf
# (Proxy) Tj
# ET Q
# 0 0 m 72 0 l s
# Q\n";
#
# # $obj[$objct]->{STREAM}=PutXY($xpos,$ypos)." m ".PutXY($xpos+$wid,$ypos)." l ".PutXY($xpos+$wid,$ypos+$hgt)." l ".PutXY($xpos,$ypos+$hgt)." l f\n";
# $obj[$objct+2]->{STREAM}=join('',@f);
# PutObj($objct);
# PutObj($objct+1);
# PutObj($objct+2);
# $objct+=2;
# return($xonm);
# }
sub LoadSWF
{
my $fil=shift;
my $bbox=shift;
my $mat=shift;
my $wid=($bbox->[2]-$bbox->[0])*$mat->[0];
my $hgt=($bbox->[3]-$bbox->[1])*$mat->[3];
my (@path)=split('/',$fil);
my $node=pop(@path);
if (!open(PDF,"<$fil"))
{
Msg(0,"Failed to open '$fil'");
return(undef);
}
my (@f)=(<PDF>);
close(PDF);
$objct++;
my $xonm="XO$objct";
$pages->{'Resources'}->{'XObject'}->{$xonm}=BuildObj($objct,{'Type' => '/XObject', 'BBox' => $bbox, 'Matrix' => $mat, 'FormType' => 1, 'Subtype' => '/Form', 'Length' => 0, 'Type' => "/XObject"});
$obj[$objct]->{STREAM}='';
PutObj($objct);
$objct++;
my $asset=BuildObj($objct,{'EF' => {'F' => BuildObj($objct+1,{})},
'F' => "($node)",
'Type' => '/Filespec',
'UF' => "($node)"});
PutObj($objct);
$objct++;
$obj[$objct]->{STREAM}=join('',@f);
PutObj($objct);
$objct++;
my $config=BuildObj($objct,{'Instances' => [BuildObj($objct+1,{'Params' => { 'Binding' => '/Background'}, 'Asset' => $asset})],
'Subtype' => '/Flash'});
PutObj($objct);
$objct++;
PutObj($objct);
$objct++;
my ($x,$y)=split(' ',PutXY($xpos,$ypos));
push(@{$cpage->{Annots}},BuildObj($objct,{'RichMediaContent' => {'Subtype' => '/Flash', 'Configurations' => [$config], 'Assets' => {'Names' => [ "($node)", $asset ] }},
'P' => "$cpageno 0 R",
'RichMediaSettings' => { 'Deactivation' => { 'Condition' => '/PI',
'Type' => '/RichMediaDeactivation'},
'Activation' => { 'Condition' => '/PV',
'Type' => '/RichMediaActivation'}},
'F' => 68,
'Subtype' => '/RichMedia',
'Type' => '/Annot',
'Rect' => "[ $x $y ".($x+$wid)." ".($y+$hgt)." ]",
'Border' => [0,0,0]}));
PutObj($objct);
return $xonm;
}
sub LoadPDF
{
my $pdfnm=shift;
my $mat=shift;
my $wid=shift;
my $hgt=shift;
my $type=shift;
my $pdf;
my $pdftxt='';
my $strmlen=0;
my $curobj=-1;
my $instream=0;
my $cont;
if (!open(PD,"<$pdfnm"))
{
Msg(0,"Failed to open PDF '$pdfnm'");
return undef;
}
my $hdr=<PD>;
$/="\r" if (length($hdr) > 10);
while (<PD>)
{
chomp;
s/\n//;
if (m/endstream(\s+.*)?$/)
{
$instream=0;
$_="endstream";
$_.=$1 if defined($1)
}
next if $instream;
if (m'/Length\s+(\d+)(\s+\d+\s+R)?')
{
if (!defined($2))
{
$strmlen=$1;
}
else
{
$strmlen=0;
}
}
if (m'^(\d+) \d+ obj')
{
$curobj=$1;
$pdf->[$curobj]->{OBJ}=undef;
}
if (m'stream\s*$' and ! m/^endstream/)
{
if ($curobj > -1)
{
$pdf->[$curobj]->{STREAMPOS}=[tell(PD),$strmlen];
seek(PD,$strmlen,1);
$instream=1;
}
else
{
Msg(0,"Parsing PDF '$pdfnm' failed");
return undef;
}
}
$pdftxt.=$_.' ';
}
close(PD);
open(PD,"<$pdfnm");
# $pdftxt=~s/\]/ \]/g;
my (@pdfwds)=split(' ',$pdftxt);
my $wd;
while ($wd=nextwd(\@pdfwds),length($wd))
{
if ($wd=~m/\d+/ and defined($pdfwds[1]) and $pdfwds[1]=~m/^obj(.*)/)
{
$curobj=$wd;
shift(@pdfwds); shift(@pdfwds);
unshift(@pdfwds,$1) if defined($1) and length($1);
$pdf->[$curobj]->{OBJ}=ParsePDFObj(\@pdfwds);
}
elsif ($wd eq 'trailer' and !exists($pdf->[0]->{OBJ}))
{
$pdf->[0]->{OBJ}=ParsePDFObj(\@pdfwds);
}
else
{
# print "Skip '$wd'\n";
}
}
my $catalog=${$pdf->[0]->{OBJ}->{Root}};
my $page=FindPage(1,$pdf);
my $xobj=++$objct;
# Load the streamas
foreach my $o (@{$pdf})
{
if (exists($o->{STREAMPOS}))
{
my $l;
$l=$o->{OBJ}->{Length} if exists($o->{OBJ}->{Length});
$l=$pdf->[$$l]->{OBJ} if (defined($l) && ref($l) eq 'OBJREF');
Msg(1,"Unable to determine length of stream \@$o->{STREAMPOS}->[0]") if !defined($l);
sysseek(PD,$o->{STREAMPOS}->[0],0);
Msg(0,'Failed to read all the stream') if $l != sysread(PD,$o->{STREAM},$l);
if (exists($o->{OBJ}->{'Filter'}) and $o->{OBJ}->{'Filter'} eq '/FlateDecode')
{
$o->{STREAM}=Compress::Zlib::uncompress($o->{STREAM});
delete($o->{OBJ }->{'Filter'});
}
}
}
close(PD);
# Find BBox
my $BBox;
my $insmap={};
foreach my $k (qw( MediaBox ArtBox TrimBox BleedBox CropBox ))
{
$BBox=FindKey($pdf,$page,$k);
last if $BBox;
}
$BBox=[0,0,595,842] if !defined($BBox);
$wid=($BBox->[2]-$BBox->[0]+1) if $wid==0;
my $xscale=abs($wid)/($BBox->[2]-$BBox->[0]+1);
my $yscale=($hgt<=0)?$xscale:(abs($hgt)/($BBox->[3]-$BBox->[1]+1));
$hgt=($BBox->[3]-$BBox->[1]+1)*$yscale;
if ($type eq "import")
{
$mat->[0]=$xscale;
$mat->[3]=$yscale;
}
# Find Resource
my $res=FindKey($pdf,$page,'Resources');
my $xonm="XO$xobj";
# Map inserted objects to current PDF
MapInsValue($pdf,$page,'',$insmap,$xobj,$pdf->[$page]->{OBJ});
#
# Many PDFs include 'Resources' at the 'Page' level but if 'Resources' is held at a higher level (i.e 'Pages')
# then we need to include its objects as well.
#
MapInsValue($pdf,$page,'',$insmap,$xobj,$res) if !exists($pdf->[$page]->{OBJ}->{Resources});
# Copy Resources
my %incres=%{$res};
$incres{ProcSet}=['/PDF', '/Text', '/ImageB', '/ImageC', '/ImageI'];
($mat->[4],$mat->[5])=split(' ',PutXY($xpos,$ypos));
$pages->{'Resources'}->{'XObject'}->{$xonm}=BuildObj($xobj,{'Type' => '/XObject', 'BBox' => $BBox, 'Name' => "/$xonm", 'FormType' => 1, 'Subtype' => '/Form', 'Length' => 0, 'Type' => "/XObject", 'Resources' => \%incres});
BuildStream($xobj,$pdf,$pdf->[$page]->{OBJ}->{Contents});
return([$xonm,$BBox] );
}
sub BuildStream
{
my $xobj=shift;
my $pdf=shift;
my $val=shift;
my $strm='';
my $objs;
my $refval=ref($val);
if ($refval eq 'OBJREF')
{
push(@{$objs}, $val);
}
elsif ($refval eq 'ARRAY')
{
$objs=$val;
}
else
{
Msg(0,"unexpected 'Contents'");
}
foreach my $o (@{$objs})
{
$strm.="\n" if $strm;
$strm.=$pdf->[$$o]->{STREAM} if exists($pdf->[$$o]->{STREAM});
}
$obj[$xobj]->{STREAM}=$strm;
}
sub MapInsHash
{
my $pdf=shift;
my $o=shift;
my $insmap=shift;
my $parent=shift;
my $val=shift;
foreach my $k (sort keys(%{$val}))
{
MapInsValue($pdf,$o,$k,$insmap,$parent,$val->{$k}) if $k ne 'Contents';
}
}
sub MapInsValue
{
my $pdf=shift;
my $o=shift;
my $k=shift;
my $insmap=shift;
my $parent=shift;
my $val=shift;
my $refval=ref($val);
if ($refval eq 'OBJREF')
{
if ($k ne 'Parent')
{
if (!exists($insmap->{IMP}->{$$val}))
{
$objct++;
$insmap->{CUR}->{$objct}=$$val;
$insmap->{IMP}->{$$val}=$objct;
$obj[$objct]->{DATA}=$pdf->[$$val]->{OBJ};
$obj[$objct]->{STREAM}=$pdf->[$$val]->{STREAM} if exists($pdf->[$$val]->{STREAM});
MapInsValue($pdf,$$val,'',$insmap,$o,$pdf->[$$val]->{OBJ});
}
$$val=$insmap->{IMP}->{$$val};
}
else
{
$$val=$parent;
}
}
elsif ($refval eq 'ARRAY')
{
foreach my $v (@{$val})
{
MapInsValue($pdf,$o,'',$insmap,$parent,$v)
}
}
elsif ($refval eq 'HASH')
{
MapInsHash($pdf,$o,$insmap,$parent,$val);
}
}
sub FindKey
{
my $pdf=shift;
my $page=shift;
my $k=shift;
if (exists($pdf->[$page]->{OBJ}->{$k}))
{
my $val=$pdf->[$page]->{OBJ}->{$k};
$val=$pdf->[$$val]->{OBJ} if ref($val) eq 'OBJREF';
return($val);
}
else
{
if (exists($pdf->[$page]->{OBJ}->{Parent}))
{
return(FindKey($pdf,${$pdf->[$page]->{OBJ}->{Parent}},$k));
}
}
return(undef);
}
sub FindPage
{
my $wantpg=shift;
my $pdf=shift;
my $catalog=${$pdf->[0]->{OBJ}->{Root}};
my $pages=${$pdf->[$catalog]->{OBJ}->{Pages}};
return(NextPage($pdf,$pages,\$wantpg));
}
sub NextPage
{
my $pdf=shift;
my $pages=shift;
my $wantpg=shift;
my $ret;
if ($pdf->[$pages]->{OBJ}->{Type} eq '/Pages')
{
foreach my $kid (@{$pdf->[$pages]->{OBJ}->{Kids}})
{
$ret=NextPage($pdf,$$kid,$wantpg);
last if $$wantpg<=0;
}
}
elsif ($pdf->[$pages]->{OBJ}->{Type} eq '/Page')
{
$$wantpg--;
$ret=$pages;
}
return($ret);
}
sub nextwd
{
my $pdfwds=shift;
my $wd=shift(@{$pdfwds});
return('') if !defined($wd);
if ($wd=~m/^(.*?)(<<|>>|(?:(?<!\\)\[|\]))(.*)/)
{
if (defined($1) and length($1))
{
unshift(@{$pdfwds},$3) if defined($3) and length($3);
unshift(@{$pdfwds},$2);
$wd=$1;
}
else
{
unshift(@{$pdfwds},$3) if defined($3) and length($3);
$wd=$2;
}
}
return($wd);
}
sub ParsePDFObj
{
my $pdfwds=shift;
my $rtn;
my $wd;
while ($wd=nextwd($pdfwds),length($wd))
{
if ($wd eq 'stream' or $wd eq 'endstream')
{
next;
}
elsif ($wd eq 'endobj' or $wd eq 'startxref')
{
last;
}
else
{
unshift(@{$pdfwds},$wd);
$rtn=ParsePDFValue($pdfwds);
}
}
return($rtn);
}
sub ParsePDFHash
{
my $pdfwds=shift;
my $rtn={};
my $wd;
while ($wd=nextwd($pdfwds),length($wd))
{
if ($wd eq '>>')
{
last;
}
my (@w)=split('/',$wd,3);
if ($w[0])
{
Msg(0,"PDF Dict Key '$wd' does not start with '/'");
exit 1;
}
else
{
unshift(@{$pdfwds},"/$w[2]") if $w[2];
$wd=$w[1];
(@w)=split('\(',$wd,2);
$wd=$w[0];
unshift(@{$pdfwds},"($w[1]") if defined($w[1]);
(@w)=split('\<',$wd,2);
$wd=$w[0];
unshift(@{$pdfwds},"<$w[1]") if defined($w[1]);
$rtn->{$wd}=ParsePDFValue($pdfwds);
}
}
return($rtn);
}
sub ParsePDFValue
{
my $pdfwds=shift;
my $rtn;
my $wd=nextwd($pdfwds);
if ($wd=~m/^\d+$/ and $pdfwds->[0]=~m/^\d+$/ and $pdfwds->[1]=~m/^R(\]|\>|\/)?/)
{
shift(@{$pdfwds});
if (defined($1) and length($1))
{
$pdfwds->[0]=substr($pdfwds->[0],1);
}
else
{
shift(@{$pdfwds});
}
return(bless(\$wd,'OBJREF'));
}
if ($wd eq '<<')
{
return(ParsePDFHash($pdfwds));
}
if ($wd eq '[')
{
return(ParsePDFArray($pdfwds));
}
if ($wd=~m/(.*?)(\(.*)$/)
{
if (defined($1) and length($1))
{
unshift(@{$pdfwds},$2);
$wd=$1;
}
else
{
return(ParsePDFString($wd,$pdfwds));
}
}
if ($wd=~m/(.*?)(\<.*)$/)
{
if (defined($1) and length($1))
{
unshift(@{$pdfwds},$2);
$wd=$1;
}
else
{
return(ParsePDFHexString($wd,$pdfwds));
}
}
if ($wd=~m/(.+?)(\/.*)$/)
{
if (defined($2) and length($2))
{
unshift(@{$pdfwds},$2);
$wd=$1;
}
}
return($wd);
}
sub ParsePDFString
{
my $wd=shift;
my $rtn='';
my $pdfwds=shift;
my $lev=0;
while (length($wd))
{
$rtn.=' ' if length($rtn);
while ($wd=~m/(?<!\\)\(/g) {$lev++;}
while ($wd=~m/(?<!\\)\)/g) {$lev--;}
if ($lev<=0 and $wd=~m/^(.*?\))([^)]+)$/)
{
unshift(@{$pdfwds},$2) if defined($2) and length($2);
$wd=$1;
}
$rtn.=$wd;
last if $lev <= 0;
$wd=nextwd($pdfwds);
}
return($rtn);
}
sub ParsePDFHexString
{
my $wd=shift;
my $rtn='';
my $pdfwds=shift;
my $lev=0;
if ($wd=~m/^(<.+?>)(.*)/)
{
unshift(@{$pdfwds},$2) if defined($2) and length($2);
$rtn=$1;
}
return($rtn);
}
sub ParsePDFArray
{
my $pdfwds=shift;
my $rtn=[];
my $wd;
while (1)
{
$wd=ParsePDFValue($pdfwds);
last if $wd eq ']' or length($wd)==0;
push(@{$rtn},$wd);
}
return($rtn);
}
sub Msg
{
my ($lev,$msg)=@_;
print STDERR "$env{SourceFile}: " if exists($env{SourceFile});
print STDERR "$msg\n";
exit 1 if $lev;
}
sub PutXY
{
my ($x,$y)=(@_);
if ($frot)
{
return("$y $x");
}
else
{
$y=$mediabox[3]-$y;
return("$x $y");
}
}
sub GraphY
{
my $y=shift;
if ($frot)
{
return($y);
}
else
{
return($mediabox[3]-$y);
}
}
sub Put
{
my $msg=shift;
print $msg;
$fct+=length($msg);
}
sub PutObj
{
my $ono=shift;
my $msg="$ono 0 obj ";
$obj[$ono]->{XREF}=$fct;
if (exists($obj[$ono]->{STREAM}))
{
if (!$debug && !exists($obj[$ono]->{DATA}->{'Filter'}))
{
$obj[$ono]->{STREAM}=Compress::Zlib::compress($obj[$ono]->{STREAM});
$obj[$ono]->{DATA}->{'Filter'}=['/FlateDecode'];
}
$obj[$ono]->{DATA}->{'Length'}=length($obj[$ono]->{STREAM});
}
PutField(\$msg,$obj[$ono]->{DATA});
PutStream(\$msg,$ono) if exists($obj[$ono]->{STREAM});
Put($msg."endobj\n");
}
sub PutStream
{
my $msg=shift;
my $ono=shift;
# We could 'flate' here
$$msg.="stream\n$obj[$ono]->{STREAM}endstream\n";
}
sub PutField
{
my $pmsg=shift;
my $fld=shift;
my $term=shift||"\n";
my $typ=ref($fld);
if ($typ eq '')
{
$$pmsg.="$fld$term";
}
elsif ($typ eq 'ARRAY')
{
$$pmsg.='[';
foreach my $cell (@{$fld})
{
PutField($pmsg,$cell,' ');
}
$$pmsg.="]$term";
}
elsif ($typ eq 'HASH')
{
$$pmsg.='<< ';
foreach my $key (sort keys %{$fld})
{
$$pmsg.="/$key ";
PutField($pmsg,$fld->{$key});
}
$$pmsg.=">>$term";
}
elsif ($typ eq 'OBJREF')
{
$$pmsg.="$$fld 0 R$term";
}
}
sub BuildObj
{
my $ono=shift;
my $val=shift;
$obj[$ono]->{DATA}=$val;
return("$ono 0 R ");
}
sub LoadFont
{
my $fontno=shift;
my $fontnm=shift;
my $ofontnm=$fontnm;
return $fontlst{$fontno}->{OBJ} if (exists($fontlst{$fontno}));
my $f;
OpenFile(\$f,$fontdir,"$fontnm");
if (!defined($f) and $Foundry)
{
# Try with no foundry
$fontnm=~s/.*?-//;
OpenFile(\$f,$fontdir,$fontnm);
}
Msg(1,"Failed to open font '$ofontnm'") if !defined($f);
my $foundry='';
$foundry=$1 if $fontnm=~m/^(.*?)-/;
my $stg=1;
my %fnt;
my @fntbbox=(0,0,0,0);
my $capheight=0;
my $lastchr=0;
my $t1flags=0;
my $fixwid=-1;
my $ascent=0;
my $charset='';
while (<$f>)
{
chomp;
s/^ +//;
s/^#.*// if $stg == 1;
next if $_ eq '';
if ($stg == 1)
{
my ($key,$val)=split(' ',$_,2);
$key=lc($key);
$stg=2,next if $key eq 'kernpairs';
$stg=3,next if lc($_) eq 'charset';
$fnt{$key}=$val
}
elsif ($stg == 2)
{
$stg=3,next if lc($_) eq 'charset';
my ($ch1,$ch2,$k)=split;
$fnt{KERN}->{$ch1}->{$ch2}=$k;
}
else
{
my (@r)=split;
my (@p)=split(',',$r[1]);
if ($r[1] eq '"')
{
$fnt{GNM}->{$r[0]}=$lastchr;
next;
}
$r[0]='u0020' if $r[3] == 32;
# next if $r[3] >255;
$fnt{GNM}->{$r[0]}=$r[3];
$fnt{GNO}->[$r[3]]='/'.$r[4];
$fnt{WID}->[$r[3]]=$p[0];
$lastchr=$r[3] if $r[3] > $lastchr;
$fixwid=$p[0] if $fixwid == -1;
$fixwid=-2 if $fixwid > 0 and $p[0] != $fixwid;
$fntbbox[1]=-$p[2] if defined($p[2]) and -$p[2] < $fntbbox[1];
$fntbbox[2]=$p[0] if $p[0] > $fntbbox[2];
$fntbbox[3]=$p[1] if defined($p[1]) and $p[1] > $fntbbox[3];
$ascent=$p[1] if defined($p[1]) and $p[1] > $ascent and $r[3] >= 32 and $r[3] < 128;
$charset.='/'.$r[4] if defined($r[4]);
$capheight=$p[1] if length($r[4]) == 1 and $r[4] ge 'A' and $r[4] le 'Z' and $p[1] > $capheight;
}
}
close($f);
unshift(@{$fnt{GNO}},0);
foreach my $glyph (@{$fnt{GNO}})
{
$glyph='/.notdef' if !defined($glyph);
}
foreach my $w (@{$fnt{WID}})
{
$w=0 if !defined($w);
}
my $fno=0;
my $slant=0;
$slant=-$fnt{'slant'} if exists($fnt{'slant'});
$fnt{'spacewidth'}=700 if !exists($fnt{'spacewidth'});
$t1flags|=2**0 if $fixwid > -1;
$t1flags|=(exists($fnt{'special'}))?2**2:2**5;
$t1flags|=2**6 if $slant != 0;
my $fontkey="$foundry $fnt{internalname}";
if (exists($download{$fontkey}))
{
# Not a Base Font
my ($l1,$l2,$l3,$t1stream)=GetType1($download{$fontkey});
Msg(0,"Incorrect font format for '$fontkey' ($l1)") if !defined($t1stream);
$fno=++$objct;
$fontlst{$fontno}->{OBJ}=BuildObj($objct,
{'Type' => '/Font',
'Subtype' => '/Type1',
'BaseFont' => '/'.$fnt{internalname},
'Widths' => $fnt{WID},
'FirstChar' => 0,
'LastChar' => $lastchr,
'Encoding' => BuildObj($objct+1,
{'Type' => '/Encoding',
'Differences' => $fnt{GNO}
}
),
'FontDescriptor' => BuildObj($objct+2,
{'Type' => '/FontDescriptor',
'FontName' => '/'.$fnt{internalname},
'Flags' => $t1flags,
'FontBBox' => \@fntbbox,
'ItalicAngle' => $slant,
'Ascent' => $ascent,
'Descent' => $fntbbox[1],
'CapHeight' => $capheight,
'StemV' => 0,
'CharSet' => "($charset)",
'FontFile' => BuildObj($objct+3,
{'Length1' => $l1,
'Length2' => $l2,
'Length3' => $l3
}
)
}
)
}
);
$objct+=3;
$fontlst{$fontno}->{NM}='/F'.$fontno;
$pages->{'Resources'}->{'Font'}->{'F'.$fontno}=$fontlst{$fontno}->{OBJ};
$fontlst{$fontno}->{FNT}=\%fnt;
$obj[$objct]->{STREAM}=$t1stream;
}
else
{
$fno=++$objct;
$fontlst{$fontno}->{OBJ}=BuildObj($objct,
{'Type' => '/Font',
'Subtype' => '/Type1',
'BaseFont' => '/'.$fnt{internalname},
'Encoding' => BuildObj($objct+1,
{'Type' => '/Encoding',
'Differences' => $fnt{GNO}
}
)
}
);
$objct+=1;
$fontlst{$fontno}->{NM}='/F'.$fontno;
$pages->{'Resources'}->{'Font'}->{'F'.$fontno}=$fontlst{$fontno}->{OBJ};
$fontlst{$fontno}->{FNT}=\%fnt;
}
if (defined($fnt{encoding}) and $fnt{encoding} eq 'text.enc' and $ucmap ne '')
{
if ($textenccmap eq '')
{
$textenccmap = BuildObj($objct+1,{});
$objct++;
$obj[$objct]->{STREAM}=$ucmap;
}
$obj[$fno]->{DATA}->{'ToUnicode'}=$textenccmap;
}
# PutObj($fno);
# PutObj($fno+1);
# PutObj($fno+2) if defined($obj[$fno+2]);
# PutObj($fno+3) if defined($obj[$fno+3]);
}
sub GetType1
{
my $file=shift;
my ($l1,$l2,$l3); # Return lengths
my ($head,$body,$tail); # Font contents
my $f;
OpenFile(\$f,$fontdir,"$file");
Msg(1,"Failed to open '$file'") if !defined($f);
binmode($f);
my $l=<$f>;
if (substr($l,0,1) eq "\x80")
{
# PFB file
sysseek($f,0,0);
my $hdr='';
$l1=$l2=$l3=0;
my $typ=0;
my $data='';
my $sl=0;
while ($typ != 3)
{
my $chk=sysread($f,$hdr,6);
if ($chk < 2)
{
# eof($f) uses buffered i/o (since file was open not sysopen)
# which screws up next sysread. So this will terminate loop if font
# has no terminating section type 3.
last if $l3;
return(5,$l2,$l3,undef);
}
$typ=ord(substr($hdr,1,1));
if ($chk == 6)
{
$sl=unpack('L',substr($hdr,2,4));
$chk=sysread($f,$data,$sl);
return(1,$l2,$l3,undef) if $chk != $sl;
}
if ($typ == 1)
{
if ($l2 == 0)
{
# First text bit(s) must be head
$head.=$data;
$l1+=$sl;
}
else
{
# A text bit after the binary sections must be tail
$tail.=$data;
$l3+=$sl;
}
}
elsif ($typ == 2)
{
return(2,$l2,$l3,undef) if $l3; # Found a binary bit after the tail
$body.=$data;
$l2+=$sl;
}
elsif ($typ != 3)
{
# What segment type is this!
return(3,$l2,$l3,undef);
}
}
close($f);
return($l1,$l2,$l3,"$head$body$tail");
}
my (@lines)=(<$f>);
unshift(@lines,$l);
close($f);
Msg(1,"Font file '$file' must be an Adobe type 1 font file") if $lines[0]!~m/\%\!PS.Adobe/i;
$head=$body=$tail='';
foreach my $line (@lines)
{
if (!defined($l1))
{
if (length($line) > 19 and $line=~s/^(currentfile eexec)//)
{
$head.=$1;
$l1=length($head);
redo;
}
$head.=$line;
if ($line=~m/eexec$/)
{
# chomp($head);
# $head.="\x0d";
$l1=length($head);
}
}
elsif (!defined($l2))
{
#$line=~s/(\0\0)0+$/&1/;
if ($line=~m/^0+$/)
{
$l2=length($body);
$tail=$line;
}
else
{
chomp($line);
$body.=pack('H*',$line);
}
}
else
{
$tail.=$line;
}
}
$l1=length($head);
$l2=length($body);
$l3=length($tail);
return($l1,$l2,$l3,"$head$body$tail");
}
sub OutStream
{
my $ono=shift;
IsGraphic();
$stream.="Q\n";
$obj[$ono]->{STREAM}=$stream;
$obj[$ono]->{DATA}->{Length}=length($stream);
$stream='';
PutObj($ono);
}
sub do_p
{
# Start of pages
if ($cpageno > 0)
{
$cpage->{MediaBox}=\@mediabox if $custompaper;
PutObj($cpageno);
OutStream($cpageno+1);
}
$cpageno=++$objct;
my $thispg=BuildObj($objct,
{'Type' => '/Page',
'Group' => {'CS' => '/DeviceRGB', 'S' => '/Transparency'},
'Parent' => '2 0 R',
'Contents' => [ BuildObj($objct+1,
{'Length' => 0}
) ],
}
);
splice(@{$pages->{Kids}},++$pginsert,0,$thispg);
splice(@outlines,$pginsert,0,[$curoutlev,$#{$curoutlev}+1,$thislev]);
$objct+=1;
$cpage=$obj[$cpageno]->{DATA};
$pages->{'Count'}++;
$stream="q 1 0 0 1 0 0 cm\n$linejoin J\n$linecap j\n";
$stream.=$strkcol."\n", $curstrk=$strkcol if $strkcol ne '';
$mode='g';
$curfill='';
# @mediabox=@defaultmb;
}
sub do_f
{
my $par=shift;
# IsText();
$cft="$par";
$fontchg=1;
# $stream.="/F$cft $cftsz Tf\n" if $cftsz;
$widtbl=CacheWid($par);
$origwidtbl=$fontlst{$par}->{FNT}->{WID};
$krntbl=$fontlst{$par}->{FNT}->{KERN};
}
sub CacheWid
{
my $par=shift;
if (!defined($fontlst{$par}->{CACHE}->{$cftsz}))
{
$fontlst{$par}->{CACHE}->{$cftsz}=BuildCache($fontlst{$par}->{FNT}->{WID});
}
return($fontlst{$par}->{CACHE}->{$cftsz});
}
sub BuildCache
{
my $wid=shift;
return([]);
my @cwid;
foreach my $w (@{$wid})
{
push(@cwid,$w*$cftsz);
}
return(\@cwid);
}
sub IsText
{
if ($mode eq 'g')
{
$xpos+=$pendmv/$unitwidth;
$stream.="q BT\n$matrix ".PutXY($xpos,$ypos)." Tm\n";
$poschg=0;
$fontchg=0;
$pendmv=0;
$matrixchg=0;
$tmxpos=$xpos;
$stream.=$textcol."\n", $curfill=$textcol if $textcol ne $curfill;
if (defined($cft))
{
$whtsz=$fontlst{$cft}->{FNT}->{spacewidth}*$cftsz;
$stream.="/F$cft $cftsz Tf\n";
}
$stream.="$curkern Tc\n";
}
if ($poschg or $matrixchg)
{
PutLine(0) if $matrixchg;
$stream.="$matrix ".PutXY($xpos,$ypos)." Tm\n", $poschg=0;
$tmxpos=$xpos;
$matrixchg=0;
$stream.="$curkern Tc\n";
}
if ($fontchg)
{
PutLine(0);
$whtsz=$fontlst{$cft}->{FNT}->{spacewidth}*$cftsz;
$stream.="/F$cft $cftsz Tf\n" if $cftsz and defined($cft);
$fontchg=0;
}
$mode='t';
}
sub IsGraphic
{
if ($mode eq 't')
{
PutLine();
$stream.="ET Q\n";
$xpos+=($pendmv-$nomove)/$unitwidth;
$pendmv=0;
$nomove=0;
$stream.=$strkcol."\n", $curstrk=$strkcol if $strkcol ne $curstrk;
$curfill=$fillcol;
}
$mode='g';
}
sub do_s
{
my $par=shift;
$par/=$unitwidth;
if ($par != $cftsz and defined($cft))
{
PutLine();
$cftsz=$par;
Set_LWidth() if $lwidth < 1;
# $stream.="/F$cft $cftsz Tf\n";
$fontchg=1;
$widtbl=CacheWid($cft);
}
else
{
$cftsz=$par;
Set_LWidth() if $lwidth < 1;
}
}
sub Set_LWidth
{
IsGraphic();
$stream.=((($desc{res}/(72*$desc{sizescale}))*$linewidth*$cftsz)/1000)." w\n";
return;
}
sub do_m
{
# Groff uses /m[] for text & graphic stroke, and /M[] (DF?) for graphic fill.
# PDF uses G/RG/K for graphic stroke, and g/rg/k for text & graphic fill.
#
# This means that we must maintain g/rg/k state separately for text colour & graphic fill (this is
# probably why 'gs' maintains seperate graphic states for text & graphics when distilling PS -> PDF).
#
# To facilitate this:-
#
# $textcol = current groff stroke colour
# $fillcol = current groff fill colour
# $curfill = current PDF fill colour
my $par=shift;
my $mcmd=substr($par,0,1);
$par=substr($par,1);
$par=~s/^ +//;
# IsGraphic();
$textcol=set_col($mcmd,$par,0);
$strkcol=set_col($mcmd,$par,1);
if ($mode eq 't')
{
PutLine();
$stream.=$textcol."\n";
$curfill=$textcol;
}
else
{
$stream.="$strkcol\n";
$curstrk=$strkcol;
}
}
sub set_col
{
my $mcmd=shift;
my $par=shift;
my $upper=shift;
my @oper=('g','k','rg');
@oper=('G','K','RG') if $upper;
if ($mcmd eq 'd')
{
# default colour
return("0 $oper[0]");
}
my (@c)=split(' ',$par);
if ($mcmd eq 'c')
{
# Text CMY
return(($c[0]/65535).' '.($c[1]/65535).' '.($c[2]/65535)." 0 $oper[1]");
}
elsif ($mcmd eq 'k')
{
# Text CMYK
return(($c[0]/65535).' '.($c[1]/65535).' '.($c[2]/65535).' '.($c[3]/65535)." $oper[1]");
}
elsif ($mcmd eq 'g')
{
# Text Grey
return(($c[0]/65535)." $oper[0]");
}
elsif ($mcmd eq 'r')
{
# Text RGB0
return(($c[0]/65535).' '.($c[1]/65535).' '.($c[2]/65535)." $oper[2]");
}
}
sub do_D
{
my $par=shift;
my $Dcmd=substr($par,0,1);
$par=substr($par,1);
$xpos+=$pendmv/$unitwidth;
$pendmv=0;
IsGraphic();
if ($Dcmd eq 'F')
{
my $mcmd=substr($par,0,1);
$par=substr($par,1);
$par=~s/^ +//;
$fillcol=set_col($mcmd,$par,0);
$stream.="$fillcol\n";
$curfill=$fillcol;
}
elsif ($Dcmd eq 'f')
{
my $mcmd=substr($par,0,1);
$par=substr($par,1);
$par=~s/^ +//;
($par)=split(' ',$par);
if ($par >= 0 and $par <= 1000)
{
$fillcol=set_col('g',int((1000-$par)*65535/1000),0);
}
else
{
$fillcol=lc($textcol);
}
$stream.="$fillcol\n";
$curfill=$fillcol;
}
elsif ($Dcmd eq '~')
{
# B-Spline
my (@p)=split(' ',$par);
my ($nxpos,$nypos);
foreach my $p (@p) { $p/=$unitwidth; }
$stream.=PutXY($xpos,$ypos)." m\n";
$xpos+=($p[0]/2);
$ypos+=($p[1]/2);
$stream.=PutXY($xpos,$ypos)." l\n";
for (my $i=0; $i < $#p-1; $i+=2)
{
$nxpos=(($p[$i]*$tnum)/(2*$tden));
$nypos=(($p[$i+1]*$tnum)/(2*$tden));
$stream.=PutXY(($xpos+$nxpos),($ypos+$nypos))." ";
$nxpos=($p[$i]/2 + ($p[$i+2]*($tden-$tnum))/(2*$tden));
$nypos=($p[$i+1]/2 + ($p[$i+3]*($tden-$tnum))/(2*$tden));
$stream.=PutXY(($xpos+$nxpos),($ypos+$nypos))." ";
$nxpos=(($p[$i]-$p[$i]/2) + $p[$i+2]/2);
$nypos=(($p[$i+1]-$p[$i+1]/2) + $p[$i+3]/2);
$stream.=PutXY(($xpos+$nxpos),($ypos+$nypos))." c\n";
$xpos+=$nxpos;
$ypos+=$nypos;
}
$xpos+=($p[$#p-1]-$p[$#p-1]/2);
$ypos+=($p[$#p]-$p[$#p]/2);
$stream.=PutXY($xpos,$ypos)." l\nS\n";
$poschg=1;
}
elsif ($Dcmd eq 'p' or $Dcmd eq 'P')
{
# Polygon
my (@p)=split(' ',$par);
my ($nxpos,$nypos);
foreach my $p (@p) { $p/=$unitwidth; }
$stream.=PutXY($xpos,$ypos)." m\n";
for (my $i=0; $i < $#p; $i+=2)
{
$xpos+=($p[$i]);
$ypos+=($p[$i+1]);
$stream.=PutXY($xpos,$ypos)." l\n";
}
if ($Dcmd eq 'p')
{
$stream.="s\n";
}
else
{
$stream.="f\n";
}
$poschg=1;
}
elsif ($Dcmd eq 'c')
{
# Stroke circle
$par=substr($par,1);
my (@p)=split(' ',$par);
DrawCircle($p[0],$p[0]);
$stream.="s\n";
$poschg=1;
}
elsif ($Dcmd eq 'C')
{
# Fill circle
$par=substr($par,1);
my (@p)=split(' ',$par);
DrawCircle($p[0],$p[0]);
$stream.="f\n";
$poschg=1;
}
elsif ($Dcmd eq 'e')
{
# Stroke ellipse
$par=substr($par,1);
my (@p)=split(' ',$par);
DrawCircle($p[0],$p[1]);
$stream.="s\n";
$poschg=1;
}
elsif ($Dcmd eq 'E')
{
# Fill ellipse
$par=substr($par,1);
my (@p)=split(' ',$par);
DrawCircle($p[0],$p[1]);
$stream.="f\n";
$poschg=1;
}
elsif ($Dcmd eq 'l')
{
# Line To
$par=substr($par,1);
my (@p)=split(' ',$par);
foreach my $p (@p) { $p/=$unitwidth; }
$stream.=PutXY($xpos,$ypos)." m\n";
$xpos+=$p[0];
$ypos+=$p[1];
$stream.=PutXY($xpos,$ypos)." l\n";
$stream.="s\n";
$poschg=1;
}
elsif ($Dcmd eq 't')
{
# Line Thickness
$par=substr($par,1);
my (@p)=split(' ',$par);
foreach my $p (@p) { $p/=$unitwidth; }
# $xpos+=$p[0]*100; # WTF!!!
#int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
$p[0]=(($desc{res}/(72*$desc{sizescale}))*$linewidth*$cftsz)/1000 if $p[0] < 0;
$lwidth=$p[0];
$stream.="$p[0] w\n";
$poschg=1;
$xpos+=$lwidth;
}
elsif ($Dcmd eq 'a')
{
# Arc
$par=substr($par,1);
my (@p)=split(' ',$par);
my $rad180=3.14159;
my $rad360=$rad180*2;
my $rad90=$rad180/2;
foreach my $p (@p) { $p/=$unitwidth; }
# Documentation is wrong. Groff does not use Dh1,Dv1 as centre of the circle!
my $centre=adjust_arc_centre(\@p);
# Using formula here : http://www.tinaja.com/glib/bezcirc2.pdf
# First calculate angle between start and end point
my ($startang,$r)=RtoP(-$centre->[0],$centre->[1]);
my ($endang,$r2)=RtoP(($p[0]+$p[2])-$centre->[0],-($p[1]+$p[3]-$centre->[1]));
$endang+=$rad360 if $endang < $startang;
my $totang=($endang-$startang)/4; # do it in 4 pieces
# Now 1 piece
my $x0=cos($totang/2);
my $y0=sin($totang/2);
my $x3=$x0;
my $y3=-$y0;
my $x1=(4-$x0)/3;
my $y1=((1-$x0)*(3-$x0))/(3*$y0);
my $x2=$x1;
my $y2=-$y1;
# Rotate to start position and draw 4 pieces
foreach my $j (0..3)
{
PlotArcSegment($totang/2+$startang+$j*$totang,$r,$xpos+$centre->[0],GraphY($ypos+$centre->[1]),$x0,$y0,$x1,$y1,$x2,$y2,$x3,$y3);
}
$xpos+=$p[0]+$p[2];
$ypos+=$p[1]+$p[3];
$poschg=1;
}
}
sub deg
{
return int($_[0]*180/3.14159);
}
sub adjust_arc_centre
{
# Taken from geometry.cpp
# We move the center along a line parallel to the line between
# the specified start point and end point so that the center
# is equidistant between the start and end point.
# It can be proved (using Lagrange multipliers) that this will
# give the point nearest to the specified center that is equidistant
# between the start and end point.
my $p=shift;
my @c;
my $x = $p->[0] + $p->[2]; # (x, y) is the end point
my $y = $p->[1] + $p->[3];
my $n = $x*$x + $y*$y;
if ($n != 0)
{
$c[0]= $p->[0];
$c[1] = $p->[1];
my $k = .5 - ($c[0]*$x + $c[1]*$y)/$n;
$c[0] += $k*$x;
$c[1] += $k*$y;
return(\@c);
}
else
{
return(undef);
}
}
sub PlotArcSegment
{
my ($ang,$r,$transx,$transy,$x0,$y0,$x1,$y1,$x2,$y2,$x3,$y3)=@_;
my $cos=cos($ang);
my $sin=sin($ang);
my @mat=($cos,$sin,-$sin,$cos,0,0);
my $lw=$lwidth/$r;
$stream.="q $r 0 0 $r $transx $transy cm ".join(' ',@mat)." cm $lw w $x0 $y0 m $x1 $y1 $x2 $y2 $x3 $y3 c S Q\n";
}
sub DrawCircle
{
my $hd=shift;
my $vd=shift;
my $hr=$hd/2/$unitwidth;
my $vr=$vd/2/$unitwidth;
my $kappa=0.5522847498;
$hd/=$unitwidth;
$vd/=$unitwidth;
$stream.=PutXY(($xpos+$hd),$ypos)." m\n";
$stream.=PutXY(($xpos+$hd),($ypos+$vr*$kappa))." ".PutXY(($xpos+$hr+$hr*$kappa),($ypos+$vr))." ".PutXY(($xpos+$hr),($ypos+$vr))." c\n";
$stream.=PutXY(($xpos+$hr-$hr*$kappa),($ypos+$vr))." ".PutXY(($xpos),($ypos+$vr*$kappa))." ".PutXY(($xpos),($ypos))." c\n";
$stream.=PutXY(($xpos),($ypos-$vr*$kappa))." ".PutXY(($xpos+$hr-$hr*$kappa),($ypos-$vr))." ".PutXY(($xpos+$hr),($ypos-$vr))." c\n";
$stream.=PutXY(($xpos+$hr+$hr*$kappa),($ypos-$vr))." ".PutXY(($xpos+$hd),($ypos-$vr*$kappa))." ".PutXY(($xpos+$hd),($ypos))." c\n";
$xpos+=$hd;
$poschg=1;
}
sub FindCircle
{
my ($x1,$y1,$x2,$y2,$x3,$y3)=@_;
my ($Xo, $Yo);
my $x=$x2+$x3;
my $y=$y2+$y3;
my $n=$x**2+$y**2;
if ($n)
{
my $k=.5-($x2*$x + $y2*$y)/$n;
return(sqrt($n),$x2+$k*$x,$y2+$k*$y);
}
else
{
return(-1);
}
}
sub PtoR
{
my ($theta,$r)=@_;
return($r*cos($theta),$r*sin($theta));
}
sub RtoP
{
my ($x,$y)=@_;
return(atan2($y,$x),sqrt($x**2+$y**2));
}
sub PutLine
{
my $f=shift;
IsText() if !defined($f);
return if (scalar(@lin) == 0) or (!defined($lin[0]->[0]) and $#lin == 0);
# $stream.="% --- wht=$whtsz, pend=$pendmv, nomv=$nomove\n" if $debug;
$pendmv-=$nomove;
$lin[$#lin]->[1]=-$pendmv/$cftsz if ($pendmv != 0);
if (0)
{
if (scalar(@lin) == 1 and (!defined($lin[0]->[1]) or $lin[0]->[1] == 0))
{
$stream.="($lin[0]->[0]) Tj\n";
}
else
{
$stream.="[";
foreach my $wd (@lin)
{
$stream.="($wd->[0]) " if defined($wd->[0]);
$stream.="$wd->[1] " if defined($wd->[1]) and $wd->[1] != 0;
}
$stream.="] TJ\n";
}
}
else
{
if (scalar(@lin) == 1 and (!defined($lin[0]->[1]) or $lin[0]->[1] == 0))
{
$stream.="0 Tw ($lin[0]->[0]) Tj\n";
}
else
{
if ($wt>=-1 or $#lin == 0 or $lin[0]->[1]>=0)
{
$stream.="0 Tw [";
foreach my $wd (@lin)
{
$stream.="($wd->[0]) " if defined($wd->[0]);
$stream.="$wd->[1] " if defined($wd->[1]) and $wd->[1] != 0;
}
$stream.="] TJ\n";
}
else
{
# $stream.="\%dg 0 Tw [";
#
# foreach my $wd (@lin)
# {
# $stream.="($wd->[0]) " if defined($wd->[0]);
# $stream.="$wd->[1] " if defined($wd->[1]) and $wd->[1] != 0;
# }
#
# $stream.="] TJ\n";
#
# my $wt=$lin[0]->[1]||0;
# while ($wt < -$whtsz/$cftsz)
# {
# $wt+=$whtsz/$cftsz;
# }
$stream.=sprintf( "%.3f Tw ",-($whtsz+$wt*$cftsz)/$unitwidth-$curkern );
if (!defined($lin[0]->[0]) and defined($lin[0]->[1]))
{
$stream.="[ $lin[0]->[1] (";
shift @lin;
}
else
{
$stream.="[(";
}
foreach my $wd (@lin)
{
my $wwt=$wd->[1]||0;
while ($wwt <= $wt+.1)
{
$wwt-=$wt;
$wd->[0].=' ';
}
if (abs($wwt) < .1 or $wwt == 0)
{
$stream.="$wd->[0]" if defined($wd->[0]);
}
else
{
$wwt=sprintf("%.3f",$wwt);
$stream.="$wd->[0]) $wwt (" if defined($wd->[0]);
}
}
$stream.=")] TJ\n";
}
}
}
@lin=();
$xpos+=$pendmv/$unitwidth;
$pendmv=0;
$nomove=0;
$wt=-1;
}
sub LoadAhead
{
my $no=shift;
foreach my $j (1..$no)
{
my $lin=<>;
chomp($lin);
$lin=~s/\r$//;
$lct++;
push(@ahead,$lin);
$stream.="%% $lin\n" if $debug;
}
}
sub do_V
{
my $par=shift;
if ($mode eq 't')
{
PutLine();
}
else
{
$xpos+=$pendmv/$unitwidth;
$pendmv=0;
}
$ypos=$par/$unitwidth;
LoadAhead(1);
if (substr($ahead[0],0,1) eq 'H')
{
$xpos=substr($ahead[0],1)/$unitwidth;
@ahead=();
}
# $nomove=$pendmv=0;
$poschg=1;
}
sub do_v
{
my $par=shift;
PutLine();
$ypos+=$par/$unitwidth;
$poschg=1;
}
sub TextWid
{
my $txt=shift;
my $w=0;
my $ck=0;
foreach my $c (split('',$txt))
{
my $cn=ord($c);
$widtbl->[$cn]=$origwidtbl->[$cn]*$cftsz if !defined($widtbl->[$cn]);
$w+=$widtbl->[$cn];
}
$ck=length($txt)*$curkern;
return(($w/$unitwidth)+$ck);
}
sub do_t
{
my $par=shift;
if ($kernadjust != $curkern)
{
PutLine();
$stream.="$kernadjust Tc\n";
$curkern=$kernadjust;
}
my $wid=TextWid($par);
$par=reverse(split('',$par)) if $xrev;
if ($n_flg and defined($mark))
{
$mark->{ypos}=$ypos;
$mark->{xpos}=$xpos;
}
$n_flg=0;
IsText();
$xpos+=$wid;
$xpos+=($pendmv-$nomove)/$unitwidth;
$stream.="% == '$par'=$wid 'xpos=$xpos\n" if $debug;
$par=~s/\\(?!\d\d\d)/\\\\/g;
$par=~s/\)/\\)/g;
$par=~s/\(/\\(/g;
# $pendmv = 'h' move since last 't'
# $nomove = width of char(s) added by 'C', 'N' or 'c'
# $w-flg = 'w' seen since last t
if ($fontchg)
{
PutLine();
$whtsz=$fontlst{$cft}->{FNT}->{spacewidth}*$cftsz;
$stream.="/F$cft $cftsz Tf\n", $fontchg=0 if $fontchg && defined($cft);
}
$gotT=1;
$stream.="% --- wht=$whtsz, pend=$pendmv, nomv=$nomove\n" if $debug;
# if ($w_flg && $#lin > -1)
# {
# $lin[$#lin]->[0].=' ';
# $pendmv-=$whtsz;
# $dontglue=1 if $pendmv==0;
# }
$wt=-$pendmv/$cftsz if $w_flg and $wt==-1;
$pendmv-=$nomove;
$nomove=0;
$w_flg=0;
if ($xrev)
{
PutLine(0) if $#lin > -1;
MakeMatrix(1);
$stream.="$matrix ".PutXY($xpos,$ypos)." Tm\n", $poschg=0;
$stream.="$curkern Tc\n";
$stream.="0 Tw ";
$stream.="($par) Tj\n";
MakeMatrix();
$stream.="$matrix ".PutXY($xpos,$ypos)." Tm\n", $poschg=0;
$matrixchg=0;
$stream.="$curkern Tc\n";
return;
}
if ($pendmv)
{
if ($#lin == -1)
{
push(@lin,[undef,-$pendmv/$cftsz]);
}
else
{
$lin[$#lin]->[1]=-$pendmv/$cftsz;
}
push(@lin,[$par,undef]);
# $xpos+=$pendmv/$unitwidth;
$pendmv=0
}
else
{
if ($#lin == -1)
{
push(@lin,[$par,undef]);
}
else
{
$lin[$#lin]->[0].=$par;
}
}
}
sub do_u
{
my $par=shift;
$par=m/([+-]?\d+) (.*)/;
$kernadjust=$1/$unitwidth;
do_t($2);
$kernadjust=0;
}
sub do_h
{
$pendmv+=shift;
}
sub do_H
{
my $par=shift;
if ($mode eq 't')
{
PutLine();
}
else
{
$xpos+=$pendmv/$unitwidth;
$pendmv=0;
}
my $newx=$par/$unitwidth;
$stream.=sprintf("%.3f",$newx-$tmxpos)." 0 Td\n" if $mode eq 't';
$tmxpos=$xpos=$newx;
$pendmv=$nomove=0;
}
sub do_C
{
my $par=shift;
my $nm;
($par,$nm)=FindChar($par);
do_t($par);
$nomove=$nm;
}
sub FindChar
{
my $chnm=shift;
my $fnt=$fontlst{$cft}->{FNT};
if (exists($fnt->{GNM}->{$chnm}))
{
my $ch=$fnt->{GNM}->{$chnm};
$ch=RemapChr($ch,$fnt,$chnm) if ($ch > 255);
return(($ch<32)?sprintf("\\%03o",$ch):chr($ch),$fnt->{WID}->[$ch]*$cftsz);
}
else
{
return(' ');
}
}
sub RemapChr
{
my $ch=shift;
my $fnt=shift;
my $chnm=shift;
my $unused=0;
foreach my $un (2..$#{$fnt->{GNO}})
{
$unused=$un,last if $fnt->{GNO}->[$un] eq '/.notdef';
}
if (--$unused <= 255)
{
$fnt->{GNM}->{$chnm}=$unused++;
$fnt->{GNO}->[$unused]=$fnt->{GNO}->[$ch+1];
$fnt->{WID}->[$unused]=$fnt->{WID}->[$ch+1];
$ch=$unused-1;
return($ch);
}
else
{
Msg(0,"Too many glyphs used in font '$cft'");
return(32);
}
}
sub do_c
{
my $par=shift;
push(@ahead,substr($par,1));
$par=substr($par,0,1);
my $ch=ord($par);
do_N($ch);
$nomove=$fontlst{$cft}->{FNT}->{WID}->[$ch]*$cftsz;
}
sub do_N
{
my $par=shift;
if ($par > 255)
{
my $fnt=$fontlst{$cft}->{FNT};
my $chnm='';
foreach my $c (sort keys %{$fnt->{GNM}})
{
$chnm=$c,last if $fnt->{GNM}->{$c} == $par;
}
$par=RemapChr($par,$fnt,$chnm);
}
do_t(chr($par));
$nomove=$fontlst{$cft}->{FNT}->{WID}->[$par]*$cftsz;
}
sub do_n
{
$gotT=0;
PutLine();
$pendmv=$nomove=0;
$n_flg=1;
@lin=();
PutHotSpot($xpos) if defined($mark);
}
1;
########################################################################
### Emacs settings
# Local Variables:
# mode: CPerl
# End:
|