2 /* vim: set expandtab sw=4 ts=4 sts=4: */
8 include_once("Export_Relation_Schema.class.php");
11 * This Class inherits the XMLwriter class and
12 * helps in developing structure of SVG Schema Export
15 * @see http://php.net/manual/en/book.xmlwriter.php
18 class PMA_SVG
extends XMLWriter
26 * The "PMA_SVG" constructor
28 * Upon instantiation This starts writing the Svg XML document
31 * @see XMLWriter::openMemory(),XMLWriter::setIndent(),XMLWriter::startDocument()
33 function __construct()
37 * Set indenting using three spaces,
38 * so output is formatted
41 $this->setIndent(true);
42 $this->setIndentString(' ');
44 * Create the XML document
47 $this->startDocument('1.0','UTF-8');
48 $this->startDtd('svg','-//W3C//DTD SVG 1.1//EN','http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd');
55 * @param string value sets the title text
59 function setTitle($value)
61 $this->title
= $value;
67 * @param string value sets the author
71 function setAuthor($value)
73 $this->author
= $value;
79 * @param string value sets the font e.g Arial, Sans-serif etc
83 function setFont($value)
91 * @return string returns the font name
100 * Set document font size
102 * @param string value sets the font size in pixels
106 function setFontSize($value)
108 $this->fontSize
= $value;
112 * Get document font size
114 * @return string returns the font size
117 function getFontSize()
119 return $this->fontSize
;
123 * Starts Svg Document
125 * svg document starts by first initializing svg tag
126 * which contains all the attributes and namespace that needed
127 * to define the svg document
129 * @param integer width total width of the Svg document
130 * @param integer height total height of the Svg document
133 * @see XMLWriter::startElement(),XMLWriter::writeAttribute()
135 function startSvgDoc($width,$height)
137 $this->startElement('svg');
138 $this->writeAttribute('width', $width);
139 $this->writeAttribute('height', $height);
140 $this->writeAttribute('xmlns', 'http://www.w3.org/2000/svg');
141 $this->writeAttribute('version', '1.1');
149 * @see XMLWriter::endElement(),XMLWriter::endDocument()
154 $this->endDocument();
158 * output Svg Document
160 * svg document prompted to the user for download
161 * Svg document saved in .svg extension and can be
162 * easily changeable by using any svg IDE
166 * @see XMLWriter::startElement(),XMLWriter::writeAttribute()
168 function showOutput($fileName)
171 header('Content-type: image/svg+xml');
172 header('Content-Disposition: attachment; filename="'.$fileName.'.svg"');
173 $output = $this->flush();
180 * SVG has some predefined shape elements like rectangle & text
181 * and other elements who have x,y co-ordinates are drawn.
182 * specify their width and height and can give styles too.
184 * @param string name Svg element name
185 * @param integer x The x attribute defines the left position of the element
186 (e.g. x="0" places the element 0 pixels from the left of
188 * @param integer y The y attribute defines the top position of the element
189 (e.g. y="0" places the element 0 pixels from the top of
191 * @param integer width The width attribute defines the width the element
192 * @param integer height The height attribute defines the height the element
193 * @param string text The text attribute defines the text the element
194 * @param string styles The style attribute defines the style the element
195 styles can be defined like CSS styles
198 * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),XMLWriter::text(),XMLWriter::endElement()
200 function printElement($name,$x,$y,$width = '',$height = '',$text = '',$styles = '')
202 $this->startElement($name);
203 $this->writeAttribute('width',$width);
204 $this->writeAttribute('height',$height);
205 $this->writeAttribute('x', $x);
206 $this->writeAttribute('y', $y);
207 $this->writeAttribute('style', $styles);
209 $this->writeAttribute('font-family', $this->font
);
210 $this->writeAttribute('font-size', $this->fontSize
);
217 * Draws Svg Line element
219 * Svg line element is drawn for connecting the tables.
220 * arrows are also drawn by specify its start and ending
223 * @param string name Svg element name i.e line
224 * @param integer x1 The x1 attribute defines the start of the line on the x-axis
225 * @param integer y1 The y1 attribute defines the start of the line on the y-axis
226 * @param integer x2 The x2 attribute defines the end of the line on the x-axis
227 * @param integer y2 The y2 attribute defines the end of the line on the y-axis
228 * @param string styles The style attribute defines the style the element
229 styles can be defined like CSS styles
232 * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),XMLWriter::endElement()
234 function printElementLine($name,$x1,$y1,$x2,$y2,$styles)
236 $this->startElement($name);
237 $this->writeAttribute('x1',$x1);
238 $this->writeAttribute('y1',$y1);
239 $this->writeAttribute('x2', $x2);
240 $this->writeAttribute('y2', $y2);
241 $this->writeAttribute('style', $styles);
246 * get width of string/text
248 * Svg text element width is calcualted depending on font name
249 * and font size. It is very important to know the width of text
250 * because rectangle is drawn around it.
252 * This is a bit hardcore method. I didn't found any other than this.
254 * @param string text string that width will be calculated
255 * @param integer font name of the font like Arial,sans-serif etc
256 * @param integer fontSize size of font
257 * @return integer width of the text
260 function getStringWidth($text,$font,$fontSize)
263 * Start by counting the width, giving each character a modifying value
266 $count = $count +
((strlen($text) - strlen(str_replace(array("i","j","l"),"",$text)))*0.23);//ijl
267 $count = $count +
((strlen($text) - strlen(str_replace(array("f"),"",$text)))*0.27);//f
268 $count = $count +
((strlen($text) - strlen(str_replace(array("t","I"),"",$text)))*0.28);//tI
269 $count = $count +
((strlen($text) - strlen(str_replace(array("r"),"",$text)))*0.34);//r
270 $count = $count +
((strlen($text) - strlen(str_replace(array("1"),"",$text)))*0.49);//1
271 $count = $count +
((strlen($text) - strlen(str_replace(array("c","k","s","v","x","y","z","J"),"",$text)))*0.5);//cksvxyzJ
272 $count = $count +
((strlen($text) - strlen(str_replace(array("a","b","d","e","g","h","n","o","p","q","u","L","0","2","3","4","5","6","7","8","9"),"",$text)))*0.56);//abdeghnopquL023456789
273 $count = $count +
((strlen($text) - strlen(str_replace(array("F","T","Z"),"",$text)))*0.61);//FTZ
274 $count = $count +
((strlen($text) - strlen(str_replace(array("A","B","E","K","P","S","V","X","Y"),"",$text)))*0.67);//ABEKPSVXY
275 $count = $count +
((strlen($text) - strlen(str_replace(array("w","C","D","H","N","R","U"),"",$text)))*0.73);//wCDHNRU
276 $count = $count +
((strlen($text) - strlen(str_replace(array("G","O","Q"),"",$text)))*0.78);//GOQ
277 $count = $count +
((strlen($text) - strlen(str_replace(array("m","M"),"",$text)))*0.84);//mM
278 $count = $count +
((strlen($text) - strlen(str_replace("W","",$text)))*.95);//W
279 $count = $count +
((strlen($text) - strlen(str_replace(" ","",$text)))*.28);//" "
280 $text = str_replace(" ","",$text);//remove the " "'s
281 $count = $count +
(strlen(preg_replace("/[a-z0-9]/i","",$text))*0.3); //all other chrs
284 $font = strtolower($font);
287 * no modifier for arial and sans-serif
293 * .92 modifer for time, serif, brushscriptstd, and californian fb
297 case 'brushscriptstd':
298 case 'californian fb':
302 * 1.23 modifier for broadway
308 $textWidth = $count*$fontSize;
309 return ceil($textWidth*$modifier);
314 * Table preferences/statistics
316 * This class preserves the table co-ordinates,fields
317 * and helps in drawing/generating the Tables in SVG XML document.
329 private $_showInfo = false;
333 public $fields = array();
334 public $heightCell = 0;
335 public $currentCell = 0;
337 public $primary = array();
340 * The "Table_Stats" constructor
342 * @param string table_name The table name
343 * @param integer ff The font size
344 * @param integer samewidth The max. with among tables
345 * @param boolean show_keys Whether to display keys or not
346 * @param boolean show_info Whether to display table position or not
347 * @global object The current SVG image document
348 * @global integer The current page number (from the
349 * $cfg['Servers'][$i]['table_coords'] table)
350 * @global array The relations settings
351 * @global string The current db name
353 * @see PMA_SVG, Table_Stats::Table_Stats_setWidth,
354 Table_Stats::Table_Stats_setHeight
356 function __construct($tableName, $font, $fontSize, $pageNumber, &$same_wide_width, $showKeys = false, $showInfo = false)
358 global $svg, $cfgRelation, $db;
360 $this->_tableName
= $tableName;
361 $sql = 'DESCRIBE ' . PMA_backquote($tableName);
362 $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE
);
363 if (!$result ||
!PMA_DBI_num_rows($result)) {
364 $svg->dieSchema($pageNumber,"SVG",sprintf(__('The %s table doesn\'t exist!'), $tableName));
369 * check to see if it will load all fields or only the foreign keys
373 $indexes = PMA_Index
::getFromTable($this->_tableName
, $db);
374 $all_columns = array();
375 foreach ($indexes as $index) {
376 $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns())));
378 $this->fields
= array_keys($all_columns);
380 while ($row = PMA_DBI_fetch_row($result)) {
381 $this->fields
[] = $row[0];
385 $this->_showInfo
= $showInfo;
388 $this->_setHeightTable($fontSize);
390 // setWidth must me after setHeight, because title
391 // can include table height which changes table width
392 $this->_setWidthTable($font,$fontSize);
393 if ($same_wide_width < $this->width
) {
394 $same_wide_width = $this->width
;
398 $sql = 'SELECT x, y FROM '
399 . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords'])
400 . ' WHERE db_name = \'' . PMA_sqlAddSlashes($db) . '\''
401 . ' AND table_name = \'' . PMA_sqlAddSlashes($tableName) . '\''
402 . ' AND pdf_page_number = ' . $pageNumber;
403 $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE
);
405 if (!$result ||
!PMA_DBI_num_rows($result)) {
406 $svg->dieSchema($pageNumber,"SVG",sprintf(__('Please configure the coordinates for table %s'), $tableName));
408 list($this->x
, $this->y
) = PMA_DBI_fetch_row($result);
409 $this->x
= (double) $this->x
;
410 $this->y
= (double) $this->y
;
412 $this->displayfield
= PMA_getDisplayField($db, $tableName);
414 $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE
);
415 if (PMA_DBI_num_rows($result) > 0) {
416 while ($row = PMA_DBI_fetch_assoc($result)) {
417 if ($row['Key_name'] == 'PRIMARY') {
418 $this->primary
[] = $row['Column_name'];
425 * Returns title of the current table,
426 * title can have the dimensions/co-ordinates of the table
430 private function _getTitle()
432 return ($this->_showInfo ?
sprintf('%.0f', $this->width
) . 'x' . sprintf('%.0f', $this->heightCell
) : '') . ' ' . $this->_tableName
;
436 * Sets the width of the table
438 * @param string font The font size
439 * @param integer fontSize The font size
440 * @global object The current SVG image document
444 function _setWidthTable($font,$fontSize)
448 foreach ($this->fields
as $field) {
449 $this->width
= max($this->width
, $svg->getStringWidth($field,$font,$fontSize));
451 $this->width +
= $svg->getStringWidth(' ',$font,$fontSize);
453 * it is unknown what value must be added, because
454 * table title is affected by the tabe width value
456 while ($this->width
< $svg->getStringWidth($this->_getTitle(),$font,$fontSize)) {
462 * Sets the height of the table
466 function _setHeightTable($fontSize)
468 $this->heightCell
= $fontSize +
4;
469 $this->height
= (count($this->fields
) +
1) * $this->heightCell
;
475 * @param boolean showColor Whether to display color
476 * @global object The current SVG image document
478 * @see PMA_SVG,PMA_SVG::printElement
480 public function tableDraw($showColor)
483 //echo $this->_tableName.'<br />';
484 $svg->printElement('rect',$this->x
,$this->y
,
485 $this->width
,$this->heightCell
,
486 NULL,'fill:red;stroke:black;'
488 $svg->printElement('text',$this->x +
5,$this->y+
14,
489 $this->width
,$this->heightCell
,
491 'fill:none;stroke:black;'
493 foreach ($this->fields
as $field) {
494 $this->currentCell +
= $this->heightCell
;
497 if (in_array($field, $this->primary
)) {
500 if ($field == $this->displayfield
) {
504 $svg->printElement('rect', $this->x
,$this->y +
$this->currentCell
,
505 $this->width
, $this->heightCell
,
507 'fill:'.$showColor.';stroke:black;'
509 $svg->printElement('text', $this->x +
5, $this->y +
14 +
$this->currentCell
,
510 $this->width
, $this->heightCell
,
512 'fill:none;stroke:black;'
520 * Relation preferences/statistics
522 * This class fetches the table master and foreign fields positions
523 * and helps in generating the Table references and then connects
524 * master table's master field to foreign table's foreign key
525 * in SVG XML document.
527 * @name Relation_Stats
528 * @see PMA_SVG::printElementLine
538 public $xDest, $yDest;
542 * The "Relation_Stats" constructor
544 * @param string master_table The master table name
545 * @param string master_field The relation field in the master table
546 * @param string foreign_table The foreign table name
547 * @param string foreigh_field The relation field in the foreign table
548 * @see Relation_Stats::_getXy
550 function __construct($master_table, $master_field, $foreign_table, $foreign_field)
552 $src_pos = $this->_getXy($master_table, $master_field);
553 $dest_pos = $this->_getXy($foreign_table, $foreign_field);
559 $src_left = $src_pos[0] - $this->wTick
;
560 $src_right = $src_pos[1] +
$this->wTick
;
561 $dest_left = $dest_pos[0] - $this->wTick
;
562 $dest_right = $dest_pos[1] +
$this->wTick
;
564 $d1 = abs($src_left - $dest_left);
565 $d2 = abs($src_right - $dest_left);
566 $d3 = abs($src_left - $dest_right);
567 $d4 = abs($src_right - $dest_right);
568 $d = min($d1, $d2, $d3, $d4);
571 $this->xSrc
= $src_pos[0];
573 $this->xDest
= $dest_pos[0];
575 } elseif ($d == $d2) {
576 $this->xSrc
= $src_pos[1];
578 $this->xDest
= $dest_pos[0];
580 } elseif ($d == $d3) {
581 $this->xSrc
= $src_pos[0];
583 $this->xDest
= $dest_pos[1];
586 $this->xSrc
= $src_pos[1];
588 $this->xDest
= $dest_pos[1];
591 $this->ySrc
= $src_pos[2];
592 $this->yDest
= $dest_pos[2];
596 * Gets arrows coordinates
598 * @param string table The current table name
599 * @param string column The relation column name
600 * @return array Arrows coordinates
603 function _getXy($table, $column)
605 $pos = array_search($column, $table->fields
);
606 // x_left, x_right, y
607 return array($table->x
, $table->x +
$table->width
, $table->y +
($pos +
1.5) * $table->heightCell
);
611 * draws relation links and arrows
612 * shows foreign key relations
614 * @param boolean changeColor Whether to use one color per relation or not
615 * @global object The current SVG image document
619 public function relationDraw($changeColor)
624 $listOfColors = array(
633 shuffle($listOfColors);
634 $color = $listOfColors[0];
639 $svg->printElementLine('line',$this->xSrc
,$this->ySrc
,
640 $this->xSrc +
$this->srcDir
* $this->wTick
,$this->ySrc
,
641 'fill:'.$color.';stroke:black;stroke-width:2;'
643 $svg->printElementLine('line',$this->xDest +
$this->destDir
* $this->wTick
, $this->yDest
,
644 $this->xDest
, $this->yDest
,
645 'fill:'.$color.';stroke:black;stroke-width:2;'
647 $svg->printElementLine('line',$this->xSrc +
$this->srcDir
* $this->wTick
,$this->ySrc
,
648 $this->xDest +
$this->destDir
* $this->wTick
, $this->yDest
,
649 'fill:'.$color.';stroke:'.$color.';stroke-width:1;'
651 $root2 = 2 * sqrt(2);
652 $svg->printElementLine('line',$this->xSrc +
$this->srcDir
* $this->wTick
* 0.75, $this->ySrc
,
653 $this->xSrc +
$this->srcDir
* (0.75 - 1 / $root2) * $this->wTick
,
654 $this->ySrc +
$this->wTick
/ $root2 ,
655 'fill:'.$color.';stroke:black;stroke-width:2;'
657 $svg->printElementLine('line',$this->xSrc +
$this->srcDir
* $this->wTick
* 0.75, $this->ySrc
,
658 $this->xSrc +
$this->srcDir
* (0.75 - 1 / $root2) * $this->wTick
,
659 $this->ySrc
- $this->wTick
/ $root2 ,
660 'fill:'.$color.';stroke:black;stroke-width:2;'
662 $svg->printElementLine('line',$this->xDest +
$this->destDir
* $this->wTick
/ 2 , $this->yDest
,
663 $this->xDest +
$this->destDir
* (0.5 +
1 / $root2) * $this->wTick
,
664 $this->yDest +
$this->wTick
/ $root2 ,
665 'fill:'.$color.';stroke:black;stroke-width:2;');
666 $svg->printElementLine('line',$this->xDest +
$this->destDir
* $this->wTick
/ 2 ,
667 $this->yDest
, $this->xDest +
$this->destDir
* (0.5 +
1 / $root2) * $this->wTick
,
668 $this->yDest
- $this->wTick
/ $root2 ,
669 'fill:'.$color.';stroke:black;stroke-width:2;'
674 * end of the "Relation_Stats" class
678 * Svg Relation Schema Class
680 * Purpose of this class is to generate the SVG XML Document because
681 * SVG defines the graphics in XML format which is used for representing
682 * the database diagrams as vector image. This class actually helps
683 * in preparing SVG XML format.
685 * SVG XML is generated by using XMLWriter php extension and this class
686 * inherits Export_Relation_Schema class has common functionality added
689 * @name Svg_Relation_Schema
691 class PMA_Svg_Relation_Schema
extends PMA_Export_Relation_Schema
694 private $tables = array();
695 private $_relations = array();
699 private $_xMin = 100000;
700 private $_yMin = 100000;
701 private $t_marg = 10;
702 private $b_marg = 10;
703 private $l_marg = 10;
704 private $r_marg = 10;
705 private $_tablewidth;
708 * The "PMA_Svg_Relation_Schema" constructor
710 * Upon instantiation This starts writing the SVG XML document
711 * user will be prompted for download as .svg extension
716 function __construct()
720 $this->setPageNumber($_POST['pdf_page_number']);
721 $this->setShowColor(isset($_POST['show_color']));
722 $this->setShowKeys(isset($_POST['show_keys']));
723 $this->setTableDimension(isset($_POST['show_table_dimension']));
724 $this->setAllTableSameWidth(isset($_POST['all_table_same_wide']));
725 $this->setExportType($_POST['export_type']);
727 $svg = new PMA_SVG();
728 $svg->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $this->pageNumber
));
729 $svg->SetAuthor('phpMyAdmin ' . PMA_VERSION
);
730 $svg->setFont('Arial');
731 $svg->setFontSize('16px');
732 $svg->startSvgDoc('1000px','1000px');
733 $alltables = $this->getAllTables($db,$this->pageNumber
);
735 foreach ($alltables AS $table) {
736 if (! isset($this->tables
[$table])) {
737 $this->tables
[$table] = new Table_Stats($table,$svg->getFont(),$svg->getFontSize(), $this->pageNumber
, $this->_tablewidth
, $this->showKeys
, $this->tableDimension
);
740 if ($this->sameWide
) {
741 $this->tables
[$table]->width
= $this->_tablewidth
;
743 $this->_setMinMax($this->tables
[$table]);
745 $seen_a_relation = false;
746 foreach ($alltables as $one_table) {
747 $exist_rel = PMA_getForeigners($db, $one_table, '', 'both');
749 $seen_a_relation = true;
750 foreach ($exist_rel as $master_field => $rel) {
751 /* put the foreign table on the schema only if selected
753 * (do not use array_search() because we would have to
754 * to do a === false and this is not PHP3 compatible)
756 if (in_array($rel['foreign_table'], $alltables)) {
757 $this->_addRelation($one_table,$svg->getFont(),$svg->getFontSize(), $master_field, $rel['foreign_table'], $rel['foreign_field'], $this->tableDimension
);
762 if ($seen_a_relation) {
763 $this->_drawRelations($this->showColor
);
766 $this->_drawTables($this->showColor
);
768 $svg->showOutput($db.'-'.$this->pageNumber
);
773 * Sets X and Y minimum and maximum for a table cell
775 * @param string table The table name
778 private function _setMinMax($table)
780 $this->_xMax
= max($this->_xMax
, $table->x +
$table->width
);
781 $this->_yMax
= max($this->_yMax
, $table->y +
$table->height
);
782 $this->_xMin
= min($this->_xMin
, $table->x
);
783 $this->_yMin
= min($this->_yMin
, $table->y
);
787 * Defines relation objects
789 * @param string masterTable The master table name
790 * @param string masterField The relation field in the master table
791 * @param string foreignTable The foreign table name
792 * @param string foreignField The relation field in the foreign table
793 * @param boolean showInfo Whether to display table position or not
795 * @see _setMinMax,Table_Stats::__construct(),Relation_Stats::__construct()
797 private function _addRelation($masterTable,$font,$fontSize, $masterField, $foreignTable, $foreignField, $showInfo)
799 if (! isset($this->tables
[$masterTable])) {
800 $this->tables
[$masterTable] = new Table_Stats($masterTable, $font, $fontSize, $this->pageNumber
, $this->_tablewidth
, false, $showInfo);
801 $this->_setMinMax($this->tables
[$masterTable]);
803 if (! isset($this->tables
[$foreignTable])) {
804 $this->tables
[$foreignTable] = new Table_Stats($foreignTable,$font,$fontSize,$this->pageNumber
, $this->_tablewidth
, false, $showInfo);
805 $this->_setMinMax($this->tables
[$foreignTable]);
807 $this->_relations
[] = new Relation_Stats($this->tables
[$masterTable], $masterField, $this->tables
[$foreignTable], $foreignField);
811 * Draws relation arrows and lines
812 * connects master table's master field to
813 * foreign table's forein field
815 * @param boolean changeColor Whether to use one color per relation or not
817 * @see Relation_Stats::relationDraw()
819 private function _drawRelations($changeColor)
821 foreach ($this->_relations
as $relation) {
822 $relation->relationDraw($changeColor);
829 * @param boolean changeColor Whether to show color for primary fields or not
831 * @see Table_Stats::Table_Stats_tableDraw()
833 private function _drawTables($changeColor)
835 foreach ($this->tables
as $table) {
836 $table->tableDraw($changeColor);