Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert CSS background shorthand to longhand

Tags:

html

css

php

I am trying to write a function that converts a short hand css declaration of background to long hand. I've written the below function but it has a couple of issues. One it doesn't take into account that background-color can be colour values such as black, yellow. In addition, what if some the properties contain inherit and none? Here is an example:

url('http://2.bp.blogspot.com/-h28qvOsfm1c/TaGyO_qAFcI/AAAAAAAAA9w/I7zPQLy0zVM/s640/funny-image.jpg') inherit inherit 0 0 #FFFFFF;

Convert the above to CSS long hand. Here is my function, can it be improved to cover other cases?

function rewrite_background($b){

    $long_hand = "";

    $count = count($b); 

    for($i=0; $i < $count; $i++){

        if(stripos($b[$i], '#') !== false){

            $long_hand .= 'background-color: '.$b[$i].'; ';

            unset($b[$i]);

        }else if(stripos($b[$i], 'url') !== false){

            $long_hand .= 'background-image: '.$b[$i].'; ';

            unset($b[$i]);

        }else if((stripos($b[$i], 'repeat') !== false) || (stripos($b[$i], 'no-repeat') !== false) || (stripos($b[$i], 'repeat-x') !== false) || (stripos($b[$i], 'repeat-y') !== false)){

            $long_hand .= 'background-repeat: '.$b[$i].'; ';

            unset($b[$i]);

        }else if((stripos($b[$i], 'scroll') !== false) || (stripos($b[$i], 'fixed') !== false)){

            $long_hand .= 'background-attachment: '.$b[$i].'; ';

            unset($b[$i]);

        }else{

            // not recognized

        }

    }

    $b = array_values($b);

    if(isset($b[0])) $long_hand .= 'background-position: '.$b[0].' '.$b[1].';';  

    return $long_hand;

} 
like image 811
Abs Avatar asked Aug 16 '11 22:08

Abs


2 Answers

Class to Parse CSS Background Shortcuts

This class will parse just about any line of background shortcut properties in any order, including those that are invalid according to the specs. For instance, background: top top is treated as background-position: center top.

