1*08f248e4SAndreas Gohr<?php 2*08f248e4SAndreas Gohr/** 3*08f248e4SAndreas Gohr * EasySVG - Generate SVG from PHP 4*08f248e4SAndreas Gohr * @author Simon Tarchichi <kartsims@gmail.com> 5*08f248e4SAndreas Gohr * @version 0.1b 6*08f248e4SAndreas Gohr * 7*08f248e4SAndreas Gohr * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform 8*08f248e4SAndreas Gohr * @see http://stackoverflow.com/questions/14684846/flattening-svg-matrix-transforms-in-inkscape 9*08f248e4SAndreas Gohr * @see http://stackoverflow.com/questions/7742148/how-to-convert-text-to-svg-paths 10*08f248e4SAndreas Gohr */ 11*08f248e4SAndreas Gohrclass EasySVG { 12*08f248e4SAndreas Gohr 13*08f248e4SAndreas Gohr protected $font; 14*08f248e4SAndreas Gohr protected $svg; 15*08f248e4SAndreas Gohr 16*08f248e4SAndreas Gohr public function __construct() { 17*08f248e4SAndreas Gohr // default font data 18*08f248e4SAndreas Gohr $this->font = new stdClass; 19*08f248e4SAndreas Gohr $this->font->id = ''; 20*08f248e4SAndreas Gohr $this->font->horizAdvX = 0; 21*08f248e4SAndreas Gohr $this->font->unitsPerEm = 0; 22*08f248e4SAndreas Gohr $this->font->ascent = 0; 23*08f248e4SAndreas Gohr $this->font->descent = 0; 24*08f248e4SAndreas Gohr $this->font->glyphs = array(); 25*08f248e4SAndreas Gohr $this->font->size = 20; 26*08f248e4SAndreas Gohr $this->font->color = ''; 27*08f248e4SAndreas Gohr $this->font->lineHeight = 1; 28*08f248e4SAndreas Gohr $this->font->letterSpacing = 0; 29*08f248e4SAndreas Gohr 30*08f248e4SAndreas Gohr $this->clearSVG(); 31*08f248e4SAndreas Gohr } 32*08f248e4SAndreas Gohr 33*08f248e4SAndreas Gohr public function clearSVG() { 34*08f248e4SAndreas Gohr $this->svg = new SimpleXMLElement('<svg></svg>'); 35*08f248e4SAndreas Gohr } 36*08f248e4SAndreas Gohr 37*08f248e4SAndreas Gohr /** 38*08f248e4SAndreas Gohr * Function takes UTF-8 encoded string and returns unicode number for every character. 39*08f248e4SAndreas Gohr * @param string $str 40*08f248e4SAndreas Gohr * @return string 41*08f248e4SAndreas Gohr */ 42*08f248e4SAndreas Gohr private function _utf8ToUnicode( $str ) { 43*08f248e4SAndreas Gohr $unicode = array(); 44*08f248e4SAndreas Gohr $values = array(); 45*08f248e4SAndreas Gohr $lookingFor = 1; 46*08f248e4SAndreas Gohr 47*08f248e4SAndreas Gohr for ($i = 0; $i < strlen( $str ); $i++ ) { 48*08f248e4SAndreas Gohr $thisValue = ord( $str[ $i ] ); 49*08f248e4SAndreas Gohr if ( $thisValue < 128 ) $unicode[] = $thisValue; 50*08f248e4SAndreas Gohr else { 51*08f248e4SAndreas Gohr if ( count( $values ) == 0 ) $lookingFor = ( $thisValue < 224 ) ? 2 : 3; 52*08f248e4SAndreas Gohr $values[] = $thisValue; 53*08f248e4SAndreas Gohr if ( count( $values ) == $lookingFor ) { 54*08f248e4SAndreas Gohr $number = ( $lookingFor == 3 ) ? 55*08f248e4SAndreas Gohr ( ( $values[0] % 16 ) * 4096 ) + ( ( $values[1] % 64 ) * 64 ) + ( $values[2] % 64 ): 56*08f248e4SAndreas Gohr ( ( $values[0] % 32 ) * 64 ) + ( $values[1] % 64 ); 57*08f248e4SAndreas Gohr 58*08f248e4SAndreas Gohr $unicode[] = $number; 59*08f248e4SAndreas Gohr $values = array(); 60*08f248e4SAndreas Gohr $lookingFor = 1; 61*08f248e4SAndreas Gohr } 62*08f248e4SAndreas Gohr } 63*08f248e4SAndreas Gohr } 64*08f248e4SAndreas Gohr 65*08f248e4SAndreas Gohr return $unicode; 66*08f248e4SAndreas Gohr } 67*08f248e4SAndreas Gohr 68*08f248e4SAndreas Gohr /** 69*08f248e4SAndreas Gohr * Set font params (short-hand method) 70*08f248e4SAndreas Gohr * @param string $filepath 71*08f248e4SAndreas Gohr * @param integer $size 72*08f248e4SAndreas Gohr * @param string $color 73*08f248e4SAndreas Gohr */ 74*08f248e4SAndreas Gohr public function setFont( $filepath, $size, $color ) { 75*08f248e4SAndreas Gohr $this->setFontSVG($filepath); 76*08f248e4SAndreas Gohr $this->setFontSize($size); 77*08f248e4SAndreas Gohr $this->setFontColor($color); 78*08f248e4SAndreas Gohr } 79*08f248e4SAndreas Gohr 80*08f248e4SAndreas Gohr /** 81*08f248e4SAndreas Gohr * Set font size for display 82*08f248e4SAndreas Gohr * @param int $size 83*08f248e4SAndreas Gohr * @return void 84*08f248e4SAndreas Gohr */ 85*08f248e4SAndreas Gohr public function setFontSize( $size ) { 86*08f248e4SAndreas Gohr $this->font->size = $size; 87*08f248e4SAndreas Gohr } 88*08f248e4SAndreas Gohr 89*08f248e4SAndreas Gohr /** 90*08f248e4SAndreas Gohr * Set font color 91*08f248e4SAndreas Gohr * @param string $color 92*08f248e4SAndreas Gohr * @return void 93*08f248e4SAndreas Gohr */ 94*08f248e4SAndreas Gohr public function setFontColor( $color ) { 95*08f248e4SAndreas Gohr $this->font->color = $color; 96*08f248e4SAndreas Gohr } 97*08f248e4SAndreas Gohr 98*08f248e4SAndreas Gohr /** 99*08f248e4SAndreas Gohr * Set the line height from default (1) to custom value 100*08f248e4SAndreas Gohr * @param float $value 101*08f248e4SAndreas Gohr * @return void 102*08f248e4SAndreas Gohr */ 103*08f248e4SAndreas Gohr public function setLineHeight( $value ) { 104*08f248e4SAndreas Gohr $this->font->lineHeight = $value; 105*08f248e4SAndreas Gohr } 106*08f248e4SAndreas Gohr 107*08f248e4SAndreas Gohr /** 108*08f248e4SAndreas Gohr * Set the letter spacing from default (0) to custom value 109*08f248e4SAndreas Gohr * @param float $value 110*08f248e4SAndreas Gohr * @return void 111*08f248e4SAndreas Gohr */ 112*08f248e4SAndreas Gohr public function setLetterSpacing( $value ) { 113*08f248e4SAndreas Gohr $this->font->letterSpacing = $value; 114*08f248e4SAndreas Gohr } 115*08f248e4SAndreas Gohr 116*08f248e4SAndreas Gohr /** 117*08f248e4SAndreas Gohr * Function takes path to SVG font (local path) and processes its xml 118*08f248e4SAndreas Gohr * to get path representation of every character and additional 119*08f248e4SAndreas Gohr * font parameters 120*08f248e4SAndreas Gohr * @param string $filepath 121*08f248e4SAndreas Gohr * @return void 122*08f248e4SAndreas Gohr */ 123*08f248e4SAndreas Gohr public function setFontSVG( $filepath ) { 124*08f248e4SAndreas Gohr $this->font->glyphs = array(); 125*08f248e4SAndreas Gohr $z = new XMLReader; 126*08f248e4SAndreas Gohr $z->open($filepath); 127*08f248e4SAndreas Gohr 128*08f248e4SAndreas Gohr // move to the first <product /> node 129*08f248e4SAndreas Gohr while ($z->read()) { 130*08f248e4SAndreas Gohr $name = $z->name; 131*08f248e4SAndreas Gohr 132*08f248e4SAndreas Gohr if ($z->nodeType == XMLReader::ELEMENT) { 133*08f248e4SAndreas Gohr if ($name == 'font') { 134*08f248e4SAndreas Gohr $this->font->id = $z->getAttribute('id'); 135*08f248e4SAndreas Gohr $this->font->horizAdvX = $z->getAttribute('horiz-adv-x'); 136*08f248e4SAndreas Gohr } 137*08f248e4SAndreas Gohr 138*08f248e4SAndreas Gohr if ($name == 'font-face') { 139*08f248e4SAndreas Gohr $this->font->unitsPerEm = $z->getAttribute('units-per-em'); 140*08f248e4SAndreas Gohr $this->font->ascent = $z->getAttribute('ascent'); 141*08f248e4SAndreas Gohr $this->font->descent = $z->getAttribute('descent'); 142*08f248e4SAndreas Gohr } 143*08f248e4SAndreas Gohr 144*08f248e4SAndreas Gohr if ($name == 'glyph') { 145*08f248e4SAndreas Gohr $unicode = $z->getAttribute('unicode'); 146*08f248e4SAndreas Gohr $unicode = $this->_utf8ToUnicode($unicode); 147*08f248e4SAndreas Gohr 148*08f248e4SAndreas Gohr if (isset($unicode[0])) { 149*08f248e4SAndreas Gohr $unicode = $unicode[0]; 150*08f248e4SAndreas Gohr 151*08f248e4SAndreas Gohr $this->font->glyphs[$unicode] = new stdClass(); 152*08f248e4SAndreas Gohr $this->font->glyphs[$unicode]->horizAdvX = $z->getAttribute('horiz-adv-x'); 153*08f248e4SAndreas Gohr if (empty($this->font->glyphs[$unicode]->horizAdvX)) { 154*08f248e4SAndreas Gohr $this->font->glyphs[$unicode]->horizAdvX = $this->font->horizAdvX; 155*08f248e4SAndreas Gohr } 156*08f248e4SAndreas Gohr $this->font->glyphs[$unicode]->d = $z->getAttribute('d'); 157*08f248e4SAndreas Gohr 158*08f248e4SAndreas Gohr // save em value for letter spacing (109 is unicode for the letter 'm') 159*08f248e4SAndreas Gohr if ($unicode == '109') { 160*08f248e4SAndreas Gohr $this->font->em = $this->font->glyphs[$unicode]->horizAdvX; 161*08f248e4SAndreas Gohr } 162*08f248e4SAndreas Gohr } 163*08f248e4SAndreas Gohr } 164*08f248e4SAndreas Gohr } 165*08f248e4SAndreas Gohr } 166*08f248e4SAndreas Gohr } 167*08f248e4SAndreas Gohr 168*08f248e4SAndreas Gohr /** 169*08f248e4SAndreas Gohr * Add a path to the SVG 170*08f248e4SAndreas Gohr * @param string $def 171*08f248e4SAndreas Gohr * @param array $attributes 172*08f248e4SAndreas Gohr * @return SimpleXMLElement 173*08f248e4SAndreas Gohr */ 174*08f248e4SAndreas Gohr public function addPath($def, $attributes=array()) { 175*08f248e4SAndreas Gohr $path = $this->svg->addChild('path'); 176*08f248e4SAndreas Gohr foreach($attributes as $key=>$value){ 177*08f248e4SAndreas Gohr $path->addAttribute($key, $value); 178*08f248e4SAndreas Gohr } 179*08f248e4SAndreas Gohr $path->addAttribute('d', $def); 180*08f248e4SAndreas Gohr return $path; 181*08f248e4SAndreas Gohr } 182*08f248e4SAndreas Gohr 183*08f248e4SAndreas Gohr /** 184*08f248e4SAndreas Gohr * Add a text to the SVG 185*08f248e4SAndreas Gohr * @param string $def 186*08f248e4SAndreas Gohr * @param float $x 187*08f248e4SAndreas Gohr * @param float $y 188*08f248e4SAndreas Gohr * @param array $attributes 189*08f248e4SAndreas Gohr * @return SimpleXMLElement 190*08f248e4SAndreas Gohr */ 191*08f248e4SAndreas Gohr public function addText($text, $x=0, $y=0, $attributes=array()) { 192*08f248e4SAndreas Gohr $def = $this->textDef($text); 193*08f248e4SAndreas Gohr 194*08f248e4SAndreas Gohr if($x!=0 || $y!=0){ 195*08f248e4SAndreas Gohr $def = $this->defTranslate($def, $x, $y); 196*08f248e4SAndreas Gohr } 197*08f248e4SAndreas Gohr 198*08f248e4SAndreas Gohr if($this->font->color) { 199*08f248e4SAndreas Gohr $attributes['fill'] = $this->font->color; 200*08f248e4SAndreas Gohr } 201*08f248e4SAndreas Gohr 202*08f248e4SAndreas Gohr return $this->addPath($def, $attributes); 203*08f248e4SAndreas Gohr } 204*08f248e4SAndreas Gohr 205*08f248e4SAndreas Gohr 206*08f248e4SAndreas Gohr /** 207*08f248e4SAndreas Gohr * Function takes UTF-8 encoded string and size, returns xml for SVG paths representing this string. 208*08f248e4SAndreas Gohr * @param string $text UTF-8 encoded text 209*08f248e4SAndreas Gohr * @return string xml for text converted into SVG paths 210*08f248e4SAndreas Gohr */ 211*08f248e4SAndreas Gohr public function textDef($text) { 212*08f248e4SAndreas Gohr $def = array(); 213*08f248e4SAndreas Gohr 214*08f248e4SAndreas Gohr $horizAdvX = 0; 215*08f248e4SAndreas Gohr $horizAdvY = $this->font->ascent + $this->font->descent; 216*08f248e4SAndreas Gohr $fontSize = floatval($this->font->size) / $this->font->unitsPerEm; 217*08f248e4SAndreas Gohr $text = $this->_utf8ToUnicode($text); 218*08f248e4SAndreas Gohr 219*08f248e4SAndreas Gohr for($i = 0; $i < count($text); $i++) { 220*08f248e4SAndreas Gohr 221*08f248e4SAndreas Gohr $letter = $text[$i]; 222*08f248e4SAndreas Gohr 223*08f248e4SAndreas Gohr // line break support (10 is unicode for linebreak) 224*08f248e4SAndreas Gohr if($letter==10){ 225*08f248e4SAndreas Gohr $horizAdvX = 0; 226*08f248e4SAndreas Gohr $horizAdvY += $this->font->lineHeight * ( $this->font->ascent + $this->font->descent ); 227*08f248e4SAndreas Gohr continue; 228*08f248e4SAndreas Gohr } 229*08f248e4SAndreas Gohr 230*08f248e4SAndreas Gohr // extract character definition 231*08f248e4SAndreas Gohr $d = $this->font->glyphs[$letter]->d; 232*08f248e4SAndreas Gohr 233*08f248e4SAndreas Gohr // transform typo from original SVG format to straight display 234*08f248e4SAndreas Gohr $d = $this->defScale($d, $fontSize, -$fontSize); 235*08f248e4SAndreas Gohr $d = $this->defTranslate($d, $horizAdvX, $horizAdvY*$fontSize*2); 236*08f248e4SAndreas Gohr 237*08f248e4SAndreas Gohr $def[] = $d; 238*08f248e4SAndreas Gohr 239*08f248e4SAndreas Gohr // next letter's position 240*08f248e4SAndreas Gohr $horizAdvX += $this->font->glyphs[$letter]->horizAdvX * $fontSize + $this->font->em * $this->font->letterSpacing * $fontSize; 241*08f248e4SAndreas Gohr } 242*08f248e4SAndreas Gohr return implode(' ', $def); 243*08f248e4SAndreas Gohr } 244*08f248e4SAndreas Gohr 245*08f248e4SAndreas Gohr 246*08f248e4SAndreas Gohr /** 247*08f248e4SAndreas Gohr * Function takes UTF-8 encoded string and size, returns width and height of the whole text 248*08f248e4SAndreas Gohr * @param string $text UTF-8 encoded text 249*08f248e4SAndreas Gohr * @return array ($width, $height) 250*08f248e4SAndreas Gohr */ 251*08f248e4SAndreas Gohr public function textDimensions($text) { 252*08f248e4SAndreas Gohr $def = array(); 253*08f248e4SAndreas Gohr 254*08f248e4SAndreas Gohr $fontSize = floatval($this->font->size) / $this->font->unitsPerEm; 255*08f248e4SAndreas Gohr $text = $this->_utf8ToUnicode($text); 256*08f248e4SAndreas Gohr 257*08f248e4SAndreas Gohr $lineWidth = 0; 258*08f248e4SAndreas Gohr $lineHeight = ( $this->font->ascent + $this->font->descent ) * $fontSize * 2; 259*08f248e4SAndreas Gohr 260*08f248e4SAndreas Gohr $width = 0; 261*08f248e4SAndreas Gohr $height = $lineHeight; 262*08f248e4SAndreas Gohr 263*08f248e4SAndreas Gohr for($i = 0; $i < count($text); $i++) { 264*08f248e4SAndreas Gohr 265*08f248e4SAndreas Gohr $letter = $text[$i]; 266*08f248e4SAndreas Gohr 267*08f248e4SAndreas Gohr // line break support (10 is unicode for linebreak) 268*08f248e4SAndreas Gohr if($letter==10){ 269*08f248e4SAndreas Gohr $width = $lineWidth>$width ? $lineWidth : $width; 270*08f248e4SAndreas Gohr $height += $lineHeight * $this->font->lineHeight; 271*08f248e4SAndreas Gohr $lineWidth = 0; 272*08f248e4SAndreas Gohr continue; 273*08f248e4SAndreas Gohr } 274*08f248e4SAndreas Gohr 275*08f248e4SAndreas Gohr $lineWidth += $this->font->glyphs[$letter]->horizAdvX * $fontSize + $this->font->em * $this->font->letterSpacing * $fontSize; 276*08f248e4SAndreas Gohr } 277*08f248e4SAndreas Gohr 278*08f248e4SAndreas Gohr // only keep the widest line's width 279*08f248e4SAndreas Gohr $width = $lineWidth>$width ? $lineWidth : $width; 280*08f248e4SAndreas Gohr 281*08f248e4SAndreas Gohr return array($width, $height); 282*08f248e4SAndreas Gohr } 283*08f248e4SAndreas Gohr 284*08f248e4SAndreas Gohr 285*08f248e4SAndreas Gohr /** 286*08f248e4SAndreas Gohr * Function takes unicode character and returns the UTF-8 equivalent 287*08f248e4SAndreas Gohr * @param string $str 288*08f248e4SAndreas Gohr * @return string 289*08f248e4SAndreas Gohr */ 290*08f248e4SAndreas Gohr public function unicodeDef( $unicode ) { 291*08f248e4SAndreas Gohr 292*08f248e4SAndreas Gohr $horizAdvY = $this->font->ascent + $this->font->descent; 293*08f248e4SAndreas Gohr $fontSize = floatval($this->font->size) / $this->font->unitsPerEm; 294*08f248e4SAndreas Gohr 295*08f248e4SAndreas Gohr // extract character definition 296*08f248e4SAndreas Gohr $d = $this->font->glyphs[hexdec($unicode)]->d; 297*08f248e4SAndreas Gohr 298*08f248e4SAndreas Gohr // transform typo from original SVG format to straight display 299*08f248e4SAndreas Gohr $d = $this->defScale($d, $fontSize, -$fontSize); 300*08f248e4SAndreas Gohr $d = $this->defTranslate($d, 0, $horizAdvY*$fontSize*2); 301*08f248e4SAndreas Gohr 302*08f248e4SAndreas Gohr return $d; 303*08f248e4SAndreas Gohr } 304*08f248e4SAndreas Gohr 305*08f248e4SAndreas Gohr /** 306*08f248e4SAndreas Gohr * Returns the character width, as set in the font file 307*08f248e4SAndreas Gohr * @param string $str 308*08f248e4SAndreas Gohr * @param boolean $is_unicode 309*08f248e4SAndreas Gohr * @return float 310*08f248e4SAndreas Gohr */ 311*08f248e4SAndreas Gohr public function characterWidth( $char, $is_unicode = false ) { 312*08f248e4SAndreas Gohr if ($is_unicode){ 313*08f248e4SAndreas Gohr $letter = hexdec($char); 314*08f248e4SAndreas Gohr } 315*08f248e4SAndreas Gohr else { 316*08f248e4SAndreas Gohr $letter = $this->_utf8ToUnicode($char); 317*08f248e4SAndreas Gohr } 318*08f248e4SAndreas Gohr 319*08f248e4SAndreas Gohr if (!isset($this->font->glyphs[$letter])) 320*08f248e4SAndreas Gohr return NULL; 321*08f248e4SAndreas Gohr 322*08f248e4SAndreas Gohr $fontSize = floatval($this->font->size) / $this->font->unitsPerEm; 323*08f248e4SAndreas Gohr return $this->font->glyphs[$letter]->horizAdvX * $fontSize; 324*08f248e4SAndreas Gohr } 325*08f248e4SAndreas Gohr 326*08f248e4SAndreas Gohr 327*08f248e4SAndreas Gohr /** 328*08f248e4SAndreas Gohr * Applies a translate transformation to definition 329*08f248e4SAndreas Gohr * @param string $def definition 330*08f248e4SAndreas Gohr * @param float $x 331*08f248e4SAndreas Gohr * @param float $y 332*08f248e4SAndreas Gohr * @return string 333*08f248e4SAndreas Gohr */ 334*08f248e4SAndreas Gohr public function defTranslate($def, $x=0, $y=0){ 335*08f248e4SAndreas Gohr return $this->defApplyMatrix($def, array(1, 0, 0, 1, $x, $y)); 336*08f248e4SAndreas Gohr } 337*08f248e4SAndreas Gohr 338*08f248e4SAndreas Gohr /** 339*08f248e4SAndreas Gohr * Applies a translate transformation to definition 340*08f248e4SAndreas Gohr * @param string $def Definition 341*08f248e4SAndreas Gohr * @param integer $angle Rotation angle (degrees) 342*08f248e4SAndreas Gohr * @param integer $x X coordinate of rotation center 343*08f248e4SAndreas Gohr * @param integer $y Y coordinate of rotation center 344*08f248e4SAndreas Gohr * @return string 345*08f248e4SAndreas Gohr */ 346*08f248e4SAndreas Gohr public function defRotate($def, $angle, $x=0, $y=0){ 347*08f248e4SAndreas Gohr if($x==0 && $y==0){ 348*08f248e4SAndreas Gohr $angle = deg2rad($angle); 349*08f248e4SAndreas Gohr return $this->defApplyMatrix($def, array(cos($angle), sin($angle), -sin($angle), cos($angle), 0, 0)); 350*08f248e4SAndreas Gohr } 351*08f248e4SAndreas Gohr 352*08f248e4SAndreas Gohr // rotate by a given point 353*08f248e4SAndreas Gohr $def = $this->defTranslate($def, $x, $y); 354*08f248e4SAndreas Gohr $def = $this->defRotate($def, $angle); 355*08f248e4SAndreas Gohr $def = $this->defTranslate($def, -$x, -$y); 356*08f248e4SAndreas Gohr return $def; 357*08f248e4SAndreas Gohr } 358*08f248e4SAndreas Gohr 359*08f248e4SAndreas Gohr /** 360*08f248e4SAndreas Gohr * Applies a scale transformation to definition 361*08f248e4SAndreas Gohr * @param string $def definition 362*08f248e4SAndreas Gohr * @param integer $x 363*08f248e4SAndreas Gohr * @param integer $y 364*08f248e4SAndreas Gohr * @return string 365*08f248e4SAndreas Gohr */ 366*08f248e4SAndreas Gohr public function defScale($def, $x=1, $y=1){ 367*08f248e4SAndreas Gohr return $this->defApplyMatrix($def, array($x, 0, 0, $y, 0, 0)); 368*08f248e4SAndreas Gohr } 369*08f248e4SAndreas Gohr 370*08f248e4SAndreas Gohr /** 371*08f248e4SAndreas Gohr * Calculates the new definition with the matrix applied 372*08f248e4SAndreas Gohr * @param string $def 373*08f248e4SAndreas Gohr * @param array $matrix 374*08f248e4SAndreas Gohr * @return string 375*08f248e4SAndreas Gohr */ 376*08f248e4SAndreas Gohr public function defApplyMatrix($def, $matrix){ 377*08f248e4SAndreas Gohr 378*08f248e4SAndreas Gohr // if there are several shapes in this definition, do the operation for each 379*08f248e4SAndreas Gohr preg_match_all('/M[^zZ]*[zZ]/', $def, $shapes); 380*08f248e4SAndreas Gohr $shapes = $shapes[0]; 381*08f248e4SAndreas Gohr if(count($shapes)>1){ 382*08f248e4SAndreas Gohr foreach($shapes as &$shape) 383*08f248e4SAndreas Gohr $shape = $this->defApplyMatrix($shape, $matrix); 384*08f248e4SAndreas Gohr return implode(' ', $shapes); 385*08f248e4SAndreas Gohr } 386*08f248e4SAndreas Gohr 387*08f248e4SAndreas Gohr preg_match_all('/[a-zA-Z]+[^a-zA-Z]*/', $def, $instructions); 388*08f248e4SAndreas Gohr $instructions = $instructions[0]; 389*08f248e4SAndreas Gohr 390*08f248e4SAndreas Gohr $return = ''; 391*08f248e4SAndreas Gohr foreach($instructions as &$instruction){ 392*08f248e4SAndreas Gohr $i = preg_replace('/[^a-zA-Z]*/', '', $instruction); 393*08f248e4SAndreas Gohr preg_match_all('/\-?[0-9\.]+/', $instruction, $coords); 394*08f248e4SAndreas Gohr $coords = $coords[0]; 395*08f248e4SAndreas Gohr 396*08f248e4SAndreas Gohr if(empty($coords)){ 397*08f248e4SAndreas Gohr continue; 398*08f248e4SAndreas Gohr } 399*08f248e4SAndreas Gohr 400*08f248e4SAndreas Gohr $new_coords = array(); 401*08f248e4SAndreas Gohr while(count($coords)>0){ 402*08f248e4SAndreas Gohr 403*08f248e4SAndreas Gohr // do the matrix calculation stuff 404*08f248e4SAndreas Gohr list($a, $b, $c, $d, $e, $f) = $matrix; 405*08f248e4SAndreas Gohr 406*08f248e4SAndreas Gohr // exception for relative instruction 407*08f248e4SAndreas Gohr if( preg_match('/[a-z]/', $i) ){ 408*08f248e4SAndreas Gohr $e = 0; 409*08f248e4SAndreas Gohr $f = 0; 410*08f248e4SAndreas Gohr } 411*08f248e4SAndreas Gohr 412*08f248e4SAndreas Gohr // convert horizontal lineto (relative) 413*08f248e4SAndreas Gohr if( $i=='h' ){ 414*08f248e4SAndreas Gohr $i = 'l'; 415*08f248e4SAndreas Gohr $x = floatval( array_shift($coords) ); 416*08f248e4SAndreas Gohr $y = 0; 417*08f248e4SAndreas Gohr 418*08f248e4SAndreas Gohr // add new point's coordinates 419*08f248e4SAndreas Gohr $current_point = array( 420*08f248e4SAndreas Gohr $a*$x + $c*$y + $e, 421*08f248e4SAndreas Gohr $b*$x + $d*$y + $f, 422*08f248e4SAndreas Gohr ); 423*08f248e4SAndreas Gohr $new_coords = array_merge($new_coords, $current_point); 424*08f248e4SAndreas Gohr } 425*08f248e4SAndreas Gohr 426*08f248e4SAndreas Gohr // convert vertical lineto (relative) 427*08f248e4SAndreas Gohr elseif( $i=='v' ){ 428*08f248e4SAndreas Gohr $i = 'l'; 429*08f248e4SAndreas Gohr $x = 0; 430*08f248e4SAndreas Gohr $y = floatval( array_shift($coords) ); 431*08f248e4SAndreas Gohr 432*08f248e4SAndreas Gohr // add new point's coordinates 433*08f248e4SAndreas Gohr $current_point = array( 434*08f248e4SAndreas Gohr $a*$x + $c*$y + $e, 435*08f248e4SAndreas Gohr $b*$x + $d*$y + $f, 436*08f248e4SAndreas Gohr ); 437*08f248e4SAndreas Gohr $new_coords = array_merge($new_coords, $current_point); 438*08f248e4SAndreas Gohr } 439*08f248e4SAndreas Gohr 440*08f248e4SAndreas Gohr // convert quadratic bezier curve (relative) 441*08f248e4SAndreas Gohr elseif( $i=='q' ){ 442*08f248e4SAndreas Gohr $x = floatval( array_shift($coords) ); 443*08f248e4SAndreas Gohr $y = floatval( array_shift($coords) ); 444*08f248e4SAndreas Gohr 445*08f248e4SAndreas Gohr // add new point's coordinates 446*08f248e4SAndreas Gohr $current_point = array( 447*08f248e4SAndreas Gohr $a*$x + $c*$y + $e, 448*08f248e4SAndreas Gohr $b*$x + $d*$y + $f, 449*08f248e4SAndreas Gohr ); 450*08f248e4SAndreas Gohr $new_coords = array_merge($new_coords, $current_point); 451*08f248e4SAndreas Gohr 452*08f248e4SAndreas Gohr // same for 2nd point 453*08f248e4SAndreas Gohr $x = floatval( array_shift($coords) ); 454*08f248e4SAndreas Gohr $y = floatval( array_shift($coords) ); 455*08f248e4SAndreas Gohr 456*08f248e4SAndreas Gohr // add new point's coordinates 457*08f248e4SAndreas Gohr $current_point = array( 458*08f248e4SAndreas Gohr $a*$x + $c*$y + $e, 459*08f248e4SAndreas Gohr $b*$x + $d*$y + $f, 460*08f248e4SAndreas Gohr ); 461*08f248e4SAndreas Gohr $new_coords = array_merge($new_coords, $current_point); 462*08f248e4SAndreas Gohr } 463*08f248e4SAndreas Gohr 464*08f248e4SAndreas Gohr // every other commands 465*08f248e4SAndreas Gohr // @TODO: handle 'a,c,s' (elliptic arc curve) commands 466*08f248e4SAndreas Gohr // cf. http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands 467*08f248e4SAndreas Gohr else{ 468*08f248e4SAndreas Gohr $x = floatval( array_shift($coords) ); 469*08f248e4SAndreas Gohr $y = floatval( array_shift($coords) ); 470*08f248e4SAndreas Gohr 471*08f248e4SAndreas Gohr // add new point's coordinates 472*08f248e4SAndreas Gohr $current_point = array( 473*08f248e4SAndreas Gohr $a*$x + $c*$y + $e, 474*08f248e4SAndreas Gohr $b*$x + $d*$y + $f, 475*08f248e4SAndreas Gohr ); 476*08f248e4SAndreas Gohr $new_coords = array_merge($new_coords, $current_point); 477*08f248e4SAndreas Gohr } 478*08f248e4SAndreas Gohr 479*08f248e4SAndreas Gohr 480*08f248e4SAndreas Gohr } 481*08f248e4SAndreas Gohr 482*08f248e4SAndreas Gohr $instruction = $i . implode(',', $new_coords); 483*08f248e4SAndreas Gohr 484*08f248e4SAndreas Gohr // remove useless commas 485*08f248e4SAndreas Gohr $instruction = preg_replace('/,\-/','-', $instruction); 486*08f248e4SAndreas Gohr } 487*08f248e4SAndreas Gohr 488*08f248e4SAndreas Gohr return implode('', $instructions); 489*08f248e4SAndreas Gohr } 490*08f248e4SAndreas Gohr 491*08f248e4SAndreas Gohr 492*08f248e4SAndreas Gohr 493*08f248e4SAndreas Gohr /** 494*08f248e4SAndreas Gohr * 495*08f248e4SAndreas Gohr * Short-hand methods 496*08f248e4SAndreas Gohr * 497*08f248e4SAndreas Gohr */ 498*08f248e4SAndreas Gohr 499*08f248e4SAndreas Gohr 500*08f248e4SAndreas Gohr /** 501*08f248e4SAndreas Gohr * Return full SVG XML 502*08f248e4SAndreas Gohr * @return string 503*08f248e4SAndreas Gohr */ 504*08f248e4SAndreas Gohr public function asXML(){ 505*08f248e4SAndreas Gohr return $this->svg->asXML(); 506*08f248e4SAndreas Gohr } 507*08f248e4SAndreas Gohr 508*08f248e4SAndreas Gohr /** 509*08f248e4SAndreas Gohr * Adds an attribute to the SVG 510*08f248e4SAndreas Gohr * @param string $key 511*08f248e4SAndreas Gohr * @param string $value 512*08f248e4SAndreas Gohr */ 513*08f248e4SAndreas Gohr public function addAttribute($key, $value){ 514*08f248e4SAndreas Gohr return $this->svg->addAttribute($key, $value); 515*08f248e4SAndreas Gohr } 516*08f248e4SAndreas Gohr} 517