File modules/wikiparser/renderer/fpdf/fpdf.php

Last commit: Fri Feb 17 02:02:18 2023 +0100	Jan Dankert	Refactoring: Script-context should be the same in all environments; New: DslPdf for creating PDF with scriptbox ;)
1 <?php 2 /******************************************************************************* 3 * FPDF * 4 * * 5 * Version: 1.85 * 6 * Date: 2022-11-10 * 7 * Author: Olivier PLATHEY * 8 *******************************************************************************/ 9 10 class FPDF 11 { 12 const VERSION = '1.85'; 13 protected $page; // current page number 14 protected $n; // current object number 15 protected $offsets; // array of object offsets 16 protected $buffer; // buffer holding in-memory PDF 17 protected $pages; // array containing pages 18 protected $state; // current document state 19 protected $compress; // compression flag 20 protected $iconv; // whether iconv is available 21 protected $k; // scale factor (number of points in user unit) 22 protected $DefOrientation; // default orientation 23 protected $CurOrientation; // current orientation 24 protected $StdPageSizes; // standard page sizes 25 protected $DefPageSize; // default page size 26 protected $CurPageSize; // current page size 27 protected $CurRotation; // current page rotation 28 protected $PageInfo; // page-related data 29 protected $wPt, $hPt; // dimensions of current page in points 30 protected $w, $h; // dimensions of current page in user unit 31 protected $lMargin; // left margin 32 protected $tMargin; // top margin 33 protected $rMargin; // right margin 34 protected $bMargin; // page break margin 35 protected $cMargin; // cell margin 36 protected $x, $y; // current position in user unit 37 protected $lasth; // height of last printed cell 38 protected $LineWidth; // line width in user unit 39 protected $fontpath; // path containing fonts 40 protected $CoreFonts; // array of core font names 41 protected $fonts; // array of used fonts 42 protected $FontFiles; // array of font files 43 protected $encodings; // array of encodings 44 protected $cmaps; // array of ToUnicode CMaps 45 protected $FontFamily; // current font family 46 protected $FontStyle; // current font style 47 protected $underline; // underlining flag 48 protected $CurrentFont; // current font info 49 protected $FontSizePt; // current font size in points 50 protected $FontSize; // current font size in user unit 51 protected $DrawColor; // commands for drawing color 52 protected $FillColor; // commands for filling color 53 protected $TextColor; // commands for text color 54 protected $ColorFlag; // indicates whether fill and text colors are different 55 protected $WithAlpha; // indicates whether alpha channel is used 56 protected $ws; // word spacing 57 protected $images; // array of used images 58 protected $PageLinks; // array of links in pages 59 protected $links; // array of internal links 60 protected $AutoPageBreak; // automatic page breaking 61 protected $PageBreakTrigger; // threshold used to trigger page breaks 62 protected $InHeader; // flag set when processing header 63 protected $InFooter; // flag set when processing footer 64 protected $AliasNbPages; // alias for total number of pages 65 protected $ZoomMode; // zoom display mode 66 protected $LayoutMode; // layout display mode 67 protected $metadata; // document properties 68 protected $CreationDate; // document creation date 69 protected $PDFVersion; // PDF version number 70 71 /******************************************************************************* 72 * Public methods * 73 *******************************************************************************/ 74 75 function __construct($orientation='P', $unit='mm', $size='A4') 76 { 77 // Initialization of properties 78 $this->state = 0; 79 $this->page = 0; 80 $this->n = 2; 81 $this->buffer = ''; 82 $this->pages = array(); 83 $this->PageInfo = array(); 84 $this->fonts = array(); 85 $this->FontFiles = array(); 86 $this->encodings = array(); 87 $this->cmaps = array(); 88 $this->images = array(); 89 $this->links = array(); 90 $this->InHeader = false; 91 $this->InFooter = false; 92 $this->lasth = 0; 93 $this->FontFamily = ''; 94 $this->FontStyle = ''; 95 $this->FontSizePt = 12; 96 $this->underline = false; 97 $this->DrawColor = '0 G'; 98 $this->FillColor = '0 g'; 99 $this->TextColor = '0 g'; 100 $this->ColorFlag = false; 101 $this->WithAlpha = false; 102 $this->ws = 0; 103 $this->iconv = function_exists('iconv'); 104 // Font path 105 if(defined('FPDF_FONTPATH')) 106 { 107 $this->fontpath = FPDF_FONTPATH; 108 if(substr($this->fontpath,-1)!='/' && substr($this->fontpath,-1)!='\\') 109 $this->fontpath .= '/'; 110 } 111 elseif(is_dir(dirname(__FILE__).'/font')) 112 $this->fontpath = dirname(__FILE__).'/font/'; 113 else 114 $this->fontpath = ''; 115 // Core fonts 116 $this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats'); 117 // Scale factor 118 if($unit=='pt') 119 $this->k = 1; 120 elseif($unit=='mm') 121 $this->k = 72/25.4; 122 elseif($unit=='cm') 123 $this->k = 72/2.54; 124 elseif($unit=='in') 125 $this->k = 72; 126 else 127 $this->Error('Incorrect unit: '.$unit); 128 // Page sizes 129 $this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28), 130 'letter'=>array(612,792), 'legal'=>array(612,1008)); 131 $size = $this->_getpagesize($size); 132 $this->DefPageSize = $size; 133 $this->CurPageSize = $size; 134 // Page orientation 135 $orientation = strtolower($orientation); 136 if($orientation=='p' || $orientation=='portrait') 137 { 138 $this->DefOrientation = 'P'; 139 $this->w = $size[0]; 140 $this->h = $size[1]; 141 } 142 elseif($orientation=='l' || $orientation=='landscape') 143 { 144 $this->DefOrientation = 'L'; 145 $this->w = $size[1]; 146 $this->h = $size[0]; 147 } 148 else 149 $this->Error('Incorrect orientation: '.$orientation); 150 $this->CurOrientation = $this->DefOrientation; 151 $this->wPt = $this->w*$this->k; 152 $this->hPt = $this->h*$this->k; 153 // Page rotation 154 $this->CurRotation = 0; 155 // Page margins (1 cm) 156 $margin = 28.35/$this->k; 157 $this->SetMargins($margin,$margin); 158 // Interior cell margin (1 mm) 159 $this->cMargin = $margin/10; 160 // Line width (0.2 mm) 161 $this->LineWidth = .567/$this->k; 162 // Automatic page break 163 $this->SetAutoPageBreak(true,2*$margin); 164 // Default display mode 165 $this->SetDisplayMode('default'); 166 // Enable compression 167 $this->SetCompression(true); 168 // Metadata 169 $this->metadata = array('Producer'=>'FPDF '.self::VERSION); 170 // Set default PDF version number 171 $this->PDFVersion = '1.3'; 172 } 173 174 function SetMargins($left, $top, $right=null) 175 { 176 // Set left, top and right margins 177 $this->lMargin = $left; 178 $this->tMargin = $top; 179 if($right===null) 180 $right = $left; 181 $this->rMargin = $right; 182 } 183 184 function SetLeftMargin($margin) 185 { 186 // Set left margin 187 $this->lMargin = $margin; 188 if($this->page>0 && $this->x<$margin) 189 $this->x = $margin; 190 } 191 192 function SetTopMargin($margin) 193 { 194 // Set top margin 195 $this->tMargin = $margin; 196 } 197 198 function SetRightMargin($margin) 199 { 200 // Set right margin 201 $this->rMargin = $margin; 202 } 203 204 function SetAutoPageBreak($auto, $margin=0) 205 { 206 // Set auto page break mode and triggering margin 207 $this->AutoPageBreak = $auto; 208 $this->bMargin = $margin; 209 $this->PageBreakTrigger = $this->h-$margin; 210 } 211 212 function SetDisplayMode($zoom, $layout='default') 213 { 214 // Set display mode in viewer 215 if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom)) 216 $this->ZoomMode = $zoom; 217 else 218 $this->Error('Incorrect zoom display mode: '.$zoom); 219 if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default') 220 $this->LayoutMode = $layout; 221 else 222 $this->Error('Incorrect layout display mode: '.$layout); 223 } 224 225 function SetCompression($compress) 226 { 227 // Set page compression 228 if(function_exists('gzcompress')) 229 $this->compress = $compress; 230 else 231 $this->compress = false; 232 } 233 234 function SetTitle($title, $isUTF8=false) 235 { 236 // Title of document 237 $this->metadata['Title'] = $isUTF8 ? $title : $this->_UTF8encode($title); 238 } 239 240 function SetAuthor($author, $isUTF8=false) 241 { 242 // Author of document 243 $this->metadata['Author'] = $isUTF8 ? $author : $this->_UTF8encode($author); 244 } 245 246 function SetSubject($subject, $isUTF8=false) 247 { 248 // Subject of document 249 $this->metadata['Subject'] = $isUTF8 ? $subject : $this->_UTF8encode($subject); 250 } 251 252 function SetKeywords($keywords, $isUTF8=false) 253 { 254 // Keywords of document 255 $this->metadata['Keywords'] = $isUTF8 ? $keywords : $this->_UTF8encode($keywords); 256 } 257 258 function SetCreator($creator, $isUTF8=false) 259 { 260 // Creator of document 261 $this->metadata['Creator'] = $isUTF8 ? $creator : $this->_UTF8encode($creator); 262 } 263 264 function AliasNbPages($alias='{nb}') 265 { 266 // Define an alias for total number of pages 267 $this->AliasNbPages = $alias; 268 } 269 270 function Error($msg) 271 { 272 // Fatal error 273 throw new Exception('FPDF error: '.$msg); 274 } 275 276 function Close() 277 { 278 // Terminate document 279 if($this->state==3) 280 return; 281 if($this->page==0) 282 $this->AddPage(); 283 // Page footer 284 $this->InFooter = true; 285 $this->Footer(); 286 $this->InFooter = false; 287 // Close page 288 $this->_endpage(); 289 // Close document 290 $this->_enddoc(); 291 } 292 293 function AddPage($orientation='', $size='', $rotation=0) 294 { 295 // Start a new page 296 if($this->state==3) 297 $this->Error('The document is closed'); 298 $family = $this->FontFamily; 299 $style = $this->FontStyle.($this->underline ? 'U' : ''); 300 $fontsize = $this->FontSizePt; 301 $lw = $this->LineWidth; 302 $dc = $this->DrawColor; 303 $fc = $this->FillColor; 304 $tc = $this->TextColor; 305 $cf = $this->ColorFlag; 306 if($this->page>0) 307 { 308 // Page footer 309 $this->InFooter = true; 310 $this->Footer(); 311 $this->InFooter = false; 312 // Close page 313 $this->_endpage(); 314 } 315 // Start new page 316 $this->_beginpage($orientation,$size,$rotation); 317 // Set line cap style to square 318 $this->_out('2 J'); 319 // Set line width 320 $this->LineWidth = $lw; 321 $this->_out(sprintf('%.2F w',$lw*$this->k)); 322 // Set font 323 if($family) 324 $this->SetFont($family,$style,$fontsize); 325 // Set colors 326 $this->DrawColor = $dc; 327 if($dc!='0 G') 328 $this->_out($dc); 329 $this->FillColor = $fc; 330 if($fc!='0 g') 331 $this->_out($fc); 332 $this->TextColor = $tc; 333 $this->ColorFlag = $cf; 334 // Page header 335 $this->InHeader = true; 336 $this->Header(); 337 $this->InHeader = false; 338 // Restore line width 339 if($this->LineWidth!=$lw) 340 { 341 $this->LineWidth = $lw; 342 $this->_out(sprintf('%.2F w',$lw*$this->k)); 343 } 344 // Restore font 345 if($family) 346 $this->SetFont($family,$style,$fontsize); 347 // Restore colors 348 if($this->DrawColor!=$dc) 349 { 350 $this->DrawColor = $dc; 351 $this->_out($dc); 352 } 353 if($this->FillColor!=$fc) 354 { 355 $this->FillColor = $fc; 356 $this->_out($fc); 357 } 358 $this->TextColor = $tc; 359 $this->ColorFlag = $cf; 360 } 361 362 function Header() 363 { 364 // To be implemented in your own inherited class 365 } 366 367 function Footer() 368 { 369 // To be implemented in your own inherited class 370 } 371 372 function PageNo() 373 { 374 // Get current page number 375 return $this->page; 376 } 377 378 function SetDrawColor($r, $g=null, $b=null) 379 { 380 // Set color for all stroking operations 381 if(($r==0 && $g==0 && $b==0) || $g===null) 382 $this->DrawColor = sprintf('%.3F G',$r/255); 383 else 384 $this->DrawColor = sprintf('%.3F %.3F %.3F RG',$r/255,$g/255,$b/255); 385 if($this->page>0) 386 $this->_out($this->DrawColor); 387 } 388 389 function SetFillColor($r, $g=null, $b=null) 390 { 391 // Set color for all filling operations 392 if(($r==0 && $g==0 && $b==0) || $g===null) 393 $this->FillColor = sprintf('%.3F g',$r/255); 394 else 395 $this->FillColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255); 396 $this->ColorFlag = ($this->FillColor!=$this->TextColor); 397 if($this->page>0) 398 $this->_out($this->FillColor); 399 } 400 401 function SetTextColor($r, $g=null, $b=null) 402 { 403 // Set color for text 404 if(($r==0 && $g==0 && $b==0) || $g===null) 405 $this->TextColor = sprintf('%.3F g',$r/255); 406 else 407 $this->TextColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255); 408 $this->ColorFlag = ($this->FillColor!=$this->TextColor); 409 } 410 411 function GetStringWidth($s) 412 { 413 // Get width of a string in the current font 414 $cw = $this->CurrentFont['cw']; 415 $w = 0; 416 $s = (string)$s; 417 $l = strlen($s); 418 for($i=0;$i<$l;$i++) 419 $w += $cw[$s[$i]]; 420 return $w*$this->FontSize/1000; 421 } 422 423 function SetLineWidth($width) 424 { 425 // Set line width 426 $this->LineWidth = $width; 427 if($this->page>0) 428 $this->_out(sprintf('%.2F w',$width*$this->k)); 429 } 430 431 function Line($x1, $y1, $x2, $y2) 432 { 433 // Draw a line 434 $this->_out(sprintf('%.2F %.2F m %.2F %.2F l S',$x1*$this->k,($this->h-$y1)*$this->k,$x2*$this->k,($this->h-$y2)*$this->k)); 435 } 436 437 function Rect($x, $y, $w, $h, $style='') 438 { 439 // Draw a rectangle 440 if($style=='F') 441 $op = 'f'; 442 elseif($style=='FD' || $style=='DF') 443 $op = 'B'; 444 else 445 $op = 'S'; 446 $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op)); 447 } 448 449 function AddFont($family, $style='', $file='') 450 { 451 // Add a TrueType, OpenType or Type1 font 452 $family = strtolower($family); 453 if($file=='') 454 $file = str_replace(' ','',$family).strtolower($style).'.php'; 455 $style = strtoupper($style); 456 if($style=='IB') 457 $style = 'BI'; 458 $fontkey = $family.$style; 459 if(isset($this->fonts[$fontkey])) 460 return; 461 $info = $this->_loadfont($file); 462 $info['i'] = count($this->fonts)+1; 463 if(!empty($info['file'])) 464 { 465 // Embedded font 466 if($info['type']=='TrueType') 467 $this->FontFiles[$info['file']] = array('length1'=>$info['originalsize']); 468 else 469 $this->FontFiles[$info['file']] = array('length1'=>$info['size1'], 'length2'=>$info['size2']); 470 } 471 $this->fonts[$fontkey] = $info; 472 } 473 474 function SetFont($family, $style='', $size=0) 475 { 476 // Select a font; size given in points 477 if($family=='') 478 $family = $this->FontFamily; 479 else 480 $family = strtolower($family); 481 $style = strtoupper($style); 482 if(strpos($style,'U')!==false) 483 { 484 $this->underline = true; 485 $style = str_replace('U','',$style); 486 } 487 else 488 $this->underline = false; 489 if($style=='IB') 490 $style = 'BI'; 491 if($size==0) 492 $size = $this->FontSizePt; 493 // Test if font is already selected 494 if($this->FontFamily==$family && $this->FontStyle==$style && $this->FontSizePt==$size) 495 return; 496 // Test if font is already loaded 497 $fontkey = $family.$style; 498 if(!isset($this->fonts[$fontkey])) 499 { 500 // Test if one of the core fonts 501 if($family=='arial') 502 $family = 'helvetica'; 503 if(in_array($family,$this->CoreFonts)) 504 { 505 if($family=='symbol' || $family=='zapfdingbats') 506 $style = ''; 507 $fontkey = $family.$style; 508 if(!isset($this->fonts[$fontkey])) 509 $this->AddFont($family,$style); 510 } 511 else 512 $this->Error('Undefined font: '.$family.' '.$style); 513 } 514 // Select it 515 $this->FontFamily = $family; 516 $this->FontStyle = $style; 517 $this->FontSizePt = $size; 518 $this->FontSize = $size/$this->k; 519 $this->CurrentFont = $this->fonts[$fontkey]; 520 if($this->page>0) 521 $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); 522 } 523 524 function SetFontSize($size) 525 { 526 // Set font size in points 527 if($this->FontSizePt==$size) 528 return; 529 $this->FontSizePt = $size; 530 $this->FontSize = $size/$this->k; 531 if($this->page>0 && isset($this->CurrentFont)) 532 $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); 533 } 534 535 function AddLink() 536 { 537 // Create a new internal link 538 $n = count($this->links)+1; 539 $this->links[$n] = array(0, 0); 540 return $n; 541 } 542 543 function SetLink($link, $y=0, $page=-1) 544 { 545 // Set destination of internal link 546 if($y==-1) 547 $y = $this->y; 548 if($page==-1) 549 $page = $this->page; 550 $this->links[$link] = array($page, $y); 551 } 552 553 function Link($x, $y, $w, $h, $link) 554 { 555 // Put a link on the page 556 $this->PageLinks[$this->page][] = array($x*$this->k, $this->hPt-$y*$this->k, $w*$this->k, $h*$this->k, $link); 557 } 558 559 function Text($x, $y, $txt) 560 { 561 // Output a string 562 if(!isset($this->CurrentFont)) 563 $this->Error('No font has been set'); 564 $txt = (string)$txt; 565 $s = sprintf('BT %.2F %.2F Td (%s) Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escape($txt)); 566 if($this->underline && $txt!=='') 567 $s .= ' '.$this->_dounderline($x,$y,$txt); 568 if($this->ColorFlag) 569 $s = 'q '.$this->TextColor.' '.$s.' Q'; 570 $this->_out($s); 571 } 572 573 function AcceptPageBreak() 574 { 575 // Accept automatic page break or not 576 return $this->AutoPageBreak; 577 } 578 579 function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='') 580 { 581 // Output a cell 582 $k = $this->k; 583 if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) 584 { 585 // Automatic page break 586 $x = $this->x; 587 $ws = $this->ws; 588 if($ws>0) 589 { 590 $this->ws = 0; 591 $this->_out('0 Tw'); 592 } 593 $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation); 594 $this->x = $x; 595 if($ws>0) 596 { 597 $this->ws = $ws; 598 $this->_out(sprintf('%.3F Tw',$ws*$k)); 599 } 600 } 601 if($w==0) 602 $w = $this->w-$this->rMargin-$this->x; 603 $s = ''; 604 if($fill || $border==1) 605 { 606 if($fill) 607 $op = ($border==1) ? 'B' : 'f'; 608 else 609 $op = 'S'; 610 $s = sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op); 611 } 612 if(is_string($border)) 613 { 614 $x = $this->x; 615 $y = $this->y; 616 if(strpos($border,'L')!==false) 617 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k); 618 if(strpos($border,'T')!==false) 619 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k); 620 if(strpos($border,'R')!==false) 621 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k); 622 if(strpos($border,'B')!==false) 623 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k); 624 } 625 $txt = (string)$txt; 626 if($txt!=='') 627 { 628 if(!isset($this->CurrentFont)) 629 $this->Error('No font has been set'); 630 if($align=='R') 631 $dx = $w-$this->cMargin-$this->GetStringWidth($txt); 632 elseif($align=='C') 633 $dx = ($w-$this->GetStringWidth($txt))/2; 634 else 635 $dx = $this->cMargin; 636 if($this->ColorFlag) 637 $s .= 'q '.$this->TextColor.' '; 638 $s .= sprintf('BT %.2F %.2F Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$this->_escape($txt)); 639 if($this->underline) 640 $s .= ' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt); 641 if($this->ColorFlag) 642 $s .= ' Q'; 643 if($link) 644 $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link); 645 } 646 if($s) 647 $this->_out($s); 648 $this->lasth = $h; 649 if($ln>0) 650 { 651 // Go to next line 652 $this->y += $h; 653 if($ln==1) 654 $this->x = $this->lMargin; 655 } 656 else 657 $this->x += $w; 658 } 659 660 function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false) 661 { 662 // Output text with automatic or explicit line breaks 663 if(!isset($this->CurrentFont)) 664 $this->Error('No font has been set'); 665 $cw = $this->CurrentFont['cw']; 666 if($w==0) 667 $w = $this->w-$this->rMargin-$this->x; 668 $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; 669 $s = str_replace("\r",'',(string)$txt); 670 $nb = strlen($s); 671 if($nb>0 && $s[$nb-1]=="\n") 672 $nb--; 673 $b = 0; 674 if($border) 675 { 676 if($border==1) 677 { 678 $border = 'LTRB'; 679 $b = 'LRT'; 680 $b2 = 'LR'; 681 } 682 else 683 { 684 $b2 = ''; 685 if(strpos($border,'L')!==false) 686 $b2 .= 'L'; 687 if(strpos($border,'R')!==false) 688 $b2 .= 'R'; 689 $b = (strpos($border,'T')!==false) ? $b2.'T' : $b2; 690 } 691 } 692 $sep = -1; 693 $i = 0; 694 $j = 0; 695 $l = 0; 696 $ns = 0; 697 $nl = 1; 698 while($i<$nb) 699 { 700 // Get next character 701 $c = $s[$i]; 702 if($c=="\n") 703 { 704 // Explicit line break 705 if($this->ws>0) 706 { 707 $this->ws = 0; 708 $this->_out('0 Tw'); 709 } 710 $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); 711 $i++; 712 $sep = -1; 713 $j = $i; 714 $l = 0; 715 $ns = 0; 716 $nl++; 717 if($border && $nl==2) 718 $b = $b2; 719 continue; 720 } 721 if($c==' ') 722 { 723 $sep = $i; 724 $ls = $l; 725 $ns++; 726 } 727 $l += $cw[$c]; 728 if($l>$wmax) 729 { 730 // Automatic line break 731 if($sep==-1) 732 { 733 if($i==$j) 734 $i++; 735 if($this->ws>0) 736 { 737 $this->ws = 0; 738 $this->_out('0 Tw'); 739 } 740 $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); 741 } 742 else 743 { 744 if($align=='J') 745 { 746 $this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0; 747 $this->_out(sprintf('%.3F Tw',$this->ws*$this->k)); 748 } 749 $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill); 750 $i = $sep+1; 751 } 752 $sep = -1; 753 $j = $i; 754 $l = 0; 755 $ns = 0; 756 $nl++; 757 if($border && $nl==2) 758 $b = $b2; 759 } 760 else 761 $i++; 762 } 763 // Last chunk 764 if($this->ws>0) 765 { 766 $this->ws = 0; 767 $this->_out('0 Tw'); 768 } 769 if($border && strpos($border,'B')!==false) 770 $b .= 'B'; 771 $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); 772 $this->x = $this->lMargin; 773 } 774 775 function Write($h, $txt, $link='') 776 { 777 // Output text in flowing mode 778 if(!isset($this->CurrentFont)) 779 $this->Error('No font has been set'); 780 $cw = $this->CurrentFont['cw']; 781 $w = $this->w-$this->rMargin-$this->x; 782 $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; 783 $s = str_replace("\r",'',(string)$txt); 784 $nb = strlen($s); 785 $sep = -1; 786 $i = 0; 787 $j = 0; 788 $l = 0; 789 $nl = 1; 790 while($i<$nb) 791 { 792 // Get next character 793 $c = $s[$i]; 794 if($c=="\n") 795 { 796 // Explicit line break 797 $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link); 798 $i++; 799 $sep = -1; 800 $j = $i; 801 $l = 0; 802 if($nl==1) 803 { 804 $this->x = $this->lMargin; 805 $w = $this->w-$this->rMargin-$this->x; 806 $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; 807 } 808 $nl++; 809 continue; 810 } 811 if($c==' ') 812 $sep = $i; 813 $l += $cw[$c]; 814 if($l>$wmax) 815 { 816 // Automatic line break 817 if($sep==-1) 818 { 819 if($this->x>$this->lMargin) 820 { 821 // Move to next line 822 $this->x = $this->lMargin; 823 $this->y += $h; 824 $w = $this->w-$this->rMargin-$this->x; 825 $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; 826 $i++; 827 $nl++; 828 continue; 829 } 830 if($i==$j) 831 $i++; 832 $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link); 833 } 834 else 835 { 836 $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',false,$link); 837 $i = $sep+1; 838 } 839 $sep = -1; 840 $j = $i; 841 $l = 0; 842 if($nl==1) 843 { 844 $this->x = $this->lMargin; 845 $w = $this->w-$this->rMargin-$this->x; 846 $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; 847 } 848 $nl++; 849 } 850 else 851 $i++; 852 } 853 // Last chunk 854 if($i!=$j) 855 $this->Cell($l/1000*$this->FontSize,$h,substr($s,$j),0,0,'',false,$link); 856 } 857 858 function Ln($h=null) 859 { 860 // Line feed; default value is the last cell height 861 $this->x = $this->lMargin; 862 if($h===null) 863 $this->y += $this->lasth; 864 else 865 $this->y += $h; 866 } 867 868 function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='') 869 { 870 // Put an image on the page 871 if($file=='') 872 $this->Error('Image file name is empty'); 873 if(!isset($this->images[$file])) 874 { 875 // First use of this image, get info 876 if($type=='') 877 { 878 $pos = strrpos($file,'.'); 879 if(!$pos) 880 $this->Error('Image file has no extension and no type was specified: '.$file); 881 $type = substr($file,$pos+1); 882 } 883 $type = strtolower($type); 884 if($type=='jpeg') 885 $type = 'jpg'; 886 $mtd = '_parse'.$type; 887 if(!method_exists($this,$mtd)) 888 $this->Error('Unsupported image type: '.$type); 889 $info = $this->$mtd($file); 890 $info['i'] = count($this->images)+1; 891 $this->images[$file] = $info; 892 } 893 else 894 $info = $this->images[$file]; 895 896 // Automatic width and height calculation if needed 897 if($w==0 && $h==0) 898 { 899 // Put image at 96 dpi 900 $w = -96; 901 $h = -96; 902 } 903 if($w<0) 904 $w = -$info['w']*72/$w/$this->k; 905 if($h<0) 906 $h = -$info['h']*72/$h/$this->k; 907 if($w==0) 908 $w = $h*$info['w']/$info['h']; 909 if($h==0) 910 $h = $w*$info['h']/$info['w']; 911 912 // Flowing mode 913 if($y===null) 914 { 915 if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) 916 { 917 // Automatic page break 918 $x2 = $this->x; 919 $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation); 920 $this->x = $x2; 921 } 922 $y = $this->y; 923 $this->y += $h; 924 } 925 926 if($x===null) 927 $x = $this->x; 928 $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q',$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i'])); 929 if($link) 930 $this->Link($x,$y,$w,$h,$link); 931 } 932 933 function GetPageWidth() 934 { 935 // Get current page width 936 return $this->w; 937 } 938 939 function GetPageHeight() 940 { 941 // Get current page height 942 return $this->h; 943 } 944 945 function GetX() 946 { 947 // Get x position 948 return $this->x; 949 } 950 951 function SetX($x) 952 { 953 // Set x position 954 if($x>=0) 955 $this->x = $x; 956 else 957 $this->x = $this->w+$x; 958 } 959 960 function GetY() 961 { 962 // Get y position 963 return $this->y; 964 } 965 966 function SetY($y, $resetX=true) 967 { 968 // Set y position and optionally reset x 969 if($y>=0) 970 $this->y = $y; 971 else 972 $this->y = $this->h+$y; 973 if($resetX) 974 $this->x = $this->lMargin; 975 } 976 977 function SetXY($x, $y) 978 { 979 // Set x and y positions 980 $this->SetX($x); 981 $this->SetY($y,false); 982 } 983 984 function Output($dest='', $name='', $isUTF8=false) 985 { 986 // Output PDF to some destination 987 $this->Close(); 988 if(strlen($name)==1 && strlen($dest)!=1) 989 { 990 // Fix parameter order 991 $tmp = $dest; 992 $dest = $name; 993 $name = $tmp; 994 } 995 if($dest=='') 996 $dest = 'I'; 997 if($name=='') 998 $name = 'doc.pdf'; 999 switch(strtoupper($dest)) 1000 { 1001 case 'I': 1002 // Send to standard output 1003 $this->_checkoutput(); 1004 if(PHP_SAPI!='cli') 1005 { 1006 // We send to a browser 1007 header('Content-Type: application/pdf'); 1008 header('Content-Disposition: inline; '.$this->_httpencode('filename',$name,$isUTF8)); 1009 header('Cache-Control: private, max-age=0, must-revalidate'); 1010 header('Pragma: public'); 1011 } 1012 echo $this->buffer; 1013 break; 1014 case 'D': 1015 // Download file 1016 $this->_checkoutput(); 1017 header('Content-Type: application/pdf'); 1018 header('Content-Disposition: attachment; '.$this->_httpencode('filename',$name,$isUTF8)); 1019 header('Cache-Control: private, max-age=0, must-revalidate'); 1020 header('Pragma: public'); 1021 echo $this->buffer; 1022 break; 1023 case 'F': 1024 // Save to local file 1025 if(!file_put_contents($name,$this->buffer)) 1026 $this->Error('Unable to create output file: '.$name); 1027 break; 1028 case 'S': 1029 // Return as a string 1030 return $this->buffer; 1031 default: 1032 $this->Error('Incorrect output destination: '.$dest); 1033 } 1034 return ''; 1035 } 1036 1037 /******************************************************************************* 1038 * Protected methods * 1039 *******************************************************************************/ 1040 1041 protected function _checkoutput() 1042 { 1043 if(PHP_SAPI!='cli') 1044 { 1045 if(headers_sent($file,$line)) 1046 $this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)"); 1047 } 1048 if(ob_get_length()) 1049 { 1050 // The output buffer is not empty 1051 if(preg_match('/^(\xEF\xBB\xBF)?\s*$/',ob_get_contents())) 1052 { 1053 // It contains only a UTF-8 BOM and/or whitespace, let's clean it 1054 ob_clean(); 1055 } 1056 else 1057 $this->Error("Some data has already been output, can't send PDF file"); 1058 } 1059 } 1060 1061 protected function _getpagesize($size) 1062 { 1063 if(is_string($size)) 1064 { 1065 $size = strtolower($size); 1066 if(!isset($this->StdPageSizes[$size])) 1067 $this->Error('Unknown page size: '.$size); 1068 $a = $this->StdPageSizes[$size]; 1069 return array($a[0]/$this->k, $a[1]/$this->k); 1070 } 1071 else 1072 { 1073 if($size[0]>$size[1]) 1074 return array($size[1], $size[0]); 1075 else 1076 return $size; 1077 } 1078 } 1079 1080 protected function _beginpage($orientation, $size, $rotation) 1081 { 1082 $this->page++; 1083 $this->pages[$this->page] = ''; 1084 $this->PageLinks[$this->page] = array(); 1085 $this->state = 2; 1086 $this->x = $this->lMargin; 1087 $this->y = $this->tMargin; 1088 $this->FontFamily = ''; 1089 // Check page size and orientation 1090 if($orientation=='') 1091 $orientation = $this->DefOrientation; 1092 else 1093 $orientation = strtoupper($orientation[0]); 1094 if($size=='') 1095 $size = $this->DefPageSize; 1096 else 1097 $size = $this->_getpagesize($size); 1098 if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1]) 1099 { 1100 // New size or orientation 1101 if($orientation=='P') 1102 { 1103 $this->w = $size[0]; 1104 $this->h = $size[1]; 1105 } 1106 else 1107 { 1108 $this->w = $size[1]; 1109 $this->h = $size[0]; 1110 } 1111 $this->wPt = $this->w*$this->k; 1112 $this->hPt = $this->h*$this->k; 1113 $this->PageBreakTrigger = $this->h-$this->bMargin; 1114 $this->CurOrientation = $orientation; 1115 $this->CurPageSize = $size; 1116 } 1117 if($orientation!=$this->DefOrientation || $size[0]!=$this->DefPageSize[0] || $size[1]!=$this->DefPageSize[1]) 1118 $this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt); 1119 if($rotation!=0) 1120 { 1121 if($rotation%90!=0) 1122 $this->Error('Incorrect rotation value: '.$rotation); 1123 $this->PageInfo[$this->page]['rotation'] = $rotation; 1124 } 1125 $this->CurRotation = $rotation; 1126 } 1127 1128 protected function _endpage() 1129 { 1130 $this->state = 1; 1131 } 1132 1133 protected function _loadfont($font) 1134 { 1135 // Load a font definition file from the font directory 1136 if(strpos($font,'/')!==false || strpos($font,"\\")!==false) 1137 $this->Error('Incorrect font definition file name: '.$font); 1138 include($this->fontpath.$font); 1139 if(!isset($name)) 1140 $this->Error('Could not include font definition file'); 1141 if(isset($enc)) 1142 $enc = strtolower($enc); 1143 if(!isset($subsetted)) 1144 $subsetted = false; 1145 return get_defined_vars(); 1146 } 1147 1148 protected function _isascii($s) 1149 { 1150 // Test if string is ASCII 1151 $nb = strlen($s); 1152 for($i=0;$i<$nb;$i++) 1153 { 1154 if(ord($s[$i])>127) 1155 return false; 1156 } 1157 return true; 1158 } 1159 1160 protected function _httpencode($param, $value, $isUTF8) 1161 { 1162 // Encode HTTP header field parameter 1163 if($this->_isascii($value)) 1164 return $param.'="'.$value.'"'; 1165 if(!$isUTF8) 1166 $value = $this->_UTF8encode($value); 1167 return $param."*=UTF-8''".rawurlencode($value); 1168 } 1169 1170 protected function _UTF8encode($s) 1171 { 1172 // Convert ISO-8859-1 to UTF-8 1173 if($this->iconv) 1174 return iconv('ISO-8859-1','UTF-8',$s); 1175 $res = ''; 1176 $nb = strlen($s); 1177 for($i=0;$i<$nb;$i++) 1178 { 1179 $c = $s[$i]; 1180 $v = ord($c); 1181 if($v>=128) 1182 { 1183 $res .= chr(0xC0 | ($v >> 6)); 1184 $res .= chr(0x80 | ($v & 0x3F)); 1185 } 1186 else 1187 $res .= $c; 1188 } 1189 return $res; 1190 } 1191 1192 protected function _UTF8toUTF16($s) 1193 { 1194 // Convert UTF-8 to UTF-16BE with BOM 1195 $res = "\xFE\xFF"; 1196 if($this->iconv) 1197 return $res.iconv('UTF-8','UTF-16BE',$s); 1198 $nb = strlen($s); 1199 $i = 0; 1200 while($i<$nb) 1201 { 1202 $c1 = ord($s[$i++]); 1203 if($c1>=224) 1204 { 1205 // 3-byte character 1206 $c2 = ord($s[$i++]); 1207 $c3 = ord($s[$i++]); 1208 $res .= chr((($c1 & 0x0F)<<4) + (($c2 & 0x3C)>>2)); 1209 $res .= chr((($c2 & 0x03)<<6) + ($c3 & 0x3F)); 1210 } 1211 elseif($c1>=192) 1212 { 1213 // 2-byte character 1214 $c2 = ord($s[$i++]); 1215 $res .= chr(($c1 & 0x1C)>>2); 1216 $res .= chr((($c1 & 0x03)<<6) + ($c2 & 0x3F)); 1217 } 1218 else 1219 { 1220 // Single-byte character 1221 $res .= "\0".chr($c1); 1222 } 1223 } 1224 return $res; 1225 } 1226 1227 protected function _escape($s) 1228 { 1229 // Escape special characters 1230 if(strpos($s,'(')!==false || strpos($s,')')!==false || strpos($s,'\\')!==false || strpos($s,"\r")!==false) 1231 return str_replace(array('\\','(',')',"\r"), array('\\\\','\\(','\\)','\\r'), $s); 1232 else 1233 return $s; 1234 } 1235 1236 protected function _textstring($s) 1237 { 1238 // Format a text string 1239 if(!$this->_isascii($s)) 1240 $s = $this->_UTF8toUTF16($s); 1241 return '('.$this->_escape($s).')'; 1242 } 1243 1244 protected function _dounderline($x, $y, $txt) 1245 { 1246 // Underline text 1247 $up = $this->CurrentFont['up']; 1248 $ut = $this->CurrentFont['ut']; 1249 $w = $this->GetStringWidth($txt)+$this->ws*substr_count($txt,' '); 1250 return sprintf('%.2F %.2F %.2F %.2F re f',$x*$this->k,($this->h-($y-$up/1000*$this->FontSize))*$this->k,$w*$this->k,-$ut/1000*$this->FontSizePt); 1251 } 1252 1253 protected function _parsejpg($file) 1254 { 1255 // Extract info from a JPEG file 1256 $a = getimagesize($file); 1257 if(!$a) 1258 $this->Error('Missing or incorrect image file: '.$file); 1259 if($a[2]!=2) 1260 $this->Error('Not a JPEG file: '.$file); 1261 if(!isset($a['channels']) || $a['channels']==3) 1262 $colspace = 'DeviceRGB'; 1263 elseif($a['channels']==4) 1264 $colspace = 'DeviceCMYK'; 1265 else 1266 $colspace = 'DeviceGray'; 1267 $bpc = isset($a['bits']) ? $a['bits'] : 8; 1268 $data = file_get_contents($file); 1269 return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data); 1270 } 1271 1272 protected function _parsepng($file) 1273 { 1274 // Extract info from a PNG file 1275 $f = fopen($file,'rb'); 1276 if(!$f) 1277 $this->Error('Can\'t open image file: '.$file); 1278 $info = $this->_parsepngstream($f,$file); 1279 fclose($f); 1280 return $info; 1281 } 1282 1283 protected function _parsepngstream($f, $file) 1284 { 1285 // Check signature 1286 if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) 1287 $this->Error('Not a PNG file: '.$file); 1288 1289 // Read header chunk 1290 $this->_readstream($f,4); 1291 if($this->_readstream($f,4)!='IHDR') 1292 $this->Error('Incorrect PNG file: '.$file); 1293 $w = $this->_readint($f); 1294 $h = $this->_readint($f); 1295 $bpc = ord($this->_readstream($f,1)); 1296 if($bpc>8) 1297 $this->Error('16-bit depth not supported: '.$file); 1298 $ct = ord($this->_readstream($f,1)); 1299 if($ct==0 || $ct==4) 1300 $colspace = 'DeviceGray'; 1301 elseif($ct==2 || $ct==6) 1302 $colspace = 'DeviceRGB'; 1303 elseif($ct==3) 1304 $colspace = 'Indexed'; 1305 else 1306 $this->Error('Unknown color type: '.$file); 1307 if(ord($this->_readstream($f,1))!=0) 1308 $this->Error('Unknown compression method: '.$file); 1309 if(ord($this->_readstream($f,1))!=0) 1310 $this->Error('Unknown filter method: '.$file); 1311 if(ord($this->_readstream($f,1))!=0) 1312 $this->Error('Interlacing not supported: '.$file); 1313 $this->_readstream($f,4); 1314 $dp = '/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w; 1315 1316 // Scan chunks looking for palette, transparency and image data 1317 $pal = ''; 1318 $trns = ''; 1319 $data = ''; 1320 do 1321 { 1322 $n = $this->_readint($f); 1323 $type = $this->_readstream($f,4); 1324 if($type=='PLTE') 1325 { 1326 // Read palette 1327 $pal = $this->_readstream($f,$n); 1328 $this->_readstream($f,4); 1329 } 1330 elseif($type=='tRNS') 1331 { 1332 // Read transparency info 1333 $t = $this->_readstream($f,$n); 1334 if($ct==0) 1335 $trns = array(ord(substr($t,1,1))); 1336 elseif($ct==2) 1337 $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1))); 1338 else 1339 { 1340 $pos = strpos($t,chr(0)); 1341 if($pos!==false) 1342 $trns = array($pos); 1343 } 1344 $this->_readstream($f,4); 1345 } 1346 elseif($type=='IDAT') 1347 { 1348 // Read image data block 1349 $data .= $this->_readstream($f,$n); 1350 $this->_readstream($f,4); 1351 } 1352 elseif($type=='IEND') 1353 break; 1354 else 1355 $this->_readstream($f,$n+4); 1356 } 1357 while($n); 1358 1359 if($colspace=='Indexed' && empty($pal)) 1360 $this->Error('Missing palette in '.$file); 1361 $info = array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'dp'=>$dp, 'pal'=>$pal, 'trns'=>$trns); 1362 if($ct>=4) 1363 { 1364 // Extract alpha channel 1365 if(!function_exists('gzuncompress')) 1366 $this->Error('Zlib not available, can\'t handle alpha channel: '.$file); 1367 $data = gzuncompress($data); 1368 $color = ''; 1369 $alpha = ''; 1370 if($ct==4) 1371 { 1372 // Gray image 1373 $len = 2*$w; 1374 for($i=0;$i<$h;$i++) 1375 { 1376 $pos = (1+$len)*$i; 1377 $color .= $data[$pos]; 1378 $alpha .= $data[$pos]; 1379 $line = substr($data,$pos+1,$len); 1380 $color .= preg_replace('/(.)./s','$1',$line); 1381 $alpha .= preg_replace('/.(.)/s','$1',$line); 1382 } 1383 } 1384 else 1385 { 1386 // RGB image 1387 $len = 4*$w; 1388 for($i=0;$i<$h;$i++) 1389 { 1390 $pos = (1+$len)*$i; 1391 $color .= $data[$pos]; 1392 $alpha .= $data[$pos]; 1393 $line = substr($data,$pos+1,$len); 1394 $color .= preg_replace('/(.{3})./s','$1',$line); 1395 $alpha .= preg_replace('/.{3}(.)/s','$1',$line); 1396 } 1397 } 1398 unset($data); 1399 $data = gzcompress($color); 1400 $info['smask'] = gzcompress($alpha); 1401 $this->WithAlpha = true; 1402 if($this->PDFVersion<'1.4') 1403 $this->PDFVersion = '1.4'; 1404 } 1405 $info['data'] = $data; 1406 return $info; 1407 } 1408 1409 protected function _readstream($f, $n) 1410 { 1411 // Read n bytes from stream 1412 $res = ''; 1413 while($n>0 && !feof($f)) 1414 { 1415 $s = fread($f,$n); 1416 if($s===false) 1417 $this->Error('Error while reading stream'); 1418 $n -= strlen($s); 1419 $res .= $s; 1420 } 1421 if($n>0) 1422 $this->Error('Unexpected end of stream'); 1423 return $res; 1424 } 1425 1426 protected function _readint($f) 1427 { 1428 // Read a 4-byte integer from stream 1429 $a = unpack('Ni',$this->_readstream($f,4)); 1430 return $a['i']; 1431 } 1432 1433 protected function _parsegif($file) 1434 { 1435 // Extract info from a GIF file (via PNG conversion) 1436 if(!function_exists('imagepng')) 1437 $this->Error('GD extension is required for GIF support'); 1438 if(!function_exists('imagecreatefromgif')) 1439 $this->Error('GD has no GIF read support'); 1440 $im = imagecreatefromgif($file); 1441 if(!$im) 1442 $this->Error('Missing or incorrect image file: '.$file); 1443 imageinterlace($im,0); 1444 ob_start(); 1445 imagepng($im); 1446 $data = ob_get_clean(); 1447 imagedestroy($im); 1448 $f = fopen('php://temp','rb+'); 1449 if(!$f) 1450 $this->Error('Unable to create memory stream'); 1451 fwrite($f,$data); 1452 rewind($f); 1453 $info = $this->_parsepngstream($f,$file); 1454 fclose($f); 1455 return $info; 1456 } 1457 1458 protected function _out($s) 1459 { 1460 // Add a line to the current page 1461 if($this->state==2) 1462 $this->pages[$this->page] .= $s."\n"; 1463 elseif($this->state==0) 1464 $this->Error('No page has been added yet'); 1465 elseif($this->state==1) 1466 $this->Error('Invalid call'); 1467 elseif($this->state==3) 1468 $this->Error('The document is closed'); 1469 } 1470 1471 protected function _put($s) 1472 { 1473 // Add a line to the document 1474 $this->buffer .= $s."\n"; 1475 } 1476 1477 protected function _getoffset() 1478 { 1479 return strlen($this->buffer); 1480 } 1481 1482 protected function _newobj($n=null) 1483 { 1484 // Begin a new object 1485 if($n===null) 1486 $n = ++$this->n; 1487 $this->offsets[$n] = $this->_getoffset(); 1488 $this->_put($n.' 0 obj'); 1489 } 1490 1491 protected function _putstream($data) 1492 { 1493 $this->_put('stream'); 1494 $this->_put($data); 1495 $this->_put('endstream'); 1496 } 1497 1498 protected function _putstreamobject($data) 1499 { 1500 if($this->compress) 1501 { 1502 $entries = '/Filter /FlateDecode '; 1503 $data = gzcompress($data); 1504 } 1505 else 1506 $entries = ''; 1507 $entries .= '/Length '.strlen($data); 1508 $this->_newobj(); 1509 $this->_put('<<'.$entries.'>>'); 1510 $this->_putstream($data); 1511 $this->_put('endobj'); 1512 } 1513 1514 protected function _putlinks($n) 1515 { 1516 foreach($this->PageLinks[$n] as $pl) 1517 { 1518 $this->_newobj(); 1519 $rect = sprintf('%.2F %.2F %.2F %.2F',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]); 1520 $s = '<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] '; 1521 if(is_string($pl[4])) 1522 $s .= '/A <</S /URI /URI '.$this->_textstring($pl[4]).'>>>>'; 1523 else 1524 { 1525 $l = $this->links[$pl[4]]; 1526 if(isset($this->PageInfo[$l[0]]['size'])) 1527 $h = $this->PageInfo[$l[0]]['size'][1]; 1528 else 1529 $h = ($this->DefOrientation=='P') ? $this->DefPageSize[1]*$this->k : $this->DefPageSize[0]*$this->k; 1530 $s .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',$this->PageInfo[$l[0]]['n'],$h-$l[1]*$this->k); 1531 } 1532 $this->_put($s); 1533 $this->_put('endobj'); 1534 } 1535 } 1536 1537 protected function _putpage($n) 1538 { 1539 $this->_newobj(); 1540 $this->_put('<</Type /Page'); 1541 $this->_put('/Parent 1 0 R'); 1542 if(isset($this->PageInfo[$n]['size'])) 1543 $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageInfo[$n]['size'][0],$this->PageInfo[$n]['size'][1])); 1544 if(isset($this->PageInfo[$n]['rotation'])) 1545 $this->_put('/Rotate '.$this->PageInfo[$n]['rotation']); 1546 $this->_put('/Resources 2 0 R'); 1547 if(!empty($this->PageLinks[$n])) 1548 { 1549 $s = '/Annots ['; 1550 foreach($this->PageLinks[$n] as $pl) 1551 $s .= $pl[5].' 0 R '; 1552 $s .= ']'; 1553 $this->_put($s); 1554 } 1555 if($this->WithAlpha) 1556 $this->_put('/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>'); 1557 $this->_put('/Contents '.($this->n+1).' 0 R>>'); 1558 $this->_put('endobj'); 1559 // Page content 1560 if(!empty($this->AliasNbPages)) 1561 $this->pages[$n] = str_replace($this->AliasNbPages,$this->page,$this->pages[$n]); 1562 $this->_putstreamobject($this->pages[$n]); 1563 // Link annotations 1564 $this->_putlinks($n); 1565 } 1566 1567 protected function _putpages() 1568 { 1569 $nb = $this->page; 1570 $n = $this->n; 1571 for($i=1;$i<=$nb;$i++) 1572 { 1573 $this->PageInfo[$i]['n'] = ++$n; 1574 $n++; 1575 foreach($this->PageLinks[$i] as &$pl) 1576 $pl[5] = ++$n; 1577 unset($pl); 1578 } 1579 for($i=1;$i<=$nb;$i++) 1580 $this->_putpage($i); 1581 // Pages root 1582 $this->_newobj(1); 1583 $this->_put('<</Type /Pages'); 1584 $kids = '/Kids ['; 1585 for($i=1;$i<=$nb;$i++) 1586 $kids .= $this->PageInfo[$i]['n'].' 0 R '; 1587 $kids .= ']'; 1588 $this->_put($kids); 1589 $this->_put('/Count '.$nb); 1590 if($this->DefOrientation=='P') 1591 { 1592 $w = $this->DefPageSize[0]; 1593 $h = $this->DefPageSize[1]; 1594 } 1595 else 1596 { 1597 $w = $this->DefPageSize[1]; 1598 $h = $this->DefPageSize[0]; 1599 } 1600 $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$w*$this->k,$h*$this->k)); 1601 $this->_put('>>'); 1602 $this->_put('endobj'); 1603 } 1604 1605 protected function _putfonts() 1606 { 1607 foreach($this->FontFiles as $file=>$info) 1608 { 1609 // Font file embedding 1610 $this->_newobj(); 1611 $this->FontFiles[$file]['n'] = $this->n; 1612 $font = file_get_contents($this->fontpath.$file,true); 1613 if(!$font) 1614 $this->Error('Font file not found: '.$file); 1615 $compressed = (substr($file,-2)=='.z'); 1616 if(!$compressed && isset($info['length2'])) 1617 $font = substr($font,6,$info['length1']).substr($font,6+$info['length1']+6,$info['length2']); 1618 $this->_put('<</Length '.strlen($font)); 1619 if($compressed) 1620 $this->_put('/Filter /FlateDecode'); 1621 $this->_put('/Length1 '.$info['length1']); 1622 if(isset($info['length2'])) 1623 $this->_put('/Length2 '.$info['length2'].' /Length3 0'); 1624 $this->_put('>>'); 1625 $this->_putstream($font); 1626 $this->_put('endobj'); 1627 } 1628 foreach($this->fonts as $k=>$font) 1629 { 1630 // Encoding 1631 if(isset($font['diff'])) 1632 { 1633 if(!isset($this->encodings[$font['enc']])) 1634 { 1635 $this->_newobj(); 1636 $this->_put('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$font['diff'].']>>'); 1637 $this->_put('endobj'); 1638 $this->encodings[$font['enc']] = $this->n; 1639 } 1640 } 1641 // ToUnicode CMap 1642 if(isset($font['uv'])) 1643 { 1644 if(isset($font['enc'])) 1645 $cmapkey = $font['enc']; 1646 else 1647 $cmapkey = $font['name']; 1648 if(!isset($this->cmaps[$cmapkey])) 1649 { 1650 $cmap = $this->_tounicodecmap($font['uv']); 1651 $this->_putstreamobject($cmap); 1652 $this->cmaps[$cmapkey] = $this->n; 1653 } 1654 } 1655 // Font object 1656 $this->fonts[$k]['n'] = $this->n+1; 1657 $type = $font['type']; 1658 $name = $font['name']; 1659 if($font['subsetted']) 1660 $name = 'AAAAAA+'.$name; 1661 if($type=='Core') 1662 { 1663 // Core font 1664 $this->_newobj(); 1665 $this->_put('<</Type /Font'); 1666 $this->_put('/BaseFont /'.$name); 1667 $this->_put('/Subtype /Type1'); 1668 if($name!='Symbol' && $name!='ZapfDingbats') 1669 $this->_put('/Encoding /WinAnsiEncoding'); 1670 if(isset($font['uv'])) 1671 $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R'); 1672 $this->_put('>>'); 1673 $this->_put('endobj'); 1674 } 1675 elseif($type=='Type1' || $type=='TrueType') 1676 { 1677 // Additional Type1 or TrueType/OpenType font 1678 $this->_newobj(); 1679 $this->_put('<</Type /Font'); 1680 $this->_put('/BaseFont /'.$name); 1681 $this->_put('/Subtype /'.$type); 1682 $this->_put('/FirstChar 32 /LastChar 255'); 1683 $this->_put('/Widths '.($this->n+1).' 0 R'); 1684 $this->_put('/FontDescriptor '.($this->n+2).' 0 R'); 1685 if(isset($font['diff'])) 1686 $this->_put('/Encoding '.$this->encodings[$font['enc']].' 0 R'); 1687 else 1688 $this->_put('/Encoding /WinAnsiEncoding'); 1689 if(isset($font['uv'])) 1690 $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R'); 1691 $this->_put('>>'); 1692 $this->_put('endobj'); 1693 // Widths 1694 $this->_newobj(); 1695 $cw = $font['cw']; 1696 $s = '['; 1697 for($i=32;$i<=255;$i++) 1698 $s .= $cw[chr($i)].' '; 1699 $this->_put($s.']'); 1700 $this->_put('endobj'); 1701 // Descriptor 1702 $this->_newobj(); 1703 $s = '<</Type /FontDescriptor /FontName /'.$name; 1704 foreach($font['desc'] as $k=>$v) 1705 $s .= ' /'.$k.' '.$v; 1706 if(!empty($font['file'])) 1707 $s .= ' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; 1708 $this->_put($s.'>>'); 1709 $this->_put('endobj'); 1710 } 1711 else 1712 { 1713 // Allow for additional types 1714 $mtd = '_put'.strtolower($type); 1715 if(!method_exists($this,$mtd)) 1716 $this->Error('Unsupported font type: '.$type); 1717 $this->$mtd($font); 1718 } 1719 } 1720 } 1721 1722 protected function _tounicodecmap($uv) 1723 { 1724 $ranges = ''; 1725 $nbr = 0; 1726 $chars = ''; 1727 $nbc = 0; 1728 foreach($uv as $c=>$v) 1729 { 1730 if(is_array($v)) 1731 { 1732 $ranges .= sprintf("<%02X> <%02X> <%04X>\n",$c,$c+$v[1]-1,$v[0]); 1733 $nbr++; 1734 } 1735 else 1736 { 1737 $chars .= sprintf("<%02X> <%04X>\n",$c,$v); 1738 $nbc++; 1739 } 1740 } 1741 $s = "/CIDInit /ProcSet findresource begin\n"; 1742 $s .= "12 dict begin\n"; 1743 $s .= "begincmap\n"; 1744 $s .= "/CIDSystemInfo\n"; 1745 $s .= "<</Registry (Adobe)\n"; 1746 $s .= "/Ordering (UCS)\n"; 1747 $s .= "/Supplement 0\n"; 1748 $s .= ">> def\n"; 1749 $s .= "/CMapName /Adobe-Identity-UCS def\n"; 1750 $s .= "/CMapType 2 def\n"; 1751 $s .= "1 begincodespacerange\n"; 1752 $s .= "<00> <FF>\n"; 1753 $s .= "endcodespacerange\n"; 1754 if($nbr>0) 1755 { 1756 $s .= "$nbr beginbfrange\n"; 1757 $s .= $ranges; 1758 $s .= "endbfrange\n"; 1759 } 1760 if($nbc>0) 1761 { 1762 $s .= "$nbc beginbfchar\n"; 1763 $s .= $chars; 1764 $s .= "endbfchar\n"; 1765 } 1766 $s .= "endcmap\n"; 1767 $s .= "CMapName currentdict /CMap defineresource pop\n"; 1768 $s .= "end\n"; 1769 $s .= "end"; 1770 return $s; 1771 } 1772 1773 protected function _putimages() 1774 { 1775 foreach(array_keys($this->images) as $file) 1776 { 1777 $this->_putimage($this->images[$file]); 1778 unset($this->images[$file]['data']); 1779 unset($this->images[$file]['smask']); 1780 } 1781 } 1782 1783 protected function _putimage(&$info) 1784 { 1785 $this->_newobj(); 1786 $info['n'] = $this->n; 1787 $this->_put('<</Type /XObject'); 1788 $this->_put('/Subtype /Image'); 1789 $this->_put('/Width '.$info['w']); 1790 $this->_put('/Height '.$info['h']); 1791 if($info['cs']=='Indexed') 1792 $this->_put('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); 1793 else 1794 { 1795 $this->_put('/ColorSpace /'.$info['cs']); 1796 if($info['cs']=='DeviceCMYK') 1797 $this->_put('/Decode [1 0 1 0 1 0 1 0]'); 1798 } 1799 $this->_put('/BitsPerComponent '.$info['bpc']); 1800 if(isset($info['f'])) 1801 $this->_put('/Filter /'.$info['f']); 1802 if(isset($info['dp'])) 1803 $this->_put('/DecodeParms <<'.$info['dp'].'>>'); 1804 if(isset($info['trns']) && is_array($info['trns'])) 1805 { 1806 $trns = ''; 1807 for($i=0;$i<count($info['trns']);$i++) 1808 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' '; 1809 $this->_put('/Mask ['.$trns.']'); 1810 } 1811 if(isset($info['smask'])) 1812 $this->_put('/SMask '.($this->n+1).' 0 R'); 1813 $this->_put('/Length '.strlen($info['data']).'>>'); 1814 $this->_putstream($info['data']); 1815 $this->_put('endobj'); 1816 // Soft mask 1817 if(isset($info['smask'])) 1818 { 1819 $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w']; 1820 $smask = array('w'=>$info['w'], 'h'=>$info['h'], 'cs'=>'DeviceGray', 'bpc'=>8, 'f'=>$info['f'], 'dp'=>$dp, 'data'=>$info['smask']); 1821 $this->_putimage($smask); 1822 } 1823 // Palette 1824 if($info['cs']=='Indexed') 1825 $this->_putstreamobject($info['pal']); 1826 } 1827 1828 protected function _putxobjectdict() 1829 { 1830 foreach($this->images as $image) 1831 $this->_put('/I'.$image['i'].' '.$image['n'].' 0 R'); 1832 } 1833 1834 protected function _putresourcedict() 1835 { 1836 $this->_put('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); 1837 $this->_put('/Font <<'); 1838 foreach($this->fonts as $font) 1839 $this->_put('/F'.$font['i'].' '.$font['n'].' 0 R'); 1840 $this->_put('>>'); 1841 $this->_put('/XObject <<'); 1842 $this->_putxobjectdict(); 1843 $this->_put('>>'); 1844 } 1845 1846 protected function _putresources() 1847 { 1848 $this->_putfonts(); 1849 $this->_putimages(); 1850 // Resource dictionary 1851 $this->_newobj(2); 1852 $this->_put('<<'); 1853 $this->_putresourcedict(); 1854 $this->_put('>>'); 1855 $this->_put('endobj'); 1856 } 1857 1858 protected function _putinfo() 1859 { 1860 $date = @date('YmdHisO', $this->CreationDate); 1861 $this->metadata['CreationDate'] = 'D:'.substr($date,0,-2)."'".substr($date,-2)."'"; 1862 foreach($this->metadata as $key=>$value) 1863 $this->_put('/'.$key.' '.$this->_textstring($value)); 1864 } 1865 1866 protected function _putcatalog() 1867 { 1868 $n = $this->PageInfo[1]['n']; 1869 $this->_put('/Type /Catalog'); 1870 $this->_put('/Pages 1 0 R'); 1871 if($this->ZoomMode=='fullpage') 1872 $this->_put('/OpenAction ['.$n.' 0 R /Fit]'); 1873 elseif($this->ZoomMode=='fullwidth') 1874 $this->_put('/OpenAction ['.$n.' 0 R /FitH null]'); 1875 elseif($this->ZoomMode=='real') 1876 $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null 1]'); 1877 elseif(!is_string($this->ZoomMode)) 1878 $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null '.sprintf('%.2F',$this->ZoomMode/100).']'); 1879 if($this->LayoutMode=='single') 1880 $this->_put('/PageLayout /SinglePage'); 1881 elseif($this->LayoutMode=='continuous') 1882 $this->_put('/PageLayout /OneColumn'); 1883 elseif($this->LayoutMode=='two') 1884 $this->_put('/PageLayout /TwoColumnLeft'); 1885 } 1886 1887 protected function _putheader() 1888 { 1889 $this->_put('%PDF-'.$this->PDFVersion); 1890 } 1891 1892 protected function _puttrailer() 1893 { 1894 $this->_put('/Size '.($this->n+1)); 1895 $this->_put('/Root '.$this->n.' 0 R'); 1896 $this->_put('/Info '.($this->n-1).' 0 R'); 1897 } 1898 1899 protected function _enddoc() 1900 { 1901 $this->_putheader(); 1902 $this->_putpages(); 1903 $this->_putresources(); 1904 // Info 1905 $this->_newobj(); 1906 $this->_put('<<'); 1907 $this->_putinfo(); 1908 $this->_put('>>'); 1909 $this->_put('endobj'); 1910 // Catalog 1911 $this->_newobj(); 1912 $this->_put('<<'); 1913 $this->_putcatalog(); 1914 $this->_put('>>'); 1915 $this->_put('endobj'); 1916 // Cross-ref 1917 $offset = $this->_getoffset(); 1918 $this->_put('xref'); 1919 $this->_put('0 '.($this->n+1)); 1920 $this->_put('0000000000 65535 f '); 1921 for($i=1;$i<=$this->n;$i++) 1922 $this->_put(sprintf('%010d 00000 n ',$this->offsets[$i])); 1923 // Trailer 1924 $this->_put('trailer'); 1925 $this->_put('<<'); 1926 $this->_puttrailer(); 1927 $this->_put('>>'); 1928 $this->_put('startxref'); 1929 $this->_put($offset); 1930 $this->_put('%%EOF'); 1931 $this->state = 3; 1932 $this->CreationDate = time(); 1933 } 1934 } 1935 ?>
Download modules/wikiparser/renderer/fpdf/fpdf.php
History Fri, 17 Feb 2023 02:02:18 +0100 Jan Dankert Refactoring: Script-context should be the same in all environments; New: DslPdf for creating PDF with scriptbox ;) Sat, 22 Feb 2020 22:45:05 +0100 Jan Dankert Refactoring: Enable Autoloading, Fix namespace structure.