Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ezTable breaks PDF for Adobe Reader when inside of openObject & closeObject #150

Open
oweuxllc opened this issue Feb 19, 2021 · 4 comments

Comments

@oweuxllc
Copy link

oweuxllc commented Feb 19, 2021

Hello, and thank you in advance,

We have encountered a situation in which we want a PDF to contain the same biographical details like name, surname and transaction number at the top of all pages of our PDFs. We would like to display this information by using ezTable.

Using code similar to the one below we are able to put our "repetitive" content so that it is added each time we call ezNewPage.

(...)
$all = $pdf->openObject();
$pdf->saveState();
$pdf->setStrokeColor(0, 0, 0, 1);
$pdf->addText(25, 180, 18, "SAMPLE TEXT");
$pdf->addJpegFromFile($path_to_JPEG,$x ,$y, $w, $h);
$pdf->restoreState();
$pdf->closeObject();
$pdf->addObject($all, 'all');
(...)

The sample code above is able to open on browsers like Chrome and on Adobe Reader. Please note that we are currently using the latest available version: 12.63 and running PHP 7.2.

So, under rospdf v12.63 and PHP 7.2 this generates a file that opens on Chrome but not Reader. Reader displays an error message and is unable to open the PDF. After cleaning up the file and removing everything that we could we ended up with a very simplified file.

(...)
$all = $pdf->openObject();
$pdf->saveState();
$pdf->setStrokeColor(0, 0, 0, 1);
$pdf->addText(25, 180, 18, "ALL SALES ARE FINAL");
$pdf->addJpegFromFile($path_to_JPEG,$x ,$y, $w, $h);

$tableArray = array(
array(
'name' => "Jane",
'surname' => "Doe",
'transaction_number' => "159263"
),
);

$pdf->ezTable(
$tableArray,
array(
'name' => "",
'surname' => "",
'transaction_number' => ""
),
'',
array()
);

$pdf->restoreState();
$pdf->closeObject();
$pdf->addObject($all, 'all');
(...)

As far as we understand it our previous code ran OK with 12rc12 on PHP 7.0. No one ever complained about not being able to use adobe reader.

When we remove all additional code and only leave the bare minimum the PDF does open on Adobe Reader, but the first page is blank. Missing are all images and even the page numbering. All of the "repetitive" text appears on page 2 and beyond. All visible on Chrome.

Seems like using ezTable like we want is no longer working.

We are able to get around the issue by drawing lines to simulate the table borders or adding the table each time a new page is created.

Updating to a newer PHP version is not currently possible. But we can plan for it in the future is you believe the issue could be related to that.

@tripulsTPfeffer
Copy link

We can confirm this issue AAAAND we can confirm that downgrading to the lowest possible version (0.12.20) fixes the issues with ezTable function. We will analyze now which version breaks this functionality and write it down into that issue. So today we will know which version is the latest working version ;-)

@tripulsTPfeffer
Copy link

To go further: The error of the pdf is:
"A graphics state stack underflow occurred.

The document does not conform to the requested standard.

The document doesn't conform to the PDF reference (missing required entries, wrong value types, etc.).

The document does not conform to the PDF 1.3 standard."

@tripulsTPfeffer
Copy link

Last working version: 0.12.26
Since 0.12.27 it is broken

@tripulsTPfeffer
Copy link

tripulsTPfeffer commented Mar 6, 2024

We got a working version using the ezTable() code from 0.12.26 and merge individual blocks from 0.12.67 inside. Here is the result, but beware that this is just a workaround for our specific pdf handling. it is possible that another ezTable configuration could cause breakdowns again.

