Skip to content

Commit

Permalink
Changes from pull dompdf#1356 (widows)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mellthas committed Jul 27, 2021
1 parent dd09636 commit 77a6dae
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 19 deletions.
36 changes: 28 additions & 8 deletions src/FrameDecorator/Page.php
Expand Up @@ -363,14 +363,33 @@ protected function _page_break_allowed(Frame $frame)

// Rule C
$block_parent = $frame->find_block_parent();
if (count($block_parent->get_line_boxes()) < $frame->get_style()->orphans) {
$block_line_boxes = $block_parent->get_line_boxes();
$frame_line_number = 0;
$i = 1;
$first_node_on_line = false;
foreach ($block_line_boxes as &$line_box) {
if ($line_box === $frame->get_containing_line()) {
$frame_line_number = $i;
$frames = $line_box->get_frames();
if (count($frames) > 0) {
$first_node_on_line = $frames[0]->get_node();
}
break;
}
$i++;
}

if ($frame_line_number <= $frame->get_style()->orphans) {
Helpers::dompdf_debug("page-break", "orphans");

return false;
}

// FIXME: Checking widows is tricky without having laid out the
// remaining line boxes. Just ignore it for now...
if (count($block_line_boxes) - $frame_line_number + 1 < $frame->get_style()->widows) {
Helpers::dompdf_debug("page-break", "widows");

return false;
}

// Rule D
$p = $block_parent;
Expand Down Expand Up @@ -398,11 +417,6 @@ protected function _page_break_allowed(Frame $frame)
return false;
}

// Skip breaks on empty text nodes
if ($frame->is_text_node() && $frame->get_node()->nodeValue == "") {
return false;
}

Helpers::dompdf_debug("page-break", "inline: break allowed");

return true;
Expand Down Expand Up @@ -557,6 +571,12 @@ function check_page_break(Frame $frame)
}

