1<?php 2/******************************************************************************* 3 * Software: FPDF * 4 * Version: 1.53 * 5 * Date: 2004-12-31 * 6 * Author: Olivier PLATHEY * 7 * License: Freeware * 8 * * 9 * You may use, modify and redistribute this software as you wish. * 10 *******************************************************************************/ 11 12/** 13 * Heavily patched to adapt to the HTML2PS/HTML2PDF script requirements by 14 * Konstantin Bournayev (bkon@bkon.ru) 15 * 16 * Note: this FPDF variant assumes that magic_quotes_runtime are disabled; 17 * the reason is that HTML2PS/PDF explicitly disables them during pipeline 18 * processing, thus all calls to FPDF API are "safe" 19 */ 20 21if (!class_exists('FPDF')) { 22 define('FPDF_VERSION','1.53'); 23 24 /** 25 * FPDF state flags 26 */ 27 define('FPDF_STATE_UNINITIALIZED', 0); 28 define('FPDF_STATE_DOCUMENT_STARTED', 1); 29 define('FPDF_STATE_PAGE_STARTED', 2); 30 define('FPDF_STATE_COMPLETED', 3); 31 32 /** 33 * See PDF Reference 1.6 p.664 for explanation of flags specific to submit form action 34 */ 35 define('PDF_SUBMIT_FORM_HTML', 1 << 2); // 1 - HTML, 0 - FDF 36 define('PDF_SUBMIT_FORM_COORDINATES', 1 << 4); 37 38 /** 39 * See PDF Reference 1.6 p.656 for explanation of flags specific to choice fields 40 */ 41 define('PDF_FIELD_CHOICE_COMBO', 1 << 17); 42 43 /** 44 * See PDF Reference 1.6 p.573 for explanation of flag specific to annotations 45 */ 46 define('PDF_ANNOTATION_INVISIBLE', 1 << 0); 47 define('PDF_ANNOTATION_HIDDEN', 1 << 1); 48 define('PDF_ANNOTATION_PRINTABLE', 1 << 2); 49 define('PDF_ANNOTATION_NOZOOM', 1 << 3); 50 define('PDF_ANNOTATION_NOROTATE', 1 << 4); 51 define('PDF_ANNOTATION_NOVIEW', 1 << 5); 52 define('PDF_ANNOTATION_READONLY', 1 << 6); 53 define('PDF_ANNOTATION_LOCKED', 1 << 7); 54 define('PDF_ANNOTATION_TOGGLENOVIEW', 1 << 8); 55 56 /** 57 * See PDF Reference 1.6 p.653 for explanation of flags specific to text fields 58 */ 59 define('PDF_FIELD_TEXT_MULTILINE',1 << 12); 60 define('PDF_FIELD_TEXT_PASSWORD', 1 << 13); 61 define('PDF_FIELD_TEXT_FILE', 1 << 20); 62 63 /** 64 * See PDF Reference 1.6 p.663 for examplanation of flags specific to for submit actions 65 */ 66 define("PDF_FORM_SUBMIT_EXCLUDE", 1 << 0); 67 define("PDF_FORM_SUBMIT_NOVALUE", 1 << 1); 68 define("PDF_FORM_SUBMIT_EFORMAT", 1 << 2); 69 define("PDF_FORM_SUBMIT_GET", 1 << 3); 70 71 class PDFIndirectObject { 72 var $object_id; 73 var $generation_id; 74 75 function get_object_id() { 76 return $this->object_id; 77 } 78 79 function get_generation_id() { 80 return $this->generation_id; 81 } 82 83 /** 84 * Outputs the PDF indirect object to PDF file. 85 * 86 * To pervent infinite loop on circular references, this method checks 87 * if current object have been already written to the file. 88 * 89 * Note that, in general, nested objects should be written to PDF file 90 * here too; this task is accomplished by calling _out_nested method, 91 * which should be overridden by children classes. 92 * 93 * @param FPDF $handler PDF file wrapper (FPDF object) 94 * 95 * @final 96 * 97 * @see FPDF::is_object_written 98 * @see PDFIndirectObject::_out_nested 99 */ 100 function out(&$handler) { 101 if (!$handler->is_object_written($this->get_object_id())) { 102 $handler->offsets[$this->get_object_id()] = strlen($handler->buffer); 103 $handler->_out($handler->_indirect_object($this)); 104 105 $this->_out_nested($handler); 106 }; 107 } 108 109 /** 110 * Writes all nested objects to the PDF file. Should be overridden by 111 * PDFIndirectObject descendants. 112 * 113 * @param FPDF $handler PDF file wrapper (FPDF object) 114 * 115 * @see PDFIndirectObject::out 116 */ 117 function _out_nested(&$handler) { 118 return true; 119 } 120 121 function PDFIndirectObject(&$handler, 122 $object_id, 123 $generation_id) { 124 $this->object_id = $object_id; 125 $this->generation_id = $generation_id; 126 } 127 128 function pdf(&$handler) { 129 return $handler->_dictionary($this->_dict($handler)); 130 } 131 132 function _dict() { 133 return array(); 134 } 135 } 136 137 class PDFCMap extends PDFIndirectObject { 138 var $_content; 139 140 function PDFCMap($mapping, &$handler, $object_id, $generation_id) { 141 $this->PDFIndirectObject($handler, 142 $object_id, 143 $generation_id); 144 145 $num_chars = count($mapping); 146 147 $chars = ""; 148 foreach ($mapping as $code => $utf) { 149 $chars .= sprintf("<%02X> <%04X> \n", $code, $utf); 150 }; 151 152 $this->_content = <<<EOF 153/CIDInit /ProcSet findresource begin 15412 dict begin 155begincmap 156CIDSystemInfo 157<< /Registry (Adobe) 158/Ordering (UCS) /Supplement 0 >> def 159/CMapName /Adobe-Identity-UCS def 160/CMapType 2 def 1611 begincodespacerange 162<0000> <FFFF> 163endcodespacerange 164${num_chars} beginbfchar 165${chars} 166endbfchar 167endcmap CMapName currentdict /CMap defineresource pop end end 168EOF 169; 170 } 171 172 function pdf(&$handler) { 173 $dict_content = array( 174 'Length' => strlen($this->_content) 175 ); 176 177 $content = $handler->_dictionary($dict_content); 178 $content .= "\n"; 179 $content .= $handler->_stream($this->_content); 180 181 return $content; 182 } 183 } 184 185 class PDFPage extends PDFIndirectObject { 186 var $annotations; 187 var $_width; 188 var $_height; 189 190 function PDFPage(&$handler, 191 $width, 192 $height, 193 $object_id, 194 $generation_id) { 195 $this->PDFIndirectObject($handler, 196 $object_id, 197 $generation_id); 198 199 $this->set_width($width); 200 $this->set_height($height); 201 } 202 203 function add_annotation(&$annotation) { 204 $this->annotations[] =& $annotation; 205 } 206 207 function _annotations(&$handler) { 208 return $handler->_reference_array($this->annotations); 209 } 210 211 function get_height() { 212 return $this->_height; 213 } 214 215 function get_width() { 216 return $this->_width; 217 } 218 219 function set_height($height) { 220 $this->_height = $height; 221 } 222 223 function set_width($width) { 224 $this->_width = $width; 225 } 226 } 227 228 class PDFAppearanceStream extends PDFIndirectObject { 229 var $_content; 230 231 function PDFAppearanceStream(&$handler, 232 $object_id, 233 $generation_id, 234 $content) { 235 $this->PDFIndirectObject($handler, 236 $object_id, 237 $generation_id); 238 239 $this->_content = $content; 240 } 241 242 function pdf(&$handler) { 243 $dict_content = array( 244 'Type' => "/XObject", 245 'Subtype' => "/Form", 246 'FormType' => "1", 247 'BBox' => "[0 0 100 100]", 248 'Matrix' => "[1 0 0 1 0 0]", 249 'Resources'=> "2 0 R", 250 'Length' => strlen($this->_content) 251 ); 252 253 $content = $handler->_dictionary($dict_content); 254 $content .= "\n"; 255 $content .= $handler->_stream($this->_content); 256 257 return $content; 258 } 259 } 260 261 class PDFAnnotation extends PDFIndirectObject { 262 function PDFAnnotation(&$handler, 263 $object_id, 264 $generation_id) { 265 $this->PDFIndirectObject($handler, 266 $object_id, 267 $generation_id); 268 } 269 270 function _dict(&$handler) { 271 return array_merge(parent::_dict($handler), 272 array("Type" => $handler->_name("Annot"))); 273 } 274 } 275 276 class PDFRect { 277 var $x; 278 var $y; 279 var $w; 280 var $h; 281 282 function PDFRect($x,$y,$w,$h) { 283 $this->x = $x; 284 $this->y = $y; 285 $this->w = $w; 286 $this->h = $h; 287 } 288 289 function left(&$handler) { 290 return $handler->x_coord($this->x); 291 } 292 293 function right(&$handler) { 294 return $handler->x_coord($this->x+$this->w); 295 } 296 297 function top(&$handler) { 298 return $handler->y_coord($this->y); 299 } 300 301 function bottom(&$handler) { 302 return $handler->y_coord($this->y+$this->h); 303 } 304 305 function pdf(&$handler) { 306 return $handler->_array(sprintf("%.2f %.2f %.2f %.2f", 307 $this->left($handler), 308 $this->top($handler), 309 $this->right($handler), 310 $this->bottom($handler))); 311 } 312 } 313 314 class PDFAnnotationExternalLink extends PDFAnnotation { 315 var $rect; 316 var $link; 317 318 function PDFAnnotationExternalLink(&$handler, 319 $object_id, 320 $generation_id, 321 $rect, 322 $link) { 323 $this->PDFAnnotation($handler, 324 $object_id, 325 $generation_id); 326 327 $this->rect = $rect; 328 $this->link = $link; 329 } 330 331 function _dict(&$handler) { 332 return array_merge(parent::_dict($handler), 333 array( 334 'Subtype' => "/Link", 335 'Rect' => $this->rect->pdf($handler), 336 'Border' => "[0 0 0]", 337 'A' => "<</S /URI /URI ".$handler->_textstring($this->link).">>" 338 )); 339 } 340 } 341 342 class PDFAnnotationInternalLink extends PDFAnnotation { 343 var $rect; 344 var $link; 345 346 function PDFAnnotationInternalLink(&$handler, 347 $object_id, 348 $generation_id, 349 $rect, 350 $link) { 351 $this->PDFAnnotation($handler, 352 $object_id, 353 $generation_id); 354 355 $this->rect = $rect; 356 $this->link = $link; 357 } 358 359 function pdf(&$handler) { 360 if ($handler->DefOrientation=='P') { 361 $wPt=$handler->fwPt; 362 $hPt=$handler->fhPt; 363 } else { 364 $wPt=$handler->fhPt; 365 $hPt=$handler->fwPt; 366 }; 367 $l = $handler->links[$this->link]; 368 $h = $hPt; 369 370 /** 371 * Sometimes hyperlinks may refer to pages NOT present in PDF document 372 * Example: a very long frame content; it it trimmed to one page, as 373 * framesets newer take more than one frame. A link targe which should be rendered 374 * on third page without frames will be never rendered at all. 375 * 376 * In this case we should disable link at all to prevent error from appearing 377 */ 378 379 if (!isset($handler->_pages[$l[0]-1])) { 380 return ""; 381 } 382 383 $content = $handler->_dictionary(array( 384 'Type' => "/Annot", 385 'Subtype' => "/Link", 386 'Rect' => $this->rect->pdf($handler), 387 'Border' => "[0 0 0]", 388 'Dest' => sprintf("[%s /XYZ 0 %.2f null]", 389 $handler->_reference($handler->_pages[$l[0]-1]), 390 $h-$l[1]*$handler->k) 391 )); 392 return $content; 393 } 394 } 395 396 class PDFAnnotationWidget extends PDFAnnotation { 397 var $_rect; 398 399 function PDFAnnotationWidget(&$handler, 400 $object_id, 401 $generation_id, 402 $rect) { 403 $this->PDFAnnotation($handler, 404 $object_id, 405 $generation_id); 406 407 $this->_rect = $rect; 408 } 409 410 function _dict(&$handler) { 411 return array_merge(parent::_dict($handler), 412 array("Subtype" => $handler->_name("Widget"), 413 'Rect' => $this->_rect->pdf($handler))); 414 } 415 } 416 417 /** 418 * Generic PDF Form 419 */ 420 class PDFFieldGroup extends PDFIndirectObject { 421 var $_kids; 422 var $_group_name; 423 424 function PDFFieldGroup(&$handler, 425 $object_id, 426 $generation_id, 427 $group_name) { 428 $this->PDFIndirectObject($handler, 429 $object_id, 430 $generation_id); 431 432 /** 433 * Generate default group name, if needed 434 */ 435 if (is_null($group_name) || $group_name == "") { 436 $group_name = sprintf("FieldGroup%d", $this->get_object_id()); 437 }; 438 $this->_group_name = $group_name; 439 440 $this->_kids = array(); 441 } 442 443 function _check_field_name($field) { 444 /** 445 * Check if field name is empty 446 */ 447 if (trim($field->get_field_name()) == "") { 448 error_log(sprintf("Found form field with empty name")); 449 return false; 450 }; 451 452 /** 453 * Check if field name is unique inside this form! If we will not do it, 454 * some widgets may become inactive (ignored by PDF Reader) 455 */ 456 foreach ($this->_kids as $kid) { 457 if ($kid->get_field_name() == $field->get_field_name()) { 458 error_log(sprintf("Interactive form '%s' already contains field named '%s'", 459 $this->_group_name, 460 $kid->get_field_name())); 461 return false; 462 } 463 }; 464 465 return true; 466 } 467 468 function add_field(&$field) { 469 if (!$this->_check_field_name($field)) { 470 /** 471 * Field name is not unique; replace it with automatically-generated one 472 */ 473 $field->set_field_name(sprintf("%s_FieldObject%d", 474 $field->get_field_name(), 475 $field->get_object_id())); 476 }; 477 478 $this->_kids[] =& $field; 479 $field->set_parent($this); 480 } 481 482 function _dict(&$handler) { 483 return array_merge(parent::_dict($handler), 484 array("Kids" => $handler->_reference_array($this->_kids), 485 "T" => $handler->_textstring($this->_group_name))); 486 return $content; 487 } 488 489 function _out_nested(&$handler) { 490 parent::_out_nested($handler); 491 492 foreach ($this->_kids as $field) { 493 $field->out($handler); 494 } 495 } 496 } 497 498 /** 499 * Generic superclass for all PDF interactive field widgets 500 */ 501 class PDFField extends PDFAnnotationWidget { 502 /** 503 * @var string Partial field name (see PDF Specification 1.6 p.638 for explanation on "partial" and 504 * "fully qualified" field names 505 * @access private 506 */ 507 var $_field_name; 508 509 /** 510 * @var PDFFieldGroup REference to a containing form object 511 * @access private 512 */ 513 var $_parent; 514 515 function PDFField(&$handler, 516 $object_id, 517 $generation_id, 518 $rect, 519 $field_name) { 520 $this->PDFAnnotationWidget($handler, 521 $object_id, 522 $generation_id, 523 $rect); 524 525 /** 526 * Generate default field name, if needed 527 * @TODO: validate field_name contents 528 */ 529 if (is_null($field_name) || $field_name == "") { 530 $field_name = sprintf("FieldObject%d", $this->get_object_id()); 531 }; 532 533 $this->_field_name = $field_name; 534 } 535 536 function get_field_name() { 537 if ($this->_field_name) { 538 return $this->_field_name; 539 } else { 540 return sprintf("FormObject%d", $this->get_object_id()); 541 }; 542 } 543 544 function _dict(&$handler) { 545 return array_merge(parent::_dict($handler), 546 array("Parent" => $handler->_reference($this->_parent), 547 "T" => $handler->_textstring($this->get_field_name()), 548 'F' => PDF_ANNOTATION_PRINTABLE)); 549 } 550 551 function pdf(&$handler) { 552 return $handler->_dictionary($this->_dict($handler)); 553 } 554 555 function set_field_name($value) { 556 $this->_field_name = $value; 557 } 558 559 function set_parent(&$form) { 560 $this->_parent =& $form; 561 } 562 563 function get_parent() { 564 return $this->_parent; 565 } 566 } 567 568 /** 569 * Checkbox interactive form widget 570 */ 571 class PDFFieldCheckBox extends PDFField { 572 var $_value; 573 var $_appearance_on; 574 var $_appearance_off; 575 var $_checked; 576 577 function PDFFieldCheckBox(&$handler, 578 $object_id, 579 $generation_id, 580 $rect, 581 $field_name, 582 $value, 583 $checked) { 584 $this->PDFField($handler, 585 $object_id, 586 $generation_id, 587 $rect, 588 $field_name); 589 590 $this->_value = $value; 591 $this->_checked = $checked; 592 593 $this->_appearance_on = new PDFAppearanceStream($handler, 594 $handler->_generate_new_object_number(), 595 $generation_id, 596 "Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q"); 597 598 $this->_appearance_off = new PDFAppearanceStream($handler, 599 $handler->_generate_new_object_number(), 600 $generation_id, 601 "Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q"); 602 } 603 604 function _dict(&$handler) { 605 return array_merge(parent::_dict($handler), 606 array( 607 'FT' => '/Btn', 608 'Ff' => sprintf("%d", 0), 609 'TU' => "<FEFF>", 610 'MK' => "<< /CA (3) >>", 611 'DV' => $this->_checked ? $handler->_name($this->_value) : "/Off", 612 'V' => $this->_checked ? $handler->_name($this->_value) : "/Off", 613 'AP' => sprintf("<< /N << /%s %s /Off %s >> >>", 614 $this->_value, 615 $handler->_reference($this->_appearance_on), 616 $handler->_reference($this->_appearance_off)) 617 ) 618 ); 619 } 620 621 function _out_nested(&$handler) { 622 parent::_out_nested($handler); 623 624 $this->_appearance_on->out($handler); 625 $this->_appearance_off->out($handler); 626 } 627 } 628 629 class PDFFieldPushButton extends PDFField { 630 var $_appearance; 631 var $fontindex; 632 var $fontsize; 633 634 function _out_nested(&$handler) { 635 parent::_out_nested($handler); 636 637 $this->_appearance->out($handler); 638 } 639 640 function PDFFieldPushButton(&$handler, 641 $object_id, 642 $generation_id, 643 $rect, 644 $fontindex, 645 $fontsize) { 646 $this->PDFField($handler, 647 $object_id, 648 $generation_id, 649 $rect, 650 null); 651 $this->fontindex = $fontindex; 652 $this->fontsize = $fontsize; 653 654 $this->_appearance = new PDFAppearanceStream($handler, 655 $handler->_generate_new_object_number(), 656 $generation_id, 657 "Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q"); 658 } 659 660 function _action(&$handler) { 661 return "<< >>"; 662 } 663 664 function _dict(&$handler) { 665 return array_merge(parent::_dict($handler), 666 array( 667 'FT' => '/Btn', 668 'Ff' => sprintf("%d", 1 << 16), 669 'TU' => "<FEFF>", 670 'DR' => "2 0 R", 671 'DA' => sprintf("(0 0 0 rg /F%d %.2f Tf)", 672 $this->fontindex, 673 $this->fontsize), 674 'AP' => "<< /N ".$handler->_reference($this->_appearance)." >>", 675 'AA' => $this->_action($handler) 676 )); 677 } 678 } 679 680 class PDFFieldPushButtonImage extends PDFFieldPushButton { 681 var $_link; 682 683 function PDFFieldPushButtonImage(&$handler, 684 $object_id, 685 $generation_id, 686 $rect, 687 $fontindex, 688 $fontsize, 689 $field_name, 690 $value, 691 $link) { 692 $this->PDFFieldPushButton($handler, 693 $object_id, 694 $generation_id, 695 $rect, 696 $fontindex, 697 $fontsize); 698 699 $this->_link = $link; 700 $this->set_field_name($field_name); 701 } 702 703 function _action(&$handler) { 704 $action = $handler->_dictionary(array( 705 'S' => "/SubmitForm", 706 'F' => $handler->_textstring($this->_link), 707 'Fields'=> $handler->_reference_array(array($this->get_parent())), 708 'Flags' => PDF_SUBMIT_FORM_HTML | PDF_SUBMIT_FORM_COORDINATES 709 ) 710 ); 711 return $handler->_dictionary(array('U' => $action)); 712 } 713 } 714 715 class PDFFieldPushButtonSubmit extends PDFFieldPushButton { 716 var $_link; 717 var $_caption; 718 719 function PDFFieldPushButtonSubmit(&$handler, 720 $object_id, 721 $generation_id, 722 $rect, 723 $fontindex, 724 $fontsize, 725 $field_name, 726 $value, 727 $link) { 728 $this->PDFFieldPushButton($handler, 729 $object_id, 730 $generation_id, 731 $rect, 732 $fontindex, 733 $fontsize); 734 735 $this->_link = $link; 736 $this->_caption = $value; 737 $this->set_field_name($field_name); 738 } 739 740 function _action(&$handler) { 741 $action = $handler->_dictionary(array( 742 'S' => "/SubmitForm", 743 'F' => $handler->_textstring($this->_link), 744 'Fields'=> $handler->_reference_array(array($this->get_parent())), 745 'Flags' => PDF_SUBMIT_FORM_HTML 746 ) 747 ); 748 return $handler->_dictionary(array('U' => $action)); 749 } 750 } 751 752 class PDFFieldPushButtonReset extends PDFFieldPushButton { 753 function PDFFieldPushButtonReset(&$handler, 754 $object_id, 755 $generation_id, 756 $rect, 757 $fontindex, 758 $fontsize) { 759 $this->PDFFieldPushButton($handler, 760 $object_id, 761 $generation_id, 762 $rect, 763 $fontindex, 764 $fontsize); 765 } 766 767 function _action(&$handler) { 768 $action = $handler->_dictionary(array('S' => "/ResetForm")); 769 return $handler->_dictionary(array('U' => $action)); 770 } 771 } 772 773 /** 774 * Radio button inside the group. 775 * 776 * Note that radio button is not a field itself; only a group of radio buttons 777 * should have name. 778 */ 779 class PDFFieldRadio extends PDFAnnotationWidget { 780 /** 781 * @var PDFFieldRadioGroup reference to a radio button group 782 * @access private 783 */ 784 var $_parent; 785 786 /** 787 * @var String value of this radio button 788 * @access private 789 */ 790 var $_value; 791 792 var $_appearance_on; 793 var $_appearance_off; 794 795 function PDFFieldRadio(&$handler, 796 $object_id, 797 $generation_id, 798 $rect, 799 $value) { 800 $this->PDFAnnotationWidget($handler, 801 $object_id, 802 $generation_id, 803 $rect); 804 805 $this->_value = $value; 806 807 $this->_appearance_on = new PDFAppearanceStream($handler, 808 $handler->_generate_new_object_number(), 809 $generation_id, 810 "Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q"); 811 812 $this->_appearance_off = new PDFAppearanceStream($handler, 813 $handler->_generate_new_object_number(), 814 $generation_id, 815 "Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q"); 816 } 817 818 function _dict(&$handler) { 819 return array_merge(parent::_dict($handler), 820 array( 821 'MK' => "<< /CA (l) >>", 822 'Parent' => $handler->_reference($this->_parent), 823 'AP' => sprintf("<< /N << /%s %s /Off %s >> >>", 824 $this->_value, 825 $handler->_reference($this->_appearance_on), 826 $handler->_reference($this->_appearance_off)) 827 )); 828 } 829 830 function _out_nested(&$handler) { 831 parent::_out_nested($handler); 832 833 $this->_appearance_on->out($handler); 834 $this->_appearance_off->out($handler); 835 } 836 837 /** 838 * Set a reference to the radio button group containing this group 839 * 840 * @param PDFFieldRadioGroup $parent reference to a group object 841 */ 842 function set_parent(&$parent) { 843 $this->_parent =& $parent; 844 } 845 } 846 847 /** 848 * Create new group of radio buttons 849 */ 850 class PDFFieldRadioGroup extends PDFFieldGroup { 851 var $_parent; 852 var $_checked; 853 854 function _dict($handler) { 855 return array_merge(parent::_dict($handler), 856 array( 857 'DV' => $this->_checked ? $handler->_name($this->_checked) : "/Off", 858 'V' => $this->_checked ? $handler->_name($this->_checked) : "/Off", 859 "FT" => $handler->_name('Btn'), 860 "Ff" => sprintf("%d", 1 << 15), 861 "Parent" => $handler->_reference($this->_parent) 862 )); 863 } 864 865 function _check_field_name($field) { 866 /** 867 * As radio buttons always have same field name, no checking should be made here 868 */ 869 870 return true; 871 } 872 873 function PDFFieldRadioGroup(&$handler, 874 $object_id, 875 $generation_id, 876 $group_name) { 877 $this->PDFFieldGroup($handler, 878 $object_id, 879 $generation_id, 880 $group_name); 881 882 $this->_checked = null; 883 } 884 885 /** 886 * @return String name of the radio group 887 */ 888 function get_field_name() { 889 return $this->_group_name; 890 } 891 892 function set_checked($value) { 893 $this->_checked = $value; 894 } 895 896 function set_parent(&$parent) { 897 $this->_parent =& $parent; 898 } 899 } 900 901 class PDFFieldSelect extends PDFField { 902 var $_options; 903 var $_value; 904 905 function _dict(&$handler) { 906 $options = array(); 907 foreach ($this->_options as $arr) { 908 $options[] = $handler->_array(sprintf("%s %s", 909 $handler->_textstring($arr[0]), 910 $handler->_textstring($arr[1]))); 911 }; 912 913 $options_str = $handler->_array(implode(" ",$options)); 914 915 return array_merge(parent::_dict($handler), 916 array('FT' => '/Ch', 917 'Ff' => PDF_FIELD_CHOICE_COMBO, 918 'V' => $handler->_textstring($this->_value), // Current value 919 'DV' => $handler->_textstring($this->_value), // Default value 920 'DR' => "2 0 R", 921 'Opt' => $options_str)); 922 } 923 924 function PDFFieldSelect(&$handler, 925 $object_id, 926 $generation_id, 927 $rect, 928 $field_name, 929 $value, 930 $options) { 931 $this->PDFField($handler, 932 $object_id, 933 $generation_id, 934 $rect, 935 $field_name); 936 937 $this->_options = $options; 938 $this->_value = $value; 939 } 940 } 941 942 /** 943 * Interactive text input 944 */ 945 class PDFFieldText extends PDFField { 946 var $fontindex; 947 var $fontsize; 948 949 var $_appearance; 950 951 /** 952 * @var String contains the default value of this text field 953 * @access private 954 */ 955 var $_value; 956 957 function _dict(&$handler) { 958 return array_merge(parent::_dict($handler), 959 array( 960 'FT' => '/Tx', 961 'V' => $handler->_textstring($this->_value), // Current value 962 'DV' => $handler->_textstring($this->_value), // Default value 963 'DR' => "2 0 R", 964 // @TODO fix font references 965 'DA' => sprintf("(0 0 0 rg /FF%d %.2f Tf)", 966 $this->fontindex, 967 $this->fontsize), 968// 'AP' => $handler->_dictionary(array("N" => $handler->_reference($this->_appearance))), 969 )); 970 } 971 972 function _out_nested(&$handler) { 973 // $this->_appearance->out($handler); 974 } 975 976 function PDFFieldText(&$handler, 977 $object_id, 978 $generation_id, 979 $rect, 980 $field_name, 981 $value, 982 $fontindex, 983 $fontsize) { 984 $this->PDFField($handler, 985 $object_id, 986 $generation_id, 987 $rect, 988 $field_name); 989 990 $this->fontindex = $fontindex; 991 $this->fontsize = $fontsize; 992 $this->_value = $value; 993 994// $this->_appearance = new PDFAppearanceStream($handler, 995// $handler->_generate_new_object_number(), 996// $generation_id, 997// "/Tx BMC EMC"); 998 } 999 } 1000 1001 class PDFFieldMultilineText extends PDFFieldText { 1002 function _dict(&$handler) { 1003 return array_merge(parent::_dict($handler), 1004 array('Ff' => PDF_FIELD_TEXT_MULTILINE)); 1005 } 1006 } 1007 1008 /** 1009 * "Password" text input field 1010 */ 1011 class PDFFieldPassword extends PDFFieldText { 1012 function PDFFieldPassword(&$handler, 1013 $object_id, 1014 $generation_id, 1015 $rect, 1016 $field_name, 1017 $value, 1018 $fontindex, 1019 $fontsize) { 1020 $this->PDFFieldText($handler, 1021 $object_id, 1022 $generation_id, 1023 $rect, 1024 $field_name, 1025 $value, 1026 $fontindex, 1027 $fontsize); 1028 } 1029 1030 function _dict(&$handler) { 1031 return array_merge(parent::_dict($handler), 1032 array('Ff' => PDF_FIELD_TEXT_PASSWORD)); 1033 } 1034 } 1035 1036 class FPDF { 1037 //Private properties 1038 1039 var $page; //current page number 1040 var $n; //current object number 1041 var $offsets; //array of object offsets 1042 var $buffer; //buffer holding in-memory PDF 1043 var $pages; //array containing pages 1044 var $state; //current document state 1045 var $compress; //compression flag 1046 var $DefOrientation; //default orientation 1047 var $k; //scale factor (number of points in user unit) 1048 var $fwPt,$fhPt; //dimensions of page format in points 1049 var $fw,$fh; //dimensions of page format in user unit 1050 var $wPt,$hPt; //current dimensions of page in points 1051 var $w,$h; //current dimensions of page in user unit 1052 var $x,$y; //current position in user unit for cell positioning 1053 var $lasth; //height of last cell printed 1054 var $LineWidth; //line width in user unit 1055 var $fonts; //array of used fonts 1056 var $FontFiles; //array of font files 1057 1058 var $diffs; //array of encoding differences 1059 var $cmaps; // List of ToUnicode 1060 1061 var $images; //array of used images 1062 // var $PageLinks; //array of links in pages 1063 var $links; //array of internal links 1064 var $FontFamily; //current font family 1065 1066 var $underline; //underlining flag 1067 var $overline; 1068 var $strikeout; 1069 1070 var $CurrentFont; //current font info 1071 var $FontSizePt; //current font size in points 1072 var $FontSize; //current font size in user unit 1073 var $DrawColor; //commands for drawing color 1074 var $FillColor; //commands for filling color 1075 var $TextColor; //commands for text color 1076 1077 var $ColorFlag; //indicates whether fill and text colors are different 1078 1079 var $ws; //word spacing 1080 var $ZoomMode; //zoom display mode 1081 var $LayoutMode; //layout display mode 1082 var $title; //title 1083 var $subject; //subject 1084 var $author; //author 1085 var $keywords; //keywords 1086 var $creator; //creator 1087 var $PDFVersion; //PDF version number 1088 1089 var $_forms; 1090 var $_form_radios; 1091 var $_pages; 1092 1093 function moveto($x, $y) { 1094 $this->_out(sprintf("%.2f %.2f m", 1095 $this->x_coord($x), 1096 $this->y_coord($y))); 1097 } 1098 1099 function lineto($x, $y) { 1100 $this->_out(sprintf("%.2f %.2f l", 1101 $this->x_coord($x), 1102 $this->y_coord($y))); 1103 } 1104 1105 function closepath() { 1106 $this->_out("h"); 1107 } 1108 1109 function stroke() { 1110 $this->_out("S"); 1111 } 1112 1113 function is_object_written($id) { 1114 return isset($this->offsets[$id]); 1115 } 1116 1117 function x_coord($x) { 1118 return $x * $this->k; 1119 } 1120 1121 function y_coord($y) { 1122 return ($this->h - $y)*$this->k; 1123 } 1124 1125 // PDF specs: 1126 // 3.2.9 Indirect Objects 1127 // Any object in a PDF file may be labeled as an indirect object. This gives the object 1128 // a unique object identifier by which other objects can refer to it (for example, as an 1129 // element of an array or as the value of a dictionary entry). The object identifier 1130 // consists of two parts: 1131 // * A positive integer object number. Indirect objects are often numbered sequentially 1132 // within a PDF file, but this is not required; object numbers may be 1133 // assigned in any arbitrary order. 1134 // * A non-negative integer generation number. In a newly created file, all indirect 1135 // objects have generation numbers of 0. Nonzero generation numbers may be introduced 1136 // when the file is later updated; see Sections 3.4.3, Cross-Reference 1137 // Table, and 3.4.5, Incremental Updates. 1138 // Together, the combination of an object number and a generation number uniquely 1139 // identifies an indirect object. The object retains the same object number and 1140 // generation number throughout its existence, even if its value is modified. 1141 // 1142 function _indirect_object($object) { 1143 $object_number = $object->get_object_id(); 1144 $generation_number = $object->get_generation_id(); 1145 $object_string = $object->pdf($this); 1146 1147 $this->offsets[$object_number] = strlen($this->buffer); 1148 1149 return "$object_number $generation_number obj\n${object_string}\nendobj"; 1150 } 1151 1152 function _stream($content) { 1153 return "stream\n".$content."\nendstream"; 1154 } 1155 1156 /** 1157 * @TODO check name for validity 1158 */ 1159 function _name($name) { 1160 return sprintf("/%s", $name); 1161 } 1162 1163 function _dictionary($dict) { 1164 $content = ""; 1165 foreach ($dict as $key => $value) { 1166 $content .= "/$key $value\n"; 1167 }; 1168 return "<<\n".$content."\n>>"; 1169 } 1170 1171 function _array($array_str) { 1172 return "[$array_str]"; 1173 } 1174 1175 function _reference(&$object) { 1176 $object_id = $object->get_object_id(); 1177 $generation_id = $object->get_generation_id(); 1178 return "$object_id $generation_id R"; 1179 } 1180 1181 function _reference_array($object_array) { 1182 $array_str = ""; 1183 for ($i=0; $i<count($object_array); $i++) { 1184 $array_str .= $this->_reference($object_array[$i])." "; 1185 }; 1186 return $this->_array($array_str); 1187 } 1188 1189 function _generate_new_object_number() { 1190 $this->n++; 1191 return $this->n; 1192 } 1193 1194 function add_form($name) { 1195 $form = new PDFFieldGroup($this, 1196 $this->_generate_new_object_number(), // Object identifier 1197 0, 1198 $name); 1199 $this->_forms[] =& $form; 1200 } 1201 1202 function add_field_select($x, $y, $w, $h, $name, $value, $options) { 1203 $field =& new PDFFieldSelect($this, 1204 $this->_generate_new_object_number(), // Object identifier 1205 0, // Generation 1206 new PDFRect($x, $y, $w, $h), // Annotation rectangle 1207 $name, // Field name 1208 $value, 1209 $options); 1210 1211 $current_form =& $this->current_form(); 1212 $current_form->add_field($field); 1213 1214 $this->_pages[count($this->_pages)-1]->add_annotation($field); 1215 } 1216 1217 /** 1218 * Create new checkbox field object 1219 * 1220 * @param $x Integer Left coordinate of the widget bounding bog 1221 * @param $y Integer Upper coordinate of the widget bounding bog 1222 * @param $w Integer Widget width 1223 * @param $h Integer Widget height 1224 * @param $name String name of the field to be created 1225 * @param $value String value to be posted for this checkbox 1226 * 1227 * @TODO check if fully qualified field name will be unique in PDF file 1228 */ 1229 function add_field_checkbox($x, $y, $w, $h, $name, $value, $checked) { 1230 $field =& new PDFFieldCheckBox($this, 1231 $this->_generate_new_object_number(), // Object identifier 1232 0, // Generation 1233 new PDFRect($x, $y, $w, $h), // Annotation rectangle 1234 $name, // Field name 1235 $value, $checked); // Checkbox "on" value 1236 1237 $current_form =& $this->current_form(); 1238 $current_form->add_field($field); 1239 1240 $this->_pages[count($this->_pages)-1]->add_annotation($field); 1241 } 1242 1243 function ¤t_form() { 1244 if (count($this->_forms) == 0) { 1245 /** 1246 * Handle invalid HTML; if we've met an input control outside the form, 1247 * generate a new form with random name 1248 */ 1249 1250 $id = $this->_generate_new_object_number(); 1251 $name = sprintf("AnonymousFormObject_%u", $id); 1252 1253 error_log(sprintf("Anonymous form generated with name %s; check your HTML for validity", 1254 $name)); 1255 1256 $form = new PDFFieldGroup($this, 1257 $id, // Object identifier 1258 0, 1259 $name); 1260 $this->_forms[] =& $form; 1261 }; 1262 1263 return $this->_forms[count($this->_forms)-1]; 1264 } 1265 1266 function add_field_radio($x, $y, $w, $h, $group_name, $value, $checked) { 1267 if (isset($this->_form_radios[$group_name])) { 1268 $field =& $this->_form_radios[$group_name]; 1269 } else { 1270 $field =& new PDFFieldRadioGroup($this, 1271 $this->_generate_new_object_number(), 1272 0, 1273 $group_name); 1274 1275 $current_form =& $this->current_form(); 1276 $current_form->add_field($field); 1277 1278 $this->_form_radios[$group_name] =& $field; 1279 }; 1280 1281 $radio =& new PDFFieldRadio($this, 1282 $this->_generate_new_object_number(), 1283 0, 1284 new PDFRect($x, $y, $w, $h), 1285 $value); 1286 $field->add_field($radio); 1287 if ($checked) { $field->set_checked($value); }; 1288 1289 $this->_pages[count($this->_pages)-1]->add_annotation($radio); 1290 } 1291 1292 /** 1293 * Create a new interactive text form 1294 * 1295 * @param $x Left coordinate of the widget bounding box 1296 * @param $y Top coordinate of the widget bounding box 1297 * @param $w Widget width 1298 * @param $h Widget height 1299 * @param $value Default widget value 1300 * @param $field_name Field name 1301 * 1302 * @return Field number 1303 */ 1304 function add_field_text($x, $y, $w, $h, $value, $field_name) { 1305 $field =& new PDFFieldText($this, 1306 $this->_generate_new_object_number(), 1307 0, 1308 new PDFRect($x, $y, $w, $h), 1309 $field_name, 1310 $value, 1311 $this->CurrentFont['i'], 1312 $this->FontSizePt); 1313 1314 $current_form =& $this->current_form(); 1315 $current_form->add_field($field); 1316 1317 $this->_pages[count($this->_pages)-1]->add_annotation($field); 1318 } 1319 1320 function add_field_multiline_text($x, $y, $w, $h, $value, $field_name) { 1321 $field =& new PDFFieldMultilineText($this, 1322 $this->_generate_new_object_number(), 1323 0, 1324 new PDFRect($x, $y, $w, $h), 1325 $field_name, 1326 $value, 1327 $this->CurrentFont['i'], 1328 $this->FontSizePt); 1329 1330 $current_form =& $this->current_form(); 1331 $current_form->add_field($field); 1332 1333 $this->_pages[count($this->_pages)-1]->add_annotation($field); 1334 } 1335 1336 /** 1337 * Create a new interactive password input field 1338 * 1339 * @param $x Left coordinate of the widget bounding box 1340 * @param $y Top coordinate of the widget bounding box 1341 * @param $w Widget width 1342 * @param $h Widget height 1343 * @param $value Default widget value 1344 * @param $field_name Field name 1345 * 1346 * @return Field number 1347 */ 1348 function add_field_password($x, $y, $w, $h, $value, $field_name) { 1349 $field =& new PDFFieldPassword($this, 1350 $this->_generate_new_object_number(), 1351 0, 1352 new PDFRect($x, $y, $w, $h), 1353 $field_name, 1354 $value, 1355 $this->CurrentFont['i'], 1356 $this->FontSizePt); 1357 1358 $current_form =& $this->current_form(); 1359 $current_form->add_field($field); 1360 1361 $this->_pages[count($this->_pages)-1]->add_annotation($field); 1362 } 1363 1364 function add_field_pushbuttonimage($x, $y, $w, $h, $field_name, $value, $actionURL) { 1365 $field =& new PDFFieldPushButtonImage($this, 1366 $this->_generate_new_object_number(), 1367 0, 1368 new PDFRect($x, $y, $w, $h), 1369 $this->CurrentFont['i'], 1370 $this->FontSizePt, 1371 $field_name, 1372 $value, 1373 $actionURL); 1374 1375 $current_form =& $this->current_form(); 1376 $current_form->add_field($field); 1377 1378 $this->_pages[count($this->_pages)-1]->add_annotation($field); 1379 } 1380 1381 function add_field_pushbuttonsubmit($x, $y, $w, $h, $field_name, $value, $actionURL) { 1382 $field =& new PDFFieldPushButtonSubmit($this, 1383 $this->_generate_new_object_number(), 1384 0, 1385 new PDFRect($x, $y, $w, $h), 1386 $this->CurrentFont['i'], 1387 $this->FontSizePt, 1388 $field_name, 1389 $value, 1390 $actionURL); 1391 1392 $current_form =& $this->current_form(); 1393 $current_form->add_field($field); 1394 1395 $this->_pages[count($this->_pages)-1]->add_annotation($field); 1396 } 1397 1398 function add_field_pushbuttonreset($x, $y, $w, $h) { 1399 $field =& new PDFFieldPushButtonReset($this, 1400 $this->_generate_new_object_number(), 1401 0, 1402 new PDFRect($x, $y, $w, $h), 1403 null, 1404 $this->CurrentFont['i'], 1405 $this->FontSizePt); 1406 1407 $current_form =& $this->current_form(); 1408 $current_form->add_field($field); 1409 1410 $this->_pages[count($this->_pages)-1]->add_annotation($field); 1411 } 1412 1413 function add_field_pushbutton($x, $y, $w, $h) { 1414 $field =& new PDFFieldPushButton($this, 1415 $this->_generate_new_object_number(), 1416 0, 1417 new PDFRect($x, $y, $w, $h), 1418 null, 1419 $this->CurrentFont['i'], 1420 $this->FontSizePt); 1421 1422 $current_form =& $this->current_form(); 1423 $current_form->add_field($field); 1424 1425 $this->_pages[count($this->_pages)-1]->add_annotation($field); 1426 } 1427 1428 1429 function SetDash($x, $y) { 1430 $x = (int)$x; 1431 $y = (int)$y; 1432 $this->_out(sprintf("[%d %d] 0 d", $x*2, $y*2)); 1433 } 1434 1435 function _GetFontBBox() { 1436 return preg_split("/[\[\]\s]+/", $this->CurrentFont['desc']['FontBBox']); 1437 } 1438 1439 function _dounderline($x,$y,$txt) { 1440 //Underline text 1441 $up=$this->CurrentFont['up']; 1442 $ut=$this->CurrentFont['ut']; 1443 $w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' '); 1444 1445 $content = sprintf('%.2f %.2f %.2f %.2f re f', 1446 $x*$this->k, 1447 ($this->h-($y-$up/1000*$this->FontSize))*$this->k, 1448 $w*$this->k, 1449 -$ut/1000*$this->FontSizePt); 1450 1451 return $content; 1452 } 1453 1454 function _dooverline($x,$y,$txt) { 1455 $bbox = $this->_GetFontBBox(); 1456 $up = round($bbox[3] * 0.8); 1457 1458 $ut=$this->CurrentFont['ut']; 1459 1460 $w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' '); 1461 return sprintf('%.2f %.2f %.2f %.2f re f', 1462 $x*$this->k, 1463 ($this->h-($y-$up/1000*$this->FontSize))*$this->k, 1464 $w*$this->k, 1465 -$ut/1000*$this->FontSizePt); 1466 } 1467 1468 function _dostrikeout($x,$y,$txt) { 1469 $bbox = $this->_GetFontBBox(); 1470 $up = round($bbox[3] * 0.25); 1471 1472 $ut=$this->CurrentFont['ut']; 1473 $w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' '); 1474 return sprintf('%.2f %.2f %.2f %.2f re f', 1475 $x*$this->k, 1476 ($this->h-($y-$up/1000*$this->FontSize))*$this->k, 1477 $w*$this->k, 1478 -$ut/1000*$this->FontSizePt); 1479 } 1480 1481 function SetDecoration($underline, $overline, $strikeout) { 1482 $this->underline = $underline; 1483 $this->overline = $overline; 1484 $this->strikeout = $strikeout; 1485 } 1486 1487 function ClipPath($path) { 1488 if (count($path) < 3) { 1489 die("Attempt to clip on the path containing less than three points"); 1490 }; 1491 1492 $this->MakePath($path); 1493 $this->Clip(); 1494 } 1495 1496 function Clip() { 1497 $this->_out("W n"); 1498 } 1499 1500 // TODO: more graceful custom encoding processing 1501 function _LoadFont($fontkey, $family, $encoding) { 1502 if (!isset($this->fonts[$fontkey])) { 1503 global $g_font_resolver_pdf; 1504 $file = $g_font_resolver_pdf->ttf_mappings[$family]; 1505 1506 $embed = $g_font_resolver_pdf->embed[$family]; 1507 1508 // Remove the '.ttf' suffix 1509 $file = substr($file, 0, strlen($file) - 4); 1510 1511 // Generate (if required) PHP font description files 1512 if (!file_exists($this->_getfontpath().$fontkey.'.php') || 1513 ManagerEncoding::is_custom_encoding($encoding)) { 1514 // As MakeFont squeaks a lot, we'll need to capture and discard its output 1515 MakeFont(TTF_FONTS_REPOSITORY.$file.'.ttf', 1516 TTF_FONTS_REPOSITORY.$file.'.afm', 1517 $this->_getfontpath(), 1518 $fontkey.'.php', 1519 $encoding); 1520 }; 1521 1522 $this->AddFont($fontkey, $family, $encoding, $fontkey.'.php', $embed); 1523 }; 1524 } 1525 1526 function _MakeFontKey($family, $encoding) { 1527 return $family.'-'.$encoding; 1528 } 1529 1530 function GetFontAscender($name, $encoding) { 1531 $fontkey = $this->_MakeFontKey($name, $encoding); 1532 $this->_LoadFont($fontkey, $name, $encoding, ''); 1533 return $this->fonts[$fontkey]['desc']['Ascent'] / 1000; 1534 } 1535 1536 function GetFontDescender($name, $encoding) { 1537 $fontkey = $this->_MakeFontKey($name, $encoding); 1538 $this->_LoadFont($fontkey, $name, $encoding, ''); 1539 return -$this->fonts[$fontkey]['desc']['Descent'] / 1000; 1540 } 1541 1542 // Note that FPDF do some caching, which can conflict with "save/restore" pairs 1543 function Save() { 1544 $this->_out("q"); 1545 } 1546 1547 function Restore() { 1548 $this->_out("Q"); 1549 } 1550 1551 function Translate($dx, $dy) { 1552 $this->_out(sprintf("1 0 0 1 %.2f %.2f cm", $dx, $dy)); 1553 } 1554 1555 function Rotate($alpha) { 1556 $this->_out(sprintf("%.2f %.2f %.2f %.2f 0 0 cm", 1557 cos($alpha/180*pi()), 1558 sin($alpha/180*pi()), 1559 -sin($alpha/180*pi()), 1560 cos($alpha/180*pi()) 1561 )); 1562 } 1563 1564 function SetTextRendering($mode) { 1565 $this->_out(sprintf("%d Tr", $mode)); 1566 } 1567 1568 function MakePath($path) { 1569 $this->_out(sprintf("%.2f %.2f m", $path[0]['x'], $path[0]['y'])); 1570 1571 for ($i=1; $i<count($path); $i++) { 1572 $this->_out(sprintf("%.2f %.2f l", $path[$i]['x'], $path[$i]['y'])); 1573 }; 1574 } 1575 1576 function FillPath($path) { 1577 if (count($path) < 3) { 1578 die("Attempt to fill path containing less than three points"); 1579 }; 1580 1581 $this->_out($this->FillColor); 1582 $this->MakePath($path); 1583 $this->Fill(); 1584 } 1585 1586 function Fill() { 1587 $this->_out("f"); 1588 } 1589 1590 /** 1591 * Thanks G. Adam Stanislav for information about approximation circle using bezier curves 1592 * http://www.whizkidtech.redprince.net/bezier/circle/ 1593 */ 1594 function Circle($x, $y, $r) { 1595 $kappa = (sqrt(2) - 1) / 3 * 4; 1596 $l = $kappa * $r; 1597 1598 $this->_out(sprintf("%.2f %.f2 m", $x + $r, $y)); 1599 $this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c", 1600 $x + $r, $y + $l, 1601 $x + $l, $y + $r, 1602 $x, $y + $r)); 1603 $this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c", 1604 $x - $l, $y + $r, 1605 $x - $r, $y + $l, 1606 $x - $r, $y)); 1607 $this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c", 1608 $x - $r, $y - $l, 1609 $x - $l, $y - $r, 1610 $x, $y - $r)); 1611 $this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c", 1612 $x + $l, $y - $r, 1613 $x + $r, $y - $l, 1614 $x + $r, $y)); 1615 } 1616 1617 /******************************************************************************* 1618 * * 1619 * Public methods * 1620 * * 1621 *******************************************************************************/ 1622 function FPDF($orientation='P', $unit='mm', $format='A4') { 1623 $this->_forms = array(); 1624 $this->_form_radios = array(); 1625 $this->_pages = array(); 1626 1627 //Some checks 1628 $this->_dochecks(); 1629 1630 //Initialization of properties 1631 $this->page=0; 1632 1633 $this->n=2; 1634 1635 $this->buffer=''; 1636 $this->pages=array(); 1637 $this->state = FPDF_STATE_UNINITIALIZED; 1638 $this->fonts=array(); 1639 $this->FontFiles=array(); 1640 $this->diffs = array(); 1641 $this->images = array(); 1642 $this->links = array(); 1643 $this->lasth=0; 1644 $this->FontFamily=''; 1645 $this->FontSizePt=12; 1646 1647 $this->underline = false; 1648 $this->overline = false; 1649 $this->strikeout = false; 1650 1651 $this->DrawColor='0 G'; 1652 $this->FillColor='0 g'; 1653 $this->TextColor='0 g'; 1654 $this->ColorFlag=false; 1655 $this->ws=0; 1656 1657 //Scale factor 1658 switch ($unit) { 1659 case 'pt': 1660 $this->k = 1; break; 1661 case 'mm': 1662 $this->k = 72/25.4; break; 1663 case 'cm': 1664 $this->k = 72/2.54; break; 1665 case 'in': 1666 $this->k = 72; 1667 default: 1668 $this->Error('Incorrect unit: '.$unit); 1669 }; 1670 1671 $this->setup_format($format[0], $format[1]); 1672 1673 //Line width (0.2 mm) 1674 $this->LineWidth=.567/$this->k; 1675 1676 //Full width display mode 1677 $this->SetDisplayMode('fullwidth'); 1678 1679 //Enable compression 1680 $this->SetCompression(true); 1681 1682 //Set default PDF version number 1683 $this->PDFVersion='1.3'; 1684 } 1685 1686 function setup_format($width, $height) { 1687 $this->fwPt = $width * $this->k; 1688 $this->fhPt = $height * $this->k; 1689 $this->wPt = $this->fwPt; 1690 $this->hPt = $this->fhPt; 1691 1692 $this->fw = $width; 1693 $this->fh = $height; 1694 $this->w = $this->fw; 1695 $this->h = $this->fh; 1696 1697 $this->DefOrientation='P'; 1698 } 1699 1700 function SetDisplayMode($zoom,$layout='continuous') { 1701 //Set display mode in viewer 1702 if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom)) 1703 $this->ZoomMode=$zoom; 1704 else 1705 $this->Error('Incorrect zoom display mode: '.$zoom); 1706 if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default') 1707 $this->LayoutMode=$layout; 1708 else 1709 $this->Error('Incorrect layout display mode: '.$layout); 1710 } 1711 1712 /** 1713 * @param $compress Boolean indicates whether compression is enabled 1714 */ 1715 function SetCompression($compress) { 1716 if (function_exists('gzcompress')) { 1717 $this->compress=$compress; 1718 } else { 1719 $this->compress=false; 1720 }; 1721 } 1722 1723 function SetTitle($title) { 1724 //Title of document 1725 $this->title=$title; 1726 } 1727 1728 function SetSubject($subject) { 1729 //Subject of document 1730 $this->subject=$subject; 1731 } 1732 1733 function SetAuthor($author) { 1734 //Author of document 1735 $this->author=$author; 1736 } 1737 1738 function SetKeywords($keywords) { 1739 //Keywords of document 1740 $this->keywords=$keywords; 1741 } 1742 1743 function SetCreator($creator) { 1744 //Creator of document 1745 $this->creator=$creator; 1746 } 1747 1748 function Error($msg) { 1749 //Fatal error 1750 die('<B>FPDF error: </B>'.$msg); 1751 } 1752 1753 function Open() { 1754 //Begin document 1755 $this->state = FPDF_STATE_DOCUMENT_STARTED; 1756 } 1757 1758 function Close() { 1759 //Terminate document 1760 if ($this->state == FPDF_STATE_COMPLETED) { 1761 return; 1762 }; 1763 1764 if ($this->page==0) { 1765 $this->AddPage(); 1766 }; 1767 1768 //Close page 1769 $this->_endpage(); 1770 //Close document 1771 $this->_enddoc(); 1772 } 1773 1774 function AddPage($width = null, $height = null) { 1775 if (!$width) { 1776 $width = $this->fwPt; 1777 }; 1778 1779 if (!$height) { 1780 $height = $this->fhPt; 1781 }; 1782 1783 $this->setup_format($width, $height); 1784 1785 $this->_pages[] =& new PDFPage($this, $width, $height, $this->_generate_new_object_number(), 0); 1786 1787 //Start a new page 1788 if ($this->state == FPDF_STATE_UNINITIALIZED) { 1789 $this->Open(); 1790 }; 1791 1792 $family=$this->FontFamily; 1793 $size=$this->FontSizePt; 1794 $lw=$this->LineWidth; 1795 $dc=$this->DrawColor; 1796 $fc=$this->FillColor; 1797 $tc=$this->TextColor; 1798 $cf=$this->ColorFlag; 1799 if ($this->page>0) { 1800 //Close page 1801 $this->_endpage(); 1802 } 1803 1804 //Start new page 1805 $this->_beginpage(); 1806 //Set line cap style to square 1807 $this->_out('2 J'); 1808 //Set line width 1809 $this->LineWidth=$lw; 1810 $this->_out(sprintf('%.2f w',$lw*$this->k)); 1811 1812 //Set colors 1813 $this->DrawColor=$dc; 1814 if ($dc!='0 G') { 1815 $this->_out($dc); 1816 }; 1817 1818 $this->FillColor=$fc; 1819 if ($fc!='0 g') { 1820 $this->_out($fc); 1821 }; 1822 1823 $this->TextColor=$tc; 1824 $this->ColorFlag=$cf; 1825 1826 //Restore line width 1827 if ($this->LineWidth!=$lw) { 1828 $this->LineWidth=$lw; 1829 $this->_out(sprintf('%.2f w',$lw*$this->k)); 1830 } 1831 1832 //Restore colors 1833 if ($this->DrawColor!=$dc) { 1834 $this->DrawColor=$dc; 1835 $this->_out($dc); 1836 } 1837 if ($this->FillColor!=$fc) { 1838 $this->FillColor=$fc; 1839 $this->_out($fc); 1840 } 1841 $this->TextColor=$tc; 1842 $this->ColorFlag=$cf; 1843 1844 if (!is_null($this->CurrentFont)) { 1845 $this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); 1846 }; 1847 } 1848 1849 function SetDrawColor($r,$g=-1,$b=-1) { 1850 // Set color for all stroking operations 1851 if (($r==0 && $g==0 && $b==0) || $g==-1) { 1852 $new_color = sprintf('%.3f G',$r/255); 1853 } else { 1854 $new_color = sprintf('%.3f %.3f %.3f RG',$r/255,$g/255,$b/255); 1855 }; 1856 1857 if ($this->page > 0 /*&& $this->DrawColor != $new_color*/) { 1858 $this->DrawColor = $new_color; 1859 $this->_out($this->DrawColor); 1860 }; 1861 } 1862 1863 function SetFillColor($r,$g=-1,$b=-1) { 1864 // Set color for all filling operations 1865 if (($r==0 && $g==0 && $b==0) || $g==-1) { 1866 $new_color = sprintf('%.3f g',$r/255); 1867 } else { 1868 $new_color = sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); 1869 }; 1870 1871 if ($this->page>0 /*&& $this->FillColor != $new_color*/) { 1872 $this->FillColor = $new_color; 1873 $this->ColorFlag = ($this->FillColor!=$this->TextColor); 1874 $this->_out($this->FillColor); 1875 }; 1876 } 1877 1878 function SetTextColor($r,$g=-1,$b=-1) { 1879 //Set color for text 1880 if (($r==0 && $g==0 && $b==0) || $g==-1) { 1881 $this->TextColor=sprintf('%.3f g',$r/255); 1882 } else { 1883 $this->TextColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); 1884 }; 1885 1886 $this->ColorFlag=($this->FillColor!=$this->TextColor); 1887 } 1888 1889 function GetStringWidth($s) { 1890 //Get width of a string in the current font 1891 $s=(string)$s; 1892 $cw = &$this->CurrentFont['cw']; 1893 $w=0; 1894 1895 $l=strlen($s); 1896 for ($i=0; $i<$l; $i++) { 1897 $w+=$cw[$s{$i}]; 1898 }; 1899 1900 return $w*$this->FontSize/1000; 1901 } 1902 1903 /** 1904 * Set line width 1905 */ 1906 function SetLineWidth($width) { 1907 $this->LineWidth = $width; 1908 if ($this->page > 0) { 1909 $this->_out(sprintf('%.2f w',$width*$this->k)); 1910 }; 1911 } 1912 1913 /** 1914 * Draw a line 1915 */ 1916 function Line($x1,$y1,$x2,$y2) { 1917 $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)); 1918 } 1919 1920 /** 1921 * Add a TrueType or Type1 font 1922 */ 1923 function AddFont($fontkey, $family, $encoding, $file, $bEmbed) { 1924 if(isset($this->fonts[$fontkey])) { 1925 $this->Error('Font already added: '.$family); 1926 }; 1927 1928 $filepath = $this->_getfontpath().$file; 1929 include($filepath); 1930 1931 // After we've executed 'include' the $file variable 1932 // have been overwritten by $file declared in font definition file; if we do not want 1933 // to embed the font in the PDF file, we should set to empty string 1934 if (!$bEmbed) { $file = ''; }; 1935 1936 if(!isset($name)) { 1937 $this->Error("Could not include font definition file: $filepath"); 1938 }; 1939 1940 $i=count($this->fonts)+1; 1941 $this->fonts[$fontkey]=array('i' =>$i, 1942 'type' =>$type, 1943 'name' =>$name, 1944 'desc' =>$desc, 1945 'up' =>$up, 1946 'ut' =>$ut, 1947 'cw' =>$cw, 1948 'enc' =>$enc, 1949 'file' =>$file); 1950 1951 if ($diff) { 1952 //Search existing encodings 1953 $d=0; 1954 $nb=count($this->diffs); 1955 for ($i=1; $i<=$nb; $i++) { 1956 if ($this->diffs[$i] == $diff) { 1957 $d=$i; 1958 break; 1959 } 1960 } 1961 if ($d==0) { 1962 $d=$nb+1; 1963 $this->diffs[$d] = $diff; 1964 1965 /** 1966 * TODO 1967 * Add CMAP for this font 1968 */ 1969 $this->cmaps[$d] = new PDFCMap($cmap, 1970 $handler, 1971 $this->_generate_new_object_number(), 1972 0); 1973 } 1974 $this->fonts[$fontkey]['diff']=$d; 1975 } 1976 1977 if ($file) { 1978 if ($type=='TrueType') { 1979 $this->FontFiles[$file]=array('length1'=>$originalsize); 1980 } else { 1981 $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2); 1982 }; 1983 } 1984 } 1985 1986 /** 1987 * Select a font; size given in points 1988 */ 1989 function SetFont($family, $encoding, $size) { 1990 global $fpdf_charwidths; 1991 1992 $fontkey = $this->_MakeFontKey($family, $encoding); 1993 $this->_LoadFont($fontkey, $family, $encoding); 1994 1995 $this->FontFamily = $family; 1996 $this->FontSizePt = $size; 1997 $this->FontSize = $size/$this->k; 1998 $this->CurrentFont = &$this->fonts[$fontkey]; 1999 2000 if ($this->page > 0) { 2001 $this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); 2002 }; 2003 } 2004 2005 /** 2006 * Create a new internal link 2007 */ 2008 function AddLink() { 2009 $n=count($this->links)+1; 2010 $this->links[$n]=array(0,0); 2011 return $n; 2012 } 2013 2014 /** 2015 * Set destination of internal link 2016 */ 2017 function SetLink($link,$y,$page) { 2018 $this->links[$link]=array($page,$y); 2019 } 2020 2021 /** 2022 * Add an external hyperlink on the page (an rectangular area). It is not bound to any other PDF element, 2023 * like text. It is the task of layout engine to draw the appropriate text inside this area. 2024 * 2025 * @param Float $x X-coordinate of the upper-left corner of the link area 2026 * @param Float $y Y-coordinate of the upper-left corner of the link area 2027 * @param Float $w link area width 2028 * @param Float $h link area height 2029 * @param String $link Link URL 2030 */ 2031 function add_link_external($x, $y, $w, $h, $link) { 2032 $link = new PDFAnnotationExternalLink($this, 2033 $this->_generate_new_object_number(), 2034 0, 2035 new PDFRect($x, $y, $w, $h), 2036 $link); 2037 $this->_pages[count($this->_pages)-1]->add_annotation($link); 2038 } 2039 2040 /** 2041 * Add an internal hyperlink on the page (an rectangular area). It is not bound to any other PDF element, 2042 * like text. It is the task of layout engine to draw the appropriate text inside this area. 2043 * 2044 * @param Float $x X-coordinate of the upper-left corner of the link area 2045 * @param Float $y Y-coordinate of the upper-left corner of the link area 2046 * @param Float $w link area width 2047 * @param Float $h link area height 2048 * @param Integer $link Internal Link identifier 2049 */ 2050 function add_link_internal($x, $y, $w, $h, $link) { 2051 $link = new PDFAnnotationInternalLink($this, 2052 $this->_generate_new_object_number(), 2053 0, 2054 new PDFRect($x, $y, $w, $h), 2055 $link); 2056 $this->_pages[count($this->_pages)-1]->add_annotation($link); 2057 } 2058 2059 function Text($x, $y, $txt) { 2060 //Output a string 2061 $s = sprintf('BT %.2f %.2f Td (%s) Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escape($txt)); 2062 2063 if ($this->underline && $txt!='') { 2064 $s.=' '.$this->_dounderline($x,$y,$txt); 2065 } 2066 2067 if ($this->overline && $txt!='') { 2068 $s.=' '.$this->_dooverline($x,$y,$txt); 2069 } 2070 2071 if ($this->strikeout && $txt!='') { 2072 $s.=' '.$this->_dostrikeout($x,$y,$txt); 2073 } 2074 2075 if ($this->ColorFlag) { 2076 $s='q '.$this->TextColor.' '.$s.' Q'; 2077 }; 2078 $this->_out($s); 2079 } 2080 2081 /** 2082 * Accepts PNG images only 2083 */ 2084 function Image($file, $x, $y, $w, $h) { 2085 // Image used first time, parse input file 2086 if (!isset($this->images[$file])) { 2087 $ext = pathinfo($file, PATHINFO_EXTENSION); 2088 switch ($ext) { 2089 case 'jpg': 2090 case 'jpeg': 2091 $info = $this->_parsejpg($file); 2092 break; 2093 case 'png': 2094 $info = $this->_parsepng($file); 2095 break; 2096 }; 2097 2098 $info['i'] = count($this->images) + 1; 2099 $this->images[$file] = $info; 2100 }; 2101 2102 $info = $this->images[$file]; 2103 $this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', 2104 $w*$this->k, 2105 $h*$this->k, 2106 $x*$this->k, 2107 ($this->h-($y+$h))*$this->k, 2108 $info['i'])); 2109 } 2110 2111 /** 2112 * @param $name String file to save generated PDF in 2113 */ 2114 function Output($name) { 2115 //Finish document if necessary 2116 if ($this->state != FPDF_STATE_COMPLETED) { 2117 $this->Close(); 2118 }; 2119 2120 $f=fopen($name,'wb'); 2121 if (!$f) { 2122 $this->Error('Unable to create output file: '.$name); 2123 }; 2124 fwrite($f,$this->buffer,strlen($this->buffer)); 2125 fclose($f); 2126 } 2127 2128 /******************************************************************************** 2129 * * 2130 * Protected methods * 2131 * * 2132 *******************************************************************************/ 2133 function _dochecks() { 2134 // Check for locale-related bug 2135 if (1.1==1) { 2136 $this->Error('Don\'t alter the locale before including class file'); 2137 }; 2138 2139 // Check for decimal separator 2140 if (sprintf('%.1f',1.0)!='1.0') { 2141 setlocale(LC_NUMERIC,'C'); 2142 }; 2143 } 2144 2145 function _getfontpath() { 2146 return CACHE_DIR; 2147 } 2148 2149 function _putpages() { 2150 $nb=$this->page; 2151 2152 if ($this->DefOrientation=='P') { 2153 $wPt=$this->fwPt; 2154 $hPt=$this->fhPt; 2155 } else { 2156 $wPt=$this->fhPt; 2157 $hPt=$this->fwPt; 2158 }; 2159 2160 $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; 2161 2162 $pages_start_obj_number = $this->n+1; 2163 2164 for ($n=1; $n<=$nb; $n++) { 2165 //Page 2166 2167 $page = $this->_pages[$n-1]; 2168 $this->offsets[$page->get_object_id()] = strlen($this->buffer); 2169 $this->_out(sprintf("%u %u obj",$page->object_id, $page->generation_id)); 2170 2171 $this->_out('<</Type /Page'); 2172 $this->_out('/Parent 1 0 R'); 2173 $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', 2174 $page->get_width(), 2175 $page->get_height())); 2176 $this->_out("/Annots ".$this->_pages[$n-1]->_annotations($this)); 2177 $this->_out('/Resources 2 0 R'); 2178 2179 $this->_out('/Contents '.($this->n+1).' 0 R>>'); 2180 $this->_out('endobj'); 2181 //Page content 2182 $p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n]; 2183 $this->_newobj(); 2184 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>'); 2185 $this->_putstream($p); 2186 $this->_out('endobj'); 2187 2188 // Output annotation object for this page 2189 $annotations = $this->_pages[$n-1]->annotations; 2190 $size = count($annotations); 2191 2192 for ($j=0; $j<$size; $j++) { 2193 $annotations[$j]->out($this); 2194 }; 2195 } 2196 2197 //Pages root 2198 $this->offsets[1] = strlen($this->buffer); 2199 $this->_out('1 0 obj'); 2200 $this->_out('<</Type /Pages'); 2201 2202 $this->_out('/Kids '.$this->_reference_array($this->_pages)); 2203 2204 $this->_out('/Count '.$nb); 2205 $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$wPt,$hPt)); 2206 $this->_out('>>'); 2207 $this->_out('endobj'); 2208 2209 return $pages_start_obj_number; 2210 } 2211 2212 function _putfonts() { 2213 $nf=$this->n; 2214 2215 $num_diffs = count($this->diffs); 2216 for ($i=1; $i<=$num_diffs; $i++) { 2217 $diff = $this->diffs[$i]; 2218 $cmap = $this->cmaps[$i]; 2219 2220 //Encodings 2221 $this->_newobj(); 2222 $this->_out($this->_dictionary(array("Type" => "/Encoding", 2223 "BaseEncoding" => "/WinAnsiEncoding", 2224 "Differences" => $this->_array($diff)))); 2225 $this->_out('endobj'); 2226 2227 $cmap->out($this); 2228 } 2229 2230 foreach ($this->FontFiles as $file=>$info) { 2231 //Font file embedding 2232 $this->_newobj(); 2233 $this->FontFiles[$file]['n'] = $this->n; 2234 $font=''; 2235 $f=fopen($this->_getfontpath().$file,'rb',1); 2236 if (!$f) { 2237 $this->Error('Font file not found'); 2238 }; 2239 2240 while (!feof($f)) { $font.=fread($f,8192); }; 2241 2242 fclose($f); 2243 $compressed=(substr($file,-2)=='.z'); 2244 if (!$compressed && isset($info['length2'])) { 2245 $header=(ord($font{0})==128); 2246 if($header) { 2247 //Strip first binary header 2248 $font=substr($font,6); 2249 } 2250 if($header && ord($font{$info['length1']})==128) { 2251 //Strip second binary header 2252 $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6); 2253 } 2254 } 2255 $this->_out('<</Length '.strlen($font)); 2256 2257 if ($compressed) { 2258 $this->_out('/Filter /FlateDecode'); 2259 }; 2260 2261 $this->_out('/Length1 '.$info['length1']); 2262 if(isset($info['length2'])) { 2263 $this->_out('/Length2 '.$info['length2'].' /Length3 0'); 2264 }; 2265 $this->_out('>>'); 2266 $this->_putstream($font); 2267 $this->_out('endobj'); 2268 } 2269 2270 foreach ($this->fonts as $k=>$font) { 2271 //Font objects 2272 $this->fonts[$k]['n'] = $this->n+1; 2273 $type=$font['type']; 2274 $name=$font['name']; 2275 2276 if ($type=='Type1' || $type=='TrueType') { 2277 //Additional Type1 or TrueType font 2278 $this->_newobj(); 2279 $this->_out('<</Type /Font'); 2280 $this->_out('/BaseFont /'.$name); 2281 $this->_out('/Subtype /'.$type); 2282 $this->_out('/FirstChar 32 /LastChar 255'); 2283 $this->_out('/Widths '.($this->n+1).' 0 R'); 2284 $this->_out('/FontDescriptor '.($this->n+2).' 0 R'); 2285 if ($font['enc']) { 2286 if(isset($font['diff'])) { 2287 $this->_out('/Encoding '.($nf+$font['diff']).' 0 R'); 2288 $this->_out('/ToUnicode '.($this->_reference($this->cmaps[$font['diff']]))); 2289 } else { 2290 $this->_out('/Encoding /WinAnsiEncoding'); 2291 }; 2292 } 2293 $this->_out('>>'); 2294 $this->_out('endobj'); 2295 2296 //Widths 2297 $this->_newobj(); 2298 $cw = &$font['cw']; 2299 $s='['; 2300 for ($i=32;$i<=255;$i++) { 2301 $s.=$cw[chr($i)].' '; 2302 }; 2303 $this->_out($s.']'); 2304 $this->_out('endobj'); 2305 2306 /** 2307 * Font descriptor 2308 */ 2309 $this->_newobj(); 2310 $fontDescriptor = array('Type' => '/FontDescriptor', 2311 'FontName' => '/'.$name, 2312 'Flags' => $font['desc']['Flags'], 2313 'FontBBox' => $font['desc']['FontBBox'], 2314 'ItalicAngle' => $font['desc']['ItalicAngle'], 2315 'Ascent' => $font['desc']['Ascent'], 2316 'Descent' => $font['desc']['Descent'], 2317 'CapHeight' => $font['desc']['CapHeight'], 2318 'StemV' => $font['desc']['StemV'] 2319 ); 2320 if ($font['file'] != "") { 2321 $fontDescriptor['FontFile'.($type=='Type1' ? '' : '2')] = 2322 $this->FontFiles[$font['file']]['n'].' 0 R'; 2323 }; 2324 $this->_out($this->_dictionary($fontDescriptor)); 2325 $this->_out('endobj'); 2326 2327 } else { 2328 //Allow for additional types 2329 $mtd='_put'.strtolower($type); 2330 if(!method_exists($this,$mtd)) 2331 $this->Error('Unsupported font type: '.$type); 2332 $this->$mtd($font); 2333 } 2334 } 2335 } 2336 2337 function _putimages() { 2338 $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; 2339 reset($this->images); 2340 while (list($file,$info) = each($this->images)) { 2341 $this->_newobj(); 2342 $this->images[$file]['n']=$this->n; 2343 $this->_out('<</Type /XObject'); 2344 $this->_out('/Subtype /Image'); 2345 $this->_out('/Width '.$info['w']); 2346 $this->_out('/Height '.$info['h']); 2347 if ($info['cs']=='Indexed') { 2348 $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); 2349 } else { 2350 $this->_out('/ColorSpace /'.$info['cs']); 2351 if($info['cs']=='DeviceCMYK') { 2352 $this->_out('/Decode [1 0 1 0 1 0 1 0]'); 2353 }; 2354 } 2355 $this->_out('/BitsPerComponent '.$info['bpc']); 2356 if (isset($info['f'])) { 2357 $this->_out('/Filter /'.$info['f']); 2358 }; 2359 2360 if(isset($info['parms'])) { 2361 $this->_out($info['parms']); 2362 }; 2363 2364 if(isset($info['trns']) && is_array($info['trns'])) { 2365 $trns=''; 2366 for ($i=0;$i<count($info['trns']);$i++) { 2367 $trns.=$info['trns'][$i].' '.$info['trns'][$i].' '; 2368 }; 2369 $this->_out('/Mask ['.$trns.']'); 2370 }; 2371 2372 $this->_out('/Length '.strlen($info['data']).'>>'); 2373 $this->_putstream($info['data']); 2374 unset($this->images[$file]['data']); 2375 $this->_out('endobj'); 2376 2377 // Palette 2378 if ($info['cs']=='Indexed') { 2379 $this->_newobj(); 2380 $pal=($this->compress) ? gzcompress($info['pal']) : $info['pal']; 2381 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>'); 2382 $this->_putstream($pal); 2383 $this->_out('endobj'); 2384 }; 2385 } 2386 } 2387 2388 function _putxobjectdict() { 2389 foreach ($this->images as $image) { 2390 $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); 2391 }; 2392 } 2393 2394 function _putresourcedict() { 2395 $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); 2396 $this->_out('/Font <<'); 2397 foreach ($this->fonts as $font) { 2398 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R'); 2399 }; 2400 $this->_out('>>'); 2401 $this->_out('/XObject <<'); 2402 $this->_putxobjectdict(); 2403 $this->_out('>>'); 2404 } 2405 2406 function _putresources() { 2407 $this->_putfonts(); 2408 $this->_putimages(); 2409 2410 //Resource dictionary 2411 $this->offsets[2]=strlen($this->buffer); 2412 $this->_out('2 0 obj'); 2413 $this->_out('<<'); 2414 $this->_putresourcedict(); 2415 $this->_out('>>'); 2416 $this->_out('endobj'); 2417 } 2418 2419 function _putinfo() { 2420 $this->_out('/Producer '.$this->_textstring('FPDF '.FPDF_VERSION)); 2421 2422 if (!empty($this->title)) { 2423 $this->_out('/Title '.$this->_textstring($this->title)); 2424 }; 2425 2426 if (!empty($this->subject)) { 2427 $this->_out('/Subject '.$this->_textstring($this->subject)); 2428 }; 2429 2430 if (!empty($this->author)) { 2431 $this->_out('/Author '.$this->_textstring($this->author)); 2432 }; 2433 2434 if (!empty($this->keywords)) { 2435 $this->_out('/Keywords '.$this->_textstring($this->keywords)); 2436 }; 2437 2438 if (!empty($this->creator)) { 2439 $this->_out('/Creator '.$this->_textstring($this->creator)); 2440 }; 2441 2442 $this->_out('/CreationDate '.$this->_textstring('D:'.date('YmdHis'))); 2443 } 2444 2445 // Generate the document catalog entry of PDF file 2446 function _putcatalog($pages_start_obj_number) { 2447 $this->_out('/Type /Catalog'); 2448 2449 $this->_out('/Pages 1 0 R'); 2450 if ($this->ZoomMode=='fullpage') { 2451 $this->_out("/OpenAction [$pages_start_obj_number 0 R /Fit]"); 2452 } elseif ($this->ZoomMode=='fullwidth') { 2453 $this->_out("/OpenAction [$pages_start_obj_number 0 R /FitH null]"); 2454 } elseif ($this->ZoomMode=='real') { 2455 $this->_out("/OpenAction [$pages_start_obj_number 0 R /XYZ null null 1]"); 2456 } elseif (!is_string($this->ZoomMode)) { 2457 $this->_out("/OpenAction [$pages_start_obj_number 0 R /XYZ null null ".($this->ZoomMode/100).']'); 2458 }; 2459 2460 if ($this->LayoutMode=='single') { 2461 $this->_out('/PageLayout /SinglePage'); 2462 } elseif ($this->LayoutMode=='continuous') { 2463 $this->_out('/PageLayout /OneColumn'); 2464 } elseif ($this->LayoutMode=='two') { 2465 $this->_out('/PageLayout /TwoColumnLeft'); 2466 }; 2467 2468 if (count($this->_forms) > 0) { 2469 $this->_out('/AcroForm <<'); 2470 $this->_out('/Fields '.$this->_reference_array($this->_forms)); 2471 $this->_out('/DR 2 0 R'); 2472 $this->_out('/NeedAppearances true'); 2473 $this->_out('>>'); 2474 }; 2475 } 2476 2477 function _putheader() { 2478 $this->_out('%PDF-'.$this->PDFVersion); 2479 } 2480 2481 function _puttrailer() { 2482 $this->_out('/Size '.($this->n+1)); 2483 $this->_out('/Root '.$this->n.' 0 R'); 2484 $this->_out('/Info '.($this->n-1).' 0 R'); 2485 } 2486 2487 function _enddoc() { 2488 $this->_putheader(); 2489 $pages_start_obj_number = $this->_putpages(); 2490 2491 $this->_putresources(); 2492 2493 //Info 2494 $this->_newobj(); 2495 $this->_out('<<'); 2496 $this->_putinfo(); 2497 $this->_out('>>'); 2498 $this->_out('endobj'); 2499 2500 // Form fields 2501 for ($i=0; $i<count($this->_forms); $i++) { 2502 $form =& $this->_forms[$i]; 2503 2504 $form->out($this); 2505 }; 2506 2507 //Catalog 2508 $this->_newobj(); 2509 $this->_out('<<'); 2510 $this->_putcatalog($pages_start_obj_number); 2511 $this->_out('>>'); 2512 $this->_out('endobj'); 2513 2514 //Cross-ref 2515 $o=strlen($this->buffer); 2516 $this->_out('xref'); 2517 $this->_out('0 '.($this->n+1)); 2518 $this->_out('0000000000 65535 f '); 2519 2520 for ($i=1; $i<=$this->n; $i++) { 2521 $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i])); 2522 }; 2523 2524 //Trailer 2525 $this->_out('trailer'); 2526 $this->_out('<<'); 2527 $this->_puttrailer(); 2528 $this->_out('>>'); 2529 $this->_out('startxref'); 2530 $this->_out($o); 2531 $this->_out('%%EOF'); 2532 $this->state = FPDF_STATE_COMPLETED; 2533 } 2534 2535 function _beginpage() { 2536 $this->page++; 2537 $this->pages[$this->page]=''; 2538 $this->state = FPDF_STATE_PAGE_STARTED; 2539 $this->FontFamily=''; 2540 } 2541 2542 /** 2543 * End of page contents 2544 */ 2545 function _endpage() { 2546 $this->state = FPDF_STATE_DOCUMENT_STARTED; 2547 } 2548 2549 /** 2550 * Start a new indirect object 2551 */ 2552 function _newobj() { 2553 $num = $this->_generate_new_object_number(); 2554 $this->offsets[$num]=strlen($this->buffer); 2555 $this->_out($num.' 0 obj'); 2556 } 2557 2558 // Extract info from a JPEG file 2559 function _parsejpg($file) { 2560 $size_info = GetImageSize($file); 2561 if (!$size_info) { 2562 $this->Error('Missing or incorrect image file: '.$file); 2563 }; 2564 2565 if ($size_info[2]!=2) { 2566 $this->Error('Not a JPEG file: '.$file); 2567 }; 2568 2569 if (!isset($size_info['channels']) || $size_info['channels']==3) { 2570 $colspace='DeviceRGB'; 2571 } elseif($size_info['channels']==4) { 2572 $colspace='DeviceCMYK'; 2573 } else { 2574 $colspace='DeviceGray'; 2575 }; 2576 2577 $bpc = isset($size_info['bits']) ? $size_info['bits'] : 8; 2578 2579 //Read whole file 2580 $f=fopen($file,'rb'); 2581 $data=''; 2582 while (!feof($f)) { 2583 $data .= fread($f, 4096); 2584 }; 2585 fclose($f); 2586 2587 return array('w' => $size_info[0], 2588 'h' => $size_info[1], 2589 'cs' => $colspace, 2590 'bpc' => $bpc, 2591 'f' => 'DCTDecode', 2592 'data' => $data); 2593 } 2594 2595 // Extract info from a PNG file 2596 function _parsepng($file) { 2597 $f = fopen($file,'rb'); 2598 if (!$f) { 2599 $this->Error('Can\'t open image file: '.$file); 2600 }; 2601 2602 //Check signature 2603 if (fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { 2604 $this->Error('Not a PNG file: '.$file); 2605 }; 2606 2607 //Read header chunk 2608 fread($f,4); 2609 if (fread($f,4)!='IHDR') { 2610 $this->Error('Incorrect PNG file: '.$file); 2611 }; 2612 2613 $w = $this->_freadint($f); 2614 $h = $this->_freadint($f); 2615 $bpc = ord(fread($f,1)); 2616 2617 if ($bpc>8) { 2618 $this->Error('16-bit depth not supported: '.$file); 2619 }; 2620 2621 $ct=ord(fread($f,1)); 2622 if ($ct==0) { 2623 $colspace='DeviceGray'; 2624 } elseif($ct==2) { 2625 $colspace='DeviceRGB'; 2626 } elseif($ct==3) { 2627 $colspace='Indexed'; 2628 } else { 2629 $this->Error('Alpha channel not supported: '.$file); 2630 }; 2631 2632 if (ord(fread($f,1))!=0) { 2633 $this->Error('Unknown compression method: '.$file); 2634 }; 2635 2636 if (ord(fread($f,1))!=0) { 2637 $this->Error('Unknown filter method: '.$file); 2638 }; 2639 2640 if (ord(fread($f,1))!=0) { 2641 $this->Error('Interlacing not supported: '.$file); 2642 }; 2643 2644 fread($f,4); 2645 $parms='/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>'; 2646 2647 //Scan chunks looking for palette, transparency and image data 2648 $pal=''; 2649 $trns=''; 2650 $data=''; 2651 do { 2652 $n=$this->_freadint($f); 2653 $type=fread($f,4); 2654 if ($type=='PLTE') { 2655 //Read palette 2656 $pal=fread($f,$n); 2657 fread($f,4); 2658 } elseif($type=='tRNS') { 2659 //Read transparency info 2660 $t=fread($f,$n); 2661 if ($ct==0) { 2662 $trns=array(ord(substr($t,1,1))); 2663 } elseif($ct==2) { 2664 $trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1))); 2665 } else { 2666 $pos=strpos($t,chr(0)); 2667 if ($pos!==false) { 2668 $trns=array($pos); 2669 } 2670 } 2671 fread($f,4); 2672 } elseif ($type=='IDAT') { 2673 //Read image data block 2674 $data.=fread($f,$n); 2675 fread($f,4); 2676 } elseif ($type=='IEND') { 2677 break; 2678 } else { 2679 fread($f,$n+4); 2680 }; 2681 } while($n); 2682 2683 if ($colspace=='Indexed' && empty($pal)) { 2684 $this->Error('Missing palette in '.$file); 2685 }; 2686 fclose($f); 2687 return array('w' => $w, 2688 'h' => $h, 2689 'cs' => $colspace, 2690 'bpc' => $bpc, 2691 'f' => 'FlateDecode', 2692 'parms' => $parms, 2693 'pal' => $pal, 2694 'trns' => $trns, 2695 'data' => $data); 2696 } 2697 2698 /** 2699 * Read a 4-byte integer from file 2700 */ 2701 function _freadint($f) { 2702 $a=unpack('Ni',fread($f,4)); 2703 return $a['i']; 2704 } 2705 2706 /** 2707 * Format a text string 2708 */ 2709 function _textstring($s) { 2710 return '('.$this->_escape($s).')'; 2711 } 2712 2713 /** 2714 * Add \ before \, ( and ) 2715 */ 2716 function _escape($s) { 2717 return str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$s))); 2718 } 2719 2720 function _putstream($s) { 2721 $this->_out('stream'); 2722 $this->_out($s); 2723 $this->_out('endstream'); 2724 } 2725 2726 /** 2727 * Add a line to the document 2728 */ 2729 function _out($s) { 2730 if ($this->state == FPDF_STATE_PAGE_STARTED) { 2731 $this->pages[$this->page].=$s."\n"; 2732 } else { 2733 $this->buffer.=$s."\n"; 2734 } 2735 } 2736 } 2737} 2738?> 2739