Home > Software engineering >  Issues with conversion of svg element to .svg
Issues with conversion of svg element to .svg

Time:07-21

After hours of looking for a solution here and there, I come here to ask for help. I'm developing a customization module using <svg> element and Javascript. In the end, I convert and download the SVG element in a .svg file but the problem is that some elements like the <text> ones are not in the right place when opening the file with Illustrator, it doesn't seem to care about the x and y attributes. When I open the file in a browser, everything is in the right place.

Here is the HTML of the downloaded .svg file I get, all at the right place (the browser read the HTML attributes well I guess) : enter image description here

CodePudding user response:

The browser contains it to the first SVG element, which is width="833" height="396". Thats why you don't see the 'Exemple', it is contained to that first SVG tag. Illustrator does not contain anything, as it is not a browser viewport. I think that if you use only 1 SVG tag, it will solve your issue. It should then render the same in both software.

CodePudding user response:

Some ideas for a workaround

  • convert/combine all transformations to matrix():
    this way we circumvent problems with transform-origin (Illustrator apparently can't use it - rotations will have a wrong pivot point)
  • convert nested svg elements to <g> group elements
  • some attributes like textLength won't work at all in AI - you might use a condensed font instead.

Working example

// getTransformToElement polyfill
SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(
  toElement) {
  return toElement.getScreenCTM().inverse().multiply(this.getScreenCTM());
};


function fixNestedSvgs() {
  let svgs = document.querySelectorAll('svg');
  let nested = document.querySelectorAll('svg svg');
  // all transformations to matrix()
  svgs.forEach(function(svg) {
    let children = [...svg.children];
    children.forEach(function(child) {
      transFormToMatrix(child);
      styleToAttribute(child, ['fill', 'stroke', 'stroke-width']);
    });
  })
  // nested to groups
  nested.forEach(function(svg) {
    let parent = svg.parentNode.closest('svg');
    nestedSvgToGroup(svg);
    svgMarkup.value = parent.outerHTML;
  })
}


// all transformations to matrix()
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.closest("svg"));
    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(" ");
    //exclude non transformed elements
    if (matrixString != "1 0 0 1 0 0") {
      el.setAttribute("transform", `matrix(${matrixString})`);
      el.removeAttribute("transform-origin");
    }
  }
  return matrixString;
}


// convert fill styles to attributes
function styleToAttribute(el, attributes = ['fill', 'stroke', 'stroke-width']) {
  let types = ["path", "polygon", "polyline", "rect", "ellipse", "circle", "line", "text", "g", "svg"];
  let type = el.nodeName.toLowerCase();
  if (types.indexOf(type) !== -1) {
    let style = window.getComputedStyle(el);
    attributes.forEach(function(attName) {
      let value = style.getPropertyValue(attName);
      if (value !== 'rgb(0, 0, 0)') {
        el.setAttribute(attName, value);
      }
    })
  }
}

//convert nested svgs to groups
function nestedSvgToGroup(svg) {
  let svgSub = svg;
  if (svg.parentNode) {
    let parent = svg.parentNode.closest('svg');
    let svgSubChildren = [...svgSub.children];
    let groupMatrix = transFormToMatrix(svgSub);

    //replace nested svg with group - apply matrix
    let group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    group.classList.add('svgNest');
    group.setAttribute('transform', `matrix( ${groupMatrix} )`);

    //copy children to group
    svgSubChildren.forEach(function(child, i) {
      group.appendChild(child);
    })
    //remove nested svg
    svgSub.replaceWith(group);
  }
}
svg {
  width: 80%
}
<svg  viewBox="0 0 833 396" xmlns="http://www.w3.org/2000/svg">
            <style>
                @import url("https://fonts.googleapis.com/css2?family=Audiowide&amp;family=Black Ops One&amp;family=Faster One&amp;family=Fugaz One&amp;family=Racing Sans One&amp;display=swap");

                .fillColor {
                    fill: currentColor;
                }

                .strokeColor {
                    stroke: currentColor;
                    fill: none;
                    stroke-width: 5px;
                }

                .fillStrokeColor {
                    stroke: currentColor;
                    fill: currentColor;
                    stroke-width: 1px;
                }

                text {
                    font-family: 'Fugaz One', cursive;
                }
            </style>
            <rect id="allCarpet" x="0" y="0" width="833" height="396"></rect>
            <rect  id="foamCarpet" x="10" y="10" width="813" height="376" style="color:#ffd100"></rect>
            <!-- <rect  id="leftLogo" x="22" y="105" width="84" height="187" style="color:white" /> -->
            <text  x="580" y="345" font-size="15" id="topNickname" fill="black" textLength="70"
                lengthAdjust="spacingAndGlyphs">FLORIAN</text>
            <text  x="590" y="368" font-size="30" id="topNickname" fill="black" textLength="80"
                lengthAdjust="spacingAndGlyphs">FLORIAN</text>
            <text  x="-25" y="55" font-size="60" id="topNumber" fill="black" textLength="80"
                lengthAdjust="spacingAndGlyphs" transform="rotate(180)" transform-origin="42px 42px">00</text>
            <text  x="725" y="366" font-size="60" id="botNumber" fill="black" textLength="80"
                lengthAdjust="spacingAndGlyphs">00</text>

            <svg version="1.1"  id="Calque_1" xmlns="http://www.w3.org/2000/svg"
                xmlns:xlink="http://www.w3.org/1999/xlink" x="1px" y="0px" viewBox="0 0 197 100"
                style="enable-background:new 0 0 197 100;" xml:space="preserve" width="150px">
                <style type="text/css">
                    .st0 {
                        fill: #CA0D25;
                    }

                    .st1 {
                        fill: none;
                    }

                    .st2 {
                        fill: #FF0000;
                    }

                    .st3 {
                        fill: #FEFEFE;
                    }
                </style>

                <path  d="M7.2,3.8v92.5h182.7V3.8H7.2z M186.8,93.2H10.2V6.8h176.5L186.8,93.2L186.8,93.2z"
                    transform="rotate(90)" transform-origin="center"></path>
                <path  fill="red" d="M66.9,62.1v0.3c-0.1-0.1-0.2-0.2-0.2-0.3H66.9z M73.7,41.5c-5.6,0-6.9,5-6.9,9.6c0,4.6,1.3,9.5,6.9,9.5
