| 1 |
<?php |
| 2 |
/** |
| 3 |
* @package dompdf |
| 4 |
* @link https://github.com/dompdf/dompdf |
| 5 |
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License |
| 6 |
*/ |
| 7 |
namespace Dompdf\FrameReflower; |
| 8 |
|
| 9 |
use Dompdf\Frame; |
| 10 |
use Dompdf\FrameDecorator\Block as BlockFrameDecorator; |
| 11 |
use Dompdf\FrameDecorator\Page as PageFrameDecorator; |
| 12 |
|
| 13 |
/** |
| 14 |
* Reflows pages |
| 15 |
* |
| 16 |
* @package dompdf |
| 17 |
*/ |
| 18 |
class Page extends AbstractFrameReflower |
| 19 |
{ |
| 20 |
|
| 21 |
/** |
| 22 |
* Cache of the callbacks array |
| 23 |
* |
| 24 |
* @var array |
| 25 |
*/ |
| 26 |
private $_callbacks; |
| 27 |
|
| 28 |
/** |
| 29 |
* Cache of the canvas |
| 30 |
* |
| 31 |
* @var \Dompdf\Canvas |
| 32 |
*/ |
| 33 |
private $_canvas; |
| 34 |
|
| 35 |
/** |
| 36 |
* Page constructor. |
| 37 |
* @param PageFrameDecorator $frame |
| 38 |
*/ |
| 39 |
function __construct(PageFrameDecorator $frame) |
| 40 |
{ |
| 41 |
parent::__construct($frame); |
| 42 |
} |
| 43 |
|
| 44 |
/** |
| 45 |
* @param PageFrameDecorator $frame |
| 46 |
* @param int $page_number |
| 47 |
*/ |
| 48 |
function apply_page_style(Frame $frame, $page_number) |
| 49 |
{ |
| 50 |
$style = $frame->get_style(); |
| 51 |
$page_styles = $style->get_stylesheet()->get_page_styles(); |
| 52 |
|
| 53 |
// http://www.w3.org/TR/CSS21/page.html#page-selectors |
| 54 |
if (count($page_styles) > 1) { |
| 55 |
$odd = $page_number % 2 == 1; |
| 56 |
$first = $page_number == 1; |
| 57 |
|
| 58 |
$style = clone $page_styles["base"]; |
| 59 |
|
| 60 |
// FIXME RTL |
| 61 |
if ($odd && isset($page_styles[":right"])) { |
| 62 |
$style->merge($page_styles[":right"]); |
| 63 |
} |
| 64 |
|
| 65 |
if ($odd && isset($page_styles[":odd"])) { |
| 66 |
$style->merge($page_styles[":odd"]); |
| 67 |
} |
| 68 |
|
| 69 |
// FIXME RTL |
| 70 |
if (!$odd && isset($page_styles[":left"])) { |
| 71 |
$style->merge($page_styles[":left"]); |
| 72 |
} |
| 73 |
|
| 74 |
if (!$odd && isset($page_styles[":even"])) { |
| 75 |
$style->merge($page_styles[":even"]); |
| 76 |
} |
| 77 |
|
| 78 |
if ($first && isset($page_styles[":first"])) { |
| 79 |
$style->merge($page_styles[":first"]); |
| 80 |
} |
| 81 |
|
| 82 |
$frame->set_style($style); |
| 83 |
} |
| 84 |
|
| 85 |
$frame->calculate_bottom_page_edge(); |
| 86 |
} |
| 87 |
|
| 88 |
/** |
| 89 |
* Paged layout: |
| 90 |
* http://www.w3.org/TR/CSS21/page.html |
| 91 |
* |
| 92 |
* @param BlockFrameDecorator|null $block |
| 93 |
*/ |
| 94 |
function reflow(BlockFrameDecorator $block = null) |
| 95 |
{ |
| 96 |
/** @var PageFrameDecorator $frame */ |
| 97 |
$frame = $this->_frame; |
| 98 |
$child = $frame->get_first_child(); |
| 99 |
$fixed_children = []; |
| 100 |
$prev_child = null; |
| 101 |
$current_page = 0; |
| 102 |
|
| 103 |
while ($child) { |
| 104 |
$this->apply_page_style($frame, $current_page + 1); |
| 105 |
|
| 106 |
$style = $frame->get_style(); |
| 107 |
|
| 108 |
// Pages are only concerned with margins |
| 109 |
$cb = $frame->get_containing_block(); |
| 110 |
$left = (float)$style->length_in_pt($style->margin_left, $cb["w"]); |
| 111 |
$right = (float)$style->length_in_pt($style->margin_right, $cb["w"]); |
| 112 |
$top = (float)$style->length_in_pt($style->margin_top, $cb["h"]); |
| 113 |
$bottom = (float)$style->length_in_pt($style->margin_bottom, $cb["h"]); |
| 114 |
|
| 115 |
$content_x = $cb["x"] + $left; |
| 116 |
$content_y = $cb["y"] + $top; |
| 117 |
$content_width = $cb["w"] - $left - $right; |
| 118 |
$content_height = $cb["h"] - $top - $bottom; |
| 119 |
|
| 120 |
// Only if it's the first page, we save the nodes with a fixed position |
| 121 |
if ($current_page == 0) { |
| 122 |
foreach ($child->get_children() as $onechild) { |
| 123 |
if ($onechild->get_style()->position === "fixed") { |
| 124 |
$fixed_children[] = $onechild->deep_copy(); |
| 125 |
} |
| 126 |
} |
| 127 |
$fixed_children = array_reverse($fixed_children); |
| 128 |
} |
| 129 |
|
| 130 |
$child->set_containing_block($content_x, $content_y, $content_width, $content_height); |
| 131 |
|
| 132 |
// Check for begin reflow callback |
| 133 |
$this->_check_callbacks("begin_page_reflow", $child); |
| 134 |
|
| 135 |
//Insert a copy of each node which have a fixed position |
| 136 |
if ($current_page >= 1) { |
| 137 |
foreach ($fixed_children as $fixed_child) { |
| 138 |
$child->insert_child_before($fixed_child->deep_copy(), $child->get_first_child()); |
| 139 |
} |
| 140 |
} |
| 141 |
|
| 142 |
$child->reflow(); |
| 143 |
$next_child = $child->get_next_sibling(); |
| 144 |
|
| 145 |
// Check for begin render callback |
| 146 |
$this->_check_callbacks("begin_page_render", $child); |
| 147 |
|
| 148 |
// Render the page |
| 149 |
$frame->get_renderer()->render($child); |
| 150 |
|
| 151 |
// Check for end render callback |
| 152 |
$this->_check_callbacks("end_page_render", $child); |
| 153 |
|
| 154 |
if ($next_child) { |
| 155 |
$frame->next_page(); |
| 156 |
} |
| 157 |
|
| 158 |
// Wait to dispose of all frames on the previous page |
| 159 |
// so callback will have access to them |
| 160 |
if ($prev_child) { |
| 161 |
$prev_child->dispose(true); |
| 162 |
} |
| 163 |
$prev_child = $child; |
| 164 |
$child = $next_child; |
| 165 |
$current_page++; |
| 166 |
} |
| 167 |
|
| 168 |
// Dispose of previous page if it still exists |
| 169 |
if ($prev_child) { |
| 170 |
$prev_child->dispose(true); |
| 171 |
} |
| 172 |
} |
| 173 |
|
| 174 |
/** |
| 175 |
* Check for callbacks that need to be performed when a given event |
| 176 |
* gets triggered on a page |
| 177 |
* |
| 178 |
* @param string $event The type of event |
| 179 |
* @param Frame $frame The frame that event is triggered on |
| 180 |
*/ |
| 181 |
protected function _check_callbacks(string $event, Frame $frame): void |
| 182 |
{ |
| 183 |
if (!isset($this->_callbacks)) { |
| 184 |
$dompdf = $this->get_dompdf(); |
| 185 |
$this->_callbacks = $dompdf->getCallbacks(); |
| 186 |
$this->_canvas = $dompdf->getCanvas(); |
| 187 |
} |
| 188 |
|
| 189 |
if (isset($this->_callbacks[$event])) { |
| 190 |
$fs = $this->_callbacks[$event]; |
| 191 |
$canvas = $this->_canvas; |
| 192 |
$fontMetrics = $this->get_dompdf()->getFontMetrics(); |
| 193 |
|
| 194 |
foreach ($fs as $f) { |
| 195 |
$f($frame, $canvas, $fontMetrics); |
| 196 |
} |
| 197 |
} |
| 198 |
} |
| 199 |
} |
| 200 |
|