108f248e4SAndreas Gohr<?php 2*18622736SAndreas Gohr 308f248e4SAndreas Gohr/** 408f248e4SAndreas Gohr * EasySVG - Generate SVG from PHP 508f248e4SAndreas Gohr * @author Simon Tarchichi <kartsims@gmail.com> 608f248e4SAndreas Gohr * @version 0.1b 708f248e4SAndreas Gohr * 808f248e4SAndreas Gohr * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform 908f248e4SAndreas Gohr * @see http://stackoverflow.com/questions/14684846/flattening-svg-matrix-transforms-in-inkscape 1008f248e4SAndreas Gohr * @see http://stackoverflow.com/questions/7742148/how-to-convert-text-to-svg-paths 1108f248e4SAndreas Gohr */ 12*18622736SAndreas Gohrclass EasySVG 13*18622736SAndreas Gohr{ 1408f248e4SAndreas Gohr 1508f248e4SAndreas Gohr protected $font; 1608f248e4SAndreas Gohr protected $svg; 1708f248e4SAndreas Gohr 18*18622736SAndreas Gohr public function __construct() 19*18622736SAndreas Gohr { 2008f248e4SAndreas Gohr // default font data 2108f248e4SAndreas Gohr $this->font = new stdClass; 2208f248e4SAndreas Gohr $this->font->id = ''; 2308f248e4SAndreas Gohr $this->font->horizAdvX = 0; 2408f248e4SAndreas Gohr $this->font->unitsPerEm = 0; 2508f248e4SAndreas Gohr $this->font->ascent = 0; 2608f248e4SAndreas Gohr $this->font->descent = 0; 2708f248e4SAndreas Gohr $this->font->glyphs = array(); 2808f248e4SAndreas Gohr $this->font->size = 20; 2908f248e4SAndreas Gohr $this->font->color = ''; 3008f248e4SAndreas Gohr $this->font->lineHeight = 1; 3108f248e4SAndreas Gohr $this->font->letterSpacing = 0; 3208f248e4SAndreas Gohr 3308f248e4SAndreas Gohr $this->clearSVG(); 3408f248e4SAndreas Gohr } 3508f248e4SAndreas Gohr 36*18622736SAndreas Gohr public function clearSVG() 37*18622736SAndreas Gohr { 3808f248e4SAndreas Gohr $this->svg = new SimpleXMLElement('<svg></svg>'); 3908f248e4SAndreas Gohr } 4008f248e4SAndreas Gohr 4108f248e4SAndreas Gohr /** 4208f248e4SAndreas Gohr * Function takes UTF-8 encoded string and returns unicode number for every character. 4308f248e4SAndreas Gohr * @param string $str 4408f248e4SAndreas Gohr * @return string 4508f248e4SAndreas Gohr */ 46*18622736SAndreas Gohr private function _utf8ToUnicode($str) 47*18622736SAndreas Gohr { 4808f248e4SAndreas Gohr $unicode = array(); 4908f248e4SAndreas Gohr $values = array(); 5008f248e4SAndreas Gohr $lookingFor = 1; 5108f248e4SAndreas Gohr 5208f248e4SAndreas Gohr for ($i = 0; $i < strlen($str); $i++) { 5308f248e4SAndreas Gohr $thisValue = ord($str[$i]); 54*18622736SAndreas Gohr if ($thisValue < 128) { 55*18622736SAndreas Gohr $unicode[] = $thisValue; 56*18622736SAndreas Gohr } else { 5708f248e4SAndreas Gohr if (count($values) == 0) $lookingFor = ($thisValue < 224) ? 2 : 3; 5808f248e4SAndreas Gohr $values[] = $thisValue; 5908f248e4SAndreas Gohr if (count($values) == $lookingFor) { 6008f248e4SAndreas Gohr $number = ($lookingFor == 3) ? 6108f248e4SAndreas Gohr (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64) : 6208f248e4SAndreas Gohr (($values[0] % 32) * 64) + ($values[1] % 64); 6308f248e4SAndreas Gohr 6408f248e4SAndreas Gohr $unicode[] = $number; 6508f248e4SAndreas Gohr $values = array(); 6608f248e4SAndreas Gohr $lookingFor = 1; 6708f248e4SAndreas Gohr } 6808f248e4SAndreas Gohr } 6908f248e4SAndreas Gohr } 7008f248e4SAndreas Gohr 7108f248e4SAndreas Gohr return $unicode; 7208f248e4SAndreas Gohr } 7308f248e4SAndreas Gohr 7408f248e4SAndreas Gohr /** 7508f248e4SAndreas Gohr * Set font params (short-hand method) 7608f248e4SAndreas Gohr * @param string $filepath 7708f248e4SAndreas Gohr * @param integer $size 7808f248e4SAndreas Gohr * @param string $color 7908f248e4SAndreas Gohr */ 80*18622736SAndreas Gohr public function setFont($filepath, $size, $color) 81*18622736SAndreas Gohr { 8208f248e4SAndreas Gohr $this->setFontSVG($filepath); 8308f248e4SAndreas Gohr $this->setFontSize($size); 8408f248e4SAndreas Gohr $this->setFontColor($color); 8508f248e4SAndreas Gohr } 8608f248e4SAndreas Gohr 8708f248e4SAndreas Gohr /** 8808f248e4SAndreas Gohr * Set font size for display 8908f248e4SAndreas Gohr * @param int $size 9008f248e4SAndreas Gohr * @return void 9108f248e4SAndreas Gohr */ 92*18622736SAndreas Gohr public function setFontSize($size) 93*18622736SAndreas Gohr { 9408f248e4SAndreas Gohr $this->font->size = $size; 9508f248e4SAndreas Gohr } 9608f248e4SAndreas Gohr 9708f248e4SAndreas Gohr /** 9808f248e4SAndreas Gohr * Set font color 9908f248e4SAndreas Gohr * @param string $color 10008f248e4SAndreas Gohr * @return void 10108f248e4SAndreas Gohr */ 102*18622736SAndreas Gohr public function setFontColor($color) 103*18622736SAndreas Gohr { 10408f248e4SAndreas Gohr $this->font->color = $color; 10508f248e4SAndreas Gohr } 10608f248e4SAndreas Gohr 10708f248e4SAndreas Gohr /** 10808f248e4SAndreas Gohr * Set the line height from default (1) to custom value 10908f248e4SAndreas Gohr * @param float $value 11008f248e4SAndreas Gohr * @return void 11108f248e4SAndreas Gohr */ 112*18622736SAndreas Gohr public function setLineHeight($value) 113*18622736SAndreas Gohr { 11408f248e4SAndreas Gohr $this->font->lineHeight = $value; 11508f248e4SAndreas Gohr } 11608f248e4SAndreas Gohr 11708f248e4SAndreas Gohr /** 11808f248e4SAndreas Gohr * Set the letter spacing from default (0) to custom value 11908f248e4SAndreas Gohr * @param float $value 12008f248e4SAndreas Gohr * @return void 12108f248e4SAndreas Gohr */ 122*18622736SAndreas Gohr public function setLetterSpacing($value) 123*18622736SAndreas Gohr { 12408f248e4SAndreas Gohr $this->font->letterSpacing = $value; 12508f248e4SAndreas Gohr } 12608f248e4SAndreas Gohr 12708f248e4SAndreas Gohr /** 12808f248e4SAndreas Gohr * Function takes path to SVG font (local path) and processes its xml 12908f248e4SAndreas Gohr * to get path representation of every character and additional 13008f248e4SAndreas Gohr * font parameters 13108f248e4SAndreas Gohr * @param string $filepath 13208f248e4SAndreas Gohr * @return void 13308f248e4SAndreas Gohr */ 134*18622736SAndreas Gohr public function setFontSVG($filepath) 135*18622736SAndreas Gohr { 13608f248e4SAndreas Gohr $this->font->glyphs = array(); 13708f248e4SAndreas Gohr $z = new XMLReader; 13808f248e4SAndreas Gohr $z->open($filepath); 13908f248e4SAndreas Gohr 14008f248e4SAndreas Gohr // move to the first <product /> node 14108f248e4SAndreas Gohr while ($z->read()) { 14208f248e4SAndreas Gohr $name = $z->name; 14308f248e4SAndreas Gohr 14408f248e4SAndreas Gohr if ($z->nodeType == XMLReader::ELEMENT) { 14508f248e4SAndreas Gohr if ($name == 'font') { 14608f248e4SAndreas Gohr $this->font->id = $z->getAttribute('id'); 14708f248e4SAndreas Gohr $this->font->horizAdvX = $z->getAttribute('horiz-adv-x'); 14808f248e4SAndreas Gohr } 14908f248e4SAndreas Gohr 15008f248e4SAndreas Gohr if ($name == 'font-face') { 15108f248e4SAndreas Gohr $this->font->unitsPerEm = $z->getAttribute('units-per-em'); 15208f248e4SAndreas Gohr $this->font->ascent = $z->getAttribute('ascent'); 15308f248e4SAndreas Gohr $this->font->descent = $z->getAttribute('descent'); 15408f248e4SAndreas Gohr } 15508f248e4SAndreas Gohr 15608f248e4SAndreas Gohr if ($name == 'glyph') { 15708f248e4SAndreas Gohr $unicode = $z->getAttribute('unicode'); 15808f248e4SAndreas Gohr $unicode = $this->_utf8ToUnicode($unicode); 15908f248e4SAndreas Gohr 16008f248e4SAndreas Gohr if (isset($unicode[0])) { 16108f248e4SAndreas Gohr $unicode = $unicode[0]; 16208f248e4SAndreas Gohr 16308f248e4SAndreas Gohr $this->font->glyphs[$unicode] = new stdClass(); 16408f248e4SAndreas Gohr $this->font->glyphs[$unicode]->horizAdvX = $z->getAttribute('horiz-adv-x'); 16508f248e4SAndreas Gohr if (empty($this->font->glyphs[$unicode]->horizAdvX)) { 16608f248e4SAndreas Gohr $this->font->glyphs[$unicode]->horizAdvX = $this->font->horizAdvX; 16708f248e4SAndreas Gohr } 16808f248e4SAndreas Gohr $this->font->glyphs[$unicode]->d = $z->getAttribute('d'); 16908f248e4SAndreas Gohr 17008f248e4SAndreas Gohr // save em value for letter spacing (109 is unicode for the letter 'm') 17108f248e4SAndreas Gohr if ($unicode == '109') { 17208f248e4SAndreas Gohr $this->font->em = $this->font->glyphs[$unicode]->horizAdvX; 17308f248e4SAndreas Gohr } 17408f248e4SAndreas Gohr } 17508f248e4SAndreas Gohr } 17608f248e4SAndreas Gohr } 17708f248e4SAndreas Gohr } 17808f248e4SAndreas Gohr } 17908f248e4SAndreas Gohr 18008f248e4SAndreas Gohr /** 18108f248e4SAndreas Gohr * Add a path to the SVG 18208f248e4SAndreas Gohr * @param string $def 18308f248e4SAndreas Gohr * @param array $attributes 18408f248e4SAndreas Gohr * @return SimpleXMLElement 18508f248e4SAndreas Gohr */ 186*18622736SAndreas Gohr public function addPath($def, $attributes = array()) 187*18622736SAndreas Gohr { 18808f248e4SAndreas Gohr $path = $this->svg->addChild('path'); 18908f248e4SAndreas Gohr foreach ($attributes as $key => $value) { 19008f248e4SAndreas Gohr $path->addAttribute($key, $value); 19108f248e4SAndreas Gohr } 19208f248e4SAndreas Gohr $path->addAttribute('d', $def); 19308f248e4SAndreas Gohr return $path; 19408f248e4SAndreas Gohr } 19508f248e4SAndreas Gohr 19608f248e4SAndreas Gohr /** 19708f248e4SAndreas Gohr * Add a text to the SVG 19808f248e4SAndreas Gohr * @param string $def 19908f248e4SAndreas Gohr * @param float $x 20008f248e4SAndreas Gohr * @param float $y 20108f248e4SAndreas Gohr * @param array $attributes 20208f248e4SAndreas Gohr * @return SimpleXMLElement 20308f248e4SAndreas Gohr */ 204*18622736SAndreas Gohr public function addText($text, $x = 0, $y = 0, $attributes = array()) 205*18622736SAndreas Gohr { 20608f248e4SAndreas Gohr $def = $this->textDef($text); 20708f248e4SAndreas Gohr 20808f248e4SAndreas Gohr if ($x != 0 || $y != 0) { 20908f248e4SAndreas Gohr $def = $this->defTranslate($def, $x, $y); 21008f248e4SAndreas Gohr } 21108f248e4SAndreas Gohr 21208f248e4SAndreas Gohr if ($this->font->color) { 21308f248e4SAndreas Gohr $attributes['fill'] = $this->font->color; 21408f248e4SAndreas Gohr } 21508f248e4SAndreas Gohr 21608f248e4SAndreas Gohr return $this->addPath($def, $attributes); 21708f248e4SAndreas Gohr } 21808f248e4SAndreas Gohr 21908f248e4SAndreas Gohr /** 22008f248e4SAndreas Gohr * Function takes UTF-8 encoded string and size, returns xml for SVG paths representing this string. 22108f248e4SAndreas Gohr * @param string $text UTF-8 encoded text 22208f248e4SAndreas Gohr * @return string xml for text converted into SVG paths 22308f248e4SAndreas Gohr */ 224*18622736SAndreas Gohr public function textDef($text) 225*18622736SAndreas Gohr { 22608f248e4SAndreas Gohr $def = array(); 22708f248e4SAndreas Gohr 22808f248e4SAndreas Gohr $horizAdvX = 0; 22908f248e4SAndreas Gohr $horizAdvY = $this->font->ascent + $this->font->descent; 23008f248e4SAndreas Gohr $fontSize = floatval($this->font->size) / $this->font->unitsPerEm; 23108f248e4SAndreas Gohr $text = $this->_utf8ToUnicode($text); 23208f248e4SAndreas Gohr 23308f248e4SAndreas Gohr for ($i = 0; $i < count($text); $i++) { 23408f248e4SAndreas Gohr 23508f248e4SAndreas Gohr $letter = $text[$i]; 23608f248e4SAndreas Gohr 23708f248e4SAndreas Gohr // line break support (10 is unicode for linebreak) 23808f248e4SAndreas Gohr if ($letter == 10) { 23908f248e4SAndreas Gohr $horizAdvX = 0; 24008f248e4SAndreas Gohr $horizAdvY += $this->font->lineHeight * ($this->font->ascent + $this->font->descent); 24108f248e4SAndreas Gohr continue; 24208f248e4SAndreas Gohr } 24308f248e4SAndreas Gohr 24408f248e4SAndreas Gohr // extract character definition 24508f248e4SAndreas Gohr $d = $this->font->glyphs[$letter]->d; 24608f248e4SAndreas Gohr 24708f248e4SAndreas Gohr // transform typo from original SVG format to straight display 24808f248e4SAndreas Gohr $d = $this->defScale($d, $fontSize, -$fontSize); 24908f248e4SAndreas Gohr $d = $this->defTranslate($d, $horizAdvX, $horizAdvY * $fontSize * 2); 25008f248e4SAndreas Gohr 25108f248e4SAndreas Gohr $def[] = $d; 25208f248e4SAndreas Gohr 25308f248e4SAndreas Gohr // next letter's position 25408f248e4SAndreas Gohr $horizAdvX += $this->font->glyphs[$letter]->horizAdvX * $fontSize + $this->font->em * $this->font->letterSpacing * $fontSize; 25508f248e4SAndreas Gohr } 25608f248e4SAndreas Gohr return implode(' ', $def); 25708f248e4SAndreas Gohr } 25808f248e4SAndreas Gohr 25908f248e4SAndreas Gohr /** 26008f248e4SAndreas Gohr * Function takes UTF-8 encoded string and size, returns width and height of the whole text 26108f248e4SAndreas Gohr * @param string $text UTF-8 encoded text 26208f248e4SAndreas Gohr * @return array ($width, $height) 26308f248e4SAndreas Gohr */ 264*18622736SAndreas Gohr public function textDimensions($text) 265*18622736SAndreas Gohr { 26608f248e4SAndreas Gohr $def = array(); 26708f248e4SAndreas Gohr 26808f248e4SAndreas Gohr $fontSize = floatval($this->font->size) / $this->font->unitsPerEm; 26908f248e4SAndreas Gohr $text = $this->_utf8ToUnicode($text); 27008f248e4SAndreas Gohr 27108f248e4SAndreas Gohr $lineWidth = 0; 27208f248e4SAndreas Gohr $lineHeight = ($this->font->ascent + $this->font->descent) * $fontSize * 2; 27308f248e4SAndreas Gohr 27408f248e4SAndreas Gohr $width = 0; 27508f248e4SAndreas Gohr $height = $lineHeight; 27608f248e4SAndreas Gohr 27708f248e4SAndreas Gohr for ($i = 0; $i < count($text); $i++) { 27808f248e4SAndreas Gohr 27908f248e4SAndreas Gohr $letter = $text[$i]; 28008f248e4SAndreas Gohr 28108f248e4SAndreas Gohr // line break support (10 is unicode for linebreak) 28208f248e4SAndreas Gohr if ($letter == 10) { 28308f248e4SAndreas Gohr $width = $lineWidth > $width ? $lineWidth : $width; 28408f248e4SAndreas Gohr $height += $lineHeight * $this->font->lineHeight; 28508f248e4SAndreas Gohr $lineWidth = 0; 28608f248e4SAndreas Gohr continue; 28708f248e4SAndreas Gohr } 28808f248e4SAndreas Gohr 28908f248e4SAndreas Gohr $lineWidth += $this->font->glyphs[$letter]->horizAdvX * $fontSize + $this->font->em * $this->font->letterSpacing * $fontSize; 29008f248e4SAndreas Gohr } 29108f248e4SAndreas Gohr 29208f248e4SAndreas Gohr // only keep the widest line's width 29308f248e4SAndreas Gohr $width = $lineWidth > $width ? $lineWidth : $width; 29408f248e4SAndreas Gohr 29508f248e4SAndreas Gohr return array($width, $height); 29608f248e4SAndreas Gohr } 29708f248e4SAndreas Gohr 29808f248e4SAndreas Gohr /** 29908f248e4SAndreas Gohr * Function takes unicode character and returns the UTF-8 equivalent 30008f248e4SAndreas Gohr * @param string $str 30108f248e4SAndreas Gohr * @return string 30208f248e4SAndreas Gohr */ 303*18622736SAndreas Gohr public function unicodeDef($unicode) 304*18622736SAndreas Gohr { 30508f248e4SAndreas Gohr 30608f248e4SAndreas Gohr $horizAdvY = $this->font->ascent + $this->font->descent; 30708f248e4SAndreas Gohr $fontSize = floatval($this->font->size) / $this->font->unitsPerEm; 30808f248e4SAndreas Gohr 30908f248e4SAndreas Gohr // extract character definition 31008f248e4SAndreas Gohr $d = $this->font->glyphs[hexdec($unicode)]->d; 31108f248e4SAndreas Gohr 31208f248e4SAndreas Gohr // transform typo from original SVG format to straight display 31308f248e4SAndreas Gohr $d = $this->defScale($d, $fontSize, -$fontSize); 31408f248e4SAndreas Gohr $d = $this->defTranslate($d, 0, $horizAdvY * $fontSize * 2); 31508f248e4SAndreas Gohr 31608f248e4SAndreas Gohr return $d; 31708f248e4SAndreas Gohr } 31808f248e4SAndreas Gohr 31908f248e4SAndreas Gohr /** 32008f248e4SAndreas Gohr * Returns the character width, as set in the font file 32108f248e4SAndreas Gohr * @param string $str 32208f248e4SAndreas Gohr * @param boolean $is_unicode 32308f248e4SAndreas Gohr * @return float 32408f248e4SAndreas Gohr */ 325*18622736SAndreas Gohr public function characterWidth($char, $is_unicode = false) 326*18622736SAndreas Gohr { 32708f248e4SAndreas Gohr if ($is_unicode) { 32808f248e4SAndreas Gohr $letter = hexdec($char); 329*18622736SAndreas Gohr } else { 33008f248e4SAndreas Gohr $letter = $this->_utf8ToUnicode($char); 33108f248e4SAndreas Gohr } 33208f248e4SAndreas Gohr 333*18622736SAndreas Gohr if (!isset($this->font->glyphs[$letter])) { 334*18622736SAndreas Gohr return null; 335*18622736SAndreas Gohr } 33608f248e4SAndreas Gohr 33708f248e4SAndreas Gohr $fontSize = floatval($this->font->size) / $this->font->unitsPerEm; 33808f248e4SAndreas Gohr return $this->font->glyphs[$letter]->horizAdvX * $fontSize; 33908f248e4SAndreas Gohr } 34008f248e4SAndreas Gohr 34108f248e4SAndreas Gohr /** 34208f248e4SAndreas Gohr * Applies a translate transformation to definition 34308f248e4SAndreas Gohr * @param string $def definition 34408f248e4SAndreas Gohr * @param float $x 34508f248e4SAndreas Gohr * @param float $y 34608f248e4SAndreas Gohr * @return string 34708f248e4SAndreas Gohr */ 348*18622736SAndreas Gohr public function defTranslate($def, $x = 0, $y = 0) 349*18622736SAndreas Gohr { 35008f248e4SAndreas Gohr return $this->defApplyMatrix($def, array(1, 0, 0, 1, $x, $y)); 35108f248e4SAndreas Gohr } 35208f248e4SAndreas Gohr 35308f248e4SAndreas Gohr /** 35408f248e4SAndreas Gohr * Applies a translate transformation to definition 35508f248e4SAndreas Gohr * @param string $def Definition 35608f248e4SAndreas Gohr * @param integer $angle Rotation angle (degrees) 35708f248e4SAndreas Gohr * @param integer $x X coordinate of rotation center 35808f248e4SAndreas Gohr * @param integer $y Y coordinate of rotation center 35908f248e4SAndreas Gohr * @return string 36008f248e4SAndreas Gohr */ 361*18622736SAndreas Gohr public function defRotate($def, $angle, $x = 0, $y = 0) 362*18622736SAndreas Gohr { 36308f248e4SAndreas Gohr if ($x == 0 && $y == 0) { 36408f248e4SAndreas Gohr $angle = deg2rad($angle); 36508f248e4SAndreas Gohr return $this->defApplyMatrix($def, array(cos($angle), sin($angle), -sin($angle), cos($angle), 0, 0)); 36608f248e4SAndreas Gohr } 36708f248e4SAndreas Gohr 36808f248e4SAndreas Gohr // rotate by a given point 36908f248e4SAndreas Gohr $def = $this->defTranslate($def, $x, $y); 37008f248e4SAndreas Gohr $def = $this->defRotate($def, $angle); 37108f248e4SAndreas Gohr $def = $this->defTranslate($def, -$x, -$y); 37208f248e4SAndreas Gohr return $def; 37308f248e4SAndreas Gohr } 37408f248e4SAndreas Gohr 37508f248e4SAndreas Gohr /** 37608f248e4SAndreas Gohr * Applies a scale transformation to definition 37708f248e4SAndreas Gohr * @param string $def definition 37808f248e4SAndreas Gohr * @param integer $x 37908f248e4SAndreas Gohr * @param integer $y 38008f248e4SAndreas Gohr * @return string 38108f248e4SAndreas Gohr */ 382*18622736SAndreas Gohr public function defScale($def, $x = 1, $y = 1) 383*18622736SAndreas Gohr { 38408f248e4SAndreas Gohr return $this->defApplyMatrix($def, array($x, 0, 0, $y, 0, 0)); 38508f248e4SAndreas Gohr } 38608f248e4SAndreas Gohr 38708f248e4SAndreas Gohr /** 38808f248e4SAndreas Gohr * Calculates the new definition with the matrix applied 38908f248e4SAndreas Gohr * @param string $def 39008f248e4SAndreas Gohr * @param array $matrix 39108f248e4SAndreas Gohr * @return string 39208f248e4SAndreas Gohr */ 393*18622736SAndreas Gohr public function defApplyMatrix($def, $matrix) 394*18622736SAndreas Gohr { 39508f248e4SAndreas Gohr 39608f248e4SAndreas Gohr // if there are several shapes in this definition, do the operation for each 39708f248e4SAndreas Gohr preg_match_all('/M[^zZ]*[zZ]/', $def, $shapes); 39808f248e4SAndreas Gohr $shapes = $shapes[0]; 39908f248e4SAndreas Gohr if (count($shapes) > 1) { 400*18622736SAndreas Gohr foreach ($shapes as &$shape) { 40108f248e4SAndreas Gohr $shape = $this->defApplyMatrix($shape, $matrix); 402*18622736SAndreas Gohr } 40308f248e4SAndreas Gohr return implode(' ', $shapes); 40408f248e4SAndreas Gohr } 40508f248e4SAndreas Gohr 40608f248e4SAndreas Gohr preg_match_all('/[a-zA-Z]+[^a-zA-Z]*/', $def, $instructions); 40708f248e4SAndreas Gohr $instructions = $instructions[0]; 40808f248e4SAndreas Gohr 40908f248e4SAndreas Gohr $return = ''; 41008f248e4SAndreas Gohr foreach ($instructions as &$instruction) { 41108f248e4SAndreas Gohr $i = preg_replace('/[^a-zA-Z]*/', '', $instruction); 41208f248e4SAndreas Gohr preg_match_all('/\-?[0-9\.]+/', $instruction, $coords); 41308f248e4SAndreas Gohr $coords = $coords[0]; 41408f248e4SAndreas Gohr 41508f248e4SAndreas Gohr if (empty($coords)) { 41608f248e4SAndreas Gohr continue; 41708f248e4SAndreas Gohr } 41808f248e4SAndreas Gohr 41908f248e4SAndreas Gohr $new_coords = array(); 42008f248e4SAndreas Gohr while (count($coords) > 0) { 42108f248e4SAndreas Gohr 42208f248e4SAndreas Gohr // do the matrix calculation stuff 42308f248e4SAndreas Gohr list($a, $b, $c, $d, $e, $f) = $matrix; 42408f248e4SAndreas Gohr 42508f248e4SAndreas Gohr // exception for relative instruction 42608f248e4SAndreas Gohr if (preg_match('/[a-z]/', $i)) { 42708f248e4SAndreas Gohr $e = 0; 42808f248e4SAndreas Gohr $f = 0; 42908f248e4SAndreas Gohr } 43008f248e4SAndreas Gohr 43108f248e4SAndreas Gohr // convert horizontal lineto (relative) 43208f248e4SAndreas Gohr if ($i == 'h') { 43308f248e4SAndreas Gohr $i = 'l'; 43408f248e4SAndreas Gohr $x = floatval(array_shift($coords)); 43508f248e4SAndreas Gohr $y = 0; 43608f248e4SAndreas Gohr 43708f248e4SAndreas Gohr // add new point's coordinates 43808f248e4SAndreas Gohr $current_point = array( 43908f248e4SAndreas Gohr $a * $x + $c * $y + $e, 44008f248e4SAndreas Gohr $b * $x + $d * $y + $f, 44108f248e4SAndreas Gohr ); 44208f248e4SAndreas Gohr $new_coords = array_merge($new_coords, $current_point); 443*18622736SAndreas Gohr } // convert vertical lineto (relative) 44408f248e4SAndreas Gohr elseif ($i == 'v') { 44508f248e4SAndreas Gohr $i = 'l'; 44608f248e4SAndreas Gohr $x = 0; 44708f248e4SAndreas Gohr $y = floatval(array_shift($coords)); 44808f248e4SAndreas Gohr 44908f248e4SAndreas Gohr // add new point's coordinates 45008f248e4SAndreas Gohr $current_point = array( 45108f248e4SAndreas Gohr $a * $x + $c * $y + $e, 45208f248e4SAndreas Gohr $b * $x + $d * $y + $f, 45308f248e4SAndreas Gohr ); 45408f248e4SAndreas Gohr $new_coords = array_merge($new_coords, $current_point); 455*18622736SAndreas Gohr } // convert quadratic bezier curve (relative) 45608f248e4SAndreas Gohr elseif ($i == 'q') { 45708f248e4SAndreas Gohr $x = floatval(array_shift($coords)); 45808f248e4SAndreas Gohr $y = floatval(array_shift($coords)); 45908f248e4SAndreas Gohr 46008f248e4SAndreas Gohr // add new point's coordinates 46108f248e4SAndreas Gohr $current_point = array( 46208f248e4SAndreas Gohr $a * $x + $c * $y + $e, 46308f248e4SAndreas Gohr $b * $x + $d * $y + $f, 46408f248e4SAndreas Gohr ); 46508f248e4SAndreas Gohr $new_coords = array_merge($new_coords, $current_point); 46608f248e4SAndreas Gohr 46708f248e4SAndreas Gohr // same for 2nd point 46808f248e4SAndreas Gohr $x = floatval(array_shift($coords)); 46908f248e4SAndreas Gohr $y = floatval(array_shift($coords)); 47008f248e4SAndreas Gohr 47108f248e4SAndreas Gohr // add new point's coordinates 47208f248e4SAndreas Gohr $current_point = array( 47308f248e4SAndreas Gohr $a * $x + $c * $y + $e, 47408f248e4SAndreas Gohr $b * $x + $d * $y + $f, 47508f248e4SAndreas Gohr ); 47608f248e4SAndreas Gohr $new_coords = array_merge($new_coords, $current_point); 47708f248e4SAndreas Gohr } 47808f248e4SAndreas Gohr 47908f248e4SAndreas Gohr // every other commands 48008f248e4SAndreas Gohr // @TODO: handle 'a,c,s' (elliptic arc curve) commands 48108f248e4SAndreas Gohr // cf. http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands 48208f248e4SAndreas Gohr else { 48308f248e4SAndreas Gohr $x = floatval(array_shift($coords)); 48408f248e4SAndreas Gohr $y = floatval(array_shift($coords)); 48508f248e4SAndreas Gohr 48608f248e4SAndreas Gohr // add new point's coordinates 48708f248e4SAndreas Gohr $current_point = array( 48808f248e4SAndreas Gohr $a * $x + $c * $y + $e, 48908f248e4SAndreas Gohr $b * $x + $d * $y + $f, 49008f248e4SAndreas Gohr ); 49108f248e4SAndreas Gohr $new_coords = array_merge($new_coords, $current_point); 49208f248e4SAndreas Gohr } 49308f248e4SAndreas Gohr 49408f248e4SAndreas Gohr } 49508f248e4SAndreas Gohr 49608f248e4SAndreas Gohr $instruction = $i . implode(',', $new_coords); 49708f248e4SAndreas Gohr 49808f248e4SAndreas Gohr // remove useless commas 49908f248e4SAndreas Gohr $instruction = preg_replace('/,\-/', '-', $instruction); 50008f248e4SAndreas Gohr } 50108f248e4SAndreas Gohr 50208f248e4SAndreas Gohr return implode('', $instructions); 50308f248e4SAndreas Gohr } 50408f248e4SAndreas Gohr 50508f248e4SAndreas Gohr 50608f248e4SAndreas Gohr 50708f248e4SAndreas Gohr /** 50808f248e4SAndreas Gohr * 50908f248e4SAndreas Gohr * Short-hand methods 51008f248e4SAndreas Gohr * 51108f248e4SAndreas Gohr */ 51208f248e4SAndreas Gohr 51308f248e4SAndreas Gohr /** 51408f248e4SAndreas Gohr * Return full SVG XML 51508f248e4SAndreas Gohr * @return string 51608f248e4SAndreas Gohr */ 517*18622736SAndreas Gohr public function asXML() 518*18622736SAndreas Gohr { 51908f248e4SAndreas Gohr return $this->svg->asXML(); 52008f248e4SAndreas Gohr } 52108f248e4SAndreas Gohr 52208f248e4SAndreas Gohr /** 52308f248e4SAndreas Gohr * Adds an attribute to the SVG 52408f248e4SAndreas Gohr * @param string $key 52508f248e4SAndreas Gohr * @param string $value 52608f248e4SAndreas Gohr */ 527*18622736SAndreas Gohr public function addAttribute($key, $value) 528*18622736SAndreas Gohr { 52908f248e4SAndreas Gohr return $this->svg->addAttribute($key, $value); 53008f248e4SAndreas Gohr } 53108f248e4SAndreas Gohr} 532