c5.2-0.1,6.9-4.7,6.9-9.5C80.5,46.4,78.7,41.6,73.7,41.5z M43.4,53.1c-0.2,0.1-0.5,0.1-0.7,0.1c-2.5,0.5-5.1,1.6-5.1,4.6
c0,3.1,2.1,3.4,4.7,3.4c0.4,0,0.7,0,1.1,0c5.2-0.5,5.7-4.4,5.7-6.1L49,51.5C47.8,52.6,45.7,52.8,43.4,53.1z M186.8,6.8v86.3H10.2
V6.8H186.8z M57.5,66.4c-1.3-1.7-0.9-3.9-0.9-6.6V44.3c0-7.6-7.8-8.4-13.3-8.4c-6.2,0.1-11.9,2.3-12.3,9.5l7.3,0.1
c0.3-2.7,2.1-4,5.1-4.1c2.7,0,5.8,0.3,5.8,4c0,2.4-2.6,2.6-5.8,2.7c-1.4,0-2.8,0.1-4.3,0.3c-4.9,0.7-9.2,2.6-9.2,9.3
c0,5.3,4.1,9.2,9.9,9.2c1.2,0,2.1,0,3.3-0.2l0.3,0c2.2-0.4,4.3-1.3,6-3.1c0,0.9,0.6,1.9,0.9,2.8L57.5,66.4L57.5,66.4z M88.7,51.3
c0-8.5-4.2-16-13.5-16c-0.5,0-1,0-1.5,0.1c-3.1,0.4-5.7,1.8-7.4,4.5h-0.1v-3.1h-6.9v37.8h7.6V62.4c1.6,2.2,4.1,3.6,6.8,4.1
c0.6,0.1,1.3,0.1,2,0.1C84.3,66.7,88.7,59.2,88.7,51.3z M106.1,35.9c-4.3,0-7.1,1.8-8.8,5.5l0-4.8h-7.1v29.8h7.6V53
c0-6.8,3.8-9.5,8.2-9.5L106.1,35.9L106.1,35.9z M116.5,36.7h-7.6v29.7h7.6V36.7z M116.5,25.4h-7.6v6.8h7.6V25.4z M126.5,25.4h-7.6
v41.1h7.6V25.4z M136.7,36.7H129v29.7h7.6V36.7z M136.7,25.4H129v6.8h7.6V25.4z M166.7,66.3c-1.3-1.7-0.9-3.9-0.9-6.6V44.2
c0-7.6-7.8-8.4-13.3-8.4c-6.2,0.1-11.9,2.3-12.3,9.5l7.3,0.1c0.3-2.7,2.1-4,5.1-4.1c2.7,0,5.8,0.3,5.8,4c0,2.4-2.6,2.6-5.8,2.7
c-1.4,0-2.8,0.1-4.3,0.3c-4.9,0.7-9.2,2.6-9.2,9.3c0,5.3,4.1,9.2,9.9,9.2c1.2,0,2.1,0,3.3-0.2l0.3,0c2.2-0.4,4.3-1.3,6-3.1
c0,0.9,0.6,1.9,0.9,2.8L166.7,66.3L166.7,66.3z M152.5,53c-0.2,0.1-0.5,0.1-0.7,0.1c-2.5,0.5-5.1,1.6-5.1,4.6c0,3.1,2.1,3.4,4.7,3.4
c0.4,0,0.7,0,1.1,0c5.2-0.5,5.7-4.4,5.7-6.1l-0.1-3.7C156.9,52.5,154.8,52.7,152.5,53z" transform="rotate(90)"
                    transform-origin="center"></path>
                <path 
                    d="M129.2,25.4h7.6v6.8h-7.6V25.4z M57.7,66.4h-7.3c-0.3-0.9-0.8-1.9-0.9-2.8c-1.6,1.8-3.7,2.7-6,3.1l-0.3,0
