property = $property; $this->value = trim($value, ';'); } /** * Get the property name of this declaration. * * @return string */ public function getProperty () { return $this->property; } /** * Get the value assigned to the property of this declaration. * * @return string */ public function getValue () { return $this->value; } /** * @param css_declaration[] $decls */ public function explode (&$decls) { if ( empty ($this->property) ) { return; } switch ($this->property) { case 'background': $this->explodeBackgroundShorthand ($decls); break; case 'font': $this->explodeFontShorthand ($decls); break; case 'padding': $this->explodePaddingShorthand ($decls); break; case 'margin': $this->explodeMarginShorthand ($decls); break; case 'border': $this->explodeBorderShorthand ($decls); break; case 'list-style': $this->explodeListStyleShorthand ($decls); break; case 'flex': $this->explodeFlexShorthand ($decls); break; case 'transition': $this->explodeTransitionShorthand ($decls); break; case 'outline': $this->explodeOutlineShorthand ($decls); break; case 'animation': $this->explodeAnimationShorthand ($decls); break; case 'border-bottom': $this->explodeBorderBottomShorthand ($decls); break; case 'columns': $this->explodeColumnsShorthand ($decls); break; case 'column-rule': $this->explodeColumnRuleShorthand ($decls); break; //FIXME: Implement all the shorthands missing //case ... } } /** * @return bool */ public function isShorthand () { switch ($this->property) { case 'background': case 'font': case 'padding': case 'margin': case 'border': case 'list-style': case 'flex': case 'transition': case 'outline': case 'animation': case 'border-bottom': case 'columns': case 'column-rule': return true; break; //FIXME: Implement all the shorthands missing //case ... } return false; } /** * @param css_declaration[] $decls */ protected function explodeBackgroundShorthand (&$decls) { if ( $this->property == 'background' ) { $values = preg_split ('/\s+/', $this->value); $index = 0; if ($index < count($values)) { $color_done = true; $value = $values [$index]; if ($value [0] == '#' || csscolors::isKnownColorName($value)) { $decls [] = new css_declaration ('background-color', $value); $index++; } else { switch ($value) { case 'transparent': case 'inherit': case 'initial': $decls [] = new css_declaration ('background-color', $value); $index++; break; } } } if ($index < count($values)) { $decls [] = new css_declaration ('background-image', $values [$index]); $index++; } if ($index < count($values)) { $decls [] = new css_declaration ('background-repeat', $values [$index]); $index++; } if ($index < count($values)) { $decls [] = new css_declaration ('background-attachment', $values [$index]); $index++; } if ($index < count($values)) { $decls [] = new css_declaration ('background-position', $values [$index]); $index++; } } } /** * @param css_declaration[] $decls */ protected function explodeFontShorthand (&$decls, $setDefaults=false) { if ( $this->property == 'font' ) { $values = preg_split ('/\s+/', $this->value); $font_style_set = false; $font_variant_set = false; $font_weight_set = false; $font_size_set = false; $font_family = ''; foreach ($values as $value) { if ( $font_style_set === false ) { $default = false; switch ($value) { case 'normal': case 'italic': case 'oblique': case 'initial': case 'inherit': $decls [] = new css_declaration ('font-style', $value); break; default: $default = true; if ($setDefaults) { $decls [] = new css_declaration ('font-style', 'normal'); } break; } $font_style_set = true; if ( $default === false ) { continue; } } if ( $font_variant_set === false ) { $default = false; switch ($value) { case 'normal': case 'small-caps': case 'initial': case 'inherit': $decls [] = new css_declaration ('font-variant', $value); break; default: $default = true; if ($setDefaults) { $decls [] = new css_declaration ('font-variant', 'normal'); } break; } $font_variant_set = true; if ( $default === false ) { continue; } } if ( $font_weight_set === false ) { $default = false; switch ($value) { case 'normal': case 'bold': case 'bolder': case 'lighter': case '100': case '200': case '300': case '400': case '500': case '600': case '700': case '800': case '900': case 'initial': case 'inherit': $decls [] = new css_declaration ('font-weight', $value); break; default: $default = true; if ($setDefaults) { $decls [] = new css_declaration ('font-weight', 'normal'); } break; } $font_weight_set = true; if ( $default === false ) { continue; } } if ( $font_size_set === false ) { $default = false; $params = explode ('/', $value); switch ($params [0]) { case 'medium': case 'xx-small': case 'x-small': case 'small': case 'large': case 'x-large': case 'xx-large': case 'smaller': case 'larger': case 'initial': case 'inherit': $decls [] = new css_declaration ('font-size', $params [0]); break; default: $found = false; foreach (self::$css_units as $css_unit) { if ( strpos ($value, $css_unit) !== false ) { $decls [] = new css_declaration ('font-size', $params [0]); $found = true; break; } } if ( $found === false ) { $default = true; if ($setDefaults) { $decls [] = new css_declaration ('font-size', 'medium'); } } break; } if ( !empty($params [1]) ) { $decls [] = new css_declaration ('line-height', $params [1]); } else { if ($setDefaults) { $decls [] = new css_declaration ('line-height', 'normal'); } } $font_size_set = true; if ( $default === false ) { continue; } } // All other properties are found. // The rest is assumed to be a font-family. if (empty ($font_family)) { $font_family = $value; } else { $font_family .= ' '.$value; } } if (!empty ($font_family)) { $decls [] = new css_declaration ('font-family', $font_family); } } } /** * @param css_declaration[] $decls */ protected function explodePaddingShorthand (&$decls) { if ( $this->property == 'padding' ) { $values = preg_split ('/\s+/', $this->value); switch (count($values)) { case 4: $decls [] = new css_declaration ('padding-top', $values [0]); $decls [] = new css_declaration ('padding-right', $values [1]); $decls [] = new css_declaration ('padding-bottom', $values [2]); $decls [] = new css_declaration ('padding-left', $values [3]); break; case 3: $decls [] = new css_declaration ('padding-top', $values [0]); $decls [] = new css_declaration ('padding-right', $values [1]); $decls [] = new css_declaration ('padding-left', $values [1]); $decls [] = new css_declaration ('padding-bottom', $values [2]); break; case 2: $decls [] = new css_declaration ('padding-top', $values [0]); $decls [] = new css_declaration ('padding-bottom', $values [0]); $decls [] = new css_declaration ('padding-right', $values [1]); $decls [] = new css_declaration ('padding-left', $values [1]); break; case 1: $decls [] = new css_declaration ('padding-top', $values [0]); $decls [] = new css_declaration ('padding-bottom', $values [0]); $decls [] = new css_declaration ('padding-right', $values [0]); $decls [] = new css_declaration ('padding-left', $values [0]); break; } } } /** * @param css_declaration[] $decls */ protected function explodeMarginShorthand (&$decls) { if ( $this->property == 'margin' ) { $values = preg_split ('/\s+/', $this->value); switch (count($values)) { case 4: $decls [] = new css_declaration ('margin-top', $values [0]); $decls [] = new css_declaration ('margin-right', $values [1]); $decls [] = new css_declaration ('margin-bottom', $values [2]); $decls [] = new css_declaration ('margin-left', $values [3]); break; case 3: $decls [] = new css_declaration ('margin-top', $values [0]); $decls [] = new css_declaration ('margin-right', $values [1]); $decls [] = new css_declaration ('margin-left', $values [1]); $decls [] = new css_declaration ('margin-bottom', $values [2]); break; case 2: $decls [] = new css_declaration ('margin-top', $values [0]); $decls [] = new css_declaration ('margin-bottom', $values [0]); $decls [] = new css_declaration ('margin-right', $values [1]); $decls [] = new css_declaration ('margin-left', $values [1]); break; case 1: $decls [] = new css_declaration ('margin-top', $values [0]); $decls [] = new css_declaration ('margin-bottom', $values [0]); $decls [] = new css_declaration ('margin-right', $values [0]); $decls [] = new css_declaration ('margin-left', $values [0]); break; } } } /** * @param css_declaration[] $decls */ protected function explodeBorderShorthand (&$decls) { $border_sides = array ('border-left', 'border-right', 'border-top', 'border-bottom'); if ( $this->property == 'border' ) { $values = preg_split ('/\s+/', $this->value); $index = 0; $border_width_set = false; $border_style_set = false; $border_color_set = false; while ( $index < 3 ) { if ( $border_width_set === false ) { if (!isset($values [$index])) $values [$index] = 'medium'; switch ($values [$index]) { case 'thin': case 'medium': case 'thick': $decls [] = new css_declaration ('border-width', $values [$index]); foreach ($border_sides as $border_side) { $decls [] = new css_declaration ($border_side.'-width', $values [$index]); } break; default: if ( strpos ($values [$index], 'px') !== false ) { $decls [] = new css_declaration ('border-width', $values [$index]); foreach ($border_sides as $border_side) { $decls [] = new css_declaration ($border_side.'-width', $values [$index]); } } else { // There is no default value? So leave it unset. } break; } $border_width_set = true; $index++; continue; } if ( $border_style_set === false ) { if (!isset($values [$index])) $values [$index] = 'none'; switch ($values [$index]) { case 'none': case 'dotted': case 'dashed': case 'solid': case 'double': case 'groove': case 'ridge': case 'inset': case 'outset': $decls [] = new css_declaration ('border-style', $values [$index]); foreach ($border_sides as $border_side) { $decls [] = new css_declaration ($border_side.'-style', $values [$index]); } break; default: $decls [] = new css_declaration ('border-style', 'none'); foreach ($border_sides as $border_side) { $decls [] = new css_declaration ($border_side.'-style', 'none'); } break; } $border_style_set = true; $index++; continue; } if ( $border_color_set === false ) { if (!isset($values [$index])) $values [$index] = 'initial'; $decls [] = new css_declaration ('border-color', $values [$index]); foreach ($border_sides as $border_side) { $decls [] = new css_declaration ($border_side.'-color', $values [$index]); } // This is the last value. break; } } foreach ($border_sides as $border_side) { $decls [] = new css_declaration ($border_side, $values [0].' '.$values [1].' '.$values [2]); } } } /** * @param css_declaration[] $decls */ protected function explodeListStyleShorthand (&$decls) { if ( $this->property == 'list-style' ) { $values = preg_split ('/\s+/', $this->value); $list_style_type_set = false; $list_style_position_set = false; $list_style_image_set = false; foreach ($values as $value) { if ( $list_style_type_set === false ) { $default = false; switch ($value) { case 'disc': case 'armenian': case 'circle': case 'cjk-ideographic': case 'decimal': case 'decimal-leading-zero': case 'georgian': case 'hebrew': case 'hiragana': case 'hiragana-iroha': case 'katakana': case 'katakana-iroha': case 'lower-alpha': case 'lower-greek': case 'lower-latin': case 'lower-roman': case 'none': case 'square': case 'upper-alpha': case 'upper-latin': case 'upper-roman': case 'initial': case 'inherit': $decls [] = new css_declaration ('list-style-type', $value); break; default: $default = true; $decls [] = new css_declaration ('list-style-type', 'disc'); break; } $list_style_type_set = true; if ( $default === false ) { continue; } } if ( $list_style_position_set === false ) { $default = false; switch ($value) { case 'inside': case 'outside': case 'initial': case 'inherit': $decls [] = new css_declaration ('list-style-position', $value); break; default: $default = true; $decls [] = new css_declaration ('list-style-position', 'outside'); break; } $list_style_position_set = true; if ( $default === false ) { continue; } } if ( $list_style_image_set === false ) { $decls [] = new css_declaration ('list-style-image', $value); $list_style_image_set = true; } } if ( $list_style_image_set === false ) { $decls [] = new css_declaration ('list-style-image', 'none'); } } } /** * @param css_declaration[] $decls */ protected function explodeFlexShorthand (&$decls) { if ( $this->property == 'flex' ) { $values = preg_split ('/\s+/', $this->value); if ( count($values) > 0 ) { $decls [] = new css_declaration ('flex-grow', $values [0]); } if ( count($values) > 1 ) { $decls [] = new css_declaration ('flex-shrink', $values [1]); } if ( count($values) > 2 ) { $decls [] = new css_declaration ('flex-basis', $values [2]); } } } /** * @param css_declaration[] $decls */ protected function explodeTransitionShorthand (&$decls) { if ( $this->property == 'transition' ) { $values = preg_split ('/\s+/', $this->value); if ( count($values) > 0 ) { $decls [] = new css_declaration ('transition-property', $values [0]); } if ( count($values) > 1 ) { $decls [] = new css_declaration ('transition-duration', $values [1]); } if ( count($values) > 2 ) { $decls [] = new css_declaration ('transition-timing-function', $values [2]); } if ( count($values) > 3 ) { $decls [] = new css_declaration ('transition-delay', $values [3]); } } } /** * @param css_declaration[] $decls */ protected function explodeOutlineShorthand (&$decls) { if ( $this->property == 'outline' ) { $values = preg_split ('/\s+/', $this->value); $outline_color_set = false; $outline_style_set = false; $outline_width_set = false; foreach ($values as $value) { if ( $outline_color_set === false ) { $decls [] = new css_declaration ('outline-color', $value); $outline_color_set = true; continue; } if ( $outline_style_set === false ) { $default = false; switch ($value) { case 'none': case 'hidden': case 'dotted': case 'dashed': case 'solid': case 'double': case 'groove': case 'ridge': case 'inset': case 'outset': case 'initial': case 'inherit': $decls [] = new css_declaration ('outline-style', $value); break; default: $default = true; $decls [] = new css_declaration ('outline-style', 'none'); break; } $outline_style_set = true; if ( $default === false ) { continue; } } if ( $outline_width_set === false ) { $default = false; switch ($value) { case 'medium': case 'thin': case 'thick': case 'initial': case 'inherit': $decls [] = new css_declaration ('outline-width', $value); break; default: $found = false; foreach (self::$css_units as $css_unit) { if ( strpos ($value, $css_unit) !== false ) { $decls [] = new css_declaration ('outline-width', $value); $found = true; break; } } if ( $found === false ) { $default = true; $decls [] = new css_declaration ('outline-width', 'medium'); } break; } $outline_width_set = true; if ( $default === false ) { continue; } } } } } /** * @param css_declaration[] $decls */ protected function explodeAnimationShorthand (&$decls) { if ( $this->property == 'animation' ) { $values = preg_split ('/\s+/', $this->value); if ( count($values) > 0 ) { $decls [] = new css_declaration ('animation-name', $values [0]); } if ( count($values) > 1 ) { $decls [] = new css_declaration ('animation-duration', $values [1]); } if ( count($values) > 2 ) { $decls [] = new css_declaration ('animation-timing-function', $values [2]); } if ( count($values) > 3 ) { $decls [] = new css_declaration ('animation-delay', $values [3]); } if ( count($values) > 4 ) { $decls [] = new css_declaration ('animation-iteration-count', $values [4]); } if ( count($values) > 5 ) { $decls [] = new css_declaration ('animation-direction', $values [5]); } if ( count($values) > 6 ) { $decls [] = new css_declaration ('animation-fill-mode', $values [6]); } if ( count($values) > 7 ) { $decls [] = new css_declaration ('animation-play-state', $values [7]); } } } /** * @param css_declaration[] $decls */ protected function explodeBorderBottomShorthand (&$decls) { if ( $this->property == 'border-bottom' ) { $values = preg_split ('/\s+/', $this->value); $border_bottom_width_set = false; $border_bottom_style_set = false; $border_bottom_color_set = false; foreach ($values as $value) { if ( $border_bottom_width_set === false ) { $default = false; switch ($value) { case 'medium': case 'thin': case 'thick': case 'initial': case 'inherit': $decls [] = new css_declaration ('border-bottom-width', $value); break; default: $found = false; foreach (self::$css_units as $css_unit) { if ( strpos ($value, $css_unit) !== false ) { $decls [] = new css_declaration ('border-bottom-width', $value); $found = true; break; } } if ( $found === false ) { $default = true; $decls [] = new css_declaration ('border-bottom-width', 'medium'); } break; } $border_bottom_width_set = true; if ( $default === false ) { continue; } } if ( $border_bottom_style_set === false ) { $default = false; switch ($value) { case 'none': case 'hidden': case 'dotted': case 'dashed': case 'solid': case 'double': case 'groove': case 'ridge': case 'inset': case 'outset': case 'initial': case 'inherit': $decls [] = new css_declaration ('border-bottom-style', $value); break; default: $default = true; $decls [] = new css_declaration ('border-bottom-style', 'none'); break; } $border_bottom_style_set = true; if ( $default === false ) { continue; } } if ( $border_bottom_color_set === false ) { $decls [] = new css_declaration ('border-bottom-color', $value); $border_bottom_color_set = true; continue; } } } } /** * @param css_declaration[] $decls */ protected function explodeColumnsShorthand (&$decls) { if ( $this->property == 'columns' ) { $values = preg_split ('/\s+/', $this->value); if ( count($values) == 1 && $values [0] == 'auto' ) { $decls [] = new css_declaration ('column-width', 'auto'); $decls [] = new css_declaration ('column-count', 'auto'); return; } if ( count($values) > 0 ) { $decls [] = new css_declaration ('column-width', $values [0]); } if ( count($values) > 1 ) { $decls [] = new css_declaration ('column-count', $values [1]); } } } /** * @param css_declaration[] $decls */ protected function explodeColumnRuleShorthand (&$decls) { if ( $this->property == 'column-rule' ) { $values = preg_split ('/\s+/', $this->value); if ( count($values) > 0 ) { $decls [] = new css_declaration ('column-rule-width', $values [0]); } if ( count($values) > 1 ) { $decls [] = new css_declaration ('column-rule-style', $values [1]); } if ( count($values) > 2 ) { $decls [] = new css_declaration ('column-rule-color', $values [2]); } } } /** * @param $callback */ public function adjustLengthValues ($callback, $rule=NULL) { switch ($this->property) { case 'border-width': case 'outline-width': case 'border-bottom-width': case 'column-rule-width': $this->value = call_user_func($callback, $this->property, $this->value, CSSValueType::StrokeOrBorderWidth, $rule); break; case 'margin-left': case 'margin-right': case 'padding-left': case 'padding-right': case 'width': case 'column-width': $this->value = call_user_func($callback, $this->property, $this->value, CSSValueType::LengthValueXAxis, $rule); break; case 'margin-top': case 'margin-bottom': case 'padding-top': case 'padding-bottom': case 'min-height': case 'height': case 'line-height': $this->value = call_user_func($callback, $this->property, $this->value, CSSValueType::LengthValueYAxis, $rule); break; case 'border': case 'border-left': case 'border-right': case 'border-top': case 'border-bottom': $this->adjustLengthValuesBorder ($callback, $rule); break; // FIXME: Shorthands are currently not processed. // Every Shorthand would need an extra function which knows if it has any length values. // Just like the explode...Shorthand functions. } } /** * @param $callback */ protected function adjustLengthValuesBorder ($callback, $rule=NULL) { switch ($this->property) { case 'border': case 'border-left': case 'border-right': case 'border-top': case 'border-bottom': $values = preg_split ('/\s+/', $this->value); if (!isset($values [1])) $values [1] = 'none'; // border-style if (!isset($values [2])) $values [2] = 'currentcolor'; // border-color $width = call_user_func($callback, $this->property, $values [0], CSSValueType::StrokeOrBorderWidth, $rule); $this->value = $width . ' ' . $values [1] . ' ' . $values [2]; break; } } /** * @param $callback */ public function replaceURLPrefixes ($callback) { if (strncmp($this->value, 'url(', 4) == 0) { $url = substr($this->value, 4, -1); $this->value = call_user_func($callback, $this->property, $this->value, $url); } } } /** * Class css_rule * * @package CSS\css_rule */ class css_rule { protected $media = NULL; protected $selectors = array (); /** @var css_declaration[] */ protected $declarations = array (); /** * @param $selector * @param $decls * @param null $media */ public function __construct($selector, $decls, $media = NULL) { $this->media = trim ($media); //print ("\nNew rule: ".$media."\n"); //Debuging $this->selectors = explode (' ', $selector); $decls = trim ($decls, '{}'); // Parse declarations $pos = 0; $end = strlen ($decls); while ( $pos < $end ) { $colon = strpos ($decls, ':', $pos); if ( $colon === false ) { break; } $semi = strpos ($decls, ';', $colon + 1); if ( $semi === false ) { break; } $property = substr ($decls, $pos, $colon - $pos); $property = trim($property); $value = substr ($decls, $colon + 1, $semi - ($colon + 1)); $value = trim ($value); $values = preg_split ('/\s+/', $value); $value = ''; foreach ($values as $part) { if ( $part != '!important' ) { $value .= ' '.$part; } } $value = trim($value); // Create new declaration $declaration = new css_declaration ($property, $value); $this->declarations [] = $declaration; // Handle CSS shorthands, e.g. 'border' if ( $declaration->isShorthand () === true ) { $declaration->explode ($this->declarations); } $pos = $semi + 1; } } /** * @return string */ public function toString () { $returnString = ''; $returnString .= "Media= \"".$this->media."\"\n"; foreach ($this->selectors as $selector) { $returnString .= $selector.' '; } $returnString .= "{\n"; foreach ($this->declarations as $declaration) { $returnString .= ' '.$declaration->getProperty ().':'.$declaration->getValue ().";\n"; } $returnString .= "}\n"; return $returnString; } /** * @param $element * @param $classString * @param null $media * @return bool|int */ public function matches ($element, $classString, $media = NULL, $cssId=NULL) { $media = trim ($media); if ( !empty($this->media) && $media != $this->media ) { // Wrong media //print ("\nNo-Match ".$this->media."==".$media); //Debuging return false; } $matches = 0; $classes = explode (' ', $classString); foreach ($this->selectors as $selector) { if ( !empty($classString) ) { foreach ($classes as $class) { if ( $selector [0] == '.' && $selector == '.'.$class ) { $matches++; break; } else if ( $selector [0] == '#' && $selector == '#'.$cssId ) { $matches++; break; } else if ( $selector == $element || $selector == $element.'.'.$class ) { $matches++; break; } } } else { if ( $selector [0] == '#' && $selector == '#'.$cssId ) { $matches++; } else if ( $selector == $element ) { $matches++; } } } // We only got a match if all selectors were matched if ( $matches == count($this->selectors) ) { // Return the number of matched selectors // This enables the caller to choose the most specific rule return $matches; } return false; } /** * @param $name * @return null */ public function getProperty ($name) { foreach ($this->declarations as $declaration) { if ( $name == $declaration->getProperty () ) { return $declaration->getValue (); } } return NULL; } /** * @param $values * @return null */ public function getProperties (&$values) { foreach ($this->declarations as $declaration) { $property = $declaration->getProperty (); $value = $declaration->getValue (); $values [$property] = $value; } return NULL; } /** * @param $callback */ public function adjustLengthValues ($callback) { foreach ($this->declarations as $declaration) { $declaration->adjustLengthValues ($callback); } } } /** * Class helper_plugin_odt_cssimport * * @package helper\cssimport */ class helper_plugin_odt_cssimport extends DokuWiki_Plugin { protected $replacements = array(); protected $raw; /** @var css_rule[] */ protected $rules = array (); /** * Imports CSS from a file. * @deprecated since 3015-05-23, use importFromFile * * @param $filename */ function importFrom($filename) { dbg_deprecated('importFromFile'); $this->importFromFile($filename); } /** * @param $contents * @return bool */ function importFromString($contents) { $this->deleteComments ($contents); return $this->importFromStringInternal ($contents); } /** * Delete comments in $contents. All comments are overwritten with spaces. * The '&' is required. DO NOT DELETE!!! * @param $contents */ protected function deleteComments (&$contents) { // Delete all comments first $pos = 0; $max = strlen ($contents); $in_comment = false; while ( $pos < $max ) { if ( ($pos+1) < $max && $contents [$pos] == '/' && $contents [$pos+1] == '*' ) { $in_comment = true; $contents [$pos] = ' '; $contents [$pos+1] = ' '; $pos += 2; continue; } if ( ($pos+1) < $max && $contents [$pos] == '*' && $contents [$pos+1] == '/' && $in_comment === true ) { $in_comment = false; $contents [$pos] = ' '; $contents [$pos+1] = ' '; $pos += 2; continue; } if ( $in_comment === true ) { $contents [$pos] = ' '; } $pos++; } } /** * @param $contents * @param null $media * @return bool */ protected function importFromStringInternal($contents, $media = NULL, &$processed = NULL) { // Find all CSS rules $pos = 0; $max = strlen ($contents); while ( $pos < $max ) { $bracket_open = strpos ($contents, '{', $pos); if ( $bracket_open === false ) { return false; } $bracket_close = strpos ($contents, '}', $pos); if ( $bracket_close === false ) { return false; } // If this is a nested call we might hit a closing } for the media section // which was the reason for this function call. In this case break and return. if ( $bracket_close < $bracket_open ) { $pos = $bracket_close + 1; break; } // Get the part before the open bracket and the last closing bracket // (or the start of the string). $before_open_bracket = substr ($contents, $pos, $bracket_open - $pos); // Is it a @media rule? $before_open_bracket = trim ($before_open_bracket); $mediapos = stripos($before_open_bracket, '@media'); if ( $mediapos !== false ) { // Yes, decode content as normal rules with @media ... { ... } //$new_media = substr_replace ($before_open_bracket, NULL, $mediapos, strlen ('@media')); $new_media = substr ($before_open_bracket, $mediapos + strlen ('@media')); $contents_in_media = substr ($contents, $bracket_open + 1); $nested_processed = 0; $result = $this->importFromStringInternal ($contents_in_media, $new_media, $nested_processed); if ( $result !== true ) { // Stop parsing on error. return false; } unset ($new_media); $pos = $bracket_open + 1 + $nested_processed; } else { // No, decode rule the normal way selector { ... } $selectors = explode (',', $before_open_bracket); $decls = substr ($contents, $bracket_open + 1, $bracket_close - $bracket_open); // Create a own, new rule for every selector foreach ( $selectors as $selector ) { $selector = trim ($selector); $this->rules [] = new css_rule ($selector, $decls, $media); } $pos = $bracket_close + 1; } } if ( isset($processed) ) { $processed = $pos; } return true; } /** * @param $filename * @return bool|void */ function importFromFile($filename) { // Try to read in the file content if ( empty($filename) ) { return false; } $handle = fopen($filename, "rb"); if ( $handle === false ) { return false; } $contents = fread($handle, filesize($filename)); fclose($handle); if ( $contents === false ) { return false; } return $this->importFromString ($contents); } /** * @param $filename * @return bool */ function loadReplacements($filename) { // Try to read in the file content if ( empty($filename) ) { return false; } $handle = fopen($filename, "rb"); if ( $handle === false ) { return false; } $filesize = filesize($filename); $contents = fread($handle, $filesize); fclose($handle); if ( $contents === false ) { return false; } // Delete all comments first $contents = preg_replace ('/;.*/', ' ', $contents); // Find the start of the replacements section $rep_start = strpos ($contents, '[replacements]'); if ( $rep_start === false ) { return false; } $rep_start += strlen ('[replacements]'); // Find the end of the replacements section // (The end is either the next section or the end of file) $rep_end = strpos ($contents, '[', $rep_start); if ( $rep_end === false ) { $rep_end = $filesize - 1; } // Find all replacment definitions $defs = substr ($contents, $rep_start, $rep_end - $rep_start); $defs_end = strlen ($defs); $def_pos = 0; while ( $def_pos < $defs_end ) { $linestart = strpos ($defs, "\n", $def_pos); if ( $linestart === false ) { break; } $linestart += strlen ("\n"); $lineend = strpos ($defs, "\n", $linestart); if ( $lineend === false ) { $lineend = $defs_end; } $equal_sign = strpos ($defs, '=', $linestart); if ( $equal_sign === false || $equal_sign > $lineend ) { $def_pos = $linestart; continue; } $quote_start = strpos ($defs, '"', $equal_sign + 1); if ( $quote_start === false || $quote_start > $lineend ) { $def_pos = $linestart; continue; } $quote_end = strpos ($defs, '"', $quote_start + 1); if ( $quote_end === false || $quote_start > $lineend) { $def_pos = $linestart; continue; } if ( $quote_end - $quote_start < 2 ) { $def_pos = $linestart; continue; } $replacement = substr ($defs, $linestart, $equal_sign - $linestart); $value = substr ($defs, $quote_start + 1, $quote_end - ($quote_start + 1)); $replacement = trim($replacement); $value = trim($value); $this->replacements [$replacement] = $value; $def_pos = $lineend; } return true; } /** * @return mixed */ public function getRaw () { return $this->raw; } /** * @param $name * @return mixed */ public function getReplacement ($name) { return $this->replacements [$name]; } /** * @param $element * @param $classString * @param $name * @param null $media * @return null */ public function getPropertyForElement ($element, $classString, $name, $media = NULL) { if ( empty ($name) ) { return NULL; } $value = NULL; foreach ($this->rules as $rule) { $matched = $rule->matches ($element, $classString, $media); if ( $matched !== false ) { $current = $rule->getProperty ($name); if ( !empty ($current) ) { $value = $current; } } } return $value; } /** * @param $classString * @param $name * @return null */ public function getProperty ($classString, $name) { if ( empty ($classString) || empty ($name) ) { return NULL; } $value = $this->getPropertyForElement (NULL, $classString, $name); return $value; } /** * @param $dest * @param $element * @param $classString * @param null $media */ public function getPropertiesForElement (&$dest, $element, $classString, $media = NULL, $cssId=NULL) { if ( empty ($element) && empty ($classString) && empty ($cssId) ) { return; } foreach ($this->rules as $rule) { $matched = $rule->matches ($element, $classString, $media, $cssId); if ( $matched !== false ) { $rule->getProperties ($dest); } } } /** * @param $value * @param int $emValue * @return string */ public function adjustValueForODT ($value, $emValue = 0) { // ODT specific function. Shouldn't be used anymore. // Call the ODT renderer's function instead. dbg_deprecated('renderer_plugin_odt_page::adjustValueForODT'); $values = preg_split ('/\s+/', $value); $value = ''; foreach ($values as $part) { // Replace it if necessary $part = trim($part); $rep = $this->getReplacement($part); if ( !empty ($rep) ) { $part = $rep; } $length = strlen ($part); // If it is a short color value (#xxx) then convert it to long value (#xxxxxx) // (ODT does not support the short form) if ( $part [0] == '#' && $length == 4 ) { $part = '#'.$part [1].$part [1].$part [2].$part [2].$part [3].$part [3]; } else { // If it is a CSS color name, get it's real color value /** @var helper_plugin_odt_csscolors $odt_colors */ $odt_colors = plugin_load('helper', 'odt_csscolors'); $color = $odt_colors->getColorValue ($part); if ( $part == 'black' || $color != '#000000' ) { $part = $color; } } if ( $length > 2 && $part [$length-2] == 'e' && $part [$length-1] == 'm' ) { $number = substr ($part, 0, $length-2); if ( is_numeric ($number) && !empty ($emValue) ) { $part = ($number * $emValue).'pt'; } } // Replace px with pt (px does not seem to be supported by ODT) if ( $length > 2 && $part [$length-2] == 'p' && $part [$length-1] == 'x' ) { $part [$length-1] = 't'; } $value .= ' '.$part; } $value = trim($value); return $value; } /** * @return string */ public function rulesToString () { $returnString = ''; foreach ($this->rules as $rule) { $returnString .= $rule->toString (); } return $returnString; } /** * @param $URL * @param $replacement * @return string */ public function replaceURLPrefix ($URL, $replacement) { if ( !empty ($URL) && !empty ($replacement) ) { // Replace 'url(...)' with $replacement $URL = substr ($URL, 3); $URL = trim ($URL, '()'); $URL = $replacement.$URL; } return $URL; } /** * @param $callback */ public function adjustLengthValues ($callback) { foreach ($this->rules as $rule) { $rule->adjustLengthValues ($callback); } } }