Completed
Branch master (8e512a)
by
unknown
17:03 queued 09:20
created
vendor/dompdf/dompdf/src/Frame/Factory.php 2 patches
Indentation   +232 added lines, -232 removed lines patch added patch discarded remove patch
@@ -28,236 +28,236 @@
 block discarded – undo
28 28
 class Factory
29 29
 {
30 30
 
31
-     /**
32
-     * Array of positioners for specific frame types
33
-     *
34
-     * @var AbstractPositioner[]
35
-     */
36
-    protected static $_positioners;
37
-
38
-    /**
39
-     * Decorate the root Frame
40
-     *
41
-     * @param $root   Frame The frame to decorate
42
-     * @param $dompdf Dompdf The dompdf instance
43
-     *
44
-     * @return PageFrameDecorator
45
-     */
46
-    static function decorate_root(Frame $root, Dompdf $dompdf)
47
-    {
48
-        $frame = new PageFrameDecorator($root, $dompdf);
49
-        $frame->set_reflower(new PageFrameReflower($frame));
50
-        $root->set_decorator($frame);
51
-
52
-        return $frame;
53
-    }
54
-
55
-    /**
56
-     * Decorate a Frame
57
-     *
58
-     * @param Frame $frame   The frame to decorate
59
-     * @param Dompdf $dompdf The dompdf instance
60
-     * @param Frame $root    The root of the frame
61
-     *
62
-     * @throws Exception
63
-     * @return AbstractFrameDecorator
64
-     * FIXME: this is admittedly a little smelly...
65
-     */
66
-    static function decorate_frame(Frame $frame, Dompdf $dompdf, Frame $root = null)
67
-    {
68
-        $style = $frame->get_style();
69
-        $display = $style->display;
70
-
71
-        switch ($display) {
72
-
73
-            case "block":
74
-                $positioner = "Block";
75
-                $decorator = "Block";
76
-                $reflower = "Block";
77
-                break;
78
-
79
-            case "inline-block":
80
-                $positioner = "Inline";
81
-                $decorator = "Block";
82
-                $reflower = "Block";
83
-                break;
84
-
85
-            case "inline":
86
-                $positioner = "Inline";
87
-                if ($frame->is_text_node()) {
88
-                    $decorator = "Text";
89
-                    $reflower = "Text";
90
-                } else {
91
-                    $decorator = "Inline";
92
-                    $reflower = "Inline";
93
-                }
94
-                break;
95
-
96
-            case "table":
97
-                $positioner = "Block";
98
-                $decorator = "Table";
99
-                $reflower = "Table";
100
-                break;
101
-
102
-            case "inline-table":
103
-                $positioner = "Inline";
104
-                $decorator = "Table";
105
-                $reflower = "Table";
106
-                break;
107
-
108
-            case "table-row-group":
109
-            case "table-header-group":
110
-            case "table-footer-group":
111
-                $positioner = "NullPositioner";
112
-                $decorator = "TableRowGroup";
113
-                $reflower = "TableRowGroup";
114
-                break;
115
-
116
-            case "table-row":
117
-                $positioner = "NullPositioner";
118
-                $decorator = "TableRow";
119
-                $reflower = "TableRow";
120
-                break;
121
-
122
-            case "table-cell":
123
-                $positioner = "TableCell";
124
-                $decorator = "TableCell";
125
-                $reflower = "TableCell";
126
-                break;
127
-
128
-            case "list-item":
129
-                $positioner = "Block";
130
-                $decorator = "Block";
131
-                $reflower = "Block";
132
-                break;
133
-
134
-            case "-dompdf-list-bullet":
135
-                if ($style->list_style_position === "inside") {
136
-                    $positioner = "Inline";
137
-                } else {
138
-                    $positioner = "ListBullet";
139
-                }
140
-
141
-                if ($style->list_style_image !== "none") {
142
-                    $decorator = "ListBulletImage";
143
-                } else {
144
-                    $decorator = "ListBullet";
145
-                }
146
-
147
-                $reflower = "ListBullet";
148
-                break;
149
-
150
-            case "-dompdf-image":
151
-                $positioner = "Inline";
152
-                $decorator = "Image";
153
-                $reflower = "Image";
154
-                break;
155
-
156
-            case "-dompdf-br":
157
-                $positioner = "Inline";
158
-                $decorator = "Inline";
159
-                $reflower = "Inline";
160
-                break;
161
-
162
-            default:
163
-            case "none":
164
-                if ($style->_dompdf_keep !== "yes") {
165
-                    // Remove the node and the frame
166
-                    $frame->get_parent()->remove_child($frame);
167
-                    return;
168
-                }
169
-
170
-                $positioner = "NullPositioner";
171
-                $decorator = "NullFrameDecorator";
172
-                $reflower = "NullFrameReflower";
173
-                break;
174
-        }
175
-
176
-        // Handle CSS position
177
-        $position = $style->position;
178
-
179
-        if ($position === "absolute") {
180
-            $positioner = "Absolute";
181
-        } else {
182
-            if ($position === "fixed") {
183
-                $positioner = "Fixed";
184
-            }
185
-        }
186
-
187
-        $node = $frame->get_node();
188
-
189
-        // Handle nodeName
190
-        if ($node->nodeName === "img") {
191
-            $style->set_prop("display", "-dompdf-image");
192
-            $decorator = "Image";
193
-            $reflower = "Image";
194
-        }
195
-
196
-        $decorator  = "Dompdf\\FrameDecorator\\$decorator";
197
-        $reflower   = "Dompdf\\FrameReflower\\$reflower";
198
-
199
-        /** @var AbstractFrameDecorator $deco */
200
-        $deco = new $decorator($frame, $dompdf);
201
-
202
-        $deco->set_positioner(self::getPositionerInstance($positioner));
203
-        $deco->set_reflower(new $reflower($deco, $dompdf->getFontMetrics()));
204
-
205
-        if ($root) {
206
-            $deco->set_root($root);
207
-        }
208
-
209
-        if ($display === "list-item") {
210
-            // Insert a list-bullet frame
211
-            $xml = $dompdf->getDom();
212
-            $bullet_node = $xml->createElement("bullet"); // arbitrary choice
213
-            $b_f = new Frame($bullet_node);
214
-
215
-            $node = $frame->get_node();
216
-            $parent_node = $node->parentNode;
217
-            if ($parent_node && $parent_node instanceof \DOMElement) {
218
-                if (!$parent_node->hasAttribute("dompdf-children-count")) {
219
-                    $xpath = new DOMXPath($xml);
220
-                    $count = $xpath->query("li", $parent_node)->length;
221
-                    $parent_node->setAttribute("dompdf-children-count", $count);
222
-                }
223
-
224
-                if (is_numeric($node->getAttribute("value"))) {
225
-                    $index = intval($node->getAttribute("value"));
226
-                } else {
227
-                    if (!$parent_node->hasAttribute("dompdf-counter")) {
228
-                        $index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1);
229
-                    } else {
230
-                        $index = (int)$parent_node->getAttribute("dompdf-counter") + 1;
231
-                    }
232
-                }
233
-
234
-                $parent_node->setAttribute("dompdf-counter", $index);
235
-                $bullet_node->setAttribute("dompdf-counter", $index);
236
-            }
237
-
238
-            $new_style = $dompdf->getCss()->create_style();
239
-            $new_style->set_prop("display", "-dompdf-list-bullet");
240
-            $new_style->inherit($style);
241
-            $b_f->set_style($new_style);
242
-
243
-            $deco->prepend_child(Factory::decorate_frame($b_f, $dompdf, $root));
244
-        }
245
-
246
-        return $deco;
247
-    }
248
-
249
-    /**
250
-     * Creates Positioners
251
-     *
252
-     * @param string $type type of positioner to use
253
-     * @return AbstractPositioner
254
-     */
255
-    protected static function getPositionerInstance($type)
256
-    {
257
-        if (!isset(self::$_positioners[$type])) {
258
-            $class = '\\Dompdf\\Positioner\\'.$type;
259
-            self::$_positioners[$type] = new $class();
260
-        }
261
-        return self::$_positioners[$type];
262
-    }
31
+	 /**
32
+	  * Array of positioners for specific frame types
33
+	  *
34
+	  * @var AbstractPositioner[]
35
+	  */
36
+	protected static $_positioners;
37
+
38
+	/**
39
+	 * Decorate the root Frame
40
+	 *
41
+	 * @param $root   Frame The frame to decorate
42
+	 * @param $dompdf Dompdf The dompdf instance
43
+	 *
44
+	 * @return PageFrameDecorator
45
+	 */
46
+	static function decorate_root(Frame $root, Dompdf $dompdf)
47
+	{
48
+		$frame = new PageFrameDecorator($root, $dompdf);
49
+		$frame->set_reflower(new PageFrameReflower($frame));
50
+		$root->set_decorator($frame);
51
+
52
+		return $frame;
53
+	}
54
+
55
+	/**
56
+	 * Decorate a Frame
57
+	 *
58
+	 * @param Frame $frame   The frame to decorate
59
+	 * @param Dompdf $dompdf The dompdf instance
60
+	 * @param Frame $root    The root of the frame
61
+	 *
62
+	 * @throws Exception
63
+	 * @return AbstractFrameDecorator
64
+	 * FIXME: this is admittedly a little smelly...
65
+	 */
66
+	static function decorate_frame(Frame $frame, Dompdf $dompdf, Frame $root = null)
67
+	{
68
+		$style = $frame->get_style();
69
+		$display = $style->display;
70
+
71
+		switch ($display) {
72
+
73
+			case "block":
74
+				$positioner = "Block";
75
+				$decorator = "Block";
76
+				$reflower = "Block";
77
+				break;
78
+
79
+			case "inline-block":
80
+				$positioner = "Inline";
81
+				$decorator = "Block";
82
+				$reflower = "Block";
83
+				break;
84
+
85
+			case "inline":
86
+				$positioner = "Inline";
87
+				if ($frame->is_text_node()) {
88
+					$decorator = "Text";
89
+					$reflower = "Text";
90
+				} else {
91
+					$decorator = "Inline";
92
+					$reflower = "Inline";
93
+				}
94
+				break;
95
+
96
+			case "table":
97
+				$positioner = "Block";
98
+				$decorator = "Table";
99
+				$reflower = "Table";
100
+				break;
101
+
102
+			case "inline-table":
103
+				$positioner = "Inline";
104
+				$decorator = "Table";
105
+				$reflower = "Table";
106
+				break;
107
+
108
+			case "table-row-group":
109
+			case "table-header-group":
110
+			case "table-footer-group":
111
+				$positioner = "NullPositioner";
112
+				$decorator = "TableRowGroup";
113
+				$reflower = "TableRowGroup";
114
+				break;
115
+
116
+			case "table-row":
117
+				$positioner = "NullPositioner";
118
+				$decorator = "TableRow";
119
+				$reflower = "TableRow";
120
+				break;
121
+
122
+			case "table-cell":
123
+				$positioner = "TableCell";
124
+				$decorator = "TableCell";
125
+				$reflower = "TableCell";
126
+				break;
127
+
128
+			case "list-item":
129
+				$positioner = "Block";
130
+				$decorator = "Block";
131
+				$reflower = "Block";
132
+				break;
133
+
134
+			case "-dompdf-list-bullet":
135
+				if ($style->list_style_position === "inside") {
136
+					$positioner = "Inline";
137
+				} else {
138
+					$positioner = "ListBullet";
139
+				}
140
+
141
+				if ($style->list_style_image !== "none") {
142
+					$decorator = "ListBulletImage";
143
+				} else {
144
+					$decorator = "ListBullet";
145
+				}
146
+
147
+				$reflower = "ListBullet";
148
+				break;
149
+
150
+			case "-dompdf-image":
151
+				$positioner = "Inline";
152
+				$decorator = "Image";
153
+				$reflower = "Image";
154
+				break;
155
+
156
+			case "-dompdf-br":
157
+				$positioner = "Inline";
158
+				$decorator = "Inline";
159
+				$reflower = "Inline";
160
+				break;
161
+
162
+			default:
163
+			case "none":
164
+				if ($style->_dompdf_keep !== "yes") {
165
+					// Remove the node and the frame
166
+					$frame->get_parent()->remove_child($frame);
167
+					return;
168
+				}
169
+
170
+				$positioner = "NullPositioner";
171
+				$decorator = "NullFrameDecorator";
172
+				$reflower = "NullFrameReflower";
173
+				break;
174
+		}
175
+
176
+		// Handle CSS position
177
+		$position = $style->position;
178
+
179
+		if ($position === "absolute") {
180
+			$positioner = "Absolute";
181
+		} else {
182
+			if ($position === "fixed") {
183
+				$positioner = "Fixed";
184
+			}
185
+		}
186
+
187
+		$node = $frame->get_node();
188
+
189
+		// Handle nodeName
190
+		if ($node->nodeName === "img") {
191
+			$style->set_prop("display", "-dompdf-image");
192
+			$decorator = "Image";
193
+			$reflower = "Image";
194
+		}
195
+
196
+		$decorator  = "Dompdf\\FrameDecorator\\$decorator";
197
+		$reflower   = "Dompdf\\FrameReflower\\$reflower";
198
+
199
+		/** @var AbstractFrameDecorator $deco */
200
+		$deco = new $decorator($frame, $dompdf);
201
+
202
+		$deco->set_positioner(self::getPositionerInstance($positioner));
203
+		$deco->set_reflower(new $reflower($deco, $dompdf->getFontMetrics()));
204
+
205
+		if ($root) {
206
+			$deco->set_root($root);
207
+		}
208
+
209
+		if ($display === "list-item") {
210
+			// Insert a list-bullet frame
211
+			$xml = $dompdf->getDom();
212
+			$bullet_node = $xml->createElement("bullet"); // arbitrary choice
213
+			$b_f = new Frame($bullet_node);
214
+
215
+			$node = $frame->get_node();
216
+			$parent_node = $node->parentNode;
217
+			if ($parent_node && $parent_node instanceof \DOMElement) {
218
+				if (!$parent_node->hasAttribute("dompdf-children-count")) {
219
+					$xpath = new DOMXPath($xml);
220
+					$count = $xpath->query("li", $parent_node)->length;
221
+					$parent_node->setAttribute("dompdf-children-count", $count);
222
+				}
223
+
224
+				if (is_numeric($node->getAttribute("value"))) {
225
+					$index = intval($node->getAttribute("value"));
226
+				} else {
227
+					if (!$parent_node->hasAttribute("dompdf-counter")) {
228
+						$index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1);
229
+					} else {
230
+						$index = (int)$parent_node->getAttribute("dompdf-counter") + 1;
231
+					}
232
+				}
233
+
234
+				$parent_node->setAttribute("dompdf-counter", $index);
235
+				$bullet_node->setAttribute("dompdf-counter", $index);
236
+			}
237
+
238
+			$new_style = $dompdf->getCss()->create_style();
239
+			$new_style->set_prop("display", "-dompdf-list-bullet");
240
+			$new_style->inherit($style);
241
+			$b_f->set_style($new_style);
242
+
243
+			$deco->prepend_child(Factory::decorate_frame($b_f, $dompdf, $root));
244
+		}
245
+
246
+		return $deco;
247
+	}
248
+
249
+	/**
250
+	 * Creates Positioners
251
+	 *
252
+	 * @param string $type type of positioner to use
253
+	 * @return AbstractPositioner
254
+	 */
255
+	protected static function getPositionerInstance($type)
256
+	{
257
+		if (!isset(self::$_positioners[$type])) {
258
+			$class = '\\Dompdf\\Positioner\\'.$type;
259
+			self::$_positioners[$type] = new $class();
260
+		}
261
+		return self::$_positioners[$type];
262
+	}
263 263
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -215,7 +215,7 @@  discard block
 block discarded – undo
215 215
             $node = $frame->get_node();
216 216
             $parent_node = $node->parentNode;