c-1.2,0.2-2.1,0.2-3.3,0.2c-5.8,0-9.9-4-9.9-9.2c0-6.7,4.3-8.6,9.2-9.3c1.5-0.2,2.9-0.3,4.3-0.3c3.2-0.2,5.8-0.4,5.8-2.7
c0-3.7-3-4-5.8-4c-2.9,0-4.8,1.3-5.1,4.1l-7.3-0.1c0.5-7.1,6.1-9.4,12.3-9.5c5.4,0,13.3,0.8,13.3,8.4v15.5
C56.9,62.4,56.5,64.7,57.7,66.4z M49.3,55.1l-0.1-3.7c-1.2,1.1-3.3,1.3-5.6,1.6c-0.2,0.1-0.5,0.1-0.7,0.1c-2.5,0.5-5.1,1.6-5.1,4.6
c0,3.1,2.1,3.4,4.7,3.4c0.4,0,0.7,0,1.1,0C48.8,60.7,49.3,56.8,49.3,55.1z M166.9,66.3h-7.3c-0.3-0.9-0.8-1.9-0.9-2.8
c-1.6,1.8-3.7,2.7-6,3.1l-0.3,0c-1.2,0.2-2.1,0.2-3.3,0.2c-5.8,0-9.9-4-9.9-9.2c0-6.7,4.3-8.6,9.2-9.3c1.5-0.2,2.9-0.3,4.3-0.3
c3.2-0.2,5.8-0.4,5.8-2.7c0-3.7-3-4-5.8-4c-2.9,0-4.8,1.3-5.1,4.1l-7.3-0.1c0.5-7.1,6.1-9.4,12.3-9.5c5.4,0,13.3,0.8,13.3,8.4v15.5
C166,62.3,165.6,64.6,166.9,66.3z M158.4,55l-0.1-3.7c-1.2,1.1-3.3,1.3-5.6,1.6c-0.2,0.1-0.5,0.1-0.7,0.1c-2.5,0.5-5.1,1.6-5.1,4.6
c0,3.1,2.1,3.4,4.7,3.4c0.4,0,0.7,0,1.1,0C157.9,60.7,158.4,56.7,158.4,55z M88.9,51.2c0,8-4.4,15.4-13.1,15.4c-0.6,0-1.3,0-2-0.1
c-2.7-0.4-5.2-1.9-6.8-4.1v-0.3h-0.2c0.1,0.1,0.2,0.2,0.2,0.3v12.2h-7.6V36.8h6.9v3.1h0.1c1.7-2.7,4.3-4.2,7.4-4.5
c0.5,0,1-0.1,1.5-0.1C84.8,35.2,88.9,42.8,88.9,51.2z M80.7,51c0-4.6-1.8-9.4-6.9-9.6c-5.6,0-6.9,5-6.9,9.6c0,4.6,1.3,9.5,6.9,9.5
C79.1,60.4,80.7,55.8,80.7,51z M129.2,66.4h7.6V36.7h-7.6V66.4z M119.1,66.5h7.6V25.4h-7.6V66.5z M109.1,66.4h7.6V36.7h-7.6V66.4z
M97.5,41.4l0-4.8h-7.1v29.8h7.6V53c0-6.8,3.8-9.5,8.2-9.5v-7.6C102,35.9,99.2,37.7,97.5,41.4z M109.1,32.2h7.6v-6.8h-7.6V32.2z"
                    transform="rotate(90)" transform-origin="center"></path>

            </svg>
<text  x="323" y="586" font-size="100" id="rightText" fill="black" transform="rotate(-90)" transform-origin="center" textLength="187" lengthAdjust="spacingAndGlyphs">EXEMPLE</text>
<polyline  id="centerPolygon" points="144,103 642,103 689,153 689,294 192,294 144,246 144,100.5" style="color:#010101;"></polyline>
<polyline  id="bottomPolygon" points="10,374 525,374 547,347 10,347" style="color:#009afb"></polyline>
<polyline  id="topPolygon" points="823,22 308,22 286,49 823,49" style="color:#009afb">
</polyline>
<polyline  id="littleBottomPolygon" points="10,340 553,340 560,332 10,332" style="color:#fefefe"></polyline>
<polyline  id="littleTopPolygon" points="823,57 280,57 273,65 823,65" style="color:#fefefe"></polyline>
</svg>

<p><button type="button" onclick="fixNestedSvgs()"> Fix nested svg</button></p>
<textarea id="svgMarkup" style="width:100%; min-height:20em"></textarea>

Drawbacks

This conversions might fail in browsers struggling with transform-origin (like some safari/webkit versions).

Some graphic applications might also have issues parsing inherited fill attributes like fill:currentColor.
The above example includes a helper styleToAttribute() to inline some attributes.

  • Related