PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.17
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.17
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 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 All 163 releases
woocommerce-pos / vendor_prefixed / dompdf / dompdf / src / Cellmap.php

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

818 lines 27.5 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;
9
10 use WCPOS\Vendor\Dompdf\FrameDecorator\AbstractFrameDecorator;
11 use WCPOS\Vendor\Dompdf\FrameDecorator\Table as TableFrameDecorator;
12 use WCPOS\Vendor\Dompdf\FrameDecorator\TableCell as TableCellFrameDecorator;
13 /**
14 * Maps table cells to the table grid.
15 *
16 * This class resolves borders in tables with collapsed borders and helps
17 * place row & column spanned table cells.
18 *
19 * @package dompdf
20 */
21 class Cellmap
22 {
23 /**
24 * Border style weight lookup for collapsed border resolution.
25 */
26 protected const BORDER_STYLE_SCORE = ["double" => 8, "solid" => 7, "dashed" => 6, "dotted" => 5, "ridge" => 4, "outset" => 3, "groove" => 2, "inset" => 1, "none" => 0];
27 /**
28 * The table object this cellmap is attached to.
29 *
30 * @var TableFrameDecorator
31 */
32 protected $_table;
33 /**
34 * The total number of rows in the table
35 *
36 * @var int
37 */
38 protected $_num_rows;
39 /**
40 * The total number of columns in the table
41 *
42 * @var int
43 */
44 protected $_num_cols;
45 /**
46 * 2D array mapping <row,column> to frames
47 *
48 * @var Frame[][]
49 */
50 protected $_cells;
51 /**
52 * 1D array of column dimensions
53 *
54 * @var array
55 */
56 protected $_columns;
57 /**
58 * 1D array of row dimensions
59 *
60 * @var array
61 */
62 protected $_rows;
63 /**
64 * 2D array of border specs
65 *
66 * @var array
67 */
68 protected $_borders;
69 /**
70 * 1D Array mapping frames to (multiple) <row, col> pairs, keyed on frame_id.
71 *
72 * @var array[]
73 */
74 protected $_frames;
75 /**
76 * Current column when adding cells, 0-based
77 *
78 * @var int
79 */
80 private $__col;
81 /**
82 * Current row when adding cells, 0-based
83 *
84 * @var int
85 */
86 private $__row;
87 /**
88 * Tells whether the columns' width can be modified
89 *
90 * @var bool
91 */
92 private $_columns_locked = \false;
93 /**
94 * Tells whether the table has table-layout:fixed
95 *
96 * @var bool
97 */
98 private $_fixed_layout = \false;
99 /**
100 * @param TableFrameDecorator $table
101 */
102 public function __construct(TableFrameDecorator $table)
103 {
104 $this->_table = $table;
105 $this->reset();
106 }
107 public function reset() : void
108 {
109 $this->_num_rows = 0;
110 $this->_num_cols = 0;
111 $this->_cells = [];
112 $this->_frames = [];
113 if (!$this->_columns_locked) {
114 $this->_columns = [];
115 }
116 $this->_rows = [];
117 $this->_borders = [];
118 $this->__col = $this->__row = 0;
119 }
120 public function lock_columns() : void
121 {
122 $this->_columns_locked = \true;
123 }
124 /**
125 * @return bool
126 */
127 public function is_columns_locked()
128 {
129 return $this->_columns_locked;
130 }
131 /**
132 * @param bool $fixed
133 */
134 public function set_layout_fixed(bool $fixed)
135 {
136 $this->_fixed_layout = $fixed;
137 }
138 /**
139 * @return bool
140 */
141 public function is_layout_fixed()
142 {
143 return $this->_fixed_layout;
144 }
145 /**
146 * @return int
147 */
148 public function get_num_rows()
149 {
150 return $this->_num_rows;
151 }
152 /**
153 * @return int
154 */
155 public function get_num_cols()
156 {
157 return $this->_num_cols;
158 }
159 /**
160 * @return array
161 */
162 public function &get_columns()
163 {
164 return $this->_columns;
165 }
166 /**
167 * @param $columns
168 */
169 public function set_columns($columns)
170 {
171 $this->_columns = $columns;
172 }
173 /**
174 * @param int $i
175 *
176 * @return mixed
177 */
178 public function &get_column($i)
179 {
180 if (!isset($this->_columns[$i])) {
181 $this->_columns[$i] = ["x" => 0, "min-width" => 0, "max-width" => 0, "used-width" => null, "absolute" => 0, "percent" => 0, "auto" => \true];
182 }
183 return $this->_columns[$i];
184 }
185 /**
186 * @return array
187 */
188 public function &get_rows()
189 {
190 return $this->_rows;
191 }
192 /**
193 * @param int $j
194 *
195 * @return mixed
196 */
197 public function &get_row($j)
198 {
199 if (!isset($this->_rows[$j])) {
200 $this->_rows[$j] = ["y" => 0, "first-column" => 0, "height" => null];
201 }
202 return $this->_rows[$j];
203 }
204 /**
205 * @param int $i
206 * @param int $j
207 * @param mixed $h_v
208 * @param null|mixed $prop
209 *
210 * @return mixed
211 */
212 public function get_border($i, $j, $h_v, $prop = null)
213 {
214 if (!isset($this->_borders[$i][$j][$h_v])) {
215 $this->_borders[$i][$j][$h_v] = ["width" => 0, "style" => "solid", "color" => "black"];
216 }
217 if (isset($prop)) {
218 return $this->_borders[$i][$j][$h_v][$prop];
219 }
220 return $this->_borders[$i][$j][$h_v];
221 }
222 /**
223 * @param int $i
224 * @param int $j
225 *
226 * @return array
227 */
228 public function get_border_properties($i, $j)
229 {
230 return ["top" => $this->get_border($i, $j, "horizontal"), "right" => $this->get_border($i, $j + 1, "vertical"), "bottom" => $this->get_border($i + 1, $j, "horizontal"), "left" => $this->get_border($i, $j, "vertical")];
231 }
232 /**
233 * @param Frame $frame
234 *
235 * @return array|null
236 */
237 public function get_spanned_cells(Frame $frame)
238 {
239 $key = $frame->get_id();
240 if (isset($this->_frames[$key])) {
241 return $this->_frames[$key];
242 }
243 return null;
244 }
245 /**
246 * @param Frame $frame
247 *
248 * @return bool
249 */
250 public function frame_exists_in_cellmap(Frame $frame)
251 {
252 $key = $frame->get_id();
253 return isset($this->_frames[$key]);
254 }
255 /**
256 * @param Frame $frame
257 *
258 * @return array
259 * @throws Exception
260 */
261 public function get_frame_position(Frame $frame)
262 {
263 global $_dompdf_warnings;
264 $key = $frame->get_id();
265 if (!isset($this->_frames[$key])) {
266 throw new Exception("Frame not found in cellmap");
267 }
268 // Positions are stored relative to the table position
269 [$table_x, $table_y] = $this->_table->get_position();
270 $col = $this->_frames[$key]["columns"][0];
271 $row = $this->_frames[$key]["rows"][0];
272 if (!isset($this->_columns[$col])) {
273 $_dompdf_warnings[] = "Frame not found in columns array. Check your table layout for missing or extra TDs.";
274 $x = $table_x;
275 } else {
276 $x = $table_x + $this->_columns[$col]["x"];
277 }
278 if (!isset($this->_rows[$row])) {
279 $_dompdf_warnings[] = "Frame not found in row array. Check your table layout for missing or extra TDs.";
280 $y = $table_y;
281 } else {
282 $y = $table_y + $this->_rows[$row]["y"];
283 }
284 return [$x, $y, "x" => $x, "y" => $y];
285 }
286 /**
287 * @param Frame $frame
288 *
289 * @return int
290 * @throws Exception
291 */
292 public function get_frame_width(Frame $frame)
293 {
294 $key = $frame->get_id();
295 if (!isset($this->_frames[$key])) {
296 throw new Exception("Frame not found in cellmap");
297 }
298 $cols = $this->_frames[$key]["columns"];
299 $w = 0;
300 foreach ($cols as $i) {
301 $w += $this->_columns[$i]["used-width"];
302 }
303 return $w;
304 }
305 /**
306 * @param Frame $frame
307 *
308 * @return int
309 * @throws Exception
310 * @throws Exception
311 */
312 public function get_frame_height(Frame $frame)
313 {
314 $key = $frame->get_id();
315 if (!isset($this->_frames[$key])) {
316 throw new Exception("Frame not found in cellmap");
317 }
318 $rows = $this->_frames[$key]["rows"];
319 $h = 0;
320 foreach ($rows as $i) {
321 if (!isset($this->_rows[$i])) {
322 throw new Exception("The row #{$i} could not be found, please file an issue in the tracker with the HTML code");
323 }
324 $h += $this->_rows[$i]["height"];
325 }
326 return $h;
327 }
328 /**
329 * @param int $j
330 * @param mixed $width
331 */
332 public function set_column_width($j, $width)
333 {
334 if ($this->_columns_locked) {
335 return;
336 }
337 $col =& $this->get_column($j);
338 $col["used-width"] = $width;
339 $next_col =& $this->get_column($j + 1);
340 $next_col["x"] = $col["x"] + $width;
341 }
342 /**
343 * @param int $i
344 * @param long $height
345 */
346 public function set_row_height($i, $height)
347 {
348 $row =& $this->get_row($i);
349 if ($height > $row["height"]) {
350 $row["height"] = $height;
351 }
352 $next_row =& $this->get_row($i + 1);
353 $next_row["y"] = $row["y"] + $row["height"];
354 }
355 /**
356 * https://www.w3.org/TR/CSS21/tables.html#border-conflict-resolution
357 *
358 * @param int $i
359 * @param int $j
360 * @param string $h_v `horizontal` or `vertical`
361 * @param array $border_spec
362 */
363 protected function resolve_border(int $i, int $j, string $h_v, array $border_spec) : void
364 {
365 if (!isset($this->_borders[$i][$j][$h_v])) {
366 $this->_borders[$i][$j][$h_v] = $border_spec;
367 return;
368 }
369 $border = $this->_borders[$i][$j][$h_v];
370 $n_width = $border_spec["width"];
371 $n_style = $border_spec["style"];
372 $o_width = $border["width"];
373 $o_style = $border["style"];
374 if ($o_style === "hidden") {
375 return;
376 }
377 // A style of `none` has lowest priority independent of its specified
378 // width here, as its resolved width is always 0
379 if ($n_style === "hidden" || $n_width > $o_width || $o_width == $n_width && isset(self::BORDER_STYLE_SCORE[$n_style]) && isset(self::BORDER_STYLE_SCORE[$o_style]) && self::BORDER_STYLE_SCORE[$n_style] > self::BORDER_STYLE_SCORE[$o_style]) {
380 $this->_borders[$i][$j][$h_v] = $border_spec;
381 }
382 }
383 /**
384 * Get the resolved border properties for the given frame.
385 *
386 * @param AbstractFrameDecorator $frame
387 *
388 * @return array[]
389 */
390 protected function get_resolved_border(AbstractFrameDecorator $frame) : array
391 {
392 $key = $frame->get_id();
393 $columns = $this->_frames[$key]["columns"];
394 $rows = $this->_frames[$key]["rows"];
395 $first_col = $columns[0];
396 $last_col = $columns[\count($columns) - 1];
397 $first_row = $rows[0];
398 $last_row = $rows[\count($rows) - 1];
399 $max_top = null;
400 $max_bottom = null;
401 $max_left = null;
402 $max_right = null;
403 foreach ($columns as $col) {
404 $top = $this->_borders[$first_row][$col]["horizontal"];
405 $bottom = $this->_borders[$last_row + 1][$col]["horizontal"];
406 if ($max_top === null || $top["width"] > $max_top["width"]) {
407 $max_top = $top;
408 }
409 if ($max_bottom === null || $bottom["width"] > $max_bottom["width"]) {
410 $max_bottom = $bottom;
411 }
412 }
413 foreach ($rows as $row) {
414 $left = $this->_borders[$row][$first_col]["vertical"];
415 $right = $this->_borders[$row][$last_col + 1]["vertical"];
416 if ($max_left === null || $left["width"] > $max_left["width"]) {
417 $max_left = $left;
418 }
419 if ($max_right === null || $right["width"] > $max_right["width"]) {
420 $max_right = $right;
421 }
422 }
423 return [$max_top, $max_right, $max_bottom, $max_left];
424 }
425 /**
426 * @param AbstractFrameDecorator $frame
427 */
428 public function add_frame(Frame $frame) : void
429 {
430 $style = $frame->get_style();
431 $display = $style->display;
432 $collapse = $this->_table->get_style()->border_collapse === "collapse";
433 // Recursively add the frames within the table, its row groups and rows
434 if ($frame === $this->_table || $display === "table-row" || \in_array($display, TableFrameDecorator::ROW_GROUPS, \true)) {
435 $start_row = $this->__row;
436 foreach ($frame->get_children() as $child) {
437 $this->add_frame($child);
438 }
439 if ($display === "table-row") {
440 $this->add_row();
441 }
442 $num_rows = $this->__row - $start_row - 1;
443 $key = $frame->get_id();
444 // Row groups always span across the entire table
445 $this->_frames[$key]["columns"] = \range(0, \max(0, $this->_num_cols - 1));
446 $this->_frames[$key]["rows"] = \range($start_row, \max(0, $this->__row - 1));
447 $this->_frames[$key]["frame"] = $frame;
448 if ($collapse) {
449 $bp = $style->get_border_properties();
450 // Resolve vertical borders
451 for ($i = 0; $i < $num_rows + 1; $i++) {
452 $this->resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
453 $this->resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
454 }
455 // Resolve horizontal borders
456 for ($j = 0; $j < $this->_num_cols; $j++) {
457 $this->resolve_border($start_row, $j, "horizontal", $bp["top"]);
458 $this->resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
459 }
460 if ($frame === $this->_table) {
461 // Clear borders because the cells are now using them. The
462 // border width still needs to be set to half the resolved
463 // width so that the table is positioned properly
464 [$top, $right, $bottom, $left] = $this->get_resolved_border($frame);
465 $style->set_used("border_top_width", $top["width"] / 2);
466 $style->set_used("border_right_width", $right["width"] / 2);
467 $style->set_used("border_bottom_width", $bottom["width"] / 2);
468 $style->set_used("border_left_width", $left["width"] / 2);
469 $style->set_used("border_style", "none");
470 }
471 }
472 if ($frame !== $this->_table) {
473 // Clear borders for rows and row groups. For the collapsed
474 // model, they have been resolved and are used by the cells now.
475 // For the separated model, they are ignored per spec
476 $style->set_used("border_width", 0);
477 $style->set_used("border_style", "none");
478 }
479 if ($frame === $this->_table) {
480 // Apply resolved borders to table cells and calculate column
481 // widths after all frames have been added
482 $this->calculate_column_widths();
483 }
484 return;
485 }
486 // Add the frame to the cellmap
487 $key = $frame->get_id();
488 $node = $frame->get_node();
489 $bp = $style->get_border_properties();
490 // Determine where this cell is going
491 $colspan = \max((int) $node->getAttribute("colspan"), 1);
492 $rowspan = \max((int) $node->getAttribute("rowspan"), 1);
493 // Find the next available column (fix by Ciro Mondueri)
494 $ac = $this->__col;
495 while (isset($this->_cells[$this->__row][$ac])) {
496 $ac++;
497 }
498 $this->__col = $ac;
499 // Rows:
500 for ($i = 0; $i < $rowspan; $i++) {
501 $row = $this->__row + $i;
502 $this->_frames[$key]["rows"][] = $row;
503 for ($j = 0; $j < $colspan; $j++) {
504 $this->_cells[$row][$this->__col + $j] = $frame;
505 }
506 if ($collapse) {
507 // Resolve vertical borders
508 $this->resolve_border($row, $this->__col, "vertical", $bp["left"]);
509 $this->resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]);
510 }
511 }
512 // Columns:
513 for ($j = 0; $j < $colspan; $j++) {
514 $col = $this->__col + $j;
515 $this->_frames[$key]["columns"][] = $col;
516 if ($collapse) {
517 // Resolve horizontal borders
518 $this->resolve_border($this->__row, $col, "horizontal", $bp["top"]);
519 $this->resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]);
520 }
521 }
522 $this->_frames[$key]["frame"] = $frame;
523 $this->__col += $colspan;
524 if ($this->__col > $this->_num_cols) {
525 $this->_num_cols = $this->__col;
526 }
527 }
528 /**
529 * Apply resolved borders to table cells and calculate column widths.
530 */
531 protected function calculate_column_widths() : void
532 {
533 $table = $this->_table;
534 $table_style = $table->get_style();
535 $collapse = $table_style->border_collapse === "collapse";
536 if ($collapse) {
537 $v_spacing = 0;
538 $h_spacing = 0;
539 } else {
540 // The additional 1/2 width gets added to the table proper
541 [$h, $v] = $table_style->border_spacing;
542 $v_spacing = $v / 2;
543 $h_spacing = $h / 2;
544 }
545 foreach ($this->_frames as $frame_info) {
546 /** @var TableCellFrameDecorator */
547 $frame = $frame_info["frame"];
548 $style = $frame->get_style();
549 $display = $style->display;
550 if ($display !== "table-cell") {
551 continue;
552 }
553 if ($collapse) {
554 // Set the resolved border at half width
555 [$top, $right, $bottom, $left] = $this->get_resolved_border($frame);
556 $style->set_used("border_top_width", $top["width"] / 2);
557 $style->set_used("border_top_style", $top["style"]);
558 $style->set_used("border_top_color", $top["color"]);
559 $style->set_used("border_right_width", $right["width"] / 2);
560 $style->set_used("border_right_style", $right["style"]);
561 $style->set_used("border_right_color", $right["color"]);
562 $style->set_used("border_bottom_width", $bottom["width"] / 2);
563 $style->set_used("border_bottom_style", $bottom["style"]);
564 $style->set_used("border_bottom_color", $bottom["color"]);
565 $style->set_used("border_left_width", $left["width"] / 2);
566 $style->set_used("border_left_style", $left["style"]);
567 $style->set_used("border_left_color", $left["color"]);
568 $style->set_used("margin", 0);
569 } else {
570 // Border spacing is effectively a margin between cells
571 $style->set_used("margin_top", $v_spacing);
572 $style->set_used("margin_bottom", $v_spacing);
573 $style->set_used("margin_left", $h_spacing);
574 $style->set_used("margin_right", $h_spacing);
575 }
576 if ($this->_columns_locked) {
577 continue;
578 }
579 // Column calculation for fixed-layout tables should ony use the first row's cells
580 // https://www.w3.org/TR/CSS2/tables.html#fixed-table-layout
581 if ($this->_fixed_layout && !\in_array(0, $frame_info["rows"], \true)) {
582 continue;
583 }
584 $node = $frame->get_node();
585 $colspan = \max((int) $node->getAttribute("colspan"), 1);
586 $first_col = $frame_info["columns"][0];
587 // Resolve the frame's width
588 if ($this->_fixed_layout) {
589 list($frame_min, $frame_max) = [0, 1.0E-9];
590 } else {
591 list($frame_min, $frame_max) = $frame->get_min_max_width();
592 }
593 $width = $style->width;
594 $val = null;
595 if (Helpers::is_percent($width) && $colspan === 1) {
596 $var = "percent";
597 $val = (float) \rtrim($width, "% ");
598 } elseif ($width !== "auto" && $colspan === 1) {
599 $var = "absolute";
600 $val = $frame_min;
601 }
602 $min = 0;
603 $max = 0;
604 for ($cs = 0; $cs < $colspan; $cs++) {
605 // Resolve the frame's width(s) with other cells
606 $col =& $this->get_column($first_col + $cs);
607 // Note: $var is either 'percent' or 'absolute'. We compare the
608 // requested percentage or absolute values with the existing widths
609 // and adjust accordingly.
610 if (isset($var) && $val > $col[$var]) {
611 $col[$var] = $val;
612 $col["auto"] = \false;
613 }
614 $min += $col["min-width"];
615 $max += $col["max-width"];
616 }
617 if ($frame_min > $min && $colspan === 1) {
618 // The frame needs more space. Expand each sub-column
619 // FIXME try to avoid putting this dummy value when table-layout:fixed
620 $inc = $this->_fixed_layout ? 1.0E-9 : $frame_min - $min;
621 for ($c = 0; $c < $colspan; $c++) {
622 $col =& $this->get_column($first_col + $c);
623 $col["min-width"] += $inc;
624 }
625 }
626 if ($frame_max > $max) {
627 // FIXME try to avoid putting this dummy value when table-layout:fixed
628 $inc = $this->_fixed_layout ? 1.0E-9 : ($frame_max - $max) / $colspan;
629 for ($c = 0; $c < $colspan; $c++) {
630 $col =& $this->get_column($first_col + $c);
631 $col["max-width"] += $inc;
632 }
633 }
634 }
635 // Adjust absolute columns so that the absolute (and max) width is the
636 // largest minimum width of all cells. This accounts for cells without
637 // absolute width within an absolute column
638 foreach ($this->_columns as &$col) {
639 if ($col["absolute"] > 0) {
640 $col["absolute"] = $col["min-width"];
641 $col["max-width"] = $col["min-width"];
642 }
643 }
644 }
645 protected function add_row() : void
646 {
647 $this->__row++;
648 $this->_num_rows++;
649 // Find the next available column
650 $i = 0;
651 while (isset($this->_cells[$this->__row][$i])) {
652 $i++;
653 }
654 $this->__col = $i;
655 }
656 /**
657 * Remove a row from the cellmap.
658 *
659 * @param Frame
660 */
661 public function remove_row(Frame $row)
662 {
663 $key = $row->get_id();
664 if (!isset($this->_frames[$key])) {
665 return;
666 // Presumably this row has already been removed
667 }
668 $this->__row = $this->_num_rows--;
669 $rows = $this->_frames[$key]["rows"];
670 $columns = $this->_frames[$key]["columns"];
671 // Remove all frames from this row
672 foreach ($rows as $r) {
673 foreach ($columns as $c) {
674 if (isset($this->_cells[$r][$c])) {
675 $id = $this->_cells[$r][$c]->get_id();
676 $this->_cells[$r][$c] = null;
677 unset($this->_cells[$r][$c]);
678 // has multiple rows?
679 if (isset($this->_frames[$id]) && \count($this->_frames[$id]["rows"]) > 1) {
680 // remove just the desired row, but leave the frame
681 if (($row_key = \array_search($r, $this->_frames[$id]["rows"])) !== \false) {
682 unset($this->_frames[$id]["rows"][$row_key]);
683 }
684 continue;
685 }
686 $this->_frames[$id] = null;
687 unset($this->_frames[$id]);
688 }
689 }
690 $this->_rows[$r] = null;
691 unset($this->_rows[$r]);
692 }
693 $this->_frames[$key] = null;
694 unset($this->_frames[$key]);
695 }
696 /**
697 * Remove a row group from the cellmap.
698 *
699 * @param Frame $group The group to remove
700 */
701 public function remove_row_group(Frame $group)
702 {
703 $key = $group->get_id();
704 if (!isset($this->_frames[$key])) {
705 return;
706 // Presumably this row has already been removed
707 }
708 $iter = $group->get_first_child();
709 while ($iter) {
710 $this->remove_row($iter);
711 $iter = $iter->get_next_sibling();
712 }
713 $this->_frames[$key] = null;
714 unset($this->_frames[$key]);
715 }
716 /**
717 * Update a row group after rows have been removed
718 *
719 * @param Frame $group The group to update
720 * @param Frame $last_row The last row in the row group
721 */
722 public function update_row_group(Frame $group, Frame $last_row)
723 {
724 $g_key = $group->get_id();
725 $first_index = $this->_frames[$g_key]["rows"][0];
726 $last_index = $first_index;
727 $row = $last_row;
728 while ($row = $row->get_prev_sibling()) {
729 $last_index++;
730 }
731 $this->_frames[$g_key]["rows"] = \range($first_index, $last_index);
732 }
733 public function assign_x_positions() : void
734 {
735 // Pre-condition: widths must be resolved and assigned to columns and
736 // column[0]["x"] must be set.
737 if ($this->_columns_locked) {
738 return;
739 }
740 $x = $this->_columns[0]["x"];
741 foreach (\array_keys($this->_columns) as $j) {
742 $this->_columns[$j]["x"] = $x;
743 $x += $this->_columns[$j]["used-width"];
744 }
745 }
746 public function assign_frame_heights() : void
747 {
748 // Pre-condition: widths and heights of each column & row must be
749 // calcluated
750 foreach ($this->_frames as $arr) {
751 $frame = $arr["frame"];
752 $h = 0.0;
753 foreach ($arr["rows"] as $row) {
754 if (!isset($this->_rows[$row])) {
755 // The row has been removed because of a page split, so skip it.
756 continue;
757 }
758 $h += $this->_rows[$row]["height"];
759 }
760 if ($frame instanceof TableCellFrameDecorator) {
761 $frame->set_cell_height($h);
762 } else {
763 $frame->get_style()->set_used("height", $h);
764 }
765 }
766 }
767 /**
768 * Re-adjust frame height if the table height is larger than its content
769 */
770 public function set_frame_heights(float $table_height, float $content_height) : void
771 {
772 // Distribute the increased height proportionally amongst each row
773 foreach ($this->_frames as $arr) {
774 $frame = $arr["frame"];
775 $h = 0.0;
776 foreach ($arr["rows"] as $row) {
777 if (!isset($this->_rows[$row])) {
778 continue;
779 }
780 $h += $this->_rows[$row]["height"];
781 }
782 if ($content_height > 0) {
783 $new_height = $h / $content_height * $table_height;
784 } else {
785 $new_height = 0.0;
786 }
787 if ($frame instanceof TableCellFrameDecorator) {
788 $frame->set_cell_height($new_height);
789 } else {
790 $frame->get_style()->set_used("height", $new_height);
791 }
792 }
793 }
794 /**
795 * Used for debugging:
796 *
797 * @return string
798 */
799 public function __toString() : string
800 {
801 $str = "";
802 $str .= "Columns:<br/>";
803 $str .= Helpers::pre_r($this->_columns, \true);
804 $str .= "Rows:<br/>";
805 $str .= Helpers::pre_r($this->_rows, \true);
806 $str .= "Frames:<br/>";
807 $arr = [];
808 foreach ($this->_frames as $key => $val) {
809 $arr[$key] = ["columns" => $val["columns"], "rows" => $val["rows"]];
810 }
811 $str .= Helpers::pre_r($arr, \true);
812 if (\php_sapi_name() == "cli") {
813 $str = \strip_tags(\str_replace(["<br/>", "<b>", "</b>"], ["\n", \chr(27) . "[01;33m", \chr(27) . "[0m"], $str));
814 }
815 return $str;
816 }
817 }
818