PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.23
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.23
1.10.25 1.10.24 1.10.23 1.10.22 1.10.21 1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 All 169 releases
woocommerce-pos / vendor_prefixed / dompdf / dompdf / src / FrameReflower / Table.php

Table.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.23, at vendor_prefixed/dompdf/dompdf/src/FrameReflower/Table.php

415 lines 16.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @package dompdf
5 * @link https://github.com/dompdf/dompdf
6 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7 */
8 namespace WCPOS\Vendor\Dompdf\FrameReflower;
9
10 use WCPOS\Vendor\Dompdf\FrameDecorator\Block as BlockFrameDecorator;
11 use WCPOS\Vendor\Dompdf\FrameDecorator\Table as TableFrameDecorator;
12 use WCPOS\Vendor\Dompdf\Helpers;
13 /**
14 * Reflows tables
15 *
16 * @package dompdf
17 */
18 class Table extends AbstractFrameReflower
19 {
20 /**
21 * Frame for this reflower
22 *
23 * @var TableFrameDecorator
24 */
25 protected $_frame;
26 /**
27 * Cache of results between call to get_min_max_width and assign_widths
28 *
29 * @var array
30 */
31 protected $_state;
32 /**
33 * Table constructor.
34 * @param TableFrameDecorator $frame
35 */
36 function __construct(TableFrameDecorator $frame)
37 {
38 $this->_state = null;
39 parent::__construct($frame);
40 }
41 /**
42 * State is held here so it needs to be reset along with the decorator
43 */
44 public function reset() : void
45 {
46 parent::reset();
47 $this->_state = null;
48 }
49 protected function _assign_widths()
50 {
51 $style = $this->_frame->get_style();
52 // Find the min/max width of the table and sort the columns into
53 // absolute/percent/auto arrays
54 $delta = $this->_state["width_delta"];
55 $min_width = $this->_state["min_width"];
56 $max_width = $this->_state["max_width"];
57 $percent_used = $this->_state["percent_used"];
58 $absolute_used = $this->_state["absolute_used"];
59 $auto_min = $this->_state["auto_min"];
60 $absolute =& $this->_state["absolute"];
61 $percent =& $this->_state["percent"];
62 $auto =& $this->_state["auto"];
63 // Determine the actual width of the table (excluding borders and
64 // padding)
65 $cb = $this->_frame->get_containing_block();
66 $columns =& $this->_frame->get_cellmap()->get_columns();
67 $width = $style->width;
68 $min_table_width = $this->resolve_min_width($cb["w"]) - $delta;
69 if ($width !== "auto") {
70 $preferred_width = (float) $style->length_in_pt($width, $cb["w"]) - $delta;
71 if ($preferred_width < $min_table_width) {
72 $preferred_width = $min_table_width;
73 }
74 if ($preferred_width > $min_width) {
75 $width = $preferred_width;
76 } else {
77 $width = $min_width;
78 }
79 } else {
80 if ($max_width + $delta < $cb["w"]) {
81 $width = $max_width;
82 } elseif ($cb["w"] - $delta > $min_width) {
83 $width = $cb["w"] - $delta;
84 } else {
85 $width = $min_width;
86 }
87 if ($width < $min_table_width) {
88 $width = $min_table_width;
89 }
90 }
91 // Store our resolved width
92 $style->set_used("width", $width);
93 $cellmap = $this->_frame->get_cellmap();
94 if ($cellmap->is_columns_locked()) {
95 return;
96 }
97 // If the whole table fits on the page, then assign each column it's max width
98 if ($width == $max_width) {
99 foreach ($columns as $i => $col) {
100 $cellmap->set_column_width($i, $col["max-width"]);
101 }
102 return;
103 }
104 // Determine leftover and assign it evenly to all columns
105 if ($width > $min_width) {
106 // We have three cases to deal with:
107 //
108 // 1. All columns are auto or absolute width. In this case we
109 // distribute extra space across all auto columns weighted by the
110 // difference between their max and min width, or by max width only
111 // if the width of the table is larger than the max width for all
112 // columns.
113 //
114 // 2. Only absolute widths have been specified, no auto columns. In
115 // this case we distribute extra space across all columns weighted
116 // by their absolute width.
117 //
118 // 3. Percentage widths have been specified. In this case we normalize
119 // the percentage values and try to assign widths as fractions of
120 // the table width. Absolute column widths are fully satisfied and
121 // any remaining space is evenly distributed among all auto columns.
122 // Case 1:
123 if ($percent_used == 0 && \count($auto)) {
124 foreach ($absolute as $i) {
125 $w = $columns[$i]["min-width"];
126 $cellmap->set_column_width($i, $w);
127 }
128 if ($width < $max_width) {
129 $increment = $width - $min_width;
130 $table_delta = $max_width - $min_width;
131 foreach ($auto as $i) {
132 $min = $columns[$i]["min-width"];
133 $max = $columns[$i]["max-width"];
134 $col_delta = $max - $min;
135 $w = $min + $increment * ($col_delta / $table_delta);
136 $cellmap->set_column_width($i, $w);
137 }
138 } else {
139 $increment = $width - $max_width;
140 $auto_max = $max_width - $absolute_used;
141 foreach ($auto as $i) {
142 $max = $columns[$i]["max-width"];
143 $f = $auto_max > 0 ? $max / $auto_max : 1 / \count($auto);
144 $w = $max + $increment * $f;
145 $cellmap->set_column_width($i, $w);
146 }
147 }
148 return;
149 }
150 // Case 2:
151 if ($percent_used == 0 && !\count($auto)) {
152 $increment = $width - $absolute_used;
153 foreach ($absolute as $i) {
154 $abs = $columns[$i]["min-width"];
155 $f = $absolute_used > 0 ? $abs / $absolute_used : 1 / \count($absolute);
156 $w = $abs + $increment * $f;
157 $cellmap->set_column_width($i, $w);
158 }
159 return;
160 }
161 // Case 3:
162 if ($percent_used > 0) {
163 // Scale percent values if the total percentage is > 100 or
164 // there are no auto values to take up slack
165 if ($percent_used > 100 || \count($auto) == 0) {
166 $scale = 100 / $percent_used;
167 } else {
168 $scale = 1;
169 }
170 // Account for the minimum space used by the unassigned auto
171 // columns, by the columns with absolute widths, and the
172 // percentage columns following the current one
173 $used_width = $auto_min + $absolute_used;
174 foreach ($absolute as $i) {
175 $w = $columns[$i]["min-width"];
176 $cellmap->set_column_width($i, $w);
177 }
178 $percent_min = 0;
179 foreach ($percent as $i) {
180 $percent_min += $columns[$i]["min-width"];
181 }
182 // First-come, first served
183 foreach ($percent as $i) {
184 $min = $columns[$i]["min-width"];
185 $percent_min -= $min;
186 $slack = $width - $used_width - $percent_min;
187 $columns[$i]["percent"] *= $scale;
188 $w = \min($columns[$i]["percent"] * $width / 100, $slack);
189 if ($w < $min) {
190 $w = $min;
191 }
192 $cellmap->set_column_width($i, $w);
193 $used_width += $w;
194 }
195 // This works because $used_width includes the min-width of each
196 // unassigned column
197 if (\count($auto) > 0) {
198 $increment = ($width - $used_width) / \count($auto);
199 foreach ($auto as $i) {
200 $w = $columns[$i]["min-width"] + $increment;
201 $cellmap->set_column_width($i, $w);
202 }
203 }
204 return;
205 }
206 } else {
207 // We are over-constrained:
208 // Each column gets its minimum width
209 foreach ($columns as $i => $col) {
210 $cellmap->set_column_width($i, $col["min-width"]);
211 }
212 }
213 }
214 /**
215 * Determine the frame's height based on min/max height
216 *
217 * @return float
218 */
219 protected function _calculate_height()
220 {
221 $frame = $this->_frame;
222 $style = $frame->get_style();
223 $cb = $frame->get_containing_block();
224 $height = $style->length_in_pt($style->height, $cb["h"]);
225 $cellmap = $frame->get_cellmap();
226 $cellmap->assign_frame_heights();
227 $rows = $cellmap->get_rows();
228 // Determine our content height
229 $content_height = 0.0;
230 foreach ($rows as $r) {
231 $content_height += $r["height"];
232 }
233 if ($height === "auto") {
234 $height = $content_height;
235 }
236 // Handle min/max height
237 // https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
238 $min_height = $this->resolve_min_height($cb["h"]);
239 $max_height = $this->resolve_max_height($cb["h"]);
240 $height = Helpers::clamp($height, $min_height, $max_height);
241 // Use the content height or the height value, whichever is greater
242 if ($height <= $content_height) {
243 $height = $content_height;
244 } else {
245 // FIXME: Borders and row positions are not properly updated by this
246 // $cellmap->set_frame_heights($height, $content_height);
247 }
248 return $height;
249 }
250 /**
251 * @param BlockFrameDecorator|null $block
252 */
253 function reflow(?BlockFrameDecorator $block = null)
254 {
255 /** @var TableFrameDecorator */
256 $frame = $this->_frame;
257 // Check if a page break is forced
258 $page = $frame->get_root();
259 $page->check_forced_page_break($frame);
260 // Bail if the page is full
261 if ($page->is_full()) {
262 return;
263 }
264 // Let the page know that we're reflowing a table so that splits
265 // are suppressed (simply setting page-break-inside: avoid won't
266 // work because we may have an arbitrary number of block elements
267 // inside tds.)
268 $page->table_reflow_start();
269 $this->determine_absolute_containing_block();
270 // Counters and generated content
271 $this->_set_content();
272 // Collapse vertical margins, if required
273 $this->_collapse_margins();
274 // Table layout algorithm:
275 // http://www.w3.org/TR/CSS21/tables.html#auto-table-layout
276 if (\is_null($this->_state)) {
277 $this->get_min_max_width();
278 }
279 $cb = $frame->get_containing_block();
280 $style = $frame->get_style();
281 // This is slightly inexact, but should be okay. Add half the
282 // border-spacing to the table as padding. The other half is added to
283 // the cells themselves.
284 if ($style->border_collapse === "separate") {
285 [$h, $v] = $style->border_spacing;
286 $v = $v / 2;
287 $h = $h / 2;
288 $style->set_used("padding_left", (float) $style->length_in_pt($style->padding_left, $cb["w"]) + $h);
289 $style->set_used("padding_right", (float) $style->length_in_pt($style->padding_right, $cb["w"]) + $h);
290 $style->set_used("padding_top", (float) $style->length_in_pt($style->padding_top, $cb["w"]) + $v);
291 $style->set_used("padding_bottom", (float) $style->length_in_pt($style->padding_bottom, $cb["w"]) + $v);
292 }
293 $this->_assign_widths();
294 // Adjust left & right margins, if they are auto
295 $delta = $this->_state["width_delta"];
296 $width = $style->width;
297 $left = $style->length_in_pt($style->margin_left, $cb["w"]);
298 $right = $style->length_in_pt($style->margin_right, $cb["w"]);
299 $diff = (float) $cb["w"] - (float) $width - $delta;
300 if ($left === "auto" && $right === "auto") {
301 if ($diff < 0) {
302 $left = 0;
303 $right = $diff;
304 } else {
305 $left = $right = $diff / 2;
306 }
307 } else {
308 if ($left === "auto") {
309 $left = \max($diff - $right, 0);
310 }
311 if ($right === "auto") {
312 $right = \max($diff - $left, 0);
313 }
314 }
315 $style->set_used("margin_left", $left);
316 $style->set_used("margin_right", $right);
317 $frame->position();
318 [$x, $y] = $frame->get_position();
319 // Determine the content edge
320 $offset_x = (float) $left + (float) $style->length_in_pt([$style->padding_left, $style->border_left_width], $cb["w"]);
321 $offset_y = (float) $style->length_in_pt([$style->margin_top, $style->border_top_width, $style->padding_top], $cb["w"]);
322 $content_x = $x + $offset_x;
323 $content_y = $y + $offset_y;
324 if (isset($cb["h"])) {
325 $h = $cb["h"];
326 } else {
327 $h = null;
328 }
329 $cellmap = $frame->get_cellmap();
330 $col =& $cellmap->get_column(0);
331 $col["x"] = $offset_x;
332 $row =& $cellmap->get_row(0);
333 $row["y"] = $offset_y;
334 $cellmap->assign_x_positions();
335 // Set the containing block of each child & reflow
336 foreach ($frame->get_children() as $child) {
337 $child->set_containing_block($content_x, $content_y, $width, $h);
338 $child->reflow();
339 if (!$page->in_nested_table()) {
340 // Check if a split has occurred
341 $page->check_page_break($child);
342 if ($page->is_full()) {
343 break;
344 }
345 }
346 }
347 // Stop reflow if a page break has occurred before the frame, in which
348 // case it has been reset, including its position
349 if ($page->is_full() && $frame->get_position("x") === null) {
350 $page->table_reflow_end();
351 return;
352 }
353 // Assign heights to our cells:
354 $style->set_used("height", $this->_calculate_height());
355 $page->table_reflow_end();
356 if ($block && $frame->is_in_flow()) {
357 $block->add_frame_to_line($frame);
358 if ($frame->is_block_level()) {
359 $block->add_line();
360 }
361 }
362 }
363 public function get_min_max_width() : array
364 {
365 if (!\is_null($this->_min_max_cache)) {
366 return $this->_min_max_cache;
367 }
368 $style = $this->_frame->get_style();
369 $cellmap = $this->_frame->get_cellmap();
370 $this->_frame->normalize();
371 // Add the cells to the cellmap (this will calculate column widths as
372 // frames are added)
373 $cellmap->add_frame($this->_frame);
374 // Find the min/max width of the table and sort the columns into
375 // absolute/percent/auto arrays
376 $this->_state = [];
377 $this->_state["min_width"] = 0;
378 $this->_state["max_width"] = 0;
379 $this->_state["percent_used"] = 0;
380 $this->_state["absolute_used"] = 0;
381 $this->_state["auto_min"] = 0;
382 $this->_state["absolute"] = [];
383 $this->_state["percent"] = [];
384 $this->_state["auto"] = [];
385 $columns =& $cellmap->get_columns();
386 foreach ($columns as $i => $col) {
387 $this->_state["min_width"] += $col["min-width"];
388 $this->_state["max_width"] += $col["max-width"];
389 if ($col["absolute"] > 0) {
390 $this->_state["absolute"][] = $i;
391 $this->_state["absolute_used"] += $col["min-width"];
392 } elseif ($col["percent"] > 0) {
393 $this->_state["percent"][] = $i;
394 $this->_state["percent_used"] += $col["percent"];
395 } else {
396 $this->_state["auto"][] = $i;
397 $this->_state["auto_min"] += $col["min-width"];
398 }
399 }
400 // Account for margins, borders, padding, and border spacing
401 $cb_w = $this->_frame->get_containing_block("w");
402 $lm = (float) $style->length_in_pt($style->margin_left, $cb_w);
403 $rm = (float) $style->length_in_pt($style->margin_right, $cb_w);
404 $dims = [$style->border_left_width, $style->border_right_width, $style->padding_left, $style->padding_right];
405 if ($style->border_collapse !== "collapse") {
406 list($dims[]) = $style->border_spacing;
407 }
408 $delta = (float) $style->length_in_pt($dims, $cb_w);
409 $this->_state["width_delta"] = $delta;
410 $min_width = $this->_state["min_width"] + $delta + $lm + $rm;
411 $max_width = $this->_state["max_width"] + $delta + $lm + $rm;
412 return $this->_min_max_cache = [$min_width, $max_width, "min" => $min_width, "max" => $max_width];
413 }
414 }
415