1

I want to rotate SVG by PHP.

I have SVG files and want to rotate them by PHP. The method I found online is to add something like

    <g transform="translate(90) rotate(45 50 50)">

inside <svg>.

For examle, I want to convert from:

<svg> <i-am-here+i-was-here>...</i-am-here+i-was-here> </svg>

or

<svg> <g transform="i-have-transform-but-no-rotate"> <i-am-here+i-was-here>...</i-am-here+i-was-here> </g> </svg>

To:

<svg> <g transform="svgrotate+old-transform"> <i-am-here+i-was-here>...</i-am-here+i-was-here> </g> </svg>

but now I don't know how to achieve this -

  1. to add a <g> inside <svg> and wrap all old inner nodes.

  2. if <g> is already wrapping all inner nodes, then skip adding new <g> tag but just rotate it to a new degree.

the following is a possible svg code before rotating.

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    <svg version="1.1" id="hello" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
         width="800px" height="700px" viewBox="0 0 800 700" enable-background="new 0 0 800 700" xml:space="preserve">        

    <g>
      <path transform="rotate(-14.1173 121.2 96.1965)" id="svg_2" d="m128,25c9,14 9,204 -35,124c-44,-80 177,-52 120,-36c-57,16 -120,-77 -185,-79c-65,-2 288,166 169,128c-119,-38 -78,-151 -69,-137z" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" stroke="#3f007f" fill="#f4f438"/>
      <path transform="rotate(152.959 126.708 104.715)" stroke="#3f007f" id="svg_3" d="m219.4411,94.86682c-50.99725,81.80002 -148.00278,74.4472 -190.13092,50.55057c-42.12811,-23.89664 5.54315,-43.19778 18.8468,-19.30115c13.30362,23.89664 81.48468,16.54382 108.64624,0.91913c27.16156,-15.62473 50.44287,-45.95508 54.87738,-56.06519c4.43463,-10.11011 58.75769,-57.90341 7.7605,23.89664z" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#7fff00"/>
    </g>

    <path transform="rotate(9.48719 272.234 246.106)" id="svg_6" d="m214,78c0,-1 -108,23 -108,23c0,0 -85,108 23,85c108,-23 122,32 167,14c45,-18 -64,289 106,197c170,-92 -31,-310 -32,-310" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" stroke="#3f007f" fill="#ffaad4"/>
    <path d="m342,184c-7,-2 -12.98453,-3.08075 -18,-4c-5.98309,-1.0966 -11.53976,-2.46873 -19,-4c-5.87747,-1.20639 -13,-1 -18,-1c-7,0 -14.05798,-0.83221 -20,0c-7.20975,1.00977 -14.33942,4.12776 -23,8c-7.36008,3.29077 -14.23541,7.73578 -21,14c-5.1882,4.80443 -11.03857,10.11638 -16,17c-5.22981,7.25597 -7.48169,14.87997 -9,22c-1.47472,6.91557 -1.49489,14.02045 -1,20c0.50171,6.06204 3,11 5,16c2,5 3.72398,9.22272 7,13c3.70638,4.2735 5,5 6,5l1,0l3,-1" id="svg_1" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" stroke="#000000" fill="none"/>   

    <g>  
      <path stroke="#000000" transform="rotate(-7.76776 324.596 222.303)" id="svg_4" d="m422.29062,129.05128c-126.20798,34.85936 91.52487,198.50475 -153.18372,103.60979c-244.70862,-94.89496 238.92807,178.17014 107.90298,114.26129c-131.02509,-63.90884 65.51254,-336.00563 -15.4147,-153.96223c-80.92725,182.04341 -141.62271,231.42751 -145.47639,168.48695c-3.85367,-62.94052 213.8792,-386.35803 113.68355,-284.68486c-100.19566,101.67317 67.43936,149.12065 111.75668,227.55424c44.31729,78.43359 -92.48828,66.81381 -114.64694,62.94052c-22.15866,-3.87326 184.97659,-174.29686 23.12207,-215.93445c-161.85452,-41.63758 -53.95151,-3.87326 -52.02466,114.26129c1.92682,118.13455 1.92682,118.13455 1.92682,118.13455" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#ff0000"/>
      <path transform="rotate(20.3513 424.5 303.53)" stroke="#000000" id="svg_5" d="m368,255.68533l113,37.43481c0,0 13.37784,104.74188 -53.79596,69.57584c-67.17383,-35.16605 32.16373,-157.30188 -21.06299,-117.59828c-53.22668,39.7036 -53.22668,39.7036 -53.22668,39.7036" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#007fff"/>  
    </g>

    <path transform="rotate(44.7788 399.595 115.757)" id="svg_8" d="m354,84c8,54 10,126 56,59c46,-67 42,-57 16,-76c-26,-19 -72,17 -72,17z" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" stroke="#3f007f" fill="#7f7f00"/>

    </svg>
michi
  • 6,565
  • 4
  • 33
  • 56
