stylesheet.cls.php

来自「国外很不错的一个开源OA系统Group-Office」· PHP 代码 · 共 875 行 · 第 1/2 页

PHP
875
字号
                switch ( $tok{$j} ) {        case "~":        case "|":          $op .= $tok{$j++};          if ( $tok{$j} != "=" )            throw new DOMPDF_Exception("Invalid CSS selector syntax: invalid attribute selector: $selector");          $op .= $tok{$j};          break;        case "=":          $op = "=";          break;        }               // Read the attribute value, if required        if ( $op != "" ) {          $j++;          while ( $j < $tok_len ) {            if ( $tok{$j} == "]" )              break;            $value .= $tok{$j++};          }                    }               if ( $attr == "" )          throw new DOMPDF_Exception("Invalid CSS selector syntax: missing attribute name");        switch ( $op ) {        case "":          $query .=  "[@$attr]";          break;                 case "=":          $query .= "[@$attr$op\"$value\"]";          break;        case "~=":          // FIXME: this will break if $value contains quoted strings          // (e.g. [type~="a b c" "d e f"])          $values = explode(" ", $value);          $query .=  "[";          foreach ( $values as $val )             $query .= "@$attr=\"$val\" or ";                   $query = rtrim($query, " or ") . "]";          break;        case "|=":          $values = explode("-", $value);          $query .= "[";          foreach ($values as $val)            $query .= "starts-with(@$attr, \"$val\") or ";          $query = rtrim($query, " or ") . "]";          break;                 }             break;      }    }    $i++;      //       case ":"://         // Pseudo selectors: ignore for now.  Partially handled directly//         // below.//         // Skip until the next special character, leaving the token as-is//         while ( $i < $len ) {//           if ( in_array($selector{$i}, $delimiters) )//             break;//           $i++;//         }//         break;        //       default://         // Add the character to the token//         $tok .= $selector{$i++};//         break;//       }//    }            // Trim the trailing '/' from the query    if ( mb_strlen($query) > 2 )      $query = rtrim($query, "/");        return $query;  }  /**   * applies all current styles to a particular document tree   *   * apply_styles() applies all currently loaded styles to the provided   * {@link Frame_Tree}.  Aside from parsing CSS, this is the main purpose   * of this class.   *   * @param Frame_Tree $tree   */  function apply_styles(Frame_Tree $tree) {    // Use XPath to select nodes.  This would be easier if we could attach    // Frame objects directly to DOMNodes using the setUserData() method, but    // we can't do that just yet.  Instead, we set a _node attribute_ in     // Frame->set_id() and use that as a handle on the Frame object via    // Frame_Tree::$_registry.    // We create a scratch array of styles indexed by frame id.  Once all    // styles have been assigned, we order the cached styles by specificity    // and create a final style object to assign to the frame.    // FIXME: this is not particularly robust...        $styles = array();    $xp = new DOMXPath($tree->get_dom());    // Apply all styles in stylesheet    foreach ($this->_styles as $selector => $style) {      $query = $this->_css_selector_to_xpath($selector);//       pre_var_dump($selector);//       pre_var_dump($query);//        echo ($style);            // Retrieve the nodes            $nodes = $xp->query($query);      foreach ($nodes as $node) {        //echo $node->nodeName . "\n";        // Retrieve the node id        if ( $node->nodeType != 1 ) // Only DOMElements get styles          continue;                $id = $node->getAttribute("frame_id");        // Assign the current style to the scratch array        $spec = $this->_specificity($selector);        $styles[$id][$spec][] = $style;      }    }    // Now create the styles and assign them to the appropriate frames.  (We    // iterate over the tree using an implicit Frame_Tree iterator.)    $root_flg = false;    foreach ($tree->get_frames() as $frame) {      // pre_r($frame->get_node()->nodeName . ":");                  if ( !$root_flg && $this->_page_style ) {        $style = $this->_page_style;        $root_flg = true;      } else         $style = $this->create_style();      // Find nearest DOMElement parent      $p = $frame;            while ( $p = $p->get_parent() )        if ($p->get_node()->nodeType == 1 )          break;            // Styles can only be applied directly to DOMElements; anonymous      // frames inherit from their parent      if ( $frame->get_node()->nodeType != 1 ) {        if ( $p )          $style->inherit($p->get_style());        $frame->set_style($style);        continue;      }      $id = $frame->get_id();      // Handle HTML 4.0 attributes      Attribute_Translator::translate_attributes($frame);          // Locate any additional style attributes            if ( ($str = $frame->get_node()->getAttribute("style")) !== "" ) {        $spec = $this->_specificity("!style attribute");        $styles[$id][$spec][] = $this->_parse_properties($str);      }            // Grab the applicable styles      if ( isset($styles[$id]) ) {                $applied_styles = $styles[ $frame->get_id() ];        // Sort by specificity        ksort($applied_styles);        // Merge the new styles with the inherited styles        foreach ($applied_styles as $arr) {          foreach ($arr as $s)             $style->merge($s);        }      }      // Inherit parent's styles if required      if ( $p ) {        $style->inherit( $p->get_style() );      }//       pre_r($frame->get_node()->nodeName . ":");//      echo "<pre>";//      echo $style;//      echo "</pre>";      $frame->set_style($style);          }        // We're done!  Clean out the registry of all styles since we    // won't be needing this later.    foreach ( array_keys($this->_styles) as $key ) {      unset($this->_styles[$key]);    }      }    /**   * parse a CSS string using a regex parser   *   * Called by {@link Stylesheet::parse_css()}    *   * @param string $str    */  private function _parse_css($str) {    // Destroy comments    $css = preg_replace("'/\*.*?\*/'si", "", $str);    // FIXME: handle '{' within strings, e.g. [attr="string {}"]    // Something more legible:    $re =      "/\s*                                   # Skip leading whitespace                             \n".      "( @([^\s]+)\s+([^{;]*) (?:;|({)) )?    # Match @rules followed by ';' or '{'                 \n".      "(?(1)                                  # Only parse sub-sections if we're in an @rule...     \n".      "  (?(4)                                # ...and if there was a leading '{'                   \n".      "    \s*( (?:(?>[^{}]+) ({)?            # Parse rulesets and individual @page rules           \n".      "            (?(6) (?>[^}]*) }) \s*)+?  \n".      "       )                               \n".      "   })                                  # Balancing '}'                                \n".      "|                                      # Branch to match regular rules (not preceeded by '@')\n".      "([^{]*{[^}]*}))                        # Parse normal rulesets\n".      "/xs";         if ( preg_match_all($re, $css, $matches, PREG_SET_ORDER) === false )      // An error occured      throw new DOMPDF_Exception("Error parsing css file: preg_match_all() failed.");    // After matching, the array indicies are set as follows:    //    // [0] => complete text of match    // [1] => contains '@import ...;' or '@media {' if applicable    // [2] => text following @ for cases where [1] is set    // [3] => media types or full text following '@import ...;'    // [4] => '{', if present    // [5] => rulesets within media rules    // [6] => '{', within media rules    // [7] => individual rules, outside of media rules    //    //pre_r($matches);    foreach ( $matches as $match ) {      $match[2] = trim($match[2]);      if ( $match[2] !== "" ) {        // Handle @rules        switch ($match[2]) {        case "import":                    $this->_parse_import($match[3]);          break;        case "media":          if ( in_array(mb_strtolower(trim($match[3])), self::$ACCEPTED_MEDIA_TYPES ) ) {            $this->_parse_sections($match[5]);          }          break;        case "page":          // Store the style for later...          if ( is_null($this->_page_style) )            $this->_page_style = $this->_parse_properties($match[5]);          else            $this->_page_style->merge($this->_parse_properties($match[5]));          break;                  default:          // ignore everything else          break;        }        continue;      }      if ( $match[7] !== "" )         $this->_parse_sections($match[7]);          }  }    /**   * parse @import{} sections   *   * @param string $url  the url of the imported CSS file   */  private function _parse_import($url) {    $arr = preg_split("/[\s\n]/", $url);    $url = array_pop($arr);    $accept = false;        if ( count($arr) > 0 ) {            // @import url media_type [media_type...]      foreach ( $arr as $type ) {        if ( in_array($type, self::$ACCEPTED_MEDIA_TYPES) ) {          $accept = true;          break;        }      }          } else      // unconditional import      $accept = true;        if ( $accept ) {      $url = str_replace(array('"',"url", "(", ")"), "", $url);      // Store our current base url properties in case the new url is elsewhere      $protocol = $this->_protocol;      $host = $this->_base_host;      $path = $this->_base_path;      // If the protocol is php, assume that we will import using file://      $url = build_url($protocol == "php://" ? "file://" : $protocol, $host, $path, $url);            $this->load_css_file($url);            // Restore the current base url      $this->_protocol = $protocol;      $this->_base_host = $host;      $this->_base_path = $path;    }      }  /**   * parse regular CSS blocks   *   * _parse_properties() creates a new Style object based on the provided   * CSS rules.   *   * @param string $str  CSS rules   * @return Style   */  private function _parse_properties($str) {    $properties = explode(";", $str);    // Create the style    $style = new Style($this);    foreach ($properties as $prop) {      $prop = trim($prop);      if ($prop == "")        continue;      $i = mb_strpos($prop, ":");      if ( $i === false )        continue;      $prop_name = mb_strtolower(mb_substr($prop, 0, $i));      $value = mb_substr($prop, $i+1);      $style->$prop_name = $value;    }    return $style;  }  /**   * parse selector + rulesets   *   * @param string $str  CSS selectors and rulesets   */  private function _parse_sections($str) {    // Pre-process: collapse all whitespace and strip whitespace around '>',    // '.', ':', '+', '#'        $patterns = array("/[\\s\n]+/", "/\\s+([>.:+#])\\s+/");    $replacements = array(" ", "\\1");    $str = preg_replace($patterns, $replacements, $str);    $sections = explode("}", $str);    foreach ($sections as $sect) {      $i = mb_strpos($sect, "{");      $selectors = explode(",", mb_substr($sect, 0, $i));      $style = $this->_parse_properties(trim(mb_substr($sect, $i+1)));      // Assign it to the selected elements      foreach ($selectors as $selector) {        $selector = trim($selector);                if ($selector == "")          continue;                $this->add_style($selector, $style);      }    }  }    /**   * dumps the entire stylesheet as a string   *   * Generates a string of each selector and associated style in the   * Stylesheet.  Useful for debugging.   *   * @return string   */  function __toString() {    $str = "";    foreach ($this->_styles as $selector => $style)       $str .= "$selector => " . $style->__toString() . "\n";    return $str;  }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?