renderer.cls.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <?php
  2. /**
  3. * @package dompdf
  4. * @link http://dompdf.github.com/
  5. * @author Benj Carson <benjcarson@digitaljunkies.ca>
  6. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  7. */
  8. /**
  9. * Concrete renderer
  10. *
  11. * Instantiates several specific renderers in order to render any given
  12. * frame.
  13. *
  14. * @access private
  15. * @package dompdf
  16. */
  17. class Renderer extends Abstract_Renderer {
  18. /**
  19. * Array of renderers for specific frame types
  20. *
  21. * @var Abstract_Renderer[]
  22. */
  23. protected $_renderers;
  24. /**
  25. * Cache of the callbacks array
  26. *
  27. * @var array
  28. */
  29. private $_callbacks;
  30. /**
  31. * Class destructor
  32. */
  33. function __destruct() {
  34. clear_object($this);
  35. }
  36. /**
  37. * Advance the canvas to the next page
  38. */
  39. function new_page() {
  40. $this->_canvas->new_page();
  41. }
  42. /**
  43. * Render frames recursively
  44. *
  45. * @param Frame $frame the frame to render
  46. */
  47. function render(Frame $frame) {
  48. global $_dompdf_debug;
  49. if ( $_dompdf_debug ) {
  50. echo $frame;
  51. flush();
  52. }
  53. $style = $frame->get_style();
  54. if ( in_array($style->visibility, array("hidden", "collapse")) ) {
  55. return;
  56. }
  57. $display = $style->display;
  58. // Starts the CSS transformation
  59. if ( $style->transform && is_array($style->transform) ) {
  60. $this->_canvas->save();
  61. list($x, $y) = $frame->get_padding_box();
  62. $origin = $style->transform_origin;
  63. foreach($style->transform as $transform) {
  64. list($function, $values) = $transform;
  65. if ( $function === "matrix" ) {
  66. $function = "transform";
  67. }
  68. $values = array_map("floatval", $values);
  69. $values[] = $x + $style->length_in_pt($origin[0], $style->width);
  70. $values[] = $y + $style->length_in_pt($origin[1], $style->height);
  71. call_user_func_array(array($this->_canvas, $function), $values);
  72. }
  73. }
  74. switch ($display) {
  75. case "block":
  76. case "list-item":
  77. case "inline-block":
  78. case "table":
  79. case "inline-table":
  80. $this->_render_frame("block", $frame);
  81. break;
  82. case "inline":
  83. if ( $frame->is_text_node() )
  84. $this->_render_frame("text", $frame);
  85. else
  86. $this->_render_frame("inline", $frame);
  87. break;
  88. case "table-cell":
  89. $this->_render_frame("table-cell", $frame);
  90. break;
  91. case "table-row-group":
  92. case "table-header-group":
  93. case "table-footer-group":
  94. $this->_render_frame("table-row-group", $frame);
  95. break;
  96. case "-dompdf-list-bullet":
  97. $this->_render_frame("list-bullet", $frame);
  98. break;
  99. case "-dompdf-image":
  100. $this->_render_frame("image", $frame);
  101. break;
  102. case "none":
  103. $node = $frame->get_node();
  104. if ( $node->nodeName === "script" ) {
  105. if ( $node->getAttribute("type") === "text/php" ||
  106. $node->getAttribute("language") === "php" ) {
  107. // Evaluate embedded php scripts
  108. $this->_render_frame("php", $frame);
  109. }
  110. elseif ( $node->getAttribute("type") === "text/javascript" ||
  111. $node->getAttribute("language") === "javascript" ) {
  112. // Insert JavaScript
  113. $this->_render_frame("javascript", $frame);
  114. }
  115. }
  116. // Don't render children, so skip to next iter
  117. return;
  118. default:
  119. break;
  120. }
  121. // Starts the overflow: hidden box
  122. if ( $style->overflow === "hidden" ) {
  123. list($x, $y, $w, $h) = $frame->get_padding_box();
  124. // get border radii
  125. $style = $frame->get_style();
  126. list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
  127. if ( $tl + $tr + $br + $bl > 0 ) {
  128. $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl);
  129. }
  130. else {
  131. $this->_canvas->clipping_rectangle($x, $y, $w, $h);
  132. }
  133. }
  134. $stack = array();
  135. foreach ($frame->get_children() as $child) {
  136. // < 0 : nagative z-index
  137. // = 0 : no z-index, no stacking context
  138. // = 1 : stacking context without z-index
  139. // > 1 : z-index
  140. $child_style = $child->get_style();
  141. $child_z_index = $child_style->z_index;
  142. $z_index = 0;
  143. if ( $child_z_index !== "auto" ) {
  144. $z_index = intval($child_z_index) + 1;
  145. }
  146. elseif ( $child_style->float !== "none" || $child->is_positionned()) {
  147. $z_index = 1;
  148. }
  149. $stack[$z_index][] = $child;
  150. }
  151. ksort($stack);
  152. foreach ($stack as $by_index) {
  153. foreach($by_index as $child) {
  154. $this->render($child);
  155. }
  156. }
  157. // Ends the overflow: hidden box
  158. if ( $style->overflow === "hidden" ) {
  159. $this->_canvas->clipping_end();
  160. }
  161. if ( $style->transform && is_array($style->transform) ) {
  162. $this->_canvas->restore();
  163. }
  164. // Check for end frame callback
  165. $this->_check_callbacks("end_frame", $frame);
  166. }
  167. /**
  168. * Check for callbacks that need to be performed when a given event
  169. * gets triggered on a frame
  170. *
  171. * @param string $event the type of event
  172. * @param Frame $frame the frame that event is triggered on
  173. */
  174. protected function _check_callbacks($event, $frame) {
  175. if (!isset($this->_callbacks)) {
  176. $this->_callbacks = $this->_dompdf->get_callbacks();
  177. }
  178. if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) {
  179. $info = array(0 => $this->_canvas, "canvas" => $this->_canvas,
  180. 1 => $frame, "frame" => $frame);
  181. $fs = $this->_callbacks[$event];
  182. foreach ($fs as $f) {
  183. if (is_callable($f)) {
  184. if (is_array($f)) {
  185. $f[0]->$f[1]($info);
  186. } else {
  187. $f($info);
  188. }
  189. }
  190. }
  191. }
  192. }
  193. /**
  194. * Render a single frame
  195. *
  196. * Creates Renderer objects on demand
  197. *
  198. * @param string $type type of renderer to use
  199. * @param Frame $frame the frame to render
  200. */
  201. protected function _render_frame($type, $frame) {
  202. if ( !isset($this->_renderers[$type]) ) {
  203. switch ($type) {
  204. case "block":
  205. $this->_renderers[$type] = new Block_Renderer($this->_dompdf);
  206. break;
  207. case "inline":
  208. $this->_renderers[$type] = new Inline_Renderer($this->_dompdf);
  209. break;
  210. case "text":
  211. $this->_renderers[$type] = new Text_Renderer($this->_dompdf);
  212. break;
  213. case "image":
  214. $this->_renderers[$type] = new Image_Renderer($this->_dompdf);
  215. break;
  216. case "table-cell":
  217. $this->_renderers[$type] = new Table_Cell_Renderer($this->_dompdf);
  218. break;
  219. case "table-row-group":
  220. $this->_renderers[$type] = new Table_Row_Group_Renderer($this->_dompdf);
  221. break;
  222. case "list-bullet":
  223. $this->_renderers[$type] = new List_Bullet_Renderer($this->_dompdf);
  224. break;
  225. case "php":
  226. $this->_renderers[$type] = new PHP_Evaluator($this->_canvas);
  227. break;
  228. case "javascript":
  229. $this->_renderers[$type] = new Javascript_Embedder($this->_dompdf);
  230. break;
  231. }
  232. }
  233. $this->_renderers[$type]->render($frame);
  234. }
  235. }