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 $output = $this->flush();
172 PMA_download_header($fileName . '.svg', 'image/svg+xml', strlen($output));
179 * SVG has some predefined shape elements like rectangle & text
180 * and other elements who have x,y co-ordinates are drawn.
181 * specify their width and height and can give styles too.
183 * @param string name Svg element name
184 * @param integer x The x attribute defines the left position of the element
185 (e.g. x="0" places the element 0 pixels from the left of
187 * @param integer y The y attribute defines the top position of the element
188 (e.g. y="0" places the element 0 pixels from the top of
190 * @param integer width The width attribute defines the width the element
191 * @param integer height The height attribute defines the height the element
192 * @param string text The text attribute defines the text the element
193 * @param string styles The style attribute defines the style the element
194 styles can be defined like CSS styles
197 * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),XMLWriter::text(),XMLWriter::endElement()
199 function printElement($name,$x,$y,$width = '',$height = '',$text = '',$styles = '')
201 $this->startElement($name);
202 $this->writeAttribute('width',$width);
203 $this->writeAttribute('height',$height);
204 $this->writeAttribute('x', $x);
205 $this->writeAttribute('y', $y);
206 $this->writeAttribute('style', $styles);
208 $this->writeAttribute('font-family', $this->font
);
209 $this->writeAttribute('font-size', $this->fontSize
);
216 * Draws Svg Line element
218 * Svg line element is drawn for connecting the tables.
219 * arrows are also drawn by specify its start and ending
222 * @param string name Svg element name i.e line
223 * @param integer x1 The x1 attribute defines the start of the line on the x-axis
224 * @param integer y1 The y1 attribute defines the start of the line on the y-axis
225 * @param integer x2 The x2 attribute defines the end of the line on the x-axis
226 * @param integer y2 The y2 attribute defines the end of the line on the y-axis
227 * @param string styles The style attribute defines the style the element
228 styles can be defined like CSS styles
231 * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),XMLWriter::endElement()
233 function printElementLine($name,$x1,$y1,$x2,$y2,$styles)
235 $this->startElement($name);
236 $this->writeAttribute('x1',$x1);
237 $this->writeAttribute('y1',$y1);
238 $this->writeAttribute('x2', $x2);
239 $this->writeAttribute('y2', $y2);
240 $this->writeAttribute('style', $styles);
245 * get width of string/text
247 * Svg text element width is calcualted depending on font name
248 * and font size. It is very important to know the width of text
249 * because rectangle is drawn around it.
251 * This is a bit hardcore method. I didn't found any other than this.
253 * @param string text string that width will be calculated
254 * @param integer font name of the font like Arial,sans-serif etc
255 * @param integer fontSize size of font
256 * @return integer width of the text
259 function getStringWidth($text,$font,$fontSize)
262 * Start by counting the width, giving each character a modifying value
265 $count = $count +
((strlen($text) - strlen(str_replace(array("i","j","l"),"",$text)))*0.23);//ijl
266 $count = $count +
((strlen($text) - strlen(str_replace(array("f"),"",$text)))*0.27);//f
267 $count = $count +
((strlen($text) - strlen(str_replace(array("t","I"),"",$text)))*0.28);//tI
268 $count = $count +
((strlen($text) - strlen(str_replace(array("r"),"",$text)))*0.34);//r
269 $count = $count +
((strlen($text) - strlen(str_replace(array("1"),"",$text)))*0.49);//1
270 $count = $count +
((strlen($text) - strlen(str_replace(array("c","k","s","v","x","y","z","J"),"",$text)))*0.5);//cksvxyzJ
271 $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
272 $count = $count +
((strlen($text) - strlen(str_replace(array("F","T","Z"),"",$text)))*0.61);//FTZ
273 $count = $count +
((strlen($text) - strlen(str_replace(array("A","B","E","K","P","S","V","X","Y"),"",$text)))*0.67);//ABEKPSVXY
274 $count = $count +
((strlen($text) - strlen(str_replace(array("w","C","D","H","N","R","U"),"",$text)))*0.73);//wCDHNRU
275 $count = $count +
((strlen($text) - strlen(str_replace(array("G","O","Q"),"",$text)))*0.78);//GOQ
276 $count = $count +
((strlen($text) - strlen(str_replace(array("m","M"),"",$text)))*0.84);//mM
277 $count = $count +
((strlen($text) - strlen(str_replace("W","",$text)))*.95);//W
278 $count = $count +
((strlen($text) - strlen(str_replace(" ","",$text)))*.28);//" "
279 $text = str_replace(" ","",$text);//remove the " "'s
280 $count = $count +
(strlen(preg_replace("/[a-z0-9]/i","",$text))*0.3); //all other chrs
283 $font = strtolower($font);
286 * no modifier for arial and sans-serif
292 * .92 modifer for time, serif, brushscriptstd, and californian fb
296 case 'brushscriptstd':
297 case 'californian fb':
301 * 1.23 modifier for broadway
307 $textWidth = $count*$fontSize;
308 return ceil($textWidth*$modifier);
313 * Table preferences/statistics
315 * This class preserves the table co-ordinates,fields
316 * and helps in drawing/generating the Tables in SVG XML document.
328 private $_showInfo = false;
332 public $fields = array();
333 public $heightCell = 0;
334 public $currentCell = 0;
336 public $primary = array();
339 * The "Table_Stats" constructor
341 * @param string table_name The table name
342 * @param integer ff The font size
343 * @param integer samewidth The max. with among tables
344 * @param boolean show_keys Whether to display keys or not
345 * @param boolean show_info Whether to display table position or not
346 * @global object The current SVG image document
347 * @global integer The current page number (from the
348 * $cfg['Servers'][$i]['table_coords'] table)
349 * @global array The relations settings
350 * @global string The current db name
352 * @see PMA_SVG, Table_Stats::Table_Stats_setWidth,
353 Table_Stats::Table_Stats_setHeight
355 function __construct($tableName, $font, $fontSize, $pageNumber, &$same_wide_width, $showKeys = false, $showInfo = false)
357 global $svg, $cfgRelation, $db;
359 $this->_tableName
= $tableName;
360 $sql = 'DESCRIBE ' . PMA_backquote($tableName);
361 $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE
);
362 if (!$result ||
!PMA_DBI_num_rows($result)) {
363 $svg->dieSchema($pageNumber,"SVG",sprintf(__('The %s table doesn\'t exist!'), $tableName));
368 * check to see if it will load all fields or only the foreign keys
372 $indexes = PMA_Index
::getFromTable($this->_tableName
, $db);
373 $all_columns = array();
374 foreach ($indexes as $index) {
375 $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns())));
377 $this->fields
= array_keys($all_columns);
379 while ($row = PMA_DBI_fetch_row($result)) {
380 $this->fields
[] = $row[0];
384 $this->_showInfo
= $showInfo;
387 $this->_setHeightTable($fontSize);
389 // setWidth must me after setHeight, because title
390 // can include table height which changes table width
391 $this->_setWidthTable($font,$fontSize);
392 if ($same_wide_width < $this->width
) {
393 $same_wide_width = $this->width
;
397 $sql = 'SELECT x, y FROM '
398 . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords'])
399 . ' WHERE db_name = \'' . PMA_sqlAddSlashes($db) . '\''
400 . ' AND table_name = \'' . PMA_sqlAddSlashes($tableName) . '\''
401 . ' AND pdf_page_number = ' . $pageNumber;
402 $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE
);
404 if (!$result ||
!PMA_DBI_num_rows($result)) {
405 $svg->dieSchema($pageNumber,"SVG",sprintf(__('Please configure the coordinates for table %s'), $tableName));
407 list($this->x
, $this->y
) = PMA_DBI_fetch_row($result);
408 $this->x
= (double) $this->x
;
409 $this->y
= (double) $this->y
;
411 $this->displayfield
= PMA_getDisplayField($db, $tableName);
413 $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE
);
414 if (PMA_DBI_num_rows($result) > 0) {
415 while ($row = PMA_DBI_fetch_assoc($result)) {
416 if ($row['Key_name'] == 'PRIMARY') {
417 $this->primary
[] = $row['Column_name'];
424 * Returns title of the current table,
425 * title can have the dimensions/co-ordinates of the table
429 private function _getTitle()
431 return ($this->_showInfo ?
sprintf('%.0f', $this->width
) . 'x' . sprintf('%.0f', $this->heightCell
) : '') . ' ' . $this->_tableName
;
435 * Sets the width of the table
437 * @param string font The font size
438 * @param integer fontSize The font size
439 * @global object The current SVG image document
443 private function _setWidthTable($font,$fontSize)
447 foreach ($this->fields
as $field) {
448 $this->width
= max($this->width
, $svg->getStringWidth($field,$font,$fontSize));
450 $this->width +
= $svg->getStringWidth(' ',$font,$fontSize);
452 * it is unknown what value must be added, because
453 * table title is affected by the tabe width value
455 while ($this->width
< $svg->getStringWidth($this->_getTitle(),$font,$fontSize)) {
461 * Sets the height of the table
465 function _setHeightTable($fontSize)
467 $this->heightCell
= $fontSize +
4;
468 $this->height
= (count($this->fields
) +
1) * $this->heightCell
;
474 * @param boolean showColor Whether to display color
475 * @global object The current SVG image document
477 * @see PMA_SVG,PMA_SVG::printElement
479 public function tableDraw($showColor)
482 //echo $this->_tableName.'<br />';
483 $svg->printElement('rect',$this->x
,$this->y
,
484 $this->width
,$this->heightCell
,
485 NULL,'fill:red;stroke:black;'
487 $svg->printElement('text',$this->x +
5,$this->y+
14,
488 $this->width
,$this->heightCell
,
490 'fill:none;stroke:black;'
492 foreach ($this->fields
as $field) {
493 $this->currentCell +
= $this->heightCell
;
496 if (in_array($field, $this->primary
)) {
499 if ($field == $this->displayfield
) {
503 $svg->printElement('rect', $this->x
,$this->y +
$this->currentCell
,
504 $this->width
, $this->heightCell
,
506 'fill:'.$showColor.';stroke:black;'
508 $svg->printElement('text', $this->x +
5, $this->y +
14 +
$this->currentCell
,
509 $this->width
, $this->heightCell
,
511 'fill:none;stroke:black;'
519 * Relation preferences/statistics
521 * This class fetches the table master and foreign fields positions
522 * and helps in generating the Table references and then connects
523 * master table's master field to foreign table's foreign key
524 * in SVG XML document.
526 * @name Relation_Stats
527 * @see PMA_SVG::printElementLine
537 public $xDest, $yDest;
541 * The "Relation_Stats" constructor
543 * @param string master_table The master table name
544 * @param string master_field The relation field in the master table
545 * @param string foreign_table The foreign table name
546 * @param string foreigh_field The relation field in the foreign table
547 * @see Relation_Stats::_getXy
549 function __construct($master_table, $master_field, $foreign_table, $foreign_field)
551 $src_pos = $this->_getXy($master_table, $master_field);
552 $dest_pos = $this->_getXy($foreign_table, $foreign_field);
558 $src_left = $src_pos[0] - $this->wTick
;
559 $src_right = $src_pos[1] +
$this->wTick
;
560 $dest_left = $dest_pos[0] - $this->wTick
;
561 $dest_right = $dest_pos[1] +
$this->wTick
;
563 $d1 = abs($src_left - $dest_left);
564 $d2 = abs($src_right - $dest_left);
565 $d3 = abs($src_left - $dest_right);
566 $d4 = abs($src_right - $dest_right);
567 $d = min($d1, $d2, $d3, $d4);
570 $this->xSrc
= $src_pos[0];
572 $this->xDest
= $dest_pos[0];
574 } elseif ($d == $d2) {
575 $this->xSrc
= $src_pos[1];
577 $this->xDest
= $dest_pos[0];
579 } elseif ($d == $d3) {
580 $this->xSrc
= $src_pos[0];
582 $this->xDest
= $dest_pos[1];
585 $this->xSrc
= $src_pos[1];
587 $this->xDest
= $dest_pos[1];
590 $this->ySrc
= $src_pos[2];
591 $this->yDest
= $dest_pos[2];
595 * Gets arrows coordinates
597 * @param string table The current table name
598 * @param string column The relation column name
599 * @return array Arrows coordinates
602 function _getXy($table, $column)
604 $pos = array_search($column, $table->fields
);
605 // x_left, x_right, y
606 return array($table->x
, $table->x +
$table->width
, $table->y +
($pos +
1.5) * $table->heightCell
);
610 * draws relation links and arrows
611 * shows foreign key relations
613 * @param boolean changeColor Whether to use one color per relation or not
614 * @global object The current SVG image document
618 public function relationDraw($changeColor)
623 $listOfColors = array(
632 shuffle($listOfColors);
633 $color = $listOfColors[0];
638 $svg->printElementLine('line',$this->xSrc
,$this->ySrc
,
639 $this->xSrc +
$this->srcDir
* $this->wTick
,$this->ySrc
,
640 'fill:'.$color.';stroke:black;stroke-width:2;'
642 $svg->printElementLine('line',$this->xDest +
$this->destDir
* $this->wTick
, $this->yDest
,
643 $this->xDest
, $this->yDest
,
644 'fill:'.$color.';stroke:black;stroke-width:2;'
646 $svg->printElementLine('line',$this->xSrc +
$this->srcDir
* $this->wTick
,$this->ySrc
,
647 $this->xDest +
$this->destDir
* $this->wTick
, $this->yDest
,
648 'fill:'.$color.';stroke:'.$color.';stroke-width:1;'
650 $root2 = 2 * sqrt(2);
651 $svg->printElementLine('line',$this->xSrc +
$this->srcDir
* $this->wTick
* 0.75, $this->ySrc
,
652 $this->xSrc +
$this->srcDir
* (0.75 - 1 / $root2) * $this->wTick
,
653 $this->ySrc +
$this->wTick
/ $root2 ,
654 'fill:'.$color.';stroke:black;stroke-width:2;'
656 $svg->printElementLine('line',$this->xSrc +
$this->srcDir
* $this->wTick
* 0.75, $this->ySrc
,
657 $this->xSrc +
$this->srcDir
* (0.75 - 1 / $root2) * $this->wTick
,
658 $this->ySrc
- $this->wTick
/ $root2 ,
659 'fill:'.$color.';stroke:black;stroke-width:2;'
661 $svg->printElementLine('line',$this->xDest +
$this->destDir
* $this->wTick
/ 2 , $this->yDest
,
662 $this->xDest +
$this->destDir
* (0.5 +
1 / $root2) * $this->wTick
,
663 $this->yDest +
$this->wTick
/ $root2 ,
664 'fill:'.$color.';stroke:black;stroke-width:2;');
665 $svg->printElementLine('line',$this->xDest +
$this->destDir
* $this->wTick
/ 2 ,
666 $this->yDest
, $this->xDest +
$this->destDir
* (0.5 +
1 / $root2) * $this->wTick
,
667 $this->yDest
- $this->wTick
/ $root2 ,
668 'fill:'.$color.';stroke:black;stroke-width:2;'
673 * end of the "Relation_Stats" class
677 * Svg Relation Schema Class
679 * Purpose of this class is to generate the SVG XML Document because
680 * SVG defines the graphics in XML format which is used for representing
681 * the database diagrams as vector image. This class actually helps
682 * in preparing SVG XML format.
684 * SVG XML is generated by using XMLWriter php extension and this class
685 * inherits Export_Relation_Schema class has common functionality added
688 * @name Svg_Relation_Schema
690 class PMA_Svg_Relation_Schema
extends PMA_Export_Relation_Schema
693 private $tables = array();
694 private $_relations = array();
698 private $_xMin = 100000;
699 private $_yMin = 100000;
700 private $t_marg = 10;
701 private $b_marg = 10;
702 private $l_marg = 10;
703 private $r_marg = 10;
704 private $_tablewidth;
707 * The "PMA_Svg_Relation_Schema" constructor
709 * Upon instantiation This starts writing the SVG XML document
710 * user will be prompted for download as .svg extension
715 function __construct()
719 $this->setPageNumber($_POST['pdf_page_number']);
720 $this->setShowColor(isset($_POST['show_color']));
721 $this->setShowKeys(isset($_POST['show_keys']));
722 $this->setTableDimension(isset($_POST['show_table_dimension']));
723 $this->setAllTableSameWidth(isset($_POST['all_table_same_wide']));
724 $this->setExportType($_POST['export_type']);
726 $svg = new PMA_SVG();
727 $svg->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $this->pageNumber
));
728 $svg->SetAuthor('phpMyAdmin ' . PMA_VERSION
);
729 $svg->setFont('Arial');
730 $svg->setFontSize('16px');
731 $svg->startSvgDoc('1000px','1000px');
732 $alltables = $this->getAllTables($db,$this->pageNumber
);
734 foreach ($alltables AS $table) {
735 if (! isset($this->tables
[$table])) {
736 $this->tables
[$table] = new Table_Stats($table,$svg->getFont(),$svg->getFontSize(), $this->pageNumber
, $this->_tablewidth
, $this->showKeys
, $this->tableDimension
);
739 if ($this->sameWide
) {
740 $this->tables
[$table]->width
= $this->_tablewidth
;
742 $this->_setMinMax($this->tables
[$table]);
744 $seen_a_relation = false;
745 foreach ($alltables as $one_table) {
746 $exist_rel = PMA_getForeigners($db, $one_table, '', 'both');
748 $seen_a_relation = true;
749 foreach ($exist_rel as $master_field => $rel) {
750 /* put the foreign table on the schema only if selected
752 * (do not use array_search() because we would have to
753 * to do a === false and this is not PHP3 compatible)
755 if (in_array($rel['foreign_table'], $alltables)) {
756 $this->_addRelation($one_table,$svg->getFont(),$svg->getFontSize(), $master_field, $rel['foreign_table'], $rel['foreign_field'], $this->tableDimension
);
761 if ($seen_a_relation) {
762 $this->_drawRelations($this->showColor
);
765 $this->_drawTables($this->showColor
);
767 $svg->showOutput($db.'-'.$this->pageNumber
);
772 * Sets X and Y minimum and maximum for a table cell
774 * @param string table The table name
777 private function _setMinMax($table)
779 $this->_xMax
= max($this->_xMax
, $table->x +
$table->width
);
780 $this->_yMax
= max($this->_yMax
, $table->y +
$table->height
);
781 $this->_xMin
= min($this->_xMin
, $table->x
);
782 $this->_yMin
= min($this->_yMin
, $table->y
);
786 * Defines relation objects
788 * @param string masterTable The master table name
789 * @param string masterField The relation field in the master table
790 * @param string foreignTable The foreign table name
791 * @param string foreignField The relation field in the foreign table
792 * @param boolean showInfo Whether to display table position or not
794 * @see _setMinMax,Table_Stats::__construct(),Relation_Stats::__construct()
796 private function _addRelation($masterTable,$font,$fontSize, $masterField, $foreignTable, $foreignField, $showInfo)
798 if (! isset($this->tables
[$masterTable])) {
799 $this->tables
[$masterTable] = new Table_Stats($masterTable, $font, $fontSize, $this->pageNumber
, $this->_tablewidth
, false, $showInfo);
800 $this->_setMinMax($this->tables
[$masterTable]);
802 if (! isset($this->tables
[$foreignTable])) {
803 $this->tables
[$foreignTable] = new Table_Stats($foreignTable,$font,$fontSize,$this->pageNumber
, $this->_tablewidth
, false, $showInfo);
804 $this->_setMinMax($this->tables
[$foreignTable]);
806 $this->_relations
[] = new Relation_Stats($this->tables
[$masterTable], $masterField, $this->tables
[$foreignTable], $foreignField);
810 * Draws relation arrows and lines
811 * connects master table's master field to
812 * foreign table's forein field
814 * @param boolean changeColor Whether to use one color per relation or not
816 * @see Relation_Stats::relationDraw()
818 private function _drawRelations($changeColor)
820 foreach ($this->_relations
as $relation) {
821 $relation->relationDraw($changeColor);
828 * @param boolean changeColor Whether to show color for primary fields or not
830 * @see Table_Stats::Table_Stats_tableDraw()
832 private function _drawTables($changeColor)
834 foreach ($this->tables
as $table) {
835 $table->tableDraw($changeColor);