attribute_translator.cls.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. <?php
  2. /**
  3. * DOMPDF - PHP5 HTML to PDF renderer
  4. *
  5. * File: $RCSfile: attribute_translator.cls.php,v $
  6. * Created on: 2004-09-13
  7. *
  8. * Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
  9. *
  10. * This library is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU Lesser General Public
  12. * License as published by the Free Software Foundation; either
  13. * version 2.1 of the License, or (at your option) any later version.
  14. *
  15. * This library is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public License
  21. * along with this library in the file LICENSE.LGPL; if not, write to the
  22. * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  23. * 02111-1307 USA
  24. *
  25. * Alternatively, you may distribute this software under the terms of the
  26. * PHP License, version 3.0 or later. A copy of this license should have
  27. * been distributed with this file in the file LICENSE.PHP . If this is not
  28. * the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
  29. *
  30. * The latest version of DOMPDF might be available at:
  31. * http://www.dompdf.com/
  32. *
  33. * @link http://www.dompdf.com/
  34. * @copyright 2004 Benj Carson
  35. * @author Benj Carson <benjcarson@digitaljunkies.ca>
  36. * @package dompdf
  37. */
  38. /* $Id: attribute_translator.cls.php 219 2010-03-11 23:18:31Z ryan.masten $ */
  39. /**
  40. * Translates HTML 4.0 attributes into CSS rules
  41. *
  42. * @access private
  43. * @package dompdf
  44. */
  45. class Attribute_Translator {
  46. // Munged data originally from
  47. // http://www.w3.org/TR/REC-html40/index/attributes.html
  48. //
  49. // thank you var_export() :D
  50. static private $__ATTRIBUTE_LOOKUP =
  51. array (//'caption' => array ( 'align' => '', ),
  52. 'img_inner' => // img tags actually end up wrapping img_inner elements
  53. array ('align' => array('bottom' => 'vertical-align: baseline;',
  54. 'middle' => 'vertical-align: middle;',
  55. 'top' => 'vertical-align: top;',
  56. 'left' => 'float: left;',
  57. 'right' => 'float: right;'),
  58. 'border' => 'border-width: %0.2f px;',
  59. 'height' => 'height: %s px;',
  60. 'hspace' => 'padding-left: %1$0.2f px; padding-right: %1$0.2f px;',
  61. 'vspace' => 'padding-top: %1$0.2f px; padding-bottom: %1$0.2f px;',
  62. 'width' => 'width: %s px;',
  63. ),
  64. 'table' =>
  65. array ("align" => array('left' => 'margin-left: 0; margin-right: auto;',
  66. 'center' => 'margin-left: auto; margin-right: auto;',
  67. 'right' => 'margin-left: auto; margin-right: 0;'
  68. ),
  69. 'bgcolor' => 'background-color: %s;',
  70. 'border' => '!set_table_border',
  71. 'cellpadding' => '!set_table_cellpadding',
  72. 'cellspacing' => 'border-spacing: %0.2f; border-collapse: separate;',
  73. 'frame' => array('void' => 'border-style: none;',
  74. 'above' => 'border-top-style: solid;',
  75. 'below' => 'border-bottom-style: solid;',
  76. 'hsides' => 'border-left-style: solid; border-right-style: solid;',
  77. 'vsides' => 'border-top-style: solid; border-bottom-style: solid;',
  78. 'lhs' => 'border-left-style: solid;',
  79. 'rhs' => 'border-right-style: solid;',
  80. 'box' => 'border-style: solid;',
  81. 'border' => 'border-style: solid;'),
  82. 'rules' => '!set_table_rules',
  83. 'width' => 'width: %s;',
  84. ),
  85. 'hr' =>
  86. array (
  87. 'align' => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly
  88. 'noshade' => 'border-style: solid;',
  89. 'size' => 'border-width: %0.2f px;',
  90. 'width' => 'width: %s;',
  91. ),
  92. 'div' =>
  93. array (
  94. 'align' => 'text-align: %s;',
  95. ),
  96. 'h1' =>
  97. array (
  98. 'align' => 'text-align: %s;',
  99. ),
  100. 'h2' =>
  101. array (
  102. 'align' => 'text-align: %s;',
  103. ),
  104. 'h3' =>
  105. array (
  106. 'align' => 'text-align: %s;',
  107. ),
  108. 'h4' =>
  109. array (
  110. 'align' => 'text-align: %s;',
  111. ),
  112. 'h5' =>
  113. array (
  114. 'align' => 'text-align: %s;',
  115. ),
  116. 'h6' =>
  117. array (
  118. 'align' => 'text-align: %s;',
  119. ),
  120. 'p' =>
  121. array (
  122. 'align' => 'text-align: %s;',
  123. ),
  124. // 'col' =>
  125. // array (
  126. // 'align' => '',
  127. // 'valign' => '',
  128. // ),
  129. // 'colgroup' =>
  130. // array (
  131. // 'align' => '',
  132. // 'valign' => '',
  133. // ),
  134. 'tbody' =>
  135. array (
  136. 'align' => '!set_table_row_align',
  137. 'valign' => '!set_table_row_valign',
  138. ),
  139. 'td' =>
  140. array (
  141. 'align' => 'text-align: %s;',
  142. 'bgcolor' => 'background-color: %s;',
  143. 'height' => 'height: %s;',
  144. 'nowrap' => 'white-space: nowrap;',
  145. 'valign' => 'vertical-align: %s;',
  146. 'width' => 'width: %s;',
  147. ),
  148. 'tfoot' =>
  149. array (
  150. 'align' => '!set_table_row_align',
  151. 'valign' => '!set_table_row_valign',
  152. ),
  153. 'th' =>
  154. array (
  155. 'align' => 'text-align: %s;',
  156. 'bgcolor' => 'background-color: %s;',
  157. 'height' => 'height: %s;',
  158. 'nowrap' => 'white-space: nowrap;',
  159. 'valign' => 'vertical-align: %s;',
  160. 'width' => 'width: %s;',
  161. ),
  162. 'thead' =>
  163. array (
  164. 'align' => '!set_table_row_align',
  165. 'valign' => '!set_table_row_valign',
  166. ),
  167. 'tr' =>
  168. array (
  169. 'align' => '!set_table_row_align',
  170. 'bgcolor' => '!set_table_row_bgcolor',
  171. 'valign' => '!set_table_row_valign',
  172. ),
  173. 'body' =>
  174. array (
  175. 'background' => 'background-image: url(%s);',
  176. 'bgcolor' => 'background-color: %s;',
  177. 'link' => '!set_body_link',
  178. 'text' => 'color: %s;',
  179. ),
  180. 'br' =>
  181. array (
  182. 'clear' => 'clear: %s;',
  183. ),
  184. 'basefont' =>
  185. array (
  186. 'color' => 'color: %s;',
  187. 'face' => 'font-family: %s;',
  188. 'size' => '!set_basefont_size',
  189. ),
  190. 'font' =>
  191. array (
  192. 'color' => 'color: %s;',
  193. 'face' => 'font-family: %s;',
  194. 'size' => '!set_font_size',
  195. ),
  196. 'dir' =>
  197. array (
  198. 'compact' => 'margin: 0.5em 0;',
  199. ),
  200. 'dl' =>
  201. array (
  202. 'compact' => 'margin: 0.5em 0;',
  203. ),
  204. 'menu' =>
  205. array (
  206. 'compact' => 'margin: 0.5em 0;',
  207. ),
  208. 'ol' =>
  209. array (
  210. 'compact' => 'margin: 0.5em 0;',
  211. 'start' => 'counter-reset: -dompdf-default-counter %d;',
  212. 'type' => 'list-style-type: %s;',
  213. ),
  214. 'ul' =>
  215. array (
  216. 'compact' => 'margin: 0.5em 0;',
  217. 'type' => 'list-style-type: %s;',
  218. ),
  219. 'li' =>
  220. array (
  221. 'type' => 'list-style-type: %s;',
  222. 'value' => 'counter-reset: -dompdf-default-counter %d;',
  223. ),
  224. 'pre' =>
  225. array (
  226. 'width' => 'width: %s;',
  227. ),
  228. );
  229. static protected $_last_basefont_size = 3;
  230. static protected $_font_size_lookup = array(1=>"xx-small",
  231. 2=>"x-small",
  232. 3=>"medium",
  233. 4=>"large",
  234. 5=>"x-large",
  235. 6=>"xx-large",
  236. 7=>"300%");
  237. static function translate_attributes($frame) {
  238. $node = $frame->get_node();
  239. $tag = $node->tagName;
  240. if ( !isset(self::$__ATTRIBUTE_LOOKUP[$tag]) )
  241. return;
  242. $valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag];
  243. $attrs = $node->attributes;
  244. $style = rtrim($node->getAttribute("style"), "; ");
  245. if ( $style != "" )
  246. $style .= ";";
  247. foreach ($attrs as $attr => $attr_node ) {
  248. if ( !isset($valid_attrs[$attr]) )
  249. continue;
  250. $value = $attr_node->value;
  251. $target = $valid_attrs[$attr];
  252. // Look up $value in $target, if $target is an array:
  253. if ( is_array($target) ) {
  254. if ( isset($target[$value]) )
  255. $style .= " " . self::_resolve_target($node, $target[$value], $value);
  256. } else {
  257. // otherwise use target directly
  258. $style .= " " . self::_resolve_target($node, $target, $value);
  259. }
  260. }
  261. if ( !is_null($style) ) {
  262. $style = ltrim($style);
  263. $node->setAttribute("style", $style);
  264. }
  265. }
  266. static protected function _resolve_target($node, $target, $value) {
  267. if ( $target[0] === "!" ) {
  268. // Function call
  269. $func = "_" . mb_substr($target, 1);
  270. return self::$func($node, $value);
  271. }
  272. return $value ? sprintf($target, $value) : "";
  273. }
  274. //.....................................................................
  275. static protected function _set_table_cellpadding($node, $value) {
  276. $td_list = $node->getElementsByTagName("td");
  277. foreach ($td_list as $td) {
  278. $style = rtrim($td->getAttribute("style"), ";");
  279. $style .= "; padding: $value" . "px;";
  280. $style = ltrim($style, ";");
  281. $td->setAttribute("style", $style);
  282. }
  283. return null;
  284. }
  285. static protected function _set_table_border($node, $value) {
  286. $td_list = $node->getElementsByTagName("td");
  287. foreach ($td_list as $td) {
  288. $style = $td->getAttribute("style");
  289. if ( strpos($style, "border") !== false )
  290. continue;
  291. $style = rtrim($style, ";");
  292. $style .= "; border-width: $value" . "px; border-style: ridge;";
  293. $style = ltrim($style, ";");
  294. $td->setAttribute("style", $style);
  295. }
  296. $th_list = $node->getElementsByTagName("th");
  297. foreach ($th_list as $th) {
  298. $style = $th->getAttribute("style");
  299. if ( strpos($style, "border") !== false )
  300. continue;
  301. $style = rtrim($style, ";");
  302. $style .= "; border-width: $value" . "px; border-style: ridge;";
  303. $style = ltrim($style, ";");
  304. $th->setAttribute("style", $style);
  305. }
  306. return null;
  307. }
  308. static protected function _set_table_cellspacing($node, $value) {
  309. $style = rtrim($td->getAttribute($style), ";");
  310. if ( $value == 0 )
  311. $style .= "; border-collapse: collapse;";
  312. else
  313. $style = "; border-collapse: separate;";
  314. return ltrim($style, ";");
  315. }
  316. static protected function _set_table_rules($node, $value) {
  317. $new_style = "; border-collapse: collapse;";
  318. switch ($value) {
  319. case "none":
  320. $new_style .= "border-style: none;";
  321. break;
  322. case "groups":
  323. // FIXME: unsupported
  324. return;
  325. case "rows":
  326. $new_style .= "border-style: solid none solid none; border-width: 1px; ";
  327. break;
  328. case "cols":
  329. $new_style .= "border-style: none solid none solid; border-width: 1px; ";
  330. break;
  331. case "all":
  332. $new_style .= "border-style: solid; border-width: 1px; ";
  333. break;
  334. default:
  335. // Invalid value
  336. return null;
  337. }
  338. $td_list = $node->getElementsByTagName("td");
  339. foreach ($td_list as $td) {
  340. $style = $td->getAttribute("style");
  341. $style .= $new_style;
  342. $td->setAttribute("style", $style);
  343. }
  344. return null;
  345. }
  346. static protected function _set_hr_align($node, $value) {
  347. $style = rtrim($node->getAttribute("style"),";");
  348. $width = $node->getAttribute("width");
  349. if ( $width == "" )
  350. $width = "100%";
  351. $remainder = 100 - (double)rtrim($width, "% ");
  352. switch ($value) {
  353. case "left":
  354. $style .= "; margin-right: $remainder %;";
  355. break;
  356. case "right":
  357. $style .= "; margin-left: $remainder %;";
  358. break;
  359. case "center":
  360. $style .= "; margin-left: auto; margin-right: auto;";
  361. break;
  362. default:
  363. return null;
  364. }
  365. return ltrim($style, "; ");
  366. }
  367. static protected function _set_table_row_align($node, $value) {
  368. $td_list = $node->getElementsByTagName("td");
  369. foreach ($td_list as $td) {
  370. $style = rtrim($td->getAttribute("style"), ";");
  371. $style .= "; text-align: $value;";
  372. $style = ltrim($style, "; ");
  373. $td->setAttribute("style", $style);
  374. }
  375. return null;
  376. }
  377. static protected function _set_table_row_valign($node, $value) {
  378. $td_list = $node->getElementsByTagName("td");
  379. foreach ($td_list as $td) {
  380. $style = rtrim($td->getAttribute("style"), ";");
  381. $style .= "; vertical-align: $value;";
  382. $style = ltrim($style, "; ");
  383. $td->setAttribute("style", $style);
  384. }
  385. return null;
  386. }
  387. static protected function _set_table_row_bgcolor($node, $value) {
  388. $td_list = $node->getElementsByTagName("td");
  389. foreach ($td_list as $td) {
  390. $style = rtrim($td->getAttribute("style"), ";");
  391. $style .= "; background-color: $value;";
  392. $style = ltrim($style, "; ");
  393. $td->setAttribute("style", $style);
  394. }
  395. return null;
  396. }
  397. static protected function _set_body_link($node, $value) {
  398. $a_list = $node->getElementsByTagName("a");
  399. foreach ($a_list as $a) {
  400. $style = rtrim($a->getAttribute("style"), ";");
  401. $style .= "; color: $value;";
  402. $style = ltrim($style, "; ");
  403. $a->setAttribute("style", $style);
  404. }
  405. return null;
  406. }
  407. static protected function _set_basefont_size($node, $value) {
  408. // FIXME: ? we don't actually set the font size of anything here, just
  409. // the base size for later modification by <font> tags.
  410. self::$_last_basefont_size = $value;
  411. return null;
  412. }
  413. static protected function _set_font_size($node, $value) {
  414. $style = $node->getAttribute("style");
  415. if ( $value[0] === "-" || $value[0] === "+" )
  416. $value = self::$_last_basefont_size + (int)$value;
  417. if ( isset(self::$_font_size_lookup[$value]) )
  418. $style .= "; font-size: " . self::$_font_size_lookup[$value] . ";";
  419. else
  420. $style .= "; font-size: $value;";
  421. return ltrim($style, "; ");
  422. }
  423. }