Files
luos/gulliver/thirdparty/html2ps_pdf/fpdf/fpdf.php

3213 lines
103 KiB
PHP
Executable File
Raw Blame History

<?php
/*******************************************************************************
* Software: FPDF *
* Version: 1.53 *
* Date: 2004-12-31 *
* Author: Olivier PLATHEY *
* License: Freeware *
* *
* You may use, modify and redistribute this software as you wish. *
*******************************************************************************/
/**
* Heavily patched to adapt to the HTML2PS/HTML2PDF script requirements by
* Konstantin Bournayev (bkon@bkon.ru)
*/
if (!class_exists('FPDF')) {
define('FPDF_VERSION','1.53');
/**
* See PDF Reference 1.6 p.664 for explanation of flags specific to submit form action
*/
define('PDF_SUBMIT_FORM_HTML', 1 << 2); // 1 - HTML, 0 - FDF
define('PDF_SUBMIT_FORM_COORDINATES', 1 << 4);
/**
* See PDF Reference 1.6 p.656 for explanation of flags specific to choice fields
*/
define('PDF_FIELD_CHOICE_COMBO', 1 << 17);
/**
* See PDF Reference 1.6 p.653 for explanation of flags specific to text fields
*/
define('PDF_FIELD_TEXT_MULTILINE',1 << 12);
define('PDF_FIELD_TEXT_PASSWORD', 1 << 13);
define('PDF_FIELD_TEXT_FILE', 1 << 20);
/**
* See PDF Reference 1.6 p.663 for examplanation of flags specific to for submit actions
*/
define("PDF_FORM_SUBMIT_EXCLUDE", 1 << 0);
define("PDF_FORM_SUBMIT_NOVALUE", 1 << 1);
define("PDF_FORM_SUBMIT_EFORMAT", 1 << 2);
define("PDF_FORM_SUBMIT_GET", 1 << 3);
class PDFIndirectObject {
var $object_id;
var $generation_id;
function get_object_id() {
return $this->object_id;
}
function get_generation_id() {
return $this->generation_id;
}
/**
* Outputs the PDF indirect object to PDF file.
*
* To pervent infinite loop on circular references, this method checks
* if current object have been already written to the file.
*
* Note that, in general, nested objects should be written to PDF file
* here too; this task is accomplished by calling _out_nested method,
* which should be overridden by children classes.
*
* @param FPDF $handler PDF file wrapper (FPDF object)
*
* @final
*
* @see FPDF::is_object_written
* @see PDFIndirectObject::_out_nested
*/
function out(&$handler) {
if (!$handler->is_object_written($this->get_object_id())) {
$handler->offsets[$this->get_object_id()] = strlen($handler->buffer);
$handler->_out($handler->_indirect_object($this));
$this->_out_nested($handler);
};
}
/**
* Writes all nested objects to the PDF file. Should be overridden by
* PDFIndirectObject descendants.
*
* @param FPDF $handler PDF file wrapper (FPDF object)
*
* @see PDFIndirectObject::out
*/
function _out_nested(&$handler) {
return true;
}
function PDFIndirectObject(&$handler,
$object_id,
$generation_id) {
$this->object_id = $object_id;
$this->generation_id = $generation_id;
}
function pdf(&$handler) {
return $handler->_dictionary($this->_dict($handler));
}
function _dict() {
return array();
}
}
class PDFPage extends PDFIndirectObject {
var $annotations;
function PDFPage(&$handler,
$object_id,
$generation_id) {
$this->PDFIndirectObject($handler,
$object_id,
$generation_id);
}
function add_annotation(&$annotation) {
$this->annotations[] =& $annotation;
}
function _annotations(&$handler) {
return $handler->_reference_array($this->annotations);
}
}
class PDFAppearanceStream extends PDFIndirectObject {
var $_content;
function PDFAppearanceStream(&$handler,
$object_id,
$generation_id,
$content) {
$this->PDFIndirectObject($handler,
$object_id,
$generation_id);
$this->_content = $content;
}
function pdf(&$handler) {
$dict_content = array(
'Type' => "/XObject",
'Subtype' => "/Form",
'FormType' => "1",
'BBox' => "[0 0 100 100]",
'Matrix' => "[1 0 0 1 0 0]",
'Resources'=> "2 0 R",
'Length' => strlen($this->_content)
);
$content = $handler->_dictionary($dict_content);
$content .= "\n";
$content .= $handler->_stream($this->_content);
return $content;
}
}
class PDFAnnotation extends PDFIndirectObject {
function PDFAnnotation(&$handler,
$object_id,
$generation_id) {
$this->PDFIndirectObject($handler,
$object_id,
$generation_id);
}
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array("Type" => $handler->_name("Annot")));
}
}
class PDFRect {
var $x;
var $y;
var $w;
var $h;
function PDFRect($x,$y,$w,$h) {
$this->x = $x;
$this->y = $y;
$this->w = $w;
$this->h = $h;
}
function left(&$handler) {
return $handler->x_coord($this->x);
}
function right(&$handler) {
return $handler->x_coord($this->x+$this->w);
}
function top(&$handler) {
return $handler->y_coord($this->y);
}
function bottom(&$handler) {
return $handler->y_coord($this->y+$this->h);
}
function pdf(&$handler) {
return $handler->_array(sprintf("%.2f %.2f %.2f %.2f",
$this->left($handler),
$this->top($handler),
$this->right($handler),
$this->bottom($handler)));
}
}
class PDFAnnotationExternalLink extends PDFAnnotation {
var $rect;
var $link;
function PDFAnnotationExternalLink(&$handler,
$object_id,
$generation_id,
$rect,
$link) {
$this->PDFAnnotation($handler,
$object_id,
$generation_id);
$this->rect = $rect;
$this->link = $link;
}
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array(
'Subtype' => "/Link",
'Rect' => $this->rect->pdf($handler),
'Border' => "[0 0 0]",
'A' => "<</S /URI /URI ".$handler->_textstring($this->link).">>"
));
}
}
class PDFAnnotationInternalLink extends PDFAnnotation {
var $rect;
var $link;
function PDFAnnotationInternalLink(&$handler,
$object_id,
$generation_id,
$rect,
$link) {
$this->PDFAnnotation($handler,
$object_id,
$generation_id);
$this->rect = $rect;
$this->link = $link;
}
function pdf(&$handler) {
if ($handler->DefOrientation=='P') {
$wPt=$handler->fwPt;
$hPt=$handler->fhPt;
} else {
$wPt=$handler->fhPt;
$hPt=$handler->fwPt;
};
$l = $handler->links[$this->link];
$h = isset($handler->OrientationChanges[$l[0]]) ? $wPt : $hPt;
/**
* Sometimes hyperlinks may refer to pages NOT present in PDF document
* Example: a very long frame content; it it trimmed to one page, as
* framesets newer take more than one frame. A link targe which should be rendered
* on third page without frames will be never rendered at all.
*
* In this case we should disable link at all to prevent error from appearing
*/
if (!isset($handler->_pages[$l[0]-1])) {
return "";
}
$content = $handler->_dictionary(array(
'Type' => "/Annot",
'Subtype' => "/Link",
'Rect' => $this->rect->pdf($handler),
'Border' => "[0 0 0]",
'Dest' => sprintf("[%s /XYZ 0 %.2f null]",
$handler->_reference($handler->_pages[$l[0]-1]),
$h-$l[1]*$handler->k)
));
return $content;
}
}
class PDFAnnotationWidget extends PDFAnnotation {
var $_rect;
function PDFAnnotationWidget(&$handler,
$object_id,
$generation_id,
$rect) {
$this->PDFAnnotation($handler,
$object_id,
$generation_id);
$this->_rect = $rect;
}
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array("Subtype" => $handler->_name("Widget"),
'Rect' => $this->_rect->pdf($handler)));
}
}
/**
* Generic PDF Form
*/
class PDFFieldGroup extends PDFIndirectObject {
var $_kids;
var $_group_name;
function PDFFieldGroup(&$handler,
$object_id,
$generation_id,
$group_name) {
$this->PDFIndirectObject($handler,
$object_id,
$generation_id);
/**
* Generate default group name, if needed
*/
if (is_null($group_name) || $group_name == "") {
$group_name = sprintf("FieldGroup%d", $this->get_object_id());
};
$this->_group_name = $group_name;
$this->_kids = array();
}
function _check_field_name($field) {
/**
* Check if field name is empty
*/
if (trim($field->get_field_name()) == "") {
error_log(sprintf("Found form field with empty name"));
return false;
};
/**
* Check if field name is unique inside this form! If we will not do it,
* some widgets may become inactive (ignored by PDF Reader)
*/
foreach ($this->_kids as $kid) {
if ($kid->get_field_name() == $field->get_field_name()) {
error_log(sprintf("Interactive form '%s' already contains field named '%s'",
$this->_group_name,
$kid->get_field_name()));
return false;
}
};
return true;
}
function add_field(&$field) {
if (!$this->_check_field_name($field)) {
/**
* Field name is not unique; replace it with automatically-generated one
*/
$field->set_field_name(sprintf("%s_FieldObject%d",
$field->get_field_name(),
$field->get_object_id()));
};
$this->_kids[] =& $field;
$field->set_parent($this);
}
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array("Kids" => $handler->_reference_array($this->_kids),
"T" => $handler->_textstring($this->_group_name)));
return $content;
}
function _out_nested(&$handler) {
parent::_out_nested($handler);
foreach ($this->_kids as $field) {
$field->out($handler);
}
}
}
/**
* Generic superclass for all PDF interactive field widgets
*/
class PDFField extends PDFAnnotationWidget {
/**
* @var string Partial field name (see PDF Specification 1.6 p.638 for explanation on "partial" and
* "fully qualified" field names
* @access private
*/
var $_field_name;
/**
* @var PDFFieldGroup REference to a containing form object
* @access private
*/
var $_parent;
function PDFField(&$handler,
$object_id,
$generation_id,
$rect,
$field_name) {
$this->PDFAnnotationWidget($handler,
$object_id,
$generation_id,
$rect);
/**
* Generate default field name, if needed
* @TODO: validate field_name contents
*/
if (is_null($field_name) || $field_name == "") {
$field_name = sprintf("FieldObject%d", $this->get_object_id());
};
$this->_field_name = $field_name;
}
function get_field_name() {
if ($this->_field_name) {
return $this->_field_name;
} else {
return sprintf("FormObject%d", $this->get_object_id());
};
}
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array("Parent" => $handler->_reference($this->_parent),
"T" => $handler->_textstring($this->get_field_name())));
}
function pdf(&$handler) {
return $handler->_dictionary($this->_dict($handler));
}
function set_field_name($value) {
$this->_field_name = $value;
}
function set_parent(&$form) {
$this->_parent =& $form;
}
function get_parent() {
return $this->_parent;
}
}
/**
* Checkbox interactive form widget
*/
class PDFFieldCheckBox extends PDFField {
var $_value;
var $_appearance_on;
var $_appearance_off;
var $_checked;
function PDFFieldCheckBox(&$handler,
$object_id,
$generation_id,
$rect,
$field_name,
$value,
$checked) {
$this->PDFField($handler,
$object_id,
$generation_id,
$rect,
$field_name);
$this->_value = $value;
$this->_checked = $checked;
$this->_appearance_on = new PDFAppearanceStream($handler,
$handler->_generate_new_object_number(),
$generation_id,
"Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q");
$this->_appearance_off = new PDFAppearanceStream($handler,
$handler->_generate_new_object_number(),
$generation_id,
"Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q");
}
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array(
'FT' => '/Btn',
'Ff' => sprintf("%d", 0),
'TU' => "<FEFF>",
'MK' => "<< /CA (3) >>",
'DV' => $this->_checked ? $handler->_name($this->_value) : "/Off",
'V' => $this->_checked ? $handler->_name($this->_value) : "/Off",
'AP' => sprintf("<< /N << /%s %s /Off %s >> >>",
$this->_value,
$handler->_reference($this->_appearance_on),
$handler->_reference($this->_appearance_off))
)
);
}
function _out_nested(&$handler) {
parent::_out_nested($handler);
$this->_appearance_on->out($handler);
$this->_appearance_off->out($handler);
}
}
class PDFFieldPushButton extends PDFField {
var $_appearance;
var $fontindex;
var $fontsize;
function _out_nested(&$handler) {
parent::_out_nested($handler);
$this->_appearance->out($handler);
}
function PDFFieldPushButton(&$handler,
$object_id,
$generation_id,
$rect,
$fontindex,
$fontsize) {
$this->PDFField($handler,
$object_id,
$generation_id,
$rect,
null);
$this->fontindex = $fontindex;
$this->fontsize = $fontsize;
$this->_appearance = new PDFAppearanceStream($handler,
$handler->_generate_new_object_number(),
$generation_id,
"Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q");
}
function _action(&$handler) {
return "<< >>";
}
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array(
'FT' => '/Btn',
'Ff' => sprintf("%d", 1 << 16),
'TU' => "<FEFF>",
'DR' => "2 0 R",
'DA' => sprintf("(0 0 0 rg /F%d %.2f Tf)",
$this->fontindex,
$this->fontsize),
'AP' => "<< /N ".$handler->_reference($this->_appearance)." >>",
'AA' => $this->_action($handler)
));
}
}
class PDFFieldPushButtonImage extends PDFFieldPushButton {
var $_link;
function PDFFieldPushButtonImage(&$handler,
$object_id,
$generation_id,
$rect,
$fontindex,
$fontsize,
$field_name,
$value,
$link) {
$this->PDFFieldPushButton($handler,
$object_id,
$generation_id,
$rect,
$fontindex,
$fontsize);
$this->_link = $link;
$this->set_field_name($field_name);
}
function _action(&$handler) {
$action = $handler->_dictionary(array(
'S' => "/SubmitForm",
'F' => $handler->_textstring($this->_link),
'Fields'=> $handler->_reference_array(array($this->get_parent())),
'Flags' => PDF_SUBMIT_FORM_HTML | PDF_SUBMIT_FORM_COORDINATES
)
);
return $handler->_dictionary(array('U' => $action));
}
}
class PDFFieldPushButtonSubmit extends PDFFieldPushButton {
var $_link;
var $_caption;
function PDFFieldPushButtonSubmit(&$handler,
$object_id,
$generation_id,
$rect,
$fontindex,
$fontsize,
$field_name,
$value,
$link) {
$this->PDFFieldPushButton($handler,
$object_id,
$generation_id,
$rect,
$fontindex,
$fontsize);
$this->_link = $link;
$this->_caption = $value;
$this->set_field_name($field_name);
}
function _action(&$handler) {
$action = $handler->_dictionary(array(
'S' => "/SubmitForm",
'F' => $handler->_textstring($this->_link),
'Fields'=> $handler->_reference_array(array($this->get_parent())),
'Flags' =>
(1 << 2) // ExportFormat (HTML)
)
);
return $handler->_dictionary(array('U' => $action));
}
}
class PDFFieldPushButtonReset extends PDFFieldPushButton {
function PDFFieldPushButtonReset(&$handler,
$object_id,
$generation_id,
$rect,
$fontindex,
$fontsize) {
$this->PDFFieldPushButton($handler,
$object_id,
$generation_id,
$rect,
$fontindex,
$fontsize);
}
function _action(&$handler) {
$action = $handler->_dictionary(array('S' => "/ResetForm"));
return $handler->_dictionary(array('U' => $action));
}
}
/**
* Radio button inside the group.
*
* Note that radio button is not a field itself; only a group of radio buttons
* should have name.
*/
class PDFFieldRadio extends PDFAnnotationWidget {
/**
* @var PDFFieldRadioGroup reference to a radio button group
* @access private
*/
var $_parent;
/**
* @var String value of this radio button
* @access private
*/
var $_value;
var $_appearance_on;
var $_appearance_off;
function PDFFieldRadio(&$handler,
$object_id,
$generation_id,
$rect,
$value) {
$this->PDFAnnotationWidget($handler,
$object_id,
$generation_id,
$rect);
$this->_value = $value;
$this->_appearance_on = new PDFAppearanceStream($handler,
$handler->_generate_new_object_number(),
$generation_id,
"Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q");
$this->_appearance_off = new PDFAppearanceStream($handler,
$handler->_generate_new_object_number(),
$generation_id,
"Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q");
}
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array(
'MK' => "<< /CA (l) >>",
'Parent' => $handler->_reference($this->_parent),
'AP' => sprintf("<< /N << /%s %s /Off %s >> >>",
$this->_value,
$handler->_reference($this->_appearance_on),
$handler->_reference($this->_appearance_off))
));
}
function _out_nested(&$handler) {
parent::_out_nested($handler);
$this->_appearance_on->out($handler);
$this->_appearance_off->out($handler);
}
/**
* Set a reference to the radio button group containing this group
*
* @param PDFFieldRadioGroup $parent reference to a group object
*/
function set_parent(&$parent) {
$this->_parent =& $parent;
}
}
/**
* Create new group of radio buttons
*/
class PDFFieldRadioGroup extends PDFFieldGroup {
var $_parent;
var $_checked;
function _dict($handler) {
return array_merge(parent::_dict($handler),
array(
'DV' => $this->_checked ? $handler->_name($this->_checked) : "/Off",
'V' => $this->_checked ? $handler->_name($this->_checked) : "/Off",
"FT" => $handler->_name('Btn'),
"Ff" => sprintf("%d", 1 << 15),
"Parent" => $handler->_reference($this->_parent)
));
}
function _check_field_name($field) {
/**
* As radio buttons always have same field name, no checking should be made here
*/
return true;
}
function PDFFieldRadioGroup(&$handler,
$object_id,
$generation_id,
$group_name) {
$this->PDFFieldGroup($handler,
$object_id,
$generation_id,
$group_name);
$this->_checked = null;
}
/**
* @return String name of the radio group
*/
function get_field_name() {
return $this->_group_name;
}
function set_checked($value) {
$this->_checked = $value;
}
function set_parent(&$parent) {
$this->_parent =& $parent;
}
}
class PDFFieldSelect extends PDFField {
var $_options;
var $_value;
function _dict(&$handler) {
$options = array();
foreach ($this->_options as $arr) {
$options[] = $handler->_array(sprintf("%s %s",
$handler->_textstring($arr[0]),
$handler->_textstring($arr[1])));
};
$options_str = $handler->_array(implode(" ",$options));
return array_merge(parent::_dict($handler),
array('FT' => '/Ch',
'Ff' => PDF_FIELD_CHOICE_COMBO,
'V' => $handler->_textstring($this->_value), // Current value
'DV' => $handler->_textstring($this->_value), // Default value
'DR' => "2 0 R",
'Opt' => $options_str));
}
function PDFFieldSelect(&$handler,
$object_id,
$generation_id,
$rect,
$field_name,
$value,
$options) {
$this->PDFField($handler,
$object_id,
$generation_id,
$rect,
$field_name);
$this->_options = $options;
$this->_value = $value;
}
}
/**
* Interactive text input
*/
class PDFFieldText extends PDFField {
var $fontindex;
var $fontsize;
var $_appearance;
/**
* @var String contains the default value of this text field
* @access private
*/
var $_value;
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array(
'FT' => '/Tx',
'V' => $handler->_textstring($this->_value), // Current value
'DV' => $handler->_textstring($this->_value), // Default value
'DR' => "2 0 R",
// @TODO fix font references
'DA' => sprintf("(0 0 0 rg /FF%d %.2f Tf)",
$this->fontindex,
$this->fontsize),
// 'AP' => $handler->_dictionary(array("N" => $handler->_reference($this->_appearance))),
));
}
function _out_nested(&$handler) {
// $this->_appearance->out($handler);
}
function PDFFieldText(&$handler,
$object_id,
$generation_id,
$rect,
$field_name,
$value,
$fontindex,
$fontsize) {
$this->PDFField($handler,
$object_id,
$generation_id,
$rect,
$field_name);
$this->fontindex = $fontindex;
$this->fontsize = $fontsize;
$this->_value = $value;
// $this->_appearance = new PDFAppearanceStream($handler,
// $handler->_generate_new_object_number(),
// $generation_id,
// "/Tx BMC EMC");
}
}
class PDFFieldMultilineText extends PDFFieldText {
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array('Ff' => PDF_FIELD_TEXT_MULTILINE));
}
}
/**
* "Password" text input field
*/
class PDFFieldPassword extends PDFFieldText {
function PDFFieldPassword(&$handler,
$object_id,
$generation_id,
$rect,
$field_name,
$value,
$fontindex,
$fontsize) {
$this->PDFFieldText($handler,
$object_id,
$generation_id,
$rect,
$field_name,
$value,
$fontindex,
$fontsize);
}
function _dict(&$handler) {
return array_merge(parent::_dict($handler),
array('Ff' => PDF_FIELD_TEXT_PASSWORD));
}
}
class FPDF {
//Private properties
var $page; //current page number
var $n; //current object number
var $offsets; //array of object offsets
var $buffer; //buffer holding in-memory PDF
var $pages; //array containing pages
var $state; //current document state
var $compress; //compression flag
var $DefOrientation; //default orientation
var $CurOrientation; //current orientation
var $OrientationChanges; //array indicating orientation changes
var $k; //scale factor (number of points in user unit)
var $fwPt,$fhPt; //dimensions of page format in points
var $fw,$fh; //dimensions of page format in user unit
var $wPt,$hPt; //current dimensions of page in points
var $w,$h; //current dimensions of page in user unit
var $lMargin; //left margin
var $tMargin; //top margin
var $rMargin; //right margin
var $bMargin; //page break margin
var $cMargin; //cell margin
var $x,$y; //current position in user unit for cell positioning
var $lasth; //height of last cell printed
var $LineWidth; //line width in user unit
var $fonts; //array of used fonts
var $FontFiles; //array of font files
var $diffs; //array of encoding differences
var $images; //array of used images
// var $PageLinks; //array of links in pages
var $links; //array of internal links
var $FontFamily; //current font family
var $FontStyle; //current font style
var $underline; //underlining flag
var $overline;
var $strikeout;
var $CurrentFont; //current font info
var $FontSizePt; //current font size in points
var $FontSize; //current font size in user unit
var $DrawColor; //commands for drawing color
var $FillColor; //commands for filling color
var $TextColor; //commands for text color
var $ColorFlag; //indicates whether fill and text colors are different
var $ws; //word spacing
var $AutoPageBreak; //automatic page breaking
var $PageBreakTrigger; //threshold used to trigger page breaks
var $InFooter; //flag set when processing footer
var $ZoomMode; //zoom display mode
var $LayoutMode; //layout display mode
var $title; //title
var $subject; //subject
var $author; //author
var $keywords; //keywords
var $creator; //creator
var $AliasNbPages; //alias for total number of pages
var $PDFVersion; //PDF version number
var $_forms;
var $_form_radios;
var $_pages;
function moveto($x, $y) {
$this->_out(sprintf("%.2f %.2f m",
$this->x_coord($x),
$this->y_coord($y)));
}
function lineto($x, $y) {
$this->_out(sprintf("%.2f %.2f l",
$this->x_coord($x),
$this->y_coord($y)));
}
function closepath() {
$this->_out("h");
}
function stroke() {
$this->_out("S");
}
function is_object_written($id) {
return isset($this->offsets[$id]);
}
function x_coord($x) {
return $x * $this->k;
}
function y_coord($y) {
return ($this->h - $y)*$this->k;
}
// PDF specs:
// 3.2.9 Indirect Objects
// Any object in a PDF file may be labeled as an indirect object. This gives the object
// a unique object identifier by which other objects can refer to it (for example, as an
// element of an array or as the value of a dictionary entry). The object identifier
// consists of two parts:
// * A positive integer object number. Indirect objects are often numbered sequentially
// within a PDF file, but this is not required; object numbers may be
// assigned in any arbitrary order.
// * A non-negative integer generation number. In a newly created file, all indirect
// objects have generation numbers of 0. Nonzero generation numbers may be introduced
// when the file is later updated; see Sections 3.4.3, <20>Cross-Reference
// Table,<2C> and 3.4.5, <20>Incremental Updates.<2E>
// Together, the combination of an object number and a generation number uniquely
// identifies an indirect object. The object retains the same object number and
// generation number throughout its existence, even if its value is modified.
//
function _indirect_object($object) {
$object_number = $object->get_object_id();
$generation_number = $object->get_generation_id();
$object_string = $object->pdf($this);
$this->offsets[$object_number] = strlen($this->buffer);
return "$object_number $generation_number obj\n${object_string}\nendobj";
}
function _stream($content) {
return "stream\n".$content."\nendstream";
}
/**
* @TODO check name for validity
*/
function _name($name) {
return sprintf("/%s", $name);
}
function _dictionary($dict) {
$content = "";
foreach ($dict as $key => $value) {
$content .= "/$key $value\n";
};
return "<<\n".$content."\n>>";
}
function _array($array_str) {
return "[$array_str]";
}
function _reference(&$object) {
$object_id = $object->get_object_id();
$generation_id = $object->get_generation_id();
return "$object_id $generation_id R";
}
function _reference_array($object_array) {
$array_str = "";
for ($i=0; $i<count($object_array); $i++) {
$array_str .= $this->_reference($object_array[$i])." ";
};
return $this->_array($array_str);
}
function _generate_new_object_number() {
$this->n++;
return $this->n;
}
function add_form($name) {
$form = new PDFFieldGroup($this,
$this->_generate_new_object_number(), // Object identifier
0,
$name);
$this->_forms[] =& $form;
}
function add_field_select($x, $y, $w, $h, $name, $value, $options) {
$field =& new PDFFieldSelect($this,
$this->_generate_new_object_number(), // Object identifier
0, // Generation
new PDFRect($x, $y, $w, $h), // Annotation rectangle
$name, // Field name
$value,
$options);
$current_form =& $this->current_form();
$current_form->add_field($field);
$this->_pages[count($this->_pages)-1]->add_annotation($field);
}
/**
* Create new checkbox field object
*
* @param $x Integer Left coordinate of the widget bounding bog
* @param $y Integer Upper coordinate of the widget bounding bog
* @param $w Integer Widget width
* @param $h Integer Widget height
* @param $name String name of the field to be created
* @param $value String value to be posted for this checkbox
*
* @TODO check if fully qualified field name will be unique in PDF file
*/
function add_field_checkbox($x, $y, $w, $h, $name, $value, $checked) {
$field =& new PDFFieldCheckBox($this,
$this->_generate_new_object_number(), // Object identifier
0, // Generation
new PDFRect($x, $y, $w, $h), // Annotation rectangle
$name, // Field name
$value, $checked); // Checkbox "on" value
$current_form =& $this->current_form();
$current_form->add_field($field);
$this->_pages[count($this->_pages)-1]->add_annotation($field);
}
function &current_form() {
if (count($this->_forms) == 0) {
/**
* Handle invalid HTML; if we've met an input control outside the form,
* generate a new form with random name
*/
$id = $this->_generate_new_object_number();
$name = sprintf("AnonymousFormObject_%u", $id);
error_log(sprintf("Anonymous form generated with name %s; check your HTML for validity",
$name));
$form = new PDFFieldGroup($this,
$id, // Object identifier
0,
$name);
$this->_forms[] =& $form;
};
return $this->_forms[count($this->_forms)-1];
}
function add_field_radio($x, $y, $w, $h, $group_name, $value, $checked) {
if (isset($this->_form_radios[$group_name])) {
$field =& $this->_form_radios[$group_name];
} else {
$field =& new PDFFieldRadioGroup($this,
$this->_generate_new_object_number(),
0,
$group_name);
$current_form =& $this->current_form();
$current_form->add_field($field);
$this->_form_radios[$group_name] =& $field;
};
$radio =& new PDFFieldRadio($this,
$this->_generate_new_object_number(),
0,
new PDFRect($x, $y, $w, $h),
$value);
$field->add_field($radio);
if ($checked) { $field->set_checked($value); };
$this->_pages[count($this->_pages)-1]->add_annotation($radio);
}
/**
* Create a new interactive text form
*
* @param $x Left coordinate of the widget bounding box
* @param $y Top coordinate of the widget bounding box
* @param $w Widget width
* @param $h Widget height
* @param $value Default widget value
* @param $field_name Field name
*
* @return Field number
*/
function add_field_text($x, $y, $w, $h, $value, $field_name) {
$field =& new PDFFieldText($this,
$this->_generate_new_object_number(),
0,
new PDFRect($x, $y, $w, $h),
$field_name,
$value,
$this->CurrentFont['i'],
$this->FontSizePt);
$current_form =& $this->current_form();
$current_form->add_field($field);
$this->_pages[count($this->_pages)-1]->add_annotation($field);
}
function add_field_multiline_text($x, $y, $w, $h, $value, $field_name) {
$field =& new PDFFieldMultilineText($this,
$this->_generate_new_object_number(),
0,
new PDFRect($x, $y, $w, $h),
$field_name,
$value,
$this->CurrentFont['i'],
$this->FontSizePt);
$current_form =& $this->current_form();
$current_form->add_field($field);
$this->_pages[count($this->_pages)-1]->add_annotation($field);
}
/**
* Create a new interactive password input field
*
* @param $x Left coordinate of the widget bounding box
* @param $y Top coordinate of the widget bounding box
* @param $w Widget width
* @param $h Widget height
* @param $value Default widget value
* @param $field_name Field name
*
* @return Field number
*/
function add_field_password($x, $y, $w, $h, $value, $field_name) {
$field =& new PDFFieldPassword($this,
$this->_generate_new_object_number(),
0,
new PDFRect($x, $y, $w, $h),
$field_name,
$value,
$this->CurrentFont['i'],
$this->FontSizePt);
$current_form =& $this->current_form();
$current_form->add_field($field);
$this->_pages[count($this->_pages)-1]->add_annotation($field);
}
function add_field_pushbuttonimage($x, $y, $w, $h, $field_name, $value, $actionURL) {
$field =& new PDFFieldPushButtonImage($this,
$this->_generate_new_object_number(),
0,
new PDFRect($x, $y, $w, $h),
$this->CurrentFont['i'],
$this->FontSizePt,
$field_name,
$value,
$actionURL);
$current_form =& $this->current_form();
$current_form->add_field($field);
$this->_pages[count($this->_pages)-1]->add_annotation($field);
}
function add_field_pushbuttonsubmit($x, $y, $w, $h, $field_name, $value, $actionURL) {
$field =& new PDFFieldPushButtonSubmit($this,
$this->_generate_new_object_number(),
0,
new PDFRect($x, $y, $w, $h),
$this->CurrentFont['i'],
$this->FontSizePt,
$field_name,
$value,
$actionURL);
$current_form =& $this->current_form();
$current_form->add_field($field);
$this->_pages[count($this->_pages)-1]->add_annotation($field);
}
function add_field_pushbuttonreset($x, $y, $w, $h) {
$field =& new PDFFieldPushButtonReset($this,
$this->_generate_new_object_number(),
0,
new PDFRect($x, $y, $w, $h),
null,
$this->CurrentFont['i'],
$this->FontSizePt);
$current_form =& $this->current_form();
$current_form->add_field($field);
$this->_pages[count($this->_pages)-1]->add_annotation($field);
}
function add_field_pushbutton($x, $y, $w, $h) {
$field =& new PDFFieldPushButton($this,
$this->_generate_new_object_number(),
0,
new PDFRect($x, $y, $w, $h),
null,
$this->CurrentFont['i'],
$this->FontSizePt);
$current_form =& $this->current_form();
$current_form->add_field($field);
$this->_pages[count($this->_pages)-1]->add_annotation($field);
}
function SetDash($x, $y) {
$x = (int)$x;
$y = (int)$y;
$this->_out(sprintf("[%d %d] 0 d", $x*2, $y*2));
}
function _GetFontBBox() {
return preg_split("/[\[\]\s]+/", $this->CurrentFont['desc']['FontBBox']);
}
function _dounderline($x,$y,$txt) {
//Underline text
$up=$this->CurrentFont['up'];
$ut=$this->CurrentFont['ut'];
$w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
return sprintf('%.2f %.2f %.2f %.2f re f',
$x*$this->k,
($this->h-($y-$up/1000*$this->FontSize))*$this->k,
$w*$this->k,
-$ut/1000*$this->FontSizePt);
}
function _dooverline($x,$y,$txt) {
$bbox = $this->_GetFontBBox();
$up = round($bbox[3] * 0.8);
$ut=$this->CurrentFont['ut'];
$w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
return sprintf('%.2f %.2f %.2f %.2f re f',
$x*$this->k,
($this->h-($y-$up/1000*$this->FontSize))*$this->k,
$w*$this->k,
-$ut/1000*$this->FontSizePt);
}
function _dostrikeout($x,$y,$txt) {
$bbox = $this->_GetFontBBox();
$up = round($bbox[3] * 0.25);
$ut=$this->CurrentFont['ut'];
$w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
return sprintf('%.2f %.2f %.2f %.2f re f',
$x*$this->k,
($this->h-($y-$up/1000*$this->FontSize))*$this->k,
$w*$this->k,
-$ut/1000*$this->FontSizePt);
}
function SetDecoration($underline, $overline, $strikeout) {
$this->underline = $underline;
$this->overline = $overline;
$this->strikeout = $strikeout;
}
function ClipPath($path) {
if (count($path) < 3) {
die("Attempt to clip on the path containing less than three points");
};
$this->MakePath($path);
$this->Clip();
}
function Clip() {
$this->_out("W n");
}
function _LoadFont($fontkey, $family, $encoding, $style) {
$style = strtolower($style);
if (!isset($this->fonts[$fontkey])) {
global $g_font_resolver_pdf;
$file = $g_font_resolver_pdf->ttf_mappings[$family];
$embed = $g_font_resolver_pdf->embed[$family];
// Remove the '.ttf' suffix
$file = substr($file, 0, strlen($file) - 4);
// Generate (if required) PHP font description files
if (!file_exists($this->_getfontpath().$fontkey.'.php')) {
// As MakeFont squeaks a lot, we'll need to capture and discard its output
ob_start();
MakeFont(PDFLIB_TTF_FONTS_REPOSITORY.$file.'.ttf',
PDFLIB_TTF_FONTS_REPOSITORY.$file.'.afm',
$this->_getfontpath(),
$fontkey.'.php',
$encoding);
ob_end_clean();
};
$this->AddFont($fontkey, $family, "", $encoding, $fontkey.'.php', $embed);
};
}
function _MakeFontKey($family, $encoding) {
return $family.'-'.$encoding;
}
function GetFontAscender($name, $encoding) {
$fontkey = $this->_MakeFontKey($name, $encoding);
$this->_LoadFont($fontkey, $name, $encoding, '');
return $this->fonts[$fontkey]['desc']['Ascent'] / 1000;
}
function GetFontDescender($name, $encoding) {
$fontkey = $this->_MakeFontKey($name, $encoding);
$this->_LoadFont($fontkey, $name, $encoding, '');
return -$this->fonts[$fontkey]['desc']['Descent'] / 1000;
}
// Note that FPDF do some caching, which can conflict with "save/restore" pairs
function Save() { $this->_out("q"); }
function Restore() { $this->_out("Q"); }
function Translate($dx, $dy) {
$this->_out(sprintf("1 0 0 1 %.2f %.2f cm", $dx, $dy));
}
function Rotate($alpha) {
$this->_out(sprintf("%.2f %.2f %.2f %.2f 0 0 cm",
cos($alpha/180*pi()),
sin($alpha/180*pi()),
-sin($alpha/180*pi()),
cos($alpha/180*pi())
));
}
function SetTextRendering($mode) {
$this->_out(sprintf("%d Tr", $mode));
}
function MakePath($path) {
$this->_out(sprintf("%.2f %.2f m", $path[0]['x'], $path[0]['y']));
for ($i=1; $i<count($path); $i++) {
$this->_out(sprintf("%.2f %.2f l", $path[$i]['x'], $path[$i]['y']));
};
}
function FillPath($path) {
if (count($path) < 3) {
die("Attempt to fill path containing less than three points");
};
$this->_out($this->FillColor);
$this->MakePath($path);
$this->Fill();
}
function Fill() {
$this->_out("f");
}
// Thanks G. Adam Stanislav for information about approximation circle using bezier curves
// http://www.whizkidtech.redprince.net/bezier/circle/
//
function Circle($x, $y, $r) {
$kappa = (sqrt(2) - 1) / 3 * 4;
$l = $kappa * $r;
$this->_out(sprintf("%.2f %.f2 m", $x + $r, $y));
$this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c",
$x + $r, $y + $l,
$x + $l, $y + $r,
$x, $y + $r));
$this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c",
$x - $l, $y + $r,
$x - $r, $y + $l,
$x - $r, $y));
$this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c",
$x - $r, $y - $l,
$x - $l, $y - $r,
$x, $y - $r));
$this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c",
$x + $l, $y - $r,
$x + $r, $y - $l,
$x + $r, $y));
}
/*******************************************************************************
* *
* Public methods *
* *
*******************************************************************************/
function FPDF($orientation='P',$unit='mm',$format='A4') {
$this->_forms = array();
$this->_form_radios = array();
$this->_pages = array();
//Some checks
$this->_dochecks();
//Initialization of properties
$this->page=0;
$this->n=2;
$this->buffer='';
$this->pages=array();
$this->OrientationChanges=array();
$this->state=0;
$this->fonts=array();
$this->FontFiles=array();
$this->diffs = array();
$this->images = array();
$this->links = array();
$this->InFooter=false;
$this->lasth=0;
$this->FontFamily='';
$this->FontStyle='';
$this->FontSizePt=12;
$this->underline = false;
$this->overline = false;
$this->strikeout = false;
$this->DrawColor='0 G';
$this->FillColor='0 g';
$this->TextColor='0 g';
$this->ColorFlag=false;
$this->ws=0;
//Scale factor
switch ($unit) {
case 'pt':
$this->k = 1; break;
case 'mm':
$this->k = 72/25.4; break;
case 'cm':
$this->k = 72/2.54; break;
case 'in':
$this->k = 72;
default:
$this->Error('Incorrect unit: '.$unit);
};
//Page format
if (is_string($format)) {
$format=strtolower($format);
switch ($format) {
case 'a3':
$format=array(841.89,1190.55); break;
case 'a4':
$format=array(595.28,841.89); break;
case 'a5':
$format=array(420.94,595.28); break;
case 'letter':
$format=array(612,792); break;
case 'legal':
$format=array(612,1008); break;
default:
$this->Error('Unknown page format: '.$format);
};
$this->fwPt=$format[0];
$this->fhPt=$format[1];
} else {
$this->fwPt=$format[0]*$this->k;
$this->fhPt=$format[1]*$this->k;
};
$this->fw=$this->fwPt/$this->k;
$this->fh=$this->fhPt/$this->k;
//Page orientation
$orientation=strtolower($orientation);
if($orientation=='p' || $orientation=='portrait') {
$this->DefOrientation='P';
$this->wPt=$this->fwPt;
$this->hPt=$this->fhPt;
} elseif($orientation=='l' || $orientation=='landscape') {
$this->DefOrientation='L';
$this->wPt=$this->fhPt;
$this->hPt=$this->fwPt;
} else {
$this->Error('Incorrect orientation: '.$orientation);
};
$this->CurOrientation=$this->DefOrientation;
$this->w=$this->wPt/$this->k;
$this->h=$this->hPt/$this->k;
//Page margins (1 cm)
$margin=28.35/$this->k;
$this->SetMargins($margin,$margin);
//Interior cell margin (1 mm)
$this->cMargin=$margin/10;
//Line width (0.2 mm)
$this->LineWidth=.567/$this->k;
//Automatic page break
$this->SetAutoPageBreak(true,2*$margin);
//Full width display mode
$this->SetDisplayMode('fullwidth');
//Enable compression
$this->SetCompression(true);
//Set default PDF version number
$this->PDFVersion='1.3';
}
function SetMargins($left,$top,$right=-1)
{
//Set left, top and right margins
$this->lMargin=$left;
$this->tMargin=$top;
if($right==-1)
$right=$left;
$this->rMargin=$right;
}
function SetLeftMargin($margin)
{
//Set left margin
$this->lMargin=$margin;
if($this->page>0 && $this->x<$margin)
$this->x=$margin;
}
function SetTopMargin($margin)
{
//Set top margin
$this->tMargin=$margin;
}
function SetRightMargin($margin)
{
//Set right margin
$this->rMargin=$margin;
}
function SetAutoPageBreak($auto,$margin=0)
{
//Set auto page break mode and triggering margin
$this->AutoPageBreak=$auto;
$this->bMargin=$margin;
$this->PageBreakTrigger=$this->h-$margin;
}
function SetDisplayMode($zoom,$layout='continuous')
{
//Set display mode in viewer
if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom))
$this->ZoomMode=$zoom;
else
$this->Error('Incorrect zoom display mode: '.$zoom);
if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default')
$this->LayoutMode=$layout;
else
$this->Error('Incorrect layout display mode: '.$layout);
}
function SetCompression($compress)
{
//Set page compression
if(function_exists('gzcompress'))
$this->compress=$compress;
else
$this->compress=false;
}
function SetTitle($title)
{
//Title of document
$this->title=$title;
}
function SetSubject($subject)
{
//Subject of document
$this->subject=$subject;
}
function SetAuthor($author)
{
//Author of document
$this->author=$author;
}
function SetKeywords($keywords)
{
//Keywords of document
$this->keywords=$keywords;
}
function SetCreator($creator)
{
//Creator of document
$this->creator=$creator;
}
function AliasNbPages($alias='{nb}')
{
//Define an alias for total number of pages
$this->AliasNbPages=$alias;
}
function Error($msg)
{
//Fatal error
die('<B>FPDF error: </B>'.$msg);
}
function Open() {
//Begin document
$this->state=1;
}
function Close() {
//Terminate document
if ($this->state==3) {
return;
};
if ($this->page==0) {
$this->AddPage();
};
//Page footer
$this->InFooter=true;
$this->Footer();
$this->InFooter=false;
//Close page
$this->_endpage();
//Close document
$this->_enddoc();
}
function AddPage($orientation='') {
$this->_pages[] =& new PDFPage($this, $this->_generate_new_object_number(), 0);
//Start a new page
if ($this->state==0) {
$this->Open();
};
$family=$this->FontFamily;
$style=$this->FontStyle.($this->underline ? 'U' : '');
$size=$this->FontSizePt;
$lw=$this->LineWidth;
$dc=$this->DrawColor;
$fc=$this->FillColor;
$tc=$this->TextColor;
$cf=$this->ColorFlag;
if ($this->page>0) {
//Page footer
$this->InFooter=true;
$this->Footer();
$this->InFooter=false;
//Close page
$this->_endpage();
}
//Start new page
$this->_beginpage($orientation);
//Set line cap style to square
$this->_out('2 J');
//Set line width
$this->LineWidth=$lw;
$this->_out(sprintf('%.2f w',$lw*$this->k));
//Set colors
$this->DrawColor=$dc;
if ($dc!='0 G') {
$this->_out($dc);
};
$this->FillColor=$fc;
if ($fc!='0 g') {
$this->_out($fc);
};
$this->TextColor=$tc;
$this->ColorFlag=$cf;
//Page header
$this->Header();
//Restore line width
if ($this->LineWidth!=$lw) {
$this->LineWidth=$lw;
$this->_out(sprintf('%.2f w',$lw*$this->k));
}
//Restore colors
if ($this->DrawColor!=$dc) {
$this->DrawColor=$dc;
$this->_out($dc);
}
if ($this->FillColor!=$fc) {
$this->FillColor=$fc;
$this->_out($fc);
}
$this->TextColor=$tc;
$this->ColorFlag=$cf;
if (!is_null($this->CurrentFont)) {
$this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
};
}
function Header()
{
//To be implemented in your own inherited class
}
function Footer()
{
//To be implemented in your own inherited class
}
function PageNo()
{
//Get current page number
return $this->page;
}
function SetDrawColor($r,$g=-1,$b=-1) {
// Set color for all stroking operations
if (($r==0 && $g==0 && $b==0) || $g==-1) {
$new_color = sprintf('%.3f G',$r/255);
} else {
$new_color = sprintf('%.3f %.3f %.3f RG',$r/255,$g/255,$b/255);
};
if ($this->page > 0 /*&& $this->DrawColor != $new_color*/) {
$this->DrawColor = $new_color;
$this->_out($this->DrawColor);
};
}
function SetFillColor($r,$g=-1,$b=-1) {
// Set color for all filling operations
if (($r==0 && $g==0 && $b==0) || $g==-1) {
$new_color = sprintf('%.3f g',$r/255);
} else {
$new_color = sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255);
};
if ($this->page>0 /*&& $this->FillColor != $new_color*/) {
$this->FillColor = $new_color;
$this->ColorFlag=($this->FillColor!=$this->TextColor);
$this->_out($this->FillColor);
};
}
function SetTextColor($r,$g=-1,$b=-1) {
//Set color for text
if (($r==0 && $g==0 && $b==0) || $g==-1) {
$this->TextColor=sprintf('%.3f g',$r/255);
} else {
$this->TextColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255);
};
$this->ColorFlag=($this->FillColor!=$this->TextColor);
}
function GetStringWidth($s) {
//Get width of a string in the current font
$s=(string)$s;
$cw = &$this->CurrentFont['cw'];
$w=0;
$l=strlen($s);
for($i=0; $i<$l; $i++) {
$w+=$cw[$s{$i}];
};
return $w*$this->FontSize/1000;
}
function SetLineWidth($width) {
//Set line width
$this->LineWidth=$width;
if($this->page>0)
$this->_out(sprintf('%.2f w',$width*$this->k));
}
function Line($x1,$y1,$x2,$y2) {
//Draw a line
$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));
}
function Rect($x,$y,$w,$h,$style='')
{
//Draw a rectangle
if($style=='F')
$op='f';
elseif($style=='FD' || $style=='DF')
$op='B';
else
$op='S';
$this->_out(sprintf('%.2f %.2f %.2f %.2f re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op));
}
function AddFont($fontkey, $family, $style, $encoding, $file, $bEmbed) {
//Add a TrueType or Type1 font
if ($file=='') {
$file=str_replace(' ','',$family).strtolower($style).'.php';
};
$style=strtoupper($style);
if ($style=='IB') { $style='BI'; };
if(isset($this->fonts[$fontkey])) {
$this->Error('Font already added: '.$family.' '.$style);
};
$filepath = $this->_getfontpath().$file;
include($filepath);
// After we've executed 'include' the $file variable
// have been overwritten by $file declared in font definition file; if we do not want
// to embed the font in the PDF file, we should set to empty string
if (!$bEmbed) { $file = ''; };
if(!isset($name)) {
$this->Error("Could not include font definition file: $filepath");
};
$i=count($this->fonts)+1;
$this->fonts[$fontkey]=array('i' =>$i,
'type' =>$type,
'name' =>$name,
'desc' =>$desc,
'up' =>$up,
'ut' =>$ut,
'cw' =>$cw,
'enc' =>$enc,
'file' =>$file);
if ($diff) {
//Search existing encodings
$d=0;
$nb=count($this->diffs);
for ($i=1; $i<=$nb; $i++) {
if($this->diffs[$i]==$diff)
{
$d=$i;
break;
}
}
if ($d==0) {
$d=$nb+1;
$this->diffs[$d]=$diff;
}
$this->fonts[$fontkey]['diff']=$d;
}
if ($file) {
if($type=='TrueType') {
$this->FontFiles[$file]=array('length1'=>$originalsize);
} else {
$this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2);
};
}
}
function SetFont($family,$encoding, $style='',$size=0) {
// Select a font; size given in points
global $fpdf_charwidths;
$style=strtoupper($style);
if (strpos($style,'U')!==false) {
$this->underline=true;
$style=str_replace('U','',$style);
} else {
$this->underline=false;
};
if ($style=='IB') { $style='BI'; };
if ($size==0) {
$size = $this->FontSizePt;
};
$fontkey = $this->_MakeFontKey($family, $encoding);
$this->_LoadFont($fontkey, $family, $encoding, $style);
if ($this->page > 0 /* &&
($this->CurrentFont['i'] != $this->fonts[$fontkey]['i'] ||
$this->FontSizePt != $size) */) {
//Select it
$this->FontFamily = $family;
$this->FontStyle = $style;
$this->FontSizePt = $size;
$this->FontSize = $size/$this->k;
$this->CurrentFont = &$this->fonts[$fontkey];
$this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
};
}
// function SetFontSize($size) {
// //Set font size in points
// if ($this->FontSizePt == $size) {
// return;
// };
// $this->FontSizePt=$size;
// $this->FontSize=$size/$this->k;
// if ($this->page>0) {
// $this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
// };
// }
function AddLink() {
//Create a new internal link
$n=count($this->links)+1;
$this->links[$n]=array(0,0);
return $n;
}
function SetLink($link,$y=0,$page=-1) {
//Set destination of internal link
if ($y==-1) {
$y=$this->y;
};
if ($page==-1) {
$page=$this->page;
};
$this->links[$link]=array($page,$y);
}
/**
* Add an external hyperlink on the page (an rectangular area). It is not bound to any other PDF element,
* like text. It is the task of layout engine to draw the appropriate text inside this area.
*
* @param Float $x X-coordinate of the upper-left corner of the link area
* @param Float $y Y-coordinate of the upper-left corner of the link area
* @param Float $w link area width
* @param Float $h link area height
* @param String $link Link URL
*/
function add_link_external($x, $y, $w, $h, $link) {
$link = new PDFAnnotationExternalLink($this,
$this->_generate_new_object_number(),
0,
new PDFRect($x, $y, $w, $h),
$link);
$this->_pages[count($this->_pages)-1]->add_annotation($link);
}
/**
* Add an internal hyperlink on the page (an rectangular area). It is not bound to any other PDF element,
* like text. It is the task of layout engine to draw the appropriate text inside this area.
*
* @param Float $x X-coordinate of the upper-left corner of the link area
* @param Float $y Y-coordinate of the upper-left corner of the link area
* @param Float $w link area width
* @param Float $h link area height
* @param Integer $link Internal Link identifier
*/
function add_link_internal($x, $y, $w, $h, $link) {
$link = new PDFAnnotationInternalLink($this,
$this->_generate_new_object_number(),
0,
new PDFRect($x, $y, $w, $h),
$link);
$this->_pages[count($this->_pages)-1]->add_annotation($link);
}
function Text($x,$y,$txt) {
//Output a string
$s=sprintf('BT %.2f %.2f Td (%s) Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escape($txt));
if ($this->underline && $txt!='') {
$s.=' '.$this->_dounderline($x,$y,$txt);
}
if ($this->overline && $txt!='') {
$s.=' '.$this->_dooverline($x,$y,$txt);
}
if ($this->strikeout && $txt!='') {
$s.=' '.$this->_dostrikeout($x,$y,$txt);
}
if($this->ColorFlag)
$s='q '.$this->TextColor.' '.$s.' Q';
$this->_out($s);
}
function AcceptPageBreak()
{
//Accept automatic page break or not
return $this->AutoPageBreak;
}
function Cell($w,$h=0,$txt='',$border=0,$ln=0,$align='',$fill=0,$link='')
{
//Output a cell
$k=$this->k;
if($this->y+$h>$this->PageBreakTrigger && !$this->InFooter && $this->AcceptPageBreak())
{
//Automatic page break
$x=$this->x;
$ws=$this->ws;
if($ws>0)
{
$this->ws=0;
$this->_out('0 Tw');
}
$this->AddPage($this->CurOrientation);
$this->x=$x;
if($ws>0)
{
$this->ws=$ws;
$this->_out(sprintf('%.3f Tw',$ws*$k));
}
}
if($w==0)
$w=$this->w-$this->rMargin-$this->x;
$s='';
if($fill==1 || $border==1)
{
if($fill==1)
$op=($border==1) ? 'B' : 'f';
else
$op='S';
$s=sprintf('%.2f %.2f %.2f %.2f re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op);
}
if(is_string($border))
{
$x=$this->x;
$y=$this->y;
if(strpos($border,'L')!==false)
$s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k);
if(strpos($border,'T')!==false)
$s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k);
if(strpos($border,'R')!==false)
$s.=sprintf('%.2f %.2f m %.2f %.2f l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
if(strpos($border,'B')!==false)
$s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
}
if($txt!=='')
{
if ($align=='R') {
$dx=$w-$this->cMargin-$this->GetStringWidth($txt);
} elseif ($align=='C') {
$dx=($w-$this->GetStringWidth($txt))/2;
} else {
$dx=$this->cMargin;
};
if ($this->ColorFlag) {
$s.='q '.$this->TextColor.' ';
};
$txt2=str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$txt)));
$s.=sprintf('BT %.2f %.2f Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2);
if ($this->underline) {
$s.=' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt);
};
if($this->ColorFlag)
$s.=' Q';
if($link)
$this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link);
}
if($s)
$this->_out($s);
$this->lasth=$h;
if($ln>0)
{
//Go to next line
$this->y+=$h;
if($ln==1)
$this->x=$this->lMargin;
}
else
$this->x+=$w;
}
function MultiCell($w,$h,$txt,$border=0,$align='J',$fill=0)
{
//Output text with automatic or explicit line breaks
$cw=&$this->CurrentFont['cw'];
if($w==0)
$w=$this->w-$this->rMargin-$this->x;
$wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
$s=str_replace("\r",'',$txt);
$nb=strlen($s);
if($nb>0 && $s[$nb-1]=="\n")
$nb--;
$b=0;
if($border)
{
if($border==1)
{
$border='LTRB';
$b='LRT';
$b2='LR';
}
else
{
$b2='';
if(strpos($border,'L')!==false)
$b2.='L';
if(strpos($border,'R')!==false)
$b2.='R';
$b=(strpos($border,'T')!==false) ? $b2.'T' : $b2;
}
}
$sep=-1;
$i=0;
$j=0;
$l=0;
$ns=0;
$nl=1;
while($i<$nb)
{
//Get next character
$c=$s{$i};
if($c=="\n")
{
//Explicit line break
if($this->ws>0)
{
$this->ws=0;
$this->_out('0 Tw');
}
$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
$i++;
$sep=-1;
$j=$i;
$l=0;
$ns=0;
$nl++;
if($border && $nl==2)
$b=$b2;
continue;
}
if($c==' ')
{
$sep=$i;
$ls=$l;
$ns++;
}
$l+=$cw[$c];
if($l>$wmax)
{
//Automatic line break
if($sep==-1)
{
if($i==$j)
$i++;
if($this->ws>0)
{
$this->ws=0;
$this->_out('0 Tw');
}
$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
}
else
{
if($align=='J')
{
$this->ws=($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0;
$this->_out(sprintf('%.3f Tw',$this->ws*$this->k));
}
$this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
$i=$sep+1;
}
$sep=-1;
$j=$i;
$l=0;
$ns=0;
$nl++;
if($border && $nl==2)
$b=$b2;
}
else
$i++;
}
//Last chunk
if($this->ws>0)
{
$this->ws=0;
$this->_out('0 Tw');
}
if($border && strpos($border,'B')!==false)
$b.='B';
$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
$this->x=$this->lMargin;
}
function Write($h, $txt, $link='') {
//Output text in flowing mode
$cw=&$this->CurrentFont['cw'];
$w=$this->w-$this->rMargin-$this->x;
$wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
$s=str_replace("\r",'',$txt);
$nb=strlen($s);
$sep=-1;
$i=0;
$j=0;
$l=0;
$nl=1;
while ($i<$nb) {
//Get next character
$c=$s{$i};
if ($c=="\n") {
//Explicit line break
$this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);
$i++;
$sep=-1;
$j=$i;
$l=0;
if ($nl==1) {
$this->x=$this->lMargin;
$w=$this->w-$this->rMargin-$this->x;
$wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
}
$nl++;
continue;
}
if ($c==' ') {
$sep=$i;
};
$l+=$cw[$c];
if ($l>$wmax) {
//Automatic line break
if ($sep==-1) {
if($this->x>$this->lMargin) {
//Move to next line
$this->x=$this->lMargin;
$this->y+=$h;
$w=$this->w-$this->rMargin-$this->x;
$wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
$i++;
$nl++;
continue;
}
if ($i==$j) {
$i++;
};
$this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);
} else {
$this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',0,$link);
$i=$sep+1;
}
$sep=-1;
$j=$i;
$l=0;
if ($nl==1) {
$this->x=$this->lMargin;
$w=$this->w-$this->rMargin-$this->x;
$wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
}
$nl++;
}
else
$i++;
}
//Last chunk
if($i!=$j)
$this->Cell($l/1000*$this->FontSize,$h,substr($s,$j),0,0,'',0,$link);
}
function Image($file,$x,$y,$w=0,$h=0,$type='',$link='')
{
//Put an image on the page
if(!isset($this->images[$file]))
{
//First use of image, get info
if($type=='')
{
$pos=strrpos($file,'.');
if(!$pos)
$this->Error('Image file has no extension and no type was specified: '.$file);
$type=substr($file,$pos+1);
}
$type=strtolower($type);
$mqr=get_magic_quotes_runtime();
set_magic_quotes_runtime(0);
if($type=='jpg' || $type=='jpeg')
$info=$this->_parsejpg($file);
elseif($type=='png')
$info=$this->_parsepng($file);
else
{
//Allow for additional formats
$mtd='_parse'.$type;
if(!method_exists($this,$mtd))
$this->Error('Unsupported image type: '.$type);
$info=$this->$mtd($file);
}
set_magic_quotes_runtime($mqr);
$info['i']=count($this->images)+1;
$this->images[$file]=$info;
}
else {
$info=$this->images[$file];
};
//Automatic width and height calculation if needed
if ($w==0 && $h==0) {
//Put image at 72 dpi
$w=$info['w']/$this->k;
$h=$info['h']/$this->k;
}
if ($w==0) {
$w=$h*$info['w']/$info['h'];
};
if ($h==0) {
$h=$w*$info['h']/$info['w'];
};
$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']));
if ($link) {
$this->Link($x,$y,$w,$h,$link);
};
}
function Ln($h='')
{
//Line feed; default value is last cell height
$this->x=$this->lMargin;
if(is_string($h))
$this->y+=$this->lasth;
else
$this->y+=$h;
}
function GetX()
{
//Get x position
return $this->x;
}
function SetX($x)
{
//Set x position
if($x>=0)
$this->x=$x;
else
$this->x=$this->w+$x;
}
function GetY()
{
//Get y position
return $this->y;
}
function SetY($y)
{
//Set y position and reset x
$this->x=$this->lMargin;
if($y>=0)
$this->y=$y;
else
$this->y=$this->h+$y;
}
function SetXY($x,$y)
{
//Set x and y positions
$this->SetY($y);
$this->SetX($x);
}
function Output($name='',$dest='')
{
//Output PDF to some destination
//Finish document if necessary
if($this->state<3)
$this->Close();
//Normalize parameters
if(is_bool($dest))
$dest=$dest ? 'D' : 'F';
$dest=strtoupper($dest);
if($dest=='')
{
if($name=='')
{
$name='doc.pdf';
$dest='I';
}
else
$dest='F';
}
switch($dest)
{
case 'I':
//Send to standard output
if(ob_get_contents())
$this->Error('Some data has already been output, can\'t send PDF file');
if(php_sapi_name()!='cli')
{
//We send to a browser
header('Content-Type: application/pdf');
if(headers_sent())
$this->Error('Some data has already been output to browser, can\'t send PDF file');
header('Content-Length: '.strlen($this->buffer));
header('Content-disposition: inline; filename="'.$name.'"');
}
echo $this->buffer;
break;
case 'D':
//Download file
if(ob_get_contents())
$this->Error('Some data has already been output, can\'t send PDF file');
if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE'))
header('Content-Type: application/force-download');
else
header('Content-Type: application/octet-stream');
if(headers_sent())
$this->Error('Some data has already been output to browser, can\'t send PDF file');
header('Content-Length: '.strlen($this->buffer));
header('Content-disposition: attachment; filename="'.$name.'"');
echo $this->buffer;
break;
case 'F':
//Save to local file
$f=fopen($name,'wb');
if(!$f)
$this->Error('Unable to create output file: '.$name);
fwrite($f,$this->buffer,strlen($this->buffer));
fclose($f);
break;
case 'S':
//Return as a string
return $this->buffer;
default:
$this->Error('Incorrect output destination: '.$dest);
}
return '';
}
/*******************************************************************************
* *
* Protected methods *
* *
*******************************************************************************/
function _dochecks()
{
//Check for locale-related bug
if(1.1==1)
$this->Error('Don\'t alter the locale before including class file');
//Check for decimal separator
if(sprintf('%.1f',1.0)!='1.0')
setlocale(LC_NUMERIC,'C');
}
function _getfontpath()
{
if(!defined('FPDF_FONTPATH') && is_dir(dirname(__FILE__).'/font'))
define('FPDF_FONTPATH',dirname(__FILE__).'/font/');
return defined('FPDF_FONTPATH') ? FPDF_FONTPATH : '';
}
function _putpages() {
$nb=$this->page;
if (!empty($this->AliasNbPages)) {
//Replace number of pages
for ($n=1; $n<=$nb; $n++) {
$this->pages[$n]=str_replace($this->AliasNbPages,$nb,$this->pages[$n]);
};
}
if ($this->DefOrientation=='P') {
$wPt=$this->fwPt;
$hPt=$this->fhPt;
} else {
$wPt=$this->fhPt;
$hPt=$this->fwPt;
};
$filter=($this->compress) ? '/Filter /FlateDecode ' : '';
$pages_start_obj_number = $this->n+1;
for ($n=1; $n<=$nb; $n++) {
//Page
// $this->_newobj();
$page = $this->_pages[$n-1];
$this->offsets[$page->get_object_id()] = strlen($this->buffer);
$this->_out(sprintf("%u %u obj",$page->object_id, $page->generation_id));
$this->_out('<</Type /Page');
$this->_out('/Parent 1 0 R');
$this->_out("/Annots ".$this->_pages[$n-1]->_annotations($this));
if (isset($this->OrientationChanges[$n])) {
$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$hPt,$wPt));
};
$this->_out('/Resources 2 0 R');
$this->_out('/Contents '.($this->n+1).' 0 R>>');
$this->_out('endobj');
//Page content
$p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
$this->_newobj();
$this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
$this->_putstream($p);
$this->_out('endobj');
// Output annotation object for this page
$annotations = $this->_pages[$n-1]->annotations;
$size = count($annotations);
for ($j=0; $j<$size; $j++) {
$annotations[$j]->out($this);
};
}
//Pages root
$this->offsets[1] = strlen($this->buffer);
$this->_out('1 0 obj');
$this->_out('<</Type /Pages');
$this->_out('/Kids '.$this->_reference_array($this->_pages));
$this->_out('/Count '.$nb);
$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$wPt,$hPt));
$this->_out('>>');
$this->_out('endobj');
return $pages_start_obj_number;
}
function _putfonts() {
$nf=$this->n;
foreach($this->diffs as $diff) {
//Encodings
$this->_newobj();
$this->_out($this->_dictionary(array("Type" => "/Encoding",
"BaseEncoding" => "/WinAnsiEncoding",
"Differences" => $this->_array($diff))));
$this->_out('endobj');
}
$mqr=get_magic_quotes_runtime();
set_magic_quotes_runtime(0);
foreach ($this->FontFiles as $file=>$info) {
//Font file embedding
$this->_newobj();
$this->FontFiles[$file]['n'] = $this->n;
$font='';
$f=fopen($this->_getfontpath().$file,'rb',1);
if (!$f) {
$this->Error('Font file not found');
};
while(!feof($f)) { $font.=fread($f,8192); };
fclose($f);
$compressed=(substr($file,-2)=='.z');
if(!$compressed && isset($info['length2'])) {
$header=(ord($font{0})==128);
if($header) {
//Strip first binary header
$font=substr($font,6);
}
if($header && ord($font{$info['length1']})==128) {
//Strip second binary header
$font=substr($font,0,$info['length1']).substr($font,$info['length1']+6);
}
}
$this->_out('<</Length '.strlen($font));
if ($compressed) {
$this->_out('/Filter /FlateDecode');
};
$this->_out('/Length1 '.$info['length1']);
if(isset($info['length2'])) {
$this->_out('/Length2 '.$info['length2'].' /Length3 0');
};
$this->_out('>>');
$this->_putstream($font);
$this->_out('endobj');
}
set_magic_quotes_runtime($mqr);
foreach ($this->fonts as $k=>$font) {
//Font objects
$this->fonts[$k]['n'] = $this->n+1;
$type=$font['type'];
$name=$font['name'];
if ($type=='Type1' || $type=='TrueType') {
//Additional Type1 or TrueType font
$this->_newobj();
$this->_out('<</Type /Font');
$this->_out('/BaseFont /'.$name);
$this->_out('/Subtype /'.$type);
$this->_out('/FirstChar 32 /LastChar 255');
$this->_out('/Widths '.($this->n+1).' 0 R');
$this->_out('/FontDescriptor '.($this->n+2).' 0 R');
if ($font['enc']) {
if(isset($font['diff'])) {
$this->_out('/Encoding '.($nf+$font['diff']).' 0 R');
} else {
$this->_out('/Encoding /WinAnsiEncoding');
};
}
$this->_out('>>');
$this->_out('endobj');
//Widths
$this->_newobj();
$cw=&$font['cw'];
$s='[';
for ($i=32;$i<=255;$i++) {
$s.=$cw[chr($i)].' ';
};
$this->_out($s.']');
$this->_out('endobj');
//Descriptor
$this->_newobj();
$s='<</Type /FontDescriptor /FontName /'.$name;
foreach($font['desc'] as $k=>$v) {
$s.=' /'.$k.' '.$v;
};
$file=$font['file'];
if($file) {
$s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
};
$this->_out($s.'>>');
$this->_out('endobj');
} else {
//Allow for additional types
$mtd='_put'.strtolower($type);
if(!method_exists($this,$mtd))
$this->Error('Unsupported font type: '.$type);
$this->$mtd($font);
}
}
}
function _putimages() {
$filter=($this->compress) ? '/Filter /FlateDecode ' : '';
reset($this->images);
while (list($file,$info) = each($this->images)) {
$this->_newobj();
$this->images[$file]['n']=$this->n;
$this->_out('<</Type /XObject');
$this->_out('/Subtype /Image');
$this->_out('/Width '.$info['w']);
$this->_out('/Height '.$info['h']);
if ($info['cs']=='Indexed') {
$this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]');
} else {
$this->_out('/ColorSpace /'.$info['cs']);
if($info['cs']=='DeviceCMYK') {
$this->_out('/Decode [1 0 1 0 1 0 1 0]');
};
}
$this->_out('/BitsPerComponent '.$info['bpc']);
if (isset($info['f'])) {
$this->_out('/Filter /'.$info['f']);
};
if(isset($info['parms'])) {
$this->_out($info['parms']);
};
if(isset($info['trns']) && is_array($info['trns'])) {
$trns='';
for ($i=0;$i<count($info['trns']);$i++) {
$trns.=$info['trns'][$i].' '.$info['trns'][$i].' ';
};
$this->_out('/Mask ['.$trns.']');
};
$this->_out('/Length '.strlen($info['data']).'>>');
$this->_putstream($info['data']);
unset($this->images[$file]['data']);
$this->_out('endobj');
// Palette
if ($info['cs']=='Indexed') {
$this->_newobj();
$pal=($this->compress) ? gzcompress($info['pal']) : $info['pal'];
$this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
$this->_putstream($pal);
$this->_out('endobj');
};
}
}
function _putxobjectdict() {
foreach($this->images as $image)
$this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
}
function _putresourcedict()
{
$this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
$this->_out('/Font <<');
foreach($this->fonts as $font)
$this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
$this->_out('>>');
$this->_out('/XObject <<');
$this->_putxobjectdict();
$this->_out('>>');
}
function _putresources()
{
$this->_putfonts();
$this->_putimages();
//Resource dictionary
$this->offsets[2]=strlen($this->buffer);
$this->_out('2 0 obj');
$this->_out('<<');
$this->_putresourcedict();
$this->_out('>>');
$this->_out('endobj');
}
function _putinfo()
{
$this->_out('/Producer '.$this->_textstring('FPDF '.FPDF_VERSION));
if(!empty($this->title))
$this->_out('/Title '.$this->_textstring($this->title));
if(!empty($this->subject))
$this->_out('/Subject '.$this->_textstring($this->subject));
if(!empty($this->author))
$this->_out('/Author '.$this->_textstring($this->author));
if(!empty($this->keywords))
$this->_out('/Keywords '.$this->_textstring($this->keywords));
if(!empty($this->creator))
$this->_out('/Creator '.$this->_textstring($this->creator));
$this->_out('/CreationDate '.$this->_textstring('D:'.date('YmdHis')));
}
// Generate the document catalog entry of PDF file
function _putcatalog($pages_start_obj_number) {
$this->_out('/Type /Catalog');
$this->_out('/Pages 1 0 R');
if ($this->ZoomMode=='fullpage') {
$this->_out("/OpenAction [$pages_start_obj_number 0 R /Fit]");
} elseif ($this->ZoomMode=='fullwidth') {
$this->_out("/OpenAction [$pages_start_obj_number 0 R /FitH null]");
} elseif ($this->ZoomMode=='real') {
$this->_out("/OpenAction [$pages_start_obj_number 0 R /XYZ null null 1]");
} elseif (!is_string($this->ZoomMode)) {
$this->_out("/OpenAction [$pages_start_obj_number 0 R /XYZ null null ".($this->ZoomMode/100).']');
};
if ($this->LayoutMode=='single') {
$this->_out('/PageLayout /SinglePage');
} elseif ($this->LayoutMode=='continuous') {
$this->_out('/PageLayout /OneColumn');
} elseif ($this->LayoutMode=='two') {
$this->_out('/PageLayout /TwoColumnLeft');
};
if (count($this->_forms) > 0) {
$this->_out('/AcroForm <<');
$this->_out('/Fields '.$this->_reference_array($this->_forms));
$this->_out('/DR 2 0 R');
$this->_out('/NeedAppearances true');
$this->_out('>>');
};
}
function _putheader() {
$this->_out('%PDF-'.$this->PDFVersion);
}
function _puttrailer()
{
$this->_out('/Size '.($this->n+1));
$this->_out('/Root '.$this->n.' 0 R');
$this->_out('/Info '.($this->n-1).' 0 R');
}
function _enddoc() {
$this->_putheader();
$pages_start_obj_number = $this->_putpages();
$this->_putresources();
//Info
$this->_newobj();
$this->_out('<<');
$this->_putinfo();
$this->_out('>>');
$this->_out('endobj');
// Form fields
for ($i=0; $i<count($this->_forms); $i++) {
$form =& $this->_forms[$i];
$form->out($this);
// // @todo: remove this dirty hack
// if (isset($form->appearance)) {
// $this->offsets[$form->appearance->get_object_id()] = strlen($this->buffer);
// $this->_out($this->_indirect_object($form->appearance));
// };
// if (isset($form->_radios)) {
// for ($i=0; $i<count($form->_radios); $i++) {
// // $radio = $form->_radios[$i];
// // $this->offsets[$radio->get_object_id()] = strlen($this->buffer);
// // $this->_out($this->_indirect_object($radio));
// };
// }
// Not required, as forms fields are annotations which are output at the end of the page
// $this->offsets[$form->get_object_id()] = strlen($this->buffer);
// $this->_out($this->_indirect_object($form));
};
//Catalog
$this->_newobj();
$this->_out('<<');
$this->_putcatalog($pages_start_obj_number);
$this->_out('>>');
$this->_out('endobj');
//Cross-ref
$o=strlen($this->buffer);
$this->_out('xref');
$this->_out('0 '.($this->n+1));
$this->_out('0000000000 65535 f ');
for ($i=1; $i<=$this->n; $i++) {
$this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
};
//Trailer
$this->_out('trailer');
$this->_out('<<');
$this->_puttrailer();
$this->_out('>>');
$this->_out('startxref');
$this->_out($o);
$this->_out('%%EOF');
$this->state=3;
}
function _beginpage($orientation) {
$this->page++;
$this->pages[$this->page]='';
$this->state=2;
$this->x=$this->lMargin;
$this->y=$this->tMargin;
$this->FontFamily='';
//Page orientation
if(!$orientation) {
$orientation=$this->DefOrientation;
} else {
$orientation=strtoupper($orientation{0});
if($orientation!=$this->DefOrientation) {
$this->OrientationChanges[$this->page]=true;
};
}
if($orientation!=$this->CurOrientation) {
//Change orientation
if($orientation=='P') {
$this->wPt=$this->fwPt;
$this->hPt=$this->fhPt;
$this->w=$this->fw;
$this->h=$this->fh;
}
else
{
$this->wPt=$this->fhPt;
$this->hPt=$this->fwPt;
$this->w=$this->fh;
$this->h=$this->fw;
}
$this->PageBreakTrigger=$this->h-$this->bMargin;
$this->CurOrientation=$orientation;
}
}
function _endpage() {
//End of page contents
$this->state=1;
}
// Start a new indirect object
function _newobj() {
$num = $this->_generate_new_object_number();
$this->offsets[$num]=strlen($this->buffer);
$this->_out($num.' 0 obj');
}
function _parsejpg($file)
{
//Extract info from a JPEG file
$a=GetImageSize($file);
if (!$a) {
$this->Error('Missing or incorrect image file: '.$file);
};
if ($a[2]!=2) {
$this->Error('Not a JPEG file: '.$file);
};
if (!isset($a['channels']) || $a['channels']==3) {
$colspace='DeviceRGB';
} elseif ($a['channels']==4) {
$colspace='DeviceCMYK';
} else {
$colspace='DeviceGray';
};
$bpc=isset($a['bits']) ? $a['bits'] : 8;
//Read whole file
$f = fopen($file,'rb');
$data = '';
while (!feof($f)) {
$data .= fread($f,4096);
};
fclose($f);
return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data);
}
function _parsepng($file) {
//Extract info from a PNG file
$f=fopen($file,'rb');
if (!$f) {
$this->Error('Can\'t open image file: '.$file);
};
//Check signature
if (fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
$this->Error('Not a PNG file: '.$file);
};
//Read header chunk
fread($f,4);
if (fread($f,4)!='IHDR') {
$this->Error('Incorrect PNG file: '.$file);
};
$w=$this->_freadint($f);
$h=$this->_freadint($f);
$bpc=ord(fread($f,1));
if ($bpc>8) {
$this->Error('16-bit depth not supported: '.$file);
};
$ct=ord(fread($f,1));
if ($ct==0) {
$colspace='DeviceGray';
} elseif($ct==2) {
$colspace='DeviceRGB';
} elseif($ct==3) {
$colspace='Indexed';
} else {
$this->Error('Alpha channel not supported: '.$file);
};
if (ord(fread($f,1))!=0) {
$this->Error('Unknown compression method: '.$file);
};
if (ord(fread($f,1))!=0) {
$this->Error('Unknown filter method: '.$file);
};
if (ord(fread($f,1))!=0) {
$this->Error('Interlacing not supported: '.$file);
};
fread($f,4);
$parms='/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
//Scan chunks looking for palette, transparency and image data
$pal='';
$trns='';
$data='';
do
{
$n=$this->_freadint($f);
$type=fread($f,4);
if($type=='PLTE')
{
//Read palette
$pal=fread($f,$n);
fread($f,4);
}
elseif($type=='tRNS')
{
//Read transparency info
$t=fread($f,$n);
if($ct==0)
$trns=array(ord(substr($t,1,1)));
elseif($ct==2)
$trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1)));
else
{
$pos=strpos($t,chr(0));
if($pos!==false)
$trns=array($pos);
}
fread($f,4);
}
elseif($type=='IDAT')
{
//Read image data block
$data.=fread($f,$n);
fread($f,4);
}
elseif($type=='IEND')
break;
else
fread($f,$n+4);
}
while($n);
if($colspace=='Indexed' && empty($pal))
$this->Error('Missing palette in '.$file);
fclose($f);
return array('w'=>$w,'h'=>$h,'cs'=>$colspace,'bpc'=>$bpc,'f'=>'FlateDecode','parms'=>$parms,'pal'=>$pal,'trns'=>$trns,'data'=>$data);
}
function _freadint($f)
{
//Read a 4-byte integer from file
$a=unpack('Ni',fread($f,4));
return $a['i'];
}
function _textstring($s)
{
//Format a text string
return '('.$this->_escape($s).')';
}
function _escape($s) {
//Add \ before \, ( and )
return str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$s)));
}
function _putstream($s) {
$this->_out('stream');
$this->_out($s);
$this->_out('endstream');
}
function _out($s) {
//Add a line to the document
if($this->state==2)
$this->pages[$this->page].=$s."\n";
else
$this->buffer.=$s."\n";
}
//End of class
}
//Handle special IE contype request
if(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT']=='contype')
{
header('Content-Type: application/pdf');
exit;
}
}
?>