2 /* vim: set expandtab sw=4 ts=4 sts=4: */
8 if (! defined('PHPMYADMIN')) {
13 * Skip the plugin if TCPDF is not available.
15 if (! file_exists(TCPDF_INC
)) {
16 $GLOBALS['skip_import'] = true;
21 * block attempts to directly run this script
23 if (getcwd() == dirname(__FILE__
)) {
24 die('Attack stopped');
27 require_once 'libraries/plugins/schema/Export_Relation_Schema.class.php';
28 require_once 'libraries/plugins/schema/pdf/RelationStatsPdf.class.php';
29 require_once 'libraries/plugins/schema/pdf/TableStatsPdf.class.php';
30 require_once 'libraries/PDF.class.php';
31 require_once 'libraries/transformations.lib.php';
34 * Extends the "TCPDF" class and helps
35 * in developing the structure of PDF Schema Export
41 class PMA_Schema_PDF
extends PMA_PDF
52 var $Outlines = array();
55 private $_ff = PMA_PDF_FONT
;
61 * Constructs PDF for schema export.
63 * @param string $orientation page orientation
64 * @param string $unit unit
65 * @param string $paper the format used for pages
66 * @param int $pageNumber schema page number that is being exported
67 * @param boolean $withDoc with document dictionary
71 public function __construct(
72 $orientation, $unit, $paper, $pageNumber, $withDoc
74 parent
::__construct($orientation, $unit, $paper);
75 $this->_pageNumber
= $pageNumber;
76 $this->_withDoc
= $withDoc;
80 * Sets the value for margins
82 * @param float $c_margin margin
86 public function setCMargin($c_margin)
88 $this->cMargin
= $c_margin;
92 * Sets the scaling factor, defines minimum coordinates and margins
94 * @param float|int $scale The scaling factor
95 * @param float|int $xMin The minimum X coordinate
96 * @param float|int $yMin The minimum Y coordinate
97 * @param float|int $leftMargin The left margin
98 * @param float|int $topMargin The top margin
104 function setScale($scale = 1, $xMin = 0, $yMin = 0,
105 $leftMargin = -1, $topMargin = -1
107 $this->scale
= $scale;
108 $this->_xMin
= $xMin;
109 $this->_yMin
= $yMin;
110 if ($this->leftMargin
!= -1) {
111 $this->leftMargin
= $leftMargin;
113 if ($this->topMargin
!= -1) {
114 $this->topMargin
= $topMargin;
119 * Outputs a scaled cell
121 * @param float|int $w The cell width
122 * @param float|int $h The cell height
123 * @param string $txt The text to output
124 * @param mixed $border Whether to add borders or not
125 * @param integer $ln Where to put the cursor once the output is done
126 * @param string $align Align mode
127 * @param integer $fill Whether to fill the cell with a color or not
128 * @param string $link Link
136 function cellScale($w, $h = 0, $txt = '', $border = 0, $ln = 0,
137 $align = '', $fill = 0, $link = ''
139 $h = $h / $this->scale
;
140 $w = $w / $this->scale
;
141 $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link);
145 * Draws a scaled line
147 * @param float $x1 The horizontal position of the starting point
148 * @param float $y1 The vertical position of the starting point
149 * @param float $x2 The horizontal position of the ending point
150 * @param float $y2 The vertical position of the ending point
158 function lineScale($x1, $y1, $x2, $y2)
160 $x1 = ($x1 - $this->_xMin
) / $this->scale +
$this->leftMargin
;
161 $y1 = ($y1 - $this->_yMin
) / $this->scale +
$this->topMargin
;
162 $x2 = ($x2 - $this->_xMin
) / $this->scale +
$this->leftMargin
;
163 $y2 = ($y2 - $this->_yMin
) / $this->scale +
$this->topMargin
;
164 $this->Line($x1, $y1, $x2, $y2);
168 * Sets x and y scaled positions
170 * @param float $x The x position
171 * @param float $y The y position
177 * @see TCPDF::SetXY()
179 function setXyScale($x, $y)
181 $x = ($x - $this->_xMin
) / $this->scale +
$this->leftMargin
;
182 $y = ($y - $this->_yMin
) / $this->scale +
$this->topMargin
;
183 $this->SetXY($x, $y);
187 * Sets the X scaled positions
189 * @param float $x The x position
197 function setXScale($x)
199 $x = ($x - $this->_xMin
) / $this->scale +
$this->leftMargin
;
204 * Sets the scaled font size
206 * @param float $size The font size (in points)
212 * @see TCPDF::SetFontSize()
214 function setFontSizeScale($size)
216 // Set font size in points
217 $size = $size / $this->scale
;
218 $this->SetFontSize($size);
222 * Sets the scaled line width
224 * @param float $width The line width
230 * @see TCPDF::SetLineWidth()
232 function setLineWidthScale($width)
234 $width = $width / $this->scale
;
235 $this->SetLineWidth($width);
239 * This method is used to render the page header.
243 * @see TCPDF::Header()
247 // We only show this if we find something in the new pdf_pages table
249 // This function must be named "Header" to work with the TCPDF library
250 if ($this->_withDoc
) {
251 if ($this->_offline ||
$this->_pageNumber
== -1) {
252 $pg_name = __("PDF export page");
254 $test_query = 'SELECT * FROM '
255 . PMA_Util
::backquote($GLOBALS['cfgRelation']['db']) . '.'
256 . PMA_Util
::backquote($GLOBALS['cfgRelation']['pdf_pages'])
257 . ' WHERE db_name = \'' . PMA_Util
::sqlAddSlashes($GLOBALS['db'])
258 . '\' AND page_nr = \'' . $this->_pageNumber
. '\'';
259 $test_rs = PMA_queryAsControlUser($test_query);
260 $pages = @$GLOBALS['dbi']->fetchAssoc($test_rs);
261 $pg_name = ucfirst($pages['page_descr']);
264 $this->SetFont($this->_ff
, 'B', 14);
265 $this->Cell(0, 6, $pg_name, 'B', 1, 'C');
266 $this->SetFont($this->_ff
, '');
272 * This function must be named "Footer" to work with the TCPDF library
276 * @see PMA_PDF::Footer()
280 if ($this->_withDoc
) {
288 * @param array $w array of widths
292 function SetWidths($w)
299 * Generates table row.
301 * @param array $data Data for table
302 * @param array $links Links for table cells
306 function Row($data, $links)
310 $data_cnt = count($data);
311 for ($i = 0;$i < $data_cnt;$i++
) {
312 $nb = max($nb, $this->NbLines($this->widths
[$i], $data[$i]));
314 $il = $this->FontSize
;
315 $h = ($il +
1) * $nb;
316 // page break if necessary
317 $this->CheckPageBreak($h);
319 $data_cnt = count($data);
320 for ($i = 0;$i < $data_cnt;$i++
) {
321 $w = $this->widths
[$i];
322 // save current position
326 $this->Rect($x, $y, $w, $h);
327 if (isset($links[$i])) {
328 $this->Link($x, $y, $w, $h, $links[$i]);
331 $this->MultiCell($w, $il +
1, $data[$i], 0, 'L');
333 $this->SetXY($x +
$w, $y);
340 * Compute number of lines used by a multicell of width w
342 * @param int $w width
343 * @param string $txt text
347 function NbLines($w, $txt)
349 $cw = &$this->CurrentFont
['cw'];
351 $w = $this->w
- $this->rMargin
- $this->x
;
353 $wmax = ($w-2 * $this->cMargin
) * 1000 / $this->FontSize
;
354 $s = str_replace("\r", '', $txt);
355 $nb = /*overload*/mb_strlen($s);
356 if ($nb > 0 and $s[$nb-1] == "\n") {
377 $l +
= isset($cw[/*overload*/mb_ord($c)])?
$cw[/*overload*/mb_ord($c)]:0 ;
398 * Set whether the document is generated from client side DB
400 * @param string $value whether offline
406 public function setOffline($value)
408 $this->_offline
= $value;
413 * Pdf Relation Schema Class
415 * Purpose of this class is to generate the PDF Document. PDF is widely
416 * used format for documenting text,fonts,images and 3d vector graphics.
418 * This class inherits Export_Relation_Schema class has common functionality added
421 * @name Pdf_Relation_Schema
422 * @package PhpMyAdmin
424 class PMA_Pdf_Relation_Schema
extends PMA_Export_Relation_Schema
431 private $_tables = array();
432 private $_ff = PMA_PDF_FONT
;
436 private $_xMin = 100000;
437 private $_yMin = 100000;
438 private $_topMargin = 10;
439 private $_bottomMargin = 10;
440 private $_leftMargin = 10;
441 private $_rightMargin = 10;
442 private $_tablewidth;
443 protected $relations = array();
446 * The "PMA_Pdf_Relation_Schema" constructor
448 * @global object $pdf The current PDF Schema document
450 * @see PMA_Schema_PDF
452 function __construct()
454 parent
::__construct();
458 $this->setShowGrid(isset($_REQUEST['pdf_show_grid']));
459 $this->setShowColor(isset($_REQUEST['pdf_show_color']));
460 $this->setShowKeys(isset($_REQUEST['pdf_show_keys']));
461 $this->setTableDimension(isset($_REQUEST['pdf_show_table_dimension']));
462 $this->setAllTablesSameWidth(isset($_REQUEST['pdf_all_tables_same_width']));
463 $this->setWithDataDictionary(isset($_REQUEST['pdf_with_doc']));
464 $this->setOrientation($_REQUEST['pdf_orientation']);
465 $this->setPaper($_REQUEST['pdf_paper']);
467 // Initializes a new document
468 $pdf = new PMA_Schema_PDF(
469 $this->orientation
, 'mm', $this->paper
,
470 $this->pageNumber
, $this->_withDoc
474 __('Schema of the %s database'),
480 $pdf->SetAutoPageBreak('auto');
481 $pdf->setOffline($this->offline
);
483 $alltables = $this->getTablesFromRequest();
485 if ($this->_withDoc
) {
486 $pdf->SetAutoPageBreak('auto', 15);
488 $this->dataDictionaryDoc($alltables);
489 $pdf->SetAutoPageBreak('auto');
495 if ($this->_withDoc
) {
496 $pdf->SetLink($pdf->PMA_links
['RT']['-'], -1);
497 $pdf->Bookmark(__('Relational schema'));
498 $pdf->SetAlias('{00}', $pdf->PageNo());
499 $this->_topMargin
= 28;
500 $this->_bottomMargin
= 28;
504 foreach ($alltables as $table) {
505 if (! isset($this->_tables
[$table])) {
506 $this->_tables
[$table] = new Table_Stats_Pdf(
512 $this->tableDimension
,
516 if ($this->sameWide
) {
517 $this->_tables
[$table]->width
= $this->_tablewidth
;
519 $this->_setMinMax($this->_tables
[$table]);
522 // Defines the scale factor
523 $this->_scale
= ceil(
525 ($this->_xMax
- $this->_xMin
)
526 / ($pdf->getPageWidth() - $this->_rightMargin
- $this->_leftMargin
),
527 ($this->_yMax
- $this->_yMin
)
528 / ($pdf->getPageHeight() - $this->_topMargin
- $this->_bottomMargin
)
539 // Builds and save the PDF document
540 $pdf->setLineWidthScale(0.1);
542 if ($this->_showGrid
) {
543 $pdf->SetFontSize(10);
544 $this->_strokeGrid();
546 $pdf->setFontSizeScale(14);
547 // previous logic was checking master tables and foreign tables
548 // but I think that looping on every table of the pdf page as a master
549 // and finding its foreigns is OK (then we can support innodb)
550 $seen_a_relation = false;
551 foreach ($alltables as $one_table) {
552 $exist_rel = PMA_getForeigners($GLOBALS['db'], $one_table, '', 'both');
557 $seen_a_relation = true;
558 foreach ($exist_rel as $master_field => $rel) {
559 // put the foreign table on the schema only if selected
561 // (do not use array_search() because we would have to
562 // to do a === false and this is not PHP3 compatible)
563 if ($master_field != 'foreign_keys_data') {
564 if (in_array($rel['foreign_table'], $alltables)) {
568 $rel['foreign_table'],
569 $rel['foreign_field']
575 foreach ($rel as $one_key) {
576 if (!in_array($one_key['ref_table_name'], $alltables)) {
580 foreach ($one_key['index_list']
581 as $index => $one_field
586 $one_key['ref_table_name'],
587 $one_key['ref_index_list'][$index]
594 if ($seen_a_relation) {
595 $this->_drawRelations();
597 $this->_drawTables();
603 * @param boolean $value show grid of the document or not
609 public function setShowGrid($value)
611 $this->_showGrid
= $value;
615 * Returns whether to show grid
617 * @return boolean whether to show grid
619 public function isShowGrid()
621 return $this->_showGrid
;
625 * Set Data Dictionary
627 * @param boolean $value show selected database data dictionary or not
633 public function setWithDataDictionary($value)
635 $this->_withDoc
= $value;
639 * Return whether to show selected database data dictionary or not
641 * @return boolean whether to show selected database data dictionary or not
643 public function isWithDataDictionary()
645 return $this->_withDoc
;
649 * Output Pdf Document for download
654 function showOutput()
657 $pdf->Download($this->getFileName('.pdf'));
661 * Sets X and Y minimum and maximum for a table cell
663 * @param object $table The table name of which sets XY co-ordinates
669 private function _setMinMax($table)
671 $this->_xMax
= max($this->_xMax
, $table->x +
$table->width
);
672 $this->_yMax
= max($this->_yMax
, $table->y +
$table->height
);
673 $this->_xMin
= min($this->_xMin
, $table->x
);
674 $this->_yMin
= min($this->_yMin
, $table->y
);
678 * Defines relation objects
680 * @param string $masterTable The master table name
681 * @param string $masterField The relation field in the master table
682 * @param string $foreignTable The foreign table name
683 * @param string $foreignField The relation field in the foreign table
691 private function _addRelation($masterTable, $masterField, $foreignTable,
694 if (! isset($this->_tables
[$masterTable])) {
695 $this->_tables
[$masterTable] = new Table_Stats_Pdf(
696 $masterTable, null, $this->pageNumber
,
699 $this->tableDimension
701 $this->_setMinMax($this->_tables
[$masterTable]);
703 if (! isset($this->_tables
[$foreignTable])) {
704 $this->_tables
[$foreignTable] = new Table_Stats_Pdf(
705 $foreignTable, null, $this->pageNumber
,
708 $this->tableDimension
710 $this->_setMinMax($this->_tables
[$foreignTable]);
712 $this->relations
[] = new Relation_Stats_Pdf(
713 $this->_tables
[$masterTable], $masterField,
714 $this->_tables
[$foreignTable], $foreignField
721 * @global object $pdf the current PMA_Schema_PDF instance
727 * @see PMA_Schema_PDF
729 private function _strokeGrid()
736 if ($this->_withDoc
) {
744 $pdf->SetMargins(0, 0);
745 $pdf->SetDrawColor(200, 200, 200);
746 // Draws horizontal lines
749 ($pdf->getPageHeight() - $topSpace - $bottomSpace) / $gridSize
754 0, $l * $gridSize +
$topSpace,
755 $pdf->getPageWidth(), $l * $gridSize +
$topSpace
760 ($pdf->getPageHeight() - $topSpace - $bottomSpace - $labelHeight)
764 $pdf->SetXY(0, $l * $gridSize +
$topSpace);
765 $label = (string) sprintf(
767 ($l * $gridSize +
$topSpace - $this->_topMargin
)
768 * $this->_scale +
$this->_yMin
770 $pdf->Cell($labelWidth, $labelHeight, ' ' . $label);
773 // Draws vertical lines
775 $j = 0, $size = intval($pdf->getPageWidth() / $gridSize);
783 $pdf->getPageHeight() - $bottomSpace
785 $pdf->SetXY($j * $gridSize, $topSpace);
786 $label = (string) sprintf(
788 ($j * $gridSize - $this->_leftMargin
) * $this->_scale +
$this->_xMin
790 $pdf->Cell($labelWidth, $labelHeight, $label);
795 * Draws relation arrows
801 * @see Relation_Stats_Pdf::relationdraw()
803 private function _drawRelations()
806 foreach ($this->relations
as $relation) {
807 $relation->relationDraw($this->showColor
, $i);
819 * @see Table_Stats_Pdf::tableDraw()
821 private function _drawTables()
823 foreach ($this->_tables
as $table) {
824 $table->tableDraw(null, $this->_withDoc
, $this->showColor
);
829 * Generates data dictionary pages.
831 * @param array $alltables Tables to document.
835 public function dataDictionaryDoc($alltables)
839 $pdf->addpage($this->orientation
);
840 $pdf->Cell(0, 9, __('Table of contents'), 1, 0, 'C');
843 foreach ($alltables as $table) {
844 $pdf->PMA_links
['doc'][$table]['-'] = $pdf->AddLink();
848 0, 6, __('Page number:') . ' {' . sprintf("%02d", $i) . '}', 0, 0,
849 'R', 0, $pdf->PMA_links
['doc'][$table]['-']
853 0, 6, $i . ' ' . $table, 0, 1,
854 'L', 0, $pdf->PMA_links
['doc'][$table]['-']
857 $fields = $GLOBALS['dbi']->getColumns($GLOBALS['db'], $table);
858 foreach ($fields as $row) {
860 $field_name = $row['Field'];
861 $pdf->PMA_links
['doc'][$table][$field_name] = $pdf->AddLink();
863 // 0, 6, $field_name, 0, 1,
864 // 'L', 0, $pdf->PMA_links['doc'][$table][$field_name]
869 $pdf->PMA_links
['RT']['-'] = $pdf->AddLink();
872 0, 6, __('Page number:') . ' {00}', 0, 0,
873 'R', 0, $pdf->PMA_links
['RT']['-']
877 0, 6, $i . ' ' . __('Relational schema'), 0, 1,
878 'L', 0, $pdf->PMA_links
['RT']['-']
881 foreach ($alltables as $table) {
883 $pdf->SetAutoPageBreak(true, 15);
884 $pdf->addpage($this->orientation
);
885 $pdf->Bookmark($table);
886 $pdf->SetAlias('{' . sprintf("%02d", $z) . '}', $pdf->PageNo());
887 $pdf->PMA_links
['RT'][$table]['-'] = $pdf->AddLink();
888 $pdf->SetLink($pdf->PMA_links
['doc'][$table]['-'], -1);
889 $pdf->SetFont($this->_ff
, 'B', 18);
891 0, 8, $z . ' ' . $table, 1, 1,
892 'C', 0, $pdf->PMA_links
['RT'][$table]['-']
894 $pdf->SetFont($this->_ff
, '', 8);
897 $cfgRelation = PMA_getRelationsParam();
898 $comments = PMA_getComments($GLOBALS['db'], $table);
899 if ($cfgRelation['mimework']) {
900 $mime_map = PMA_getMIME($GLOBALS['db'], $table, true);
904 * Gets table information
906 $showtable = PMA_Table
::sGetStatusInfo($GLOBALS['db'], $table);
907 $show_comment = isset($showtable['Comment'])
908 ?
$showtable['Comment']
910 $create_time = isset($showtable['Create_time'])
911 ? PMA_Util
::localisedDate(
912 strtotime($showtable['Create_time'])
915 $update_time = isset($showtable['Update_time'])
916 ? PMA_Util
::localisedDate(
917 strtotime($showtable['Update_time'])
920 $check_time = isset($showtable['Check_time'])
921 ? PMA_Util
::localisedDate(
922 strtotime($showtable['Check_time'])
927 * Gets fields properties
929 $columns = $GLOBALS['dbi']->getColumns($GLOBALS['db'], $table);
930 // Check if we can use Relations
931 if (!empty($cfgRelation['relation'])) {
932 // Find which tables are related with the current one and write it in
934 $res_rel = PMA_getForeigners($GLOBALS['db'], $table);
938 * Displays the comments of the table if MySQL >= 3.23
942 if (! empty($show_comment)) {
943 $pdf->Cell(0, 3, __('Table comments:') . ' ' . $show_comment, 0, 1);
947 if (! empty($create_time)) {
948 $pdf->Cell(0, 3, __('Creation:') . ' ' . $create_time, 0, 1);
952 if (! empty($update_time)) {
953 $pdf->Cell(0, 3, __('Last update:') . ' ' . $update_time, 0, 1);
957 if (! empty($check_time)) {
958 $pdf->Cell(0, 3, __('Last check:') . ' ' . $check_time, 0, 1);
962 if ($break == true) {
963 $pdf->Cell(0, 3, '', 0, 1);
967 $pdf->SetFont($this->_ff
, 'B');
968 if (isset($this->orientation
) && $this->orientation
== 'L') {
969 $pdf->Cell(25, 8, __('Column'), 1, 0, 'C');
970 $pdf->Cell(20, 8, __('Type'), 1, 0, 'C');
971 $pdf->Cell(20, 8, __('Attributes'), 1, 0, 'C');
972 $pdf->Cell(10, 8, __('Null'), 1, 0, 'C');
973 $pdf->Cell(20, 8, __('Default'), 1, 0, 'C');
974 $pdf->Cell(25, 8, __('Extra'), 1, 0, 'C');
975 $pdf->Cell(45, 8, __('Links to'), 1, 0, 'C');
977 if ($this->paper
== 'A4') {
978 $comments_width = 67;
980 // this is really intended for 'letter'
982 * @todo find optimal width for all formats
984 $comments_width = 50;
986 $pdf->Cell($comments_width, 8, __('Comments'), 1, 0, 'C');
987 $pdf->Cell(45, 8, 'MIME', 1, 1, 'C');
989 array(25, 20, 20, 10, 20, 25, 45, $comments_width, 45)
992 $pdf->Cell(20, 8, __('Column'), 1, 0, 'C');
993 $pdf->Cell(20, 8, __('Type'), 1, 0, 'C');
994 $pdf->Cell(20, 8, __('Attributes'), 1, 0, 'C');
995 $pdf->Cell(10, 8, __('Null'), 1, 0, 'C');
996 $pdf->Cell(15, 8, __('Default'), 1, 0, 'C');
997 $pdf->Cell(15, 8, __('Extra'), 1, 0, 'C');
998 $pdf->Cell(30, 8, __('Links to'), 1, 0, 'C');
999 $pdf->Cell(30, 8, __('Comments'), 1, 0, 'C');
1000 $pdf->Cell(30, 8, 'MIME', 1, 1, 'C');
1001 $pdf->SetWidths(array(20, 20, 20, 10, 15, 15, 30, 30, 30));
1003 $pdf->SetFont($this->_ff
, '');
1005 foreach ($columns as $row) {
1006 $extracted_columnspec
1007 = PMA_Util
::extractColumnSpec($row['Type']);
1008 $type = $extracted_columnspec['print_type'];
1009 $attribute = $extracted_columnspec['attribute'];
1010 if (! isset($row['Default'])) {
1011 if ($row['Null'] != '' && $row['Null'] != 'NO') {
1012 $row['Default'] = 'NULL';
1015 $field_name = $row['Field'];
1017 $pdf->PMA_links
['RT'][$table][$field_name] = $pdf->AddLink();
1018 $pdf->Bookmark($field_name, 1, -1);
1019 $pdf->SetLink($pdf->PMA_links
['doc'][$table][$field_name], -1);
1020 $foreigner = PMA_searchColumnInForeigners($res_rel, $field_name);
1025 if ($foreigner['foreign_db'] != $GLOBALS['db']) {
1026 $linksTo .= $foreigner['foreign_db'] . '.';
1028 $linksTo .= $foreigner['foreign_table']
1029 . '.' . $foreigner['foreign_field'];
1031 if (isset($foreigner['on_update'])) { // not set for internal
1032 $linksTo .= "\n" . 'ON UPDATE ' . $foreigner['on_update'];
1033 $linksTo .= "\n" . 'ON DELETE ' . $foreigner['on_delete'];
1041 (($row['Null'] == '' ||
$row['Null'] == 'NO')
1044 (isset($row['Default']) ?
$row['Default'] : ''),
1047 (isset($comments[$field_name])
1048 ?
$comments[$field_name]
1050 (isset($mime_map) && isset($mime_map[$field_name])
1051 ?
str_replace('_', '/', $mime_map[$field_name]['mimetype'])
1055 $links[0] = $pdf->PMA_links
['RT'][$table][$field_name];
1057 && isset($pdf->PMA_links
['doc'][$foreigner['foreign_table']][$foreigner['foreign_field']])
1059 $links[6] = $pdf->PMA_links
['doc'][$foreigner['foreign_table']]
1060 [$foreigner['foreign_field']];
1064 $pdf->Row($pdf_row, $links);
1066 $pdf->SetFont($this->_ff
, '', 14);