217 217
             if ($parent_node && $parent_node instanceof \DOMElement) {
218
-                if (!$parent_node->hasAttribute("dompdf-children-count")) {
218
+                if ( ! $parent_node->hasAttribute("dompdf-children-count")) {
219 219
                     $xpath = new DOMXPath($xml);
220 220
                     $count = $xpath->query("li", $parent_node)->length;
221 221
                     $parent_node->setAttribute("dompdf-children-count", $count);
@@ -224,10 +224,10 @@  discard block
 block discarded – undo
224 224
                 if (is_numeric($node->getAttribute("value"))) {
225 225
                     $index = intval($node->getAttribute("value"));
226 226
                 } else {
227
-                    if (!$parent_node->hasAttribute("dompdf-counter")) {
227
+                    if ( ! $parent_node->hasAttribute("dompdf-counter")) {
228 228
                         $index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1);
229 229
                     } else {
230
-                        $index = (int)$parent_node->getAttribute("dompdf-counter") + 1;
230
+                        $index = (int) $parent_node->getAttribute("dompdf-counter") + 1;
231 231
                     }
232 232
                 }
233 233
 
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
      */
255 255
     protected static function getPositionerInstance($type)
256 256
     {
257
-        if (!isset(self::$_positioners[$type])) {
257
+        if ( ! isset(self::$_positioners[$type])) {
258 258
             $class = '\\Dompdf\\Positioner\\'.$type;
259 259
             self::$_positioners[$type] = new $class();
260 260
         }
Please login to merge, or discard this patch.
vendor/dompdf/dompdf/src/Adapter/CPDF.php 1 patch
Indentation   +900 added lines, -900 removed lines patch added patch discarded remove patch
@@ -34,911 +34,911 @@
 block discarded – undo
34 34
 class CPDF implements Canvas
35 35
 {
36 36
 
37
-    /**
38
-     * Dimensions of paper sizes in points
39
-     *
40
-     * @var array
41
-     */
42
-    static $PAPER_SIZES = [
43
-        "4a0" => [0.0, 0.0, 4767.87, 6740.79],
44
-        "2a0" => [0.0, 0.0, 3370.39, 4767.87],
45
-        "a0" => [0.0, 0.0, 2383.94, 3370.39],
46
-        "a1" => [0.0, 0.0, 1683.78, 2383.94],
47
-        "a2" => [0.0, 0.0, 1190.55, 1683.78],
48
-        "a3" => [0.0, 0.0, 841.89, 1190.55],
49
-        "a4" => [0.0, 0.0, 595.28, 841.89],
50
-        "a5" => [0.0, 0.0, 419.53, 595.28],
51
-        "a6" => [0.0, 0.0, 297.64, 419.53],
52
-        "a7" => [0.0, 0.0, 209.76, 297.64],
53
-        "a8" => [0.0, 0.0, 147.40, 209.76],
54
-        "a9" => [0.0, 0.0, 104.88, 147.40],
55
-        "a10" => [0.0, 0.0, 73.70, 104.88],
56
-        "b0" => [0.0, 0.0, 2834.65, 4008.19],
57
-        "b1" => [0.0, 0.0, 2004.09, 2834.65],
58
-        "b2" => [0.0, 0.0, 1417.32, 2004.09],
59
-        "b3" => [0.0, 0.0, 1000.63, 1417.32],
60
-        "b4" => [0.0, 0.0, 708.66, 1000.63],
61
-        "b5" => [0.0, 0.0, 498.90, 708.66],
62
-        "b6" => [0.0, 0.0, 354.33, 498.90],
63
-        "b7" => [0.0, 0.0, 249.45, 354.33],
64
-        "b8" => [0.0, 0.0, 175.75, 249.45],
65
-        "b9" => [0.0, 0.0, 124.72, 175.75],
66
-        "b10" => [0.0, 0.0, 87.87, 124.72],
67
-        "c0" => [0.0, 0.0, 2599.37, 3676.54],
68
-        "c1" => [0.0, 0.0, 1836.85, 2599.37],
69
-        "c2" => [0.0, 0.0, 1298.27, 1836.85],
70
-        "c3" => [0.0, 0.0, 918.43, 1298.27],
71
-        "c4" => [0.0, 0.0, 649.13, 918.43],
72
-        "c5" => [0.0, 0.0, 459.21, 649.13],
73
-        "c6" => [0.0, 0.0, 323.15, 459.21],
74
-        "c7" => [0.0, 0.0, 229.61, 323.15],
75
-        "c8" => [0.0, 0.0, 161.57, 229.61],
76
-        "c9" => [0.0, 0.0, 113.39, 161.57],
77
-        "c10" => [0.0, 0.0, 79.37, 113.39],
78
-        "ra0" => [0.0, 0.0, 2437.80, 3458.27],
79
-        "ra1" => [0.0, 0.0, 1729.13, 2437.80],
80
-        "ra2" => [0.0, 0.0, 1218.90, 1729.13],
81
-        "ra3" => [0.0, 0.0, 864.57, 1218.90],
82
-        "ra4" => [0.0, 0.0, 609.45, 864.57],
83
-        "sra0" => [0.0, 0.0, 2551.18, 3628.35],
84
-        "sra1" => [0.0, 0.0, 1814.17, 2551.18],
85
-        "sra2" => [0.0, 0.0, 1275.59, 1814.17],
86
-        "sra3" => [0.0, 0.0, 907.09, 1275.59],
87
-        "sra4" => [0.0, 0.0, 637.80, 907.09],
88
-        "letter" => [0.0, 0.0, 612.00, 792.00],
89
-        "half-letter" => [0.0, 0.0, 396.00, 612.00],
90
-        "legal" => [0.0, 0.0, 612.00, 1008.00],
91
-        "ledger" => [0.0, 0.0, 1224.00, 792.00],
92
-        "tabloid" => [0.0, 0.0, 792.00, 1224.00],
93
-        "executive" => [0.0, 0.0, 521.86, 756.00],
94
-        "folio" => [0.0, 0.0, 612.00, 936.00],
95
-        "commercial #10 envelope" => [0.0, 0.0, 684.00, 297.00],
96
-        "catalog #10 1/2 envelope" => [0.0, 0.0, 648.00, 864.00],
97
-        "8.5x11" => [0.0, 0.0, 612.00, 792.00],
98
-        "8.5x14" => [0.0, 0.0, 612.00, 1008.00],
99
-        "11x17" => [0.0, 0.0, 792.00, 1224.00],
100
-    ];
101
-
102
-    /**
103
-     * The Dompdf object
104
-     *
105
-     * @var Dompdf
106
-     */
107
-    protected $_dompdf;
108
-
109
-    /**
110
-     * Instance of Cpdf class
111
-     *
112
-     * @var \Dompdf\Cpdf
113
-     */
114
-    protected $_pdf;
115
-
116
-    /**
117
-     * PDF width, in points
118
-     *
119
-     * @var float
120
-     */
121
-    protected $_width;
122
-
123
-    /**
124
-     * PDF height, in points
125
-     *
126
-     * @var float
127
-     */
128
-    protected $_height;
129
-
130
-    /**
131
-     * Current page number
132
-     *
133
-     * @var int
134
-     */
135
-    protected $_page_number;
136
-
137
-    /**
138
-     * Total number of pages
139
-     *
140
-     * @var int
141
-     */
142
-    protected $_page_count;
143
-
144
-    /**
145
-     * Array of pages for accessing after rendering is initially complete
146
-     *
147
-     * @var array
148
-     */
149
-    protected $_pages;
150
-
151
-    /**
152
-     * Currently-applied opacity level (0 - 1)
153
-     *
154
-     * @var float
155
-     */
156
-    protected $_current_opacity = 1;
157
-
158
-    public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null)
159
-    {
160
-        if (is_array($paper)) {
161
-            $size = array_map("floatval", $paper);
162
-        } else {
163
-            $paper = strtolower($paper);
164
-            $size = self::$PAPER_SIZES[$paper] ?? self::$PAPER_SIZES["letter"];
165
-        }
166
-
167
-        if (strtolower($orientation) === "landscape") {
168
-            [$size[2], $size[3]] = [$size[3], $size[2]];
169
-        }
170
-
171
-        if ($dompdf === null) {
172
-            $this->_dompdf = new Dompdf();
173
-        } else {
174
-            $this->_dompdf = $dompdf;
175
-        }
176
-
177
-        $this->_pdf = new \Dompdf\Cpdf(
178
-            $size,
179
-            true,
180
-            $this->_dompdf->getOptions()->getFontCache(),
181
-            $this->_dompdf->getOptions()->getTempDir()
182
-        );
183
-
184
-        $this->_pdf->addInfo("Producer", sprintf("%s + CPDF", $this->_dompdf->version));
185
-        $time = substr_replace(date('YmdHisO'), '\'', -2, 0) . '\'';
186
-        $this->_pdf->addInfo("CreationDate", "D:$time");
187
-        $this->_pdf->addInfo("ModDate", "D:$time");
188
-
189
-        $this->_width = $size[2] - $size[0];
190
-        $this->_height = $size[3] - $size[1];
191
-
192
-        $this->_page_number = $this->_page_count = 1;
193
-
194
-        $this->_pages = [$this->_pdf->getFirstPageId()];
195
-    }
196
-
197
-    public function get_dompdf()
198
-    {
199
-        return $this->_dompdf;
200
-    }
201
-
202
-    /**
203
-     * Returns the Cpdf instance
204
-     *
205
-     * @return \Dompdf\Cpdf
206
-     */
207
-    public function get_cpdf()
208
-    {
209
-        return $this->_pdf;
210
-    }
211
-
212
-    public function add_info(string $label, string $value): void
213
-    {
214
-        $this->_pdf->addInfo($label, $value);
215
-    }
216
-
217
-    /**
218
-     * Opens a new 'object'
219
-     *
220
-     * While an object is open, all drawing actions are recorded in the object,
221
-     * as opposed to being drawn on the current page.  Objects can be added
222
-     * later to a specific page or to several pages.
223
-     *
224
-     * The return value is an integer ID for the new object.
225
-     *
226
-     * @see CPDF::close_object()
227
-     * @see CPDF::add_object()
228
-     *
229
-     * @return int
230
-     */
231
-    public function open_object()
232
-    {
233
-        $ret = $this->_pdf->openObject();
234
-        $this->_pdf->saveState();
235
-        return $ret;
236
-    }
237
-
238
-    /**
239
-     * Reopens an existing 'object'
240
-     *
241
-     * @see CPDF::open_object()
242
-     * @param int $object the ID of a previously opened object
243
-     */
244
-    public function reopen_object($object)
245
-    {
246
-        $this->_pdf->reopenObject($object);
247
-        $this->_pdf->saveState();
248
-    }
249
-
250
-    /**
251
-     * Closes the current 'object'
252
-     *
253
-     * @see CPDF::open_object()
254
-     */
255
-    public function close_object()
256
-    {
257
-        $this->_pdf->restoreState();
258
-        $this->_pdf->closeObject();
259
-    }
260
-
261
-    /**
262
-     * Adds a specified 'object' to the document
263
-     *
264
-     * $object int specifying an object created with {@link
265
-     * CPDF::open_object()}.  $where can be one of:
266
-     * - 'add' add to current page only
267
-     * - 'all' add to every page from the current one onwards
268
-     * - 'odd' add to all odd numbered pages from now on
269
-     * - 'even' add to all even numbered pages from now on
270
-     * - 'next' add the object to the next page only
271
-     * - 'nextodd' add to all odd numbered pages from the next one
272
-     * - 'nexteven' add to all even numbered pages from the next one
273
-     *
274
-     * @see Cpdf::addObject()
275
-     *
276
-     * @param int $object
277
-     * @param string $where
278
-     */
279
-    public function add_object($object, $where = 'all')
280
-    {
281
-        $this->_pdf->addObject($object, $where);
282
-    }
283
-
284
-    /**
285
-     * Stops the specified 'object' from appearing in the document.
286
-     *
287
-     * The object will stop being displayed on the page following the current
288
-     * one.
289
-     *
290
-     * @param int $object
291
-     */
292
-    public function stop_object($object)
293
-    {
294
-        $this->_pdf->stopObject($object);
295
-    }
296
-
297
-    /**
298
-     * Serialize the pdf object's current state for retrieval later
299
-     */
300
-    public function serialize_object($id)
301
-    {
302
-        return $this->_pdf->serializeObject($id);
303
-    }
304
-
305
-    public function reopen_serialized_object($obj)
306
-    {
307
-        return $this->_pdf->restoreSerializedObject($obj);
308
-    }
309
-
310
-    //........................................................................
311
-
312
-    public function get_width()
313
-    {
314
-        return $this->_width;
315
-    }
316
-
317
-    public function get_height()
318
-    {
319
-        return $this->_height;
320
-    }
321
-
322
-    public function get_page_number()
323
-    {
324
-        return $this->_page_number;
325
-    }
326
-
327
-    public function get_page_count()
328
-    {
329
-        return $this->_page_count;
330
-    }
331
-
332
-    /**
333
-     * Sets the current page number
334
-     *
335
-     * @param int $num
336
-     */
337
-    public function set_page_number($num)
338
-    {
339
-        $this->_page_number = $num;
340
-    }
341
-
342
-    public function set_page_count($count)
343
-    {
344
-        $this->_page_count = $count;
345
-    }
346
-
347
-    /**
348
-     * Sets the stroke color
349
-     *
350
-     * See {@link Style::set_color()} for the format of the color array.
351
-     *
352
-     * @param array $color
353
-     */
354
-    protected function _set_stroke_color($color)
355
-    {
356
-        $this->_pdf->setStrokeColor($color);
357
-        $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
358
-        $alpha *= $this->_current_opacity;
359
-        $this->_set_line_transparency("Normal", $alpha);
360
-    }
361
-
362
-    /**
363
-     * Sets the fill colour
364
-     *
365
-     * See {@link Style::set_color()} for the format of the colour array.
366
-     *
367
-     * @param array $color
368
-     */
369
-    protected function _set_fill_color($color)
370
-    {
371
-        $this->_pdf->setColor($color);
372
-        $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
373
-        $alpha *= $this->_current_opacity;
374
-        $this->_set_fill_transparency("Normal", $alpha);
375
-    }
376
-
377
-    /**
378
-     * Sets line transparency
379
-     * @see Cpdf::setLineTransparency()
380
-     *
381
-     * Valid blend modes are (case-sensitive):
382
-     *
383
-     * Normal, Multiply, Screen, Overlay, Darken, Lighten,
384
-     * ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
385
-     * Exclusion
386
-     *
387
-     * @param string $mode    the blending mode to use
388
-     * @param float  $opacity 0.0 fully transparent, 1.0 fully opaque
389
-     */
390
-    protected function _set_line_transparency($mode, $opacity)
391
-    {
392
-        $this->_pdf->setLineTransparency($mode, $opacity);
393
-    }
394
-
395
-    /**
396
-     * Sets fill transparency
397
-     * @see Cpdf::setFillTransparency()
398
-     *
399
-     * Valid blend modes are (case-sensitive):
400
-     *
401
-     * Normal, Multiply, Screen, Overlay, Darken, Lighten,
402
-     * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
403
-     * Exclusion
404
-     *
405
-     * @param string $mode    the blending mode to use
406
-     * @param float  $opacity 0.0 fully transparent, 1.0 fully opaque
407
-     */
408
-    protected function _set_fill_transparency($mode, $opacity)
409
-    {
410
-        $this->_pdf->setFillTransparency($mode, $opacity);
411
-    }
412
-
413
-    /**
414
-     * Sets the line style
415
-     *
416
-     * @see Cpdf::setLineStyle()
417
-     *
418
-     * @param float  $width
419
-     * @param string $cap
420
-     * @param string $join
421
-     * @param array  $dash
422
-     */
423
-    protected function _set_line_style($width, $cap, $join, $dash)
424
-    {
425
-        $this->_pdf->setLineStyle($width, $cap, $join, $dash);
426
-    }
427
-
428
-    public function set_opacity(float $opacity, string $mode = "Normal"): void
429
-    {
430
-        $this->_set_line_transparency($mode, $opacity);
431
-        $this->_set_fill_transparency($mode, $opacity);
432
-        $this->_current_opacity = $opacity;
433
-    }
434
-
435
-    public function set_default_view($view, $options = [])
436
-    {
437
-        array_unshift($options, $view);
438
-        call_user_func_array([$this->_pdf, "openHere"], $options);
439
-    }
440
-
441
-    /**
442
-     * Remaps y coords from 4th to 1st quadrant
443
-     *
444
-     * @param float $y
445
-     * @return float
446
-     */
447
-    protected function y($y)
448
-    {
449
-        return $this->_height - $y;
450
-    }
451
-
452
-    public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
453
-    {
454
-        $this->_set_stroke_color($color);
455
-        $this->_set_line_style($width, $cap, "", $style);
456
-
457
-        $this->_pdf->line($x1, $this->y($y1),
458
-            $x2, $this->y($y2));
459
-        $this->_set_line_transparency("Normal", $this->_current_opacity);
460
-    }
461
-
462
-    public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
463
-    {
464
-        $this->_set_stroke_color($color);
465
-        $this->_set_line_style($width, $cap, "", $style);
466
-
467
-        $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
468
-        $this->_set_line_transparency("Normal", $this->_current_opacity);
469
-    }
470
-
471
-    public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
472
-    {
473
-        $this->_set_stroke_color($color);
474
-        $this->_set_line_style($width, $cap, "", $style);
475
-        $this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
476
-        $this->_set_line_transparency("Normal", $this->_current_opacity);
477
-    }
478
-
479
-    public function filled_rectangle($x1, $y1, $w, $h, $color)
480
-    {
481
-        $this->_set_fill_color($color);
482
-        $this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
483
-        $this->_set_fill_transparency("Normal", $this->_current_opacity);
484
-    }
485
-
486
-    public function clipping_rectangle($x1, $y1, $w, $h)
487
-    {
488
-        $this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
489
-    }
490
-
491
-    public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
492
-    {
493
-        $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
494
-    }
495
-
496
-    public function clipping_polygon(array $points): void
497
-    {
498
-        // Adjust y values
499
-        for ($i = 1; $i < count($points); $i += 2) {
500
-            $points[$i] = $this->y($points[$i]);
501
-        }
502
-
503
-        $this->_pdf->clippingPolygon($points);
504
-    }
505
-
506
-    public function clipping_end()
507
-    {
508
-        $this->_pdf->clippingEnd();
509
-    }
510
-
511
-    public function save()
512
-    {
513
-        $this->_pdf->saveState();
514
-    }
515
-
516
-    public function restore()
517
-    {
518
-        $this->_pdf->restoreState();
519
-    }
520
-
521
-    public function rotate($angle, $x, $y)
522
-    {
523
-        $this->_pdf->rotate($angle, $x, $y);
524
-    }
525
-
526
-    public function skew($angle_x, $angle_y, $x, $y)
527
-    {
528
-        $this->_pdf->skew($angle_x, $angle_y, $x, $y);
529
-    }
530
-
531
-    public function scale($s_x, $s_y, $x, $y)
532
-    {
533
-        $this->_pdf->scale($s_x, $s_y, $x, $y);
534
-    }
535
-
536
-    public function translate($t_x, $t_y)
537
-    {
538
-        $this->_pdf->translate($t_x, $t_y);
539
-    }
540
-
541
-    public function transform($a, $b, $c, $d, $e, $f)
542
-    {
543
-        $this->_pdf->transform([$a, $b, $c, $d, $e, $f]);
544
-    }
545
-
546
-    public function polygon($points, $color, $width = null, $style = [], $fill = false)
547
-    {
548
-        $this->_set_fill_color($color);
549
-        $this->_set_stroke_color($color);
550
-
551
-        if (!$fill && isset($width)) {
552
-            $this->_set_line_style($width, "square", "miter", $style);
553
-        }
554
-
555
-        // Adjust y values
556
-        for ($i = 1; $i < count($points); $i += 2) {
557
-            $points[$i] = $this->y($points[$i]);
558
-        }
559
-
560
-        $this->_pdf->polygon($points, $fill);
561
-
562
-        $this->_set_fill_transparency("Normal", $this->_current_opacity);
563
-        $this->_set_line_transparency("Normal", $this->_current_opacity);
564
-    }
565
-
566
-    public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false)
567
-    {
568
-        $this->_set_fill_color($color);
569
-        $this->_set_stroke_color($color);
570
-
571
-        if (!$fill && isset($width)) {
572
-            $this->_set_line_style($width, "round", "round", $style);
573
-        }
574
-
575
-        $this->_pdf->ellipse($x, $this->y($y), $r, 0, 0, 8, 0, 360, 1, $fill);
576
-
577
-        $this->_set_fill_transparency("Normal", $this->_current_opacity);
578
-        $this->_set_line_transparency("Normal", $this->_current_opacity);
579
-    }
580
-
581
-    /**
582
-     * Convert image to a PNG image
583
-     *
584
-     * @param string $image_url
585
-     * @param string $type
586
-     *
587
-     * @return string|null The url of the newly converted image
588
-     */
589
-    protected function _convert_to_png($image_url, $type)
590
-    {
591
-        $filename = Cache::getTempImage($image_url);
592
-
593
-        if ($filename !== null && file_exists($filename)) {
594
-            return $filename;
595
-        }
37
+	/**
38
+	 * Dimensions of paper sizes in points
39
+	 *
40
+	 * @var array
41
+	 */
42
+	static $PAPER_SIZES = [
43
+		"4a0" => [0.0, 0.0, 4767.87, 6740.79],
44
+		"2a0" => [0.0, 0.0, 3370.39, 4767.87],
45
+		"a0" => [0.0, 0.0, 2383.94, 3370.39],
46
+		"a1" => [0.0, 0.0, 1683.78, 2383.94],
47
+		"a2" => [0.0, 0.0, 1190.55, 1683.78],
48
+		"a3" => [0.0, 0.0, 841.89, 1190.55],
49
+		"a4" => [0.0, 0.0, 595.28, 841.89],
50
+		"a5" => [0.0, 0.0, 419.53, 595.28],
51
+		"a6" => [0.0, 0.0, 297.64, 419.53],
52
+		"a7" => [0.0, 0.0, 209.76, 297.64],
53
+		"a8" => [0.0, 0.0, 147.40, 209.76],
54
+		"a9" => [0.0, 0.0, 104.88, 147.40],
55
+		"a10" => [0.0, 0.0, 73.70, 104.88],
56
+		"b0" => [0.0, 0.0, 2834.65, 4008.19],
57
+		"b1" => [0.0, 0.0, 2004.09, 2834.65],
58
+		"b2" => [0.0, 0.0, 1417.32, 2004.09],
59
+		"b3" => [0.0, 0.0, 1000.63, 1417.32],
60
+		"b4" => [0.0, 0.0, 708.66, 1000.63],
61
+		"b5" => [0.0, 0.0, 498.90, 708.66],
62
+		"b6" => [0.0, 0.0, 354.33, 498.90],
63
+		"b7" => [0.0, 0.0, 249.45, 354.33],
64
+		"b8" => [0.0, 0.0, 175.75, 249.45],
65
+		"b9" => [0.0, 0.0, 124.72, 175.75],
66
+		"b10" => [0.0, 0.0, 87.87, 124.72],
67
+		"c0" => [0.0, 0.0, 2599.37, 3676.54],
68
+		"c1" => [0.0, 0.0, 1836.85, 2599.37],
69
+		"c2" => [0.0, 0.0, 1298.27, 1836.85],
70
+		"c3" => [0.0, 0.0, 918.43, 1298.27],
71
+		"c4" => [0.0, 0.0, 649.13, 918.43],
72
+		"c5" => [0.0, 0.0, 459.21, 649.13],
73
+		"c6" => [0.0, 0.0, 323.15, 459.21],
74
+		"c7" => [0.0, 0.0, 229.61, 323.15],
75
+		"c8" => [0.0, 0.0, 161.57, 229.61],
76
+		"c9" => [0.0, 0.0, 113.39, 161.57],
77
+		"c10" => [0.0, 0.0, 79.37, 113.39],
78
+		"ra0" => [0.0, 0.0, 2437.80, 3458.27],
79
+		"ra1" => [0.0, 0.0, 1729.13, 2437.80],
80
+		"ra2" => [0.0, 0.0, 1218.90, 1729.13],
81
+		"ra3" => [0.0, 0.0, 864.57, 1218.90],
82
+		"ra4" => [0.0, 0.0, 609.45, 864.57],
83
+		"sra0" => [0.0, 0.0, 2551.18, 3628.35],
84
+		"sra1" => [0.0, 0.0, 1814.17, 2551.18],
85
+		"sra2" => [0.0, 0.0, 1275.59, 1814.17],
86
+		"sra3" => [0.0, 0.0, 907.09, 1275.59],
87
+		"sra4" => [0.0, 0.0, 637.80, 907.09],
88
+		"letter" => [0.0, 0.0, 612.00, 792.00],
89
+		"half-letter" => [0.0, 0.0, 396.00, 612.00],
90
+		"legal" => [0.0, 0.0, 612.00, 1008.00],
91
+		"ledger" => [0.0, 0.0, 1224.00, 792.00],
92
+		"tabloid" => [0.0, 0.0, 792.00, 1224.00],
93
+		"executive" => [0.0, 0.0, 521.86, 756.00],
94
+		"folio" => [0.0, 0.0, 612.00, 936.00],
95
+		"commercial #10 envelope" => [0.0, 0.0, 684.00, 297.00],
96
+		"catalog #10 1/2 envelope" => [0.0, 0.0, 648.00, 864.00],
97
+		"8.5x11" => [0.0, 0.0, 612.00, 792.00],
98
+		"8.5x14" => [0.0, 0.0, 612.00, 1008.00],
99
+		"11x17" => [0.0, 0.0, 792.00, 1224.00],
100
+	];
101
+
102
+	/**
103
+	 * The Dompdf object
104
+	 *
105
+	 * @var Dompdf
106
+	 */
107
+	protected $_dompdf;
108
+
109
+	/**
110
+	 * Instance of Cpdf class
111
+	 *
112
+	 * @var \Dompdf\Cpdf
113
+	 */
114
+	protected $_pdf;
115
+
116
+	/**
117
+	 * PDF width, in points
118
+	 *
119
+	 * @var float
120
+	 */
121
+	protected $_width;
122
+
123
+	/**
124
+	 * PDF height, in points
125
+	 *
126
+	 * @var float
127
+	 */
128
+	protected $_height;
129
+
130
+	/**
131
+	 * Current page number
132
+	 *
133
+	 * @var int
134
+	 */
135
+	protected $_page_number;
136
+
137
+	/**
138
+	 * Total number of pages
139
+	 *
140
+	 * @var int
141
+	 */
142
+	protected $_page_count;
143
+
144
+	/**
145
+	 * Array of pages for accessing after rendering is initially complete
146
+	 *
147
+	 * @var array
148
+	 */
149
+	protected $_pages;
150
+
151
+	/**
152
+	 * Currently-applied opacity level (0 - 1)
153
+	 *
154
+	 * @var float
155
+	 */
156
+	protected $_current_opacity = 1;
157
+
158
+	public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null)
159
+	{
160
+		if (is_array($paper)) {
161
+			$size = array_map("floatval", $paper);
162
+		} else {
163
+			$paper = strtolower($paper);
164
+			$size = self::$PAPER_SIZES[$paper] ?? self::$PAPER_SIZES["letter"];
165
+		}
166
+
167
+		if (strtolower($orientation) === "landscape") {
168
+			[$size[2], $size[3]] = [$size[3], $size[2]];
169
+		}
170
+
171
+		if ($dompdf === null) {
172
+			$this->_dompdf = new Dompdf();
173
+		} else {
174
+			$this->_dompdf = $dompdf;
175
+		}
176
+
177
+		$this->_pdf = new \Dompdf\Cpdf(
178
+			$size,
179
+			true,
180
+			$this->_dompdf->getOptions()->getFontCache(),
181
+			$this->_dompdf->getOptions()->getTempDir()
182
+		);
183
+
184
+		$this->_pdf->addInfo("Producer", sprintf("%s + CPDF", $this->_dompdf->version));
185
+		$time = substr_replace(date('YmdHisO'), '\'', -2, 0) . '\'';
186
+		$this->_pdf->addInfo("CreationDate", "D:$time");
187
+		$this->_pdf->addInfo("ModDate", "D:$time");
188
+
189
+		$this->_width = $size[2] - $size[0];
190
+		$this->_height = $size[3] - $size[1];
191
+
192
+		$this->_page_number = $this->_page_count = 1;
193
+
194
+		$this->_pages = [$this->_pdf->getFirstPageId()];
195
+	}
196
+
197
+	public function get_dompdf()
198
+	{
199
+		return $this->_dompdf;
200
+	}
201
+
202
+	/**
203
+	 * Returns the Cpdf instance
204
+	 *
205
+	 * @return \Dompdf\Cpdf
206
+	 */
207
+	public function get_cpdf()
208
+	{
209
+		return $this->_pdf;
210
+	}
211
+
212
+	public function add_info(string $label, string $value): void
213
+	{
214
+		$this->_pdf->addInfo($label, $value);
215
+	}
216
+
217
+	/**
218
+	 * Opens a new 'object'
219
+	 *
220
+	 * While an object is open, all drawing actions are recorded in the object,
221
+	 * as opposed to being drawn on the current page.  Objects can be added
222
+	 * later to a specific page or to several pages.
223
+	 *
224
+	 * The return value is an integer ID for the new object.
225
+	 *
226
+	 * @see CPDF::close_object()
227
+	 * @see CPDF::add_object()
228
+	 *
229
+	 * @return int
230
+	 */
231
+	public function open_object()
232
+	{
233
+		$ret = $this->_pdf->openObject();
234
+		$this->_pdf->saveState();
235
+		return $ret;
236
+	}
237
+
238
+	/**
239
+	 * Reopens an existing 'object'
240
+	 *
241
+	 * @see CPDF::open_object()
242
+	 * @param int $object the ID of a previously opened object
243
+	 */
244
+	public function reopen_object($object)
245
+	{
246
+		$this->_pdf->reopenObject($object);
247
+		$this->_pdf->saveState();
248
+	}
249
+
250
+	/**
251
+	 * Closes the current 'object'
252
+	 *
253
+	 * @see CPDF::open_object()
254
+	 */
255
+	public function close_object()
256
+	{
257
+		$this->_pdf->restoreState();
258
+		$this->_pdf->closeObject();
259
+	}
260
+
261
+	/**
262
+	 * Adds a specified 'object' to the document
263
+	 *
264
+	 * $object int specifying an object created with {@link
265
+	 * CPDF::open_object()}.  $where can be one of:
266
+	 * - 'add' add to current page only
267
+	 * - 'all' add to every page from the current one onwards
268
+	 * - 'odd' add to all odd numbered pages from now on
269
+	 * - 'even' add to all even numbered pages from now on
270
+	 * - 'next' add the object to the next page only
271
+	 * - 'nextodd' add to all odd numbered pages from the next one
272
+	 * - 'nexteven' add to all even numbered pages from the next one
273
+	 *
274
+	 * @see Cpdf::addObject()
275
+	 *
276
+	 * @param int $object
277
+	 * @param string $where
278
+	 */
279
+	public function add_object($object, $where = 'all')
280
+	{
281
+		$this->_pdf->addObject($object, $where);
282
+	}
283
+
284
+	/**
285
+	 * Stops the specified 'object' from appearing in the document.
286
+	 *
287
+	 * The object will stop being displayed on the page following the current
288
+	 * one.
289
+	 *
290
+	 * @param int $object
291
+	 */
292
+	public function stop_object($object)
293
+	{
294
+		$this->_pdf->stopObject($object);
295
+	}
296
+
297
+	/**
298
+	 * Serialize the pdf object's current state for retrieval later
299
+	 */
300
+	public function serialize_object($id)
301
+	{
302
+		return $this->_pdf->serializeObject($id);
303
+	}
304
+
305
+	public function reopen_serialized_object($obj)
306
+	{
307
+		return $this->_pdf->restoreSerializedObject($obj);
308
+	}
309
+
310
+	//........................................................................
311
+
312
+	public function get_width()
313
+	{
314
+		return $this->_width;
315
+	}
316
+
317
+	public function get_height()
318
+	{
319
+		return $this->_height;
320
+	}
321
+
322
+	public function get_page_number()
323
+	{
324
+		return $this->_page_number;
325
+	}
326
+
327
+	public function get_page_count()
328
+	{
329
+		return $this->_page_count;
330
+	}
331
+
332
+	/**
333
+	 * Sets the current page number
334
+	 *
335
+	 * @param int $num
336
+	 */
337
+	public function set_page_number($num)
338
+	{
339
+		$this->_page_number = $num;
340
+	}
341
+
342
+	public function set_page_count($count)
343
+	{
344
+		$this->_page_count = $count;
345
+	}
346
+
347
+	/**
348
+	 * Sets the stroke color
349
+	 *
350
+	 * See {@link Style::set_color()} for the format of the color array.
351
+	 *
352
+	 * @param array $color
353
+	 */
354
+	protected function _set_stroke_color($color)
355
+	{
356
+		$this->_pdf->setStrokeColor($color);
357
+		$alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
358
+		$alpha *= $this->_current_opacity;
359
+		$this->_set_line_transparency("Normal", $alpha);
360
+	}
361
+
362
+	/**
363
+	 * Sets the fill colour
364
+	 *
365
+	 * See {@link Style::set_color()} for the format of the colour array.
366
+	 *
367
+	 * @param array $color
368
+	 */
369
+	protected function _set_fill_color($color)
370
+	{
371
+		$this->_pdf->setColor($color);
372
+		$alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
373
+		$alpha *= $this->_current_opacity;
374
+		$this->_set_fill_transparency("Normal", $alpha);
375
+	}
376
+
377
+	/**
378
+	 * Sets line transparency
379
+	 * @see Cpdf::setLineTransparency()
380
+	 *
381
+	 * Valid blend modes are (case-sensitive):
382
+	 *
383
+	 * Normal, Multiply, Screen, Overlay, Darken, Lighten,
384
+	 * ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
385
+	 * Exclusion
386
+	 *
387
+	 * @param string $mode    the blending mode to use
388
+	 * @param float  $opacity 0.0 fully transparent, 1.0 fully opaque
389
+	 */
390
+	protected function _set_line_transparency($mode, $opacity)
391
+	{
392
+		$this->_pdf->setLineTransparency($mode, $opacity);
393
+	}
394
+
395
+	/**
396
+	 * Sets fill transparency
397
+	 * @see Cpdf::setFillTransparency()
398
+	 *
399
+	 * Valid blend modes are (case-sensitive):
400
+	 *
401
+	 * Normal, Multiply, Screen, Overlay, Darken, Lighten,
402
+	 * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
403
+	 * Exclusion
404
+	 *
405
+	 * @param string $mode    the blending mode to use
406
+	 * @param float  $opacity 0.0 fully transparent, 1.0 fully opaque
407
+	 */
408
+	protected function _set_fill_transparency($mode, $opacity)
409
+	{
410
+		$this->_pdf->setFillTransparency($mode, $opacity);
411
+	}
412
+
413
+	/**
414
+	 * Sets the line style
415
+	 *
416
+	 * @see Cpdf::setLineStyle()
417
+	 *
418
+	 * @param float  $width
419
+	 * @param string $cap
420
+	 * @param string $join
421
+	 * @param array  $dash
422
+	 */
423
+	protected function _set_line_style($width, $cap, $join, $dash)
424
+	{
425
+		$this->_pdf->setLineStyle($width, $cap, $join, $dash);
426
+	}
427
+
428
+	public function set_opacity(float $opacity, string $mode = "Normal"): void
429
+	{
430
+		$this->_set_line_transparency($mode, $opacity);
431
+		$this->_set_fill_transparency($mode, $opacity);
432
+		$this->_current_opacity = $opacity;
433
+	}
434
+
435
+	public function set_default_view($view, $options = [])
436
+	{
437
+		array_unshift($options, $view);
438
+		call_user_func_array([$this->_pdf, "openHere"], $options);
439
+	}
440
+
441
+	/**
442
+	 * Remaps y coords from 4th to 1st quadrant
443
+	 *
444
+	 * @param float $y
445
+	 * @return float
446
+	 */
447
+	protected function y($y)
448
+	{
449
+		return $this->_height - $y;
450
+	}
451
+
452
+	public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
453
+	{
454
+		$this->_set_stroke_color($color);
455
+		$this->_set_line_style($width, $cap, "", $style);
456
+
457
+		$this->_pdf->line($x1, $this->y($y1),
458
+			$x2, $this->y($y2));
459
+		$this->_set_line_transparency("Normal", $this->_current_opacity);
460
+	}
461
+
462
+	public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
463
+	{
464
+		$this->_set_stroke_color($color);
465
+		$this->_set_line_style($width, $cap, "", $style);
466
+
467
+		$this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
468
+		$this->_set_line_transparency("Normal", $this->_current_opacity);
469
+	}
470
+
471
+	public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
472
+	{
473
+		$this->_set_stroke_color($color);
474
+		$this->_set_line_style($width, $cap, "", $style);
475
+		$this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
476
+		$this->_set_line_transparency("Normal", $this->_current_opacity);
477
+	}
478
+
479
+	public function filled_rectangle($x1, $y1, $w, $h, $color)
480
+	{
481
+		$this->_set_fill_color($color);
482
+		$this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
483
+		$this->_set_fill_transparency("Normal", $this->_current_opacity);
484
+	}
485
+
486
+	public function clipping_rectangle($x1, $y1, $w, $h)
487
+	{
488
+		$this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
489
+	}
490
+
491
+	public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
492
+	{
493
+		$this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
494
+	}
495
+
496
+	public function clipping_polygon(array $points): void
497
+	{
498
+		// Adjust y values
499
+		for ($i = 1; $i < count($points); $i += 2) {
500
+			$points[$i] = $this->y($points[$i]);
501
+		}
502
+
503
+		$this->_pdf->clippingPolygon($points);
504
+	}
505
+
506
+	public function clipping_end()
507
+	{
508
+		$this->_pdf->clippingEnd();
509
+	}
510
+
511
+	public function save()
512
+	{
513
+		$this->_pdf->saveState();
514
+	}
515
+
516
+	public function restore()
517
+	{
518
+		$this->_pdf->restoreState();
519
+	}
520
+
521
+	public function rotate($angle, $x, $y)
522
+	{
523
+		$this->_pdf->rotate($angle, $x, $y);
524
+	}
525
+
526
+	public function skew($angle_x, $angle_y, $x, $y)
527
+	{
528
+		$this->_pdf->skew($angle_x, $angle_y, $x, $y);
529
+	}
530
+
531
+	public function scale($s_x, $s_y, $x, $y)
532
+	{
533
+		$this->_pdf->scale($s_x, $s_y, $x, $y);
534
+	}
535
+
536
+	public function translate($t_x, $t_y)
537
+	{
538
+		$this->_pdf->translate($t_x, $t_y);
539
+	}
540
+
541
+	public function transform($a, $b, $c, $d, $e, $f)
542
+	{
543
+		$this->_pdf->transform([$a, $b, $c, $d, $e, $f]);
544
+	}
545
+
546
+	public function polygon($points, $color, $width = null, $style = [], $fill = false)
547
+	{
548
+		$this->_set_fill_color($color);
549
+		$this->_set_stroke_color($color);
550
+
551
+		if (!$fill && isset($width)) {
552
+			$this->_set_line_style($width, "square", "miter", $style);
553
+		}
554
+
555
+		// Adjust y values
556
+		for ($i = 1; $i < count($points); $i += 2) {
557
+			$points[$i] = $this->y($points[$i]);
558
+		}
559
+
560
+		$this->_pdf->polygon($points, $fill);
561
+
562
+		$this->_set_fill_transparency("Normal", $this->_current_opacity);
563
+		$this->_set_line_transparency("Normal", $this->_current_opacity);
564
+	}
565
+
566
+	public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false)
567
+	{
568
+		$this->_set_fill_color($color);
569
+		$this->_set_stroke_color($color);
570
+
571
+		if (!$fill && isset($width)) {
572
+			$this->_set_line_style($width, "round", "round", $style);
573
+		}
574
+
575
+		$this->_pdf->ellipse($x, $this->y($y), $r, 0, 0, 8, 0, 360, 1, $fill);
576
+
577
+		$this->_set_fill_transparency("Normal", $this->_current_opacity);
578
+		$this->_set_line_transparency("Normal", $this->_current_opacity);
579
+	}
580
+
581
+	/**
582
+	 * Convert image to a PNG image
583
+	 *
584
+	 * @param string $image_url
585
+	 * @param string $type
586
+	 *
587
+	 * @return string|null The url of the newly converted image
588
+	 */
589
+	protected function _convert_to_png($image_url, $type)
590
+	{
591
+		$filename = Cache::getTempImage($image_url);
592
+
593
+		if ($filename !== null && file_exists($filename)) {
594
+			return $filename;
595
+		}
596 596
  
597
-        $func_name = "imagecreatefrom$type";
598
-
599
-        set_error_handler([Helpers::class, "record_warnings"]);
600
-
601
-        if (!function_exists($func_name)) {
602
-            if (!method_exists(Helpers::class, $func_name)) {
603
-                throw new Exception("Function $func_name() not found.  Cannot convert $type image: $image_url.  Please install the image PHP extension.");
604
-            }
605
-            $func_name = [Helpers::class, $func_name];
606
-        }
607
-
608
-        try {
609
-            $im = call_user_func($func_name, $image_url);
610
-
611
-            if ($im) {
612
-                imageinterlace($im, false);
613
-
614
-                $tmp_dir = $this->_dompdf->getOptions()->getTempDir();
615
-                $tmp_name = @tempnam($tmp_dir, "{$type}_dompdf_img_");
616
-                @unlink($tmp_name);
617
-                $filename = "$tmp_name.png";
618
-
619
-                imagepng($im, $filename);
620
-                imagedestroy($im);
621
-            } else {
622
-                $filename = null;
623
-            }
624
-        } finally {
625
-            restore_error_handler();
626
-        }
627
-
628
-        if ($filename !== null) {
629
-            Cache::addTempImage($image_url, $filename);
630
-        }
597
+		$func_name = "imagecreatefrom$type";
598
+
599
+		set_error_handler([Helpers::class, "record_warnings"]);
600
+
601
+		if (!function_exists($func_name)) {
602
+			if (!method_exists(Helpers::class, $func_name)) {
603
+				throw new Exception("Function $func_name() not found.  Cannot convert $type image: $image_url.  Please install the image PHP extension.");
604
+			}
605
+			$func_name = [Helpers::class, $func_name];
606
+		}
607
+
608
+		try {
609
+			$im = call_user_func($func_name, $image_url);
610
+
611
+			if ($im) {
612
+				imageinterlace($im, false);
613
+
614
+				$tmp_dir = $this->_dompdf->getOptions()->getTempDir();
615
+				$tmp_name = @tempnam($tmp_dir, "{$type}_dompdf_img_");
616
+				@unlink($tmp_name);
617
+				$filename = "$tmp_name.png";
618
+
619
+				imagepng($im, $filename);
620
+				imagedestroy($im);
621
+			} else {
622
+				$filename = null;
623
+			}
624
+		} finally {
625
+			restore_error_handler();
626
+		}
627
+
628
+		if ($filename !== null) {
629
+			Cache::addTempImage($image_url, $filename);
630
+		}
631 631
 
632
-        return $filename;
633
-    }
634
-
635
-    public function image($img, $x, $y, $w, $h, $resolution = "normal")
636
-    {
637
-        [$width, $height, $type] = Helpers::dompdf_getimagesize($img, $this->get_dompdf()->getHttpContext());
638
-
639
-        $debug_png = $this->_dompdf->getOptions()->getDebugPng();
640
-
641
-        if ($debug_png) {
642
-            print "[image:$img|$width|$height|$type]";
643
-        }
644
-
645
-        switch ($type) {
646
-            case "jpeg":
647
-                if ($debug_png) {
648
-                    print '!!!jpg!!!';
649
-                }
650
-                $this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
651
-                break;
652
-
653
-            case "webp":
654
-            /** @noinspection PhpMissingBreakStatementInspection */
655
-            case "gif":
656
-            /** @noinspection PhpMissingBreakStatementInspection */
657
-            case "bmp":
658
-                if ($debug_png) print "!!!{$type}!!!";
659
-                $img = $this->_convert_to_png($img, $type);
660
-                if ($img === null) {
661
-                    if ($debug_png) print '!!!conversion to PDF failed!!!';
662
-                    $this->image(Cache::$broken_image, $x, $y, $w, $h, $resolution);
663
-                    break;
664
-                }
665
-
666
-            case "png":
667
-                if ($debug_png) print '!!!png!!!';
668
-
669
-                $this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
670
-                break;
671
-
672
-            case "svg":
673
-                if ($debug_png) print '!!!SVG!!!';
674
-
675
-                $this->_pdf->addSvgFromFile($img, $x, $this->y($y) - $h, $w, $h);
676
-                break;
677
-
678
-            default:
679
-                if ($debug_png) print '!!!unknown!!!';
680
-        }
681
-    }
682
-
683
-    public function select($x, $y, $w, $h, $font, $size, $color = [0, 0, 0], $opts = [])
684
-    {
685
-        $pdf = $this->_pdf;
686
-
687
-        $font .= ".afm";
688
-        $pdf->selectFont($font);
689
-
690
-        if (!isset($pdf->acroFormId)) {
691
-            $pdf->addForm();
692
-        }
693
-
694
-        $ft = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE;
695
-        $ff = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE_COMBO;
696
-
697
-        $id = $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
698
-        $pdf->setFormFieldOpt($id, $opts);
699
-    }
700
-
701
-    public function textarea($x, $y, $w, $h, $font, $size, $color = [0, 0, 0])
702
-    {
703
-        $pdf = $this->_pdf;
704
-
705
-        $font .= ".afm";
706
-        $pdf->selectFont($font);
707
-
708
-        if (!isset($pdf->acroFormId)) {
709
-            $pdf->addForm();
710
-        }
711
-
712
-        $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
713
-        $ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_MULTILINE;
714
-
715
-        $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
716
-    }
717
-
718
-    public function input($x, $y, $w, $h, $type, $font, $size, $color = [0, 0, 0])
719
-    {
720
-        $pdf = $this->_pdf;
721
-
722
-        $font .= ".afm";
723
-        $pdf->selectFont($font);
724
-
725
-        if (!isset($pdf->acroFormId)) {
726
-            $pdf->addForm();
727
-        }
728
-
729
-        $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
730
-        $ff = 0;
731
-
732
-        switch ($type) {
733
-            case 'text':
734
-                $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
735
-                break;
736
-            case 'password':
737
-                $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
738
-                $ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_PASSWORD;
739
-                break;
740
-            case 'submit':
741
-                $ft = \Dompdf\Cpdf::ACROFORM_FIELD_BUTTON;
742
-                break;
743
-        }
744
-
745
-        $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
746
-    }
747
-
748
-    public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
749
-    {
750
-        $pdf = $this->_pdf;
751
-
752
-        $this->_set_fill_color($color);
753
-
754
-        $is_font_subsetting = $this->_dompdf->getOptions()->getIsFontSubsettingEnabled();
755
-        $pdf->selectFont($font . '.afm', '', true, $is_font_subsetting);
756
-
757
-        $pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space);
758
-
759
-        $this->_set_fill_transparency("Normal", $this->_current_opacity);
760
-    }
761
-
762
-    public function javascript($code)
763
-    {
764
-        $this->_pdf->addJavascript($code);
765
-    }
766
-
767
-    //........................................................................
768
-
769
-    public function add_named_dest($anchorname)
770
-    {
771
-        $this->_pdf->addDestination($anchorname, "Fit");
772
-    }
773
-
774
-    public function add_link($url, $x, $y, $width, $height)
775
-    {
776
-        $y = $this->y($y) - $height;
777
-
778
-        if (strpos($url, '#') === 0) {
779
-            // Local link
780
-            $name = substr($url, 1);
781
-            if ($name) {
782
-                $this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
783
-            }
784
-        } else {
785
-            $this->_pdf->addLink($url, $x, $y, $x + $width, $y + $height);
786
-        }
787
-    }
788
-
789
-    /**
790
-     * @throws FontNotFoundException
791
-     */
792
-    public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
793
-    {
794
-        $this->_pdf->selectFont($font, '', true, $this->_dompdf->getOptions()->getIsFontSubsettingEnabled());
795
-        return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
796
-    }
797
-
798
-    /**
799
-     * @throws FontNotFoundException
800
-     */
801
-    public function get_font_height($font, $size)
802
-    {
803
-        $options = $this->_dompdf->getOptions();
804
-        $this->_pdf->selectFont($font, '', true, $options->getIsFontSubsettingEnabled());
805
-
806
-        return $this->_pdf->getFontHeight($size) * $options->getFontHeightRatio();
807
-    }
808
-
809
-    /*function get_font_x_height($font, $size) {
632
+		return $filename;
633
+	}
634
+
635
+	public function image($img, $x, $y, $w, $h, $resolution = "normal")
636
+	{
637
+		[$width, $height, $type] = Helpers::dompdf_getimagesize($img, $this->get_dompdf()->getHttpContext());
638
+
639
+		$debug_png = $this->_dompdf->getOptions()->getDebugPng();
640
+
641
+		if ($debug_png) {
642
+			print "[image:$img|$width|$height|$type]";
643
+		}
644
+
645
+		switch ($type) {
646
+			case "jpeg":
647
+				if ($debug_png) {
648
+					print '!!!jpg!!!';
649
+				}
650
+				$this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
651
+				break;
652
+
653
+			case "webp":
654
+			/** @noinspection PhpMissingBreakStatementInspection */
655
+			case "gif":
656
+			/** @noinspection PhpMissingBreakStatementInspection */
657
+			case "bmp":
658
+				if ($debug_png) print "!!!{$type}!!!";
659
+				$img = $this->_convert_to_png($img, $type);
660
+				if ($img === null) {
661
+					if ($debug_png) print '!!!conversion to PDF failed!!!';
662
+					$this->image(Cache::$broken_image, $x, $y, $w, $h, $resolution);
663
+					break;
664
+				}
665
+
666
+			case "png":
667
+				if ($debug_png) print '!!!png!!!';
668
+
669
+				$this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
670
+				break;
671
+
672
+			case "svg":
673
+				if ($debug_png) print '!!!SVG!!!';
674
+
675
+				$this->_pdf->addSvgFromFile($img, $x, $this->y($y) - $h, $w, $h);
676
+				break;
677
+
678
+			default:
679
+				if ($debug_png) print '!!!unknown!!!';
680
+		}
681
+	}
682
+
683
+	public function select($x, $y, $w, $h, $font, $size, $color = [0, 0, 0], $opts = [])
684
+	{
685
+		$pdf = $this->_pdf;
686
+
687
+		$font .= ".afm";
688
+		$pdf->selectFont($font);
689
+
690
+		if (!isset($pdf->acroFormId)) {
691
+			$pdf->addForm();
692
+		}
693
+
694
+		$ft = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE;
695
+		$ff = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE_COMBO;
696
+
697
+		$id = $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
698
+		$pdf->setFormFieldOpt($id, $opts);
699
+	}
700
+
701
+	public function textarea($x, $y, $w, $h, $font, $size, $color = [0, 0, 0])
702
+	{
703
+		$pdf = $this->_pdf;
704
+
705
+		$font .= ".afm";
706
+		$pdf->selectFont($font);
707
+
708
+		if (!isset($pdf->acroFormId)) {
709
+			$pdf->addForm();
710
+		}
711
+
712
+		$ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
713
+		$ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_MULTILINE;
714
+
715
+		$pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
716
+	}
717
+
718
+	public function input($x, $y, $w, $h, $type, $font, $size, $color = [0, 0, 0])
719
+	{
720
+		$pdf = $this->_pdf;
721
+
722
+		$font .= ".afm";
723
+		$pdf->selectFont($font);
724
+
725
+		if (!isset($pdf->acroFormId)) {
726
+			$pdf->addForm();
727
+		}
728
+
729
+		$ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
730
+		$ff = 0;
731
+
732
+		switch ($type) {
733
+			case 'text':
734
+				$ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
735
+				break;
736
+			case 'password':
737
+				$ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
738
+				$ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_PASSWORD;
739
+				break;
740
+			case 'submit':
741
+				$ft = \Dompdf\Cpdf::ACROFORM_FIELD_BUTTON;
742
+				break;
743
+		}
744
+
745
+		$pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
746
+	}
747
+
748
+	public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
749
+	{
750
+		$pdf = $this->_pdf;
751
+
752
+		$this->_set_fill_color($color);
753
+
754
+		$is_font_subsetting = $this->_dompdf->getOptions()->getIsFontSubsettingEnabled();
755
+		$pdf->selectFont($font . '.afm', '', true, $is_font_subsetting);
756
+
757
+		$pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space);
758
+
759
+		$this->_set_fill_transparency("Normal", $this->_current_opacity);
760
+	}
761
+
762
+	public function javascript($code)
763
+	{
764
+		$this->_pdf->addJavascript($code);
765
+	}
766
+
767
+	//........................................................................
768
+
769
+	public function add_named_dest($anchorname)
770
+	{
771
+		$this->_pdf->addDestination($anchorname, "Fit");
772
+	}
773
+
774
+	public function add_link($url, $x, $y, $width, $height)
775
+	{
776
+		$y = $this->y($y) - $height;
777
+
778
+		if (strpos($url, '#') === 0) {
779
+			// Local link
780
+			$name = substr($url, 1);
781
+			if ($name) {
782
+				$this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
783
+			}
784
+		} else {
785
+			$this->_pdf->addLink($url, $x, $y, $x + $width, $y + $height);
786
+		}
787
+	}
788
+
789
+	/**
790
+	 * @throws FontNotFoundException
791
+	 */
792
+	public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
793
+	{
794
+		$this->_pdf->selectFont($font, '', true, $this->_dompdf->getOptions()->getIsFontSubsettingEnabled());
795
+		return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
796
+	}
797
+
798
+	/**
799
+	 * @throws FontNotFoundException
800
+	 */
801
+	public function get_font_height($font, $size)
802
+	{
803
+		$options = $this->_dompdf->getOptions();
804
+		$this->_pdf->selectFont($font, '', true, $options->getIsFontSubsettingEnabled());
805
+
806
+		return $this->_pdf->getFontHeight($size) * $options->getFontHeightRatio();
807
+	}
808
+
809
+	/*function get_font_x_height($font, $size) {
810 810
       $this->_pdf->selectFont($font);
811 811
       $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
812 812
       return $this->_pdf->getFontXHeight($size) * $ratio;
813 813
     }*/
