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) :
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 withtransform-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&family=Black Ops One&family=Faster One&family=Fugaz One&family=Racing Sans One&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.