@@ -52,7 +52,7 @@ export interface SpAsset {
5252 mime : string ;
5353 /** Payload length in characters; for a vector, the standalone markup's. */
5454 chars : number ;
55- /** For a vector, the viewBox size in board units. */
55+ /** For a vector, the viewBox size in board units, else its width and height attributes . */
5656 w : number ;
5757 h : number ;
5858 /** The `alt`; for a vector, its accessible name, class or the caption beside it. */
@@ -318,9 +318,9 @@ function addAsset(i,uri,via,el){var c=uri.indexOf(','),payload=uri.slice(c+1),k=
318318 cascade supplied (currentColor, var(), a fill from a stylesheet) are written in, and xmlns is
319319 added where a literal icon had none, so an <img> in the parent draws it and Figma accepts it. */
320320function addSvg(i,el){
321- if(!el.querySelector('path,rect,circle,ellipse,line,polygon,polyline,text,image,use'))return;
322- var c= el.cloneNode(true),cs=getComputedStyle(el),inner=c.querySelectorAll('[data-sp]'),n ;
323- for(n=0;n<inner.length;n++)inner[n].removeAttribute('data-sp') ;
321+ /* Geometry that only defines, a sprite host's <defs> of <symbol>s, draws nothing and is no asset. */
322+ if(! el.querySelector(':is(path,rect,circle,ellipse,line,polygon,polyline,text,image,use):not(defs *,symbol *)'))return ;
323+ var c=el.cloneNode(true),cs=getComputedStyle(el),inner,n ;
324324 c.removeAttribute('data-sp');c.removeAttribute('style');c.removeAttribute('class');c.removeAttribute('preserveAspectRatio');
325325 if(!c.hasAttribute('xmlns'))c.setAttribute('xmlns','http://www.w3.org/2000/svg');
326326 if(!c.hasAttribute('fill'))c.setAttribute('fill',cs.fill);
@@ -332,15 +332,38 @@ function addSvg(i,el){
332332 for(j=0;j<src.length;j++){s=getComputedStyle(src[j]);ps=getComputedStyle(src[j].parentNode);
333333 if(!dst[j].hasAttribute('fill')&&s.fill!==ps.fill)dst[j].setAttribute('fill',s.fill);
334334 if(!dst[j].hasAttribute('stroke')&&s.stroke!==ps.stroke)dst[j].setAttribute('stroke',s.stroke);}
335+ /* A same-document <use> is inlined here and not before the pass above, which walks el and c in
336+ lockstep by index: a node inlined into c has no counterpart in el. A <symbol> becomes an inner
337+ <svg> carrying the use's own attributes (x, y, width, height, a fill) and the symbol's viewBox;
338+ any other target is cloned in place. The inlined geometry takes the root's fill and stroke
339+ written in above, not the sprite's own cascade. An external reference (file.svg#id) stays as
340+ it is, and data-sp goes last because the inlined nodes bring the document's in. The root
341+ adopts the symbol's viewBox only when that one use, unoffset and unsized, is the whole root: a
342+ root composing two symbols side by side keeps its own coordinate system, and its size then
343+ reads from its width and height.
344+ ponytail: 64 resolutions in all, then whatever <use> is still queued stays dangling; a symbol
345+ holding two references to itself ends as 64 wrappers, not two to the power of the depth. */
346+ var q=Array.prototype.slice.call(c.querySelectorAll('use')),left=64,seen=0,svb='',u,t,g,h;
347+ while(q.length&&left){u=q.shift();h=u.getAttribute('href')||u.getAttribute('xlink:href')||'';
348+ t=h.charAt(0)==='#'?document.getElementById(h.slice(1)):null;if(!t)continue;left--;seen++;
349+ g=document.createElementNS('http://www.w3.org/2000/svg','svg');
350+ for(n=0;n<u.attributes.length;n++)if(!/href$/.test(u.attributes[n].name))g.setAttribute(u.attributes[n].name,u.attributes[n].value);
351+ if(t.tagName==='symbol'){if(t.hasAttribute('viewBox'))g.setAttribute('viewBox',t.getAttribute('viewBox'));
352+ if(!(u.getAttribute('x')||u.getAttribute('y')||u.getAttribute('width')||u.getAttribute('height')))svb=t.getAttribute('viewBox')||'';
353+ t=t.cloneNode(true);while(t.firstChild)g.appendChild(t.firstChild);}
354+ else g.appendChild(t.cloneNode(true));
355+ u.parentNode.replaceChild(g,u);q.push.apply(q,g.querySelectorAll('use'));}
356+ if(seen===1&&svb&&!c.hasAttribute('viewBox'))c.setAttribute('viewBox',svb);
357+ inner=c.querySelectorAll('[data-sp]');for(n=0;n<inner.length;n++)inner[n].removeAttribute('data-sp');
335358 var svg=c.outerHTML.replace(/currentColor/gi,cs.color)
336359 .replace(/var\(\s*(--[\w-]+)[^)]*\)/g,function(m,p){return cs.getPropertyValue(p).replace(/^\s+|\s+$/g,'')||m;});
337360 /* One row per glyph and colour: the geometry key joins the file, and the colours after it keep
338361 a red and a black instance of the same glyph apart, so what a row shows is what it copies.
339362 ponytail: the root's computed colours; a colour that differs only in a child's var() collapses. */
340363 var k='svg:'+fnv(svgSignature(svg))+':'+fnv(cs.fill+'|'+cs.stroke+'|'+cs.color),a=byKey[k];
341- if(!a){var vb=(el .getAttribute('viewBox')||'').split(/[\s,]+/);
364+ if(!a){var vb=(c .getAttribute('viewBox')||'').split(/[\s,]+/);
342365 a=byKey[k]={key:k,uri:'data:image/svg+xml;charset=utf-8,'+encodeURIComponent(svg),via:'svg',mime:'image/svg+xml',
343- chars:svg.length,w:+vb[2]||0,h:+vb[3]||0,alt:svgLabel(el),svg:svg,uses:[]};assets.push(a);}
366+ chars:svg.length,w:+vb[2]||+c.getAttribute('width')|| 0,h:+vb[3]||+c.getAttribute('height') ||0,alt:svgLabel(el),svg:svg,uses:[]};assets.push(a);}
344367 if(a.uses.indexOf(i)<0)a.uses.push(i);nodes[i].img=true;}
345368/* No board writes a title on its icons, so with no file to name one the name is a guess from
346369 context: the accessible name if there is one; else a class, when it is a word (logo, aiface)
0 commit comments