<?php
/**
* @name printform-pdf.php, contains class CPrintFormPdf,
* for generating "on the fly" PDF document populated with user data, possibly from source pdf template file,
* loaded configuration from XML file or string.
* @uses TCPDF, FPDFI classes for reading/writing pdf body, see http://www.tcpdf.org/
* @Author Alexander Selifonov, <alex [at] selifan {dot} ru>
* @version 1.57.080 2022-10-18
* @Link: https://github.com/selifan/prinformpdf
* @license http://www.opensource.org/licenses/bsd-license.php BSD
*
**/
if(!class_exists('TCPDF',false)) {
# You should add this block in your module BEFORE include printform-pdf.php,
# OR define PRINTPDF_LANGUAGE with Your base language prefix
# ( include your TCPDF language module in tcpdf/config/lang folder, my case is lang/rus.php )
$lang = (defined('PRINTPDF_LANGUAGE') ? constant('PRINTPDF_LANGUAGE') : 'rus');
# require_once("tcpdf/config/lang/$lang.php"); // latest TCPDF versions have no lang modules!
require_once('tcpdf/tcpdf.php');
}
if (!class_exists('FPDF')) {
require_once('fpdf/fpdi2tcpdf_bridge.php');
require_once('fpdf/fpdi.php'); # 1.4.2 had parazite "g" echoing ! Use latest FPDI !!!
}
/**
* Abstract class for PrintFormPDF plugins - modules that will draw by specific algorhytms in desired rectangle regions
* @since 1.1.0016 (2013-01-20)
*/
class PRINTPDFCONST {
static $currentPageNo = 0;
const PF_ADDPAGE = '{NP}'; # pseudo field in grid, command "add New Page" (NP)
}
abstract class PfPdfPlugin {
protected $_error_message = 'no error';
protected $_region_pos = [0, 0];
protected $_region_dim = [0, 0];
abstract public function __construct($tcpdfobj, $cfg = null, $x=0,$y=0,$w=0,$h=0);
# Render method should draw something according to passed _data, inside defined rectangle area (_region_pos, _region_dim)
abstract public function Render($data);
public function setAreaPosition($x, $y, $w=0, $h=0) {
$this->_region_pos = array(floatval($x),floatval($y));
$this->_region_dim = array(floatval($w),floatval($h));
return $this;
}
public function getErrorMessage() { return $this->_error_message; }
}
class CPrintFormPdf {
const VERSION = '1.45';
const DEFAULT_MARGIN = 5;
const DEFAULT_GRID_ROWS = 30;
const DEFAULT_GRID_STEPY = 6;
const BOTTOM_H = 5; # Minimal height for "bottom" area for page numbers etc.
const PAGINATION_FONTSIZE = 7; # Font size for page numbers
static public $MIN_ZIGZAG_WEIGHT = 6;
static private $currentPageNo = 1;
static $debug = 0;
public $father = NULL;
static $errorMessages = [];
protected $childDefs = []; # child definitions appended by AppendPageDefFromXml()
protected $_data = [];
protected $_dataBlock = NULL;
protected $dataentity = []; # data for single document (one element from _data beeing printed)
protected $pageSubData = []; # data for the page passed as sub-array
protected $_pageno = 0;
protected $_pluginData = [];
protected $_basepar = [];
protected $_tmpBasepar = FALSE;
protected $_paginatonMode = FALSE;
public $_templatefile = []; # source file(s) to be used as template
protected $_alttemplatefile = []; # alternative template
protected $_outname = '';
protected $defaultTextColor = 0;
protected $_pdf = null;
protected $pgno = 0;
protected $prnPage = 0; // printed page number
protected $_tofile = false;
protected $_configfile = '';
public $datasource = FALSE; # if set, array '_datasource_id' will hold N rows data to create N printed documents
protected $_config = [];
protected $_pagedefs = [];
protected $_errormessage = '';
protected $_alttemplate = false;
protected $_rulerStep = false;
protected $_rulerColor = array(80,80,200);
protected $_compression = false; # compress result PDF
protected $offsets = array(0,0); # for fine tuning field positions, depending on user printer
protected $_printedfields = false;
protected $_datagrids = [];
protected $_curGridRow = [];
protected $_specialPages = 0;
protected $_mpgrid = FALSE; # becomes TRUE if multi-paging grid detected
protected $_hide_pages = [];
protected $_hide_fields = []; # field list to be hidden
protected $_field_newattr = []; # "dynamically changed" field attribs array
protected $_images_disabled = false;
protected $_homedir = ''; # directory where loaded XML file resides
protected $_pdf_path = ''; # PDF templates folder
protected $_img_path = ''; # images folder
protected $_curSrcPdf = '';
protected $_curSrcFile = -1, $_curSrcPage=-1, $_curSrcPgcount=-1;
protected $_apFields = []; # fields to draw on All Pages
protected $_apFinally = FALSE; # if TRUE, AllFields should be rendered last
protected $_apValues = []; # values for All Pages fields
protected $_tmpFldNo = 0;
# "ALL pages" fields in "added" page definitions:
protected $_subApFields = [];
protected $_subApValues = [];
protected $spot_colors = [];
protected $_debug = 0;
protected $_outputMode = 'I';
static protected $_cached = [];
protected $adaptive_sizing = 0; # auto-change font size to make text fit in area (width * height)
protected $signFunc = false; # external function for instant "signing" generated PDF
protected $userParams = []; # to store data from <userparams> block
protected $hooks = [];
protected $callbackObj = NULL;
public static function getVersion() {
return self::VERSION;
}
public function __construct($param='', $callbackObj=FALSE, $father = NULL, $subid = NULL) {
# writeDebugInfo("__construct subid: [$subid], param: ", $param);
$loadData = NULL;
$printMe = FALSE;
if (is_object($callbackObj)) {
$this->callbackObj = $callbackObj;
}
if(is_object($father)) $this->father = $father;
else {
$this->father = $this;
$printMe = TRUE;
}
$this->_basepar = array(
'page' => array('orientation'=>'P', 'size'=>'A4', 'units'=>'mm')
,'font' => array('name'=>'arial', 'size'=>8, 'color'=> '', 'style'=>'')
,'margins' => array('left'=>0, 'top'=>0, 'right'=>0, 'bottom'=>0)
,'pgprefix'=> '_page' # prefix for "specific" data in 'pageNN'
);
$this->_config = array(
'subject' => ''
,'author' => ''
,'creator' => ''
,'protectfile' => false
);
if(is_array($param)) {
if(isset($param['template'])) $this->_templatefile[] = array('src'=>(string) array($param['template']));
if(isset($param['alttemplate'])) $this->_alttemplatefile[] = (string)$param['template'];
if(isset($param['outname'])) $this->_outname= $param['outname'];
if(isset($param['output'])) $this->_outputMode = $param['output'];
if(!empty($param['compression'])) $this->_compression = true;
if(isset($param['tofile'])) $this->_tofile = $param['tofile'];
if(isset($param['configfile'])) {
$this->_configfile = $param['configfile'];
}
if(isset($param['subject'])) $this->_config['subject'] = (string)$param['subject'];
if(isset($param['description'])) $this->_config['description'] = (string)$param['description'];
if(isset($param['author'])) $this->_config['author'] = (string)$param['author'];
if(isset($param['creator'])) $this->_config['creator'] = (string)$param['creator'];
if(isset($param['stringcharset'])) {
$this->_config['stringcharset'] = self::parseParam((string)$param['stringcharset']);
}
if(isset($param['datasource'])) $this->datasource = (string)$param['datasource'];
if(isset($param['pdfpath']))
$this->_pdf_path = (string)$param['pdfpath'];
if(isset($param['imgpath']))
$this->_img_path = (string)$param['imgpath'];
# callback function to be called after last page done:
if(isset($param['hook_end']))
$this->hooks['hook_end'] = (string)$param['hook_end'];
}
elseif(is_string($param)) { # configuration XML filename or whole XML string was passed
$this->_configfile = $param;
}
if(!empty($this->_configfile)) {
$this->_homedir = dirname($this->_configfile) .'/';
if ($this->_pdf_path == '') $this->_pdf_path = $this->_homedir;
if ($this->_img_path == '') $this->_img_path = $this->_homedir;
$ok = $this->LoadConfig(NULL, $subid);
}
$this->_pageno = 0;
# if hook function name passed, save to call after PDF generation finished
if (!empty($param['hook_end'])) $this->hooks['hook_end'] = $param['hook_end'];
# if($printMe) writeDebugInfo("printPdf Object: ", $this);
}
public function setCallbackObject($callbackObj) {
$this->callbackObj = $callbackObj;
}
public function setOutput($out = 'I') {
$this->_outputMode = $out;
}
// setting signing function for final PDF body
public function addSignFunction($funcName) {
$this->signFunc = $funcName;
}
/**
* Loads configuration from prepared XML file, see it's format in docs and examples
*
* @param mixed $cfgname full path-filename to config XML file
* @Returns true if configuration successfully loaded, false otherwise
*/
public function LoadConfig($cfgname=null, $operSubid = '') {
# writeDebugInfo(">>> LoadConfig(cfg:'$cfgname', subid:'$operSubid')");
$this->_alttemplate = false;
if(!$cfgname) $cfgname = $this->_configfile;
else $this->_configfile = $cfgname;
# writeDebugInfo("LoadConfig(), cfg: ", $this->_configfile);
$ret = true;
if(is_file($cfgname)) {
$xml = simplexml_load_file($cfgname);
}
elseif(substr($cfgname,0,5)=='<'.'?xml') {
$xml = @simplexml_load_string($cfgname);
$cfgname='';
}
else {
$this->_errormessage = 'Configuration XML file not found: '.$cfgname;
return false;
}
$homePath = dirname(realpath($this->_configfile)) . '/'; # folder of parsed XML config file
if(!($xml) OR !@isset($xml->pages)) {
$this->_errormessage = 'Wrong XML file or XML string syntax, '.$cfgname;
echo $this->_errormessage ;
return false;
}
if(isset($xml->description)) $this->_config['description'] = (string)$xml->description; # protect workpages by password
if(isset($xml->protectfile)) $this->_config['protectfile'] = (int)$xml->protectfile; # protect workpages by password
if(isset($xml->password)) $this->_config['password'] = (string)$xml->password; # password to protect with
if(isset($xml->title)) $this->_config['title'] = (string)$xml->title;
if(isset($xml->stringcharset)) $this->_config['stringcharset'] = strtoupper(self::parseParam($xml->stringcharset));
# if not UTF-8 and not '', use iconv() to make UTF-8 !
if(isset($xml->baseparameters->page)) {
if(isset($xml->baseparameters->page['orientation'])) $this->_basepar['page']['orientation'] = (string)$xml->baseparameters->page['orientation'];
if(isset($xml->baseparameters->page['size'])) $this->_basepar['page']['size'] = (string)$xml->baseparameters->page['size'];
if(isset($xml->baseparameters->page['units'])) $this->_basepar['page']['units'] = (string)$xml->baseparameters->page['units'];
}
# misc "common" parameters
if(isset($xml->baseparameters->params)) {
if(isset($xml->baseparameters->params['pgprefix']))
$this->_basepar['pgprefix'] = (string)$xml->baseparameters->params['pgprefix'];
}
# @since 1.42: user defined params in XML block <userparameters>...</userparameters>
if(isset($xml->userparameters)) {
foreach($xml->userparameters->children() as $key => $usrPar) {
$pname = (isset($usrPar['name']) ? (string) $usrPar['name'] : '');
$pvalue = (isset($usrPar['value']) ? self::parseParam($usrPar['value']) : NULL);
if ($pname) $this->userParams[$pname] = $pvalue;
}
}
if(isset($xml->baseparameters->pagination)) {
$this->_basepar['pagination'] = [];
$this->_basepar['pagination']['align'] = (isset($xml->baseparameters->pagination['align'])) ?
strtoupper((string)$xml->baseparameters->pagination['align']) : 'C';
$this->_basepar['pagination']['posy'] = (isset($xml->baseparameters->pagination['posy'])) ?
strtolower((string)$xml->baseparameters->pagination['posy']): 'bottom';
$this->_basepar['pagination']['format'] = (isset($xml->baseparameters->pagination['format'])) ?
trim((string)$xml->baseparameters->pagination['format']) : '%page%';
}
if(isset($xml->baseparameters->pdfpath)) {
$this->_pdf_path = (string)$xml->baseparameters->pdfpath;
}
if(isset($xml->baseparameters->imgpath)) $this->_img_path = (string)$xml->baseparameters->imgpath;
if(isset($xml->baseparameters->font)) {
if(!empty($xml->baseparameters->font['name'])) $this->_basepar['font']['name'] = (string)$xml->baseparameters->font['name'];
if(!empty($xml->baseparameters->font['size'])) $this->_basepar['font']['size'] = (float)$xml->baseparameters->font['size'];
if(!empty($xml->baseparameters->font['color'])) $this->_basepar['font']['color'] = (string) $xml->baseparameters->font['color'];
if(!empty($xml->baseparameters->font['style'])) $this->_basepar['font']['style'] = (string) $xml->baseparameters->font['style'];
if(isset($xml->baseparameters->font['autosize'])) $this->adaptive_sizing = (int)$xml->baseparameters->font['autosize'];
}
if(isset($xml->baseparameters->margins)) {
if(isset($xml->baseparameters->margins['left'])) $this->_basepar['margins']['left'] = (float)$xml->baseparameters->font['left'];
if(isset($xml->baseparameters->margins['right'])) $this->_basepar['margins']['right'] = (float)$xml->baseparameters->font['right'];
if(isset($xml->baseparameters->margins['top'])) $this->_basepar['margins']['top'] = (float)$xml->baseparameters->font['top'];
if(isset($xml->baseparameters->margins['bottom'])) $this->_basepar['margins']['bottom'] = (float)$xml->baseparameters->font['bottom'];
}
if (isset($xml->baseparameters->templatefile)) {
if(!empty($xml->baseparameters->templatefile['src']))
$this->_templatefile[] = $this->readTemplateDef($xml->baseparameters->templatefile);
if(!empty($xml->baseparameters->templatefile['altsrc']))
$this->_alttemplatefile[] = (string)$xml->baseparameters->templatefile['altsrc'];
}
if ( isset($xml->templatefiles) ) {
foreach($xml->templatefiles->children() as $key=>$item) {
if(!empty($item['src'])) {
$this->_templatefile[] = $this->readTemplateDef($item);
}
}
}
if(isset($xml->allpages)) { # All Pages Fields exist, load them!
$this->_apFinally = !empty($xml->allpages['finally']);
foreach($xml->allpages->children() as $key=>$item) {
if($key === 'field' OR $key ==='image') {
$value = isset($item['value'])? trim("{$item['value']}") : null;
if($value !=='' and strtoupper($this->_config['stringcharset']) !== 'UTF-8')
$value = iconv('UTF-8',$this->_config['stringcharset'],$value);
$this->addAllPagesField($item, $value);
}
}
}
$fileversion = isset($xml->version) ? $xml->version : 1; # for future needs
if(!empty($this->_config['templatefile'])) $this->_templatefile[] = array(
'src'=> (string)$this->_config['templatefile']
);
$this->_pagedefs = [];
$fldcnt = 0;
foreach($xml->pages->children() as $key => $pageitem) {
# $pageno = isset($pageitem['number']) ? (int) $pageitem['number'] : $ipage;
$hide_it = isset($pageitem['hide']) ? (int) $pageitem['hide'] : 0;
$pg_orient = isset($pageitem['orientation']) ? $pageitem['orientation'] : $this->_basepar['page']['orientation'];
$gridpage = $gridFields = false;
if ($key == 'importdef') { # Append pages definition from another XML cfg
$addXml = (isset($pageitem['src']) ? (string)$pageitem['src'] : '');
if(!empty($operSubid)) $subid = $operSubid; # passed from caller
else $subid = (isset($pageitem['datasubid']) ? (string)$pageitem['datasubid'] : '');
$ifCond = (isset($pageitem['if']) ? (string)$pageitem['if'] : FALSE);
if (substr($ifCond,0,1) === '@') $ifCond = substr($ifCond,1);
$b_import = TRUE;
if (!empty($ifCond)) {
if(is_callable($ifCond)) {
$b_import = call_user_func($ifCond, $this->dataentity);
}
elseif(is_object($this->callbackObj) && method_exists($this->callbackObj, $ifCond)) {
$b_import = $this->callbackObj->$ifCond();
}
else writeDebugInfo("if=[$ifCond] is not callable nor method of obj ", $this->callbackObj);
}
if ($b_import && strlen($addXml)) {
$realXml = $this->_evalAttribute($addXml);
if ($realXml) {
$filePath = self::getRelativeFilename($this->_homedir, $realXml);
if ($filePath) $this->AppendPageDefFromXml($filePath, $subid);
else die('Importdef: file not found: '.$filePath);
}
}
continue;
}
$pageno = $ipage = self::$currentPageNo++;
if (isset($pageitem['datasource']) ) { # "conditionally printed" data-grid page
$gridFields = isset($pageitem['fields']) ? explode(',', (string)$pageitem['fields']) : false;
if (is_array($gridFields)) $gridpage = array(
'datasource' => trim((string)$pageitem['datasource'])
,'fields' => $gridFields
,'rows' => (isset($pageitem['rows']) ? (integer)$pageitem['rows'] : self::DEFAULT_GRID_ROWS)
,'posx' => (isset($pageitem['posx']) ? (float)$pageitem['posx'] : 0)
,'posy' => (isset($pageitem['posy']) ? (float)$pageitem['posy'] : 0)
,'step_y' => (isset($pageitem['rows']) ? (float)$pageitem['step_y'] : self::DEFAULT_GRID_STEPY)
,'step_x' => (isset($pageitem['step_x']) ? (float)$pageitem['step_x'] : 0)
,'order' => (isset($pageitem['order']) ? (string)$pageitem['order'] : 'R') # "Row first"
,'cols' => (isset($pageitem['cols']) ? (integer)$pageitem['cols'] : 1)
,'fillempty' => (isset($pageitem['fillempty']) ? (string)$pageitem['fillempty'] : FALSE)
);
}
if($hide_it) $this->_hide_pages[$pageno] = true;
$this->father -> _pagedefs[$ipage] = array('pageno'=>$pageno, 'fields'=>[]
,'homepath' => $homePath
,'repeat'=>[]
,'hide'=>$hide_it
,'gridpage'=>$gridpage
,'datasubid' => $operSubid
,'orientation' => $pg_orient
,'ruler' => ( isset($pageitem['ruler']) ? (int)$pageitem['ruler'] : FALSE )
,'copies' => ( isset($pageitem['copies']) ? (int)$pageitem['copies'] : FALSE )
,'pageevent' => ( isset($pageitem['pageevent']) ? (string)$pageitem['pageevent'] : FALSE )
,'datagrids' => []
);
# if ($gridpage) { echo '<pre>' . print_r($gridpage,1) .'</pre>'; exit; } # debug echo
foreach($pageitem->children() as $key=>$item) {
if($key=='template') { # there is specific template for this page
$this->father->_pagedefs[$ipage]['template'] = array(
'src' => (isset($item['src']) ? (string) $item['src'] : false)
,'altsrc' => (isset($item['altsrc']) ? (string) $item['altsrc'] : false)
,'page' => (!empty($item['page']) ? (int) $item['page'] : 0)
,'orientation' => (!empty($item['orientation']) ? $item['orientation'] : $this->_basepar['page']['orientation'])
);
continue;
}
elseif(in_array($key, ['field', 'image', 'plugin'])) {
$fldname = isset($item['name'])? trim($item['name']) : '';
if(!$fldname) {
writeDebugInfo("bad (no-name) item: ", $item);
continue;
}
$newar = $this->prepareFieldDef($item);
if($key=='image') $newar['type'] = 'image';
if($key=='plugin' || $newar['type']=='plugin') { # drawing specific data plugin
$newar['plugintype'] = isset($item['plugintype']) ? strtolower((string)$item['plugintype']) : '';
if($newar['plugintype']=='' && $newar['type']!='plugin') $newar['plugintype'] = $newar['type'];
$newar['type'] = 'plugin';
}
$this->father->_pagedefs[$ipage]['fields'][] = $newar;
$fldcnt++;
}
elseif($key=='datagrid') {
$datasource = isset($item['datasource']) ? (string) $item['datasource'] : '';
$dtfields = isset($item['fields']) ? (string) $item['fields'] : '';
$dtfields = str_replace(' ','',$dtfields);
$fldArr = []; # field definitions are inside <datagrid> tag
foreach($item->Children() as $chKey => $chItem) {
if($chKey === 'field') {
$chfldName = isset($chItem['name'])? trim($chItem['name']) : '';
if(empty($chfldName)) continue;
$fldArr[] = $this->prepareFieldDef($chItem);
}
}
$fields = FALSE;
if(count($fldArr)) $fields = $fldArr;
elseif(!empty($dtfields)) $fields = explode(',',$dtfields);
if(!$fields) continue; # ignore empty datagrid tag (w/o field list)
$gridId = !empty($item['name']) ?
(string)$item['name'] : 'datagrid'.(count($this->father->_datagrids)+1);
$this->father->_datagrids[$gridId] = array(
'page' => $ipage
,'datasource' => $datasource
,'fields'=> $fields # field names OR FieldDef array defined inside <datagrid>
,'posx' => (isset($item['posx']) ? (string)$item['posx'] : '0')
,'posy' => (isset($item['posy']) ? (string)$item['posy'] : '0')
,'step_y'=> (isset($item['step_y']) ? (float)$item['step_y'] : 0)
,'rows' => (isset($item['rows']) ? (int)$item['rows'] : 2)
,'step_x' => (isset($item['step_x']) ? (float)$item['step_x'] : 0)
,'order' => (isset($item['order']) ? (string)$item['order'] : 'R')
,'cols' => (isset($item['cols']) ? (int)$item['cols'] : 1)
,'fillempty' => (isset($item['fillempty']) ? (string)$item['fillempty'] : FALSE)
);
$pageno = count($this->father->_pagedefs)-1;
# if ($datasource) {
$this->father->_pagedefs[$ipage]['datagrids'][] = $gridId;
# WriteDebugInfo("register datagrid $gridId for pagedef $ipage: data source=", $datasource);
# }
if (count($this->father->_datagrids[$gridId]['fields'])) {
foreach($this->father->_datagrids[$gridId]['fields'] as $gridField) {
if(!is_string($gridField)) continue;
foreach($this->father->_pagedefs[$ipage]['fields'] as &$fdef) {
if($fdef['name'] === $gridField) {
$fdef['ingrid'] = 1;
# to avoid print empty grid row
}
}
}
}
# writeDebugInfo("datagrid: ",$this->_datagrids[$gridId]);
}
elseif($key=='repeat') { # repeat all data on the sheet, with x/y shifting
$off_x = isset($item['offset_x']) ? (float)$item['offset_x'] : 0;
$off_y = isset($item['offset_y']) ? (float)$item['offset_y'] : 0;
$enabled = isset($item['enabled']) ? (string)$item['enabled'] : 1;
if(!empty($enabled) && is_callable($enabled)) $enabled = call_user_func($enabled, count($this->father->_pagedefs[$ipage]['repeat']));
if($off_x != 0 || $off_y != 0) $this->father->_pagedefs[$ipage]['repeat'][] = array($off_x,$off_y, $enabled);
}
}
if(count($this->_datagrids)) foreach($this->_datagrids as $gid => $dtgrid) {
// fill datagrid by prepared "numbered" fields: name1,age1; name2,age2,...
if ($dtgrid['page'] != $ipage) continue;
$posyarr = explode(',', $dtgrid['posy']);
$farr = [];
$gridFldDefs = $this->getFieldDefs($dtgrid['fields'], $ipage);
if(!count($gridFldDefs)) continue; # Skip grid: no one field has a name listed in datagrid
if (!empty($dtgrid['datasource'])){
# save $gridId for current pdf page def
continue; # named datasource will be handled w/o creating "pseudo-fields"
}
$rows = $dtgrid['rows'] ? $dtgrid['rows'] : count($posyarr);
$cols = $dtgrid['cols'] ? $dtgrid['cols'] : 1;
$posx = $dtgrid['posx'] ? floatval($dtgrid['posx']) : 0;
$fillEmpty = $dtgrid['fillempty'] ? $dtgrid['fillempty'] : false;
$sumItems = $rows * $cols;
# \writeDebugInfo("rows in grid = $rows * $cols = $sumItems");
for($kk=1; $kk <= $sumItems; $kk++) {
$poy_off = $kk-1;
$posy = isset($posyarr[$poy_off]) ? $posyarr[$poy_off] : ($posyarr[0]+($poy_off * $dtgrid['step_y']));
foreach($gridFldDefs as $fdef) {
$newdef = ($fdef);
$newdef['name'] = $fdef['name'].$kk;
$newdef['posy'] = $fdef['posy'] + $posy;
if($posx!=0) foreach($newdef['posx'] as $k=>$v) {
$newdef['posx'][$k] += $posx;
}
$newdef['_gridtmp_'] = 1; # not a real field, just a clone
if ($newdef['type']!='checkbox') $newdef['fillempty'] = $fillEmpty;
$this->father->_pagedefs[$ipage]['fields'][] = $newdef;
# if ($fillEmpty) \writeDebugInfo("grid fillempty $newdef[name]");
}
}
}
# $ipage++;
}
if (isset($xml->import)) {
foreach($xml->import->children() as $key => $item) {
if ($key === 'importdef') {
$addXml = (isset($item['src']) ? (string)$item['src'] : '');
if($operSubid) $subid = $operSubid;
else $subid = (isset($item['datasubid']) ? (string)$item['datasubid'] : '');
if (strlen($addXml)) {
$filePath = self::getRelativeFilename($this->_homedir, $addXml);
if ($filePath) $this->father->AppendPageDefFromXml($filePath, $subid);
}
}
}
}
if(!$fldcnt) {
$this->_errormessage = 'No valid workpage definitions found (no fields defined)!';
$ret = false;
}
# if (self::$debug) \writeDebugInfo("$cfgname / _basepar is: ", $this->_basepar);
return $ret;
}
# read "template" sub-element to assoc.array
protected function readTemplateDef($item) {
$ret = [
'src' => $this->_evalAttribute($item['src'])
,'altsrc'=> (isset($item['altsrc']) ? (string)$item['altsrc'] : '')
,'pagination'=> (isset($item['pagination']) ? (string)$item['pagination'] : FALSE)
,'pages'=> (isset($item['pages']) ? (string)$item['pages'] : '')
];
# if (!empty($ret['pagination'])) \WriteDebugInfo("template with pagination:", $ret);
return $ret;
}
/**
* Disables/enables all images import (debug needs)
* @since 1.3.0020
* @param mixed $par
*/
public function disableImages($par=true) {
$this->_images_disabled = $par;
}
public function setResourcePaths($pdfPath=null, $imgPath=null) {
if($pdfPath!==null) $this->_pdf_path = $pdfPath;
if($imgPath!==null) $this->_img_path = $imgPath;
}
/**
* Add page definition programmatically
* Programmer can add some PDF templates to output document without editing XML config-file
* @since 1.2
* @param mixed $opts
*/
public function addPageDef($opts=[]) {
$ipage = count($this->_pagedefs);
$pg = [];
$pg['orientation'] = (isset($opts['orientation']) ? $opts['orientation'] : $this->_basepar['page']['orientation']);
$pg['pageno'] = (isset($opts['pageno']) ? $opts['pageno'] : $ipage);
$pg['fields'] = (isset($opts['fields']) ? $opts['fields'] : []);
$pg['repeat'] = (isset($opts['repeat']) ? $opts['repeat'] : []);
$pg['hide'] = (isset($opts['hide']) ? $opts['hide'] : false);
$pg['template'] = array(
'src' => (isset($opts['src']) ? (string)$opts['src'] : '')
,'altsrc' => (isset($opts['altsrc']) ? (string)$opts['altsrc'] : '')
,'page' => (isset($opts['page']) ? max(1,(int)$opts['page']) : 1)
);
$this->_pagedefs[$ipage] = $pg;
}
/**
* Overrides template PDF file for specified page
*
* @param mixed $pageno, 0-based!
* @param mixed $srcPdf new PDF template file name
* @param mixed $srcPage page to use from PDF template
* @since: 1.5
*/
public function setPageTemplateFile($pageno, $srcPdf, $srcPage=1) {
if (isset($this->_pagedefs[$pageno])) {
if (!empty($srcPdf)) $this->_pagedefs[$pageno]['template']['src'] = $srcPdf;
if (!empty($srcPage)) $this->_pagedefs[$pageno]['template']['page'] = $srcPage;
return true;
}
else {
return false;
}
}
/**
* Get contents of base config, to be able programmatically saving XML def.
*
*/
public function GetBaseConfig() { return $this->_config; }
public function GetBaseParams() { return $this->_basepar; }
public function SetOffsets($offset_x=0, $offset_y=0) {
$this->offsets = is_array($offset_x) ? $offset_x : array(floatval($offset_x),floatval($offset_y));
}
/**
* Switches to using alternative" PDF template file(s) in all pages
*
* @param mixed $alt 1 or true to turn alternate template ON, false otherwise
*/
public function SetAltTemplate($alt=true) {
$this->_alttemplate = $alt;
}
/**
* "Manually" add one field definition
*
* @param int $ipage page index (offset)
* @param array $parm associative arrya with all definitions : 'name','col','row','type' etc.
*/
public function AddFieldDefinition($ipage, $parm) {
if(!isset($this->_pagedefs[$ipage]))
$this->_pagedefs[$ipage] = array('offset'=>count($this->_pagedefs), 'fields'=>[]);
if(empty($parm['name'])) return false; # array MUST contain 'name' element
$tp = (isset($parm['type']) ? strtolower((string) $parm['type']) : '');
$posy = (isset($parm['posy']) ? (string) $parm['posy'] : '1');
$fldDef = [
'name' => strtolower(trim($parm['name']))
,'type' => $tp
,'posx' => (isset($parm['posx']) ? explode(',', (string)$parm['posx']) : array(0))
,'posy' => ($tp=='poly'? floatval($pos) : explode(',',$posy))
,'charstep'=> (isset($parm['charstep']) ? (float)$parm['charstep'] : 0)
,'maxlength'=> (isset($parm['maxlength']) ? (int)$parm['maxlength'] : 0)
,'width' => (isset($parm['width']) ? floatval($parm['width']) : 0)
,'font' => (isset($parm['font']) ? (string)($parm['font']) : '')
,'fontstyle' => (isset($parm['fontstyle']) ? (string)($parm['fontstyle']) : '')
,'size' => (isset($parm['size']) ? floatval($parm['size']) : 0)
,'align' => (isset($parm['align']) ? (string) $parm['align'] : '')
,'valign' => (isset($parm['valign']) ? (string) $parm['valign'] : '')
,'convert' => (isset($parm['convert']) ? (string) $parm['convert'] : '')
,'color' => (isset($parm['color']) ? (string) $parm['color'] : '')
,'bgcolor' => (isset($parm['bgcolor']) ? (string) $parm['bgcolor'] : '')
,'rotate' => (isset($parm['rotate']) ? (float) $parm['rotate'] : 0)
,'norepeat'=> (isset($parm['norepeat']) ? (int) $parm['norepeat'] : 0)
,'border' => (isset($parm['border']) ? (string) $parm['border'] : 0)
,'options' => (isset($parm['options']) ? (string) $parm['options'] : '')
,'opacity' => (isset($parm['opacity']) ? (float) $parm['opacity'] : 1)
,'thickness' => (isset($parm['thickness']) ? (float) $parm['thickness'] : 0)
,'src' => (isset($parm['src']) ? (string) $parm['src'] : '')
];
if ($fldDef['fontstyle']!='') exit("$parm[name] has fontstyle");
$this->_pagedefs[$ipage]['fields'][] = $fldDef;
}
public function setTitle($strg) { $this->_config['title'] = $strg; }
public function setSubject($strg) { $this->_config['subject'] = $strg; }
public function setAuthor($strg) { $this->_config['author'] = $strg; }
public function setCreator($strg) { $this->_config['creator'] = $strg; }
public function protectFile($protect, $password=null, $options = false) {
$this->_config['protectfile'] = $protect;
if($password!==null) $this->_config['password'] = $password;
$this->_config['protectoptions'] = $options;
}
/**
* Pass whole data array for printing one "PDF entity"
*
* @param array $param associative array with all values. Don't call this more than once for one page/page set,
* because next calling will create new page/page set (creating multi-paged formed PDF)
*/
public function AddData($entitydata, $datacharset='') {
if(!is_array($entitydata)) return false;
if(!empty($datacharset) && !empty($this->_config['stringcharset']) && $datacharset != $this->_config['stringcharset']) {
#\writeDebugInfo("try to convert data from ",$this->_config['stringcharset'], ' to ', $datacharset);
mb_convert_variables($this->_config['stringcharset'], $datacharset, $entitydata);
}
$pageData = [];
foreach($entitydata as $key=>$value) {
if(substr($key,0,5) == 'grid:' && is_array($value)) {
$gridid = substr($key,5);
foreach($value as $gridrow) {
$pageData = array_merge($pageData,$this->AddDataGridRow($gridid,$gridrow,true));
}
}
else $pageData[$key] = $value;
}
$this->_data[] = $pageData;
}
/**
* Change printing coordinates, font size etc before generating PDF,
* for "tuning/testing" needs...
* @param mixed $params assoc.array containing new page/fields parameters as $param[0]['fields'] - new fields for page1, etc.
*/
public function ModifyParameters($param) {
if(!is_array($param)) return false;
$ipage = 0;
foreach($param as $key=>$fielddefs) {
# overwrite $this->_pagedefs[$ipage]['fields'] with values from array
if(!isset($this->_pagedefs[$ipage])) break;
if(!is_array($fielddefs['fields'])) break;
# $this->_pagedefs[$ipage]['fields'] = $fielddefs['fields'];
for($kk=0; $kk<count($this->_pagedefs[$ipage]['fields']);$kk++) { # each($item as $itkey=>$fldef)
if(!isset($fielddefs['fields'][$kk])) break; # passed array is shorter than our field list
if(isset($fielddefs['fields'][$kk]['posx'])) {
for($kj=0;$kj<count($fielddefs['fields'][$kk]['posx']); $kj++) $this->_pagedefs[$ipage]['fields'][$kk]['posx'][$kj] = floatval($fielddefs['fields'][$kk]['posx'][$kj]);
}
if(isset($fielddefs['fields'][$kk]['posy'])) $this->_pagedefs[$ipage]['fields'][$kk]['posy'] = floatval($fielddefs['fields'][$kk]['posy']);
if(isset($fielddefs['fields'][$kk]['size'])) $this->_pagedefs[$ipage]['fields'][$kk]['size'] = floatval($fielddefs['fields'][$kk]['size']);
if(isset($fielddefs['fields'][$kk]['charstep'])) $this->_pagedefs[$ipage]['fields'][$kk]['charstep'] = floatval($fielddefs['fields'][$kk]['charstep']);
if(isset($fielddefs['fields'][$kk]['width'])) $this->_pagedefs[$ipage]['fields'][$kk]['width'] = floatval($fielddefs['fields'][$kk]['width']);
if(isset($fielddefs['fields'][$kk]['height'])) $this->_pagedefs[$ipage]['fields'][$kk]['height'] = floatval($fielddefs['fields'][$kk]['height']);
# charstep, size, etc...
}
$ipage++;
}
}
/**
* Adding fields to be hidden (not printed) in final document
* @since 1.8
* @param mixed $fldnames comma(space) separated string or array containing field names to hide
*/
public function hideFields($fldnames) {
if (is_string($fldnames))
$fldnames = preg_split( "/\s,]/", $fldnames, -1, PREG_SPLIT_NO_EMPTY );
if (is_array($fldnames))
$this->_hide_fields = array_merge($this->_hide_fields,$fldnames);
return $this;
}
/**
* Change any attributes for one field
* @since 1.8
*
* @param mixed $fieldname field name
* @param mixed $attribs assoc array in form "key" => new_value.
* Supported keys: "src"
*/
public function setFieldAttribs($fieldname, $attribs) {
if (is_array($attribs)) {
if (!isset($this->_field_newattr[$fieldname])) $this->_field_newattr[$fieldname] = [];
$this->_field_newattr[$fieldname] = array_merge($this->_field_newattr[$fieldname],$attribs);
}
return $this;
}
# obsolete function name, use drawRuler() !
public function DrawMeasuringGrid($step = 10, $color = false) {
$this->drawRuler($step, $color);
}
/**
* Turns ON creating of measuring grid (will be drawn on the first page only)
*
* @param mixed $step
*/
public function drawRuler($step = 10, $color = false) {
$this->_rulerStep = ($step==1) ? 10 : $step;
if($color && is_array($color) && count($color)>=3) $this->_rulerColor = $color;
else $this->_rulerColor = array(20,20,200);
}
/**
* Draws "measuring" grid on current pdf page
* @since 1.12
*/
protected function _renderMeasuringGrid() {
if($this->_rulerStep > 0 ) { # let's draw a measuring grid
$pageWidth = $this->_pdf->getPageWidth();
$pageHeight = $this->_pdf->getPageHeight();
$result = $this->_pdf->SetDrawColor($this->_rulerColor[0],$this->_rulerColor[1],$this->_rulerColor[2]);
$this->_pdf->SetTextColor($this->_rulerColor[0],$this->_rulerColor[1],$this->_rulerColor[2]);
$this->_pdf->SetFontSize(6.0);
# $this->_pdf->SetLineWidth(0.2);
for($posx=0; $posx<$pageWidth; $posx+=$this->_rulerStep) {
$this->_pdf->Line($posx,0,$posx, $pageHeight, array('dash'=>'1,3'));
if($posx>0) {
$this->_pdf->Text($posx+0.05, 1.0, "$posx");
$this->_pdf->Text($posx+0.05, $pageHeight-5, "$posx");
}
}
for($posy=0; $posy<$pageHeight; $posy+=$this->_rulerStep) {
$this->_pdf->Line(0,$posy,$pageWidth, $posy);
if($posy>0) {
$this->_pdf->Text(1.0, $posy+0.1, "$posy");
$this->_pdf->Text($pageWidth-5, $posy+0.1, "$posy");
}
}
$this->_pdf->setFontSize($this->_basepar['font']['size']);
}
}
/**
* Adds field definition (and possible value) to ALL PAGES (AP) in document.
* For drawing "DRAFT" sign, bar-code, logo image and so on, on every PDF page
* @param mixed $fdef full field definition array: name,type, posx,posy,rotate,color,...
* @param mixed $value optional initial value for the field
* @since 1.4
*/
public function addAllPagesField($fdef, $value=null) {
$new_fld = $this->prepareFieldDef($fdef);
$name = $new_fld['name'];
$this->_apFields[$name] = $new_fld;
if($value) $this->_apValues[$name] = $value;
}
/**
* Sets value for one "AP" field
* @param mixed $fldname field name
* @param mixed $value new value
* @since 1.4
*/
public function setAllPagesFieldValue($fldname, $value) {
$this->_apValues[$fldname] = $value;
}
/**
* Reads user (possibly not full) field definition and return a full definition for working
*
* @param mixed $item
*/
public function prepareFieldDef($item) {
$fldname = isset($item['name'])? trim("{$item['name']}") : ('_field_'. (++$this->father->_tmpFldNo));
$ret = array(
'name' => strtolower($fldname)
,'type' => (isset($item['type']) ? strtolower((string)$item['type']) : 'text')
,'posx' => (isset($item['posx']) ? explode(',', (string) $item['posx']) : array(0))
,'posy' => (isset($item['posy']) ? (float) $item['posy'] : 0)
,'charstep'=> (isset($item['charstep']) ? (float)$item['charstep'] : 0)
,'maxlength'=> (isset($item['maxlength']) ? (int)$item['maxlength'] : 0)
,'width' => (isset($item['width']) ? (float) $item['width'] : 0)
,'height' => (isset($item['height']) ? (float) $item['height'] : 0)
,'font' => (isset($item['font']) ? (string) $item['font'] :'')
,'fontstyle' => (isset($item['fontstyle']) ? (string) $item['fontstyle'] :'')
,'size' => (isset($item['size']) ? (float) $item['size'] : 0)
,'convert' => (isset($item['convert']) ? (string) $item['convert'] : '')
,'if' => (isset($item['if']) ? (string) $item['if'] : NULL)
,'color' => (isset($item['color']) ? (string) $item['color'] : '')
,'bgcolor' => (isset($item['bgcolor']) ? (string) $item['bgcolor'] : '')
,'rotate' => (isset($item['rotate']) ? (float) $item['rotate'] : 0)
,'norepeat'=> (isset($item['norepeat']) ? (int) $item['norepeat'] : 0)
,'align' => (isset($item['align']) ? (string) $item['align'] : '')
,'valign' => (isset($item['valign']) ? (string) $item['valign'] : '')
,'options' => (isset($item['options']) ? (string) $item['options'] : '')
,'opacity' => (isset($item['opacity']) ? (float) $item['opacity'] : 1)
,'thickness' => (isset($item['thickness']) ? (float) $item['thickness'] : 0)
,'src' => (isset($item['src'])? (string)$item['src'] : '')
,'fillempty' => (isset($item['fillempty'])? (int)$item['fillempty'] : FALSE)
);
if($ret['type'] === 'rectangle' || $ret['type'] === 'rect') { # draw a rectangle, 'width' & 'height' sets its width/height
$ret['type'] = 'rect';
}
elseif($ret['type'] === 'poly' || $ret['type'] === 'polygone') { # draw a polygone, and posy must contain at least 2 values: x0,x1, y0,y1
$ret['type'] = 'poly';
$ret['posy'] = (isset($item['posy']) ? explode(',', (string) $item['posy']) : array(0));
}
return $ret;
}
# make array of field definitions for datagrid
public function getFieldDefs($arParam, $ipage) {
if(is_array($arParam[0])) return $arParam; # array of field definitions (in datagrid)
$ret = [];
foreach($this->_pagedefs[$ipage]['fields'] as $fno => $ftdef) {
if(in_array($ftdef['name'],$arParam)) {
$ret[] = $ftdef;
}
}
return $ret;
}
/**
* Activates "only selected fields" printing mode
*
* @param mixed $filter assoc.array with pages/fields to print
*/
public function PrintFieldsFilter($filter) {
$this->_printedfields = $filter;
}
/**
* Renders PDF document [and sends to the browser or saves on the disc]
*
* @params $output if true (default) PDF contents will be sent to client (or saved to file),
* otherwise You can do something after rendering and before sendind final document
* @return true if pdf file generated, false if some errors
*/
public function Render($output=true, $debug=false) {
if ($debug) self::$debug = $debug;
if(count($this->_pagedefs)<1 && $this->_specialPages==0) {
$this->_errormessage = 'Configuration not loaded, Rendering impossible !';
return false;
}
@ini_set('max_execution_time', 600);
if (is_callable('WebApp::getAppState')) {
if (100 <= WebApp::getAppState()) {
while(ob_get_level()) {
ob_end_flush();
}
return;
}
}
# writeDebugInfo("_outname: ", $this->_outname);
if(empty($this->_outname)) {
$this->_outname = 'generated.pdf';
$off = max(strrpos($this->_outname, '/'), strrpos($this->_outname, '\\'));
if($off!==false) $this->_outname = substr($this->_outname, $off+1);
}
if(!($this->_createPdfObject())) {
if (self::$debug) \writeDebugInfo("PDF object not created");
return false;
}
$this->_pdf->SetAutoPageBreak(false,0); # disable auto creating new pages if data doesn't fit on page
$creator = empty($this->_config['creator']) ? 'Printform-pdf module by Alexander Selifonov, using TCPDF/FPDF classes' : $this->_convertCset($this->_config['creator']);
$this->_pdf->SetCreator($creator);
$author = empty($this->_config['author']) ? 'CPrintFormPdf, TCPDF wrapper PHP class' : $this->_convertCset($this->_config['author']);
$this->_pdf->SetAuthor($author);
if(!empty($this->_config['title'])) $this->_pdf->SetTitle($this->_convertCset($this->_config['title']));
if(!empty($this->_config['subject'])) $this->_pdf->Setsubject($this->_convertCset($this->_config['subject']));
if($this->_rulerStep > 0 ) { # we'll draw a measuring grid
$grcolor = array('R'=>200,'G'=>200,'B'=>200);
if($this->_rulerColor) {
$grcolor = $this->_parseColor($this->_rulerColor);
# \WriteDebugInfo('grid color from ', $this->_rulerColor, ' is ', $grcolor);
}
}
# Populating with data...
foreach($this->_data as $entno=>$onedatablock) { #<3>
$this->_dataBlock = $onedatablock;
$this->pgno = $this->prnPage = 0;
$this->_curSrcFile = $this->_curSrcPage = $this->_curSrcPgcount = -1;
$this->dataentity = $onedatablock;
$this->pageSubData = [];
# Main loop for generating one multi-paged document entity
foreach($this->_pagedefs as $no=>$pagedef) { #<4>
# skip the page if no printed fields in TEST mode:
# $this->pageSubData = []; # particular data for this page addresserd by subdataid
# writeDebugInfo("printing [$no] by pagedef: ", $pagedef);
if(!empty($pagedef['hide'])) {
continue; # page is temporary hidden by attrib "hide"
}
if (!empty($pagedef['basepar'])) {
$this->_tmpBasepar = $pagedef['basepar'];
}
else
$this->_tmpBasepar = $this->_basepar;
if (!empty($this->_tmpBasepar['font']['color'])) {
$this->defaultTextColor = $this->colorToDec($this->_tmpBasepar['font']['color']);
}
else $this->defaultTextColor = 0;
if(is_array($this->_printedfields) && (!isset($this->_printedfields[$no+1]) OR count($this->_printedfields[$no+1])<=0)) {
continue;
}
if (!empty($pagedef['gridpage'])) {
$this->_drawGridPage($entno, $pagedef);
continue;
# $dataid = $pagedef['gridpage']['datasource'];
}
$orientation = isset($pagedef['orientation'])? $pagedef['orientation'] : $this->_tmpBasepar['page']['orientation'];
$this->_pdf->addPage($orientation);
$this->pgno++;
$this->prnPage++;
if (!empty($pagedef['pageevent'])) {
if(is_callable($pagedef['pageevent'])) {
@call_user_func($pagedef['pageevent'], $this->prnPage); # run callback page-event hook
}
elseif(is_object($this->callbackObj) && method_exists($this->callbackObj, $pagedef['pageevent'])) {
$callFnc = $pagedef['pageevent'];
$this->callbackObj->$callFnc();
}
}
$pgPref = $this->_tmpBasepar['pgprefix'] . $this->pgno;
if (isset($onedatablock[$pgPref]) && is_array($onedatablock[$pgPref])) {
# there is specific [_pageNN] data sub-array for this page
$this->dataentity = array_merge($this->dataentity, $onedatablock[$pgPref]);
}
if (!empty($pagedef['datasubid'])) {
$pgPref = $pagedef['datasubid'];
if (isset($onedatablock[$pgPref]) && is_array($onedatablock[$pgPref])) {
# there is specific [subid] data sub-array for this page
$this->pageSubData = $onedatablock[$pgPref];
# $this->dataentity = array_merge($this->dataentity, $onedatablock[$pgPref]); # commented!
}
}
$pdfTpl = $pdfPage = false;
# use explicit PDF file if set inside "<page">, otherwise - load page from "basic PDF template listed in XML "templatefiles" section
if(!empty($pagedef['template']['page'])) {
$home = $pagedef['homepath'];
if(!empty($pagedef['template']['src'])) {
$pdfTpl = (string)$pagedef['template']['src'];
$pdfPage = isset($pagedef['template']['page']) ? ($pagedef['template']['page']) : 1;
}
if ( !empty($pdfTpl) && !empty($pdfPage) ) try {
if (is_file($pdfTpl)) $pdfTpl = $pdfTpl; # dummy op.
elseif(is_file($this->_pdf_path . $pdfTpl))
$pdfTpl = $this->_pdf_path . $pdfTpl;
elseif(is_file($home . $pdfTpl))
$pdfTpl = $home . $pdfTpl;
elseif(is_file($this->_homedir . $pdfTpl))
$pdfTpl = $this->_homedir . $pdfTpl;
elseif(!empty($pagedef['sourcepath']) && is_file($pagedef['sourcepath'] . $pdfTpl))
$pdfTpl = $pagedef['sourcepath'] . $pdfTpl;
if (is_file($pdfTpl)) {
$pc = $this->_pdf->setSourceFile($pdfTpl);
$pg = $this->_pdf->importPage($pdfPage);
$this->_pdf->useTemplate($pg);
}
else {
# writeDebugInfo("ERR-01: PDF not found ", $pdfTpl, ' _pdf_path: ',$this->_pdf_path);
# writeDebugInfo("pagedef: ", $pagedef);
# writeDebugInfo("this->_configfile: ", $this->_configfile);
die ("Wrong config: template file $pdfTpl not found ! ");
}
# if (self::$debug) \writeDebugInfo("pdf template :", $pdfTpl);
}
catch (Exception $e) {
$this->_errormessage = 'Loading template PDF error, cause : '.$e->getMessage();
if (self::$debug && function_exists('\WriteDebugInfo')) \WriteDebugInfo($this->_errormessage," pagedef $no:",$this->pagedef);
}
}
else $this->getNextTemplatePage();
$pageWidth = $this->_pdf->getPageWidth();
$pageHeight = $this->_pdf->getPageHeight();
# if($this->pgno==1) { #operations to do on the first page only
if($this->_rulerStep > 0 ) { # draw a measuring grid
$this->_renderMeasuringGrid();
}
elseif (!empty($pagedef['ruler'])) {
# personal ruler grid for this page
$saveStep = $this->_rulerStep;
$this->_rulerStep = ($pagedef['ruler'] <=2) ? 10 : $pagedef['ruler'];
$this->_renderMeasuringGrid();
$this->_rulerStep = $saveStep;
}
# Common fields existing on ALL pages
if(!$this->_apFinally) {
if(count($this->_apFields)) {
$this->_renderFieldSet($this->_apFields, array_merge($this->_apValues, $this->dataentity), $debug);
}
# "ALL PAGES" fields from nested loaded XML, if current page is "nested":
if (!empty($pagedef['pageid'])) {
$pgid = $pagedef['pageid'];
if (isset($this->_subApFields[$pgid]) && count($this->_subApFields[$pgid])>0)
$this->_renderFieldSet($this->_subApFields[$pgid], array_merge($this->_subApValues[$pgid], $this->dataentity), $debug);
}
}
if (self::$debug && !isset($pagedef['fields'])) \WriteDebugInfo('bad pagedef(no fields):', $pagedef);
$this->_renderFieldSet($pagedef['fields'], $this->dataentity, $debug, $pagedef);
if (isset($pagedef['datagrids']) && is_array($pagedef['datagrids']) && count($pagedef['datagrids'])>0)
foreach ($pagedef['datagrids'] as $gridid) {
# only grids with assigned datasource array
$sourceid = trim($this->_datagrids[$gridid]['datasource']);
$isempty = empty($sourceid);
if (!empty($sourceid) && (!array_key_exists($sourceid, $this->dataentity))) {
writeDebugInfo("skip grid/No data in page values: sourceid: $sourceid, ");
continue; // no named data sub-array for datagrid, skip it
}
# if($sourceid) writeDebugInfo("grid sub-array[$sourceid]: ", $this->dataentity[$sourceid]);
# writeDebugInfo("datasource data for grid: ", $this->_data[$no][$sourceid]);
$step_y = $this->_datagrids[$gridid]['step_y'];
$step_x = $this->_datagrids[$gridid]['step_x'];
$max_x = $this->_datagrids[$gridid]['cols'];
$max_y = $this->_datagrids[$gridid]['rows'];
$order = $this->_datagrids[$gridid]['order'];
$fillEmpty = $this->_datagrids[$gridid]['fillempty'];
$gridFields = $this->_datagrids[$gridid]['fields'];
# writeDebugInfo("gridFields: ", $gridFields);
# WriteDebugInfo("step_y=$step_y step_x=$step_x max_y=$max_y max_x=$max_x order=[$order], fillEmpty = $fillEmpty!");
$page_xoff = $page_yoff = 0;
# $endRow = ($fillEmpty) ? $max_y: (count($this->dataentity[$sourceid]);
$endRow = $max_y;
for ($krow = 0; $krow < $endRow; $krow++) {
# if (self::$debug) writeDebugInfo("krow: $krow, data exist: [".isset($this->_data[$no][$sourceid][$krow]).']');
$rowExist = FALSE;
if (1) {
foreach ( $gridFields as $fldItem ) {
$fldid = is_array($fldItem) ? $fldItem['name'] : $fldItem;
$nRow = $krow+1;
if(!empty($sourceid))
$fldvalue = (isset($this->dataentity[$sourceid][$krow][$fldid])) ? $this->dataentity[$sourceid][$krow][$fldid] : NULL;
else
$fldvalue = (isset($this->dataentity[$fldid.$nRow])) ? $this->dataentity[$fldid.$nRow] : NULL;
# if (!in_array($fldid, $gridFields)) continue;
if($fldvalue !== NULL) $rowExist = TRUE;
if ($fldid === '_rowno_' && !empty($sourceid) && isset($this->dataentity[$sourceid][$krow]))
$fldvalue = $nRow; # line no. TODO: set for field_1 case
$myFdef = FALSE;
if(is_array($fldItem)) $myFdef = $fldItem;
else # seek field definition
foreach($pagedef['fields'] as $no => $fdef) {
if ($fdef['name'] !== $fldid) {
# writeDebugInfo("$fdef[name] - no what we seek");
continue;
}
$myFdef = $fdef;
break;
# print shifted value and break;
}
if($myFdef) { # filed definition found
$myFdef['posy'] += ($step_y * $page_yoff) + $this->_datagrids[$gridid]['posy'];
if($fillEmpty) $myFdef['fillempty'] = $fillEmpty;
foreach($myFdef['posx'] as &$oposx) {
$oposx += $this->_datagrids[$gridid]['posx'] + $page_xoff * $step_x;
}
# if(empty($fldvalue) && $myFdef['type'] === 'cross') continue;
# writeDebugInfo("empty $myFdef[name]: type: $myFdef[type]");
$this->_valueToPdf($fldvalue, $myFdef);
$rowExist = 1;
}
}
}
if(!$rowExist) { # no more data, should we fill empty rows?
if (self::$debug) writeDebugInfo("TODO: $gridid / will empty row data $krow");
if($fillEmpty) foreach ( $gridFields as $fldid) {
if(is_array($fldid)) $fdef = $fldid;
else $fdef = $this->findFieldDef($pagedef['fields'], $fldid);
if(!$fdef) {
if($krow <=1) writeDebugInfo("grid field $fldid not found in page field list ", $pagedef['fields']);
continue;
}
if ($fldid === '_rowno_')
$fldvalue = '--'; # line no.
else $fldvalue = '---------';
if(!empty($fdef['width'])) $fldvalue = str_repeat('-',ceil($fdef['width']*0.7));
# writeDebugInfo("$fdef[width] / $fldid: $fldvalue");
# seek field definition
# foreach($pagedef['fields'] as $no => $fdef) {
# print shifted value and break;
$fdef['posy'] += ($step_y * $page_yoff) + $this->_datagrids[$gridid]['posy'];
foreach($fdef['posx'] as &$oposx) {
$oposx += $this->_datagrids[$gridid]['posx'] + $page_xoff * $step_x;
}
$this->_valueToPdf($fldvalue, $fdef);
# }
}
}
# if ($krow < $max_y) {
if (self::$debug) writeDebugInfo("$krow/$sourceid: less than max rows in grid row greater than fact rows in grid $max_y");
# }
if ($order == 'R') { # row fill first
if (++$page_yoff >=$max_y) {
$page_yoff = 0;
if (++$page_xoff >= $max_x) break;
}
}
else { #'C' - column fills first
if (++$page_xoff >=$max_x) {
$page_xoff = 0;
if (++$page_yoff >= $max_y) break;
}
}
}
}
# Common fields existing on ALL pages - if order is "Finally" (after all printed fields)
if($this->_apFinally) {
if(count($this->_apFields)) {
$this->_renderFieldSet($this->_apFields, array_merge($this->_apValues, $this->dataentity), $debug);
}
# "ALL PAGES" fields from nested loaded XML, if current page is "nested":
if (!empty($pagedef['pageid'])) {
$pgid = $pagedef['pageid'];
if (isset($this->_subApFields[$pgid]) && count($this->_subApFields[$pgid])>0)
$this->_renderFieldSet($this->_subApFields[$pgid], array_merge($this->_subApValues[$pgid], $this->dataentity), $debug);
}
}
if ($this->prnPage > 1) $this->_drawPageNo();
} #<4>
} #<3>
$this->_pdf->SetCompression($this->_compression); # will work if 'gzcompress' function enabled ! (see TCPDF docs)
if(!empty($this->_config['protectfile'])) { # file protecting
$pwd = isset($this->_config['password']) ? (string)$this->_config['password'] : '00000';
$this->_pdf->SetProtection(array(),$pwd); # ATTENTION: protecting (encrypting) can take ve-e-ery long, could cause PHP timeout
}
# send to callback final page count in generated PDF
if (!empty($this->hooks['hook_end'])) {
if(is_callable($this->hooks['hook_end'])) {
$callResult = call_user_func($this->hooks['hook_end'], [ 'lastPage'=> $this->prnPage ]);
}
elseif(is_object($this->callbackObj) && method_exists($this->callbackObj, $this->hooks['hook_end'])) {
$callFnc = $this->hooks['hook_end'];
$callResult = $this->callbackObj->$callFnc();
}
}
$ret = true;
if($output) {
$ret = $this->Output($this->_outname);
}
return $ret;
}
/**
* Loads next page from current "global" pdf template
* @since 1.5
*/
protected function getNextTemplatePage() {
if (count($this->_templatefile)<1) {
return;
}
if ($this->_curSrcFile >= count($this->_templatefile)) {
# WriteDebugInfo('page '.$this->_curSrcFile . ': getNextTemplatePage: no more pdf templates');
return; # no more PDF templates
}
if ($this->_curSrcFile <0) {
# WriteDebugInfo('Start using PDF templates...');
while($this->_curSrcFile < count( $this->_templatefile)) {
$this->_curSrcFile++;
if (!isset($this->_templatefile[$this->_curSrcFile])) return false;
if (is_string($this->_templatefile[$this->_curSrcFile]['src'])) {
$justName = $this->_evalAttribute($this->_templatefile[$this->_curSrcFile]['src'], $this->_data);
if (is_file($justName)) $thisPdf = $justName;
else $thisPdf = $this->_pdf_path . $justName;
if (!empty($this->_templatefile[$this->_curSrcFile]['pagination'])) {
$this->setPaginationMode($this->_templatefile[$this->_curSrcFile]['pagination']);
# WriteDebugInfo("001.pagination set to ",$this->_paginatonMode);
}
else $this->setPaginationMode(FALSE);
}
else { # if(is_array($this->_templatefile[$this->_curSrcFile])) # templates from imported definitions
$thisPdf = $this->_templatefile[$this->_curSrcFile]['src'][0] . $this->_evalAttribute($this->_templatefile[$this->_curSrcFile]['src'][1]); # path+fname
$this->_paginatonMode = FALSE;
}
# $thisPdf = $this->_evalAttribute($thisPdf);
if (is_file($thisPdf)) break; # file exists
}
if (is_file($thisPdf)) {
$this->_curSrcPdf = $thisPdf;
$this->_curSrcPgcount = $this->_pdf->setSourceFile($thisPdf);
$this->_curSrcPage = 0;
}
# else WriteDebugInfo('след.шаблон не найден:' . $this->_pdf_path . $this->_templatefile[$this->_curSrcFile]);
}
++ $this->_curSrcPage;
if ( $this->_curSrcPage <= $this->_curSrcPgcount) {
# $pc = $this->_pdf->setSourceFile($pdfTpl);
$pg = $this->_pdf->importPage($this->_curSrcPage);
$this->_pdf->useTemplate($pg);
return true;
}
# end of current PDF file, find next...
$thisPdf = FALSE;
while(++$this->_curSrcFile < count( $this->_templatefile)) {
if (is_string($this->_templatefile[$this->_curSrcFile]['src'])) {
$thisPdf = $this->_pdf_path . $this->_evalAttribute($this->_templatefile[$this->_curSrcFile]['src']);
if (!empty($this->_templatefile[$this->_curSrcFile]['pagination'])) {
$this->setPaginationMode($this->_templatefile[$this->_curSrcFile]['pagination']);
}
else $this->setPaginationMode(FALSE);
}
else # if (is_array($this->_templatefile[$this->_curSrcFile])) # templates from imported definitions
$thisPdf = $this->_templatefile[$this->_curSrcFile]['src'][0] . $this->_evalAttribute($this->_templatefile[$this->_curSrcFile]['src'][1]); # path+fname
if (is_file($thisPdf)) break; # file exists
# else WriteDebugInfo("pdf template {$this->_curSrcFile} not found and skipped: ", $this->_templatefile[$this->_curSrcFile]);
}
if (!$thisPdf) return false;
$this->_curSrcPdf = $thisPdf;
$this->_curSrcPgcount = $this->_pdf->setSourceFile($this->_curSrcPdf);
$this->_curSrcPage = 1;
if ($this->_curSrcPage <= $this->_curSrcPgcount ) {
$pg = $this->_pdf->importPage($this->_curSrcPage);
$this->_pdf->useTemplate($pg);
return true;
}
}
protected function _drawPageNo() {
# TODO: define _basepar or _tmpBasepar use
if (!empty($this->_basepar['pagination']) && empty($this->_paginatonMode)) {
$pageWidth = $this->_pdf->getPageWidth();
$pageHeight = $this->_pdf->getPageHeight();
$margins = $this->_pdf->getMargins();
$width = $pageWidth - 20;
$posx = 10;
$height = 10;
$align = $this->_basepar['pagination']['align'];
if ($align==='EDGE' || $align ==='E') # align page no to "outer" edge of page
$align = ( $this->prnPage % 2) ? 'R' : 'L';
$value = str_replace('%page%', $this->prnPage, $this->_basepar['pagination']['format']);
$posy = ($this->_basepar['pagination']['posy'] === 'top') ?
max(8,$margins['top']) : min( ($pageHeight-self::BOTTOM_H), ($pageHeight-$margins['bottom']));
$this->_pdf->SetFontSize(self::PAGINATION_FONTSIZE);
$this->_pdf->MultiCell($width,$height,$value,0 , $align, 0, 1, $posx, $posy );
# $this->_pdf->SetFontSize($this->_basepar['font']['size']);
}
}
protected function setPaginationMode($mode) {
$this->_paginatonMode = $mode;
# WriteDebugInfo("Page $this->prnPage, paginationMode set to [$mode]" );
if ($mode === 'reset') $this->prnPage = 0; # TODO: 0 or 1?
}
/**
* Printing one or more (or none, if no data) data-grid filled pages
*
* @param mixed $entno adata array offset for current document
* @param mixed $pagedef page definition
*/
protected function _drawGridPage($entno, $pagedef) {
# writeDebugInfo("_drawGridPage : ",$pagedef);
$debug = false;
$listid = $pagedef['gridpage']['datasource'];
$fillEmpty = $pagedef['gridpage']['fillempty'];
# $fillEmpty = $this->_datagrids[
$fieldlist = $pagedef['gridpage']['fields'];
$gridFldDefs = $stdFldDefs = [];
foreach($pagedef['fields'] as $fldno => $fldef) {
if (in_array($fldef['name'], $pagedef['gridpage']['fields']) || $fldef['name'] === '_rowno_')
$gridFldDefs[$fldef['name']] = $fldef;
else
$stdFldDefs[$fldef['name']] = $fldef;
}
# if ($debug) WriteDebugInfo('defs for grid fields:', $gridFldDefs);
# if ($debug) WriteDebugInfo('defs for std fields:', $stdFldDefs);
if (empty($this->_data[$entno][$listid])) return false; // No data for grid pages!
# if ($debug) WriteDebugInfo('to continue: $this->_data : ', $this->_data[$entno]);
$orientation = isset($pagedef['orientation'])? $pagedef['orientation'] : '';
$pdfTpl = $pdfPage = false;
# use explicit PDF file if set inside "<page">, otherwise - load page from "basic PDF template listed in XML "templatefiles" section
$startTplPage = 1;
if(!empty($pagedef['template'])) {
if(!empty($pagedef['template']['src'])) {
$pdfTpl = (string)$pagedef['template']['src'];
$pdfPage = isset($pagedef['template']['page']) ? ($pagedef['template']['page']) : 1;
$startTplPage = $pdfPage;
}
if ( !empty($pdfTpl) && !empty($pdfPage) ) try {
if (is_file($pdfTpl)) $fileFound = 1;
elseif(is_file($this->_pdf_path . $pdfTpl)) $pdfTpl = $this->_pdf_path . $pdfTpl;
elseif(is_file($pagedef['homepath'] . $pdfTpl)) $pdfTpl = $pagedef['homepath'] . $pdfTpl;
elseif(!empty($pagedef['sourcepath']) && is_file($pagedef['sourcepath'] . $pdfTpl))
$pdfTpl = $pagedef['sourcepath'] . $pdfTpl;
if (!is_file($pdfTpl)) {
# writeDebugInfo("ERR-02: PDF nnot found ", $pdfTpl, ' _pdf_path: ',$this->_pdf_path);
die ("Wrong config: template file $pdfTpl not found !");
}
if (self::$debug) writeDebugInfo("pdf path: ",$pdfTpl);
}
catch (Exception $e) {
$this->_errormessage = 'Loading template PDF error, cause : '.$e->getMessage();
if (self::$debug) WriteDebugInfo($this->_errormessage," pagedef $no:",$this->pagedef);
}
}
# output multi-paged grid data
$itemoff = 0;
$_rowno_ = 1;
$printedrow = 0;
$base_y = $cur_y = $pagedef['gridpage']['posy'];
$base_x = $pagedef['gridpage']['posx'];
$step_y = $pagedef['gridpage']['step_y'];
$step_x = $pagedef['gridpage']['step_x'];
$max_x = $pagedef['gridpage']['cols'];
$max_y = $pagedef['gridpage']['rows'];
$order = $pagedef['gridpage']['order'];
$gdata = $this->_data[$entno][$listid];
# writeDebugInfo("data to grid($max_y/$listid): ", $gdata);
$lastItem = $max_x * $max_y;
$stopper = 0;
while (++$stopper <=20) { # was: count($gdata)>0
$startTplPage = $pdfPage;
$this->_addGridPage($orientation,$pdfTpl,$startTplPage, $stdFldDefs, $entno, $debug);
# populate with grid rows
$page_xoff = $page_yoff = 0;
for($krow = 0; $page_xoff<$max_x && $page_yoff < $max_y; $krow++) {
if ($step_x <= 0 && $step_y <= 0) break; # avoid endless loop
$rowdata = (count($gdata) ? array_shift($gdata) : []);
# foreach ($gridFldDefs as $flid => $fldef) { # draw one "row" of grid data
foreach($pagedef['gridpage']['fields'] as $flid) {
if (strtoupper($flid) === PRINTPDFCONST::PF_ADDPAGE) {
# pseudo field: add page before continue printing (one data row spreads to 2 or more pages)
$startTplPage++;
$this->_addGridPage($orientation,$pdfTpl,$startTplPage, $stdFldDefs, $entno, $debug);
continue;
}
if(!isset($gridFldDefs[$flid])) {
# writeDebugInfo("grid field $flid not in gridFldDefs, list is ", implode(',', array_keys($gridFldDefs)));
continue;
}
$fldef = $gridFldDefs[$flid];
foreach($fldef['posx'] as &$oposx) {
$oposx += $base_x + $page_xoff * $step_x;
}
$fldef['posy'] += $base_y + $page_yoff*$step_y;
$value = (isset($rowdata[$flid]) ? $rowdata[$flid] : '');
if ($flid === '_rowno_' && $value ==='' && count($rowdata)) $value = $_rowno_;
if ($fldef['type'] === 'plugin') {
$this->renderPlugin($fldef, $rowdata, ($page_xoff * $step_x), ($page_yoff*$step_y));
}
else {
if($fillEmpty) $fldef['fillempty'] = 1;
# if($value !=='' || $value!=0)
$this->_valueToPdf($value, $fldef);
# elseif($fldef['name'] == 'fullname') writeDebugInfo("KT-002B $fldef[name] empty grid field :" , $fldef);
/*
elseif($fillEmpty && count($rowdata)==0) {
if(!empty($fldef['height'])) $this->strokeEmptyValue($fldef);
else {
$value = ( empty($fldef['width']) ? '---------' : str_repeat('-',ceil($fldef['width']*0.7)) );
$this->_valueToPdf($value, $fldef);
}
}
*/
}
}
$_rowno_++;
if ($order == 'R') { # row fill first
if (++$page_yoff >=$max_y) {
$page_yoff = 0;
if (++$page_xoff >= $max_x) {
if (count($gdata)<1) break 2;
break;
}
}
}
else { #'C' - column fills first
if (++$page_xoff >=$max_x) {
$page_xoff = 0;
if (++$page_yoff >= $max_y) {
if (count($gdata)<1) break 2;
break 2;
}
}
}
if (count($gdata)<1 && !$fillEmpty) {
if (self::$debug) \writeDebugInfo("3 $page_xoff/$page_yoff: breaking gridpawe, data count:", count($gdata));
break 2;
}
}
}
}
# add (another one) page before drawing "grid/datasourced" page
private function _addGridPage($orientation,$pdfTpl,$pdfPage, $stdFldDefs, $entno,$debug) {
$this->_pdf->addPage($orientation);
if ($this->_paginatonMode!=='stop') $this->prnPage++;
if ($pdfTpl) { # apply PDF template
$pc = $this->_pdf->setSourceFile($pdfTpl);
$pg = $this->_pdf->importPage($pdfPage);
$this->_pdf->useTemplate($pg);
}
if ($this->_rulerStep > 0) $this->_renderMeasuringGrid();
if(count($this->_apFields)) { # strings printed on all pages
$this->_renderFieldSet($this->_apFields, array_merge($this->_apValues, $this->_data[$entno]), $debug); # first print "ALL PAGES" fields
}
if(count($stdFldDefs)) { # "standard" fields (no grid data)
$this->_renderFieldSet($stdFldDefs, array_merge($this->_apValues, $this->_data[$entno]), $debug); # first print "ALL PAGES" fields
}
$this->_drawPageNo();
}
/**
* Stroke non-filled array by Horizonytal line, to avoid "hand-made" adding text
*
* @param mixed $fldef field definition
* @param mixed $step_y verticval step in current print units
*/
public function strokeEmptyValue($fldef, $thickness = 1) {
if ($fldef['type'] == 'checkbox') return;
$x1 = $fldef['posx'][0] + 1;
$fntWeight = empty($fldef['size']) ? 1.7 : $fldef['size']*0.2;
$ypos = $fldef['posy'] + $fntWeight;
$x2 = $x1 + (empty($fldef['width']) ? 10 : max(floatval($fldef['width'])-2, 8));
// $this->_pdf->SetDrawColor($this->defaultTextColor);
$color = !empty($fldef['color']) ? $fldef['color'] : $this->defaultTextColor;
$dcolor = $this->colorToDec($color);
if ($fldef['fillempty']!== 1) $thickness = max(0.1, floatval($fldef['fillempty']));
$style = ['width' => floatval($thickness/2), 'cap' => 'butt', 'join' => 'miter',
'dash' => 0, 'color' => $dcolor
];
$this->_pdf->SetLineStyle($style);
$polyArr = [$x1, $ypos, $x2, $ypos];
if (!empty($fldef['height']) && $fldef['height'] >= self::$MIN_ZIGZAG_WEIGHT) {
# make "zigzag" over printing field area
$polyArr[] = $x1;
$polyArr[] = ($ypos + $fldef['height'] - 2.4);
$polyArr[] = $x2;
$polyArr[] = ($ypos + $fldef['height'] - 2.4);
}
$this->_pdf->Polygon($polyArr,'', [], [], FALSE);
}
/**
* Adds block of data for "datagrid" block
*
* @param string $gridId - existing datagrid definitions's ID (name)
* @param mixed $data assoc.array containing all data values for one grid row
* @param $returnval if true, function generates assoc/array and returns it, otherwise, adds these values to the current _data[] block.
* TODO: implement "multipage" grid feature: when added row exceeds grid capacity, add new page and start filling from the top.
*/
public function AddDataGridRow($gridId, $data, $returnval=false) {
if(!is_array($data)) return [];
if(!isset($this->_datagrids[$gridId])) return [];
if(!isset($this->_curGridRow[$gridId])) $this->_curGridRow[$gridId] = 0;
$this->_curGridRow[$gridId] +=1;
if($this->_curGridRow[$gridId] > $this->_datagrids[$gridId]['rows']) {
if(empty($this->_datagrids[$gridId]['multipage'])) return false; # no more rows allowed!
# TODO: add new page, re-print "header" fields and begin new grid
# foreach(
}
$griddata = [];
foreach($this->_datagrids[$gridId]['fields'] as $fldid) {
# writeDebugInfo("grid $gridId field: ", $fldid);
$thisfield = $fldid . $this->_curGridRow[$gridId];
if(isset($data[$fldid])) $griddata[$thisfield] = $data[$fldid];
}
if($returnval) return $griddata;
$pageNo = count($this->_data)-1; # AddData() should be called first !
if($pageNo<0) return false;
$this->_data[$pageNo] = array_merge($this->_data[$pageNo],$griddata);
return true;
}
protected function _renderFieldSet($fldset, $dataentity, $debug=false, $pagedef=null) {
if(count($fldset)) foreach($fldset as $no=>$fcfg) {
if(is_array($this->_printedfields) && !in_array($no, $this->_printedfields[$this->pgno])) continue; // print only selectd fields
$fldname = $fcfg['name'];
$fldtype = $fcfg['type'];
$value = NULL;
# if (!empty($fcfg['fillempty'])) writeDebugInfo("to fillempty for $fcfg[name]!");
if(in_array($fldtype,array('alwais_rect','poly','image'))) $dataentity[$fldname] = 1;
if ($fcfg['if']!==NULL) {
$ifFunc = (substr($fcfg['if'],0,1)==='@') ? substr($fcfg['if'],1) : $fcfg['if'];
$bDo = FALSE;
if(is_callable($ifFunc)) {
# perform user "signing" function over created PDF file
$bDo = call_user_func($ifFunc, $dataentity);
}
elseif(is_object($this->callbackObj) && method_exists($this->callbackObj, $ifFunc)) {
$bDo = $this->callbackObj->$ifFunc();
}
if (!$bDo) continue;
}
if($fldtype!=='plugin') {
if(!isset($this->pageSubData[$fldname]) && !isset($dataentity[$fldname]) && empty($fcfg['fillempty']))
continue;
if (isset($this->pageSubData[$fldname])) $value = $this->pageSubData[$fldname];
elseif (isset($dataentity[$fldname])) $value = $dataentity[$fldname];
elseif (isset($this->dataentity[$fldname])) $value = $this->dataentity[$fldname];
else $value = '';
if(!is_scalar($value)) {
# if (self::$debug) writeDebugInfo("non-scalar value, skip", $value);
continue;
}
}
else {
$debugValue = ($fcfg['type']=='checkbox') ? 'X' : "X $fldname";
}
$strval = ($value !== NULL) ? $value : ($debug ? $debugValue : '');
if(!empty($fcfg['convert'])) {
# if (self::$debug) writeDebugInfo("goint to convert value by ", $fcfg['convert']);
if(is_callable($fcfg['convert'])) # user converter function
$strval = call_user_func($fcfg['convert'], $strval, $dataentity); # second param - the whole data array
}
/****
if($strval === '' && !empty($fcfg['fillempty'])) {
if(!empty($fldef['height'])) {
$this->strokeEmptyValue($fcfg);
continue;
}
else {
$strval = ( empty($fldef['width']) ? '---------' : str_repeat('-',ceil($fldef['width']*0.7)) );
}
}
if($strval==='' && $fldtype!=='plugin') {
continue;
}
****/
$initval = '';
$fontsize = empty($fcfg['fontsize'])? 0 : $fcfg['fontsize'];
$this->_valueToPdf($strval, $fcfg);
# repeat printing with offsets defined in 'repeat' block
if (empty($fcfg['norepeat']) && isset($pagedef['repeat']) && count($pagedef['repeat'])>0) foreach($pagedef['repeat'] as $repeat) {
if (empty($repeat[2])) continue;
$tmpcfg = $fcfg;
foreach(array_keys($tmpcfg['posx']) as $kk) { $tmpcfg['posx'][$kk] += $repeat[0]; }
if(is_array($tmpcfg['posy'])) foreach(array_keys($tmpcfg['posy']) as $kk) { $tmpcfg['posy'][$kk] += $repeat[1]; }
else $tmpcfg['posy'] += $repeat[1];
$this->_valueToPdf($strval, $tmpcfg);
}
}
else {
if (self::$debug) \writeDebugInfo("empty fieldset, none to print ", $fldset);
}
}
/**
* Outputs generated PDF according to _tofile parameter (sends to te browser or saves to disc)
* @param $toMemory - pass true if Ypu want to return PDF in memory
*/
public function Output($outfilename=false, $toMemory = false) {
if($outfilename) $this->_outname = $outfilename;
$result = '';
if($this->_tofile && !$toMemory) {
# writeDebugInfo("_templatefile: ", $this->_templatefile);
if (isset($this->_templatefile[0]['src']) && is_string($this->_templatefile[0]['src']) &&
strtolower($this->_templatefile[0]['src'])==strtolower($this->_outname)) $this->_outname .= '.pdf'; # protect accidental template overwriting !
$result = $this->_pdf->Output($this->_outname,'F');
if (!empty($this->signFunc)) {
if (self::$debug) writeDebugInfo("calling $this->signFunc ...");
$callResult = 'no call';
if(is_callable($this->signFunc)) {
# perform user "signing" function over created PDF file
$callResult = call_user_func($this->signFunc, $this->_outname);
}
elseif(is_object($this->callbackObj) && method_exists($this->callbackObj, $this->signFunc)) {
$callFnc = $this->signFunc;
$callResult = $this->callbackObj->$callFnc($this->_outname);
}
if (self::$debug) writeDebugInfo("result of signing func call: ",$callResult);
if (!empty($callResult['errorMessage'])) self::$errorMessages[] = $callResult['errorMessage'];
}
}
else {
if (ob_get_level()) ob_end_clean();
if (ob_get_level()) ob_end_clean();
$outDest = ($toMemory ? 'S' : 'D');
$mdest = $outDest;
if (!empty($this->signFunc) ) {
$mdest = 'S'; # to sign file, we need it's body!
}
$result = $this->_pdf->Output($this->_outname, $mdest);
$callFnc = $this->signFunc;
if (self::$debug) writeDebugInfo("sign func: $callFnc");
if (!empty($callFnc)) {
if( $result && is_callable($callFnc) ) {
$result = call_user_func($callFnc, $result);
}
elseif( $result && is_object($this->callbackObj) && method_exists($this->callbackObj, $callFnc) ) {
$result = $this->callbackObj->$callFnc($this->_outname);
}
if (self::$debug) writeDebugInfo("signFunc call result: ", $result);
}
if ($toMemory) return $result;
else {
# send to client response
# WriteDebugInfo("sending signed PDF body to client...");
if (!headers_sent()) {
header('Content-Description: File Transfer');
header('Cache-Control: protected, must-revalidate, post-check=0, pre-check=0, max-age=1');
//header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
header('Pragma: public');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
// force download dialog
if (strpos(php_sapi_name(), 'cgi') === false) {
header('Content-Type: application/force-download');
header('Content-Type: application/octet-stream', false);
header('Content-Type: application/download', false);
header('Content-Type: application/pdf', false);
} else {
header('Content-Type: application/pdf');
}
// use the Content-Disposition header to supply a recommended filename
header('Content-Disposition: attachment; filename="'.basename($this->_outname).'";');
header('Content-Transfer-Encoding: binary');
}
echo $result;
# WriteDebugInfo("PDF body sent to client");
exit;
}
}
$this->_pdf = null;
$this->_data = [];
if ($toMemory) return $result;
}
public function findFieldDef($fldList, $fldid) {
foreach($fldList as $no =>$fdef) {
if($fdef['name'] === $fldid) return $fdef;
}
return FALSE;
}
public static function getErrorMessages() {
return self::$errorMessages;
}
protected function _evalAttribute($attr) {
if(!$attr) return $attr;
$attr = (string)$attr;
if(substr($attr,0,1)=='@') {
$atfunc = substr($attr,1);
if(is_callable($atfunc)) return call_user_func($atfunc, $this->dataentity);
elseif( is_object($this->callbackObj) && method_exists($this->callbackObj, $atfunc) ) {
return $this->callbackObj->$atfunc($this->dataentity);
}
return '';
}
else {
return $attr;
}
}
# TCPDF 6.x moves convertHTMLColorToDec() to static method of TCPDF_COLORS class
protected function colorToDec($clr) {
if (is_array($clr)) return $clr;
if (is_numeric($clr) && $clr<=255)
return ['R'=>$clr, 'G'=>$clr, 'B'=>$clr];
if(class_exists('TCPDF_COLORS')) $ret = TCPDF_COLORS::convertHTMLColorToDec($clr,$this->spot_colors);
else $ret = $this->_pdf->convertHTMLColorToDec($clr);
return $ret;
}
protected function _valueToPdf($value, $fcfg) {
if(!isset($fcfg['name'])) return;
$fldname = $fcfg['name'];
if ( in_array($fldname, $this->_hide_fields) ) return;
if (empty($value) && $fcfg['fillempty']) {
if(!empty($fcfg['height'])) {
$this->strokeEmptyValue($fcfg);
return;
}
else {
$value = ( empty($fcfg['width']) ? '---------' : str_repeat('-',ceil($fcfg['width']*0.7)) );
}
}
# $locDeb = ($fldname === 'sessign_date');
# if ($locDeb) writeDebugInfo("$fldame value is : ", $value);
$origwidth = $width = empty($fcfg['width']) ? 0 : (float)$fcfg['width'];
$origheight = $height = empty($fcfg['height']) ? 0 : (float)$fcfg['height'];
$fldtype = isset($fcfg['type']) ? $fcfg['type'] : '';
$posx = empty($fcfg['posx']) ? array(0) : $fcfg['posx'];
$posy = empty($fcfg['posy']) ? 0 : $fcfg['posy'];
# auto-adjust zero width and height
if($width<=0 && $fldtype!=='image') {
if (!is_scalar($posx[0])) \writeDebugInfo("strange posx : ", $posx, ' fcfg:', $fcfg);
$width = $this->_pdf->getPageWidth() - $posx[0] - self::DEFAULT_MARGIN;
}
if($height<=0 && $fldtype!=='image') {
$height = $this->_pdf->getPageHeight() - $posy - self::DEFAULT_MARGIN;
}
$cstep = empty($fcfg['charstep']) ? 0 : $fcfg['charstep'];
$maxlen = empty($fcfg['maxlength']) ? 0 : $fcfg['maxlength'];
$fntsize = empty($fcfg['size']) ? $this->_basepar['font']['size'] : (float)$fcfg['size'];
$fntname = empty($fcfg['font']) ? $this->_basepar['font']['name'] : $fcfg['font'];
$fntStyle = empty($fcfg['fontstyle']) ? $this->_basepar['font']['style'] : $fcfg['fontstyle'];
if (!empty($fcfg['color']) || $fcfg['color'] === '0')
$color = $this->_evalAttribute($fcfg['color']);
else
$color = $this->_basepar['font']['color']; # $this->defaultTextColor;
$bgcolor = $this->_evalAttribute($fcfg['bgcolor']);
$src = empty($fcfg['src']) ? '' : $fcfg['src'];
if (isset($this->_field_newattr[$fldname]['src']))
$src = $this->_field_newattr[$fldname]['src'];
$rotate = empty($fcfg['rotate']) ? 0 : floatval($fcfg['rotate']);
$border = empty($fcfg['border']) ? 0 : (intval($fcfg['border'])? intval($fcfg['border']) : $fcfg['border']);
$align = isset($fcfg['align']) ? $fcfg['align'] : '';
$valign = isset($fcfg['valign']) ? $fcfg['valign'] : '';
if ($valign ==='C') $valifn = 'M';
$opacity = isset($fcfg['opacity']) ? $fcfg['opacity'] : 1;
$foptions = $this->_evalAttribute($fcfg['options']);
if($color) {
$rgb = is_array($color) ? $color : $this->colorToDec($color);
}
else $rgb = array(0,0,0);
$this->_pdf->SetTextColorArray($rgb);
if ($opacity < 1) $this->_pdf->setAlpha($opacity);
if($rotate) {
$this->_pdf->StartTransform();
$this->_pdf->Rotate($rotate,$posx[0],$posy);
}
if($bgcolor) { # draw "background" filled ractangle
$brgb = $this->colorToDec($bgcolor);
if($brgb && $width>0 && $height>0) {
$this->_pdf->Rect($posx[0], $posy, $width, $height, 'F', [], array_values($brgb));
# Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array())
}
}
if ($fldtype==='image') {
if( $this->_images_disabled ) return;
if(is_file($this->_img_path . $src)) $src = $this->_img_path . $src;
elseif(is_file($this->_homedir . $src)) $src = $this->_homedir . $src;
if (!is_file($src)) {
return; # image not found!
}
$isize = self::getImageInfo($src);
if ($width==0 || $height ==0 ) { // get skipped parameter (height|width) from real image file
$isize = self::getImageInfo($src);
if (empty($isize[1])) return;
$wtoh = $isize[0] / $isize[1];
if ($height <=0 ) $height = round($width / $wtoh, 4);
elseif ($width <= 0) $width = round($width * $wtoh, 4);
}
$this->_pdf->Image($src,$posx[0],$posy,$width,$height);
}
elseif(in_array($fldtype, array('','text','money','date'))) {
if($fldtype === 'money' && is_numeric($value)) {
$vUtf = number_format(floatval($value),2,'.',' ');
}
else $vUtf = $this->_convertCset($value);
if($maxlen>0 && mb_strlen($vUtf)>$maxlen) {
$vUtf = mb_substr($vUtf,0,$maxlen,'UTF-8');
}
$this->_pdf->SetFont($fntname, $fntStyle, $fntsize);
$fitCell = ($this->adaptive_sizing && !empty($origheight) && !empty($origwidth));
if ($fntsize > 0) {
$this->_pdf->setFontSize($fntsize);
}
if(count($posx)>1) { # output by char to predefined x positions
for($kk=0;$kk<mb_strlen($vUtf);$kk++) {
if(!isset($posx[$kk])) break;
if($posx[$kk]>0)
$this->_pdf->MultiCell($width,$height,mb_substr($vUtf,$kk,1,'UTF-8'), $border, $align, 0, 1, floatval(($posx[$kk]+$this->offsets[0])), floatval(($posy+$this->offsets[1])));
}
}
elseif($cstep) { # TODO: use emptyfill attr Char to fill "empty" boxes (maxlength attrib must be set!)
for($kk=0; $kk<mb_strlen($vUtf); $kk++) {
$this->_pdf->MultiCell($width,$height,mb_substr($vUtf,$kk,1,'UTF-8'), $border, $align, 0, 1, floatval($posx[0]+$this->offsets[0]+($cstep*$kk)), floatval($posy+$this->offsets[1]) );
}
}
else {
$b_border = 0;
if ($border) {
$brdclr = $this->_parseColor($border);
$b_border = 1;
$this->_pdf->setLineStyle(array('width'=>0.2, 'color'=>$brdclr));
$this->_pdf->Rect($posx[0]+$this->offsets[0], $posy+$this->offsets[1], $width, $height,'',array('L'=>1,'T'=>1,'B'=>1,'R'=>1));
}
$this->_pdf->MultiCell($width,$height,$vUtf, $border , $align, 0, 1, ($posx[0]+$this->offsets[0]), ($posy+$this->offsets[1]), TRUE,0,false,TRUE,$height,$valign, $fitCell);
# writeDebugInfo("out $fcfg[name] = $vUtf");
}
# Get back to "std" font & text color
# if($this->_tmpBasepar['font']['name'])
# $this->_pdf->SetFont($this->_tmpBasepar['font']['name'],'',(float)$this->_basepar['font']['size']);
$this->_pdf->SetFont($this->_tmpBasepar['font']['name'],$this->_tmpBasepar['font']['style'],
(float)$this->_basepar['font']['size']);
$this->_pdf->SetTextColorArray($this->defaultTextColor); # back to default text color
}
elseif ($fldtype === 'html') { # render string as HTML code
$vUtf = $this->_convertCset($value);
if($fntname !='' && $this->_tmpBasepar['font']['name'] != $fntname) {
$this->_pdf->SetFont($fntname, $fntStyle, $fntsize);
}
if ($fntsize !=0 && $this->_tmpBasepar['font']['size'] != $fntsize) {
$this->_pdf->setFontSize($fntsize);
}
# writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true)
$this->_pdf->writeHTMLCell($width, $height, $posx[0], $posy, $vUtf, 0, 0, 0, true, $align, true);
// get back to defaults:
# if($this->_tmpBasepar['font']['name'])
$this->_pdf->SetFont($this->_tmpBasepar['font']['name'],$this->_tmpBasepar['font']['style'],
(float)$this->_tmpBasepar['font']['size']);
$this->_pdf->setFontSize($this->_tmpBasepar['font']['size']);
}
elseif($fldtype==='checkbox' or $fldtype==='check') {
if ( $fntsize !=0 ) {
$this->_pdf->setFontSize($fntsize);
}
if(!empty($value)) $this->_pdf->MultiCell($width,$height,'X', $border, $align, 0, 1, ($posx[0]+$this->offsets[0]), ($posy+$this->offsets[1]) );
}
elseif(substr($fldtype,0,7)==='barcode') { # barcode field marked as "barcode:<BCTYPE>"
$bctype = substr($fldtype,8);
if(empty($bctype)) $bctype = 'C39';
#bctype is one of TCPDF supported: C39 | C39+ | C39E | C39E+ |C93|S25|S25+|I25|I25+|C128|C128A|C128B|C128C|EAN2|EAN5|EAN8 ...
$xres = '';
$arr = explode(',',$foptions);
$style= array(
'fgcolor'=>$rgb
,'text'=>in_array('text',$arr)
,'border'=>in_array('border',$arr)
);
foreach($arr as $item) {
$splt = explode('=',$item);
if(count($splt)>1) { # attribute=value pair, try to use numbers as numbers, not strings
$val = is_int($splt[1]) ? intval($splt[1]) : $splt[1];
$style[$splt[0]] = $val; # so 'fontsize=8' will become $style['fontsize']=8
}
}
if(!isset($style['stretchtext'])) $style['stretchtext'] = 0; # text without stretching by default!
if(!empty($style['fontsize']) && empty($style['font'])) $style['font'] = $this->_tmpBasepar['font']['name']; # without 'font', fontsize is ignored in TCPDF!
# if($rgb) $style['fgcolor'] = $rgb;
$this->_pdf->write1DBarcode($value, $bctype, ($posx[0]+$this->offsets[0]), ($posy+$this->offsets[1]), $width, $height, $xres, $style, $align);
}
elseif(substr($fldtype,0,6)=='qrcode') { # Printing QRCode "qrcode:QRCODE,M"
$qrtype = substr($fldtype,7);
if (empty($qrtype)) $qrtype = 'QRCODE,H'; # supported: "QRCODE"="QRCODE,L","QRCODE,M","QRCODE,Q","QRCODE,H"
$style=[];
if($rgb) $style['fgcolor'] = $rgb;
$this->_pdf->write2DBarcode($value, $qrtype, ($posx[0]+$this->offsets[0]), ($posy+$this->offsets[1]), $width, $height, $style, $align, false);
}
elseif($fldtype=='rect') {
if (!empty($value)) { # draw only if value non empty!
# $line_style = array('all'=> array('width' => 1, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $rgb));
$this->_pdf->SetDrawColorArray($rgb);
# $this->_pdf->Polygon($p, $style, array(),array(),true);
$style = '';
$border_style = [];
$fill_color = [];
if ($bgcolor) $fill_color = $this->colorToDec($bgcolor);
if ($fcfg['thickness']>0) {
# $border_style['width'] = $fcfg['thickness'];
$this->_pdf->SetLineWidth($fcfg['thickness']);
# $border_style['join'] = 'bevel';
}
$this->_pdf->Rect($posx[0], $posy, $width, $height, $style, $border_style, $fill_color);
$this->_pdf->SetLineWidth(0.2);
}
# Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true)
}
elseif($fldtype=='poly') {
$p = [];
for($kk=0;$kk<min(count($posx),count($posy));$kk++) {
$p[] = $posx[$kk]; $p[] = $posy[$kk];
}
$style = ['dash'=>0, 'join' => 'miter', 'color' => $rgb];
if ($fcfg['thickness']>0) {
$style['width'] = $fcfg['thickness'];
}
$this->_pdf->SetDrawColorArray($rgb);
$this->_pdf->Polygon($p, $style, [],[], false);
}
elseif($fldtype==='cross') {
if(empty($value)) return;
$style = ['dash'=>0, 'join' => 'miter', 'color' => $rgb];
if ($fcfg['thickness']>0) {
$style['width'] = $fcfg['thickness'];
}
$this->_pdf->Line($posx[0], $posy,$posx[0]+$width, $posy+$height, $style);
$this->_pdf->Line($posx[0], $posy+$height,$posx[0]+$width, $posy, $style);
}
elseif($fldtype==='plugin') { # Render specific area by calling plugin.Render()
# if (self::$debug) writeDebugInfo("plugin field: ", $fcfg);
$plgclass = isset($fcfg['plugintype']) ? $fcfg['plugintype'] : '';
$this->renderPlugin($fcfg,$this->dataentity);
}
if($rotate) $this->_pdf->StopTransform();
if ($opacity < 1) $this->_pdf->setAlpha(1); # back to normal (no transparency)
$this->_pdf->setFontSize($this->_tmpBasepar['font']['size']);
$this->_pdf->SetDrawColorArray(0);
}
# TODO: render Plugin
public function renderPlugin($fcfg, $data, $xOff=0, $yOff=0) {
$fldname = $fcfg['name'];
$plgclass = isset($fcfg['plugintype']) ? $fcfg['plugintype'] : '';
$foptions = $this->_evalAttribute($fcfg['options']);
if($plgclass!='' && !class_exists($plgclass)) {
# try auto-load class file
$clsName = strtolower($plgclass);
if (substr($clsName,0,6) === 'pfpdf_') $clsName = substr($clsName,6);
$clsFile = __DIR__ . '/' . strtolower($clsName) . '.php';
if (self::$debug) {
error_reporting(E_ALL & ~E_STRICT & ~E_NOTICE);
ini_set('display_errors', 1);
ini_set('log_errors', 1);
}
if (is_file($clsFile)) {
include_once($clsFile);
}
else {
if (self::$debug) \writeDebugInfo("No plugin file found for $clsName");
}
}
if($plgclass!='' && class_exists($plgclass)) {
$opt_arr = [];
if (!empty($foptions)) {
$arr = explode(',',$foptions);
foreach($arr as $elem) {
$optpair = explode('=',$elem);
if(count($optpair)>=2) $opt_arr[$optpair[0]] = $optpair[1];
}
}
if (!empty($fcfg['color'])) $opt_arr['color'] = $fcfg['color'];
else $opt_arr['font'] = $this->defaultTextColor;
if (!empty($fcfg['size'])) $opt_arr['size'] = $fcfg['size'];
else $opt_arr['size'] = $this->_basepar['font']['size'];
if (!empty($fcfg['font'])) $opt_arr['font'] = $fcfg['font'];
else $opt_arr['font'] = $this->_basepar['font']['name'];
if (!empty($fcfg['bgcolor'])) $opt_arr['bgcolor'] = $fcfg['bgcolor'];
if (!empty($fcfg['thickness'])) $opt_arr['thickness'] = $fcfg['thickness'];
$posy = $fcfg['posy'];
$width = $fcfg['width'];
$height = $fcfg['height'];
$xReal = $fcfg['posx'][0] + $xOff;
$yReal = $posy+$yOff;
$pdf_plg = new $plgclass($this->_pdf, array_merge($this->_config, $opt_arr), $xReal,$yReal,$width,$height);
if($pdf_plg instanceof PfPdfPlugin) {
# plugin-specific options can be passed in plugin attribute options="name=value1,..."
# $pdf_plg->setConfig(array_merge($this->_config, $opt_arr));
# if(count($opt_arr)) $pdf_plg->setConfig($opt_arr);
$renderData = (isset($this->_pluginData[$fldname])) ? $this->_pluginData[$fldname] : $this->_dataBlock;
$result = $pdf_plg->Render($renderData);
if(!$result) {
$this->_errormessage = $pdf_plg->getErrorMessage();
if(self::$debug && is_callable('\WriteDebugInfo'))
\WriteDebugInfo("plugin $plgclass::Render() error : ".$this->_errormessage);
}
}
else {
$this->_errormessage = "Unknown plugin class [$plgclass] or not instance of PfPdfPlugin, rendering skipped";
if (self::$debug && is_callable('WriteDebugInfo')) \WriteDebugInfo($this->_errormessage);
}
unset($pdf_plg);
}
$this->_pdf->SetTextColorArray([0,0,0]);
}
/**
* Appends a page to the PDF document
*
* @param mixed $orientation optional orientation ('P' for portrait, 'L' for Landscape', default - current configured value)
* @param mixed $units optional units for the page ('mm' for millimeters etc., default - current configured value)
*/
public function AddPage($orientation=false, $units=false) {
if(!$this->_pdf) $this->_createPdfObject();
$this->_pdf->AddPage(($orientation ? $orientation : $this->_config['page']['orientation']));
$this->_pdf->setPageUnit(($units ? $units : $this->_config['page']['units']));
}
/**
* Appends all page definitions from another XML config.file
* All "base" parameters ignored (excluding <templatefiles>)
* @param mixed $xmlCfg path to XML configuration file
* @param mixed $subid id of element in data, that is sub-array of data specific to this page
* @since 1.9
*/
public function AppendPageDefFromXml($xmlCfg, $subid='', $relpath = NULL) {
# echo 'AppendPageDefFromXml: <pre>' . print_r($xmlCfg,1). '</pre>';
# \writeDebugInfo("AppendPageDefFromXml($xmlCfg, $subid, '$relpath'");
if(!$this->father)
$this->father = $this;
$tmpCfg = new CPrintFormPdf($xmlCfg, $this->callbackObj, $this->father, $subid);
# \writeDebugInfo("tmpCfg: ", $tmpCfg);
if (!$relpath) $relpath = $tmpCfg->_homedir;
if (substr($relpath,-1) !== '/' && substr($relpath,-1) !== '\\')
$relpath .= '/';
$orientation = $tmpCfg->_basepar['page']['orientation'];
if (self::$debug) \writeDebugInfo("$xmlCfg: basepar is ", $tmpCfg->_basepar);
if (count($tmpCfg->_templatefile)) {
foreach($tmpCfg->_templatefile as $oneTpl) {
# echo ("template :<pre>". print_r($oneTpl, 1).'</pre>');
if (!is_string($oneTpl['src'])) continue; # array - WTF?!
$realXmlname = self::getRelativeFilename($relpath, (string)$oneTpl['src']);
$tplpath = dirname($realXmlname);
if ($tplpath) $tplpath .= '/';
$tplname = basename($oneTpl['src']);
# $oneTpl['src'] = $tplpath . $tplname;
# $this->_templatefile[] = array('src'=>array($tplpath, $tplname, $tmpCfg->_img_path));
$this->father->_templatefile[] = array('src'=>array($tplpath, $tplname, $tmpCfg->_img_path));
## STOP HERE!
}
}
# writeDebugInfo("cnt of pagedefs: ", count($tmpCfg->_pagedefs));
/*
if (count($tmpCfg->_pagedefs)) {
$tFont = $tmpCfg->_basepar['font'];
# WriteDebugInfo('child basepar font:', $tFont);
# WriteDebugInfo('this basepar font:', $this->_basepar['font']);
# echo 'child basepar:<pre>' . print_r($tmpCfg->_basepar,1) .'</pre>'; exit;
$newName = ($tFont['name'] === $this->_basepar['font']['name']) ? '': $tFont['name'];
$newSize = ($tFont['size'] === $this->_basepar['font']['size']) ? '': $tFont['size'];
$newStyle = ($tFont['style'] === $this->_basepar['font']['style']) ? '': $tFont['style'];
# apply BASE font & size as default for imported fields (if they're different vs "parent" cfg)
$pageid = 'subpage_'.rand(100000,99999999);
foreach($tmpCfg->_pagedefs as &$pdef) {
if ($subid) $pdef['datasubid'] = (string)$subid;
$pdef['sourcepath'] = $relpath;
$pdef['pageid'] = $pageid;
# each imported page will have it's own basepar (font name,size, color):
$pdef['basepar'] = $tmpCfg->_basepar;
}
writeDebugInfo("to add page defs: ", $tmpCfg->_pagedefs);
$this->father->_pagedefs = array_merge($this->_pagedefs, $tmpCfg->_pagedefs);
if (count($tmpCfg->_apFields)) {
$this->_subApFields[$pageid] = $tmpCfg->_apFields;
$this->_subApValues[$pageid] = $tmpCfg->_apValues;
}
}
*/
# merge all user parameters
$childPars = $tmpCfg->getUserParameters();
if (count($childPars)) $this->userParams = array_merge($this->userParams,$childPars);
unset($tmpCfg);
if (self::$debug) \writeDebugInfo("my own basepar after AppendPageDefFromXml: ", $this->_basepar);
}
# returns last error message
public function GetErrorMessage() { return $this->_errormessage; }
/**
* Printing music staff page for writing music. Misicians will like it:)
*
* @param string $title page title
* @param array $options - optional parameters associative array
* 'measures' - measures per line: if 2 or greater, piano roll will have "measure border" vertical bars.
* 'step_roll' - distance between piano-roll blocks (mm), default is 27mm
* 'step_line' - distance between lines in the piano roll (mm), default is 2mm
* 'color' - drawing color (RGB array), default is black - [0,0,0]
*/
public function AddPageMusicStaff($title='',$options=null) {
$margin_l = $margin_r = 18;
$margin_t = 20; $margin_b = 10;
# $merged_staves=0, $measures=0, $step_y=0, $stepLine=0
$merged_staves = isset($options['merged_staves']) ? intval($options['merged_staves']) : 0;
$measures = isset($options['measures']) ? $options['measures'] : 0;
$step_y = isset($options['step_roll']) ? intval($options['step_roll']) : 27;
$stepLine = isset($options['step_line']) ? intval($options['step_line']) : 2;
$color = isset($options['color']) ? $this->_parseColor($options['color']) : array(0,0,0);
$accolade = isset($options['accolade'])? $options['accolade'] : false;
$blkheight = $step_y + ($stepLine*4);
$this->AddPage('P','mm');
$y_lowest = $this->_pdf->getPageHeight()-$margin_b;
$rightpos = $this->_pdf->getPageWidth() - $margin_r;
$beatstep = ($measures>0) ? round(($rightpos-$margin_l)/$measures,2) : 0;
if($title) $this->_valueToPdf($title,
array(
'name'=>'pagetitle'
,'type'=>'text'
,'posx'=> $margin_l
,'posy'=> min(2,($margin_t-16))
,'width' => ($this->_pdf->getPageWidth() - $margin_l - $margin_r)
,'align'=>'C'
,'color'=>''
,'bgcolor'=>''
,'options'=>''
,'size'=> 10)
);
$this->_pdf->SetDrawColorArray($color);
for($ypos=$margin_t; $ypos+(4*$stepLine) <= $y_lowest; $ypos+=$step_y) {
for($line=0;$line<=4;$line++) {
$this->_pdf->Line($margin_l,$ypos+($line*$stepLine),$rightpos,$ypos+($line*$stepLine));
}
$this->_pdf->Line($margin_l,$ypos,$margin_l,$ypos+(4*$stepLine)); # vertical bars
$this->_pdf->Line($rightpos,$ypos,$rightpos,$ypos+(4*$stepLine));
if($beatstep) for($beatpos = $margin_l; $beatpos<$rightpos; $beatpos += $beatstep)
$this->_pdf->Line($beatpos,$ypos,$beatpos,$ypos+(4*$stepLine)); # vertical bars
}
if($merged_staves>1) {
$yStart = $margin_t;
$yEnd = $yStart + ($merged_staves-1)*$step_y;
while( $yEnd<$y_lowest ) {
$this->_pdf->Line($margin_l,$yStart,$margin_l,$yEnd);
$this->_pdf->Line($rightpos,$yStart,$rightpos,$yEnd);
if($accolade) $this->drawAccolade($margin_l-4.4,$yStart,$margin_l-0.5,($yEnd+4*$stepLine), $color);
if($beatstep) for($beatpos = $margin_l; $beatpos<$rightpos; $beatpos +=$beatstep)
$this->_pdf->Line($beatpos,$yStart,$beatpos,$yEnd); # vertical bars
$yStart += ($merged_staves)*$step_y;
$yEnd = $yStart + ($merged_staves-1)*$step_y;
}
}
$this->_specialPages++;
}
/**
* Printing lined sheet page
*
* @param string $title page title
* @param array $options - optional parameters associative array
* 'step_y' - distance between horizontal lines, (mm). Default is 5mm
* 'step_x' - distance between vertical lines, (mm). Default equal to step_y
* 'color' - drawing color (RGB array), default is light gray - [180,180,180]
*/
public function AddPageLined($title='',$options=null) {
$margin_l = $margin_r = $margin_t = $margin_b = 5;
$step_y = isset($options['step_y']) ? intval($options['step_y']) : 5;
$step_x = isset($options['step_x']) ? intval($options['step_x']) : $step_y;
$color = $color1 = isset($options['color']) ? $this->_parseColor($options['color']) : array(180,180,180);
$millimetrovka = ($options==='mm' OR !empty($options['mm'])); # millimeter sheet mode
if($millimetrovka) {
$step_x = $step_y = 1;
$color1 = isset($options['color']) ? $this->_parseColor($options['color']) : array(90,90,90); # thick lines color
$color = array(ceil($color[0]+(255-$color[0])*0.5),ceil($color[0]+(255-$color[1])*0.5),ceil($color[2]+(255-$color[0])*0.5)); # thin lines color
}
$this->AddPage('P','mm');
$y_lowest = $step_y>0 ? $this->_pdf->getPageHeight()-$margin_b - ($this->_pdf->getPageHeight()-$margin_t-$margin_b)%$step_y : $this->_pdf->getPageHeight()-$margin_b;
$rightpos = ($step_x>0) ? $this->_pdf->getPageWidth() - $margin_r - ($this->_pdf->getPageWidth()-$margin_l-$margin_r)%$step_x : $this->_pdf->getPageWidth()-$margin_r;
if($title) {
$this->_valueToPdf($title,
array(
'name'=> 'page_title'
,'type'=>'text'
,'posx'=> $margin_l
,'posy'=> min(2,($margin_t))
,'width' => ($this->_pdf->getPageWidth() - $margin_l - $margin_r)
,'align'=>'C'
,'size'=> 10)
);
$margin_t+=5;
}
$this->_pdf->SetDrawColorArray($color);
$xx=0;
if($step_y>0) for($ypos=$margin_t; $ypos<= $y_lowest; $ypos+=$step_y) {
if($millimetrovka) {
if(($ypos-$margin_t)%5==0) { # think line every 5 mm
$width = (($ypos-$margin_t)%10) ? 0.15 : 0.25; # 10mm-thicker
$this->_pdf->SetLineWidth($width);
$this->_pdf->SetDrawColorArray($color1);
}
else { # thin line
$this->_pdf->SetDrawColorArray($color);
$this->_pdf->SetLineWidth(0.05);
}
}
$this->_pdf->Line($margin_l,$ypos,$rightpos,$ypos); # horiz.lines
}
if($step_x>0) for($xpos=$margin_l; $xpos<= $rightpos; $xpos+=$step_x) {
if($millimetrovka) {
if(($xpos-$margin_l)%5==0) { # think line every 5 mm
$width = (($xpos-$margin_l)%10) ? 0.15 : 0.25; # 10mm-thicker
$this->_pdf->SetLineWidth($width);
$this->_pdf->SetDrawColorArray($color1);
}
else { # thin line
$this->_pdf->SetDrawColorArray($color);
$this->_pdf->SetLineWidth(0.05);
}
}
$this->_pdf->Line($xpos, $margin_t,$xpos, $y_lowest); # vert.lines
}
$this->_pdf->SetDrawColorArray(array(0,0,0)); # back to normal
$this->_pdf->SetLineWidth(0.1);
$this->_specialPages++;
}
/**
* Fetching TCPDF object to manipulate beyong of this class functionality
* @returns TCPDF object ref.
*/
public function getPdfObject() {
if(!$this->_pdf) $this->_createPdfObject();
return $this->_pdf;
}
public function GetPageDefs() { return $this->_pagedefs; }
/**
* Draws music "Accolade" sign. Accolade will fit inside rectangle limited by x0,y0 - xk,$yk coordinates.
* @param mixed $x0 start X pos
* @param mixed $y0 start Y pos
* @param mixed $xk ending X pos
* @param mixed $yk ending Y pos
* @param mixed $color color, black by default
*/
public function drawAccolade($x0,$y0,$xk,$yk, $color=[]) {
$koefx = 1.2; $kxd = 0.4;
$koefy=0.05;
$y_mid = ($y0+$yk)/2;
$segments = array();
$segments[] = array($xk-($xk-$x0)*($koefx-0), $y0+($yk-$y0)*$koefy, $x0+($xk-$x0)*($koefx-0),$y_mid-($yk-$y0)*$koefy, $x0, $y_mid);
$segments[] = array($x0+($xk-$x0)*($koefx-0), $y_mid+($yk-$y0)*$koefy, $xk-($xk-$x0)*($koefx-0),$yk-($yk-$y0)*$koefy, $xk, $yk);
$segments[] = array($xk-($xk-$x0)*($koefx-$kxd), $yk-($yk-$y0)*$koefy, $x0+($xk-$x0)*($koefx+$kxd),$y_mid+($yk-$y0)*$koefy, $x0, $y_mid);
$segments[] = array($x0+($xk-$x0)*($koefx+$kxd), $y_mid-($yk-$y0)*$koefy, $xk-($xk-$x0)*($koefx-$kxd),$y0+($yk-$y0)*$koefy, $xk, $y0);
$this->_pdf->Polycurve($xk, $y0, $segments, 'F', array(), $color);
}
/**
* @return true if PDF object created (and template loaded from $this->_templatefile)
*/
protected function _createPdfObject() {
if(is_object($this->_pdf)) return true;
try {
$this->_pdf = new FPDI($this->_basepar['page']['orientation'],$this->_basepar['page']['units'],$this->_basepar['page']['size']);
$this->_pdf->setPrintHeader(false);
$this->_pdf->setPrintFooter(false);
$this->_pdf->SetFont($this->_basepar['font']['name'], $this->_basepar['font']['style'], $this->_basepar['font']['size']);
} catch (Exception $e) {
$this->_errormessage = 'Creating PDF object error: '. $e->getMessage();
return false;
}
$this->_pdf->setMargins(0,0,0,true);
$this->_pdf->SetRightMargin(0);
return true;
}
# convert to UTF8 before drawing
protected function _convertCset($strval) {
$srcCharset = (isset($this->_config['stringcharset']) ? $this->_config['stringcharset'] : 'UTF-8');
$ret = ($srcCharset!='' && $srcCharset!='UTF-8') ?
@iconv($this->_config['stringcharset'],'UTF-8',$strval) : $strval;
return $ret;
}
protected function _parseColor($param) {
if(is_array($param)) $color = $param;
elseif(is_int($param)) $color = array($param,$param, $param);
else {
if(!$this->_pdf) $this->_createPdfObject();
$color = $this->colorToDec((string)$param);
}
return $color;
}
public static function getRelativeFilename($basepth, $filename) {
# writeDebugInfo("getRelativeFilename: ", $basepth, $filename);
if(!is_string($filename)) {
echo ('bad filename <pre>'.print_r($filename,1).'</pre>');
echo '<pre>'; debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,4); echo '</pre>';
exit;
}
if (is_file($filename)) return $filename;
$path = $basepth;
if (substr($filename,0,1)==='@' && ($fncname = susbtr($filename,2) && is_calleble($fncname))) {
$srcfile = call_user_func($fncname, $this->_data);
return $srcfile;
}
if ($path === './') $path = getcwd() . '/';
if (substr($filename,0,2)==='./') $filename = substr($filename,2);
while(substr($filename,0,3) === '../') {
$path = dirname($path) ;
if ($path!='') $path .= '/';
$filename = substr($filename,3);
}
return ($path . $filename);
}
/**
* Passes data that will be visualized by plugin
*
* @param mixed $name plugin data block unique name. There maight be more than one plugin-rendered block on the PDF page(s)
* @param mixed $data data to be visualized
*/
public function setPluginData($name, $data) {
$this->_pluginData[$name] = $data;
}
public static function getImageInfo($imgsrc) {
if (!isset(self::$_cached[$imgsrc])) self::$_cached[$imgsrc] = @getimagesize($imgsrc);
return self::$_cached[$imgsrc];
}
public static function parseParam($par, $data=0) {
$ret = (string)$par;
if (substr($ret,0,1) === '@') {
$ret = substr($ret,1);
if (is_callable($ret))
$ret = call_user_func($ret, $data);
elseif( is_object($this->callbackObj) && method_exists($this->callbackObj, $ret) ) {
return $this->callbackObj->$ret($data);
}
}
return $ret;
}
public function getUserParameters() {
return $this->userParams;
}
} # class CPrintFormPdf definition end
|