Rebuild Cyan PDF MODEL

This commit is contained in:
John BOTELLA 2024-10-08 22:44:36 +02:00
parent 6240246d1d
commit 498e1473aa
2 changed files with 168 additions and 137 deletions

View file

@ -158,6 +158,11 @@ abstract class CommonDocGenerator
*/
public $cols;
/**
* @var array<string,array{page:int,y:float|int}> Array of position data
*/
public $afterColsLinePositions;
/**
* @var array{fullpath:string} Array with result of doc generation. content is array('fullpath'=>$file)
*/
@ -1079,6 +1084,64 @@ abstract class CommonDocGenerator
}
/**
* Get position in PDF after col display
* @return false|array<string,array{page:int,y:float|int,col:string}
*/
public function getMaxAfterColsLinePositionsData()
{
if (empty($this->afterColsLinePositions) || !is_array($this->afterColsLinePositions)) {
return false;
}
$colId = '';
$maxPage = $maxY = 0;
foreach ($this->afterColsLinePositions as $colKey => $value) {
if ($value['page'] > $maxPage) {
$colId = $colKey;
$maxPage = $value['page'];
$maxY = $value['y']; // if page is higher we need to reset y to new max page y
} elseif ($value['page'] == $maxPage) {
$maxY = max($value['y'], $maxY);
$colId = $colKey;
}
}
return [
'col' => $colId,
'y' => $maxY,
'page' => $maxPage
];
}
/**
* Used for reset afterColsLinePositions var in start of a new pdf draw line loop
* @param float $y the new $y position usually get by TCPDF::GetY()
* @param int $pageNumb the page number to reset at
* @return null
*/
public function resetAfterColsLinePositionsData(float $y, int $pageNumb)
{
$this->afterColsLinePositions = [];
return $this->setAfterColsLinePositionsData('startLine', $y, $pageNumb);
}
/**
* Used for to set afterColsLinePositions var in a pdf draw line loop
* @param string $colId the column id used as key in $this->cols or an unique id code like startLine or separateLine ....
* @param float $y the $y position usually get by TCPDF::GetY() where print data ended
* @param int $pageNumb the page number where print data ended
* @return void
*/
public function setAfterColsLinePositionsData(string $colId, float $y, int $pageNumb)
{
$this->afterColsLinePositions[$colId] = [
'page' => $pageNumb,
'y' => $y
];
}
/**
* uasort callback function to Sort columns fields
*
@ -1292,6 +1355,7 @@ abstract class CommonDocGenerator
// set cell padding with column content definition
$pdf->setCellPaddings(isset($colDef['content']['padding'][3]) ? $colDef['content']['padding'][3] : 0, isset($colDef['content']['padding'][0]) ? $colDef['content']['padding'][0] : 0, isset($colDef['content']['padding'][1]) ? $colDef['content']['padding'][1] : 0, isset($colDef['content']['padding'][2]) ? $colDef['content']['padding'][2] : 0);
$pdf->writeHTMLCell($colDef['width'], 2, isset($colDef['xStartPos']) ? $colDef['xStartPos'] : 0, $curY, $columnText, 0, 1, 0, true, $colDef['content']['align']);
$this->setAfterColsLinePositionsData($colKey, $pdf->GetY(), $pdf->getPage());
// restore cell padding
$pdf->setCellPaddings($curentCellPaddinds['L'], $curentCellPaddinds['T'], $curentCellPaddinds['R'], $curentCellPaddinds['B']);

View file

@ -78,7 +78,6 @@ class pdf_cyan extends ModelePDFPropales
*/
public $cols;
/**
* Constructor
*
@ -185,10 +184,7 @@ class pdf_cyan extends ModelePDFPropales
$nblines = count($object->lines);
$hidetop = 0;
if (getDolGlobalString('MAIN_PDF_DISABLE_COL_HEAD_TITLE')) {
$hidetop = getDolGlobalString('MAIN_PDF_DISABLE_COL_HEAD_TITLE');
}
$hidetop = getDolGlobalInt('MAIN_PDF_DISABLE_COL_HEAD_TITLE');
// Loop on each lines to detect if there is at least one image to show
$realpatharray = array();
@ -352,6 +348,10 @@ class pdf_cyan extends ModelePDFPropales
$tab_top = 90 + $top_shift;
$tab_top_newpage = (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD') ? 42 + $top_shift : 10);
if (!$hidetop && getDolGlobalInt('MAIN_PDF_ENABLE_COL_HEAD_TITLE_REPEAT')) {
// TODO : make this hidden conf the default behavior for each PDF when each PDF managed this new Display
$tab_top_newpage+= $this->tabTitleHeight;
}
$nexY = $tab_top;
@ -549,8 +549,25 @@ class pdf_cyan extends ModelePDFPropales
// Loop on each lines
$pageposbeforeprintlines = $pdf->getPage();
$pagenb = $pageposbeforeprintlines;
for ($i = 0; $i < $nblines; $i++) {
$linePosition = $i + 1;
$curY = $nexY;
// in First Check line page break and add page if needed
if (isset($object->lines[$i]->pagebreak) && $object->lines[$i]->pagebreak) {
// New page
$pdf->AddPage();
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
}
$pdf->setPage($pdf->getNumPages());
$nexY = $tab_top_newpage;
}
$this->resetAfterColsLinePositionsData($nexY, $pdf->getPage());
$pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
$pdf->SetTextColor(0, 0, 0);
@ -561,7 +578,7 @@ class pdf_cyan extends ModelePDFPropales
}
$pdf->setTopMargin($tab_top_newpage);
$pdf->setPageOrientation('', 1, $heightforfooter + $heightforfreetext + $heightforsignature + $heightforinfotot); // The only function to edit the bottom margin of current page to set it.
$pdf->setPageOrientation('', 1, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
$pageposbefore = $pdf->getPage();
$curYBefore = $curY;
@ -569,28 +586,17 @@ class pdf_cyan extends ModelePDFPropales
$showpricebeforepagebreak = getDolGlobalInt('MAIN_PDF_DATA_ON_FIRST_PAGE');
$posYAfterImage = 0;
$posYAfterDescription = 0;
if ($this->getColumnStatus('position')) {
$this->printStdColumnContent($pdf, $curY, 'position', $i + 1);
}
$newPageAddedForPhoto = false;
if ($this->getColumnStatus('photo')) {
// We start with Photo of product line
if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imglinesize['height']) > ($this->page_hauteur - ($heightforfooter + $heightforfreetext))) { // If photo too high, we moved completely on new page
if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imglinesize['height']) > ($this->page_hauteur - $heightforfooter)) { // If photo too high, we moved completely on new page
$pdf->AddPage('', '', true);
$newPageAddedForPhoto = true;
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
}
$pdf->setPage($pageposbefore + 1);
$pdf->setPageOrientation('', 1, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
$curY = $tab_top_newpage;
// disable MAIN_PDF_DATA_ON_FIRST_PAGE near page break limit to anticipate auto page break jump before add line due to padding
// (ex: description is on page 2 and price on page 1)
$showpricebeforepagebreak = 0;
}
$pdf->setPageOrientation('', 0, $heightforfooter + $heightforfreetext); // The only function to edit the bottom margin of current page to set it.
@ -598,85 +604,46 @@ class pdf_cyan extends ModelePDFPropales
$pdf->Image($realpatharray[$i], $this->getColumnContentXStart('photo'), $curY + 1, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
// $pdf->Image does not increase value return by getY, so we save it manually
$posYAfterImage = $curY + $imglinesize['height'];
}
$pageposafter = $pdf->getPage();
if ($pageposafter > $pageposbefore) { // There is a pagebreak
$pdf->setPage($pageposbefore+1);
$this->setAfterColsLinePositionsData('photo', $posYAfterImage, $pdf->getPage());
}
// restore page bottom margin
$pdf->setPageOrientation('', 1, $heightforfooter + $heightforfreetext + $heightforsignature + $heightforinfotot); // The only function to edit the bottom margin of current page to set it.
}
$newPageAddedForDescription = false;
if ($this->getColumnStatus('desc')) {
$pdf->startTransaction();
$this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
$pageposafter = $pdf->getPage();
if ($pageposafter > $pageposbefore) { // There is a pagebreak
$pdf->rollbackTransaction(true);
$newPageAddedForDescription = true;
$pdf->setPageOrientation('', 1, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
$this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc);
$pageposafter = $pdf->getPage();
$posyafter = $pdf->GetY();
//var_dump($posyafter); var_dump(($this->page_hauteur - ($heightforfooter+$heightforfreetext+$heightforinfotot))); exit;
if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforsignature + $heightforinfotot))) { // There is no space left for total+free text
if ($i == ($nblines - 1)) { // No more lines, and no space left to show total, so we create a new page
$pdf->AddPage('', '', true);
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
}
$pdf->setPage($pageposafter + 1);
}
}
} else { // No pagebreak
$pdf->commitTransaction();
}
$posYAfterDescription = $pdf->GetY();
$this->setAfterColsLinePositionsData('desc', $pdf->GetY(), $pdf->getPage());
}
$nexY = $pdf->GetY();
// check if image height is more than description height
if (!$newPageAddedForPhoto && $posYAfterImage) {
$nexY = max($posYAfterImage, $nexY);
}
$pageposafter = $pdf->getPage();
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$pdf->setPage($pageposbefore);
$pdf->setTopMargin($this->marge_haute);
$pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
// We suppose that a too long description or photo were moved completely on next page
if ($pageposafter > $pageposbefore && empty($showpricebeforepagebreak)) {
$pdf->setPage($pageposafter);
if ($afterPosData['page'] > $pageposbefore && empty($showpricebeforepagebreak)) {
$pdf->setPage($afterPosData['page']);
$curY = $tab_top_newpage;
} else {
$curY = $curYBefore;
}
$pdf->SetFont('', '', $default_font_size - 1); // We reposition the default font
// Line position
if ($this->getColumnStatus('position')) {
$this->printStdColumnContent($pdf, $curY, 'position', $linePosition);
}
// VAT Rate
if ($this->getColumnStatus('vat')) {
$vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'vat', $vat_rate);
$nexY = max($pdf->GetY(), $nexY);
}
// Unit price before discount
if ($this->getColumnStatus('subprice')) {
$up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'subprice', $up_excl_tax);
$nexY = max($pdf->GetY(), $nexY);
}
// Quantity
@ -684,7 +651,6 @@ class pdf_cyan extends ModelePDFPropales
if ($this->getColumnStatus('qty')) {
$qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'qty', $qty);
$nexY = max($pdf->GetY(), $nexY);
}
@ -692,28 +658,24 @@ class pdf_cyan extends ModelePDFPropales
if ($this->getColumnStatus('unit')) {
$unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'unit', $unit);
$nexY = max($pdf->GetY(), $nexY);
}
// Discount on line
if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent) {
$remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'discount', $remise_percent);
$nexY = max($pdf->GetY(), $nexY);
}
// Total excl tax line (HT)
if ($this->getColumnStatus('totalexcltax')) {
$total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax);
$nexY = max($pdf->GetY(), $nexY);
}
// Total with tax line (TTC)
if ($this->getColumnStatus('totalincltax')) {
$total_incl_tax = pdf_getlinetotalwithtax($object, $i, $outputlangs, $hidedetails);
$this->printStdColumnContent($pdf, $curY, 'totalincltax', $total_incl_tax);
$nexY = max($pdf->GetY(), $nexY);
}
// Extrafields
@ -722,17 +684,19 @@ class pdf_cyan extends ModelePDFPropales
if ($this->getColumnStatus($extrafieldColKey)) {
$extrafieldValue = $this->getExtrafieldContent($object->lines[$i], $extrafieldColKey, $outputlangs);
$this->printStdColumnContent($pdf, $curY, $extrafieldColKey, $extrafieldValue);
$nexY = max($pdf->GetY(), $nexY);
$this->setAfterColsLinePositionsData('options_'.$extrafieldColKey, $pdf->GetY(), $pdf->getPage());
}
}
}
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$parameters = array(
'object' => $object,
'i' => $i,
'pdf' => & $pdf,
'curY' => & $curY,
'nexY' => & $nexY,
'nexY' => & $afterPosData['y'], // for backward module hook compatibility Y will be accessible by $object->getMaxAfterColsLinePositionsData()
'outputlangs' => $outputlangs,
'hidedetails' => $hidedetails
);
@ -768,7 +732,7 @@ class pdf_cyan extends ModelePDFPropales
// Retrieve type from database for backward compatibility with old records
if ((!isset($localtax1_type) || $localtax1_type == '' || !isset($localtax2_type) || $localtax2_type == '') // if tax type not defined
&& (!empty($localtax1_rate) || !empty($localtax2_rate))) { // and there is local tax
&& (!empty($localtax1_rate) || !empty($localtax2_rate))) { // and there is local tax
$localtaxtmp_array = getLocalTaxesFromRate($vatrate, 0, $object->thirdparty, $mysoc);
$localtax1_type = isset($localtaxtmp_array[0]) ? $localtaxtmp_array[0] : '';
$localtax2_type = isset($localtaxtmp_array[2]) ? $localtaxtmp_array[2] : '';
@ -806,26 +770,13 @@ class pdf_cyan extends ModelePDFPropales
$this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne);
// if new pages have been added, we need to check whether they were created by the image display script or the description display script
if ($pageposafter > $pageposbefore && !empty($showpricebeforepagebreak)) {
$pdf->setPage($pageposafter);
if ($pageposafter - $pageposbefore > 2 && $newPageAddedForDescription) {
// photo can't add 2 pages, so it must be description who break others pages
$nexY = $posYAfterDescription;
} elseif ($newPageAddedForPhoto && $newPageAddedForDescription) {
// if break page is detect for photo and description we need to take the bigger Y
$nexY = max($posYAfterImage, $posYAfterDescription);
} elseif ($newPageAddedForPhoto) {
$nexY = $posYAfterImage;
} else {
$nexY = $posYAfterDescription;
}
}
$afterPosData = $this->getMaxAfterColsLinePositionsData();
$pdf->setPage($afterPosData['page']);
$nexY = $afterPosData['y'];
// Add line
if (getDolGlobalString('MAIN_PDF_DASH_BETWEEN_LINES') && $i < ($nblines - 1)) {
$pdf->setPage($pageposafter);
if (getDolGlobalString('MAIN_PDF_DASH_BETWEEN_LINES') && $i < ($nblines - 1) && $afterPosData['y'] < $this->page_hauteur - $heightforfooter - 5) {
$pdf->SetLineStyle(array('dash' => '1,1', 'color' => array(80, 80, 80)));
//$pdf->SetDrawColor(190,190,200);
$pdf->line($this->marge_gauche, $nexY + 1, $this->page_largeur - $this->marge_droite, $nexY + 1);
@ -833,54 +784,70 @@ class pdf_cyan extends ModelePDFPropales
}
$nexY += 2; // Add space between lines
}
// Detect if some page were added automatically and output _tableau for past pages
while ($pagenb < $pageposafter) {
$pdf->setPage($pagenb);
if ($pagenb == $pageposbeforeprintlines) {
$this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis);
// Add last page for document footer if there are not enough size left
$afterPosData = $this->getMaxAfterColsLinePositionsData();
if ($afterPosData['y'] > $this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforsignature + $heightforinfotot) ) {
$pdf->AddPage();
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
}
$pagenb++;
$pdf->setPage($pagenb);
}
// Draw table frames and columns borders
$drawTabNumbPage = $pdf->getNumPages();
for ($i=$pageposbeforeprintlines; $i<=$drawTabNumbPage; $i++) {
// reset page orientation each loop to override it if it was changed
$pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
$pdf->setPage($i);
$drawTabHideTop = $hidetop;
$drawTabTop = $tab_top_newpage;
$drawTabBottom = $this->page_hauteur - $heightforfooter;;
$hideBottom = 0; // TODO understand why it change to 1 or 0 during process
if ($i == $pageposbeforeprintlines) {
// first page need to start after notes
$drawTabTop = $tab_top;
} elseif (!$drawTabHideTop) {
if (getDolGlobalInt('MAIN_PDF_ENABLE_COL_HEAD_TITLE_REPEAT')) {
$drawTabTop-= $this->tabTitleHeight;
} else {
$this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code, $outputlangsbis);
}
$this->_pagefoot($pdf, $object, $outputlangs, 1);
$pagenb++;
$pdf->setPage($pagenb);
$pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
$this->_pagehead($pdf, $object, 0, $outputlangs);
}
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
$drawTabHideTop = 1;
}
}
if (isset($object->lines[$i + 1]->pagebreak) && $object->lines[$i + 1]->pagebreak) {
if ($pagenb == $pageposafter) {
$this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis);
} else {
$this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code, $outputlangsbis);
}
$this->_pagefoot($pdf, $object, $outputlangs, 1);
// New page
$pdf->AddPage();
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
}
$pagenb++;
if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
$this->_pagehead($pdf, $object, 0, $outputlangs);
}
// last page need to include document footer
if ($i == $pdf->getNumPages()) {
// remove document footer height to tab bottom position
$drawTabBottom-= $heightforsignature + $heightforfreetext + $heightforinfotot;
}
$drawTabHeight = $drawTabBottom - $drawTabTop;
$this->_tableau($pdf, $drawTabTop, $drawTabHeight, 0, $outputlangs, $drawTabHideTop, $hideBottom, $object->multicurrency_code, $outputlangsbis);
$this->_pagefoot($pdf, $object, $outputlangs, 1);
$pdf->setPage($i); // in case of _pagefoot or _tableau change it
// reset page orientation each loop to override it if it was changed by _pagefoot or _tableau change it
$pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
if (!getDolGlobalInt('MAIN_PDF_DONOTREPEAT_HEAD')) {
$this->_pagehead($pdf, $object, 0, $outputlangs);
}
if (!empty($tplidx)) {
$pdf->useTemplate($tplidx);
}
}
// Show square
if ($pagenb == $pageposbeforeprintlines) {
$this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter, 0, $outputlangs, $hidetop, 0, $object->multicurrency_code, $outputlangsbis);
$bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter + 1;
} else {
$this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter, 0, $outputlangs, 1, 0, $object->multicurrency_code, $outputlangsbis);
$bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter + 1;
}
$pdf->setPage($pdf->getNumPages());
$bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter + 1;
// Display infos area
$posy = $this->drawInfoTable($pdf, $object, $bottomlasttab, $outputlangs);
@ -1105,8 +1072,8 @@ class pdf_cyan extends ModelePDFPropales
if (!getDolGlobalString('PROPOSAL_PDF_HIDE_PAYMENTMODE')) {
// Show payment mode
if ($object->mode_reglement_code
&& $object->mode_reglement_code != 'CHQ'
&& $object->mode_reglement_code != 'VIR') {
&& $object->mode_reglement_code != 'CHQ'
&& $object->mode_reglement_code != 'VIR') {
$pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
$pdf->SetXY($this->marge_gauche, $posy);
$titre = $outputlangs->transnoentities("PaymentMode").':';