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