Search code examples
csssvgpropertiesadobeadobe-illustrator

Transform-origin CSS property not recognized by Illustrator


I'm doing an online customization tool on a website which directly changes an SVG element made on Adobe Illustrator by a graphist. I have a problem with the transform-origin: center CSS property which is not understood when opening the svg on Illusrator.

However, for exemple, the transform: rotate(180) is understood and read.

How can I do to make it works another way (or this way but different ?) please ? Can't find anything on the Internet so far..


Solution

  • A possible workaround might be to convert all transform properties to matrix transformations.

    This way we can reset all transform-orgin attributes to the default "0 0".

    Example: convert all transformations to matrix

    // getTransformToElement polyfill
    SVGElement.prototype.getTransformToElement =
      SVGElement.prototype.getTransformToElement ||
      function(toElement) {
        return toElement.getScreenCTM().inverse().multiply(this.getScreenCTM());
      };
    
    
    //let svgOrigMarkup = svgOrig.value;
    svgPreview.innerHTML = svgOrig.value;
    let svg = document.querySelector("svg");
    optimizeForAi(svg);
    
    
    svgOrig.addEventListener('change', function(e) {
      svgPreview.innerHTML = e.currentTarget.value;
      svg = document.querySelector("svg");
      optimizeForAi(svg);
    })
    
    
    
    function optimizeForAi(svg) {
      //add namespace
      let xmlns = svg.getAttribute('xmlns');
      if (!xmlns) {
        svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
      }
      //add viewBox and dimensions
      addViewBoxAndWidth(svg);
      let els = svg.querySelectorAll("*");
      //convert units and transformations
      els.forEach(function(el) {
        transFormToMatrix(el);
        percentageToAbs(svg, el)
      });
      //update markup
      svgMarkup.value = svg.outerHTML;
    }
    
    
    
    function addViewBoxAndWidth(svg) {
      let vB = svg.getAttribute("viewBox");
      let [width, height] = [svg.getAttribute("width"), svg.getAttribute("height")];
      let [svgW, svgH] = vB
        ?
        [vB.split(" ")[2], vB.split(" ")[3]] : [width, height];
      if (!svgW || !svgH) {
        let bb = svg.getBBox();
        [svgW, svgH] = [bb.width, bb.height];
      }
      //add viewBox
      if (!vB) {
        svg.setAttribute("viewBox", [0, 0, svgW, svgH].join(" "));
      }
      //add width and height
      if (!width || !height) {
        svg.setAttribute("width", svgW + "px");
        svg.setAttribute("height", svgH + "px");
      }
    }
    
    function percentageToAbs(svg, el) {
      let vB = svg.getAttribute("viewBox");
      let [svgW, svgH] = [vB.split(" ")[2], vB.split(" ")[3]];
    
      // find percentage values
      let atts = [...el.attributes];
      atts.forEach(function(att, i) {
        let attName = att.nodeName;
        let attVal = att.value;
        if (attVal.indexOf("%") !== -1) {
          let rel = attName.indexOf("y") !== -1 ? svgH : svgW;
          //convert to absolute value
          let attValAbs = (rel / 100) * parseFloat(attVal);
          el.setAttribute(attName, attValAbs);
        }
      });
    }
    
    function transFormToMatrix(el) {
      let type = el.nodeName.toLowerCase();
      let matrixString = "";
      let types = [
        "path",
        "polygon",
        "polyline",
        "rect",
        "ellipse",
        "circle",
        "line",
        "text",
        "g",
        "svg"
      ];
      if (types.indexOf(type) !== -1) {
        // get el matrix
        let matrix = el.getTransformToElement(el.parentNode);
        let [a, b, c, d, e, f] = [
          matrix.a,
          matrix.b,
          matrix.c,
          matrix.d,
          matrix.e,
          matrix.f
        ];
        matrixString = [a, b, c, d, e, f].join(" ");
        let matrixStringRound = [a, b, c, d, e, f]
          .map((val) => {
            return +val.toFixed(3);
          })
          .join(" ");
        //exclude non transformed elements
        if (matrixStringRound != "1 0 0 1 0 0") {
          el.setAttribute("transform", `matrix(${matrixString})`);
          el.removeAttribute("transform-origin");
          el.style.setProperty('transform-origin', '0px 0px', 'important');
        }
      }
      return matrixString;
    }
    svg{
    max-width:75%;
    height:auto;
    }
    <div id="svgPreview"></div>
    <h3>Paste in your svg markup</h3>
    <textarea id="svgOrig" style="width:100%; min-height:20em">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 400" >
        <style>
          .g2{
            transform-origin: center;
          }
        </style>
        <rect width="1000" height="400" fill="orange"></rect>
        <g transform="translate(-200 50) rotate(-45)" transform-origin="500 200">
          <g class="g2" transform="translate(200 -50)" style="color:green">
            <text  x="50%" y="50%" transform="rotate(45)" transform-origin="center" text-anchor="middle" font-size="50">Text</text>
          </g>
        </g>
      </svg>
    </textarea>
    
    <h3>Optimized</h3>
    <textarea id="svgMarkup" style="width:100%; min-height:20em"></textarea>

    The transFormToMatrix(el) method does the matrix calculation and depends on the deprecated getTransformToElement() method (polyfill included).

    Unfortunately transform-orgin is not be the only property AI or other graphic applications might struggle with (it also depends on you version).

    Recommendations:

    • Prefer svg style attributes like transform="rotate(45 100 100)"
      Especially svg's rotate() tends to be more robust, since it also allows to define pivot point coordinates
    • include xmlns, viewBox, xmlns:xlink, width, height attributes - otherwise AI might calculate wrong artboard dimensions.
    • relative (%) values tend to make troubles – better use userUnits (so unitless values like x="100")
    • if your svg contains use elements add legacy xlink:href references like <use id="sym1" href="#symbol1" xlink:href="#symbol1" />