1<?php 2 3require_once(HTML2PS_DIR.'css.constants.inc.php'); 4 5class CSSPageSelector { 6 var $_type; 7 8 function CSSPageSelector($type) { 9 $this->set_type($type); 10 } 11 12 function get_type() { 13 return $this->_type; 14 } 15 16 function set_type($type) { 17 $this->_type = $type; 18 } 19} 20 21class CSSPageSelectorAll extends CSSPageSelector { 22 function CSSPageSelectorAll() { 23 $this->CSSPageSelector(CSS_PAGE_SELECTOR_ALL); 24 } 25} 26 27class CSSPageSelectorNamed extends CSSPageSelector { 28 var $_name; 29 30 function CSSPageSelectorNamed($name) { 31 $this->CSSPageSelector(CSS_PAGE_SELECTOR_NAMED); 32 $this->set_name($name); 33 } 34 35 function get_name() { 36 return $this->_name; 37 } 38 39 function set_name($name) { 40 $this->_name = $name; 41 } 42} 43 44class CSSPageSelectorFirst extends CSSPageSelector { 45 function CSSPageSelectorFirst() { 46 $this->CSSPageSelector(CSS_PAGE_SELECTOR_FIRST); 47 } 48} 49 50class CSSPageSelectorLeft extends CSSPageSelector { 51 function CSSPageSelectorLeft() { 52 $this->CSSPageSelector(CSS_PAGE_SELECTOR_LEFT); 53 } 54} 55 56class CSSPageSelectorRight extends CSSPageSelector { 57 function CSSPageSelectorRight() { 58 $this->CSSPageSelector(CSS_PAGE_SELECTOR_RIGHT); 59 } 60} 61 62class CSSAtRulePage { 63 var $selector; 64 var $margin_boxes; 65 var $css; 66 67 function CSSAtRulePage($selector, &$pipeline) { 68 $this->selector = $selector; 69 $this->margin_boxes = array(); 70 71 $this->css =& new CSSPropertyCollection(); 72 } 73 74 function &getSelector() { 75 return $this->selector; 76 } 77 78 function getAtRuleMarginBoxes() { 79 return $this->margin_boxes; 80 } 81 82 /** 83 * Note that only one margin box rule could be added; subsequent adds 84 * will overwrite existing data 85 */ 86 function addAtRuleMarginBox($rule) { 87 $this->margin_boxes[$rule->getSelector()] = $rule; 88 } 89 90 function setCSSProperty($property) { 91 $this->css->add_property($property); 92 } 93} 94 95class CSSAtRuleMarginBox { 96 var $selector; 97 var $css; 98 99 /** 100 * TODO: CSS_TEXT_ALIGN should get top/bottom values by default for 101 * left-top, left-bottom, right-top and right-bottom boxes 102 */ 103 function CSSAtRuleMarginBox($selector, &$pipeline) { 104 $this->selector = $selector; 105 106 $css = "-html2ps-html-content: ''; content: ''; width: auto; height: auto; margin: 0; border: none; padding: 0; font: auto;"; 107 $css = $css . $this->_getCSSDefaults($selector); 108 109 $this->css = new CSSRule(array( 110 array(SELECTOR_ANY), 111 parse_css_properties($css, $null), 112 '', 113 null), 114 $pipeline); 115 } 116 117 function getSelector() { 118 return $this->selector; 119 } 120 121 function _getCSSDefaults($selector) { 122 $text_align_handler =& CSS::get_handler(CSS_TEXT_ALIGN); 123 $vertical_align_handler =& CSS::get_handler(CSS_VERTICAL_ALIGN); 124 125 switch ($selector) { 126 case CSS_MARGIN_BOX_SELECTOR_TOP: 127 return 'text-align: left; vertical-align: middle'; 128 case CSS_MARGIN_BOX_SELECTOR_TOP_LEFT_CORNER: 129 return 'text-align: right; vertical-align: middle'; 130 case CSS_MARGIN_BOX_SELECTOR_TOP_LEFT: 131 return 'text-align: left; vertical-align: middle'; 132 case CSS_MARGIN_BOX_SELECTOR_TOP_CENTER: 133 return 'text-align: center; vertical-align: middle'; 134 case CSS_MARGIN_BOX_SELECTOR_TOP_RIGHT: 135 return 'text-align: right; vertical-align: middle'; 136 case CSS_MARGIN_BOX_SELECTOR_TOP_RIGHT_CORNER: 137 return 'text-align: left; vertical-align: middle'; 138 case CSS_MARGIN_BOX_SELECTOR_BOTTOM: 139 return 'text-align: left; vertical-align: middle'; 140 case CSS_MARGIN_BOX_SELECTOR_BOTTOM_LEFT_CORNER: 141 return 'text-align: right; vertical-align: middle'; 142 case CSS_MARGIN_BOX_SELECTOR_BOTTOM_LEFT: 143 return 'text-align: left; vertical-align: middle'; 144 case CSS_MARGIN_BOX_SELECTOR_BOTTOM_CENTER: 145 return 'text-align: center; vertical-align: middle'; 146 case CSS_MARGIN_BOX_SELECTOR_BOTTOM_RIGHT: 147 return 'text-align: right; vertical-align: middle'; 148 case CSS_MARGIN_BOX_SELECTOR_BOTTOM_RIGHT_CORNER: 149 return 'text-align: left; vertical-align: middle'; 150 case CSS_MARGIN_BOX_SELECTOR_LEFT_TOP: 151 return 'text-align: center; vertical-align: top'; 152 case CSS_MARGIN_BOX_SELECTOR_LEFT_MIDDLE: 153 return 'text-align: center; vertical-align: middle'; 154 case CSS_MARGIN_BOX_SELECTOR_LEFT_BOTTOM: 155 return 'text-align: center; vertical-align: bottom'; 156 case CSS_MARGIN_BOX_SELECTOR_RIGHT_TOP: 157 return 'text-align: center; vertical-align: top'; 158 case CSS_MARGIN_BOX_SELECTOR_RIGHT_MIDDLE: 159 return 'text-align: center; vertical-align: middle'; 160 case CSS_MARGIN_BOX_SELECTOR_RIGHT_BOTTOM: 161 return 'text-align: center; vertical-align: bottom'; 162 }; 163 } 164 165 function setCSSProperty($property) { 166 $this->css->add_property($property); 167 } 168 169 function &get_css_property($code) { 170 return $this->css->get_property($code); 171 } 172} 173 174/** 175 * Handle @page rules in current CSS media As parse_css_media is 176 * called for selected media only, we can store data to global CSS 177 * state - no data should be ignored 178 * 179 * at-page rules will be removed after parsing 180 * 181 * @param $css String contains raw CSS data to be processed 182 * @return String CSS text without at-page rules 183 */ 184function parse_css_atpage_rules($css, &$css_ruleset) { 185 while (preg_match('/^(.*?)@page(.*)/is', $css, $matches)) { 186 $data = $matches[2]; 187 $css = $matches[1].parse_css_atpage_rule(trim($data), $css_ruleset); 188 }; 189 return $css; 190} 191 192function parse_css_atpage_rule($css, &$css_ruleset) { 193 /** 194 * Extract selector and left bracket 195 */ 196 if (!preg_match('/^(.*?){(.*)$/is', $css, $matches)) { 197 error_log('No selector and/or open bracket found in @page rule'); 198 return $css; 199 }; 200 $raw_selector = trim($matches[1]); 201 $css = trim($matches[2]); 202 203 $selector =& parse_css_atpage_selector($raw_selector); 204 $at_rule =& new CSSAtRulePage($selector, $css_ruleset); 205 206 /** 207 * The body of @page rule may contain declaraction (detected by ';'), 208 * margin box at-rule (detected by @top and similar tokens) or } indicating termination of 209 * @page rule 210 */ 211 while (preg_match('/^(.*?)(;|@|})(.*)$/is', $css, $matches)) { 212 $raw_prefix = trim($matches[1]); 213 $raw_token = trim($matches[2]); 214 $raw_suffix = trim($matches[3]); 215 216 switch ($raw_token) { 217 case ';': 218 /** 219 * Normal declaration (text contained in $raw_prefix 220 */ 221 parse_css_atpage_declaration($raw_prefix, $at_rule, $css_ruleset); 222 $css = $raw_suffix; 223 break; 224 225 case '@': 226 /** 227 * Margin box at-rule 228 */ 229 $css = parse_css_atpage_margin_box($raw_suffix, $at_rule, $css_ruleset); 230 break; 231 232 case '}': 233 /** 234 * End-of-rule 235 */ 236 $css_ruleset->add_at_rule_page($at_rule); 237 return $raw_suffix; 238 }; 239 }; 240 241 /** 242 * Note that we should normally exit via '}' token handler above 243 */ 244 error_log('No close bracket found in @page rule'); 245 $css_ruleset->add_at_rule_page($at_rule); 246 return $css; 247} 248 249/** 250 * Parses CSS at-page rule selector; syntax of this selector can be seen in 251 * CSS 3 specification at http://www.w3.org/TR/css3-page/#syntax-page-selector 252 * 253 * 254 */ 255function &parse_css_atpage_selector($selector) { 256 switch ($selector) { 257 case '': 258 $selector =& new CSSPageSelectorAll(); 259 return $selector; 260 case ':first': 261 $selector =& new CSSPageSelectorFirst(); 262 return $selector; 263 case ':left': 264 $selector =& new CSSPageSelectorLeft(); 265 return $selector; 266 case ':right': 267 $selector =& new CSSPageSelectorRight(); 268 return $selector; 269 default: 270 if (CSS::is_identifier($selector)) { 271 $selector =& new CSSPageSelectorNamed($selector); 272 return $selector; 273 } else { 274 error_log(sprintf('Unknown page selector in @page rule: \'%s\'', $selector)); 275 $selector =& new CSSPageSelectorAll(); 276 return $selector; 277 }; 278 }; 279} 280 281function parse_css_atpage_margin_box($css, &$at_rule, &$pipeline) { 282 if (!preg_match("/^([-\w]*)\s*{(.*)/is",$css,$matches)) { 283 error_log("Invalid margin box at-rule format"); 284 return $css; 285 }; 286 287 $raw_margin_box_selector = trim($matches[1]); 288 $css = trim($matches[2]); 289 290 $margin_box_selector = parse_css_atpage_margin_box_selector($raw_margin_box_selector); 291 $at_rule_margin_box = new CSSAtRuleMarginBox($margin_box_selector, $pipeline); 292 293 /** 294 * The body of margin box at-rule may contain declaraction (detected 295 * by ';'), or } indicating termination of at-rule 296 */ 297 while (preg_match('/^(.*?)(;|})(.*)$/is', $css, $matches)) { 298 $raw_prefix = trim($matches[1]); 299 $raw_token = trim($matches[2]); 300 $raw_suffix = trim($matches[3]); 301 302 switch ($raw_token) { 303 case ';': 304 /** 305 * Normal declaration (text contained in $raw_prefix 306 */ 307 parse_css_atpage_margin_box_declaration($raw_prefix, $at_rule_margin_box, $pipeline); 308 $css = $raw_suffix; 309 break; 310 311 case '}': 312 /** 313 * End-of-rule 314 */ 315 $at_rule->addAtRuleMarginBox($at_rule_margin_box); 316 return $raw_suffix; 317 }; 318 }; 319 320 /** 321 * Note that we should normally exit via '}' token handler above 322 */ 323 error_log('No close bracket found in margin box at-rule'); 324 $at_rule->addAtRuleMarginBox($at_rule_margin_box); 325 return $css; 326} 327 328function parse_css_atpage_margin_box_selector($css) { 329 switch ($css) { 330 case 'top': 331 return CSS_MARGIN_BOX_SELECTOR_TOP; 332 case 'top-left-corner': 333 return CSS_MARGIN_BOX_SELECTOR_TOP_LEFT_CORNER; 334 case 'top-left': 335 return CSS_MARGIN_BOX_SELECTOR_TOP_LEFT; 336 case 'top-center': 337 return CSS_MARGIN_BOX_SELECTOR_TOP_CENTER; 338 case 'top-right': 339 return CSS_MARGIN_BOX_SELECTOR_TOP_RIGHT; 340 case 'top-right-corner': 341 return CSS_MARGIN_BOX_SELECTOR_TOP_RIGHT_CORNER; 342 case 'bottom': 343 return CSS_MARGIN_BOX_SELECTOR_BOTTOM; 344 case 'bottom-left-corner': 345 return CSS_MARGIN_BOX_SELECTOR_BOTTOM_LEFT_CORNER; 346 case 'bottom-left': 347 return CSS_MARGIN_BOX_SELECTOR_BOTTOM_LEFT; 348 case 'bottom-center': 349 return CSS_MARGIN_BOX_SELECTOR_BOTTOM_CENTER; 350 case 'bottom-right': 351 return CSS_MARGIN_BOX_SELECTOR_BOTTOM_RIGHT; 352 case 'bottom-right-corner': 353 return CSS_MARGIN_BOX_SELECTOR_BOTTOM_RIGHT_CORNER; 354 case 'left-top': 355 return CSS_MARGIN_BOX_SELECTOR_LEFT_TOP; 356 case 'left-middle': 357 return CSS_MARGIN_BOX_SELECTOR_LEFT_MIDDLE; 358 case 'left-bottom': 359 return CSS_MARGIN_BOX_SELECTOR_LEFT_BOTTOM; 360 case 'right-top': 361 return CSS_MARGIN_BOX_SELECTOR_RIGHT_TOP; 362 case 'right-middle': 363 return CSS_MARGIN_BOX_SELECTOR_RIGHT_MIDDLE; 364 case 'right-bottom': 365 return CSS_MARGIN_BOX_SELECTOR_RIGHT_BOTTOM; 366 default: 367 error_log(sprintf('Unrecognized margin box selector: \'%s\'', $css)); 368 return CSS_MARGIN_BOX_SELECTOR_TOP; 369 } 370}; 371 372function parse_css_atpage_declaration($css, &$at_rule, &$pipeline) { 373 $parsed =& parse_css_property($css, $pipeline); 374 375 if (!is_null($parsed)) { 376 $properties = $parsed->getPropertiesSortedByPriority(); 377 foreach ($properties as $property) { 378 $at_rule->setCSSProperty($property); 379 }; 380 }; 381} 382 383function parse_css_atpage_margin_box_declaration($css, &$at_rule, &$pipeline) { 384 $parsed =& parse_css_property($css, $pipeline); 385 386 if (!is_null($parsed)) { 387 $properties = $parsed->getPropertiesSortedByPriority(); 388 foreach ($properties as $property) { 389 $at_rule->setCSSProperty($property); 390 }; 391 }; 392} 393 394?>