1*b6f35a76SGreg Roach<?php 2*b6f35a76SGreg Roach 3*b6f35a76SGreg Roach/** 4*b6f35a76SGreg Roach * webtrees: online genealogy 5*b6f35a76SGreg Roach * Copyright (C) 2019 webtrees development team 6*b6f35a76SGreg Roach * This program is free software: you can redistribute it and/or modify 7*b6f35a76SGreg Roach * it under the terms of the GNU General Public License as published by 8*b6f35a76SGreg Roach * the Free Software Foundation, either version 3 of the License, or 9*b6f35a76SGreg Roach * (at your option) any later version. 10*b6f35a76SGreg Roach * This program is distributed in the hope that it will be useful, 11*b6f35a76SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 12*b6f35a76SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13*b6f35a76SGreg Roach * GNU General Public License for more details. 14*b6f35a76SGreg Roach * You should have received a copy of the GNU General Public License 15*b6f35a76SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 16*b6f35a76SGreg Roach */ 17*b6f35a76SGreg Roach 18*b6f35a76SGreg Roachdeclare(strict_types=1); 19*b6f35a76SGreg Roach 20*b6f35a76SGreg Roachnamespace Fisharebest\Webtrees\Report; 21*b6f35a76SGreg Roach 22*b6f35a76SGreg Roachuse function count; 23*b6f35a76SGreg Roachuse function hexdec; 24*b6f35a76SGreg Roachuse function is_array; 25*b6f35a76SGreg Roachuse function is_object; 26*b6f35a76SGreg Roachuse function ksort; 27*b6f35a76SGreg Roachuse function preg_match; 28*b6f35a76SGreg Roachuse function str_replace; 29*b6f35a76SGreg Roachuse function trim; 30*b6f35a76SGreg Roach 31*b6f35a76SGreg Roach/** 32*b6f35a76SGreg Roach * Class ReportPdfTextBox 33*b6f35a76SGreg Roach */ 34*b6f35a76SGreg Roachclass ReportPdfTextBox extends ReportBaseTextbox 35*b6f35a76SGreg Roach{ 36*b6f35a76SGreg Roach /** 37*b6f35a76SGreg Roach * PDF Text Box renderer 38*b6f35a76SGreg Roach * 39*b6f35a76SGreg Roach * @param PdfRenderer $renderer 40*b6f35a76SGreg Roach * 41*b6f35a76SGreg Roach * @return void 42*b6f35a76SGreg Roach */ 43*b6f35a76SGreg Roach public function render($renderer) 44*b6f35a76SGreg Roach { 45*b6f35a76SGreg Roach $newelements = []; 46*b6f35a76SGreg Roach $lastelement = ''; 47*b6f35a76SGreg Roach $footnote_element = []; 48*b6f35a76SGreg Roach // Element counter 49*b6f35a76SGreg Roach $cE = count($this->elements); 50*b6f35a76SGreg Roach //-- collapse duplicate elements 51*b6f35a76SGreg Roach for ($i = 0; $i < $cE; $i++) { 52*b6f35a76SGreg Roach $element = $this->elements[$i]; 53*b6f35a76SGreg Roach if ($element instanceof ReportBaseElement) { 54*b6f35a76SGreg Roach if ($element instanceof ReportBaseText) { 55*b6f35a76SGreg Roach if (!empty($footnote_element)) { 56*b6f35a76SGreg Roach ksort($footnote_element); 57*b6f35a76SGreg Roach foreach ($footnote_element as $links) { 58*b6f35a76SGreg Roach $newelements[] = $links; 59*b6f35a76SGreg Roach } 60*b6f35a76SGreg Roach $footnote_element = []; 61*b6f35a76SGreg Roach } 62*b6f35a76SGreg Roach if (empty($lastelement)) { 63*b6f35a76SGreg Roach $lastelement = $element; 64*b6f35a76SGreg Roach } else { 65*b6f35a76SGreg Roach // Checking if the Text has the same style 66*b6f35a76SGreg Roach if ($element->getStyleName() == $lastelement->getStyleName()) { 67*b6f35a76SGreg Roach $lastelement->addText(str_replace("\n", '<br>', $element->getValue())); 68*b6f35a76SGreg Roach } elseif (!empty($lastelement)) { 69*b6f35a76SGreg Roach $newelements[] = $lastelement; 70*b6f35a76SGreg Roach $lastelement = $element; 71*b6f35a76SGreg Roach } 72*b6f35a76SGreg Roach } 73*b6f35a76SGreg Roach } elseif ($element instanceof ReportPdfFootnote) { 74*b6f35a76SGreg Roach // Check if the Footnote has been set with it’s link number 75*b6f35a76SGreg Roach $renderer->checkFootnote($element); 76*b6f35a76SGreg Roach // Save first the last element if any 77*b6f35a76SGreg Roach if (!empty($lastelement)) { 78*b6f35a76SGreg Roach $newelements[] = $lastelement; 79*b6f35a76SGreg Roach $lastelement = []; 80*b6f35a76SGreg Roach } 81*b6f35a76SGreg Roach // Save the Footnote with it’s link number as key for sorting later 82*b6f35a76SGreg Roach $footnote_element[$element->num] = $element; 83*b6f35a76SGreg Roach } elseif (!($element instanceof ReportPdfFootnote) || trim($element->getValue()) != '') { 84*b6f35a76SGreg Roach // Do not keep empty footnotes 85*b6f35a76SGreg Roach if (!empty($footnote_element)) { 86*b6f35a76SGreg Roach ksort($footnote_element); 87*b6f35a76SGreg Roach foreach ($footnote_element as $links) { 88*b6f35a76SGreg Roach $newelements[] = $links; 89*b6f35a76SGreg Roach } 90*b6f35a76SGreg Roach $footnote_element = []; 91*b6f35a76SGreg Roach } 92*b6f35a76SGreg Roach if (!empty($lastelement)) { 93*b6f35a76SGreg Roach $newelements[] = $lastelement; 94*b6f35a76SGreg Roach $lastelement = []; 95*b6f35a76SGreg Roach } 96*b6f35a76SGreg Roach $newelements[] = $element; 97*b6f35a76SGreg Roach } 98*b6f35a76SGreg Roach } else { 99*b6f35a76SGreg Roach if (!empty($lastelement)) { 100*b6f35a76SGreg Roach $newelements[] = $lastelement; 101*b6f35a76SGreg Roach $lastelement = []; 102*b6f35a76SGreg Roach } 103*b6f35a76SGreg Roach if (!empty($footnote_element)) { 104*b6f35a76SGreg Roach ksort($footnote_element); 105*b6f35a76SGreg Roach foreach ($footnote_element as $links) { 106*b6f35a76SGreg Roach $newelements[] = $links; 107*b6f35a76SGreg Roach } 108*b6f35a76SGreg Roach $footnote_element = []; 109*b6f35a76SGreg Roach } 110*b6f35a76SGreg Roach $newelements[] = $element; 111*b6f35a76SGreg Roach } 112*b6f35a76SGreg Roach } 113*b6f35a76SGreg Roach if (!empty($lastelement)) { 114*b6f35a76SGreg Roach $newelements[] = $lastelement; 115*b6f35a76SGreg Roach } 116*b6f35a76SGreg Roach if (!empty($footnote_element)) { 117*b6f35a76SGreg Roach ksort($footnote_element); 118*b6f35a76SGreg Roach foreach ($footnote_element as $links) { 119*b6f35a76SGreg Roach $newelements[] = $links; 120*b6f35a76SGreg Roach } 121*b6f35a76SGreg Roach } 122*b6f35a76SGreg Roach $this->elements = $newelements; 123*b6f35a76SGreg Roach unset($footnote_element, $lastelement, $links, $newelements); 124*b6f35a76SGreg Roach 125*b6f35a76SGreg Roach // Used with line breaks and cell height calculation within this box 126*b6f35a76SGreg Roach $renderer->largestFontHeight = 0; 127*b6f35a76SGreg Roach 128*b6f35a76SGreg Roach // If current position (left) 129*b6f35a76SGreg Roach if ($this->left === ReportBaseElement::CURRENT_POSITION) { 130*b6f35a76SGreg Roach $cX = $renderer->tcpdf->GetX(); 131*b6f35a76SGreg Roach } else { 132*b6f35a76SGreg Roach // For static position add margin (returns and updates X) 133*b6f35a76SGreg Roach $cX = $renderer->addMarginX($this->left); 134*b6f35a76SGreg Roach } 135*b6f35a76SGreg Roach 136*b6f35a76SGreg Roach // If current position (top) 137*b6f35a76SGreg Roach if ($this->top === ReportBaseElement::CURRENT_POSITION) { 138*b6f35a76SGreg Roach $cY = $renderer->tcpdf->GetY(); 139*b6f35a76SGreg Roach } else { 140*b6f35a76SGreg Roach $cY = $this->top; 141*b6f35a76SGreg Roach $renderer->tcpdf->SetY($cY); 142*b6f35a76SGreg Roach } 143*b6f35a76SGreg Roach 144*b6f35a76SGreg Roach // Check the width if set to page wide OR set by xml to larger then page width (margin) 145*b6f35a76SGreg Roach if ($this->width == 0 || $this->width > $renderer->getRemainingWidthPDF()) { 146*b6f35a76SGreg Roach $cW = $renderer->getRemainingWidthPDF(); 147*b6f35a76SGreg Roach } else { 148*b6f35a76SGreg Roach $cW = $this->width; 149*b6f35a76SGreg Roach } 150*b6f35a76SGreg Roach 151*b6f35a76SGreg Roach // Save the original margins 152*b6f35a76SGreg Roach $cM = $renderer->tcpdf->getMargins(); 153*b6f35a76SGreg Roach // Use cell padding to wrap the width 154*b6f35a76SGreg Roach // Temp Width with cell padding 155*b6f35a76SGreg Roach if (is_array($cM['cell'])) { 156*b6f35a76SGreg Roach $cWT = $cW - ($cM['padding_left'] + $cM['padding_right']); 157*b6f35a76SGreg Roach } else { 158*b6f35a76SGreg Roach $cWT = $cW - ($cM['cell'] * 2); 159*b6f35a76SGreg Roach } 160*b6f35a76SGreg Roach // Element height (exept text) 161*b6f35a76SGreg Roach $eH = 0; 162*b6f35a76SGreg Roach $w = 0; 163*b6f35a76SGreg Roach // Temp Height 164*b6f35a76SGreg Roach $cHT = 0; 165*b6f35a76SGreg Roach //-- $lw is an array 166*b6f35a76SGreg Roach // 0 => last line width 167*b6f35a76SGreg Roach // 1 => 1 if text was wrapped, 0 if text did not wrap 168*b6f35a76SGreg Roach // 2 => number of LF 169*b6f35a76SGreg Roach $lw = []; 170*b6f35a76SGreg Roach // Element counter 171*b6f35a76SGreg Roach $cE = count($this->elements); 172*b6f35a76SGreg Roach //-- calculate the text box height + width 173*b6f35a76SGreg Roach for ($i = 0; $i < $cE; $i++) { 174*b6f35a76SGreg Roach if (is_object($this->elements[$i])) { 175*b6f35a76SGreg Roach $ew = $this->elements[$i]->setWrapWidth($cWT - $w, $cWT); 176*b6f35a76SGreg Roach if ($ew == $cWT) { 177*b6f35a76SGreg Roach $w = 0; 178*b6f35a76SGreg Roach } 179*b6f35a76SGreg Roach $lw = $this->elements[$i]->getWidth($renderer); 180*b6f35a76SGreg Roach // Text is already gets the # LF 181*b6f35a76SGreg Roach $cHT += $lw[2]; 182*b6f35a76SGreg Roach if ($lw[1] == 1) { 183*b6f35a76SGreg Roach $w = $lw[0]; 184*b6f35a76SGreg Roach } elseif ($lw[1] == 2) { 185*b6f35a76SGreg Roach $w = 0; 186*b6f35a76SGreg Roach } else { 187*b6f35a76SGreg Roach $w += $lw[0]; 188*b6f35a76SGreg Roach } 189*b6f35a76SGreg Roach if ($w > $cWT) { 190*b6f35a76SGreg Roach $w = $lw[0]; 191*b6f35a76SGreg Roach } 192*b6f35a76SGreg Roach // Footnote is at the bottom of the page. No need to calculate it’s height or wrap the text! 193*b6f35a76SGreg Roach // We are changing the margins anyway! 194*b6f35a76SGreg Roach // For anything else but text (images), get the height 195*b6f35a76SGreg Roach $eH += $this->elements[$i]->getHeight($renderer); 196*b6f35a76SGreg Roach } 197*b6f35a76SGreg Roach } 198*b6f35a76SGreg Roach 199*b6f35a76SGreg Roach // Add up what’s the final height 200*b6f35a76SGreg Roach $cH = $this->height; 201*b6f35a76SGreg Roach // If any element exist 202*b6f35a76SGreg Roach if ($cE > 0) { 203*b6f35a76SGreg Roach // Check if this is text or some other element, like images 204*b6f35a76SGreg Roach if ($eH == 0) { 205*b6f35a76SGreg Roach // This is text elements. Number of LF but at least one line 206*b6f35a76SGreg Roach $cHT = ($cHT + 1) * $renderer->tcpdf->getCellHeightRatio(); 207*b6f35a76SGreg Roach // Calculate the cell hight with the largest font size used within this Box 208*b6f35a76SGreg Roach $cHT *= $renderer->largestFontHeight; 209*b6f35a76SGreg Roach // Add cell padding 210*b6f35a76SGreg Roach if ($this->padding) { 211*b6f35a76SGreg Roach if (is_array($cM['cell'])) { 212*b6f35a76SGreg Roach $cHT += ($cM['padding_bottom'] + $cM['padding_top']); 213*b6f35a76SGreg Roach } else { 214*b6f35a76SGreg Roach $cHT += ($cM['cell'] * 2); 215*b6f35a76SGreg Roach } 216*b6f35a76SGreg Roach } 217*b6f35a76SGreg Roach if ($cH < $cHT) { 218*b6f35a76SGreg Roach $cH = $cHT; 219*b6f35a76SGreg Roach } 220*b6f35a76SGreg Roach } elseif ($cH < $eH) { 221*b6f35a76SGreg Roach // This is any other element 222*b6f35a76SGreg Roach $cH = $eH; 223*b6f35a76SGreg Roach } 224*b6f35a76SGreg Roach } 225*b6f35a76SGreg Roach // Finaly, check the last cells height 226*b6f35a76SGreg Roach if ($cH < $renderer->lastCellHeight) { 227*b6f35a76SGreg Roach $cH = $renderer->lastCellHeight; 228*b6f35a76SGreg Roach } 229*b6f35a76SGreg Roach // Add a new page if needed 230*b6f35a76SGreg Roach if ($this->pagecheck) { 231*b6f35a76SGreg Roach // Reset last cell height or Header/Footer will inherit it, in case of pagebreak 232*b6f35a76SGreg Roach $renderer->lastCellHeight = 0; 233*b6f35a76SGreg Roach if ($renderer->checkPageBreakPDF($cH)) { 234*b6f35a76SGreg Roach $cY = $renderer->tcpdf->GetY(); 235*b6f35a76SGreg Roach } 236*b6f35a76SGreg Roach } 237*b6f35a76SGreg Roach 238*b6f35a76SGreg Roach // Setup the border and background color 239*b6f35a76SGreg Roach $cS = ''; // Class Style 240*b6f35a76SGreg Roach if ($this->border) { 241*b6f35a76SGreg Roach $cS = 'D'; 242*b6f35a76SGreg Roach } // D or empty string: Draw (default) 243*b6f35a76SGreg Roach $match = []; 244*b6f35a76SGreg Roach // Fill the background 245*b6f35a76SGreg Roach if ($this->fill) { 246*b6f35a76SGreg Roach if (preg_match('/#?(..)(..)(..)/', $this->bgcolor, $match)) { 247*b6f35a76SGreg Roach $cS .= 'F'; // F: Fill the background 248*b6f35a76SGreg Roach $r = hexdec($match[1]); 249*b6f35a76SGreg Roach $g = hexdec($match[2]); 250*b6f35a76SGreg Roach $b = hexdec($match[3]); 251*b6f35a76SGreg Roach $renderer->tcpdf->SetFillColor($r, $g, $b); 252*b6f35a76SGreg Roach } 253*b6f35a76SGreg Roach } 254*b6f35a76SGreg Roach // Clean up a bit 255*b6f35a76SGreg Roach unset($lw, $w, $match, $cE, $eH); 256*b6f35a76SGreg Roach // Draw the border 257*b6f35a76SGreg Roach if (!empty($cS)) { 258*b6f35a76SGreg Roach if (!$renderer->tcpdf->getRTL()) { 259*b6f35a76SGreg Roach $cXM = $cX; 260*b6f35a76SGreg Roach } else { 261*b6f35a76SGreg Roach $cXM = $renderer->tcpdf->getPageWidth() - $cX - $cW; 262*b6f35a76SGreg Roach } 263*b6f35a76SGreg Roach $renderer->tcpdf->Rect($cXM, $cY, $cW, $cH, $cS); 264*b6f35a76SGreg Roach } 265*b6f35a76SGreg Roach // Add cell padding if set and if any text (element) exist 266*b6f35a76SGreg Roach if ($this->padding) { 267*b6f35a76SGreg Roach if ($cHT > 0) { 268*b6f35a76SGreg Roach if (is_array($cM['cell'])) { 269*b6f35a76SGreg Roach $renderer->tcpdf->SetY($cY + $cM['padding_top']); 270*b6f35a76SGreg Roach } else { 271*b6f35a76SGreg Roach $renderer->tcpdf->SetY($cY + $cM['cell']); 272*b6f35a76SGreg Roach } 273*b6f35a76SGreg Roach } 274*b6f35a76SGreg Roach } 275*b6f35a76SGreg Roach // Change the margins X, Width 276*b6f35a76SGreg Roach if (!$renderer->tcpdf->getRTL()) { 277*b6f35a76SGreg Roach if ($this->padding) { 278*b6f35a76SGreg Roach if (is_array($cM['cell'])) { 279*b6f35a76SGreg Roach $renderer->tcpdf->SetLeftMargin($cX + $cM['padding_left']); 280*b6f35a76SGreg Roach } else { 281*b6f35a76SGreg Roach $renderer->tcpdf->SetLeftMargin($cX + $cM['cell']); 282*b6f35a76SGreg Roach } 283*b6f35a76SGreg Roach $renderer->tcpdf->SetRightMargin($renderer->getRemainingWidthPDF() - $cW + $cM['right']); 284*b6f35a76SGreg Roach } else { 285*b6f35a76SGreg Roach $renderer->tcpdf->SetLeftMargin($cX); 286*b6f35a76SGreg Roach $renderer->tcpdf->SetRightMargin($renderer->getRemainingWidthPDF() - $cW + $cM['right']); 287*b6f35a76SGreg Roach } 288*b6f35a76SGreg Roach } else { 289*b6f35a76SGreg Roach if ($this->padding) { 290*b6f35a76SGreg Roach if (is_array($cM['cell'])) { 291*b6f35a76SGreg Roach $renderer->tcpdf->SetRightMargin($cX + $cM['padding_right']); 292*b6f35a76SGreg Roach } else { 293*b6f35a76SGreg Roach $renderer->tcpdf->SetRightMargin($cX + $cM['cell']); 294*b6f35a76SGreg Roach } 295*b6f35a76SGreg Roach $renderer->tcpdf->SetLeftMargin($renderer->getRemainingWidthPDF() - $cW + $cM['left']); 296*b6f35a76SGreg Roach } else { 297*b6f35a76SGreg Roach $renderer->tcpdf->SetRightMargin($cX); 298*b6f35a76SGreg Roach $renderer->tcpdf->SetLeftMargin($renderer->getRemainingWidthPDF() - $cW + $cM['left']); 299*b6f35a76SGreg Roach } 300*b6f35a76SGreg Roach } 301*b6f35a76SGreg Roach // Save the current page number 302*b6f35a76SGreg Roach $cPN = $renderer->tcpdf->getPage(); 303*b6f35a76SGreg Roach 304*b6f35a76SGreg Roach // Render the elements (write text, print picture...) 305*b6f35a76SGreg Roach foreach ($this->elements as $element) { 306*b6f35a76SGreg Roach if ($element instanceof ReportBaseElement) { 307*b6f35a76SGreg Roach $element->render($renderer); 308*b6f35a76SGreg Roach } elseif ($element === 'footnotetexts') { 309*b6f35a76SGreg Roach $renderer->footnotes(); 310*b6f35a76SGreg Roach } elseif ($element === 'addpage') { 311*b6f35a76SGreg Roach $renderer->newPage(); 312*b6f35a76SGreg Roach } 313*b6f35a76SGreg Roach } 314*b6f35a76SGreg Roach // Restore the margins 315*b6f35a76SGreg Roach $renderer->tcpdf->SetLeftMargin($cM['left']); 316*b6f35a76SGreg Roach $renderer->tcpdf->SetRightMargin($cM['right']); 317*b6f35a76SGreg Roach 318*b6f35a76SGreg Roach // This will be mostly used to trick the multiple images last height 319*b6f35a76SGreg Roach if ($this->reseth) { 320*b6f35a76SGreg Roach $cH = 0; 321*b6f35a76SGreg Roach // This can only happen with multiple images and with pagebreak 322*b6f35a76SGreg Roach if ($cPN != $renderer->tcpdf->getPage()) { 323*b6f35a76SGreg Roach $renderer->tcpdf->setPage($cPN); 324*b6f35a76SGreg Roach } 325*b6f35a76SGreg Roach } 326*b6f35a76SGreg Roach // New line and some clean up 327*b6f35a76SGreg Roach if (!$this->newline) { 328*b6f35a76SGreg Roach $renderer->tcpdf->SetXY($cX + $cW, $cY); 329*b6f35a76SGreg Roach $renderer->lastCellHeight = $cH; 330*b6f35a76SGreg Roach } else { 331*b6f35a76SGreg Roach // addMarginX() also updates X 332*b6f35a76SGreg Roach $renderer->addMarginX(0); 333*b6f35a76SGreg Roach $renderer->tcpdf->SetY($cY + $cH); 334*b6f35a76SGreg Roach $renderer->lastCellHeight = 0; 335*b6f35a76SGreg Roach } 336*b6f35a76SGreg Roach } 337*b6f35a76SGreg Roach} 338