814 814
 
815
-    /**
816
-     * @throws FontNotFoundException
817
-     */
818
-    public function get_font_baseline($font, $size)
819
-    {
820
-        $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
821
-        return $this->get_font_height($font, $size) / $ratio;
822
-    }
823
-
824
-    /**
825
-     * Processes a callback or script on every page.
826
-     *
827
-     * The callback function receives the four parameters `int $pageNumber`,
828
-     * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
829
-     * that order. If a script is passed as string, the variables `$PAGE_NUM`,
830
-     * `$PAGE_COUNT`, `$pdf`, and `$fontMetrics` are available instead. Passing
831
-     * a script as string is deprecated and will be removed in a future version.
832
-     *
833
-     * This function can be used to add page numbers to all pages after the
834
-     * first one, for example.
835
-     *
836
-     * @param callable|string $callback The callback function or PHP script to process on every page
837
-     */
838
-    public function page_script($callback): void
839
-    {
840
-        if (is_string($callback)) {
841
-            $this->processPageScript(function (
842
-                int $PAGE_NUM,
843
-                int $PAGE_COUNT,
844
-                self $pdf,
845
-                FontMetrics $fontMetrics
846
-            ) use ($callback) {
847
-                eval($callback);
848
-            });
849
-            return;
850
-        }
851
-
852
-        $this->processPageScript($callback);
853
-    }
854
-
855
-    public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
856
-    {
857
-        $this->processPageScript(function (int $pageNumber, int $pageCount) use ($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle) {
858
-            $text = str_replace(
859
-                ["{PAGE_NUM}", "{PAGE_COUNT}"],
860
-                [$pageNumber, $pageCount],
861
-                $text
862
-            );
863
-            $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
864
-        });
865
-    }
866
-
867
-    public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
868
-    {
869
-        $this->processPageScript(function () use ($x1, $y1, $x2, $y2, $color, $width, $style) {
870
-            $this->line($x1, $y1, $x2, $y2, $color, $width, $style);
871
-        });
872
-    }
873
-
874
-    /**
875
-     * @return int
876
-     */
877
-    public function new_page()
878
-    {
879
-        $this->_page_number++;
880
-        $this->_page_count++;
881
-
882
-        $ret = $this->_pdf->newPage();
883
-        $this->_pages[] = $ret;
884
-        return $ret;
885
-    }
886
-
887
-    protected function processPageScript(callable $callback): void
888
-    {
889
-        $pageNumber = 1;
890
-
891
-        foreach ($this->_pages as $pid) {
892
-            $this->reopen_object($pid);
893
-
894
-            $fontMetrics = $this->_dompdf->getFontMetrics();
895
-            $callback($pageNumber, $this->_page_count, $this, $fontMetrics);
896
-
897
-            $this->close_object();
898
-            $pageNumber++;
899
-        }
900
-    }
901
-
902
-    public function stream($filename = "document.pdf", $options = [])
903
-    {
904
-        if (headers_sent()) {
905
-            die("Unable to stream pdf: headers already sent");
906
-        }
907
-
908
-        if (!isset($options["compress"])) $options["compress"] = true;
909
-        if (!isset($options["Attachment"])) $options["Attachment"] = true;
910
-
911
-        $debug = !$options['compress'];
912
-        $tmp = ltrim($this->_pdf->output($debug));
913
-
914
-        header("Cache-Control: private");
915
-        header("Content-Type: application/pdf");
916
-        header("Content-Length: " . mb_strlen($tmp, "8bit"));
917
-
918
-        $filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf";
919
-        $attachment = $options["Attachment"] ? "attachment" : "inline";
920
-        header(Helpers::buildContentDispositionHeader($attachment, $filename));
921
-
922
-        echo $tmp;
923
-        flush();
924
-    }
925
-
926
-    public function output($options = [])
927
-    {
928
-        if (!isset($options["compress"])) $options["compress"] = true;
929
-
930
-        $debug = !$options['compress'];
931
-
932
-        return $this->_pdf->output($debug);
933
-    }
934
-
935
-    /**
936
-     * Returns logging messages generated by the Cpdf class
937
-     *
938
-     * @return string
939
-     */
940
-    public function get_messages()
941
-    {
942
-        return $this->_pdf->messages;
943
-    }
815
+	/**
816
+	 * @throws FontNotFoundException
817
+	 */
818
+	public function get_font_baseline($font, $size)
819
+	{
820
+		$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
821
+		return $this->get_font_height($font, $size) / $ratio;
822
+	}
823
+
824
+	/**
825
+	 * Processes a callback or script on every page.
826
+	 *
827
+	 * The callback function receives the four parameters `int $pageNumber`,
828
+	 * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
829
+	 * that order. If a script is passed as string, the variables `$PAGE_NUM`,
830
+	 * `$PAGE_COUNT`, `$pdf`, and `$fontMetrics` are available instead. Passing
831
+	 * a script as string is deprecated and will be removed in a future version.
832
+	 *
833
+	 * This function can be used to add page numbers to all pages after the
834
+	 * first one, for example.
835
+	 *
836
+	 * @param callable|string $callback The callback function or PHP script to process on every page
837
+	 */
838
+	public function page_script($callback): void
839
+	{
840
+		if (is_string($callback)) {
841
+			$this->processPageScript(function (
842
+				int $PAGE_NUM,
843
+				int $PAGE_COUNT,
844
+				self $pdf,
845
+				FontMetrics $fontMetrics
846
+			) use ($callback) {
847
+				eval($callback);
848
+			});
849
+			return;
850
+		}
851
+
852
+		$this->processPageScript($callback);
853
+	}
854
+
855
+	public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
856
+	{
857
+		$this->processPageScript(function (int $pageNumber, int $pageCount) use ($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle) {
858
+			$text = str_replace(
859
+				["{PAGE_NUM}", "{PAGE_COUNT}"],
860
+				[$pageNumber, $pageCount],
861
+				$text
862
+			);
863
+			$this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
864
+		});
865
+	}
866
+
867
+	public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
868
+	{
869
+		$this->processPageScript(function () use ($x1, $y1, $x2, $y2, $color, $width, $style) {
870
+			$this->line($x1, $y1, $x2, $y2, $color, $width, $style);
871
+		});
872
+	}
873
+
874
+	/**
875
+	 * @return int
876
+	 */
877
+	public function new_page()
878
+	{
879
+		$this->_page_number++;
880
+		$this->_page_count++;
881
+
882
+		$ret = $this->_pdf->newPage();
883
+		$this->_pages[] = $ret;
884
+		return $ret;
885
+	}
886
+
887
+	protected function processPageScript(callable $callback): void
888
+	{
889
+		$pageNumber = 1;
890
+
891
+		foreach ($this->_pages as $pid) {
892
+			$this->reopen_object($pid);
893
+
894
+			$fontMetrics = $this->_dompdf->getFontMetrics();
895
+			$callback($pageNumber, $this->_page_count, $this, $fontMetrics);
896
+
897
+			$this->close_object();
898
+			$pageNumber++;
899
+		}
900
+	}
901
+
902
+	public function stream($filename = "document.pdf", $options = [])
903
+	{
904
+		if (headers_sent()) {
905
+			die("Unable to stream pdf: headers already sent");
906
+		}
907
+
908
+		if (!isset($options["compress"])) $options["compress"] = true;
909
+		if (!isset($options["Attachment"])) $options["Attachment"] = true;
910
+
911
+		$debug = !$options['compress'];
912
+		$tmp = ltrim($this->_pdf->output($debug));
913
+
914
+		header("Cache-Control: private");
915
+		header("Content-Type: application/pdf");
916
+		header("Content-Length: " . mb_strlen($tmp, "8bit"));
917
+
918
+		$filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf";
919
+		$attachment = $options["Attachment"] ? "attachment" : "inline";
920
+		header(Helpers::buildContentDispositionHeader($attachment, $filename));
921
+
922
+		echo $tmp;
923
+		flush();
924
+	}
925
+
926
+	public function output($options = [])
927
+	{
928
+		if (!isset($options["compress"])) $options["compress"] = true;
929
+
930
+		$debug = !$options['compress'];
931
+
932
+		return $this->_pdf->output($debug);
933
+	}
934
+
935
+	/**
936
+	 * Returns logging messages generated by the Cpdf class
937
+	 *
938
+	 * @return string
939
+	 */
940
+	public function get_messages()
941
+	{
942
+		return $this->_pdf->messages;
943
+	}
944 944
 }
Please login to merge, or discard this patch.
vendor/dompdf/dompdf/src/Positioner/Block.php 1 patch
Indentation   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -16,25 +16,25 @@
 block discarded – undo
16 16
 class Block extends AbstractPositioner
17 17
 {
18 18
 
19
-    function position(AbstractFrameDecorator $frame): void
20
-    {
21
-        $style = $frame->get_style();
22
-        $cb = $frame->get_containing_block();
23
-        $p = $frame->find_block_parent();
19
+	function position(AbstractFrameDecorator $frame): void
20
+	{
21
+		$style = $frame->get_style();
22
+		$cb = $frame->get_containing_block();
23
+		$p = $frame->find_block_parent();
24 24
 
25
-        if ($p) {
26
-            $float = $style->float;
25
+		if ($p) {
26
+			$float = $style->float;
27 27
 
28
-            if (!$float || $float === "none") {
29
-                $p->add_line(true);
30
-            }
31
-            $y = $p->get_current_line_box()->y;
32
-        } else {
33
-            $y = $cb["y"];
34
-        }
28
+			if (!$float || $float === "none") {
29
+				$p->add_line(true);
30
+			}
31
+			$y = $p->get_current_line_box()->y;
32
+		} else {
33
+			$y = $cb["y"];
34
+		}
35 35
 
36
-        $x = $cb["x"];
36
+		$x = $cb["x"];
37 37
 
38
-        $frame->set_position($x, $y);
39
-    }
38
+		$frame->set_position($x, $y);
39
+	}
40 40
 }
Please login to merge, or discard this patch.
vendor/dompdf/dompdf/src/FontMetrics.php 2 patches
Indentation   +592 added lines, -592 removed lines patch added patch discarded remove patch
@@ -20,381 +20,381 @@  discard block
 block discarded – undo
20 20
  */
21 21
 class FontMetrics
22 22
 {
23
-    /**
24
-     * Name of the user font families file
25
-     *
26
-     * This file must be writable by the webserver process only to update it
27
-     * with save_font_families() after adding the .afm file references of a new font family
28
-     * with FontMetrics::saveFontFamilies().
29
-     * This is typically done only from command line with load_font.php on converting
30
-     * ttf fonts to ufm with php-font-lib.
31
-     */
32
-    const USER_FONTS_FILE = "installed-fonts.json";
33
-
34
-
35
-    /**
36
-     * Underlying {@link Canvas} object to perform text size calculations
37
-     *
38
-     * @var Canvas
39
-     */
40
-    protected $canvas;
41
-
42
-    /**
43
-     * Array of bundled font family names to variants
44
-     *
45
-     * @var array
46
-     */
47
-    protected $bundledFonts = [];
48
-
49
-    /**
50
-     * Array of user defined font family names to variants
51
-     *
52
-     * @var array
53
-     */
54
-    protected $userFonts = [];
55
-
56
-    /**
57
-     * combined list of all font families with absolute paths
58
-     *
59
-     * @var array
60
-     */
61
-    protected $fontFamilies;
62
-
63
-    /**
64
-     * @var Options
65
-     */
66
-    private $options;
67
-
68
-    /**
69
-     * Class initialization
70
-     */
71
-    public function __construct(Canvas $canvas, Options $options)
72
-    {
73
-        $this->setCanvas($canvas);
74
-        $this->setOptions($options);
75
-        $this->loadFontFamilies();
76
-    }
77
-
78
-    /**
79
-     * @deprecated
80
-     */
81
-    public function save_font_families()
82
-    {
83
-        $this->saveFontFamilies();
84
-    }
85
-
86
-    /**
87
-     * Saves the stored font family cache
88
-     *
89
-     * The name and location of the cache file are determined by {@link
90
-     * FontMetrics::USER_FONTS_FILE}. This file should be writable by the
91
-     * webserver process.
92
-     *
93
-     * @see FontMetrics::loadFontFamilies()
94
-     */
95
-    public function saveFontFamilies()
96
-    {
97
-        file_put_contents($this->getUserFontsFilePath(), json_encode($this->userFonts, JSON_PRETTY_PRINT));
98
-    }
99
-
100
-    /**
101
-     * @deprecated
102
-     */
103
-    public function load_font_families()
104
-    {
105
-        $this->loadFontFamilies();
106
-    }
107
-
108
-    /**
109
-     * Loads the stored font family cache
110
-     *
111
-     * @see FontMetrics::saveFontFamilies()
112
-     */
113
-    public function loadFontFamilies()
114
-    {
115
-        $file = $this->options->getRootDir() . "/lib/fonts/installed-fonts.dist.json";
116
-        $this->bundledFonts = json_decode(file_get_contents($file), true);
117
-
118
-        if (is_readable($this->getUserFontsFilePath())) {
119
-            $this->userFonts = json_decode(file_get_contents($this->getUserFontsFilePath()), true);
120
-        } else {
121
-            $this->loadFontFamiliesLegacy();
122
-        }
123
-    }
124
-
125
-    private function loadFontFamiliesLegacy()
126
-    {
127
-        $legacyCacheFile = $this->options->getFontDir() . '/dompdf_font_family_cache.php';
128
-        if (is_readable($legacyCacheFile)) {
129
-            $fontDir = $this->options->getFontDir();
130
-            $rootDir = $this->options->getRootDir();
23
+	/**
24
+	 * Name of the user font families file
25
+	 *
26
+	 * This file must be writable by the webserver process only to update it
27
+	 * with save_font_families() after adding the .afm file references of a new font family
28
+	 * with FontMetrics::saveFontFamilies().
29
+	 * This is typically done only from command line with load_font.php on converting
30
+	 * ttf fonts to ufm with php-font-lib.
31
+	 */
32
+	const USER_FONTS_FILE = "installed-fonts.json";
33
+
34
+
35
+	/**
36
+	 * Underlying {@link Canvas} object to perform text size calculations
37
+	 *
38
+	 * @var Canvas
39
+	 */
40
+	protected $canvas;
41
+
42
+	/**
43
+	 * Array of bundled font family names to variants
44
+	 *
45
+	 * @var array
46
+	 */
47
+	protected $bundledFonts = [];
48
+
49
+	/**
50
+	 * Array of user defined font family names to variants
51
+	 *
52
+	 * @var array
53
+	 */
54
+	protected $userFonts = [];
55
+
56
+	/**
57
+	 * combined list of all font families with absolute paths
58
+	 *
59
+	 * @var array
60
+	 */
61
+	protected $fontFamilies;
62
+
63
+	/**
64
+	 * @var Options
65
+	 */
66
+	private $options;
67
+
68
+	/**
69
+	 * Class initialization
70
+	 */
71
+	public function __construct(Canvas $canvas, Options $options)
72
+	{
73
+		$this->setCanvas($canvas);
74
+		$this->setOptions($options);
75
+		$this->loadFontFamilies();
76
+	}
77
+
78
+	/**
79
+	 * @deprecated
80
+	 */
81
+	public function save_font_families()
82
+	{
83
+		$this->saveFontFamilies();
84
+	}
85
+
86
+	/**
87
+	 * Saves the stored font family cache
88
+	 *
89
+	 * The name and location of the cache file are determined by {@link
90
+	 * FontMetrics::USER_FONTS_FILE}. This file should be writable by the
91
+	 * webserver process.
92
+	 *
93
+	 * @see FontMetrics::loadFontFamilies()
94
+	 */
95
+	public function saveFontFamilies()
96
+	{
97
+		file_put_contents($this->getUserFontsFilePath(), json_encode($this->userFonts, JSON_PRETTY_PRINT));
98
+	}
99
+
100
+	/**
101
+	 * @deprecated
102
+	 */
103
+	public function load_font_families()
104
+	{
105
+		$this->loadFontFamilies();
106
+	}
107
+
108
+	/**
109
+	 * Loads the stored font family cache
110
+	 *
111
+	 * @see FontMetrics::saveFontFamilies()
112
+	 */
113
+	public function loadFontFamilies()
114
+	{
115
+		$file = $this->options->getRootDir() . "/lib/fonts/installed-fonts.dist.json";
116
+		$this->bundledFonts = json_decode(file_get_contents($file), true);
117
+
118
+		if (is_readable($this->getUserFontsFilePath())) {
119
+			$this->userFonts = json_decode(file_get_contents($this->getUserFontsFilePath()), true);
120
+		} else {
121
+			$this->loadFontFamiliesLegacy();
122
+		}
123
+	}
124
+
125
+	private function loadFontFamiliesLegacy()
126
+	{
127
+		$legacyCacheFile = $this->options->getFontDir() . '/dompdf_font_family_cache.php';
128
+		if (is_readable($legacyCacheFile)) {
129
+			$fontDir = $this->options->getFontDir();
130
+			$rootDir = $this->options->getRootDir();
131 131
     
132
-            if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); }
133
-            if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); }
132
+			if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); }
133
+			if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); }
134 134
     