Hello
  • 13
  • 1
  • 3
  • XML? Where is the XML? – Naftali Jul 30 '13 at 13:45
  • So you want to rewrite the svg so that the whole content is wrapped in a `g` tag with a transform? – Ben Jul 30 '13 at 23:10
  • You could use [DOMDocument](http://php.net/manual/en/class.domdocument.php) to load and manipulate your svg file or use some of the libraries mentioned in this SO question... http://stackoverflow.com/questions/3577641/how-do-you-parse-and-process-html-xml-in-php – Ben Jul 30 '13 at 23:18

1 Answers1

1

I searched for quite awhile and finally put this together. Hope it helps.

You can use the SimpleXML Library to access the nodes as needed.

My solution works, but could probably simplified.

First, I added this class (found in the comments here):

class ExSimpleXMLElement extends SimpleXMLElement 
{ 


    public function _construct($xml){
        parent::_construct($xml);
    }

    /** 
     * Add SimpleXMLElement code into a SimpleXMLElement 
     * @param SimpleXMLElement $append 
     */ 
    public function appendXML($append) 
    { 
        if ($append) { 
            if (strlen(trim((string) $append))==0) { 
                $xml = $this->addChild($append->getName()); 
                foreach($append->children() as $child) { 
                    $xml->appendXML($child); 
                } 
            } else { 
                $xml = $this->addChild($append->getName(), (string) $append); 
            }     
            foreach($append->attributes() as $n => $v) { 
                $xml->addAttribute($n, $v); 
            } 
         } 
     } 
} 

Just include that before any of the code.

//file
$file = 'svg-nog.svg';

//http://www.webdeveloper.com/forum/showthread.php?165648-Editing-XML-using-PHP
$xml = simplexml_load_file($file); //Load the File. 
$sxe = new EXSimpleXMLElement($xml->asXML()); //Load the child class (it will load the main one)

// Get the parent node's childrens
$nodeChildrens = $sxe->children();
//Check if there's more than one child and if there is more than one <g> tag
if(count($nodeChildrens) > 1 and count($sxe->g) > 1){

//Add our Wrapper Node
$g = $sxe->addChild("g");
//You can go ahead and add your desired transform code
$g['transform'] = "rotate(90 600 600)";
//Set an ID so it's unique
$g['id'] = "myWrapper";

$unsets = array();

//Loop through the children
foreach ($nodeChildrens as $value) {
            //Skip our wrapper element
    if($value->attributes()->id == "myWrapper"){
        continue;
    }

            //Append the element to the wrapper
            //Will recursively add elements as needed
    $g->appendXML($value);

    //Save a reference for the different elements we have
    $unsets[] = $value->getName();
}   

//Loop through the old/loose elements and remove them
foreach ($unsets as $name) {
    $segarr = $sxe->{$name};

    $count = count($segarr);

    $j = 0;

    for ($i = 0; $i < $count; $i++) {

        if ($segarr[$j]['id'] != 'myWrapper') {
            unset($segarr[$j]);
            $j = $j - 1;
        }
        $j = $j + 1;
    }
}
} else {
    //Just apply the transform
    $sxe->g['transform'] = "rotate(90 600 600)";
        //Can also be done with:
        //$sxe->g->addAttribute('transform', "rotate(90 600 600)");
}

//Save
$sxe->asXML('test.svg');

This worked quite well for me for both SVG's wrapped in a tag and those that weren't. There might be simpler ways to do it, but this is certainly effective.

One more thing! This may not be correct but when I rotated my SVG's, they were usually clipped. Thus I had to alter the main attributes. What this entailed was swapping the height and width, as well as translating the SVG to re-center it. Here you go:

//Set the vars of the primary attributes we want to edit
$w = "width";
$h= "height";
$viewbox = "viewBox";
$bg = 'enable-background';

//Get SVG Width & Height
$width = (string)$sxe->attributes()->$w;
$height = (string)$sxe->attributes()->$h;

//Get viewBox and background
$sxe->attributes()->$viewbox = "0 0 " . str_replace('px', '', $height) . " " . str_replace('px', '', $width);
$sxe->attributes()->$bg = "new 0 0 " . str_replace('px', '', $height) . " " . str_replace('px', '', $width);

//Get the numerical values for width and height
$rw = floatval(str_replace("px", "", $width));
$rh = floatval(str_replace("px", "", $height));

//Get the center point
$rx = $rw / 2;
$ry = $rh / 2;

//Logic will determine the appropiate translate value for the svg so that it will center in the new viewbox
if($rw < $rh){
    $trans = ($rw - $rh) / 2;
}

if($rw > $rh){
    $trans = -1 * ($rh - $rw) / 2;
}

//Swap width and height
$oldW = (string)$sxe->attributes()->$w;
$sxe->attributes()->$w = $height;
$sxe->attributes()->$h = $oldW;


// Get the parents node childrens
$nodeChildrens = $sxe->children();

//If the transform attribute is set, this svg has already been rotated, as such, we'll just unset it
if(isset($nodeChildrens->g->attributes()->transform)){
    unset($nodeChildrens->g->attributes()->transform);
} else {
    // Otherwise, we're going to add in the transform data for the rotation and re-centering.
    $nodeChildrens->g->addAttribute('transform', "rotate(90 ".$rx." ".$ry.") translate(".$trans." ".$trans.")");
}

//This next line will overwrite the original XML file with new data added 
$sxe->asXML("test.svg");

You may notice towards the bottom, this code will also reverse the rotation process as well.

Hope that helps!

Richard Cagle
  • 158
  • 1
  • 11