Abstract_Renderer

Base renderer class.

Defined (1)

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

/lib/dompdf/include/abstract_renderer.cls.php  
  1. abstract class Abstract_Renderer { 
  2.  
  3. /** 
  4. * Rendering backend 
  5. * @var Canvas 
  6. */ 
  7. protected $_canvas; 
  8.  
  9. /** 
  10. * Current dompdf instance 
  11. * @var DOMPDF 
  12. */ 
  13. protected $_dompdf; 
  14.  
  15. /** 
  16. * Class constructor 
  17. * @param DOMPDF $dompdf The current dompdf instance 
  18. */ 
  19. function __construct(DOMPDF $dompdf) { 
  20. $this->_dompdf = $dompdf; 
  21. $this->_canvas = $dompdf->get_canvas(); 
  22.  
  23. /** 
  24. * Render a frame. 
  25. * Specialized in child classes 
  26. * @param Frame $frame The frame to render 
  27. */ 
  28. abstract function render(Frame $frame); 
  29.  
  30. //........................................................................ 
  31.  
  32. /** 
  33. * Render a background image over a rectangular area 
  34. * @param string $url The background image to load 
  35. * @param float $x The left edge of the rectangular area 
  36. * @param float $y The top edge of the rectangular area 
  37. * @param float $width The width of the rectangular area 
  38. * @param float $height The height of the rectangular area 
  39. * @param Style $style The associated Style object 
  40. * @throws Exception 
  41. */ 
  42. protected function _background_image($url, $x, $y, $width, $height, $style) { 
  43. if ( !function_exists("imagecreatetruecolor") ) { 
  44. throw new Exception("The PHP GD extension is required, but is not installed."); 
  45.  
  46. $sheet = $style->get_stylesheet(); 
  47.  
  48. // Skip degenerate cases 
  49. if ( $width == 0 || $height == 0 ) { 
  50. return; 
  51.  
  52. $box_width = $width; 
  53. $box_height = $height; 
  54.  
  55. //debugpng 
  56. if (DEBUGPNG) print '[_background_image '.$url.']'; 
  57.  
  58. list($img, $type, /**$msg*/) = Image_Cache::resolve_url( 
  59. $url,  
  60. $sheet->get_protocol(),  
  61. $sheet->get_host(),  
  62. $sheet->get_base_path(),  
  63. $this->_dompdf 
  64. ); 
  65.  
  66. // Bail if the image is no good 
  67. if ( Image_Cache::is_broken($img) ) { 
  68. return; 
  69.  
  70. //Try to optimize away reading and composing of same background multiple times 
  71. //Postponing read with imagecreatefrom ...() 
  72. //final composition parameters and name not known yet 
  73. //Therefore read dimension directly from file, instead of creating gd object first. 
  74. //$img_w = imagesx($src); $img_h = imagesy($src); 
  75.  
  76. list($img_w, $img_h) = dompdf_getimagesize($img); 
  77. if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) { 
  78. return; 
  79.  
  80. $repeat = $style->background_repeat; 
  81. $dpi = $this->_dompdf->get_option("dpi"); 
  82.  
  83. //Increase background resolution and dependent box size according to image resolution to be placed in 
  84. //Then image can be copied in without resize 
  85. $bg_width = round((float)($width * $dpi) / 72); 
  86. $bg_height = round((float)($height * $dpi) / 72); 
  87.  
  88. //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel 
  89.  
  90. list($bg_x, $bg_y) = $style->background_position; 
  91.  
  92. if ( is_percent($bg_x) ) { 
  93. // The point $bg_x % from the left edge of the image is placed 
  94. // $bg_x % from the left edge of the background rectangle 
  95. $p = ((float)$bg_x)/100.0; 
  96. $x1 = $p * $img_w; 
  97. $x2 = $p * $bg_width; 
  98.  
  99. $bg_x = $x2 - $x1; 
  100. else { 
  101. $bg_x = (float)($style->length_in_pt($bg_x)*$dpi) / 72; 
  102.  
  103. $bg_x = round($bg_x + $style->length_in_pt($style->border_left_width)*$dpi / 72); 
  104.  
  105. if ( is_percent($bg_y) ) { 
  106. // The point $bg_y % from the left edge of the image is placed 
  107. // $bg_y % from the left edge of the background rectangle 
  108. $p = ((float)$bg_y)/100.0; 
  109. $y1 = $p * $img_h; 
  110. $y2 = $p * $bg_height; 
  111.  
  112. $bg_y = $y2 - $y1; 
  113. else { 
  114. $bg_y = (float)($style->length_in_pt($bg_y)*$dpi) / 72; 
  115.  
  116. $bg_y = round($bg_y + $style->length_in_pt($style->border_top_width)*$dpi / 72); 
  117.  
  118. //clip background to the image area on partial repeat. Nothing to do if img off area 
  119. //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area 
  120. //On no repeat with positive offset: move size/start to have offset==0 
  121. //Handle x/y Dimensions separately 
  122.  
  123. if ( $repeat !== "repeat" && $repeat !== "repeat-x" ) { 
  124. //No repeat x 
  125. if ($bg_x < 0) { 
  126. $bg_width = $img_w + $bg_x; 
  127. else { 
  128. $x += ($bg_x * 72)/$dpi; 
  129. $bg_width = $bg_width - $bg_x; 
  130. if ($bg_width > $img_w) { 
  131. $bg_width = $img_w; 
  132. $bg_x = 0; 
  133.  
  134. if ($bg_width <= 0) { 
  135. return; 
  136.  
  137. $width = (float)($bg_width * 72)/$dpi; 
  138. else { 
  139. //repeat x 
  140. if ($bg_x < 0) { 
  141. $bg_x = - ((-$bg_x) % $img_w); 
  142. else { 
  143. $bg_x = $bg_x % $img_w; 
  144. if ($bg_x > 0) { 
  145. $bg_x -= $img_w; 
  146.  
  147. if ( $repeat !== "repeat" && $repeat !== "repeat-y" ) { 
  148. //no repeat y 
  149. if ($bg_y < 0) { 
  150. $bg_height = $img_h + $bg_y; 
  151. else { 
  152. $y += ($bg_y * 72)/$dpi; 
  153. $bg_height = $bg_height - $bg_y; 
  154. if ($bg_height > $img_h) { 
  155. $bg_height = $img_h; 
  156. $bg_y = 0; 
  157. if ($bg_height <= 0) { 
  158. return; 
  159. $height = (float)($bg_height * 72)/$dpi; 
  160. else { 
  161. //repeat y 
  162. if ($bg_y < 0) { 
  163. $bg_y = - ((-$bg_y) % $img_h); 
  164. else { 
  165. $bg_y = $bg_y % $img_h; 
  166. if ($bg_y > 0) { 
  167. $bg_y -= $img_h; 
  168.  
  169. //Optimization, if repeat has no effect 
  170. if ( $repeat === "repeat" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height ) { 
  171. $repeat = "repeat-x"; 
  172.  
  173. if ( $repeat === "repeat" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width ) { 
  174. $repeat = "repeat-y"; 
  175.  
  176. if ( ($repeat === "repeat-x" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width) || 
  177. ($repeat === "repeat-y" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height) ) { 
  178. $repeat = "no-repeat"; 
  179.  
  180. //Use filename as indicator only 
  181. //different names for different variants to have different copies in the pdf 
  182. //This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color) 
  183. //Note: Here, bg_* are the start values, not end values after going through the tile loops! 
  184.  
  185. $filedummy = $img; 
  186.  
  187. $is_png = false; 
  188. $filedummy .= '_'.$bg_width.'_'.$bg_height.'_'.$bg_x.'_'.$bg_y.'_'.$repeat; 
  189.  
  190. //Optimization to avoid multiple times rendering the same image. 
  191. //If check functions are existing and identical image already cached,  
  192. //then skip creation of duplicate, because it is not needed by addImagePng 
  193. if ( $this->_canvas instanceof CPDF_Adapter && 
  194. $this->_canvas->get_cpdf()->image_iscached($filedummy) ) { 
  195. $bg = null; 
  196.  
  197. else { 
  198.  
  199. // Create a new image to fit over the background rectangle 
  200. $bg = imagecreatetruecolor($bg_width, $bg_height); 
  201.  
  202. switch (strtolower($type)) { 
  203. case IMAGETYPE_PNG: 
  204. $is_png = true; 
  205. imagesavealpha($bg, true); 
  206. imagealphablending($bg, false); 
  207. $src = imagecreatefrompng($img); 
  208. break; 
  209.  
  210. case IMAGETYPE_JPEG: 
  211. $src = imagecreatefromjpeg($img); 
  212. break; 
  213.  
  214. case IMAGETYPE_GIF: 
  215. $src = imagecreatefromgif($img); 
  216. break; 
  217.  
  218. case IMAGETYPE_BMP: 
  219. $src = imagecreatefrombmp($img); 
  220. break; 
  221.  
  222. default: 
  223. return; // Unsupported image type 
  224.  
  225. if ( $src == null ) { 
  226. return; 
  227.  
  228. //Background color if box is not relevant here 
  229. //Non transparent image: box clipped to real size. Background non relevant. 
  230. //Transparent image: The image controls the transparency and lets shine through whatever background. 
  231. //However on transparent image preset the composed image with the transparency color,  
  232. //to keep the transparency when copying over the non transparent parts of the tiles. 
  233. $ti = imagecolortransparent($src); 
  234.  
  235. if ( $ti >= 0 ) { 
  236. $tc = imagecolorsforindex($src, $ti); 
  237. $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']); 
  238. imagefill($bg, 0, 0, $ti); 
  239. imagecolortransparent($bg, $ti); 
  240.  
  241. //This has only an effect for the non repeatable dimension. 
  242. //compute start of src and dest coordinates of the single copy 
  243. if ( $bg_x < 0 ) { 
  244. $dst_x = 0; 
  245. $src_x = -$bg_x; 
  246. else { 
  247. $src_x = 0; 
  248. $dst_x = $bg_x; 
  249.  
  250. if ( $bg_y < 0 ) { 
  251. $dst_y = 0; 
  252. $src_y = -$bg_y; 
  253. else { 
  254. $src_y = 0; 
  255. $dst_y = $bg_y; 
  256.  
  257. //For historical reasons exchange meanings of variables: 
  258. //start_* will be the start values, while bg_* will be the temporary start values in the loops 
  259. $start_x = $bg_x; 
  260. $start_y = $bg_y; 
  261.  
  262. // Copy regions from the source image to the background 
  263. if ( $repeat === "no-repeat" ) { 
  264.  
  265. // Simply place the image on the background 
  266. imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h); 
  267.  
  268. else if ( $repeat === "repeat-x" ) { 
  269.  
  270. for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) { 
  271. if ( $bg_x < 0 ) { 
  272. $dst_x = 0; 
  273. $src_x = -$bg_x; 
  274. $w = $img_w + $bg_x; 
  275. else { 
  276. $dst_x = $bg_x; 
  277. $src_x = 0; 
  278. $w = $img_w; 
  279. imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h); 
  280.  
  281. else if ( $repeat === "repeat-y" ) { 
  282.  
  283. for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) { 
  284. if ( $bg_y < 0 ) { 
  285. $dst_y = 0; 
  286. $src_y = -$bg_y; 
  287. $h = $img_h + $bg_y; 
  288. else { 
  289. $dst_y = $bg_y; 
  290. $src_y = 0; 
  291. $h = $img_h; 
  292. imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h); 
  293.  
  294.  
  295. else if ( $repeat === "repeat" ) { 
  296.  
  297. for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) { 
  298. for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) { 
  299.  
  300. if ( $bg_x < 0 ) { 
  301. $dst_x = 0; 
  302. $src_x = -$bg_x; 
  303. $w = $img_w + $bg_x; 
  304. else { 
  305. $dst_x = $bg_x; 
  306. $src_x = 0; 
  307. $w = $img_w; 
  308.  
  309. if ( $bg_y < 0 ) { 
  310. $dst_y = 0; 
  311. $src_y = -$bg_y; 
  312. $h = $img_h + $bg_y; 
  313. else { 
  314. $dst_y = $bg_y; 
  315. $src_y = 0; 
  316. $h = $img_h; 
  317. imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h); 
  318.  
  319. else { 
  320. print 'Unknown repeat!'; 
  321.  
  322. imagedestroy($src); 
  323.  
  324. } /** End optimize away creation of duplicates */ 
  325.  
  326. $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height); 
  327.  
  328. //img: image url string 
  329. //img_w, img_h: original image size in px 
  330. //width, height: box size in pt 
  331. //bg_width, bg_height: box size in px 
  332. //x, y: left/top edge of box on page in pt 
  333. //start_x, start_y: placement of image relative to pattern 
  334. //$repeat: repeat mode 
  335. //$bg: GD object of result image 
  336. //$src: GD object of original image 
  337. //When using cpdf and optimization to direct png creation from gd object is available,  
  338. //don't create temp file, but place gd object directly into the pdf 
  339. if ( !$is_png && $this->_canvas instanceof CPDF_Adapter ) { 
  340. // Note: CPDF_Adapter image converts y position 
  341. $this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg); 
  342. }  
  343.  
  344. else { 
  345. $tmp_dir = $this->_dompdf->get_option("temp_dir"); 
  346. $tmp_name = tempnam($tmp_dir, "bg_dompdf_img_"); 
  347. @unlink($tmp_name); 
  348. $tmp_file = "$tmp_name.png"; 
  349.  
  350. //debugpng 
  351. if (DEBUGPNG) print '[_background_image '.$tmp_file.']'; 
  352.  
  353. imagepng($bg, $tmp_file); 
  354. $this->_canvas->image($tmp_file, $x, $y, $width, $height); 
  355. imagedestroy($bg); 
  356.  
  357. //debugpng 
  358. if (DEBUGPNG) print '[_background_image unlink '.$tmp_file.']'; 
  359.  
  360. if (!DEBUGKEEPTEMP) { 
  361. unlink($tmp_file); 
  362.  
  363. $this->_canvas->clipping_end(); 
  364.  
  365. protected function _get_dash_pattern($style, $width) { 
  366. $pattern = array(); 
  367.  
  368. switch ($style) { 
  369. default: 
  370. /**case "solid": 
  371. case "double": 
  372. case "groove": 
  373. case "inset": 
  374. case "outset": 
  375. case "ridge":*/ 
  376. case "none": break; 
  377.  
  378. case "dotted":  
  379. if ( $width <= 1 ) 
  380. $pattern = array($width, $width*2); 
  381. else 
  382. $pattern = array($width); 
  383. break; 
  384.  
  385. case "dashed":  
  386. $pattern = array(3 * $width); 
  387. break; 
  388.  
  389. return $pattern; 
  390.  
  391. protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { 
  392. return; 
  393.  
  394. protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { 
  395. return; 
  396.  
  397. // Border rendering functions 
  398.  
  399. protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { 
  400. $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2); 
  401.  
  402.  
  403. protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { 
  404. $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2); 
  405.  
  406.  
  407. protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { 
  408. // TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't. 
  409. if ( $corner_style !== "bevel" || $r1 > 0 || $r2 > 0 ) { 
  410. // do it the simple way 
  411. $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2); 
  412. return; 
  413.  
  414. list($top, $right, $bottom, $left) = $widths; 
  415.  
  416. // All this polygon business is for beveled corners... 
  417. switch ($side) { 
  418. case "top": 
  419. $points = array($x, $y,  
  420. $x + $length, $y,  
  421. $x + $length - $right, $y + $top,  
  422. $x + $left, $y + $top); 
  423. $this->_canvas->polygon($points, $color, null, null, true); 
  424. break; 
  425.  
  426. case "bottom": 
  427. $points = array($x, $y,  
  428. $x + $length, $y,  
  429. $x + $length - $right, $y - $bottom,  
  430. $x + $left, $y - $bottom); 
  431. $this->_canvas->polygon($points, $color, null, null, true); 
  432. break; 
  433.  
  434. case "left": 
  435. $points = array($x, $y,  
  436. $x, $y + $length,  
  437. $x + $left, $y + $length - $bottom,  
  438. $x + $left, $y + $top); 
  439. $this->_canvas->polygon($points, $color, null, null, true); 
  440. break; 
  441.  
  442. case "right": 
  443. $points = array($x, $y,  
  444. $x, $y + $length,  
  445. $x - $right, $y + $length - $bottom,  
  446. $x - $right, $y + $top); 
  447. $this->_canvas->polygon($points, $color, null, null, true); 
  448. break; 
  449.  
  450. default: 
  451. return; 
  452.  
  453. protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) { 
  454. switch ($side) { 
  455.  
  456. case "top": 
  457. $r1 -= $left * $ratio; 
  458. $r2 -= $right * $ratio; 
  459. $x += $left * $ratio; 
  460. $y += $top * $ratio; 
  461. $length -= $left * $ratio + $right * $ratio; 
  462. break; 
  463.  
  464. case "bottom": 
  465. $r1 -= $right * $ratio; 
  466. $r2 -= $left * $ratio; 
  467. $x += $left * $ratio; 
  468. $y -= $bottom * $ratio; 
  469. $length -= $left * $ratio + $right * $ratio; 
  470. break; 
  471.  
  472. case "left": 
  473. $r1 -= $top * $ratio; 
  474. $r2 -= $bottom * $ratio; 
  475. $x += $left * $ratio; 
  476. $y += $top * $ratio; 
  477. $length -= $top * $ratio + $bottom * $ratio; 
  478. break; 
  479.  
  480. case "right": 
  481. $r1 -= $bottom * $ratio; 
  482. $r2 -= $top * $ratio; 
  483. $x -= $right * $ratio; 
  484. $y += $top * $ratio; 
  485. $length -= $top * $ratio + $bottom * $ratio; 
  486. break; 
  487.  
  488. default: 
  489. return; 
  490.  
  491.  
  492. protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { 
  493. list($top, $right, $bottom, $left) = $widths; 
  494.  
  495. $third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3); 
  496.  
  497. // draw the outer border 
  498. $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); 
  499.  
  500. $this->_apply_ratio($side, 2/3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); 
  501.  
  502. $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); 
  503.  
  504. protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { 
  505. list($top, $right, $bottom, $left) = $widths; 
  506.  
  507. $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2); 
  508.  
  509. $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); 
  510.  
  511. $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); 
  512.  
  513. $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); 
  514.  
  515.  
  516. protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { 
  517. list($top, $right, $bottom, $left) = $widths; 
  518.  
  519. $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2); 
  520.  
  521. $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); 
  522.  
  523. $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); 
  524.  
  525. $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); 
  526.  
  527.  
  528. protected function _tint($c) { 
  529. if ( !is_numeric($c) ) 
  530. return $c; 
  531.  
  532. return min(1, $c + 0.16); 
  533.  
  534. protected function _shade($c) { 
  535. if ( !is_numeric($c) ) 
  536. return $c; 
  537.  
  538. return max(0, $c - 0.33); 
  539.  
  540. protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { 
  541. switch ($side) { 
  542. case "top": 
  543. case "left": 
  544. $shade = array_map(array($this, "_shade"), $color); 
  545. $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); 
  546. break; 
  547.  
  548. case "bottom": 
  549. case "right": 
  550. $tint = array_map(array($this, "_tint"), $color); 
  551. $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); 
  552. break; 
  553.  
  554. default: 
  555. return; 
  556.  
  557. protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { 
  558. switch ($side) { 
  559. case "top": 
  560. case "left": 
  561. $tint = array_map(array($this, "_tint"), $color); 
  562. $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); 
  563. break; 
  564.  
  565. case "bottom": 
  566. case "right": 
  567. $shade = array_map(array($this, "_shade"), $color); 
  568. $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); 
  569. break; 
  570.  
  571. default: 
  572. return; 
  573. // Draws a solid, dotted, or dashed line, observing the border radius 
  574. protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0) { 
  575. list($top, $right, $bottom, $left) = $widths; 
  576.  
  577. $width = $$side; 
  578. $pattern = $this->_get_dash_pattern($pattern_name, $width); 
  579.  
  580. $half_width = $width/2; 
  581. $r1 -= $half_width; 
  582. $r2 -= $half_width; 
  583. $adjust = $r1/80; 
  584. $length -= $width;  
  585.  
  586. switch ($side) { 
  587. case "top": 
  588. $x += $half_width;  
  589. $y += $half_width; 
  590.  
  591. if ( $r1 > 0 ) { 
  592. $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90-$adjust, 135+$adjust, $color, $width, $pattern); 
  593.  
  594. $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern); 
  595.  
  596. if ( $r2 > 0 ) { 
  597. $this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45-$adjust, 90+$adjust, $color, $width, $pattern); 
  598. break; 
  599.  
  600. case "bottom": 
  601. $x += $half_width;  
  602. $y -= $half_width; 
  603.  
  604. if ( $r1 > 0 ) { 
  605. $this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225-$adjust, 270+$adjust, $color, $width, $pattern); 
  606.  
  607. $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern); 
  608.  
  609. if ( $r2 > 0 ) { 
  610. $this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270-$adjust, 315+$adjust, $color, $width, $pattern); 
  611. break; 
  612.  
  613. case "left": 
  614. $y += $half_width;  
  615. $x += $half_width; 
  616.  
  617. if ( $r1 > 0 ) { 
  618. $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135-$adjust, 180+$adjust, $color, $width, $pattern); 
  619.  
  620. $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern); 
  621.  
  622. if ( $r2 > 0 ) { 
  623. $this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180-$adjust, 225+$adjust, $color, $width, $pattern); 
  624. break; 
  625.  
  626. case "right": 
  627. $y += $half_width;  
  628. $x -= $half_width; 
  629.  
  630. if ( $r1 > 0 ) { 
  631. $this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0-$adjust, 45+$adjust, $color, $width, $pattern); 
  632.  
  633. $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern); 
  634.  
  635. if ( $r2 > 0 ) { 
  636. $this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315-$adjust, 360+$adjust, $color, $width, $pattern); 
  637. break; 
  638.  
  639. protected function _set_opacity($opacity) { 
  640. if ( is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0 ) { 
  641. $this->_canvas->set_opacity( $opacity ); 
  642.  
  643. protected function _debug_layout($box, $color = "red", $style = array()) { 
  644. $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], CSS_Color::parse($color), 0.1, $style);