PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.2
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.2
2.4.3 2.4.4 2.4.2 2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 All 60 releases
easy-invoice / vendor / sabberworm / php-css-parser / src / RuleSet / DeclarationBlock.php

DeclarationBlock.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.2, at vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php

872 lines 30.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Sabberworm\CSS\RuleSet;
4
5 use Sabberworm\CSS\CSSList\CSSList;
6 use Sabberworm\CSS\CSSList\KeyFrame;
7 use Sabberworm\CSS\OutputFormat;
8 use Sabberworm\CSS\Parsing\OutputException;
9 use Sabberworm\CSS\Parsing\ParserState;
10 use Sabberworm\CSS\Parsing\UnexpectedEOFException;
11 use Sabberworm\CSS\Parsing\UnexpectedTokenException;
12 use Sabberworm\CSS\Property\KeyframeSelector;
13 use Sabberworm\CSS\Property\Selector;
14 use Sabberworm\CSS\Rule\Rule;
15 use Sabberworm\CSS\Value\Color;
16 use Sabberworm\CSS\Value\RuleValueList;
17 use Sabberworm\CSS\Value\Size;
18 use Sabberworm\CSS\Value\URL;
19 use Sabberworm\CSS\Value\Value;
20
21 /**
22 * This class represents a `RuleSet` constrained by a `Selector`.
23 *
24 * It contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the
25 * matching elements.
26 *
27 * Declaration blocks usually appear directly inside a `Document` or another `CSSList` (mostly a `MediaQuery`).
28 */
29 class DeclarationBlock extends RuleSet
30 {
31 /**
32 * @var array<int, Selector|string>
33 */
34 private $aSelectors;
35
36 /**
37 * @param int $iLineNo
38 */
39 public function __construct($iLineNo = 0)
40 {
41 parent::__construct($iLineNo);
42 $this->aSelectors = [];
43 }
44
45 /**
46 * @param CSSList|null $oList
47 *
48 * @return DeclarationBlock|false
49 *
50 * @throws UnexpectedTokenException
51 * @throws UnexpectedEOFException
52 *
53 * @internal since V8.8.0
54 */
55 public static function parse(ParserState $oParserState, $oList = null)
56 {
57 $aComments = [];
58 $oResult = new DeclarationBlock($oParserState->currentLine());
59 try {
60 $aSelectorParts = [];
61 $sStringWrapperChar = false;
62 do {
63 $aSelectorParts[] = $oParserState->consume(1)
64 . $oParserState->consumeUntil(['{', '}', '\'', '"'], false, false, $aComments);
65 if (in_array($oParserState->peek(), ['\'', '"']) && substr(end($aSelectorParts), -1) != "\\") {
66 if ($sStringWrapperChar === false) {
67 $sStringWrapperChar = $oParserState->peek();
68 } elseif ($sStringWrapperChar == $oParserState->peek()) {
69 $sStringWrapperChar = false;
70 }
71 }
72 } while (!in_array($oParserState->peek(), ['{', '}']) || $sStringWrapperChar !== false);
73 $oResult->setSelectors(implode('', $aSelectorParts), $oList);
74 if ($oParserState->comes('{')) {
75 $oParserState->consume(1);
76 }
77 } catch (UnexpectedTokenException $e) {
78 if ($oParserState->getSettings()->bLenientParsing) {
79 if (!$oParserState->comes('}')) {
80 $oParserState->consumeUntil('}', false, true);
81 }
82 return false;
83 } else {
84 throw $e;
85 }
86 }
87 $oResult->setComments($aComments);
88 RuleSet::parseRuleSet($oParserState, $oResult);
89 return $oResult;
90 }
91
92 /**
93 * @param array<int, Selector|string>|string $mSelector
94 * @param CSSList|null $oList
95 *
96 * @throws UnexpectedTokenException
97 */
98 public function setSelectors($mSelector, $oList = null)
99 {
100 if (is_array($mSelector)) {
101 $this->aSelectors = $mSelector;
102 } else {
103 $this->aSelectors = explode(',', $mSelector);
104 }
105 foreach ($this->aSelectors as $iKey => $mSelector) {
106 if (!($mSelector instanceof Selector)) {
107 if ($oList === null || !($oList instanceof KeyFrame)) {
108 if (!Selector::isValid($mSelector)) {
109 throw new UnexpectedTokenException(
110 "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.",
111 $mSelector,
112 "custom"
113 );
114 }
115 $this->aSelectors[$iKey] = new Selector($mSelector);
116 } else {
117 if (!KeyframeSelector::isValid($mSelector)) {
118 throw new UnexpectedTokenException(
119 "Selector did not match '" . KeyframeSelector::SELECTOR_VALIDATION_RX . "'.",
120 $mSelector,
121 "custom"
122 );
123 }
124 $this->aSelectors[$iKey] = new KeyframeSelector($mSelector);
125 }
126 }
127 }
128 }
129
130 /**
131 * Remove one of the selectors of the block.
132 *
133 * @param Selector|string $mSelector
134 *
135 * @return bool
136 */
137 public function removeSelector($mSelector)
138 {
139 if ($mSelector instanceof Selector) {
140 $mSelector = $mSelector->getSelector();
141 }
142 foreach ($this->aSelectors as $iKey => $oSelector) {
143 if ($oSelector->getSelector() === $mSelector) {
144 unset($this->aSelectors[$iKey]);
145 return true;
146 }
147 }
148 return false;
149 }
150
151 /**
152 * @return array<int, Selector|string>
153 *
154 * @deprecated will be removed in version 9.0; use `getSelectors()` instead
155 */
156 public function getSelector()
157 {
158 return $this->getSelectors();
159 }
160
161 /**
162 * @param Selector|string $mSelector
163 * @param CSSList|null $oList
164 *
165 * @return void
166 *
167 * @deprecated will be removed in version 9.0; use `setSelectors()` instead
168 */
169 public function setSelector($mSelector, $oList = null)
170 {
171 $this->setSelectors($mSelector, $oList);
172 }
173
174 /**
175 * @return array<int, Selector|string>
176 */
177 public function getSelectors()
178 {
179 return $this->aSelectors;
180 }
181
182 /**
183 * Splits shorthand declarations (e.g. `margin` or `font`) into their constituent parts.
184 *
185 * @return void
186 *
187 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
188 */
189 public function expandShorthands()
190 {
191 // border must be expanded before dimensions
192 $this->expandBorderShorthand();
193 $this->expandDimensionsShorthand();
194 $this->expandFontShorthand();
195 $this->expandBackgroundShorthand();
196 $this->expandListStyleShorthand();
197 }
198
199 /**
200 * Creates shorthand declarations (e.g. `margin` or `font`) whenever possible.
201 *
202 * @return void
203 *
204 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
205 */
206 public function createShorthands()
207 {
208 $this->createBackgroundShorthand();
209 $this->createDimensionsShorthand();
210 // border must be shortened after dimensions
211 $this->createBorderShorthand();
212 $this->createFontShorthand();
213 $this->createListStyleShorthand();
214 }
215
216 /**
217 * Splits shorthand border declarations (e.g. `border: 1px red;`).
218 *
219 * Additional splitting happens in expandDimensionsShorthand.
220 *
221 * Multiple borders are not yet supported as of 3.
222 *
223 * @return void
224 *
225 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
226 */
227 public function expandBorderShorthand()
228 {
229 $aBorderRules = [
230 'border',
231 'border-left',
232 'border-right',
233 'border-top',
234 'border-bottom',
235 ];
236 $aBorderSizes = [
237 'thin',
238 'medium',
239 'thick',
240 ];
241 $aRules = $this->getRulesAssoc();
242 foreach ($aBorderRules as $sBorderRule) {
243 if (!isset($aRules[$sBorderRule])) {
244 continue;
245 }
246 $oRule = $aRules[$sBorderRule];
247 $mRuleValue = $oRule->getValue();
248 $aValues = [];
249 if (!$mRuleValue instanceof RuleValueList) {
250 $aValues[] = $mRuleValue;
251 } else {
252 $aValues = $mRuleValue->getListComponents();
253 }
254 foreach ($aValues as $mValue) {
255 if ($mValue instanceof Value) {
256 $mNewValue = clone $mValue;
257 } else {
258 $mNewValue = $mValue;
259 }
260 if ($mValue instanceof Size) {
261 $sNewRuleName = $sBorderRule . "-width";
262 } elseif ($mValue instanceof Color) {
263 $sNewRuleName = $sBorderRule . "-color";
264 } else {
265 if (in_array($mValue, $aBorderSizes)) {
266 $sNewRuleName = $sBorderRule . "-width";
267 } else {
268 $sNewRuleName = $sBorderRule . "-style";
269 }
270 }
271 $oNewRule = new Rule($sNewRuleName, $oRule->getLineNo(), $oRule->getColNo());
272 $oNewRule->setIsImportant($oRule->getIsImportant());
273 $oNewRule->addValue([$mNewValue]);
274 $this->addRule($oNewRule);
275 }
276 $this->removeRule($sBorderRule);
277 }
278 }
279
280 /**
281 * Splits shorthand dimensional declarations (e.g. `margin: 0px auto;`)
282 * into their constituent parts.
283 *
284 * Handles `margin`, `padding`, `border-color`, `border-style` and `border-width`.
285 *
286 * @return void
287 *
288 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
289 */
290 public function expandDimensionsShorthand()
291 {
292 $aExpansions = [
293 'margin' => 'margin-%s',
294 'padding' => 'padding-%s',
295 'border-color' => 'border-%s-color',
296 'border-style' => 'border-%s-style',
297 'border-width' => 'border-%s-width',
298 ];
299 $aRules = $this->getRulesAssoc();
300 foreach ($aExpansions as $sProperty => $sExpanded) {
301 if (!isset($aRules[$sProperty])) {
302 continue;
303 }
304 $oRule = $aRules[$sProperty];
305 $mRuleValue = $oRule->getValue();
306 $aValues = [];
307 if (!$mRuleValue instanceof RuleValueList) {
308 $aValues[] = $mRuleValue;
309 } else {
310 $aValues = $mRuleValue->getListComponents();
311 }
312 $top = $right = $bottom = $left = null;
313 switch (count($aValues)) {
314 case 1:
315 $top = $right = $bottom = $left = $aValues[0];
316 break;
317 case 2:
318 $top = $bottom = $aValues[0];
319 $left = $right = $aValues[1];
320 break;
321 case 3:
322 $top = $aValues[0];
323 $left = $right = $aValues[1];
324 $bottom = $aValues[2];
325 break;
326 case 4:
327 $top = $aValues[0];
328 $right = $aValues[1];
329 $bottom = $aValues[2];
330 $left = $aValues[3];
331 break;
332 }
333 foreach (['top', 'right', 'bottom', 'left'] as $sPosition) {
334 $oNewRule = new Rule(sprintf($sExpanded, $sPosition), $oRule->getLineNo(), $oRule->getColNo());
335 $oNewRule->setIsImportant($oRule->getIsImportant());
336 $oNewRule->addValue(${$sPosition});
337 $this->addRule($oNewRule);
338 }
339 $this->removeRule($sProperty);
340 }
341 }
342
343 /**
344 * Converts shorthand font declarations
345 * (e.g. `font: 300 italic 11px/14px verdana, helvetica, sans-serif;`)
346 * into their constituent parts.
347 *
348 * @return void
349 *
350 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
351 */
352 public function expandFontShorthand()
353 {
354 $aRules = $this->getRulesAssoc();
355 if (!isset($aRules['font'])) {
356 return;
357 }
358 $oRule = $aRules['font'];
359 // reset properties to 'normal' per http://www.w3.org/TR/21/fonts.html#font-shorthand
360 $aFontProperties = [
361 'font-style' => 'normal',
362 'font-variant' => 'normal',
363 'font-weight' => 'normal',
364 'font-size' => 'normal',
365 'line-height' => 'normal',
366 ];
367 $mRuleValue = $oRule->getValue();
368 $aValues = [];
369 if (!$mRuleValue instanceof RuleValueList) {
370 $aValues[] = $mRuleValue;
371 } else {
372 $aValues = $mRuleValue->getListComponents();
373 }
374 foreach ($aValues as $mValue) {
375 if (!$mValue instanceof Value) {
376 $mValue = mb_strtolower($mValue);
377 }
378 if (in_array($mValue, ['normal', 'inherit'])) {
379 foreach (['font-style', 'font-weight', 'font-variant'] as $sProperty) {
380 if (!isset($aFontProperties[$sProperty])) {
381 $aFontProperties[$sProperty] = $mValue;
382 }
383 }
384 } elseif (in_array($mValue, ['italic', 'oblique'])) {
385 $aFontProperties['font-style'] = $mValue;
386 } elseif ($mValue == 'small-caps') {
387 $aFontProperties['font-variant'] = $mValue;
388 } elseif (
389 in_array($mValue, ['bold', 'bolder', 'lighter'])
390 || ($mValue instanceof Size
391 && in_array($mValue->getSize(), range(100, 900, 100)))
392 ) {
393 $aFontProperties['font-weight'] = $mValue;
394 } elseif ($mValue instanceof RuleValueList && $mValue->getListSeparator() == '/') {
395 list($oSize, $oHeight) = $mValue->getListComponents();
396 $aFontProperties['font-size'] = $oSize;
397 $aFontProperties['line-height'] = $oHeight;
398 } elseif ($mValue instanceof Size && $mValue->getUnit() !== null) {
399 $aFontProperties['font-size'] = $mValue;
400 } else {
401 $aFontProperties['font-family'] = $mValue;
402 }
403 }
404 foreach ($aFontProperties as $sProperty => $mValue) {
405 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
406 $oNewRule->addValue($mValue);
407 $oNewRule->setIsImportant($oRule->getIsImportant());
408 $this->addRule($oNewRule);
409 }
410 $this->removeRule('font');
411 }
412
413 /**
414 * Converts shorthand background declarations
415 * (e.g. `background: url("chess.png") gray 50% repeat fixed;`)
416 * into their constituent parts.
417 *
418 * @see http://www.w3.org/TR/21/colors.html#propdef-background
419 *
420 * @return void
421 *
422 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
423 */
424 public function expandBackgroundShorthand()
425 {
426 $aRules = $this->getRulesAssoc();
427 if (!isset($aRules['background'])) {
428 return;
429 }
430 $oRule = $aRules['background'];
431 $aBgProperties = [
432 'background-color' => ['transparent'],
433 'background-image' => ['none'],
434 'background-repeat' => ['repeat'],
435 'background-attachment' => ['scroll'],
436 'background-position' => [
437 new Size(0, '%', false, $this->getLineNo()),
438 new Size(0, '%', false, $this->getLineNo()),
439 ],
440 ];
441 $mRuleValue = $oRule->getValue();
442 $aValues = [];
443 if (!$mRuleValue instanceof RuleValueList) {
444 $aValues[] = $mRuleValue;
445 } else {
446 $aValues = $mRuleValue->getListComponents();
447 }
448 if (count($aValues) == 1 && $aValues[0] == 'inherit') {
449 foreach ($aBgProperties as $sProperty => $mValue) {
450 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
451 $oNewRule->addValue('inherit');
452 $oNewRule->setIsImportant($oRule->getIsImportant());
453 $this->addRule($oNewRule);
454 }
455 $this->removeRule('background');
456 return;
457 }
458 $iNumBgPos = 0;
459 foreach ($aValues as $mValue) {
460 if (!$mValue instanceof Value) {
461 $mValue = mb_strtolower($mValue);
462 }
463 if ($mValue instanceof URL) {
464 $aBgProperties['background-image'] = $mValue;
465 } elseif ($mValue instanceof Color) {
466 $aBgProperties['background-color'] = $mValue;
467 } elseif (in_array($mValue, ['scroll', 'fixed'])) {
468 $aBgProperties['background-attachment'] = $mValue;
469 } elseif (in_array($mValue, ['repeat', 'no-repeat', 'repeat-x', 'repeat-y'])) {
470 $aBgProperties['background-repeat'] = $mValue;
471 } elseif (
472 in_array($mValue, ['left', 'center', 'right', 'top', 'bottom'])
473 || $mValue instanceof Size
474 ) {
475 if ($iNumBgPos == 0) {
476 $aBgProperties['background-position'][0] = $mValue;
477 $aBgProperties['background-position'][1] = 'center';
478 } else {
479 $aBgProperties['background-position'][$iNumBgPos] = $mValue;
480 }
481 $iNumBgPos++;
482 }
483 }
484 foreach ($aBgProperties as $sProperty => $mValue) {
485 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
486 $oNewRule->setIsImportant($oRule->getIsImportant());
487 $oNewRule->addValue($mValue);
488 $this->addRule($oNewRule);
489 }
490 $this->removeRule('background');
491 }
492
493 /**
494 * @return void
495 *
496 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
497 */
498 public function expandListStyleShorthand()
499 {
500 $aListProperties = [
501 'list-style-type' => 'disc',
502 'list-style-position' => 'outside',
503 'list-style-image' => 'none',
504 ];
505 $aListStyleTypes = [
506 'none',
507 'disc',
508 'circle',
509 'square',
510 'decimal-leading-zero',
511 'decimal',
512 'lower-roman',
513 'upper-roman',
514 'lower-greek',
515 'lower-alpha',
516 'lower-latin',
517 'upper-alpha',
518 'upper-latin',
519 'hebrew',
520 'armenian',
521 'georgian',
522 'cjk-ideographic',
523 'hiragana',
524 'hira-gana-iroha',
525 'katakana-iroha',
526 'katakana',
527 ];
528 $aListStylePositions = [
529 'inside',
530 'outside',
531 ];
532 $aRules = $this->getRulesAssoc();
533 if (!isset($aRules['list-style'])) {
534 return;
535 }
536 $oRule = $aRules['list-style'];
537 $mRuleValue = $oRule->getValue();
538 $aValues = [];
539 if (!$mRuleValue instanceof RuleValueList) {
540 $aValues[] = $mRuleValue;
541 } else {
542 $aValues = $mRuleValue->getListComponents();
543 }
544 if (count($aValues) == 1 && $aValues[0] == 'inherit') {
545 foreach ($aListProperties as $sProperty => $mValue) {
546 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
547 $oNewRule->addValue('inherit');
548 $oNewRule->setIsImportant($oRule->getIsImportant());
549 $this->addRule($oNewRule);
550 }
551 $this->removeRule('list-style');
552 return;
553 }
554 foreach ($aValues as $mValue) {
555 if (!$mValue instanceof Value) {
556 $mValue = mb_strtolower($mValue);
557 }
558 if ($mValue instanceof Url) {
559 $aListProperties['list-style-image'] = $mValue;
560 } elseif (in_array($mValue, $aListStyleTypes)) {
561 $aListProperties['list-style-types'] = $mValue;
562 } elseif (in_array($mValue, $aListStylePositions)) {
563 $aListProperties['list-style-position'] = $mValue;
564 }
565 }
566 foreach ($aListProperties as $sProperty => $mValue) {
567 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
568 $oNewRule->setIsImportant($oRule->getIsImportant());
569 $oNewRule->addValue($mValue);
570 $this->addRule($oNewRule);
571 }
572 $this->removeRule('list-style');
573 }
574
575 /**
576 * @param array<array-key, string> $aProperties
577 * @param string $sShorthand
578 *
579 * @return void
580 *
581 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
582 */
583 public function createShorthandProperties(array $aProperties, $sShorthand)
584 {
585 $aRules = $this->getRulesAssoc();
586 $oRule = null;
587 $aNewValues = [];
588 foreach ($aProperties as $sProperty) {
589 if (!isset($aRules[$sProperty])) {
590 continue;
591 }
592 $oRule = $aRules[$sProperty];
593 if (!$oRule->getIsImportant()) {
594 $mRuleValue = $oRule->getValue();
595 $aValues = [];
596 if (!$mRuleValue instanceof RuleValueList) {
597 $aValues[] = $mRuleValue;
598 } else {
599 $aValues = $mRuleValue->getListComponents();
600 }
601 foreach ($aValues as $mValue) {
602 $aNewValues[] = $mValue;
603 }
604 $this->removeRule($sProperty);
605 }
606 }
607 if ($aNewValues !== [] && $oRule instanceof Rule) {
608 $oNewRule = new Rule($sShorthand, $oRule->getLineNo(), $oRule->getColNo());
609 foreach ($aNewValues as $mValue) {
610 $oNewRule->addValue($mValue);
611 }
612 $this->addRule($oNewRule);
613 }
614 }
615
616 /**
617 * @return void
618 *
619 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
620 */
621 public function createBackgroundShorthand()
622 {
623 $aProperties = [
624 'background-color',
625 'background-image',
626 'background-repeat',
627 'background-position',
628 'background-attachment',
629 ];
630 $this->createShorthandProperties($aProperties, 'background');
631 }
632
633 /**
634 * @return void
635 *
636 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
637 */
638 public function createListStyleShorthand()
639 {
640 $aProperties = [
641 'list-style-type',
642 'list-style-position',
643 'list-style-image',
644 ];
645 $this->createShorthandProperties($aProperties, 'list-style');
646 }
647
648 /**
649 * Combines `border-color`, `border-style` and `border-width` into `border`.
650 *
651 * Should be run after `create_dimensions_shorthand`!
652 *
653 * @return void
654 *
655 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
656 */
657 public function createBorderShorthand()
658 {
659 $aProperties = [
660 'border-width',
661 'border-style',
662 'border-color',
663 ];
664 $this->createShorthandProperties($aProperties, 'border');
665 }
666
667 /**
668 * Looks for long format CSS dimensional properties
669 * (margin, padding, border-color, border-style and border-width)
670 * and converts them into shorthand CSS properties.
671 *
672 * @return void
673 *
674 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
675 */
676 public function createDimensionsShorthand()
677 {
678 $aPositions = ['top', 'right', 'bottom', 'left'];
679 $aExpansions = [
680 'margin' => 'margin-%s',
681 'padding' => 'padding-%s',
682 'border-color' => 'border-%s-color',
683 'border-style' => 'border-%s-style',
684 'border-width' => 'border-%s-width',
685 ];
686 $aRules = $this->getRulesAssoc();
687 foreach ($aExpansions as $sProperty => $sExpanded) {
688 $aFoldable = [];
689 foreach ($aRules as $sRuleName => $oRule) {
690 foreach ($aPositions as $sPosition) {
691 if ($sRuleName == sprintf($sExpanded, $sPosition)) {
692 $aFoldable[$sRuleName] = $oRule;
693 }
694 }
695 }
696 // All four dimensions must be present
697 if (count($aFoldable) == 4) {
698 $aValues = [];
699 foreach ($aPositions as $sPosition) {
700 $oRule = $aRules[sprintf($sExpanded, $sPosition)];
701 $mRuleValue = $oRule->getValue();
702 $aRuleValues = [];
703 if (!$mRuleValue instanceof RuleValueList) {
704 $aRuleValues[] = $mRuleValue;
705 } else {
706 $aRuleValues = $mRuleValue->getListComponents();
707 }
708 $aValues[$sPosition] = $aRuleValues;
709 }
710 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
711 if ((string)$aValues['left'][0] == (string)$aValues['right'][0]) {
712 if ((string)$aValues['top'][0] == (string)$aValues['bottom'][0]) {
713 if ((string)$aValues['top'][0] == (string)$aValues['left'][0]) {
714 // All 4 sides are equal
715 $oNewRule->addValue($aValues['top']);
716 } else {
717 // Top and bottom are equal, left and right are equal
718 $oNewRule->addValue($aValues['top']);
719 $oNewRule->addValue($aValues['left']);
720 }
721 } else {
722 // Only left and right are equal
723 $oNewRule->addValue($aValues['top']);
724 $oNewRule->addValue($aValues['left']);
725 $oNewRule->addValue($aValues['bottom']);
726 }
727 } else {
728 // No sides are equal
729 $oNewRule->addValue($aValues['top']);
730 $oNewRule->addValue($aValues['left']);
731 $oNewRule->addValue($aValues['bottom']);
732 $oNewRule->addValue($aValues['right']);
733 }
734 $this->addRule($oNewRule);
735 foreach ($aPositions as $sPosition) {
736 $this->removeRule(sprintf($sExpanded, $sPosition));
737 }
738 }
739 }
740 }
741
742 /**
743 * Looks for long format CSS font properties (e.g. `font-weight`) and
744 * tries to convert them into a shorthand CSS `font` property.
745 *
746 * At least `font-size` AND `font-family` must be present in order to create a shorthand declaration.
747 *
748 * @return void
749 *
750 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
751 */
752 public function createFontShorthand()
753 {
754 $aFontProperties = [
755 'font-style',
756 'font-variant',
757 'font-weight',
758 'font-size',
759 'line-height',
760 'font-family',
761 ];
762 $aRules = $this->getRulesAssoc();
763 if (!isset($aRules['font-size']) || !isset($aRules['font-family'])) {
764 return;
765 }
766 $oOldRule = isset($aRules['font-size']) ? $aRules['font-size'] : $aRules['font-family'];
767 $oNewRule = new Rule('font', $oOldRule->getLineNo(), $oOldRule->getColNo());
768 unset($oOldRule);
769 foreach (['font-style', 'font-variant', 'font-weight'] as $sProperty) {
770 if (isset($aRules[$sProperty])) {
771 $oRule = $aRules[$sProperty];
772 $mRuleValue = $oRule->getValue();
773 $aValues = [];
774 if (!$mRuleValue instanceof RuleValueList) {
775 $aValues[] = $mRuleValue;
776 } else {
777 $aValues = $mRuleValue->getListComponents();
778 }
779 if ($aValues[0] !== 'normal') {
780 $oNewRule->addValue($aValues[0]);
781 }
782 }
783 }
784 // Get the font-size value
785 $oRule = $aRules['font-size'];
786 $mRuleValue = $oRule->getValue();
787 $aFSValues = [];
788 if (!$mRuleValue instanceof RuleValueList) {
789 $aFSValues[] = $mRuleValue;
790 } else {
791 $aFSValues = $mRuleValue->getListComponents();
792 }
793 // But wait to know if we have line-height to add it
794 if (isset($aRules['line-height'])) {
795 $oRule = $aRules['line-height'];
796 $mRuleValue = $oRule->getValue();
797 $aLHValues = [];
798 if (!$mRuleValue instanceof RuleValueList) {
799 $aLHValues[] = $mRuleValue;
800 } else {
801 $aLHValues = $mRuleValue->getListComponents();
802 }
803 if ($aLHValues[0] !== 'normal') {
804 $val = new RuleValueList('/', $this->getLineNo());
805 $val->addListComponent($aFSValues[0]);
806 $val->addListComponent($aLHValues[0]);
807 $oNewRule->addValue($val);
808 }
809 } else {
810 $oNewRule->addValue($aFSValues[0]);
811 }
812 $oRule = $aRules['font-family'];
813 $mRuleValue = $oRule->getValue();
814 $aFFValues = [];
815 if (!$mRuleValue instanceof RuleValueList) {
816 $aFFValues[] = $mRuleValue;
817 } else {
818 $aFFValues = $mRuleValue->getListComponents();
819 }
820 $oFFValue = new RuleValueList(',', $this->getLineNo());
821 $oFFValue->setListComponents($aFFValues);
822 $oNewRule->addValue($oFFValue);
823
824 $this->addRule($oNewRule);
825 foreach ($aFontProperties as $sProperty) {
826 $this->removeRule($sProperty);
827 }
828 }
829
830 /**
831 * @return string
832 *
833 * @throws OutputException
834 *
835 * @deprecated in V8.8.0, will be removed in V9.0.0. Use `render` instead.
836 */
837 public function __toString()
838 {
839 return $this->render(new OutputFormat());
840 }
841
842 /**
843 * @param OutputFormat|null $oOutputFormat
844 *
845 * @return string
846 *
847 * @throws OutputException
848 */
849 public function render($oOutputFormat)
850 {
851 $sResult = $oOutputFormat->comments($this);
852 if (count($this->aSelectors) === 0) {
853 // If all the selectors have been removed, this declaration block becomes invalid
854 throw new OutputException(
855 'Attempt to print declaration block with missing selector',
856 $this->getLineNumber()
857 );
858 }
859 $sResult .= $oOutputFormat->sBeforeDeclarationBlock;
860 $sResult .= $oOutputFormat->implode(
861 $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(),
862 $this->aSelectors
863 );
864 $sResult .= $oOutputFormat->sAfterDeclarationBlockSelectors;
865 $sResult .= $oOutputFormat->spaceBeforeOpeningBrace() . '{';
866 $sResult .= $this->renderRules($oOutputFormat);
867 $sResult .= '}';
868 $sResult .= $oOutputFormat->sAfterDeclarationBlock;
869 return $sResult;
870 }
871 }
872