1: <?php
2:
3: /**
4: * Validates shorthand CSS property font.
5: */
6: class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef
7: {
8:
9: /**
10: * Local copy of validators
11: * @type HTMLPurifier_AttrDef[]
12: * @note If we moved specific CSS property definitions to their own
13: * classes instead of having them be assembled at run time by
14: * CSSDefinition, this wouldn't be necessary. We'd instantiate
15: * our own copies.
16: */
17: protected $info = array();
18:
19: /**
20: * @param HTMLPurifier_Config $config
21: */
22: public function __construct($config)
23: {
24: $def = $config->getCSSDefinition();
25: $this->info['font-style'] = $def->info['font-style'];
26: $this->info['font-variant'] = $def->info['font-variant'];
27: $this->info['font-weight'] = $def->info['font-weight'];
28: $this->info['font-size'] = $def->info['font-size'];
29: $this->info['line-height'] = $def->info['line-height'];
30: $this->info['font-family'] = $def->info['font-family'];
31: }
32:
33: /**
34: * @param string $string
35: * @param HTMLPurifier_Config $config
36: * @param HTMLPurifier_Context $context
37: * @return bool|string
38: */
39: public function validate($string, $config, $context)
40: {
41: static $system_fonts = array(
42: 'caption' => true,
43: 'icon' => true,
44: 'menu' => true,
45: 'message-box' => true,
46: 'small-caption' => true,
47: 'status-bar' => true
48: );
49:
50: // regular pre-processing
51: $string = $this->parseCDATA($string);
52: if ($string === '') {
53: return false;
54: }
55:
56: // check if it's one of the keywords
57: $lowercase_string = strtolower($string);
58: if (isset($system_fonts[$lowercase_string])) {
59: return $lowercase_string;
60: }
61:
62: $bits = explode(' ', $string); // bits to process
63: $stage = 0; // this indicates what we're looking for
64: $caught = array(); // which stage 0 properties have we caught?
65: $stage_1 = array('font-style', 'font-variant', 'font-weight');
66: $final = ''; // output
67:
68: for ($i = 0, $size = count($bits); $i < $size; $i++) {
69: if ($bits[$i] === '') {
70: continue;
71: }
72: switch ($stage) {
73: case 0: // attempting to catch font-style, font-variant or font-weight
74: foreach ($stage_1 as $validator_name) {
75: if (isset($caught[$validator_name])) {
76: continue;
77: }
78: $r = $this->info[$validator_name]->validate(
79: $bits[$i],
80: $config,
81: $context
82: );
83: if ($r !== false) {
84: $final .= $r . ' ';
85: $caught[$validator_name] = true;
86: break;
87: }
88: }
89: // all three caught, continue on
90: if (count($caught) >= 3) {
91: $stage = 1;
92: }
93: if ($r !== false) {
94: break;
95: }
96: case 1: // attempting to catch font-size and perhaps line-height
97: $found_slash = false;
98: if (strpos($bits[$i], '/') !== false) {
99: list($font_size, $line_height) =
100: explode('/', $bits[$i]);
101: if ($line_height === '') {
102: // ooh, there's a space after the slash!
103: $line_height = false;
104: $found_slash = true;
105: }
106: } else {
107: $font_size = $bits[$i];
108: $line_height = false;
109: }
110: $r = $this->info['font-size']->validate(
111: $font_size,
112: $config,
113: $context
114: );
115: if ($r !== false) {
116: $final .= $r;
117: // attempt to catch line-height
118: if ($line_height === false) {
119: // we need to scroll forward
120: for ($j = $i + 1; $j < $size; $j++) {
121: if ($bits[$j] === '') {
122: continue;
123: }
124: if ($bits[$j] === '/') {
125: if ($found_slash) {
126: return false;
127: } else {
128: $found_slash = true;
129: continue;
130: }
131: }
132: $line_height = $bits[$j];
133: break;
134: }
135: } else {
136: // slash already found
137: $found_slash = true;
138: $j = $i;
139: }
140: if ($found_slash) {
141: $i = $j;
142: $r = $this->info['line-height']->validate(
143: $line_height,
144: $config,
145: $context
146: );
147: if ($r !== false) {
148: $final .= '/' . $r;
149: }
150: }
151: $final .= ' ';
152: $stage = 2;
153: break;
154: }
155: return false;
156: case 2: // attempting to catch font-family
157: $font_family =
158: implode(' ', array_slice($bits, $i, $size - $i));
159: $r = $this->info['font-family']->validate(
160: $font_family,
161: $config,
162: $context
163: );
164: if ($r !== false) {
165: $final .= $r . ' ';
166: // processing completed successfully
167: return rtrim($final);
168: }
169: return false;
170: }
171: }
172: return false;
173: }
174: }
175:
176: // vim: et sw=4 sts=4
177: