PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/rospdf/pdf-php/extensions/CezTableImage.php

https://bitbucket.org/openemr/openemr
PHP | 988 lines | 703 code | 91 blank | 194 comment | 166 complexity | 477b003fae5b67244df3e578582fae5e MD5 | raw file
Possible License(s): Apache-2.0, AGPL-1.0, GPL-2.0, LGPL-3.0, BSD-3-Clause, Unlicense, MPL-2.0, GPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. * syntax for the image tag:
  4. *
  5. * <C:showimage:<filename> <opt. width> <opt. height>>
  6. *
  7. * it is possible to specify the image width without specifying the image height (the image will
  8. * be scaled to the appropriate height).
  9. *
  10. * supported filename in the image tag:
  11. * '<C:showimage:'.urlencode('http://myserver.mytld/myimage.png').'>'
  12. * '<C:showimage:'.urlencode('/home/my home/my image.png').'>'
  13. * the url encoding is required for:
  14. * - files from remote servers (first entry from above)
  15. * - local files with whitespaces in the directory or file names
  16. *
  17. * local files without whitespaces in their filename can be specified without
  18. * url encoding:
  19. *
  20. * '<C:showimage:/home/myhome/myimage.png>'
  21. *
  22. * the php gd2 extension must be enabled for remote files and local gif files.
  23. * local png- and jpeg-files are supported without the gd2 extension.
  24. *
  25. * @author Kristian Herpel <Kristian.Herpel@gmx.net>
  26. * @author Ole K <ole1986@users.sourceforge.net>
  27. * @version 1.11 fix for problems with big pictures (pictures with width > 2000px were not shown in the table)
  28. */
  29. error_reporting(E_ALL);
  30. set_time_limit(1800);
  31. set_include_path('../src/' . PATH_SEPARATOR . get_include_path());
  32. include 'Cezpdf.php';
  33. /**
  34. * cezpdf extension for displaying images in table cells
  35. */
  36. class CezTableImage extends Cezpdf {
  37. /**
  38. * @param Cezpdf $ezpdf current cezpdf object
  39. */
  40. function CezTableImage($p,$o = 'portrait',$t = 'none', $op = array()){
  41. parent::__construct($p, $o,$t,$op);
  42. $this->allowedTags .= '|showimage:.*?';
  43. }
  44. /**
  45. * Modification to this function from Cezpdf
  46. * line 495-495: added parseImages function
  47. * line 518-524: added some condition to calculate cell height
  48. * line 528-528: modified to set the new cell height
  49. */
  50. public function ezTable(&$data,$cols='',$title='',$options=''){
  51. if (!is_array($data)){
  52. return;
  53. }
  54. if (!is_array($cols)){
  55. // take the columns from the first row of the data set
  56. reset($data);
  57. list($k,$v)=each($data);
  58. if (!is_array($v)){
  59. return;
  60. }
  61. $cols=array();
  62. foreach ($v as $k1=>$v1){
  63. $cols[$k1]=$k1;
  64. }
  65. }
  66. if (!is_array($options)){
  67. $options=array();
  68. }
  69. $defaults = array('shaded'=>1,'showBgCol'=>0,'shadeCol'=>array(0.8,0.8,0.8),'shadeCol2'=>array(0.7,0.7,0.7),'fontSize'=>10,'titleFontSize'=>12,
  70. 'titleGap'=>5,'lineCol'=>array(0,0,0),'gap'=>5,'xPos'=>'centre','xOrientation'=>'centre',
  71. 'showHeadings'=>1,'textCol'=>array(0,0,0),'width'=>0,'maxWidth'=>0,'cols'=>array(),'minRowSpace'=>-100,'rowGap'=>2,'colGap'=>5,
  72. 'innerLineThickness'=>1,'outerLineThickness'=>1,'splitRows'=>0,'protectRows'=>1,'nextPageY'=>0,
  73. 'shadeHeadingCol'=>array(), 'gridlines' => EZ_GRIDLINE_DEFAULT
  74. );
  75. foreach ($defaults as $key=>$value){
  76. if (is_array($value)){
  77. if (!isset($options[$key]) || !is_array($options[$key])){
  78. $options[$key]=$value;
  79. }
  80. } else {
  81. if (!isset($options[$key])){
  82. $options[$key]=$value;
  83. }
  84. }
  85. }
  86. // @deprecated Compatibility with 'showLines' option
  87. if(isset($options['showLines'])){
  88. switch ($options['showLines']) {
  89. case 0: $options['gridlines'] = 0; break;
  90. case 1: $options['gridlines'] = EZ_GRIDLINE_DEFAULT; break;
  91. case 2: $options['gridlines'] = EZ_GRIDLINE_HEADERONLY + EZ_GRIDLINE_ROWS; break;
  92. case 3: $options['gridlines'] = EZ_GRIDLINE_ROWS; break;
  93. case 4: $options['gridlines'] = EZ_GRIDLINE_HEADERONLY; break;
  94. default: $options['gridlines'] = EZ_GRIDLINE_TABLE + EZ_GRIDLINE_HEADERONLY + EZ_GRIDLINE_COLUMNS;
  95. }
  96. unset($options['showLines']);
  97. }
  98. $options['gap']=2*$options['colGap'];
  99. // Use Y Position of Current Page position in Table
  100. if ($options['nextPageY']) $nextPageY = $this->y;
  101. $middle = ($this->ez['pageWidth']-$this->ez['rightMargin'])/2+($this->ez['leftMargin'])/2;
  102. // figure out the maximum widths of the text within each column
  103. $maxWidth=array();
  104. foreach ($cols as $colName=>$colHeading){
  105. $maxWidth[$colName]=0;
  106. }
  107. // find the maximum cell widths based on the data
  108. foreach ($data as $row){
  109. foreach ($cols as $colName=>$colHeading){
  110. // BUGFIX #16 ignore empty columns | thanks jafjaf
  111. if (empty($row[$colName])) continue;
  112. $w = $this->ezGetTextWidth($options['fontSize'],(string)$row[$colName])*1.01;
  113. if ($w > $maxWidth[$colName]){
  114. $maxWidth[$colName]=$w;
  115. }
  116. }
  117. }
  118. // and the maximum widths to fit in the headings
  119. foreach ($cols as $colName=>$colTitle){
  120. $w = $this->ezGetTextWidth($options['fontSize'],(string)$colTitle)*1.01;
  121. if ($w > $maxWidth[$colName]){
  122. $maxWidth[$colName]=$w;
  123. }
  124. }
  125. // calculate the start positions of each of the columns
  126. $pos=array();
  127. $x=0;
  128. $t=$x;
  129. $adjustmentWidth=0;
  130. $setWidth=0;
  131. foreach ($maxWidth as $colName => $w){
  132. $pos[$colName]=$t;
  133. // if the column width has been specified then set that here, also total the
  134. // width avaliable for adjustment
  135. if (isset($options['cols'][$colName]) && isset($options['cols'][$colName]['width']) && $options['cols'][$colName]['width']>0){
  136. $t=$t+$options['cols'][$colName]['width'];
  137. $maxWidth[$colName] = $options['cols'][$colName]['width']-$options['gap'];
  138. $setWidth += $options['cols'][$colName]['width'];
  139. } else {
  140. $t=$t+$w+$options['gap'];
  141. $adjustmentWidth += $w;
  142. $setWidth += $options['gap'];
  143. }
  144. }
  145. $pos['_end_']=$t;
  146. // if maxWidth is specified, and the table is too wide, and the width has not been set,
  147. // then set the width.
  148. if ($options['width']==0 && $options['maxWidth'] && ($t-$x)>$options['maxWidth']){
  149. // then need to make this one smaller
  150. $options['width']=$options['maxWidth'];
  151. }
  152. if ($options['width'] && $adjustmentWidth>0 && $setWidth<$options['width']){
  153. // first find the current widths of the columns involved in this mystery
  154. $cols0 = array();
  155. $cols1 = array();
  156. $xq=0;
  157. $presentWidth=0;
  158. $last='';
  159. foreach ($pos as $colName=>$p){
  160. if (!isset($options['cols'][$last]) || !isset($options['cols'][$last]['width']) || $options['cols'][$last]['width']<=0){
  161. if (strlen($last)){
  162. $cols0[$last]=$p-$xq -$options['gap'];
  163. $presentWidth += ($p-$xq - $options['gap']);
  164. }
  165. } else {
  166. $cols1[$last]=$p-$xq;
  167. }
  168. $last=$colName;
  169. $xq=$p;
  170. }
  171. // $cols0 contains the widths of all the columns which are not set
  172. $neededWidth = $options['width']-$setWidth;
  173. // if needed width is negative then add it equally to each column, else get more tricky
  174. if ($presentWidth<$neededWidth){
  175. foreach ($cols0 as $colName=>$w){
  176. $cols0[$colName]+= ($neededWidth-$presentWidth)/count($cols0);
  177. }
  178. } else {
  179. $cnt=0;
  180. while ($presentWidth>$neededWidth && $cnt<100){
  181. $cnt++; // insurance policy
  182. // find the widest columns, and the next to widest width
  183. $aWidest = array();
  184. $nWidest=0;
  185. $widest=0;
  186. foreach ($cols0 as $colName=>$w){
  187. if ($w>$widest){
  188. $aWidest=array($colName);
  189. $nWidest = $widest;
  190. $widest=$w;
  191. } else if ($w==$widest){
  192. $aWidest[]=$colName;
  193. }
  194. }
  195. // then figure out what the width of the widest columns would have to be to take up all the slack
  196. $newWidestWidth = $widest - ($presentWidth-$neededWidth)/count($aWidest);
  197. if ($newWidestWidth > $nWidest){
  198. // then there is space to set them to this
  199. foreach ($aWidest as $colName){
  200. $cols0[$colName] = $newWidestWidth;
  201. }
  202. $presentWidth=$neededWidth;
  203. } else {
  204. // there is not space, reduce the size of the widest ones down to the next size down, and we
  205. // will go round again
  206. foreach ($aWidest as $colName){
  207. $cols0[$colName] = $nWidest;
  208. }
  209. $presentWidth=$presentWidth-($widest-$nWidest)*count($aWidest);
  210. }
  211. }
  212. }
  213. // $cols0 now contains the new widths of the constrained columns.
  214. // now need to update the $pos and $maxWidth arrays
  215. $xq=0;
  216. foreach ($pos as $colName=>$p){
  217. $pos[$colName]=$xq;
  218. if (!isset($options['cols'][$colName]) || !isset($options['cols'][$colName]['width']) || $options['cols'][$colName]['width']<=0){
  219. if (isset($cols0[$colName])){
  220. $xq += $cols0[$colName] + $options['gap'];
  221. $maxWidth[$colName]=$cols0[$colName];
  222. }
  223. } else {
  224. if (isset($cols1[$colName])){
  225. $xq += $cols1[$colName];
  226. }
  227. }
  228. }
  229. $t=$x+$options['width'];
  230. $pos['_end_']=$t;
  231. }
  232. // now adjust the table to the correct location across the page
  233. switch ($options['xPos']){
  234. case 'left':
  235. $xref = $this->ez['leftMargin'];
  236. break;
  237. case 'right':
  238. $xref = $this->ez['pageWidth'] - $this->ez['rightMargin'];
  239. break;
  240. case 'centre':
  241. case 'center':
  242. $xref = $middle;
  243. break;
  244. default:
  245. $xref = $options['xPos'];
  246. break;
  247. }
  248. switch ($options['xOrientation']){
  249. case 'left':
  250. $dx = $xref-$t;
  251. break;
  252. case 'right':
  253. $dx = $xref;
  254. break;
  255. case 'centre':
  256. case 'center':
  257. $dx = $xref-$t/2;
  258. break;
  259. }
  260. // applied patch #18 alignment fixes for tables and images | thank you Emil Totev
  261. $dx += $options['colGap'];
  262. foreach ($pos as $k=>$v){
  263. $pos[$k]=$v+$dx;
  264. }
  265. $x0=$x+$dx;
  266. $x1=$t+$dx;
  267. $baseLeftMargin = $this->ez['leftMargin'];
  268. $basePos = $pos;
  269. $baseX0 = $x0;
  270. $baseX1 = $x1;
  271. // ok, just about ready to make me a table
  272. $this->setColor($options['textCol'][0],$options['textCol'][1],$options['textCol'][2]);
  273. $this->setStrokeColor($options['shadeCol'][0],$options['shadeCol'][1],$options['shadeCol'][2]);
  274. $middle = ($x1+$x0)/2;
  275. // start a transaction which will be used to regress the table, if there are not enough rows protected
  276. if ($options['protectRows']>0){
  277. $this->transaction('start');
  278. $movedOnce=0;
  279. }
  280. $abortTable = 1;
  281. while ($abortTable){
  282. $abortTable=0;
  283. $dm = $this->ez['leftMargin']-$baseLeftMargin;
  284. foreach ($basePos as $k=>$v){
  285. $pos[$k]=$v+$dm;
  286. }
  287. $x0=$baseX0+$dm;
  288. $x1=$baseX1+$dm;
  289. $middle = ($x1+$x0)/2;
  290. // if the title is set, then do that
  291. if (strlen($title)){
  292. $w = $this->getTextWidth($options['titleFontSize'],$title);
  293. $this->y -= $this->getFontHeight($options['titleFontSize']);
  294. if ($this->y < $this->ez['bottomMargin']){
  295. $this->ezNewPage();
  296. // margins may have changed on the newpage
  297. $dm = $this->ez['leftMargin']-$baseLeftMargin;
  298. foreach ($basePos as $k=>$v){
  299. $pos[$k]=$v+$dm;
  300. }
  301. $x0=$baseX0+$dm;
  302. $x1=$baseX1+$dm;
  303. $middle = ($x1+$x0)/2;
  304. $this->y -= $this->getFontHeight($options['titleFontSize']);
  305. }
  306. $this->addText($middle-$w/2,$this->y,$options['titleFontSize'],$title);
  307. $this->y -= $options['titleGap'];
  308. }
  309. // margins may have changed on the newpage
  310. $dm = $this->ez['leftMargin']-$baseLeftMargin;
  311. foreach ($basePos as $k=>$v){
  312. $pos[$k]=$v+$dm;
  313. }
  314. $x0=$baseX0+$dm;
  315. $x1=$baseX1+$dm;
  316. $y=$this->y; // to simplify the code a bit
  317. // make the table
  318. $height = $this->getFontHeight($options['fontSize']);
  319. $descender = $this->getFontDescender($options['fontSize']);
  320. $y0=$y+$descender;
  321. $dy=0;
  322. if ($options['showHeadings']){
  323. // patch #9 start
  324. if (isset($options['shadeHeadingCol']) && count($options['shadeHeadingCol']) == 3){
  325. $this->saveState();
  326. $textHeadingsObjectId = $this->openObject();
  327. $this->closeObject();
  328. $this->addObject($textHeadingsObjectId);
  329. $this->reopenObject($textHeadingsObjectId);
  330. }
  331. // patch #9 end
  332. // this function will move the start of the table to a new page if it does not fit on this one
  333. $headingHeight = $this->ezTableColumnHeadings($cols,$pos,$maxWidth,$height,$descender,$options['rowGap'],$options['fontSize'],$y,$options);
  334. $y0 = $y+$headingHeight+$options['rowGap'];
  335. $y1 = $y - $options['rowGap']*2;
  336. $dm = $this->ez['leftMargin']-$baseLeftMargin;
  337. foreach ($basePos as $k=>$v){
  338. $pos[$k]=$v+$dm;
  339. }
  340. $x0=$baseX0+$dm;
  341. $x1=$baseX1+$dm;
  342. // patch #9 start
  343. if (isset($options['shadeHeadingCol']) && count($options['shadeHeadingCol']) == 3){
  344. $this->closeObject();
  345. $this->setColor($options['shadeHeadingCol'][0],$options['shadeHeadingCol'][1],$options['shadeHeadingCol'][2],1);
  346. $this->filledRectangle($x0-$options['gap']/2,$y+$descender,$x1-$x0,($y0 - $y - $descender));
  347. $this->reopenObject($textHeadingsObjectId);
  348. $this->closeObject();
  349. $this->restoreState();
  350. }
  351. // patch #9 end
  352. } else {
  353. $y1 = $y0;
  354. }
  355. $firstLine=1;
  356. // open an object here so that the text can be put in over the shading
  357. if ($options['shaded'] || $options['showBgCol']){
  358. $this->saveState();
  359. $textObjectId = $this->openObject();
  360. $this->closeObject();
  361. $this->addObject($textObjectId);
  362. $this->reopenObject($textObjectId);
  363. }
  364. $cnt=0;
  365. $newPage=0;
  366. foreach ($data as $row){
  367. $cnt++;
  368. // the transaction support will be used to prevent rows being split
  369. if ($options['splitRows']==0){
  370. $pageStart = $this->ezPageCount;
  371. if (isset($this->ez['columns']) && $this->ez['columns']['on']==1){
  372. $columnStart = $this->ez['columns']['colNum'];
  373. }
  374. $this->transaction('start');
  375. $row_orig = $row;
  376. $y_orig = $y;
  377. $y0_orig = $y0;
  378. $y1_orig = $y1;
  379. }
  380. $ok=0;
  381. $secondTurn=0;
  382. while(!$abortTable && $ok == 0){
  383. $mx=0;
  384. $newRow=1;
  385. while(!$abortTable && ($newPage || $newRow)){
  386. $y-=$height;
  387. if ($newPage || $y<$this->ez['bottomMargin'] || (isset($options['minRowSpace']) && $y<($this->ez['bottomMargin']+$options['minRowSpace'])) ){
  388. // check that enough rows are with the heading
  389. if ($options['protectRows']>0 && $movedOnce==0 && $cnt<=$options['protectRows']){
  390. // then we need to move the whole table onto the next page
  391. $movedOnce = 1;
  392. $abortTable = 1;
  393. }
  394. $y2=$y-$mx+2*$height+$descender-$newRow*$height;
  395. if ($options['gridlines']){
  396. $y1+=$descender;
  397. if (!$options['showHeadings']){
  398. $y0=$y1;
  399. }
  400. $this->ezTableDrawLines($pos,$options['gap'], $options['rowGap'],$x0,$x1,$y0,$y1,$y2,$options['lineCol'],$options['innerLineThickness'],$options['outerLineThickness'], $options['gridlines']);
  401. }
  402. if ($options['shaded'] || $options['showBgCol']){
  403. $this->closeObject();
  404. $this->restoreState();
  405. }
  406. $this->ezNewPage();
  407. // and the margins may have changed, this is due to the possibility of the columns being turned on
  408. // as the columns are managed by manipulating the margins
  409. $dm = $this->ez['leftMargin']-$baseLeftMargin;
  410. foreach ($basePos as $k=>$v){
  411. $pos[$k]=$v+$dm;
  412. }
  413. $x0=$baseX0+$dm;
  414. $x1=$baseX1+$dm;
  415. if ($options['shaded'] || $options['showBgCol']){
  416. $this->saveState();
  417. $textObjectId = $this->openObject();
  418. $this->closeObject();
  419. $this->addObject($textObjectId);
  420. $this->reopenObject($textObjectId);
  421. }
  422. $this->setColor($options['textCol'][0],$options['textCol'][1],$options['textCol'][2],1);
  423. $y = ($options['nextPageY'])?$nextPageY:($this->ez['pageHeight']-$this->ez['topMargin']);
  424. $y0=$y+$descender;
  425. $mx=0;
  426. if ($options['showHeadings']){
  427. // patch #9 start
  428. if (isset($options['shadeHeadingCol']) && count($options['shadeHeadingCol']) == 3){
  429. $this->saveState();
  430. $textHeadingsObjectId = $this->openObject();
  431. $this->closeObject();
  432. $this->addObject($textHeadingsObjectId);
  433. $this->reopenObject($textHeadingsObjectId);
  434. $this->closeObject();
  435. $this->setColor($options['shadeHeadingCol'][0],$options['shadeHeadingCol'][1],$options['shadeHeadingCol'][2],1);
  436. $this->filledRectangle($x0-$options['gap']/2,$y0,$x1-$x0,-($headingHeight-$descender+$options['rowGap']) );
  437. $this->reopenObject($textHeadingsObjectId);
  438. $this->closeObject();
  439. $this->restoreState();
  440. }
  441. // patch #9 end
  442. $this->ezTableColumnHeadings($cols,$pos,$maxWidth,$height,$descender,$options['rowGap'],$options['fontSize'],$y,$options);
  443. $y1 = $y - $options['rowGap']*2;
  444. } else {
  445. $y1=$y0;
  446. }
  447. $firstLine=1;
  448. $y -= $height;
  449. }
  450. $newRow=0;
  451. // write the actual data
  452. // if these cells need to be split over a page, then $newPage will be set, and the remaining
  453. // text will be placed in $leftOvers
  454. $newPage=0;
  455. $leftOvers=array();
  456. foreach ($cols as $colName=>$colTitle){
  457. $this->ezSetY($y+$height);
  458. $colNewPage=0;
  459. if (isset($row[$colName])){
  460. // KH: parse image tags and calculate the position and size for the images
  461. $this->parseImages($row[$colName],$maxWidth[$colName],0,($this->y - $options['rowGap'] - 2 * abs($descender)));
  462. if (isset($options['cols'][$colName]) && isset($options['cols'][$colName]['link']) && strlen($options['cols'][$colName]['link'])){
  463. //$lines = explode("\n",$row[$colName]);
  464. $lines = preg_split("[\r\n|\r|\n]",$row[$colName]);
  465. if (isset($row[$options['cols'][$colName]['link']]) && strlen($row[$options['cols'][$colName]['link']])){
  466. foreach ($lines as $k=>$v){
  467. $lines[$k]='<c:alink:'.$row[$options['cols'][$colName]['link']].'>'.$v.'</c:alink>';
  468. }
  469. }
  470. } else {
  471. //$lines = explode("\n",$row[$colName]);
  472. $lines = preg_split("[\r\n|\r|\n]",$row[$colName]);
  473. }
  474. } else {
  475. $lines = array();
  476. }
  477. $this->y -= $options['rowGap'];
  478. foreach ($lines as $line){
  479. $line = $this->ezProcessText($line);
  480. $start=1;
  481. while (strlen($line) || $start){
  482. // KH: get the height of all images in the current table cell
  483. $_image = $this->checkForImage($line);
  484. if ($_image>0) {
  485. // TODO: Bildbreite anpassen, wenn Bild breiter ist als die Spalte
  486. $_lineheight = $_image + 2 * abs($descender);
  487. } else {
  488. $_lineheight = $height;
  489. }
  490. $start=0;
  491. if (!$colNewPage){
  492. // KH: modified to set the new cell height
  493. $this->y=$this->y-$_lineheight;
  494. }
  495. if ($this->y < $this->ez['bottomMargin']){
  496. // $this->ezNewPage();
  497. $newPage=1; // whether a new page is required for any of the columns
  498. $colNewPage=1; // whether a new page is required for this column
  499. }
  500. if ($colNewPage){
  501. if (isset($leftOvers[$colName])){
  502. $leftOvers[$colName].="\n".$line;
  503. } else {
  504. $leftOvers[$colName] = $line;
  505. }
  506. $line='';
  507. } else {
  508. if (isset($options['cols'][$colName]) && isset($options['cols'][$colName]['justification']) ){
  509. $just = $options['cols'][$colName]['justification'];
  510. } else {
  511. $just='left';
  512. }
  513. $line=$this->addText($pos[$colName],$this->y, $options['fontSize'], $line, $maxWidth[$colName], $just);
  514. }
  515. }
  516. }
  517. $dy=$y+$height-$this->y+$options['rowGap'];
  518. if ($dy-$height*$newPage>$mx){
  519. $mx=$dy-$height*$newPage;
  520. }
  521. }
  522. // set $row to $leftOvers so that they will be processed onto the new page
  523. $row = $leftOvers;
  524. // now add the shading underneath
  525. if ($options['shaded'] && $cnt%2==0){
  526. $this->closeObject();
  527. $this->setColor($options['shadeCol'][0],$options['shadeCol'][1],$options['shadeCol'][2],1);
  528. $this->filledRectangle($x0-$options['gap']/2,$y+$descender+$height-$mx,$x1-$x0,$mx);
  529. $this->reopenObject($textObjectId);
  530. }
  531. if ($options['shaded']==2 && $cnt%2==1){
  532. $this->closeObject();
  533. $this->setColor($options['shadeCol2'][0],$options['shadeCol2'][1],$options['shadeCol2'][2],1);
  534. $this->filledRectangle($x0-$options['gap']/2,$y+$descender+$height-$mx,$x1-$x0,$mx);
  535. $this->reopenObject($textObjectId);
  536. }
  537. // if option showColColor is set, then can draw filledrectangle column
  538. if ($options['showBgCol'] == 1) {
  539. foreach ($cols as $colName=>$colTitle){
  540. if ( isset($options['cols'][$colName]) && isset($options['cols'][$colName]['bgcolor'])) {
  541. $arrColColor = $options['cols'][$colName]['bgcolor'];
  542. $this->closeObject();
  543. $this->setColor($arrColColor[0],$arrColColor[1],$arrColColor[2],1);
  544. $this->filledRectangle($pos[$colName]-$options['gap']/2,$y+$descender+$height-$mx,$maxWidth[$colName]+$options['gap'],$mx);
  545. $this->reopenObject($textObjectId);
  546. }
  547. }
  548. }
  549. if ($options['gridlines'] & EZ_GRIDLINE_ROWS){
  550. // then draw a line on the top of each block
  551. // $this->closeObject();
  552. $this->saveState();
  553. $this->setStrokeColor($options['lineCol'][0],$options['lineCol'][1],$options['lineCol'][2],1);
  554. // $this->line($x0-$options['gap']/2,$y+$descender+$height-$mx,$x1-$x0,$mx);
  555. if ($firstLine){
  556. $firstLine=0;
  557. } else {
  558. $this->setLineStyle($options['innerLineThickness']);
  559. $this->line($x0-$options['gap']/2,$y+$descender+$height,$x1-$options['gap']/2,$y+$descender+$height);
  560. }
  561. $this->restoreState();
  562. // $this->reopenObject($textObjectId);
  563. }
  564. } // end of while
  565. $y=$y-$mx+$height;
  566. // checking row split over pages
  567. if ($options['splitRows']==0){
  568. if ( ( ($this->ezPageCount != $pageStart) || (isset($this->ez['columns']) && $this->ez['columns']['on']==1 && $columnStart != $this->ez['columns']['colNum'] )) && $secondTurn==0){
  569. // then we need to go back and try that again !
  570. $newPage=1;
  571. $secondTurn=1;
  572. $this->transaction('rewind');
  573. $row = $row_orig;
  574. $y = $y_orig;
  575. $y0 = $y0_orig;
  576. $y1 = $y1_orig;
  577. $ok=0;
  578. $dm = $this->ez['leftMargin']-$baseLeftMargin;
  579. foreach ($basePos as $k=>$v){
  580. $pos[$k]=$v+$dm;
  581. }
  582. $x0=$baseX0+$dm;
  583. $x1=$baseX1+$dm;
  584. } else {
  585. $this->transaction('commit');
  586. $ok=1;
  587. }
  588. } else {
  589. $ok=1; // don't go round the loop if splitting rows is allowed
  590. }
  591. } // end of while to check for row splitting
  592. if ($abortTable){
  593. if ($ok==0){
  594. $this->transaction('abort');
  595. }
  596. // only the outer transaction should be operational
  597. $this->transaction('rewind');
  598. $this->ezNewPage();
  599. break;
  600. }
  601. } // end of foreach ($data as $row)
  602. } // end of while ($abortTable)
  603. // table has been put on the page, the rows guarded as required, commit.
  604. $this->transaction('commit');
  605. $y2=$y+$descender;
  606. if ($options['gridlines']){
  607. $y1+=$descender;
  608. if (!$options['showHeadings']){
  609. $y0=$y1;
  610. }
  611. $this->ezTableDrawLines($pos,$options['gap'], $options['rowGap'],$x0,$x1,$y0,$y1,$y2,$options['lineCol'],$options['innerLineThickness'],$options['outerLineThickness'], $options['gridlines']);
  612. }
  613. // close the object for drawing the text on top
  614. if ($options['shaded'] || $options['showBgCol']){
  615. $this->closeObject();
  616. $this->restoreState();
  617. }
  618. $this->y=$y;
  619. return $y;
  620. }
  621. /**
  622. * parses and returns all image tags from the given text
  623. *
  624. * @param String $text input text
  625. * @return array found image tags
  626. * @access public
  627. */
  628. function getImagesFromText($text = '') {
  629. preg_match_all("/\<C:showimage:([^>]*)\>/U",$text,$matches);
  630. return $matches;
  631. }
  632. /**
  633. * calculate the total height for all images in text
  634. *
  635. * @param String $text input text
  636. * @return float total height of all images in the text
  637. * @access public
  638. */
  639. function checkForImage($text) {
  640. $height = 0;
  641. $matches = $this->getImagesFromText($text);
  642. for ($key=0; $key<count($matches[0]); $key++) {
  643. $CezShowimageParameter = new CezShowimageParameter();
  644. $params = $CezShowimageParameter->create($matches[1][$key]);
  645. if ($params->getHeight()>0) {
  646. $height = $height + $params->getHeight();
  647. } else {
  648. $height = $height + $params->getOriginalHeight();
  649. }
  650. }
  651. return $height;
  652. }
  653. /**
  654. * add a given image to the document
  655. *
  656. * @param CezShowimageParameter $params image parameter
  657. * @param float $x horizontal position
  658. * @param float $y vertical position
  659. * @param $w width
  660. * @param $h height
  661. * @param $quality image quality
  662. * @access protected
  663. */
  664. function addImage(& $params, $x = 0, $y = 0, $w=0,$h=0,$quality=75, $angle = 0) {
  665. if ($params->isUrl()) {
  666. if (function_exists('imagecreatefrompng')) {
  667. switch($params->getImageType()) {
  668. case 3: // png
  669. $image = imagecreatefrompng($params->getFilename());
  670. break;
  671. case 2: // jpeg
  672. $image = imagecreatefromjpeg($params->getFilename());
  673. break;
  674. case 1: // gif
  675. $image = imagecreatefromgif($params->getFilename());
  676. break;
  677. }
  678. parent::addImage($image, $x, $y, $params->getWidth(), $params->getHeight(), $quality, $angle);
  679. }
  680. } else {
  681. // check for image type, currently only png and jpeg supported
  682. switch($params->getImageType()) {
  683. case 3: // png
  684. parent::addPngFromFile($params->getFilename(), $x, $y, $params->getWidth(), $params->getHeight(), $angle);
  685. break;
  686. case 2: // jpeg
  687. parent::addJpegFromFile($params->getFilename(), $x, $y, $params->getWidth(), $params->getHeight(), $angle);
  688. break;
  689. case 1: // gif
  690. parent::addGifFromFile($params->getFilename(), $x, $y, $params->getWidth(), $params->getHeight(), $angle);
  691. break;
  692. }
  693. }
  694. }
  695. /**
  696. * callback method for adding an image to the document
  697. *
  698. * note: this method is called by the pdf generator class callback method <code>showImage($info)</code>
  699. *
  700. * @param Cezpdf $ezpdf current cezpdf object
  701. * @param array $info callback data array (see the callback function part in the R&amp;OS pdf documentation)
  702. * @access public
  703. */
  704. function showimage($info) {
  705. if ($info['status']=='start') {
  706. $CezShowimageParameter = new CezShowimageParameter();
  707. $params = $CezShowimageParameter->create($info['p']);
  708. if ($params->isReadable()) {
  709. $y = ($params->getPositionY()>0) ? $params->getPositionY() : $info['y'];
  710. $this->addImage($params, $info['x'], $y);
  711. }
  712. }
  713. }
  714. /**
  715. * searches for <C:showimage:...> and replaces all occurences with extended tag information
  716. *
  717. * the extended informations contains the vertical position and the (calculated) image
  718. * dimensions
  719. *
  720. * @param String $text text to parse
  721. * @param int $maxwidth optional maximal width for the image
  722. * @param int $maxheight optional maximal height for the image
  723. * @param int $currenty current vertical (y) position on the pdf page
  724. * @access public
  725. */
  726. function parseImages(&$text, $maxwidth = 0, $maxheight = 0, $currenty = 0) {
  727. $matches = $this->getImagesFromText($text);
  728. for ($key=0; $key<count($matches[0]); $key++) {
  729. $CezShowimageParameter = new CezShowimageParameter();
  730. $params = $CezShowimageParameter->create($matches[1][$key]);
  731. if ($params->isReadable()) {
  732. $width = $params->getWidth();
  733. $height = $params->getHeight();
  734. if ($width==0 && $height>0) {
  735. $width = $height/$params->getOriginalHeight() * $params->getOriginalWidth();
  736. } elseif ($height==0 && $width>0) {
  737. $height = $width/$params->getOriginalWidth() * $params->getOriginalHeight();
  738. } elseif ($height==0 && $width==0) {
  739. $width = $params->getOriginalWidth();
  740. $height = $params->getOriginalHeight();
  741. }
  742. if ($maxwidth>0 && $width>$maxwidth) {
  743. $height = ($maxwidth * $height)/$width;
  744. $width = $maxwidth;
  745. }
  746. if ($maxheight>0 && $height>$maxheight) {
  747. $width = ($maxheight * $width)/$height;
  748. $height = $maxheight;
  749. }
  750. $currenty = $currenty - $height;
  751. $imagetag = '<C:showimage:'.$params->getFilename().' '.round($width).' '.round($height).' '.$currenty.'>';
  752. } else {
  753. $imagetag = '';
  754. }
  755. $text = str_replace($matches[0][$key],$imagetag,$text);
  756. }
  757. }
  758. /**
  759. * check for images an calculate their maximum width
  760. *
  761. * note: the image tags will be removed from the input text, this method is required due to further
  762. * calculation in the original pdf class
  763. *
  764. * @param String $text input text
  765. * @return float calculated maximum height
  766. * @access public
  767. */
  768. function parseMaximumWidth(& $text) {
  769. $mx = 0;
  770. $matches = $this->getImagesFromText($text);
  771. for ($key=0; $key<count($matches[0]); $key++) {
  772. $CezShowimageParameter = new CezShowimageParameter();
  773. $params = $CezShowimageParameter->create($matches[1][$key]);
  774. if ($params->getWidth()>$mx) {
  775. $mx = $params->getWidth();
  776. } elseif ($params->getOriginalWidth()>$mx) {
  777. $mx = $params->getOriginalWidth();
  778. }
  779. // remove the Image-Tag from the text for further calculation
  780. $text = str_replace($matches[0][$key],'',$text);
  781. }
  782. $mx = min($mx, $this->ezPdf->ez['pageWidth']);
  783. return $mx;
  784. }
  785. }
  786. /**
  787. * parameter object
  788. */
  789. class CezShowimageParameter {
  790. var $filename = '';
  791. var $width = 0;
  792. var $height = 0;
  793. var $imageType = 0;
  794. var $imageHeight = 0;
  795. var $imageWidth = 0;
  796. var $IMAGETYPE_GIF = 1;
  797. var $IMAGETYPE_JPG = 2;
  798. var $IMAGETYPE_PNG = 3;
  799. var $fileIsUrl = false;
  800. var $fileIsReadable = false;
  801. var $positionX = 0;
  802. var $positionY = 0;
  803. function CezShowimageParameter() {
  804. }
  805. /**
  806. * gets the image width
  807. *
  808. * @return int image width
  809. * @access public
  810. */
  811. function getWidth() {
  812. return $this->width;
  813. }
  814. /**
  815. * gets the image height
  816. *
  817. * @return int image height
  818. * @access public
  819. */
  820. function getHeight() {
  821. return $this->height;
  822. }
  823. /**
  824. * gets the filename
  825. *
  826. * @return String filename
  827. * @access public
  828. */
  829. function getFilename() {
  830. return $this->filename;
  831. }
  832. function getImagetype() {
  833. return $this->imageType;
  834. }
  835. function getOriginalHeight() {
  836. return $this->imageHeight;
  837. }
  838. function getOriginalWidth() {
  839. return $this->imageWidth;
  840. }
  841. function isUrl() {
  842. return $this->fileIsUrl;
  843. }
  844. function isReadable() {
  845. return $this->fileIsReadable;
  846. }
  847. function getPositionX() {
  848. return $this->positionX;
  849. }
  850. function getPositionY() {
  851. return $this->positionY;
  852. }
  853. /**
  854. * @access protected
  855. */
  856. function _parse($param = '') {
  857. $params = explode(" ", $param);
  858. $this->filename = urldecode($params[0]);
  859. if (substr($this->filename,0,5)=='http:' || substr($this->filename,0,6)=='https:') {
  860. $this->fileIsUrl = true;
  861. $fd = fopen($this->filename, "r");
  862. $this->fileIsReadable = ($fd!==false) ? true : false;
  863. fclose($fd);
  864. } else {
  865. $this->fileIsUrl = false;
  866. $this->fileIsReadable = is_readable($this->filename);
  867. }
  868. if (isset($params[2]) && $params[2]>0) {
  869. $this->height = $params[2];
  870. }
  871. if (isset($params[1]) && $params[1]>0) {
  872. $this->width = $params[1];
  873. }
  874. if (isset($params[3]) && $params[3]>0) {
  875. $this->positionY = $params[3];
  876. }
  877. $_imagesize = getImagesize($this->filename);
  878. if (is_array($_imagesize)) {
  879. $this->imageType = $_imagesize[2];
  880. $this->imageWidth = $_imagesize[0];
  881. $this->imageHeight = $_imagesize[1];
  882. }
  883. }
  884. /**
  885. * creates the parameter object from the given parameter list
  886. *
  887. * @param String $param parameter list as string
  888. * @return CezShowimageParameter parameter object
  889. * @access public
  890. */
  891. function create($param = '') {
  892. $obj = new CezShowimageParameter();
  893. $obj->_parse($param);
  894. return $obj;
  895. }
  896. }
  897. ?>