PMA_ExportPdf.class.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * TableProperty class
  5. *
  6. * @package PhpMyAdmin-Export
  7. * @subpackage PDF
  8. */
  9. if (! defined('PHPMYADMIN')) {
  10. exit;
  11. }
  12. /* Get the PDF class */
  13. require_once 'libraries/PDF.class.php';
  14. /**
  15. * Adapted from a LGPL script by Philip Clarke
  16. *
  17. * @package PhpMyAdmin-Export
  18. * @subpackage PDF
  19. */
  20. class PMA_ExportPdf extends PMA_PDF
  21. {
  22. var $tablewidths;
  23. var $headerset;
  24. /**
  25. * Add page if needed.
  26. *
  27. * @param float|int $h cell height. Default value: 0
  28. * @param mixed $y starting y position, leave empty for current
  29. * position
  30. * @param boolean $addpage if true add a page, otherwise only return
  31. * the true/false state
  32. *
  33. * @return boolean true in case of page break, false otherwise.
  34. */
  35. function checkPageBreak($h = 0, $y = '', $addpage = true)
  36. {
  37. if (TCPDF_STATIC::empty_string($y)) {
  38. $y = $this->y;
  39. }
  40. $current_page = $this->page;
  41. if ((($y + $h) > $this->PageBreakTrigger)
  42. AND (! $this->InFooter)
  43. AND ($this->AcceptPageBreak())
  44. ) {
  45. if ($addpage) {
  46. //Automatic page break
  47. $x = $this->x;
  48. $this->AddPage($this->CurOrientation);
  49. $this->y = $this->dataY;
  50. $oldpage = $this->page - 1;
  51. $this_page_orm = $this->pagedim[$this->page]['orm'];
  52. $old_page_orm = $this->pagedim[$oldpage]['orm'];
  53. $this_page_olm = $this->pagedim[$this->page]['olm'];
  54. $old_page_olm = $this->pagedim[$oldpage]['olm'];
  55. if ($this->rtl) {
  56. if ($this_page_orm!= $old_page_orm) {
  57. $this->x = $x - ($this_page_orm - $old_page_orm);
  58. } else {
  59. $this->x = $x;
  60. }
  61. } else {
  62. if ($this_page_olm != $old_page_olm) {
  63. $this->x = $x + ($this_page_olm - $old_page_olm);
  64. } else {
  65. $this->x = $x;
  66. }
  67. }
  68. }
  69. return true;
  70. }
  71. if ($current_page != $this->page) {
  72. // account for columns mode
  73. return true;
  74. }
  75. return false;
  76. }
  77. /**
  78. * This method is used to render the page header.
  79. *
  80. * @return void
  81. */
  82. function Header()
  83. {
  84. global $maxY;
  85. // We don't want automatic page breaks while generating header
  86. // as this can lead to infinite recursion as auto generated page
  87. // will want header as well causing another page break
  88. // FIXME: Better approach might be to try to compact the content
  89. $this->SetAutoPageBreak(false);
  90. // Check if header for this page already exists
  91. if (! isset($this->headerset[$this->page])) {
  92. $fullwidth = 0;
  93. foreach ($this->tablewidths as $width) {
  94. $fullwidth += $width;
  95. }
  96. $this->SetY(($this->tMargin) - ($this->FontSizePt / $this->k) * 5);
  97. $this->cellFontSize = $this->FontSizePt ;
  98. $this->SetFont(
  99. PMA_PDF_FONT,
  100. '',
  101. ($this->titleFontSize
  102. ? $this->titleFontSize
  103. : $this->FontSizePt)
  104. );
  105. $this->Cell(0, $this->FontSizePt, $this->titleText, 0, 1, 'C');
  106. $this->SetFont(PMA_PDF_FONT, '', $this->cellFontSize);
  107. $this->SetY(($this->tMargin) - ($this->FontSizePt / $this->k) * 2.5);
  108. $this->Cell(
  109. 0,
  110. $this->FontSizePt,
  111. __('Database:') . ' ' . $this->currentDb . ', '
  112. . __('Table:') . ' ' . $this->currentTable,
  113. 0, 1, 'L'
  114. );
  115. $l = ($this->lMargin);
  116. foreach ($this->colTitles as $col => $txt) {
  117. $this->SetXY($l, ($this->tMargin));
  118. $this->MultiCell(
  119. $this->tablewidths[$col],
  120. $this->FontSizePt,
  121. $txt
  122. );
  123. $l += $this->tablewidths[$col] ;
  124. $maxY = ($maxY < $this->getY()) ? $this->getY() : $maxY ;
  125. }
  126. $this->SetXY($this->lMargin, $this->tMargin);
  127. $this->setFillColor(200, 200, 200);
  128. $l = ($this->lMargin);
  129. foreach ($this->colTitles as $col => $txt) {
  130. $this->SetXY($l, $this->tMargin);
  131. $this->cell(
  132. $this->tablewidths[$col],
  133. $maxY-($this->tMargin),
  134. '',
  135. 1,
  136. 0,
  137. 'L',
  138. 1
  139. );
  140. $this->SetXY($l, $this->tMargin);
  141. $this->MultiCell(
  142. $this->tablewidths[$col],
  143. $this->FontSizePt,
  144. $txt,
  145. 0,
  146. 'C'
  147. );
  148. $l += $this->tablewidths[$col];
  149. }
  150. $this->setFillColor(255, 255, 255);
  151. // set headerset
  152. $this->headerset[$this->page] = 1;
  153. }
  154. $this->dataY = $maxY;
  155. $this->SetAutoPageBreak(true);
  156. }
  157. /**
  158. * Generate table
  159. *
  160. * @param int $lineheight Height of line
  161. *
  162. * @return void
  163. */
  164. function morepagestable($lineheight = 8)
  165. {
  166. // some things to set and 'remember'
  167. $l = $this->lMargin;
  168. $startheight = $h = $this->dataY;
  169. $startpage = $currpage = $this->page;
  170. // calculate the whole width
  171. $fullwidth = 0;
  172. foreach ($this->tablewidths as $width) {
  173. $fullwidth += $width;
  174. }
  175. // Now let's start to write the table
  176. $row = 0;
  177. $tmpheight = array();
  178. $maxpage = $this->page;
  179. while ($data = $GLOBALS['dbi']->fetchRow($this->results)) {
  180. $this->page = $currpage;
  181. // write the horizontal borders
  182. $this->Line($l, $h, $fullwidth+$l, $h);
  183. // write the content and remember the height of the highest col
  184. foreach ($data as $col => $txt) {
  185. $this->page = $currpage;
  186. $this->SetXY($l, $h);
  187. if ($this->tablewidths[$col] > 0) {
  188. $this->MultiCell(
  189. $this->tablewidths[$col],
  190. $lineheight,
  191. $txt,
  192. 0,
  193. $this->colAlign[$col]
  194. );
  195. $l += $this->tablewidths[$col];
  196. }
  197. if (! isset($tmpheight[$row . '-' . $this->page])) {
  198. $tmpheight[$row . '-' . $this->page] = 0;
  199. }
  200. if ($tmpheight[$row . '-' . $this->page] < $this->GetY()) {
  201. $tmpheight[$row . '-' . $this->page] = $this->GetY();
  202. }
  203. if ($this->page > $maxpage) {
  204. $maxpage = $this->page;
  205. }
  206. unset($data[$col]);
  207. }
  208. // get the height we were in the last used page
  209. $h = $tmpheight[$row . '-' . $maxpage];
  210. // set the "pointer" to the left margin
  211. $l = $this->lMargin;
  212. // set the $currpage to the last page
  213. $currpage = $maxpage;
  214. unset($data[$row]);
  215. $row++;
  216. }
  217. // draw the borders
  218. // we start adding a horizontal line on the last page
  219. $this->page = $maxpage;
  220. $this->Line($l, $h, $fullwidth+$l, $h);
  221. // now we start at the top of the document and walk down
  222. for ($i = $startpage; $i <= $maxpage; $i++) {
  223. $this->page = $i;
  224. $l = $this->lMargin;
  225. $t = ($i == $startpage) ? $startheight : $this->tMargin;
  226. $lh = ($i == $maxpage) ? $h : $this->h-$this->bMargin;
  227. $this->Line($l, $t, $l, $lh);
  228. foreach ($this->tablewidths as $width) {
  229. $l += $width;
  230. $this->Line($l, $t, $l, $lh);
  231. }
  232. }
  233. // set it to the last page, if not it'll cause some problems
  234. $this->page = $maxpage;
  235. }
  236. /**
  237. * Sets a set of attributes.
  238. *
  239. * @param array $attr array containing the attributes
  240. *
  241. * @return void
  242. */
  243. function setAttributes($attr = array())
  244. {
  245. foreach ($attr as $key => $val) {
  246. $this->$key = $val ;
  247. }
  248. }
  249. /**
  250. * Defines the top margin.
  251. * The method can be called before creating the first page.
  252. *
  253. * @param float $topMargin the margin
  254. *
  255. * @return void
  256. */
  257. function setTopMargin($topMargin)
  258. {
  259. $this->tMargin = $topMargin;
  260. }
  261. /**
  262. * MySQL report
  263. *
  264. * @param string $query Query to execute
  265. *
  266. * @return void
  267. */
  268. function mysqlReport($query)
  269. {
  270. unset($this->tablewidths);
  271. unset($this->colTitles);
  272. unset($this->titleWidth);
  273. unset($this->colFits);
  274. unset($this->display_column);
  275. unset($this->colAlign);
  276. /**
  277. * Pass 1 for column widths
  278. */
  279. $this->results = $GLOBALS['dbi']->query(
  280. $query, null, PMA_DatabaseInterface::QUERY_UNBUFFERED
  281. );
  282. $this->numFields = $GLOBALS['dbi']->numFields($this->results);
  283. $this->fields = $GLOBALS['dbi']->getFieldsMeta($this->results);
  284. // sColWidth = starting col width (an average size width)
  285. $availableWidth = $this->w - $this->lMargin - $this->rMargin;
  286. $this->sColWidth = $availableWidth / $this->numFields;
  287. $totalTitleWidth = 0;
  288. // loop through results header and set initial
  289. // col widths/ titles/ alignment
  290. // if a col title is less than the starting col width,
  291. // reduce that column size
  292. $colFits = array();
  293. $titleWidth = array();
  294. for ($i = 0; $i < $this->numFields; $i++) {
  295. $stringWidth = $this->getstringwidth($this->fields[$i]->name) + 6 ;
  296. // save the real title's width
  297. $titleWidth[$i] = $stringWidth;
  298. $totalTitleWidth += $stringWidth;
  299. // set any column titles less than the start width to
  300. // the column title width
  301. if ($stringWidth < $this->sColWidth) {
  302. $colFits[$i] = $stringWidth ;
  303. }
  304. $this->colTitles[$i] = $this->fields[$i]->name;
  305. $this->display_column[$i] = true;
  306. switch ($this->fields[$i]->type) {
  307. case 'int':
  308. $this->colAlign[$i] = 'R';
  309. break;
  310. case 'blob':
  311. case 'tinyblob':
  312. case 'mediumblob':
  313. case 'longblob':
  314. /**
  315. * @todo do not deactivate completely the display
  316. * but show the field's name and [BLOB]
  317. */
  318. if (stristr($this->fields[$i]->flags, 'BINARY')) {
  319. $this->display_column[$i] = false;
  320. unset($this->colTitles[$i]);
  321. }
  322. $this->colAlign[$i] = 'L';
  323. break;
  324. default:
  325. $this->colAlign[$i] = 'L';
  326. }
  327. }
  328. // title width verification
  329. if ($totalTitleWidth > $availableWidth) {
  330. $adjustingMode = true;
  331. } else {
  332. $adjustingMode = false;
  333. // we have enough space for all the titles at their
  334. // original width so use the true title's width
  335. foreach ($titleWidth as $key => $val) {
  336. $colFits[$key] = $val;
  337. }
  338. }
  339. // loop through the data; any column whose contents
  340. // is greater than the column size is resized
  341. /**
  342. * @todo force here a LIMIT to avoid reading all rows
  343. */
  344. while ($row = $GLOBALS['dbi']->fetchRow($this->results)) {
  345. foreach ($colFits as $key => $val) {
  346. $stringWidth = $this->getstringwidth($row[$key]) + 6 ;
  347. if ($adjustingMode && ($stringWidth > $this->sColWidth)) {
  348. // any column whose data's width is bigger than
  349. // the start width is now discarded
  350. unset($colFits[$key]);
  351. } else {
  352. // if data's width is bigger than the current column width,
  353. // enlarge the column (but avoid enlarging it if the
  354. // data's width is very big)
  355. if ($stringWidth > $val
  356. && $stringWidth < ($this->sColWidth * 3)
  357. ) {
  358. $colFits[$key] = $stringWidth ;
  359. }
  360. }
  361. }
  362. }
  363. $totAlreadyFitted = 0;
  364. foreach ($colFits as $key => $val) {
  365. // set fitted columns to smallest size
  366. $this->tablewidths[$key] = $val;
  367. // to work out how much (if any) space has been freed up
  368. $totAlreadyFitted += $val;
  369. }
  370. if ($adjustingMode) {
  371. $surplus = (sizeof($colFits) * $this->sColWidth) - $totAlreadyFitted;
  372. $surplusToAdd = $surplus / ($this->numFields - sizeof($colFits));
  373. } else {
  374. $surplusToAdd = 0;
  375. }
  376. for ($i = 0; $i < $this->numFields; $i++) {
  377. if (! in_array($i, array_keys($colFits))) {
  378. $this->tablewidths[$i] = $this->sColWidth + $surplusToAdd;
  379. }
  380. if ($this->display_column[$i] == false) {
  381. $this->tablewidths[$i] = 0;
  382. }
  383. }
  384. ksort($this->tablewidths);
  385. $GLOBALS['dbi']->freeResult($this->results);
  386. // Pass 2
  387. $this->results = $GLOBALS['dbi']->query(
  388. $query, null, PMA_DatabaseInterface::QUERY_UNBUFFERED
  389. );
  390. $this->setY($this->tMargin);
  391. $this->AddPage();
  392. $this->SetFont(PMA_PDF_FONT, '', 9);
  393. $this->morepagestable($this->FontSizePt);
  394. $GLOBALS['dbi']->freeResult($this->results);
  395. } // end of mysqlReport function
  396. } // end of PMA_Export_PDF class
  397. ?>