Skip to content

Commit ccdc359

Browse files
committed
Improve large table cell page break handling
This update should address the potential for Dompdf to enter an infinite loop attempting to page a table row when one of the cells inside that row is larger than the page size. See dompdf#98
1 parent 84f0d04 commit ccdc359

File tree

3 files changed

+59
-16
lines changed

3 files changed

+59
-16
lines changed

src/FrameDecorator/AbstractFrameDecorator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@ function split(Frame $child = null, $force_pagebreak = false)
695695
$split->reset();
696696
$split->get_original_style()->text_indent = 0;
697697
$split->_splitted = true;
698+
$split->_already_pushed = true;
698699

699700
// The body's properties must be kept
700701
if ($node->nodeName !== "body") {

src/FrameDecorator/Page.php

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use Dompdf\Dompdf;
1212
use Dompdf\Helpers;
1313
use Dompdf\Frame;
14+
use Dompdf\FrameDecorator\Table as TableFrameDecorator;
15+
use Dompdf\FrameDecorator\TableRow as TableRowFrameDecorator;
1416
use Dompdf\Renderer;
1517

1618
/**
@@ -192,7 +194,6 @@ function check_forced_page_break(Frame $frame)
192194
}
193195

194196
if (in_array($style->page_break_before, $page_breaks)) {
195-
196197
// Prevent cascading splits
197198
$frame->split(null, true);
198199
// We have to grab the style again here because split() resets
@@ -277,7 +278,6 @@ function check_forced_page_break(Frame $frame)
277278
*/
278279
protected function _page_break_allowed(Frame $frame)
279280
{
280-
281281
$block_types = array("block", "list-item", "table", "-dompdf-image");
282282
Helpers::dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName . ")");
283283
$display = $frame->get_style()->display;
@@ -399,13 +399,14 @@ protected function _page_break_allowed(Frame $frame)
399399

400400
return true;
401401

402-
// Table-rows
402+
// Table-rows
403403
} else {
404404
if ($display === "table-row") {
405405
// Simply check if the parent table's page_break_inside property is
406406
// not 'avoid'
407-
$p = Table::find_parent_table($frame);
407+
$table = Table::find_parent_table($frame);
408408

409+
$p = $table;
409410
while ($p) {
410411
if ($p->get_style()->page_break_inside === "avoid") {
411412
Helpers::dompdf_debug("page-break", "parent->inside: avoid");
@@ -416,7 +417,7 @@ protected function _page_break_allowed(Frame $frame)
416417
}
417418

418419
// Avoid breaking after the first row of a table
419-
if ($p && $p->get_first_child() === $frame) {
420+
if ($table && $table->get_first_child() === $frame || $table->get_first_child()->get_first_child() === $frame) {
420421
Helpers::dompdf_debug("page-break", "table: first-row");
421422

422423
return false;
@@ -460,30 +461,55 @@ protected function _page_break_allowed(Frame $frame)
460461
*/
461462
function check_page_break(Frame $frame)
462463
{
464+
//FIXME: should not need to do this since we're tracking table status in `$this->_in_table`
465+
$p = $frame;
466+
$in_table = false;
467+
while ($p) {
468+
if ($p->is_table()) { $in_table = true; break; }
469+
$p = $p->get_parent();
470+
}
463471
// Do not split if we have already or if the frame was already
464472
// pushed to the next page (prevents infinite loops)
465-
if ($this->_page_full || $frame->_already_pushed) {
473+
if ($in_table) {
474+
if ($this->_page_full && $frame->_already_pushed) {
475+
return false;
476+
}
477+
} elseif ($this->_page_full || $frame->_already_pushed) {
478+
return false;
479+
}
480+
481+
//FIXME: work-around for infinite loop due to tables
482+
if ($in_table && $frame->_already_pushed) {
466483
return false;
467484
}
485+
$p = $frame;
486+
do {
487+
$display = $p->get_style()->display;
488+
if ($display == "table-row") {
489+
if ($p->_already_pushed) { return false; }
490+
}
491+
} while ($p = $p->get_parent());
468492

469493
// If the frame is absolute of fixed it shouldn't break
470494
$p = $frame;
471495
do {
472496
if ($p->is_absolute()) {
473497
return false;
474498
}
499+
500+
// FIXME If the row is taller than the page and
501+
// if it the first of the page, we don't break
502+
$display = $p->get_style()->display;
503+
if ($display === "table-row"
504+
&& !$p->get_prev_sibling()
505+
&& $p->get_margin_height() > $this->get_margin_height()
506+
) {
507+
return false;
508+
}
475509
} while ($p = $p->get_parent());
476510

477511
$margin_height = $frame->get_margin_height();
478512

479-
// FIXME If the row is taller than the page and
480-
// if it the first of the page, we don't break
481-
if ($frame->get_style()->display === "table-row" &&
482-
!$frame->get_prev_sibling() &&
483-
$margin_height > $this->get_margin_height()
484-
)
485-
return false;
486-
487513
// Determine the frame's maximum y value
488514
$max_y = (float)$frame->get_position("y") + $margin_height;
489515

@@ -578,11 +604,15 @@ function check_page_break(Frame $frame)
578604
// If we are in a table, backtrack to the nearest top-level table row
579605
if ($this->_in_table) {
580606
$iter = $frame;
581-
while ($iter && $iter->get_style()->display !== "table-row" && $iter->get_style()->display !== 'table-row-group') {
607+
while ($iter && $iter->get_style()->display !== "table-row" && $iter->get_style()->display !== 'table-row-group' && $iter->_already_pushed === false) {
582608
$iter = $iter->get_parent();
583609
}
584610

585-
$iter->split(null, true);
611+
if ($iter) {
612+
$iter->split(null, true);
613+
} else {
614+
return false;
615+
}
586616
} else {
587617
$frame->split(null, true);
588618
}

src/FrameDecorator/TableRow.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,16 @@ function normalise()
5353
$p->move_after($frame);
5454
}
5555
}
56+
57+
function split(Frame $child = null, $force_pagebreak = false)
58+
{
59+
$this->_already_pushed = true;
60+
61+
if (is_null($child)) {
62+
parent::split();
63+
return;
64+
}
65+
66+
parent::split($child, $force_pagebreak);
67+
}
5668
}

0 commit comments

Comments
 (0)