将CSS字体缩写转换为长格式

3

如果您有这样的字体CSS字符串:

font:italic bold 12px/30px Georgia, serif;

或者

font:12px verdana;

我希望将其转换为全称格式,即:

字体风格:斜体;字重:粗体;

以下是我的糟糕尝试:http://pastebin.com/e3KdMvGT

但是,对于第二个示例,它无法正常工作,因为它期望按顺序进行,我应该如何改进?


你知道每个参数(大小、系列等)可以接受哪些值,所以只需循环遍历所有值并分析它们的格式,以找出其含义。附注:我认为最好在此处发布源代码,而不是提供一些易变的外部资源链接。 - binaryLV
2
这是一个有趣的用例。我可以问一下,你为什么要尝试这个? - Sukumar
我有一个旧的遗留系统,它呈现HTML并且需要所有内容都是长手写的。 - Abs
4个回答

5
这里有一个函数应该可以完成工作。问题在于font-style,font-variant和font-weight属性以及值“normal”,正如你可以在CSS规范中读到的那样(css specs)([[<'font-style'> || <'font-variant'> || <'font-weight'>]? <'font-size'> [/<'line-height'>]? <'font-family'>] |标题|图标|菜单|消息框|小标题|状态栏|继承)。
$testStrings = array('12px/14px sans-serif',
                     '80% sans-serif',
                     'x-large/110% "New Century Schoolbook", serif',
                     'x-large/110% "New Century Schoolbook"',
                     'bold italic large Palatino, serif ',
                     'normal small-caps 120%/120% fantasy',
                     'italic bold 12px/30px Georgia, serif',
                     '12px verdana');

foreach($testStrings as $font){
  echo "Test ($font)\n<pre>
";
  $details = extractFull($font);
  print_r($details);
  echo "
</pre>";
}


