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