135
-            $cacheDataClosure = require $legacyCacheFile;
136
-            $cacheData = is_array($cacheDataClosure) ? $cacheDataClosure : $cacheDataClosure($fontDir, $rootDir);
137
-            if (is_array($cacheData)) {
138
-                foreach ($cacheData as $family => $variants) {
139
-                    if (!isset($this->bundledFonts[$family]) && is_array($variants)) {
140
-                        foreach ($variants as $variant => $variantPath) {
141
-                            $variantName = basename($variantPath);
142
-                            $variantDir = dirname($variantPath);
143
-                            if ($variantDir == $fontDir) {
144
-                                $this->userFonts[$family][$variant] = $variantName;
145
-                            } else {
146
-                                $this->userFonts[$family][$variant] = $variantPath;
147
-                            }
148
-                        }
149
-                    }
150
-                }
151
-                $this->saveFontFamilies();
152
-            }
153
-        }
154
-    }
155
-
156
-    /**
157
-     * @param array $style
158
-     * @param string $remote_file
159
-     * @param resource $context
160
-     * @return bool
161
-     * @deprecated
162
-     */
163
-    public function register_font($style, $remote_file, $context = null)
164
-    {
165
-        return $this->registerFont($style, $remote_file);
166
-    }
167
-
168
-    /**
169
-     * @param array $style
170
-     * @param string $remoteFile
171
-     * @param resource $context
172
-     * @return bool
173
-     */
174
-    public function registerFont($style, $remoteFile, $context = null)
175
-    {
176
-        $fontname = mb_strtolower($style["family"]);
177
-        $families = $this->getFontFamilies();
178
-
179
-        $entry = [];
180
-        if (isset($families[$fontname])) {
181
-            $entry = $families[$fontname];
182
-        }
183
-
184
-        $styleString = $this->getType("{$style['weight']} {$style['style']}");
185
-
186
-        $remoteHash = md5($remoteFile);
187
-
188
-        $prefix = $fontname . "_" . $styleString;
189
-        $prefix = trim($prefix, "-");
190
-        if (function_exists('iconv')) {
191
-            $prefix = @iconv('utf-8', 'us-ascii//TRANSLIT', $prefix);
192
-        }
193
-        $prefix_encoding = mb_detect_encoding($prefix, mb_detect_order(), true);
194
-        $substchar = mb_substitute_character();
195
-        mb_substitute_character(0x005F);
196
-        $prefix = mb_convert_encoding($prefix, "ISO-8859-1", $prefix_encoding);
197
-        mb_substitute_character($substchar);
198
-        $prefix = preg_replace("[\W]", "_", $prefix);
199
-        $prefix = preg_replace("/[^-_\w]+/", "", $prefix);
200
-
201
-        $localFile = $prefix . "_" . $remoteHash;
202
-        $localFilePath = $this->getOptions()->getFontDir() . "/" . $localFile;
203
-
204
-        if (isset($entry[$styleString]) && $localFilePath == $entry[$styleString]) {
205
-            return true;
206
-        }
207
-
208
-
209
-        $entry[$styleString] = $localFile;
210
-
211
-        // Download the remote file
212
-        [$protocol] = Helpers::explode_url($remoteFile);
213
-        $allowed_protocols = $this->options->getAllowedProtocols();
214
-        if (!array_key_exists($protocol, $allowed_protocols)) {
215
-            Helpers::record_warnings(E_USER_WARNING, "Permission denied on $remoteFile. The communication protocol is not supported.", __FILE__, __LINE__);
216
-            return false;
217
-        }
218
-
219
-        foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
220
-            [$result, $message] = $rule($remoteFile);
221
-            if ($result !== true) {
222
-                Helpers::record_warnings(E_USER_WARNING, "Error loading $remoteFile: $message", __FILE__, __LINE__);
223
-                return false;
224
-            }
225
-        }
226
-
227
-        list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context);
228
-        if ($remoteFileContent === null) {
229
-            return false;
230
-        }
231
-
232
-        $localTempFile = @tempnam($this->options->get("tempDir"), "dompdf-font-");
233
-        file_put_contents($localTempFile, $remoteFileContent);
234
-
235
-        $font = Font::load($localTempFile);
236
-
237
-        if (!$font) {
238
-            unlink($localTempFile);
239
-            return false;
240
-        }
241
-
242
-        $font->parse();
243
-        $font->saveAdobeFontMetrics("$localFilePath.ufm");
244
-        $font->close();
245
-
246
-        unlink($localTempFile);
247
-
248
-        if ( !file_exists("$localFilePath.ufm") ) {
249
-            return false;
250
-        }
251
-
252
-        $fontExtension = ".ttf";
253
-        switch ($font->getFontType()) {
254
-            case "TrueType":
255
-            default:
256
-                $fontExtension = ".ttf";
257
-                break;
258
-        }
259
-
260
-        // Save the changes
261
-        file_put_contents($localFilePath.$fontExtension, $remoteFileContent);
262
-
263
-        if ( !file_exists($localFilePath.$fontExtension) ) {
264
-            unlink("$localFilePath.ufm");
265
-            return false;
266
-        }
267
-
268
-        $this->setFontFamily($fontname, $entry);
269
-
270
-        return true;
271
-    }
272
-
273
-    /**
274
-     * @param $text
275
-     * @param $font
276
-     * @param $size
277
-     * @param float $word_spacing
278
-     * @param float $char_spacing
279
-     * @return float
280
-     * @deprecated
281
-     */
282
-    public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
283
-    {
284
-        //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
285
-        return $this->getTextWidth($text, $font, $size, $word_spacing, $char_spacing);
286
-    }
287
-
288
-    /**
289
-     * Calculates text size, in points
290
-     *
291
-     * @param string $text        The text to be sized
292
-     * @param string $font        The font file to use
293
-     * @param float  $size        The font size, in points
294
-     * @param float  $wordSpacing Word spacing, if any
295
-     * @param float  $charSpacing Char spacing, if any
296
-     *
297
-     * @return float
298
-     */
299
-    public function getTextWidth(string $text, $font, float $size, float $wordSpacing = 0.0, float $charSpacing = 0.0): float
300
-    {
301
-        // @todo Make sure this cache is efficient before enabling it
302
-        static $cache = [];
303
-
304
-        if ($text === "") {
305
-            return 0;
306
-        }
307
-
308
-        // Don't cache long strings
309
-        $useCache = !isset($text[50]); // Faster than strlen
310
-
311
-        // Text-size calculations depend on the canvas used. Make sure to not
312
-        // return wrong values when switching canvas backends
313
-        $canvasClass = get_class($this->canvas);
314
-        $key = "$canvasClass/$font/$size/$wordSpacing/$charSpacing";
315
-
316
-        if ($useCache && isset($cache[$key][$text])) {
317
-            return $cache[$key][$text];
318
-        }
319
-
320
-        $width = $this->canvas->get_text_width($text, $font, $size, $wordSpacing, $charSpacing);
321
-
322
-        if ($useCache) {
323
-            $cache[$key][$text] = $width;
324
-        }
325
-
326
-        return $width;
327
-    }
328
-
329
-    /**
330
-     * @param $font
331
-     * @param $size
332
-     * @return float
333
-     * @deprecated
334
-     */
335
-    public function get_font_height($font, $size)
336
-    {
337
-        return $this->getFontHeight($font, $size);
338
-    }
339
-
340
-    /**
341
-     * Calculates font height, in points
342
-     *
343
-     * @param string $font The font file to use
344
-     * @param float  $size The font size, in points
345
-     *
346
-     * @return float
347
-     */
348
-    public function getFontHeight($font, float $size): float
349
-    {
350
-        return $this->canvas->get_font_height($font, $size);
351
-    }
352
-
353
-    /**
354
-     * Calculates font baseline, in points
355
-     *
356
-     * @param string $font The font file to use
357
-     * @param float  $size The font size, in points
358
-     *
359
-     * @return float
360
-     */
361
-    public function getFontBaseline($font, float $size): float
362
-    {
363
-        return $this->canvas->get_font_baseline($font, $size);
364
-    }
365
-
366
-    /**
367
-     * @param $family_raw
368
-     * @param string $subtype_raw
369
-     * @return string
370
-     * @deprecated
371
-     */
372
-    public function get_font($family_raw, $subtype_raw = "normal")
373
-    {
374
-        return $this->getFont($family_raw, $subtype_raw);
375
-    }
376
-
377
-    /**
378
-     * Resolves a font family & subtype into an actual font file
379
-     * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'.  If
380
-     * the particular font family has no suitable font file, the default font
381
-     * ({@link Options::defaultFont}) is used.  The font file returned
382
-     * is the absolute pathname to the font file on the system.
383
-     *
384
-     * @param string|null $familyRaw
385
-     * @param string      $subtypeRaw
386
-     *
387
-     * @return string|null
388
-     */
389
-    public function getFont($familyRaw, $subtypeRaw = "normal")
390
-    {
391
-        static $cache = [];
392
-
393
-        if (isset($cache[$familyRaw][$subtypeRaw])) {
394
-            return $cache[$familyRaw][$subtypeRaw];
395
-        }
396
-
397
-        /* Allow calling for various fonts in search path. Therefore not immediately
135
+			$cacheDataClosure = require $legacyCacheFile;
136
+			$cacheData = is_array($cacheDataClosure) ? $cacheDataClosure : $cacheDataClosure($fontDir, $rootDir);
137
+			if (is_array($cacheData)) {
138
+				foreach ($cacheData as $family => $variants) {
139
+					if (!isset($this->bundledFonts[$family]) && is_array($variants)) {
140
+						foreach ($variants as $variant => $variantPath) {
141
+							$variantName = basename($variantPath);
142
+							$variantDir = dirname($variantPath);
143
+							if ($variantDir == $fontDir) {
144
+								$this->userFonts[$family][$variant] = $variantName;
145
+							} else {
146
+								$this->userFonts[$family][$variant] = $variantPath;
147
+							}
148
+						}
149
+					}
150
+				}
151
+				$this->saveFontFamilies();
152
+			}
153
+		}
154
+	}
155
+
156
+	/**
157
+	 * @param array $style
158
+	 * @param string $remote_file
159
+	 * @param resource $context
160
+	 * @return bool
161
+	 * @deprecated
162
+	 */
163
+	public function register_font($style, $remote_file, $context = null)
164
+	{
165
+		return $this->registerFont($style, $remote_file);
166
+	}
167
+
168
+	/**
169
+	 * @param array $style
170
+	 * @param string $remoteFile
171
+	 * @param resource $context
172
+	 * @return bool
173
+	 */
174
+	public function registerFont($style, $remoteFile, $context = null)
175
+	{
176
+		$fontname = mb_strtolower($style["family"]);
177
+		$families = $this->getFontFamilies();
178
+
179
+		$entry = [];
180
+		if (isset($families[$fontname])) {
181
+			$entry = $families[$fontname];
182
+		}
183
+
184
+		$styleString = $this->getType("{$style['weight']} {$style['style']}");
185
+
186
+		$remoteHash = md5($remoteFile);
187
+
188
+		$prefix = $fontname . "_" . $styleString;
189
+		$prefix = trim($prefix, "-");
190
+		if (function_exists('iconv')) {
191
+			$prefix = @iconv('utf-8', 'us-ascii//TRANSLIT', $prefix);
192
+		}
193
+		$prefix_encoding = mb_detect_encoding($prefix, mb_detect_order(), true);
194
+		$substchar = mb_substitute_character();
195
+		mb_substitute_character(0x005F);
196
+		$prefix = mb_convert_encoding($prefix, "ISO-8859-1", $prefix_encoding);
197
+		mb_substitute_character($substchar);
198
+		$prefix = preg_replace("[\W]", "_", $prefix);
199
+		$prefix = preg_replace("/[^-_\w]+/", "", $prefix);
200
+
201
+		$localFile = $prefix . "_" . $remoteHash;
202
+		$localFilePath = $this->getOptions()->getFontDir() . "/" . $localFile;
203
+
204
+		if (isset($entry[$styleString]) && $localFilePath == $entry[$styleString]) {
205
+			return true;
206
+		}
207
+
208
+
209
+		$entry[$styleString] = $localFile;
210
+
211
+		// Download the remote file
212
+		[$protocol] = Helpers::explode_url($remoteFile);
213
+		$allowed_protocols = $this->options->getAllowedProtocols();
214
+		if (!array_key_exists($protocol, $allowed_protocols)) {
215
+			Helpers::record_warnings(E_USER_WARNING, "Permission denied on $remoteFile. The communication protocol is not supported.", __FILE__, __LINE__);
216
+			return false;
217
+		}
218
+
219
+		foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
220
+			[$result, $message] = $rule($remoteFile);
221
+			if ($result !== true) {
222
+				Helpers::record_warnings(E_USER_WARNING, "Error loading $remoteFile: $message", __FILE__, __LINE__);
223
+				return false;
224
+			}
225
+		}
226
+
227
+		list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context);
228
+		if ($remoteFileContent === null) {
229
+			return false;
230
+		}
231
+
232
+		$localTempFile = @tempnam($this->options->get("tempDir"), "dompdf-font-");
233
+		file_put_contents($localTempFile, $remoteFileContent);
234
+
235
+		$font = Font::load($localTempFile);
236
+
237
+		if (!$font) {
238
+			unlink($localTempFile);
239
+			return false;
240
+		}
241
+
242
+		$font->parse();
243
+		$font->saveAdobeFontMetrics("$localFilePath.ufm");
244
+		$font->close();
245
+
246
+		unlink($localTempFile);
247
+
248
+		if ( !file_exists("$localFilePath.ufm") ) {
249
+			return false;
250
+		}
251
+
252
+		$fontExtension = ".ttf";
253
+		switch ($font->getFontType()) {
254
+			case "TrueType":
255
+			default:
256
+				$fontExtension = ".ttf";
257
+				break;
258
+		}
259
+
260
+		// Save the changes
261
+		file_put_contents($localFilePath.$fontExtension, $remoteFileContent);
262
+
263
+		if ( !file_exists($localFilePath.$fontExtension) ) {
264
+			unlink("$localFilePath.ufm");
265
+			return false;
266
+		}
267
+
268
+		$this->setFontFamily($fontname, $entry);
269
+
270
+		return true;
271
+	}
272
+
273
+	/**
274
+	 * @param $text
275
+	 * @param $font
276
+	 * @param $size
277
+	 * @param float $word_spacing
278
+	 * @param float $char_spacing
279
+	 * @return float
280
+	 * @deprecated
281
+	 */
282
+	public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
283
+	{
284
+		//return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
285
+		return $this->getTextWidth($text, $font, $size, $word_spacing, $char_spacing);
286
+	}
287
+
288
+	/**
289
+	 * Calculates text size, in points
290
+	 *
291
+	 * @param string $text        The text to be sized
292
+	 * @param string $font        The font file to use
293
+	 * @param float  $size        The font size, in points
294
+	 * @param float  $wordSpacing Word spacing, if any
295
+	 * @param float  $charSpacing Char spacing, if any
296
+	 *
297
+	 * @return float
298
+	 */
299
+	public function getTextWidth(string $text, $font, float $size, float $wordSpacing = 0.0, float $charSpacing = 0.0): float
300
+	{
301
+		// @todo Make sure this cache is efficient before enabling it
302
+		static $cache = [];
303
+
304
+		if ($text === "") {
305
+			return 0;
306
+		}
307
+
308
+		// Don't cache long strings
309
+		$useCache = !isset($text[50]); // Faster than strlen
310
+
311
+		// Text-size calculations depend on the canvas used. Make sure to not
312
+		// return wrong values when switching canvas backends
313
+		$canvasClass = get_class($this->canvas);
314
+		$key = "$canvasClass/$font/$size/$wordSpacing/$charSpacing";
315
+
316
+		if ($useCache && isset($cache[$key][$text])) {
317
+			return $cache[$key][$text];
318
+		}
319
+
320
+		$width = $this->canvas->get_text_width($text, $font, $size, $wordSpacing, $charSpacing);
321
+
322
+		if ($useCache) {
323
+			$cache[$key][$text] = $width;
324
+		}
325
+
326
+		return $width;
327
+	}
328
+
329
+	/**
330
+	 * @param $font
331
+	 * @param $size
332
+	 * @return float
333
+	 * @deprecated
334
+	 */
335
+	public function get_font_height($font, $size)
336
+	{
337
+		return $this->getFontHeight($font, $size);
338
+	}
339
+
340
+	/**
341
+	 * Calculates font height, in points
342
+	 *
343
+	 * @param string $font The font file to use
344
+	 * @param float  $size The font size, in points
345
+	 *
346
+	 * @return float
347
+	 */
348
+	public function getFontHeight($font, float $size): float
349
+	{
350
+		return $this->canvas->get_font_height($font, $size);
351
+	}
352
+
353
+	/**
354
+	 * Calculates font baseline, in points
355
+	 *
356
+	 * @param string $font The font file to use
357
+	 * @param float  $size The font size, in points
358
+	 *
359
+	 * @return float
360
+	 */
361
+	public function getFontBaseline($font, float $size): float
362
+	{
363
+		return $this->canvas->get_font_baseline($font, $size);
364
+	}
365
+
366
+	/**
367
+	 * @param $family_raw
368
+	 * @param string $subtype_raw
369
+	 * @return string
370
+	 * @deprecated
371
+	 */
372
+	public function get_font($family_raw, $subtype_raw = "normal")
373
+	{
374
+		return $this->getFont($family_raw, $subtype_raw);
375
+	}
376
+
377
+	/**
378
+	 * Resolves a font family & subtype into an actual font file
379
+	 * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'.  If
380
+	 * the particular font family has no suitable font file, the default font
381
+	 * ({@link Options::defaultFont}) is used.  The font file returned
382
+	 * is the absolute pathname to the font file on the system.
383
+	 *
384
+	 * @param string|null $familyRaw
385
+	 * @param string      $subtypeRaw
386
+	 *
387
+	 * @return string|null
388
+	 */
389
+	public function getFont($familyRaw, $subtypeRaw = "normal")
390
+	{
391
+		static $cache = [];
392
+
393
+		if (isset($cache[$familyRaw][$subtypeRaw])) {
394
+			return $cache[$familyRaw][$subtypeRaw];
395
+		}
396
+
397
+		/* Allow calling for various fonts in search path. Therefore not immediately
398 398
          * return replacement on non match.
399 399
          * Only when called with NULL try replacement.
400 400
          * When this is also missing there is really trouble.
@@ -402,234 +402,234 @@  discard block
 block discarded – undo
402 402
          * Only on checking the fallback font, check various subtypes on same font.
403 403
          */
404 404
 
405
-        $subtype = strtolower($subtypeRaw);
405
+		$subtype = strtolower($subtypeRaw);
406 406
 
407
-        $families = $this->getFontFamilies();
408
-        if ($familyRaw) {
409
-            $family = str_replace(["'", '"'], "", strtolower($familyRaw));
407
+		$families = $this->getFontFamilies();
408
+		if ($familyRaw) {
409
+			$family = str_replace(["'", '"'], "", strtolower($familyRaw));
410 410
 
411
-            if (isset($families[$family][$subtype])) {
412
-                return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
413
-            }
411
+			if (isset($families[$family][$subtype])) {
412
+				return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
413
+			}
414 414
 
415
-            return null;
416
-        }
415
+			return null;
416
+		}
417 417
 
418
-        $fallback_families = [strtolower($this->options->getDefaultFont()), "serif"];
419
-        foreach ($fallback_families as $family) {
420
-            if (isset($families[$family][$subtype])) {
421
-                return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
422
-            }
418
+		$fallback_families = [strtolower($this->options->getDefaultFont()), "serif"];
419
+		foreach ($fallback_families as $family) {
420
+			if (isset($families[$family][$subtype])) {
421
+				return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
422
+			}
423 423
     
424
-            if (!isset($families[$family])) {
425
-                continue;
426
-            }
424
+			if (!isset($families[$family])) {
425
+				continue;
426
+			}
427 427
     
428
-            $family = $families[$family];
428
+			$family = $families[$family];
429 429
     
430
-            foreach ($family as $sub => $font) {
431
-                if (strpos($subtype, $sub) !== false) {
432
-                    return $cache[$familyRaw][$subtypeRaw] = $font;
433
-                }
434
-            }
430
+			foreach ($family as $sub => $font) {
431
+				if (strpos($subtype, $sub) !== false) {
432
+					return $cache[$familyRaw][$subtypeRaw] = $font;
433
+				}
434
+			}
435 435
     
436
-            if ($subtype !== "normal") {
437
-                foreach ($family as $sub => $font) {
438
-                    if ($sub !== "normal") {
439
-                        return $cache[$familyRaw][$subtypeRaw] = $font;
440
-                    }
441
-                }
442
-            }
436
+			if ($subtype !== "normal") {
437
+				foreach ($family as $sub => $font) {
438
+					if ($sub !== "normal") {
439
+						return $cache[$familyRaw][$subtypeRaw] = $font;
440
+					}
441
+				}
442
+			}
443 443
     
444
-            $subtype = "normal";
444
+			$subtype = "normal";
445 445
     
446
-            if (isset($family[$subtype])) {
447
-                return $cache[$familyRaw][$subtypeRaw] = $family[$subtype];
448
-            }
449
-        }
446
+			if (isset($family[$subtype])) {
447
+				return $cache[$familyRaw][$subtypeRaw] = $family[$subtype];
448
+			}
449
+		}
450 450
         
451
-        return null;
452
-    }
453
-
454
-    /**
455
-     * @param $family
456
-     * @return null|string
457
-     * @deprecated
458
-     */
459
-    public function get_family($family)
460
-    {
461
-        return $this->getFamily($family);
462
-    }
463
-
464
-    /**
465
-     * @param string $family
466
-     * @return null|string
467
-     */
468
-    public function getFamily($family)
469
-    {
470
-        $family = str_replace(["'", '"'], "", mb_strtolower($family));
471
-        $families = $this->getFontFamilies();
472
-
473
-        if (isset($families[$family])) {
474
-            return $families[$family];
475
-        }
476
-
477
-        return null;
478
-    }
479
-
480
-    /**
481
-     * @param $type
482
-     * @return string
483
-     * @deprecated
484
-     */
485
-    public function get_type($type)
486
-    {
487
-        return $this->getType($type);
488
-    }
489
-
490
-    /**
491
-     * @param string $type
492
-     * @return string
493
-     */
494
-    public function getType($type)
495
-    {
496
-        if (preg_match('/bold/i', $type)) {
497
-            $weight = 700;
498
-        } elseif (preg_match('/([1-9]00)/', $type, $match)) {
499
-            $weight = (int)$match[0];
500
-        } else {
501
-            $weight = 400;
502
-        }
503
-        $weight = $weight === 400 ? 'normal' : $weight;
504
-        $weight = $weight === 700 ? 'bold' : $weight;
505
-
506
-        $style = preg_match('/italic|oblique/i', $type) ? 'italic' : null;
507
-
508
-        if ($weight === 'normal' && $style !== null) {
509
-            return $style;
510
-        }
511
-
512
-        return $style === null
513
-            ? $weight
514
-            : $weight.'_'.$style;
515
-    }
516
-
517
-    /**
518
-     * @return array
519
-     * @deprecated
520
-     */
521
-    public function get_font_families()
522
-    {
523
-        return $this->getFontFamilies();
524
-    }
525
-
526
-    /**
527
-     * Returns the current font lookup table
528
-     *
529
-     * @return array
530
-     */
531
-    public function getFontFamilies()
532
-    {
533
-        if (!isset($this->fontFamilies)) {
534
-            $this->setFontFamilies();
535
-        }
536
-        return $this->fontFamilies;
537
-    }
538
-
539
-    /**
540
-     * Convert loaded fonts to font lookup table
541
-     *
542
-     * @return array
543
-     */
544
-    public function setFontFamilies()
545
-    {
546
-        $fontFamilies = [];
547
-        if (isset($this->bundledFonts) && is_array($this->bundledFonts)) {
548
-            foreach ($this->bundledFonts as $family => $variants) {
549
-                if (!isset($fontFamilies[$family])) {
550
-                    $fontFamilies[$family] = array_map(function ($variant) {
551
-                        return $this->getOptions()->getRootDir() . '/lib/fonts/' . $variant;
552
-                    }, $variants);
553
-                }
554
-            }
555
-        }
556
-        if (isset($this->userFonts) && is_array($this->userFonts)) {
557
-            foreach ($this->userFonts as $family => $variants) {
558
-                $fontFamilies[$family] = array_map(function ($variant) {
559
-                    $variantName = basename($variant);
560
-                    if ($variantName === $variant) {
561
-                        return $this->getOptions()->getFontDir() . '/' . $variant;
562
-                    }
563
-                    return $variant;
564
-                }, $variants);
565
-            }
566
-        }
567
-        $this->fontFamilies = $fontFamilies;
568
-    }
569
-
570
-    /**
571
-     * @param string $fontname
572
-     * @param mixed $entry
573
-     * @deprecated
574
-     */
575
-    public function set_font_family($fontname, $entry)
576
-    {
577
-        $this->setFontFamily($fontname, $entry);
578
-    }
579
-
580
-    /**
581
-     * @param string $fontname
582
-     * @param mixed $entry
583
-     */
584
-    public function setFontFamily($fontname, $entry)
585
-    {
586
-        $this->userFonts[mb_strtolower($fontname)] = $entry;
587
-        $this->saveFontFamilies();
588
-        unset($this->fontFamilies);
589
-    }
590
-
591
-    /**
592
-     * @return string
593
-     */
594
-    public function getUserFontsFilePath()
595
-    {
596
-        return $this->options->getFontDir() . '/' . self::USER_FONTS_FILE;
597
-    }
598
-
599
-    /**
600
-     * @param Options $options
601
-     * @return $this
602
-     */
603
-    public function setOptions(Options $options)
604
-    {
605
-        $this->options = $options;
606
-        unset($this->fontFamilies);
607
-        return $this;
608
-    }
609
-
610
-    /**
611
-     * @return Options
612
-     */
613
-    public function getOptions()
614
-    {
615
-        return $this->options;
616
-    }
617
-
618
-    /**
619
-     * @param Canvas $canvas
620
-     * @return $this
621
-     */
622
-    public function setCanvas(Canvas $canvas)
623
-    {
624
-        $this->canvas = $canvas;
625
-        return $this;
626
-    }
627
-
628
-    /**
629
-     * @return Canvas
630
-     */
631
-    public function getCanvas()
632
-    {
633
-        return $this->canvas;
634
-    }
451
+		return null;
452
+	}
453
+
454
+	/**
455
+	 * @param $family
456
+	 * @return null|string
457
+	 * @deprecated
458
+	 */
459
+	public function get_family($family)
460
+	{
461
+		return $this->getFamily($family);
462
+	}
463
+
464
+	/**
465
+	 * @param string $family
466
+	 * @return null|string
467
+	 */
468
+	public function getFamily($family)
469
+	{
470
+		$family = str_replace(["'", '"'], "", mb_strtolower($family));
471
+		$families = $this->getFontFamilies();
472
+
473
+		if (isset($families[$family])) {
474
+			return $families[$family];
475
+		}
476
+
477
+		return null;
478
+	}
479
+
480
+	/**
481
+	 * @param $type
482
+	 * @return string
483
+	 * @deprecated
484
+	 */
485
+	public function get_type($type)
486
+	{
487
+		return $this->getType($type);
488
+	}
489
+
490
+	/**
491
+	 * @param string $type
492
+	 * @return string
493
+	 */
494
+	public function getType($type)
495
+	{
496
+		if (preg_match('/bold/i', $type)) {
497
+			$weight = 700;
498
+		} elseif (preg_match('/([1-9]00)/', $type, $match)) {
499
+			$weight = (int)$match[0];
500
+		} else {
501
+			$weight = 400;
502
+		}
503
+		$weight = $weight === 400 ? 'normal' : $weight;
504
+		$weight = $weight === 700 ? 'bold' : $weight;
505
+
506
+		$style = preg_match('/italic|oblique/i', $type) ? 'italic' : null;
507
+
508
+		if ($weight === 'normal' && $style !== null) {
509
+			return $style;
510
+		}
511
+
512
+		return $style === null
513
+			? $weight
514
+			: $weight.'_'.$style;
515
+	}
516
+
517
+	/**
518
+	 * @return array
519
+	 * @deprecated
520
+	 */
521
+	public function get_font_families()
522
+	{
523
+		return $this->getFontFamilies();
524
+	}
525
+
526
+	/**
527
+	 * Returns the current font lookup table
528
+	 *
529
+	 * @return array
530
+	 */
531
+	public function getFontFamilies()
532
+	{
533
+		if (!isset($this->fontFamilies)) {
534
+			$this->setFontFamilies();
535
+		}
536
+		return $this->fontFamilies;
537
+	}
538
+
539
+	/**
540
+	 * Convert loaded fonts to font lookup table
541
+	 *
542
+	 * @return array
543
+	 */
544
+	public function setFontFamilies()
545
+	{
546
+		$fontFamilies = [];
547
+		if (isset($this->bundledFonts) && is_array($this->bundledFonts)) {
548
+			foreach ($this->bundledFonts as $family => $variants) {
549
+				if (!isset($fontFamilies[$family])) {
550
+					$fontFamilies[$family] = array_map(function ($variant) {
551
+						return $this->getOptions()->getRootDir() . '/lib/fonts/' . $variant;
552
+					}, $variants);
553
+				}
554
+			}
555
+		}
556
+		if (isset($this->userFonts) && is_array($this->userFonts)) {
557
+			foreach ($this->userFonts as $family => $variants) {
558
+				$fontFamilies[$family] = array_map(function ($variant) {
559
+					$variantName = basename($variant);
560
+					if ($variantName === $variant) {
561
+						return $this->getOptions()->getFontDir() . '/' . $variant;
562
+					}
563
+					return $variant;
564
+				}, $variants);
565
+			}
566
+		}
567
+		$this->fontFamilies = $fontFamilies;
568
+	}
569
+
570
+	/**
571
+	 * @param string $fontname
572
+	 * @param mixed $entry
573
+	 * @deprecated
574
+	 */
575
+	public function set_font_family($fontname, $entry)
576
+	{
577
+		$this->setFontFamily($fontname, $entry);
578
+	}
579
+
580
+	/**
581
+	 * @param string $fontname
582
+	 * @param mixed $entry
583
+	 */
584
+	public function setFontFamily($fontname, $entry)
585
+	{
586
+		$this->userFonts[mb_strtolower($fontname)] = $entry;
587
+		$this->saveFontFamilies();
588
+		unset($this->fontFamilies);
589
+	}
590
+
591
+	/**
592
+	 * @return string
593
+	 */
594
+	public function getUserFontsFilePath()
595
+	{
596
+		return $this->options->getFontDir() . '/' . self::USER_FONTS_FILE;
597
+	}
598
+
599
+	/**
600
+	 * @param Options $options
601
+	 * @return $this
602
+	 */
603
+	public function setOptions(Options $options)
604
+	{
605
+		$this->options = $options;
606
+		unset($this->fontFamilies);
607
+		return $this;
608
+	}
609
+
610
+	/**
611
+	 * @return Options
612
+	 */
613
+	public function getOptions()
614
+	{
615
+		return $this->options;
616
+	}
617
+
618
+	/**
619
+	 * @param Canvas $canvas
620
+	 * @return $this
621
+	 */
622
+	public function setCanvas(Canvas $canvas)
623
+	{
624
+		$this->canvas = $canvas;
625
+		return $this;
626
+	}
627
+
628
+	/**
629
+	 * @return Canvas
630
+	 */
631
+	public function getCanvas()
632
+	{
633
+		return $this->canvas;
634
+	}
635 635
 }
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -112,7 +112,7 @@  discard block
 block discarded – undo
112 112
      */
113 113
     public function loadFontFamilies()
114 114
     {
115
-        $file = $this->options->getRootDir() . "/lib/fonts/installed-fonts.dist.json";
115
+        $file = $this->options->getRootDir()."/lib/fonts/installed-fonts.dist.json";
116 116
         $this->bundledFonts = json_decode(file_get_contents($file), true);
117 117
 
118 118
         if (is_readable($this->getUserFontsFilePath())) {
@@ -124,19 +124,19 @@  discard block
 block discarded – undo
124 124
 
125 125
     private function loadFontFamiliesLegacy()
126 126
     {
127
-        $legacyCacheFile = $this->options->getFontDir() . '/dompdf_font_family_cache.php';
127
+        $legacyCacheFile = $this->options->getFontDir().'/dompdf_font_family_cache.php';
128 128
         if (is_readable($legacyCacheFile)) {
129 129
             $fontDir = $this->options->getFontDir();
130 130
             $rootDir = $this->options->getRootDir();
131 131
     
132
-            if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); }
133
-            if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); }
132
+            if ( ! defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); }
133
+            if ( ! defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); }
134 134
     
135 135
             $cacheDataClosure = require $legacyCacheFile;
136 136
             $cacheData = is_array($cacheDataClosure) ? $cacheDataClosure : $cacheDataClosure($fontDir, $rootDir);
