00001 <?php
00023 class Cpdf {
00024
00028 var $numObj=0;
00032 var $objects = array();
00036 var $catalogId;
00041 var $fonts=array();
00045 var $currentFont='';
00049 var $currentBaseFont='';
00053 var $currentFontNum=0;
00057 var $currentNode;
00061 var $currentPage;
00065 var $currentContents;
00069 var $numFonts=0;
00073 var $currentColour=array('r'=>-1,'g'=>-1,'b'=>-1);
00077 var $currentStrokeColour=array('r'=>-1,'g'=>-1,'b'=>-1);
00081 var $currentLineStyle='';
00086 var $stateStack = array();
00090 var $nStateStack = 0;
00094 var $numPages=0;
00098 var $stack=array();
00102 var $nStack=0;
00107 var $looseObjects=array();
00111 var $addLooseObjects=array();
00116 var $infoObject=0;
00120 var $numImages=0;
00125 var $options=array('compression'=>1);
00129 var $firstPageId;
00134 var $wordSpaceAdjust=0;
00138 var $procsetObjectId;
00144 var $fontFamilies = array();
00148 var $currentTextState = '';
00152 var $messages='';
00156 var $arc4='';
00160 var $arc4_objnum=0;
00164 var $fileIdentifier='';
00168 var $encrypted=0;
00172 var $encryptionKey='';
00176 var $callback = array();
00180 var $nCallback = 0;
00185 var $destinations = array();
00191 var $checkpoint = '';
00197 function Cpdf ($pageSize=array(0,0,612,792)){
00198 $this->newDocument($pageSize);
00199
00200
00201 $this->setFontFamily('init');
00202
00203
00204 }
00205
00224 function o_destination($id,$action,$options=''){
00225 if ($action!='new'){
00226 $o =& $this->objects[$id];
00227 }
00228 switch($action){
00229 case 'new':
00230 $this->objects[$id]=array('t'=>'destination','info'=>array());
00231 $tmp = '';
00232 switch ($options['type']){
00233 case 'XYZ':
00234 case 'FitR':
00235 $tmp = ' '.$options['p3'].$tmp;
00236 case 'FitH':
00237 case 'FitV':
00238 case 'FitBH':
00239 case 'FitBV':
00240 $tmp = ' '.$options['p1'].' '.$options['p2'].$tmp;
00241 case 'Fit':
00242 case 'FitB':
00243 $tmp = $options['type'].$tmp;
00244 $this->objects[$id]['info']['string']=$tmp;
00245 $this->objects[$id]['info']['page']=$options['page'];
00246 }
00247 break;
00248 case 'out':
00249 $tmp = $o['info'];
00250 $res="\n".$id." 0 obj\n".'['.$tmp['page'].' 0 R /'.$tmp['string']."]\nendobj\n";
00251 return $res;
00252 break;
00253 }
00254 }
00255
00259 function o_viewerPreferences($id,$action,$options=''){
00260 if ($action!='new'){
00261 $o =& $this->objects[$id];
00262 }
00263 switch ($action){
00264 case 'new':
00265 $this->objects[$id]=array('t'=>'viewerPreferences','info'=>array());
00266 break;
00267 case 'add':
00268 foreach($options as $k=>$v){
00269 switch ($k){
00270 case 'HideToolbar':
00271 case 'HideMenubar':
00272 case 'HideWindowUI':
00273 case 'FitWindow':
00274 case 'CenterWindow':
00275 case 'NonFullScreenPageMode':
00276 case 'Direction':
00277 $o['info'][$k]=$v;
00278 break;
00279 }
00280 }
00281 break;
00282 case 'out':
00283
00284 $res="\n".$id." 0 obj\n".'<< ';
00285 foreach($o['info'] as $k=>$v){
00286 $res.="\n/".$k.' '.$v;
00287 }
00288 $res.="\n>>\n";
00289 return $res;
00290 break;
00291 }
00292 }
00293
00297 function o_catalog($id,$action,$options=''){
00298 if ($action!='new'){
00299 $o =& $this->objects[$id];
00300 }
00301 switch ($action){
00302 case 'new':
00303 $this->objects[$id]=array('t'=>'catalog','info'=>array());
00304 $this->catalogId=$id;
00305 break;
00306 case 'outlines':
00307 case 'pages':
00308 case 'openHere':
00309 $o['info'][$action]=$options;
00310 break;
00311 case 'viewerPreferences':
00312 if (!isset($o['info']['viewerPreferences'])){
00313 $this->numObj++;
00314 $this->o_viewerPreferences($this->numObj,'new');
00315 $o['info']['viewerPreferences']=$this->numObj;
00316 }
00317 $vp = $o['info']['viewerPreferences'];
00318 $this->o_viewerPreferences($vp,'add',$options);
00319 break;
00320 case 'out':
00321 $res="\n".$id." 0 obj\n".'<< /Type /Catalog';
00322 foreach($o['info'] as $k=>$v){
00323 switch($k){
00324 case 'outlines':
00325 $res.="\n".'/Outlines '.$v.' 0 R';
00326 break;
00327 case 'pages':
00328 $res.="\n".'/Pages '.$v.' 0 R';
00329 break;
00330 case 'viewerPreferences':
00331 $res.="\n".'/ViewerPreferences '.$o['info']['viewerPreferences'].' 0 R';
00332 break;
00333 case 'openHere':
00334 $res.="\n".'/OpenAction '.$o['info']['openHere'].' 0 R';
00335 break;
00336 }
00337 }
00338 $res.=" >>\nendobj";
00339 return $res;
00340 break;
00341 }
00342 }
00343
00347 function o_pages($id,$action,$options=''){
00348 if ($action!='new'){
00349 $o =& $this->objects[$id];
00350 }
00351 switch ($action){
00352 case 'new':
00353 $this->objects[$id]=array('t'=>'pages','info'=>array());
00354 $this->o_catalog($this->catalogId,'pages',$id);
00355 break;
00356 case 'page':
00357 if (!is_array($options)){
00358
00359 $o['info']['pages'][]=$options;
00360 } else {
00361
00362
00363 if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])){
00364 $i = array_search($options['rid'],$o['info']['pages']);
00365 if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i]==$options['rid']){
00366
00367
00368 switch ($options['pos']){
00369 case 'before':
00370 $k = $i;
00371 break;
00372 case 'after':
00373 $k=$i+1;
00374 break;
00375 default:
00376 $k=-1;
00377 break;
00378 }
00379 if ($k>=0){
00380 for ($j=count($o['info']['pages'])-1;$j>=$k;$j--){
00381 $o['info']['pages'][$j+1]=$o['info']['pages'][$j];
00382 }
00383 $o['info']['pages'][$k]=$options['id'];
00384 }
00385 }
00386 }
00387 }
00388 break;
00389 case 'procset':
00390 $o['info']['procset']=$options;
00391 break;
00392 case 'mediaBox':
00393 $o['info']['mediaBox']=$options;
00394 break;
00395 case 'font':
00396 $o['info']['fonts'][]=array('objNum'=>$options['objNum'],'fontNum'=>$options['fontNum']);
00397 break;
00398 case 'xObject':
00399 $o['info']['xObjects'][]=array('objNum'=>$options['objNum'],'label'=>$options['label']);
00400 break;
00401 case 'out':
00402 if (count($o['info']['pages'])){
00403 $res="\n".$id." 0 obj\n<< /Type /Pages\n/Kids [";
00404 foreach($o['info']['pages'] as $k=>$v){
00405 $res.=$v." 0 R\n";
00406 }
00407 $res.="]\n/Count ".count($this->objects[$id]['info']['pages']);
00408 if ((isset($o['info']['fonts']) && count($o['info']['fonts'])) || isset($o['info']['procset'])){
00409 $res.="\n/Resources <<";
00410 if (isset($o['info']['procset'])){
00411 $res.="\n/ProcSet ".$o['info']['procset']." 0 R";
00412 }
00413 if (isset($o['info']['fonts']) && count($o['info']['fonts'])){
00414 $res.="\n/Font << ";
00415 foreach($o['info']['fonts'] as $finfo){
00416 $res.="\n/F".$finfo['fontNum']." ".$finfo['objNum']." 0 R";
00417 }
00418 $res.=" >>";
00419 }
00420 if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])){
00421 $res.="\n/XObject << ";
00422 foreach($o['info']['xObjects'] as $finfo){
00423 $res.="\n/".$finfo['label']." ".$finfo['objNum']." 0 R";
00424 }
00425 $res.=" >>";
00426 }
00427 $res.="\n>>";
00428 if (isset($o['info']['mediaBox'])){
00429 $tmp=$o['info']['mediaBox'];
00430 $res.="\n/MediaBox [".sprintf('%.3f',$tmp[0]).' '.sprintf('%.3f',$tmp[1]).' '.sprintf('%.3f',$tmp[2]).' '.sprintf('%.3f',$tmp[3]).']';
00431 }
00432 }
00433 $res.="\n >>\nendobj";
00434 } else {
00435 $res="\n".$id." 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
00436 }
00437 return $res;
00438 break;
00439 }
00440 }
00441
00445 function o_outlines($id,$action,$options=''){
00446 if ($action!='new'){
00447 $o =& $this->objects[$id];
00448 }
00449 switch ($action){
00450 case 'new':
00451 $this->objects[$id]=array('t'=>'outlines','info'=>array('outlines'=>array()));
00452 $this->o_catalog($this->catalogId,'outlines',$id);
00453 break;
00454 case 'outline':
00455 $o['info']['outlines'][]=$options;
00456 break;
00457 case 'out':
00458 if (count($o['info']['outlines'])){
00459 $res="\n".$id." 0 obj\n<< /Type /Outlines /Kids [";
00460 foreach($o['info']['outlines'] as $k=>$v){
00461 $res.=$v." 0 R ";
00462 }
00463 $res.="] /Count ".count($o['info']['outlines'])." >>\nendobj";
00464 } else {
00465 $res="\n".$id." 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
00466 }
00467 return $res;
00468 break;
00469 }
00470 }
00471
00475 function o_font($id,$action,$options=''){
00476 if ($action!='new'){
00477 $o =& $this->objects[$id];
00478 }
00479 switch ($action){
00480 case 'new':
00481 $this->objects[$id]=array('t'=>'font','info'=>array('name'=>$options['name'],'SubType'=>'Type1'));
00482 $fontNum=$this->numFonts;
00483 $this->objects[$id]['info']['fontNum']=$fontNum;
00484
00485 if (isset($options['differences'])){
00486
00487 $this->numObj++;
00488 $this->o_fontEncoding($this->numObj,'new',$options);
00489 $this->objects[$id]['info']['encodingDictionary']=$this->numObj;
00490 } else if (isset($options['encoding'])){
00491
00492 switch($options['encoding']){
00493 case 'WinAnsiEncoding':
00494 case 'MacRomanEncoding':
00495 case 'MacExpertEncoding':
00496 $this->objects[$id]['info']['encoding']=$options['encoding'];
00497 break;
00498 case 'none':
00499 break;
00500 default:
00501 $this->objects[$id]['info']['encoding']='WinAnsiEncoding';
00502 break;
00503 }
00504 } else {
00505 $this->objects[$id]['info']['encoding']='WinAnsiEncoding';
00506 }
00507
00508 $this->o_pages($this->currentNode,'font',array('fontNum'=>$fontNum,'objNum'=>$id));
00509 break;
00510 case 'add':
00511 foreach ($options as $k=>$v){
00512 switch ($k){
00513 case 'BaseFont':
00514 $o['info']['name'] = $v;
00515 break;
00516 case 'FirstChar':
00517 case 'LastChar':
00518 case 'Widths':
00519 case 'FontDescriptor':
00520 case 'SubType':
00521 $this->addMessage('o_font '.$k." : ".$v);
00522 $o['info'][$k] = $v;
00523 break;
00524 }
00525 }
00526 break;
00527 case 'out':
00528 $res="\n".$id." 0 obj\n<< /Type /Font\n/Subtype /".$o['info']['SubType']."\n";
00529 $res.="/Name /F".$o['info']['fontNum']."\n";
00530 $res.="/BaseFont /".$o['info']['name']."\n";
00531 if (isset($o['info']['encodingDictionary'])){
00532
00533 $res.="/Encoding ".$o['info']['encodingDictionary']." 0 R\n";
00534 } else if (isset($o['info']['encoding'])){
00535
00536 $res.="/Encoding /".$o['info']['encoding']."\n";
00537 }
00538 if (isset($o['info']['FirstChar'])){
00539 $res.="/FirstChar ".$o['info']['FirstChar']."\n";
00540 }
00541 if (isset($o['info']['LastChar'])){
00542 $res.="/LastChar ".$o['info']['LastChar']."\n";
00543 }
00544 if (isset($o['info']['Widths'])){
00545 $res.="/Widths ".$o['info']['Widths']." 0 R\n";
00546 }
00547 if (isset($o['info']['FontDescriptor'])){
00548 $res.="/FontDescriptor ".$o['info']['FontDescriptor']." 0 R\n";
00549 }
00550 $res.=">>\nendobj";
00551 return $res;
00552 break;
00553 }
00554 }
00555
00559 function o_fontDescriptor($id,$action,$options=''){
00560 if ($action!='new'){
00561 $o =& $this->objects[$id];
00562 }
00563 switch ($action){
00564 case 'new':
00565 $this->objects[$id]=array('t'=>'fontDescriptor','info'=>$options);
00566 break;
00567 case 'out':
00568 $res="\n".$id." 0 obj\n<< /Type /FontDescriptor\n";
00569 foreach ($o['info'] as $label => $value){
00570 switch ($label){
00571 case 'Ascent':
00572 case 'CapHeight':
00573 case 'Descent':
00574 case 'Flags':
00575 case 'ItalicAngle':
00576 case 'StemV':
00577 case 'AvgWidth':
00578 case 'Leading':
00579 case 'MaxWidth':
00580 case 'MissingWidth':
00581 case 'StemH':
00582 case 'XHeight':
00583 case 'CharSet':
00584 if (strlen($value)){
00585 $res.='/'.$label.' '.$value."\n";
00586 }
00587 break;
00588 case 'FontFile':
00589 case 'FontFile2':
00590 case 'FontFile3':
00591 $res.='/'.$label.' '.$value." 0 R\n";
00592 break;
00593 case 'FontBBox':
00594 $res.='/'.$label.' ['.$value[0].' '.$value[1].' '.$value[2].' '.$value[3]."]\n";
00595 break;
00596 case 'FontName':
00597 $res.='/'.$label.' /'.$value."\n";
00598 break;
00599 }
00600 }
00601 $res.=">>\nendobj";
00602 return $res;
00603 break;
00604 }
00605 }
00606
00610 function o_fontEncoding($id,$action,$options=''){
00611 if ($action!='new'){
00612 $o =& $this->objects[$id];
00613 }
00614 switch ($action){
00615 case 'new':
00616
00617 $this->objects[$id]=array('t'=>'fontEncoding','info'=>$options);
00618 break;
00619 case 'out':
00620 $res="\n".$id." 0 obj\n<< /Type /Encoding\n";
00621 if (!isset($o['info']['encoding'])){
00622 $o['info']['encoding']='WinAnsiEncoding';
00623 }
00624 if ($o['info']['encoding']!='none'){
00625 $res.="/BaseEncoding /".$o['info']['encoding']."\n";
00626 }
00627 $res.="/Differences \n[";
00628 $onum=-100;
00629 foreach($o['info']['differences'] as $num=>$label){
00630 if ($num!=$onum+1){
00631
00632 $res.= "\n".$num." /".$label;
00633 } else {
00634 $res.= " /".$label;
00635 }
00636 $onum=$num;
00637 }
00638 $res.="\n]\n>>\nendobj";
00639 return $res;
00640 break;
00641 }
00642 }
00643
00647 function o_procset($id,$action,$options=''){
00648 if ($action!='new'){
00649 $o =& $this->objects[$id];
00650 }
00651 switch ($action){
00652 case 'new':
00653 $this->objects[$id]=array('t'=>'procset','info'=>array('PDF'=>1,'Text'=>1));
00654 $this->o_pages($this->currentNode,'procset',$id);
00655 $this->procsetObjectId=$id;
00656 break;
00657 case 'add':
00658
00659
00660 switch ($options) {
00661 case 'ImageB':
00662 case 'ImageC':
00663 case 'ImageI':
00664 $o['info'][$options]=1;
00665 break;
00666 }
00667 break;
00668 case 'out':
00669 $res="\n".$id." 0 obj\n[";
00670 foreach ($o['info'] as $label=>$val){
00671 $res.='/'.$label.' ';
00672 }
00673 $res.="]\nendobj";
00674 return $res;
00675 break;
00676 }
00677 }
00678
00682 function o_info($id,$action,$options=''){
00683 if ($action!='new'){
00684 $o =& $this->objects[$id];
00685 }
00686 switch ($action){
00687 case 'new':
00688 $this->infoObject=$id;
00689 $date='D:'.date('Ymd');
00690 $this->objects[$id]=array('t'=>'info','info'=>array('Creator'=>'R and OS php pdf writer, http:
00691 break;
00692 case 'Title':
00693 case 'Author':
00694 case 'Subject':
00695 case 'Keywords':
00696 case 'Creator':
00697 case 'Producer':
00698 case 'CreationDate':
00699 case 'ModDate':
00700 case 'Trapped':
00701 $o['info'][$action]=$options;
00702 break;
00703 case 'out':
00704 if ($this->encrypted){
00705 $this->encryptInit($id);
00706 }
00707 $res="\n".$id." 0 obj\n<<\n";
00708 foreach ($o['info'] as $k=>$v){
00709 $res.='/'.$k.' (';
00710 if ($this->encrypted){
00711 $res.=$this->filterText($this->ARC4($v));
00712 } else {
00713 $res.=$this->filterText($v);
00714 }
00715 $res.=")\n";
00716 }
00717 $res.=">>\nendobj";
00718 return $res;
00719 break;
00720 }
00721 }
00722
00726 function o_action($id,$action,$options=''){
00727 if ($action!='new'){
00728 $o =& $this->objects[$id];
00729 }
00730 switch ($action){
00731 case 'new':
00732 if (is_array($options)){
00733 $this->objects[$id]=array('t'=>'action','info'=>$options,'type'=>$options['type']);
00734 } else {
00735
00736 $this->objects[$id]=array('t'=>'action','info'=>$options,'type'=>'URI');
00737 }
00738 break;
00739 case 'out':
00740 if ($this->encrypted){
00741 $this->encryptInit($id);
00742 }
00743 $res="\n".$id." 0 obj\n<< /Type /Action";
00744 switch($o['type']){
00745 case 'ilink':
00746
00747 $res.="\n/S /GoTo\n/D ".$this->destinations[(string)$o['info']['label']]." 0 R";
00748 break;
00749 case 'URI':
00750 $res.="\n/S /URI\n/URI (";
00751 if ($this->encrypted){
00752 $res.=$this->filterText($this->ARC4($o['info']));
00753 } else {
00754 $res.=$this->filterText($o['info']);
00755 }
00756 $res.=")";
00757 break;
00758 }
00759 $res.="\n>>\nendobj";
00760 return $res;
00761 break;
00762 }
00763 }
00764
00769 function o_annotation($id,$action,$options=''){
00770 if ($action!='new'){
00771 $o =& $this->objects[$id];
00772 }
00773 switch ($action){
00774 case 'new':
00775
00776 $pageId = $this->currentPage;
00777 $this->o_page($pageId,'annot',$id);
00778
00779 switch($options['type']){
00780 case 'link':
00781 $this->objects[$id]=array('t'=>'annotation','info'=>$options);
00782 $this->numObj++;
00783 $this->o_action($this->numObj,'new',$options['url']);
00784 $this->objects[$id]['info']['actionId']=$this->numObj;
00785 break;
00786 case 'ilink':
00787
00788 $label = $options['label'];
00789 $this->objects[$id]=array('t'=>'annotation','info'=>$options);
00790 $this->numObj++;
00791 $this->o_action($this->numObj,'new',array('type'=>'ilink','label'=>$label));
00792 $this->objects[$id]['info']['actionId']=$this->numObj;
00793 break;
00794 }
00795 break;
00796 case 'out':
00797 $res="\n".$id." 0 obj\n<< /Type /Annot";
00798 switch($o['info']['type']){
00799 case 'link':
00800 case 'ilink':
00801 $res.= "\n/Subtype /Link";
00802 break;
00803 }
00804 $res.="\n/A ".$o['info']['actionId']." 0 R";
00805 $res.="\n/Border [0 0 0]";
00806 $res.="\n/H /I";
00807 $res.="\n/Rect [ ";
00808 foreach($o['info']['rect'] as $v){
00809 $res.= sprintf("%.4f ",$v);
00810 }
00811 $res.="]";
00812 $res.="\n>>\nendobj";
00813 return $res;
00814 break;
00815 }
00816 }
00817
00821 function o_page($id,$action,$options=''){
00822 if ($action!='new'){
00823 $o =& $this->objects[$id];
00824 }
00825 switch ($action){
00826 case 'new':
00827 $this->numPages++;
00828 $this->objects[$id]=array('t'=>'page','info'=>array('parent'=>$this->currentNode,'pageNum'=>$this->numPages));
00829 if (is_array($options)){
00830
00831 $options['id']=$id;
00832 $this->o_pages($this->currentNode,'page',$options);
00833 } else {
00834 $this->o_pages($this->currentNode,'page',$id);
00835 }
00836 $this->currentPage=$id;
00837
00838 $this->numObj++;
00839 $this->o_contents($this->numObj,'new',$id);
00840 $this->currentContents=$this->numObj;
00841 $this->objects[$id]['info']['contents']=array();
00842 $this->objects[$id]['info']['contents'][]=$this->numObj;
00843 $match = ($this->numPages%2 ? 'odd' : 'even');
00844 foreach($this->addLooseObjects as $oId=>$target){
00845 if ($target=='all' || $match==$target){
00846 $this->objects[$id]['info']['contents'][]=$oId;
00847 }
00848 }
00849 break;
00850 case 'content':
00851 $o['info']['contents'][]=$options;
00852 break;
00853 case 'annot':
00854
00855 if (!isset($o['info']['annot'])){
00856 $o['info']['annot']=array();
00857 }
00858
00859 $o['info']['annot'][]=$options;
00860 break;
00861 case 'out':
00862 $res="\n".$id." 0 obj\n<< /Type /Page";
00863 $res.="\n/Parent ".$o['info']['parent']." 0 R";
00864 if (isset($o['info']['annot'])){
00865 $res.="\n/Annots [";
00866 foreach($o['info']['annot'] as $aId){
00867 $res.=" ".$aId." 0 R";
00868 }
00869 $res.=" ]";
00870 }
00871 $count = count($o['info']['contents']);
00872 if ($count==1){
00873 $res.="\n/Contents ".$o['info']['contents'][0]." 0 R";
00874 } else if ($count>1){
00875 $res.="\n/Contents [\n";
00876 foreach ($o['info']['contents'] as $cId){
00877 $res.=$cId." 0 R\n";
00878 }
00879 $res.="]";
00880 }
00881 $res.="\n>>\nendobj";
00882 return $res;
00883 break;
00884 }
00885 }
00886
00890 function o_contents($id,$action,$options=''){
00891 if ($action!='new'){
00892 $o =& $this->objects[$id];
00893 }
00894 switch ($action){
00895 case 'new':
00896 $this->objects[$id]=array('t'=>'contents','c'=>'','info'=>array());
00897 if (strlen($options) && intval($options)){
00898
00899 $this->objects[$id]['onPage']=$options;
00900 } else if ($options=='raw'){
00901
00902 $this->objects[$id]['raw']=1;
00903 }
00904 break;
00905 case 'add':
00906
00907 foreach ($options as $k=>$v){
00908 $o['info'][$k]=$v;
00909 }
00910 case 'out':
00911 $tmp=$o['c'];
00912 $res= "\n".$id." 0 obj\n";
00913 if (isset($this->objects[$id]['raw'])){
00914 $res.=$tmp;
00915 } else {
00916 $res.= "<<";
00917 if (function_exists('gzcompress') && $this->options['compression']){
00918
00919 $res.=" /Filter /FlateDecode";
00920 $tmp = gzcompress($tmp);
00921 }
00922 if ($this->encrypted){
00923 $this->encryptInit($id);
00924 $tmp = $this->ARC4($tmp);
00925 }
00926 foreach($o['info'] as $k=>$v){
00927 $res .= "\n/".$k.' '.$v;
00928 }
00929 $res.="\n/Length ".strlen($tmp)." >>\nstream\n".$tmp."\nendstream";
00930 }
00931 $res.="\nendobj\n";
00932 return $res;
00933 break;
00934 }
00935 }
00936
00940 function o_image($id,$action,$options=''){
00941 if ($action!='new'){
00942 $o =& $this->objects[$id];
00943 }
00944 switch($action){
00945 case 'new':
00946
00947 $this->objects[$id]=array('t'=>'image','data'=>$options['data'],'info'=>array());
00948 $this->objects[$id]['info']['Type']='/XObject';
00949 $this->objects[$id]['info']['Subtype']='/Image';
00950 $this->objects[$id]['info']['Width']=$options['iw'];
00951 $this->objects[$id]['info']['Height']=$options['ih'];
00952 if (!isset($options['type']) || $options['type']=='jpg'){
00953 if (!isset($options['channels'])){
00954 $options['channels']=3;
00955 }
00956 switch($options['channels']){
00957 case 1:
00958 $this->objects[$id]['info']['ColorSpace']='/DeviceGray';
00959 break;
00960 default:
00961 $this->objects[$id]['info']['ColorSpace']='/DeviceRGB';
00962 break;
00963 }
00964 $this->objects[$id]['info']['Filter']='/DCTDecode';
00965 $this->objects[$id]['info']['BitsPerComponent']=8;
00966 } else if ($options['type']=='png'){
00967 $this->objects[$id]['info']['Filter']='/FlateDecode';
00968 $this->objects[$id]['info']['DecodeParms']='<< /Predictor 15 /Colors '.$options['ncolor'].' /Columns '.$options['iw'].' /BitsPerComponent '.$options['bitsPerComponent'].'>>';
00969 if (strlen($options['pdata'])){
00970 $tmp = ' [ /Indexed /DeviceRGB '.(strlen($options['pdata'])/3-1).' ';
00971 $this->numObj++;
00972 $this->o_contents($this->numObj,'new');
00973 $this->objects[$this->numObj]['c']=$options['pdata'];
00974 $tmp.=$this->numObj.' 0 R';
00975 $tmp .=' ]';
00976 $this->objects[$id]['info']['ColorSpace'] = $tmp;
00977 if (isset($options['transparency'])){
00978 switch($options['transparency']['type']){
00979 case 'indexed':
00980 $tmp=' [ '.$options['transparency']['data'].' '.$options['transparency']['data'].'] ';
00981 $this->objects[$id]['info']['Mask'] = $tmp;
00982 break;
00983 }
00984 }
00985 } else {
00986 $this->objects[$id]['info']['ColorSpace']='/'.$options['color'];
00987 }
00988 $this->objects[$id]['info']['BitsPerComponent']=$options['bitsPerComponent'];
00989 }
00990
00991
00992 $this->o_pages($this->currentNode,'xObject',array('label'=>$options['label'],'objNum'=>$id));
00993
00994 $this->o_procset($this->procsetObjectId,'add','ImageC');
00995 break;
00996 case 'out':
00997 $tmp=$o['data'];
00998 $res= "\n".$id." 0 obj\n<<";
00999 foreach($o['info'] as $k=>$v){
01000 $res.="\n/".$k.' '.$v;
01001 }
01002 if ($this->encrypted){
01003 $this->encryptInit($id);
01004 $tmp = $this->ARC4($tmp);
01005 }
01006 $res.="\n/Length ".strlen($tmp)." >>\nstream\n".$tmp."\nendstream\nendobj\n";
01007 return $res;
01008 break;
01009 }
01010 }
01011
01015 function o_encryption($id,$action,$options=''){
01016 if ($action!='new'){
01017 $o =& $this->objects[$id];
01018 }
01019 switch($action){
01020 case 'new':
01021
01022 $this->objects[$id]=array('t'=>'encryption','info'=>$options);
01023 $this->arc4_objnum=$id;
01024
01025 $pad = chr(0x28).chr(0xBF).chr(0x4E).chr(0x5E).chr(0x4E).chr(0x75).chr(0x8A).chr(0x41).chr(0x64).chr(0x00).chr(0x4E).chr(0x56).chr(0xFF).chr(0xFA).chr(0x01).chr(0x08).chr(0x2E).chr(0x2E).chr(0x00).chr(0xB6).chr(0xD0).chr(0x68).chr(0x3E).chr(0x80).chr(0x2F).chr(0x0C).chr(0xA9).chr(0xFE).chr(0x64).chr(0x53).chr(0x69).chr(0x7A);
01026 $len = strlen($options['owner']);
01027 if ($len>32){
01028 $owner = substr($options['owner'],0,32);
01029 } else if ($len<32){
01030 $owner = $options['owner'].substr($pad,0,32-$len);
01031 } else {
01032 $owner = $options['owner'];
01033 }
01034 $len = strlen($options['user']);
01035 if ($len>32){
01036 $user = substr($options['user'],0,32);
01037 } else if ($len<32){
01038 $user = $options['user'].substr($pad,0,32-$len);
01039 } else {
01040 $user = $options['user'];
01041 }
01042 $tmp = $this->md5_16($owner);
01043 $okey = substr($tmp,0,5);
01044 $this->ARC4_init($okey);
01045 $ovalue=$this->ARC4($user);
01046 $this->objects[$id]['info']['O']=$ovalue;
01047
01048 $tmp = $this->md5_16($user.$ovalue.chr($options['p']).chr(255).chr(255).chr(255).$this->fileIdentifier);
01049 $ukey = substr($tmp,0,5);
01050
01051 $this->ARC4_init($ukey);
01052 $this->encryptionKey = $ukey;
01053 $this->encrypted=1;
01054 $uvalue=$this->ARC4($pad);
01055
01056 $this->objects[$id]['info']['U']=$uvalue;
01057 $this->encryptionKey=$ukey;
01058
01059
01060 break;
01061 case 'out':
01062 $res= "\n".$id." 0 obj\n<<";
01063 $res.="\n/Filter /Standard";
01064 $res.="\n/V 1";
01065 $res.="\n/R 2";
01066 $res.="\n/O (".$this->filterText($o['info']['O']).')';
01067 $res.="\n/U (".$this->filterText($o['info']['U']).')';
01068
01069 $o['info']['p'] = (($o['info']['p']^255)+1)*-1;
01070 $res.="\n/P ".($o['info']['p']);
01071 $res.="\n>>\nendobj\n";
01072
01073 return $res;
01074 break;
01075 }
01076 }
01077
01086 function md5_16($string){
01087 $tmp = md5($string);
01088 $out='';
01089 for ($i=0;$i<=30;$i=$i+2){
01090 $out.=chr(hexdec(substr($tmp,$i,2)));
01091 }
01092 return $out;
01093 }
01094
01098 function encryptInit($id){
01099 $tmp = $this->encryptionKey;
01100 $hex = dechex($id);
01101 if (strlen($hex)<6){
01102 $hex = substr('000000',0,6-strlen($hex)).$hex;
01103 }
01104 $tmp.= chr(hexdec(substr($hex,4,2))).chr(hexdec(substr($hex,2,2))).chr(hexdec(substr($hex,0,2))).chr(0).chr(0);
01105 $key = $this->md5_16($tmp);
01106 $this->ARC4_init(substr($key,0,10));
01107 }
01108
01112 function ARC4_init($key=''){
01113 $this->arc4 = '';
01114
01115 if (strlen($key)==0){
01116 return;
01117 }
01118 $k = '';
01119 while(strlen($k)<256){
01120 $k.=$key;
01121 }
01122 $k=substr($k,0,256);
01123 for ($i=0;$i<256;$i++){
01124 $this->arc4 .= chr($i);
01125 }
01126 $j=0;
01127 for ($i=0;$i<256;$i++){
01128 $t = $this->arc4[$i];
01129 $j = ($j + ord($t) + ord($k[$i]))%256;
01130 $this->arc4[$i]=$this->arc4[$j];
01131 $this->arc4[$j]=$t;
01132 }
01133 }
01134
01138 function ARC4($text){
01139 $len=strlen($text);
01140 $a=0;
01141 $b=0;
01142 $c = $this->arc4;
01143 $out='';
01144 for ($i=0;$i<$len;$i++){
01145 $a = ($a+1)%256;
01146 $t= $c[$a];
01147 $b = ($b+ord($t))%256;
01148 $c[$a]=$c[$b];
01149 $c[$b]=$t;
01150 $k = ord($c[(ord($c[$a])+ord($c[$b]))%256]);
01151 $out.=chr(ord($text[$i]) ^ $k);
01152 }
01153
01154 return $out;
01155 }
01156
01164 function addLink($url,$x0,$y0,$x1,$y1){
01165 $this->numObj++;
01166 $info = array('type'=>'link','url'=>$url,'rect'=>array($x0,$y0,$x1,$y1));
01167 $this->o_annotation($this->numObj,'new',$info);
01168 }
01169
01173 function addInternalLink($label,$x0,$y0,$x1,$y1){
01174 $this->numObj++;
01175 $info = array('type'=>'ilink','label'=>$label,'rect'=>array($x0,$y0,$x1,$y1));
01176 $this->o_annotation($this->numObj,'new',$info);
01177 }
01178
01184 function setEncryption($userPass='',$ownerPass='',$pc=array()){
01185 $p=bindec(11000000);
01186
01187 $options = array(
01188 'print'=>4
01189 ,'modify'=>8
01190 ,'copy'=>16
01191 ,'add'=>32
01192 );
01193 foreach($pc as $k=>$v){
01194 if ($v && isset($options[$k])){
01195 $p+=$options[$k];
01196 } else if (isset($options[$v])){
01197 $p+=$options[$v];
01198 }
01199 }
01200
01201 if ($this->arc4_objnum == 0){
01202
01203 $this->numObj++;
01204 if (strlen($ownerPass)==0){
01205 $ownerPass=$userPass;
01206 }
01207 $this->o_encryption($this->numObj,'new',array('user'=>$userPass,'owner'=>$ownerPass,'p'=>$p));
01208 }
01209 }
01210
01214 function checkAllHere(){
01215 }
01216
01220 function output($debug=0){
01221
01222 if ($debug){
01223
01224 $this->options['compression']=0;
01225 }
01226
01227 if ($this->arc4_objnum){
01228 $this->ARC4_init($this->encryptionKey);
01229 }
01230
01231 $this->checkAllHere();
01232
01233 $xref=array();
01234 $content="%PDF-1.3\n%âãÏÓ\n";
01235
01236 $pos=strlen($content);
01237 foreach($this->objects as $k=>$v){
01238 $tmp='o_'.$v['t'];
01239 $cont=$this->$tmp($k,'out');
01240 $content.=$cont;
01241 $xref[]=$pos;
01242 $pos+=strlen($cont);
01243 }
01244 $content.="\nxref\n0 ".(count($xref)+1)."\n0000000000 65535 f \n";
01245 foreach($xref as $p){
01246 $content.=substr('0000000000',0,10-strlen($p)).$p." 00000 n \n";
01247 }
01248 $content.="\ntrailer\n << /Size ".(count($xref)+1)."\n /Root 1 0 R\n /Info ".$this->infoObject." 0 R\n";
01249
01250 if ($this->arc4_objnum > 0){
01251 $content .= "/Encrypt ".$this->arc4_objnum." 0 R\n";
01252 }
01253 if (strlen($this->fileIdentifier)){
01254 $content .= "/ID[<".$this->fileIdentifier."><".$this->fileIdentifier.">]\n";
01255 }
01256 $content .= " >>\nstartxref\n".$pos."\n%%EOF\n";
01257 return $content;
01258 }
01259
01267 function newDocument($pageSize=array(0,0,612,792)){
01268 $this->numObj=0;
01269 $this->objects = array();
01270
01271 $this->numObj++;
01272 $this->o_catalog($this->numObj,'new');
01273
01274 $this->numObj++;
01275 $this->o_outlines($this->numObj,'new');
01276
01277 $this->numObj++;
01278 $this->o_pages($this->numObj,'new');
01279
01280 $this->o_pages($this->numObj,'mediaBox',$pageSize);
01281 $this->currentNode = 3;
01282
01283 $this->numObj++;
01284 $this->o_procset($this->numObj,'new');
01285
01286 $this->numObj++;
01287 $this->o_info($this->numObj,'new');
01288
01289 $this->numObj++;
01290 $this->o_page($this->numObj,'new');
01291
01292
01293
01294 $this->firstPageId = $this->currentContents;
01295 }
01296
01306 function openFont($font){
01307
01308 $pos=strrpos($font,'/');
01309 if ($pos===false){
01310 $dir = './';
01311 $name = $font;
01312 } else {
01313 $dir=substr($font,0,$pos+1);
01314 $name=substr($font,$pos+1);
01315 }
01316
01317 if (substr($name,-4)=='.afm'){
01318 $name=substr($name,0,strlen($name)-4);
01319 }
01320 $this->addMessage('openFont: '.$font.' - '.$name);
01321 if (file_exists($dir.'php_'.$name.'.afm')){
01322 $this->addMessage('openFont: php file exists '.$dir.'php_'.$name.'.afm');
01323 $tmp = file($dir.'php_'.$name.'.afm');
01324 $this->fonts[$font]=unserialize($tmp[0]);
01325 if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_']<1){
01326
01327 $this->addMessage('openFont: clear out, make way for new version.');
01328 unset($this->fonts[$font]);
01329 }
01330 }
01331 if (!isset($this->fonts[$font]) && file_exists($dir.$name.'.afm')){
01332
01333 $this->addMessage('openFont: build php file from '.$dir.$name.'.afm');
01334 $data = array();
01335 $file = file($dir.$name.'.afm');
01336 foreach ($file as $rowA){
01337 $row=trim($rowA);
01338 $pos=strpos($row,' ');
01339 if ($pos){
01340
01341 $key = substr($row,0,$pos);
01342 switch ($key){
01343 case 'FontName':
01344 case 'FullName':
01345 case 'FamilyName':
01346 case 'Weight':
01347 case 'ItalicAngle':
01348 case 'IsFixedPitch':
01349 case 'CharacterSet':
01350 case 'UnderlinePosition':
01351 case 'UnderlineThickness':
01352 case 'Version':
01353 case 'EncodingScheme':
01354 case 'CapHeight':
01355 case 'XHeight':
01356 case 'Ascender':
01357 case 'Descender':
01358 case 'StdHW':
01359 case 'StdVW':
01360 case 'StartCharMetrics':
01361 $data[$key]=trim(substr($row,$pos));
01362 break;
01363 case 'FontBBox':
01364 $data[$key]=explode(' ',trim(substr($row,$pos)));
01365 break;
01366 case 'C':
01367
01368 $bits=explode(';',trim($row));
01369 $dtmp=array();
01370 foreach($bits as $bit){
01371 $bits2 = explode(' ',trim($bit));
01372 if (strlen($bits2[0])){
01373 if (count($bits2)>2){
01374 $dtmp[$bits2[0]]=array();
01375 for ($i=1;$i<count($bits2);$i++){
01376 $dtmp[$bits2[0]][]=$bits2[$i];
01377 }
01378 } else if (count($bits2)==2){
01379 $dtmp[$bits2[0]]=$bits2[1];
01380 }
01381 }
01382 }
01383 if ($dtmp['C']>=0){
01384 $data['C'][$dtmp['C']]=$dtmp;
01385 $data['C'][$dtmp['N']]=$dtmp;
01386 } else {
01387 $data['C'][$dtmp['N']]=$dtmp;
01388 }
01389 break;
01390 case 'KPX':
01391
01392 $bits=explode(' ',trim($row));
01393 $data['KPX'][$bits[1]][$bits[2]]=$bits[3];
01394 break;
01395 }
01396 }
01397 }
01398 $data['_version_']=1;
01399 $this->fonts[$font]=$data;
01400 $fp = fopen($dir.'php_'.$name.'.afm','w');
01401 fwrite($fp,serialize($data));
01402 fclose($fp);
01403 } else if (!isset($this->fonts[$font])){
01404 $this->addMessage('openFont: no font file found');
01405
01406 }
01407 }
01408
01417 function selectFont($fontName,$encoding='',$set=1){
01418 if (!isset($this->fonts[$fontName])){
01419
01420 $this->openFont($fontName);
01421 if (isset($this->fonts[$fontName])){
01422 $this->numObj++;
01423 $this->numFonts++;
01424 $pos=strrpos($fontName,'/');
01425
01426 $name=substr($fontName,$pos+1);
01427 if (substr($name,-4)=='.afm'){
01428 $name=substr($name,0,strlen($name)-4);
01429 }
01430 $options=array('name'=>$name);
01431 if (is_array($encoding)){
01432
01433 if (isset($encoding['encoding'])){
01434 $options['encoding']=$encoding['encoding'];
01435 }
01436 if (isset($encoding['differences'])){
01437 $options['differences']=$encoding['differences'];
01438 }
01439 } else if (strlen($encoding)){
01440
01441 $options['encoding']=$encoding;
01442 }
01443 $fontObj = $this->numObj;
01444 $this->o_font($this->numObj,'new',$options);
01445 $this->fonts[$fontName]['fontNum']=$this->numFonts;
01446
01447
01448
01449 $basefile = substr($fontName,0,strlen($fontName)-4);
01450 if (file_exists($basefile.'.pfb')){
01451 $fbtype = 'pfb';
01452 } else if (file_exists($basefile.'.ttf')){
01453 $fbtype = 'ttf';
01454 } else {
01455 $fbtype='';
01456 }
01457 $fbfile = $basefile.'.'.$fbtype;
01458
01459
01460
01461 $this->addMessage('selectFont: checking for - '.$fbfile);
01462 if (substr($fontName,-4)=='.afm' && strlen($fbtype) ){
01463 $adobeFontName = $this->fonts[$fontName]['FontName'];
01464
01465 $this->addMessage('selectFont: adding font file - '.$fbfile.' - '.$adobeFontName);
01466
01467 $firstChar = -1;
01468 $lastChar = 0;
01469 $widths = array();
01470 foreach ($this->fonts[$fontName]['C'] as $num=>$d){
01471 if (intval($num)>0 || $num=='0'){
01472 if ($lastChar>0 && $num>$lastChar+1){
01473 for($i=$lastChar+1;$i<$num;$i++){
01474 $widths[] = 0;
01475 }
01476 }
01477 $widths[] = $d['WX'];
01478 if ($firstChar==-1){
01479 $firstChar = $num;
01480 }
01481 $lastChar = $num;
01482 }
01483 }
01484
01485 if (isset($options['differences'])){
01486 foreach($options['differences'] as $charNum=>$charName){
01487 if ($charNum>$lastChar){
01488 for($i=$lastChar+1;$i<=$charNum;$i++){
01489 $widths[]=0;
01490 }
01491 $lastChar=$charNum;
01492 }
01493 if (isset($this->fonts[$fontName]['C'][$charName])){
01494 $widths[$charNum-$firstChar]=$this->fonts[$fontName]['C'][$charName]['WX'];
01495 }
01496 }
01497 }
01498 $this->addMessage('selectFont: FirstChar='.$firstChar);
01499 $this->addMessage('selectFont: LastChar='.$lastChar);
01500 $this->numObj++;
01501 $this->o_contents($this->numObj,'new','raw');
01502 $this->objects[$this->numObj]['c'].='[';
01503 foreach($widths as $width){
01504 $this->objects[$this->numObj]['c'].=' '.$width;
01505 }
01506 $this->objects[$this->numObj]['c'].=' ]';
01507 $widthid = $this->numObj;
01508
01509
01510
01511
01512 $fp = fopen($fbfile,'rb');
01513 $tmp = get_magic_quotes_runtime();
01514 set_magic_quotes_runtime(0);
01515 $data = fread($fp,filesize($fbfile));
01516 set_magic_quotes_runtime($tmp);
01517 fclose($fp);
01518
01519
01520 $this->numObj++;
01521 $fontDescriptorId = $this->numObj;
01522 $this->numObj++;
01523 $pfbid = $this->numObj;
01524
01525 $flags=0;
01526 if ($this->fonts[$fontName]['ItalicAngle']!=0){ $flags+=pow(2,6); }
01527 if ($this->fonts[$fontName]['IsFixedPitch']=='true'){ $flags+=1; }
01528 $flags+=pow(2,5);
01529
01530 $list = array('Ascent'=>'Ascender','CapHeight'=>'CapHeight','Descent'=>'Descender','FontBBox'=>'FontBBox','ItalicAngle'=>'ItalicAngle');
01531 $fdopt = array(
01532 'Flags'=>$flags
01533 ,'FontName'=>$adobeFontName
01534 ,'StemV'=>100
01535 );
01536 foreach($list as $k=>$v){
01537 if (isset($this->fonts[$fontName][$v])){
01538 $fdopt[$k]=$this->fonts[$fontName][$v];
01539 }
01540 }
01541
01542 if ($fbtype=='pfb'){
01543 $fdopt['FontFile']=$pfbid;
01544 } else if ($fbtype=='ttf'){
01545 $fdopt['FontFile2']=$pfbid;
01546 }
01547 $this->o_fontDescriptor($fontDescriptorId,'new',$fdopt);
01548
01549
01550 $this->o_contents($this->numObj,'new');
01551 $this->objects[$pfbid]['c'].=$data;
01552
01553 if ($fbtype=='pfb'){
01554 $l1 = strpos($data,'eexec')+6;
01555 $l2 = strpos($data,'00000000')-$l1;
01556 $l3 = strlen($data)-$l2-$l1;
01557 $this->o_contents($this->numObj,'add',array('Length1'=>$l1,'Length2'=>$l2,'Length3'=>$l3));
01558 } else if ($fbtype=='ttf'){
01559 $l1 = strlen($data);
01560 $this->o_contents($this->numObj,'add',array('Length1'=>$l1));
01561 }
01562
01563
01564
01565 $tmp = array('BaseFont'=>$adobeFontName,'Widths'=>$widthid
01566 ,'FirstChar'=>$firstChar,'LastChar'=>$lastChar
01567 ,'FontDescriptor'=>$fontDescriptorId);
01568 if ($fbtype=='ttf'){
01569 $tmp['SubType']='TrueType';
01570 }
01571 $this->addMessage('adding extra info to font.('.$fontObj.')');
01572 foreach($tmp as $fk=>$fv){
01573 $this->addMessage($fk." : ".$fv);
01574 }
01575 $this->o_font($fontObj,'add',$tmp);
01576
01577 } else {
01578 $this->addMessage('selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts');
01579 }
01580
01581
01582
01583
01584 if (isset($options['differences'])){
01585 $this->fonts[$fontName]['differences']=$options['differences'];
01586 }
01587 }
01588 }
01589 if ($set && isset($this->fonts[$fontName])){
01590
01591 $this->currentBaseFont=$fontName;
01592
01593
01594 $this->setCurrentFont();
01595 }
01596 return $this->currentFontNum;
01597 }
01598
01611 function setCurrentFont(){
01612 if (strlen($this->currentBaseFont)==0){
01613
01614 $this->selectFont('./fonts/Helvetica.afm');
01615 }
01616 $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1);
01617 if (strlen($this->currentTextState)
01618 && isset($this->fontFamilies[$cf])
01619 && isset($this->fontFamilies[$cf][$this->currentTextState])){
01620
01621
01622
01623 $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState];
01624 $this->selectFont($nf,'',0);
01625 $this->currentFont = $nf;
01626 $this->currentFontNum = $this->fonts[$nf]['fontNum'];
01627 } else {
01628
01629
01630 $this->currentFont = $this->currentBaseFont;
01631 $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
01632 }
01633 }
01634
01639 function getFirstPageId(){
01640 return $this->firstPageId;
01641 }
01642
01648 function addContent($content){
01649 $this->objects[$this->currentContents]['c'].=$content;
01650 }
01651
01655 function setColor($r,$g,$b,$force=0){
01656 if ($r>=0 && ($force || $r!=$this->currentColour['r'] || $g!=$this->currentColour['g'] || $b!=$this->currentColour['b'])){
01657 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3f',$r).' '.sprintf('%.3f',$g).' '.sprintf('%.3f',$b).' rg';
01658 $this->currentColour=array('r'=>$r,'g'=>$g,'b'=>$b);
01659 }
01660 }
01661
01665 function setStrokeColor($r,$g,$b,$force=0){
01666 if ($r>=0 && ($force || $r!=$this->currentStrokeColour['r'] || $g!=$this->currentStrokeColour['g'] || $b!=$this->currentStrokeColour['b'])){
01667 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3f',$r).' '.sprintf('%.3f',$g).' '.sprintf('%.3f',$b).' RG';
01668 $this->currentStrokeColour=array('r'=>$r,'g'=>$g,'b'=>$b);
01669 }
01670 }
01671
01675 function line($x1,$y1,$x2,$y2){
01676 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3f',$x1).' '.sprintf('%.3f',$y1).' m '.sprintf('%.3f',$x2).' '.sprintf('%.3f',$y2).' l S';
01677 }
01678
01682 function curve($x0,$y0,$x1,$y1,$x2,$y2,$x3,$y3){
01683
01684
01685 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3f',$x0).' '.sprintf('%.3f',$y0).' m '.sprintf('%.3f',$x1).' '.sprintf('%.3f',$y1);
01686 $this->objects[$this->currentContents]['c'].= ' '.sprintf('%.3f',$x2).' '.sprintf('%.3f',$y2).' '.sprintf('%.3f',$x3).' '.sprintf('%.3f',$y3).' c S';
01687 }
01688
01692 function partEllipse($x0,$y0,$astart,$afinish,$r1,$r2=0,$angle=0,$nSeg=8){
01693 $this->ellipse($x0,$y0,$r1,$r2,$angle,$nSeg,$astart,$afinish,0);
01694 }
01695
01699 function filledEllipse($x0,$y0,$r1,$r2=0,$angle=0,$nSeg=8,$astart=0,$afinish=360){
01700 return $this->ellipse($x0,$y0,$r1,$r2=0,$angle,$nSeg,$astart,$afinish,1,1);
01701 }
01702
01713 function ellipse($x0,$y0,$r1,$r2=0,$angle=0,$nSeg=8,$astart=0,$afinish=360,$close=1,$fill=0){
01714 if ($r1==0){
01715 return;
01716 }
01717 if ($r2==0){
01718 $r2=$r1;
01719 }
01720 if ($nSeg<2){
01721 $nSeg=2;
01722 }
01723
01724 $astart = deg2rad((float)$astart);
01725 $afinish = deg2rad((float)$afinish);
01726 $totalAngle =$afinish-$astart;
01727
01728 $dt = $totalAngle/$nSeg;
01729 $dtm = $dt/3;
01730
01731 if ($angle != 0){
01732 $a = -1*deg2rad((float)$angle);
01733 $tmp = "\n q ";
01734 $tmp .= sprintf('%.3f',cos($a)).' '.sprintf('%.3f',(-1.0*sin($a))).' '.sprintf('%.3f',sin($a)).' '.sprintf('%.3f',cos($a)).' ';
01735 $tmp .= sprintf('%.3f',$x0).' '.sprintf('%.3f',$y0).' cm';
01736 $this->objects[$this->currentContents]['c'].= $tmp;
01737 $x0=0;
01738 $y0=0;
01739 }
01740
01741 $t1 = $astart;
01742 $a0 = $x0+$r1*cos($t1);
01743 $b0 = $y0+$r2*sin($t1);
01744 $c0 = -$r1*sin($t1);
01745 $d0 = $r2*cos($t1);
01746
01747 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3f',$a0).' '.sprintf('%.3f',$b0).' m ';
01748 for ($i=1;$i<=$nSeg;$i++){
01749
01750 $t1 = $i*$dt+$astart;
01751 $a1 = $x0+$r1*cos($t1);
01752 $b1 = $y0+$r2*sin($t1);
01753 $c1 = -$r1*sin($t1);
01754 $d1 = $r2*cos($t1);
01755 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3f',($a0+$c0*$dtm)).' '.sprintf('%.3f',($b0+$d0*$dtm));
01756 $this->objects[$this->currentContents]['c'].= ' '.sprintf('%.3f',($a1-$c1*$dtm)).' '.sprintf('%.3f',($b1-$d1*$dtm)).' '.sprintf('%.3f',$a1).' '.sprintf('%.3f',$b1).' c';
01757 $a0=$a1;
01758 $b0=$b1;
01759 $c0=$c1;
01760 $d0=$d1;
01761 }
01762 if ($fill){
01763 $this->objects[$this->currentContents]['c'].=' f';
01764 } else {
01765 if ($close){
01766 $this->objects[$this->currentContents]['c'].=' s';
01767 } else {
01768 $this->objects[$this->currentContents]['c'].=' S';
01769 }
01770 }
01771 if ($angle !=0){
01772 $this->objects[$this->currentContents]['c'].=' Q';
01773 }
01774 }
01775
01789 function setLineStyle($width=1,$cap='',$join='',$dash='',$phase=0){
01790
01791
01792 $string = '';
01793 if ($width>0){
01794 $string.= $width.' w';
01795 }
01796 $ca = array('butt'=>0,'round'=>1,'square'=>2);
01797 if (isset($ca[$cap])){
01798 $string.= ' '.$ca[$cap].' J';
01799 }
01800 $ja = array('miter'=>0,'round'=>1,'bevel'=>2);
01801 if (isset($ja[$join])){
01802 $string.= ' '.$ja[$join].' j';
01803 }
01804 if (is_array($dash)){
01805 $string.= ' [';
01806 foreach ($dash as $len){
01807 $string.=' '.$len;
01808 }
01809 $string.= ' ] '.$phase.' d';
01810 }
01811 $this->currentLineStyle = $string;
01812 $this->objects[$this->currentContents]['c'].="\n".$string;
01813 }
01814
01818 function polygon($p,$np,$f=0){
01819 $this->objects[$this->currentContents]['c'].="\n";
01820 $this->objects[$this->currentContents]['c'].=sprintf('%.3f',$p[0]).' '.sprintf('%.3f',$p[1]).' m ';
01821 for ($i=2;$i<$np*2;$i=$i+2){
01822 $this->objects[$this->currentContents]['c'].= sprintf('%.3f',$p[$i]).' '.sprintf('%.3f',$p[$i+1]).' l ';
01823 }
01824 if ($f==1){
01825 $this->objects[$this->currentContents]['c'].=' f';
01826 } else {
01827 $this->objects[$this->currentContents]['c'].=' S';
01828 }
01829 }
01830
01835 function filledRectangle($x1,$y1,$width,$height){
01836 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3f',$x1).' '.sprintf('%.3f',$y1).' '.sprintf('%.3f',$width).' '.sprintf('%.3f',$height).' re f';
01837 }
01838
01843 function rectangle($x1,$y1,$width,$height){
01844 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3f',$x1).' '.sprintf('%.3f',$y1).' '.sprintf('%.3f',$width).' '.sprintf('%.3f',$height).' re S';
01845 }
01846
01851 function newPage($insert=0,$id=0,$pos='after'){
01852
01853
01854
01855
01856 if ($this->nStateStack){
01857 for ($i=$this->nStateStack;$i>=1;$i--){
01858 $this->restoreState($i);
01859 }
01860 }
01861
01862 $this->numObj++;
01863 if ($insert){
01864
01865
01866 $rid = $this->objects[$id]['onPage'];
01867 $opt= array('rid'=>$rid,'pos'=>$pos);
01868 $this->o_page($this->numObj,'new',$opt);
01869 } else {
01870 $this->o_page($this->numObj,'new');
01871 }
01872
01873 if ($this->nStateStack){
01874 for ($i=1;$i<=$this->nStateStack;$i++){
01875 $this->saveState($i);
01876 }
01877 }
01878
01879 if ($this->currentColour['r']>=0){
01880 $this->setColor($this->currentColour['r'],$this->currentColour['g'],$this->currentColour['b'],1);
01881 }
01882 if ($this->currentStrokeColour['r']>=0){
01883 $this->setStrokeColor($this->currentStrokeColour['r'],$this->currentStrokeColour['g'],$this->currentStrokeColour['b'],1);
01884 }
01885
01886
01887 if (strlen($this->currentLineStyle)){
01888 $this->objects[$this->currentContents]['c'].="\n".$this->currentLineStyle;
01889 }
01890
01891
01892 return $this->currentContents;
01893 }
01894
01899 function stream($options=''){
01900
01901
01902
01903
01904
01905
01906
01907
01908 if (!is_array($options)){
01909 $options=array();
01910 }
01911 if ( isset($options['compress']) && $options['compress']==0){
01912 $tmp = $this->output(1);
01913 } else {
01914 $tmp = $this->output();
01915 }
01916 header("Content-type: application/pdf");
01917 header("Content-Length: ".strlen(ltrim($tmp)));
01918 $fileName = (isset($options['Content-Disposition'])?$options['Content-Disposition']:'file.pdf');
01919 header("Content-Disposition: inline; filename=".$fileName);
01920 if (isset($options['Accept-Ranges']) && $options['Accept-Ranges']==1){
01921 header("Accept-Ranges: ".strlen(ltrim($tmp)));
01922 }
01923 echo ltrim($tmp);
01924 }
01925
01929 function getFontHeight($size){
01930 if (!$this->numFonts){
01931 $this->selectFont('./fonts/Helvetica');
01932 }
01933
01934 $h = $this->fonts[$this->currentFont]['FontBBox'][3]-$this->fonts[$this->currentFont]['FontBBox'][1];
01935 return $size*$h/1000;
01936 }
01937
01943 function getFontDecender($size){
01944
01945 if (!$this->numFonts){
01946 $this->selectFont('./fonts/Helvetica');
01947 }
01948 $h = $this->fonts[$this->currentFont]['FontBBox'][1];
01949 return $size*$h/1000;
01950 }
01951
01958 function filterText($text){
01959 $text = str_replace('\\','\\\\',$text);
01960 $text = str_replace('(','\(',$text);
01961 $text = str_replace(')','\)',$text);
01962 $text = str_replace('<','<',$text);
01963 $text = str_replace('>','>',$text);
01964 $text = str_replace(''','\'',$text);
01965 $text = str_replace('"','"',$text);
01966 $text = str_replace('&','&',$text);
01967
01968 return $text;
01969 }
01970
01977 function PRVTgetTextPosition($x,$y,$angle,$size,$wa,$text){
01978 // given this information return an array containing x and y for the end position as elements 0 and 1
01979 $w = $this->getTextWidth($size,$text);
01980 // need to adjust for the number of spaces in this text
01981 $words = explode(' ',$text);
01982 $nspaces=count($words)-1;
01983 $w += $wa*$nspaces;
01984 $a = deg2rad((float)$angle);
01985 return array(cos($a)*$w+$x,-sin($a)*$w+$y);
01986 }
01987
01993 function PRVTcheckTextDirective(&$text,$i,&$f){
01994 $x=0;
01995 $y=0;
01996 return $this->PRVTcheckTextDirective1($text,$i,$f,0,$x,$y);
01997 }
01998
02008 function PRVTcheckTextDirective1(&$text,$i,&$f,$final,&$x,&$y,$size=0,$angle=0,$wordSpaceAdjust=0){
02009 $directive = 0;
02010 $j=$i;
02011 if ($text[$j]=='<'){
02012 $j++;
02013 switch($text[$j]){
02014 case '/':
02015 $j++;
02016 if (strlen($text) <= $j){
02017 return $directive;
02018 }
02019 switch($text[$j]){
02020 case 'b':
02021 case 'i':
02022 $j++;
02023 if ($text[$j]=='>'){
02024 $p = strrpos($this->currentTextState,$text[$j-1]);
02025 if ($p !== false){
02026 // then there is one to remove
02027 $this->currentTextState = substr($this->currentTextState,0,$p).substr($this->currentTextState,$p+1);
02028 }
02029 $directive=$j-$i+1;
02030 }
02031 break;
02032 case 'c':
02033 // this this might be a callback function
02034 $j++;
02035 $k = strpos($text,'>',$j);
02036 if ($k!==false && $text[$j]==':'){
02037 // then this will be treated as a callback directive
02038 $directive = $k-$i+1;
02039 $f=0;
02040 // split the remainder on colons to get the function name and the paramater
02041 $tmp = substr($text,$j+1,$k-$j-1);
02042 $b1 = strpos($tmp,':');
02043 if ($b1!==false){
02044 $func = substr($tmp,0,$b1);
02045 $parm = substr($tmp,$b1+1);
02046 } else {
02047 $func=$tmp;
02048 $parm='';
02049 }
02050 if (!isset($func) || !strlen(trim($func))){
02051 $directive=0;
02052 } else {
02053 // only call the function if this is the final call
02054 if ($final){
02055 // need to assess the text position, calculate the text width to this point
02056 // can use getTextWidth to find the text width I think
02057 $tmp = $this->PRVTgetTextPosition($x,$y,$angle,$size,$wordSpaceAdjust,substr($text,0,$i));
02058 $info = array('x'=>$tmp[0],'y'=>$tmp[1],'angle'=>$angle,'status'=>'end','p'=>$parm,'nCallback'=>$this->nCallback);
02059 $x=$tmp[0];
02060 $y=$tmp[1];
02061 $ret = $this->$func($info);
02062 if (is_array($ret)){
02063 // then the return from the callback function could set the position, to start with, later will do font colour, and font
02064 foreach($ret as $rk=>$rv){
02065 switch($rk){
02066 case 'x':
02067 case 'y':
02068 $$rk=$rv;
02069 break;
02070 }
02071 }
02072 }
02073 // also remove from to the stack
02074 // for simplicity, just take from the end, fix this another day
02075 $this->nCallback--;
02076 if ($this->nCallback<0){
02077 $this->nCallBack=0;
02078 }
02079 }
02080 }
02081 }
02082 break;
02083 }
02084 break;
02085 case 'b':
02086 case 'i':
02087 $j++;
02088 if ($text[$j]=='>'){
02089 $this->currentTextState.=$text[$j-1];
02090 $directive=$j-$i+1;
02091 }
02092 break;
02093 case 'C':
02094 $noClose=1;
02095 case 'c':
02096 // this this might be a callback function
02097 $j++;
02098 $k = strpos($text,'>',$j);
02099 if ($k!==false && $text[$j]==':'){
02100 // then this will be treated as a callback directive
02101 $directive = $k-$i+1;
02102 $f=0;
02103 // split the remainder on colons to get the function name and the paramater
02104 // $bits = explode(':',substr($text,$j+1,$k-$j-1));
02105 $tmp = substr($text,$j+1,$k-$j-1);
02106 $b1 = strpos($tmp,':');
02107 if ($b1!==false){
02108 $func = substr($tmp,0,$b1);
02109 $parm = substr($tmp,$b1+1);
02110 } else {
02111 $func=$tmp;
02112 $parm='';
02113 }
02114 if (!isset($func) || !strlen(trim($func))){
02115 $directive=0;
02116 } else {
02117 // only call the function if this is the final call, ie, the one actually doing printing, not measurement
02118 if ($final){
02119 // need to assess the text position, calculate the text width to this point
02120 // can use getTextWidth to find the text width I think
02121 // also add the text height and decender
02122 $tmp = $this->PRVTgetTextPosition($x,$y,$angle,$size,$wordSpaceAdjust,substr($text,0,$i));
02123 $info = array('x'=>$tmp[0],'y'=>$tmp[1],'angle'=>$angle,'status'=>'start','p'=>$parm,'f'=>$func,'height'=>$this->getFontHeight($size),'decender'=>$this->getFontDecender($size));
02124 $x=$tmp[0];
02125 $y=$tmp[1];
02126 if (!isset($noClose) || !$noClose){
02127 // only add to the stack if this is a small 'c', therefore is a start-stop pair
02128 $this->nCallback++;
02129 $info['nCallback']=$this->nCallback;
02130 $this->callback[$this->nCallback]=$info;
02131 }
02132 $ret = $this->$func($info);
02133 if (is_array($ret)){
02134 // then the return from the callback function could set the position, to start with, later will do font colour, and font
02135 foreach($ret as $rk=>$rv){
02136 switch($rk){
02137 case 'x':
02138 case 'y':
02139 $$rk=$rv;
02140 break;
02141 }
02142 }
02143 }
02144 }
02145 }
02146 }
02147 break;
02148 }
02149 }
02150 return $directive;
02151 }
02152
02156 function addText($x,$y,$size,$text,$angle=0,$wordSpaceAdjust=0){
02157 if (!$this->numFonts){$this->selectFont('./fonts/Helvetica');}
02158
02159 // if there are any open callbacks, then they should be called, to show the start of the line
02160 if ($this->nCallback>0){
02161 for ($i=$this->nCallback;$i>0;$i--){
02162 // call each function
02163 $info = array('x'=>$x,'y'=>$y,'angle'=>$angle,'status'=>'sol','p'=>$this->callback[$i]['p'],'nCallback'=>$this->callback[$i]['nCallback'],'height'=>$this->callback[$i]['height'],'decender'=>$this->callback[$i]['decender']);
02164 $func = $this->callback[$i]['f'];
02165 $this->$func($info);
02166 }
02167 }
02168 if ($angle==0){
02169 $this->objects[$this->currentContents]['c'].="\n".'BT '.sprintf('%.3f',$x).' '.sprintf('%.3f',$y).' Td';
02170 } else {
02171 $a = deg2rad((float)$angle);
02172 $tmp = "\n".'BT ';
02173 $tmp .= sprintf('%.3f',cos($a)).' '.sprintf('%.3f',(-1.0*sin($a))).' '.sprintf('%.3f',sin($a)).' '.sprintf('%.3f',cos($a)).' ';
02174 $tmp .= sprintf('%.3f',$x).' '.sprintf('%.3f',$y).' Tm';
02175 $this->objects[$this->currentContents]['c'] .= $tmp;
02176 }
02177 if ($wordSpaceAdjust!=0 || $wordSpaceAdjust != $this->wordSpaceAdjust){
02178 $this->wordSpaceAdjust=$wordSpaceAdjust;
02179 $this->objects[$this->currentContents]['c'].=' '.sprintf('%.3f',$wordSpaceAdjust).' Tw';
02180 }
02181 $len=strlen($text);
02182 $start=0;
02183 for ($i=0;$i<$len;$i++){
02184 $f=1;
02185 $directive = $this->PRVTcheckTextDirective($text,$i,$f);
02186 if ($directive){
02187 // then we should write what we need to
02188 if ($i>$start){
02189 $part = substr($text,$start,$i-$start);
02190 $this->objects[$this->currentContents]['c'].=' /F'.$this->currentFontNum.' '.sprintf('%.1f',$size).' Tf ';
02191 $this->objects[$this->currentContents]['c'].=' ('.$this->filterText($part).') Tj';
02192 }
02193 if ($f){
02194 // then there was nothing drastic done here, restore the contents
02195 $this->setCurrentFont();
02196 } else {
02197 $this->objects[$this->currentContents]['c'] .= ' ET';
02198 $f=1;
02199 $xp=$x;
02200 $yp=$y;
02201 $directive = $this->PRVTcheckTextDirective1($text,$i,$f,1,$xp,$yp,$size,$angle,$wordSpaceAdjust);
02202
02203 // restart the text object
02204 if ($angle==0){
02205 $this->objects[$this->currentContents]['c'].="\n".'BT '.sprintf('%.3f',$xp).' '.sprintf('%.3f',$yp).' Td';
02206 } else {
02207 $a = deg2rad((float)$angle);
02208 $tmp = "\n".'BT ';
02209 $tmp .= sprintf('%.3f',cos($a)).' '.sprintf('%.3f',(-1.0*sin($a))).' '.sprintf('%.3f',sin($a)).' '.sprintf('%.3f',cos($a)).' ';
02210 $tmp .= sprintf('%.3f',$xp).' '.sprintf('%.3f',$yp).' Tm';
02211 $this->objects[$this->currentContents]['c'] .= $tmp;
02212 }
02213 if ($wordSpaceAdjust!=0 || $wordSpaceAdjust != $this->wordSpaceAdjust){
02214 $this->wordSpaceAdjust=$wordSpaceAdjust;
02215 $this->objects[$this->currentContents]['c'].=' '.sprintf('%.3f',$wordSpaceAdjust).' Tw';
02216 }
02217 }
02218 // and move the writing point to the next piece of text
02219 $i=$i+$directive-1;
02220 $start=$i+1;
02221 }
02222
02223 }
02224 if ($start<$len){
02225 $part = substr($text,$start);
02226 $this->objects[$this->currentContents]['c'].=' /F'.$this->currentFontNum.' '.sprintf('%.1f',$size).' Tf ';
02227 $this->objects[$this->currentContents]['c'].=' ('.$this->filterText($part).') Tj';
02228 }
02229 $this->objects[$this->currentContents]['c'].=' ET';
02230
02231 // if there are any open callbacks, then they should be called, to show the end of the line
02232 if ($this->nCallback>0){
02233 for ($i=$this->nCallback;$i>0;$i--){
02234 // call each function
02235 $tmp = $this->PRVTgetTextPosition($x,$y,$angle,$size,$wordSpaceAdjust,$text);
02236 $info = array('x'=>$tmp[0],'y'=>$tmp[1],'angle'=>$angle,'status'=>'eol','p'=>$this->callback[$i]['p'],'nCallback'=>$this->callback[$i]['nCallback'],'height'=>$this->callback[$i]['height'],'decender'=>$this->callback[$i]['decender']);
02237 $func = $this->callback[$i]['f'];
02238 $this->$func($info);
02239 }
02240 }
02241
02242 }
02243
02248 function getTextWidth($size,$text){
02249 // this function should not change any of the settings, though it will need to
02250 // track any directives which change during calculation, so copy them at the start
02251 // and put them back at the end.
02252 $store_currentTextState = $this->currentTextState;
02253
02254 if (!$this->numFonts){
02255 $this->selectFont('./fonts/Helvetica');
02256 }
02257
02258 // converts a number or a float to a string so it can get the width
02259 $text = "$text";
02260
02261 // hmm, this is where it all starts to get tricky - use the font information to
02262 // calculate the width of each character, add them up and convert to user units
02263 $w=0;
02264 $len=strlen($text);
02265 $cf = $this->currentFont;
02266 for ($i=0;$i<$len;$i++){
02267 $f=1;
02268 $directive = $this->PRVTcheckTextDirective($text,$i,$f);
02269 if ($directive){
02270 if ($f){
02271 $this->setCurrentFont();
02272 $cf = $this->currentFont;
02273 }
02274 $i=$i+$directive-1;
02275 } else {
02276 $char=ord($text[$i]);
02277 if (isset($this->fonts[$cf]['differences'][$char])){
02278 // then this character is being replaced by another
02279 $name = $this->fonts[$cf]['differences'][$char];
02280 if (isset($this->fonts[$cf]['C'][$name]['WX'])){
02281 $w+=$this->fonts[$cf]['C'][$name]['WX'];
02282 }
02283 } else if (isset($this->fonts[$cf]['C'][$char]['WX'])){
02284 $w+=$this->fonts[$cf]['C'][$char]['WX'];
02285 }
02286 }
02287 }
02288
02289 $this->currentTextState = $store_currentTextState;
02290 $this->setCurrentFont();
02291
02292 return $w*$size/1000;
02293 }
02294
02300 function PRVTadjustWrapText($text,$actual,$width,&$x,&$adjust,$justification){
02301 switch ($justification){
02302 case 'left':
02303 return;
02304 break;
02305 case 'right':
02306 $x+=$width-$actual;
02307 break;
02308 case 'center':
02309 case 'centre':
02310 $x+=($width-$actual)/2;
02311 break;
02312 case 'full':
02313 // count the number of words
02314 $words = explode(' ',$text);
02315 $nspaces=count($words)-1;
02316 if ($nspaces>0){
02317 $adjust = ($width-$actual)/$nspaces;
02318 } else {
02319 $adjust=0;
02320 }
02321 break;
02322 }
02323 }
02324
02331 function addTextWrap($x,$y,$width,$size,$text,$justification='left',$angle=0,$test=0){
02332 // this will display the text, and if it goes beyond the width $width, will backtrack to the
02333 // previous space or hyphen, and return the remainder of the text.
02334
02335 // $justification can be set to 'left','right','center','centre','full'
02336
02337 // need to store the initial text state, as this will change during the width calculation
02338 // but will need to be re-set before printing, so that the chars work out right
02339 $store_currentTextState = $this->currentTextState;
02340
02341 if (!$this->numFonts){$this->selectFont('./fonts/Helvetica');}
02342 if ($width<=0){
02343 // error, pretend it printed ok, otherwise risking a loop
02344 return '';
02345 }
02346 $w=0;
02347 $break=0;
02348 $breakWidth=0;
02349 $len=strlen($text);
02350 $cf = $this->currentFont;
02351 $tw = $width/$size*1000;
02352 for ($i=0;$i<$len;$i++){
02353 $f=1;
02354 $directive = $this->PRVTcheckTextDirective($text,$i,$f);
02355 if ($directive){
02356 if ($f){
02357 $this->setCurrentFont();
02358 $cf = $this->currentFont;
02359 }
02360 $i=$i+$directive-1;
02361 } else {
02362 $cOrd = ord($text[$i]);
02363 if (isset($this->fonts[$cf]['differences'][$cOrd])){
02364 // then this character is being replaced by another
02365 $cOrd2 = $this->fonts[$cf]['differences'][$cOrd];
02366 } else {
02367 $cOrd2 = $cOrd;
02368 }
02369
02370 if (isset($this->fonts[$cf]['C'][$cOrd2]['WX'])){
02371 $w+=$this->fonts[$cf]['C'][$cOrd2]['WX'];
02372 }
02373 if ($w>$tw){
02374 // then we need to truncate this line
02375 if ($break>0){
02376 // then we have somewhere that we can split :)
02377 if ($text[$break]==' '){
02378 $tmp = substr($text,0,$break);
02379 } else {
02380 $tmp = substr($text,0,$break+1);
02381 }
02382 $adjust=0;
02383 $this->PRVTadjustWrapText($tmp,$breakWidth,$width,$x,$adjust,$justification);
02384
02385 // reset the text state
02386 $this->currentTextState = $store_currentTextState;
02387 $this->setCurrentFont();
02388 if (!$test){
02389 $this->addText($x,$y,$size,$tmp,$angle,$adjust);
02390 }
02391 return substr($text,$break+1);
02392 } else {
02393 // just split before the current character
02394 $tmp = substr($text,0,$i);
02395 $adjust=0;
02396 $ctmp=ord($text[$i]);
02397 if (isset($this->fonts[$cf]['differences'][$ctmp])){
02398 $ctmp=$this->fonts[$cf]['differences'][$ctmp];
02399 }
02400 $tmpw=($w-$this->fonts[$cf]['C'][$ctmp]['WX'])*$size/1000;
02401 $this->PRVTadjustWrapText($tmp,$tmpw,$width,$x,$adjust,$justification);
02402 // reset the text state
02403 $this->currentTextState = $store_currentTextState;
02404 $this->setCurrentFont();
02405 if (!$test){
02406 $this->addText($x,$y,$size,$tmp,$angle,$adjust);
02407 }
02408 return substr($text,$i);
02409 }
02410 }
02411 if ($text[$i]=='-'){
02412 $break=$i;
02413 $breakWidth = $w*$size/1000;
02414 }
02415 if ($text[$i]==' '){
02416 $break=$i;
02417 $ctmp=ord($text[$i]);
02418 if (isset($this->fonts[$cf]['differences'][$ctmp])){
02419 $ctmp=$this->fonts[$cf]['differences'][$ctmp];
02420 }
02421 $breakWidth = ($w-$this->fonts[$cf]['C'][$ctmp]['WX'])*$size/1000;
02422 }
02423 }
02424 }
02425 // then there was no need to break this line
02426 if ($justification=='full'){
02427 $justification='left';
02428 }
02429 $adjust=0;
02430 $tmpw=$w*$size/1000;
02431 $this->PRVTadjustWrapText($text,$tmpw,$width,$x,$adjust,$justification);
02432 // reset the text state
02433 $this->currentTextState = $store_currentTextState;
02434 $this->setCurrentFont();
02435 if (!$test){
02436 $this->addText($x,$y,$size,$text,$angle,$adjust,$angle);
02437 }
02438 return '';
02439 }
02440
02447 function saveState($pageEnd=0){
02448 if ($pageEnd){
02449 // this will be called at a new page to return the state to what it was on the
02450 // end of the previous page, before the stack was closed down
02451 // This is to get around not being able to have open 'q' across pages
02452 $opt = $this->stateStack[$pageEnd]; // ok to use this as stack starts numbering at 1
02453 $this->setColor($opt['col']['r'],$opt['col']['g'],$opt['col']['b'],1);
02454 $this->setStrokeColor($opt['str']['r'],$opt['str']['g'],$opt['str']['b'],1);
02455 $this->objects[$this->currentContents]['c'].="\n".$opt['lin'];
02456 // $this->currentLineStyle = $opt['lin'];
02457 } else {
02458 $this->nStateStack++;
02459 $this->stateStack[$this->nStateStack]=array(
02460 'col'=>$this->currentColour
02461 ,'str'=>$this->currentStrokeColour
02462 ,'lin'=>$this->currentLineStyle
02463 );
02464 }
02465 $this->objects[$this->currentContents]['c'].="\nq";
02466 }
02467
02471 function restoreState($pageEnd=0){
02472 if (!$pageEnd){
02473 $n = $this->nStateStack;
02474 $this->currentColour = $this->stateStack[$n]['col'];
02475 $this->currentStrokeColour = $this->stateStack[$n]['str'];
02476 $this->objects[$this->currentContents]['c'].="\n".$this->stateStack[$n]['lin'];
02477 $this->currentLineStyle = $this->stateStack[$n]['lin'];
02478 unset($this->stateStack[$n]);
02479 $this->nStateStack--;
02480 }
02481 $this->objects[$this->currentContents]['c'].="\nQ";
02482 }
02483
02490 function openObject(){
02491 $this->nStack++;
02492 $this->stack[$this->nStack]=array('c'=>$this->currentContents,'p'=>$this->currentPage);
02493 // add a new object of the content type, to hold the data flow
02494 $this->numObj++;
02495 $this->o_contents($this->numObj,'new');
02496 $this->currentContents=$this->numObj;
02497 $this->looseObjects[$this->numObj]=1;
02498
02499 return $this->numObj;
02500 }
02501
02505 function reopenObject($id){
02506 $this->nStack++;
02507 $this->stack[$this->nStack]=array('c'=>$this->currentContents,'p'=>$this->currentPage);
02508 $this->currentContents=$id;
02509 // also if this object is the primary contents for a page, then set the current page to its parent
02510 if (isset($this->objects[$id]['onPage'])){
02511 $this->currentPage = $this->objects[$id]['onPage'];
02512 }
02513 }
02514
02518 function closeObject(){
02519 // close the object, as long as there was one open in the first place, which will be indicated by
02520 // an objectId on the stack.
02521 if ($this->nStack>0){
02522 $this->currentContents=$this->stack[$this->nStack]['c'];
02523 $this->currentPage=$this->stack[$this->nStack]['p'];
02524 $this->nStack--;
02525 // easier to probably not worry about removing the old entries, they will be overwritten
02526 // if there are new ones.
02527 }
02528 }
02529
02533 function stopObject($id){
02534 // if an object has been appearing on pages up to now, then stop it, this page will
02535 // be the last one that could contian it.
02536 if (isset($this->addLooseObjects[$id])){
02537 $this->addLooseObjects[$id]='';
02538 }
02539 }
02540
02544 function addObject($id,$options='add'){
02545 // add the specified object to the page
02546 if (isset($this->looseObjects[$id]) && $this->currentContents!=$id){
02547 // then it is a valid object, and it is not being added to itself
02548 switch($options){
02549 case 'all':
02550 // then this object is to be added to this page (done in the next block) and
02551 // all future new pages.
02552 $this->addLooseObjects[$id]='all';
02553 case 'add':
02554 if (isset($this->objects[$this->currentContents]['onPage'])){
02555 // then the destination contents is the primary for the page
02556 // (though this object is actually added to that page)
02557 $this->o_page($this->objects[$this->currentContents]['onPage'],'content',$id);
02558 }
02559 break;
02560 case 'even':
02561 $this->addLooseObjects[$id]='even';
02562 $pageObjectId=$this->objects[$this->currentContents]['onPage'];
02563 if ($this->objects[$pageObjectId]['info']['pageNum']%2==0){
02564 $this->addObject($id); // hacky huh :)
02565 }
02566 break;
02567 case 'odd':
02568 $this->addLooseObjects[$id]='odd';
02569 $pageObjectId=$this->objects[$this->currentContents]['onPage'];
02570 if ($this->objects[$pageObjectId]['info']['pageNum']%2==1){
02571 $this->addObject($id); // hacky huh :)
02572 }
02573 break;
02574 case 'next':
02575 $this->addLooseObjects[$id]='all';
02576 break;
02577 case 'nexteven':
02578 $this->addLooseObjects[$id]='even';
02579 break;
02580 case 'nextodd':
02581 $this->addLooseObjects[$id]='odd';
02582 break;
02583 }
02584 }
02585 }
02586
02590 function addInfo($label,$value=0){
02591 // this will only work if the label is one of the valid ones.
02592 // modify this so that arrays can be passed as well.
02593 // if $label is an array then assume that it is key=>value pairs
02594 // else assume that they are both scalar, anything else will probably error
02595 if (is_array($label)){
02596 foreach ($label as $l=>$v){
02597 $this->o_info($this->infoObject,$l,$v);
02598 }
02599 } else {
02600 $this->o_info($this->infoObject,$label,$value);
02601 }
02602 }
02603
02607 function setPreferences($label,$value=0){
02608 // this will only work if the label is one of the valid ones.
02609 if (is_array($label)){
02610 foreach ($label as $l=>$v){
02611 $this->o_catalog($this->catalogId,'viewerPreferences',array($l=>$v));
02612 }
02613 } else {
02614 $this->o_catalog($this->catalogId,'viewerPreferences',array($label=>$value));
02615 }
02616 }
02617
02623 function PRVT_getBytes(&$data,$pos,$num){
02624 // return the integer represented by $num bytes from $pos within $data
02625 $ret=0;
02626 for ($i=0;$i<$num;$i++){
02627 $ret=$ret*256;
02628 $ret+=ord($data[$pos+$i]);
02629 }
02630 return $ret;
02631 }
02632
02637 function addPngFromFile($file,$x,$y,$w=0,$h=0){
02638 // read in a png file, interpret it, then add to the system
02639 $error=0;
02640 $tmp = get_magic_quotes_runtime();
02641 set_magic_quotes_runtime(0);
02642 $fp = @fopen($file,'rb');
02643 if ($fp){
02644 $data='';
02645 while(!feof($fp)){
02646 $data .= fread($fp,1024);
02647 }
02648 fclose($fp);
02649 } else {
02650 $error = 1;
02651 $errormsg = 'trouble opening file: '.$file;
02652 }
02653 set_magic_quotes_runtime($tmp);
02654
02655 if (!$error){
02656 $header = chr(137).chr(80).chr(78).chr(71).chr(13).chr(10).chr(26).chr(10);
02657 if (substr($data,0,8)!=$header){
02658 $error=1;
02659 $errormsg = 'this file does not have a valid header';
02660 }
02661 }
02662
02663 if (!$error){
02664 // set pointer
02665 $p = 8;
02666 $len = strlen($data);
02667 // cycle through the file, identifying chunks
02668 $haveHeader=0;
02669 $info=array();
02670 $idata='';
02671 $pdata='';
02672 while ($p<$len){
02673 $chunkLen = $this->PRVT_getBytes($data,$p,4);
02674 $chunkType = substr($data,$p+4,4);
02675 // echo $chunkType.' - '.$chunkLen.'<br>';
02676
02677 switch($chunkType){
02678 case 'IHDR':
02679 // this is where all the file information comes from
02680 $info['width']=$this->PRVT_getBytes($data,$p+8,4);
02681 $info['height']=$this->PRVT_getBytes($data,$p+12,4);
02682 $info['bitDepth']=ord($data[$p+16]);
02683 $info['colorType']=ord($data[$p+17]);
02684 $info['compressionMethod']=ord($data[$p+18]);
02685 $info['filterMethod']=ord($data[$p+19]);
02686 $info['interlaceMethod']=ord($data[$p+20]);
02687 //print_r($info);
02688 $haveHeader=1;
02689 if ($info['compressionMethod']!=0){
02690 $error=1;
02691 $errormsg = 'unsupported compression method';
02692 }
02693 if ($info['filterMethod']!=0){
02694 $error=1;
02695 $errormsg = 'unsupported filter method';
02696 }
02697 break;
02698 case 'PLTE':
02699 $pdata.=substr($data,$p+8,$chunkLen);
02700 break;
02701 case 'IDAT':
02702 $idata.=substr($data,$p+8,$chunkLen);
02703 break;
02704 case 'tRNS':
02705 //this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
02706 //print "tRNS found, color type = ".$info['colorType']."<BR>";
02707 $transparency = array();
02708 if ($info['colorType'] == 3) { // indexed color, rbg
02709 /* corresponding to entries in the plte chunk
02710 Alpha for palette index 0: 1 byte
02711 Alpha for palette index 1: 1 byte
02712 ...etc...
02713 */
02714 // there will be one entry for each palette entry. up until the last non-opaque entry.
02715 // set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
02716 $transparency['type']='indexed';
02717 $numPalette = strlen($pdata)/3;
02718 $trans=0;
02719 for ($i=$chunkLen;$i>=0;$i--){
02720 if (ord($data[$p+8+$i])==0){
02721 $trans=$i;
02722 }
02723 }
02724 $transparency['data'] = $trans;
02725
02726 } elseif($info['colorType'] == 0) { // grayscale
02727 /* corresponding to entries in the plte chunk
02728 Gray: 2 bytes, range 0 .. (2^bitdepth)-1
02729 */
02730 // $transparency['grayscale']=$this->PRVT_getBytes($data,$p+8,2); // g = grayscale
02731 $transparency['type']='indexed';
02732 $transparency['data'] = ord($data[$p+8+1]);
02733
02734 } elseif($info['colorType'] == 2) { // truecolor
02735 /* corresponding to entries in the plte chunk
02736 Red: 2 bytes, range 0 .. (2^bitdepth)-1
02737 Green: 2 bytes, range 0 .. (2^bitdepth)-1
02738 Blue: 2 bytes, range 0 .. (2^bitdepth)-1
02739 */
02740 $transparency['r']=$this->PRVT_getBytes($data,$p+8,2); // r from truecolor
02741 $transparency['g']=$this->PRVT_getBytes($data,$p+10,2); // g from truecolor
02742 $transparency['b']=$this->PRVT_getBytes($data,$p+12,2); // b from truecolor
02743
02744 } else {
02745 //unsupported transparency type
02746 }
02747 // KS End new code
02748 break;
02749 default:
02750 break;
02751 }
02752
02753 $p += $chunkLen+12;
02754 }
02755
02756 if(!$haveHeader){
02757 $error = 1;
02758 $errormsg = 'information header is missing';
02759 }
02760 if (isset($info['interlaceMethod']) && $info['interlaceMethod']){
02761 $error = 1;
02762 $errormsg = 'There appears to be no support for interlaced images in pdf.';
02763 }
02764 }
02765
02766 if (!$error && $info['bitDepth'] > 8){
02767 $error = 1;
02768 $errormsg = 'only bit depth of 8 or less is supported';
02769 }
02770
02771 if (!$error){
02772 if ($info['colorType']!=2 && $info['colorType']!=0 && $info['colorType']!=3){
02773 $error = 1;
02774 $errormsg = 'transparancey alpha channel not supported, transparency only supported for palette images.';
02775 } else {
02776 switch ($info['colorType']){
02777 case 3:
02778 $color = 'DeviceRGB';
02779 $ncolor=1;
02780 break;
02781 case 2:
02782 $color = 'DeviceRGB';
02783 $ncolor=3;
02784 break;
02785 case 0:
02786 $color = 'DeviceGray';
02787 $ncolor=1;
02788 break;
02789 }
02790 }
02791 }
02792 if ($error){
02793 $this->addMessage('PNG error - ('.$file.') '.$errormsg);
02794 return;
02795 }
02796 if ($w==0){
02797 $w=$h/$info['height']*$info['width'];
02798 }
02799 if ($h==0){
02800 $h=$w*$info['height']/$info['width'];
02801 }
02802 //print_r($info);
02803 // so this image is ok... add it in.
02804 $this->numImages++;
02805 $im=$this->numImages;
02806 $label='I'.$im;
02807 $this->numObj++;
02808 // $this->o_image($this->numObj,'new',array('label'=>$label,'data'=>$idata,'iw'=>$w,'ih'=>$h,'type'=>'png','ic'=>$info['width']));
02809 $options = array('label'=>$label,'data'=>$idata,'bitsPerComponent'=>$info['bitDepth'],'pdata'=>$pdata
02810 ,'iw'=>$info['width'],'ih'=>$info['height'],'type'=>'png','color'=>$color,'ncolor'=>$ncolor);
02811 if (isset($transparency)){
02812 $options['transparency']=$transparency;
02813 }
02814 $this->o_image($this->numObj,'new',$options);
02815
02816 $this->objects[$this->currentContents]['c'].="\nq";
02817 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3f',$w)." 0 0 ".sprintf('%.3f',$h)." ".sprintf('%.3f',$x)." ".sprintf('%.3f',$y)." cm";
02818 $this->objects[$this->currentContents]['c'].="\n/".$label.' Do';
02819 $this->objects[$this->currentContents]['c'].="\nQ";
02820 }
02821
02825 function addJpegFromFile($img,$x,$y,$w=0,$h=0){
02826 // attempt to add a jpeg image straight from a file, using no GD commands
02827 // note that this function is unable to operate on a remote file.
02828
02829 if (!file_exists($img)){
02830 return;
02831 }
02832
02833 $tmp=getimagesize($img);
02834 $imageWidth=$tmp[0];
02835 $imageHeight=$tmp[1];
02836
02837 if (isset($tmp['channels'])){
02838 $channels = $tmp['channels'];
02839 } else {
02840 $channels = 3;
02841 }
02842
02843 if ($w<=0 && $h<=0){
02844 $w=$imageWidth;
02845 }
02846 if ($w==0){
02847 $w=$h/$imageHeight*$imageWidth;
02848 }
02849 if ($h==0){
02850 $h=$w*$imageHeight/$imageWidth;
02851 }
02852
02853 $fp=fopen($img,'rb');
02854
02855 $tmp = get_magic_quotes_runtime();
02856 set_magic_quotes_runtime(0);
02857 $data = fread($fp,filesize($img));
02858 set_magic_quotes_runtime($tmp);
02859
02860 fclose($fp);
02861
02862 $this->addJpegImage_common($data,$x,$y,$w,$h,$imageWidth,$imageHeight,$channels);
02863 }
02864
02870 function addImage(&$img,$x,$y,$w=0,$h=0,$quality=75){
02871 // add a new image into the current location, as an external object
02872 // add the image at $x,$y, and with width and height as defined by $w & $h
02873
02874 // note that this will only work with full colour images and makes them jpg images for display
02875 // later versions could present lossless image formats if there is interest.
02876
02877 // there seems to be some problem here in that images that have quality set above 75 do not appear
02878 // not too sure why this is, but in the meantime I have restricted this to 75.
02879 if ($quality>75){
02880 $quality=75;
02881 }
02882
02883 // if the width or height are set to zero, then set the other one based on keeping the image
02884 // height/width ratio the same, if they are both zero, then give up :)
02885 $imageWidth=imagesx($img);
02886 $imageHeight=imagesy($img);
02887
02888 if ($w<=0 && $h<=0){
02889 return;
02890 }
02891 if ($w==0){
02892 $w=$h/$imageHeight*$imageWidth;
02893 }
02894 if ($h==0){
02895 $h=$w*$imageHeight/$imageWidth;
02896 }
02897
02898 // gotta get the data out of the img..
02899
02900 // so I write to a temp file, and then read it back.. soo ugly, my apologies.
02901 $tmpDir='/tmp';
02902 $tmpName=tempnam($tmpDir,'img');
02903 imagejpeg($img,$tmpName,$quality);
02904 $fp=fopen($tmpName,'rb');
02905
02906 $tmp = get_magic_quotes_runtime();
02907 set_magic_quotes_runtime(0);
02908 $fp = @fopen($tmpName,'rb');
02909 if ($fp){
02910 $data='';
02911 while(!feof($fp)){
02912 $data .= fread($fp,1024);
02913 }
02914 fclose($fp);
02915 } else {
02916 $error = 1;
02917 $errormsg = 'trouble opening file';
02918 }
02919 // $data = fread($fp,filesize($tmpName));
02920 set_magic_quotes_runtime($tmp);
02921 // fclose($fp);
02922 unlink($tmpName);
02923 $this->addJpegImage_common($data,$x,$y,$w,$h,$imageWidth,$imageHeight);
02924 }
02925
02931 function addJpegImage_common(&$data,$x,$y,$w=0,$h=0,$imageWidth,$imageHeight,$channels=3){
02932 // note that this function is not to be called externally
02933 // it is just the common code between the GD and the file options
02934 $this->numImages++;
02935 $im=$this->numImages;
02936 $label='I'.$im;
02937 $this->numObj++;
02938 $this->o_image($this->numObj,'new',array('label'=>$label,'data'=>$data,'iw'=>$imageWidth,'ih'=>$imageHeight,'channels'=>$channels));
02939
02940 $this->objects[$this->currentContents]['c'].="\nq";
02941 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3f',$w)." 0 0 ".sprintf('%.3f',$h)." ".sprintf('%.3f',$x)." ".sprintf('%.3f',$y)." cm";
02942 $this->objects[$this->currentContents]['c'].="\n/".$label.' Do';
02943 $this->objects[$this->currentContents]['c'].="\nQ";
02944 }
02945
02949 function openHere($style,$a=0,$b=0,$c=0){
02950 // this function will open the document at a specified page, in a specified style
02951 // the values for style, and the required paramters are:
02952 // 'XYZ' left, top, zoom
02953 // 'Fit'
02954 // 'FitH' top
02955 // 'FitV' left
02956 // 'FitR' left,bottom,right
02957 // 'FitB'
02958 // 'FitBH' top
02959 // 'FitBV' left
02960 $this->numObj++;
02961 $this->o_destination($this->numObj,'new',array('page'=>$this->currentPage,'type'=>$style,'p1'=>$a,'p2'=>$b,'p3'=>$c));
02962 $id = $this->catalogId;
02963 $this->o_catalog($id,'openHere',$this->numObj);
02964 }
02965
02969 function addDestination($label,$style,$a=0,$b=0,$c=0){
02970 // associates the given label with the destination, it is done this way so that a destination can be specified after
02971 // it has been linked to
02972 // styles are the same as the 'openHere' function
02973 $this->numObj++;
02974 $this->o_destination($this->numObj,'new',array('page'=>$this->currentPage,'type'=>$style,'p1'=>$a,'p2'=>$b,'p3'=>$c));
02975 $id = $this->numObj;
02976 // store the label->idf relationship, note that this means that labels can be used only once
02977 $this->destinations["$label"]=$id;
02978 }
02979
02985 function setFontFamily($family,$options=''){
02986 if (!is_array($options)){
02987 if ($family=='init'){
02988 // set the known family groups
02989 // these font families will be used to enable bold and italic markers to be included
02990 // within text streams. html forms will be used... <b></b> <i></i>
02991 $this->fontFamilies['Helvetica.afm']=array(
02992 'b'=>'Helvetica-Bold.afm'
02993 ,'i'=>'Helvetica-Oblique.afm'
02994 ,'bi'=>'Helvetica-BoldOblique.afm'
02995 ,'ib'=>'Helvetica-BoldOblique.afm'
02996 );
02997 $this->fontFamilies['Courier.afm']=array(
02998 'b'=>'Courier-Bold.afm'
02999 ,'i'=>'Courier-Oblique.afm'
03000 ,'bi'=>'Courier-BoldOblique.afm'
03001 ,'ib'=>'Courier-BoldOblique.afm'
03002 );
03003 $this->fontFamilies['Times-Roman.afm']=array(
03004 'b'=>'Times-Bold.afm'
03005 ,'i'=>'Times-Italic.afm'
03006 ,'bi'=>'Times-BoldItalic.afm'
03007 ,'ib'=>'Times-BoldItalic.afm'
03008 );
03009 }
03010 } else {
03011 // the user is trying to set a font family
03012 // note that this can also be used to set the base ones to something else
03013 if (strlen($family)){
03014 $this->fontFamilies[$family] = $options;
03015 }
03016 }
03017 }
03018
03022 function addMessage($message){
03023 $this->messages.=$message."\n";
03024 }
03025
03029 function transaction($action){
03030 switch ($action){
03031 case 'start':
03032 // store all the data away into the checkpoint variable
03033 $data = get_object_vars($this);
03034 $this->checkpoint = $data;
03035 unset($data);
03036 break;
03037 case 'commit':
03038 if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])){
03039 $tmp = $this->checkpoint['checkpoint'];
03040 $this->checkpoint = $tmp;
03041 unset($tmp);
03042 } else {
03043 $this->checkpoint='';
03044 }
03045 break;
03046 case 'rewind':
03047 // do not destroy the current checkpoint, but move us back to the state then, so that we can try again
03048 if (is_array($this->checkpoint)){
03049 // can only abort if were inside a checkpoint
03050 $tmp = $this->checkpoint;
03051 foreach ($tmp as $k=>$v){
03052 if ($k != 'checkpoint'){
03053 $this->$k=$v;
03054 }
03055 }
03056 unset($tmp);
03057 }
03058 break;
03059 case 'abort':
03060 if (is_array($this->checkpoint)){
03061 // can only abort if were inside a checkpoint
03062 $tmp = $this->checkpoint;
03063 foreach ($tmp as $k=>$v){
03064 $this->$k=$v;
03065 }
03066 unset($tmp);
03067 }
03068 break;
03069 }
03070
03071 }
03072
03073 } // end of class
03074
03075 ?>