changed git call from https to git readonly
[atutor.git] / mods / pdf_converter / class.ezpdf.php
1 <?php\r
2 \r
3 include_once('class.pdf.php');\r
4 \r
5 class Cezpdf extends Cpdf {\r
6 //==============================================================================\r
7 // this class will take the basic interaction facilities of the Cpdf class\r
8 // and make more useful functions so that the user does not have to\r
9 // know all the ins and outs of pdf presentation to produce something pretty.\r
10 //\r
11 // IMPORTANT NOTE\r
12 // there is no warranty, implied or otherwise with this software.\r
13 //\r
14 // version 009 (versioning is linked to class.pdf.php)\r
15 //\r
16 // released under a public domain licence.\r
17 //\r
18 // Wayne Munro, R&OS Ltd, http://www.ros.co.nz/pdf\r
19 //==============================================================================\r
20 \r
21 var $ez=array('fontSize'=>10); // used for storing most of the page configuration parameters\r
22 var $y; // this is the current vertical positon on the page of the writing point, very important\r
23 var $ezPages=array(); // keep an array of the ids of the pages, making it easy to go back and add page numbers etc.\r
24 var $ezPageCount=0;\r
25 \r
26 // ------------------------------------------------------------------------------\r
27 \r
28 function Cezpdf($paper='a4',$orientation='portrait'){\r
29         // Assuming that people don't want to specify the paper size using the absolute coordinates\r
30         // allow a couple of options:\r
31         // orientation can be 'portrait' or 'landscape'\r
32         // or, to actually set the coordinates, then pass an array in as the first parameter.\r
33         // the defaults are as shown.\r
34         //\r
35         // -------------------------\r
36         // 2002-07-24 - Nicola Asuni (info@tecnick.com):\r
37         // Added new page formats (45 standard ISO paper formats and 4 american common formats)\r
38         // paper cordinates are calculated in this way: (inches * 72) where 1 inch = 2.54 cm\r
39         //\r
40         // Now you may also pass a 2 values array containing the page width and height in centimeters\r
41         // -------------------------\r
42 \r
43         if (!is_array($paper)){\r
44                 switch (strtoupper($paper)){\r
45                         case '4A0': {$size = array(0,0,4767.87,6740.79); break;}\r
46                         case '2A0': {$size = array(0,0,3370.39,4767.87); break;}\r
47                         case 'A0': {$size = array(0,0,2383.94,3370.39); break;}\r
48                         case 'A1': {$size = array(0,0,1683.78,2383.94); break;}\r
49                         case 'A2': {$size = array(0,0,1190.55,1683.78); break;}\r
50                         case 'A3': {$size = array(0,0,841.89,1190.55); break;}\r
51                         case 'A4': default: {$size = array(0,0,595.28,841.89); break;}\r
52                         case 'A5': {$size = array(0,0,419.53,595.28); break;}\r
53                         case 'A6': {$size = array(0,0,297.64,419.53); break;}\r
54                         case 'A7': {$size = array(0,0,209.76,297.64); break;}\r
55                         case 'A8': {$size = array(0,0,147.40,209.76); break;}\r
56                         case 'A9': {$size = array(0,0,104.88,147.40); break;}\r
57                         case 'A10': {$size = array(0,0,73.70,104.88); break;}\r
58                         case 'B0': {$size = array(0,0,2834.65,4008.19); break;}\r
59                         case 'B1': {$size = array(0,0,2004.09,2834.65); break;}\r
60                         case 'B2': {$size = array(0,0,1417.32,2004.09); break;}\r
61                         case 'B3': {$size = array(0,0,1000.63,1417.32); break;}\r
62                         case 'B4': {$size = array(0,0,708.66,1000.63); break;}\r
63                         case 'B5': {$size = array(0,0,498.90,708.66); break;}\r
64                         case 'B6': {$size = array(0,0,354.33,498.90); break;}\r
65                         case 'B7': {$size = array(0,0,249.45,354.33); break;}\r
66                         case 'B8': {$size = array(0,0,175.75,249.45); break;}\r
67                         case 'B9': {$size = array(0,0,124.72,175.75); break;}\r
68                         case 'B10': {$size = array(0,0,87.87,124.72); break;}\r
69                         case 'C0': {$size = array(0,0,2599.37,3676.54); break;}\r
70                         case 'C1': {$size = array(0,0,1836.85,2599.37); break;}\r
71                         case 'C2': {$size = array(0,0,1298.27,1836.85); break;}\r
72                         case 'C3': {$size = array(0,0,918.43,1298.27); break;}\r
73                         case 'C4': {$size = array(0,0,649.13,918.43); break;}\r
74                         case 'C5': {$size = array(0,0,459.21,649.13); break;}\r
75                         case 'C6': {$size = array(0,0,323.15,459.21); break;}\r
76                         case 'C7': {$size = array(0,0,229.61,323.15); break;}\r
77                         case 'C8': {$size = array(0,0,161.57,229.61); break;}\r
78                         case 'C9': {$size = array(0,0,113.39,161.57); break;}\r
79                         case 'C10': {$size = array(0,0,79.37,113.39); break;}\r
80                         case 'RA0': {$size = array(0,0,2437.80,3458.27); break;}\r
81                         case 'RA1': {$size = array(0,0,1729.13,2437.80); break;}\r
82                         case 'RA2': {$size = array(0,0,1218.90,1729.13); break;}\r
83                         case 'RA3': {$size = array(0,0,864.57,1218.90); break;}\r
84                         case 'RA4': {$size = array(0,0,609.45,864.57); break;}\r
85                         case 'SRA0': {$size = array(0,0,2551.18,3628.35); break;}\r
86                         case 'SRA1': {$size = array(0,0,1814.17,2551.18); break;}\r
87                         case 'SRA2': {$size = array(0,0,1275.59,1814.17); break;}\r
88                         case 'SRA3': {$size = array(0,0,907.09,1275.59); break;}\r
89                         case 'SRA4': {$size = array(0,0,637.80,907.09); break;}\r
90                         case 'LETTER': {$size = array(0,0,612.00,792.00); break;}\r
91                         case 'LEGAL': {$size = array(0,0,612.00,1008.00); break;}\r
92                         case 'EXECUTIVE': {$size = array(0,0,521.86,756.00); break;}\r
93                         case 'FOLIO': {$size = array(0,0,612.00,936.00); break;}\r
94                 }\r
95                 switch (strtolower($orientation)){\r
96                         case 'landscape':\r
97                                 $a=$size[3];\r
98                                 $size[3]=$size[2];\r
99                                 $size[2]=$a;\r
100                                 break;\r
101                 }\r
102         } else {\r
103                 if (count($paper)>2) {\r
104                         // then an array was sent it to set the size\r
105                         $size = $paper;\r
106                 }\r
107                 else { //size in centimeters has been passed\r
108                         $size[0] = 0;\r
109                         $size[1] = 0;\r
110                         $size[2] = ( $paper[0] / 2.54 ) * 72;\r
111                         $size[3] = ( $paper[1] / 2.54 ) * 72;\r
112                 }\r
113         }\r
114         $this->Cpdf($size);\r
115         $this->ez['pageWidth']=$size[2];\r
116         $this->ez['pageHeight']=$size[3];\r
117 \r
118         // also set the margins to some reasonable defaults\r
119         $this->ez['topMargin']=30;\r
120         $this->ez['bottomMargin']=30;\r
121         $this->ez['leftMargin']=30;\r
122         $this->ez['rightMargin']=30;\r
123 \r
124         // set the current writing position to the top of the first page\r
125         $this->y = $this->ez['pageHeight']-$this->ez['topMargin'];\r
126         // and get the ID of the page that was created during the instancing process.\r
127         $this->ezPages[1]=$this->getFirstPageId();\r
128         $this->ezPageCount=1;\r
129 }\r
130 \r
131 // ------------------------------------------------------------------------------\r
132 // 2002-07-24: Nicola Asuni (info@tecnick.com)\r
133 // Set Margins in centimeters\r
134 function ezSetCmMargins($top,$bottom,$left,$right){\r
135         $top = ( $top / 2.54 ) * 72;\r
136         $bottom = ( $bottom / 2.54 ) * 72;\r
137         $left = ( $left / 2.54 ) * 72;\r
138         $right = ( $right / 2.54 ) * 72;\r
139         $this->ezSetMargins($top,$bottom,$left,$right);\r
140 }\r
141 // ------------------------------------------------------------------------------\r
142 \r
143 \r
144 function ezColumnsStart($options=array()){\r
145   // start from the current y-position, make the set number of columne\r
146   if (isset($this->ez['columns']) && $this->ez['columns']==1){\r
147         // if we are already in a column mode then just return.\r
148         return;\r
149   }\r
150   $def=array('gap'=>10,'num'=>2);\r
151   foreach($def as $k=>$v){\r
152         if (!isset($options[$k])){\r
153           $options[$k]=$v;\r
154         }\r
155   }\r
156   // setup the columns\r
157   $this->ez['columns']=array('on'=>1,'colNum'=>1);\r
158 \r
159   // store the current margins\r
160   $this->ez['columns']['margins']=array(\r
161          $this->ez['leftMargin']\r
162         ,$this->ez['rightMargin']\r
163         ,$this->ez['topMargin']\r
164         ,$this->ez['bottomMargin']\r
165   );\r
166   // and store the settings for the columns\r
167   $this->ez['columns']['options']=$options;\r
168   // then reset the margins to suit the new columns\r
169   // safe enough to assume the first column here, but start from the current y-position\r
170   $this->ez['topMargin']=$this->ez['pageHeight']-$this->y;\r
171   $width=($this->ez['pageWidth']-$this->ez['leftMargin']-$this->ez['rightMargin']-($options['num']-1)*$options['gap'])/$options['num'];\r
172   $this->ez['columns']['width']=$width;\r
173   $this->ez['rightMargin']=$this->ez['pageWidth']-$this->ez['leftMargin']-$width;\r
174 \r
175 }\r
176 // ------------------------------------------------------------------------------\r
177 function ezColumnsStop(){\r
178   if (isset($this->ez['columns']) && $this->ez['columns']['on']==1){\r
179         $this->ez['columns']['on']=0;\r
180         $this->ez['leftMargin']=$this->ez['columns']['margins'][0];\r
181         $this->ez['rightMargin']=$this->ez['columns']['margins'][1];\r
182         $this->ez['topMargin']=$this->ez['columns']['margins'][2];\r
183         $this->ez['bottomMargin']=$this->ez['columns']['margins'][3];\r
184   }\r
185 }\r
186 // ------------------------------------------------------------------------------\r
187 function ezInsertMode($status=1,$pageNum=1,$pos='before'){\r
188   // puts the document into insert mode. new pages are inserted until this is re-called with status=0\r
189   // by default pages wil be inserted at the start of the document\r
190   switch($status){\r
191         case '1':\r
192           if (isset($this->ezPages[$pageNum])){\r
193                 $this->ez['insertMode']=1;\r
194                 $this->ez['insertOptions']=array('id'=>$this->ezPages[$pageNum],'pos'=>$pos);\r
195           }\r
196           break;\r
197         case '0':\r
198           $this->ez['insertMode']=0;\r
199           break;\r
200   }\r
201 }\r
202 // ------------------------------------------------------------------------------\r
203 \r
204 function ezNewPage(){\r
205   $pageRequired=1;\r
206   if (isset($this->ez['columns']) && $this->ez['columns']['on']==1){\r
207         // check if this is just going to a new column\r
208         // increment the column number\r
209 //echo 'HERE<br>';\r
210         $this->ez['columns']['colNum']++;\r
211 //echo $this->ez['columns']['colNum'].'<br>';\r
212         if ($this->ez['columns']['colNum'] <= $this->ez['columns']['options']['num']){\r
213           // then just reset to the top of the next column\r
214           $pageRequired=0;\r
215         } else {\r
216           $this->ez['columns']['colNum']=1;\r
217           $this->ez['topMargin']=$this->ez['columns']['margins'][2];\r
218         }\r
219 \r
220         $width = $this->ez['columns']['width'];\r
221         $this->ez['leftMargin']=$this->ez['columns']['margins'][0]+($this->ez['columns']['colNum']-1)*($this->ez['columns']['options']['gap']+$width);\r
222         $this->ez['rightMargin']=$this->ez['pageWidth']-$this->ez['leftMargin']-$width;\r
223   }\r
224 //echo 'left='.$this->ez['leftMargin'].'   right='.$this->ez['rightMargin'].'<br>';\r
225 \r
226   if ($pageRequired){\r
227         // make a new page, setting the writing point back to the top\r
228         $this->y = $this->ez['pageHeight']-$this->ez['topMargin'];\r
229         // make the new page with a call to the basic class.\r
230         $this->ezPageCount++;\r
231         if (isset($this->ez['insertMode']) && $this->ez['insertMode']==1){\r
232           $id = $this->ezPages[$this->ezPageCount] = $this->newPage(1,$this->ez['insertOptions']['id'],$this->ez['insertOptions']['pos']);\r
233           // then manipulate the insert options so that inserted pages follow each other\r
234           $this->ez['insertOptions']['id']=$id;\r
235           $this->ez['insertOptions']['pos']='after';\r
236         } else {\r
237           $this->ezPages[$this->ezPageCount] = $this->newPage();\r
238         }\r
239   } else {\r
240         $this->y = $this->ez['pageHeight']-$this->ez['topMargin'];\r
241   }\r
242 }\r
243 \r
244 // ------------------------------------------------------------------------------\r
245 \r
246 function ezSetMargins($top,$bottom,$left,$right){\r
247   // sets the margins to new values\r
248   $this->ez['topMargin']=$top;\r
249   $this->ez['bottomMargin']=$bottom;\r
250   $this->ez['leftMargin']=$left;\r
251   $this->ez['rightMargin']=$right;\r
252   // check to see if this means that the current writing position is outside the\r
253   // writable area\r
254   if ($this->y > $this->ez['pageHeight']-$top){\r
255         // then move y down\r
256         $this->y = $this->ez['pageHeight']-$top;\r
257   }\r
258   if ( $this->y < $bottom){\r
259         // then make a new page\r
260         $this->ezNewPage();\r
261   }\r
262 }\r
263 \r
264 // ------------------------------------------------------------------------------\r
265 \r
266 function ezGetCurrentPageNumber(){\r
267   // return the strict numbering (1,2,3,4..) number of the current page\r
268   return $this->ezPageCount;\r
269 }\r
270 \r
271 // ------------------------------------------------------------------------------\r
272 \r
273 function ezStartPageNumbers($x,$y,$size,$pos='left',$pattern='{PAGENUM} of {TOTALPAGENUM}',$num=''){\r
274   // put page numbers on the pages from here.\r
275   // place then on the 'pos' side of the coordinates (x,y).\r
276   // pos can be 'left' or 'right'\r
277   // use the given 'pattern' for display, where (PAGENUM} and {TOTALPAGENUM} are replaced\r
278   // as required.\r
279   // if $num is set, then make the first page this number, the number of total pages will\r
280   // be adjusted to account for this.\r
281   // Adjust this function so that each time you 'start' page numbers then you effectively start a different batch\r
282   // return the number of the batch, so that they can be stopped in a different order if required.\r
283   if (!$pos || !strlen($pos)){\r
284         $pos='left';\r
285   }\r
286   if (!$pattern || !strlen($pattern)){\r
287         $pattern='{PAGENUM} of {TOTALPAGENUM}';\r
288   }\r
289   if (!isset($this->ez['pageNumbering'])){\r
290         $this->ez['pageNumbering']=array();\r
291   }\r
292   $i = count($this->ez['pageNumbering']);\r
293   $this->ez['pageNumbering'][$i][$this->ezPageCount]=array('x'=>$x,'y'=>$y,'pos'=>$pos,'pattern'=>$pattern,'num'=>$num,'size'=>$size);\r
294   return $i;\r
295 }\r
296 \r
297 // ------------------------------------------------------------------------------\r
298 \r
299 function ezWhatPageNumber($pageNum,$i=0){\r
300   // given a particular generic page number (ie, document numbered sequentially from beginning),\r
301   // return the page number under a particular page numbering scheme ($i)\r
302   $num=0;\r
303   $start=1;\r
304   $startNum=1;\r
305   if (!isset($this->ez['pageNumbering']))\r
306   {\r
307         $this->addMessage('WARNING: page numbering called for and wasn\'t started with ezStartPageNumbers');\r
308         return 0;\r
309   }\r
310   foreach($this->ez['pageNumbering'][$i] as $k=>$v){\r
311         if ($k<=$pageNum){\r
312           if (is_array($v)){\r
313                 // start block\r
314                 if (strlen($v['num'])){\r
315                   // a start was specified\r
316                   $start=$v['num'];\r
317                   $startNum=$k;\r
318                   $num=$pageNum-$startNum+$start;\r
319                 }\r
320           } else {\r
321                 // stop block\r
322                 $num=0;\r
323           }\r
324         }\r
325   }\r
326   return $num;\r
327 }\r
328 \r
329 // ------------------------------------------------------------------------------\r
330 \r
331 function ezStopPageNumbers($stopTotal=0,$next=0,$i=0){\r
332   // if stopTotal=1 then the totalling of pages for this number will stop too\r
333   // if $next=1, then do this page, but not the next, else do not do this page either\r
334   // if $i is set, then stop that particular pagenumbering sequence.\r
335   if (!isset($this->ez['pageNumbering'])){\r
336         $this->ez['pageNumbering']=array();\r
337   }\r
338   if ($next && isset($this->ez['pageNumbering'][$i][$this->ezPageCount]) && is_array($this->ez['pageNumbering'][$i][$this->ezPageCount])){\r
339         // then this has only just been started, this will over-write the start, and nothing will appear\r
340         // add a special command to the start block, telling it to stop as well\r
341         if ($stopTotal){\r
342           $this->ez['pageNumbering'][$i][$this->ezPageCount]['stoptn']=1;\r
343         } else {\r
344           $this->ez['pageNumbering'][$i][$this->ezPageCount]['stopn']=1;\r
345         }\r
346   } else {\r
347         if ($stopTotal){\r
348           $this->ez['pageNumbering'][$i][$this->ezPageCount]='stopt';\r
349         } else {\r
350           $this->ez['pageNumbering'][$i][$this->ezPageCount]='stop';\r
351         }\r
352         if ($next){\r
353           $this->ez['pageNumbering'][$i][$this->ezPageCount].='n';\r
354         }\r
355   }\r
356 }\r
357 \r
358 // ------------------------------------------------------------------------------\r
359 \r
360 function ezPRVTpageNumberSearch($lbl,&$tmp){\r
361   foreach($tmp as $i=>$v){\r
362         if (is_array($v)){\r
363           if (isset($v[$lbl])){\r
364                 return $i;\r
365           }\r
366         } else {\r
367           if ($v==$lbl){\r
368                 return $i;\r
369           }\r
370         }\r
371   }\r
372   return 0;\r
373 }\r
374 \r
375 // ------------------------------------------------------------------------------\r
376 \r
377 function ezPRVTaddPageNumbers(){\r
378   // this will go through the pageNumbering array and add the page numbers are required\r
379   if (isset($this->ez['pageNumbering'])){\r
380         $totalPages1 = $this->ezPageCount;\r
381         $tmp1=$this->ez['pageNumbering'];\r
382         $status=0;\r
383         foreach($tmp1 as $i=>$tmp){\r
384           // do each of the page numbering systems\r
385           // firstly, find the total pages for this one\r
386           $k = $this->ezPRVTpageNumberSearch('stopt',$tmp);\r
387           if ($k && $k>0){\r
388                 $totalPages = $k-1;\r
389           } else {\r
390                 $l = $this->ezPRVTpageNumberSearch('stoptn',$tmp);\r
391                 if ($l && $l>0){\r
392                   $totalPages = $l;\r
393                 } else {\r
394                   $totalPages = $totalPages1;\r
395                 }\r
396           }\r
397           foreach ($this->ezPages as $pageNum=>$id){\r
398                 if (isset($tmp[$pageNum])){\r
399                   if (is_array($tmp[$pageNum])){\r
400                         // then this must be starting page numbers\r
401                         $status=1;\r
402                         $info = $tmp[$pageNum];\r
403                         $info['dnum']=$info['num']-$pageNum;\r
404                         // also check for the special case of the numbering stopping and starting on the same page\r
405                         if (isset($info['stopn']) || isset($info['stoptn']) ){\r
406                           $status=2;\r
407                         }\r
408                   } else if ($tmp[$pageNum]=='stop' || $tmp[$pageNum]=='stopt'){\r
409                         // then we are stopping page numbers\r
410                         $status=0;\r
411                   } else if ($status==1 && ($tmp[$pageNum]=='stoptn' || $tmp[$pageNum]=='stopn')){\r
412                         // then we are stopping page numbers\r
413                         $status=2;\r
414                   }\r
415                 }\r
416                 if ($status){\r
417                   // then add the page numbering to this page\r
418                   if (strlen($info['num'])){\r
419                         $num=$pageNum+$info['dnum'];\r
420                   } else {\r
421                         $num=$pageNum;\r
422                   }\r
423                   $total = $totalPages+$num-$pageNum;\r
424                   $pat = str_replace('{PAGENUM}',$num,$info['pattern']);\r
425                   $pat = str_replace('{TOTALPAGENUM}',$total,$pat);\r
426                   $this->reopenObject($id);\r
427                   switch($info['pos']){\r
428                         case 'right':\r
429                           $this->addText($info['x'],$info['y'],$info['size'],$pat);\r
430                           break;\r
431                         default:\r
432                           $w=$this->getTextWidth($info['size'],$pat);\r
433                           $this->addText($info['x']-$w,$info['y'],$info['size'],$pat);\r
434                           break;\r
435                   }\r
436                   $this->closeObject();\r
437                 }\r
438                 if ($status==2){\r
439                   $status=0;\r
440                 }\r
441           }\r
442         }\r
443   }\r
444 }\r
445 \r
446 // ------------------------------------------------------------------------------\r
447 \r
448 function ezPRVTcleanUp(){\r
449   $this->ezPRVTaddPageNumbers();\r
450 }\r
451 \r
452 // ------------------------------------------------------------------------------\r
453 \r
454 function ezStream($options=''){\r
455   $this->ezPRVTcleanUp();\r
456   $this->stream($options);\r
457 }\r
458 \r
459 // ------------------------------------------------------------------------------\r
460 \r
461 function ezOutput($options=0){\r
462   $this->ezPRVTcleanUp();\r
463   return $this->output($options);\r
464 }\r
465 \r
466 // ------------------------------------------------------------------------------\r
467 \r
468 function ezSetY($y){\r
469   // used to change the vertical position of the writing point.\r
470   $this->y = $y;\r
471   if ( $this->y < $this->ez['bottomMargin']){\r
472         // then make a new page\r
473         $this->ezNewPage();\r
474   }\r
475 }\r
476 \r
477 // ------------------------------------------------------------------------------\r
478 \r
479 function ezSetDy($dy,$mod=''){\r
480   // used to change the vertical position of the writing point.\r
481   // changes up by a positive increment, so enter a negative number to go\r
482   // down the page\r
483   // if $mod is set to 'makeSpace' and a new page is forced, then the pointed will be moved\r
484   // down on the new page, this will allow space to be reserved for graphics etc.\r
485   $this->y += $dy;\r
486   if ( $this->y < $this->ez['bottomMargin']){\r
487         // then make a new page\r
488         $this->ezNewPage();\r
489         if ($mod=='makeSpace'){\r
490           $this->y += $dy;\r
491         }\r
492   }\r
493 }\r
494 \r
495 // ------------------------------------------------------------------------------\r
496 \r
497 function ezPrvtTableDrawLines($pos,$gap,$x0,$x1,$y0,$y1,$y2,$col,$inner,$outer,$opt=1){\r
498   $x0=1000;\r
499   $x1=0;\r
500   $this->setStrokeColor($col[0],$col[1],$col[2]);\r
501   $cnt=0;\r
502   $n = count($pos);\r
503   foreach($pos as $x){\r
504         $cnt++;\r
505         if ($cnt==1 || $cnt==$n){\r
506           $this->setLineStyle($outer);\r
507         } else {\r
508           $this->setLineStyle($inner);\r
509         }\r
510         $this->line($x-$gap/2,$y0,$x-$gap/2,$y2);\r
511         if ($x>$x1){ $x1=$x; };\r
512         if ($x<$x0){ $x0=$x; };\r
513   }\r
514   $this->setLineStyle($outer);\r
515   $this->line($x0-$gap/2-$outer/2,$y0,$x1-$gap/2+$outer/2,$y0);\r
516   // only do the second line if it is different to the first, AND each row does not have\r
517   // a line on it.\r
518   if ($y0!=$y1 && $opt<2){\r
519         $this->line($x0-$gap/2,$y1,$x1-$gap/2,$y1);\r
520   }\r
521   $this->line($x0-$gap/2-$outer/2,$y2,$x1-$gap/2+$outer/2,$y2);\r
522 }\r
523 \r
524 // ------------------------------------------------------------------------------\r
525 \r
526 function ezPrvtTableColumnHeadings($cols,$pos,$maxWidth,$height,$decender,$gap,$size,&$y,$optionsAll=array()){\r
527   // uses ezText to add the text, and returns the height taken by the largest heading\r
528   // this page will move the headings to a new page if they will not fit completely on this one\r
529   // transaction support will be used to implement this\r
530 \r
531   if (isset($optionsAll['cols'])){\r
532         $options = $optionsAll['cols'];\r
533   } else {\r
534         $options = array();\r
535   }\r
536 \r
537   $mx=0;\r
538   $startPage = $this->ezPageCount;\r
539   $secondGo=0;\r
540 \r
541   // $y is the position at which the top of the table should start, so the base\r
542   // of the first text, is $y-$height-$gap-$decender, but ezText starts by dropping $height\r
543 \r
544   // the return from this function is the total cell height, including gaps, and $y is adjusted\r
545   // to be the postion of the bottom line\r
546 \r
547   // begin the transaction\r
548   $this->transaction('start');\r
549   $ok=0;\r
550 //  $y-=$gap-$decender;\r
551   $y-=$gap;\r
552   while ($ok==0){\r
553         foreach($cols as $colName=>$colHeading){\r
554           $this->ezSetY($y);\r
555           if (isset($options[$colName]) && isset($options[$colName]['justification'])){\r
556                 $justification = $options[$colName]['justification'];\r
557           } else {\r
558                 $justification = 'left';\r
559           }\r
560           $this->ezText($colHeading,$size,array('aleft'=> $pos[$colName],'aright'=>($maxWidth[$colName]+$pos[$colName]),'justification'=>$justification));\r
561           $dy = $y-$this->y;\r
562           if ($dy>$mx){\r
563                 $mx=$dy;\r
564           }\r
565         }\r
566         $y = $y - $mx - $gap + $decender;\r
567 //      $y -= $mx-$gap+$decender;\r
568 \r
569         // now, if this has moved to a new page, then abort the transaction, move to a new page, and put it there\r
570         // do not check on the second time around, to avoid an infinite loop\r
571         if ($this->ezPageCount != $startPage && $secondGo==0){\r
572           $this->transaction('rewind');\r
573           $this->ezNewPage();\r
574           $y = $this->y - $gap-$decender;\r
575           $ok=0;\r
576           $secondGo=1;\r
577 //        $y = $store_y;\r
578           $mx=0;\r
579 \r
580         } else {\r
581           $this->transaction('commit');\r
582           $ok=1;\r
583         }\r
584   }\r
585 \r
586   return $mx+$gap*2-$decender;\r
587 }\r
588 \r
589 // ------------------------------------------------------------------------------\r
590 \r
591 function ezPrvtGetTextWidth($size,$text){\r
592   // will calculate the maximum width, taking into account that the text may be broken\r
593   // by line breaks.\r
594   $mx=0;\r
595   $lines = explode("\n",$text);\r
596   foreach ($lines as $line){\r
597         $w = $this->getTextWidth($size,$line);\r
598         if ($w>$mx){\r
599           $mx=$w;\r
600         }\r
601   }\r
602   return $mx;\r
603 }\r
604 \r
605 // ------------------------------------------------------------------------------\r
606 \r
607 function ezTable(&$data,$cols='',$title='',$options=''){\r
608   // add a table of information to the pdf document\r
609   // $data is a two dimensional array\r
610   // $cols (optional) is an associative array, the keys are the names of the columns from $data\r
611   // to be presented (and in that order), the values are the titles to be given to the columns\r
612   // $title (optional) is the title to be put on the top of the table\r
613   //\r
614   // $options is an associative array which can contain:\r
615   // 'showLines'=> 0,1,2, default is 1 (show outside and top lines only), 2=> lines on each row\r
616   // 'showHeadings' => 0 or 1\r
617   // 'shaded'=> 0,1,2,3 default is 1 (1->alternate lines are shaded, 0->no shading, 2-> both shaded, second uses shadeCol2)\r
618   // 'shadeCol' => (r,g,b) array, defining the colour of the shading, default is (0.8,0.8,0.8)\r
619   // 'shadeCol2' => (r,g,b) array, defining the colour of the shading of the other blocks, default is (0.7,0.7,0.7)\r
620   // 'fontSize' => 10\r
621   // 'textCol' => (r,g,b) array, text colour\r
622   // 'titleFontSize' => 12\r
623   // 'rowGap' => 2 , the space added at the top and bottom of each row, between the text and the lines\r
624   // 'colGap' => 5 , the space on the left and right sides of each cell\r
625   // 'lineCol' => (r,g,b) array, defining the colour of the lines, default, black.\r
626   // 'xPos' => 'left','right','center','centre',or coordinate, reference coordinate in the x-direction\r
627   // 'xOrientation' => 'left','right','center','centre', position of the table w.r.t 'xPos'\r
628   // 'width'=> <number> which will specify the width of the table, if it turns out to not be this\r
629   //    wide, then it will stretch the table to fit, if it is wider then each cell will be made\r
630   //    proportionalty smaller, and the content may have to wrap.\r
631   // 'maxWidth'=> <number> similar to 'width', but will only make table smaller than it wants to be\r
632   // 'options' => array(<colname>=>array('justification'=>'left','width'=>100,'link'=>linkDataName),<colname>=>....)\r
633   //                     allow the setting of other paramaters for the individual columns\r
634   // 'minRowSpace'=> the minimum space between the bottom of each row and the bottom margin, in which a new row will be started\r
635   //                              if it is less, then a new page would be started, default=-100\r
636   // 'innerLineThickness'=>1\r
637   // 'outerLineThickness'=>1\r
638   // 'splitRows'=>0, 0 or 1, whether or not to allow the rows to be split across page boundaries\r
639   // 'protectRows'=>number, the number of rows to hold with the heading on page, ie, if there less than this number of\r
640   //                            rows on the page, then move the whole lot onto the next page, default=1\r
641   //\r
642   // note that the user will have had to make a font selection already or this will not\r
643   // produce a valid pdf file.\r
644 \r
645   if (!is_array($data)){\r
646         return;\r
647   }\r
648 \r
649   if (!is_array($cols)){\r
650         // take the columns from the first row of the data set\r
651         reset($data);\r
652         list($k,$v)=each($data);\r
653         if (!is_array($v)){\r
654           return;\r
655         }\r
656         $cols=array();\r
657         foreach($v as $k1=>$v1){\r
658           $cols[$k1]=$k1;\r
659         }\r
660   }\r
661 \r
662   if (!is_array($options)){\r
663         $options=array();\r
664   }\r
665 \r
666   $defaults = array(\r
667         'shaded'=>1,'showLines'=>1,'shadeCol'=>array(0.8,0.8,0.8),'shadeCol2'=>array(0.7,0.7,0.7),'fontSize'=>10,'titleFontSize'=>12\r
668         ,'titleGap'=>5,'lineCol'=>array(0,0,0),'gap'=>5,'xPos'=>'centre','xOrientation'=>'centre'\r
669         ,'showHeadings'=>1,'textCol'=>array(0,0,0),'width'=>0,'maxWidth'=>0,'cols'=>array(),'minRowSpace'=>-100,'rowGap'=>2,'colGap'=>5\r
670         ,'innerLineThickness'=>1,'outerLineThickness'=>1,'splitRows'=>0,'protectRows'=>1\r
671         );\r
672 \r
673   foreach($defaults as $key=>$value){\r
674         if (is_array($value)){\r
675           if (!isset($options[$key]) || !is_array($options[$key])){\r
676                 $options[$key]=$value;\r
677           }\r
678         } else {\r
679           if (!isset($options[$key])){\r
680                 $options[$key]=$value;\r
681           }\r
682         }\r
683   }\r
684   $options['gap']=2*$options['colGap'];\r
685 \r
686   $middle = ($this->ez['pageWidth']-$this->ez['rightMargin'])/2+($this->ez['leftMargin'])/2;\r
687   // figure out the maximum widths of the text within each column\r
688   $maxWidth=array();\r
689   foreach($cols as $colName=>$colHeading){\r
690         $maxWidth[$colName]=0;\r
691   }\r
692   // find the maximum cell widths based on the data\r
693   foreach($data as $row){\r
694         foreach($cols as $colName=>$colHeading){\r
695           $w = $this->ezPrvtGetTextWidth($options['fontSize'],(string)$row[$colName])*1.01;\r
696           if ($w > $maxWidth[$colName]){\r
697                 $maxWidth[$colName]=$w;\r
698           }\r
699         }\r
700   }\r
701   // and the maximum widths to fit in the headings\r
702   foreach($cols as $colName=>$colTitle){\r
703         $w = $this->ezPrvtGetTextWidth($options['fontSize'],(string)$colTitle)*1.01;\r
704         if ($w > $maxWidth[$colName]){\r
705           $maxWidth[$colName]=$w;\r
706         }\r
707   }\r
708 \r
709   // calculate the start positions of each of the columns\r
710   $pos=array();\r
711   $x=0;\r
712   $t=$x;\r
713   $adjustmentWidth=0;\r
714   $setWidth=0;\r
715   foreach($maxWidth as $colName => $w){\r
716         $pos[$colName]=$t;\r
717         // if the column width has been specified then set that here, also total the\r
718         // width avaliable for adjustment\r
719         if (isset($options['cols'][$colName]) && isset($options['cols'][$colName]['width']) && $options['cols'][$colName]['width']>0){\r
720           $t=$t+$options['cols'][$colName]['width'];\r
721           $maxWidth[$colName] = $options['cols'][$colName]['width']-$options['gap'];\r
722           $setWidth += $options['cols'][$colName]['width'];\r
723         } else {\r
724           $t=$t+$w+$options['gap'];\r
725           $adjustmentWidth += $w;\r
726           $setWidth += $options['gap'];\r
727         }\r
728   }\r
729   $pos['_end_']=$t;\r
730 \r
731   // if maxWidth is specified, and the table is too wide, and the width has not been set,\r
732   // then set the width.\r
733   if ($options['width']==0 && $options['maxWidth'] && ($t-$x)>$options['maxWidth']){\r
734         // then need to make this one smaller\r
735         $options['width']=$options['maxWidth'];\r
736   }\r
737 \r
738   if ($options['width'] && $adjustmentWidth>0 && $setWidth<$options['width']){\r
739         // first find the current widths of the columns involved in this mystery\r
740         $cols0 = array();\r
741         $cols1 = array();\r
742         $xq=0;\r
743         $presentWidth=0;\r
744         $last='';\r
745         foreach($pos as $colName=>$p){\r
746           if (!isset($options['cols'][$last]) || !isset($options['cols'][$last]['width']) || $options['cols'][$last]['width']<=0){\r
747                 if (strlen($last)){\r
748                   $cols0[$last]=$p-$xq -$options['gap'];\r
749                   $presentWidth += ($p-$xq - $options['gap']);\r
750                 }\r
751           } else {\r
752                 $cols1[$last]=$p-$xq;\r
753           }\r
754           $last=$colName;\r
755           $xq=$p;\r
756         }\r
757         // $cols0 contains the widths of all the columns which are not set\r
758         $neededWidth = $options['width']-$setWidth;\r
759         // if needed width is negative then add it equally to each column, else get more tricky\r
760         if ($presentWidth<$neededWidth){\r
761           foreach($cols0 as $colName=>$w){\r
762                 $cols0[$colName]+= ($neededWidth-$presentWidth)/count($cols0);\r
763           }\r
764         } else {\r
765 \r
766           $cnt=0;\r
767           while ($presentWidth>$neededWidth && $cnt<100){\r
768                 $cnt++; // insurance policy\r
769                 // find the widest columns, and the next to widest width\r
770                 $aWidest = array();\r
771                 $nWidest=0;\r
772                 $widest=0;\r
773                 foreach($cols0 as $colName=>$w){\r
774                   if ($w>$widest){\r
775                         $aWidest=array($colName);\r
776                         $nWidest = $widest;\r
777                         $widest=$w;\r
778                   } else if ($w==$widest){\r
779                         $aWidest[]=$colName;\r
780                   }\r
781                 }\r
782                 // then figure out what the width of the widest columns would have to be to take up all the slack\r
783                 $newWidestWidth = $widest - ($presentWidth-$neededWidth)/count($aWidest);\r
784                 if ($newWidestWidth > $nWidest){\r
785                   // then there is space to set them to this\r
786                   foreach($aWidest as $colName){\r
787                         $cols0[$colName] = $newWidestWidth;\r
788                   }\r
789                   $presentWidth=$neededWidth;\r
790                 } else {\r
791                   // there is not space, reduce the size of the widest ones down to the next size down, and we\r
792                   // will go round again\r
793                   foreach($aWidest as $colName){\r
794                         $cols0[$colName] = $nWidest;\r
795                   }\r
796                   $presentWidth=$presentWidth-($widest-$nWidest)*count($aWidest);\r
797                 }\r
798           }\r
799         }\r
800         // $cols0 now contains the new widths of the constrained columns.\r
801         // now need to update the $pos and $maxWidth arrays\r
802         $xq=0;\r
803         foreach($pos as $colName=>$p){\r
804           $pos[$colName]=$xq;\r
805           if (!isset($options['cols'][$colName]) || !isset($options['cols'][$colName]['width']) || $options['cols'][$colName]['width']<=0){\r
806                 if (isset($cols0[$colName])){\r
807                   $xq += $cols0[$colName] + $options['gap'];\r
808                   $maxWidth[$colName]=$cols0[$colName];\r
809                 }\r
810           } else {\r
811                 if (isset($cols1[$colName])){\r
812                   $xq += $cols1[$colName];\r
813                 }\r
814           }\r
815         }\r
816 \r
817         $t=$x+$options['width'];\r
818         $pos['_end_']=$t;\r
819   }\r
820 \r
821   // now adjust the table to the correct location across the page\r
822   switch ($options['xPos']){\r
823         case 'left':\r
824           $xref = $this->ez['leftMargin'];\r
825           break;\r
826         case 'right':\r
827           $xref = $this->ez['pageWidth'] - $this->ez['rightMargin'];\r
828           break;\r
829         case 'centre':\r
830         case 'center':\r
831           $xref = $middle;\r
832           break;\r
833         default:\r
834           $xref = $options['xPos'];\r
835           break;\r
836   }\r
837   switch ($options['xOrientation']){\r
838         case 'left':\r
839           $dx = $xref-$t;\r
840           break;\r
841         case 'right':\r
842           $dx = $xref;\r
843           break;\r
844         case 'centre':\r
845         case 'center':\r
846           $dx = $xref-$t/2;\r
847           break;\r
848   }\r
849 \r
850 \r
851   foreach($pos as $k=>$v){\r
852         $pos[$k]=$v+$dx;\r
853   }\r
854   $x0=$x+$dx;\r
855   $x1=$t+$dx;\r
856 \r
857   $baseLeftMargin = $this->ez['leftMargin'];\r
858   $basePos = $pos;\r
859   $baseX0 = $x0;\r
860   $baseX1 = $x1;\r
861 \r
862   // ok, just about ready to make me a table\r
863   $this->setColor($options['textCol'][0],$options['textCol'][1],$options['textCol'][2]);\r
864   $this->setStrokeColor($options['shadeCol'][0],$options['shadeCol'][1],$options['shadeCol'][2]);\r
865 \r
866   $middle = ($x1+$x0)/2;\r
867 \r
868   // start a transaction which will be used to regress the table, if there are not enough rows protected\r
869   if ($options['protectRows']>0){\r
870         $this->transaction('start');\r
871         $movedOnce=0;\r
872   }\r
873   $abortTable = 1;\r
874   while ($abortTable){\r
875   $abortTable=0;\r
876 \r
877   $dm = $this->ez['leftMargin']-$baseLeftMargin;\r
878   foreach($basePos as $k=>$v){\r
879         $pos[$k]=$v+$dm;\r
880   }\r
881   $x0=$baseX0+$dm;\r
882   $x1=$baseX1+$dm;\r
883   $middle = ($x1+$x0)/2;\r
884 \r
885 \r
886   // if the title is set, then do that\r
887   if (strlen($title)){\r
888         $w = $this->getTextWidth($options['titleFontSize'],$title);\r
889         $this->y -= $this->getFontHeight($options['titleFontSize']);\r
890         if ($this->y < $this->ez['bottomMargin']){\r
891           $this->ezNewPage();\r
892                 // margins may have changed on the newpage\r
893                 $dm = $this->ez['leftMargin']-$baseLeftMargin;\r
894                 foreach($basePos as $k=>$v){\r
895                   $pos[$k]=$v+$dm;\r
896                 }\r
897                 $x0=$baseX0+$dm;\r
898                 $x1=$baseX1+$dm;\r
899                 $middle = ($x1+$x0)/2;\r
900           $this->y -= $this->getFontHeight($options['titleFontSize']);\r
901         }\r
902         $this->addText($middle-$w/2,$this->y,$options['titleFontSize'],$title);\r
903         $this->y -= $options['titleGap'];\r
904   }\r
905 \r
906                 // margins may have changed on the newpage\r
907                 $dm = $this->ez['leftMargin']-$baseLeftMargin;\r
908                 foreach($basePos as $k=>$v){\r
909                   $pos[$k]=$v+$dm;\r
910                 }\r
911                 $x0=$baseX0+$dm;\r
912                 $x1=$baseX1+$dm;\r
913 \r
914   $y=$this->y; // to simplify the code a bit\r
915 \r
916   // make the table\r
917   $height = $this->getFontHeight($options['fontSize']);\r
918   $decender = $this->getFontDecender($options['fontSize']);\r
919 \r
920 \r
921 \r
922   $y0=$y+$decender;\r
923   $dy=0;\r
924   if ($options['showHeadings']){\r
925         // this function will move the start of the table to a new page if it does not fit on this one\r
926         $headingHeight = $this->ezPrvtTableColumnHeadings($cols,$pos,$maxWidth,$height,$decender,$options['rowGap'],$options['fontSize'],$y,$options);\r
927         $y0 = $y+$headingHeight;\r
928         $y1 = $y;\r
929 \r
930 \r
931         $dm = $this->ez['leftMargin']-$baseLeftMargin;\r
932         foreach($basePos as $k=>$v){\r
933           $pos[$k]=$v+$dm;\r
934         }\r
935         $x0=$baseX0+$dm;\r
936         $x1=$baseX1+$dm;\r
937 \r
938   } else {\r
939         $y1 = $y0;\r
940   }\r
941   $firstLine=1;\r
942 \r
943 \r
944   // open an object here so that the text can be put in over the shading\r
945   if ($options['shaded']){\r
946         $this->saveState();\r
947         $textObjectId = $this->openObject();\r
948         $this->closeObject();\r
949         $this->addObject($textObjectId);\r
950         $this->reopenObject($textObjectId);\r
951   }\r
952 \r
953   $cnt=0;\r
954   $newPage=0;\r
955   foreach($data as $row){\r
956         $cnt++;\r
957         // the transaction support will be used to prevent rows being split\r
958         if ($options['splitRows']==0){\r
959           $pageStart = $this->ezPageCount;\r
960           if (isset($this->ez['columns']) && $this->ez['columns']['on']==1){\r
961                 $columnStart = $this->ez['columns']['colNum'];\r
962           }\r
963           $this->transaction('start');\r
964           $row_orig = $row;\r
965           $y_orig = $y;\r
966           $y0_orig = $y0;\r
967           $y1_orig = $y1;\r
968         }\r
969         $ok=0;\r
970         $secondTurn=0;\r
971         while(!$abortTable && $ok == 0){\r
972 \r
973         $mx=0;\r
974         $newRow=1;\r
975         while(!$abortTable && ($newPage || $newRow)){\r
976 \r
977           $y-=$height;\r
978           if ($newPage || $y<$this->ez['bottomMargin'] || (isset($options['minRowSpace']) && $y<($this->ez['bottomMargin']+$options['minRowSpace'])) ){\r
979                 // check that enough rows are with the heading\r
980                 if ($options['protectRows']>0 && $movedOnce==0 && $cnt<=$options['protectRows']){\r
981                   // then we need to move the whole table onto the next page\r
982                   $movedOnce = 1;\r
983                   $abortTable = 1;\r
984                 }\r
985 \r
986                 $y2=$y-$mx+2*$height+$decender-$newRow*$height;\r
987                 if ($options['showLines']){\r
988                   if (!$options['showHeadings']){\r
989                         $y0=$y1;\r
990                   }\r
991                   $this->ezPrvtTableDrawLines($pos,$options['gap'],$x0,$x1,$y0,$y1,$y2,$options['lineCol'],$options['innerLineThickness'],$options['outerLineThickness'],$options['showLines']);\r
992                 }\r
993                 if ($options['shaded']){\r
994                   $this->closeObject();\r
995                   $this->restoreState();\r
996                 }\r
997                 $this->ezNewPage();\r
998                 // and the margins may have changed, this is due to the possibility of the columns being turned on\r
999                 // as the columns are managed by manipulating the margins\r
1000 \r
1001                 $dm = $this->ez['leftMargin']-$baseLeftMargin;\r
1002                 foreach($basePos as $k=>$v){\r
1003                   $pos[$k]=$v+$dm;\r
1004                 }\r
1005 //              $x0=$x0+$dm;\r
1006 //              $x1=$x1+$dm;\r
1007                 $x0=$baseX0+$dm;\r
1008                 $x1=$baseX1+$dm;\r
1009 \r
1010                 if ($options['shaded']){\r
1011                   $this->saveState();\r
1012                   $textObjectId = $this->openObject();\r
1013                   $this->closeObject();\r
1014                   $this->addObject($textObjectId);\r
1015                   $this->reopenObject($textObjectId);\r
1016                 }\r
1017                 $this->setColor($options['textCol'][0],$options['textCol'][1],$options['textCol'][2],1);\r
1018                 $y = $this->ez['pageHeight']-$this->ez['topMargin'];\r
1019                 $y0=$y+$decender;\r
1020                 $mx=0;\r
1021                 if ($options['showHeadings']){\r
1022                   $this->ezPrvtTableColumnHeadings($cols,$pos,$maxWidth,$height,$decender,$options['rowGap'],$options['fontSize'],$y,$options);\r
1023                   $y1=$y;\r
1024                 } else {\r
1025                   $y1=$y0;\r
1026                 }\r
1027                 $firstLine=1;\r
1028                 $y -= $height;\r
1029           }\r
1030           $newRow=0;\r
1031           // write the actual data\r
1032           // if these cells need to be split over a page, then $newPage will be set, and the remaining\r
1033           // text will be placed in $leftOvers\r
1034           $newPage=0;\r
1035           $leftOvers=array();\r
1036 \r
1037           foreach($cols as $colName=>$colTitle){\r
1038                 $this->ezSetY($y+$height);\r
1039                 $colNewPage=0;\r
1040                 if (isset($row[$colName])){\r
1041                   if (isset($options['cols'][$colName]) && isset($options['cols'][$colName]['link']) && strlen($options['cols'][$colName]['link'])){\r
1042 \r
1043                         $lines = explode("\n",$row[$colName]);\r
1044                         if (isset($row[$options['cols'][$colName]['link']]) && strlen($row[$options['cols'][$colName]['link']])){\r
1045                           foreach($lines as $k=>$v){\r
1046                                 $lines[$k]='<c:alink:'.$row[$options['cols'][$colName]['link']].'>'.$v.'</c:alink>';\r
1047                           }\r
1048                         }\r
1049                   } else {\r
1050                         $lines = explode("\n",$row[$colName]);\r
1051                   }\r
1052                 } else {\r
1053                   $lines = array();\r
1054                 }\r
1055                 $this->y -= $options['rowGap'];\r
1056                 foreach ($lines as $line){\r
1057                   $line = $this->ezProcessText($line);\r
1058                   $start=1;\r
1059 \r
1060                   while (strlen($line) || $start){\r
1061                         $start=0;\r
1062                         if (!$colNewPage){\r
1063                           $this->y=$this->y-$height;\r
1064                         }\r
1065                         if ($this->y < $this->ez['bottomMargin']){\r
1066   //                    $this->ezNewPage();\r
1067                           $newPage=1;  // whether a new page is required for any of the columns\r
1068                           $colNewPage=1; // whether a new page is required for this column\r
1069                         }\r
1070                         if ($colNewPage){\r
1071                           if (isset($leftOvers[$colName])){\r
1072                                 $leftOvers[$colName].="\n".$line;\r
1073                           } else {\r
1074                                 $leftOvers[$colName] = $line;\r
1075                           }\r
1076                           $line='';\r
1077                         } else {\r
1078                           if (isset($options['cols'][$colName]) && isset($options['cols'][$colName]['justification']) ){\r
1079                                 $just = $options['cols'][$colName]['justification'];\r
1080                           } else {\r
1081                                 $just='left';\r
1082                           }\r
1083 \r
1084                           $line=$this->addTextWrap($pos[$colName],$this->y,$maxWidth[$colName],$options['fontSize'],$line,$just);\r
1085                         }\r
1086                   }\r
1087                 }\r
1088 \r
1089                 $dy=$y+$height-$this->y+$options['rowGap'];\r
1090                 if ($dy-$height*$newPage>$mx){\r
1091                   $mx=$dy-$height*$newPage;\r
1092                 }\r
1093           }\r
1094           // set $row to $leftOvers so that they will be processed onto the new page\r
1095           $row = $leftOvers;\r
1096           // now add the shading underneath\r
1097           if ($options['shaded'] && $cnt%2==0){\r
1098                 $this->closeObject();\r
1099                 $this->setColor($options['shadeCol'][0],$options['shadeCol'][1],$options['shadeCol'][2],1);\r
1100                 $this->filledRectangle($x0-$options['gap']/2,$y+$decender+$height-$mx,$x1-$x0,$mx);\r
1101                 $this->reopenObject($textObjectId);\r
1102           }\r
1103 \r
1104           if ($options['shaded']==2 && $cnt%2==1){\r
1105                 $this->closeObject();\r
1106                 $this->setColor($options['shadeCol2'][0],$options['shadeCol2'][1],$options['shadeCol2'][2],1);\r
1107                 $this->filledRectangle($x0-$options['gap']/2,$y+$decender+$height-$mx,$x1-$x0,$mx);\r
1108                 $this->reopenObject($textObjectId);\r
1109           }\r
1110 \r
1111           if ($options['showLines']>1){\r
1112                 // then draw a line on the top of each block\r
1113 //              $this->closeObject();\r
1114                 $this->saveState();\r
1115                 $this->setStrokeColor($options['lineCol'][0],$options['lineCol'][1],$options['lineCol'][2],1);\r
1116 //              $this->line($x0-$options['gap']/2,$y+$decender+$height-$mx,$x1-$x0,$mx);\r
1117                 if ($firstLine){\r
1118                   $this->setLineStyle($options['outerLineThickness']);\r
1119                   $firstLine=0;\r
1120                 } else {\r
1121                   $this->setLineStyle($options['innerLineThickness']);\r
1122                 }\r
1123                 $this->line($x0-$options['gap']/2,$y+$decender+$height,$x1-$options['gap']/2,$y+$decender+$height);\r
1124                 $this->restoreState();\r
1125 //              $this->reopenObject($textObjectId);\r
1126           }\r
1127         } // end of while\r
1128         $y=$y-$mx+$height;\r
1129 \r
1130         // checking row split over pages\r
1131         if ($options['splitRows']==0){\r
1132           if ( ( ($this->ezPageCount != $pageStart) || (isset($this->ez['columns']) && $this->ez['columns']['on']==1 && $columnStart != $this->ez['columns']['colNum'] ))  && $secondTurn==0){\r
1133                 // then we need to go back and try that again !\r
1134                 $newPage=1;\r
1135                 $secondTurn=1;\r
1136                 $this->transaction('rewind');\r
1137                 $row = $row_orig;\r
1138                 $y = $y_orig;\r
1139                 $y0 = $y0_orig;\r
1140                 $y1 = $y1_orig;\r
1141                 $ok=0;\r
1142 \r
1143                 $dm = $this->ez['leftMargin']-$baseLeftMargin;\r
1144                 foreach($basePos as $k=>$v){\r
1145                   $pos[$k]=$v+$dm;\r
1146                 }\r
1147                 $x0=$baseX0+$dm;\r
1148                 $x1=$baseX1+$dm;\r
1149 \r
1150           } else {\r
1151                 $this->transaction('commit');\r
1152                 $ok=1;\r
1153           }\r
1154         } else {\r
1155           $ok=1;  // don't go round the loop if splitting rows is allowed\r
1156         }\r
1157 \r
1158         }  // end of while to check for row splitting\r
1159         if ($abortTable){\r
1160           if ($ok==0){\r
1161                 $this->transaction('abort');\r
1162           }\r
1163           // only the outer transaction should be operational\r
1164           $this->transaction('rewind');\r
1165           $this->ezNewPage();\r
1166           break;\r
1167         }\r
1168 \r
1169   } // end of foreach ($data as $row)\r
1170 \r
1171   } // end of while ($abortTable)\r
1172 \r
1173   // table has been put on the page, the rows guarded as required, commit.\r
1174   $this->transaction('commit');\r
1175 \r
1176   $y2=$y+$decender;\r
1177   if ($options['showLines']){\r
1178         if (!$options['showHeadings']){\r
1179           $y0=$y1;\r
1180         }\r
1181         $this->ezPrvtTableDrawLines($pos,$options['gap'],$x0,$x1,$y0,$y1,$y2,$options['lineCol'],$options['innerLineThickness'],$options['outerLineThickness'],$options['showLines']);\r
1182   }\r
1183 \r
1184   // close the object for drawing the text on top\r
1185   if ($options['shaded']){\r
1186         $this->closeObject();\r
1187         $this->restoreState();\r
1188   }\r
1189 \r
1190   $this->y=$y;\r
1191   return $y;\r
1192 }\r
1193 \r
1194 // ------------------------------------------------------------------------------\r
1195 function ezProcessText($text){\r
1196   // this function will intially be used to implement underlining support, but could be used for a range of other\r
1197   // purposes\r
1198   $search = array('<u>','<U>','</u>','</U>');\r
1199   $replace = array('<c:uline>','<c:uline>','</c:uline>','</c:uline>');\r
1200   return str_replace($search,$replace,$text);\r
1201 }\r
1202 \r
1203 // ------------------------------------------------------------------------------\r
1204 \r
1205 function ezText($text,$size=0,$options=array(),$test=0){\r
1206   // this will add a string of text to the document, starting at the current drawing\r
1207   // position.\r
1208   // it will wrap to keep within the margins, including optional offsets from the left\r
1209   // and the right, if $size is not specified, then it will be the last one used, or\r
1210   // the default value (12 I think).\r
1211   // the text will go to the start of the next line when a return code "\n" is found.\r
1212   // possible options are:\r
1213   // 'left'=> number, gap to leave from the left margin\r
1214   // 'right'=> number, gap to leave from the right margin\r
1215   // 'aleft'=> number, absolute left position (overrides 'left')\r
1216   // 'aright'=> number, absolute right position (overrides 'right')\r
1217   // 'justification' => 'left','right','center','centre','full'\r
1218 \r
1219   // only set one of the next two items (leading overrides spacing)\r
1220   // 'leading' => number, defines the total height taken by the line, independent of the font height.\r
1221   // 'spacing' => a real number, though usually set to one of 1, 1.5, 2 (line spacing as used in word processing)\r
1222 \r
1223   // if $test is set then this should just check if the text is going to flow onto a new page or not, returning true or false\r
1224 \r
1225   // apply the filtering which will make the underlining function.\r
1226   $text = $this->ezProcessText($text);\r
1227 \r
1228   $newPage=false;\r
1229   $store_y = $this->y;\r
1230 \r
1231   if (is_array($options) && isset($options['aleft'])){\r
1232         $left=$options['aleft'];\r
1233   } else {\r
1234         $left = $this->ez['leftMargin'] + ((is_array($options) && isset($options['left']))?$options['left']:0);\r
1235   }\r
1236   if (is_array($options) && isset($options['aright'])){\r
1237         $right=$options['aright'];\r
1238   } else {\r
1239         $right = $this->ez['pageWidth'] - $this->ez['rightMargin'] - ((is_array($options) && isset($options['right']))?$options['right']:0);\r
1240   }\r
1241   if ($size<=0){\r
1242         $size = $this->ez['fontSize'];\r
1243   } else {\r
1244         $this->ez['fontSize']=$size;\r
1245   }\r
1246 \r
1247   if (is_array($options) && isset($options['justification'])){\r
1248         $just = $options['justification'];\r
1249   } else {\r
1250         $just = 'left';\r
1251   }\r
1252 \r
1253   // modifications to give leading and spacing based on those given by Craig Heydenburg 1/1/02\r
1254   if (is_array($options) && isset($options['leading'])) { ## use leading instead of spacing\r
1255         $height = $options['leading'];\r
1256         } else if (is_array($options) && isset($options['spacing'])) {\r
1257         $height = $this->getFontHeight($size) * $options['spacing'];\r
1258         } else {\r
1259                 $height = $this->getFontHeight($size);\r
1260         }\r
1261 \r
1262 \r
1263   $lines = explode("\n",$text);\r
1264   foreach ($lines as $line){\r
1265         $start=1;\r
1266         while (strlen($line) || $start){\r
1267           $start=0;\r
1268           $this->y=$this->y-$height;\r
1269           if ($this->y < $this->ez['bottomMargin']){\r
1270                 if ($test){\r
1271                   $newPage=true;\r
1272                 } else {\r
1273                   $this->ezNewPage();\r
1274                   // and then re-calc the left and right, in case they have changed due to columns\r
1275                 }\r
1276           }\r
1277           if (is_array($options) && isset($options['aleft'])){\r
1278                 $left=$options['aleft'];\r
1279           } else {\r
1280                 $left = $this->ez['leftMargin'] + ((is_array($options) && isset($options['left']))?$options['left']:0);\r
1281           }\r
1282           if (is_array($options) && isset($options['aright'])){\r
1283                 $right=$options['aright'];\r
1284           } else {\r
1285                 $right = $this->ez['pageWidth'] - $this->ez['rightMargin'] - ((is_array($options) && isset($options['right']))?$options['right']:0);\r
1286           }\r
1287           $line=$this->addTextWrap($left,$this->y,$right-$left,$size,$line,$just,0,$test);\r
1288         }\r
1289   }\r
1290 \r
1291   if ($test){\r
1292         $this->y=$store_y;\r
1293         return $newPage;\r
1294   } else {\r
1295         return $this->y;\r
1296   }\r
1297 }\r
1298 \r
1299 // ------------------------------------------------------------------------------\r
1300 \r
1301 function ezImage($image,$pad = 5,$width = 0,$resize = 'full',$just = 'center',$border = ''){\r
1302         //beta ezimage function\r
1303         if (stristr($image,'://'))//copy to temp file\r
1304         {\r
1305                 $fp = @fopen($image,"rb");\r
1306                 while(!feof($fp))\r
1307                 {\r
1308                                 $cont.= fread($fp,1024);\r
1309                 }\r
1310                 fclose($fp);\r
1311                 $image = tempnam ("/tmp", "php-pdf");\r
1312                 $fp2 = @fopen($image,"w");\r
1313                 fwrite($fp2,$cont);\r
1314                 fclose($fp2);\r
1315                 $temp = true;\r
1316         }\r
1317 \r
1318         if (!(file_exists($image))) return false; //return immediately if image file does not exist\r
1319         $imageInfo = getimagesize($image);\r
1320         switch ($imageInfo[2]){\r
1321                 case 2:\r
1322                         $type = "jpeg";\r
1323                         break;\r
1324                 case 3:\r
1325                         $type = "png";\r
1326                         break;\r
1327                 default:\r
1328                         return false; //return if file is not jpg or png\r
1329         }\r
1330         if ($width == 0) $width = $imageInfo[0]; //set width\r
1331         $ratio = $imageInfo[0]/$imageInfo[1];\r
1332 \r
1333         //get maximum width of image\r
1334         if (isset($this->ez['columns']) && $this->ez['columns']['on'] == 1)\r
1335         {\r
1336                 $bigwidth = $this->ez['columns']['width'] - ($pad * 2);\r
1337         }\r
1338         else\r
1339         {\r
1340                 $bigwidth = $this->ez['pageWidth'] - ($pad * 2);\r
1341         }\r
1342         //fix width if larger than maximum or if $resize=full\r
1343         if ($resize == 'full' || $resize == 'width' || $width > $bigwidth)\r
1344         {\r
1345                 $width = $bigwidth;\r
1346 \r
1347         }\r
1348 \r
1349         $height = ($width/$ratio); //set height\r
1350 \r
1351         //fix size if runs off page\r
1352         if ($height > ($this->y - $this->ez['bottomMargin'] - ($pad * 2)))\r
1353         {\r
1354                 if ($resize != 'full')\r
1355                 {\r
1356                         $this->ezNewPage();\r
1357                 }\r
1358                 else\r
1359                 {\r
1360                         $height = ($this->y - $this->ez['bottomMargin'] - ($pad * 2)); //shrink height\r
1361                         $width = ($height*$ratio); //fix width\r
1362                 }\r
1363         }\r
1364 \r
1365         //fix x-offset if image smaller than bigwidth\r
1366         if ($width < $bigwidth)\r
1367         {\r
1368                 //center if justification=center\r
1369                 if ($just == 'center')\r
1370                 {\r
1371                         $offset = ($bigwidth - $width) / 2;\r
1372                 }\r
1373                 //move to right if justification=right\r
1374                 if ($just == 'right')\r
1375                 {\r
1376                         $offset = ($bigwidth - $width);\r
1377                 }\r
1378                 //leave at left if justification=left\r
1379                 if ($just == 'left')\r
1380                 {\r
1381                         $offset = 0;\r
1382                 }\r
1383         }\r
1384 \r
1385 \r
1386         //call appropriate function\r
1387         if ($type == "jpeg"){\r
1388                 $this->addJpegFromFile($image,$this->ez['leftMargin'] + $pad + $offset, $this->y + $this->getFontHeight($this->ez['fontSize']) - $pad - $height,$width);\r
1389         }\r
1390 \r
1391         if ($type == "png"){\r
1392                 $this->addPngFromFile($image,$this->ez['leftMargin'] + $pad + $offset, $this->y + $this->getFontHeight($this->ez['fontSize']) - $pad - $height,$width);\r
1393         }\r
1394         //draw border\r
1395         if ($border != '')\r
1396         {\r
1397         if (!(isset($border['color'])))\r
1398         {\r
1399                 $border['color']['red'] = .5;\r
1400                 $border['color']['blue'] = .5;\r
1401                 $border['color']['green'] = .5;\r
1402         }\r
1403         if (!(isset($border['width']))) $border['width'] = 1;\r
1404         if (!(isset($border['cap']))) $border['cap'] = 'round';\r
1405         if (!(isset($border['join']))) $border['join'] = 'round';\r
1406 \r
1407 \r
1408         $this->setStrokeColor($border['color']['red'],$border['color']['green'],$border['color']['blue']);\r
1409         $this->setLineStyle($border['width'],$border['cap'],$border['join']);\r
1410         $this->rectangle($this->ez['leftMargin'] + $pad + $offset, $this->y + $this->getFontHeight($this->ez['fontSize']) - $pad - $height,$width,$height);\r
1411 \r
1412         }\r
1413         // move y below image\r
1414         $this->y = $this->y - $pad - $height;\r
1415         //remove tempfile for remote images\r
1416         if ($temp == true) unlink($image);\r
1417 \r
1418 }\r
1419 // ------------------------------------------------------------------------------\r
1420 \r
1421 // note that templating code is still considered developmental - have not really figured\r
1422 // out a good way of doing this yet.\r
1423 function loadTemplate($templateFile){\r
1424   // this function will load the requested template ($file includes full or relative pathname)\r
1425   // the code for the template will be modified to make it name safe, and then stored in\r
1426   // an array for later use\r
1427   // The id of the template will be returned for the user to operate on it later\r
1428   if (!file_exists($templateFile)){\r
1429         return -1;\r
1430   }\r
1431 \r
1432   $code = implode('',file($templateFile));\r
1433   if (!strlen($code)){\r
1434         return;\r
1435   }\r
1436 \r
1437   $code = trim($code);\r
1438   if (substr($code,0,5)=='<?php'){\r
1439         $code = substr($code,5);\r
1440   }\r
1441   if (substr($code,-2)=='?>'){\r
1442         $code = substr($code,0,strlen($code)-2);\r
1443   }\r
1444   if (isset($this->ez['numTemplates'])){\r
1445         $newNum = $this->ez['numTemplates'];\r
1446         $this->ez['numTemplates']++;\r
1447   } else {\r
1448         $newNum=0;\r
1449         $this->ez['numTemplates']=1;\r
1450         $this->ez['templates']=array();\r
1451   }\r
1452 \r
1453   $this->ez['templates'][$newNum]['code']=$code;\r
1454 \r
1455   return $newNum;\r
1456 }\r
1457 \r
1458 // ------------------------------------------------------------------------------\r
1459 \r
1460 function execTemplate($id,$data=array(),$options=array()){\r
1461   // execute the given template on the current document.\r
1462   if (!isset($this->ez['templates'][$id])){\r
1463         return;\r
1464   }\r
1465   eval($this->ez['templates'][$id]['code']);\r
1466 }\r
1467 \r
1468 // ------------------------------------------------------------------------------\r
1469 function ilink($info){\r
1470   $this->alink($info,1);\r
1471 }\r
1472 \r
1473 function alink($info,$internal=0){\r
1474   // a callback function to support the formation of clickable links within the document\r
1475   $lineFactor=0.05; // the thickness of the line as a proportion of the height. also the drop of the line.\r
1476   switch($info['status']){\r
1477         case 'start':\r
1478         case 'sol':\r
1479           // the beginning of the link\r
1480           // this should contain the URl for the link as the 'p' entry, and will also contain the value of 'nCallback'\r
1481           if (!isset($this->ez['links'])){\r
1482                 $this->ez['links']=array();\r
1483           }\r
1484           $i = $info['nCallback'];\r
1485           $this->ez['links'][$i] = array('x'=>$info['x'],'y'=>$info['y'],'angle'=>$info['angle'],'decender'=>$info['decender'],'height'=>$info['height'],'url'=>$info['p']);\r
1486           if ($internal==0){\r
1487                 $this->saveState();\r
1488                 $this->setColor(0,0,1);\r
1489                 $this->setStrokeColor(0,0,1);\r
1490                 $thick = $info['height']*$lineFactor;\r
1491                 $this->setLineStyle($thick);\r
1492           }\r
1493           break;\r
1494         case 'end':\r
1495         case 'eol':\r
1496           // the end of the link\r
1497           // assume that it is the most recent opening which has closed\r
1498           $i = $info['nCallback'];\r
1499           $start = $this->ez['links'][$i];\r
1500           // add underlining\r
1501           if ($internal){\r
1502                 $this->addInternalLink($start['url'],$start['x'],$start['y']+$start['decender'],$info['x'],$start['y']+$start['decender']+$start['height']);\r
1503           } else {\r
1504                 $a = deg2rad((float)$start['angle']-90.0);\r
1505                 $drop = $start['height']*$lineFactor*1.5;\r
1506                 $dropx = cos($a)*$drop;\r
1507                 $dropy = -sin($a)*$drop;\r
1508                 $this->line($start['x']-$dropx,$start['y']-$dropy,$info['x']-$dropx,$info['y']-$dropy);\r
1509                 $this->addLink($start['url'],$start['x'],$start['y']+$start['decender'],$info['x'],$start['y']+$start['decender']+$start['height']);\r
1510                 $this->restoreState();\r
1511           }\r
1512           break;\r
1513   }\r
1514 }\r
1515 \r
1516 // ------------------------------------------------------------------------------\r
1517 \r
1518 function uline($info){\r
1519   // a callback function to support underlining\r
1520   $lineFactor=0.05; // the thickness of the line as a proportion of the height. also the drop of the line.\r
1521   switch($info['status']){\r
1522         case 'start':\r
1523         case 'sol':\r
1524 \r
1525           // the beginning of the underline zone\r
1526           if (!isset($this->ez['links'])){\r
1527                 $this->ez['links']=array();\r
1528           }\r
1529           $i = $info['nCallback'];\r
1530           $this->ez['links'][$i] = array('x'=>$info['x'],'y'=>$info['y'],'angle'=>$info['angle'],'decender'=>$info['decender'],'height'=>$info['height']);\r
1531           $this->saveState();\r
1532           $thick = $info['height']*$lineFactor;\r
1533           $this->setLineStyle($thick);\r
1534           break;\r
1535         case 'end':\r
1536         case 'eol':\r
1537           // the end of the link\r
1538           // assume that it is the most recent opening which has closed\r
1539           $i = $info['nCallback'];\r
1540           $start = $this->ez['links'][$i];\r
1541           // add underlining\r
1542           $a = deg2rad((float)$start['angle']-90.0);\r
1543           $drop = $start['height']*$lineFactor*1.5;\r
1544           $dropx = cos($a)*$drop;\r
1545           $dropy = -sin($a)*$drop;\r
1546           $this->line($start['x']-$dropx,$start['y']-$dropy,$info['x']-$dropx,$info['y']-$dropy);\r
1547           $this->restoreState();\r
1548           break;\r
1549   }\r
1550 }\r
1551 \r
1552 // ------------------------------------------------------------------------------\r
1553 \r
1554 }\r
1555 ?>