137 137
             if (is_array($cacheData)) {
138 138
                 foreach ($cacheData as $family => $variants) {
139
-                    if (!isset($this->bundledFonts[$family]) && is_array($variants)) {
139
+                    if ( ! isset($this->bundledFonts[$family]) && is_array($variants)) {
140 140
                         foreach ($variants as $variant => $variantPath) {
141 141
                             $variantName = basename($variantPath);
142 142
                             $variantDir = dirname($variantPath);
@@ -185,7 +185,7 @@  discard block
 block discarded – undo
185 185
 
186 186
         $remoteHash = md5($remoteFile);
187 187
 
188
-        $prefix = $fontname . "_" . $styleString;
188
+        $prefix = $fontname."_".$styleString;
189 189
         $prefix = trim($prefix, "-");
190 190
         if (function_exists('iconv')) {
191 191
             $prefix = @iconv('utf-8', 'us-ascii//TRANSLIT', $prefix);
@@ -198,8 +198,8 @@  discard block
 block discarded – undo
198 198
         $prefix = preg_replace("[\W]", "_", $prefix);
199 199
         $prefix = preg_replace("/[^-_\w]+/", "", $prefix);
200 200
 
201
-        $localFile = $prefix . "_" . $remoteHash;
202
-        $localFilePath = $this->getOptions()->getFontDir() . "/" . $localFile;
201
+        $localFile = $prefix."_".$remoteHash;
202
+        $localFilePath = $this->getOptions()->getFontDir()."/".$localFile;
203 203
 
204 204
         if (isset($entry[$styleString]) && $localFilePath == $entry[$styleString]) {
205 205
             return true;
@@ -211,7 +211,7 @@  discard block
 block discarded – undo
211 211
         // Download the remote file
212 212
         [$protocol] = Helpers::explode_url($remoteFile);
213 213
         $allowed_protocols = $this->options->getAllowedProtocols();
214
-        if (!array_key_exists($protocol, $allowed_protocols)) {
214
+        if ( ! array_key_exists($protocol, $allowed_protocols)) {
215 215
             Helpers::record_warnings(E_USER_WARNING, "Permission denied on $remoteFile. The communication protocol is not supported.", __FILE__, __LINE__);
216 216
             return false;
217 217
         }
@@ -234,7 +234,7 @@  discard block
 block discarded – undo
234 234
 
235 235
         $font = Font::load($localTempFile);
236 236
 
237
-        if (!$font) {
237
+        if ( ! $font) {
238 238
             unlink($localTempFile);
239 239
             return false;
240 240
         }
@@ -245,7 +245,7 @@  discard block
 block discarded – undo
245 245
 
246 246
         unlink($localTempFile);
247 247
 
248
-        if ( !file_exists("$localFilePath.ufm") ) {
248
+        if ( ! file_exists("$localFilePath.ufm")) {
249 249
             return false;
250 250
         }
251 251
 
@@ -260,7 +260,7 @@  discard block
 block discarded – undo
260 260
         // Save the changes
261 261
         file_put_contents($localFilePath.$fontExtension, $remoteFileContent);
262 262
 
263
-        if ( !file_exists($localFilePath.$fontExtension) ) {
263
+        if ( ! file_exists($localFilePath.$fontExtension)) {
264 264
             unlink("$localFilePath.ufm");
265 265
             return false;
266 266
         }
@@ -306,7 +306,7 @@  discard block
 block discarded – undo
306 306
         }
307 307
 
308 308
         // Don't cache long strings
309
-        $useCache = !isset($text[50]); // Faster than strlen
309
+        $useCache = ! isset($text[50]); // Faster than strlen
310 310
 
311 311
         // Text-size calculations depend on the canvas used. Make sure to not
312 312
         // return wrong values when switching canvas backends
@@ -421,7 +421,7 @@  discard block
 block discarded – undo
421 421
                 return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
422 422
             }
423 423
     
424
-            if (!isset($families[$family])) {
424
+            if ( ! isset($families[$family])) {
425 425
                 continue;
426 426
             }
427 427
     
@@ -496,7 +496,7 @@  discard block
 block discarded – undo
496 496
         if (preg_match('/bold/i', $type)) {
497 497
             $weight = 700;
498 498
         } elseif (preg_match('/([1-9]00)/', $type, $match)) {
499
-            $weight = (int)$match[0];
499
+            $weight = (int) $match[0];
500 500
         } else {
501 501
             $weight = 400;
502 502
         }
@@ -530,7 +530,7 @@  discard block
 block discarded – undo
530 530
      */
531 531
     public function getFontFamilies()
532 532
     {
533
-        if (!isset($this->fontFamilies)) {
533
+        if ( ! isset($this->fontFamilies)) {
534 534
             $this->setFontFamilies();
535 535
         }
536 536
         return $this->fontFamilies;
@@ -546,19 +546,19 @@  discard block
 block discarded – undo
546 546
         $fontFamilies = [];
547 547
         if (isset($this->bundledFonts) && is_array($this->bundledFonts)) {
548 548
             foreach ($this->bundledFonts as $family => $variants) {
549
-                if (!isset($fontFamilies[$family])) {
550
-                    $fontFamilies[$family] = array_map(function ($variant) {
551
-                        return $this->getOptions()->getRootDir() . '/lib/fonts/' . $variant;
549
+                if ( ! isset($fontFamilies[$family])) {
550
+                    $fontFamilies[$family] = array_map(function($variant) {
551
+                        return $this->getOptions()->getRootDir().'/lib/fonts/'.$variant;
552 552
                     }, $variants);
553 553
                 }
554 554
             }
555 555
         }
556 556
         if (isset($this->userFonts) && is_array($this->userFonts)) {
557 557
             foreach ($this->userFonts as $family => $variants) {
558
-                $fontFamilies[$family] = array_map(function ($variant) {
558
+                $fontFamilies[$family] = array_map(function($variant) {
559 559
                     $variantName = basename($variant);
560 560
                     if ($variantName === $variant) {
561
-                        return $this->getOptions()->getFontDir() . '/' . $variant;
561
+                        return $this->getOptions()->getFontDir().'/'.$variant;
562 562
                     }
563 563
                     return $variant;
564 564
                 }, $variants);
@@ -593,7 +593,7 @@  discard block
 block discarded – undo
593 593
      */
594 594
     public function getUserFontsFilePath()
595 595
     {
596
-        return $this->options->getFontDir() . '/' . self::USER_FONTS_FILE;
596
+        return $this->options->getFontDir().'/'.self::USER_FONTS_FILE;
597 597
     }
598 598
 
599 599
     /**
Please login to merge, or discard this patch.
vendor/dompdf/dompdf/src/FrameReflower/TableCell.php 2 patches
Indentation   +139 added lines, -139 removed lines patch added patch discarded remove patch
@@ -17,145 +17,145 @@
 block discarded – undo
17 17
  */
18 18
 class TableCell extends Block
19 19
 {
20
-    /**
21
-     * TableCell constructor.
22
-     * @param BlockFrameDecorator $frame
23
-     */
24
-    function __construct(BlockFrameDecorator $frame)
25
-    {
26
-        parent::__construct($frame);
27
-    }
20
+	/**
21
+	 * TableCell constructor.
22
+	 * @param BlockFrameDecorator $frame
23
+	 */
24
+	function __construct(BlockFrameDecorator $frame)
25
+	{
26
+		parent::__construct($frame);
27
+	}
28 28
 
29
-    /**
30
-     * @param BlockFrameDecorator|null $block
31
-     */
32
-    function reflow(BlockFrameDecorator $block = null)
33
-    {
34
-        // Counters and generated content
35
-        $this->_set_content();
36
-
37
-        $style = $this->_frame->get_style();
38
-
39
-        $table = TableFrameDecorator::find_parent_table($this->_frame);
40
-        $cellmap = $table->get_cellmap();
41
-
42
-        list($x, $y) = $cellmap->get_frame_position($this->_frame);
43
-        $this->_frame->set_position($x, $y);
44
-
45
-        $cells = $cellmap->get_spanned_cells($this->_frame);
46
-
47
-        $w = 0;
48
-        foreach ($cells["columns"] as $i) {
49
-            $col = $cellmap->get_column($i);
50
-            $w += $col["used-width"];
51
-        }
52
-
53
-        //FIXME?
54
-        $h = $this->_frame->get_containing_block("h");
55
-
56
-        $left_space = (float)$style->length_in_pt([$style->margin_left,
57
-                $style->padding_left,
58
-                $style->border_left_width],
59
-            $w);
60
-
61
-        $right_space = (float)$style->length_in_pt([$style->padding_right,
62
-                $style->margin_right,
63
-                $style->border_right_width],
64
-            $w);
65
-
66
-        $top_space = (float)$style->length_in_pt([$style->margin_top,
67
-                $style->padding_top,
68
-                $style->border_top_width],
69
-            $h);
70
-        $bottom_space = (float)$style->length_in_pt([$style->margin_bottom,
71
-                $style->padding_bottom,
72
-                $style->border_bottom_width],
73
-            $h);
74
-
75
-        $cb_w = $w - $left_space - $right_space;
76
-        $style->set_used("width", $cb_w);
77
-
78
-        $content_x = $x + $left_space;
79
-        $content_y = $line_y = $y + $top_space;
80
-
81
-        // Adjust the first line based on the text-indent property
82
-        $indent = (float)$style->length_in_pt($style->text_indent, $w);
83
-        $this->_frame->increase_line_width($indent);
84
-
85
-        $page = $this->_frame->get_root();
86
-
87
-        // Set the y position of the first line in the cell
88
-        $line_box = $this->_frame->get_current_line_box();
89
-        $line_box->y = $line_y;
90
-
91
-        // Set the containing blocks and reflow each child
92
-        foreach ($this->_frame->get_children() as $child) {
93
-            $child->set_containing_block($content_x, $content_y, $cb_w, $h);
94
-            $this->process_clear($child);
95
-            $child->reflow($this->_frame);
96
-            $this->process_float($child, $content_x, $cb_w);
97
-
98
-            if ($page->is_full()) {
99
-                break;
100
-            }
101
-        }
102
-
103
-        // Determine our height
104
-        $style_height = (float)$style->length_in_pt($style->height, $h);
105
-
106
-        /** @var FrameDecorator\TableCell */
107
-        $frame = $this->_frame;
108
-
109
-        $frame->set_content_height($this->_calculate_content_height());
110
-
111
-        $height = max($style_height, (float)$frame->get_content_height());
112
-
113
-        // Let the cellmap know our height
114
-        $cell_height = $height / count($cells["rows"]);
115
-
116
-        if ($style_height <= $height) {
117
-            $cell_height += $top_space + $bottom_space;
118
-        }
119
-
120
-        foreach ($cells["rows"] as $i) {
121
-            $cellmap->set_row_height($i, $cell_height);
122
-        }
123
-
124
-        $style->set_used("height", $height);
125
-
126
-        $this->_text_align();
127
-        $this->vertical_align();
128
-
129
-        // Handle relative positioning
130
-        foreach ($this->_frame->get_children() as $child) {
131
-            $this->position_relative($child);
132
-        }
133
-    }
29
+	/**
30
+	 * @param BlockFrameDecorator|null $block
31
+	 */
32
+	function reflow(BlockFrameDecorator $block = null)
33
+	{
34
+		// Counters and generated content
35
+		$this->_set_content();
36
+
37
+		$style = $this->_frame->get_style();
38
+
39
+		$table = TableFrameDecorator::find_parent_table($this->_frame);
40
+		$cellmap = $table->get_cellmap();
41
+
42
+		list($x, $y) = $cellmap->get_frame_position($this->_frame);
43
+		$this->_frame->set_position($x, $y);
44
+
45
+		$cells = $cellmap->get_spanned_cells($this->_frame);
46
+
47
+		$w = 0;
48
+		foreach ($cells["columns"] as $i) {
49
+			$col = $cellmap->get_column($i);
50
+			$w += $col["used-width"];
51
+		}
52
+
53
+		//FIXME?
54
+		$h = $this->_frame->get_containing_block("h");
55
+
56
+		$left_space = (float)$style->length_in_pt([$style->margin_left,
57
+				$style->padding_left,
58
+				$style->border_left_width],
59
+			$w);
60
+
61
+		$right_space = (float)$style->length_in_pt([$style->padding_right,
62
+				$style->margin_right,
63
+				$style->border_right_width],
64
+			$w);
65
+
66
+		$top_space = (float)$style->length_in_pt([$style->margin_top,
67
+				$style->padding_top,
68
+				$style->border_top_width],
69
+			$h);
70
+		$bottom_space = (float)$style->length_in_pt([$style->margin_bottom,
71
+				$style->padding_bottom,
72
+				$style->border_bottom_width],
73
+			$h);
74
+
75
+		$cb_w = $w - $left_space - $right_space;
76
+		$style->set_used("width", $cb_w);
77
+
78
+		$content_x = $x + $left_space;
79
+		$content_y = $line_y = $y + $top_space;
80
+
81
+		// Adjust the first line based on the text-indent property
82
+		$indent = (float)$style->length_in_pt($style->text_indent, $w);
83
+		$this->_frame->increase_line_width($indent);
84
+
85
+		$page = $this->_frame->get_root();
86
+
87
+		// Set the y position of the first line in the cell
88
+		$line_box = $this->_frame->get_current_line_box();
89
+		$line_box->y = $line_y;
90
+
91
+		// Set the containing blocks and reflow each child
92
+		foreach ($this->_frame->get_children() as $child) {
93
+			$child->set_containing_block($content_x, $content_y, $cb_w, $h);
94
+			$this->process_clear($child);
95
+			$child->reflow($this->_frame);
96
+			$this->process_float($child, $content_x, $cb_w);
97
+
98
+			if ($page->is_full()) {
99
+				break;
100
+			}
101
+		}
102
+
103
+		// Determine our height
104
+		$style_height = (float)$style->length_in_pt($style->height, $h);
105
+
106
+		/** @var FrameDecorator\TableCell */
107
+		$frame = $this->_frame;
108
+
109
+		$frame->set_content_height($this->_calculate_content_height());
110
+
111
+		$height = max($style_height, (float)$frame->get_content_height());
112
+
113
+		// Let the cellmap know our height
114
+		$cell_height = $height / count($cells["rows"]);
115
+
116
+		if ($style_height <= $height) {
117
+			$cell_height += $top_space + $bottom_space;
118
+		}
119
+
120
+		foreach ($cells["rows"] as $i) {
121
+			$cellmap->set_row_height($i, $cell_height);
122
+		}
123
+
124
+		$style->set_used("height", $height);
125
+
126
+		$this->_text_align();
127
+		$this->vertical_align();
128
+
129
+		// Handle relative positioning
130
+		foreach ($this->_frame->get_children() as $child) {
131
+			$this->position_relative($child);
132
+		}
133
+	}
134 134
 
135
-    public function get_min_max_content_width(): array
136
-    {
137
-        // Ignore percentage values for a specified width here, as they are
138
-        // relative to the table width, which is not determined yet
139
-        $style = $this->_frame->get_style();
140
-        $width = $style->width;
141
-        $fixed_width = $width !== "auto" && !Helpers::is_percent($width);
142
-
143
-        [$min, $max] = $this->get_min_max_child_width();
144
-
145
-        // For table cells: Use specified width if it is greater than the
146
-        // minimum defined by the content
147
-        if ($fixed_width) {
148
-            $width = (float) $style->length_in_pt($width, 0);
149
-            $min = max($width, $min);
150
-            $max = $min;
151
-        }
152
-
153
-        // Handle min/max width style properties
154
-        $min_width = $this->resolve_min_width(null);
155
-        $max_width = $this->resolve_max_width(null);
156
-        $min = Helpers::clamp($min, $min_width, $max_width);
157
-        $max = Helpers::clamp($max, $min_width, $max_width);
158
-
159
-        return [$min, $max];
160
-    }
135
+	public function get_min_max_content_width(): array
136
+	{
137
+		// Ignore percentage values for a specified width here, as they are
138
+		// relative to the table width, which is not determined yet
139
+		$style = $this->_frame->get_style();
140
+		$width = $style->width;
141
+		$fixed_width = $width !== "auto" && !Helpers::is_percent($width);
142
+
143
+		[$min, $max] = $this->get_min_max_child_width();
144
+
145
+		// For table cells: Use specified width if it is greater than the
146
+		// minimum defined by the content
147
+		if ($fixed_width) {
148
+			$width = (float) $style->length_in_pt($width, 0);
149
+			$min = max($width, $min);
150
+			$max = $min;
151
+		}
152
+
153
+		// Handle min/max width style properties
154
+		$min_width = $this->resolve_min_width(null);
155
+		$max_width = $this->resolve_max_width(null);
156
+		$min = Helpers::clamp($min, $min_width, $max_width);
157
+		$max = Helpers::clamp($max, $min_width, $max_width);
158
+
159
+		return [$min, $max];
160
+	}
161 161
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -53,21 +53,21 @@  discard block
 block discarded – undo
53 53
         //FIXME?
54 54
         $h = $this->_frame->get_containing_block("h");
55 55
 
56
-        $left_space = (float)$style->length_in_pt([$style->margin_left,
56
+        $left_space = (float) $style->length_in_pt([$style->margin_left,
57 57
                 $style->padding_left,
58 58
                 $style->border_left_width],
59 59
             $w);
60 60
 
61
-        $right_space = (float)$style->length_in_pt([$style->padding_right,
61
+        $right_space = (float) $style->length_in_pt([$style->padding_right,
62 62
                 $style->margin_right,
63 63
                 $style->border_right_width],
64 64
             $w);
65 65
 
66
-        $top_space = (float)$style->length_in_pt([$style->margin_top,
66
+        $top_space = (float) $style->length_in_pt([$style->margin_top,
67 67
                 $style->padding_top,
68 68
                 $style->border_top_width],
69 69
             $h);
70
-        $bottom_space = (float)$style->length_in_pt([$style->margin_bottom,
70
+        $bottom_space = (float) $style->length_in_pt([$style->margin_bottom,
71 71
                 $style->padding_bottom,
72 72
                 $style->border_bottom_width],
73 73
             $h);
@@ -79,7 +79,7 @@  discard block
 block discarded – undo
79 79
         $content_y = $line_y = $y + $top_space;
80 80
 
81 81
         // Adjust the first line based on the text-indent property
82
-        $indent = (float)$style->length_in_pt($style->text_indent, $w);
82
+        $indent = (float) $style->length_in_pt($style->text_indent, $w);
83 83
         $this->_frame->increase_line_width($indent);
84 84
 
85 85
         $page = $this->_frame->get_root();
@@ -101,14 +101,14 @@  discard block
 block discarded – undo
101 101
         }
102 102
 
103 103
         // Determine our height
104
-        $style_height = (float)$style->length_in_pt($style->height, $h);
104
+        $style_height = (float) $style->length_in_pt($style->height, $h);
105 105
 
106 106
         /** @var FrameDecorator\TableCell */
107 107
         $frame = $this->_frame;
108 108
 
109 109
         $frame->set_content_height($this->_calculate_content_height());
110 110
 
111
-        $height = max($style_height, (float)$frame->get_content_height());
111
+        $height = max($style_height, (float) $frame->get_content_height());
112 112
 
113 113
         // Let the cellmap know our height
114 114
         $cell_height = $height / count($cells["rows"]);
@@ -138,7 +138,7 @@  discard block
 block discarded – undo
138 138
         // relative to the table width, which is not determined yet
139 139
         $style = $this->_frame->get_style();
140 140
         $width = $style->width;
141
-        $fixed_width = $width !== "auto" && !Helpers::is_percent($width);
141
+        $fixed_width = $width !== "auto" && ! Helpers::is_percent($width);
142 142
 
143 143
         [$min, $max] = $this->get_min_max_child_width();
144 144
 
Please login to merge, or discard this patch.
vendor/dompdf/dompdf/src/FrameReflower/Page.php 2 patches
Indentation   +178 added lines, -178 removed lines patch added patch discarded remove patch
@@ -18,182 +18,182 @@
 block discarded – undo
18 18
 class Page extends AbstractFrameReflower
19 19
 {
20 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
-    }
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 199
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -67,11 +67,11 @@  discard block
 block discarded – undo
67 67
             }
68 68
 
69 69
             // FIXME RTL
70
-            if (!$odd && isset($page_styles[":left"])) {
70
+            if ( ! $odd && isset($page_styles[":left"])) {
71 71
                 $style->merge($page_styles[":left"]);
72 72
             }
73 73
 
74
-            if (!$odd && isset($page_styles[":even"])) {
74
+            if ( ! $odd && isset($page_styles[":even"])) {
75 75
                 $style->merge($page_styles[":even"]);
76 76
             }
77 77
 
@@ -107,10 +107,10 @@  discard block
 block discarded – undo
107 107
 
108 108
             // Pages are only concerned with margins
109 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"]);
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 114
 
115 115
             $content_x = $cb["x"] + $left;
116 116
             $content_y = $cb["y"] + $top;
@@ -180,7 +180,7 @@  discard block
 block discarded – undo
180 180
      */
181 181
     protected function _check_callbacks(string $event, Frame $frame): void
182 182
     {
183
-        if (!isset($this->_callbacks)) {
183
+        if ( ! isset($this->_callbacks)) {
184 184
             $dompdf = $this->get_dompdf();
185 185
             $this->_callbacks = $dompdf->getCallbacks();
186 186
             $this->_canvas = $dompdf->getCanvas();
Please login to merge, or discard this patch.
vendor/dompdf/dompdf/src/FrameReflower/Text.php 1 patch
Indentation   +583 added lines, -583 removed lines patch added patch discarded remove patch
@@ -19,587 +19,587 @@
 block discarded – undo
19 19
  */
20 20
 class Text extends AbstractFrameReflower
21 21
 {
22
-    /**
23
-     * PHP string representation of HTML entity <shy>
24
-     */
25
-    const SOFT_HYPHEN = "\xC2\xAD";
26
-
27
-    /**
28
-     * The regex splits on everything that's a separator (^\S double negative),
29
-     * excluding the following non-breaking space characters:
30
-     * * nbsp (\xA0)
31
-     * * narrow nbsp (\x{202F})
32
-     * * figure space (\x{2007})
33
-     */
34
-    public static $_whitespace_pattern = '/([^\S\xA0\x{202F}\x{2007}]+)/u';
35
-
36
-    /**
37
-     * The regex splits on everything that's a separator (^\S double negative)
38
-     * plus dashes, excluding the following non-breaking space characters:
39
-     * * nbsp (\xA0)
40
-     * * narrow nbsp (\x{202F})
41
-     * * figure space (\x{2007})
42
-     */
43
-    public static $_wordbreak_pattern = '/([^\S\xA0\x{202F}\x{2007}\n]+|\R|\-+|\xAD+)/u';
44
-
45
-    /**
46
-     * Frame for this reflower
47
-     *
48
-     * @var TextFrameDecorator
49
-     */
50
-    protected $_frame;
51
-
52
-    /**
53
-     * Saves trailing whitespace trimmed after a line break, so it can be
54
-     * restored when needed.
55
-     *
56
-     * @var string|null
57
-     */
58
-    protected $trailingWs = null;
59
-
60
-    /**
61
-     * @var FontMetrics
62
-     */
63
-    private $fontMetrics;
64
-
65
-    /**
66
-     * @param TextFrameDecorator $frame
67
-     * @param FontMetrics $fontMetrics
68
-     */
69
-    public function __construct(TextFrameDecorator $frame, FontMetrics $fontMetrics)
70
-    {
71
-        parent::__construct($frame);
72
-        $this->setFontMetrics($fontMetrics);
73
-    }
74
-
75
-    /**
76
-     * Apply text transform and white-space collapse according to style.
77
-     *
78
-     * * http://www.w3.org/TR/CSS21/text.html#propdef-text-transform
79
-     * * http://www.w3.org/TR/CSS21/text.html#propdef-white-space
80
-     *
81
-     * @param string $text
82
-     * @return string
83
-     */
84
-    protected function pre_process_text(string $text): string
85
-    {
86
-        $style = $this->_frame->get_style();
87
-
88
-        // Handle text transform
89
-        switch ($style->text_transform) {
90
-            case "capitalize":
91
-                $text = Helpers::mb_ucwords($text);
92
-                break;
93
-            case "uppercase":
94
-                $text = mb_convert_case($text, MB_CASE_UPPER);
95
-                break;
96
-            case "lowercase":
97
-                $text = mb_convert_case($text, MB_CASE_LOWER);
98
-                break;
99
-            default:
100
-                break;
101
-        }
102
-
103
-        // Handle white-space collapse
104
-        switch ($style->white_space) {
105
-            default:
106
-            case "normal":
107
-            case "nowrap":
108
-                $text = preg_replace(self::$_whitespace_pattern, " ", $text) ?? "";
109
-                break;
110
-
111
-            case "pre-line":
112
-                // Collapse white space except for line breaks
113
-                $text = preg_replace('/([^\S\xA0\x{202F}\x{2007}\n]+)/u', " ", $text) ?? "";
114
-                break;
115
-
116
-            case "pre":
117
-            case "pre-wrap":
118
-                break;
119
-
120
-        }
121
-
122
-        return $text;
123
-    }
124
-
125
-    /**
126
-     * @param string              $text
127
-     * @param BlockFrameDecorator $block
128
-     * @param bool                $nowrap
129
-     *
130
-     * @return bool|int
131
-     */
132
-    protected function line_break(string $text, BlockFrameDecorator $block, bool $nowrap = false)
133
-    {
134
-        $fontMetrics = $this->getFontMetrics();
135
-        $frame = $this->_frame;
136
-        $style = $frame->get_style();
137
-        $font = $style->font_family;
138
-        $size = $style->font_size;
139
-        $word_spacing = $style->word_spacing;
140
-        $letter_spacing = $style->letter_spacing;
141
-
142
-        // Determine the available width
143
-        $current_line = $block->get_current_line_box();
144
-        $line_width = $frame->get_containing_block("w");
145
-        $current_line_width = $current_line->left + $current_line->w + $current_line->right;
146
-        $available_width = $line_width - $current_line_width;
147
-
148
-        // Determine the frame width including margin, padding & border
149
-        $visible_text = preg_replace('/\xAD/u', "", $text);
150
-        $text_width = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
151
-        $mbp_width = (float) $style->length_in_pt([
152
-            $style->margin_left,
153
-            $style->border_left_width,
154
-            $style->padding_left,
155
-            $style->padding_right,
156
-            $style->border_right_width,
157
-            $style->margin_right
158
-        ], $line_width);
159
-        $frame_width = $text_width + $mbp_width;
160
-
161
-        if (Helpers::lengthLessOrEqual($frame_width, $available_width)) {
162
-            return false;
163
-        }
164
-
165
-        if ($nowrap) {
166
-            return $current_line_width == 0 ? false : 0;
167
-        }
168
-
169
-        // Split the text into words
170
-        $words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
171
-        $wc = count($words);
172
-
173
-        // Determine the split point
174
-        $width = 0.0;
175
-        $str = "";
176
-
177
-        $space_width = $fontMetrics->getTextWidth(" ", $font, $size, $word_spacing, $letter_spacing);
178
-        $shy_width = $fontMetrics->getTextWidth(self::SOFT_HYPHEN, $font, $size);
179
-
180
-        // @todo support <wbr>
181
-        for ($i = 0; $i < $wc; $i += 2) {
182
-            // Allow trailing white space to overflow. White space is always
183
-            // collapsed to the standard space character currently, so only
184
-            // handle that for now
185
-            $sep = $words[$i + 1] ?? "";
186
-            $word = $sep === " " ? $words[$i] : $words[$i] . $sep;
187
-            $word_width = $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing);
188
-            $used_width = $width + $word_width + $mbp_width;
189
-
190
-            if (Helpers::lengthGreater($used_width, $available_width)) {
191
-                // If the previous split happened by soft hyphen, we have to
192
-                // append its width again because the last hyphen of a line
193
-                // won't be removed
194
-                if (isset($words[$i - 1]) && self::SOFT_HYPHEN === $words[$i - 1]) {
195
-                    $width += $shy_width;
196
-                }
197
-                break;
198
-            }
199
-
200
-            // If the word is splitted by soft hyphen, but no line break is needed
201
-            // we have to reduce the width. But the str is not modified, otherwise
202
-            // the wrong offset is calculated at the end of this method.
203
-            if ($sep === self::SOFT_HYPHEN) {
204
-                $width += $word_width - $shy_width;
205
-                $str .= $word;
206
-            } elseif ($sep === " ") {
207
-                $width += $word_width + $space_width;
208
-                $str .= $word . $sep;
209
-            } else {
210
-                $width += $word_width;
211
-                $str .= $word;
212
-            }
213
-        }
214
-
215
-        // The first word has overflowed. Force it onto the line, or as many
216
-        // characters as fit if breaking words is allowed
217
-        if ($current_line_width == 0 && $width === 0.0) {
218
-            if ($sep === " ") {
219
-                $word .= $sep;
220
-            }
221
-
222
-            // https://www.w3.org/TR/css-text-3/#overflow-wrap-property
223
-            $wrap = $style->overflow_wrap;
224
-            $break_word = $wrap === "anywhere" || $wrap === "break-word";
225
-
226
-            if ($break_word) {
227
-                $s = "";
228
-
229
-                for ($j = 0; $j < mb_strlen($word); $j++) {
230
-                    $c = mb_substr($word, $j, 1);
231
-                    $w = $fontMetrics->getTextWidth($s . $c, $font, $size, $word_spacing, $letter_spacing);
232
-
233
-                    if (Helpers::lengthGreater($w, $available_width)) {
234
-                        break;
235
-                    }
236
-
237
-                    $s .= $c;
238
-                }
239
-
240
-                // Always force the first character onto the line
241
-                $str = $j === 0 ? $s . $c : $s;
242
-            } else {
243
-                $str = $word;
244
-            }
245
-        }
246
-
247
-        $offset = mb_strlen($str);
248
-        return $offset;
249
-    }
250
-
251
-    /**
252
-     * @param string $text
253
-     * @return bool|int
254
-     */
255
-    protected function newline_break(string $text)
256
-    {
257
-        if (($i = mb_strpos($text, "\n")) === false) {
258
-            return false;
259
-        }
260
-
261
-        return $i + 1;
262
-    }
263
-
264
-    /**
265
-     * @param BlockFrameDecorator $block
266
-     * @return bool|null Whether to add a new line at the end. `null` if reflow
267
-     *         should be stopped.
268
-     */
269
-    protected function layout_line(BlockFrameDecorator $block): ?bool
270
-    {
271
-        $frame = $this->_frame;
272
-        $style = $frame->get_style();
273
-        $current_line = $block->get_current_line_box();
274
-        $text = $frame->get_text();
275
-
276
-        // Trim leading white space if this is the first text on the line
277
-        if ($current_line->w === 0.0 && !$frame->is_pre()) {
278
-            $text = ltrim($text, " ");
279
-        }
280
-
281
-        // Exclude wrapped white space. This handles white space between block
282
-        // elements in case white space is collapsed
283
-        if ($text === "") {
284
-            $frame->set_text("");
285
-            $style->set_used("width", 0.0);
286
-            return null;
287
-        }
288
-
289
-        // Determine the next line break
290
-        // http://www.w3.org/TR/CSS21/text.html#propdef-white-space
291
-        $white_space = $style->white_space;
292
-        $nowrap = $white_space === "nowrap" || $white_space === "pre";
293
-
294
-        switch ($white_space) {
295
-            default:
296
-            case "normal":
297
-            case "nowrap":
298
-                $split = $this->line_break($text, $block, $nowrap);
299
-                $add_line = false;
300
-                break;
301
-
302
-            case "pre":
303
-            case "pre-line":
304
-            case "pre-wrap":
305
-                $hard_split = $this->newline_break($text);
306
-                $first_line = $hard_split !== false
307
-                    ? mb_substr($text, 0, $hard_split)
308
-                    : $text;
309
-                $soft_split = $this->line_break($first_line, $block, $nowrap);
310
-
311
-                $split = $soft_split !== false ? $soft_split : $hard_split;
312
-                $add_line = $hard_split !== false;
313
-                break;
314
-        }
315
-
316
-        if ($split === 0) {
317
-            // Make sure to move text when floating frames leave no space to
318
-            // place anything onto the line
319
-            // TODO: Would probably be better to move just below the current
320
-            // floating frame instead of trying to place text in line-height
321
-            // increments
322
-            if ($current_line->h === 0.0) {
323
-                // Line height might be 0
324
-                $h = max($frame->get_margin_height(), 1.0);
325
-                $block->maximize_line_height($h, $frame);
326
-            }
327
-
328
-            // Break line and repeat layout
329
-            $block->add_line();
330
-
331
-            // Find the appropriate inline ancestor to split
332
-            $child = $frame;
333
-            $p = $child->get_parent();
334
-            while ($p instanceof InlineFrameDecorator && !$child->get_prev_sibling()) {
335
-                $child = $p;
336
-                $p = $p->get_parent();
337
-            }
338
-
339
-            if ($p instanceof InlineFrameDecorator) {
340
-                // Split parent and stop current reflow. Reflow continues
341
-                // via child-reflow loop of split parent
342
-                $p->split($child);
343
-                return null;
344
-            }
345
-
346
-            return $this->layout_line($block);
347
-        }
348
-
349
-        // Final split point is determined
350
-        if ($split !== false && $split < mb_strlen($text)) {
351
-            // Split the line
352
-            $frame->set_text($text);
353
-            $frame->split_text($split);
354
-            $add_line = true;
355
-
356
-            // Remove inner soft hyphens
357
-            $t = $frame->get_text();
358
-            $shyPosition = mb_strpos($t, self::SOFT_HYPHEN);
359
-            if (false !== $shyPosition && $shyPosition < mb_strlen($t) - 1) {
360
-                $t = str_replace(self::SOFT_HYPHEN, "", mb_substr($t, 0, -1)) . mb_substr($t, -1);
361
-                $frame->set_text($t);
362
-            }
363
-        } else {
364
-            // No split required
365
-            // Remove soft hyphens
366
-            $text = str_replace(self::SOFT_HYPHEN, "", $text);
367
-            $frame->set_text($text);
368
-        }
369
-
370
-        // Set our new width
371
-        $frame->recalculate_width();
372
-
373
-        return $add_line;
374
-    }
375
-
376
-    /**
377
-     * @param BlockFrameDecorator|null $block
378
-     */
379
-    function reflow(BlockFrameDecorator $block = null)
380
-    {
381
-        $frame = $this->_frame;
382
-        $page = $frame->get_root();
383
-        $page->check_forced_page_break($frame);
384
-
385
-        if ($page->is_full()) {
386
-            return;
387
-        }
388
-
389
-        // Determine the text height
390
-        $style = $frame->get_style();
391
-        $size = $style->font_size;
392
-        $font = $style->font_family;
393
-        $font_height = $this->getFontMetrics()->getFontHeight($font, $size);
394
-        $style->set_used("height", $font_height);
395
-
396
-        // Handle text transform and white space
397
-        $text = $this->pre_process_text($frame->get_text());
398
-        $frame->set_text($text);
399
-
400
-        $add_line = $this->layout_line($block);
401
-
402
-        if ($add_line === null) {
403
-            return;
404
-        }
405
-
406
-        $frame->position();
407
-
408
-        if ($block) {
409
-            $line = $block->add_frame_to_line($frame);
410
-            $trimmed = trim($frame->get_text());
411
-
412
-            // Split the text into words (used to determine spacing between
413
-            // words on justified lines)
414
-            if ($trimmed !== "") {
415
-                $words = preg_split(self::$_whitespace_pattern, $trimmed);
416
-                $line->wc += count($words);
417
-            }
418
-
419
-            if ($add_line) {
420
-                $block->add_line();
421
-            }
422
-        }
423
-    }
424
-
425
-    /**
426
-     * Trim trailing white space from the frame text.
427
-     */
428
-    public function trim_trailing_ws(): void
429
-    {
430
-        $frame = $this->_frame;
431
-        $text = $frame->get_text();
432
-        $trailing = mb_substr($text, -1);
433
-
434
-        // White space is always collapsed to the standard space character
435
-        // currently, so only handle that for now
436
-        if ($trailing === " ") {
437
-            $this->trailingWs = $trailing;
438
-            $frame->set_text(mb_substr($text, 0, -1));
439
-            $frame->recalculate_width();
440
-        }
441
-    }
442
-
443
-    public function reset(): void
444
-    {
445
-        parent::reset();
446
-
447
-        // Restore trimmed trailing white space, as the frame will go through
448
-        // another reflow and line breaks might be different after a split
449
-        if ($this->trailingWs !== null) {
450
-            $text = $this->_frame->get_text();
451
-            $this->_frame->set_text($text . $this->trailingWs);
452
-            $this->trailingWs = null;
453
-        }
454
-    }
455
-
456
-    //........................................................................
457
-
458
-    public function get_min_max_width(): array
459
-    {
460
-        $fontMetrics = $this->getFontMetrics();
461
-        $frame = $this->_frame;
462
-        $style = $frame->get_style();
463
-        $text = $frame->get_text();
464
-        $font = $style->font_family;
465
-        $size = $style->font_size;
466
-        $word_spacing = $style->word_spacing;
467
-        $letter_spacing = $style->letter_spacing;
468
-
469
-        // Handle text transform and white space
470
-        $text = $this->pre_process_text($frame->get_text());
471
-
472
-        if (!$frame->is_pre()) {
473
-            // Determine whether the frame is at the start of its parent block.
474
-            // Trim leading white space in that case
475
-            $child = $frame;
476
-            $p = $frame->get_parent();
477
-            while (!$p->is_block() && !$child->get_prev_sibling()) {
478
-                $child = $p;
479
-                $p = $p->get_parent();
480
-            }
481
-
482
-            if (!$child->get_prev_sibling()) {
483
-                $text = ltrim($text, " ");
484
-            }
485
-
486
-            // Determine whether the frame is at the end of its parent block.
487
-            // Trim trailing white space in that case
488
-            $child = $frame;
489
-            $p = $frame->get_parent();
490
-            while (!$p->is_block() && !$child->get_next_sibling()) {
491
-                $child = $p;
492
-                $p = $p->get_parent();
493
-            }
494
-
495
-            if (!$child->get_next_sibling()) {
496
-                $text = rtrim($text, " ");
497
-            }
498
-        }
499
-
500
-        // Strip soft hyphens for max-line-width calculations
501
-        $visible_text = preg_replace('/\xAD/u', "", $text);
502
-
503
-        // Determine minimum text width
504
-        switch ($style->white_space) {
505
-            default:
506
-            case "normal":
507
-            case "pre-line":
508
-            case "pre-wrap":
509
-                // The min width is the longest word or, if breaking words is
510
-                // allowed with the `anywhere` keyword, the widest character.
511
-                // For performance reasons, we only check the first character in
512
-                // the latter case.
513
-                // https://www.w3.org/TR/css-text-3/#overflow-wrap-property
514
-                if ($style->overflow_wrap === "anywhere") {
515
-                    $char = mb_substr($visible_text, 0, 1);
516
-                    $min = $fontMetrics->getTextWidth($char, $font, $size, $word_spacing, $letter_spacing);
517
-                } else {
518
-                    // Find the longest word
519
-                    $words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
520
-                    $lengths = array_map(function ($chunk) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
521
-                        // Allow trailing white space to overflow. As in actual
522
-                        // layout above, only handle a single space for now
523
-                        $sep = $chunk[1] ?? "";
524
-                        $word = $sep === " " ? $chunk[0] : $chunk[0] . $sep;
525
-                        return $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing);
526
-                    }, array_chunk($words, 2));
527
-                    $min = max($lengths);
528
-                }
529
-                break;
530
-
531
-            case "pre":
532
-                // Find the longest line
533
-                $lines = array_flip(preg_split("/\R/u", $visible_text));
534
-                array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
535
-                    $chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing);
536
-                });
537
-                arsort($lines);
538
-                $min = reset($lines);
539
-                break;
540
-
541
-            case "nowrap":
542
-                $min = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
543
-                break;
544
-        }
545
-
546
-        // Determine maximum text width
547
-        switch ($style->white_space) {
548
-            default:
549
-            case "normal":
550
-                $max = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
551
-                break;
552
-
553
-            case "pre-line":
554
-            case "pre-wrap":
555
-                // Find the longest line
556
-                $lines = array_flip(preg_split("/\R/u", $visible_text));
557
-                array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
558
-                    $chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing);
559
-                });
560
-                arsort($lines);
561
-                $max = reset($lines);
562
-                break;
563
-
564
-            case "pre":
565
-            case "nowrap":
566
-                $max = $min;
567
-                break;
568
-        }
569
-
570
-        // Account for margins, borders, and padding
571
-        $dims = [
572
-            $style->padding_left,
573
-            $style->padding_right,
574
-            $style->border_left_width,
575
-            $style->border_right_width,
576
-            $style->margin_left,
577
-            $style->margin_right
578
-        ];
579
-
580
-        // The containing block is not defined yet, treat percentages as 0
581
-        $delta = (float) $style->length_in_pt($dims, 0);
582
-        $min += $delta;
583
-        $max += $delta;
584
-
585
-        return [$min, $max, "min" => $min, "max" => $max];
586
-    }
587
-
588
-    /**
589
-     * @param FontMetrics $fontMetrics
590
-     * @return $this
591
-     */
592
-    public function setFontMetrics(FontMetrics $fontMetrics)
593
-    {
594
-        $this->fontMetrics = $fontMetrics;
595
-        return $this;
596
-    }
597
-
598
-    /**
599
-     * @return FontMetrics
600
-     */
601
-    public function getFontMetrics()
602
-    {
603
-        return $this->fontMetrics;
604
-    }
22
+	/**
23
+	 * PHP string representation of HTML entity <shy>
24
+	 */
25
+	const SOFT_HYPHEN = "\xC2\xAD";
26
+
27
+	/**
28
+	 * The regex splits on everything that's a separator (^\S double negative),
29
+	 * excluding the following non-breaking space characters:
30
+	 * * nbsp (\xA0)
31
+	 * * narrow nbsp (\x{202F})
32
+	 * * figure space (\x{2007})
33
+	 */
34
+	public static $_whitespace_pattern = '/([^\S\xA0\x{202F}\x{2007}]+)/u';
35
+
36
+	/**
37
+	 * The regex splits on everything that's a separator (^\S double negative)
38
+	 * plus dashes, excluding the following non-breaking space characters:
39
+	 * * nbsp (\xA0)
40
+	 * * narrow nbsp (\x{202F})
41
+	 * * figure space (\x{2007})
42
+	 */
43
+	public static $_wordbreak_pattern = '/([^\S\xA0\x{202F}\x{2007}\n]+|\R|\-+|\xAD+)/u';
44
+
45
+	/**
46
+	 * Frame for this reflower
47
+	 *
48
+	 * @var TextFrameDecorator
49
+	 */
50
+	protected $_frame;
51
+
52
+	/**
53
+	 * Saves trailing whitespace trimmed after a line break, so it can be
54
+	 * restored when needed.
55
+	 *
56
+	 * @var string|null
57
+	 */
58
+	protected $trailingWs = null;
59
+
60
+	/**
61
+	 * @var FontMetrics
62
+	 */
63
+	private $fontMetrics;
64
+
65
+	/**
66
+	 * @param TextFrameDecorator $frame
67
+	 * @param FontMetrics $fontMetrics
68
+	 */
69
+	public function __construct(TextFrameDecorator $frame, FontMetrics $fontMetrics)
70
+	{
71
+		parent::__construct($frame);
72
+		$this->setFontMetrics($fontMetrics);
73
+	}
74
+
75
+	/**
76
+	 * Apply text transform and white-space collapse according to style.
77
+	 *
78
+	 * * http://www.w3.org/TR/CSS21/text.html#propdef-text-transform
79
+	 * * http://www.w3.org/TR/CSS21/text.html#propdef-white-space
80
+	 *
81
+	 * @param string $text
82
+	 * @return string
83
+	 */
84
+	protected function pre_process_text(string $text): string
85
+	{
86
+		$style = $this->_frame->get_style();
87
+
88
+		// Handle text transform
89
+		switch ($style->text_transform) {
90
+			case "capitalize":
91
+				$text = Helpers::mb_ucwords($text);
92
+				break;
93
+			case "uppercase":
94
+				$text = mb_convert_case($text, MB_CASE_UPPER);
95
+				break;
96
+			case "lowercase":
97
+				$text = mb_convert_case($text, MB_CASE_LOWER);
98
+				break;
99
+			default:
100
+				break;
101
+		}
102
+
103
+		// Handle white-space collapse
104
+		switch ($style->white_space) {
105
+			default:
106
+			case "normal":
107
+			case "nowrap":
108
+				$text = preg_replace(self::$_whitespace_pattern, " ", $text) ?? "";
109
+				break;
110
+
111
+			case "pre-line":
112
+				// Collapse white space except for line breaks
113
+				$text = preg_replace('/([^\S\xA0\x{202F}\x{2007}\n]+)/u', " ", $text) ?? "";
114
+				break;
115
+
116
+			case "pre":
117
+			case "pre-wrap":
118
+				break;
119
+
120
+		}
121
+
122
+		return $text;
123
+	}
124
+
125
+	/**
126
+	 * @param string              $text
127
+	 * @param BlockFrameDecorator $block
128
+	 * @param bool                $nowrap
129
+	 *
130
+	 * @return bool|int
131
+	 */
132
+	protected function line_break(string $text, BlockFrameDecorator $block, bool $nowrap = false)
133
+	{
134
+		$fontMetrics = $this->getFontMetrics();
135
+		$frame = $this->_frame;
136
+		$style = $frame->get_style();
137
+		$font = $style->font_family;
138
+		$size = $style->font_size;
139
+		$word_spacing = $style->word_spacing;
140
+		$letter_spacing = $style->letter_spacing;
141
+
142
+		// Determine the available width
143
+		$current_line = $block->get_current_line_box();
144
+		$line_width = $frame->get_containing_block("w");
145
+		$current_line_width = $current_line->left + $current_line->w + $current_line->right;
146
+		$available_width = $line_width - $current_line_width;
147
+
148
+		// Determine the frame width including margin, padding & border
149
+		$visible_text = preg_replace('/\xAD/u', "", $text);
150
+		$text_width = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
151
+		$mbp_width = (float) $style->length_in_pt([
152
+			$style->margin_left,
153
+			$style->border_left_width,
154
+			$style->padding_left,
155
+			$style->padding_right,
156
+			$style->border_right_width,
157
+			$style->margin_right
158
+		], $line_width);
159
+		$frame_width = $text_width + $mbp_width;
160
+
161
+		if (Helpers::lengthLessOrEqual($frame_width, $available_width)) {
162
+			return false;
163
+		}
164
+
165
+		if ($nowrap) {
166
+			return $current_line_width == 0 ? false : 0;
167
+		}
168
+
169
+		// Split the text into words
170
+		$words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
171
+		$wc = count($words);
172
+
173
+		// Determine the split point
174
+		$width = 0.0;
175
+		$str = "";
176
+
177
+		$space_width = $fontMetrics->getTextWidth(" ", $font, $size, $word_spacing, $letter_spacing);
178
+		$shy_width = $fontMetrics->getTextWidth(self::SOFT_HYPHEN, $font, $size);
179
+
180
+		// @todo support <wbr>
181
+		for ($i = 0; $i < $wc; $i += 2) {
182
+			// Allow trailing white space to overflow. White space is always
183
+			// collapsed to the standard space character currently, so only
184
+			// handle that for now
185
+			$sep = $words[$i + 1] ?? "";
186
+			$word = $sep === " " ? $words[$i] : $words[$i] . $sep;
187
+			$word_width = $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing);
188
+			$used_width = $width + $word_width + $mbp_width;
189
+
190
+			if (Helpers::lengthGreater($used_width, $available_width)) {
191
+				// If the previous split happened by soft hyphen, we have to
192
+				// append its width again because the last hyphen of a line
193
+				// won't be removed
194
+				if (isset($words[$i - 1]) && self::SOFT_HYPHEN === $words[$i - 1]) {
195
+					$width += $shy_width;
196
+				}
197
+				break;
198
+			}
199
+
200
+			// If the word is splitted by soft hyphen, but no line break is needed
201
+			// we have to reduce the width. But the str is not modified, otherwise
202
+			// the wrong offset is calculated at the end of this method.
203
+			if ($sep === self::SOFT_HYPHEN) {
204
+				$width += $word_width - $shy_width;
205
+				$str .= $word;
206
+			} elseif ($sep === " ") {
207
+				$width += $word_width + $space_width;
208
+				$str .= $word . $sep;
209
+			} else {
210
+				$width += $word_width;
211
+				$str .= $word;
212
+			}
213
+		}
214
+
215
+		// The first word has overflowed. Force it onto the line, or as many
216
+		// characters as fit if breaking words is allowed
217
+		if ($current_line_width == 0 && $width === 0.0) {
218
+			if ($sep === " ") {
219
+				$word .= $sep;
220
+			}
221
+
222
+			// https://www.w3.org/TR/css-text-3/#overflow-wrap-property
223
+			$wrap = $style->overflow_wrap;
224
+			$break_word = $wrap === "anywhere" || $wrap === "break-word";
225
+
226
+			if ($break_word) {
227
+				$s = "";
228
+
229
+				for ($j = 0; $j < mb_strlen($word); $j++) {
230
+					$c = mb_substr($word, $j, 1);
231
+					$w = $fontMetrics->getTextWidth($s . $c, $font, $size, $word_spacing, $letter_spacing);
232
+
233
+					if (Helpers::lengthGreater($w, $available_width)) {
234
+						break;
235
+					}
236
+
237
+					$s .= $c;
238
+				}
239
+
240
+				// Always force the first character onto the line
241
+				$str = $j === 0 ? $s . $c : $s;
242
+			} else {
243
+				$str = $word;
244
+			}
245
+		}
246
+
247
+		$offset = mb_strlen($str);
248
+		return $offset;
249
+	}
250
+
251
+	/**
252
+	 * @param string $text
253
+	 * @return bool|int
254
+	 */
255
+	protected function newline_break(string $text)
256
+	{
257
+		if (($i = mb_strpos($text, "\n")) === false) {
258
+			return false;
259
+		}
260
+
261
+		return $i + 1;
262
+	}
263
+
264
+	/**
265
+	 * @param BlockFrameDecorator $block
266
+	 * @return bool|null Whether to add a new line at the end. `null` if reflow
267
+	 *         should be stopped.
268
+	 */
269
+	protected function layout_line(BlockFrameDecorator $block): ?bool
270
+	{
271
+		$frame = $this->_frame;
272
+		$style = $frame->get_style();
273
+		$current_line = $block->get_current_line_box();
274
+		$text = $frame->get_text();
275
+
276
+		// Trim leading white space if this is the first text on the line
277
+		if ($current_line->w === 0.0 && !$frame->is_pre()) {
278
+			$text = ltrim($text, " ");
279
+		}
280
+
281
+		// Exclude wrapped white space. This handles white space between block
282
+		// elements in case white space is collapsed
283
+		if ($text === "") {
284
+			$frame->set_text("");
285
+			$style->set_used("width", 0.0);
286
+			return null;
287
+		}
288
+
289
+		// Determine the next line break
290
+		// http://www.w3.org/TR/CSS21/text.html#propdef-white-space
291
+		$white_space = $style->white_space;
292
+		$nowrap = $white_space === "nowrap" || $white_space === "pre";
293
+
294
+		switch ($white_space) {
295
+			default:
296
+			case "normal":
297
+			case "nowrap":
298
+				$split = $this->line_break($text, $block, $nowrap);
299
+				$add_line = false;
300
+				break;
301
+
302
+			case "pre":
303
+			case "pre-line":
304
+			case "pre-wrap":
305
+				$hard_split = $this->newline_break($text);
306
+				$first_line = $hard_split !== false
307
+					? mb_substr($text, 0, $hard_split)
308
+					: $text;
309
+				$soft_split = $this->line_break($first_line, $block, $nowrap);
310
+
311
+				$split = $soft_split !== false ? $soft_split : $hard_split;
312
+				$add_line = $hard_split !== false;
313
+				break;
314
+		}
315
+
316
+		if ($split === 0) {
317
+			// Make sure to move text when floating frames leave no space to
318
+			// place anything onto the line
319
+			// TODO: Would probably be better to move just below the current
320
+			// floating frame instead of trying to place text in line-height
321
+			// increments
322
+			if ($current_line->h === 0.0) {
323
+				// Line height might be 0
324
+				$h = max($frame->get_margin_height(), 1.0);
325
+				$block->maximize_line_height($h, $frame);
326
+			}
327
+
328
+			// Break line and repeat layout
329
+			$block->add_line();
330
+
331
+			// Find the appropriate inline ancestor to split
332
+			$child = $frame;
333
+			$p = $child->get_parent();
334
+			while ($p instanceof InlineFrameDecorator && !$child->get_prev_sibling()) {
335
+				$child = $p;
336
+				$p = $p->get_parent();
337
+			}
338
+
339
+			if ($p instanceof InlineFrameDecorator) {
340
+				// Split parent and stop current reflow. Reflow continues
341
+				// via child-reflow loop of split parent
342
+				$p->split($child);
343
+				return null;
344
+			}
345
+
346
+			return $this->layout_line($block);
347
+		}
348
+
349
+		// Final split point is determined
350
+		if ($split !== false && $split < mb_strlen($text)) {
351
+			// Split the line
352
+			$frame->set_text($text);
353
+			$frame->split_text($split);
354
+			$add_line = true;
355
+
356
+			// Remove inner soft hyphens
357
+			$t = $frame->get_text();
358
+			$shyPosition = mb_strpos($t, self::SOFT_HYPHEN);
359
+			if (false !== $shyPosition && $shyPosition < mb_strlen($t) - 1) {
360
+				$t = str_replace(self::SOFT_HYPHEN, "", mb_substr($t, 0, -1)) . mb_substr($t, -1);
361
+				$frame->set_text($t);
362
+			}
363
+		} else {
364
+			// No split required
365
+			// Remove soft hyphens
366
+			$text = str_replace(self::SOFT_HYPHEN, "", $text);
367
+			$frame->set_text($text);
368
+		}
369
+
370
+		// Set our new width
371
+		$frame->recalculate_width();
372
+
373
+		return $add_line;
374
+	}
375
+
376
+	/**
377
+	 * @param BlockFrameDecorator|null $block
378
+	 */
379
+	function reflow(BlockFrameDecorator $block = null)
380
+	{
381
+		$frame = $this->_frame;
382
+		$page = $frame->get_root();
383
+		$page->check_forced_page_break($frame);
384
+
385
+		if ($page->is_full()) {
386
+			return;
387
+		}
388
+
389
+		// Determine the text height
390
+		$style = $frame->get_style();
391
+		$size = $style->font_size;
392
+		$font = $style->font_family;
393
+		$font_height = $this->getFontMetrics()->getFontHeight($font, $size);
394
+		$style->set_used("height", $font_height);
395
+
396
+		// Handle text transform and white space
397
+		$text = $this->pre_process_text($frame->get_text());
398
+		$frame->set_text($text);
399
+
400
+		$add_line = $this->layout_line($block);
401
+
402
+		if ($add_line === null) {
403
+			return;
404
+		}
405
+
406
+		$frame->position();
407
+
408
+		if ($block) {
409
+			$line = $block->add_frame_to_line($frame);
410
+			$trimmed = trim($frame->get_text());
411
+
412
+			// Split the text into words (used to determine spacing between
413
+			// words on justified lines)
414
+			if ($trimmed !== "") {
415
+				$words = preg_split(self::$_whitespace_pattern, $trimmed);
416
+				$line->wc += count($words);
417
+			}
418
+
419
+			if ($add_line) {
420
+				$block->add_line();
421
+			}
422
+		}
423
+	}
424
+
425
+	/**
426
+	 * Trim trailing white space from the frame text.
427
+	 */
428
+	public function trim_trailing_ws(): void
429
+	{
430
+		$frame = $this->_frame;
431
+		$text = $frame->get_text();
432
+		$trailing = mb_substr($text, -1);
433
+
434
+		// White space is always collapsed to the standard space character
435
+		// currently, so only handle that for now
436
+		if ($trailing === " ") {
437
+			$this->trailingWs = $trailing;
438
+			$frame->set_text(mb_substr($text, 0, -1));
439
+			$frame->recalculate_width();
440
+		}
441
+	}
442
+
443
+	public function reset(): void
444
+	{
445
+		parent::reset();
446
+
447
+		// Restore trimmed trailing white space, as the frame will go through
448
+		// another reflow and line breaks might be different after a split
449
+		if ($this->trailingWs !== null) {
450
+			$text = $this->_frame->get_text();
451
+			$this->_frame->set_text($text . $this->trailingWs);
452
+			$this->trailingWs = null;
453
+		}
454
+	}
455
+
456
+	//........................................................................
457
+
458
+	public function get_min_max_width(): array
459
+	{
460
+		$fontMetrics = $this->getFontMetrics();
461
+		$frame = $this->_frame;
462
+		$style = $frame->get_style();
463
+		$text = $frame->get_text();
464
+		$font = $style->font_family;
465
+		$size = $style->font_size;
466
+		$word_spacing = $style->word_spacing;
467
+		$letter_spacing = $style->letter_spacing;
468
+
469
+		// Handle text transform and white space
470
+		$text = $this->pre_process_text($frame->get_text());
471
+
472
+		if (!$frame->is_pre()) {
473
+			// Determine whether the frame is at the start of its parent block.
474
+			// Trim leading white space in that case
475
+			$child = $frame;
476
+			$p = $frame->get_parent();
477
+			while (!$p->is_block() && !$child->get_prev_sibling()) {
478
+				$child = $p;
479
+				$p = $p->get_parent();
480
+			}
481
+
482
+			if (!$child->get_prev_sibling()) {
483
+				$text = ltrim($text, " ");
484
+			}
485
+
486
+			// Determine whether the frame is at the end of its parent block.
487
+			// Trim trailing white space in that case
488
+			$child = $frame;
489
+			$p = $frame->get_parent();
490
+			while (!$p->is_block() && !$child->get_next_sibling()) {
491
+				$child = $p;
492
+				$p = $p->get_parent();
493
+			}
494
+
495
+			if (!$child->get_next_sibling()) {
496
+				$text = rtrim($text, " ");
497
+			}
498
+		}
499
+
500
+		// Strip soft hyphens for max-line-width calculations
501
+		$visible_text = preg_replace('/\xAD/u', "", $text);
502
+
503
+		// Determine minimum text width
504
+		switch ($style->white_space) {
505
+			default:
506
+			case "normal":
507
+			case "pre-line":
508
+			case "pre-wrap":
509
+				// The min width is the longest word or, if breaking words is
510
+				// allowed with the `anywhere` keyword, the widest character.
511
+				// For performance reasons, we only check the first character in
512
+				// the latter case.
513
+				// https://www.w3.org/TR/css-text-3/#overflow-wrap-property
514
+				if ($style->overflow_wrap === "anywhere") {
515
+					$char = mb_substr($visible_text, 0, 1);
516
+					$min = $fontMetrics->getTextWidth($char, $font, $size, $word_spacing, $letter_spacing);
517
+				} else {
518
+					// Find the longest word
519
+					$words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
520
+					$lengths = array_map(function ($chunk) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
521
+						// Allow trailing white space to overflow. As in actual
522
+						// layout above, only handle a single space for now
523
+						$sep = $chunk[1] ?? "";
524
+						$word = $sep === " " ? $chunk[0] : $chunk[0] . $sep;
525
+						return $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing);
526
+					}, array_chunk($words, 2));
527
+					$min = max($lengths);
528
+				}
529
+				break;
530
+
531
+			case "pre":
532
+				// Find the longest line
533
+				$lines = array_flip(preg_split("/\R/u", $visible_text));
534
+				array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
535
+					$chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing);
536
+				});
537
+				arsort($lines);
538
+				$min = reset($lines);
539
+				break;
540
+
541
+			case "nowrap":
542
+				$min = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
543
+				break;
544
+		}
545
+
546
+		// Determine maximum text width
547
+		switch ($style->white_space) {
548
+			default:
549
+			case "normal":
550
+				$max = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
551
+				break;
552
+
553
+			case "pre-line":
554
+			case "pre-wrap":
555
+				// Find the longest line
556
+				$lines = array_flip(preg_split("/\R/u", $visible_text));
557
+				array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
558
+					$chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing);
559
+				});
560
+				arsort($lines);
561
+				$max = reset($lines);
562
+				break;
563
+
564
+			case "pre":
565
+			case "nowrap":
566
+				$max = $min;
567
+				break;
568
+		}
569
+
570
+		// Account for margins, borders, and padding
571
+		$dims = [
572
+			$style->padding_left,
573
+			$style->padding_right,
574
+			$style->border_left_width,
575
+			$style->border_right_width,
576
+			$style->margin_left,
577
+			$style->margin_right
578
+		];
579
+
580
+		// The containing block is not defined yet, treat percentages as 0
581
+		$delta = (float) $style->length_in_pt($dims, 0);
582
+		$min += $delta;
583
+		$max += $delta;
584
+
585
+		return [$min, $max, "min" => $min, "max" => $max];
586
+	}
587
+
588
+	/**
589
+	 * @param FontMetrics $fontMetrics
590
+	 * @return $this
591
+	 */
592
+	public function setFontMetrics(FontMetrics $fontMetrics)
593
+	{
594
+		$this->fontMetrics = $fontMetrics;
595
+		return $this;
596
+	}
597
+
598
+	/**
599
+	 * @return FontMetrics
600
+	 */
601
+	public function getFontMetrics()
602
+	{
603
+		return $this->fontMetrics;
604
+	}
605 605
 }
Please login to merge, or discard this patch.
vendor/dompdf/dompdf/src/FrameReflower/Block.php 1 patch
Indentation   +924 added lines, -924 removed lines patch added patch discarded remove patch
@@ -22,928 +22,928 @@
 block discarded – undo
22 22
  */
23 23
 class Block extends AbstractFrameReflower
24 24
 {
25
-    // Minimum line width to justify, as fraction of available width
26
-    const MIN_JUSTIFY_WIDTH = 0.80;
27
-
28
-    /**
29
-     * Frame for this reflower
30
-     *
31
-     * @var BlockFrameDecorator
32
-     */
33
-    protected $_frame;
34
-
35
-    function __construct(BlockFrameDecorator $frame)
36
-    {
37
-        parent::__construct($frame);
38
-    }
39
-
40
-    /**
41
-     *  Calculate the ideal used value for the width property as per:
42
-     *  http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
43
-     *
44
-     * @param float $width
45
-     *
46
-     * @return array
47
-     */
48
-    protected function _calculate_width($width)
49
-    {
50
-        $frame = $this->_frame;
51
-        $style = $frame->get_style();
52
-        $absolute = $frame->is_absolute();
53
-
54
-        $cb = $frame->get_containing_block();
55
-        $w = $cb["w"];
56
-
57
-        $rm = $style->length_in_pt($style->margin_right, $w);
58
-        $lm = $style->length_in_pt($style->margin_left, $w);
59
-
60
-        $left = $style->length_in_pt($style->left, $w);
61
-        $right = $style->length_in_pt($style->right, $w);
62
-
63
-        // Handle 'auto' values
64
-        $dims = [$style->border_left_width,
65
-            $style->border_right_width,
66
-            $style->padding_left,
67
-            $style->padding_right,
68
-            $width !== "auto" ? $width : 0,
69
-            $rm !== "auto" ? $rm : 0,
70
-            $lm !== "auto" ? $lm : 0];
71
-
72
-        // absolutely positioned boxes take the 'left' and 'right' properties into account
73
-        if ($absolute) {
74
-            $dims[] = $left !== "auto" ? $left : 0;
75
-            $dims[] = $right !== "auto" ? $right : 0;
76
-        }
77
-
78
-        $sum = (float)$style->length_in_pt($dims, $w);
79
-
80
-        // Compare to the containing block
81
-        $diff = $w - $sum;
82
-
83
-        if ($absolute) {
84
-            // Absolutely positioned
85
-            // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
86
-
87
-            if ($width === "auto" || $left === "auto" || $right === "auto") {
88
-                // "all of the three are 'auto'" logic + otherwise case
89
-                if ($lm === "auto") {
90
-                    $lm = 0;
91
-                }
92
-                if ($rm === "auto") {
93
-                    $rm = 0;
94
-                }
95
-
96
-                $block_parent = $frame->find_block_parent();
97
-                $parent_content = $block_parent->get_content_box();
98
-                $line = $block_parent->get_current_line_box();
99
-
100
-                // TODO: This is the in-flow inline position. Use the in-flow
101
-                // block position if the original display type is block-level
102
-                $inflow_x = $parent_content["x"] - $cb["x"] + $line->left + $line->w;
103
-
104
-                if ($width === "auto" && $left === "auto" && $right === "auto") {
105
-                    // rule 3, per instruction preceding rule set
106
-                    // shrink-to-fit width
107
-                    $left = $inflow_x;
108
-                    [$min, $max] = $this->get_min_max_child_width();
109
-                    $width = min(max($min, $diff - $left), $max);
110
-                    $right = $diff - $left - $width;
111
-                } elseif ($width === "auto" && $left === "auto") {
112
-                    // rule 1
113
-                    // shrink-to-fit width
114
-                    [$min, $max] = $this->get_min_max_child_width();
115
-                    $width = min(max($min, $diff), $max);
116
-                    $left = $diff - $width;
117
-                } elseif ($width === "auto" && $right === "auto") {
118
-                    // rule 3
119
-                    // shrink-to-fit width
120
-                    [$min, $max] = $this->get_min_max_child_width();
121
-                    $width = min(max($min, $diff), $max);
122
-                    $right = $diff - $width;
123
-                } elseif ($left === "auto" && $right === "auto") {
124
-                    // rule 2
125
-                    $left = $inflow_x;
126
-                    $right = $diff - $left;
127
-                } elseif ($left === "auto") {
128
-                    // rule 4
129
-                    $left = $diff;
130
-                } elseif ($width === "auto") {
131
-                    // rule 5
132
-                    $width = max($diff, 0);
133
-                } else {
134
-                    // $right === "auto"
135
-                    // rule 6
136
-                    $right = $diff;
137
-                }
138
-            } else {
139
-                // "none of the three are 'auto'" logic described in paragraph preceding the rules
140
-                if ($diff >= 0) {
141
-                    if ($lm === "auto" && $rm === "auto") {
142
-                        $lm = $rm = $diff / 2;
143
-                    } elseif ($lm === "auto") {
144
-                        $lm = $diff;
145
-                    } elseif ($rm === "auto") {
146
-                        $rm = $diff;
147
-                    }
148
-                } else {
149
-                    // over-constrained, solve for right
150
-                    $right = $right + $diff;
151
-
152
-                    if ($lm === "auto") {
153
-                        $lm = 0;
154
-                    }
155
-                    if ($rm === "auto") {
156
-                        $rm = 0;
157
-                    }
158
-                }
159
-            }
160
-        } elseif ($style->float !== "none" || $style->display === "inline-block") {
161
-            // Shrink-to-fit width for float and inline block
162
-            // https://www.w3.org/TR/CSS21/visudet.html#float-width
163
-            // https://www.w3.org/TR/CSS21/visudet.html#inlineblock-width
164
-
165
-            if ($width === "auto") {
166
-                [$min, $max] = $this->get_min_max_child_width();
167
-                $width = min(max($min, $diff), $max);
168
-            }
169
-            if ($lm === "auto") {
170
-                $lm = 0;
171
-            }
172
-            if ($rm === "auto") {
173
-                $rm = 0;
174
-            }
175
-        } else {
176
-            // Block-level, normal flow
177
-            // https://www.w3.org/TR/CSS21/visudet.html#blockwidth
178
-
179
-            if ($diff >= 0) {
180
-                // Find auto properties and get them to take up the slack
181
-                if ($width === "auto") {
182
-                    $width = $diff;
183
-
184
-                    if ($lm === "auto") {
185
-                        $lm = 0;
186
-                    }
187
-                    if ($rm === "auto") {
188
-                        $rm = 0;
189
-                    }
190
-                } elseif ($lm === "auto" && $rm === "auto") {
191
-                    $lm = $rm = $diff / 2;
192
-                } elseif ($lm === "auto") {
193
-                    $lm = $diff;
194
-                } elseif ($rm === "auto") {
195
-                    $rm = $diff;
196
-                }
197
-            } else {
198
-                // We are over constrained--set margin-right to the difference
199
-                $rm = (float) $rm + $diff;
200
-
201
-                if ($width === "auto") {
202
-                    $width = 0;
203
-                }
204
-                if ($lm === "auto") {
205
-                    $lm = 0;
206
-                }
207
-            }
208
-        }
209
-
210
-        return [
211
-            "width" => $width,
212
-            "margin_left" => $lm,
213
-            "margin_right" => $rm,
214
-            "left" => $left,
215
-            "right" => $right,
216
-        ];
217
-    }
218
-
219
-    /**
220
-     * Call the above function, but resolve max/min widths
221
-     *
222
-     * @throws Exception
223
-     * @return array
224
-     */
225
-    protected function _calculate_restricted_width()
226
-    {
227
-        $frame = $this->_frame;
228
-        $style = $frame->get_style();
229
-        $cb = $frame->get_containing_block();
230
-
231
-        if (!isset($cb["w"])) {
232
-            throw new Exception("Box property calculation requires containing block width");
233
-        }
234
-
235
-        $width = $style->length_in_pt($style->width, $cb["w"]);
236
-
237
-        $values = $this->_calculate_width($width);
238
-        $margin_left = $values["margin_left"];
239
-        $margin_right = $values["margin_right"];
240
-        $width = $values["width"];
241
-        $left = $values["left"];
242
-        $right = $values["right"];
243
-
244
-        // Handle min/max width
245
-        // https://www.w3.org/TR/CSS21/visudet.html#min-max-widths
246
-        $min_width = $this->resolve_min_width($cb["w"]);
247
-        $max_width = $this->resolve_max_width($cb["w"]);
248
-
249
-        if ($width > $max_width) {
250
-            $values = $this->_calculate_width($max_width);
251
-            $margin_left = $values["margin_left"];
252
-            $margin_right = $values["margin_right"];
253
-            $width = $values["width"];
254
-            $left = $values["left"];
255
-            $right = $values["right"];
256
-        }
257
-
258
-        if ($width < $min_width) {
259
-            $values = $this->_calculate_width($min_width);
260
-            $margin_left = $values["margin_left"];
261
-            $margin_right = $values["margin_right"];
262
-            $width = $values["width"];
263
-            $left = $values["left"];
264
-            $right = $values["right"];
265
-        }
266
-
267
-        return [$width, $margin_left, $margin_right, $left, $right];
268
-    }
269
-
270
-    /**
271
-     * Determine the unrestricted height of content within the block
272
-     * not by adding each line's height, but by getting the last line's position.
273
-     * This because lines could have been pushed lower by a clearing element.
274
-     *
275
-     * @return float
276
-     */
277
-    protected function _calculate_content_height()
278
-    {
279
-        $height = 0;
280
-        $lines = $this->_frame->get_line_boxes();
281
-        if (count($lines) > 0) {
282
-            $last_line = end($lines);
283
-            $content_box = $this->_frame->get_content_box();
284
-            $height = $last_line->y + $last_line->h - $content_box["y"];
285
-        }
286
-        return $height;
287
-    }
288
-
289
-    /**
290
-     * Determine the frame's restricted height
291
-     *
292
-     * @return array
293
-     */
294
-    protected function _calculate_restricted_height()
295
-    {
296
-        $frame = $this->_frame;
297
-        $style = $frame->get_style();
298
-        $content_height = $this->_calculate_content_height();
299
-        $cb = $frame->get_containing_block();
300
-
301
-        $height = $style->length_in_pt($style->height, $cb["h"]);
302
-        $margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
303
-        $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
304
-
305
-        $top = $style->length_in_pt($style->top, $cb["h"]);
306
-        $bottom = $style->length_in_pt($style->bottom, $cb["h"]);
307
-
308
-        if ($frame->is_absolute()) {
309
-            // Absolutely positioned
310
-            // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
311
-
312
-            $h_dims = [
313
-                $top !== "auto" ? $top : 0,
314
-                $height !== "auto" ? $height : 0,
315
-                $bottom !== "auto" ? $bottom : 0
316
-            ];
317
-            $w_dims = [
318
-                $style->margin_top !== "auto" ? $style->margin_top : 0,
319
-                $style->padding_top,
320
-                $style->border_top_width,
321
-                $style->border_bottom_width,
322
-                $style->padding_bottom,
323
-                $style->margin_bottom !== "auto" ? $style->margin_bottom : 0
324
-            ];
325
-
326
-            $sum = (float)$style->length_in_pt($h_dims, $cb["h"])
327
-                + (float)$style->length_in_pt($w_dims, $cb["w"]);
328
-
329
-            $diff = $cb["h"] - $sum;
330
-
331
-            if ($height === "auto" || $top === "auto" || $bottom === "auto") {
332
-                // "all of the three are 'auto'" logic + otherwise case
333
-                if ($margin_top === "auto") {
334
-                    $margin_top = 0;
335
-                }
336
-                if ($margin_bottom === "auto") {
337
-                    $margin_bottom = 0;
338
-                }
339
-
340
-                $block_parent = $frame->find_block_parent();
341
-                $current_line = $block_parent->get_current_line_box();
342
-
343
-                // TODO: This is the in-flow inline position. Use the in-flow
344
-                // block position if the original display type is block-level
345
-                $inflow_y = $current_line->y - $cb["y"];
346
-
347
-                if ($height === "auto" && $top === "auto" && $bottom === "auto") {
348
-                    // rule 3, per instruction preceding rule set
349
-                    $top = $inflow_y;
350
-                    $height = $content_height;
351
-                    $bottom = $diff - $top - $height;
352
-                } elseif ($height === "auto" && $top === "auto") {
353
-                    // rule 1
354
-                    $height = $content_height;
355
-                    $top = $diff - $height;
356
-                } elseif ($height === "auto" && $bottom === "auto") {
357
-                    // rule 3
358
-                    $height = $content_height;
359
-                    $bottom = $diff - $height;
360
-                } elseif ($top === "auto" && $bottom === "auto") {
361
-                    // rule 2
362
-                    $top = $inflow_y;
363
-                    $bottom = $diff - $top;
364
-                } elseif ($top === "auto") {
365
-                    // rule 4
366
-                    $top = $diff;
367
-                } elseif ($height === "auto") {
368
-                    // rule 5
369
-                    $height = max($diff, 0);
370
-                } else {
371
-                    // $bottom === "auto"
372
-                    // rule 6
373
-                    $bottom = $diff;
374
-                }
375
-            } else {
376
-                // "none of the three are 'auto'" logic described in paragraph preceding the rules
377
-                if ($diff >= 0) {
378
-                    if ($margin_top === "auto" && $margin_bottom === "auto") {
379
-                        $margin_top = $margin_bottom = $diff / 2;
380
-                    } elseif ($margin_top === "auto") {
381
-                        $margin_top = $diff;
382
-                    } elseif ($margin_bottom === "auto") {
383
-                        $margin_bottom = $diff;
384
-                    }
385
-                } else {
386
-                    // over-constrained, solve for bottom
387
-                    $bottom = $bottom + $diff;
388
-
389
-                    if ($margin_top === "auto") {
390
-                        $margin_top = 0;
391
-                    }
392
-                    if ($margin_bottom === "auto") {
393
-                        $margin_bottom = 0;
394
-                    }
395
-                }
396
-            }
397
-        } else {
398
-            // https://www.w3.org/TR/CSS21/visudet.html#normal-block
399
-            // https://www.w3.org/TR/CSS21/visudet.html#block-root-margin
400
-
401
-            if ($height === "auto") {
402
-                $height = $content_height;
403
-            }
404
-            if ($margin_top === "auto") {
405
-                $margin_top = 0;
406
-            }
407
-            if ($margin_bottom === "auto") {
408
-                $margin_bottom = 0;
409
-            }
410
-
411
-            // Handle min/max height
412
-            // https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
413
-            $min_height = $this->resolve_min_height($cb["h"]);
414
-            $max_height = $this->resolve_max_height($cb["h"]);
415
-            $height = Helpers::clamp($height, $min_height, $max_height);
416
-        }
417
-
418
-        // TODO: Need to also take min/max height into account for absolute
419
-        // positioning, using similar logic to the `_calculate_width`/
420
-        // `calculate_restricted_width` split above. The non-absolute case
421
-        // can simply clamp height within min/max, as margins and offsets are
422
-        // not affected
423
-
424
-        return [$height, $margin_top, $margin_bottom, $top, $bottom];
425
-    }
426
-
427
-    /**
428
-     * Adjust the justification of each of our lines.
429
-     * http://www.w3.org/TR/CSS21/text.html#propdef-text-align
430
-     */
431
-    protected function _text_align()
432
-    {
433
-        $style = $this->_frame->get_style();
434
-        $w = $this->_frame->get_containing_block("w");
435
-        $width = (float)$style->length_in_pt($style->width, $w);
436
-        $text_indent = (float)$style->length_in_pt($style->text_indent, $w);
437
-
438
-        switch ($style->text_align) {
439
-            default:
440
-            case "left":
441
-                foreach ($this->_frame->get_line_boxes() as $line) {
442
-                    if (!$line->inline) {
443
-                        continue;
444
-                    }
445
-
446
-                    $line->trim_trailing_ws();
447
-
448
-                    if ($line->left) {
449
-                        foreach ($line->frames_to_align() as $frame) {
450
-                            $frame->move($line->left, 0);
451
-                        }
452
-                    }
453
-                }
454
-                break;
455
-
456
-            case "right":
457
-                foreach ($this->_frame->get_line_boxes() as $i => $line) {
458
-                    if (!$line->inline) {
459
-                        continue;
460
-                    }
461
-
462
-                    $line->trim_trailing_ws();
463
-
464
-                    $indent = $i === 0 ? $text_indent : 0;
465
-                    $dx = $width - $line->w - $line->right - $indent;
466
-
467
-                    foreach ($line->frames_to_align() as $frame) {
468
-                        $frame->move($dx, 0);
469
-                    }
470
-                }
471
-                break;
472
-
473
-            case "justify":
474
-                // We justify all lines except the last one, unless the frame
475
-                // has been split, in which case the actual last line is part of
476
-                // the split-off frame
477
-                $lines = $this->_frame->get_line_boxes();
478
-                $last_line_index = $this->_frame->is_split ? null : count($lines) - 1;
479
-
480
-                foreach ($lines as $i => $line) {
481
-                    if (!$line->inline) {
482
-                        continue;
483
-                    }
484
-
485
-                    $line->trim_trailing_ws();
486
-
487
-                    if ($line->left) {
488
-                        foreach ($line->frames_to_align() as $frame) {
489
-                            $frame->move($line->left, 0);
490
-                        }
491
-                    }
492
-
493
-                    if ($line->br || $i === $last_line_index) {
494
-                        continue;
495
-                    }
496
-
497
-                    $frames = $line->get_frames();
498
-                    $other_frame_count = 0;
499
-
500
-                    foreach ($frames as $frame) {
501
-                        if (!($frame instanceof TextFrameDecorator)) {
502
-                            $other_frame_count++;
503
-                        }
504
-                    }
505
-
506
-                    $word_count = $line->wc + $other_frame_count;
507
-
508
-                    // Set the spacing for each child
509
-                    if ($word_count > 1) {
510
-                        $indent = $i === 0 ? $text_indent : 0;
511
-                        $spacing = ($width - $line->get_width() - $indent) / ($word_count - 1);
512
-                    } else {
513
-                        $spacing = 0;
514
-                    }
515
-
516
-                    $dx = 0;
517
-                    foreach ($frames as $frame) {
518
-                        if ($frame instanceof TextFrameDecorator) {
519
-                            $text = $frame->get_text();
520
-                            $spaces = mb_substr_count($text, " ");
521
-
522
-                            $frame->move($dx, 0);
523
-                            $frame->set_text_spacing($spacing);
524
-
525
-                            $dx += $spaces * $spacing;
526
-                        } else {
527
-                            $frame->move($dx, 0);
528
-                        }
529
-                    }
530
-
531
-                    // The line (should) now occupy the entire width
532
-                    $line->w = $width;
533
-                }
534
-                break;
535
-
536
-            case "center":
537
-            case "centre":
538
-                foreach ($this->_frame->get_line_boxes() as $i => $line) {
539
-                    if (!$line->inline) {
540
-                        continue;
541
-                    }
542
-
543
-                    $line->trim_trailing_ws();
544
-
545
-                    $indent = $i === 0 ? $text_indent : 0;
546
-                    $dx = ($width + $line->left - $line->w - $line->right - $indent) / 2;
547
-
548
-                    foreach ($line->frames_to_align() as $frame) {
549
-                        $frame->move($dx, 0);
550
-                    }
551
-                }
552
-                break;
553
-        }
554
-    }
555
-
556
-    /**
557
-     * Align inline children vertically.
558
-     * Aligns each child vertically after each line is reflowed
559
-     */
560
-    function vertical_align()
561
-    {
562
-        $fontMetrics = $this->get_dompdf()->getFontMetrics();
563
-
564
-        foreach ($this->_frame->get_line_boxes() as $line) {
565
-            $height = $line->h;
566
-
567
-            // Move all markers to the top of the line box
568
-            foreach ($line->get_list_markers() as $marker) {
569
-                $x = $marker->get_position("x");
570
-                $marker->set_position($x, $line->y);
571
-            }
572
-
573
-            foreach ($line->frames_to_align() as $frame) {
574
-                $style = $frame->get_style();
575
-                $isInlineBlock = $style->display !== "inline"
576
-                    && $style->display !== "-dompdf-list-bullet";
577
-
578
-                $baseline = $fontMetrics->getFontBaseline($style->font_family, $style->font_size);
579
-                $y_offset = 0;
580
-
581
-                //FIXME: The 0.8 ratio applied to the height is arbitrary (used to accommodate descenders?)
582
-                if ($isInlineBlock) {
583
-                    // Workaround: Skip vertical alignment if the frame is the
584
-                    // only one one the line, excluding empty text frames, which
585
-                    // may be the result of trailing white space
586
-                    // FIXME: This special case should be removed once vertical
587
-                    // alignment is properly fixed
588
-                    $skip = true;
589
-
590
-                    foreach ($line->get_frames() as $other) {
591
-                        if ($other !== $frame
592
-                            && !($other->is_text_node() && $other->get_node()->nodeValue === "")
593
-                         ) {
594
-                            $skip = false;
595
-                            break;
596
-                        }
597
-                    }
598
-
599
-                    if ($skip) {
600
-                        continue;
601
-                    }
602
-
603
-                    $marginHeight = $frame->get_margin_height();
604
-                    $imageHeightDiff = $height * 0.8 - $marginHeight;
605
-
606
-                    $align = $frame->get_style()->vertical_align;
607
-                    if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) {
608
-                        switch ($align) {
609
-                            case "middle":
610
-                                $y_offset = $imageHeightDiff / 2;
611
-                                break;
612
-
613
-                            case "sub":
614
-                                $y_offset = 0.3 * $height + $imageHeightDiff;
615
-                                break;
616
-
617
-                            case "super":
618
-                                $y_offset = -0.2 * $height + $imageHeightDiff;
619
-                                break;
620
-
621
-                            case "text-top": // FIXME: this should be the height of the frame minus the height of the text
622
-                                $y_offset = $height - $style->line_height;
623
-                                break;
624
-
625
-                            case "top":
626
-                                break;
627
-
628
-                            case "text-bottom": // FIXME: align bottom of image with the descender?
629
-                            case "bottom":
630
-                                $y_offset = 0.3 * $height + $imageHeightDiff;
631
-                                break;
632
-
633
-                            case "baseline":
634
-                            default:
635
-                                $y_offset = $imageHeightDiff;
636
-                                break;
637
-                        }
638
-                    } else {
639
-                        $y_offset = $baseline - (float)$style->length_in_pt($align, $style->font_size) - $marginHeight;
640
-                    }
641
-                } else {
642
-                    $parent = $frame->get_parent();
643
-                    if ($parent instanceof TableCellFrameDecorator) {
644
-                        $align = "baseline";
645
-                    } else {
646
-                        $align = $parent->get_style()->vertical_align;
647
-                    }
648
-                    if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) {
649
-                        switch ($align) {
650
-                            case "middle":
651
-                                $y_offset = ($height * 0.8 - $baseline) / 2;
652
-                                break;
653
-
654
-                            case "sub":
655
-                                $y_offset = $height * 0.8 - $baseline * 0.5;
656
-                                break;
657
-
658
-                            case "super":
659
-                                $y_offset = $height * 0.8 - $baseline * 1.4;
660
-                                break;
661
-
662
-                            case "text-top":
663
-                            case "top": // Not strictly accurate, but good enough for now
664
-                                break;
665
-
666
-                            case "text-bottom":
667
-                            case "bottom":
668
-                                $y_offset = $height * 0.8 - $baseline;
669
-                                break;
670
-
671
-                            case "baseline":
672
-                            default:
673
-                                $y_offset = $height * 0.8 - $baseline;
674
-                                break;
675
-                        }
676
-                    } else {
677
-                        $y_offset = $height * 0.8 - $baseline - (float)$style->length_in_pt($align, $style->font_size);
678
-                    }
679
-                }
680
-
681
-                if ($y_offset !== 0) {
682
-                    $frame->move(0, $y_offset);
683
-                }
684
-            }
685
-        }
686
-    }
687
-
688
-    /**
689
-     * @param AbstractFrameDecorator $child
690
-     */
691
-    function process_clear(AbstractFrameDecorator $child)
692
-    {
693
-        $child_style = $child->get_style();
694
-        $root = $this->_frame->get_root();
695
-
696
-        // Handle "clear"
697
-        if ($child_style->clear !== "none") {
698
-            //TODO: this is a WIP for handling clear/float frames that are in between inline frames
699
-            if ($child->get_prev_sibling() !== null) {
700
-                $this->_frame->add_line();
701
-            }
702
-            if ($child_style->float !== "none" && $child->get_next_sibling()) {
703
-                $this->_frame->set_current_line_number($this->_frame->get_current_line_number() - 1);
704
-            }
705
-
706
-            $lowest_y = $root->get_lowest_float_offset($child);
707
-
708
-            // If a float is still applying, we handle it
709
-            if ($lowest_y) {
710
-                if ($child->is_in_flow()) {
711
-                    $line_box = $this->_frame->get_current_line_box();
712
-                    $line_box->y = $lowest_y + $child->get_margin_height();
713
-                    $line_box->left = 0;
714
-                    $line_box->right = 0;
715
-                }
716
-
717
-                $child->move(0, $lowest_y - $child->get_position("y"));
718
-            }
719
-        }
720
-    }
721
-
722
-    /**
723
-     * @param AbstractFrameDecorator $child
724
-     * @param float $cb_x
725
-     * @param float $cb_w
726
-     */
727
-    function process_float(AbstractFrameDecorator $child, $cb_x, $cb_w)
728
-    {
729
-        $child_style = $child->get_style();
730
-        $root = $this->_frame->get_root();
731
-
732
-        // Handle "float"
733
-        if ($child_style->float !== "none") {
734
-            $root->add_floating_frame($child);
735
-
736
-            // Remove next frame's beginning whitespace
737
-            $next = $child->get_next_sibling();
738
-            if ($next && $next instanceof TextFrameDecorator) {
739
-                $next->set_text(ltrim($next->get_text()));
740
-            }
741
-
742
-            $line_box = $this->_frame->get_current_line_box();
743
-            list($old_x, $old_y) = $child->get_position();
744
-
745
-            $float_x = $cb_x;
746
-            $float_y = $old_y;
747
-            $float_w = $child->get_margin_width();
748
-
749
-            if ($child_style->clear === "none") {
750
-                switch ($child_style->float) {
751
-                    case "left":
752
-                        $float_x += $line_box->left;
753
-                        break;
754
-                    case "right":
755
-                        $float_x += ($cb_w - $line_box->right - $float_w);
756
-                        break;
757
-                }
758
-            } else {
759
-                if ($child_style->float === "right") {
760
-                    $float_x += ($cb_w - $float_w);
761
-                }
762
-            }
763
-
764
-            if ($cb_w < $float_x + $float_w - $old_x) {
765
-                // TODO handle when floating elements don't fit
766
-            }
767
-
768
-            $line_box->get_float_offsets();
769
-
770
-            if ($child->_float_next_line) {
771
-                $float_y += $line_box->h;
772
-            }
773
-
774
-            $child->set_position($float_x, $float_y);
775
-            $child->move($float_x - $old_x, $float_y - $old_y, true);
776
-        }
777
-    }
778
-
779
-    /**
780
-     * @param BlockFrameDecorator $block
781
-     */
782
-    function reflow(BlockFrameDecorator $block = null)
783
-    {
784
-
785
-        // Check if a page break is forced
786
-        $page = $this->_frame->get_root();
787
-        $page->check_forced_page_break($this->_frame);
788
-
789
-        // Bail if the page is full
790
-        if ($page->is_full()) {
791
-            return;
792
-        }
793
-
794
-        $this->determine_absolute_containing_block();
795
-
796
-        // Counters and generated content
797
-        $this->_set_content();
798
-
799
-        // Inherit any dangling list markers
800
-        if ($block && $this->_frame->is_in_flow()) {
801
-            $this->_frame->inherit_dangling_markers($block);
802
-        }
803
-
804
-        // Collapse margins if required
805
-        $this->_collapse_margins();
806
-
807
-        $style = $this->_frame->get_style();
808
-        $cb = $this->_frame->get_containing_block();
809
-
810
-        // Determine the constraints imposed by this frame: calculate the width
811
-        // of the content area:
812
-        [$width, $margin_left, $margin_right, $left, $right] = $this->_calculate_restricted_width();
813
-
814
-        // Store the calculated properties
815
-        $style->set_used("width", $width);
816
-        $style->set_used("margin_left", $margin_left);
817
-        $style->set_used("margin_right", $margin_right);
818
-        $style->set_used("left", $left);
819
-        $style->set_used("right", $right);
820
-
821
-        $margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
822
-        $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
823
-
824
-        $auto_top = $style->top === "auto";
825
-        $auto_margin_top = $margin_top === "auto";
826
-
827
-        // Update the position
828
-        $this->_frame->position();
829
-        [$x, $y] = $this->_frame->get_position();
830
-
831
-        // Adjust the first line based on the text-indent property
832
-        $indent = (float)$style->length_in_pt($style->text_indent, $cb["w"]);
833
-        $this->_frame->increase_line_width($indent);
834
-
835
-        // Determine the content edge
836
-        $top = (float)$style->length_in_pt([
837
-            $margin_top !== "auto" ? $margin_top : 0,
838
-            $style->border_top_width,
839
-            $style->padding_top
840
-        ], $cb["w"]);
841
-        $bottom = (float)$style->length_in_pt([
842
-            $margin_bottom !== "auto" ? $margin_bottom : 0,
843
-            $style->border_bottom_width,
844
-            $style->padding_bottom
845
-        ], $cb["w"]);
846
-
847
-        $cb_x = $x + (float)$margin_left + (float)$style->length_in_pt([$style->border_left_width,
848
-                $style->padding_left], $cb["w"]);
849
-
850
-        $cb_y = $y + $top;
851
-
852
-        $height = $style->length_in_pt($style->height, $cb["h"]);
853
-        if ($height === "auto") {
854
-            $height = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
855
-        }
856
-
857
-        // Set the y position of the first line in this block
858
-        $line_box = $this->_frame->get_current_line_box();
859
-        $line_box->y = $cb_y;
860
-        $line_box->get_float_offsets();
861
-
862
-        // Set the containing blocks and reflow each child
863
-        foreach ($this->_frame->get_children() as $child) {
864
-            $child->set_containing_block($cb_x, $cb_y, $width, $height);
865
-            $this->process_clear($child);
866
-            $child->reflow($this->_frame);
867
-
868
-            // Check for a page break before the child
869
-            $page->check_page_break($child);
870
-
871
-            // Don't add the child to the line if a page break has occurred
872
-            // before it (possibly via a descendant), in which case it has been
873
-            // reset, including its position
874
-            if ($page->is_full() && $child->get_position("x") === null) {
875
-                break;
876
-            }
877
-
878
-            $this->process_float($child, $cb_x, $width);
879
-        }
880
-
881
-        // Stop reflow if a page break has occurred before the frame, in which
882
-        // case it has been reset, including its position
883
-        if ($page->is_full() && $this->_frame->get_position("x") === null) {
884
-            return;
885
-        }
886
-
887
-        // Determine our height
888
-        [$height, $margin_top, $margin_bottom, $top, $bottom] = $this->_calculate_restricted_height();
889
-
890
-        $style->set_used("height", $height);
891
-        $style->set_used("margin_top", $margin_top);
892
-        $style->set_used("margin_bottom", $margin_bottom);
893
-        $style->set_used("top", $top);
894
-        $style->set_used("bottom", $bottom);
895
-
896
-        if ($this->_frame->is_absolute()) {
897
-            if ($auto_top) {
898
-                $this->_frame->move(0, $top);
899
-            }
900
-            if ($auto_margin_top) {
901
-                $this->_frame->move(0, $margin_top, true);
902
-            }
903
-        }
904
-
905
-        $this->_text_align();
906
-        $this->vertical_align();
907
-
908
-        // Handle relative positioning
909
-        foreach ($this->_frame->get_children() as $child) {
910
-            $this->position_relative($child);
911
-        }
912
-
913
-        if ($block && $this->_frame->is_in_flow()) {
914
-            $block->add_frame_to_line($this->_frame);
915
-
916
-            if ($this->_frame->is_block_level()) {
917
-                $block->add_line();
918
-            }
919
-        }
920
-    }
921
-
922
-    public function get_min_max_content_width(): array
923
-    {
924
-        // TODO: While the containing block is not set yet on the frame, it can
925
-        // already be determined in some cases due to fixed dimensions on the
926
-        // ancestor forming the containing block. In such cases, percentage
927
-        // values could be resolved here
928
-        $style = $this->_frame->get_style();
929
-        $width = $style->width;
930
-        $fixed_width = $width !== "auto" && !Helpers::is_percent($width);
931
-
932
-        // If the frame has a specified width, then we don't need to check
933
-        // its children
934
-        if ($fixed_width) {
935
-            $min = (float) $style->length_in_pt($width, 0);
936
-            $max = $min;
937
-        } else {
938
-            [$min, $max] = $this->get_min_max_child_width();
939
-        }
940
-
941
-        // Handle min/max width style properties
942
-        $min_width = $this->resolve_min_width(null);
943
-        $max_width = $this->resolve_max_width(null);
944
-        $min = Helpers::clamp($min, $min_width, $max_width);
945
-        $max = Helpers::clamp($max, $min_width, $max_width);
946
-
947
-        return [$min, $max];
948
-    }
25
+	// Minimum line width to justify, as fraction of available width
26
+	const MIN_JUSTIFY_WIDTH = 0.80;
27
+
28
+	/**
29
+	 * Frame for this reflower
30
+	 *
31
+	 * @var BlockFrameDecorator
32
+	 */
33
+	protected $_frame;
34
+
35
+	function __construct(BlockFrameDecorator $frame)
36
+	{
37
+		parent::__construct($frame);
38
+	}
39
+
40
+	/**
41
+	 *  Calculate the ideal used value for the width property as per:
42
+	 *  http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
43
+	 *
44
+	 * @param float $width
45
+	 *
46
+	 * @return array
47
+	 */
48
+	protected function _calculate_width($width)
49
+	{
50
+		$frame = $this->_frame;
51
+		$style = $frame->get_style();
52
+		$absolute = $frame->is_absolute();
53
+
54
+		$cb = $frame->get_containing_block();
55
+		$w = $cb["w"];
56
+
57
+		$rm = $style->length_in_pt($style->margin_right, $w);
58
+		$lm = $style->length_in_pt($style->margin_left, $w);
59
+
60
+		$left = $style->length_in_pt($style->left, $w);
61
+		$right = $style->length_in_pt($style->right, $w);
62
+
63
+		// Handle 'auto' values
64
+		$dims = [$style->border_left_width,
65
+			$style->border_right_width,
66
+			$style->padding_left,
67
+			$style->padding_right,
68
+			$width !== "auto" ? $width : 0,
69
+			$rm !== "auto" ? $rm : 0,
70
+			$lm !== "auto" ? $lm : 0];
71
+
72
+		// absolutely positioned boxes take the 'left' and 'right' properties into account
73
+		if ($absolute) {
74
+			$dims[] = $left !== "auto" ? $left : 0;
75
+			$dims[] = $right !== "auto" ? $right : 0;
76
+		}
77
+
78
+		$sum = (float)$style->length_in_pt($dims, $w);
79
+
80
+		// Compare to the containing block
81
+		$diff = $w - $sum;
82
+
83
+		if ($absolute) {
84
+			// Absolutely positioned
85
+			// http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
86
+
87
+			if ($width === "auto" || $left === "auto" || $right === "auto") {
88
+				// "all of the three are 'auto'" logic + otherwise case
89
+				if ($lm === "auto") {
90
+					$lm = 0;
91
+				}
92
+				if ($rm === "auto") {
93
+					$rm = 0;
94
+				}
95
+
96
+				$block_parent = $frame->find_block_parent();
97
+				$parent_content = $block_parent->get_content_box();
98
+				$line = $block_parent->get_current_line_box();
99
+
100
+				// TODO: This is the in-flow inline position. Use the in-flow
101
+				// block position if the original display type is block-level
102
+				$inflow_x = $parent_content["x"] - $cb["x"] + $line->left + $line->w;
103
+
104
+				if ($width === "auto" && $left === "auto" && $right === "auto") {
105
+					// rule 3, per instruction preceding rule set
106
+					// shrink-to-fit width
107
+					$left = $inflow_x;
108
+					[$min, $max] = $this->get_min_max_child_width();
109
+					$width = min(max($min, $diff - $left), $max);
110
+					$right = $diff - $left - $width;
111
+				} elseif ($width === "auto" && $left === "auto") {
112
+					// rule 1
113
+					// shrink-to-fit width
114
+					[$min, $max] = $this->get_min_max_child_width();
115
+					$width = min(max($min, $diff), $max);
116
+					$left = $diff - $width;
117
+				} elseif ($width === "auto" && $right === "auto") {
118
+					// rule 3
119
+					// shrink-to-fit width
120
+					[$min, $max] = $this->get_min_max_child_width();
121
+					$width = min(max($min, $diff), $max);
122
+					$right = $diff - $width;
123
+				} elseif ($left === "auto" && $right === "auto") {
124
+					// rule 2
125
+					$left = $inflow_x;
126
+					$right = $diff - $left;
127
+				} elseif ($left === "auto") {
128
+					// rule 4
129
+					$left = $diff;
130
+				} elseif ($width === "auto") {
131
+					// rule 5
132
+					$width = max($diff, 0);
133
+				} else {
134
+					// $right === "auto"
135
+					// rule 6
136
+					$right = $diff;
137
+				}
138
+			} else {
139
+				// "none of the three are 'auto'" logic described in paragraph preceding the rules
140
+				if ($diff >= 0) {
141
+					if ($lm === "auto" && $rm === "auto") {
142
+						$lm = $rm = $diff / 2;
143
+					} elseif ($lm === "auto") {
144
+						$lm = $diff;
145
+					} elseif ($rm === "auto") {
146
+						$rm = $diff;
147
+					}
148
+				} else {
149
+					// over-constrained, solve for right
150
+					$right = $right + $diff;
151
+
152
+					if ($lm === "auto") {
153
+						$lm = 0;
154
+					}
155
+					if ($rm === "auto") {
156
+						$rm = 0;
157
+					}
158
+				}
159
+			}
160
+		} elseif ($style->float !== "none" || $style->display === "inline-block") {
161
+			// Shrink-to-fit width for float and inline block
162
+			// https://www.w3.org/TR/CSS21/visudet.html#float-width
163
+			// https://www.w3.org/TR/CSS21/visudet.html#inlineblock-width
164
+
165
+			if ($width === "auto") {
166
+				[$min, $max] = $this->get_min_max_child_width();
167
+				$width = min(max($min, $diff), $max);
168
+			}
169
+			if ($lm === "auto") {
170
+				$lm = 0;
171
+			}
172
+			if ($rm === "auto") {
173
+				$rm = 0;
174
+			}
175
+		} else {
176
+			// Block-level, normal flow
177
+			// https://www.w3.org/TR/CSS21/visudet.html#blockwidth
178
+
179
+			if ($diff >= 0) {
180
+				// Find auto properties and get them to take up the slack
181
+				if ($width === "auto") {
182
+					$width = $diff;
183
+
184
+					if ($lm === "auto") {
185
+						$lm = 0;
186
+					}
187
+					if ($rm === "auto") {
188
+						$rm = 0;
189
+					}
190
+				} elseif ($lm === "auto" && $rm === "auto") {
191
+					$lm = $rm = $diff / 2;
192
+				} elseif ($lm === "auto") {
193
+					$lm = $diff;
194
+				} elseif ($rm === "auto") {
195
+					$rm = $diff;
196
+				}
197
+			} else {
198
+				// We are over constrained--set margin-right to the difference
199
+				$rm = (float) $rm + $diff;
200
+
201
+				if ($width === "auto") {
202
+					$width = 0;
203
+				}
204
+				if ($lm === "auto") {
205
+					$lm = 0;
206
+				}
207
+			}
208
+		}
209
+
210
+		return [
211
+			"width" => $width,
212
+			"margin_left" => $lm,
213
+			"margin_right" => $rm,
214
+			"left" => $left,
215
+			"right" => $right,
216
+		];
217
+	}
218
+
219
+	/**
220
+	 * Call the above function, but resolve max/min widths
221
+	 *
222
+	 * @throws Exception
223
+	 * @return array
224
+	 */
225
+	protected function _calculate_restricted_width()
226
+	{
227
+		$frame = $this->_frame;
228
+		$style = $frame->get_style();
229
+		$cb = $frame->get_containing_block();
230
+
231
+		if (!isset($cb["w"])) {
232
+			throw new Exception("Box property calculation requires containing block width");
233
+		}
234
+
235
+		$width = $style->length_in_pt($style->width, $cb["w"]);
236
+
237
+		$values = $this->_calculate_width($width);
238
+		$margin_left = $values["margin_left"];
239
+		$margin_right = $values["margin_right"];
240
+		$width = $values["width"];
241
+		$left = $values["left"];
242
+		$right = $values["right"];
243
+
244
+		// Handle min/max width
245
+		// https://www.w3.org/TR/CSS21/visudet.html#min-max-widths
246
+		$min_width = $this->resolve_min_width($cb["w"]);
247
+		$max_width = $this->resolve_max_width($cb["w"]);
248
+
249
+		if ($width > $max_width) {
250
+			$values = $this->_calculate_width($max_width);
251
+			$margin_left = $values["margin_left"];
252
+			$margin_right = $values["margin_right"];
253
+			$width = $values["width"];
254
+			$left = $values["left"];
255
+			$right = $values["right"];
256
+		}
257
+
258
+		if ($width < $min_width) {
259
+			$values = $this->_calculate_width($min_width);
260
+			$margin_left = $values["margin_left"];
261
+			$margin_right = $values["margin_right"];
262
+			$width = $values["width"];
263
+			$left = $values["left"];
264
+			$right = $values["right"];
265
+		}
266
+
267
+		return [$width, $margin_left, $margin_right, $left, $right];
268
+	}
269
+
270
+	/**
271
+	 * Determine the unrestricted height of content within the block
272
+	 * not by adding each line's height, but by getting the last line's position.
273
+	 * This because lines could have been pushed lower by a clearing element.
274
+	 *
275
+	 * @return float
276
+	 */
277
+	protected function _calculate_content_height()
278
+	{
279
+		$height = 0;
280
+		$lines = $this->_frame->get_line_boxes();
281
+		if (count($lines) > 0) {
282
+			$last_line = end($lines);
283
+			$content_box = $this->_frame->get_content_box();
284
+			$height = $last_line->y + $last_line->h - $content_box["y"];
285
+		}
286
+		return $height;
287
+	}
288
+
289
+	/**
290
+	 * Determine the frame's restricted height
291
+	 *
292
+	 * @return array
293
+	 */
294
+	protected function _calculate_restricted_height()
295
+	{
296
+		$frame = $this->_frame;
297
+		$style = $frame->get_style();
298
+		$content_height = $this->_calculate_content_height();
299
+		$cb = $frame->get_containing_block();
300
+
301
+		$height = $style->length_in_pt($style->height, $cb["h"]);
302
+		$margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
303
+		$margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
304
+
305
+		$top = $style->length_in_pt($style->top, $cb["h"]);
306
+		$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
307
+
308
+		if ($frame->is_absolute()) {
309
+			// Absolutely positioned
310
+			// http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
311
+
312
+			$h_dims = [
313
+				$top !== "auto" ? $top : 0,
314
+				$height !== "auto" ? $height : 0,
315
+				$bottom !== "auto" ? $bottom : 0
316
+			];
317
+			$w_dims = [
318
+				$style->margin_top !== "auto" ? $style->margin_top : 0,
319
+				$style->padding_top,
320
+				$style->border_top_width,
321
+				$style->border_bottom_width,
322
+				$style->padding_bottom,
323
+				$style->margin_bottom !== "auto" ? $style->margin_bottom : 0
324
+			];
325
+
326
+			$sum = (float)$style->length_in_pt($h_dims, $cb["h"])
327
+				+ (float)$style->length_in_pt($w_dims, $cb["w"]);
328
+
329
+			$diff = $cb["h"] - $sum;
330
+
331
+			if ($height === "auto" || $top === "auto" || $bottom === "auto") {
332
+				// "all of the three are 'auto'" logic + otherwise case
333
+				if ($margin_top === "auto") {
334
+					$margin_top = 0;
335
+				}
336
+				if ($margin_bottom === "auto") {
337
+					$margin_bottom = 0;
338
+				}
339
+
340
+				$block_parent = $frame->find_block_parent();
341
+				$current_line = $block_parent->get_current_line_box();
342
+
343
+				// TODO: This is the in-flow inline position. Use the in-flow
344
+				// block position if the original display type is block-level
345
+				$inflow_y = $current_line->y - $cb["y"];
346
+
347
+				if ($height === "auto" && $top === "auto" && $bottom === "auto") {
348
+					// rule 3, per instruction preceding rule set
349
+					$top = $inflow_y;
350
+					$height = $content_height;
351
+					$bottom = $diff - $top - $height;
352
+				} elseif ($height === "auto" && $top === "auto") {
353
+					// rule 1
354
+					$height = $content_height;
355
+					$top = $diff - $height;
356
+				} elseif ($height === "auto" && $bottom === "auto") {
357
+					// rule 3
358
+					$height = $content_height;
359
+					$bottom = $diff - $height;
360
+				} elseif ($top === "auto" && $bottom === "auto") {
361
+					// rule 2
362
+					$top = $inflow_y;
363
+					$bottom = $diff - $top;
364
+				} elseif ($top === "auto") {
365
+					// rule 4
366
+					$top = $diff;
367
+				} elseif ($height === "auto") {
368
+					// rule 5
369
+					$height = max($diff, 0);
370
+				} else {
371
+					// $bottom === "auto"
372
+					// rule 6
373
+					$bottom = $diff;
374
+				}
375
+			} else {
376
+				// "none of the three are 'auto'" logic described in paragraph preceding the rules
377
+				if ($diff >= 0) {
378
+					if ($margin_top === "auto" && $margin_bottom === "auto") {
379
+						$margin_top = $margin_bottom = $diff / 2;
380
+					} elseif ($margin_top === "auto") {
381
+						$margin_top = $diff;
382
+					} elseif ($margin_bottom === "auto") {
383
+						$margin_bottom = $diff;
384
+					}
385
+				} else {
386
+					// over-constrained, solve for bottom
387
+					$bottom = $bottom + $diff;
388
+
389
+					if ($margin_top === "auto") {
390
+						$margin_top = 0;
391
+					}
392
+					if ($margin_bottom === "auto") {
393
+						$margin_bottom = 0;
394
+					}
395
+				}
396
+			}
397
+		} else {
398
+			// https://www.w3.org/TR/CSS21/visudet.html#normal-block
399
+			// https://www.w3.org/TR/CSS21/visudet.html#block-root-margin
400
+
401
+			if ($height === "auto") {
402
+				$height = $content_height;
403
+			}
404
+			if ($margin_top === "auto") {
405
+				$margin_top = 0;
406
+			}
407
+			if ($margin_bottom === "auto") {
408
+				$margin_bottom = 0;
409
+			}
410
+
411
+			// Handle min/max height
412
+			// https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
413
+			$min_height = $this->resolve_min_height($cb["h"]);
414
+			$max_height = $this->resolve_max_height($cb["h"]);
415
+			$height = Helpers::clamp($height, $min_height, $max_height);
416
+		}
417
+
418
+		// TODO: Need to also take min/max height into account for absolute
419
+		// positioning, using similar logic to the `_calculate_width`/
420
+		// `calculate_restricted_width` split above. The non-absolute case
421
+		// can simply clamp height within min/max, as margins and offsets are
422
+		// not affected
423
+
424
+		return [$height, $margin_top, $margin_bottom, $top, $bottom];
425
+	}
426
+
427
+	/**
428
+	 * Adjust the justification of each of our lines.
429
+	 * http://www.w3.org/TR/CSS21/text.html#propdef-text-align
430
+	 */
431
+	protected function _text_align()
432
+	{
433
+		$style = $this->_frame->get_style();
434
+		$w = $this->_frame->get_containing_block("w");
435
+		$width = (float)$style->length_in_pt($style->width, $w);
436
+		$text_indent = (float)$style->length_in_pt($style->text_indent, $w);
437
+
438
+		switch ($style->text_align) {
439
+			default:
440
+			case "left":
441
+				foreach ($this->_frame->get_line_boxes() as $line) {
442
+					if (!$line->inline) {
443
+						continue;
444
+					}
445
+
446
+					$line->trim_trailing_ws();
447
+
448
+					if ($line->left) {
449
+						foreach ($line->frames_to_align() as $frame) {
450
+							$frame->move($line->left, 0);
451
+						}
452
+					}
453
+				}
454
+				break;
455
+
456
+			case "right":
457
+				foreach ($this->_frame->get_line_boxes() as $i => $line) {
458
+					if (!$line->inline) {
459
+						continue;
460
+					}
461
+
462
+					$line->trim_trailing_ws();
463
+
464
+					$indent = $i === 0 ? $text_indent : 0;
465
+					$dx = $width - $line->w - $line->right - $indent;
466
+
467
+					foreach ($line->frames_to_align() as $frame) {
468
+						$frame->move($dx, 0);
469
+					}
470
+				}
471
+				break;
472
+
473
+			case "justify":
474
+				// We justify all lines except the last one, unless the frame
475
+				// has been split, in which case the actual last line is part of
476
+				// the split-off frame
477
+				$lines = $this->_frame->get_line_boxes();
478
+				$last_line_index = $this->_frame->is_split ? null : count($lines) - 1;
479
+
480
+				foreach ($lines as $i => $line) {
481
+					if (!$line->inline) {
482
+						continue;
483
+					}
484
+
485
+					$line->trim_trailing_ws();
486
+
487
+					if ($line->left) {
488
+						foreach ($line->frames_to_align() as $frame) {
489
+							$frame->move($line->left, 0);
490
+						}
491
+					}
492
+
493
+					if ($line->br || $i === $last_line_index) {
494
+						continue;
495
+					}
496
+
497
+					$frames = $line->get_frames();
498
+					$other_frame_count = 0;
499
+
500
+					foreach ($frames as $frame) {
501
+						if (!($frame instanceof TextFrameDecorator)) {
502
+							$other_frame_count++;
503
+						}
504
+					}
505
+
506
+					$word_count = $line->wc + $other_frame_count;
507
+
508
+					// Set the spacing for each child
509
+					if ($word_count > 1) {
510
+						$indent = $i === 0 ? $text_indent : 0;
511
+						$spacing = ($width - $line->get_width() - $indent) / ($word_count - 1);
512
+					} else {
513
+						$spacing = 0;
514
+					}
515
+
516
+					$dx = 0;
517
+					foreach ($frames as $frame) {
518
+						if ($frame instanceof TextFrameDecorator) {
519
+							$text = $frame->get_text();
520
+							$spaces = mb_substr_count($text, " ");
521
+
522
+							$frame->move($dx, 0);
523
+							$frame->set_text_spacing($spacing);
524
+
525
+							$dx += $spaces * $spacing;
526
+						} else {
527
+							$frame->move($dx, 0);
528
+						}
529
+					}
530
+
531
+					// The line (should) now occupy the entire width
532
+					$line->w = $width;
533
+				}
534
+				break;
535
+
536
+			case "center":
537
+			case "centre":
538
+				foreach ($this->_frame->get_line_boxes() as $i => $line) {
539
+					if (!$line->inline) {
540
+						continue;
541
+					}
542
+
543
+					$line->trim_trailing_ws();
544
+
545
+					$indent = $i === 0 ? $text_indent : 0;
546
+					$dx = ($width + $line->left - $line->w - $line->right - $indent) / 2;
547
+
548
+					foreach ($line->frames_to_align() as $frame) {
549
+						$frame->move($dx, 0);
550
+					}
551
+				}
552
+				break;
553
+		}
554
+	}
555
+
556
+	/**
557
+	 * Align inline children vertically.
558
+	 * Aligns each child vertically after each line is reflowed
559
+	 */
560
+	function vertical_align()
561
+	{
562
+		$fontMetrics = $this->get_dompdf()->getFontMetrics();
563
+
564
+		foreach ($this->_frame->get_line_boxes() as $line) {
565
+			$height = $line->h;
566
+
567
+			// Move all markers to the top of the line box
568
+			foreach ($line->get_list_markers() as $marker) {
569
+				$x = $marker->get_position("x");
570
+				$marker->set_position($x, $line->y);
571
+			}
572
+
573
+			foreach ($line->frames_to_align() as $frame) {
574
+				$style = $frame->get_style();
575
+				$isInlineBlock = $style->display !== "inline"
576
+					&& $style->display !== "-dompdf-list-bullet";
577
+
578
+				$baseline = $fontMetrics->getFontBaseline($style->font_family, $style->font_size);
579
+				$y_offset = 0;
580
+
581
+				//FIXME: The 0.8 ratio applied to the height is arbitrary (used to accommodate descenders?)
582
+				if ($isInlineBlock) {
583
+					// Workaround: Skip vertical alignment if the frame is the
584
+					// only one one the line, excluding empty text frames, which
585
+					// may be the result of trailing white space
586
+					// FIXME: This special case should be removed once vertical
587
+					// alignment is properly fixed
588
+					$skip = true;
589
+
590
+					foreach ($line->get_frames() as $other) {
591
+						if ($other !== $frame
592
+							&& !($other->is_text_node() && $other->get_node()->nodeValue === "")
593
+						 ) {
594
+							$skip = false;
595
+							break;
596
+						}
597
+					}
598
+
599
+					if ($skip) {
600
+						continue;
601
+					}
602
+
603
+					$marginHeight = $frame->get_margin_height();
604
+					$imageHeightDiff = $height * 0.8 - $marginHeight;
605
+
606
+					$align = $frame->get_style()->vertical_align;
607
+					if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) {
608
+						switch ($align) {
609
+							case "middle":
610
+								$y_offset = $imageHeightDiff / 2;
611
+								break;
612
+
613
+							case "sub":
614
+								$y_offset = 0.3 * $height + $imageHeightDiff;
615
+								break;
616
+
617
+							case "super":
618
+								$y_offset = -0.2 * $height + $imageHeightDiff;
619
+								break;
620
+
621
+							case "text-top": // FIXME: this should be the height of the frame minus the height of the text
622
+								$y_offset = $height - $style->line_height;
623
+								break;
624
+
625
+							case "top":
626
+								break;
627
+
628
+							case "text-bottom": // FIXME: align bottom of image with the descender?
629
+							case "bottom":
630
+								$y_offset = 0.3 * $height + $imageHeightDiff;
631
+								break;
632
+
633
+							case "baseline":
634
+							default:
635
+								$y_offset = $imageHeightDiff;
636
+								break;
637
+						}
638
+					} else {
639
+						$y_offset = $baseline - (float)$style->length_in_pt($align, $style->font_size) - $marginHeight;
640
+					}
641
+				} else {
642
+					$parent = $frame->get_parent();
643
+					if ($parent instanceof TableCellFrameDecorator) {
644
+						$align = "baseline";
645
+					} else {
646
+						$align = $parent->get_style()->vertical_align;
647
+					}
648
+					if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) {
649
+						switch ($align) {
650
+							case "middle":
651
+								$y_offset = ($height * 0.8 - $baseline) / 2;
652
+								break;
653
+
654
+							case "sub":
655
+								$y_offset = $height * 0.8 - $baseline * 0.5;
656
+								break;
657
+
658
+							case "super":
659
+								$y_offset = $height * 0.8 - $baseline * 1.4;
660
+								break;
661
+
662
+							case "text-top":
663
+							case "top": // Not strictly accurate, but good enough for now
664
+								break;
665
+
666
+							case "text-bottom":
667
+							case "bottom":
668
+								$y_offset = $height * 0.8 - $baseline;
669
+								break;
670
+
671
+							case "baseline":
672
+							default:
673
+								$y_offset = $height * 0.8 - $baseline;
674
+								break;
675
+						}
676
+					} else {
677
+						$y_offset = $height * 0.8 - $baseline - (float)$style->length_in_pt($align, $style->font_size);
678
+					}
679
+				}
680
+
681
+				if ($y_offset !== 0) {
682
+					$frame->move(0, $y_offset);
683
+				}
684
+			}
685
+		}
686
+	}
687
+
688
+	/**
689
+	 * @param AbstractFrameDecorator $child
690
+	 */
691
+	function process_clear(AbstractFrameDecorator $child)
692
+	{
693
+		$child_style = $child->get_style();
694
+		$root = $this->_frame->get_root();
695
+
696
+		// Handle "clear"
697
+		if ($child_style->clear !== "none") {
698
+			//TODO: this is a WIP for handling clear/float frames that are in between inline frames
699
+			if ($child->get_prev_sibling() !== null) {
700
+				$this->_frame->add_line();
701
+			}
702
+			if ($child_style->float !== "none" && $child->get_next_sibling()) {
703
+				$this->_frame->set_current_line_number($this->_frame->get_current_line_number() - 1);
704
+			}
705
+
706
+			$lowest_y = $root->get_lowest_float_offset($child);
707
+
708
+			// If a float is still applying, we handle it
709
+			if ($lowest_y) {
710
+				if ($child->is_in_flow()) {
711
+					$line_box = $this->_frame->get_current_line_box();
712
+					$line_box->y = $lowest_y + $child->get_margin_height();
713
+					$line_box->left = 0;
714
+					$line_box->right = 0;
715
+				}
716
+
717
+				$child->move(0, $lowest_y - $child->get_position("y"));
718
+			}
719
+		}
720
+	}
721
+
722
+	/**
723
+	 * @param AbstractFrameDecorator $child
724
+	 * @param float $cb_x
725
+	 * @param float $cb_w
726
+	 */
727
+	function process_float(AbstractFrameDecorator $child, $cb_x, $cb_w)
728
+	{
729
+		$child_style = $child->get_style();
730
+		$root = $this->_frame->get_root();
731
+
732
+		// Handle "float"
733
+		if ($child_style->float !== "none") {
734
+			$root->add_floating_frame($child);
735
+
736
+			// Remove next frame's beginning whitespace
737
+			$next = $child->get_next_sibling();
738
+			if ($next && $next instanceof TextFrameDecorator) {
739
+				$next->set_text(ltrim($next->get_text()));
740
+			}
741
+
742
+			$line_box = $this->_frame->get_current_line_box();
743
+			list($old_x, $old_y) = $child->get_position();
744
+
745
+			$float_x = $cb_x;
746
+			$float_y = $old_y;
747
+			$float_w = $child->get_margin_width();
748
+
749
+			if ($child_style->clear === "none") {
750
+				switch ($child_style->float) {
751
+					case "left":
752
+						$float_x += $line_box->left;
753
+						break;
754
+					case "right":
755
+						$float_x += ($cb_w - $line_box->right - $float_w);
756
+						break;
757
+				}
758
+			} else {
759
+				if ($child_style->float === "right") {
760
+					$float_x += ($cb_w - $float_w);
761
+				}
762
+			}
763
+
764
+			if ($cb_w < $float_x + $float_w - $old_x) {
765
+				// TODO handle when floating elements don't fit
766
+			}
767
+
768
+			$line_box->get_float_offsets();
769
+
770
+			if ($child->_float_next_line) {
771
+				$float_y += $line_box->h;
772
+			}
773
+
774
+			$child->set_position($float_x, $float_y);
775
+			$child->move($float_x - $old_x, $float_y - $old_y, true);
776
+		}
777
+	}
778
+
779
+	/**
780
+	 * @param BlockFrameDecorator $block
781
+	 */
782
+	function reflow(BlockFrameDecorator $block = null)
783
+	{
784
+
785
+		// Check if a page break is forced
786
+		$page = $this->_frame->get_root();
787
+		$page->check_forced_page_break($this->_frame);
788
+
789
+		// Bail if the page is full
790
+		if ($page->is_full()) {
791
+			return;
792
+		}
793
+
794
+		$this->determine_absolute_containing_block();
795
+
796
+		// Counters and generated content
797
+		$this->_set_content();
798
+
799
+		// Inherit any dangling list markers
800
+		if ($block && $this->_frame->is_in_flow()) {
801
+			$this->_frame->inherit_dangling_markers($block);
802
+		}
803
+
804
+		// Collapse margins if required
805
+		$this->_collapse_margins();
806
+
807
+		$style = $this->_frame->get_style();
808
+		$cb = $this->_frame->get_containing_block();
809
+
810
+		// Determine the constraints imposed by this frame: calculate the width
811
+		// of the content area:
812
+		[$width, $margin_left, $margin_right, $left, $right] = $this->_calculate_restricted_width();
813
+
814
+		// Store the calculated properties
815
+		$style->set_used("width", $width);
816
+		$style->set_used("margin_left", $margin_left);
817
+		$style->set_used("margin_right", $margin_right);
818
+		$style->set_used("left", $left);
819
+		$style->set_used("right", $right);
820
+
821
+		$margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
822
+		$margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
823
+
824
+		$auto_top = $style->top === "auto";
825
+		$auto_margin_top = $margin_top === "auto";
826
+
827
+		// Update the position
828
+		$this->_frame->position();
829
+		[$x, $y] = $this->_frame->get_position();
830
+
831
+		// Adjust the first line based on the text-indent property
832
+		$indent = (float)$style->length_in_pt($style->text_indent, $cb["w"]);
833
+		$this->_frame->increase_line_width($indent);
834
+
835
+		// Determine the content edge
836
+		$top = (float)$style->length_in_pt([
837
+			$margin_top !== "auto" ? $margin_top : 0,
838
+			$style->border_top_width,
839
+			$style->padding_top
840
+		], $cb["w"]);
841
+		$bottom = (float)$style->length_in_pt([
842
+			$margin_bottom !== "auto" ? $margin_bottom : 0,
843
+			$style->border_bottom_width,
844
+			$style->padding_bottom
845
+		], $cb["w"]);
846
+
847
+		$cb_x = $x + (float)$margin_left + (float)$style->length_in_pt([$style->border_left_width,
848
+				$style->padding_left], $cb["w"]);
849
+
850
+		$cb_y = $y + $top;
851
+
852
+		$height = $style->length_in_pt($style->height, $cb["h"]);
853
+		if ($height === "auto") {
854
+			$height = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
855
+		}
856
+
857
+		// Set the y position of the first line in this block
858
+		$line_box = $this->_frame->get_current_line_box();
859
+		$line_box->y = $cb_y;
860
+		$line_box->get_float_offsets();
861
+
862
+		// Set the containing blocks and reflow each child
863
+		foreach ($this->_frame->get_children() as $child) {
864
+			$child->set_containing_block($cb_x, $cb_y, $width, $height);
865
+			$this->process_clear($child);
866
+			$child->reflow($this->_frame);
867
+
868
+			// Check for a page break before the child
869
+			$page->check_page_break($child);
870
+
871
+			// Don't add the child to the line if a page break has occurred
872
+			// before it (possibly via a descendant), in which case it has been
873
+			// reset, including its position
874
+			if ($page->is_full() && $child->get_position("x") === null) {
875
+				break;
876
+			}
877
+
878
+			$this->process_float($child, $cb_x, $width);
879
+		}
880
+
881
+		// Stop reflow if a page break has occurred before the frame, in which
882
+		// case it has been reset, including its position
883
+		if ($page->is_full() && $this->_frame->get_position("x") === null) {
884
+			return;
885
+		}
886
+
887
+		// Determine our height
888
+		[$height, $margin_top, $margin_bottom, $top, $bottom] = $this->_calculate_restricted_height();
889
+
890
+		$style->set_used("height", $height);
891
+		$style->set_used("margin_top", $margin_top);
892
+		$style->set_used("margin_bottom", $margin_bottom);
893
+		$style->set_used("top", $top);
894
+		$style->set_used("bottom", $bottom);
895
+
896
+		if ($this->_frame->is_absolute()) {
897
+			if ($auto_top) {
898
+				$this->_frame->move(0, $top);
899
+			}
900
+			if ($auto_margin_top) {
901
+				$this->_frame->move(0, $margin_top, true);
902
+			}
903
+		}
904
+
905
+		$this->_text_align();
906
+		$this->vertical_align();
907
+
908
+		// Handle relative positioning
909
+		foreach ($this->_frame->get_children() as $child) {
910
+			$this->position_relative($child);
911
+		}
912
+
913
+		if ($block && $this->_frame->is_in_flow()) {
914
+			$block->add_frame_to_line($this->_frame);
915
+
916
+			if ($this->_frame->is_block_level()) {
917
+				$block->add_line();
918
+			}
919
+		}
920
+	}
921
+
922
+	public function get_min_max_content_width(): array
923
+	{
924
+		// TODO: While the containing block is not set yet on the frame, it can
925
+		// already be determined in some cases due to fixed dimensions on the
926
+		// ancestor forming the containing block. In such cases, percentage
927
+		// values could be resolved here
928
+		$style = $this->_frame->get_style();
929
+		$width = $style->width;
930
+		$fixed_width = $width !== "auto" && !Helpers::is_percent($width);
931
+
932
+		// If the frame has a specified width, then we don't need to check
933
+		// its children
934
+		if ($fixed_width) {
935
+			$min = (float) $style->length_in_pt($width, 0);
936
+			$max = $min;
937
+		} else {
938
+			[$min, $max] = $this->get_min_max_child_width();
939
+		}
940
+
941
+		// Handle min/max width style properties
942
+		$min_width = $this->resolve_min_width(null);
943
+		$max_width = $this->resolve_max_width(null);
944
+		$min = Helpers::clamp($min, $min_width, $max_width);
945
+		$max = Helpers::clamp($max, $min_width, $max_width);
946
+
947
+		return [$min, $max];
948
+	}
949 949
 }
Please login to merge, or discard this patch.
vendor/dompdf/dompdf/src/FrameDecorator/TableRowGroup.php 1 patch
Indentation   +45 added lines, -45 removed lines patch added patch discarded remove patch
@@ -19,56 +19,56 @@
 block discarded – undo
19 19
 class TableRowGroup extends AbstractFrameDecorator
20 20
 {
21 21
 
22
-    /**
23
-     * Class constructor
24
-     *
25
-     * @param Frame $frame   Frame to decorate
26
-     * @param Dompdf $dompdf Current dompdf instance
27
-     */
28
-    function __construct(Frame $frame, Dompdf $dompdf)
29
-    {
30
-        parent::__construct($frame, $dompdf);
31
-    }
22
+	/**
23
+	 * Class constructor
24
+	 *
25
+	 * @param Frame $frame   Frame to decorate
26
+	 * @param Dompdf $dompdf Current dompdf instance
27
+	 */
28
+	function __construct(Frame $frame, Dompdf $dompdf)
29
+	{
30
+		parent::__construct($frame, $dompdf);
31
+	}
32 32
 
33
-    /**
34
-     * Split the row group at the given child and remove all subsequent child
35
-     * rows and all subsequent row groups from the cellmap.
36
-     */
37
-    public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
38
-    {
39
-        if (is_null($child)) {
40
-            parent::split($child, $page_break, $forced);
41
-            return;
42
-        }
33
+	/**
34
+	 * Split the row group at the given child and remove all subsequent child
35
+	 * rows and all subsequent row groups from the cellmap.
36
+	 */
37
+	public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
38
+	{
39
+		if (is_null($child)) {
40
+			parent::split($child, $page_break, $forced);
41
+			return;
42
+		}
43 43
 
44
-        // Remove child & all subsequent rows from the cellmap
45
-        /** @var Table $parent */
46
-        $parent = $this->get_parent();
47
-        $cellmap = $parent->get_cellmap();
48
-        $iter = $child;
44
+		// Remove child & all subsequent rows from the cellmap
45
+		/** @var Table $parent */
46
+		$parent = $this->get_parent();
47
+		$cellmap = $parent->get_cellmap();
48
+		$iter = $child;
49 49
 
50
-        while ($iter) {
51
-            $cellmap->remove_row($iter);
52
-            $iter = $iter->get_next_sibling();
53
-        }
50
+		while ($iter) {
51
+			$cellmap->remove_row($iter);
52
+			$iter = $iter->get_next_sibling();
53
+		}
54 54
 
55
-        // Remove all subsequent row groups from the cellmap
56
-        $iter = $this->get_next_sibling();
55
+		// Remove all subsequent row groups from the cellmap
56
+		$iter = $this->get_next_sibling();
57 57
 
58
-        while ($iter) {
59
-            $cellmap->remove_row_group($iter);
60
-            $iter = $iter->get_next_sibling();
61
-        }
58
+		while ($iter) {
59
+			$cellmap->remove_row_group($iter);
60
+			$iter = $iter->get_next_sibling();
61
+		}
62 62
 
63
-        // If we are splitting at the first child remove the
64
-        // table-row-group from the cellmap as well
65
-        if ($child === $this->get_first_child()) {
66
-            $cellmap->remove_row_group($this);
67
-            parent::split(null, $page_break, $forced);
68
-            return;
69
-        }
63
+		// If we are splitting at the first child remove the
64
+		// table-row-group from the cellmap as well
65
+		if ($child === $this->get_first_child()) {
66
+			$cellmap->remove_row_group($this);
67
+			parent::split(null, $page_break, $forced);
68
+			return;
69
+		}
70 70
 
71
-        $cellmap->update_row_group($this, $child->get_prev_sibling());
72
-        parent::split($child, $page_break, $forced);
73
-    }
71
+		$cellmap->update_row_group($this, $child->get_prev_sibling());
72
+		parent::split($child, $page_break, $forced);
73
+	}
74 74
 }
Please login to merge, or discard this patch.