1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @package dompdf |
4
|
|
|
* @link http://dompdf.github.com/ |
5
|
|
|
* @author Benj Carson <[email protected]> |
6
|
|
|
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License |
7
|
|
|
*/ |
8
|
|
|
namespace Dompdf\FrameReflower; |
9
|
|
|
|
10
|
|
|
use Dompdf\Adapter\CPDF; |
11
|
|
|
use Dompdf\Css\Style; |
12
|
|
|
use Dompdf\Dompdf; |
13
|
|
|
use Dompdf\Helpers; |
14
|
|
|
use Dompdf\Frame; |
15
|
|
|
use Dompdf\FrameDecorator\Block; |
|
|
|
|
16
|
|
|
use Dompdf\Frame\Factory; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Base reflower class |
20
|
|
|
* |
21
|
|
|
* Reflower objects are responsible for determining the width and height of |
22
|
|
|
* individual frames. They also create line and page breaks as necessary. |
23
|
|
|
* |
24
|
|
|
* @package dompdf |
25
|
|
|
*/ |
26
|
|
|
abstract class AbstractFrameReflower |
27
|
|
|
{ |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Frame for this reflower |
31
|
|
|
* |
32
|
|
|
* @var Frame |
33
|
|
|
*/ |
34
|
|
|
protected $_frame; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Cached min/max size |
38
|
|
|
* |
39
|
|
|
* @var array |
40
|
|
|
*/ |
41
|
|
|
protected $_min_max_cache; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* AbstractFrameReflower constructor. |
45
|
|
|
* @param Frame $frame |
46
|
|
|
*/ |
47
|
|
|
function __construct(Frame $frame) |
|
|
|
|
48
|
|
|
{ |
49
|
|
|
$this->_frame = $frame; |
50
|
|
|
$this->_min_max_cache = null; |
|
|
|
|
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
function dispose() |
|
|
|
|
54
|
|
|
{ |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @return Dompdf |
59
|
|
|
*/ |
60
|
|
|
function get_dompdf() |
|
|
|
|
61
|
|
|
{ |
62
|
|
|
return $this->_frame->get_dompdf(); |
|
|
|
|
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Collapse frames margins |
67
|
|
|
* http://www.w3.org/TR/CSS2/box.html#collapsing-margins |
68
|
|
|
*/ |
69
|
|
|
protected function _collapse_margins() |
70
|
|
|
{ |
71
|
|
|
$frame = $this->_frame; |
72
|
|
|
$cb = $frame->get_containing_block(); |
73
|
|
|
$style = $frame->get_style(); |
74
|
|
|
|
75
|
|
|
// Margins of float/absolutely positioned/inline-block elements do not collapse. |
76
|
|
|
if (!$frame->is_in_flow() || $frame->is_inline_block()) { |
77
|
|
|
return; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
$t = $style->length_in_pt($style->margin_top, $cb["h"]); |
|
|
|
|
81
|
|
|
$b = $style->length_in_pt($style->margin_bottom, $cb["h"]); |
|
|
|
|
82
|
|
|
|
83
|
|
|
// Handle 'auto' values |
84
|
|
|
if ($t === "auto") { |
85
|
|
|
$style->margin_top = "0pt"; |
|
|
|
|
86
|
|
|
$t = 0; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
if ($b === "auto") { |
90
|
|
|
$style->margin_bottom = "0pt"; |
|
|
|
|
91
|
|
|
$b = 0; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
// Collapse vertical margins: |
95
|
|
|
$n = $frame->get_next_sibling(); |
96
|
|
|
if ( $n && !$n->is_block() & !$n->is_table() ) { |
97
|
|
|
while ($n = $n->get_next_sibling()) { |
98
|
|
|
if ($n->is_block() || $n->is_table()) { |
99
|
|
|
break; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
if (!$n->get_first_child()) { |
103
|
|
|
$n = null; |
104
|
|
|
break; |
105
|
|
|
} |
106
|
|
|
} |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
if ($n) { |
110
|
|
|
$n_style = $n->get_style(); |
111
|
|
|
$n_t = (float)$n_style->length_in_pt($n_style->margin_top, $cb["h"]); |
|
|
|
|
112
|
|
|
|
113
|
|
|
$b = $this->_get_collapsed_margin_length($b, $n_t); |
114
|
|
|
$style->margin_bottom = $b . "pt"; |
|
|
|
|
115
|
|
|
$n_style->margin_top = "0pt"; |
|
|
|
|
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
// Collapse our first child's margin, if there is no border or padding |
119
|
|
View Code Duplication |
if ($style->get_border_top_width() == 0 && $style->length_in_pt($style->padding_top) == 0) { |
|
|
|
|
120
|
|
|
$f = $this->_frame->get_first_child(); |
121
|
|
|
if ( $f && !$f->is_block() && !$f->is_table() ) { |
122
|
|
|
while ( $f = $f->get_next_sibling() ) { |
123
|
|
|
if ( $f->is_block() || $f->is_table() ) { |
124
|
|
|
break; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
if ( !$f->get_first_child() ) { |
128
|
|
|
$f = null; |
129
|
|
|
break; |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
// Margin are collapsed only between block-level boxes |
135
|
|
|
if ($f) { |
136
|
|
|
$f_style = $f->get_style(); |
137
|
|
|
$f_t = (float)$f_style->length_in_pt($f_style->margin_top, $cb["h"]); |
|
|
|
|
138
|
|
|
|
139
|
|
|
$t = $this->_get_collapsed_margin_length($t, $f_t); |
140
|
|
|
$style->margin_top = $t."pt"; |
|
|
|
|
141
|
|
|
$f_style->margin_top = "0pt"; |
|
|
|
|
142
|
|
|
} |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
// Collapse our last child's margin, if there is no border or padding |
146
|
|
View Code Duplication |
if ($style->get_border_bottom_width() == 0 && $style->length_in_pt($style->padding_bottom) == 0) { |
|
|
|
|
147
|
|
|
$l = $this->_frame->get_last_child(); |
148
|
|
|
if ( $l && !$l->is_block() && !$l->is_table() ) { |
149
|
|
|
while ( $l = $l->get_prev_sibling() ) { |
150
|
|
|
if ( $l->is_block() || $l->is_table() ) { |
151
|
|
|
break; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
if ( !$l->get_last_child() ) { |
155
|
|
|
$l = null; |
156
|
|
|
break; |
157
|
|
|
} |
158
|
|
|
} |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
// Margin are collapsed only between block-level boxes |
162
|
|
|
if ($l) { |
163
|
|
|
$l_style = $l->get_style(); |
164
|
|
|
$l_b = (float)$l_style->length_in_pt($l_style->margin_bottom, $cb["h"]); |
|
|
|
|
165
|
|
|
|
166
|
|
|
$b = $this->_get_collapsed_margin_length($b, $l_b); |
167
|
|
|
$style->margin_bottom = $b."pt"; |
|
|
|
|
168
|
|
|
$l_style->margin_bottom = "0pt"; |
|
|
|
|
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* Get the combined (collapsed) length of two adjoining margins. |
175
|
|
|
* |
176
|
|
|
* See http://www.w3.org/TR/CSS2/box.html#collapsing-margins. |
177
|
|
|
* |
178
|
|
|
* @param number $length1 |
179
|
|
|
* @param number $length2 |
180
|
|
|
* @return number |
181
|
|
|
*/ |
182
|
|
|
private function _get_collapsed_margin_length($length1, $length2) |
183
|
|
|
{ |
184
|
|
|
if ($length1 < 0 && $length2 < 0) { |
185
|
|
|
return min($length1, $length2); // min(x, y) = - max(abs(x), abs(y)), if x < 0 && y < 0 |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
if ($length1 < 0 || $length2 < 0) { |
189
|
|
|
return $length1 + $length2; // x + y = x - abs(y), if y < 0 |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
return max($length1, $length2); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* @param Block|null $block |
197
|
|
|
* @return mixed |
198
|
|
|
*/ |
199
|
|
|
abstract function reflow(Block $block = null); |
|
|
|
|
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Required for table layout: Returns an array(0 => min, 1 => max, "min" |
203
|
|
|
* => min, "max" => max) of the minimum and maximum widths of this frame. |
204
|
|
|
* This provides a basic implementation. Child classes should override |
205
|
|
|
* this if necessary. |
206
|
|
|
* |
207
|
|
|
* @return array|null |
208
|
|
|
*/ |
209
|
|
|
function get_min_max_width() |
|
|
|
|
210
|
|
|
{ |
211
|
|
|
if (!is_null($this->_min_max_cache)) { |
212
|
|
|
return $this->_min_max_cache; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
$style = $this->_frame->get_style(); |
216
|
|
|
|
217
|
|
|
// Account for margins & padding |
218
|
|
|
$dims = array($style->padding_left, |
|
|
|
|
219
|
|
|
$style->padding_right, |
|
|
|
|
220
|
|
|
$style->border_left_width, |
|
|
|
|
221
|
|
|
$style->border_right_width, |
|
|
|
|
222
|
|
|
$style->margin_left, |
|
|
|
|
223
|
|
|
$style->margin_right); |
|
|
|
|
224
|
|
|
|
225
|
|
|
$cb_w = $this->_frame->get_containing_block("w"); |
226
|
|
|
$delta = (float)$style->length_in_pt($dims, $cb_w); |
|
|
|
|
227
|
|
|
|
228
|
|
|
// Handle degenerate case |
229
|
|
|
if (!$this->_frame->get_first_child()) { |
230
|
|
|
return $this->_min_max_cache = array( |
231
|
|
|
$delta, $delta, |
232
|
|
|
"min" => $delta, |
233
|
|
|
"max" => $delta, |
234
|
|
|
); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
$low = array(); |
238
|
|
|
$high = array(); |
239
|
|
|
|
240
|
|
|
for ($iter = $this->_frame->get_children()->getIterator(); $iter->valid(); $iter->next()) { |
241
|
|
|
$inline_min = 0; |
242
|
|
|
$inline_max = 0; |
243
|
|
|
|
244
|
|
|
// Add all adjacent inline widths together to calculate max width |
245
|
|
|
while ($iter->valid() && in_array($iter->current()->get_style()->display, Style::$INLINE_TYPES)) { |
|
|
|
|
246
|
|
|
$child = $iter->current(); |
247
|
|
|
|
248
|
|
|
$minmax = $child->get_min_max_width(); |
249
|
|
|
|
250
|
|
|
if (in_array($iter->current()->get_style()->white_space, array("pre", "nowrap"))) { |
251
|
|
|
$inline_min += $minmax["min"]; |
252
|
|
|
} else { |
253
|
|
|
$low[] = $minmax["min"]; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
$inline_max += $minmax["max"]; |
257
|
|
|
$iter->next(); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
if ($inline_max > 0) { |
261
|
|
|
$high[] = $inline_max; |
262
|
|
|
} |
263
|
|
|
if ($inline_min > 0) { |
264
|
|
|
$low[] = $inline_min; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
if ($iter->valid()) { |
268
|
|
|
list($low[], $high[]) = $iter->current()->get_min_max_width(); |
269
|
|
|
continue; |
270
|
|
|
} |
271
|
|
|
} |
272
|
|
|
$min = count($low) ? max($low) : 0; |
273
|
|
|
$max = count($high) ? max($high) : 0; |
274
|
|
|
|
275
|
|
|
// Use specified width if it is greater than the minimum defined by the |
276
|
|
|
// content. If the width is a percentage ignore it for now. |
277
|
|
|
$width = $style->width; |
|
|
|
|
278
|
|
|
if ($width !== "auto" && !Helpers::is_percent($width)) { |
279
|
|
|
$width = (float)$style->length_in_pt($width, $cb_w); |
|
|
|
|
280
|
|
|
if ($min < $width) { |
281
|
|
|
$min = $width; |
282
|
|
|
} |
283
|
|
|
if ($max < $width) { |
284
|
|
|
$max = $width; |
285
|
|
|
} |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
$min += $delta; |
289
|
|
|
$max += $delta; |
290
|
|
|
return $this->_min_max_cache = array($min, $max, "min" => $min, "max" => $max); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Parses a CSS string containing quotes and escaped hex characters |
295
|
|
|
* |
296
|
|
|
* @param $string string The CSS string to parse |
297
|
|
|
* @param $single_trim |
298
|
|
|
* @return string |
299
|
|
|
*/ |
300
|
|
|
protected function _parse_string($string, $single_trim = false) |
301
|
|
|
{ |
302
|
|
|
if ($single_trim) { |
303
|
|
|
$string = preg_replace('/^[\"\']/', "", $string); |
304
|
|
|
$string = preg_replace('/[\"\']$/', "", $string); |
305
|
|
|
} else { |
306
|
|
|
$string = trim($string, "'\""); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
$string = str_replace(array("\\\n", '\\"', "\\'"), |
310
|
|
|
array("", '"', "'"), $string); |
311
|
|
|
|
312
|
|
|
// Convert escaped hex characters into ascii characters (e.g. \A => newline) |
313
|
|
|
$string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/", |
314
|
|
|
function ($matches) { return \Dompdf\Helpers::unichr(hexdec($matches[1])); }, |
315
|
|
|
$string); |
316
|
|
|
return $string; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Parses a CSS "quotes" property |
321
|
|
|
* |
322
|
|
|
* @return array|null An array of pairs of quotes |
323
|
|
|
*/ |
324
|
|
|
protected function _parse_quotes() |
325
|
|
|
{ |
326
|
|
|
// Matches quote types |
327
|
|
|
$re = '/(\'[^\']*\')|(\"[^\"]*\")/'; |
328
|
|
|
|
329
|
|
|
$quotes = $this->_frame->get_style()->quotes; |
|
|
|
|
330
|
|
|
|
331
|
|
|
// split on spaces, except within quotes |
332
|
|
|
if (!preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER)) { |
333
|
|
|
return null; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
$quotes_array = array(); |
337
|
|
|
foreach ($matches as $_quote) { |
338
|
|
|
$quotes_array[] = $this->_parse_string($_quote[0], true); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
if (empty($quotes_array)) { |
342
|
|
|
$quotes_array = array('"', '"'); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
return array_chunk($quotes_array, 2); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* Parses the CSS "content" property |
350
|
|
|
* |
351
|
|
|
* @return string|null The resulting string |
352
|
|
|
*/ |
353
|
|
|
protected function _parse_content() |
354
|
|
|
{ |
355
|
|
|
// Matches generated content |
356
|
|
|
$re = "/\n" . |
357
|
|
|
"\s(counters?\\([^)]*\\))|\n" . |
358
|
|
|
"\A(counters?\\([^)]*\\))|\n" . |
359
|
|
|
"\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\3|\n" . |
360
|
|
|
"\A([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\5|\n" . |
361
|
|
|
"\s([^\s\"']+)|\n" . |
362
|
|
|
"\A([^\s\"']+)\n" . |
363
|
|
|
"/xi"; |
364
|
|
|
|
365
|
|
|
$content = $this->_frame->get_style()->content; |
|
|
|
|
366
|
|
|
|
367
|
|
|
$quotes = $this->_parse_quotes(); |
368
|
|
|
|
369
|
|
|
// split on spaces, except within quotes |
370
|
|
|
if (!preg_match_all($re, $content, $matches, PREG_SET_ORDER)) { |
371
|
|
|
return null; |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
$text = ""; |
375
|
|
|
|
376
|
|
|
foreach ($matches as $match) { |
377
|
|
View Code Duplication |
if (isset($match[2]) && $match[2] !== "") { |
|
|
|
|
378
|
|
|
$match[1] = $match[2]; |
379
|
|
|
} |
380
|
|
|
|
381
|
|
View Code Duplication |
if (isset($match[6]) && $match[6] !== "") { |
|
|
|
|
382
|
|
|
$match[4] = $match[6]; |
383
|
|
|
} |
384
|
|
|
|
385
|
|
View Code Duplication |
if (isset($match[8]) && $match[8] !== "") { |
|
|
|
|
386
|
|
|
$match[7] = $match[8]; |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
if (isset($match[1]) && $match[1] !== "") { |
390
|
|
|
// counters?(...) |
|
|
|
|
391
|
|
|
$match[1] = mb_strtolower(trim($match[1])); |
392
|
|
|
|
393
|
|
|
// Handle counter() references: |
394
|
|
|
// http://www.w3.org/TR/CSS21/generate.html#content |
395
|
|
|
|
396
|
|
|
$i = mb_strpos($match[1], ")"); |
397
|
|
|
if ($i === false) { |
398
|
|
|
continue; |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
preg_match('/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]+)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i', $match[1], $args); |
402
|
|
|
$counter_id = $args[3]; |
403
|
|
|
if (strtolower($args[1]) == 'counter') { |
404
|
|
|
// counter(name [,style]) |
|
|
|
|
405
|
|
View Code Duplication |
if (isset($args[5])) { |
|
|
|
|
406
|
|
|
$type = trim($args[5]); |
407
|
|
|
} else { |
408
|
|
|
$type = null; |
409
|
|
|
} |
410
|
|
|
$p = $this->_frame->lookup_counter_frame($counter_id); |
|
|
|
|
411
|
|
|
|
412
|
|
|
$text .= $p->counter_value($counter_id, $type); |
413
|
|
|
|
414
|
|
|
} else if (strtolower($args[1]) == 'counters') { |
415
|
|
|
// counters(name, string [,style]) |
|
|
|
|
416
|
|
|
if (isset($args[5])) { |
417
|
|
|
$string = $this->_parse_string($args[5]); |
418
|
|
|
} else { |
419
|
|
|
$string = ""; |
420
|
|
|
} |
421
|
|
|
|
422
|
|
View Code Duplication |
if (isset($args[7])) { |
|
|
|
|
423
|
|
|
$type = trim($args[7]); |
424
|
|
|
} else { |
425
|
|
|
$type = null; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
$p = $this->_frame->lookup_counter_frame($counter_id); |
|
|
|
|
429
|
|
|
$tmp = array(); |
430
|
|
|
while ($p) { |
431
|
|
|
// We only want to use the counter values when they actually increment the counter |
432
|
|
|
if (array_key_exists($counter_id, $p->_counters)) { |
433
|
|
|
array_unshift($tmp, $p->counter_value($counter_id, $type)); |
434
|
|
|
} |
435
|
|
|
$p = $p->lookup_counter_frame($counter_id); |
436
|
|
|
} |
437
|
|
|
$text .= implode($string, $tmp); |
438
|
|
|
} else { |
439
|
|
|
// countertops? |
440
|
|
|
continue; |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
} else if (isset($match[4]) && $match[4] !== "") { |
444
|
|
|
// String match |
445
|
|
|
$text .= $this->_parse_string($match[4]); |
446
|
|
|
} else if (isset($match[7]) && $match[7] !== "") { |
447
|
|
|
// Directive match |
448
|
|
|
|
449
|
|
|
if ($match[7] === "open-quote") { |
450
|
|
|
// FIXME: do something here |
451
|
|
|
$text .= $quotes[0][0]; |
452
|
|
|
} else if ($match[7] === "close-quote") { |
453
|
|
|
// FIXME: do something else here |
454
|
|
|
$text .= $quotes[0][1]; |
455
|
|
|
} else if ($match[7] === "no-open-quote") { |
|
|
|
|
456
|
|
|
// FIXME: |
457
|
|
|
} else if ($match[7] === "no-close-quote") { |
|
|
|
|
458
|
|
|
// FIXME: |
459
|
|
|
} else if (mb_strpos($match[7], "attr(") === 0) { |
460
|
|
|
$i = mb_strpos($match[7], ")"); |
461
|
|
|
if ($i === false) { |
462
|
|
|
continue; |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
$attr = mb_substr($match[7], 5, $i - 5); |
466
|
|
|
if ($attr == "") { |
467
|
|
|
continue; |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
$text .= $this->_frame->get_parent()->get_node()->getAttribute($attr); |
|
|
|
|
471
|
|
|
} else { |
472
|
|
|
continue; |
473
|
|
|
} |
474
|
|
|
} |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
return $text; |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
/** |
481
|
|
|
* Sets the generated content of a generated frame |
482
|
|
|
*/ |
483
|
|
|
protected function _set_content() |
484
|
|
|
{ |
485
|
|
|
$frame = $this->_frame; |
486
|
|
|
$style = $frame->get_style(); |
487
|
|
|
|
488
|
|
|
// if the element was pushed to a new page use the saved counter value, otherwise use the CSS reset value |
489
|
|
|
if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") { |
|
|
|
|
490
|
|
|
$vars = preg_split('/\s+/', trim($reset), 2); |
491
|
|
|
$frame->reset_counter($vars[0], (isset($frame->_counters['__' . $vars[0]]) ? $frame->_counters['__' . $vars[0]] : (isset($vars[1]) ? $vars[1] : 0))); |
|
|
|
|
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
if ($style->counter_increment && ($increment = $style->counter_increment) !== "none") { |
|
|
|
|
495
|
|
|
$frame->increment_counters($increment); |
|
|
|
|
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
if ($style->content && $frame->get_node()->nodeName === "dompdf_generated") { |
|
|
|
|
499
|
|
|
$content = $this->_parse_content(); |
500
|
|
|
// add generated content to the font subset |
501
|
|
|
// FIXME: This is currently too late because the font subset has already been generated. |
502
|
|
|
// See notes in issue #750. |
503
|
|
|
if ($frame->get_dompdf()->getOptions()->getIsFontSubsettingEnabled() && $frame->get_dompdf()->get_canvas() instanceof CPDF) { |
|
|
|
|
504
|
|
|
$frame->get_dompdf()->get_canvas()->register_string_subset($style->font_family, $content); |
|
|
|
|
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
$node = $frame->get_node()->ownerDocument->createTextNode($content); |
508
|
|
|
|
509
|
|
|
$new_style = $style->get_stylesheet()->create_style(); |
510
|
|
|
$new_style->inherit($style); |
511
|
|
|
|
512
|
|
|
$new_frame = new Frame($node); |
513
|
|
|
$new_frame->set_style($new_style); |
514
|
|
|
|
515
|
|
|
Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root()); |
|
|
|
|
516
|
|
|
$frame->append_child($new_frame); |
517
|
|
|
} |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
/** |
521
|
|
|
* Determine current frame width based on contents |
522
|
|
|
* |
523
|
|
|
* @return float |
524
|
|
|
*/ |
525
|
|
|
public function calculate_auto_width() |
526
|
|
|
{ |
527
|
|
|
return $this->_frame->get_margin_width(); |
528
|
|
|
} |
529
|
|
|
} |
530
|
|
|
|
Let’s assume that you have a directory layout like this:
and let’s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: