UNPKG

savgy

Version:

Get self-contained SVGs or bitmaps from SVG

151 lines (116 loc) 4.96 kB
/** * inline references for patterns, masks, * clipaths etc */ export function inlineRefs(svg, assetCache={}) { let els = svg.querySelectorAll('path, polyline, polygon, rect, circle, ellipse, line, text, tspan'); //attributes that may reference an external asset e.g a gradient, pattern, mask etc let props = ['fill', 'stroke', 'clip-path', 'mask', 'href', 'xlink:href']; // create defs elements let defs = svg.querySelector('defs') if (!defs) { defs = document.createElementNS(ns, 'defs'); svg.insertBefore(defs, svg.children[0]) } for (let i = 0; i < els.length; i++) { let el = els[i]; let styles = window.getComputedStyle(el); let refID, refID_clone, refEl, refEl_clone; for (let p = 0; p < props.length; p++) { let prop = props[p]; let att = el.getAttribute(prop) let value = att ? att : styles.getPropertyValue(prop); let isHref = prop === 'href' || prop === 'xlink:href' ? true : false; /** * check props to find * patterns, gradients, masks etc */ if (value.includes('url') || isHref) { //url = getUrls(value) refID = isHref ? value.substring(1) : getUrls(value).substring(1) //console.log('has ref', prop, url, refID); refEl = refID ? svg.getElementById(refID) : null // already present in svg if (refEl) { //console.log('exists'); continue; } // not in parent SVG - copy refEl = refID ? document.getElementById(refID) : null if (refEl) { refEl_clone = refEl.cloneNode(true); //svg.insertBefore(refEl_clone, svg.children[0]); defs.append(refEl_clone) } } } } function getUrls(str) { let regex = /url\((['"]?)([^'"()]+)\1\)/gi; let match = str.match(regex) ? str.match(regex)[0].split(/\(|\)/)[1].replace(/"|'/g, '') : '' return match } } export async function inlineUseRefs(svg, assetCache = {}) { let useEls = svg.querySelectorAll('use'); if (!useEls.length) return; const ns = "http://www.w3.org/2000/svg"; const nsXlink = "http://www.w3.org/1999/xlink"; // create defs elements let defs = svg.querySelector('defs') if (!defs) { defs = document.createElementNS(ns, 'defs'); svg.insertBefore(defs, svg.children[0]) } for (let i = 0; i < useEls.length; i++) { let use = useEls[i]; let href = use.getAttributeNS(nsXlink, 'href') ? use.getAttributeNS(nsXlink, 'href') : use.getAttribute('href'); // xlink sucks - we normalize it to href use.removeAttribute('xlink:href') // find external use references let hrefArr = href.split('#').filter(Boolean); let [url, id] = hrefArr; let isExternal = hrefArr.length > 1; let inlineRef, inlineDef, defId; // use definition is in document let inlinedSymbol = document.getElementById(hrefArr[0]); if (inlinedSymbol) { inlineDef = inlinedSymbol.cloneNode(true) //svg.insertBefore(inlineDef, svg.children[0]); defs.append(inlineDef); inlineRef = `#${hrefArr[0]}` use.setAttribute('href', inlineRef) } // fetch external ref else if (isExternal) { let spriteName = url.replace(/\./g, '_') // is cached if (assetCache[url] && assetCache[url][id]) { //console.log('is cached', id); inlineDef = assetCache[url][id].cloneNode(true) } else { // fetch and cache let res = await fetch(url); if (res.ok) { let spriteMarkup = await res.text(); let sprite = new DOMParser().parseFromString(spriteMarkup, 'text/html').querySelector('svg') let symbol = sprite.getElementById(id) // cache assetCache[url] = {} assetCache[url][id] = symbol; inlineDef = symbol.cloneNode(true) } } defId = `${spriteName}_${id}`; inlineDef.id = defId // add to svg if not already done if (!svg.getElementById(defId)) { //svg.insertBefore(inlineDef, svg.children[0]); defs.append(inlineDef); } // new reference use.setAttribute('href', `#${defId}`) } //console.log(assetCache); } }