1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
17: class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
18: {
19:
20: public $name = 'ExtractStyleBlocks';
21: private $_styleMatches = array();
22: private $_tidy;
23:
24: public function __construct() {
25: $this->_tidy = new csstidy();
26: }
27:
28: 29: 30: 31:
32: protected function styleCallback($matches) {
33: $this->_styleMatches[] = $matches[1];
34: }
35:
36: 37: 38: 39:
40: public function preFilter($html, $config, $context) {
41: $tidy = $config->get('Filter.ExtractStyleBlocks.TidyImpl');
42: if ($tidy !== null) $this->_tidy = $tidy;
43: $html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html);
44: $style_blocks = $this->_styleMatches;
45: $this->_styleMatches = array();
46: $context->register('StyleBlocks', $style_blocks);
47: if ($this->_tidy) {
48: foreach ($style_blocks as &$style) {
49: $style = $this->cleanCSS($style, $config, $context);
50: }
51: }
52: return $html;
53: }
54:
55: 56: 57: 58: 59: 60: 61: 62:
63: public function cleanCSS($css, $config, $context) {
64:
65: $scope = $config->get('Filter.ExtractStyleBlocks.Scope');
66: if ($scope !== null) {
67: $scopes = array_map('trim', explode(',', $scope));
68: } else {
69: $scopes = array();
70: }
71:
72: $css = trim($css);
73: if (strncmp('<!--', $css, 4) === 0) {
74: $css = substr($css, 4);
75: }
76: if (strlen($css) > 3 && substr($css, -3) == '-->') {
77: $css = substr($css, 0, -3);
78: }
79: $css = trim($css);
80: $this->_tidy->parse($css);
81: $css_definition = $config->getDefinition('CSS');
82: foreach ($this->_tidy->css as $k => $decls) {
83:
84: $new_decls = array();
85: foreach ($decls as $selector => $style) {
86: $selector = trim($selector);
87: if ($selector === '') continue;
88: if ($selector[0] === '+') {
89: if ($selector !== '' && $selector[0] === '+') continue;
90: }
91: if (!empty($scopes)) {
92: $new_selector = array();
93: $selectors = array_map('trim', explode(',', $selector));
94: foreach ($scopes as $s1) {
95: foreach ($selectors as $s2) {
96: $new_selector[] = "$s1 $s2";
97: }
98: }
99: $selector = implode(', ', $new_selector);
100: }
101: foreach ($style as $name => $value) {
102: if (!isset($css_definition->info[$name])) {
103: unset($style[$name]);
104: continue;
105: }
106: $def = $css_definition->info[$name];
107: $ret = $def->validate($value, $config, $context);
108: if ($ret === false) unset($style[$name]);
109: else $style[$name] = $ret;
110: }
111: $new_decls[$selector] = $style;
112: }
113: $this->_tidy->css[$k] = $new_decls;
114: }
115:
116:
117: $this->_tidy->import = array();
118: $this->_tidy->charset = null;
119: $this->_tidy->namespace = null;
120: $css = $this->_tidy->print->plain();
121:
122:
123: if ($config->get('Filter.ExtractStyleBlocks.Escaping')) {
124: $css = str_replace(
125: array('<', '>', '&'),
126: array('\3C ', '\3E ', '\26 '),
127: $css
128: );
129: }
130: return $css;
131: }
132:
133: }
134:
135:
136: