Skip to content

Commit 08da065

Browse files
committed
fix: keep every cell bounding box so MultiCell() frames continue across pages and writeHTML() moves the cursor below the text (v7.0.17)
1 parent 5b4fb77 commit 08da065

4 files changed

Lines changed: 85 additions & 8 deletions

File tree

‎VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7.0.16
1+
7.0.17

‎composer.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"require": {
3030
"php": ">=8.2",
3131
"ext-curl": "*",
32-
"tecnickcom/tc-lib-pdf": "^8.78"
32+
"tecnickcom/tc-lib-pdf": "^8.80"
3333
},
3434
"require-dev": {
3535
"carthage-software/mago": "1.55.0",

‎tcpdf.php‎

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
//============================================================+
44
// File name : tcpdf.php
5-
// Version : 7.0.16
5+
// Version : 7.0.17
66
// Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
77
// License : GNU-LGPL v3 (https://www.gnu.org/copyleft/lesser.html)
88
// Copyright (C): 2002-2026 Nicola Asuni - Tecnick.com LTD
@@ -15,7 +15,7 @@
1515
* See: https://tcpdf.org
1616
* @package com.tecnick.tcpdf
1717
* @author Nicola Asuni
18-
* @version 7.0.16
18+
* @version 7.0.17
1919
*/
2020

2121
// TCPDF configuration
@@ -1827,7 +1827,10 @@ protected function overflowCellOutput(
18271827
default => $cellx,
18281828
};
18291829

1830-
return (is_string($box) ? $box : '') . $this->cellTextRun($txt, $boxx, $boxw, $height, $valign, $halign, $cell, $this->engineShadow());
1830+
return (
1831+
(is_string($box) ? $box : '')
1832+
. $this->cellTextRun($txt, $boxx, $boxw, $height, $valign, $halign, $cell, $this->engineShadow())
1833+
);
18311834
}
18321835

18331836
/**
@@ -3577,7 +3580,7 @@ public function Cell(
35773580
if ($stretchmode === 0 && $txt !== '') {
35783581
$txtw = $stretchtextw > 0 ? $stretchtextw : $this->engineTextWidth($txt);
35793582
$innerw = $width - $borderpadding['L'] - $borderpadding['R'];
3580-
if ($txtw <= $innerw * 0.95) {
3583+
if ($txtw <= ($innerw * 0.95)) {
35813584
$txtw = 0.0;
35823585
}
35833586
}
@@ -8800,6 +8803,8 @@ public function ImageSVG(
88008803
* start of every page the engine opens.
88018804
*
88028805
* @package com.tecnick.tcpdf
8806+
*
8807+
* @phpstan-import-type TBBox from \Com\Tecnick\Pdf\Base
88038808
*/
88048809
// The single-file layout (tcpdf.php as the only historical include) and the
88058810
// legacy ALL-CAPS underscore class naming (TCPDF_STATIC, TCPDF_FONTS, ...)
@@ -8834,6 +8839,22 @@ public function setPageContext(int $pid = -1): void
88348839
}
88358840
}
88368841

8842+
/**
8843+
* Keeps every bounding box in the list: the facade reads the cell boxes
8844+
* added by a call (see TCPDF::cellBBoxCount() and cellBBoxBottomSince()).
8845+
*
8846+
* @param array<int, TBBox> $list Current bounding box list.
8847+
* @param TBBox $box New bounding box.
8848+
*
8849+
* @return array<int, TBBox>
8850+
*/
8851+
protected function getNextBBoxList(array $list, array $box): array
8852+
{
8853+
$list[] = $box;
8854+
8855+
return $list;
8856+
}
8857+
88378858
/**
88388859
* Anchor the page regions (columns) at the content top margin.
88398860
*

‎test/TcpdfCellTest.php‎

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,37 @@ public function testMultiCellWrapsAndAdvances(): void
8383
$this->assertSame(10.0, $pdf->GetX(), 'default ln=1 must reset X to the left margin');
8484
}
8585

86+
public function testMultiCellFrameContinuesOnEveryPage(): void
87+
{
88+
$pdf = $this->newPdf();
89+
$pdf->setFont('helvetica', '', 12);
90+
$pdf->AddPage();
91+
$pdf->MultiCell(80, 0, str_repeat('frame me over the pages please ', 150), 1, 'L');
92+
93+
$pages = $this->pageContents($pdf);
94+
$this->assertGreaterThan(2, count($pages));
95+
foreach ($pages as $idx => $content) {
96+
$this->assertMatchesRegularExpression('/ l\n/', $content, 'page ' . ($idx + 1) . ' must draw the frame');
97+
}
98+
}
99+
100+
public function testWriteHTMLMovesTheCursorBelowTheText(): void
101+
{
102+
$pdf = $this->newPdf();
103+
$pdf->setFont('helvetica', '', 12);
104+
$pdf->AddPage();
105+
$starty = $pdf->GetY();
106+
$pdf->writeHTML('<div style="text-align:center">first line</div>', true, false, true, false, '');
107+
$this->assertGreaterThan($starty, $pdf->GetY(), 'the cursor must move below the text');
108+
109+
$pdf->writeHTML('<p>second line</p>', true, false, true, false, '');
110+
$this->assertGreaterThanOrEqual(
111+
$this->wordBox($pdf, 'first')['ymax'] - 0.1,
112+
$this->wordBox($pdf, 'second')['ymin'],
113+
'the second block must start below the first',
114+
);
115+
}
116+
86117
public function testWriteFlowsFromTheCursor(): void
87118
{
88119
$pdf = $this->newPdf();
@@ -255,8 +286,8 @@ public function testRtlStretchedCellTextStaysInsideTheCell(): void
255286

256287
$box = $this->wordBox($pdf, 'stretched');
257288
$k = 72 / 25.4;
258-
$this->assertGreaterThanOrEqual(($right - 20) * $k - 0.1, $box['xmin']);
259-
$this->assertLessThanOrEqual($right * $k + 0.1, $box['xmax']);
289+
$this->assertGreaterThanOrEqual((($right - 20) * $k) - 0.1, $box['xmin']);
290+
$this->assertLessThanOrEqual(($right * $k) + 0.1, $box['xmax']);
260291
}
261292

262293
public function testCellOverflowKeepsTheBorderGeometry(): void
@@ -302,6 +333,31 @@ private function borderedCellContent(string $txt): string
302333
return $content;
303334
}
304335

336+
/**
337+
* Decompressed content streams of the document pages that hold text.
338+
*
339+
* @return list<string>
340+
*/
341+
private function pageContents(TCPDF $pdf): array
342+
{
343+
$raw = (string) $pdf->getPDFData();
344+
$streams = [];
345+
preg_match_all('/stream\r?\n(.*?)\r?\nendstream/s', $raw, $streams);
346+
$pages = [];
347+
foreach ($streams[1] ?? [] as $stream) {
348+
if (!str_starts_with($stream, "\x78")) {
349+
continue;
350+
}
351+
352+
$data = gzuncompress($stream);
353+
if (is_string($data) && str_contains($data, 'BT')) {
354+
$pages[] = $data;
355+
}
356+
}
357+
358+
return $pages;
359+
}
360+
305361
/**
306362
* Graphics state lines of a content stream, without text objects.
307363
*/

0 commit comments

Comments
 (0)