csstidy_print

CSS Printing class.

Defined (1)

The class is defined in the following location(s).

/lib/CSSTidy/class.csstidy_print.php  
  1. class csstidy_print { 
  2.  
  3. /** 
  4. * csstidy object 
  5. * @var object 
  6. */ 
  7. public $parser; 
  8.  
  9. /** 
  10. * Saves the input CSS string 
  11. * @var string 
  12. * @access private 
  13. */ 
  14. public $input_css = ''; 
  15. /** 
  16. * Saves the formatted CSS string 
  17. * @var string 
  18. * @access public 
  19. */ 
  20. public $output_css = ''; 
  21. /** 
  22. * Saves the formatted CSS string (plain text) 
  23. * @var string 
  24. * @access public 
  25. */ 
  26. public $output_css_plain = ''; 
  27.  
  28. /** 
  29. * Constructor 
  30. * @param array $css contains the class csstidy 
  31. * @access private 
  32. * @version 1.0 
  33. */ 
  34. public function __construct($css) { 
  35. $this->parser = $css; 
  36. $this->css = & $css->css; 
  37. $this->template = & $css->template; 
  38. $this->tokens = & $css->tokens; 
  39. $this->charset = & $css->charset; 
  40. $this->import = & $css->import; 
  41. $this->namespace = & $css->namespace; 
  42.  
  43. /** 
  44. * Resets output_css and output_css_plain (new css code) 
  45. * @access private 
  46. * @version 1.0 
  47. */ 
  48. public function _reset() { 
  49. $this->output_css = ''; 
  50. $this->output_css_plain = ''; 
  51.  
  52. /** 
  53. * Returns the CSS code as plain text 
  54. * @param string $default_media default @media to add to selectors without any @media 
  55. * @return string 
  56. * @access public 
  57. * @version 1.0 
  58. */ 
  59. public function plain($default_media='') { 
  60. $this->_print(true, $default_media); 
  61. return $this->output_css_plain; 
  62.  
  63. /** 
  64. * Returns the formatted CSS code 
  65. * @param string $default_media default @media to add to selectors without any @media 
  66. * @return string 
  67. * @access public 
  68. * @version 1.0 
  69. */ 
  70. public function formatted($default_media='') { 
  71. $this->_print(false, $default_media); 
  72. return $this->output_css; 
  73.  
  74. /** 
  75. * Returns the formatted CSS code to make a complete webpage 
  76. * @param string $doctype shorthand for the document type 
  77. * @param bool $externalcss indicates whether styles to be attached internally or as an external stylesheet 
  78. * @param string $title title to be added in the head of the document 
  79. * @param string $lang two-letter language code to be added to the output 
  80. * @return string 
  81. * @access public 
  82. * @version 1.4 
  83. */ 
  84. public function formatted_page($doctype='html5', $externalcss=true, $title='', $lang='en') { 
  85. switch ($doctype) { 
  86. case 'html5': 
  87. $doctype_output = '<!DOCTYPE html>'; 
  88. break; 
  89. case 'xhtml1.0strict': 
  90. $doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  91. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'; 
  92. break; 
  93. case 'xhtml1.1': 
  94. default: 
  95. $doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
  96. "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'; 
  97. break; 
  98.  
  99. $output = $cssparsed = ''; 
  100. $this->output_css_plain = & $output; 
  101.  
  102. $output .= $doctype_output . "\n" . '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . $lang . '"'; 
  103. $output .= ( $doctype === 'xhtml1.1') ? '>' : ' lang="' . $lang . '">'; 
  104. $output .= "\n<head>\n <title>$title</title>"; 
  105.  
  106. if ($externalcss) { 
  107. $output .= "\n <style type=\"text/css\">\n"; 
  108. $cssparsed = file_get_contents('cssparsed.css'); 
  109. $output .= $cssparsed; // Adds an invisible BOM or something, but not in css_optimised.php 
  110. $output .= "\n</style>"; 
  111. } else { 
  112. $output .= "\n" . ' <link rel="stylesheet" type="text/css" href="cssparsed.css" />'; 
  113. $output .= "\n</head>\n<body><code id=\"copytext\">"; 
  114. $output .= $this->formatted(); 
  115. $output .= '</code>' . "\n" . '</body></html>'; 
  116. return $this->output_css_plain; 
  117.  
  118. /** 
  119. * Returns the formatted CSS Code and saves it into $this->output_css and $this->output_css_plain 
  120. * @param bool $plain plain text or not 
  121. * @param string $default_media default @media to add to selectors without any @media 
  122. * @access private 
  123. * @version 2.0 
  124. */ 
  125. public function _print($plain = false, $default_media='') { 
  126. if ($this->output_css && $this->output_css_plain) { 
  127. return; 
  128.  
  129. $output = ''; 
  130. if (!$this->parser->get_cfg('preserve_css')) { 
  131. $this->_convert_raw_css($default_media); 
  132.  
  133. $template = & $this->template; 
  134.  
  135. if ($plain) { 
  136. $template = array_map('strip_tags', $template); 
  137.  
  138. if ($this->parser->get_cfg('timestamp')) { 
  139. array_unshift($this->tokens, array(COMMENT, ' CSSTidy ' . $this->parser->version . ': ' . date('r') . ' ')); 
  140.  
  141. if (!empty($this->charset)) { 
  142. $output .= $template[0] . '@charset ' . $template[5] . $this->charset . $template[6] . $template[13]; 
  143.  
  144. if (!empty($this->import)) { 
  145. for ($i = 0, $size = count($this->import); $i < $size; $i++) { 
  146. if (substr($this->import[$i], 0, 4) === 'url(' && substr($this->import[$i], -1, 1) === ')') { 
  147. $this->import[$i] = '"' . substr($this->import[$i], 4, -1) . '"'; 
  148. $this->parser->log('Optimised @import : Removed "url("', 'Information'); 
  149. else if (!preg_match('/^".+"$/', $this->import[$i])) { 
  150. // fixes a bug for @import ".." instead of the expected @import url("..") 
  151. // If it comes in due to @import ".." the "" will be missing and the output will become @import .. (which is an error) 
  152. $this->import[$i] = '"' . $this->import[$i] . '"'; 
  153.  
  154. $output .= $template[0] . '@import ' . $template[5] . $this->import[$i] . $template[6] . $template[13]; 
  155.  
  156. if (!empty($this->namespace)) { 
  157. if (($p=strpos($this->namespace, "url("))!==false && substr($this->namespace, -1, 1) === ')') { 
  158. $this->namespace = substr_replace($this->namespace, '"', $p, 4); 
  159. $this->namespace = substr($this->namespace, 0, -1) . '"'; 
  160. $this->parser->log('Optimised @namespace : Removed "url("', 'Information'); 
  161. $output .= $template[0] . '@namespace ' . $template[5] . $this->namespace . $template[6] . $template[13]; 
  162.  
  163. $in_at_out = ''; 
  164. $out = & $output; 
  165.  
  166. foreach ($this->tokens as $key => $token) { 
  167. switch ($token[0]) { 
  168. case AT_START: 
  169. $out .= $template[0] . $this->_htmlsp($token[1], $plain) . $template[1]; 
  170. $out = & $in_at_out; 
  171. break; 
  172.  
  173. case SEL_START: 
  174. if ($this->parser->get_cfg('lowercase_s')) 
  175. $token[1] = strtolower($token[1]); 
  176. $out .= ( $token[1]{0} !== '@') ? $template[2] . $this->_htmlsp($token[1], $plain) : $template[0] . $this->_htmlsp($token[1], $plain); 
  177. $out .= $template[3]; 
  178. break; 
  179.  
  180. case PROPERTY: 
  181. if ($this->parser->get_cfg('case_properties') === 2) { 
  182. $token[1] = strtoupper($token[1]); 
  183. } elseif ($this->parser->get_cfg('case_properties') === 1) { 
  184. $token[1] = strtolower($token[1]); 
  185. $out .= $template[4] . $this->_htmlsp($token[1], $plain) . ':' . $template[5]; 
  186. break; 
  187.  
  188. case VALUE: 
  189. $out .= $this->_htmlsp($token[1], $plain); 
  190. if ($this->_seeknocomment($key, 1) == SEL_END && $this->parser->get_cfg('remove_last_;')) { 
  191. $out .= str_replace(';', '', $template[6]); 
  192. } else { 
  193. $out .= $template[6]; 
  194. break; 
  195.  
  196. case SEL_END: 
  197. $out .= $template[7]; 
  198. if ($this->_seeknocomment($key, 1) != AT_END) 
  199. $out .= $template[8]; 
  200. break; 
  201.  
  202. case AT_END: 
  203. $out = & $output; 
  204. $in_at_out = str_replace("\n\n", "\r\n", $in_at_out); // don't fill empty lines 
  205. $in_at_out = str_replace("\n", "\n" . $template[10], $in_at_out); 
  206. $in_at_out = str_replace("\r\n", "\n\n", $in_at_out); 
  207. $out .= $template[10] . $in_at_out . $template[9]; 
  208. $in_at_out = ''; 
  209. break; 
  210.  
  211. case COMMENT: 
  212. $out .= $template[11] . '/*' . $this->_htmlsp($token[1], $plain) . '*/' . $template[12]; 
  213. break; 
  214.  
  215. $output = trim($output); 
  216.  
  217. if (!$plain) { 
  218. $this->output_css = $output; 
  219. $this->_print(true); 
  220. } else { 
  221. // If using spaces in the template, don't want these to appear in the plain output 
  222. $this->output_css_plain = str_replace(' ', '', $output); 
  223.  
  224. /** 
  225. * Gets the next token type which is $move away from $key, excluding comments 
  226. * @param integer $key current position 
  227. * @param integer $move move this far 
  228. * @return mixed a token type 
  229. * @access private 
  230. * @version 1.0 
  231. */ 
  232. public function _seeknocomment($key, $move) { 
  233. $go = ($move > 0) ? 1 : -1; 
  234. for ($i = $key + 1; abs($key - $i) - 1 < abs($move); $i += $go) { 
  235. if (!isset($this->tokens[$i])) { 
  236. return; 
  237. if ($this->tokens[$i][0] == COMMENT) { 
  238. $move += 1; 
  239. continue; 
  240. return $this->tokens[$i][0]; 
  241.  
  242. /** 
  243. * Converts $this->css array to a raw array ($this->tokens) 
  244. * @param string $default_media default @media to add to selectors without any @media 
  245. * @access private 
  246. * @version 1.0 
  247. */ 
  248. public function _convert_raw_css($default_media='') { 
  249. $this->tokens = array(); 
  250. $sort_selectors = $this->parser->get_cfg('sort_selectors'); 
  251. $sort_properties = $this->parser->get_cfg('sort_properties'); 
  252.  
  253. foreach ($this->css as $medium => $val) { 
  254. if ($sort_selectors) 
  255. ksort($val); 
  256. if (intval($medium) < DEFAULT_AT) { 
  257. // un medium vide (contenant @font-face ou autre @) ne produit aucun conteneur 
  258. if (strlen(trim($medium))) { 
  259. $this->parser->_add_token(AT_START, $medium, true); 
  260. } elseif ($default_media) { 
  261. $this->parser->_add_token(AT_START, $default_media, true); 
  262.  
  263. foreach ($val as $selector => $vali) { 
  264. if ($sort_properties) 
  265. ksort($vali); 
  266. $this->parser->_add_token(SEL_START, $selector, true); 
  267.  
  268. $invalid = array( 
  269. '*' => array(), // IE7 hacks first 
  270. '_' => array(), // IE6 hacks 
  271. '/' => array(), // IE6 hacks 
  272. '-' => array() // IE6 hacks 
  273. ); 
  274. foreach ($vali as $property => $valj) { 
  275. if (strncmp($property, "//", 2)!==0) { 
  276. $matches = array(); 
  277. if ($sort_properties && preg_match('/^(\*|_|\/|-)(?!(ms|moz|o\b|xv|atsc|wap|khtml|webkit|ah|hp|ro|rim|tc)-)/', $property, $matches)) { 
  278. $invalid[$matches[1]][$property] = $valj; 
  279. } else { 
  280. $this->parser->_add_token(PROPERTY, $property, true); 
  281. $this->parser->_add_token(VALUE, $valj, true); 
  282. foreach ($invalid as $prefix => $props) { 
  283. foreach ($props as $property => $valj) { 
  284. $this->parser->_add_token(PROPERTY, $property, true); 
  285. $this->parser->_add_token(VALUE, $valj, true); 
  286. $this->parser->_add_token(SEL_END, $selector, true); 
  287.  
  288. if (intval($medium) < DEFAULT_AT) { 
  289. // un medium vide (contenant @font-face ou autre @) ne produit aucun conteneur 
  290. if (strlen(trim($medium))) { 
  291. $this->parser->_add_token(AT_END, $medium, true); 
  292. } elseif ($default_media) { 
  293. $this->parser->_add_token(AT_END, $default_media, true); 
  294.  
  295. /** 
  296. * Same as htmlspecialchars, only that chars are not replaced if $plain !== true. This makes print_code() cleaner. 
  297. * @param string $string 
  298. * @param bool $plain 
  299. * @return string 
  300. * @see csstidy_print::_print() 
  301. * @access private 
  302. * @version 1.0 
  303. */ 
  304. public function _htmlsp($string, $plain) { 
  305. if (!$plain) { 
  306. return htmlspecialchars($string, ENT_QUOTES, 'utf-8'); 
  307. return $string; 
  308.  
  309. /** 
  310. * Get compression ratio 
  311. * @access public 
  312. * @return float 
  313. * @version 1.2 
  314. */ 
  315. public function get_ratio() { 
  316. if (!$this->output_css_plain) { 
  317. $this->formatted(); 
  318. return round((strlen($this->input_css) - strlen($this->output_css_plain)) / strlen($this->input_css), 3) * 100; 
  319.  
  320. /** 
  321. * Get difference between the old and new code in bytes and prints the code if necessary. 
  322. * @access public 
  323. * @return string 
  324. * @version 1.1 
  325. */ 
  326. public function get_diff() { 
  327. if (!$this->output_css_plain) { 
  328. $this->formatted(); 
  329.  
  330. $diff = strlen($this->output_css_plain) - strlen($this->input_css); 
  331.  
  332. if ($diff > 0) { 
  333. return '+' . $diff; 
  334. } elseif ($diff == 0) { 
  335. return '+-' . $diff; 
  336.  
  337. return $diff; 
  338.  
  339. /** 
  340. * Get the size of either input or output CSS in KB 
  341. * @param string $loc default is "output" 
  342. * @access public 
  343. * @return integer 
  344. * @version 1.0 
  345. */ 
  346. public function size($loc = 'output') { 
  347. if ($loc === 'output' && !$this->output_css) { 
  348. $this->formatted(); 
  349.  
  350. if ($loc === 'input') { 
  351. return (strlen($this->input_css) / 1000); 
  352. } else { 
  353. return (strlen($this->output_css_plain) / 1000); 
  354.