All color values are fully supported, including: rgb, rgba, hls, hlsa, case-insensitive short-form hex (e.g. #fff), case-insensitive long-form hex (e.g. #123Abc), and case-insensitive color names.

!important is now supported.

inherit seemed as though it would be the most challenging problem, but turns out to be the simplest. For this property I referred to http://reference.sitepoint.com/css/inheritvalue, which states:

When you’re using shorthand notation such as background, you can’t mix inherit with other values. For example, the following background declaration is wrong:

p {
  background: #fff inherit left top;
}

... inherit must be the only value in the declaration, because there’s simply no way of identifying the subproperty to which the value inherit refers—after all, it’s not unique within the sequence. In the example above, inherit becomes ambiguous.

To deal with ambiguity, this class simply ignores everything else (except !important) and applies inherit to all properties as if you had used background: inherit.

The Code

<?php
class CSSBackground
{
    private $color_names = array(
        'AliceBlue', 'AntiqueWhite', 'Aqua', 'Aquamarine', 'Azure',
        'Beige', 'Bisque', 'Black', 'BlanchedAlmond', 'Blue',
        'BlueViolet', 'Brown', 'BurlyWood', 'CadetBlue', 'Chartreuse',
        'Chocolate', 'Coral', 'CornflowerBlue', 'Cornsilk', 'Crimson',
        'Cyan', 'DarkBlue', 'DarkCyan', 'DarkGoldenRod', 'DarkGray',
        'DarkGrey', 'DarkGreen', 'DarkKhaki', 'DarkMagenta',
        'DarkOliveGreen', 'Darkorange', 'DarkOrchid', 'DarkRed',
        'DarkSalmon', 'DarkSeaGreen', 'DarkSlateBlue', 'DarkSlateGray',
        'DarkSlateGrey', 'DarkTurquoise', 'DarkViolet', 'DeepPink',
        'DeepSkyBlue', 'DimGray', 'DimGrey', 'DodgerBlue', 'FireBrick',
        'FloralWhite', 'ForestGreen', 'Fuchsia', 'Gainsboro',
        'GhostWhite', 'Gold', 'GoldenRod', 'Gray', 'Grey', 'Green',
        'GreenYellow', 'HoneyDew', 'HotPink', 'IndianRed', 'Indigo',
        'Ivory', 'Khaki', 'Lavender', 'LavenderBlush', 'LawnGreen',
        'LemonChiffon', 'LightBlue', 'LightCoral', 'LightCyan',
        'LightGoldenRodYellow', 'LightGray', 'LightGrey', 'LightGreen',
        'LightPink', 'LightSalmon', 'LightSeaGreen', 'LightSkyBlue',
        'LightSlateGray', 'LightSlateGrey', 'LightSteelBlue', 'LightYellow',
        'Lime', 'LimeGreen', 'Linen', 'Magenta', 'Maroon',
        'MediumAquaMarine', 'MediumBlue', 'MediumOrchid', 'MediumPurple',
        'MediumSeaGreen', 'MediumSlateBlue', 'MediumSpringGreen',
        'MediumTurquoise', 'MediumVioletRed', 'MidnightBlue', 'MintCream',
        'MistyRose', 'Moccasin', 'NavajoWhite', 'Navy', 'OldLace', 'Olive',
        'OliveDrab', 'Orange', 'OrangeRed', 'Orchid', 'PaleGoldenRod',
        'PaleGreen', 'PaleTurquoise', 'PaleVioletRed', 'PapayaWhip',
        'PeachPuff', 'Peru', 'Pink', 'Plum', 'PowderBlue', 'Purple', 'Red',
        'RosyBrown', 'RoyalBlue', 'SaddleBrown', 'Salmon', 'SandyBrown',
        'SeaGreen', 'SeaShell', 'Sienna', 'Silver', 'SkyBlue', 'SlateBlue',
        'SlateGray', 'SlateGrey', 'Snow', 'SpringGreen', 'SteelBlue', 'Tan',
        'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'White',
        'WhiteSmoke', 'Yellow', 'YellowGreen'
    );

    private $m_bgcolor = 'transparent';
    private $m_bgimage = 'none';
    private $m_bgrepeat = 'repeat';
    private $m_bgattachment = 'scroll';
    private $m_bgposition = '0% 0%';
    private $m_bgimportant = false;
    private $m_bg;

    public function __construct($bg)
    {
        // reformat array names for efficient pattern matching
        $this->color_names = '/\b('.implode('|',$this->color_names).')\b/i';

        $this->m_bg = $bg;  // save original

        $bg = $this->parse_important($bg);
        $bg = $this->parse_inherit($bg);
        $bg = $this->parse_color($bg);
        $bg = $this->parse_image($bg);
        $bg = $this->parse_repeat($bg);
        $bg = $this->parse_attachment($bg);
        $bg = $this->parse_position($bg);
    }

    public function original()
    {
        return $this->m_bg;
    }

    public function color()
    {
        return $this->m_bgcolor;
    }

    public function image()
    {
        return $this->m_bgimage;
    }

    public function repeat()
    {
        return $this->m_bgrepeat;
    }

    public function attachment()
    {
        return $this->m_bgattachment;
    }

    public function position()
    {
        return $this->m_bgposition;
    }

    public function important()
    {
        return $this->m_bgimportant;
    }

    private function parse_important($c)
    {
        // check for !important
        if (preg_match('/!important/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgimportant = true ;
        }

        return $c;
    }

    private function parse_inherit($c)
    {
        // check for !important
        if (preg_match('/inherit/i', $c, $m))
        {
            $this->m_bgcolor = $this->apply_important('inherit');
            $this->m_bgimage = $this->apply_important('inherit');
            $this->m_bgrepeat = $this->apply_important('inherit');
            $this->m_bgattachment = $this->apply_important('inherit');
            $this->m_bgposition = $this->apply_important('inherit');
            $c = '';
        }

        return $c;
    }

    private function parse_color($c)
    {
        // check for hexit color value
        if (preg_match('/#([[:xdigit:]]{3}){1,2}/', $c, $m))
        {
            $c = str_replace($m[0], '', $c);

            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        // check for rgb color value
        elseif (preg_match('/rgb\(\d{0,3}\,\d{0,3},\d{0,3}\)/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        // check for rgba color value
        elseif (preg_match('/rgba\(\d{0,3}%?\,\d{0,3}%?,\d{0,3}%?\,\d(\.\d)?\)/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        // check for hls color value
        elseif (preg_match('/hls\(\d{0,3}\,\d{0,3}%,\d{0,3}%\)/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        // check for hlsa color value
        elseif (preg_match('/hlsa\(\d{0,3}\,\d{0,3}%,\d{0,3}%\,\d(\.\d)?\)/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        // check for transparent
        elseif (preg_match('/transparent/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important('transparent');
        }

        // check for color names
        elseif (preg_match($this->color_names, $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        return $c;
    }

    private function parse_image($c)
    {
        // check for double word positions
        if (preg_match('/url\((.*?)\)|none/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            if (isset($m[1]))
            {
                $m[0] = str_replace($m[1], urlencode($m[1]), $m[0]);
            }
            $this->m_bgimage = $this->apply_important($m[0]);
        }

        return $c;
    }

    private function parse_repeat($c)
    {
        // check for repeat values
        if (preg_match('/\b(repeat-x|repeat-y|no-repeat|repeat)\b/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgrepeat = $this->apply_important($m[0]);
        }

        return $c;
    }

    private function parse_attachment($c)
    {
        // check for repeat values
        if (preg_match('/scroll|fixed/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgattachment = $this->apply_important($m[0]);
        }

        return $c;
    }

    private function parse_position($c)
    {
        // check for position values
        if (preg_match_all('/left|right|center|top|bottom|-?\d+([a-zA-Z]{2}|%?)/i', $c, $m))
        {
            $horz = '0%';
            $vert = '0%';

            if (!isset($m[0][1]))
            {
                $x = strtolower($m[0][0]);
                switch ($x)
                {
                    case 'top':
                    case 'bottom':
                        $horz = 'center';
                        $vert = $x;
                        break;
                    case 'left':
                    case 'right':
                    case 'center':
                        $horz = $x;
                        $vert = 'center';
                        break;
                    default:
                        $horz = is_numeric($x) ? "{$x}px" : $x;
                        $vert = 'center';
                }
            }

            else
            {
                $horz = strtolower($m[0][0]);
                $vert = strtolower($m[0][1]);

                if (($horz === $vert) && in_array($horz, array('left','right')))
                {
                    $vert = 'center';
                }

                if (($horz === $vert) && in_array($horz, array('top','bottom')))
                {
                    $horz = 'center';
                }

                if ($horz === 'top' || $horz === 'bottom')
                {
                    list($horz,$vert) = array($vert,$horz);
                }

                if ($vert === 'left' || $vert === 'right')
                {
                    list($horz,$vert) = array($vert,$horz);
                }
            }

            $this->m_bgposition = $this->apply_important("$horz $vert");
        }

        return $c;
    }

    private function apply_important($prop)
    {
        return $prop . ($this->m_bgimportant ? ' !important' : '');
    }
}

?>

Example Usage

<?php
header('Content-type: text/plain');

$bg = 'url("chess.png") gray 50% repeat fixed';

$cssbg = new CSSBackground($bg);

echo "background: ", $cssbg->original(), "\n\n";
echo "background-color: ", $cssbg->color(), "\n";
echo "background-image: ", $cssbg->image(), "\n";
echo "background-repeat: ", $cssbg->repeat(), "\n";
echo "background-attachment: ", $cssbg->attachment(), "\n";
echo "background-position: ", $cssbg->position(), "\n\n";
echo "!important applied: ", $cssbg->important() ? 'true' : 'false', "\n";
?>

This class was developed through extensive analysis of the w3c specifications for the CSS background property. Additional CSS properties would require the same analytical treatment.

like image 94
Herbert Avatar answered Oct 05 '22 22:10

Herbert


I think the best you will do here is build a close approximation, because your program has to fully understand the CSS in order to transform it at all, and regular expressions are not quite up to the task. You would need a real parsing library instead. But for a start, there is a really nice tutorial here on using the Coco/R parser to process CSS: http://www.codeproject.com/KB/recipes/CSSParser.aspx It will give you a better idea of the scope of the problem.

There is also a PHP class for CSS parsing here http://www.phpclasses.org/package/1289-PHP-CSS-parser-class.html#download but I don't expect much from that.

like image 25
Matt Montag Avatar answered Oct 05 '22 21:10

Matt Montag