TimeTrex/classes/GovernmentForms/GovernmentForms_Base.class.php

1184 lines
34 KiB
PHP

<?php
/*********************************************************************************
*
* TimeTrex is a Workforce Management program developed by
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
*
* You should have received a copy of the GNU Affero General Public License along
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
*
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
*
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by TimeTrex".
*
********************************************************************************/
/**
* @package GovernmentForms
*/
class GovernmentForms_Base {
public $debug = false;
public $metadata = null; //Metadata about the form itself, such as the
public $original_data = null; //Original object data before any record processing has been performed. So we can revert back to a "clean" state at any point.
public $data = null; //Form data is stored here in an array.
public $records = []; //Store multiple records here to process on a single form. ie: T4's where two employees can be on a single page.
public $records_total = []; //Total for all records.
private $messages = []; //Messages/warnings to user
public $class_directory = null;
/*
* XML related variables
*/
public $xml_object = null; //Prevent __set() from sticking this into the data property.
/*
* PDF related variables
*/
public $pdf_object = null; //Prevent __set() from sticking this into the data property.
public $template_index = [];
public $current_template_index = null;
public $page_margins = [ 0, 0 ]; //x, y - 43pt = 15mm Absolute margins that affect all drawing and templates.
public $page_offsets = [ 0, 0 ]; //x, y - Only affects drawing fields within the template.
public $template_offsets = [ 0, 0 ]; //x, y - Only affects placement of the template on the page.
public $temp_page_offsets = [ 0, 0 ]; //x, y - Only affects drawing and is reset based on page_offets above.
public $show_background = true; //Shows the PDF background
public $default_font = 'helvetica';
function __construct() {
$this->temp_page_offsets = $this->page_offsets; //Default temp page offets to whatever page offsets is originally set to.
return true;
}
function setDebug( $bool ) {
$this->debug = $bool;
}
function getDebug() {
return $this->debug;
}
function setClassDirectory( $dir ) {
$this->class_directory = $dir;
}
function getClassDirectory() {
return $this->class_directory;
}
function Output( $type, $clear_records = true ) {
$this->saveOriginDataState();
$this->calculate(); //Run all calculation functions prior to outputting anything.
switch ( strtolower( $type ) ) {
case 'pdf':
$retval = $this->_outputPDF( $type );
break;
case 'xml':
$retval = $this->_outputXML( $type );
break;
case 'efile':
$retval = $this->_outputEFILE( $type );
break;
default:
$retval = false;
break;
}
if ( $clear_records == true ) {
$this->clearRecords(); //This also calls revertToOriginalDataState()
} else {
$this->revertToOriginalDataState();
}
return $retval;
}
function saveOriginDataState() {
$this->original_data = $this->data;
return true;
}
function revertToOriginalDataState() {
if ( isset( $this->original_data ) ) {
if ( !defined( 'UNIT_TEST_MODE' ) || UNIT_TEST_MODE === false ) {
$this->data = $this->original_data;
}
}
return true;
}
function getRecords() {
return $this->records;
}
function setRecords( $data ) {
if ( is_array( $data ) ) {
foreach ( $data as $record ) {
$this->addRecord( $record ); //Make sure preCalc() is called for each record.
}
} else {
$this->records = $data;
}
return true;
}
function addRecord( $data ) {
//Filter functions should only be used for drawing the PDF, they do not modify the actual values themselves.
//preCalc functions should be used to modify the actual values themselves, prior to drawing on the PDF, as well prior to totalling.
//This is also important for calculating totals, so we can cap maximum contributions and such and get totals based on those properly.
//preCalc functions can modify any other value in the record as well.
if ( is_array( $data ) ) {
$template_schema = $this->getTemplateSchema();
if ( is_array( $template_schema ) ) {
foreach ( $data as $key => $value ) {
if ( isset( $template_schema[$key]['function']['precalc'] ) ) {
$filter_function = $template_schema[$key]['function']['precalc'];
if ( $filter_function != '' ) {
if ( !is_array( $filter_function ) ) {
$filter_function = (array)$filter_function;
}
foreach ( $filter_function as $function ) {
//Call function
if ( method_exists( $this, $function ) ) {
$value = $this->$function( $value, $key, $data );
}
}
unset( $function );
}
$data[$key] = $value;
}
}
}
$this->records[] = $data;
}
return true;
}
function clearRecords() {
$this->records = [];
$this->revertToOriginalDataState();
}
function countRecords() {
return count( $this->records );
}
//Totals all the values for all the records.
function sumRecords() {
//Make sure we handle array elements with letters, so we can properly combine boxes with the same letters.
$this->records_total = Misc::ArrayAssocSum( $this->records, null, null, true );
return true;
}
function getRecordsTotal() {
return $this->records_total;
}
/**
* Serializes the object to an array for storing in the DB and later retrieval. Especially important for handling correction reports like W2C.
* @return false|string
*/
function serialize( $clear_records = true ) {
//Don't include $this->records here, as all the object properties from the records gets put into $this->data
$retval = [ 'metadata' => [ 'class' => get_class( $this ), 'object_data' => $this->metadata, 'tt_version' => APPLICATION_VERSION ], 'data' => $this->data, 'records' => $this->getRecords() ];
if ( $clear_records == true ) {
$this->clearRecords();
}
//*NOTE: This should not be serialized to JSON here, as we may need to allow other formats, so just return an array.
return $retval;
}
/**
* Unserializes a array into the form itself.
* @return false|string
*/
function unserialize( $data ) {
if ( is_array( $data ) && isset( $data['metadata'] ) ) {
$this->data = $data['data'];
$this->setRecords( $data['records'] );
return true;
}
return false;
}
/**
* @param int $type_id 'note', 'warning', 'error'
* @param string $message
* @param array $field_notice_position [ 'page' => 1, 'x' => 0, 'y' => 0 ]
*/
function addMessage( $type_id, $message, $field_notice_position = [] ) {
$this->messages[$type_id][] = [ 'message' => $message, 'field_notice_position' => $field_notice_position ];
return true;
}
function getMessages() {
return $this->messages;
}
function clearMessages() {
$this->messages = [];
return true;
}
/*
*
* Math functions
*
*/
function MoneyFormatPretty( $value ) {
if ( !is_numeric( $value ) ) {
return false;
}
return number_format( $value, 2, '.', ',' );
}
function MoneyFormat( $value ) {
if ( !is_numeric( $value ) ) {
return false;
}
return number_format( $value, 2, '.', '' );
}
function getBeforeDecimal( $float ) {
$float = $this->MoneyFormat( $float );
$float_array = preg_split( '/\./', $float );
if ( isset( $float_array[0] ) ) {
return $float_array[0];
}
return false;
}
function getAfterDecimal( $float, $format_number = true ) {
if ( $format_number == true ) {
$float = $this->MoneyFormat( $float );
}
$float_array = preg_split( '/\./', $float );
if ( isset( $float_array[1] ) ) {
return str_pad( $float_array[1], 2, '0' );
}
return false;
}
/**
* Need to use bcmath for large numbers, especially on 32bit PHP installs.
* @param $array
* @return int|string
*/
static function arraySum( $array ) {
$retval = 0;
foreach ( $array as $value ) {
$retval = bcadd( $retval, $value );
}
return $retval;
}
/*
*
* Date functions
*
*/
public function getYear( $epoch = null ) {
if ( $epoch == null ) {
$epoch = TTDate::getTime();
}
return date( 'Y', $epoch );
}
/*
*
* Formatting functions
*
*/
public function formatSSN( $value ) {
if ( $value != '' ) {
$value = substr_replace( $value, '-', 3, 0 );
$value = substr_replace( $value, '-', 6, 0 );
return $value;
}
return null;
}
public function formatEIN( $value ) {
if ( $value != '' ) {
return substr_replace( $value, '-', 2, 0 );
}
return null;
}
/*
*
* Validation functions
*
*/
function isNumeric( $value ) {
if ( is_numeric( $value ) ) {
return $value;
}
return false;
}
/*
*
* Filter functions
*
*/
function stripSpaces( $value ) {
return str_replace( ' ', '', trim( $value ) );
}
function stripNonNumeric( $value ) {
$retval = preg_replace( '/[^0-9]/', '', $value );
return $retval;
}
function stripNonAlphaNumeric( $value ) {
$retval = preg_replace( '/[^A-Za-z0-9\ ]/', '', $value ); //Don't strip spaces
return $retval;
}
function stripNonFloat( $value ) {
$retval = preg_replace( '/[^-0-9\.]/', '', $value );
return $retval;
}
/*
*
* EFILE (Fixed Length) Helper functions
*
*/
function removeDecimal( $value ) {
$retval = str_replace( '.', '', number_format( $value, 2, '.', '' ) );
return $retval;
}
function padRecord( $value, $length, $type ) {
$type = strtolower( $type );
//Trim record incase its too long.
$value = substr( $value, 0, $length );
switch ( $type ) {
case 'n':
$retval = str_pad( $value, $length, 0, STR_PAD_LEFT );
break;
case 'an':
$retval = str_pad( $value, $length, ' ', STR_PAD_RIGHT );
break;
}
return $retval;
}
function padLine( $line, $length = false ) {
if ( $line == '' ) {
return false;
}
$retval = str_pad( $line, ( $length == false ) ? strlen( $line ) : $length, ' ', STR_PAD_RIGHT );
return $retval . "\r\n";
}
/*
*
* XML helper functions
*
*/
function setXMLObject( &$obj ) {
$this->xml_object = $obj;
return true;
}
function getXMLObject() {
return $this->xml_object;
}
/*
*
* PDF helper functions
*
*/
function setPDFObject( &$obj ) {
$this->pdf_object = $obj;
return true;
}
function getPDFObject() {
return $this->pdf_object;
}
function setShowBackground( $bool ) {
$this->show_background = $bool;
return true;
}
function getShowBackground() {
return $this->show_background;
}
function setPageMargins( $x, $y ) {
$this->page_margins = [ $x, $y ];
return true;
}
function getPageMargins( $type = null ) {
switch ( strtolower( $type ) ) {
case 'x':
return $this->page_margins[0];
break;
case 'y':
return $this->page_margins[1];
break;
default:
return $this->page_margins;
break;
}
}
function getCurrentPage() {
return $this->getPDFObject()->getPage();
}
function setTempPageOffsets( $x, $y ) {
$this->temp_page_offsets = [ $x, $y ];
return true;
}
function getTempPageOffsets( $type = null ) {
switch ( strtolower( $type ) ) {
case 'x':
return $this->temp_page_offsets[0];
break;
case 'y':
return $this->temp_page_offsets[1];
break;
default:
return $this->temp_page_offsets;
break;
}
}
function setPageOffsets( $x, $y ) {
$this->page_offsets = [ $x, $y ];
$this->setTempPageOffsets( $x, $y ); //Update temp page offsets each time this is called.
return true;
}
function getPageOffsets( $type = null ) {
switch ( strtolower( $type ) ) {
case 'x':
return $this->page_offsets[0];
break;
case 'y':
return $this->page_offsets[1];
break;
default:
return $this->page_offsets;
break;
}
}
function setTemplateOffsets( $x, $y ) {
$this->template_offsets = [ $x, $y ];
return true;
}
function getTemplateOffsets( $type = null ) {
switch ( strtolower( $type ) ) {
case 'x':
return $this->template_offsets[0];
break;
case 'y':
return $this->template_offsets[1];
break;
default:
return $this->template_offsets;
break;
}
}
function getTemplateDirectory() {
$dir = $this->getClassDirectory() . DIRECTORY_SEPARATOR . 'templates';
return $dir;
}
function getSchemaSpecificCoordinates( $schema, $key, $sub_key1 = null ) {
unset( $schema['function'] );
if ( $sub_key1 !== null ) {
if ( isset( $schema['coordinates'][$key][$sub_key1] ) ) {
return [ 'coordinates' => $schema['coordinates'][$key][$sub_key1] ];
}
} else {
if ( isset( $schema['coordinates'][$key] ) ) {
return [ 'coordinates' => $schema['coordinates'][$key], 'font' => ( isset( $schema['font'] ) ) ? $schema['font'] : [] ];
}
}
return false;
}
//This gives the same affect of adding a new page on the next time Draw() is called.
//Can be used when multiple records are processed for a single form.
function resetTemplatePage() {
$this->current_template_index = null;
return true;
}
//Draw all digits before the decimal in the first location, and after the decimal in the second location.
function drawSplitDecimalFloat( $value, $schema ) {
if ( $value != 0 || isset( $schema['draw_zero_value'] ) && $schema['draw_zero_value'] == true ) {
$this->Draw( $this->getBeforeDecimal( $value ), $this->getSchemaSpecificCoordinates( $schema, 0 ) );
$this->Draw( $this->getAfterDecimal( $value ), $this->getSchemaSpecificCoordinates( $schema, 1 ) );
}
return true;
}
//Draw each char/digit one at a time in different locations.
function drawChars( $value, $schema ) {
$value = (string)$value; //convert integer to string.
$max = strlen( $value );
for ( $i = 0; $i < $max; $i++ ) {
$this->Draw( $value[$i], $this->getSchemaSpecificCoordinates( $schema, $i ) );
}
return true;
}
// Draw the same data at different locations
// value should be string
function drawPiecemeal( $value, $schema ) {
unset( $schema['function'] );
foreach ( $schema['coordinates'] as $key => $coordinates ) {
if ( is_array( $coordinates ) ) {
if ( isset( $schema['font'] ) ) {
$this->Draw( $value, [ 'coordinates' => $coordinates, 'font' => $schema['font'] ] );
} else {
$this->Draw( $value, [ 'coordinates' => $coordinates ] );
}
}
}
return true;
}
//Draw each element of an array at different locations.
//Value must be an array.
function drawSegments( $value, $schema ) {
if ( is_array( $value ) ) {
$i = 0;
foreach ( $value as $segment ) {
$this->Draw( $segment, $this->getSchemaSpecificCoordinates( $schema, $i ) );
$i++;
}
}
return true;
}
//Draw an normal values in a grid.
function drawNormalGrid( $value, $schema ) {
if ( !is_array( $value ) ) {
$value = (array)$value;
}
foreach ( $value as $key => $tmp_value ) {
if ( $tmp_value !== false ) {
//var_dump($tmp_value, $schema['coordinates'][$key] );
//$this->Draw( $this->getBeforeDecimal( $value ), array('coordinates' => $schema['coordinates'][$key][0] ) );
//var_dump( $this->getSchemaSpecificCoordinates( $schema, $key, 0 ) );
//$this->Draw( $this->getBeforeDecimal( $value ), $this->getSchemaSpecificCoordinates( $schema, $key, 0 ) );
if ( is_array( $tmp_value ) ) {
foreach ( $tmp_value as $value ) {
$this->drawNormal( $value, $this->getSchemaSpecificCoordinates( $schema, $key ) );
}
} else {
$this->drawNormal( $tmp_value, $this->getSchemaSpecificCoordinates( $schema, $key ) );
}
//$this->Draw( $tmp_value, $this->getSchemaSpecificCoordinates( $schema, $key ) );
}
}
return true;
}
//Draw an split decimal values in a grid.
function drawSplitDecimalFloatGrid( $value, $schema ) {
if ( !is_array( $value ) ) {
$value = (array)$value;
}
foreach ( $value as $key => $tmp_value ) {
if ( $tmp_value !== false ) {
//var_dump($tmp_value, $schema['coordinates'][$key] );
//$this->Draw( $this->getBeforeDecimal( $value ), array('coordinates' => $schema['coordinates'][$key][0] ) );
//var_dump( $this->getSchemaSpecificCoordinates( $schema, $key, 0 ) );
//$this->Draw( $this->getBeforeDecimal( $value ), $this->getSchemaSpecificCoordinates( $schema, $key, 0 ) );
if ( is_array( $tmp_value ) ) {
foreach ( $tmp_value as $value ) {
$this->drawSplitDecimalFloat( $value, $this->getSchemaSpecificCoordinates( $schema, $key ) );
}
} else {
$this->drawSplitDecimalFloat( $tmp_value, $this->getSchemaSpecificCoordinates( $schema, $key ) );
}
//$this->Draw( $tmp_value, $this->getSchemaSpecificCoordinates( $schema, $key ) );
}
}
return true;
}
//Draw an X in each of the specified locations
//$value must be an array.
function drawCheckBox( $value, $schema ) {
$char = 'x';
if ( !is_array( $value ) ) {
$value = (array)$value;
}
foreach ( $value as $tmp_value ) {
//Skip any false values.
if ( $tmp_value === false ) {
continue;
}
if ( is_string( $tmp_value ) ) {
$tmp_value = strtolower( $tmp_value );
}
if ( is_bool( $tmp_value ) && $tmp_value == true ) {
$tmp_value = 0;
}
$this->Draw( $char, $this->getSchemaSpecificCoordinates( $schema, $tmp_value ) );
}
return true;
}
function drawNormal( $value, $schema ) {
if ( $value !== false ) { //If value is FALSE don't draw anything, this prevents a blank cell from being drawn overtop of other text.
unset( $schema['function'] ); //Strip off the function element to prevent infinite loop
$this->Draw( $value, $schema );
return true;
}
return false;
}
function drawGrid( $value, $schema ) {
unset( $schema['function'] );
if ( isset( $schema['grid'] ) ) {
$grid = $schema['grid'];
}
if ( is_array( $value ) ) {
if ( isset( $grid ) && is_array( $grid ) ) {
$top_left_x = $x = $grid['top_left_x'];
$top_left_y = $y = $grid['top_left_y'];
$h = $grid['h'];
$w = $grid['w'];
$step_x = $grid['step_x'];
$step_y = $grid['step_y'];
$col = $grid['column'];
$i = 1;
foreach ( $value as $val ) {
$coordinates = [
'x' => $x,
'y' => $y,
'h' => $h,
'w' => $w,
];
$schema['coordinates'] = array_merge( $schema['coordinates'], $coordinates );
$this->Draw( $val, $schema );
if ( $i > 0 && $i % $col == 0 ) {
$x = $top_left_x;
$y += $step_y;
} else {
$x += $step_x;
}
$i++;
}
}
}
return true;
}
function drawMessageFieldNotice( $pdf, $i, $messages_arr ) {
if ( !isset( $messages_arr['field_notice_position']['x'] ) ) {
$messages_arr['field_notice_position']['x'] = 10;
}
if ( !isset( $messages_arr['field_notice_position']['y'] ) ) {
$messages_arr['field_notice_position']['y'] = 10;
}
if ( !isset( $messages_arr['field_notice_position']['w'] ) ) {
$messages_arr['field_notice_position']['w'] = 20;
}
if ( !isset( $messages_arr['field_notice_position']['h'] ) ) {
$messages_arr['field_notice_position']['h'] = 15;
}
if ( !isset( $messages_arr['field_notice_position']['page'] ) ) {
$messages_arr['field_notice_position']['page'] = 1;
}
$current_page = $pdf->getPage();
$current_x = $pdf->getX();
$current_y = $pdf->getY();
$pdf->setPage( $messages_arr['field_notice_position']['page'] );
$pdf->SetFont( '', 'B', 10 );
$pdf->setXY( ( $messages_arr['field_notice_position']['x'] + $this->getTempPageOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), ( $messages_arr['field_notice_position']['y'] + $this->getTempPageOffsets( 'y' ) + $this->getPageMargins( 'y' ) ) );
$pdf->Cell( $messages_arr['field_notice_position']['w'], $messages_arr['field_notice_position']['h'], '['. $i .']', 0 );
$pdf->setPage( $current_page );
$pdf->setXY( $current_x, $current_y );
return true;
}
//Draws messages, warnings and errors.
function drawMessages() {
$messages = $this->getMessages();
if ( is_array( $messages ) && !empty( $messages ) ) {
$pdf = $this->getPDFObject();
$pdf->AddPage();
$current_page = $pdf->getPage();
$pdf->setTextColor( 255, 0, 0 );
$cell_width = 570;
$pdf->setXY( ( 20 + $this->getTempPageOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), ( 20 + $this->getTempPageOffsets( 'y' ) + $this->getPageMargins( 'y' ) ) );
$i = 'A'; //Subscript to reference each message. Should be letters rather than numbers so they don't get mixed up when printed on the form.
if ( isset( $messages['error'] ) ) {
$pdf->SetFont( '', 'B', 32 );
$pdf->setXY( ( 20 + $this->getTempPageOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), $pdf->getY() );
$pdf->Cell( $cell_width, 10, TTi18n::getText( 'ERROR' ), 0, 1, 'C', 1, false, 1 );
$pdf->SetFont( '', '', 12 );
foreach ( $messages['error'] as $message_arr ) {
$message = $i .'. '. $message_arr['message'];
$pdf->setXY( ( 20 + $this->getTempPageOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), $pdf->getY() );
$pdf->MultiCell( $cell_width, $pdf->getNumLines( $message, $cell_width ), $message, 0, 'L' );
if ( isset( $message_arr['field_notice_position'] ) && is_array( $message_arr['field_notice_position'] ) ) {
$this->drawMessageFieldNotice( $pdf, $i, $message_arr );
}
$i++;
}
$pdf->Ln();
}
if ( isset( $messages['warning'] ) ) {
$pdf->SetFont( '', 'B', 32 );
$pdf->setXY( ( 20 + $this->getTempPageOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), $pdf->getY() );
$pdf->Cell( $cell_width, 10, TTi18n::getText( 'WARNING' ), 0, 1, 'C', 1, false, 1 );
$pdf->SetFont( '', '', 12 );
foreach ( $messages['warning'] as $message_arr ) {
$message = $i .'. '. $message_arr['message'];
$pdf->setXY( ( 20 + $this->getTempPageOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), $pdf->getY() );
$pdf->MultiCell( $cell_width, $pdf->getNumLines( $message, $cell_width ), $message, 0, 'L' );
if ( isset( $message_arr['field_notice_position'] ) && is_array( $message_arr['field_notice_position'] ) ) {
$this->drawMessageFieldNotice( $pdf, $i, $message_arr );
}
$i++;
}
$pdf->Ln();
}
if ( isset( $messages['note'] ) ) {
$pdf->setTextColor( 0, 0, 0 );
$pdf->SetFont( '', 'B', 32 );
$pdf->setXY( ( 20 + $this->getTempPageOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), $pdf->getY() );
$pdf->Cell( $cell_width, 10, TTi18n::getText( 'NOTE' ), 0, 1, 'C', 1, false, 1 );
$pdf->SetFont( '', '', 12 );
foreach ( $messages['note'] as $message_arr ) {
$message = $i .'. '. $message_arr['message'];
$pdf->setXY( ( 20 + $this->getTempPageOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), $pdf->getY() );
$pdf->MultiCell( $cell_width, $pdf->getNumLines( $message, $cell_width ), $message, 0, 'L' );
if ( isset( $message_arr['field_notice_position'] ) && is_array( $message_arr['field_notice_position'] ) ) {
$this->drawMessageFieldNotice( $pdf, $i, $message_arr );
}
$i++;
}
}
$pdf->setPage( $current_page );
$pdf->movePage( $current_page, 1 ); //Move messages page to the beginning now that its been generated.
//Clear messages after they are drawn, so more can be added for the next page/employee?
$this->clearMessages();
}
return true;
}
function addPage( $schema = null ) {
$pdf = $this->getPDFObject();
$pdf->AddPage();
if ( $this->getShowBackground() == true && isset( $this->template_index[$schema['template_page']] ) ) {
if ( isset( $schema['combine_templates'] ) && is_array( $schema['combine_templates'] ) ) {
$template_schema = $this->getTemplateSchema();
//Handle combining multiple template together with a X,Y offset.
foreach ( $schema['combine_templates'] as $combine_template ) {
//Debug::text('Combining Template Pages... Template: '. $combine_template['template_page'] .' Y: '. $combine_template['y'], __FILE__, __LINE__, __METHOD__, 10);
$pdf->useTemplate( $this->template_index[$combine_template['template_page']], ( $combine_template['x'] + $this->getTemplateOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), ( $combine_template['y'] + $this->getTemplateOffsets( 'y' ) + $this->getPageMargins( 'y' ) ) );
$this->setTempPageOffsets( ( $combine_template['x'] + $this->getPageOffsets( 'x' ) ), ( $combine_template['y'] + $this->getPageOffsets( 'y' ) ) );
$this->current_template_index = $combine_template['template_page'];
//For things like W2 instruction templates at the bottom half of the page, allow the initPage() function to be disabled for the template.
if ( !isset( $combine_template['init'] ) || ( isset( $combine_template['init'] ) && $combine_template['init'] == true ) ) {
$this->initPage( $template_schema );
}
}
unset( $combine_templates );
$this->setTempPageOffsets( $this->getPageOffsets( 'x' ), $this->getPageOffsets( 'y' ) ); //Reset page offsets after each template is initialized.
} else {
$pdf->useTemplate( $this->template_index[$schema['template_page']], ( $this->getTemplateOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), ( $this->getTemplateOffsets( 'y' ) + $this->getPageMargins( 'y' ) ) );
}
}
$this->current_template_index = $schema['template_page'];
return true;
}
function initPage( $template_schema ) {
if ( is_array( $template_schema ) ) {
foreach ( $template_schema as $field => $init_schema ) {
if ( is_numeric( $field ) ) {
//Debug::text(' Initializing Template Page... Field: '. $field, __FILE__, __LINE__, __METHOD__, 10);
$this->Draw( $this->$field, $init_schema );
}
}
unset( $template_schema, $field, $init_schema );
return true;
}
return false;
}
//Run all calculate functions on their own.
// This separates calculating values from the drawing process, so we can easily pull out calculated values before anything is drawn, as other forms may need that data.
function calculate() {
//Get location map, start looping over each variable and drawing
$template_schema = ( method_exists( $this, 'getTemplateSchema') ) ? $this->getTemplateSchema() : false;
if ( is_array( $template_schema ) ) {
foreach ( $template_schema as $field => $schema ) {
//If custom function is defined, pass off to that immediate.
//Else, try the generic drawing method.
if ( isset( $schema['function']['calc'] ) ) {
if ( !is_array( $schema['function']['calc'] ) ) {
$schema['function']['calc'] = (array)$schema['function']['calc'];
}
foreach ( $schema['function']['calc'] as $function ) {
if ( method_exists( $this, $function ) ) {
if ( !isset( $template_schema[$field]['value'] ) ) {
//$template_schema[$field]['value'] = ( isset( $this->{$field} ) ? $this->{$field} : null ); //This passes in the field value, which the calculate function can get on its own without a problem.
$template_schema[$field]['value'] = null;
}
$template_schema[$field]['value'] = $this->$function( $template_schema[$field]['value'], $schema );
}
}
unset( $function );
}
}
}
return true;
}
//Generic draw function that works strictly off the coordinate map.
//It checks for a variable specific function before running though, so we can handle more complex
//drawing functionality.
function Draw( $value, $schema ) {
if ( !is_array( $schema ) ) {
return false;
}
//If its set, use the static value from the schema.
if ( isset( $schema['value'] ) ) {
$value = $schema['value'];
unset( $schema['value'] );
}
//If custom function is defined, pass off to that immediate.
//Else, try the generic drawing method.
if ( isset( $schema['function']['draw'] ) ) {
if ( !is_array( $schema['function']['draw'] ) ) {
$schema['function']['draw'] = (array)$schema['function']['draw'];
}
foreach ( $schema['function']['draw'] as $function ) {
if ( method_exists( $this, $function ) ) {
$value = $this->$function( $value, $schema );
}
}
unset( $function );
return $value;
}
$pdf = $this->getPDFObject();
//Make sure we don't load the same template more than once.
if ( isset( $schema['template_page'] ) && $schema['template_page'] != $this->current_template_index ) {
//Debug::text('Adding new page: '. $schema .' Template Page: '. $schema['template_page'], __FILE__, __LINE__, __METHOD__, 10);
$this->addPage( $schema );
} else {
//Debug::text('Skipping template... Value: '. $value, __FILE__, __LINE__, __METHOD__, 10);
}
//If only_template_page is set, then only draw when we are on that template.
if ( isset( $schema['only_template_page'] ) && ( ( is_array( $schema['only_template_page'] ) && !in_array( $this->current_template_index, $schema['only_template_page'] ) ) || ( !is_array( $schema['only_template_page'] ) && $schema['only_template_page'] != $this->current_template_index ) ) ) {
//Debug::text('Skipping template based on filter... Value: '. $value, __FILE__, __LINE__, __METHOD__, 10);
return false;
}
//on_background flag forces that item to only be shown if the background is as well.
//This has to go below any addPage() call, otherwise pages won't be added if the first cell is only to be shown on the background.
if ( isset( $schema['on_background'] ) && $schema['on_background'] == true && $this->getShowBackground() == false ) {
return false;
}
if ( isset( $schema['font'] ) ) {
if ( !isset( $schema['font']['font'] ) ) {
$schema['font']['font'] = $this->default_font;
}
if ( !isset( $schema['font']['type'] ) ) {
$schema['font']['type'] = '';
}
if ( !isset( $schema['font']['size'] ) ) {
$schema['font']['size'] = 8;
}
$pdf->SetFont( $schema['font']['font'], $schema['font']['type'], $schema['font']['size'] );
} else {
$pdf->SetFont( $this->default_font, '', 8 );
}
if ( isset( $schema['coordinates'] ) ) {
$coordinates = $schema['coordinates'];
//var_dump( Debug::BackTrace() );
if ( isset( $coordinates['text_color'] ) && is_array( $coordinates['text_color'] ) ) {
$pdf->setTextColor( $coordinates['text_color'][0], $coordinates['text_color'][1], $coordinates['text_color'][2] );
} else {
$pdf->setTextColor( 0, 0, 0 ); //Black text.
}
if ( isset( $coordinates['fill_color'] ) && is_array( $coordinates['fill_color'] ) ) {
$pdf->setFillColor( $coordinates['fill_color'][0], $coordinates['fill_color'][1], $coordinates['fill_color'][2] );
$coordinates['fill'] = 1;
} else {
$pdf->setFillColor( 255, 255, 255 ); //White
$coordinates['fill'] = 0;
}
$pdf->setXY( ( $coordinates['x'] + $this->getTempPageOffsets( 'x' ) + $this->getPageMargins( 'x' ) ), ( $coordinates['y'] + $this->getTempPageOffsets( 'y' ) + $this->getPageMargins( 'y' ) ) );
if ( $this->getDebug() == true ) {
$pdf->setDrawColor( 0, 0, 255 );
$coordinates['border'] = 1;
} else {
if ( !isset( $coordinates['border'] ) ) {
$coordinates['border'] = 0;
}
}
if ( isset( $schema['multicell'] ) && $schema['multicell'] == true ) {
//Debug::text('Drawing MultiCell... Value: '. $value, __FILE__, __LINE__, __METHOD__, 10);
$pdf->MultiCell( $coordinates['w'], $coordinates['h'], $value, $coordinates['border'], strtoupper( $coordinates['halign'] ), $coordinates['fill'] );
} else {
//Debug::text('Drawing Cell... Value: '. $value, __FILE__, __LINE__, __METHOD__, 10);
$pdf->Cell( $coordinates['w'], $coordinates['h'], $value, $coordinates['border'], 0, strtoupper( $coordinates['halign'] ), $coordinates['fill'], false, 1 );
}
unset( $coordinates );
} else {
Debug::text( 'NOT Drawing Cell... Value: ' . $value, __FILE__, __LINE__, __METHOD__, 10 );
}
return true;
}
//Make sure we pass *ALL* data to this function, as it will overwrite existing data, but if one record has a field and another one doesn't,
//we need to send blank fields so the data is overwritten correctly.
function arrayToObject( $array ) {
if ( is_array( $array ) ) {
foreach ( $array as $key => $value ) {
$this->$key = $value;
}
}
return true;
}
/*
*
* Magic functions.
*
*/
function __set( $name, $value ) {
$template_schema = ( method_exists( $this, 'getTemplateSchema') ) ? $this->getTemplateSchema() : false;
if ( is_array( $template_schema ) && isset( $template_schema[$name]['function']['prefilter'] ) ) {
$filter_function = $template_schema[$name]['function']['prefilter'];
if ( $filter_function != '' ) {
if ( !is_array( $filter_function ) ) {
$filter_function = (array)$filter_function;
}
foreach ( $filter_function as $function ) {
//Call function
if ( method_exists( $this, $function ) ) {
$value = $this->$function( $value );
if ( $value === false ) {
return false;
}
}
}
unset( $function );
}
}
$this->data[$name] = $value;
return true;
}
function __get( $name ) {
if ( isset( $this->data[$name] ) ) {
return $this->data[$name];
}
return false;
}
public function __isset( $name ) {
return isset( $this->data[$name] );
}
public function __unset( $name ) {
unset( $this->data[$name] );
}
}
?>