function extractFull($fontString){
  // Split $fontString. The only area where quotes should be found is around font-families. Which are at the end.
  $parts = preg_split('`("|\')`', $fontString, 2, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);

  $chunks = preg_split('` `', $parts[0], NULL, PREG_SPLIT_NO_EMPTY);
  if(isset($parts[1])){
    $chunks[] = $parts[1] . $parts[2];
  }

  $details = array();
  $next = -1;

  // Manage font-style / font-variant / font-weight properties
  $possibilities = array();
  $fontStyle = array('italic', 'oblique');
  $fontVariant = array('small-caps');
  $fontWeight = array('bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900');

  // First pass on chunks 0 to 2 to see what each can be
  for($i = 0; $i < 3; $i++){
    $possibilities[$i] = array();
    if(!isset($chunks[$i])){
      if($next == -1){
        $next = $i;
      }
      continue;
    }
    if(in_array($chunks[$i], $fontStyle)){
      $possibilities[$i] = 'font-style';
    }
    else if(in_array($chunks[$i], $fontVariant)){
      $possibilities[$i] = 'font-variant';
    }
    else if(in_array($chunks[$i], $fontWeight)){
      $possibilities[$i] = 'font-weight';
    }
    else if($chunks[$i] == 'normal'){
      $possibilities['normal'] = 1;
    }
    else if($next == -1){
      // Used to know where other properties will start at
      $next = $i;
    }
  }

  // Second pass to determine what real properties are defined
  for($i = 0; $i < 3; $i++){
    if(!empty($possibilities[$i])){
      $details[$possibilities[$i]] = $chunks[$i];
    }
  }

  // Third pass to set the properties which have to be set as "normal"
  if(!empty($possibilities['normal'])){
    $properties = array('font-style', 'font-variant', 'font-weight');
    foreach($properties as $property){
      if(!isset($details[$property])){
        $details[$property] = 'normal';
      }
    }
  }

  if(!isset($chunks[$next])){
    return $details;
  }

  // Extract font-size and line height
  if(strpos($chunks[$next], '/')){
    $size = explode('/', $chunks[$next]);
    $details['font-size'] = $size[0];
    $details['line-height'] = $size[1];
    $details['font-family'] = $chunks[$next+1];
  }
  else if(preg_match('`^-?[0-9]+`', $chunks[$next]) || 
          in_array($chunks[$next], array('xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'larger', 'smaller'))){
    $details['font-size'] = $chunks[$next];
    $details['font-family'] = $chunks[$next+1];
  }
  else{
    $details['font-family'] = $chunks[$next];
  }

  return $details;
}

我本来要写自己的,但它与这个非常相似。@Arkh 你真的应该添加这个链接:http://www.w3.org/TR/CSS2/fonts.html#propdef-font 和这段文字:[[ <'font-style'> || <'font-variant'> || <'font-weight'> ]? <'font-size'> [ / <'line-height'> ]? <'font-family'> ] | caption | icon | menu | message-box | small-caption | status-bar | inherit - Christian

2

这里是我的尝试

function rewriteFont($short) { 
    $short = str_replace("font:","",$short);
    $values = explode(" ", $short);
    $length = count($values);
    $familyLength = 0;

    for($i = 0; $i < $length; $i++) {
        $currentValue = $values[$i];
        if($currentValue == 'italic' || $currentValue == 'oblique' ) { 
            echo "font-style:{$currentValue};";
        } 
        else if($currentValue == 'small-caps') { 
            echo "font-variant:{$currentValue};";
        }
        else if ($currentValue == 'bold' || $currentValue == 'bolder' || $currentValue == 'lighter' || is_numeric($currentValue) ) {
            echo "font-weight:{$currentValue};";
        }
        else if (strpos($currentValue, "px") || strpos($currentValue, "em") || strpos($currentValue, "ex")|| strpos($currentValue, "pt")
        || strpos($currentValue, "%") || $currentValue == "xx-small" || $currentValue == "x-small" || $currentValue == "small"
        || $currentValue == "medium" || $currentValue == "large" || $currentValue == "x-large" || $currentValue == "xx-large") {
            $sizeLineHeight = explode("/", $currentValue);
            echo "font-size:{$sizeLineHeight[0]};";
            if(isset($sizeLineHeight[1])) {
                echo "line-height:{$sizeLineHeight[1]};";
            }
        }
        else {
            if($familyLength == 0) { 
                echo "font-family:{$currentValue} ";
            }
            else {
                echo $currentValue; 
            }
            $familyLength++;
        }   
    }

}

很棒的解决方案,但你的解决方案存在一个与Shikiryu解决方案相同的小问题。请看我对Shikiryu的评论。 - Abs

2

根据规范,我会写以下代码。它适用于规范中的所有示例。您可以在此处查看:http://codepad.viper-7.com/ABhc22

function font($s){
    $fontStyle      = array('normal', 'italic', 'oblique', 'inherit');
    $fontVariant        = array('normal', 'small-caps','inherit');
    $fontSize           = array('xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large');
    $fontWeight     = array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900', 'inherit');
    $s              = str_replace('font: ', '', $s);
    $array          = explode(' ', $s);
    $count          = count($array);
    $sizeFound      = false;
    $final          = array();
    $final['font-style']    = 'normal';
    $final['font-variant']  = 'normal';
    $final['font-weight']   = 'normal';
    for($i = 0; $i < $count; $i++){
        $cur = $array[$i];
        if($sizeFound){
            $final['font-family'] = isset($final['font-family']) ? $final['font-family'].$cur : $cur;
        }else if(strripos($cur,'%') !== false || strripos($cur,'px') !== false || in_array($cur, $fontSize)){
            $sizeFound = true;
            $size = explode('/', $cur);
            if(count($size) > 1){
                $final['font-size'] = $size[0];
                $final['line-height'] = $size[1];
            }else
                $final['font-size'] = $size[0];
        }else{ 
            if(in_array($cur, $fontStyle))
                $final['font-style'] = $cur;
            if(in_array($cur, $fontVariant))
                $final['font-variant'] = $cur;
            if(in_array($cur, $fontWeight))
                $final['font-weight'] = $cur;
        }   
    }
    $ret = '';
    foreach($final as $prop => $val)
        $ret .= $prop.': '.$val.';';
    return $ret;
}

我想它可以进行优化。 :)

我喜欢你的解决方案!正在努力理解它是如何工作的!但我注意到一个问题,我试图解决,“font-family:“NewCenturySchoolbook”,serif;”-如果你注意到了,它已经删除了字体名称之间的空格。 - Abs
好的,它可以在有空格或无空格的情况下工作。问题是,一旦我捕捉到“字号/行高”,后面的一切都将成为字体系列。 - Shikiryu

0
你可以查看 CSSTidy,它的代码是开源的,尝试逆向工程它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接