Files
luos/thirdparty/html2ps_pdf/pdf.fpdf.php
Paula Quispe 9eb7d6cac2 HOR-2689
2017-08-03 17:00:30 -04:00

2689 lines
85 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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');
/**
* FPDF state flags
*/
define('FPDF_STATE_UNINITIALIZED', 0);
define('FPDF_STATE_DOCUMENT_STARTED', 1);
define('FPDF_STATE_PAGE_STARTED', 2);
define('FPDF_STATE_COMPLETED', 3);
/**
* 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.573 for explanation of flag specific to annotations
*/
define('PDF_ANNOTATION_INVISIBLE', 1 << 0);
define('PDF_ANNOTATION_HIDDEN', 1 << 1);
define('PDF_ANNOTATION_PRINTABLE', 1 << 2);
define('PDF_ANNOTATION_NOZOOM', 1 << 3);
define('PDF_ANNOTATION_NOROTATE', 1 << 4);
define('PDF_ANNOTATION_NOVIEW', 1 << 5);
define('PDF_ANNOTATION_READONLY', 1 << 6);
define('PDF_ANNOTATION_LOCKED', 1 << 7);
define('PDF_ANNOTATION_TOGGLENOVIEW', 1 << 8);
/**
* 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 PDFCMap extends PDFIndirectObject {
var $_content;
function PDFCMap($mapping, &$handler, $object_id, $generation_id) {
$this->PDFIndirectObject($handler,
$object_id,
$generation_id);
$num_chars = count($mapping);
$chars = "";
foreach ($mapping as $code => $utf) {
$chars .= sprintf("<%02X> <%04X> \n", $code, $utf);
};
$this->_content = <<<EOF
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
CIDSystemInfo
<< /Registry (Adobe)
/Ordering (UCS) /Supplement 0 >> def
/CMapName /Adobe-Identity-UCS def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
${num_chars} beginbfchar
${chars}
endbfchar
endcmap CMapName currentdict /CMap defineresource pop end end
EOF
;
}
function pdf(&$handler) {
$dict_content = array(
'Length' => strlen($this->_content)
);
$content = $handler->_dictionary($dict_content);
$content .= "\n";
$content .= $handler->_stream($this->_content);
return $content;
}
}
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 = $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()),
'F' => PDF_ANNOTATION_PRINTABLE));
}
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' => PDF_SUBMIT_FORM_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 $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 $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 $cmaps; // List of ToUnicode
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 $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 $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 $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, BCross-Reference
// Table,B and 3.4.5, BIncremental Updates.B
// 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,' ');
$content = 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);
return $content;
}
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");
}
// TODO: more graceful custom encoding processing
function _LoadFont($fontkey, $family, $encoding) {
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') || $encoding == 'custom') {
// As MakeFont squeaks a lot, we'll need to capture and discard its output
MakeFont(TTF_FONTS_REPOSITORY.$file.'.ttf',
TTF_FONTS_REPOSITORY.$file.'.afm',
$this->_getfontpath(),
$fontkey.'.php',
$encoding);
};
$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->state = FPDF_STATE_UNINITIALIZED;
$this->fonts=array();
$this->FontFiles=array();
$this->diffs = array();
$this->images = array();
$this->links = array();
$this->lasth=0;
$this->FontFamily='';
$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->w=$this->wPt/$this->k;
$this->h=$this->hPt/$this->k;
//Line width (0.2 mm)
$this->LineWidth=.567/$this->k;
//Full width display mode
$this->SetDisplayMode('fullwidth');
//Enable compression
$this->SetCompression(true);
//Set default PDF version number
$this->PDFVersion='1.3';
}
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);
}
/**
* @param $compress Boolean indicates whether compression is enabled
*/
function SetCompression($compress) {
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 Error($msg) {
//Fatal error
die('<B>FPDF error: </B>'.$msg);
}
function Open() {
//Begin document
$this->state = FPDF_STATE_DOCUMENT_STARTED;
}
function Close() {
//Terminate document
if ($this->state == FPDF_STATE_COMPLETED) {
return;
};
if ($this->page==0) {
$this->AddPage();
};
//Close page
$this->_endpage();
//Close document
$this->_enddoc();
}
function AddPage() {
$this->_pages[] =& new PDFPage($this, $this->_generate_new_object_number(), 0);
//Start a new page
if ($this->state == FPDF_STATE_UNINITIALIZED) {
$this->Open();
};
$family=$this->FontFamily;
$size=$this->FontSizePt;
$lw=$this->LineWidth;
$dc=$this->DrawColor;
$fc=$this->FillColor;
$tc=$this->TextColor;
$cf=$this->ColorFlag;
if ($this->page>0) {
//Close page
$this->_endpage();
}
//Start new page
$this->_beginpage();
//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;
//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 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;
}
/**
* Set line width
*/
function SetLineWidth($width) {
$this->LineWidth = $width;
if ($this->page > 0) {
$this->_out(sprintf('%.2f w',$width*$this->k));
};
}
/**
* Draw a line
*/
function Line($x1,$y1,$x2,$y2) {
$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));
}
/**
* Add a TrueType or Type1 font
*/
function AddFont($fontkey, $family, $encoding, $file, $bEmbed) {
if(isset($this->fonts[$fontkey])) {
$this->Error('Font already added: '.$family);
};
$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;
/**
* TODO
* Add CMAP for this font
*/
$this->cmaps[$d] = new PDFCMap($cmap,
$handler,
$this->_generate_new_object_number(),
0);
}
$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);
};
}
}
/**
* Select a font; size given in points
*/
function SetFont($family, $encoding, $size) {
global $fpdf_charwidths;
$fontkey = $this->_MakeFontKey($family, $encoding);
$this->_LoadFont($fontkey, $family, $encoding);
if ($this->page > 0) {
//Select it
$this->FontFamily = $family;
$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));
};
}
/**
* Create a new internal link
*/
function AddLink() {
$n=count($this->links)+1;
$this->links[$n]=array(0,0);
return $n;
}
/**
* Set destination of internal link
*/
function SetLink($link,$y,$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);
}
/**
* Accepts PNG images only
*/
function Image($file, $x, $y, $w, $h) {
// Put an image on the page
if (!isset($this->images[$file])) {
$mqr=get_magic_quotes_runtime();
set_magic_quotes_runtime(0);
$info=$this->_parsepng($file);
set_magic_quotes_runtime($mqr);
$info['i']=count($this->images)+1;
$this->images[$file]=$info;
} else {
$info=$this->images[$file];
};
$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']));
}
/**
* @param $name String file to save generated PDF in
*/
function Output($name) {
//Finish document if necessary
if ($this->state != FPDF_STATE_COMPLETED) {
$this->Close();
};
$f=fopen($name,'wb');
if (!$f) {
$this->Error('Unable to create output file: '.$name);
};
fwrite($f,$this->buffer,strlen($this->buffer));
fclose($f);
}
/********************************************************************************
* *
* 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() {
return CACHE_DIR;
}
function _putpages() {
$nb=$this->page;
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
$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));
$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;
$num_diffs = count($this->diffs);
for ($i=1; $i<=$num_diffs; $i++) {
$diff = $this->diffs[$i];
$cmap = $this->cmaps[$i];
//Encodings
$this->_newobj();
$this->_out($this->_dictionary(array("Type" => "/Encoding",
"BaseEncoding" => "/WinAnsiEncoding",
"Differences" => $this->_array($diff))));
$this->_out('endobj');
$cmap->out($this);
}
$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');
$this->_out('/ToUnicode '.($this->_reference($this->cmaps[$font['diff']])));
} 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');
/**
* Font descriptor
*/
$this->_newobj();
$fontDescriptor = array('Type' => '/FontDescriptor',
'FontName' => '/'.$name,
'Flags' => $font['desc']['Flags'],
'FontBBox' => $font['desc']['FontBBox'],
'ItalicAngle' => $font['desc']['ItalicAngle'],
'Ascent' => $font['desc']['Ascent'],
'Descent' => $font['desc']['Descent'],
'CapHeight' => $font['desc']['CapHeight'],
'StemV' => $font['desc']['StemV']
);
if ($font['file'] != "") {
$fontDescriptor['FontFile'.($type=='Type1' ? '' : '2')] =
$this->FontFiles[$font['file']]['n'].' 0 R';
};
$this->_out($this->_dictionary($fontDescriptor));
$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);
};
//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 = FPDF_STATE_COMPLETED;
}
function _beginpage() {
$this->page++;
$this->pages[$this->page]='';
$this->state = FPDF_STATE_PAGE_STARTED;
$this->FontFamily='';
}
/**
* End of page contents
*/
function _endpage() {
$this->state = FPDF_STATE_DOCUMENT_STARTED;
}
/**
* 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 _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);
}
/**
* Read a 4-byte integer from file
*/
function _freadint($f) {
$a=unpack('Ni',fread($f,4));
return $a['i'];
}
/**
* Format a text string
*/
function _textstring($s) {
return '('.$this->_escape($s).')';
}
/**
* Add \ before \, ( and )
*/
function _escape($s) {
return str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$s)));
}
function _putstream($s) {
$this->_out('stream');
$this->_out($s);
$this->_out('endstream');
}
/**
* Add a line to the document
*/
function _out($s) {
if ($this->state == FPDF_STATE_PAGE_STARTED) {
$this->pages[$this->page].=$s."\n";
} else {
$this->buffer.=$s."\n";
}
}
}
}
?>