public function ezTable(&$data, $cols = '', $title = '', $options = '')
    {
        if (!is_array($data)) {
            return;
        }

        if (!is_array($cols)) {
            // take the columns from the first row of the data set
            $first = array_slice($data, 0, 1);
            $first = array_keys(array_shift($first));
            if (!is_array($first)) {
                return;
            }
            $cols = array_combine($first, $first);
        }

        if (!is_array($options)) {
            $options = [];
        }

        $defaults = array(
            /* shading */
            'shaded' => 1, 'shadeCol' => [0.8, 0.8, 0.8], 'shadeCol2' => [0.7, 0.7, 0.7], 'shadeHeadingCol' => [],
            /* font */
            'fontSize' => 10, 'titleFontSize' => 12, 'textCol' => [0, 0, 0],
            /* border */
            'gridlines' => EZ_GRIDLINE_DEFAULT, 'lineCol' => [0, 0, 0], 'innerLineThickness' => 1, 'outerLineThickness' => 1,
            /* position, size and padding */
            'width' => 0, 'maxWidth' => 0, 'titleGap' => 5, 'gap' => 5, 'xPos' => 'centre', 'xOrientation' => 'centre',
            'minRowSpace' => -100, 'rowGap' => 2, 'colGap' => 5, 'splitRows' => 0, 'protectRows' => 1, 'nextPageY' => 0,
            /* other */
            'showHeadings' => 1, 'cols' => [], 'evenColumns' => 0, 'evenColumnsMin' => 20
        );

        foreach ($defaults as $key => $value) {
            if (!isset($options[$key])) {
                $options[$key] = $value;
            } elseif (is_array($value) && !is_array($options[$key])) {
                $options[$key] = $value;
            }
        }

        // @deprecated Compatibility with 'showLines' option
        if (isset($options['showLines'])) {
            switch ($options['showLines']) {
                case 0:
                    $options['gridlines'] = 0;
                    break;
                case 1:
                    $options['gridlines'] = EZ_GRIDLINE_DEFAULT;
                    break;
                case 2:
                    $options['gridlines'] = EZ_GRIDLINE_HEADERONLY + EZ_GRIDLINE_ROWS;
                    break;
                case 3:
                    $options['gridlines'] = EZ_GRIDLINE_ROWS;
                    break;
                case 4:
                    $options['gridlines'] = EZ_GRIDLINE_HEADERONLY;
                    break;
                default:
                    $options['gridlines'] = EZ_GRIDLINE_TABLE + EZ_GRIDLINE_HEADERONLY + EZ_GRIDLINE_COLUMNS;
            }
            unset($options['showLines']);
        }

        $options['gap'] = 2 * $options['colGap'];
        // Use Y Position of Current Page position in Table
        if ($options['nextPageY']) {
            $nextPageY = $this->y;
        }

        $middle = ($this->ez['pageWidth'] - $this->ez['rightMargin']) / 2 + ($this->ez['leftMargin']) / 2;

        if (!$this->numFonts) {
            $this->selectFont('Helvetica');
        }

        // figure out the maximum widths of the text within each column
        $maxWidth = [];
        foreach ($cols as $colName => $colTitle) {
            if (empty($colTitle)) {
                $maxWidth[$colName] = 0;
            }
            $w = $this->ezGetTextWidth($options['fontSize'], (string) $colTitle) * 1.01;
            $maxWidth[$colName] = $w;
        }
        // find the maximum cell widths based on the data
        foreach ($data as $row) {
            foreach ($cols as $colName => $colHeading) {
                // BUGFIX #16 ignore empty columns | thanks jafjaf
                if (empty($row[$colName])) {
                    continue;
                }
                $w = $this->ezGetTextWidth($options['fontSize'], (string) $row[$colName]) * 1.01;
                if ($w > $maxWidth[$colName]) {
                    $maxWidth[$colName] = $w;
                }
            }
        }

        $minFontWidth = intval($this->fonts[$this->currentFont]['FontBBox'][2] / 1000 * $options['fontSize']);

        // calculate the start positions of each of the columns
        $pos = [];
        $x = 0;
        $t = $x;
        $adjustmentWidth = 0;
        $setWidth = 0;
        foreach ($maxWidth as $colName => $w) {
            $pos[$colName] = $t;
            // if the column width has been specified then set that here, also total the
            // width avaliable for adjustment
            if (isset($options['cols'][$colName]) && isset($options['cols'][$colName]['width']) && $options['cols'][$colName]['width'] > 0) {
                $t = $t + $options['cols'][$colName]['width'];
                $maxWidth[$colName] = $options['cols'][$colName]['width'] - $options['gap'];
                if ($maxWidth[$colName] < $minFontWidth) {
                    $maxWidth[$colName] = $minFontWidth;
                }
                $setWidth += $options['cols'][$colName]['width'];
            } else {
                $t = $t + $w + $options['gap'];
                $adjustmentWidth += $w;
                $setWidth += $options['gap'];
            }
        }
        $pos['_end_'] = $t;

        // we need to cache the first version of the calculated columns
        $cachepos = $pos;

        if ($options['maxWidth'] == 0) {
            $options['maxWidth'] = $this->ez['pageWidth'] - ($this->ez['rightMargin'] + $this->ez['leftMargin']);
        }
        // if maxWidth is specified, and the table is too wide, and the width has not been set,
        // then set the width.
        if ($options['width'] == 0 && $options['maxWidth'] && ($t - $x) > $options['maxWidth']) {
            // then need to make this one smaller
            $options['width'] = $options['maxWidth'];
        }

        if ($options['width'] && $adjustmentWidth > 0 && $setWidth < $options['width']) {
            // first find the current widths of the columns involved in this mystery
            $cols0 = [];
            $cols1 = [];
            $xq = 0;
            $presentWidth = 0;
            $last = '';
            foreach ($pos as $colName => $p) {
                if (!isset($options['cols'][$last]) || !isset($options['cols'][$last]['width']) || $options['cols'][$last]['width'] <= 0) {
                    if (strlen($last)) {
                        $cols0[$last] = $p - $xq - $options['gap'];
                        $presentWidth += ($p - $xq - $options['gap']);
                    }
                } else {
                    $cols1[$last] = $p - $xq;
                }
                $last = $colName;
                $xq = $p;
            }
            // $cols0 contains the widths of all the columns which are not set
            $neededWidth = $options['width'] - $setWidth;
            // if needed width is negative then add it equally to each column, else get more tricky
            if ($presentWidth < $neededWidth) {
                foreach ($cols0 as $colName => $w) {
                    $cols0[$colName] += ($neededWidth - $presentWidth) / count($cols0);
                }
            } else {
                $cnt = 0;
                while ($presentWidth > $neededWidth && $cnt < 100) {
                    ++$cnt; // insurance policy
                    // find the widest columns, and the next to widest width
                    $aWidest = [];
                    $nWidest = 0;
                    $widest = 0;
                    foreach ($cols0 as $colName => $w) {
                        if ($w > $widest) {
                            $aWidest = [$colName];
                            $nWidest = $widest;
                            $widest = $w;
                        } elseif ($w == $widest) {
                            $aWidest[] = $colName;
                        }
                    }
                    // then figure out what the width of the widest columns would have to be to take up all the slack
                    $newWidestWidth = $widest - ($presentWidth - $neededWidth) / count($aWidest);
                    if ($newWidestWidth > $nWidest) {
                        // then there is space to set them to this
                        foreach ($aWidest as $colName) {
                            $cols0[$colName] = $newWidestWidth;
                        }
                        $presentWidth = $neededWidth;
                    } else {
                        // there is not space, reduce the size of the widest ones down to the next size down, and we
                        // will go round again
                        foreach ($aWidest as $colName) {
                            $cols0[$colName] = $nWidest;
                        }
                        $presentWidth = $presentWidth - ($widest - $nWidest) * count($aWidest);
                    }
                }
            }
            // $cols0 now contains the new widths of the constrained columns.
            // now need to update the $pos and $maxWidth arrays
            $xq = 0;
            foreach ($pos as $colName => $p) {
                $pos[$colName] = $xq;
                if (!isset($options['cols'][$colName]) || !isset($options['cols'][$colName]['width']) || $options['cols'][$colName]['width'] <= 0) {
                    if (isset($cols0[$colName])) {
                        $xq += $cols0[$colName] + $options['gap'];
                        $maxWidth[$colName] = $cols0[$colName];
                    }
                } else {
                    if (isset($cols1[$colName])) {
                        $xq += $cols1[$colName];
                    }
                }
            }

            $t = $x + $options['width'];
            $pos['_end_'] = $t;
        }

        // if the option is set to 2 and one of the columns is too narrow we need to look at recalculating the columns
        if ($options['evenColumns'] == 2) {
            $posVals = [];
            foreach ($pos as $w) {
                array_unshift($posVals, $w);
            }
            $narrowestCol = 9999;
            $last = array_pop($posVals);
            while (sizeof($posVals)) {
                $current = array_pop($posVals);
                $currentWidth = $current - $last;
                if ($narrowestCol > $currentWidth) {
                    $narrowestCol = $currentWidth;
                }
                $last = $current;
            }
            if ($narrowestCol < $options['evenColumnsMin']) {
                $options['evenColumns'] = 1;
            }
        }

        // if the option is turned on we need to look at recalculating the columns
        if ($options['evenColumns'] == 1) {
            // what is the maximum width? it is either specified or the page width between the margins
            $redistribution = $options['maxWidth'];

            // what are the manually specified column widths?
            // what is the narrowest auto column? (columns with a specifically defined width are ignored)
            $manualWidth = 0;
            $manualCount = 0;
            $narrowest = 999999999;
            foreach ($options['cols'] as $colName => $col) {
                if (isset($col['width'])) { // was the width of this column specified?
                    ++$manualCount;
                    $manualWidth += $col['width'] * 1;
                } elseif ($narrowest > $maxWidth[$colName]) {
                    $narrowest = $maxWidth[$colName];
                }
            }
            // the total width to be redistributed
            $redistributedWidth = ($redistribution - $manualWidth) / (sizeof($pos) - 1 - $manualCount);
            // recalculate the x positions of the columnn
            $new = 0;
            foreach ($pos as $key => $old) {
                $pos[$key] = $new;
                if (isset($options['cols'][$key]['width'])) {
                    $new += $options['cols'][$key]['width'];
                } else {
                    $new += $redistributedWidth;
                }
            }
            // recalculate the column widths
            $last = -1;
            $newWidth = [];
            foreach ($pos as $key => $val) {
                if ($last >= 0) {
                    $newWidth[$lastKey] = ($val - $last) - $options['gap'];
                }
                $last = $val;
                $lastKey = $key;
            }
            $maxWidth = $newWidth;
            $t = array_sum($maxWidth) + (sizeof($maxWidth) * 2 * $options['colGap']);
        }

        switch ($options['xPos']) {
            case 'left':
                $xref = $this->ez['leftMargin'];
                break;
            case 'right':
                $xref = $this->ez['pageWidth'] - $this->ez['rightMargin'];
                break;
            case 'centre':
            case 'center':
                $xref = $middle;
                break;
            default:
                $xref = $options['xPos'];
                break;
        }
        switch ($options['xOrientation']) {
            case 'left':
                $dx = $xref - $t;
                break;
            case 'right':
                $dx = $xref;
                break;
            case 'centre':
            case 'center':
                $dx = $xref - $t / 2;
                break;
        }
        // applied patch #18 alignment fixes for tables and images | thank you Emil Totev
        $dx += $options['colGap'];

        foreach ($pos as $k => $v) {
            $pos[$k] = $v + $dx;
        }
        $x0 = $x + $dx;
        $x1 = $t + $dx;

        $baseLeftMargin = $this->ez['leftMargin'];
        $basePos = $pos;
        $baseX0 = $x0;
        $baseX1 = $x1;

        $middle = ($x1 + $x0) / 2;

        // start a transaction which will be used to regress the table, if there are not enough rows protected
        if ($options['protectRows'] > 0) {
            $this->transaction('start');
            $movedOnce = 0;
        }
        $abortTable = 1;
        while ($abortTable) {
            $abortTable = 0;
            $dm = $this->ez['leftMargin'] - $baseLeftMargin;
            foreach ($basePos as $k => $v) {
                $pos[$k] = $v + $dm;
            }
            $x0 = $baseX0 + $dm;
            $x1 = $baseX1 + $dm;
            $middle = ($x1 + $x0) / 2;

            // if the title is set, then do that
            if (strlen($title)) {
                $w = $this->getTextWidth($options['titleFontSize'], $title);
                $this->y -= $this->getFontHeight($options['titleFontSize']);
                if ($this->y < $this->ez['bottomMargin']) {
                    $this->ezNewPage();
                    // margins may have changed on the newpage
                    $dm = $this->ez['leftMargin'] - $baseLeftMargin;
                    foreach ($basePos as $k => $v) {
                        $pos[$k] = $v + $dm;
                    }
                    $x0 = $baseX0 + $dm;
                    $x1 = $baseX1 + $dm;
                    $middle = ($x1 + $x0) / 2;
                    $this->y -= $this->getFontHeight($options['titleFontSize']);
                }
                $this->addText($middle - $w / 2, $this->y, $options['titleFontSize'], $title);
                $this->y -= $options['titleGap'];
            }
            // margins may have changed on the newpage
            $dm = $this->ez['leftMargin'] - $baseLeftMargin;
            foreach ($basePos as $k => $v) {
                $pos[$k] = $v + $dm;
            }
            $x0 = $baseX0 + $dm;
            $x1 = $baseX1 + $dm;

            $y = $this->y; // to simplify the code a bit

            // make the table
            $height = $this->getFontHeight($options['fontSize']);
            $descender = $this->getFontDescender($options['fontSize']);

            $y0 = $y - $options['rowGap'];
            $dy = 0;
            if ($options['showHeadings']) {
                // patch #9 start
                if (isset($options['shadeHeadingCol']) && count($options['shadeHeadingCol']) == 3) {
                    $this->saveState();
                    $textHeadingsObjectId = $this->openObject();
                    $this->closeObject();
                    $this->addObject($textHeadingsObjectId);
                    $this->reopenObject($textHeadingsObjectId);
                }
                // patch #9 end
                // this function will move the start of the table to a new page if it does not fit on this one
                $headingHeight = $this->ezTableColumnHeadings($cols, $pos, $maxWidth, $height, $descender, $options['rowGap'], $options['fontSize'], $y, $options);
                $y0 = $y + $headingHeight + $options['rowGap'];
                $y1 = $y - $options['rowGap'] * 2;

                $dm = $this->ez['leftMargin'] - $baseLeftMargin;
                foreach ($basePos as $k => $v) {
                    $pos[$k] = $v + $dm;
                }
                $x0 = $baseX0 + $dm;
                $x1 = $baseX1 + $dm;
                // patch #9 start
                if (isset($options['shadeHeadingCol']) && count($options['shadeHeadingCol']) == 3) {
                    $this->closeObject();
                    $this->setColor($options['shadeHeadingCol'][0], $options['shadeHeadingCol'][1], $options['shadeHeadingCol'][2], 1);
                    $this->filledRectangle($x0 - $options['gap'] / 2, $y + $descender, $x1 - $x0, ($y0 - $y - $descender));
                    $this->reopenObject($textHeadingsObjectId);
                    $this->closeObject();
                    $this->restoreState();
                }
                // patch #9 end
            } else {
                $y1 = $y0 + ($options['rowGap'] / 2);
            }
            $firstLine = 1;

            // open an object here so that the text can be put in over the shading
            if ($options['shaded'] || $options['showBgCol']) {
                $this->saveState();

                if (!$this->IsObjectOpened()) {
                    $textObjectId = $this->openObject();
                    $this->closeObject();
                    $this->addObject($textObjectId);
                    $this->reopenObject($textObjectId);
                }
            }

            $cnt = 0;
            $newPage = 0;
            foreach ($data as $row) {
                $rowColShading = [];
                foreach ($cols as $colName => $colHeading) {
                    // grab the defined colors for this cell
                    if (isset($row[$colName.'Fill'])) {
                        $fillColor = $row[$colName.'Fill'];
                    } else {
                        $fillColor = '';
                    }

                    $rowX = $pos[$colName] - ($options['gap'] / 2);
                    $rowY = $y + $descender + $height; // BUGGY
                    $rowW = $maxWidth[$colName] + ($options['colGap'] * 2);

                    // decide which color to use!
                    // specified for the cell is first choice
                    if ($fillColor && count($fillColor) && is_array($fillColor)) {
                        $rowColShading[] = ['x' => $rowX, 'y' => $rowY, 'width' => $rowW, 'color' => $fillColor];
                    } elseif (isset($options['cols']) && isset($options['cols'][$colName]) && isset($options['cols'][$colName]['bgcolor']) && is_array($options['cols'][$colName]['bgcolor'])) {
                        $rowColShading[] = ['x' => $rowX, 'y' => $rowY, 'width' => $rowW, 'color' => $options['cols'][$colName]['bgcolor']];
                    } elseif ($options['shaded'] == 1 && $cnt % 2 == 1) {
                        $rowColShading[] = ['x' => $rowX, 'y' => $rowY, 'width' => $rowW, 'color' => $options['shadeCol']];
                    } elseif (($options['shaded'] == 2) && $cnt % 2 == 0) {
                        $rowColShading[] = ['x' => $rowX, 'y' => $rowY, 'width' => $rowW, 'color' => $options['shadeCol']];
                    } elseif (($options['shaded'] == 2) && $cnt % 2 == 1) {
                        $rowColShading[] = ['x' => $rowX, 'y' => $rowY, 'width' => $rowW, 'color' => $options['shadeCol2']];
                    } else {
                        $rowColShading[] = ['color' => []];
                    }
                }

                ++$cnt;
                // the transaction support will be used to prevent rows being split
                if ($options['splitRows'] == 0) {
                    $pageStart = $this->ezPageCount;
                    if (isset($this->ez['columns']) && $this->ez['columns']['on'] == 1) {
                        $columnStart = $this->ez['columns']['colNum'];
                    }
                    $this->transaction('start');
                    $row_orig = $row;
                    $y_orig = $y;
                    $y0_orig = $y0;
                    $y1_orig = $y1;
                }
                $ok = 0;
                $secondTurn = 0;
                while (!$abortTable && $ok == 0) {
                    $mx = 0;
                    $newRow = 1;
                    while (!$abortTable && ($newPage || $newRow)) {
                        $y -= $height;
                        if ($newPage || $y < $this->ez['bottomMargin'] || (isset($options['minRowSpace']) && $y < ($this->ez['bottomMargin'] + $options['minRowSpace']))) {
                            // check that enough rows are with the heading
                            if ($options['protectRows'] > 0 && $movedOnce == 0 && $cnt <= $options['protectRows']) {
                                // then we need to move the whole table onto the next page
                                $movedOnce = 1;
                                $abortTable = 1;
                            }

                            $y2 = $y - $mx + 2 * $height + $descender - $newRow * $height;
                            if ($options['gridlines']) {
                                $y1 += $descender;
                                if (!$options['showHeadings']) {
                                    $y1 += ($options['rowGap'] / 2); // added line
                                    $y0 = $y1;
                                }
                                $this->ezTableDrawLines($pos, $options['gap'], $options['rowGap'], $x0, $x1, $y0, $y1, $y2, $options['lineCol'], $options['innerLineThickness'], $options['outerLineThickness'], $options['gridlines']);
                            }
                            if ($options['shaded'] || $options['showBgCol']) {
                                $this->closeObject();
                                $this->restoreState();
                            }
                            $this->ezNewPage();

                            // and the margins may have changed, this is due to the possibility of the columns being turned on
                            // as the columns are managed by manipulating the margins
                            $dm = $this->ez['leftMargin'] - $baseLeftMargin;
                            foreach ($basePos as $k => $v) {
                                $pos[$k] = $v + $dm;
                            }

                            $x0 = $baseX0 + $dm; // even
                            $x1 = $baseX1 + $dm; // even

                            if ($options['shaded'] || $options['showBgCol']) {
                                $this->saveState();
                                $textObjectId = $this->openObject();
                                $this->closeObject();
                                $this->addObject($textObjectId);
                                $this->reopenObject($textObjectId);
                            }
                            $this->setColor($options['textCol'][0], $options['textCol'][1], $options['textCol'][2], 1);
                            $y = ($options['nextPageY']) ? $nextPageY : ($this->ez['pageHeight'] - $this->ez['topMargin']);
                            $y0 = $y - $options['rowGap'];
                            $mx = 0;
                            if ($options['showHeadings']) {
                                // patch #9 start
                                if (isset($options['shadeHeadingCol']) && count($options['shadeHeadingCol']) == 3) {
                                    $this->saveState();
                                    $textHeadingsObjectId = $this->openObject();
                                    $this->closeObject();
                                    $this->addObject($textHeadingsObjectId);
                                    $this->reopenObject($textHeadingsObjectId);
                                    $this->closeObject();
                                    $this->setColor($options['shadeHeadingCol'][0], $options['shadeHeadingCol'][1], $options['shadeHeadingCol'][2], 1);
                                    $this->filledRectangle($x0 - $options['gap'] / 2, $y0, $x1 - $x0, -($headingHeight - $descender + $options['rowGap']));
                                    $this->reopenObject($textHeadingsObjectId);
                                    $this->closeObject();
                                    $this->restoreState();
                                }
                                // patch #9 end
                                $this->ezTableColumnHeadings($cols, $pos, $maxWidth, $height, $descender, $options['rowGap'], $options['fontSize'], $y, $options);
                                $y1 = $y - $options['rowGap'] * 2;
                            } else {
                                $y1 = $y0;
                            }
                            $firstLine = 1;
                            $y -= $height;
                        }
                        $newRow = 0;
                        // write the actual data
                        // if these cells need to be split over a page, then $newPage will be set, and the remaining
                        // text will be placed in $leftOvers
                        $newPage = 0;
                        $leftOvers = [];

                        foreach ($cols as $colName => $colTitle) {
                            $this->ezSetY($y + $height);
                            $colNewPage = 0;
                            if (isset($row[$colName])) {
                                if (isset($options['cols'][$colName]) && isset($options['cols'][$colName]['link']) && strlen($options['cols'][$colName]['link'])) {
                                    $lines = preg_split("[\r\n|\r|\n]", $row[$colName]);
                                    if (isset($row[$options['cols'][$colName]['link']]) && strlen($row[$options['cols'][$colName]['link']])) {
                                        foreach ($lines as $k => $v) {
                                            $lines[$k] = '<c:alink:'.$row[$options['cols'][$colName]['link']].'>'.$v.'</c:alink>';
                                        }
                                    }
                                } else {
                                    $lines = preg_split("[\r\n|\r|\n]", $row[$colName]);
                                }
                            } else {
                                $lines = [];
                            }
                            $this->y -= $options['rowGap'];
                            foreach ($lines as $line) {
                                $line = $this->ezProcessText($line);
                                // set the text color
                                // grab the defined colors for this cell
                                if (isset($row[$colName.'Color'])) {
                                    $textColor = $row[$colName.'Color'];
                                    $this->setColor($textColor[0], $textColor[1], $textColor[2], true);
                                } else {
                                    $this->setColor(0, 0, 0, true);
                                    $this->setStrokeColor(0, 0, 0, true);
                                }

                                $start = 1;
                                while (strlen($line) || $start) {
                                    $start = 0;
                                    if (!$colNewPage) {
                                        $this->y = $this->y - $height;
                                    }
                                    if ($this->y < $this->ez['bottomMargin']) {
                                        // $this->ezNewPage();
                                        $newPage = 1; // whether a new page is required for any of the columns
                                        $colNewPage = 1; // whether a new page is required for this column
                                    }
                                    if ($colNewPage) {
                                        if (isset($leftOvers[$colName])) {
                                            $leftOvers[$colName] .= "\n".$line;
                                        } else {
                                            $leftOvers[$colName] = $line;
                                        }
                                        $line = '';
                                    } else {
                                        if (isset($options['cols'][$colName]) && isset($options['cols'][$colName]['justification'])) {
                                            $just = $options['cols'][$colName]['justification'];
                                        } else {
                                            $just = 'left';
                                        }

                                        // grab the defined colors for this cell
                                        if (isset($row[$colName."Color"])) {
                                            $textColor = $row[$colName."Color"];
                                        } else {
                                            $textColor = "";
                                        }

                                        // apply the color to the text
                                        if (is_array($textColor)) {
                                            $this->setColor($textColor[0], $textColor[1], $textColor[2]);
                                            $line = $this->addText($pos[$colName], $this->y, $options['fontSize'], $line, $maxWidth[$colName], $just);
                                        } else {
                                            $this->setColor($options['textCol'][0], $options['textCol'][1], $options['textCol'][2]);
                                            $line = $this->addText($pos[$colName], $this->y, $options['fontSize'], $line, $maxWidth[$colName], $just);
                                        }
                                    }
                                }
                            }

                            $dy = $y + $height - $this->y + $options['rowGap'];
                            if ($dy - $height * $newPage > $mx) {
                                $mx = $dy - $height * $newPage;
                            }
                        }

                        // set $row to $leftOvers so that they will be processed onto the new page (we need to add the colours to the leftovers)
                        foreach ($cols as $colName => $colHeading) {
                            if (isset($row[$colName.'Fill'])) {
                                $leftOvers[$colName.'Fill'] = $row[$colName.'Fill'];
                            }
                            if (isset($row[$colName.'Color'])) {
                                $leftOvers[$colName.'Color'] = $row[$colName.'Color'];
                            }
                        }
                        $row = $leftOvers;

                        if ($options['gridlines'] & EZ_GRIDLINE_ROWS) {
                            // then draw a line on the top of each block
                            // $this->closeObject();
                            $this->saveState();
                            $this->setStrokeColor($options['lineCol'][0], $options['lineCol'][1], $options['lineCol'][2], 1);
                            // $this->line($x0-$options['gap']/2,$y+$descender+$height-$mx,$x1-$x0,$mx);
                            if ($firstLine) {
                                $firstLine = 0;
                            } else {
                                $this->setLineStyle($options['innerLineThickness']);
                                $this->line($x0 - $options['gap'] / 2, $y + $descender + $height, $x1 - $options['gap'] / 2, $y + $descender + $height);
                            }

                            $this->restoreState();
                            // $this->reopenObject($textObjectId);
                        }
                    } // end of while
                    $y = $y - $mx + $height;

                    // checking row split over pages
                    if ($options['splitRows'] == 0) {
                        if ((($this->ezPageCount != $pageStart) || (isset($this->ez['columns']) && $this->ez['columns']['on'] == 1 && $columnStart != $this->ez['columns']['colNum'])) && $secondTurn == 0) {
                            // then we need to go back and try that again !
                            $newPage = 1;
                            $secondTurn = 1;
                            $this->transaction('rewind');
                            $row = $row_orig;
                            $y = $y_orig;
                            $y0 = $y0_orig;
                            $y1 = $y1_orig;
                            $ok = 0;

                            $dm = $this->ez['leftMargin'] - $baseLeftMargin;
                            foreach ($basePos as $k => $v) {
                                $pos[$k] = $v + $dm;
                            }
                            $x0 = $baseX0 + $dm;
                            $x1 = $baseX1 + $dm;
                        } else {
                            $this->transaction('commit');
                            $ok = 1;
                        }
                    } else {
                        $ok = 1; // don't go round the loop if splitting rows is allowed
                    }
                } // end of while to check for row splitting
                if ($abortTable) {
                    if ($ok == 0) {
                        $this->transaction('abort');
                    }
                    // only the outer transaction should be operational
                    $this->transaction('rewind');
                    $this->ezNewPage();
                    break;
                }
            } // end of foreach ($data as $row)
        } // end of while ($abortTable)

        // table has been put on the page, the rows guarded as required, commit.
        $this->transaction('commit');

        $y2 = $y + $descender;
        if ($options['gridlines']) {
            $y1 += $descender;
            if (!$options['showHeadings']) {
                $y1 += ($options['rowGap'] / 2); // added line
                $y0 = $y1;
            }
            $this->ezTableDrawLines($pos, $options['gap'], $options['rowGap'], $x0, $x1, $y0, $y1, $y2, $options['lineCol'], $options['innerLineThickness'], $options['outerLineThickness'], $options['gridlines']);
        }
        // close the object for drawing the text on top
        if ($options['shaded'] || $options['showBgCol']) {
            $this->closeObject();
            $this->restoreState();
        }

        $this->y = $y;

        return $y;
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants