Attribute_Translator

Translates HTML 4.0 attributes into CSS rules.

Defined (1)

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

/lib/dompdf/include/attribute_translator.cls.php  
  1. class Attribute_Translator { 
  2. static $_style_attr = "_html_style_attribute"; 
  3.  
  4. // Munged data originally from 
  5. // http://www.w3.org/TR/REC-html40/index/attributes.html 
  6. // http://www.cs.tut.fi/~jkorpela/html2css.html 
  7. static private $__ATTRIBUTE_LOOKUP = array( 
  8. //'caption' => array ( 'align' => '', ),  
  9. 'img' => array( 
  10. 'align' => array( 
  11. 'bottom' => 'vertical-align: baseline;',  
  12. 'middle' => 'vertical-align: middle;',  
  13. 'top' => 'vertical-align: top;',  
  14. 'left' => 'float: left;',  
  15. 'right' => 'float: right;' 
  16. ),  
  17. 'border' => 'border: %0.2F px solid;',  
  18. 'height' => 'height: %s px;',  
  19. 'hspace' => 'padding-left: %1$0.2F px; padding-right: %1$0.2F px;',  
  20. 'vspace' => 'padding-top: %1$0.2F px; padding-bottom: %1$0.2F px;',  
  21. 'width' => 'width: %s px;',  
  22. ),  
  23. 'table' => array( 
  24. 'align' => array( 
  25. 'left' => 'margin-left: 0; margin-right: auto;',  
  26. 'center' => 'margin-left: auto; margin-right: auto;',  
  27. 'right' => 'margin-left: auto; margin-right: 0;' 
  28. ),  
  29. 'bgcolor' => 'background-color: %s;',  
  30. 'border' => '!set_table_border',  
  31. 'cellpadding' => '!set_table_cellpadding', //'border-spacing: %0.2F; border-collapse: separate;',  
  32. 'cellspacing' => '!set_table_cellspacing',  
  33. 'frame' => array( 
  34. 'void' => 'border-style: none;',  
  35. 'above' => 'border-top-style: solid;',  
  36. 'below' => 'border-bottom-style: solid;',  
  37. 'hsides' => 'border-left-style: solid; border-right-style: solid;',  
  38. 'vsides' => 'border-top-style: solid; border-bottom-style: solid;',  
  39. 'lhs' => 'border-left-style: solid;',  
  40. 'rhs' => 'border-right-style: solid;',  
  41. 'box' => 'border-style: solid;',  
  42. 'border' => 'border-style: solid;' 
  43. ),  
  44. 'rules' => '!set_table_rules',  
  45. 'width' => 'width: %s;',  
  46. ),  
  47. 'hr' => array( 
  48. 'align' => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly 
  49. 'noshade' => 'border-style: solid;',  
  50. 'size' => '!set_hr_size', //'border-width: %0.2F px;',  
  51. 'width' => 'width: %s;',  
  52. ),  
  53. 'div' => array( 
  54. 'align' => 'text-align: %s;',  
  55. ),  
  56. 'h1' => array( 
  57. 'align' => 'text-align: %s;',  
  58. ),  
  59. 'h2' => array( 
  60. 'align' => 'text-align: %s;',  
  61. ),  
  62. 'h3' => array( 
  63. 'align' => 'text-align: %s;',  
  64. ),  
  65. 'h4' => array( 
  66. 'align' => 'text-align: %s;',  
  67. ),  
  68. 'h5' => array( 
  69. 'align' => 'text-align: %s;',  
  70. ),  
  71. 'h6' => array( 
  72. 'align' => 'text-align: %s;',  
  73. ),  
  74. 'p' => array( 
  75. 'align' => 'text-align: %s;',  
  76. ),  
  77. // 'col' => array( 
  78. // 'align' => '',  
  79. // 'valign' => '',  
  80. // ),  
  81. // 'colgroup' => array( 
  82. // 'align' => '',  
  83. // 'valign' => '',  
  84. // ),  
  85. 'tbody' => array( 
  86. 'align' => '!set_table_row_align',  
  87. 'valign' => '!set_table_row_valign',  
  88. ),  
  89. 'td' => array( 
  90. 'align' => 'text-align: %s;',  
  91. 'bgcolor' => '!set_background_color',  
  92. 'height' => 'height: %s;',  
  93. 'nowrap' => 'white-space: nowrap;',  
  94. 'valign' => 'vertical-align: %s;',  
  95. 'width' => 'width: %s;',  
  96. ),  
  97. 'tfoot' => array( 
  98. 'align' => '!set_table_row_align',  
  99. 'valign' => '!set_table_row_valign',  
  100. ),  
  101. 'th' => array( 
  102. 'align' => 'text-align: %s;',  
  103. 'bgcolor' => '!set_background_color',  
  104. 'height' => 'height: %s;',  
  105. 'nowrap' => 'white-space: nowrap;',  
  106. 'valign' => 'vertical-align: %s;',  
  107. 'width' => 'width: %s;',  
  108. ),  
  109. 'thead' => array( 
  110. 'align' => '!set_table_row_align',  
  111. 'valign' => '!set_table_row_valign',  
  112. ),  
  113. 'tr' => array( 
  114. 'align' => '!set_table_row_align',  
  115. 'bgcolor' => '!set_table_row_bgcolor',  
  116. 'valign' => '!set_table_row_valign',  
  117. ),  
  118. 'body' => array( 
  119. 'background' => 'background-image: url(%s);',  
  120. 'bgcolor' => '!set_background_color',  
  121. 'link' => '!set_body_link',  
  122. 'text' => '!set_color',  
  123. ),  
  124. 'br' => array( 
  125. 'clear' => 'clear: %s;',  
  126. ),  
  127. 'basefont' => array( 
  128. 'color' => '!set_color',  
  129. 'face' => 'font-family: %s;',  
  130. 'size' => '!set_basefont_size',  
  131. ),  
  132. 'font' => array( 
  133. 'color' => '!set_color',  
  134. 'face' => 'font-family: %s;',  
  135. 'size' => '!set_font_size',  
  136. ),  
  137. 'dir' => array( 
  138. 'compact' => 'margin: 0.5em 0;',  
  139. ),  
  140. 'dl' => array( 
  141. 'compact' => 'margin: 0.5em 0;',  
  142. ),  
  143. 'menu' => array( 
  144. 'compact' => 'margin: 0.5em 0;',  
  145. ),  
  146. 'ol' => array( 
  147. 'compact' => 'margin: 0.5em 0;',  
  148. 'start' => 'counter-reset: -dompdf-default-counter %d;',  
  149. 'type' => 'list-style-type: %s;',  
  150. ),  
  151. 'ul' => array( 
  152. 'compact' => 'margin: 0.5em 0;',  
  153. 'type' => 'list-style-type: %s;',  
  154. ),  
  155. 'li' => array( 
  156. 'type' => 'list-style-type: %s;',  
  157. 'value' => 'counter-reset: -dompdf-default-counter %d;',  
  158. ),  
  159. 'pre' => array( 
  160. 'width' => 'width: %s;',  
  161. ),  
  162. ); 
  163.  
  164. static protected $_last_basefont_size = 3; 
  165. static protected $_font_size_lookup = array( 
  166. // For basefont support 
  167. -3 => "4pt",  
  168. -2 => "5pt",  
  169. -1 => "6pt",  
  170. 0 => "7pt",  
  171.  
  172. 1 => "8pt",  
  173. 2 => "10pt",  
  174. 3 => "12pt",  
  175. 4 => "14pt",  
  176. 5 => "18pt",  
  177. 6 => "24pt",  
  178. 7 => "34pt",  
  179.  
  180. // For basefont support 
  181. 8 => "48pt",  
  182. 9 => "44pt",  
  183. 10 => "52pt",  
  184. 11 => "60pt",  
  185. ); 
  186.  
  187. /** 
  188. * @param Frame $frame 
  189. */ 
  190. static function translate_attributes(Frame $frame) { 
  191. $node = $frame->get_node(); 
  192. $tag = $node->nodeName; 
  193.  
  194. if ( !isset(self::$__ATTRIBUTE_LOOKUP[$tag]) ) { 
  195. return; 
  196.  
  197. $valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag]; 
  198. $attrs = $node->attributes; 
  199. $style = rtrim($node->getAttribute(self::$_style_attr), "; "); 
  200. if ( $style != "" ) { 
  201. $style .= ";"; 
  202.  
  203. foreach ($attrs as $attr => $attr_node ) { 
  204. if ( !isset($valid_attrs[$attr]) ) { 
  205. continue; 
  206.  
  207. $value = $attr_node->value; 
  208.  
  209. $target = $valid_attrs[$attr]; 
  210.  
  211. // Look up $value in $target, if $target is an array: 
  212. if ( is_array($target) ) { 
  213. if ( isset($target[$value]) ) { 
  214. $style .= " " . self::_resolve_target($node, $target[$value], $value); 
  215. else { 
  216. // otherwise use target directly 
  217. $style .= " " . self::_resolve_target($node, $target, $value); 
  218.  
  219. if ( !is_null($style) ) { 
  220. $style = ltrim($style); 
  221. $node->setAttribute(self::$_style_attr, $style); 
  222.  
  223.  
  224. /** 
  225. * @param DOMNode $node 
  226. * @param string $target 
  227. * @param string $value 
  228. * @return string 
  229. */ 
  230. static protected function _resolve_target(DOMNode $node, $target, $value) { 
  231. if ( $target[0] === "!" ) { 
  232. // Function call 
  233. $func = "_" . mb_substr($target, 1); 
  234. return self::$func($node, $value); 
  235.  
  236. return $value ? sprintf($target, $value) : ""; 
  237.  
  238. /** 
  239. * @param DOMElement $node 
  240. * @param string $new_style 
  241. */ 
  242. static function append_style(DOMElement $node, $new_style) { 
  243. $style = rtrim($node->getAttribute(self::$_style_attr), ";"); 
  244. $style .= $new_style; 
  245. $style = ltrim($style, ";"); 
  246. $node->setAttribute(self::$_style_attr, $style); 
  247.  
  248. /** 
  249. * @param DOMNode $node 
  250. * @return DOMNodeList|DOMElement[] 
  251. */ 
  252. static protected function get_cell_list(DOMNode $node) { 
  253. $xpath = new DOMXpath($node->ownerDocument); 
  254.  
  255. switch($node->nodeName) { 
  256. default: 
  257. case "table": 
  258. $query = "tr/td | thead/tr/td | tbody/tr/td | tfoot/tr/td | tr/th | thead/tr/th | tbody/tr/th | tfoot/tr/th"; 
  259. break; 
  260.  
  261. case "tbody": 
  262. case "tfoot": 
  263. case "thead": 
  264. $query = "tr/td | tr/th"; 
  265. break; 
  266.  
  267. case "tr": 
  268. $query = "td | th"; 
  269. break; 
  270.  
  271. return $xpath->query($query, $node); 
  272.  
  273. /** 
  274. * @param string $value 
  275. * @return string 
  276. */ 
  277. static protected function _get_valid_color($value) { 
  278. if ( preg_match('/^#?([0-9A-F]{6})$/i', $value, $matches) ) { 
  279. $value = "#$matches[1]"; 
  280.  
  281. return $value; 
  282.  
  283. /** 
  284. * @param DOMElement $node 
  285. * @param string $value 
  286. * @return string 
  287. */ 
  288. static protected function _set_color(DOMElement $node, $value) { 
  289. $value = self::_get_valid_color($value); 
  290. return "color: $value;"; 
  291.  
  292. /** 
  293. * @param DOMElement $node 
  294. * @param string $value 
  295. * @return string 
  296. */ 
  297. static protected function _set_background_color(DOMElement $node, $value) { 
  298. $value = self::_get_valid_color($value); 
  299. return "background-color: $value;"; 
  300.  
  301. /** 
  302. * @param DOMElement $node 
  303. * @param string $value 
  304. * @return null 
  305. */ 
  306. static protected function _set_table_cellpadding(DOMElement $node, $value) { 
  307. $cell_list = self::get_cell_list($node); 
  308.  
  309. foreach ($cell_list as $cell) { 
  310. self::append_style($cell, "; padding: {$value}px;"); 
  311.  
  312. return null; 
  313.  
  314. /** 
  315. * @param DOMElement $node 
  316. * @param string $value 
  317. * @return string 
  318. */ 
  319. static protected function _set_table_border(DOMElement $node, $value) { 
  320. $cell_list = self::get_cell_list($node); 
  321.  
  322. foreach ($cell_list as $cell) { 
  323. $style = rtrim($cell->getAttribute(self::$_style_attr)); 
  324. $style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;"; 
  325. $style = ltrim($style, ";"); 
  326. $cell->setAttribute(self::$_style_attr, $style); 
  327.  
  328. $style = rtrim($node->getAttribute(self::$_style_attr), ";"); 
  329. $style .= "; border-width: $value" . "px; "; 
  330. return ltrim($style, "; "); 
  331.  
  332. /** 
  333. * @param DOMElement $node 
  334. * @param string $value 
  335. * @return string 
  336. */ 
  337. static protected function _set_table_cellspacing(DOMElement $node, $value) { 
  338. $style = rtrim($node->getAttribute(self::$_style_attr), ";"); 
  339.  
  340. if ( $value == 0 ) { 
  341. $style .= "; border-collapse: collapse;"; 
  342. else { 
  343. $style .= "; border-spacing: {$value}px; border-collapse: separate;"; 
  344.  
  345. return ltrim($style, ";"); 
  346.  
  347. /** 
  348. * @param DOMElement $node 
  349. * @param string $value 
  350. * @return null|string 
  351. */ 
  352. static protected function _set_table_rules(DOMElement $node, $value) { 
  353. $new_style = "; border-collapse: collapse;"; 
  354.  
  355. switch ($value) { 
  356. case "none": 
  357. $new_style .= "border-style: none;"; 
  358. break; 
  359.  
  360. case "groups": 
  361. // FIXME: unsupported 
  362. return null; 
  363.  
  364. case "rows": 
  365. $new_style .= "border-style: solid none solid none; border-width: 1px; "; 
  366. break; 
  367.  
  368. case "cols": 
  369. $new_style .= "border-style: none solid none solid; border-width: 1px; "; 
  370. break; 
  371.  
  372. case "all": 
  373. $new_style .= "border-style: solid; border-width: 1px; "; 
  374. break; 
  375.  
  376. default: 
  377. // Invalid value 
  378. return null; 
  379.  
  380. $cell_list = self::get_cell_list($node); 
  381.  
  382. foreach ($cell_list as $cell) { 
  383. $style = $cell->getAttribute(self::$_style_attr); 
  384. $style .= $new_style; 
  385. $cell->setAttribute(self::$_style_attr, $style); 
  386.  
  387. $style = rtrim($node->getAttribute(self::$_style_attr), ";"); 
  388. $style .= "; border-collapse: collapse; "; 
  389.  
  390. return ltrim($style, "; "); 
  391.  
  392. /** 
  393. * @param DOMElement $node 
  394. * @param string $value 
  395. * @return string 
  396. */ 
  397. static protected function _set_hr_size(DOMElement $node, $value) { 
  398. $style = rtrim($node->getAttribute(self::$_style_attr), ";"); 
  399. $style .= "; border-width: ".max(0, $value-2)."; "; 
  400. return ltrim($style, "; "); 
  401.  
  402. /** 
  403. * @param DOMElement $node 
  404. * @param string $value 
  405. * @return null|string 
  406. */ 
  407. static protected function _set_hr_align(DOMElement $node, $value) { 
  408. $style = rtrim($node->getAttribute(self::$_style_attr), ";"); 
  409. $width = $node->getAttribute("width"); 
  410.  
  411. if ( $width == "" ) { 
  412. $width = "100%"; 
  413.  
  414. $remainder = 100 - (double)rtrim($width, "% "); 
  415.  
  416. switch ($value) { 
  417. case "left": 
  418. $style .= "; margin-right: $remainder %;"; 
  419. break; 
  420.  
  421. case "right": 
  422. $style .= "; margin-left: $remainder %;"; 
  423. break; 
  424.  
  425. case "center": 
  426. $style .= "; margin-left: auto; margin-right: auto;"; 
  427. break; 
  428.  
  429. default: 
  430. return null; 
  431.  
  432. return ltrim($style, "; "); 
  433.  
  434. /** 
  435. * @param DOMElement $node 
  436. * @param string $value 
  437. * @return null 
  438. */ 
  439. static protected function _set_table_row_align(DOMElement $node, $value) { 
  440. $cell_list = self::get_cell_list($node); 
  441.  
  442. foreach ($cell_list as $cell) { 
  443. self::append_style($cell, "; text-align: $value;"); 
  444.  
  445. return null; 
  446.  
  447. /** 
  448. * @param DOMElement $node 
  449. * @param string $value 
  450. * @return null 
  451. */ 
  452. static protected function _set_table_row_valign(DOMElement $node, $value) { 
  453. $cell_list = self::get_cell_list($node); 
  454.  
  455. foreach ($cell_list as $cell) { 
  456. self::append_style($cell, "; vertical-align: $value;"); 
  457.  
  458. return null; 
  459.  
  460. /** 
  461. * @param DOMElement $node 
  462. * @param string $value 
  463. * @return null 
  464. */ 
  465. static protected function _set_table_row_bgcolor(DOMElement $node, $value) { 
  466. $cell_list = self::get_cell_list($node); 
  467. $value = self::_get_valid_color($value); 
  468.  
  469. foreach ($cell_list as $cell) { 
  470. self::append_style($cell, "; background-color: $value;"); 
  471.  
  472. return null; 
  473.  
  474. /** 
  475. * @param DOMElement $node 
  476. * @param string $value 
  477. * @return null 
  478. */ 
  479. static protected function _set_body_link(DOMElement $node, $value) { 
  480. $a_list = $node->getElementsByTagName("a"); 
  481. $value = self::_get_valid_color($value); 
  482.  
  483. foreach ($a_list as $a) { 
  484. self::append_style($a, "; color: $value;"); 
  485.  
  486. return null; 
  487.  
  488. /** 
  489. * @param DOMElement $node 
  490. * @param string $value 
  491. * @return null 
  492. */ 
  493. static protected function _set_basefont_size(DOMElement $node, $value) { 
  494. // FIXME: ? we don't actually set the font size of anything here, just 
  495. // the base size for later modification by <font> tags. 
  496. self::$_last_basefont_size = $value; 
  497. return null; 
  498.  
  499. /** 
  500. * @param DOMElement $node 
  501. * @param string $value 
  502. * @return string 
  503. */ 
  504. static protected function _set_font_size(DOMElement $node, $value) { 
  505. $style = $node->getAttribute(self::$_style_attr); 
  506.  
  507. if ( $value[0] === "-" || $value[0] === "+" ) { 
  508. $value = self::$_last_basefont_size + (int)$value; 
  509.  
  510. if ( isset(self::$_font_size_lookup[$value]) ) { 
  511. $style .= "; font-size: " . self::$_font_size_lookup[$value] . ";"; 
  512. else { 
  513. $style .= "; font-size: $value;"; 
  514.  
  515. return ltrim($style, "; ");