if ($next = $iter->get_prev_sibling()) {
while ($next->is_text_node() && $next->get_node()->nodeValue == "") {
$next = $next->get_prev_sibling();
if (!$next)
break 2;
}

Helpers::dompdf_debug("page-break", "following prev sibling.");

if ($next->is_table() && !$iter->is_table()) {
Expand Down
28 changes: 17 additions & 11 deletions src/FrameReflower/Block.php
Expand Up @@ -832,26 +832,32 @@ function reflow(BlockFrameDecorator $block = null)

// Set the containing blocks and reflow each child
foreach ($this->_frame->get_children() as $child) {

// Bail out if the page is full
if ($page->is_full()) {
break;
}

$child->set_containing_block($cb_x, $cb_y, $w, $cb_h);

$this->process_clear($child);

$child->reflow($this->_frame);

// Don't add the child to the line if a page break has occurred
if ($page->check_page_break($child)) {
break;
}

$this->process_float($child, $cb_x, $w);
}

// Go back through and handle any page breaks
foreach ($this->_frame->get_children() as $child) {
// Skip children that are empty text nodes
if (!$child->is_text_node() ||
$child->get_node()->nodeValue != ""
) {
if ($page->check_page_break($child)) {
break;
}

// Bail out if the page is full
if ($page->is_full()) {
break;
}
}
}

// Determine our height
list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height();
$style->height = $height;
Expand Down
120 changes: 120 additions & 0 deletions tests/DompdfTest.php
Expand Up @@ -96,4 +96,124 @@ public function testSpaceAtStartOfSecondInlineTag()
$this->assertEquals("one", $text_frame_contents[0]);
$this->assertEquals(" - two", $text_frame_contents[1]);
}

private function _pageBreak($orphans, $widows, $lines_in_paragraph, $lines_that_could_fit_on_first_page)
{
$dompdf = new Dompdf();

$callback_count = 0;
$number_of_lines_on_second_page = 0;

// Use a callback to inspect the frame tree; otherwise FrameReflower\Page::reflow()
// will dispose of it before dompdf->render finishes
$dompdf->setCallbacks(array('test' => array(
'event' => 'end_page_render',
'f' => function($params) use (&$number_of_lines_on_second_page, &$callback_count) {
if ($callback_count == 1) {
$frame = $params["frame"];
foreach ($frame->get_children() as $child) {
foreach ($child->get_children() as $grandchild) {
if ($grandchild->get_node()->nodeName == "#text")
$number_of_lines_on_second_page++;
}
}
}
$callback_count++;
}
)));

$html = "<p>";
$filler = array();
$MAX_FILLER = 47; // Is there a better way to know how many lines fit on a page?
for ($i = 1; $i <= $MAX_FILLER - $lines_that_could_fit_on_first_page; $i++) {
$filler[] = "Filler $i";
}
$html .= join("<br>", $filler);
$html .= "</p>";

$html .= "<p style=\"widows: $widows; orphans: $orphans;\">";
$lines = array();
for ($i = 1; $i <= $lines_in_paragraph; $i++) {
$lines[] = "Line $i";
}
$html .= join("<br>", $lines);
$html .= "</p>";

$dompdf->loadHtml($html);
$dompdf->render();

return $number_of_lines_on_second_page;
}

public function testPageBreak1orphan1widow()
{
$orphans = 1; // Minimum lines on first page
$widows = 1; // Minimum lines on second page
$lines_in_paragraph = 4; // Expected: 2 on first, 2 on second
$lines_that_could_fit_on_first_page = 2;
$number_of_lines_on_second_page = $this->_pageBreak($orphans, $widows, $lines_in_paragraph, $lines_that_could_fit_on_first_page);

$this->assertEquals(2, $number_of_lines_on_second_page, "Unexpected number of lines on the second page");
}

public function testPageBreak1orphan2widows()
{
$orphans = 1;
$widows = 2; // Minimum lines on second page
$lines_in_paragraph = 4;
$lines_that_could_fit_on_first_page = 2; // Expected: 2 on first, 2 on second
$number_of_lines_on_second_page = $this->_pageBreak($orphans, $widows, $lines_in_paragraph, $lines_that_could_fit_on_first_page);

$this->assertEquals(2, $number_of_lines_on_second_page, "Unexpected number of lines on the second page");
}

public function testPageBreak1orphan3widows()
{
$orphans = 1;
$widows = 3; // Minimum lines on second page
$lines_in_paragraph = 4;
$lines_that_could_fit_on_first_page = 2; // Expected: 1 on first, 3 on second
$number_of_lines_on_second_page = $this->_pageBreak($orphans, $widows, $lines_in_paragraph, $lines_that_could_fit_on_first_page);
$this->assertEquals(3, $number_of_lines_on_second_page, "Unexpected number of lines on the second page");
}

public function testPageBreak1orphan4widows()
{
$orphans = 1;
$widows = 4; // Minimum lines on second page
$lines_in_paragraph = 4;
$lines_that_could_fit_on_first_page = 2; // Expected: 0 on first, 4 on second
$number_of_lines_on_second_page = $this->_pageBreak($orphans, $widows, $lines_in_paragraph, $lines_that_could_fit_on_first_page);
$this->assertEquals(4, $number_of_lines_on_second_page, "Unexpected number of lines on the second page");
}

public function testPageBreak2orphans1widow()
{
$orphans = 2;
$widows = 1; // Minimum lines on second page
$lines_in_paragraph = 4;
$lines_that_could_fit_on_first_page = 2; // Expected: 2 on first, 2 on second
$number_of_lines_on_second_page = $this->_pageBreak($orphans, $widows, $lines_in_paragraph, $lines_that_could_fit_on_first_page);
$this->assertEquals(2, $number_of_lines_on_second_page, "Unexpected number of lines on the second page");
}

public function testPageBreak3orphans1widow()
{
$orphans = 3;
$widows = 1; // Minimum lines on second page
$lines_in_paragraph = 4;
$lines_that_could_fit_on_first_page = 2; // Expected: 0 on first, 4 on second
$number_of_lines_on_second_page = $this->_pageBreak($orphans, $widows, $lines_in_paragraph, $lines_that_could_fit_on_first_page);
$this->assertEquals(4, $number_of_lines_on_second_page, "Unexpected number of lines on the second page");
}

public function testPageBreak4orphans1widow()
{
$orphans = 4;
$widows = 1; // Minimum lines on second page
$lines_in_paragraph = 4;
$lines_that_could_fit_on_first_page = 2; // Expected: 0 on first, 4 on second
$number_of_lines_on_second_page = $this->_pageBreak($orphans, $widows, $lines_in_paragraph, $lines_that_could_fit_on_first_page);
$this->assertEquals(4, $number_of_lines_on_second_page, "Unexpected number of lines on the second page");
}
}

0 comments on commit 77a6dae

Please sign in to comment.