Commit | Line | Data |
---|---|---|
fbe18cc0 FM |
1 | <?php |
2 | ||
3 | namespace Sabberworm\CSS\RuleSet; | |
4 | ||
376eb156 MM |
5 | use Sabberworm\CSS\Parsing\ParserState; |
6 | use Sabberworm\CSS\Parsing\OutputException; | |
fbe18cc0 FM |
7 | use Sabberworm\CSS\Property\Selector; |
8 | use Sabberworm\CSS\Rule\Rule; | |
9 | use Sabberworm\CSS\Value\RuleValueList; | |
10 | use Sabberworm\CSS\Value\Value; | |
11 | use Sabberworm\CSS\Value\Size; | |
12 | use Sabberworm\CSS\Value\Color; | |
13 | use Sabberworm\CSS\Value\URL; | |
fbe18cc0 FM |
14 | |
15 | /** | |
16 | * Declaration blocks are the parts of a css file which denote the rules belonging to a selector. | |
17 | * Declaration blocks usually appear directly inside a Document or another CSSList (mostly a MediaQuery). | |
18 | */ | |
19 | class DeclarationBlock extends RuleSet { | |
20 | ||
21 | private $aSelectors; | |
22 | ||
23 | public function __construct($iLineNo = 0) { | |
24 | parent::__construct($iLineNo); | |
25 | $this->aSelectors = array(); | |
26 | } | |
27 | ||
376eb156 MM |
28 | public static function parse(ParserState $oParserState) { |
29 | $aComments = array(); | |
30 | $oResult = new DeclarationBlock($oParserState->currentLine()); | |
31 | $oResult->setSelector($oParserState->consumeUntil('{', false, true, $aComments)); | |
32 | $oResult->setComments($aComments); | |
33 | RuleSet::parseRuleSet($oParserState, $oResult); | |
34 | return $oResult; | |
35 | } | |
36 | ||
37 | ||
fbe18cc0 FM |
38 | public function setSelectors($mSelector) { |
39 | if (is_array($mSelector)) { | |
40 | $this->aSelectors = $mSelector; | |
41 | } else { | |
42 | $this->aSelectors = explode(',', $mSelector); | |
43 | } | |
44 | foreach ($this->aSelectors as $iKey => $mSelector) { | |
45 | if (!($mSelector instanceof Selector)) { | |
46 | $this->aSelectors[$iKey] = new Selector($mSelector); | |
47 | } | |
48 | } | |
49 | } | |
50 | ||
51 | // remove one of the selector of the block | |
52 | public function removeSelector($mSelector) { | |
53 | if($mSelector instanceof Selector) { | |
54 | $mSelector = $mSelector->getSelector(); | |
55 | } | |
56 | foreach($this->aSelectors as $iKey => $oSelector) { | |
57 | if($oSelector->getSelector() === $mSelector) { | |
58 | unset($this->aSelectors[$iKey]); | |
59 | return true; | |
60 | } | |
61 | } | |
62 | return false; | |
63 | } | |
64 | ||
65 | /** | |
66 | * @deprecated use getSelectors() | |
67 | */ | |
68 | public function getSelector() { | |
69 | return $this->getSelectors(); | |
70 | } | |
71 | ||
72 | /** | |
73 | * @deprecated use setSelectors() | |
74 | */ | |
75 | public function setSelector($mSelector) { | |
76 | $this->setSelectors($mSelector); | |
77 | } | |
78 | ||
376eb156 MM |
79 | /** |
80 | * Get selectors. | |
81 | * | |
82 | * @return Selector[] Selectors. | |
83 | */ | |
fbe18cc0 FM |
84 | public function getSelectors() { |
85 | return $this->aSelectors; | |
86 | } | |
87 | ||
88 | /** | |
89 | * Split shorthand declarations (e.g. +margin+ or +font+) into their constituent parts. | |
90 | * */ | |
91 | public function expandShorthands() { | |
92 | // border must be expanded before dimensions | |
93 | $this->expandBorderShorthand(); | |
94 | $this->expandDimensionsShorthand(); | |
95 | $this->expandFontShorthand(); | |
96 | $this->expandBackgroundShorthand(); | |
97 | $this->expandListStyleShorthand(); | |
98 | } | |
99 | ||
100 | /** | |
101 | * Create shorthand declarations (e.g. +margin+ or +font+) whenever possible. | |
102 | * */ | |
103 | public function createShorthands() { | |
104 | $this->createBackgroundShorthand(); | |
105 | $this->createDimensionsShorthand(); | |
106 | // border must be shortened after dimensions | |
107 | $this->createBorderShorthand(); | |
108 | $this->createFontShorthand(); | |
109 | $this->createListStyleShorthand(); | |
110 | } | |
111 | ||
112 | /** | |
113 | * Split shorthand border declarations (e.g. <tt>border: 1px red;</tt>) | |
114 | * Additional splitting happens in expandDimensionsShorthand | |
115 | * Multiple borders are not yet supported as of 3 | |
116 | * */ | |
117 | public function expandBorderShorthand() { | |
118 | $aBorderRules = array( | |
119 | 'border', 'border-left', 'border-right', 'border-top', 'border-bottom' | |
120 | ); | |
121 | $aBorderSizes = array( | |
122 | 'thin', 'medium', 'thick' | |
123 | ); | |
124 | $aRules = $this->getRulesAssoc(); | |
125 | foreach ($aBorderRules as $sBorderRule) { | |
126 | if (!isset($aRules[$sBorderRule])) | |
127 | continue; | |
128 | $oRule = $aRules[$sBorderRule]; | |
129 | $mRuleValue = $oRule->getValue(); | |
130 | $aValues = array(); | |
131 | if (!$mRuleValue instanceof RuleValueList) { | |
132 | $aValues[] = $mRuleValue; | |
133 | } else { | |
134 | $aValues = $mRuleValue->getListComponents(); | |
135 | } | |
136 | foreach ($aValues as $mValue) { | |
137 | if ($mValue instanceof Value) { | |
138 | $mNewValue = clone $mValue; | |
139 | } else { | |
140 | $mNewValue = $mValue; | |
141 | } | |
142 | if ($mValue instanceof Size) { | |
143 | $sNewRuleName = $sBorderRule . "-width"; | |
144 | } else if ($mValue instanceof Color) { | |
145 | $sNewRuleName = $sBorderRule . "-color"; | |
146 | } else { | |
147 | if (in_array($mValue, $aBorderSizes)) { | |
148 | $sNewRuleName = $sBorderRule . "-width"; | |
149 | } else/* if(in_array($mValue, $aBorderStyles)) */ { | |
150 | $sNewRuleName = $sBorderRule . "-style"; | |
151 | } | |
152 | } | |
153 | $oNewRule = new Rule($sNewRuleName, $this->iLineNo); | |
154 | $oNewRule->setIsImportant($oRule->getIsImportant()); | |
155 | $oNewRule->addValue(array($mNewValue)); | |
156 | $this->addRule($oNewRule); | |
157 | } | |
158 | $this->removeRule($sBorderRule); | |
159 | } | |
160 | } | |
161 | ||
162 | /** | |
163 | * Split shorthand dimensional declarations (e.g. <tt>margin: 0px auto;</tt>) | |
164 | * into their constituent parts. | |
165 | * Handles margin, padding, border-color, border-style and border-width. | |
166 | * */ | |
167 | public function expandDimensionsShorthand() { | |
168 | $aExpansions = array( | |
169 | 'margin' => 'margin-%s', | |
170 | 'padding' => 'padding-%s', | |
171 | 'border-color' => 'border-%s-color', | |
172 | 'border-style' => 'border-%s-style', | |
173 | 'border-width' => 'border-%s-width' | |
174 | ); | |
175 | $aRules = $this->getRulesAssoc(); | |
176 | foreach ($aExpansions as $sProperty => $sExpanded) { | |
177 | if (!isset($aRules[$sProperty])) | |
178 | continue; | |
179 | $oRule = $aRules[$sProperty]; | |
180 | $mRuleValue = $oRule->getValue(); | |
181 | $aValues = array(); | |
182 | if (!$mRuleValue instanceof RuleValueList) { | |
183 | $aValues[] = $mRuleValue; | |
184 | } else { | |
185 | $aValues = $mRuleValue->getListComponents(); | |
186 | } | |
187 | $top = $right = $bottom = $left = null; | |
188 | switch (count($aValues)) { | |
189 | case 1: | |
190 | $top = $right = $bottom = $left = $aValues[0]; | |
191 | break; | |
192 | case 2: | |
193 | $top = $bottom = $aValues[0]; | |
194 | $left = $right = $aValues[1]; | |
195 | break; | |
196 | case 3: | |
197 | $top = $aValues[0]; | |
198 | $left = $right = $aValues[1]; | |
199 | $bottom = $aValues[2]; | |
200 | break; | |
201 | case 4: | |
202 | $top = $aValues[0]; | |
203 | $right = $aValues[1]; | |
204 | $bottom = $aValues[2]; | |
205 | $left = $aValues[3]; | |
206 | break; | |
207 | } | |
208 | foreach (array('top', 'right', 'bottom', 'left') as $sPosition) { | |
209 | $oNewRule = new Rule(sprintf($sExpanded, $sPosition), $this->iLineNo); | |
210 | $oNewRule->setIsImportant($oRule->getIsImportant()); | |
211 | $oNewRule->addValue(${$sPosition}); | |
212 | $this->addRule($oNewRule); | |
213 | } | |
214 | $this->removeRule($sProperty); | |
215 | } | |
216 | } | |
217 | ||
218 | /** | |
219 | * Convert shorthand font declarations | |
220 | * (e.g. <tt>font: 300 italic 11px/14px verdana, helvetica, sans-serif;</tt>) | |
221 | * into their constituent parts. | |
222 | * */ | |
223 | public function expandFontShorthand() { | |
224 | $aRules = $this->getRulesAssoc(); | |
225 | if (!isset($aRules['font'])) | |
226 | return; | |
227 | $oRule = $aRules['font']; | |
228 | // reset properties to 'normal' per http://www.w3.org/TR/21/fonts.html#font-shorthand | |
229 | $aFontProperties = array( | |
230 | 'font-style' => 'normal', | |
231 | 'font-variant' => 'normal', | |
232 | 'font-weight' => 'normal', | |
233 | 'font-size' => 'normal', | |
234 | 'line-height' => 'normal' | |
235 | ); | |
236 | $mRuleValue = $oRule->getValue(); | |
237 | $aValues = array(); | |
238 | if (!$mRuleValue instanceof RuleValueList) { | |
239 | $aValues[] = $mRuleValue; | |
240 | } else { | |
241 | $aValues = $mRuleValue->getListComponents(); | |
242 | } | |
243 | foreach ($aValues as $mValue) { | |
244 | if (!$mValue instanceof Value) { | |
245 | $mValue = mb_strtolower($mValue); | |
246 | } | |
247 | if (in_array($mValue, array('normal', 'inherit'))) { | |
248 | foreach (array('font-style', 'font-weight', 'font-variant') as $sProperty) { | |
249 | if (!isset($aFontProperties[$sProperty])) { | |
250 | $aFontProperties[$sProperty] = $mValue; | |
251 | } | |
252 | } | |
253 | } else if (in_array($mValue, array('italic', 'oblique'))) { | |
254 | $aFontProperties['font-style'] = $mValue; | |
255 | } else if ($mValue == 'small-caps') { | |
256 | $aFontProperties['font-variant'] = $mValue; | |
257 | } else if ( | |
258 | in_array($mValue, array('bold', 'bolder', 'lighter')) | |
259 | || ($mValue instanceof Size | |
260 | && in_array($mValue->getSize(), range(100, 900, 100))) | |
261 | ) { | |
262 | $aFontProperties['font-weight'] = $mValue; | |
263 | } else if ($mValue instanceof RuleValueList && $mValue->getListSeparator() == '/') { | |
264 | list($oSize, $oHeight) = $mValue->getListComponents(); | |
265 | $aFontProperties['font-size'] = $oSize; | |
266 | $aFontProperties['line-height'] = $oHeight; | |
267 | } else if ($mValue instanceof Size && $mValue->getUnit() !== null) { | |
268 | $aFontProperties['font-size'] = $mValue; | |
269 | } else { | |
270 | $aFontProperties['font-family'] = $mValue; | |
271 | } | |
272 | } | |
273 | foreach ($aFontProperties as $sProperty => $mValue) { | |
274 | $oNewRule = new Rule($sProperty, $this->iLineNo); | |
275 | $oNewRule->addValue($mValue); | |
276 | $oNewRule->setIsImportant($oRule->getIsImportant()); | |
277 | $this->addRule($oNewRule); | |
278 | } | |
279 | $this->removeRule('font'); | |
280 | } | |
281 | ||
282 | /* | |
283 | * Convert shorthand background declarations | |
284 | * (e.g. <tt>background: url("chess.png") gray 50% repeat fixed;</tt>) | |
285 | * into their constituent parts. | |
286 | * @see http://www.w3.org/TR/21/colors.html#propdef-background | |
287 | * */ | |
288 | ||
289 | public function expandBackgroundShorthand() { | |
290 | $aRules = $this->getRulesAssoc(); | |
291 | if (!isset($aRules['background'])) | |
292 | return; | |
293 | $oRule = $aRules['background']; | |
294 | $aBgProperties = array( | |
295 | 'background-color' => array('transparent'), 'background-image' => array('none'), | |
296 | 'background-repeat' => array('repeat'), 'background-attachment' => array('scroll'), | |
297 | 'background-position' => array(new Size(0, '%', null, false, $this->iLineNo), new Size(0, '%', null, false, $this->iLineNo)) | |
298 | ); | |
299 | $mRuleValue = $oRule->getValue(); | |
300 | $aValues = array(); | |
301 | if (!$mRuleValue instanceof RuleValueList) { | |
302 | $aValues[] = $mRuleValue; | |
303 | } else { | |
304 | $aValues = $mRuleValue->getListComponents(); | |
305 | } | |
306 | if (count($aValues) == 1 && $aValues[0] == 'inherit') { | |
307 | foreach ($aBgProperties as $sProperty => $mValue) { | |
308 | $oNewRule = new Rule($sProperty, $this->iLineNo); | |
309 | $oNewRule->addValue('inherit'); | |
310 | $oNewRule->setIsImportant($oRule->getIsImportant()); | |
311 | $this->addRule($oNewRule); | |
312 | } | |
313 | $this->removeRule('background'); | |
314 | return; | |
315 | } | |
316 | $iNumBgPos = 0; | |
317 | foreach ($aValues as $mValue) { | |
318 | if (!$mValue instanceof Value) { | |
319 | $mValue = mb_strtolower($mValue); | |
320 | } | |
321 | if ($mValue instanceof URL) { | |
322 | $aBgProperties['background-image'] = $mValue; | |
323 | } else if ($mValue instanceof Color) { | |
324 | $aBgProperties['background-color'] = $mValue; | |
325 | } else if (in_array($mValue, array('scroll', 'fixed'))) { | |
326 | $aBgProperties['background-attachment'] = $mValue; | |
327 | } else if (in_array($mValue, array('repeat', 'no-repeat', 'repeat-x', 'repeat-y'))) { | |
328 | $aBgProperties['background-repeat'] = $mValue; | |
329 | } else if (in_array($mValue, array('left', 'center', 'right', 'top', 'bottom')) | |
330 | || $mValue instanceof Size | |
331 | ) { | |
332 | if ($iNumBgPos == 0) { | |
333 | $aBgProperties['background-position'][0] = $mValue; | |
334 | $aBgProperties['background-position'][1] = 'center'; | |
335 | } else { | |
336 | $aBgProperties['background-position'][$iNumBgPos] = $mValue; | |
337 | } | |
338 | $iNumBgPos++; | |
339 | } | |
340 | } | |
341 | foreach ($aBgProperties as $sProperty => $mValue) { | |
342 | $oNewRule = new Rule($sProperty, $this->iLineNo); | |
343 | $oNewRule->setIsImportant($oRule->getIsImportant()); | |
344 | $oNewRule->addValue($mValue); | |
345 | $this->addRule($oNewRule); | |
346 | } | |
347 | $this->removeRule('background'); | |
348 | } | |
349 | ||
350 | public function expandListStyleShorthand() { | |
351 | $aListProperties = array( | |
352 | 'list-style-type' => 'disc', | |
353 | 'list-style-position' => 'outside', | |
354 | 'list-style-image' => 'none' | |
355 | ); | |
356 | $aListStyleTypes = array( | |
357 | 'none', 'disc', 'circle', 'square', 'decimal-leading-zero', 'decimal', | |
358 | 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha', 'lower-latin', | |
359 | 'upper-alpha', 'upper-latin', 'hebrew', 'armenian', 'georgian', 'cjk-ideographic', | |
360 | 'hiragana', 'hira-gana-iroha', 'katakana-iroha', 'katakana' | |
361 | ); | |
362 | $aListStylePositions = array( | |
363 | 'inside', 'outside' | |
364 | ); | |
365 | $aRules = $this->getRulesAssoc(); | |
366 | if (!isset($aRules['list-style'])) | |
367 | return; | |
368 | $oRule = $aRules['list-style']; | |
369 | $mRuleValue = $oRule->getValue(); | |
370 | $aValues = array(); | |
371 | if (!$mRuleValue instanceof RuleValueList) { | |
372 | $aValues[] = $mRuleValue; | |
373 | } else { | |
374 | $aValues = $mRuleValue->getListComponents(); | |
375 | } | |
376 | if (count($aValues) == 1 && $aValues[0] == 'inherit') { | |
377 | foreach ($aListProperties as $sProperty => $mValue) { | |
378 | $oNewRule = new Rule($sProperty, $this->iLineNo); | |
379 | $oNewRule->addValue('inherit'); | |
380 | $oNewRule->setIsImportant($oRule->getIsImportant()); | |
381 | $this->addRule($oNewRule); | |
382 | } | |
383 | $this->removeRule('list-style'); | |
384 | return; | |
385 | } | |
386 | foreach ($aValues as $mValue) { | |
387 | if (!$mValue instanceof Value) { | |
388 | $mValue = mb_strtolower($mValue); | |
389 | } | |
390 | if ($mValue instanceof Url) { | |
391 | $aListProperties['list-style-image'] = $mValue; | |
392 | } else if (in_array($mValue, $aListStyleTypes)) { | |
393 | $aListProperties['list-style-types'] = $mValue; | |
394 | } else if (in_array($mValue, $aListStylePositions)) { | |
395 | $aListProperties['list-style-position'] = $mValue; | |
396 | } | |
397 | } | |
398 | foreach ($aListProperties as $sProperty => $mValue) { | |
399 | $oNewRule = new Rule($sProperty, $this->iLineNo); | |
400 | $oNewRule->setIsImportant($oRule->getIsImportant()); | |
401 | $oNewRule->addValue($mValue); | |
402 | $this->addRule($oNewRule); | |
403 | } | |
404 | $this->removeRule('list-style'); | |
405 | } | |
406 | ||
407 | public function createShorthandProperties(array $aProperties, $sShorthand) { | |
408 | $aRules = $this->getRulesAssoc(); | |
409 | $aNewValues = array(); | |
410 | foreach ($aProperties as $sProperty) { | |
411 | if (!isset($aRules[$sProperty])) | |
412 | continue; | |
413 | $oRule = $aRules[$sProperty]; | |
414 | if (!$oRule->getIsImportant()) { | |
415 | $mRuleValue = $oRule->getValue(); | |
416 | $aValues = array(); | |
417 | if (!$mRuleValue instanceof RuleValueList) { | |
418 | $aValues[] = $mRuleValue; | |
419 | } else { | |
420 | $aValues = $mRuleValue->getListComponents(); | |
421 | } | |
422 | foreach ($aValues as $mValue) { | |
423 | $aNewValues[] = $mValue; | |
424 | } | |
425 | $this->removeRule($sProperty); | |
426 | } | |
427 | } | |
428 | if (count($aNewValues)) { | |
429 | $oNewRule = new Rule($sShorthand, $this->iLineNo); | |
430 | foreach ($aNewValues as $mValue) { | |
431 | $oNewRule->addValue($mValue); | |
432 | } | |
433 | $this->addRule($oNewRule); | |
434 | } | |
435 | } | |
436 | ||
437 | public function createBackgroundShorthand() { | |
438 | $aProperties = array( | |
439 | 'background-color', 'background-image', 'background-repeat', | |
440 | 'background-position', 'background-attachment' | |
441 | ); | |
442 | $this->createShorthandProperties($aProperties, 'background'); | |
443 | } | |
444 | ||
445 | public function createListStyleShorthand() { | |
446 | $aProperties = array( | |
447 | 'list-style-type', 'list-style-position', 'list-style-image' | |
448 | ); | |
449 | $this->createShorthandProperties($aProperties, 'list-style'); | |
450 | } | |
451 | ||
452 | /** | |
453 | * Combine border-color, border-style and border-width into border | |
454 | * Should be run after create_dimensions_shorthand! | |
455 | * */ | |
456 | public function createBorderShorthand() { | |
457 | $aProperties = array( | |
458 | 'border-width', 'border-style', 'border-color' | |
459 | ); | |
460 | $this->createShorthandProperties($aProperties, 'border'); | |
461 | } | |
462 | ||
463 | /* | |
464 | * Looks for long format CSS dimensional properties | |
465 | * (margin, padding, border-color, border-style and border-width) | |
466 | * and converts them into shorthand CSS properties. | |
467 | * */ | |
468 | ||
469 | public function createDimensionsShorthand() { | |
470 | $aPositions = array('top', 'right', 'bottom', 'left'); | |
471 | $aExpansions = array( | |
472 | 'margin' => 'margin-%s', | |
473 | 'padding' => 'padding-%s', | |
474 | 'border-color' => 'border-%s-color', | |
475 | 'border-style' => 'border-%s-style', | |
476 | 'border-width' => 'border-%s-width' | |
477 | ); | |
478 | $aRules = $this->getRulesAssoc(); | |
479 | foreach ($aExpansions as $sProperty => $sExpanded) { | |
480 | $aFoldable = array(); | |
481 | foreach ($aRules as $sRuleName => $oRule) { | |
482 | foreach ($aPositions as $sPosition) { | |
483 | if ($sRuleName == sprintf($sExpanded, $sPosition)) { | |
484 | $aFoldable[$sRuleName] = $oRule; | |
485 | } | |
486 | } | |
487 | } | |
488 | // All four dimensions must be present | |
489 | if (count($aFoldable) == 4) { | |
490 | $aValues = array(); | |
491 | foreach ($aPositions as $sPosition) { | |
492 | $oRule = $aRules[sprintf($sExpanded, $sPosition)]; | |
493 | $mRuleValue = $oRule->getValue(); | |
494 | $aRuleValues = array(); | |
495 | if (!$mRuleValue instanceof RuleValueList) { | |
496 | $aRuleValues[] = $mRuleValue; | |
497 | } else { | |
498 | $aRuleValues = $mRuleValue->getListComponents(); | |
499 | } | |
500 | $aValues[$sPosition] = $aRuleValues; | |
501 | } | |
502 | $oNewRule = new Rule($sProperty, $this->iLineNo); | |
503 | if ((string) $aValues['left'][0] == (string) $aValues['right'][0]) { | |
504 | if ((string) $aValues['top'][0] == (string) $aValues['bottom'][0]) { | |
505 | if ((string) $aValues['top'][0] == (string) $aValues['left'][0]) { | |
506 | // All 4 sides are equal | |
507 | $oNewRule->addValue($aValues['top']); | |
508 | } else { | |
509 | // Top and bottom are equal, left and right are equal | |
510 | $oNewRule->addValue($aValues['top']); | |
511 | $oNewRule->addValue($aValues['left']); | |
512 | } | |
513 | } else { | |
514 | // Only left and right are equal | |
515 | $oNewRule->addValue($aValues['top']); | |
516 | $oNewRule->addValue($aValues['left']); | |
517 | $oNewRule->addValue($aValues['bottom']); | |
518 | } | |
519 | } else { | |
520 | // No sides are equal | |
521 | $oNewRule->addValue($aValues['top']); | |
522 | $oNewRule->addValue($aValues['left']); | |
523 | $oNewRule->addValue($aValues['bottom']); | |
524 | $oNewRule->addValue($aValues['right']); | |
525 | } | |
526 | $this->addRule($oNewRule); | |
527 | foreach ($aPositions as $sPosition) { | |
528 | $this->removeRule(sprintf($sExpanded, $sPosition)); | |
529 | } | |
530 | } | |
531 | } | |
532 | } | |
533 | ||
534 | /** | |
535 | * Looks for long format CSS font properties (e.g. <tt>font-weight</tt>) and | |
536 | * tries to convert them into a shorthand CSS <tt>font</tt> property. | |
537 | * At least font-size AND font-family must be present in order to create a shorthand declaration. | |
538 | * */ | |
539 | public function createFontShorthand() { | |
540 | $aFontProperties = array( | |
541 | 'font-style', 'font-variant', 'font-weight', 'font-size', 'line-height', 'font-family' | |
542 | ); | |
543 | $aRules = $this->getRulesAssoc(); | |
544 | if (!isset($aRules['font-size']) || !isset($aRules['font-family'])) { | |
545 | return; | |
546 | } | |
547 | $oNewRule = new Rule('font', $this->iLineNo); | |
548 | foreach (array('font-style', 'font-variant', 'font-weight') as $sProperty) { | |
549 | if (isset($aRules[$sProperty])) { | |
550 | $oRule = $aRules[$sProperty]; | |
551 | $mRuleValue = $oRule->getValue(); | |
552 | $aValues = array(); | |
553 | if (!$mRuleValue instanceof RuleValueList) { | |
554 | $aValues[] = $mRuleValue; | |
555 | } else { | |
556 | $aValues = $mRuleValue->getListComponents(); | |
557 | } | |
558 | if ($aValues[0] !== 'normal') { | |
559 | $oNewRule->addValue($aValues[0]); | |
560 | } | |
561 | } | |
562 | } | |
563 | // Get the font-size value | |
564 | $oRule = $aRules['font-size']; | |
565 | $mRuleValue = $oRule->getValue(); | |
566 | $aFSValues = array(); | |
567 | if (!$mRuleValue instanceof RuleValueList) { | |
568 | $aFSValues[] = $mRuleValue; | |
569 | } else { | |
570 | $aFSValues = $mRuleValue->getListComponents(); | |
571 | } | |
572 | // But wait to know if we have line-height to add it | |
573 | if (isset($aRules['line-height'])) { | |
574 | $oRule = $aRules['line-height']; | |
575 | $mRuleValue = $oRule->getValue(); | |
576 | $aLHValues = array(); | |
577 | if (!$mRuleValue instanceof RuleValueList) { | |
578 | $aLHValues[] = $mRuleValue; | |
579 | } else { | |
580 | $aLHValues = $mRuleValue->getListComponents(); | |
581 | } | |
582 | if ($aLHValues[0] !== 'normal') { | |
583 | $val = new RuleValueList('/', $this->iLineNo); | |
584 | $val->addListComponent($aFSValues[0]); | |
585 | $val->addListComponent($aLHValues[0]); | |
586 | $oNewRule->addValue($val); | |
587 | } | |
588 | } else { | |
589 | $oNewRule->addValue($aFSValues[0]); | |
590 | } | |
591 | $oRule = $aRules['font-family']; | |
592 | $mRuleValue = $oRule->getValue(); | |
593 | $aFFValues = array(); | |
594 | if (!$mRuleValue instanceof RuleValueList) { | |
595 | $aFFValues[] = $mRuleValue; | |
596 | } else { | |
597 | $aFFValues = $mRuleValue->getListComponents(); | |
598 | } | |
599 | $oFFValue = new RuleValueList(',', $this->iLineNo); | |
600 | $oFFValue->setListComponents($aFFValues); | |
601 | $oNewRule->addValue($oFFValue); | |
602 | ||
603 | $this->addRule($oNewRule); | |
604 | foreach ($aFontProperties as $sProperty) { | |
605 | $this->removeRule($sProperty); | |
606 | } | |
607 | } | |
608 | ||
609 | public function __toString() { | |
610 | return $this->render(new \Sabberworm\CSS\OutputFormat()); | |
611 | } | |
612 | ||
613 | public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { | |
614 | if(count($this->aSelectors) === 0) { | |
615 | // If all the selectors have been removed, this declaration block becomes invalid | |
616 | throw new OutputException("Attempt to print declaration block with missing selector", $this->iLineNo); | |
617 | } | |
376eb156 MM |
618 | $sResult = $oOutputFormat->sBeforeDeclarationBlock; |
619 | $sResult .= $oOutputFormat->implode($oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(), $this->aSelectors); | |
620 | $sResult .= $oOutputFormat->sAfterDeclarationBlockSelectors; | |
621 | $sResult .= $oOutputFormat->spaceBeforeOpeningBrace() . '{'; | |
fbe18cc0 FM |
622 | $sResult .= parent::render($oOutputFormat); |
623 | $sResult .= '}'; | |
376eb156 | 624 | $sResult .= $oOutputFormat->sAfterDeclarationBlock; |
fbe18cc0 FM |
625 | return $sResult; |
626 | } | |
627 | ||
628 | } |